四、PyTorch—自定义反向传播
-
在神经网络 —— 损失函数 & 反向传播中已经介绍了反向传播的基本概念。
反向传播的本质就是条偏导链,通过最后的loss值往前逐层计算梯度,更新权重。
在
PyTorch
中,使用loss.backward()
便是执行反向传播的过程,不过本文的重点主要是了解如何自定义反向传播过程。一般来说,有两种情况我们需要用到自定义反向传播:- 引入了不可微的函数,此时需要自定义求导方式;
- 希望在偏导链中引入新的约束(类似损失函数的惩罚项)。
在神经网络 —— 损失函数 & 反向传播中已经介绍了反向传播的基本概念。
反向传播的本质就是N条偏导链,通过最后的loss值往前逐层计算梯度,更新权重。
在PyTorch
中,使用loss.backward()
便是执行反向传播的过程,不过本文的重点主要是了解如何自定义反向传播过程。一般来说,有两种情况我们需要用到自定义反向传播:
nn.Module
来创建了一个线性回归模型。本文将来介绍Module的具体属性以及行为,了解如何搭建一个复杂的机器学习模型。这里我们介绍的是torch.nn
子包的Module
类,它是所有网络层的基类。我们将通过继承该类来实现自己的网络。
class myNet(nn.Module):
def __init__(self):
pass
def forward(self, x):
pass
这是一个基本的网络模型结构,对于一般的网络模型,我们只需要在__init__
定义每一层的结构,在forward
实现具体正向传播预测过程即可。
在上一篇博文文末例子中我们已经使用了torch.utils.data.DataLoader
来处理数据,这个类的初始化参数为
DataLoader(dataset,
batch_size=1,
shuffle=False,
sampler=None,
num_workers=0,
collate_fn=<function default_collate>, pin_memory=False, drop_last=False)
其中我们平时会用到的参数如下
dataset
(Dataset) – 加载数据的数据集;batch_size
(int, optional) – 每个batch加载多少个样本(默认: 1);shuffle
(bool, optional) – 设置为True时会在每个epoch重新打乱数据(默认: False);num_workers
(int, optional) – 用多少个子进程加载数据。0表示数据将在主进程中加载(默认: 0);drop_last
(bool, optional) – 如果数据集大小不能被batch size整除,则设置为True后可删除最后一个不完整的batch。如果设为False并且数据集的大小不能被batch size整除,则最后一个batch将更小。(默认: False)。张量(tensor)是torch中的数据结构,也是最基本的概念,一个张量本质就是一个多维数组,统一了标量、向量、矩阵的概念。
在torch中一个张量包含以下8个属性。
data
:数据,带包装类的。
dtype
:张量数据类型。
shape
:张量的性质。一个n维列表,第i个代表第i维的元素个数。
device
:张量所在设备。
grad
:数据的梯度。
grad_fn
:张量生成的函数过程,如果是用户生成的则为None,依赖于其他张量生成时则为相应操作。通过它可以得到当前张量的梯度。
就是记录张量的依赖关系,利用链式法则简化求梯度运算。
requires_grad
:是否需要梯度,不需要则不会记录自动生成的grad_fn
属性。
is_leaf
:是否为叶节点。
torch创建张量
torch.tensor() # 直接创建
torch.from_numpy() # 从np创建
torch.zeros() # 根据大小创建全0的张量
torch.ones() # 根据大小创建全1的张量
torch.full() # 根据大小创建自定义填充的张量
torch.arange(start, end) # range语法创建一维张量。
torch.linspace() # 创建均分的1维张量,闭区间。
torch.logspace() # 创建对数均分的1维张量,闭区间。
torch.eye() # 创建单位对角矩阵。
torch.normal(mean, std) # 高斯分布
torch.bernoulli(mean, std) # 伯努利分布
torch操作、运算,这些就不写了,不知道就找文档就行,基本你想要的操作/运算都是实现好的了,别想着自己写个函数。
对于一个样本数据,它可以包含很多的特征,例如一个人的身高、体重、性别、年龄,但是并不是每个特征都能用于指定的任务中,例如判断一个人是否患病,身高可能不仅没有作用还会让分类器效果变差,所以对于具体的任务要选择不同的特征输入给模型,这个过程便是特征选择。
而对于特征提取来说,是指从已有的特征中生成新的特征,例如体重和是否应减肥并不是完全相关(不同年龄、身高、性别标准都不一样),更多的我们会用身高和体重计算出新的指标BMI,再更具BMI确定是否应减肥。那么从原有的特征计算出BMI的过程,便是一个特征提取的过程。
而PCA也就是干这样一件事,从原有的特征空间进行特征提取得到特征空间,达到减少维度的效果。
关于线性分类的概念请参考逻辑回归,对于不同的分类器可能会得到不同的拟合结果,例如
上图中是三个分类器得到的决策边界,对于蓝色和黄色分类器,不能将正负类别分开,拟合能力较弱,同时对于黑色分类器来说,其不仅可以达到分类效果,还满足距离两个类别的最近样本最远。而这两个类别中离决策边界最近的样本我们称其为支持向量,其与决策边界之间的距离称为margin。
当从二维扩展到多维空间时,决策边界将变成一个超平面,我们同样是寻找满足下面条件的拟合结果:
对于一个超平面我们可以用如下线性方程来描述
wTx+b=0
同时在n维空间中,点x=(x1,x2,…,xn)到这个平面的距离为
d=∥w∥∣wTx+b∣
其中∥w∥=w12+w22+⋯+wn2,我们可以设支持向量到超平面的距离为d,其他样本到超平面的距离大于d。为了方便我们将正向类别定义为y=1,负向类别定义为y=−1,此时我们得到超平面的一个约束
{∥w∥wTx+b≥d∥w∥wTx+b≤−d,y=1,y=−1
同时我们两边乘以∥w∥变成
{wTx+b≥∥w∥dwTx+b≤−∥w∥d,y=1,y=−1
因为∥w∥d是正数,我们可以移走或者说令其等于1简化算式(这对后续的优化没有影响),并且合并两个算式,得到
y(wTx+b)≥1
由于该式恒大于0,故有y(wTx+b)=∣wTx+b∣,所以之前的距离公式可以改为
d=∥w∥y(wTx+b)
而我们的目标则是最大化这个距离,同时对于支持向量有y(wTx+b)=1,所以最大化的目标为
γ=max(∥w∥2)
乘2的目的和MSE中乘2类似,不影响性质的情况下简化后续的运算。
同时这里还可以进一步转换将目标变为求
min(21∥w∥)
去除掉除法,同时为了去除模长的根号可以再添加一个平方变为
min(21∥w∥2)
其中y(i)(wx(i)+b)≥1
kMeans是一种无监督学习聚类算法,也就是给样本分簇。是一种适合用在数据分布未知时的聚类算法,聚类的目的是最大化簇的内聚性(同一簇距离近),最小化簇间的耦合性(不同簇距离远)。
决策树是一个树形结构,每个非叶节点表示一个特征属性的测试(也就是条件),每个分支代表这个特征属性在某个值域上的输出(也就是满足某条件的下一步),每个叶节点存放一个类别。对于待预测数据,从根节点开始根据节点的条件选择不同的分支直到到达叶子节点,得到其预测类别。
由上述对决策树的定义我们可以直到决策树也是一个用于分类的监督学习模型,当然我们依然可以使用决策树进行回归,此时称为回归决策树,叶子节点上输出数值而不是类别标签。
决策树和kNN算法一样,理解起来非常直观易懂,例如下面是一个现实生活中决策树的例子
我们会发现所谓决策,就是对数据进一步提纯,将其分裂成两个不同的集合,不断进行这个过程直到得出最终结果。
显然样本最终属于什么类别取决于节点中的各个分支,我们当然可以人为的构建决策树,但是如何让模型从数据特征空间中构建出决策树,才是机器学习要干的事。