- softmax回归的简洁实现
- 1. 数据集
- 2. 初始化模型参数
- 3. 重新审视softmax实现
- 4. 定义优化算法
- 5. 训练
我们仍旧使用Fashion-MNIST数据集,并且使用上一节中定义好的函数进行数据集的加载。
batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)2. 初始化模型参数
由于softmax回归的输出层是一个全连接层,因此,我们只需要在sequential中添加一个带有10个输出的全连接层。
def init_weight(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight,std=0.01)
net = nn.Sequential(nn.Flatten(),nn.Linear(784,10))
net.apply(init_weight)
注:1)由于pytorch不可以隐式的调整输入的形状,所以在线性层前定义**展平层(flatten())**调整网络输入形状。
2)nn.init.修改参数,net.apply对网络层进行自定义修改,这一部分在后面章节会着重讲解。
3. 重新审视softmax实现上一节说到softmax函数为 y ^ j = e x p ( o j ) ∑ k e x p ( o k ) hat{y}_j = frac{exp(o_j)}{sum_kexp(o_k)} y^j=∑kexp(ok)exp(oj),由于指数函数本身特点,如果 o k o_k ok中有一些数值非常大,那么 e x p ( o k ) exp(o_k) exp(ok)可能会大于数据类型容许的最大数字,即上溢(overflow)。这将使分母或分子变为inf(无穷大),使得我们最后遇到的是0,inf 或 nan(不是数字)的 y ^ j hat{y}_j y^j.这种情况下,我们将不能得到一个明确定义的交叉熵函数。
softmax处理技巧之一,以下引用原文内容:
解决这个问题的一个技巧是,在继续softmax计算之前,先从所有 o k o_k ok中减去 m a x ( 표 푘 ) max(표_푘) max(ok)。你可以证明每个 o k o_k ok 按常数进行的移动不会改变softmax的返回值。在减法和归一化步骤之后,可能有些 o j o_j oj 具有较大的负值。由于精度受限, e x p ( 표 푗 ) exp(표_푗) exp(oj) 将有接近零的值,即 下溢(underflow)。这些值可能会四舍五入为零,使 y ^ j hat{y}_j y^j 为零,并且使得 $log(푦̂ _푗) $的值为 -inf。反向传播几步后,我们可能会发现自己面对一屏幕可怕的nan结果。
为了使得交叉熵可以正常计算,我们可以在计算交叉熵时直接传入原始
o
j
o_j
oj(未经过softmax处理过的输出),因为在计算交叉熵时取的是log值,会将exp抵消掉。具体过程将如下公式:
l
o
g
(
y
^
j
)
=
l
o
g
(
e
x
p
(
o
j
)
∑
k
e
x
p
(
o
k
)
)
=
l
o
g
(
e
x
p
(
o
j
)
)
−
l
o
g
(
∑
k
e
x
p
(
o
k
)
)
=
o
j
−
l
o
g
(
∑
k
e
x
p
(
o
k
)
)
log(hat{y}_j) = log(frac{exp(o_j)}{sum_kexp(o_k)})\ quad quad qquad qquad qquad =log(exp(o_j)) - log(sum_kexp(o_k))\ quad qquad qquad=o_j - log(sum_kexp(o_k))
log(y^j)=log(∑kexp(ok)exp(oj)) =log(exp(oj))−log(k∑exp(ok))=oj−log(k∑exp(ok))
所以我们保留传统的softmax函数,用来评估通过模型输出的概率,但我们并没有将softmax函数传递到损失函数中,而是在交叉熵函数中传递未归一化的预测,并同时计算softmax及其对数。
loss = nn.CrossEntropyLoss()4. 定义优化算法
我们仍然使用小批量随机梯度下降作为优化算法。
trainer = torch.optim.SGD(net.parameters(),lr=0.1)5. 训练
我们调用之前定义好的函数进行训练
num_epochs = 10 d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)



