计算机系统应用教程网站

网站首页 > 技术文章 正文

Python pytorch 深度学习神经网络 softmax线性回归分类学习笔记

btikc 2024-10-12 11:47:25 技术文章 9 ℃ 0 评论

#暑期创作大赛#

这里是我理解完交叉熵损失函数,优化算法后,在实现代码过程中出现各种奇怪的错误,主要就维度是不对,要么就是数据类型不对,最后找到了原因后,回来写的。有种很郁闷的心情,原因竟然是大多数教程所给的交叉熵函数算法公式和pytorch nn.CrossEntropyLoss交叉熵函数算法公式竟然不一样,两种完全是两种意义上的东西,但是基础原理都是交叉熵。所以从文章正式开始,到交叉熵公式的位置之间的内容,感兴趣的可以看一下。不感兴趣的可以越过,毕竟这些公式网上一大把。哪为什么不都删了,还不是因为这些是自己辛辛苦苦看教程,好不容易自己理解后的笔记。


关于文中线性回归的部分,这篇文章都有,重复部分就不在这里重复讲述了

python pytorch 深度学习神经网络 线性回归学习笔记

正文开始

softmax是线性回归的一种,主要解决分类问题,但是softmax并不是解决某一个事物是某一类,而是某一事物是某一类的概率是多大,所以说他是解决概率的问题。

由之前学的线性回归构架,知道其构架内包含线性回归模型,损失函数,优化方法。softmax线性回归算法也包含了这些构件。

一、softmax线性回归模型

softmax虽说不具有线性,但是他如果作为分类回归的一部分的话,他就变成了一个具有线性的线性回归分类模型,模型公式:

softmax的主要作用是求出保证y中每个值不为负数,对i位置进行求幂,保证不为负数,而且y中数据的和为1,用对i位置元素求幂后的结果除以y中每个元素求幂的和,并且保证数据的可导性。也就是计算出y中元素在y中的概率分布。

1、softmax 代码示例

softmax=torch.nn.Softmax(dim=0)#dim要softmax的轴
x=torch.tensor([[1.2,1.2,0.3,0.4,0.5],
              [2.3,2.3,5.4,1.2,1.2]])
y=softmax(x)
print('x的值:\n',x)
print('softmax(x)输出结果:\n',y)
print(f'取所有行,第1列数据,并求和:\n值: {y[:,0]},和: {sum(y[:,0])}')
print(f'取第1行,所有数据,并求和:\n值: {y[0,:]},和: {sum(y[0,:])}')

2、softmax dim参数等于0时输出结果 把每一列的所有行数据作为一个输入计算softmax

#dim=0的输出结果

'''x的值:
 tensor([[1.2000, 1.2000, 0.3000, 0.4000, 0.5000],
        [2.3000, 2.3000, 5.4000, 1.2000, 1.2000]])
softmax(x)输出结果:
 tensor([[0.2497, 0.2497, 0.0061, 0.3100, 0.3318],
        [0.7503, 0.7503, 0.9939, 0.6900, 0.6682]])
取所有行,第1列数据,并求和:
值: tensor([0.2497, 0.7503]),和: 1.0
取第1行,所有数据,并求和:
值: tensor([0.2497, 0.2497, 0.0061, 0.3100, 0.3318]),和: 1.147377371788025
'''

3、softmax dim参数等于1时输出结果 把每一行的所有列数据作为一个输入计算softmax

#dim=1的输出结果
'''
x的值:
 tensor([[1.2000, 1.2000, 0.3000, 0.4000, 0.5000],
        [2.3000, 2.3000, 5.4000, 1.2000, 1.2000]])
softmax(x)输出结果:
 tensor([[0.2983, 0.2983, 0.1213, 0.1340, 0.1481],
        [0.0402, 0.0402, 0.8928, 0.0134, 0.0134]])
取所有行,第1列数据,并求和:
值: tensor([0.2983, 0.0402]),和: 0.3385055661201477
取第1行,所有数据,并求和:
值: tensor([0.2983, 0.2983, 0.1213, 0.1340, 0.1481]),和: 1.0
'''

二、损失函数

在之前学过的线性回归模型中,其损失函数使用的是梯度下降损失函数。但是分类线性回归是概率问题,所以使用交叉熵损失函数。

交叉熵是信息论中的概念,其主要作用是测预q的概率分布和正确值p的概率分布之间的距离,距离越大值越大,差异就越大,距离越近值月小,差异就越小。

交叉熵公式:

三、为什么softmax线性回归没有使用上面的算法。

是因为这里softmax遇到一个问题,就是当预测的值有极大值的时候,会导致softmax的结果上溢,极小值会导致softmax结果下溢。pytorch为我们提供的nn.CrossEntropyLoss损失函数解决了这个问题,他接受全连接层的直接输出,并在内部做logsoftmax也就是log(softmax(y))再加上NLLLoss进行交叉熵计算的,这就是为什么没有使用上面算法的原因。由于NLLLoss计算的参与,导致了标签集只能是整数。

以下是自己对两种分类过程的理解:

1、原交叉熵公式,其过程是对数据集和标签集同时做softmax,算出两个数据的数据分布,在用交叉熵算法对两个分布计算,对比出两个数据的差异值。差异越大其值越大,差异越小其值越小。

2、oftmax线性回归nn.CrossEntropyLoss损失熵算法,是标签归类算法。训练集内保存着正确的训练数据和正确的标签数据,其算法通过线性回归层,计算出训练集中每个标签类别,最适合这个标签的w权重和b偏移量。对训练集训练后,就可以输入一个预测数据,并根据计算好的w和b来预测这个数据属于哪个标签的最大概率。

由于已经在nn.CrossEntropyLoss内部进行了softmax,所以在进行构建深度学习框架的时候,你会发现框里只有个线性回归层,而没有softmax层。

四、优化算法

优化算法还是使用线性回归的优化算法,SGD算法。线性回归里面已经学过了。

框架图

五、进行代码构建

1、准备数据集

现在网上基本都是用这个对图片数据集进行标签分类,为了更好的能理解softmax线性回顾分类算法,我在这里用随机正态分布来制作训练集。

1、导入库

import torch
from torch import nn
from torch.utils import data
import numpy as np 
#由于随机正态分布是随机的所以要保存生成的数据到文件以固定数据。
fbq='./bq.npy'#保存标签集到numpy文件路径
fcs='./cs.npy'#保存训练集到numpy文件路径

2、生成训练数据

这里为了模仿图片,生成每个数据是由20x20个数据点构成的矩阵。

dc={0:-5,1:0,2:5}#字典key是训练用的分类标签,值是随机正态分布的平均值。
list=[0,1,2]#标签列表
bq=np.random.choice(list,1000)#用随机抽取标签列表,生成一个由1000个随机标记组成的标签集
np.save(fbq,bq)#保存标签集
cs=[np.random.normal(dc[i],1,(1,20,20)) for i in bq]#for 迭代标签集,标签集是字典的key,读取对应的随机正态分布的平均值。
#生成1000个与标签集对应的训练数据。
np.save(fcs,cs)#保存训练数据

3、转换成tensor格式

bq=np.load(fbq)#读取标签集文件
cs=np.load(fcs)#读取训练数据文件
label=torch.Tensor(bq)#转换成tensor格式
trainset=torch.Tensor(cs)#转换成tensor格式
label=label.long()#由于损失函数的标签集要求为长整型格式,转换下格式
print(trainset.shape,label.shape)#输出训练数据和标签集的形状
#输出结果
#torch.Size([1000, 1, 20, 20]) torch.Size([1000])

4、打包成数据集

dataset=data.TensorDataset(*(trainset,label))#打包成数据集
iterdata=data.DataLoader(dataset,batch_size=100,shuffle=True,num_workers=4)
#把数据集分割成每个块100行数据,shuffle=True并且随机打乱数据,num_workers=4开4个工作线程。

2、架构深度神经网络

相比上次的线性回归,这里构建的的深度学习网络框架内多了一个展平层,其主要作用是把20x20的二维数据展平成一个一维tensor向量,也就是一个数据里20x20有400个数据点。

nn.Linear线性回归层第一个参数400正好是一个数据20x20=400个数据点的数量,第二个参数是总共分几个类别。

net = nn.Sequential(nn.Flatten(),nn.Linear(400,3))
nn.init.constant_(net[1].weight, 1)#初始化weght参数

如果你不想用这个层的话,在构建数据的时候这样构建就可以了.

cs=[np.random.normal(dc[i],1,(1,400)) for i in bq]#直接设置成400个数据点
net = nn.Sequential(nn.Linear(400,3))

3、损失函数和优化算法

这里没设么可讲的只不过是定义两个对象,损失函数在训练的时候讲,优化算法还是linear的梯度下降算法。主要是根据损失函数的值调整weight和bias的参数值。

loss = nn.CrossEntropyLoss(reduction='none')#定义损失函数
trainer = torch.optim.SGD(net.parameters(), lr=0.3)#定义优化算法

4、定义训练代码

trains=10#训练次数
for epoch in range(trains): 
    net.train()#调整到训练模式
    for X, y in iterdata:#迭代数据集
        ly = net(X)#把每次迭代的数据通过线性层计算并输出,总共三个标签,每一个数据输出,是由三个线性层输出的结果组成的一个向量[ly1,ly2,ly3]
        l = loss(ly, y)#损失函数,把线性输出的三个结果组成一个概率分布,对应的标签做交叉熵
        trainer.zero_grad()#清除上次梯度
        l.mean().backward()#使用损失函数mean方法计算梯度
        #torch.nn.utils.clip_grad_norm_(net[1].parameters(), max_norm=0.5)如果损失值过大容易梯度爆炸这个时候可以使用梯度裁剪
        trainer.step()#根据梯度优化线性层的weight和bias参数
        #print('mean')
    l = loss(ly, y)#计算单次计算后的损失值
    print(l.mean()#输出损失值

损失值越小越好,

如果过大可能有以下解决办法

1、优化算法 lr过小,这样可以增加训练次数

2、lr过小可以增加lr值,增加训练步长

3、准备的数据集不合适。或者训练集数量太少

5、可以使用torch.nn.utils.clipgradnorm_梯度裁剪

5、开始训练

10次训练后的输出,可能特征比较明显,损失值很理想

tensor(1.9489, grad_fn=<MeanBackward0>)
tensor(1.0209, grad_fn=<MeanBackward0>)
tensor(1.1892, grad_fn=<MeanBackward0>)
tensor(0.2931, grad_fn=<MeanBackward0>)
tensor(0.0603, grad_fn=<MeanBackward0>)
tensor(0.1497, grad_fn=<MeanBackward0>)
tensor(0.0819, grad_fn=<MeanBackward0>)
tensor(0.0508, grad_fn=<MeanBackward0>)
tensor(0.0381, grad_fn=<MeanBackward0>)
tensor(0.0393, grad_fn=<MeanBackward0>)
dc={0:1,1:2,2:3}#如果把三个随机正态分布的平局值改成1,2,3
l = loss(ly, y)#计算单次计算后的损失值
print(l.mean()#输出损失值
#你会发现损失值特别的大。
'''
tensor(119.3791, grad_fn=<MeanBackward0>)
tensor(110.2976, grad_fn=<MeanBackward0>)
tensor(182.5345, grad_fn=<MeanBackward0>)
tensor(118.5711, grad_fn=<MeanBackward0>)
tensor(223.0627, grad_fn=<MeanBackward0>)
tensor(82.9666, grad_fn=<MeanBackward0>)
tensor(181.2609, grad_fn=<MeanBackward0>)
tensor(30.2465, grad_fn=<MeanBackward0>)
tensor(120.2238, grad_fn=<MeanBackward0>)
tensor(81.6077, grad_fn=<MeanBackward0>)'''

6、测试训练结果

ran=torch.randint(0,high=500,size=(1,100))#生成100个随机整数用于训练集的索引
testset=data.TensorDataset(*(trainset[ran],label[[ran]]))#利用随机抽取的索引,抽取训练数据和标签集的数据生成一个测试集。
for xa,y in testset:#for迭代测试集拆分成测试数据和测试标签
    break
for x,y in zip(y,net(xa).argmax(axis=1)):#迭代测试标签和用net(xa).argmax(axis=1)对测试集预测出的标签
    print(f'实际标签:{x},预测到的标签{y}')
 #输出结果
'''
实际标签:1,预测到的标签1
实际标签:1,预测到的标签1
实际标签:1,预测到的标签1
实际标签:2,预测到的标签2
实际标签:0,预测到的标签0
实际标签:2,预测到的标签2
实际标签:2,预测到的标签2'''

上面为什么要用net(xa).argmax(axis=1),net(xa)的作用是用训练后的weight、bias利用线性层计算测试数据,在线性层设置了有3个分类,会输出3个值。

ceshi=torch.normal(5,1,(1,20,20))#生成一个测试数据
print(net(ceshi))#输出全连接层的数据
print(net[1].weight,net[1].bias)#输出训练后得出的weight和bias值
#输出结果
'''
tensor([[1206.8986, 1939.9656, 2804.1672]], grad_fn=<AddmmBackward0>)
Parameter containing:
tensor([[0.4142, 0.6124, 0.4152,  ..., 0.5302, 0.6660, 0.5268],
        [1.0520, 1.3901, 1.0559,  ..., 1.1287, 0.9636, 0.9536],
        [1.5338, 0.9975, 1.5289,  ..., 1.3411, 1.3705, 1.5196]],
       requires_grad=True) Parameter containing:
tensor([-1.6314,  3.3663, -1.7753], requires_grad=True)'''

在写个代码实现

flatten=nn.Flatten()#展平
ceshi=flatten(ceshi)#20x20展平成400
l1=torch.matmul(ceshi,net[1].weight[0])+net[1].bias[0]#模拟线性层1
l2=torch.matmul(ceshi,net[1].weight[1])+net[1].bias[1]#模拟线性层2
l3=torch.matmul(ceshi,net[1].weight[2])+net[1].bias[2]#模拟线性层3
print(net(ceshi))#输出预测框架的结果
print(l1,l2,l3)#输出手搓的结果
#输出结果
'''
tensor([[1206.8986, 1939.9656, 2804.1672]], grad_fn=<AddmmBackward0>)
tensor([1206.8986], grad_fn=<AddBackward0>) tensor([1939.9656], grad_fn=<AddBackward0>) tensor([2804.1672], grad_fn=<AddBackward0>)'''

上面的结果是不是一样,可是为什么有个.argmax(axis=1)这个是取最大值,看三个线性层输出,第三个是不是最大而且与预测的标签集一样。

对上面的结果在做下损失函数

ls1=loss(net(ceshi),torch.tensor([0]))#对标签0做交叉熵
ls2=loss(net(ceshi),torch.tensor([1]))#对标签1做交叉熵
ls3=loss(net(ceshi),torch.tensor([2]))#对标签2做交叉熵
print(ls1,ls2,ls3)
#输出结果
'''
tensor([1597.2687], grad_fn=<NllLossBackward0>) 
tensor([864.2017], grad_fn=<NllLossBackward0>) 
tensor([-0.], grad_fn=<NllLossBackward0>)'''

根据上面讲的,交叉熵函数的结果越小与标签越接近,明显ls3输出最小对应上面线性层l3最大,标签2。预测结果完全正确

总结

最后的总结就是,深度学习 softmax线性回归主要,是通过对训练集训练,找出每个数据对应类别标签,最拟合的weight权重,和bias偏移值。在通过weight和bias的结果去预测数据属于哪个标签。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表