前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【动手学深度学习笔记】之实现softmax回归模型

【动手学深度学习笔记】之实现softmax回归模型

作者头像
树枝990
发布2020-08-20 07:59:17
8290
发布2020-08-20 07:59:17
举报
文章被收录于专栏:拇指笔记

1.实现softmax回归模型

首先还是导入需要的包

代码语言:javascript
复制
import torchimport torchvisionimport sysimport numpy as np#替代d2l库的库from IPython import displayimport torchvision.transforms as transforms

1.1获取和读取数据

设置小批量数目为256。这一部分与之前的线性回归的读取数据大同小异,都是转换类型-->生成迭代器。

代码语言:javascript
复制
batch_size = 256
mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST',train=True,download=True,transform=transforms.ToTensor())#获取训练集mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST',train=False,download=True,transform=transforms.ToTensor())#获取测试集(这两个数据集在已存在的情况下不会被再次下载)
#生成迭代器(调用一次返回一次数据)train_iter = torch.utils.data.DataLoader(mnist_train,batch_size=batch_size,shuffle = True,num_workers = 0)
test_iter = torch.utils.data.DataLoader(mnist_test,batch_size = batch_size,shuffle=False,num_workers=0)

1.2初始化模型参数

由输入的数据可知:每个图像都是28*28像素,也就是说每个图像都有28*28=784个特征值。由于图像有10个类别,所以这个网络一共有10个输出。共计存在:784*10个权重参数和10个偏差参数。

代码语言:javascript
复制
num_inputs = 784num_outputs = 10
#初始化参数与线性回归也类似,权重参数设置为均值为0 标准差为0.01的正态分布;偏差设置为0W = torch.tensor(np.random.normal(0,0.01,(num_inputs,num_outouts)),dtype = torch.float)b = torch.zeros(num_outputs,dtype=torch.float32)
#同样的,开启模型参数梯度W.requires_grad_(requires_grad=True)b.requires_grad_(requires_grad=True)

1.3实现softmax运算

softmax运算本质就是将每个元素变成非负数,且每一行和为1。

首先回顾一下tensor的按维度操作。

代码语言:javascript
复制
X = torch.tensor([[1,2,3],[4,5,6]])
#dim=0表示对列求和。keepdim表示是否在结果中保留行和列这两个维度X.sum(dim=0,keepdim=True)X.sum(dim=1,keepdim=True)

然后定义一下softmax运算:softmax运算会先对每个元素做指数运算,再对exp矩阵同行元素求和,最后令矩阵每行各元素与该行元素之和相除,最终得到的矩阵每行元素和为1且非负数。

代码语言:javascript
复制
def softmax(X):    X_exp = X.exp()    partition = X_exp.sum(dim=1,keepdim=True)    return X_exp/partition	#这部分用了广播机制

1.4定义模型

将第二步做的和第三步做的合起来。

代码语言:javascript
复制
def net(X):	return softmax(torch.mm(X.view((-1,num_inputs)), W) +b)	#第一步:首先将X换形成28*28的张量,然后用.mm函数将换形后的张量与权重参数相乘,最后加偏差参数    #第二步:对第一步进行softmax运算

1.5定义损失函数

首先介绍一下torch.gather函数

代码语言:javascript
复制
#gather函数的定义torch.gather(input,dim,index,out=None) → Tensor#gather的作用是这样的,index实际上是索引,具体是行(dim=1)还是列(dim=0)的索引要看前面dim 的指定,输出的大小由index决定

这个函数的原理我归结如下

代码语言:javascript
复制
假设输入与上同;index=B;输出为CB中每个元素分别为b(0,0)=0,b(0,1)=0        b(1,0)=1,b(1,1)=0    如果dim=0(列)则取B中元素的列号,如:b(0,1)的1b(0,1)=0,所以C中的c(0,1)=输入的(0,1)处元素2
如果dim=1(行)则取B中元素的列号,如:b(0,1)的0b(0,1)=0,所以C中的c(0,1)=输入的(0,0)处元素1
总结如下:输出  元素  在  输入张量  中的位置为:输出元素位置取决于同位置的index元素dim=1时,取同位置的index元素的行号做行号,该位置处index元素做列号dim=0时,取同位置的index元素的列号做列号,该位置处index元素做行号。
最后根据得到的索引在输入中取值
index类型必须为LongTensorgather最终的输出变量与index同形。

例子如下:

代码语言:javascript
复制
import torch
a = torch.Tensor([[1,2],                 [3,4]])
b = torch.gather(a,1,torch.LongTensor([[0,0],[1,0]]))#1. 取各个元素行号:[(0,y)(0,y)][(1,y)(1,y)]#2. 取各个元素值做行号:[(0,0)(0,0)][(1,1)(1,0)]#3. 根据得到的索引在输入中取值#[1,1],[4,3]
c = torch.gather(a,0,torch.LongTensor([[0,0],[1,0]]))#1. 取各个元素列号:[(x,0)(x,1)][(x,0)(x,1)]#2. 取各个元素值做行号:[(0,0)(0,1)][(1,0)(0,1)]#3. 根据得到的索引在输入中取值#[1,2],[3,2]

因为softmax回归模型得到的结果可能是多个标签对应的概率,为了得到与真实标签之间的损失值,我们需要使用gather函数提取出在结果中提取出真实标签对应的概率。

假设y_hat是1个样本在3个类别中的预测概率(其余七个为0),y是这个样本的真实标签(数字0-9表示)。

代码语言:javascript
复制
y_hat = torch.tensor([0.1,0.3,0.6])y = torch.LongTensor([0])	#gather函数中的index参数类型必须为LongTensory_hat.gather(1,y.vies(-1,1))	#如果y不是列向量,则需要将变量y换形为列向量。选取第一维度(行)。#套用上述公式可知,输出为0.1,0.1就是真是类别0的概率。

有了上述理论基础,并根据交叉熵函数的公式

我们可以得到最终的损失函数。

代码语言:javascript
复制
def cross_entropy(y_hat,y):	return -torch.log(y_hat.gather(1,y.view(-1,1)))

1.6计算分类准确率

计算准确率的原理:

代码语言:javascript
复制
我们把预测概率最大的类别作为输出类别,如果它与真实类别y一致,说明预测正确。分类准确率就是正确预测数量与总预测数量之比

首先我们需要得到预测的结果。

从一组预测概率(变量y_hat)中找出最大的概率对应的索引(索引即代表了类别)

代码语言:javascript
复制
#argmax(f(x))函数,对f(x)求最大值所对应的点x。我们令f(x)= dim=1,即可实现求所有行上的最大值对应的索引。A = y_hat.argmax(dim=1)	#最终输出结果为一个行数与y_hat相同的列向量

然后我们需要将得到的最大概率对应的类别与真实类别(y)比较,判断预测是否是正确的

代码语言:javascript
复制
B = (y_hat.argmax(dim=1)==y).float()#由于y_hat.argmax(dim=1)==y得到的是ByteTensor型数据,所以我们通过.float()将其转换为浮点型Tensor()

最后我们需要计算分类准确率

我们知道y_hat的行数就对应着样本总数,所以,对B求平均值得到的就是分类准确率

代码语言:javascript
复制
(y_hat.argmax(dim=1)==y).float().mean()

上一步最终得到的数据为tensor(x)的形式,为了得到最终的pytorch number,需要对其进行下一步操作

代码语言:javascript
复制
(y_hat.argmax(dim=1)==y).float().mean().item()#pytorch number的获取统一通过.item()实现

整理一下,得到计算分类准确率函数

代码语言:javascript
复制
def accuracy(y_hat,y):    return (y_hat.argmax(dim=1).float().mean().item())

作为推广,该函数还可以评价模型net在数据集data_iter上的准确率。

代码语言:javascript
复制
def net_accurary(data_iter,net):    right_sum,n = 0.0,0    for X,y in data_iter:    #从迭代器data_iter中获取X和y        right_sum += (net(X).argmax(dim=1)==y).float().sum().item()        #计算准确判断的数量        n +=y.shape[0]        #通过shape[0]获取y的零维度(列)的元素数量    return right_sum/n

1.7优化算法

softmax回归应用的优化算法同样使用小批量随机梯度下降算法。

代码语言:javascript
复制
def sgd(params,lr,batch_size):    #lr:学习率,params:权重参数和偏差参数    for param in params:        param.data -= lr*param.grad/batch_size        #.data是对数据备份进行操作,不改变数据本身。

1.8训练模型

在训练模型时,迭代周期数num_epochs和学习率lr都是可以调节的超参数,通过调节超参数的值可以获得分类更准确的模型。

代码语言:javascript
复制
num_epochs,lr = 5,0.1
def train_softmax(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr ,optimizer):    for epoch in range(num_epochs):        #损失值、正确数量、总数 初始化。        train_l_sum,train_right_sum,n= 0.0,0.0,0                for X,y in train_iter:            y_hat = net(X)            l = loss(y_hat,y).sum()            #数据集损失函数的值=每个样本的损失函数值的和。            if optimizer is not None:                optimizer.zero_grad()			#对优化函数梯度清零            elif params is not None and params[0].grad is not None:                for param in params:                    param.grad.data.zero_()            l.backgrad()	#对损失函数求梯度            optimzer(params,lr,batch_size)                        train_l_sum += l.item()            train_right_sum += (y_hat.argmax(dim=1) == y).sum().item()            n += y.shape[0]                    test_acc = net_accuracy(test_iter, net)	#测试集的准确率        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f' % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))        train_softmax(net,train_iter,test_iter,cross_entropy,num_epochs,batch_size,[W,b],lr,sgd)

1.9预测

做一个模型的最终目的当然不是训练了,所以来预测一下试试。

代码语言:javascript
复制
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]    #labels是一个列表,所以有了for循环获取这个列表对应的文本列表
def show_fashion_mnist(images,labels):    display.set_matplotlib_formats('svg')    #绘制矢量图    _,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])

最终效果

由于训练比较耗时,我只训练了五次,可以看出,随着训练次数的增加,损失值从0.7854减少到0.4846;准确率从0.785提升到0.826。

程序

代码语言:javascript
复制
#实现softmax回归import torchimport torchvisionimport sysimport numpy as np
from IPython import displayfrom numpy import argmaximport torchvision.transforms as transformsfrom time import timeimport matplotlib.pyplot as plt
batch_size =256num_inputs = 784num_outputs = 10num_epochs,lr = 5,0.1
mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST',train=True,download=True,transform=transforms.ToTensor())#获取训练集mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST',train=False,download=True,transform=transforms.ToTensor())#获取测试集(这两个数据集在已存在的情况下不会被再次下载)
#生成迭代器(调用一次返回一次数据)train_iter = torch.utils.data.DataLoader(mnist_train,batch_size=batch_size,shuffle = True,num_workers = 0)
test_iter = torch.utils.data.DataLoader(mnist_test,batch_size = batch_size,shuffle=False,num_workers=0)
#初始化参数与线性回归也类似,权重参数设置为均值为0 标准差为0.01的正态分布;偏差设置为0W = torch.tensor(np.random.normal(0,0.01,(num_inputs,num_outputs)),dtype = torch.float)b = torch.zeros(num_outputs,dtype=torch.float32)
#同样的,开启模型参数梯度W.requires_grad_(requires_grad=True)b.requires_grad_(requires_grad=True)
def softmax(X):    X_exp = X.exp()    partition = X_exp.sum(dim=1,keepdim=True)    return X_exp/partition	#这部分用了广播机制
def net(X):	return softmax(torch.mm(X.view((-1,num_inputs)), W) +b)
def cross_entropy(y_hat,y):    return -torch.log(y_hat.gather(1,y.view(-1,1)))
def accuracy(y_hat,y):    return (y_hat.argmax(dim=1).float().mean().item())
def net_accurary(data_iter,net):    right_sum,n = 0.0,0    for X,y in data_iter:    #从迭代器data_iter中获取X和y        right_sum += (net(X).argmax(dim=1)==y).float().sum().item()        #计算准确判断的数量        n +=y.shape[0]        #通过shape[0]获取y的零维度(列)的元素数量    return right_sum/n
def sgd(params,lr,batch_size):    #lr:学习率,params:权重参数和偏差参数    for param in params:        param.data -= lr*param.grad/batch_size        
def train_softmax(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr ,optimizer,net_accuracy):    for epoch in range(num_epochs):        #损失值、正确数量、总数 初始化。        train_l_sum,train_right_sum,n= 0.0,0.0,0                for X,y in train_iter:            y_hat = net(X)            l = loss(y_hat,y).sum()            #数据集损失函数的值=每个样本的损失函数值的和。
            if params is not None and params[0].grad is not None:                for param in params:                    param.grad.data.zero_()            l.backward()    #对损失函数求梯度            optimizer(params,lr,batch_size)                        train_l_sum += l.item()            train_right_sum += (y_hat.argmax(dim=1) == y).sum().item()            n += y.shape[0]                    test_acc = net_accurary(test_iter, net)	#测试集的准确率        print('epoch %d, loss %.4f, train right %.3f, test right %.3f' % (epoch + 1, train_l_sum / n, train_right_sum / n, test_acc))
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]    #labels是一个列表,所以有了for循环获取这个列表对应的文本列表
def show_fashion_mnist(images,labels):    display.set_matplotlib_formats('svg')    #绘制矢量图    _,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()
        time1 = time()        train_softmax(net,train_iter,test_iter,cross_entropy,num_epochs,batch_size,[W,b],lr,sgd,net_accurary)print('\n',time()-time1,'s')

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])
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 拇指笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.实现softmax回归模型
    • 1.1获取和读取数据
      • 1.2初始化模型参数
        • 1.3实现softmax运算
          • 1.4定义模型
            • 1.5定义损失函数
              • 1.6计算分类准确率
                • 1.7优化算法
                  • 1.8训练模型
                    • 1.9预测
                      • 最终效果
                        • 程序
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档