沐神-动手学深度学习-softmax回归

Softmax回归的概念

上一节提到的线性回归是在做数据的拟合,而softmax是在进行数据的分类,线性回归和Softmax回归的区别在于前者是多输入单输出,后者是多输入多输出(因为需要衡量分到不同类的概率)。那相较于线性回归,softmax需要解决的问题包括:

  1. 通过输入值得到多个输出值

  1. 多个输出值进行损失计算

得到多个输出值

其实解决办法很容易想到,如果一个图片是10*10像素的,不考虑位置关系,那就相当于100个输入;图片共有3类,那就需要有3个输出值,分别指明属于不同类的概率,比如[0.1, 0.5, 0.4],第一类概率为0.1,第二类概率为0.5,第三类概率为0.4。为了达到这个效果,还需要不同的步骤:

  1. 得到多个输出值

  1. 将多个输出值转化为非负且和为1的数值

第一个目标很容易达到,输入X是1*100的行向量,输出Y是1*3的行向量,调整权重W的尺寸为100*3的矩阵。通过线性代数的知识,简单判断可知在尺寸上X*W =Y。

得到多个输出值之后,我们需要对输出值进行调整。非负的解决方法是输出值取指数,和为一的解决办法是每个输出除以其总和,代码实现如下:

##softmax输出值调整,输出值调整,并使得每组输出值均大于0且和为1
def softmax(X):
    X_exp = torch.exp(X)
    X_exp_sum = X_exp.sum(1, keepdim = True)#0是将同一列元素求和,1是将同一行元素求和
    return X_exp/X_exp_sum#广播机制

多个输出值进行损失计算

我们通过上文的操作可以得到预测值为输出属于不同类的概率,而真值为标量,例如2,说明该数据为第二类。在进行损失计算时,我们仅关注真确类的预测概率,比如说得到的预测值为[0.5 , 0.3, 0.2],而真值为1,也就是第一类,我们的损失就在于0.5,那怎么衡量损失的大小呢?对正确概率求负对数。由于概率在0~1之间,转为负对数之后,概率越大,得到的结果结果就越小,代码实现如下:

##损失函数计算:正确类概率的负对数,由于通过上一操作,概率在0~1之间,取负对数之后,概率越大,得到的交叉商越小
def cross_entropy(y_hat, y):
    return -torch.log(y_hat[range(len(y_hat)), y])

训练结果的显示

在训练时,除了整体的训练框架搭建,训练结果的显示也是重要的内容,在这里介绍简单的得到正确率和损失的办法。代码如下,其中y_hat.argmax(axis=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)#将每行最大值的序号赋给y_hat
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum())#分类对的数量

代码实现

下面中的代码是通过torch库实现的网络搭建、损失函数计算以及梯度下降。

import os
os.environ['KMP_DUPLICATE_LIB_OK']='True' ## 若不加这两句,则会一直出现:挂掉的内核

import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l
from torch import nn

##定义累加器,再算正确率的时候使用
class Accumulator: 
    """在n个变量上累加"""
    def __init__(self, n):
        self.data = [0.0] * n

    def 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]

##数据读取
batch_size = 268
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

##创建模型,并初始化参数
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

def init_wight(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std = 0.01)
        
net.apply(init_wight)

##损失函数
loss = nn.CrossEntropyLoss(reduction='none')

##梯度下降
trainer = torch.optim.SGD(net.parameters(), lr=0.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)#将每行最大值的序号赋给y_hat
    cmp = y_hat.type(y.dtype) == y
    return 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]

##进行训练
num_epochs = 10
for epochs in range(num_epochs):
    metric = Accumulator(3)
    for X, y in train_iter:
        y_hat = net(X)
        l = loss(y_hat, y)
        trainer.zero_grad()
        l.mean().backward()
        trainer.step()
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    print(metric[0] / metric[2], metric[1] / metric[2])
    
print(evaluate_accuracy(net, test_iter))

你可能感兴趣的:(人工智能,学习)