【动手学习深度学习--逐行代码解析合集】04softmax回归的从零开始实现

【动手学习深度学习】逐行代码解析合集

04softmax回归的从零开始实现


视频链接:动手学习深度学习–softmax回归的从零开始实现
课程主页:https://courses.d2l.ai/zh-v2/
教材:https://zh-v2.d2l.ai/

1、 softmax网络架构

在这里插入图片描述
2、 softmax运算

在这里插入图片描述
3、 交叉熵损失函数

3.1、 对数似然函数

在这里插入图片描述
在这里插入图片描述

3.2、 softmax及其导数
在这里插入图片描述
3.3、 交叉熵损失

在这里插入图片描述
4、代码

以下代码是在PyCharm中运行的

实用程序类Accumulator若不懂,参考文章链接

import torch
from IPython import display
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

"====================1、定义初始化模型参数===================="
# 输入28*28=784 ,拉成一条向量;数据集10个类别,所以网络输出维度为10
num_inputs = 784
num_outputs = 10

# W初始化为高斯随机分布的值
# 权重将构成一个784×10的矩阵, 偏置将构成一个1×10的行向量
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)

"====================2、定义softmax操作===================="
'''
实现softmax由三个步骤组成:
1、对每个项求幂(使用exp);
2、对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数;
3、将每一行除以其规范化常数,确保结果的和为1。
'''
def softmax(X):
    # 做指数运算
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)  # 对每一行求和
    return X_exp / partition  # 这里应用了广播机制
# 正如上述代码,对于任何随机输入,我们将每个元素变成一个非负数。 此外,依据概率原理,每行总和为1。

"====================3、定义模型===================="
def net(X):
    # W.shape[0]=784,X==>256*784
    # reshape中的-1表示系统帮助计算(结果为256)
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b) # 交叉熵损失

"====================4、定义损失函数===================="
y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
y_hat[[0, 1], y] # numpy高级索引 : 拿出对应真实标号的预测值
" 输出:tensor([0.1000, 0.5000]) "

# 一行代码就可以实现交叉熵损失函数
def cross_entropy(y_hat, y):
    return - torch.log(y_hat[range(len(y_hat)), y])

cross_entropy(y_hat, y)
" 输出:tensor([2.3026, 0.6931]) "

"====================5、分类精度===================="
def accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    # 现在 y_hat是一个256*10的一个矩阵
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        # 对每一行中元素值最大的下标存到y_hat中
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y  # 作比较转为布尔类型
    return float(cmp.type(y.dtype).sum())  # 预测正确的个数总和
# 预测正确的概率
accuracy(y_hat, y) / len(y)
"输出:0.5"

"评估在任意模型net上的准确率"
def evaluate_accuracy(net, data_iter):  #@save
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 正确预测数、预测总数
    with torch.no_grad():
        for X, y in data_iter:
            # 将所有预测正确的样本数,样本总数量加入迭代器中
            metric.add(accuracy(net(X), y), y.numel())
    # 返回预测正确的样本数和样本总数
    return metric[0] / metric[1]

"在Accumulator实例中创建了2个变量, 分别用于存储正确预测的数量和预测的总数量"
class Accumulator:  #@save
    """在n个变量上累加"""
    def __init__(self, n):
        self.data = [0.0] * n
        # n=2时,self.data = [0.0,0.0]

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]
        # 若args接收的传参为(4, 5),那么for a, b in zip(self.data, args)表示(0,4)(0,5)
        # a = 0.0,b = 4,然后执行a + float(b),得到结果4.0,此时self.data = [4.0, 0.0],
        # a = 0.0, b = 5,然后执行a + float(b) 得到结果5.0,最后self.data = [4.0, 5.0]。

    def reset(self):  # 重新设置空间大小并初始化。
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):  # 实现类似数组的取操作。
        return self.data[idx]
# 由于我们使用随机权重初始化net模型,因此该模型的精度应接近于随机猜测.例如在有10个类别情况下的精度为0.1。
evaluate_accuracy(net, test_iter)
"输出: 0.0598"

训练部分相关代码

"====================6、训练===================="
def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """训练模型一个迭代周期(定义见第3章)"""
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 训练损失总和、训练准确度总和、样本数
    metric = Accumulator(3)
    for X, y in train_iter:
        # 计算梯度并更新参数
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # 使用定制的优化器和损失函数
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回训练损失和训练精度
    return metric[0] / metric[2], metric[1] / metric[2]

"定义一个在动画中绘制数据的实用程序类"
class Animator:  #@save
    """在动画中绘制数据"""
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        # 增量地绘制多条线
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函数捕获参数
        self.config_axes = lambda: d2l.set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # 向图表中添加多个数据点
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    """训练模型(定义见第3章)"""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc'])
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)  # 得到训练损失和训练精度
        test_acc = evaluate_accuracy(net, test_iter)  # 在测试数据集上评估模型精度
        # 可视化训练误差,训练精度,测试精度
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_loss, train_acc = train_metrics
    # 判断训练损失(train_loss)是否小于0.5,如果不满足条件就会抛出异常并打印出train_loss的值
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

"小批量随机梯度下降来优化模型的损失函数"
lr = 0.1
def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)

"训练模型10个迭代周期"
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

"====================7、预测===================="
def predict_ch3(net, test_iter, n=6):  #@save
    """预测标签(定义见第3章)"""
    for X, y in test_iter:
        break
    trues = d2l.get_fashion_mnist_labels(y)  # 真实标号
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))  # 预测标号
    titles = [true +'\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])
    d2l.plt.show()

predict_ch3(net, test_iter)

训练损失、训练准确率、测试准确率可视化

在这里插入图片描述

预测结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值