3.7 softmax回归的简洁实现

导入需要的库

import torch
from torch import nn
from torch.nn import init
import numpy as np
import sys
import torchvision
from torch.utils import data
from torchvision import transforms

获取和读取数据

def get_dataloader_workers():
    if sys.platform.startswith('win'):
        return 0
    else:
        return 4

def load_data_fashion_mnist(batch_size, resize=None): 
    # 下载Fashion-MNIST数据集,然后将其加载到内存中,返回训练集和测试集的数据迭代器
    mnist_train = torchvision.datasets.FashionMNIST(root="data", train=True, transform=transforms.ToTensor(), download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="data", train=False, transform=transforms.ToTensor(), download=True)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=False, num_workers=get_dataloader_workers()))

batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size)

定义和初始化模型

softmax回归的输出层是一个全连接层,所以用一个线性模块就可以了。

num_inputs = 28*28*1
num_outputs = 10

class LinearNet(nn.Module):
    def __init__(self, num_inputs, num_outputs):
        super(LinearNet, self).__init__()
        self.linear = nn.Linear(num_inputs, num_outputs) # nn.Linear(input_size, output_size), 该线性层的输入特征数为num_inputs,输出特征数为1
  
    def forward(self, x): # 前面数据返回的每个batch样本x的形状为(batch_size, 1, 28, 28), 要先用view()将x的形状转换成(batch_size, 784)才送入全连接层。
        y = self.linear(x.view(x.shape[0], -1)) # 等价于 y = xw + b
        return y

net = LinearNet(num_inputs, num_outputs)

# 用OrederedDict定义网络
# class FlattenLayer(nn.Module):
#     def __init__(self):
#         super(FlattenLayer, self).__init__()
#     def forward(self, x): 
#         return x.view(x.shape[0], -1) # x 的形状转换成(batch, 784),x.shape[0]表示batch_size,-1表示自动推测


# from collections import OrderedDict

# net = nn.Sequential(
#     OrderedDict([ # 是一个有序字典
#         ('flatten', FlattenLayer()), # 将输入x展平
#         ('linear', nn.Linear(num_inputs, num_outputs)) # 全连接层
#     ])
# )

初始化模型参数:

init.normal_(net.linear.weight, mean=0, std=0.01) # 正态分布初始化
init.constant_(net.linear.bias, val=0) 

softmax和交叉熵损失函数

分开定义softmax运算和交叉熵损失函数可能会造成数值不稳定。PyTorch提供了一个包括softmax运算和交叉熵损失计算的函数。它的数值稳定性更好。

loss = nn.CrossEntropyLoss() # 交叉熵损失函数,其中包括softmax运算和交叉熵损失计算
# 先用softmax运算得到类别的概率分布,再用交叉熵损失计算得到交叉熵损失

定义优化算法

optimizer = torch.optim.SGD(net.parameters(), lr=0.1) # 调用optim实例的step函数来迭代模型参数
# net.parameters()返回一个包含模型所有参数的生成器,每个参数是一个tensor

训练模型

def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for X, y in data_iter: # X是图像,y是标签,数量为batch_size
        acc_sum += (net(X).argmax(dim=1) == y).float().sum().item() # net(X) 返回预测概率,argmax(dim=1)返回概率最大的类别,与标签y比较
        n += y.shape[0] # y.shape[0]是y的行数,也就是batch_size
    return acc_sum / n # 返回正确率

def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params=None, lr=None, optimizer=None): 
    for epoch in range(num_epochs):  # 训练模型一共需要num_epochs个迭代周期
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0 # 训练损失总和,训练准确度总和,样本数
        for X, y in train_iter: # X是图像,y是标签,数量为batch_size
            y_hat = net(X) # 预测概率
            l = loss(y_hat, y).sum() # 计算损失,sum()将所有loss值相加得到一个标量
            optimizer.zero_grad() # 梯度清零
            l.backward() # 计算梯度
            optimizer.step()  # 更新模型参数

            train_l_sum += l.item() # 将当前批次loss值相加得到一个总的loss值
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item() # 计算总准确率
            n += y.shape[0] # y.shape[0]是y的行数,也就是batch_size,计算总样本数

        test_acc = evaluate_accuracy(test_iter, net) # 计算测试集准确率
        print('周期 %d, 损失 %.4f, 数据集准确率 %.3f, 测试集准确率 %.3f'
              % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))


# 训练模型
num_epochs = 5
train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
上一篇
下一篇