【深度学习】:《PyTorch入门到项目实战》第五天:从0到1实现Softmax回归(含源码)

【深度学习】:《PyTorch入门到项目实战》第五天:从0到1实现Softmax回归

  • ✨本文收录于【深度学习】:《PyTorch入门到项目实战》专栏,此专栏主要记录如何使用 PyTorch实现深度学习笔记,尽量坚持每周持续更新,欢迎大家订阅!
  • 🌸个人主页:JoJo的数据分析历险记
  • 📝个人介绍:小编大四统计在读,目前保研到 统计学top3高校继续攻读统计研究生
  • 💌如果文章对你有帮助,欢迎✌ 关注、👍 点赞、✌ 收藏、👍 订阅专栏

参考资料:本专栏主要以沐神《动手学深度学习》为学习资料,记录自己的学习笔记,能力有限,如有错误,欢迎大家指正。同时沐神上传了的教学视频和教材,大家可以前往学习。

【深度学习】:《PyTorch入门到项目实战》第五天:从0到1实现Softmax回归(含源码)

文章目录

; 写在前面

softmax回归模型是logistic回归模型在多分类问题上的推广,在多分类问题中,类标签y可以取两个以上的值。本文基于MNIST手写数字数据集来演示如何使用Pytorch实现softmax回归。🎄

🍓1. 数据集导入

首先我们来简单的介绍一些 softmax回归基本模型,基本思路如下:
P ( c l a s s = i ) = e i ∑ e i P(class=i) = \frac{e^i}{\sum e^i}P (c l a s s =i )=∑e i e i ​

损失函数使用 交叉熵
l ( y , y ^ ) = − 1 m ∑ y i l o g y ^ i l(y,\hat y) = -\frac{1}{m}\sum y_ilog{\hat y_i}l (y ,y ^​)=−m 1 ​∑y i ​l o g y ^​i ​


import torch
import torch.nn as nn
from torchvision import datasets,transforms
from torch.utils import data
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim

在这里与之前不同的是我们导入了 torchvision,它是处理计算机视觉常用的一个库。沐神在这里使用了 FashionMnist数据集,我在这里还是使用 Mnist数据集,具体的下载代码如下所示。其中train参数可以设置训练集和测试集

trans = transforms.ToTensor()
train = datasets.MNIST(root='./data',download=True,train=True,transform=trans)
test = datasets.MNIST(root='./data',download=True,train=False,transform=trans)

Mnist数据集由10个数字的图像组成的。其中训练集有60000张图片,测试集有10000张图片。训练集用于模型的拟合,测试集用于评估模型的好坏

len(train), len(test)
(60000, 10000)

每张图片的像素均是 28*28,并且是灰度图像,所以通道数为1

train[0][0].shape
torch.Size([1, 28, 28])

我们来看一下训练集中的特征和标签,.

X, y = next(iter(data.DataLoader(train, batch_size=25)))
y
tensor([5, 0, 4, 1, 9, 2, 1, 3, 1, 4, 3, 5, 3, 6, 1, 7, 2, 8, 6, 9, 4, 0, 9, 1,
        1])

y代表的是 0-9的数字,下面我们将图形绘制出来

def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    """绘制图像列表"""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):

            ax.imshow(img.numpy())
        else:

            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if titles:
            ax.set_title(titles[i])
    return axes
X, y = next(iter(data.DataLoader(train, batch_size=25)))
show_images(X.reshape(25, 28, 28), 2, 9)


【深度学习】:《PyTorch入门到项目实战》第五天:从0到1实现Softmax回归(含源码)

可以看到第一张图片是5,第二张图片是0。接下来我们想要做的事情是,给电脑一张图片,如何让其返回一个正确的数字。

🍅2.初始化参数

因为 softmax回归需要输入的数据是一个向量,因此首先我们需要将数据进行转换,下面要注意初始化参数的大小。

num_inputs = 784
num_outputs = 10

W = torch.normal(0,0.01,size = (num_inputs,num_outputs),requires_grad = True)
b = torch.zeros(num_outputs,requires_grad=True)

🍒3.定义softmax回归

根据 softmax回归定义,我们可以通过以下三步实现:

  • 1.对每一项求指数
  • 2.求和
  • 3.用每一行的数除以和

具体实现代码如下

def softmax(X):
    X_exp = torch.exp(X)
    s = X_exp.sum(1, keepdims=True)
    return X_exp / s

下面我们举一个简单的例子看一下 softmax函数是如何工作的

z = torch.rand(3, 5)
h = softmax(z)
print(h)
tensor([[0.1768, 0.1426, 0.2773, 0.2582, 0.1450],
        [0.1580, 0.1307, 0.2118, 0.2411, 0.2583],
        [0.1863, 0.2572, 0.1148, 0.1996, 0.2420]])

这样就得出了每一个样本中每一类的概率

进一步定义 softmax回归模型

def nex(X):
    return softmax((X.reshape((-1,W.shape[0])).matmul(W)+b))

🍑4. 损失函数定义

在这里我们依然使用交叉熵函数处理多分类问题
损失函数:
l ( y , y ^ ) = − 1 m ∑ y i l o g y ^ i l(y,\hat y) = -\frac{1}{m}\sum y_ilog{\hat y_i}l (y ,y ^​)=−m 1 ​∑y i ​l o g y ^​i ​
其中y i = 0 , 1 y_i=0,1 y i ​=0 ,1,y ^ i \hat{y}_i y ^​i ​是预测的概率

在这里我想介绍两种方法计算损失函数,一种的沐神介绍的,通过索引来进行计算,具体如下所示

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

这里我们使用了y来进行索引,我们来看看一个具体的例子

y_true = torch.tensor([0,1])
y_hat = torch.tensor([[0.1,0.2,0.7],[0.3,0.5,0.2]])
y_hat[[0,1],y_true]
tensor([0.1000, 0.5000])

这里返回的是第一个样本中第一类是正确分类的,和第二个样本中的第二类是正确分类的。所以交叉熵的计算就是
− 1 2 ( 1 × l o g ( 0.1 ) + 1 × l o g ( 0.5 ) ) -\frac{1}{2}(1\times log(0.1)+ 1\times log(0.5))−2 1 ​(1 ×l o g (0 .1 )+1 ×l o g (0 .5 ))

cross_entropy(y_hat,y_true).mean()
tensor(1.4979)

等价于:

(-np.log(0.1)-np.log(0.5))/2
1.4978661367769954

上面这种方式虽然简洁,但是可能不太好理解,下面介绍一种更直观的方式。首先我们要将y转换成 one-hot编码。

y_true = torch.tensor([0,1])
y_hat = torch.tensor([[0.1,0.2,0.7],[0.3,0.5,0.2]])
y_one_hot = torch.zeros_like(y_hat)
y_one_hot.scatter_(1, y_true.unsqueeze(1), 1)
y_one_hot
tensor([[1., 0., 0.],
        [0., 1., 0.]])

可以看出此时的y_one_hot和y_hat维度相同,并且y_one_hot对应类上的元素是1,其余元素为0,此时再根据公式计算交叉熵即可

− 1 2 ( 1 × l o g ( 0.1 ) + 0 × l o g ( 0.2 ) + 0 × l o g ( 0.7 ) + 0 × l o g ( 0.2 ) + 1 × l o g ( 0.5 ) + 0 × l o g ( 0.3 ) -\frac{1}{2}(1\times log(0.1)+0\times log(0.2)+0\times log(0.7) +0 \times log(0.2) +1\times log(0.5)+0\times log(0.3)−2 1 ​(1 ×l o g (0 .1 )+0 ×l o g (0 .2 )+0 ×l o g (0 .7 )+0 ×l o g (0 .2 )+1 ×l o g (0 .5 )+0 ×l o g (0 .3 )

cost = (y_one_hot * -torch.log(y_hat)).sum(dim=1).mean()
cost
tensor(1.4979)

可以看出两种方法得到的结果一致

def opt(W,b):
    return optim.SGD([W,b],lr=0.1)

🍐5.训练模型

'''
初始化参数
'''
W = torch.zeros((784, 10), requires_grad=True)
b = torch.zeros(10, requires_grad=True)
'''
定义SGD优化器
'''
optimizer = optim.SGD([W, b], lr=0.1)

'''
训练模型
'''
nb_epochs = 1000
for epoch in range(nb_epochs + 1):

    z = net(X)
    cost = cross_entropy(z,y)

    optimizer.zero_grad()
    cost.mean().backward()
    optimizer.step()
    if epoch % 100 == 0 :
        print('Epoch {:4d}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, cost.mean().item()
        ))

Epoch    0/1000 Cost: 2.302585
Epoch  100/1000 Cost: 0.055274
Epoch  200/1000 Cost: 0.026265
Epoch  300/1000 Cost: 0.017182
Epoch  400/1000 Cost: 0.012762
Epoch  500/1000 Cost: 0.010150
Epoch  600/1000 Cost: 0.008425
Epoch  700/1000 Cost: 0.007202
Epoch  800/1000 Cost: 0.006290
Epoch  900/1000 Cost: 0.005582
Epoch 1000/1000 Cost: 0.005018

🍏6.模型预测

首先我们从测试集中随机抽取10个样本

X_test, y_test = next(iter(data.DataLoader(test, batch_size=10)))
show_images(X_test.reshape(10, 28, 28), 2, 5)
array([<axessubplot:>, <axessubplot:>, <axessubplot:>, <axessubplot:>,
       <axessubplot:>, <axessubplot:>, <axessubplot:>, <axessubplot:>,
       <axessubplot:>, <axessubplot:>], dtype=object)
</axessubplot:></axessubplot:></axessubplot:></axessubplot:></axessubplot:></axessubplot:></axessubplot:></axessubplot:></axessubplot:></axessubplot:>


【深度学习】:《PyTorch入门到项目实战》第五天:从0到1实现Softmax回归(含源码)

测试集拿到的十个数字为 7,2,1,0,4,1,4,9,5,9下面我们用刚刚训练好的模型来预测,看看结果如何

z = net(X_test)
predict = z.argmax(dim=1)
predict
tensor([7, 3, 1, 0, 4, 1, 4, 1, 4, 7])

可以看出预测的结果有六个正确,四个错误,模型效果一般。因为我们刚刚只使用了训练集中的25个样本,所以在训练集上预测效果并不好。如何提升预测精度问题将在后续讨论。

🍎7.使用内置api简单实现softmax回归

上面我们演示了如何从0到1实现 softmax回归,在 pytorch中,有内置的api可以直接帮我们更简洁的实现,具体代码如下

from torch import nn

X, y = next(iter(data.DataLoader(train, batch_size=25)))

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)
nb_epochs = 1000
for epoch in range(nb_epochs + 1):

    z = net(X)
    cost = loss(z,y)

    trainer.zero_grad()
    cost.mean().backward()
    trainer.step()
    if epoch % 100 == 0 :
        print('Epoch {:4d}/{} Cost: {:.6f}'.format(
            epoch, nb_epochs, cost.mean().item()
        ))
Epoch    0/1000 Cost: 2.318002
Epoch  100/1000 Cost: 0.062154
Epoch  200/1000 Cost: 0.028716
Epoch  300/1000 Cost: 0.018596
Epoch  400/1000 Cost: 0.013739
Epoch  500/1000 Cost: 0.010891
Epoch  600/1000 Cost: 0.009021
Epoch  700/1000 Cost: 0.007699
Epoch  800/1000 Cost: 0.006715
Epoch  900/1000 Cost: 0.005955
Epoch 1000/1000 Cost: 0.005349

本章的介绍到此介绍,如果文章对你有帮助,请多多点赞、收藏、评论、关注支持!!

Original: https://blog.csdn.net/weixin_45052363/article/details/124829325
Author: JOJO数据科学
Title: 【深度学习】:《PyTorch入门到项目实战》第五天:从0到1实现Softmax回归(含源码)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/667663/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球