沐神《动手学深度学习》之3.7softmax回归的简洁实现

目录

 

前言

一、加载数据集

二、初始化模型参数

三、损失函数

四、优化器

五、训练


 

前言

        前面3.3节我们实现了线性回归的简洁实现。 同样,通过深度学习框架的高级API也能更方便地实现softmax回归模型。 

        至此,我们对深度学习已经有了基本的认识。起码应该明白了,深度学习大概是个什么流程。为后面学习更加复杂的回归打下了基础。

        本节是第三章的最后一节,接下来我们开始。

一、加载数据集

        这一部分和前一节一样,d2l包里面有集成好的load_data_fashion_mnist,我不用d2l包。但是我们之前已经把load_data_fashion_minist写成了一个类。

from load_data_fashion_mnist import FashionMNISTDataLoader
# 加载数据集
fashion_minist = FashionMNISTDataLoader(batch_size=256)
train_iter, test_iter = fashion_minist.create_dataloaders()

二、初始化模型参数

from torch import nn
from torch.nn import Sequential
# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
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);

        net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

        nn.Flatten()将多维输入张量展平为一维向量,nn.Linear(784, 10),创造一个输入为28*28=784输出为10的线性网络。

        Sequential中,对于 Fashion-MNIST 的 [batch_size, 1, 28, 28] 输入,经过 Flatten() 后会变成 [batch_size, 784],然后传递给Linear(748,10)层。

       def init_weights(m):#权重初始化

       if判断语句的意思是,只对nn.Linear层进行初始化,把权重设置成均值0和标准差0.01随机初始值。

       apply()方法会递归地遍历网络中的所有模块,对所有子模块调用init_weights函数。

三、损失函数

        上一节,我们在实现损失函数之前。需要定义softmax操作,然后单独定义交叉熵损失。

        而这里只需要一行代码:

loss = nn.CrossEntropyLoss(reduction='none')

        nn.CrossentropyLoss是用于多酚类问题的标准损失函数,它结合了:

        LogSoftmax(对数softmax)
        NLLLoss(负数对数似然损失)

        当设置reduction='none'时,损失函数不会对批次中的样本损失进行任何聚合,而是为每个样本返回独立的损失值。

四、优化器

        和3.3节简单实现线性回归一样。使用学习率为0.1的小批量随机梯度下降作为优化算法。

import torch
trainer = torch.optim.SGD(net.parameters(), lr=0.1)

五、训练

        调用3.6节中定义的训练函数来训练模型。
        我把训练模型封装成了一个类ClassificationTrainer,只要调用它的训练方法就可以进行训练了。

num_epochs = 10
train = ClassificationTrainer(net,train_iter,test_iter,loss,trainer)
train.train(num_epochs)

        训练类:

import torch

from accumulator import Accumulator
from animator import Animator


class ClassificationTrainer:
    def __init__(self, net, train_iter, test_iter, loss, updater):
        self.net = net
        self.train_iter = train_iter
        self.test_iter = test_iter
        self.loss = loss
        self.updater = updater

    def accuracy(self, y_hat, y):
        """计算预测正确的数量"""
        # 使用 detach() 分离计算图,避免梯度计算
        if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
            y_hat = y_hat.argmax(axis=1)
        # 确保使用 detach() 来分离计算图
        y_hat = y_hat.detach()
        cmp = y_hat.type(y.dtype) == y
        return float(cmp.type(y.dtype).sum())

    def evaluate_accuracy(self):
        """计算模型在测试数据集上的精度"""
        metric = Accumulator(2)  # 正确预测数、预测总数
        for X, y in self.test_iter:
            # 使用 torch.no_grad() 来禁用梯度计算
            with torch.no_grad():
                y_hat = self.net(X)
            metric.add(self.accuracy(y_hat, y), y.numel())
        return metric[0] / metric[1]

    def train_epoch(self):
        """训练模型一个迭代周期"""
        metric = Accumulator(3)  # 训练损失总和、训练准确度总和、样本数
        for X, y in self.train_iter:
            # 计算梯度并更新参数
            y_hat = self.net(X)
            l = self.loss(y_hat, y)

            if isinstance(self.updater, torch.optim.Optimizer):
                # 使用PyTorch内置的优化器和损失函数
                self.updater.zero_grad()
                l.mean().backward()
                self.updater.step()
            else:
                # 使用定制的优化器和损失函数
                l.sum().backward()
                self.updater(X.shape[0])

            # 使用 detach() 和 item() 来安全地获取标量值
            metric.add(float(l.sum().detach()), self.accuracy(y_hat, y), y.numel())
        # 返回训练损失和训练精度
        return metric[0] / metric[2], metric[1] / metric[2]

    def train(self, num_epochs):
        """训练模型并显示训练过程"""
        # 创建动画器
        animator = Animator(xlabel='epoch', xlim=[1, num_epochs],
                            legend=['train loss', 'train acc', 'test acc'])

        for epoch in range(num_epochs):
            # 训练一个epoch
            train_metrics = self.train_epoch()
            # 在测试集上评估精度
            test_acc = self.evaluate_accuracy()

            # 添加到动画器
            animator.add(epoch + 1,
                         [train_metrics[0], train_metrics[1], test_acc])

            print(f'Epoch {epoch + 1}: '
                  f'train loss {train_metrics[0]:.4f}, '
                  f'train acc {train_metrics[1]:.3f}, '
                  f'test acc {test_acc:.3f}')

        animator.close()

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值