卷积网络

主体:卷积+池化

 
数据集[批次,特征数,特征shape]
    
卷积变换特征数,先少后多

池化层 对特征shape降维,舍弃 次要/部分 信息 
- 既然卷积没有减少特征的shape,还将特征由少变多了,
- 这导致网络的参数量爆增 
- 因此就需要一个组件去降低参数量,选择了池化层来解决这个问题
  - 卷积当然也可以将参数由多变少,但卷积的计算量大 
  - 并且参数由少变多,重复数据及无效信息也是爆增
  - 这种情况下,用池化这种直接按相对大小舍弃信息的做法显得高效有用
    - 实际上是将原信息图(即原特征图)划分成等分的区间
    - 每个区间都取出一个 
    - 特征图的整体架构还在,只是每张特征图变得稀疏了
    - 个人认为:可以一个维度取一个最大池化,另外一个维度取一个最小池化
      - 仅是想法,尚未验证... 
      - 按理论来说,这种做法不会取得更好效果,因为如果交易好,早就流行了
      - 但至今未见过有人这么做 
      - 但有时间有条件的话,本人还会尝试验证一下 

优化

 
BN加速收敛 
激活函数Rule增加 神经网络 的非线性能力 

健壮性

 
dropout 

全连接分类

 
不管数据原来多少维,最后还是要放到一个维度上,做全连接分类 

损失函数与优化器

 
损失函数用于衡量模型与样本两个分布的差异 

分类问题:交叉熵 CE
回归问题:均方误差 MSE 

优化器
- 梯度下降法修改参数
- w = w - w*w.grad
- adam
- sgd 


论文

 
重在思想及转化

抓住论文的核心思想,结合实际问题进行转化 

 
设计一个网络,是有原因的,是要去解决一个问题

这就是网络应该具备的能力,要验证以确定网络具备该能力  

 
卷积的参数分为两类:
一类控制特征的维度变换
一类控制如何滑动取窗,每个窗口得到一个数值 

 
in_channels: int,
out_channels: int,

kernel_size: Union[int, Tuple[int]],
stride: Union[int, Tuple[int]] = 1,
padding: Union[str, int, Tuple[int]] = 0,

二维卷积
    
- 输入:
    - 类--对象:
        - in_channels:代表输入的图像是几个通道的
        - out_channels:代表输出的特征图是几个通道的
        - kernel_size:代表卷积核的大小
        - stride:代表卷积移动时的步长
        - padding:代表补零的个数(上下对称,左右对称)
    
    - 对象 -- 函数:
        - [N, 输入特征数, H_in, W_in]
        - [N, 输出特征数, H_out, W_out]
    - 功能:
        - 提取区域特征

 

    
常用卷积

输入输出及功能

 
"""
二维卷积

- 输入:
    - 类--对象:
        - in_channels:代表输入的图像是几个通道的
        - out_channels:代表输出的特征图是几个通道的
        - kernel_size:代表卷积核的大小
        - stride:代表卷积移动时的步长
        - padding:代表补零的个数(上下对称,左右对称)
    
    - 对象 -- 函数:
        - [N, C_in, H_in, W_in]
        - [N, C_out, H_out, W_out]
    - 功能:
        - 提取区域特征
"""

 
import torch
from torch import nn
from torch.nn import functional as F

 
X=torch.linspace(start=1,end=20,steps=20).reshape(-1,5).unsqueeze(0).unsqueeze(0)
X=X.reshape(2,1,2,5)
X
tensor([[[[ 1.,  2.,  3.,  4.,  5.],
          [ 6.,  7.,  8.,  9., 10.]]],


        [[[11., 12., 13., 14., 15.],
          [16., 17., 18., 19., 20.]]]])

X.shape
torch.Size([2, 1, 2, 5])

[2, 5]就代表一张图片,重在观察各API的参数,输入,输出 
两个批次,一个批次就是一张图片,一个样本

 
imgs=X 
conv2d = nn.Conv2d(in_channels=1, out_channels=2, kernel_size=3, stride=1, padding=1)
y = conv2d(imgs)
y


 


输入输出及功能

 
"""
一维卷积
- 输入和输出:
    - 类 -- 对象:
        - 跟二维一样
    - 对象 -- 函数:
        - 输入:[N, C_in, L_in]
        - 输出:[N, C_out, L_out]
- 功能:
    - 提取序列特征
"""

 
import torch
from torch import nn
from torch.nn import functional as F

X=torch.linspace(start=1,end=20,steps=20).reshape(-1,5).unsqueeze(0).unsqueeze(0)
X

1维卷积的数据格式为[B,C,L]

 
imgs=X.reshape(2,1,2*5)
conv1d = nn.Conv1d(in_channels=1, out_channels=2, kernel_size=3, stride=1, padding=1)
y = conv1d(imgs)
 
BN

 
"""
Batch Normalization

- 目的:
    - 对卷积层的输出,进行标准化:
        - 将分布中心拉到0,让分布以0为中心,
        - 将数据元素之间平均距离变换到1
            - 分布中元素尽量保持相对位置收缩/扩大,
            - 使得元素之间的平均距离接近1 
    - 加快模型的收敛速度
- 输入:
    - num_features:特征个数
    - 针对图像来说,指的是通道数(特征图的个数,也就是把一个特征图看做是一个特征)
- 输出:
    - 输入和输出维度都不变
- 特点:
    - 训练时:从本批次的数据中提取参数,提取参数来进行标准化操作
    - 推断时:使用训练时各批次的参数来评估了一个整体的参数来标准化操作
    
"""

 
import torch
from torch import nn
from torch.nn import functional as F

 
X=torch.linspace(start=1,end=20,steps=20).reshape(-1,5).unsqueeze(0).unsqueeze(0)
X=X.reshape(2,1,2,5)
X
tensor([[[[ 1.,  2.,  3.,  4.,  5.],
            [ 6.,  7.,  8.,  9., 10.]]],


        [[[11., 12., 13., 14., 15.],
            [16., 17., 18., 19., 20.]]]])

X.shape
torch.Size([2, 1, 2, 5])

[2, 5]就代表一张图片,重在观察各API的参数,输入,输出 
两个批次,一个批次就是一张图片,一个样本

 
bn = nn.BatchNorm2d(num_features=1)

y = bn(imgs)
y

 
tensor([[[[-1.6475, -1.4741, -1.3007, -1.1272, -0.9538],
    [-0.7804, -0.6070, -0.4336, -0.2601, -0.0867]]],


  [[[ 0.0867,  0.2601,  0.4336,  0.6070,  0.7804],
    [ 0.9538,  1.1272,  1.3007,  1.4741,  1.6475]]]],
 grad_fn=NativeBatchNormBackward0)

 


 
定义一个模板类,参数是特征数,即同一特征才一起做归一化,不同特征之间无交叉
nn.BatchNorm2d(num_features=1)

调用对象,传入数据即可 
y = bn(imgs)


归一化,就是(x-mean)/std  

 
批归一化中的“批”,指的是同一特征,所有批次为一个整体进行归一化 
没有跨特征,但跨了样本,将批次中所有样本的相同特征看作一个整体 

观察数据输入输出

 
tensor([[[[ 1.,  2.,  3.,  4.,  5.],
    [ 6.,  7.,  8.,  9., 10.]]],

[[[11., 12., 13., 14., 15.],
    [16., 17., 18., 19., 20.]]]])

从整个批次看,原数据的中心在10附近,
批归一化后,数据的中心在[-0.0867,0.0867],即0附近

tensor([[[[-1.6475, -1.4741, -1.3007, -1.1272, -0.9538],
    [-0.7804, -0.6070, -0.4336, -0.2601, -0.0867]]],

  [[[ 0.0867,  0.2601,  0.4336,  0.6070,  0.7804],
    [ 0.9538,  1.1272,  1.3007,  1.4741,  1.6475]]]],
 grad_fn=NativeBatchNormBackward0)

 


 

  

 


池化

 
import torch
from torch import nn
from torch.nn import functional as F

X=torch.linspace(start=1,end=20,steps=20).reshape(-1,5).unsqueeze(0).unsqueeze(0)
X

 
tensor([[[[ 1.,  2.,  3.,  4.,  5.],
    [ 6.,  7.,  8.,  9., 10.],
    [11., 12., 13., 14., 15.],
    [16., 17., 18., 19., 20.]]]])

 
imgs=X.reshape(2,1,2,5)

2*2选1,padding=0直接舍弃

 
pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
y = pool(imgs)
y
tensor([[[[ 7.,  9.]]],
        [[[17., 19.]]]])

 
的确没有取中最后一个核的最大值,但池化的作用就是舍弃,在边上的数据,丢掉还可以,
当然,补一下也不会有什么问题,应该说会更好一些 

 


激活层

 
"""
激活层
- ReLU
- 引入非线性因素
- 负的舍弃,正的原样输出
"""
    

 
舍弃一部分值,留下的,是符合某个维度的数据,
这来自于对错,是非,开关的二选一,
是某个维度的数据留下,不是的,过滤/丢弃 

 
直接看的话,是比较难理解的,但网络参数是动态的
随着训练,参数改变自己,以达到可以二选一的位置,
数据分布也在改变,改变到从某个维度看,符合二选一的标准 

也就是说,数据本身是错综复杂的,但流过参数网络后,
变得有条理,有纹理,经纬分明,
变得可以二选一之后,rule这个激活函数的作用就出来了

是rule这个激活函数加入到 具有自适应能力的参数网络中后, 
参数网络经过适应,慢慢地让relu可以起到作用,
从这点来讲,relu应该放到maxpool的后面
- 因为maxpool分块选择出了特征图的主要结构/节点脉络
- 从稀疏但仍有原特征的结构中进行操作更高效一些 
- 但个人感觉,实际上可能效果不明显

 
import torch
from torch import nn
from torch.nn import functional as F

X=torch.linspace(start=1,end=20,steps=20).reshape(-1,5).unsqueeze(0).unsqueeze(0)
X
tensor([[[[ 1.,  2.,  3.,  4.,  5.],
          [ 6.,  7.,  8.,  9., 10.],
          [11., 12., 13., 14., 15.],
          [16., 17., 18., 19., 20.]]]])

 
imgs=X.reshape(2,1,2,5)
bn=nn.BatchNorm2d(num_features=1)
imgs = bn(imgs)
imgs

 
tensor([[[[-1.6475, -1.4741, -1.3007, -1.1272, -0.9538],
    [-0.7804, -0.6070, -0.4336, -0.2601, -0.0867]]],


  [[[ 0.0867,  0.2601,  0.4336,  0.6070,  0.7804],
    [ 0.9538,  1.1272,  1.3007,  1.4741,  1.6475]]]],
 grad_fn=NativeBatchNormBackward0)

ReLU在BN之后,真的就直接抹掉了一半数据

 
relu = nn.ReLU()
relu(imgs)
tensor([[[[0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
          [0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]],

        [[[0.0867, 0.2601, 0.4336, 0.6070, 0.7804],
          [0.9538, 1.1272, 1.3007, 1.4741, 1.6475]]]], grad_fn=ReluBackward0)

 
之前在上课的时候,向老师提过个人的理解:
BN将数据拉到标准正态分布,relu放在BN之后,直接抹掉了一半数据
剩下的一半,仍然具有数据的结构,有数据的规律/性质,
并且不是那种随机去减,可能会让整体规律集中彻底丢失的减,
而是完整保留对称结构中一半的减,这样即保证了数据的规律,而降低了数据量 

老师说不是这样的,但没说原因,我理解他的不是,是从各个组件本身的作用去说的,
- BN就是为了加速收敛
- relu就是增加了非线性能力 
而没有,或者说老师从未从联系的角度思考过他们,一时听到这样的说法大脑一时有些思考不过来... 
好像有道理,但又与原来已有的理解有些不符... 

如果这个人理解有道理,那么应该将以下顺序放更合理一些:
maxpool从原数据中取主脉络
relu抹去0以下的数据
BN再将之拉回正态分布 

 


 

  

 


dropout

 
一叶知秋,蛛丝马迹,
从少量的数据中判断出某些规律,这样的能力是强大的 

全力出手解决一件事,当然没有只出七分力来得轻松,前提是出七分力也把这事解决了 

从事物的一部分推断出整体,这就是dropout的能力 
- 建立在提取的这部分,足以分类/达到预期效果 的前提上 

 
过拟合
    - 如果训练数据的信息量足够大,那么噪声也随之会足够大
    - 这时的模型,它不会主动丢弃噪声,都是有什么学什么 
    - 在模型看来,所有的数据都是可学习的,且是要学习的,
    - 这里就容易出现过拟合
    - 而dropout一定程度上,可以解决/缓解了 过拟合的问题    
    

 
import torch
from torch import nn
from torch.nn import functional as F

X=torch.linspace(start=1,end=20,steps=20).reshape(-1,5).unsqueeze(0).unsqueeze(0)
X
tensor([[[[ 1.,  2.,  3.,  4.,  5.],
          [ 6.,  7.,  8.,  9., 10.],
          [11., 12., 13., 14., 15.],
          [16., 17., 18., 19., 20.]]]])

 
imgs=X.reshape(2,1,2,5)

dropout = nn.Dropout(p=0.2)
dropout(imgs)

 
tensor([[[[ 1.2500,  2.5000,  3.7500,  5.0000,  0.0000],
    [ 7.5000,  8.7500, 10.0000,  0.0000,  0.0000]]],


  [[[13.7500, 15.0000, 16.2500, 17.5000, 18.7500],
    [ 0.0000, 21.2500, 22.5000, 23.7500, 25.0000]]]])

 
0.2*20=4 
有4个数变成了0,同时非0元素都膨胀了一点点 

舍弃哪些数据是概率随机的

 
dropout = nn.Dropout(p=0.2)
dropout(imgs)

 
tensor([[[[ 1.2500,  0.0000,  3.7500,  0.0000,  0.0000],
    [ 0.0000,  8.7500,  0.0000, 11.2500, 12.5000]]],

  [[[ 0.0000, 15.0000, 16.2500, 17.5000, 18.7500],
    [ 0.0000, 21.2500, 22.5000, 23.7500, 25.0000]]]])

全连接

 
将一个向量变换成另外一个向量
全连接每次计算的是1维数据,其数据的输入格式最多为2维,
其中dim=0是批次的维度

2维矩阵进行全连接,本质还是遍历dim=0的维度,每次处理的还是1维数据 

1维示例

 
import torch
from torch import nn

x=torch.tensor([1.,2.,3.])

line = nn.Linear(in_features=1*3,out_features=2)
line(x)

 
tensor([-0.9335, -1.3012], grad_fn=ViewBackward0)
  

2维示例

 
import torch
from torch import nn

X=torch.linspace(start=1,end=20,steps=20).reshape(-1,5).unsqueeze(0).unsqueeze(0)
imgs=X.reshape(2,1,2,5)


使用全连接之前,要先将一个样本转化为1维矩阵,即向量

 
flat=nn.Flatten()
imgs = flat(imgs)

 
line = nn.Linear(in_features=1*2*5,out_features=2)
line(imgs)

 
tensor([[ 6.9716,  0.8715],
    [22.2193, -0.9051]], grad_fn=AddmmBackward0)

 


参考