机器学习&&深度学习——softmax回归的简洁实现

👨‍🎓作者简介:一位即将上大四,正专攻机器学习的保研er

🌌上期文章:机器学习&&深度学习------softmax回归从零开始实现

📚订阅专栏:机器学习&&深度学习

希望文章对你们有所帮助

继续使用Fashion-MNIST数据集,并保持批量大小为256:

python 复制代码
import torch
from torch import nn
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

softmax回归的简洁实现

初始化模型参数

softmax的输出层是一个全连接层,因此,为了实现模型,我们只需要在Sequential中添加一个带有10个输出的全连接层。当然这里的Sequential并不是必要的,但是他是深度模型的基础。我们仍旧以均值为0,标准差为0.01来随机初始化权重。

python 复制代码
# pytorch不会隐式地调整输入的形状
# 因此在线性层前就定义了展平层flatten,来调整网络输入的形状
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights)  # 给net每一层跑一次init_weights函数

重新审视softmax的实现

数学推导

在之前的例子里,我们计算了模型的输出,然后将此输出送入交叉熵损失。看似合理,但是指数级计算可能会造成数值的稳定性问题。

回想一下之前的softmax函数:
y ^ j = e x p ( o j ) ∑ k e x p ( o k ) 其中 y ^ j 是预测的概率分布, o j 是未规范化的第 j 个元素 \hat{y}_j=\frac{exp(o_j)}{\sum_kexp(o_k)}\\ 其中\hat{y}_j是预测的概率分布,o_j是未规范化的第j个元素 y^j=∑kexp(ok)exp(oj)其中y^j是预测的概率分布,oj是未规范化的第j个元素

由于o中的一些数值会非常大,所以可能会让其指数值上溢,使得分子或分母变成inf,最后得到的预测值可能变成的0、inf或者nan。此时我们无法得到一个明确的交叉熵值。

提出解决这个问题的一个技巧:在继续softmax计算之前,先从所有的o中减去max(o),修改softmax函数的构造且不改变其返回值:
y ^ j = e x p ( o j − m a x ( o k ) ) e x p ( m a x ( o k ) ) ∑ k e x p ( o j − m a x ( o k ) ) e x p ( m a x ( o k ) ) \hat{y}_j=\frac{exp(o_j-max(o_k))exp(max(o_k))}{\sum_kexp(o_j-max(o_k))exp(max(o_k))} y^j=∑kexp(oj−max(ok))exp(max(ok))exp(oj−max(ok))exp(max(ok))

这样操作以后,可能会使得一些分子的exp(o-max(o))有接近0的值,即为下溢。这些值可能会四舍五入为0,这样就会使得预测值为0,那么此时要是取对数以后就会变为-inf。要是这样反向传播几步,我们可能会发现自己屏幕有一堆的nan。

尽管我们需要计算指数函数,但是我们最终会在计算交叉熵损失的时候会取他们的对数。尽管通过将softmax和交叉熵结合在一起,可以避免反向传播过程中可能会困扰我们的数值稳定性问题。如下面的式子:
l o g ( y ^ j ) = l o g ( e x p ( o j − m a x ( o k ) ) ∑ k e x p ( o k − m a x ( o k ) ) ) = l o g ( e x p ( o j − m a x ( o k ) ) ) − l o g ( ∑ k e x p ( o k − m a x ( o k ) ) ) = o j − m a x ( o k ) − l o g ( ∑ k e x p ( o k − m a x ( o k ) ) ) log(\hat{y}_j)=log(\frac{exp(o_j-max(o_k))}{\sum_kexp(o_k-max(o_k))})\\ =log(exp(o_j-max(o_k)))-log(\sum_kexp(o_k-max(o_k)))\\ =o_j-max(o_k)-log(\sum_kexp(o_k-max(o_k))) log(y^j)=log(∑kexp(ok−max(ok))exp(oj−max(ok)))=log(exp(oj−max(ok)))−log(k∑exp(ok−max(ok)))=oj−max(ok)−log(k∑exp(ok−max(ok)))

通过上式,我们避免了计算单独的exp(o-max(o)),而是直接使用o-max(o)。

因此,我们计算交叉熵函数的时候,传递的不是未规范化的预测o,而不是softmax。

但是我们也希望保留传统的softmax函数,以备我们要评估通过模型输出的概率。

交叉熵函数

在这里介绍一下交叉熵函数,以用于上面推导所需的需求:

python 复制代码
torch.nn.CrossEntropyLoss(weight=None,
						ignore_index=-100,
						reduction='mean')

交叉熵函数是将LogSoftMax和NLLLoss集成到一个类中,通常用于多分类问题。其参数使用情况:

ignore_index:指定被忽略且对输入梯度没有贡献的目标值。

reduction:string类型的可选项,可在none,mean,sum中选。none表示不降维,返回和target一样的形状;mean表示对一个batch的损失求均值;sum表示对一个batch的损失求和。

weight:是一个一维的张量,包含n个元素,分别代表n类的权重,在训练样本不均衡时很有用,默认为None:

(1)当weight=None时,损失函数计算方式为

loss(x,class)=-log(exp(xclass)/Σexp(xj))=-xclass+log(Σexp(xj)

(2)当weight被指定时,损失函数计算方式为:

loss(x,class)=weightclass×(-xclass+log(Σexp(xj))

python 复制代码
# 在交叉熵损失函数中传递未归一化的预测,并同时计算softmax及其导数
loss = nn.CrossEntropyLoss(reduction='none')

优化算法

python 复制代码
# 优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

训练

调用之前定义的训练函数来训练模型:

python 复制代码
# 调用之前的训练函数来训练模型
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
d2l.plt.show()
相关推荐
码农小白AI4 分钟前
标准错配、限值误判难被普通工具识别,IACheck AI 报告文档审核精准对标国标
人工智能
hangyuekejiGEO7 分钟前
临沂GEO服务企业技术选型分析
人工智能·python
极客侃科技15 分钟前
2026知识付费拼团系统哪个好用?裂变营销工具实测
人工智能
sunywz30 分钟前
【AI RAG知识库】05.【导入】【节点7】node_import_milvus.md
人工智能·milvus
KaMeidebaby36 分钟前
卡梅德生物技术快报|蛋白的叠氮基修饰:实操解析:核酸模板耦合蛋白的叠氮基修饰实现靶蛋白定点共价标记
前端·人工智能·物联网·算法·百度
让学习成为一种生活方式43 分钟前
望春玉兰基因组--文献精读258
人工智能
Token炼金师1 小时前
推理部署层:吞吐上不去的显存账本与调度博弈
人工智能·深度学习·llm
OpenCloudOS1 小时前
OpenCloudOS 理事会更新 | 腾讯云、海光助力建设 AI Infra 开源开放底座
人工智能·开源·腾讯云
私人珍藏库1 小时前
[Android] 一木百宝箱-万能百宝箱+抖音去水印等几百种功能
android·人工智能·app·软件·多功能
没落英雄1 小时前
3. DeepAgents 实战 - Memory Skills 与上下文工程
前端·人工智能·架构