主要内容来自于B站up主 刘二大人的《PyTorch深度学习实践》以及 跟李沐学AI的《动手学深度学习》
部分内容来自网络
训练目的: 找到一组参数使得Loss (预测值与真实值的差值) 最小
MSE: 平均平方误差 Mean Square Error
名词 | 定义 |
---|---|
Epoch (时期) | 将所有训练样本训练一次的过程 即训练一轮 所有训练样本在神经网络中都进行了一次正向传播和一次反向传播 |
Batch (批次) | 当一个Epoch的样本(所有的训练样本)数量可能过大 就需要把它分块(分批),也就是分成多个Batch进行训练 Batch_Size:每批样本的大小 |
Iteration (迭代) | 训练一个Batch就是一次Iteration (类似迭代器) |
ω = ω − α ∂ c o s t ∂ ω \omega = \omega - \alpha\frac{\partial cost}{\partial \omega} ω=ω−α∂ω∂cost α是学习率
鞍点 —— 梯度为零点 —— 梯度消失
梯度 —— 对损失函数求导 —— 复合函数求导
前馈计算 —— 数据正向通过每层网络结构进行运算
如果数据集比较小 采取全数据集肯定是最好的 可以更好的代表样本总体 从而更准确的找出梯度下降方向 从而确定全局最优
但对于大数据集 采用全部数据集样本量过大 走向另一个极端便是令Batch_Size为1 即每次只训练一个样本
两者折中后 通过选择一个合适的Batch_Size
梯度下降方法 | 具体操作 | 准确性 | 时间复杂度 |
---|---|---|---|
BGD (Batch Gradient Descent) 批量梯度下降 |
在更新参数时使用所有的样本进行更新 | 可能局部最优 准确性低 |
可以并行计算 时间复杂度低 |
SGD (Stochastic Gradient Descent) 随机梯度下降 |
每次对其中一个样本求梯度进行更新 Cost变成Loss |
避免局部最优 准确性高 |
每一轮都需要上一轮结果 时间复杂度高 |
MBGD (Mini-Batch Gradient Descent) 小批量梯度下降 |
两者折中 选择一个合适的Batch_Size进行分批训练 |
通常选用的Mini-Batch_Size在50-256之间
目前大部分文章中所说的随机梯度下降(SGD)默认指的就是MBGD
MM:Matrix Multiplication
每一层的输出都要加一个非线性变换函数 —— 激活函数
否则叠加再多层网络最终都会等价成一个线性模型 增加模型深度失去了意义
增加模型深度的意义就在于需要通过多层参数的叠加来实现用线性函数逼近非线性函数 只有加入激活函数后,神经网络才有了能够逼近任意函数形式的能力
链式求导
创建计算图 前馈计算 求出最终的Loss
因为Loss的具体函数式已知 可以直接得到Loss对上一层输入的偏导
先算出当前层结果(设为z)对当前层各输入的偏导 ∂ z ∂ ω ∂ z ∂ x \frac{\partial z}{\partial \omega} \frac{\partial z}{\partial x} ∂ω∂z∂x∂z
反向传播得到最终Loss对当前层结果的偏导 ∂ L o s s ∂ z \frac{\partial Loss}{\partial z} ∂z∂Loss
链式法则得到Loss对当前层输入的偏导
∂ L o s s ∂ ω = ∂ L o s s ∂ z ⋅ ∂ z ∂ ω \frac{\partial Loss}{\partial \omega} = \frac{\partial Loss}{\partial z} · \frac{\partial z}{\partial \omega} ∂ω∂Loss=∂z∂Loss⋅∂ω∂z
∂ L o s s ∂ x = ∂ L o s s ∂ z ⋅ ∂ z ∂ x \frac{\partial Loss}{\partial x} = \frac{\partial Loss}{\partial z} · \frac{\partial z}{\partial x} ∂x∂Loss=∂z∂Loss⋅∂x∂z
得到结果后继续向上一层反向传播
Pytorch中的基础数据类型
两个成员: data(权重本身的值) grad(损失函数对参数的导数)
loss.backward() #只有标量才能反向传播
#pytorch中的各种nn.xxLoss() 得到的都是minibatch中各结果平均/求和后的值 都是标量
#backward完成计算之后 计算图就会被释放 计算得到的梯度会存到相关的tensor中
tensor.grad.data #获取梯度数值 仍然是tensor类型
tensor.grad.item() #取得元素值 标量
#用tensor类型相互运算会一直产生新的计算图
#更新权重时需要使用data进行纯数值运算 避免运算图的产生
tensor.grad.data.zero() #一轮学习结束后需要手动将梯度清零 避免代入下一轮计算
#实际应用中常用
model.zero_grad()
optimizer.zero_grad()
import torch
# 1.准备数据集 prepare dataset
# x,y是矩阵,3行1列 也就是说总共有3个数据,每个数据只有1个特征
x_data = torch.tensor([[1.0], [2.0], [3.0]])
y_data = torch.tensor([[2.0], [4.0], [6.0]])
# 2.通过Pytorch自带类设计网络模型 design model using class
"""
our model class should be inherit from nn.Module, which is base class for all neural network modules.
member methods __init__() and forward() have to be implemented
class nn.linear contain two member Tensors: weight and bias
class nn.Linear has implemented the magic method __call__(),which enable the instance of the class can
be called just like a function.Normally the forward() will be called
"""
class LinearModel(torch.nn.Module):
def __init__(self):
super(LinearModel, self).__init__()
# (1,1)是指输入x和输出y的特征维度,这里数据集中的x和y的特征都是1维的
# 该线性层需要学习的参数是w和b 获取w/b的方式分别是linear.weight/linear.bias
self.linear = torch.nn.Linear(1, 1)
def forward(self, x):
y_pred = self.linear(x)
return y_pred
model = LinearModel()
# 3.通过Pytorch API 构建损失函数和评估器 construct loss and optimizer
# criterion = torch.nn.MSELoss(size_average = False)
criterion = torch.nn.MSELoss(reduction = 'sum')
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01) # model.parameters()自动完成参数的初始化操作
# 4.进行循环训练 training cycle forward, backward, update
for epoch in range(100):
y_pred = model(x_data) # forward:predict
loss = criterion(y_pred, y_data) # forward: loss
print(epoch, loss.item())
optimizer.zero_grad()
loss.backward() # backward: autograd,自动计算梯度
optimizer.step() # update 参数,即更新w和b的值
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())
x_test = torch.tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)
虽然名字叫回归 但是用于分类
分类问题——求的是 P ( X = 1 ) P(X=1) P(X=1) 的概率 最终的结果要从实数空间映射到0-1上
激活函数作用 1.建立非线性化映射 2.使输出的值映射到0-1之间
Sigmoid函数(Logistic Function σ ( x ) \sigma(x) σ(x)) y = 1 1 + e − x y=\frac{1}{1+e^{-x}} y=1+e−x1 S型曲线 饱和函数
x → + ∞ y → 1 x\rightarrow+\infty \quad y\rightarrow1 x→+∞y→1 x → − ∞ y → 0 x\rightarrow-\infty \quad y\rightarrow0 x→−∞y→0
Loss函数 要比较两个分布之间的差异
在线性回归问题中,常常使用MSE(Mean Squared Error)作为loss函数
对于分类问题 通常使用交叉熵
原因一,使用交叉熵loss下降的更快;
原因二,使用交叉熵是凸优化,MSE是非凸优化
信息量 “当越不可能的事件发生了,我们获取到的信息量就越大。越可能发生的事件发生了,我们获取到的信息量就越小。” —— 信息量应该与事件发生概率有关
定义信息量为 I ( x 0 ) = − l o g ( p ( x 0 ) ) I(x_0)=-log(p(x_0)) I(x0)=−log(p(x0)) 既概率p越接近1 信息量越接近0 p越接近0 信息量越接近负无穷(取反后变成正无穷)
熵 —— 所有信息量的期望
对于一个事件 有很多种可能性 每一种可能性都会对应一个概率 也就有一个信息量
将所有n种可能性的信息量求期望就是熵
H ( X ) = − ∑ i = 1 n p ( x i ) l o g ( p ( x i ) ) H(X)=-\sum_{i=1}^n p(x_i)log(p(x_i)) H(X)=−∑i=1np(xi)log(p(xi))
y i y_i yi表示样本i的label p i p_i pi表示样本i预测为真的概率
相对熵(KL散度) 对于同一个随机变量X有两个单独的概率分布 P(x) 和 Q(x),可以用 KL 散度(Kullback-Leibler (KL) divergence)来衡量这两个分布的差异
即如果用P来描述目标问题,而不是用Q来描述目标问题,得到的信息增量
D K L ( p ∣ ∣ q ) = ∑ i = 1 n p ( x i ) l o g ( p ( x i ) q ( x i ) ) D_{KL}(p||q)=\sum_{i=1}^np(x_i)log(\frac{p(x_i)}{q(x_i)}) DKL(p∣∣q)=∑i=1np(xi)log(q(xi)p(xi)) D K L D_{KL} DKL的值越小,表示q分布和p分布越接近
交叉熵(Cross Entropy)
对相对熵进行变形,得到
KaTeX parse error: No such environment: eqnarray at position 7: \begin{̲e̲q̲n̲a̲r̲r̲a̲y̲}̲ D_{KL}(p||q) &…
等式的前一部分恰巧就是p的熵,因为在模型训练中对应Label的 H ( p ( x ) ) H(p(x)) H(p(x))不变,因此只需要计算后一部分,定义为交叉熵 H ( p , q ) = − ∑ i = 1 n p ( x i ) l o g ( q ( x i ) ) H(p,q)=-\sum_{i=1}^np(x_i)log(q(x_i)) H(p,q)=−∑i=1np(xi)log(q(xi))
对于二分类问题 l o s s = − ( y l o g y ^ + ( 1 − y ) l o g ( 1 − y ^ ) ) loss=-(ylog\hat{y}+(1-y)log(1-\hat{y})) loss=−(ylogy^+(1−y)log(1−y^)) y ^ \hat{y} y^表示predict值,即预测概率
对于Mini-Batch训练,需要算出多个小批量loss再求均值
pytorch中实际调用
# 构造函数部分无变化 只是在forward中加入激活函数
y_pred = torch.sigmoid(self.linear(x))
# Loss函数更换为BCE(Binary Cross Entropy)
criterion = torch.nn.BCELoss(size_average = False)
输出都大于零且和为1 即满足概率模型 各类别之间具有竞争性
最后一层线性层之间使用Softmax作为激活函数 σ ( z i ) = e z i ∑ j = 1 K e z j f o r i = 1 , 2 , … , K \sigma(z_i) = \frac{e^{z_{i}}}{\sum_{j=1}^K e^{z_{j}}} \ \ \ for\ i=1,2,\dots,K σ(zi)=∑j=1Kezjezi for i=1,2,…,K
先映射到0到正无穷上,再通过除以总和满足和为1(先转正再归一)
Torch.nn.CrossEntropyLoss()
注:单独使用NLLLoss()的时候,需要最后输出层加上激活函数,这也正是likelihood的定义,即输出一个概率;而使用CrossEntropyLoss()的时候,网络的最后输出不要加激活,在CrossEntropyLoss()中会帮我们完成此操作,实际是结合了LogSofmax和NLLLoss两个函数,先利用logsofmax归一化概率,再求交叉熵。
对于图像输入,像素值需要标准化,即映射到0到1上的浮点数
图像张量 W×H×C 在PyTorch中需要转换成C×W×H
transform = transforms.Compose([
transforms.ToTensor(), # 转换成图像张量
transforms.Normalize((均值,), (标准差,)) # 标准化到01分布
])
高维图像首先要转换到一维 x = x.view(-1, C×W×H)
SGD中加入冲量(momentum)的作用:给数据处理一个惯性值,可以从局部极值中出来尽可能找到全局最优解
对于全连接网络,逐层改变尺寸并激活,会损失空间特征,导致准确性不高
ReLU (Rectified Linear Unit,修正线性单元)
卷积和下采样 —— 提取特征,减少数据量
图像局部与卷积核逐个数乘,整个卷积核在图像上遍历一遍
多通道卷积,各个通道分别做卷积(一一对应),再将结果对应相加
卷积核通道数 = 输入通道个数
卷积核个数 = 输出通道个数(每个卷积核输出的结果都是单通道)
对于一个 n × w i d t h i n × h e i g h t i n n×width_{in}×height_{in} n×widthin×heightin的输入,要得到 m × w i d t h o u t × h e i g h t o u t m×width_{out}×height_{out} m×widthout×heightout的输出
需要m个 n × k e r n e l w i d t h × k e r n e l h e i g h t n×kernel_{width}×kernel_{height} n×kernelwidth×kernelheight的卷积核,拼成一个四维张量
tensor.view(B,C,W,H)
padding —— 填充零 stride —— 步长
最大池化 MaxPooling 分组后保留最大值
池化与通道无关,因此通道数量不变,图像大小会变
GoogleNet
Inception
concatenate 拼接
1×1卷积核的作用:直接改变通道数量,减少运算数量(升降维度)
ResNet(Residual 残差)
避免梯度消失 H(x) = F(x) + x 使得求导后梯度不会趋近1
跳连接
Residual Block 要保证输入输出的大小一致 层间处理
增强理论学习
阅读PyTorch文档
复现论文 读论文
扩充视野
专门处理序列模型
之前的卷积 —— 空间数据
序列数据 —— 时间数据 —— 不独立的随机变量
对条件概率 —— 用之前的历史数据预测自身 —— 自回归模型
P ( x t ∣ x 1 , . . . , x t − 1 ) = P ( x t ) ∣ f ( x 1 , . . . , x t − 1 ) ) {P(x_t|x_1,...,x_{t-1})=P(x_t)|f(x1,...,x_{t-1}))} P(xt∣x1,...,xt−1)=P(xt)∣f(x1,...,xt−1))
方案A 马尔科夫假设 —— 假设当前数据只跟过去τ个数据点(定长数据)相关
回归模型 MLP模型
方案B 潜变量模型 —— 建立两个模型 —— 其中一个作为潜变量概括历史信息
Fc Fully Connection 全连接层
X t → h t X_t \rightarrow h_t Xt→ht hidden 隐/潜变量
共享权重
激活函数 tanh x = e x − e − x e x + e − x \tanh{x} = \frac{e^x-e{-x}}{e^x+e{-x}} tanhx=ex+e−xex−e−x双曲正切 取值(-1,1)
h t = tanh ( W h i x t + b i h + W h h h t − 1 + b h h ) h_t = \tanh(W_{hi}x_t+b_{ih}+W_{hh}h_{t-1} + b_{hh}) ht=tanh(Whixt+bih+Whhht−1+bhh)
cell = torch.nn.RNNCell()
hidden = cell(input,hidden)
各种维度长度
batchSize(同时处理多个序列的同一位置) seqLen(序列/句子长度)
inputSize hiddenSize(RNN内部Cell数量)
对于RNNCell:
input.shape = (batchSize, inputSize)
output.shape = (batchSize, hiddenSize)
数据集: dataset.shape = (seqLen,batchSize,inputSize)
对于RNN:
需要指定numLayers(层数)
输入
inputs包含每一层的输入 input.shape = (seqLen,batchSize,inputSize)
隐层输入h_0.shape = (numLayers,batchSize,hiddenSize)
输出
output.shape = (seqLen,batchSize,hiddenSize)
h_n.shape = (numLayers,batchSize,hiddenSize)
应用 seq2seq
首先需要 文本向量化
最简单——独热向量 缺点:高维 sparse(稀疏) 硬编码
Embedding 数据降维 用有限的维度表示更多层
word2vec
LSTM GRU