深度学习之Pytorch(一)神经网络基础及代码实现

    科技2026-03-14  6

    1.1 Tensor (张量)

    Tensor 可以和 numpy 的 ndarray相互转换Tensor有不同数据类型,有32位浮点型torch.FloatTensor、64位浮点型 torch.DoubleTensor等

    1.2 Variable (变量)

    Variable 和 Tensor 本质上没有区别,不过 Variable 会被放入一个计算图中,然后进行前向传播,反向传播,自动求导。Variable 有三个比较重要的组成属性: data, grad 和 grad_fn通过 data可以取出 Variable 里面的 tensor 数值, grad_fn 表示的是得到这个Variable 的操作,比如通过加减还是乘除来得到的,最后 grad 是这个Variabel 的反向传播梯度.构建Variable. 要注意参数 requires_grad=True/ False,这个参数表示是否对这个变量求梯度,默认的是 False ,也就是不对这个变量求梯度XXX.backward() 是对于标量求导,里面的参数可以不写;对于矢量求导,要传入参数声明,比如:y.backward(torch.FloatTensor( [1, 0.1 , 0. 01] )),这样得到的梯度就是它们原本的梯度分别乘上1,0.1和0.01

    例子见P37

    1.3 Dataset (数据集)

    torch.utils.data.Dataset是代表这一数据的抽象类,可以自定义数据类 继承和重写这个抽象类,可以通过迭代的方式来取得每一个数据。通过torch.utils.data.DataLoader定义一个新的迭代器,实现取batch, shuffle或者是多线程去读取数据。

    例:

    torch.utils.data.DataLoader(dataset,batch_size=1,shuffle=False,sampler=None, batch_sampler

    =None,num_workers=0,collate_fn=<function default_collate>, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None): 自定义数据加载器。组合了一个数据集和采样器,并提供关于数据的迭代器。

    dataset (Dataset):需要加载的数据集(可以是自定义或者自带的数据集)。

    batch_size:batch的大小(可选项,默认值为1)。

    shuffle:是否在每个epoch中shuffle整个数据集, 默认值为False。

    sampler:定义从数据中抽取样本的策略. 如果指定了, shuffle参数必须为False。

    num_workers:表示读取样本的线程数,0表示只有主线程。

    collate_fn:合并一个样本列表称为一个batch。

    pin_memory:是否在返回数据之前将张量拷贝到CUDA。

    drop_last (bool, optional) :设置是否丢弃最后一个不完整的batch,默认为False。

    在torchvision这个包中还有更高级的有关于计算机视觉的数据读取类: ImageFolder

    要求图片是下面这种存放形式:

    root/dog/xxx.png 

    root/dog/xxy.png

    root/cat/ 123.png

    root/cat/ 456.png

    dset = lmageFolder(root='root_path',transform=None,loader=default_loader)

    root是根目录,该目录下有几个文件夹,每个文件夹表示一个类别: transform和 target_transform 是图片增强,之后我们会详细讲;

    loader是图片读取的办法,然后通过 loader将图片转换成我们需要的图片类型进入神经网络

     

    1.4 nn.Module (模组)

    所有的层结构和损失函数都来自于torch.nn,所有的模型构建都是从这个基类nn.Module 继承的,所有的网络层都是由 nn 这个包得到的,比如线性层 nn.Linear。定义完模型之后,我们需要通过 nn 这个包来定义损失函数。

    1.5 torch.optim (优化)

    优化算法是一种调整模型参数更新的策略,通过修改参数使得损失函数最小化(或最大化)

    1. 一阶优化算法

    使用各个参数的梯度值来更新参数,最常用的一阶优化算法是梯度下降。梯度下降的功能是通过寻找最小值,控制方差,更新模型参数,最终使模型收敛

    网络的参数更新公式

     

    2. 二阶优化算法

    使用了二阶导数(也叫做 Hessian 方法)来最小化或最大化损失函 数,主要基于牛顿法,但是由于二阶导数的计算成本很高,所以这种方法并没有广泛使用。

     torch.optim 是一个实现各种优化算法的包,大多数常见的算法都能够直接通过这个包来调用,比如随机梯度下降,具体官方实例(在优化之前需要先将梯度归零,即 optimizer.zeros() , 然后通过 loss.backward()反向传播,自动求导得到每个参数的梯度,最后只需要 optimizer. step ()就可以通过梯度做一步参数更新)

     

    1.6 模型的保存与加载

    使用 torch.save,有两种保存方式: (1) 保存整个模型的结构和参数,保存的对象是模型 model (2) 保存模型的参数,保存的对象是模型的状态 model.state dict()

    对应于保存模型,加载模型的方式: (1) 加载完整的模型和参数,使用 load model = torch.load('XXX' ) (2) 加载模型参数,需要先导入模型的结构,然后model.load state dic (torch.load('XXX')) 来导入

     

    2.1 分类问题

    二分类算法一一Logistic 回归

    非线性变换 Sigmoid 函数是 Logistic 分布函数中 γ=1 ,μ=0 的特殊形式。

     Logistic 回归要更进一步,通过找到分类概率 P(Y =1) 与输入变量x的直接关系,然后通过比较概率值来判断类别. Logistic 回归的思路是先拟合决策边界(这里的决策边界不局限于线性,还可以是多项式),再建立这个边界和分类概率的关系,从而得到二分类情况下的概率。模型的参数估计 具体见P44

     

    2.2 代码实现

    定义模型并训练

    定义 Logistic 回归的模型

    class  LogisticRegression(nn.Module) :

        self.lr=nn.Linear()  

        self.sm=nn.Sigmoid ()

    criterion = nn.BCELoss ()

    optimizer = torch . optim.SGD( logistic_model.parameters () , lr=le-3 ,momentum=0.9 )

    训练模型

    for epoch in range(50000):

        变量转成Variable形式

        前向传播:out = model (x)    #self.lr=nn.Linear()  self.sm=nn.Sigmoid ()

                  loss = criterion(out , y)

        反向传播:optimizer. zero_grad ()

                  loss.backward ()

                  optimizer. step () 

     

    3.1 激活函数

    简单的一层神经网络的数学形式简单,可以看成一个线性运算再加上一个激活函数(可激活变大/变小),Logistic 回归是使用了 Sigmoid 作为激活函数的一层神经网络

     

    适合用于前向传播(实际基本不用)

    Sigmoid 函数有以下两大缺点

    ( 1 )  Sigmoid 函数靠近1和0的两端会造成梯度消失,梯度下降法通过梯度乘上学习率来更新参数,因此如果梯度接近 0,那么没有任何信息来更新参数,造成模型不收敛

    ( 2 )  经过 Sigmoid 激活函数的输出,作为后一层网络的输入是非 0 均值的,若输入进入下一层神经元的时候全是正的,则导致梯度全是正的,在更新参数时永远都是正梯度。

    ReLU 的优点:适合用于后向传播

    (1)相比于 Sigmoid 和 Tanh 激活函数, ReLU 激活函数可极大地加速随机梯度下降法的收敛速度,这因为它是线性的,且不存在梯度消失的问题

    ( 2 )相比于 Sigmoid 和 Tanh 激活函数, ReLU 的计算方法更加简单.只需要一个阈值运算就可以得到结果,不需要进行一大堆复杂的运算。

    ReLU 的缺点:

    训练的时候很脆弱.举个例子:由于ReLU在x<0时梯度为0,这样就导致负的梯度在这个ReLU被置零,那么这个神经元之后的梯度就永远是0了,不再对任何数据有所响应。在实际操作中可以通过设置比较小的学习率来避免这个小问题

     

    4.Leaky ReLU   略

    5.Maxout   略

     

    3.2 神经网络的结构、模型的表示能力与容量

    N 层神经网络并不会把输入层算进去,因此一个一层的神经网络是指没有隐藏层,只有输入层和输出层的神经网络。Logistic 回归就是一个一层的神经网络但是更深的全连接神经网络结构效果提升就不大了,而与此对比的是卷积神经网络的深层结构则会有更好的效果。对于容量更大(更深)的神经网络,它的局部极小点的方差特别小,也就是说训练多次虽然可能陷入不同的局部极小点,但是它们之间的差异是很小的,这样训练就不会完全依靠随机初始化。控制网络过拟合见后面

     

    4.1 梯度下降法

    损失函数f , 我们用反向传播算法算出 f 的梯度 f(x),更新参数反向传播就是链式求导:如果需要对其中的元素求导,那么可以一层一层求导,然后将结果乘起来。求解每层网络的参数都是通过链式法则将前面的结果求出不断迭代到这一层的参数更新:

    第一个参数代表下一步的位置

    第二个参数代表当前位置

    α代表学习率,每迈出一步的距离,不能太大也不能太小

    代表梯度,损失函数

     

    4.2 梯度下降法的变式

    人工设置学习率

    1.SGD 随机梯度下降

    每次使用一批 (batch) 数掘进行梯度的计算,而不是计算全部数据的梯度

    2.Momentum

    相比于标准梯度下降算法,Momentum项能够在相关方向加速SGD,使得参数更新方向更多地沿着横轴进行,抑制振荡,从而加快收敛

    3.Nesterov

    Momentum加上nesterov项后,梯度在大的跳跃后,进行计算对当前梯度进行校正

     

    自适应学习率

    1.Adagrad

    适合处理稀疏梯度中后期,分母上梯度平方的累加将会越来越大,使gradient→0,使得训练提前结束

    2.Adadelta

    训练初中期,加速效果不错,很快训练后期,反复在局部最小值附近抖动

    3.RMSprop

    RMSprop算是Adagrad的一种发展,和Adadelta的变体,效果趋于二者之间适合处理非平稳目标对于RNN效果很好

    4.Adam

    这是一种综合型的学习方法,可以看成是RMSprop 加上动量 (Momentum) 的学习方法,达到比 RMSProp 更好的效果。

    经验之谈

    对于稀疏数据,尽量使用学习率可自适应的优化方法,不用手动调节,而且最好采用默认值SGD通常训练时间更长,容易陷入鞍点,但是在好的初始化和学习率调度方案的情况下,结果更可靠如果在意更快的收敛,并且需要训练较深较复杂的网络时,推荐使用学习率自适应的优化方法。Adadelta,RMSprop,Adam是比较相近的算法,在相似的情况下表现差不多。在想使用带动量的RMSprop,或者Adam的地方,大多可以使用Nadam取得更好的效果

    补充 深度学习最全优化方法总结比较

     

    5.1 数据预处理

    1.中心化

    每个特征维度都减去相应的均值实现中心化,这样可以使得数据变成 0 均值,特别对于一些图像数据.

    2.标准化/归一化

    有两种常用的方法:

    StandardScaler:减去均值然后除以方差(或标准差),这种数据标准化方法经过处理后数据符合标准正态分布,即均值为0,标准差为1

    MinMaxScaler:将特征缩放到给定的最小值和最大值之间,或者也可以将每个特征的最大绝对值转换至单位大小。

    标准化(归一化)两个优点:

      1)归一化后加快了梯度下降求最优解的速度;

      2)归一化有可能提高精度。

    经验之谈

    1)在分类、聚类算法中,需要使用距离来度量相似性的时候、或者使用PCA技术进行降维的时候,StandardScaler表现更好。

    2)在不涉及协方差计算、数据不符合正态分布的时候,可以使用MinMaxScaler。比如图像处理中,将RGB图像转换为灰度图像后将其值限定在[0,255]的范围。

    3)使用MinMaxScaler,其协方差产生了倍数值的缩放,因此无法消除量纲对方差、协方差的影响,对PCA分析影响巨大;同时,由于量纲的存在,距离的计算结果会不同。

    在StandardScaler中,每个维度都是去量纲化的,避免了不同量纲的选取对距离计算产生的巨大影响。

    4)若数据包含许多异常值,RobustScaler 对数据的中心和范围使用更有鲁棒性的估计。

          针对稀疏矩阵,用MaxAbs

    3.PCA(去噪)

    数据中心化,用协方差矩阵对数据进行去相关性,投影到一个特征空间,取主要特征向量。

    4.白噪声(加噪)

    将数据投影到一个特征空间, 然后每个维度除以特征值来标准化这些数据,直观上就是一个多元高斯分布转化到了 一个 0 均值,协方差矩阵为 1 的多元高斯分布 。

    3. 4.在CNN中基本不用

     

    5.2 权重初始化

    1.全 0 初始化

    不可取,否则权重相同

    2. 随机初始化

    经常用,在训练深度神经网络时可能会造成两个问题,梯度消失和梯度爆炸。

    梯度消失和梯度爆炸 补充:详解机器学习中的梯度消失、爆炸原因及其解决方法

    3. 稀疏初始化

    实际较少用到

    4. 初始化偏置( bias )

    偏置 (bias)通常是初始化为 0

    5. 批标准化( Batch Normalization )

    效果好,它位于X=WU+B激活值获得之后,非线性函数变换之前

    对于每个隐层神经元,把逐渐往非线性函数的取值区间的上下限两端靠近的激活输入分布 强制拉回到 均值为0方差为1的比较标准的正态分布,使得输入值落入非线性函数比较敏感的区域,以此避免梯度消失问题。

    具体见 深入理解Batch Normalization批标准化

     

    5.3 防止过拟合

    1.正则化(目的是限制参数过多或者过大,避免模型更加复杂)

    L2正则化是正则化(regularization)中比较常用的形式:对于权重过大的部分进行惩罚,让参数更新之后更加靠近0 

    L1正则化是另一种正则化方法:在损失函数中增加权重的 1 范数

     

    在实际中通常使用 L2 正则化,我们也可以把L1正则化和 L2 正则化结合起来,如 。L1相对于 L2 ,在优化的过程中可以让权重变得更加稀疏,在优化结束的时候,权重只会取一些与最重要的输入有关的权重,这就使得与噪声相关的权重被尽可能降为 0L2 会比L1更加发散,权重也会被限制得更小

    补充:机器学习中 L1 和 L2 正则化的直观解释

    还有一种正则化方法叫做最大范数限制,其迫使权重在更新的过程中范数有一个上界,可以使得当学习率设置太高的时候网络不会"爆炸"

     

    2.Dropout

    在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作, 也就是说每次训练忽略一部分的特征检测器(让一部分的隐层节点值为0)在预测的时候,如果保留所有的权重(预测没办法随机丢弃,若丢弃一些神经元,会带来结果不稳定的问题),每一个神经单元的权重参数要乘以概率p。每一次训练batch,Dropout之后就可以看作是一个新的模型,然后训练了很多次之后就可以看成是这些模型的集成,整个dropout过程就相当于对很多个不同的神经网络取平均

     

    5.4 神经网络实现

    快速回忆版

    1、构建好一个神经网络

    2、训练网络必须经过4步:

    第一步:将输入input向前传播,进行运算后得到输出output

    第二步:将output再输入loss函数,计算loss值(是个标量)

    第三步:将梯度反向传播到每个参数

    第四步:利用公式进行权重更新

     

    细抠代码版

    1、搭建网络:

    黄字重点

    2、导入包、定义超参数(如 batch size 、 learning rate 还有 num_epoches)

    3、用torchvision.transforms进行数据预处理

    4、读取数据集(见1.3)

    torchvision.datasets

    torch.utils.data.DataLoader。建立数据迭代器

    5、导入网络,定义损害函数和优化方法

    model =  net.Batch_Net(输入图片维度, 隐藏层1 , 隐藏层2, 分类数)

    criterion  =  nn.CrossEntropyLoss()

    optimizer =  optim.SGD(model.parameters() ,  lr=learning_rate)

    6、训练网络

    例:for epoch in range(50000):

        变量转成Variable形式

        前向传播:out = model (x)    #self.lr=nn.Linear()  self.sm=nn.Sigmoid ()

                  loss = criterion(out , y)

        反向传播:optimizer. zero_grad ()

                  loss.backward ()

                  optimizer. step () 

    7、测试网络

     

     

     

     

     


    Processed: 0.014, SQL: 9