两类预测问题:
仿射变换(affine transformation)
解析解:线性回归存在解析解,但并不是所有问题都存在解析解
批量大小(batch size)∣ B ∣ |\mathcal{B}| ∣ B ∣ :每个小批量中的样本数
学习率(learning rate)∣ η ∣ |\eta| ∣ η ∣
预测(prediction):给定特征估计目标
推断(inference):基于数据集估计参数
假设观测中包含噪声,噪声服从正态分布。
y = w ⊤ x + b + ϵ ϵ ∼ N ( 0 , σ 2 ) y=\mathbf{w}^\top\mathbf{x}+b+\epsilon\\
\epsilon\sim\mathcal{N}(0,\sigma^2) y = w ⊤ x + b + ϵ ϵ ∼ N ( 0 , σ 2 )
P ( y ∣ x ) = 1 2 π σ 2 exp ( − 1 2 σ 2 ( y − w ⊤ x − b ) 2 ) P ( y ∣ X ) = ∏ i = 1 n p ( y ( i ) ∣ x ( i ) ) − log P ( y ∣ X ) = ∑ i = 1 n 1 2 log ( 2 π σ 2 ) + 1 2 σ 2 ( y ( i ) − w ⊤ x ( i ) − b ) 2 P
(y \mid \mathbf{x})=\frac{1}{\sqrt{2 \pi \sigma^{2}}} \exp \left(-\frac{1}{2 \sigma^{2}}\left(y-\mathbf{w}^{\top} \mathbf{x}-b\right)^{2}\right)
\\
P(\mathbf{y} \mid \mathbf{X})=\prod_{i=1}^{n} p\left(y^{(i)} \mid \mathbf{x}^{(i)}\right)
\\
-\log P(\mathbf{y} \mid \mathbf{X})=\sum_{i=1}^{n} \frac{1}{2} \log \left(2 \pi \sigma^{2}\right)+\frac{1}{2 \sigma^{2}}\left(y^{(i)}-\mathbf{w}^{\top} \mathbf{x}^{(i)}-b\right)^{2} P ( y ∣ x ) = 2 π σ 2 1 exp ( − 2 σ 2 1 ( y − w ⊤ x − b ) 2 ) P ( y ∣ X ) = i = 1 ∏ n p ( y ( i ) ∣ x ( i ) ) − log P ( y ∣ X ) = i = 1 ∑ n 2 1 log ( 2 π σ 2 ) + 2 σ 2 1 ( y ( i ) − w ⊤ x ( i ) − b ) 2
在高斯噪声的假设下,最小化均方误差等价于对线性模型的极大似然估计。
下面是具体实现:
假设ϵ \epsilon ϵ 服从正态分布
定义一个data_iter函数,接受批量大小、特征矩阵、标签向量作为输入,生成大小为batch_size的小批量。
样本随机读取,用到的函数有:
indices = list ( range ( num_examples) )
random. shuffle( indices)
为什么要分批?利用GPU并行运算的优势,处理合理大小的“小批量”。每个样本都可以并行地进行模型计算。
自定义迭代器要求将所有数据加载到内存中,并执行大量随机内存访问;内置迭代器效率要高很多。
若使用框架中现有API来读取数据,则可以通过data.DataLoader
来实现。
from torch. utils import data
dataset = data. TensorDataset( X, y)
data_iter = data. DataLoader( dataset= dataset, batch_size= batch_size, shuffle= True )
线性回归模型
from torch import nn
net = nn. Sequential( nn. Linear( 2 , 1 ) ) ;
如果使用自定义的模型,需要requires_grad=True
如果使用预定义的框架,可以直接访问参数以设定初始值。
通过net[0]
选择网络中的第一个图层
使用weight.data
和bias.data
方法访问参数
使用替换方法normal_
和fill_
来重写参数值
net[ 0 ] . weight. data. normal_( 0 , 0.01 )
net[ 0 ] . bias. data. fill_( 0 )
平方损失,MSELoss类(Mean Square Error),squared L 2 L_2 L 2 norm
loss = nn. MSELoss( )
小批量随机梯度下降
一些技巧:
with torch. no_grad( ) :
Disabling gradient calculation is useful for inference, when you are sure that you will not call :meth:Tensor.backward()
. It will reduce memory consumption for computations that would otherwise have requires_grad=True
.
若使用框架预定义的SGD(Stochastic gradient descent)算法:
trainer = torch. optim. SGD( net. parameters( ) , lr= 0.03 )
在每次迭代中,读取一小批量训练样本,并通过模型来获得一组预测。计算完损失后,开始反向传播,存储每个参数的梯度。最后,调用优化算法sgd来更新模型参数。
迭代周期个数num_epochs和学习率learning_rate都是超参数。
for epoch in range ( num_epochs) :
for X, y in data_iter:
l = loss( net( X) , y)
trainer. zero_grad( )
l. backward( )
trainer. step( )
l = loss( net( features) , labels)
print ( f'epoch { epoch + 1 } , loss { l: f } ' )
即使我们只关心硬类别,我们仍然使用软类别的模型——用回归解决分类问题。
表示分类数据的方法:独热编码(向量表示)
输出规范化:
非负
总和为1
校准(calibration):在分类器输出0.5的所有样本中,有一半实际属于预测的类。
y ^ = softmax ( o ) 其中 y ^ j = exp ( o j ) ∑ k exp ( o k ) \hat{\mathbf{y}}=\operatorname{softmax}(\mathbf{o}) \quad \text { 其中 } \quad \hat{y}_{j}=\frac{\exp \left(o_{j}\right)}{\sum_{k} \exp \left(o_{k}\right)} y ^ = softmax ( o ) 其中 y ^ j = ∑ k exp ( o k ) exp ( o j )
最可能的类别为
argmax j y ^ j = argmax j o j \underset{j}{\operatorname{argmax}} \hat{y}_{j}=\underset{j}{\operatorname{argmax}} o_{j} j argmax y ^ j = j argmax o j
设特征维度(输入数量)为d d d ,批量大小为n n n ,输出类别数为q q q ,则根据最小化负对数似然,有:
− log P ( Y ∣ X ) = ∑ i = 1 n − log P ( y ( i ) ∣ x ( i ) ) = ∑ i = 1 n l ( y ( i ) , y ^ ( i ) ) , -\log P(\mathbf{Y} \mid \mathbf{X})=\sum_{i=1}^{n}-\log P\left(\mathbf{y}^{(i)} \mid \mathbf{x}^{(i)}\right)=\sum_{i=1}^{n} l\left(\mathbf{y}^{(i)}, \hat{\mathbf{y}}^{(i)}\right), − log P ( Y ∣ X ) = i = 1 ∑ n − log P ( y ( i ) ∣ x ( i ) ) = i = 1 ∑ n l ( y ( i ) , y ^ ( i ) ) ,
对于特定的标签y \mathbf{y} y 和模型预测y ^ \hat{\mathbf{y}} y ^ ,损失函数为
l ( y , y ^ ) = − ∑ j = 1 q y j log y ^ j l(\mathbf{y}, \hat{\mathbf{y}})=-\sum_{j=1}^{q} y_{j} \log \hat{y}_{j} l ( y , y ^ ) = − j = 1 ∑ q y j log y ^ j
由于y \mathbf{y} y 独热编码,上式右边只有一项非零,此项即为预测概率的负对数。
l ( y , y ^ ) = − ∑ j = 1 q y j log exp ( o j ) ∑ k = 1 q exp ( o k ) = ∑ j = 1 q y j log ∑ k = 1 q exp ( o k ) − ∑ j = 1 q y j o j = log ∑ k = 1 q exp ( o k ) − ∑ j = 1 q y j o j . ∂ o j l ( y , y ^ ) = exp ( o j ) ∑ k = 1 q exp ( o k ) − y j = softmax ( o ) j − y j \begin{aligned}
l(\mathbf{y}, \hat{\mathbf{y}}) &=-\sum_{j=1}^{q} y_{j} \log \frac{\exp \left(o_{j}\right)}{\sum_{k=1}^{q} \exp \left(o_{k}\right)} \\
&=\sum_{j=1}^{q} y_{j} \log \sum_{k=1}^{q} \exp \left(o_{k}\right)-\sum_{j=1}^{q} y_{j} o_{j} \\
&=\log \sum_{k=1}^{q} \exp \left(o_{k}\right)-\sum_{j=1}^{q} y_{j} o_{j} .
\end{aligned}
\\
\partial_{o_{j}} l(\mathbf{y}, \hat{\mathbf{y}})=\frac{\exp \left(o_{j}\right)}{\sum_{k=1}^{q} \exp \left(o_{k}\right)}-y_{j}=\operatorname{softmax}(\mathbf{o})_{j}-y_{j} l ( y , y ^ ) = − j = 1 ∑ q y j log ∑ k = 1 q exp ( o k ) exp ( o j ) = j = 1 ∑ q y j log k = 1 ∑ q exp ( o k ) − j = 1 ∑ q y j o j = log k = 1 ∑ q exp ( o k ) − j = 1 ∑ q y j o j . ∂ o j l ( y , y ^ ) = ∑ k = 1 q exp ( o k ) exp ( o j ) − y j = softmax ( o ) j − y j
一行代码实现交叉熵函数:
def cross_entropy ( y_hat, y) :
return - torch. log( y_hat[ range ( len ( y_hat) ) , y] )
实际应用中,在对y_hat的计算时,若一些o j o_j o j 非常大,会导致上溢为inf,解决方案是在计算softmax前做一步减法:
y ^ j = exp ( o j − max ( o k ) ) exp ( max ( o k ) ) ∑ k exp ( o k − max ( o k ) ) exp ( max ( o k ) ) = exp ( o j − max ( o k ) ) ∑ k exp ( o k − max ( o k ) ) \begin{aligned}
\hat{y}_{j} &=\frac{\exp \left(o_{j}-\max \left(o_{k}\right)\right) \exp \left(\max \left(o_{k}\right)\right)}{\sum_{k} \exp \left(o_{k}-\max \left(o_{k}\right)\right) \exp \left(\max \left(o_{k}\right)\right)} \\
&=\frac{\exp \left(o_{j}-\max \left(o_{k}\right)\right)}{\sum_{k} \exp \left(o_{k}-\max \left(o_{k}\right)\right)}
\end{aligned} y ^ j = ∑ k exp ( o k − max ( o k ) ) exp ( max ( o k ) ) exp ( o j − max ( o k ) ) exp ( max ( o k ) ) = ∑ k exp ( o k − max ( o k ) ) exp ( o j − max ( o k ) )
若一些o j o_j o j 非常小,会导致y ^ j \hat{y}_j y ^ j 为为零,log y ^ j \log{\hat{y}_j} log y ^ j 值为-inf,反向传播后会出现nan,解决方案是按照下述化简式计算交叉熵损失函数:
log ( y ^ j ) = log ( exp ( o j − max ( o k ) ) ∑ k exp ( o k − max ( o k ) ) ) = o j − max ( o k ) − log ( ∑ k exp ( o k − max ( o k ) ) ) \begin{aligned}
\log \left(\hat{y}_{j}\right) &=\log \left(\frac{\exp \left(o_{j}-\max \left(o_{k}\right)\right)}{\sum_{k} \exp \left(o_{k}-\max \left(o_{k}\right)\right)}\right) \\
&=o_{j}-\max \left(o_{k}\right)-\log \left(\sum_{k} \exp \left(o_{k}-\max \left(o_{k}\right)\right)\right)
\end{aligned} log ( y ^ j ) = log ( ∑ k exp ( o k − max ( o k ) ) exp ( o j − max ( o k ) ) ) = o j − max ( o k ) − log ( k ∑ exp ( o k − max ( o k ) ) )
H [ P ] = ∑ j − P ( j ) log P ( j ) H[P]=\sum_{j}-P(j) \log P(j) H [ P ] = j ∑ − P ( j ) log P ( j )
可以把信息熵H ( P ) H(P) H ( P ) 理解为“知道真实概率的人所经历的惊异程度 ”
可以把交叉熵H ( P , Q ) H(P,Q) H ( P , Q ) 理解为“主观概率为Q的观察者在看到根据概率P生成的数据时的预期惊异 ”
最大化观测数据的似然,亦即,最小化传达标签所需的惊异
如无必要,勿增实体。
知之为知之,不知为不知,是知也。
使用精度(accuracy)来评估模型性能。
精度等于正确预测数占预测总数的比率。
net = nn. Sequential( nn. Flatten( ) , nn. Linear( 784 , 10 ) ) ;
def init_weights ( m) :
if isinstance ( m, nn. Linear) :
torch. nn. init. normal_( m. weight, mean= 0 , std= 1 )
torch. nn. init. constant_( m. bias, 0 )
net. apply ( init_weights)
loss = nn. CrossEntropyLoss( )
trainer = torch. optim. SGD( net. parameters( ) , lr= 0.1 )