softmax回归的从零开始实现

  • 实验前思考
  • 获取和读取数据
    • 获取数据集
    • 查看数据集
      • 查看下载后的.pt文件
      • 查看mnist_train和mnist_test
    • 读取数据集
      • 查看数据迭代器内容
  • 初始化模型参数
  • 定义softmax函数
  • 定义模型
  • 定义损失函数
  • 计算分类准确率
  • 模型评价--准确率
  • 开始训练
  • 可视化
  • 总结
  • 完整代码

实验前思考

  • 本文不使用框架解决多分类问题。
  • 一定要先明白实验原理。可看博客深度学习pytorch–softmax回归(一)
  • 一定要先弄清楚数据是怎么用Tensor表示的。

获取和读取数据

这里使用Fashion-MNIST为例,训练集60000张,测试集10000张,设置batch_size大小为256。

获取数据集

如果没有下载的话,将download设置为True。

mnist_train=torchvision.datasets.FashionMNIST('./Datasets/FashionMNIST',train=True,download=False,transform=transforms.ToTensor())
mnist_test=torchvision.datasets.FashionMNIST('./Datasets/FashionMNIST',train=False,download=False,transform=transforms.ToTensor())

查看数据集

查看下载后的.pt文件

要使用的是processed文件夹下的training.pttest.pt文件,包含了数据以及其标签,用元组的形式存储。
形如:

(tensor([],dtype=torch.unit8),tensor([]))
  • 第一个tensor表示数据的数量,形状为torch.Size([60000,28,28]),60000表示.pt文件包含1000张图片,28为宽和高都是28个像素,每个像素用一个数字表示(也可以说是图像的特征)。
  • 第二个tensor表示每张图片的真实标签(0-9)。形状为torch.Size([60000]),每个数字代表真实标签。

上次线性回归的数据集是自己手工构造的tensor,这次是图片的tensor表示。

查看mnist_train和mnist_test

查看经过torchvision.datasets.FashionMNIST处理后的mnist_train和mnist_test,

print(mnist_train) #查看mnist_train
print(type(mnist_train)) #查看它的类型
print(len(mnist_train), len(mnist_test))
feature, label = mnist_train[0] #访问数据集的任一个样本
print(feature.shape, label)  # Channel x Height x Width

输出:

Dataset FashionMNISTNumber of datapoints: 60000Root location: ./Datasets/FashionMNISTSplit: TrainStandardTransformTransform: ToTensor()
<class 'torchvision.datasets.mnist.FashionMNIST'>
60000 10000
torch.Size([1, 28, 28]) 9

由上面代码可见,torchvision.datasets.FashionMNIST将原来存储在一个张量的训练数据分成了60000份,可迭代访问或下标访问mnist.train。

  • feature为单张图片的tensor表示,形状为torch.Size([1, 28, 28]),其中1为通道数量.
  • label为int类型,表示真实标签。

读取数据集

DataLoader将之前的数据做成一个批量、随机打乱且可以通过多进程加速读取的迭代器。

batch_size=256
num_workers=0 #多进程加速数据读取,0则不需要额外的进程
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)

查看数据迭代器内容

for X,y in train_iter:print(X.size())print(y.size())break

输出:

torch.Size([256, 1, 28, 28])
torch.Size([256])

一个X表示256个样本的特征(也就是softmax回归(一)中公式里的4个x,这里是28*28个x),一个y表示其每个样本的标签。

说明DataLoader只是将原来单个数据的张量通过增加一个维度合成了256个数据的张量。通俗地讲就是将256个1*28*28的张量放在一起,然后外面加一层[]。以后升维和降维要有这个思维。

关于该数据集的详细使用可看博客深度学习pytorch–MNIST数据集

初始化模型参数

由于像素(特征)有28*28=786个,所以输入层为一个786长度的向量。
由于为10分类任务,所以输出层的输出个数为10。

因此,根据softmax回归(一)的公式,可以推出 权重为参数为784X10的矩阵,偏置为1X10的矩阵。
上述分析的时候,脑子一定要有神经网络图和公式,就会觉得很明朗。根据公式还能够发现一点,不管是单样本还是批量样本,权重的形状是不发生改变的。

num_inputs=784 #28*28个像素(特征)
num_outputs=10 #分类的个数#权重初始化为均值为0,标准差为0.01的分布。
W=torch.tensor(np.random.normal(0,0.01,(num_inputs,num_outputs)),dtype=torch.float,requires_grad=True)
b=torch.zeros(num_outputs,dtype=torch.float,requires_grad=True) #偏置初始化为0

得到权重形状为torch.Size([786,10])的W和一个形状为torch.Size(1,10])的偏置b。

定义softmax函数

首先,想一下,根据公式,softmax函数的输入和输出是什么?
输入是网络输出层的结果,也就是每个样本10个输出。此处为批量处理,所以根据softmax回归(一)的公式,可以知道模型输出为一个256*10的矩阵。256为样本数量,10为10个分类的输出。
输出是根据softmax公式将其转化成概率的形式。

def softmax(X): #X为一个形状为256*10的二维张量X_exp=X.exp() #矩阵中每个元素取指数partition=X_exp.sum(dim=1,keepdim=True) #每行进行求和,并且保持维度不变,不懂的话可以看博客return X_exp/partition #实现softmax公式,用了广播机制

先随意定义一个X测试一下函数,下面的sum(dim=1)表示每一行相加,并且降低一个维度。如果dim=0则表示每一列相加。

X = torch.rand((2, 5))
X_prob = softmax(X)
print(X_prob, X_prob.sum(dim=1))

输出:

tensor([[0.2206, 0.1520, 0.1446, 0.2690, 0.2138],[0.1540, 0.2290, 0.1387, 0.2019, 0.2765]]) tensor([1., 1.])

从输出可以看到softmax函数定义的代码没有问题。

定义模型

定义模型之前想一想模型是什么,模型的输入是什么,输出是什么?
模型是一层神经网络+一层softmax函数,
模型的输入是迭代器的X,表示256个图片的像素(数据特征),形状为torch.Size([256, 1, 28, 28]),其中1表示通道数量
模型的输出是softmax函数的输出,形状为torch.Size([256, 10]),256表示batch_size(256)个样本,10表示每个样本对每个分类的概率分布情况。

def net(X):O=torch.mm(X.view(-1, num_inputs),W)+b #如果最后一个维度变了则,按以前的元素顺序来组合。return softmax(O)

由于迭代器中X的形状不对应神经网络输入层该有的形状,所以通过view把X从torch.Size([256, 1, 28, 28])变成torch.Size([256, 784])。
以前不理解为什么经常要像这样用view,现在思路很清晰。主要有2点原因:

  • 1.脑子一定要有神经网络结构(此处只有一层786输入,10输出),可以在纸上画一画。
  • 2.张量降维和升维的思维一定要正确,升维就是加括号,降维就是减少中括号,没有改变内容,不要被形状的数字给迷惑了。

定义损失函数

本实验采用交叉熵损失函数。
输入为预测结果y_hat和真实标签y
其中y_hat为torch.Size([256, 10])的张量,y为torch.Size([256])的张量。
输出为运算结果。

def cross_entropy(y_hat,y): #y_hat为模型输出(所有样本对所有样本的softmax概率值),y为标签(真实值)return - torch.log(y_hat.gather(1, y.view(-1, 1)))

真正计算的时候还需要用.sum()将张量中的元素相加才是损失函数值。
对比之后用框架实现该实验后的loss值才发现在计算完log之后少乘了 预测标签概率值 。算啦,不重要,以后有机会再回来改叭。
这里用到了gather函数,这个函数的作用就是找出y_hat中对应真实标签的概率,举例如下:

y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y = torch.LongTensor([0, 2])
y_hat.gather(1, y.view(-1, 1))

输出:

tensor([[0.1000],[0.5000]])

0.1就是将真实标签中的0作为索引,在y_hat中得到的,相当于y_hat[0][0]。
0.5就是将真实标签中的2作为索引,在y_hat中得到的,相当于y_hat[1][2]。

计算分类准确率

先计算预测正确的个数,也就是判断每个样本预测最大概率的标签是否和真实标签y相等就可以了。
再除以预测总数,就得到准确率。
下面的代码用的是y_hat处理后的张量和y张量中的每个元素比较,通过float()将true和false分别转换为1,0,再通过mean()求和平均

def accuracy(y_hat, y):return (y_hat.argmax(dim=1) == y).float().mean().item()

其中dim=1表示每行,dim=0表示每列。

模型评价–准确率

根据上面的思想,可以评价模型net在测试集上的准确率。

def evaluate_accuracy(data_iter,net): #在所有样本上的准确率acc_sum,n=0.0,0for X,y in data_iter:acc_sum+=(net(X).argmax(dim=1) == y).float().sum().item()n+=y.shape[0] #获取总数量(此处每批256)return acc_sum / n

其中net(X)就是y_hat,因为都表示模型的输出,形状为torch.Size([256, 10])。

开始训练

训练步骤:

  • 迭代数据
  • 将数据导入模型得到输出
  • 计算损失函数
  • 反向传播计算梯度
  • 使用优化算法更新参数
  • 梯度清零

每个epoch过后将训练好的参数在测试集上计算一下准确率。

num_epochs=5
lr=0.1
for epoch in range(num_epochs):train_loss_sum=0.0train_acc_sum=0.0n=0 #用来计算数据总数for X,y in train_iter:y_hat=net(X) loss=cross_entropy(y_hat,y).sum() #计算损失函数loss.backward() #反向传播#参数更新sgd([W,b],lr,batch_size)#梯度清零W.grad.data.zero_()b.grad.data.zero_()train_loss_sum+=loss.item()train_acc_sum+=(y_hat.argmax(dim=1) == y).sum().item()n+=y.shape[0]test_acc=evaluate_accuracy(test_iter,net)print('epoch {}, loss {:.4f}, train acc {:.3f}, test acc {:.3f}'.format(epoch + 1, train_loss_sum / n, train_acc_sum / n, test_acc))

可视化

对训练结果进行可视化。
下图第一行文本为真实标签。
第二行文本为模型预测结果。
第三行为图像输出(预先给定的图像)。

def use_svg_display():"""Use svg format to display plot in jupyter"""display.set_matplotlib_formats('svg')def get_fashion_mnist_labels(labels):text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat','sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']return [text_labels[int(i)] for i in labels]def show_fashion_mnist(images, labels):use_svg_display()# 这里的_表示我们忽略(不使用)的变量_, figs = plt.subplots(1, len(images), figsize=(12, 12))for f, img, lbl in zip(figs, images, labels):f.imshow(img.view((28, 28)).numpy())f.set_title(lbl)f.axes.get_xaxis().set_visible(False)f.axes.get_yaxis().set_visible(False)plt.show()X, y = iter(test_iter).next()
true_labels = get_fashion_mnist_labels(y.numpy())
pred_labels = get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]
show_fashion_mnist(X[0:9], titles[0:9])

总结

  • 所谓特征就是每个像素的的tensor表示。
  • 提取特征就是通过一个权重w与特征x相乘,权重越大则说明该像素的特征提取得越多。
  • 张量降维和升维的思维要正确,升维就是加括号,降维就是减少中括号,没有改变内容,不要被形状的数字给迷惑了。
  • 写函数或者模型的时候,先想一想要实现什么功能,再想一想输入和输出是什么。

完整代码

#softmax回归的从零开始实现(MINST数据集)
import torch
import torchvision
import numpy as np
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from IPython import display#获取数据集
mnist_train=torchvision.datasets.FashionMNIST('./Datasets/FashionMNIST',train=True,download=False,transform=transforms.ToTensor())
mnist_test=torchvision.datasets.FashionMNIST('./Datasets/FashionMNIST',train=False,download=False,transform=transforms.ToTensor())#读取数据集
batch_size=256
num_workers=0 #多进程加速数据读取,0则不使用多进程
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)#初始化模型参数
num_inputs=784 #28*28个像素(特征)
num_outputs=10 #分类的个数W=torch.tensor(np.random.normal(0,0.01,(num_inputs,num_outputs)),dtype=torch.float,requires_grad=True)
b=torch.zeros(num_outputs,dtype=torch.float,requires_grad=True)#定义模型
def softmax(X):X_exp=X.exp() #矩阵中每个元素取指数partition=X_exp.sum(dim=1,keepdim=True) #每行进行求和,并且保持维度不变,不懂的话可以看博客return X_exp/partition #实现softmax公式,用了广播机制def net(X):O=torch.mm(X.view(-1, num_inputs),W)+breturn softmax(O)#定义损失函数
def cross_entropy(y_hat,y): #y_hat为模型输出(所有样本对所有样本的softmax概率值),y为标签(真实值)return - torch.log(y_hat.gather(1, y.view(-1, 1)))#模型评价
def evaluate_accuracy(data_iter,net): #在所有样本上的准确率acc_sum,n=0.0,0for X,y in data_iter:acc_sum+=(net(X).argmax(dim=1) == y).float().sum().item()n+=y.shape[0] #获取总数量(此处每批256)return acc_sum / n#定义优化算法
def sgd(params,lr,batch_size):for param in params:param.data-=lr*param.grad/batch_size #除以batch_size之后计算批量loss的时候就不用求平均了,只需要sum()
#开始训练
num_epochs=5
lr=0.1
for epoch in range(num_epochs):train_loss_sum=0.0train_acc_sum=0.0n=0 #用来计算数据总数for X,y in train_iter:y_hat=net(X) loss=cross_entropy(y_hat,y).sum() #计算损失函数loss.backward() #反向传播#参数更新sgd([W,b],lr,batch_size)#梯度清零W.grad.data.zero_()b.grad.data.zero_()train_loss_sum+=loss.item()train_acc_sum+=(y_hat.argmax(dim=1) == y).sum().item()n+=y.shape[0]test_acc=evaluate_accuracy(test_iter,net)print('epoch {}, loss {:.4f}, train acc {:.3f}, test acc {:.3f}'.format(epoch + 1, train_loss_sum / n, train_acc_sum / n, test_acc))   #可视化
def use_svg_display():"""Use svg format to display plot in jupyter"""display.set_matplotlib_formats('svg')def get_fashion_mnist_labels(labels):text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat','sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']return [text_labels[int(i)] for i in labels]def show_fashion_mnist(images, labels):use_svg_display()# 这里的_表示我们忽略(不使用)的变量_, figs = plt.subplots(1, len(images), figsize=(12, 12))for f, img, lbl in zip(figs, images, labels):f.imshow(img.view((28, 28)).numpy())f.set_title(lbl)f.axes.get_xaxis().set_visible(False)f.axes.get_yaxis().set_visible(False)plt.show()X, y = iter(test_iter).next()
true_labels = get_fashion_mnist_labels(y.numpy())
pred_labels = get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]
show_fashion_mnist(X[0:9], titles[0:9])

深度学习pytorch--softmax回归(二)相关推荐

  1. 深度学习基础--SOFTMAX回归(单层神经网络)

    深度学习基础–SOFTMAX回归(单层神经网络) 最近在阅读一本书籍–Dive-into-DL-Pytorch(动手学深度学习),链接:https://github.com/newmonkey/Div ...

  2. 深度学习 — — PyTorch入门(二)

    在深度学习--PyTorch入门(一)中我们介绍了构建网络模型和加载数据的内容,本篇将继续介绍如何完成对模型的训练. 训练:更新网络权重 构建网络结构和加载完数据集之后,便可以开始进行网络权重的训练. ...

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

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

  4. 深度学习:Softmax回归

    在前面,我们介绍了线性回归模型的原理及实现.线性回归适合于预测连续值,而对于分类问题的离散值则束手无策.因此引出了本文所要介绍的softmax回归模型,该模型是针对多分类问题所提出的.下面我们将从so ...

  5. 动手深度学习PyTorch(十二)word2vec

    独热编码 独热编码即 One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都有它独立的寄存器位,并且在任意时候,其中只有一位有效.举个例子,假设我们有四个样 ...

  6. 深度学习pytorch--线性回归(二)

    线性回归无框架实现 线性回归的从零开始实现 生成数据集(简单的人工构造) 读取数据 初始化模型参数 定义模型 定义损失函数 定义优化算法 训练模型 小结 完整代码(可直接运行) 线性回归的从零开始实现 ...

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

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

  8. [动手学深度学习]02 softmax回归

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

  9. 动手学深度学习(PyTorch实现)(十二)--批量归一化(BatchNormalization)

    批量归一化-BatchNormalization 1. 前言 2. 批量归一化的优势 3. BN算法介绍 4. PyTorch实现 4.1 导入相应的包 4.2 定义BN函数 4.3 定义BN类 5. ...

  10. 【分类器 Softmax-Classifier softmax数学原理与源码详解 深度学习 Pytorch笔记 B站刘二大人(8/10)】

    分类器 Softmax-Classifier softmax数学原理与源码详解 深度学习 Pytorch笔记 B站刘二大人 (8/10) 在进行本章的数学推导前,有必要先粗浅的介绍一下,笔者在广泛查找 ...

最新文章

  1. Python统计字符串中的中英文字符、数字空格,特殊字符
  2. elasticsearch使用bulk实现批量操作
  3. Android应用开发提高篇(4)-----Socket编程(多线程、双向通信)(转载)
  4. 有符号二进制数--补码
  5. 双眼融合训练一个月_视觉融合你知道多少
  6. flink实时同步mysql_基于Canal与Flink实现数据实时增量同步(一)
  7. [MySQL基础]MySQL常见命令介绍
  8. FreeSql (九)删除数据
  9. 东华理工大学arm试卷_ARM东华理工大学2015-2016试卷A
  10. 合伙和合作的区别是什么?
  11. logback配置文件模板
  12. 从北斗到Mate 50:星空中的中国式浪漫
  13. 常见职位角色及其英文缩写
  14. Python语言程序设计基础_期末作品设计——收银软件(2020-2 B)_答案_通识教育必修课程_上海师范大学
  15. [数独进阶技巧]区块摒除法
  16. 关于linux重启后磁盘分区消失的情况复现与修复
  17. 解决客户之间的矛盾-生米煮成熟饭
  18. linux git 命令备忘
  19. Vue项目中实现sm4 CBC、ECB加密传输
  20. 今夕何夕(思路详解)

热门文章

  1. jvm高并发_JVM上的高并发HTTP客户端
  2. pcl_openmap_OpenMap教程第2部分–使用MapHandler构建基本地图应用程序–第1部分
  3. redis nosql_Redis教程:NoSQL键值存储
  4. web编程 端口分配_以编程方式衡量分配
  5. java压缩文件读取_用Java读取/写入压缩和非压缩文件
  6. java 并发的原子性_Java并发教程–原子性和竞争条件
  7. 遗传算法可用什么算法代替_获取可用密码算法的列表
  8. Spring @Value批注
  9. java pojo使用_在POJO中使用ThreadLocal进行Java嵌套事务
  10. 控制Java并行流的并行度