softmax回归

  • 1. softmax回归
  • 2. softmax操作
  • 3. 最大似然估计
  • 4. 损失函数
  • 5. 梯度
  • 6. 实现
    • 6.1 从零实现softmax回归
    • 6.2 简洁实现
  • 7. 课后习题
  • Reference

1. softmax回归

虽然softmax回归里面带有回归二字,但是其实这是一个分类模型。类似于线性回归,softmax回归是一个单层神经网络,不同的是,线性回归的输出只有一个,而softmax回归的输出有多个。

(图片引用自zh-v2.d2l.ai)
之所以softmax回归用来做分类,是该模型经过softmax操作将输出转换成概率,根据较大的概率选择归为哪一类。以上图为例,可以使用o=Wx+bo=Wx+bo=Wx+b的形式进行表达。其中WWW是3X4的矩阵,每一行就是输入层的每一个参数对某一个分类的权重,xxx就是输入层,是一个4X1的列向量,bbb和xxx一样,也是一个4X1的列向量,最终的ooo是一个3X1的列向量。
在得到向量ooo之后,再经过softmax操作转换成概率形式,例如[0.2,0.3,0.5]T[0.2, 0.3,0.5]^T[0.2,0.3,0.5]T,那么就可以把该样本归为第3类。
softmax的向量表达形式和线性回归非常类似,softmax回归同样也是线性模型,尽管softmax是一个非线性函数(softmax回归的输出由输入特征的仿射变换决定)。在线性模型中常常提到的logistic回归就是softmax回归k分类中k=2的一般形式。

2. softmax操作

首先,对于给定的样本标签xxx经过仿射变换后得到输出ooo,然后再经过softmax函数得到预测标签。可以看成是一个概率分布。
y^=softmax(o)\hat{y}=softmax(o)y^​=softmax(o)
yj^=exp(oj)∑kexp(ok)\hat{y_j}=\frac{exp(o_j)}{\sum_kexp(o_k)}yj​^​=∑k​exp(ok​)exp(oj​)​
即softmax操作就是把列向量的数值加起来,然后让每个元素除以这个总数,这样就能得到总和为1,不小于0的概率形式。
最后,argmaxjyj^=argmaxjoj{argmax}_j\hat{y_j}=argmax_jo_jargmaxj​yj​^​=argmaxj​oj​

3. 最大似然估计

假设噪声服从正态分布。在线性回归中,
y=wTx+b+εy=w^Tx+b+\varepsilony=wTx+b+ε
其中ε∼N(0,δ2)\varepsilon\sim N(0,\delta^2)ε∼N(0,δ2)
给定x到y的似然函数:
P(y∣x)=12πδ2exp(−12δ2(y−wTx−b)2)P(y|x)=\frac{1}{\sqrt{2\pi\delta^2}}exp(-\frac{1}{2\delta^2}(y-w^Tx-b)^2)P(y∣x)=2πδ2​1​exp(−2δ21​(y−wTx−b)2)
P(y∣X)=∏i=1np(y(i)∣x(i))P(y|X)=\prod^n_{i=1}p(y^{(i)}|x^{(i)})P(y∣X)=i=1∏n​p(y(i)∣x(i))
要使上式最大,可以等价位最小化负对数似然,可以得到:
−logP(y∣X)=∑i=1n12log(2πδ2)+12δ2(y(i)−wTx(i)−b)-logP(y|X)=\sum^n_{i=1}\frac{1}{2}log(2\pi\delta^2)+\frac{1}{2\delta^2}(y^{(i)}-w^Tx^{(i)}-b)−logP(y∣X)=i=1∑n​21​log(2πδ2)+2δ21​(y(i)−wTx(i)−b)
第一项是一个常数项,剩余部分就是均方误差。这就是为什么,在高斯噪声的假设下,最小化均方误差等价于对线性模型的极大似然估计。

4. 损失函数

同理,利用极大似然估计:
P(Y∣X)=∏i=1nP(y(i)∣x(i))P(Y|X)=\prod^n_{i=1}P(y^{(i)}|x^{(i)})P(Y∣X)=i=1∏n​P(y(i)∣x(i))
同样,求最小化负对数似然:
−logP(Y∣X)=∑1n−logP(y(i)∣x(i))-logP(Y|X)=\sum^n_1-logP(y^{(i)}|x^{(i)})−logP(Y∣X)=1∑n​−logP(y(i)∣x(i))
这里,softmax函数给出预测向量y^\hat{y}y^​,为每一类的概率。所以可以写成,P(y(i)∣x(i))=y^=∑j−yjlog(yj^)P(y^{(i)}|x^{(i)})=\hat{y}=\sum_j-y_jlog(\hat{y_j})P(y(i)∣x(i))=y^​=∑j​−yj​log(yj​^​)
因为最终标签yyy是one-hot向量,所以乘起来其实最终只会有一项。
故:−logP(Y∣X)=∑i=1n∑j=1q−yijlogyij^-logP(Y|X)=\sum_{i=1}^n\sum_{j=1}^q-y_{ij}log\hat{y_{ij}}−logP(Y∣X)=i=1∑n​j=1∑q​−yij​logyij​^​
softmax回归中的损失函数为交叉熵损失函数。这是由于交叉熵损失
l(y,y^)=−∑j=1qyjlogyj^l(y,\hat{y})=-\sum_{j=1}^qy_jlog\hat{y_j}l(y,y^​)=−j=1∑q​yj​logyj​^​
所以,最终最小化负对数似然可以写成:
−logP(Y∣X)=∑1nll(y(i),y^(i))-logP(Y|X)=\sum^n_1ll(y^{(i)},\hat{y}^{(i)})−logP(Y∣X)=1∑n​ll(y(i),y^​(i))
最小化损失函数即可。

但是在后文的实际编码中,损失函数通常写为:

def cross_entropy(y_hat, y):return - torch.log(y_hat[range(len(y_hat)), y])

这是因为在Fashion_Mnist数据集中,获取的yyy标签并不是one-hot向量,而是一个类别。
举个例子,对于批量大小为n,即n个样例:
O=XW+bO=XW+bO=XW+b
Y^=softmax(O)\hat{Y}=softmax(O)Y^=softmax(O)
其中X为nxd的矩阵,W为dxq的矩阵,b为1xq的矩阵,则O和y^\hat{y}y^​都是nxq的矩阵。而得到的y向量为nx1的向量,表示每一个样例对应的分类,例如[2,3,1,0,5]T[2,3,1,0,5]^T[2,3,1,0,5]T. 在损失函数中,yjy_jyj​非0即1,则只会保留预测概率的log值的对应的实际值为1的那一项。这和损失函数的概念是吻合的。我们需要最小化损失函数,也就是最大化预测概率log和实际值y的乘积。
而以上代码就是直接取实际值为1的那一项的预测概率,再取log,达到的效果是一样的。

5. 梯度

求损失函数的梯度,因为梯度下降更新的是线性网络中的参数,所以是对预测值y^\hat{y}y^​求导,对softmax函数中的ojo_joj​求导:
l(y,y^)=−∑j=1qyjlogexp(oj)∑k=1qexp(ok)l(y,\hat{y})=-\sum^q_{j=1}y_jlog\frac{exp(o_j)}{\sum_{k=1}^qexp(o_k)}l(y,y^​)=−j=1∑q​yj​log∑k=1q​exp(ok​)exp(oj​)​
=∑j=1qyjlog∑k=1qexp(ok)−∑j=1qyjoj=\sum_{j=1}^qy_jlog\sum_{k=1}^qexp(o_k)-\sum_{j=1}^qy_jo_j=j=1∑q​yj​logk=1∑q​exp(ok​)−j=1∑q​yj​oj​
=log∑k=1qexp(ok)−∑j=1qyjoj=log\sum_{k=1}^qexp(o_k)-\sum_{j=1}^qy_jo_j=logk=1∑q​exp(ok​)−j=1∑q​yj​oj​
∂ojl(y,y^)=exp(oj)∑k=1qexp(ok)−yj=softmax(o)j−yj\partial_{o_j}l(y,\hat{y})=\frac{exp(o_j)}{\sum_{k=1}^qexp(o_k)}-y_j=softmax(o)_j-y_j∂oj​​l(y,y^​)=∑k=1q​exp(ok​)exp(oj​)​−yj​=softmax(o)j​−yj​
即梯度是观测值yyy和估计值y^\hat{y}y^​之间的差异。

6. 实现

6.1 从零实现softmax回归

  1. 导入相关库并下载数据集,这里使用的是FashionMnist数据集。
%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l
from IPython import displayd2l.use_svg_display()trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(root="../data",train=True,transform=trans,download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="../data",train=False,transform=trans,download=True)
  1. 导入数据集,分批进行训练
batch_size =256
def get_dataloader_workers():return 0
def load_data_fashion_mnist(batch_size,resize=None):trans = [transforms.ToTensor()]if resize:trans.insert(0,transforms.Resize(resize))trans = transforms.Compose(trans)return (data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers()),data.DataLoader(mnist_test,batch_size,shuffle=False,num_workers=get_dataloader_workers))batch_size= 256
train_iter,test_iter =d2l.load_data_fashion_mnist(batch_size)

这里的get_dataloader_workers是线程的数量,表示可以使用多个线程并行load数据,大于0就会加快加载速度。
3. 定义网络模型以及softmax函数

def softmax(X):X_exp = torch.exp(X)partition = X_exp.sum(1,keepdim=True)return X_exp/partitionnum_inputs = 784
num_outputs = 10W = torch.normal(0,0.01,size=(num_inputs,num_outputs),requires_grad=True)
b = torch.zeros(num_outputs,requires_grad=True)def net(X):return softmax(torch.matmul(X.reshape((-1,W.shape[0])),W)+b)
  1. 定义损失函数
def cross_entropy(y_hat,y):return -torch.log(y_hat[range(len(y_hat)),y])
  1. 定义累加类和动图类
class Accumulator:def __init__(self,n):self.data=[0.0]*ndef add(self,*args):self.data = [a+float(b) for a,b in zip(self.data,args)]def reset(self):self.data = [0.0]*len(self.data)def __getitem__(self,idx):return self.data[idx]class Animator:def __init__(self,xlabel=None,ylabel=None,legend=None,xlim=None,ylim=None,xscale='linear',yscale='linear',fmts=('-','m--','g-','r:'),nrows=1,ncols=1,figsize=(3.5,2.5)):if legend is None:legend=[]d2l.use_svg_display()self.fig,self.axes = d2l.plt.subplots(nrows,ncols,figsize=figsize)if nrows * ncols == 1:self.axes = [self.axes,]self.config_axes = lambda:d2l.set_axes(self.axes[0],xlabel,ylabel,xlim,ylim,xscale,yscale,legend)self.X,self.Y,self.fmts = None,None,fmtsdef add(self,x,y):if not hasattr(y,"__len__"):y=[y]n = len(y)if not hasattr(x,"__len__"):x=[x]*nif not self.X:self.X = [[] for _ in range(n)]if not self.Y:self.Y = [[] for _ in range(n)]for i,(a,b) in enumerate(zip(x,y)):if a is not None and b is not None:self.X[i].append(a)self.Y[i].append(b)self.axes[0].cla()for x,y,fmt in zip(self.X,self.Y,self.fmts):self.axes[0].plot(x,y,fmt)self.config_axes()display.display(self.fig)display.clear_output(wait=True)
  1. 评估精度
def accuracy(y_hat,y):if len(y_hat.shape)>1 and y_hat.shape[1]>1:y_hat = y_hat.argmax(axis=1)cmp = y_hat.type(y.dtype)==yreturn float(cmp.type(y.dtype).sum())
def evaluate_accuracy(net,data_iter):if isinstance(net,torch.nn.Module):net.eval()metric = Accumulator(2)for X,y in data_iter:metric.add(accuracy(net(X),y),y.numel())return metric[0]/metric[1]
  1. 梯度下降进行训练
def train_epoch_ch3(net,train_iter,loss,updater):if isinstance(net,torch.nn.Module):net.train()metric = Accumulator(3)for X,y in train_iter:y_hat = net(X)l = loss(y_hat,y)if isinstance(updater,torch.optim.Optimizer):updater.zero_grad()l.mean().backward()updater.step()else:l.sum().backward()updater(X.shape[0])metric.add(float(l.sum()),accuracy(y_hat,y),y.numel())return metric[0]/metric[2],metric[1]/metric[2]def train_ch3(net,train_iter,test_iter,loss,num_epochs,updater):animator = Animator(xlabel='epoch',xlim=[1,num_epochs],ylim=[0.3,0.9],legend=['train_loss','train acc','test acc'])for epoch in range(num_epochs):train_metrics = train_epoch_ch3(net,train_iter,loss,updater)test_acc = evaluate_accuracy(net,test_iter)animator.add(epoch+1,train_metrics+(test_acc,))train_loss , train_acc = train_metricsassert train_loss <0.5 ,train_lossassert train_acc <=1 and train_acc >0.7 , train_accassert test_acc <=1 and test_acc > 0.7 , test_acc
  1. 最后使用之前用过的随机梯度下降进行参数训练
lr = 0.1
def updater(batch_size):return d2l.sgd([W,b],lr,batch_size)
num_epochs =10
train_ch3(net,train_iter,test_iter,cross_entropy,num_epochs,updater)


9.可以使用训练好的模型进行预测

def predict_ch3(net,test_iterm,n=6):for X,y in test_iter:breaktrues = d2l.get_fashion_mnist_labels(y)preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))titles=[true + '\n' + pred for true,pred in zip(trues,preds)]d2l.show_images(X[0:n].reshape((n,28,28)),1,n,titles=titles[0:n])
predict_ch3(net,test_iter)

6.2 简洁实现

softmax的简洁实现基于已有的框架实现,代码非常简洁

首先读取数据集

import torch
from torch import nn
from d2l import torch as d2lbatch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

然后定义网络。这里需要在前面加入一个nn.Flatten()层,这是展平层,就是把数据集的维度变成1x(-1)维,-1表示自动推导。

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);

然后定义损失函数

loss = nn.CrossEntropyLoss(reduction='none')

然后定义随机梯度下降优化器

trainer = torch.optim.SGD(net.parameters(), lr=0.1)

最后训练就行

num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

需要注意的是: 在代码中我们看不到softmax,这很奇怪。
这是因为这里将softmax操作合并进入了损失函数。
在softmax中有指数操作,如果指数很大,就可能出现溢出。
这里有一个处理方式是:在指数之中提前减去最大值,这样就不会溢出了,这是因为:
yj^=exp(oj−max(ok))(oj−max(ok))∑kexp(ok−max(ok))(oj−max(ok))\hat{y_j}=\frac{exp(o_j-max(o_k))(o_j-max(o_k))}{\sum_kexp(o_k-max(o_k))(o_j-max(o_k))}yj​^​=∑k​exp(ok​−max(ok​))(oj​−max(ok​))exp(oj​−max(ok​))(oj​−max(ok​))​
但是同样带来的问题是:分子可能很小,出现下溢,导致整个概率输出为0。
所以可以把损失函数中的log和softmax中的指数操作结合起来:
log(yj^)=oj−max(ok)−∑kexp(ok−max(ok))log(\hat{y_j})=o_j-max(o_k)-\sum_kexp(o_k-max(o_k))log(yj​^​)=oj​−max(ok​)−k∑​exp(ok​−max(ok​))

7. 课后习题

Reference

https://zh-v2.d2l.ai/

[动手学深度学习]02 softmax回归相关推荐

  1. 【动手学深度学习】Softmax 回归 + 损失函数 + 图片分类数据集

    学习资料: 09 Softmax 回归 + 损失函数 + 图片分类数据集[动手学深度学习v2]_哔哩哔哩_bilibili torchvision.transforms.ToTensor详解 | 使用 ...

  2. 【动手学深度学习】softmax回归

    softmax回归 1.softmax回归基本概念 2.图像分类数据集流程图 3.softmax从零开始实现流程图 4.softmax回归的简洁实现 1.softmax回归基本概念 分类问题 独热编码 ...

  3. 动手学深度学习之softmax实现

    Softmax回归 回归VS分类 回归估计的是一个连续值 分类预测一个离散类别 从回归到多类分类 回归 单连续数值输出 自然区间R\mathbb{R}R 跟真实值区别作为损失 分类 通常为多个输出 输 ...

  4. 【动手学深度学习】02-softmax回归

    1.softmax回归 1.1 从回归到分类 输出i是预测为第i类的置信度.输出的个数等于类别的个数 均方损失 n个类别,假设先用1,0表示是否类别正确.使用均方损失训练,最大值作为预测. 校验比例 ...

  5. 动手学深度学习实现DAY-2

    节选自"ElitesAI·动手学深度学习PyTorch版" Task03:过拟合.欠拟合及其解决方案:梯度消失.梯度爆炸:循环神经网络进阶(1天) Task04:机器翻译及相关技术 ...

  6. 《动手学深度学习》—学习笔记

    文章目录 深度学习简介 起源 特点 小结 预备知识 获取和运行本书的代码 pytorch环境安装 方式一 方式二 数据操作 创建 运算 广播机制 索引 运算的内存开销 NDArray和NumPy相互变 ...

  7. 资源 | 李沐等人开源中文书《动手学深度学习》预览版上线

    来源:机器之心 本文约2000字,建议阅读10分钟. 本文为大家介绍了一本交互式深度学习书籍. 近日,由 Aston Zhang.李沐等人所著图书<动手学深度学习>放出了在线预览版,以供读 ...

  8. 《动手学深度学习》PyTorch版本

    Dive-Into-Deep-Learning-PyTorch-PDF 简介   本项目对中文版<动手学深度学习>中的代码进行整理,并参考一些优秀的GitHub项目给出基于PyTorch的 ...

  9. 【深度学习】李沐《动手学深度学习》的PyTorch实现已完成

    这个项目是中文版<动手学深度学习>中的代码进行整理,用Pytorch实现,是目前全网最全的Pytorch版本. 项目作者:吴振宇博士 简介   Dive-Into-Deep-Learnin ...

最新文章

  1. R语言plotly可视化:plotly可视化在散点图中添加误差条(Scatterplot with Error Bars with plotly in R)
  2. NYOJ 683 Jim的实验
  3. 使用PXE+NFS EFI引导安装RHEL6/7以及Kickstart安装
  4. 【计算机类】大学生计算机专业常用工具汇总
  5. 博士毕业的人也会交“智商税”?现实远比我们想象的残酷……
  6. 开发工具总结(2)之全面总结Android Studio2.X的填坑指南
  7. 第二次想上传demo到github
  8. Perl文件处理示例——批量添加Copyright版权信息
  9. 如何在Mac禁用NTFS for Mac 15
  10. WGS-84与北京-54、西安-80及地方坐标系的转换
  11. vue 下载本地文件
  12. RGB颜色对照表以及十六进制
  13. oracle12c 查看库,12c 检查pdb数据库状态
  14. Android-深色模式篇
  15. warning:discards qualifiers from pointer target type解决办法
  16. iOS实现网速实时监测
  17. GitLab服务器安装完成之后;瓦力系统的安装(之一)
  18. 如何准备互联网大厂算法面试和笔试?
  19. 获取滑动条位置,动态调整对话框显示位置;为动态添加的button添加click事件
  20. 良心推荐:看完这20部BBC神级纪录片,英语水平提升几个Level!

热门文章

  1. 天合光能发布业内首个500W级超高功率光伏电站解决方案
  2. 让“书香”溢满美丽乡村
  3. 如何安装python3.8.2_Python-3.8.2安装
  4. 2022年注册会计师考试财务成本管理模拟题及答案
  5. 实力爆表,日日新成为AI领航者
  6. ng-cli新建项目
  7. 清微智能1周年庆:我们距离百年老店还有99年
  8. 轻松使用git命令查看版本记录
  9. 【BFS】献给阿尔吉侬的花束、噩梦
  10. LONG类型的最大值限制