深度学习-Softmax回归+损失函数+图像分类数据集

news2024/11/26 2:29:00

目录

  • Softmax回归
  • 回归 VS 分类
    • Kaggle上的分类问题
  • 从回归到多类分类
    • 回归
    • 分类
    • 从回归到多类分类-均方损失
    • 从回归到多类分类-无校验比例
    • 从回归到多类分类-校验比例
  • Softmax和交叉熵损失
  • 总结
  • 损失函数
    • 均方损失
    • 绝对值损失函数
    • 鲁棒损失
  • 图像分类数据集
    • 通过框架中内置函数将FashionMNIST数据集下载并读取到内存中
    • 可视化数据集的函数
    • 几个样本的图像及其相应的标签
    • 读取一小批量数据,大小为batch_size
    • 定义load_data_fashion_mnist函数
  • softmax回归的从零实现
    • ① 给定一个矩阵X,可以对所有元素求和。
    • ②实现softmax
    • 实现softmax回归模型
  • 交叉熵损失
    • 实现交叉熵损失函数
    • 将预测类别与真实y元素进行比较(即:预测正确的概率)
    • 评估在任意模型net的准确率
  • Softmax回归的训练
    • 训练函数(完整的数据集通过神经网络一次)
    • 定义一个在动画中绘制数据的实用程序类
    • 轮次总训练函数
  • 对图像进行分类预测
  • Softmax回归的简洁实现
  • 问题

Softmax回归

在这里插入图片描述




回归 VS 分类

回归估计一个连续值

分类预测一个离散类别
例如:
MNIST:手写数字识别(10类)

在这里插入图片描述

ImageNet:自然物体分类(1000类)
在这里插入图片描述

Kaggle上的分类问题

将人类蛋白质显微镜图片分为28类

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

将恶意软件分为9个类别

在这里插入图片描述
将恶意的Wikipedia评论分成7类
在这里插入图片描述




从回归到多类分类

回归

单连续数值输出
自然区间R
跟真实值的区别作为损失
在这里插入图片描述

分类

通常多个输出
输出i是预测为第i类的置信度
在这里插入图片描述

解释举例说明:

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

softmax回归原理及损失函数-跟李沐老师动手学深度学习




从回归到多类分类-均方损失

对类别进行一位有效编码
在这里插入图片描述

使用均方损失训练




从回归到多类分类-无校验比例

最大值预测
在这里插入图片描述
选取i,使得最大化 O i O_i Oi的置信度的值作为预测。其中 O i O_i Oi中的i是预测的标号,叫做one-hot(独热编码)

需要更置信的识别正确类(大余量)

在这里插入图片描述




从回归到多类分类-校验比例

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

输出匹配概率(非负,和为1)




Softmax和交叉熵损失

在这里插入图片描述



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




在Pycharm中损失函数中,它默认的是e为底,log不写参数时的底数取决于具体的上下文和所使用的工具或库。在数学中,通常默认为10;在编程中,可能会因库或语言的不同而有所差异。
在这里插入图片描述
需要用到数学中的log公式:


在这里插入图片描述


在这里插入图片描述




总结

Softmax回归是一个多类分类模型
使用Softmax操作子得到每个类的预测置信度
使用交叉熵来衡量预测和标号的区别




损失函数

损失函数:用来衡量预测值和真实值之间的区别,是机器学习里面一个重要的概念。
三个常用的损失函数 L2 lossL1 lossHuber’s Robust loss


均方损失

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

① 蓝色曲线为当y=0时,变换预测值y'所获得的曲线。 ② 绿色曲线为当y=0时,变换y'所获得的曲线是似然函数,即$1^{-l(y,y')}$,似然函数呈高斯分布。最小化损失函数就是最大化似然函数。 ③ 橙色曲线为损失函数的梯度,梯度是一次函数,所以穿过原点。

在这里插入图片描述
④ 在梯度下降的时候,我们是对负梯度方向来更新我们的参数。所以它的导数就决定我们是如何更新我们的参数的。当预测值y’跟真实值y隔的比较远的时候,(真实值y为0,预测值就是下面的曲线里的x轴),梯度比较大,所以参数更新比较多。
⑤ 随着预测值靠近真实值的时候,梯度越来越小,意味着对参数的更新的幅度越来越小。

当我对于离原点比较远的时候,我不一定想要那么大的梯度来更新我的参数。所以另外一个选择是考虑绝对值损失函数。




绝对值损失函数

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

①主要特性:当预测值和真实值隔的比较远时,不管有多远,我的梯度永远是常数。所以权重的更新也不是特别大。会带来很多稳定性的好处。 ② 它的缺点是在零点处不可导,并在零点处左右有±1的变化,这个不平滑性导致预测值与真实值靠的比较近的时候,也就是优化到末期的时候,可能会不那么稳定。

在这里插入图片描述




鲁棒损失

① 结合L1 loss 和L2 loss损失。
定义:当预测值和真实值差的比较大时,绝对值大于1的时候是一个绝对值误差。当预测值和真实值靠的比较近的时候就是一个平方误差。
在这里插入图片描述
当y’大于1或小于-1的时候,它的导数是常数,在之间的时候是一个渐变的过程。
它的好处:当预测值和真实值差的比较远的时候,梯度比较均匀的力度往回拉。当靠近的时候(优化比较默契的时候),梯度的绝对值会越来越小,从而保证优化的平滑。
在这里插入图片描述




图像分类数据集

MNIST数据集是图像分类中广泛使用的数据集之一,但作为基准数据集过于简单。我们将使用类似但更复杂的Fashion-MNIST数据集。

FashionMNIST是一个常用的数据集,它包含70,000个28x28的灰度图片,分为10个类别,每个类别有7,000个样本。其中,60,000个样本用于训练,10,000个样本用于测试。

通过框架中内置函数将FashionMNIST数据集下载并读取到内存中

import torchvision
from torchvision import transforms
from d2l import torch as d2l

d2l.use_svg_display()
# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
# 并除以255使得所有像素的数值均在0到1之间,将其归一化到[0.0, 1.0]范围。
trans = transforms.ToTensor()#预处理
#第一个参数数据集的根目录,第二个参数加载训练数据集,第三个参数使用上面定义的转换对象,第四个参数如果数据集不在指定的根目录下,则从互联网上下载。
mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)
print(len(mnist_train)) # 训练数据集长度
print(len(mnist_test))  # 测试数据集长度

print(mnist_train[0][0].shape) #第一张图片的形状,黑白图片,所以RGB的channel为1。
print(mnist_train[0][1]) # [0][0]表示第一个样本的图片信息,[0][1]表示该样本对应的标签值

结果:在这里插入图片描述




可视化数据集的函数

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


def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]


def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    """Plot a list of images."""
    #元组figsize,它表示图形的宽度和高度。图形的宽度是列数(num_cols)乘以一个放缩比例因子(scale),而高度是行数(num_rows)乘以相同的放缩比例因子。
    # 传进来的图像尺寸,scale 为放缩比例因子
    figsize = (num_cols * scale, num_rows * scale)
    #_, axes: 返回的图形对象和子图坐标轴对象的列表分别赋值给_和axes。axes是一个二维数组,其中包含了所有子图的坐标轴对象,可以通过索引来访问和修改它们。
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    print(_)
    print(axes)  # axes 为构建的两行九列的画布
    #将二维数组转换为一维数组。可以更容易地遍历所有的子图,而不需要考虑它们的原始二维布局。
    axes = axes.flatten()
    print(axes)  # axes 变成一维数据
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if i < 1:
            print("i:", i)
            print("ax,img:", ax, img)
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
            ax.set_title(titles[i])
        else:
            # PIL图片
            ax.imshow(img)


d2l.use_svg_display()
# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)
X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))  # X,y 为仅抽取一次的18个样本的图片、以及对应的标签值
#show_images用来显示一批图像。.reshape(18, 28, 28) 意味着 X 被重新塑形为一个包含 18 个图像的数据集,每个图像的大小是 28x28 像素。
#2, 9: 这两个参数指定了如何在一个网格中布局显示的图像。在一个 2 行 9 列的网格中显示图像。因此,总共会显示 2x9=18 个图像,与 X.reshape(18, 28, 28) 中的图像数量相匹配。
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y))

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

在这里插入图片描述

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




几个样本的图像及其相应的标签

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


def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]


def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    """Plot a list of images."""
    figsize = (num_cols * scale, num_rows * scale)  # 传进来的图像尺寸,scale 为放缩比例因子
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
            ax.set_title(titles[i])
            ax.axis('off')  # 隐藏坐标轴
        else:
            # PIL图片
            ax.imshow(img)


d2l.use_svg_display()
# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)
X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))  # X,y 为仅抽取一次的18个样本的图片、以及对应的标签值
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y))
d2l.plt.show()#在PyCharm等IDE中可能需要显式调用show()来显示图形

结果:
在这里插入图片描述




读取一小批量数据,大小为batch_size

import torch
import torchvision
from torch.utils import data
from torchvision import transforms


def get_fashion_mnist_labels(labels):
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]


def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    """Plot a list of images."""
    #元组figsize,它表示图形的宽度和高度。图形的宽度是列数(num_cols)乘以一个放缩比例因子(scale),而高度是行数(num_rows)乘以相同的放缩比例因子。
    # 传进来的图像尺寸,scale 为放缩比例因子
    figsize = (num_cols * scale, num_rows * scale)
    #_, axes: 返回的图形对象和子图坐标轴对象的列表分别赋值给_和axes。axes是一个二维数组,其中包含了所有子图的坐标轴对象,可以通过索引来访问和修改它们。
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    #将二维数组转换为一维数组。可以更容易地遍历所有的子图,而不需要考虑它们的原始二维布局。
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
            ax.set_title(titles[i])
        else:
            # PIL图片
            ax.imshow(img)


def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 4


# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)
# batch_size用于图像显示
X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))  # X,y 为仅抽取一次的18个样本的图片、以及对应的标签值
# show_images用来显示一批图像。.reshape(18, 28, 28) 意味着 X 被重新塑形为一个包含 18 个图像的数据集,每个图像的大小是 28x28 像素。
# 2, 9: 这两个参数指定了如何在一个网格中布局显示的图像。在一个 2 行 9 列的网格中显示图像。因此,总共会显示 2x9=18 个图像,与 X.reshape(18, 28, 28) 中的图像数量相匹配。
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y))

batch_size = 256 #用于模型训练
train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers())
timer = d2l.Timer() # 计时器对象实例化,开始计时
for X, y in train_iter:  # 遍历一个batch_size数据的时间
    continue
print(f'{timer.stop():.2f}sec') # 计时器停止时,停止与开始的时间间隔事件

结果:在这里插入图片描述




定义load_data_fashion_mnist函数

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


def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 4


# 加载数据集的函数
def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
    trans = transforms.Compose(trans)

    mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, 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()))  # 测试集通常不shuffle


# 使用函数加载数据
if __name__ == '__main__':
    batch_size = 256  # 用于模型训练
    train_iter, test_iter = load_data_fashion_mnist(batch_size)
    # 计时器部分
    timer = d2l.Timer()
    for X, y in train_iter:
        continue
    print(f'{timer.stop():.2f}sec')

结果:在这里插入图片描述




softmax回归的从零实现

①我们之前的每张图片是一个长为28宽为28的图片,通道数为1,是个3D的输入,但对于softmax回归来说我的输入相当于向量所以要将图片拉长,拉成一个向量(会损失空间信息–>留给卷积神经网络来继续),28×28=784,所以softmax回归的输入是一个784的向量

② 因为数据集有10个类别,所以网络输出维度为10.

import torch

num_inputs = 784
num_outputs = 10
w = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)
print(w.shape)
print(b.shape)

① 给定一个矩阵X,可以对所有元素求和。

import torch

# 给定一个矩阵X,2*3的矩阵,我们可以按照维度进行元素求和
X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
#X.sum(0, keepdim=True)其中0是按照第一维度(行)求和,X.sum(1, keepdim=True)按照第二维度(列)求和
#keepdim=True保持是二维矩阵,这个参数用于指定在归并操作后是否保持原始张量的维度。
print(X.sum(0, keepdim=True), X.sum(1, keepdim=True))
print("------------------分开查看,看的更明白------------------")
print(X.sum(0, keepdim=True))
print(X.sum(1, keepdim=True))

结果:
在这里插入图片描述




②实现softmax

在这里插入图片描述

import torch


def softmax(X):
    X_exp = torch.exp(X) # 每个都进行指数运算
    partition = X_exp.sum(1, keepdim=True)#按照第二维度,列方向求和(就是对一行求和)
    return X_exp / partition # 这里应用了广播机制扩展partition的第二个维度以匹配X_exp的形状,从而进行逐元素的除法。


# 将每个元素变成一个非负数。此外,依据概率原理,每行总和为1。
X = torch.normal(0, 1, (2, 5))  # 两行五列的数,数符合标准正态分布
X_prob = softmax(X)
print(X_prob) # 形状没有发生变化,还是一个两行五列的矩阵,Softmax转换后所有值为正的
print(X_prob.sum(1)) # 相当于 X_prob.sum(axis=1) 按行求和,概率和为1

结果:
在这里插入图片描述

详细分析代码

import torch


def softmax(X):
    X_exp = torch.exp(X) # 每个都进行指数运算
    print(X_exp.shape) #(batch_size, num_classes)
    partition = X_exp.sum(1, keepdim=True)#按照第二维度,列方向求和(就是对一行求和)
    print(partition.shape) #(batch_size, 1)
    return X_exp / partition # 这里应用了广播机制扩展partition的第二个维度以匹配X_exp的形状,从而进行逐元素的除法。


# 将每个元素变成一个非负数。此外,依据概率原理,每行总和为1。
X = torch.normal(0, 1, (2, 5))  # 两行五列的数,数符合标准正态分布
print('X矩阵:')
print(X)
X_prob = softmax(X)
print(X_prob) # 形状没有发生变化,还是一个两行五列的矩阵,Softmax转换后所有值为正的
print(X_prob.sum(1)) # 相当于 X_prob.sum(axis=1) 按行求和,概率和为1

结果:

在这里插入图片描述




实现softmax回归模型

import torch


def softmax(X):
    X_exp = torch.exp(X) # 每个都进行指数运算
    partition = X_exp.sum(1,keepdim=True)
    return X_exp / partition # 这里应用了广播机制


def net(X):
    #需要一个批量大小*输入维数的矩阵,reshape成一个2D的矩阵,-1表示自动计算(其实就是批量大小batch_size=256)而w.shape[0]=784
    #X已经被重新塑形为(batch_size, 784),而w的形状假设为(784, num_classes)(其中num_classes是类别的数量)
    #所以矩阵乘法的结果将是一个(batch_size, num_classes)的矩阵。即:256*10的矩阵
    return softmax(torch.matmul(X.reshape((-1, w.shape[0])), w)+b)


batch_size = 256
num_inputs = 784
num_outputs = 10
w = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)
# 生成模拟输入数据
X = torch.randn(batch_size, num_inputs)  # 创建一个形状为(batch_size, num_inputs)的随机数矩阵

# 通过网络计算输出
outputs = net(X)
# 验证输出的形状和内容
print("Output shape:", outputs.shape)  # 输出 (batch_size, num_outputs),即 (256, 10)

# 打印部分输出内容
print("Sample outputs:")
print(outputs[:5])  # 打印前5个样本的输出,每个样本有10个类别的概率

# 如果需要的话,也可以检查每行的概率是否加起来为1(接近1,因为浮点数的精度问题)
for i in range(outputs.size(0)):
    assert torch.allclose(outputs[i].sum(), torch.tensor(1.0),
                          atol=1e-5), "Probabilities in row {} do not sum to 1".format(i)

print("All probabilities in each row sum to approximately 1.")

结果:

在这里插入图片描述




交叉熵损失

① 创建一个数据y_hat,其中包含2个样本在3个类别的预测概率,使用y作为y_hat中概率的索引。

import torch

# 包含两个整数元素:0和2
# 表示两个样本的真实类别标签。第一个样本的真实类别是0,而第二个样本的真实类别是2。
y = torch.tensor([0, 2])

# y_hat表示两个一维数组,表示是预测的类型'0','1','2'的概率
# 一维数组的标量大小和位置 对应 类型和概率。比如0.1就是类型’0‘ 的概率为0.1,   0.3表示类型’1‘的概率为0.3,    0.6表示类型’2‘的概率为0.6
# [0.1, 0.3, 0.6]第0个样本的预测值,[0.3, 0.2, 0.5]第1个样本的预测值
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])  # 两个样本在3个类别的预测概率

# 第三行代码是索引,[0,1]是行索引,y是列索引,然后配对,最后取y_hat[0][0],y_hat[1][2]
print(y_hat[[0, 1], y])  # 把第0个样本对应标号"0"的预测值拿出来、第1个样本对应标号"2"的预测值拿出来


结果:在这里插入图片描述




实现交叉熵损失函数


在这里插入图片描述

解释为什么 ∑ i \sum_{i} i y i y_i yi=1,因为只有当i=t的时 y t y_t yt是真实类别的情况才等于1,其余的都为0.

公式:-log( y ^ y \hat{y}_y y^y)其中 y ^ y \hat{y}_y y^y是预测的类别的概率。

range(len(y_hat))表示行标号、y表示列标号
y_hat[range(len(y_hat)), y]表示根据行标和列标来查找真实类别的预测概率

对于此题的预测的类别的概率是y_hat[行号][列号]
所以公式可以写为:-log( y − h a t [ 行号 ] [ 列号 ] y_-{hat}[行号][列号] yhat[行号][列号])

import torch


def cross_entropy(y_hat, y):
    # range(len(y_hat))每一行拿出一个0到n的向量
    # 其中range(len(y_hat))是生成0和1,也就是只有两个样本,在这两个样本中查找真实标号的预测值
    # range(y_hat)是所有行,y是真实值所对应的列
    return -torch.log(y_hat[range(len(y_hat)), y]) # y_hat[range(len(y_hat)),y]为把y的标号列表对应的值拿出来。传入的y要是最大概率的标号


# 包含两个整数元素:0和2
# 表示两个样本的真实类别标签。第一个样本的真实类别是0,而第二个样本的真实类别是2。
y = torch.tensor([0, 2])

# y_hat表示两个一维数组,表示是预测的类型'0','1','2'的概率
# 一维数组的标量大小和位置 对应 类型和概率。比如0.1就是类型’0‘ 的概率为0.1,   0.3表示类型’1‘的概率为0.3,    0.6表示类型’2‘的概率为0.6
# [0.1, 0.3, 0.6]第0个样本的预测值,[0.3, 0.2, 0.5]第1个样本的预测值
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])  # 两个样本在3个类别的预测概率

# y_hat预测和真实标号y
print(cross_entropy(y_hat, y))

结果:在这里插入图片描述

-ln0.1≈-(-2.3026)=2.3026
-ln0.5≈-(-0.6931)=0.6931




将预测类别与真实y元素进行比较(即:预测正确的概率)

import torch

def accuracy(y_hat, y):
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: 
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum()) 

y = torch.tensor([0, 2])
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])  
print("accuracy(y_hat,y) / len(y):", accuracy(y_hat, y) / len(y))

代码注释详细说明:

import torch


def accuracy(y_hat, y):
    """计算预测正确的数量"""
    # 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # y_hat.shape[1]>1表示不止一个类别,每个类别有各自的概率
        # y_hat.argmax(axis=1)为求每行数值最大的那列(最大的预测概率)的索引号,y_hat是预测分类的类别
        y_hat = y_hat.argmax(axis=1)
        # 输出查看一下是否正确:不出意外索引都是2,因为第一行是0.6最大、第二行是0.5最大
        print("y_hat:", y_hat)

    print(y_hat.type(y.dtype)) # 输出:tensor([2, 2])
    print(y) # 输出:tensor([0, 2])
    cmp = y_hat.type(y.dtype) == y # 先判断逻辑运算符==,再赋值给cmp,cmp为布尔类型的数据
    print(cmp) # 输出tensor([False,  True]) 下面的cmp.type(y.dtype)就是tensor([0, 1])求和就是1
    return float(cmp.type(y.dtype).sum()) # 获得y.dtype的类型作为传入参数,将cmp的类型转为y的类型(int型),然后再求和


# 包含两个整数元素:0和2
# 表示两个样本的真实类别标签。第一个样本的真实类别是0,而第二个样本的真实类别是2。
y = torch.tensor([0, 2])

# y_hat表示两个一维数组,表示是预测的类型'0','1','2'的概率
# 一维数组的标量大小和位置 对应 类型和概率。比如0.1就是类型’0‘ 的概率为0.1,   0.3表示类型’1‘的概率为0.3,    0.6表示类型’2‘的概率为0.6
# [0.1, 0.3, 0.6]第0个样本的预测值,[0.3, 0.2, 0.5]第1个样本的预测值
y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])  # 两个样本在3个类别的预测概率
# print("accuracy(y_hat,y):",accuracy(y_hat,y)) 预测正确的样本数
print("accuracy(y_hat,y) / len(y):", accuracy(y_hat, y) / len(y))

结果:
在这里插入图片描述

正确的类别是0和1
0对应y_hat的第一行的0.1
1对应y_hat的第二行的0.5
然后使用该函数只预测到第二行的0.5,没用预测到第一行的0.1的概率




评估在任意模型net的准确率

import torch
import torchvision
from torch.utils import data
from torchvision import transforms


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)]  # zip函数把两个列表第一个位置元素打包、第二个位置元素打包....

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]


def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 4


def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
    trans = transforms.Compose(trans)

    mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, 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()))  # 测试集通常不shuffle


def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1,keepdim=True)
    return X_exp / partition 


def net(X):
    return softmax(torch.matmul(X.reshape((-1, w.shape[0])), w)+b)


def accuracy(y_hat, y):
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: 
        y_hat = y_hat.argmax(axis=1)
    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] 


if __name__ == '__main__':
    batch_size = 256
    train_iter, test_iter = load_data_fashion_mnist(batch_size) 
    num_inputs = 784
    num_outputs = 10
    w = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
    b = torch.zeros(num_outputs, requires_grad=True)
    print(evaluate_accuracy(net, test_iter))

结果:在这里插入图片描述

详细代码分析:

import torch
import torchvision
from torch.utils import data
from torchvision import transforms


# Accumulator实例中创建了2个变量,用于分别存储正确预测的数量和预测的总数量
class Accumulator:
    """在n个变量上累加"""
    # 初始化一个长度为n的列表,所有元素都是0.0
    # [0, 0] 是一个包含两个元素(都是0)的列表。
    def __init__(self, n):
        self.data = [0, 0] * n

    def add(self, *args):
        #  使用列表推导式和zip函数将self.data中的当前值与args中的对应值相加,并将结果重新赋值给self.data。
        self.data = [a + float(b) for a, b in zip(self.data, args)]  # zip函数把两个列表第一个位置元素打包、第二个位置元素打包....
        # 通过print函数我们可知将猜测正确样本数(即参数accuracy(net(X), y))和总样本数y.numel()分别累加
        print(self.data)

    def reset(self):
        # 将self.data重置为一个新的列表,长度与原来的self.data相同,但所有元素都是0.0。
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        # 使用索引来访问self.data中的元素。
        return self.data[idx]


def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 4


# 加载数据集的函数
# batch_size(用于指定数据加载器加载的批次大小)和 resize(可选参数,用于指定图像在加载前是否需要调整大小,默认为 None,即不进行大小调整)。
def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    # 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
    # 并除以255使得所有像素的数值均在0到1之间
    # 创建一个列表 trans,其中包含一个 transforms.ToTensor() 实例。这个列表将用于构建图像转换的流水线。
    trans = [transforms.ToTensor()]
    # 判断 resize 参数是否有值
    if resize:
        # 如果 resize 有值,则在 trans 列表的开头插入一个 transforms.Resize(resize) 实例。这表示在转换为张量之前,先对图像进行大小调整。
        trans.insert(0, transforms.Resize(resize))
    # 将 trans 列表中的转换 组合成一个流水线,并重新赋值给 trans 变量。
    trans = transforms.Compose(trans)

    mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)

    # 使用 data.DataLoader 创建一个数据加载器,用于加载训练集。参数 batch_size 指定批次大小,shuffle=True 表示在训练时打乱数据顺序,
    # num_workers=get_dataloader_workers() 指定用于数据加载的子进程数
    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()))  # 测试集通常不shuffle


def softmax(X):
    X_exp = torch.exp(X) # 每个都进行指数运算
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition # 这里应用了广播机制


def net(X):
    #需要一个批量大小*输入维数的矩阵,reshape成一个2D的矩阵,-1表示自动计算(其实就是批量大小batch_size=256)而w.shape[0]=784
    #X已经被重新塑形为(batch_size, 784),而w的形状假设为(784, num_classes)(其中num_classes是类别的数量)
    #所以矩阵乘法的结果将是一个(batch_size, num_classes)的矩阵。即:256*10的矩阵
    return softmax(torch.matmul(X.reshape((-1, w.shape[0])), w)+b)


def accuracy(y_hat, y):
    """计算预测正确的数量"""
    # 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # y_hat.shape[1]>1表示不止一个类别,每个类别有各自的概率
        # y_hat.argmax(axis=1)为求每行数值最大的那列(最大的预测概率)的索引号,y_hat是预测分类的类别
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y # 先判断逻辑运算符==,再赋值给cmp,cmp为布尔类型的数据
    return float(cmp.type(y.dtype).sum()) # 获得y.dtype的类型作为传入参数,将cmp的类型转为y的类型(int型),然后再求和


# 可以评估在任意模型net的准确率,
def evaluate_accuracy(net, data_iter):
    """计算在指定数据集上模型的精度"""
    # 如果net模型是torch.nn.Module实现的神经网络的话,将它变成评估模式
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2) # 正确预测数、预测总数,metric为累加器的实例化对象,里面存了两个数
    # 对于迭代器中每次拿出一个X和y
    for X, y in data_iter:
        # 通过net(X)算出评测值,accuracy(net(X), y)计算所有猜测正确的样本数, y.numel()是样本的总数
        print("猜测正确样本数", accuracy(net(X), y)) # 样本数
        print("总样本数", y.numel()) # 总样本数
        # 然后放入Accumulator累加器
        metric.add(accuracy(net(X), y), y.numel()) # net(X)将X输入模型,获得预测值。
    print("对所有的猜测正确样本数/总样本数=它的准确率")
    print(metric[0])
    print(metric[1])
    return metric[0] / metric[1] # 分类正确的样本数 / 总样本数


if __name__ == '__main__':
    batch_size = 256
    train_iter, test_iter = load_data_fashion_mnist(batch_size) # 返回训练集、测试集的迭代器
    # 28×28=784,数据集有10个类别
    num_inputs = 784
    num_outputs = 10
    w = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
    b = torch.zeros(num_outputs, requires_grad=True)
    print(evaluate_accuracy(net, test_iter))



结果:

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

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




Softmax回归的训练

训练函数(完整的数据集通过神经网络一次)

# 训练函数
def train_epoch_ch3(net, train_iter, loss, updater):
    # 如果是nn.Module模型的话,则开始训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 用一个长度为3的迭代器累加我们需要的信息
    metric = Accumulator(3)
    # 扫一遍我们的数据
    for X, y in train_iter:
        y_hat = net(X) # net(X)算出评测值y_hat
        l = loss(y_hat, y) # 通过交叉熵损失函数来算出预测值 y_hat 和实际值 y 之间的误差l
        # 更新模型参数(如果使用优化器,updater 是一个 PyTorch 的优化器实例)
        if isinstance(updater, torch.optim.Optimizer):
            # 先把梯度设成0
            updater.zero_grad()
            l.backward() # 计算梯度
            updater.step() # 对参数进行一次更新
            metric.add(
                # 累加损失、准确率和样本数。
                float(l) * len(y), accuracy(y_hat, y), y.size().numel()
            )
        # 更新模型参数(如果未使用优化器,updater 不是优化器实例)
        else:
            # 对损失 l 进行求和,然后对求和后的损失进行反向传播。将计算损失关于模型参数的梯度,并将这些梯度存储在模型参数的 .grad 属性中。
            l.sum().backward()
            # 传入当前批次的样本数作为参数,手动更新模型参数。
            updater(X.shape[0])
            # 这次直接使用l.sum()(没有乘以len(y)),因为已经进行了求和。
            metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回整个训练周期的平均损失和平均准确率
    return metric[0] / metric[2], metric[1] / metric[2]



定义一个在动画中绘制数据的实用程序类

from IPython import display
from d2l import torch as d2l


class Animator:
    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, ]
        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)




轮次总训练函数

import matplotlib.pyplot as plt
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from IPython import display
from d2l import torch as d2l


# Accumulator实例中创建了2个变量,用于分别存储正确预测的数量和预测的总数量
class Accumulator:
    """在n个变量上累加"""
    # 初始化一个长度为n的列表,所有元素都是0.0
    # [0, 0] 是一个包含两个元素(都是0)的列表。
    def __init__(self, n):
        self.data = [0, 0] * n

    def add(self, *args):
        #  使用列表推导式和zip函数将self.data中的当前值与args中的对应值相加,并将结果重新赋值给self.data。
        self.data = [a + float(b) for a, b in zip(self.data, args)]  # zip函数把两个列表第一个位置元素打包、第二个位置元素打包....
        # 通过print函数我们可知将猜测正确样本数(即参数accuracy(net(X), y))和总样本数y.numel()分别累加
        # print(self.data)

    def reset(self):
        # 将self.data重置为一个新的列表,长度与原来的self.data相同,但所有元素都是0.0。
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        # 使用索引来访问self.data中的元素。
        return self.data[idx]


class Animator:
    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, ]
        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()

        plt.draw()
        plt.pause(0.001)
        display.display(self.fig)
        display.clear_output(wait=True)

    def show(self):
        display.display(self.fig)


def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 4


# 加载数据集的函数
# batch_size(用于指定数据加载器加载的批次大小)和 resize(可选参数,用于指定图像在加载前是否需要调整大小,默认为 None,即不进行大小调整)。
def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    # 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
    # 并除以255使得所有像素的数值均在0到1之间
    # 创建一个列表 trans,其中包含一个 transforms.ToTensor() 实例。这个列表将用于构建图像转换的流水线。
    trans = [transforms.ToTensor()]
    # 判断 resize 参数是否有值
    if resize:
        # 如果 resize 有值,则在 trans 列表的开头插入一个 transforms.Resize(resize) 实例。这表示在转换为张量之前,先对图像进行大小调整。
        trans.insert(0, transforms.Resize(resize))
    # 将 trans 列表中的转换 组合成一个流水线,并重新赋值给 trans 变量。
    trans = transforms.Compose(trans)

    mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)

    # 使用 data.DataLoader 创建一个数据加载器,用于加载训练集。参数 batch_size 指定批次大小,shuffle=True 表示在训练时打乱数据顺序,
    # num_workers=get_dataloader_workers() 指定用于数据加载的子进程数
    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()))  # 测试集通常不shuffle


def softmax(X):
    X_exp = torch.exp(X) # 每个都进行指数运算
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition # 这里应用了广播机制


def cross_entropy(y_hat, y):
    # range(len(y_hat))每一行拿出一个0到n的向量
    # 其中range(len(y_hat))是生成0和1,也就是只有两个样本,在这两个样本中查找真实标号的预测值
    # range(y_hat)是所有行,y是真实值所对应的列
    return -torch.log(y_hat[range(len(y_hat)), y]) # y_hat[range(len(y_hat)),y]为把y的标号列表对应的值拿出来。传入的y要是最大概率的标号


def net(X):
    #需要一个批量大小*输入维数的矩阵,reshape成一个2D的矩阵,-1表示自动计算(其实就是批量大小batch_size=256)而w.shape[0]=784
    #X已经被重新塑形为(batch_size, 784),而w的形状假设为(784, num_classes)(其中num_classes是类别的数量)
    #所以矩阵乘法的结果将是一个(batch_size, num_classes)的矩阵。即:256*10的矩阵
    return softmax(torch.matmul(X.reshape((-1, w.shape[0])), w)+b)


def accuracy(y_hat, y):
    """计算预测正确的数量"""
    # 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # y_hat.shape[1]>1表示不止一个类别,每个类别有各自的概率
        # y_hat.argmax(axis=1)为求每行数值最大的那列(最大的预测概率)的索引号,y_hat是预测分类的类别
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y # 先判断逻辑运算符==,再赋值给cmp,cmp为布尔类型的数据
    return float(cmp.type(y.dtype).sum()) # 获得y.dtype的类型作为传入参数,将cmp的类型转为y的类型(int型),然后再求和


# 可以评估在任意模型net的准确率,
def evaluate_accuracy(net, data_iter):
    """计算在指定数据集上模型的精度"""
    # 如果net模型是torch.nn.Module实现的神经网络的话,将它变成评估模式
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2) # 正确预测数、预测总数,metric为累加器的实例化对象,里面存了两个数
    # 对于迭代器中每次拿出一个X和y
    for X, y in data_iter:
        # 通过net(X)算出评测值,accuracy(net(X), y)计算所有猜测正确的样本数, y.numel()是样本的总数
        # print("猜测正确样本数", accuracy(net(X), y)) # 样本数
        # print("总样本数", y.numel()) # 总样本数
        # 然后放入Accumulator累加器
        metric.add(accuracy(net(X), y), y.numel()) # net(X)将X输入模型,获得预测值。
    # print("对所有的猜测正确样本数/总样本数=它的准确率")
    # print(metric[0])
    # print(metric[1])
    return metric[0] / metric[1] # 分类正确的样本数 / 总样本数


# 训练函数(epoch是指一个完整的数据集通过神经网络一次)
def train_epoch_ch3(net, train_iter, loss, updater):
    # 如果是nn.Module模型的话,则开始训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 用一个长度为3的迭代器累加我们需要的信息
    metric = Accumulator(3)
    # 扫一遍我们的数据
    for X, y in train_iter:
        y_hat = net(X) # net(X)算出评测值y_hat
        l = loss(y_hat, y) # 通过交叉熵损失函数来算出预测值 y_hat 和实际值 y 之间的误差l
        # 更新模型参数(如果使用优化器,updater 是一个 PyTorch 的优化器实例)
        if isinstance(updater, torch.optim.Optimizer):
            # 先把梯度设成0
            updater.zero_grad()
            l.backward() # 计算梯度
            updater.step() # 对参数进行一次更新
            metric.add(
                # 累加损失、准确率和样本数。
                float(l) * len(y), accuracy(y_hat, y), y.size().numel()
            )
        # 更新模型参数(如果未使用优化器,updater 不是优化器实例)
        else:
            # 对损失 l 进行求和,然后对求和后的损失进行反向传播。将计算损失关于模型参数的梯度,并将这些梯度存储在模型参数的 .grad 属性中。
            l.sum().backward()
            # 传入当前批次的样本数作为参数,手动更新模型参数。
            updater(X.shape[0])
            # 这次直接使用l.sum()(没有乘以len(y)),因为已经进行了求和。
            metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回整个训练周期的平均损失和平均准确率
    return metric[0] / metric[2], metric[1] / metric[2]


# 总训练函数
# net: 神经网络模型、train_iter: 训练数据集迭代器、test_iter: 测试数据集迭代器、loss: 损失函数、num_epochs: 训练的轮数(即整个数据集被遍历的次数)、updater: 一个用于更新模型参数的函数
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    # 使用 Animator 工具来绘制训练过程中的指标,设置了x轴标签为“epoch”,x轴的范围从1到num_epochs,y轴的范围从0.3到0.9
    # 曲线分别有训练损失、训练正确率和测试正确率。
    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):  # 变量num_epochs遍数据
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater) # 返回两个值,一个总损失、一个总正确率
        # 在测试数据集上评估模型的精度。这个函数只返回一个值:仅返回测试集上的总正确率。
        test_acc = evaluate_accuracy(net, test_iter)
        # 因为通常我们从第1个epoch开始计数,而不是从第0个,所以这里加了1
        # train_metrics+(test_acc,):这不是将两个正确率相加,而是将训练指标(损失和正确率)的元组与测试正确率合并成一个新的元组。
        animator.add(epoch+1, train_metrics+(test_acc,)) # train_metrics+(test_acc,) 仅将两个值的正确率相加,
    train_loss, train_acc = train_metrics
    animator.show()


lr = 0.1


def updater(batch_size):
    # 调用sgd函数来更新参数w和b
    return d2l.sgd([w, b], lr, batch_size)


if __name__ == '__main__':
    batch_size = 256
    train_iter, test_iter = load_data_fashion_mnist(batch_size) # 返回训练集、测试集的迭代器
    # 28×28=784,数据集有10个类别
    num_inputs = 784
    num_outputs = 10
    w = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
    b = torch.zeros(num_outputs, requires_grad=True)
    # print(evaluate_accuracy(net, test_iter))

    num_epochs = 10
    train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
    # d2l.plt.pause(0)
    d2l.plt.show()

结果:

在这里插入图片描述




对图像进行分类预测

import matplotlib.pyplot as plt
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from IPython import display
from d2l import torch as d2l


# Accumulator实例中创建了2个变量,用于分别存储正确预测的数量和预测的总数量
class Accumulator:
    """在n个变量上累加"""
    # 初始化一个长度为n的列表,所有元素都是0.0
    # [0, 0] 是一个包含两个元素(都是0)的列表。
    def __init__(self, n):
        self.data = [0, 0] * n

    def add(self, *args):
        #  使用列表推导式和zip函数将self.data中的当前值与args中的对应值相加,并将结果重新赋值给self.data。
        self.data = [a + float(b) for a, b in zip(self.data, args)]  # zip函数把两个列表第一个位置元素打包、第二个位置元素打包....
        # 通过print函数我们可知将猜测正确样本数(即参数accuracy(net(X), y))和总样本数y.numel()分别累加
        # print(self.data)

    def reset(self):
        # 将self.data重置为一个新的列表,长度与原来的self.data相同,但所有元素都是0.0。
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        # 使用索引来访问self.data中的元素。
        return self.data[idx]


class Animator:
    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, ]
        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()

        plt.draw()
        plt.pause(0.001)
        display.display(self.fig)
        display.clear_output(wait=True)

    def show(self):
        display.display(self.fig)


def get_dataloader_workers():
    """使用4个进程来读取的数据"""
    return 4


# 加载数据集的函数
# batch_size(用于指定数据加载器加载的批次大小)和 resize(可选参数,用于指定图像在加载前是否需要调整大小,默认为 None,即不进行大小调整)。
def load_data_fashion_mnist(batch_size, resize=None):
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    # 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式
    # 并除以255使得所有像素的数值均在0到1之间
    # 创建一个列表 trans,其中包含一个 transforms.ToTensor() 实例。这个列表将用于构建图像转换的流水线。
    trans = [transforms.ToTensor()]
    # 判断 resize 参数是否有值
    if resize:
        # 如果 resize 有值,则在 trans 列表的开头插入一个 transforms.Resize(resize) 实例。这表示在转换为张量之前,先对图像进行大小调整。
        trans.insert(0, transforms.Resize(resize))
    # 将 trans 列表中的转换 组合成一个流水线,并重新赋值给 trans 变量。
    trans = transforms.Compose(trans)

    mnist_train = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(root="../data", train=False, transform=trans, download=True)

    # 使用 data.DataLoader 创建一个数据加载器,用于加载训练集。参数 batch_size 指定批次大小,shuffle=True 表示在训练时打乱数据顺序,
    # num_workers=get_dataloader_workers() 指定用于数据加载的子进程数
    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()))  # 测试集通常不shuffle


def softmax(X):
    X_exp = torch.exp(X) # 每个都进行指数运算
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition # 这里应用了广播机制


def cross_entropy(y_hat, y):
    # range(len(y_hat))每一行拿出一个0到n的向量
    # 其中range(len(y_hat))是生成0和1,也就是只有两个样本,在这两个样本中查找真实标号的预测值
    # range(y_hat)是所有行,y是真实值所对应的列
    return -torch.log(y_hat[range(len(y_hat)), y]) # y_hat[range(len(y_hat)),y]为把y的标号列表对应的值拿出来。传入的y要是最大概率的标号


def net(X):
    #需要一个批量大小*输入维数的矩阵,reshape成一个2D的矩阵,-1表示自动计算(其实就是批量大小batch_size=256)而w.shape[0]=784
    #X已经被重新塑形为(batch_size, 784),而w的形状假设为(784, num_classes)(其中num_classes是类别的数量)
    #所以矩阵乘法的结果将是一个(batch_size, num_classes)的矩阵。即:256*10的矩阵
    return softmax(torch.matmul(X.reshape((-1, w.shape[0])), w)+b)


def accuracy(y_hat, y):
    """计算预测正确的数量"""
    # 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # y_hat.shape[1]>1表示不止一个类别,每个类别有各自的概率
        # y_hat.argmax(axis=1)为求每行数值最大的那列(最大的预测概率)的索引号,y_hat是预测分类的类别
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y # 先判断逻辑运算符==,再赋值给cmp,cmp为布尔类型的数据
    return float(cmp.type(y.dtype).sum()) # 获得y.dtype的类型作为传入参数,将cmp的类型转为y的类型(int型),然后再求和


# 可以评估在任意模型net的准确率,
def evaluate_accuracy(net, data_iter):
    """计算在指定数据集上模型的精度"""
    # 如果net模型是torch.nn.Module实现的神经网络的话,将它变成评估模式
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2) # 正确预测数、预测总数,metric为累加器的实例化对象,里面存了两个数
    # 对于迭代器中每次拿出一个X和y
    for X, y in data_iter:
        # 通过net(X)算出评测值,accuracy(net(X), y)计算所有猜测正确的样本数, y.numel()是样本的总数
        # print("猜测正确样本数", accuracy(net(X), y)) # 样本数
        # print("总样本数", y.numel()) # 总样本数
        # 然后放入Accumulator累加器
        metric.add(accuracy(net(X), y), y.numel()) # net(X)将X输入模型,获得预测值。
    # print("对所有的猜测正确样本数/总样本数=它的准确率")
    # print(metric[0])
    # print(metric[1])
    return metric[0] / metric[1] # 分类正确的样本数 / 总样本数


# 训练函数(epoch是指一个完整的数据集通过神经网络一次)
def train_epoch_ch3(net, train_iter, loss, updater):
    # 如果是nn.Module模型的话,则开始训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 用一个长度为3的迭代器累加我们需要的信息
    metric = Accumulator(3)
    # 扫一遍我们的数据
    for X, y in train_iter:
        y_hat = net(X) # net(X)算出评测值y_hat
        l = loss(y_hat, y) # 通过交叉熵损失函数来算出预测值 y_hat 和实际值 y 之间的误差l
        # 更新模型参数(如果使用优化器,updater 是一个 PyTorch 的优化器实例)
        if isinstance(updater, torch.optim.Optimizer):
            # 先把梯度设成0
            updater.zero_grad()
            l.backward() # 计算梯度
            updater.step() # 对参数进行一次更新
            metric.add(
                # 累加损失、准确率和样本数。
                float(l) * len(y), accuracy(y_hat, y), y.size().numel()
            )
        # 更新模型参数(如果未使用优化器,updater 不是优化器实例)
        else:
            # 对损失 l 进行求和,然后对求和后的损失进行反向传播。将计算损失关于模型参数的梯度,并将这些梯度存储在模型参数的 .grad 属性中。
            l.sum().backward()
            # 传入当前批次的样本数作为参数,手动更新模型参数。
            updater(X.shape[0])
            # 这次直接使用l.sum()(没有乘以len(y)),因为已经进行了求和。
            metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回整个训练周期的平均损失和平均准确率
    return metric[0] / metric[2], metric[1] / metric[2]


# 总训练函数
# net: 神经网络模型、train_iter: 训练数据集迭代器、test_iter: 测试数据集迭代器、loss: 损失函数、num_epochs: 训练的轮数(即整个数据集被遍历的次数)、updater: 一个用于更新模型参数的函数
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    # 使用 Animator 工具来绘制训练过程中的指标,设置了x轴标签为“epoch”,x轴的范围从1到num_epochs,y轴的范围从0.3到0.9
    # 曲线分别有训练损失、训练正确率和测试正确率。
    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):  # 变量num_epochs遍数据
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater) # 返回两个值,一个总损失、一个总正确率
        # 在测试数据集上评估模型的精度。这个函数只返回一个值:仅返回测试集上的总正确率。
        test_acc = evaluate_accuracy(net, test_iter)
        # 因为通常我们从第1个epoch开始计数,而不是从第0个,所以这里加了1
        # train_metrics+(test_acc,):这不是将两个正确率相加,而是将训练指标(损失和正确率)的元组与测试正确率合并成一个新的元组。
        animator.add(epoch+1, train_metrics+(test_acc,)) # train_metrics+(test_acc,) 仅将两个值的正确率相加,
    train_loss, train_acc = train_metrics
    animator.show()


def predict_ch3(net, test_iter, n=6):
    # net: 神经网络模型
    # test_iter: 测试数据集迭代器
    # n: 需要显示的图像数量,默认为6
    """预测标签"""
    # 取一次数据就结束,因为有break
    for X, y in test_iter:
        break
    # 获取真实的标签
    trues = d2l.get_fashion_mnist_labels(y)
    # 使用神经网络模型net对取出的X进行预测,net(X) 返回的是每个类别的得分,使用argmax(axis=1)找到得分最高的类别索引,即预测的标签
    preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))
    # 创建一个titles列表,其中每个元素都是一个字符串,包含真实的标签和预测的标签
    # 使用列表推导式和zip函数将trues和preds中的元素配对并格式化字符串
    # true 和 pred是从 trues 和 preds 列表中提取的元素
    # zip函数将两个列表元素配对。例如,如果 trues = [1, 2, 3] 且 preds = [0, 2, 1],那么 zip(trues, preds) 将返回 [(1, 0), (2, 2), (3, 1)]。
    titles = [true + '\n' + pred for true, pred in zip(trues, preds)]
    # # 使用d2l.show_images函数显示前n个图像,并将对应的标题设置为上面创建的titles列表中的前n个元素
    # X[0:n]取出的是前n个图像的数据,但需要先reshape为(n, 28, 28)的形式,
    # 因为原始数据可能是(batch_size, 28*28)的形式
    d2l.show_images(X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])
    # X形状可能是 (batch_size, 28*28),其中 batch_size 是批处理中的图像数量,28*28 是单个图像的展平后的像素数量。
    # 从输入数据 X 中选取前 n 个图像的数据,重新塑形为 (n, 28, 28) 的形状,然后水平排列成一行显示这些图像,并为每个图像附上一个标题(这些标题来自 titles 列表的前 n 个元素)。


lr = 0.1


def updater(batch_size):
    # 调用sgd函数来更新参数w和b
    return d2l.sgd([w, b], lr, batch_size)


if __name__ == '__main__':
    batch_size = 256
    train_iter, test_iter = load_data_fashion_mnist(batch_size) # 返回训练集、测试集的迭代器
    # 28×28=784,数据集有10个类别
    num_inputs = 784
    num_outputs = 10
    w = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
    b = torch.zeros(num_outputs, requires_grad=True)
    # print(evaluate_accuracy(net, test_iter))

    num_epochs = 10
    # 必须加上,要么测试predict_ch3无法正确对应
    train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
    # d2l.plt.pause(0)
    predict_ch3(net, test_iter)
    d2l.plt.tight_layout()  # 调整子图参数,使之填充整个图像区域
    d2l.plt.show()

结果:

在这里插入图片描述




Softmax回归的简洁实现

import torch
import matplotlib.pyplot as plt
from torch import nn
from d2l import torch as d2l
from IPython import display


# Accumulator实例中创建了2个变量,用于分别存储正确预测的数量和预测的总数量
class Accumulator:
    """在n个变量上累加"""
    # 初始化一个长度为n的列表,所有元素都是0.0
    # [0, 0] 是一个包含两个元素(都是0)的列表。
    def __init__(self, n):
        self.data = [0, 0] * n

    def add(self, *args):
        #  使用列表推导式和zip函数将self.data中的当前值与args中的对应值相加,并将结果重新赋值给self.data。
        self.data = [a + float(b) for a, b in zip(self.data, args)]  # zip函数把两个列表第一个位置元素打包、第二个位置元素打包....
        # 通过print函数我们可知将猜测正确样本数(即参数accuracy(net(X), y))和总样本数y.numel()分别累加
        # print(self.data)

    def reset(self):
        # 将self.data重置为一个新的列表,长度与原来的self.data相同,但所有元素都是0.0。
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        # 使用索引来访问self.data中的元素。
        return self.data[idx]


class Animator:
    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, ]
        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()

        plt.draw()
        plt.pause(0.001)
        display.display(self.fig)
        display.clear_output(wait=True)

    def show(self):
        display.display(self.fig)


def accuracy(y_hat, y):
    """计算预测正确的数量"""
    # 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # y_hat.shape[1]>1表示不止一个类别,每个类别有各自的概率
        # y_hat.argmax(axis=1)为求每行数值最大的那列(最大的预测概率)的索引号,y_hat是预测分类的类别
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y # 先判断逻辑运算符==,再赋值给cmp,cmp为布尔类型的数据
    return float(cmp.type(y.dtype).sum()) # 获得y.dtype的类型作为传入参数,将cmp的类型转为y的类型(int型),然后再求和


# 可以评估在任意模型net的准确率,
def evaluate_accuracy(net, data_iter):
    """计算在指定数据集上模型的精度"""
    # 如果net模型是torch.nn.Module实现的神经网络的话,将它变成评估模式
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2) # 正确预测数、预测总数,metric为累加器的实例化对象,里面存了两个数
    # 对于迭代器中每次拿出一个X和y
    for X, y in data_iter:
        # 通过net(X)算出评测值,accuracy(net(X), y)计算所有猜测正确的样本数, y.numel()是样本的总数
        # print("猜测正确样本数", accuracy(net(X), y)) # 样本数
        # print("总样本数", y.numel()) # 总样本数
        # 然后放入Accumulator累加器
        metric.add(accuracy(net(X), y), y.numel()) # net(X)将X输入模型,获得预测值。
    # print("对所有的猜测正确样本数/总样本数=它的准确率")
    # print(metric[0])
    # print(metric[1])
    return metric[0] / metric[1] # 分类正确的样本数 / 总样本数


# 训练函数(epoch是指一个完整的数据集通过神经网络一次)
def train_epoch_ch3(net, train_iter, loss, updater):
    # 如果是nn.Module模型的话,则开始训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 用一个长度为3的迭代器累加我们需要的信息
    metric = Accumulator(3)
    # 扫一遍我们的数据
    for X, y in train_iter:
        y_hat = net(X) # net(X)算出评测值y_hat
        l = loss(y_hat, y) # 通过交叉熵损失函数来算出预测值 y_hat 和实际值 y 之间的误差l
        # 更新模型参数(如果使用优化器,updater 是一个 PyTorch 的优化器实例)
        if isinstance(updater, torch.optim.Optimizer):
            # 先把梯度设成0
            updater.zero_grad()
            l.backward() # 计算梯度
            updater.step() # 对参数进行一次更新
            metric.add(
                # 累加损失、准确率和样本数。
                float(l) * len(y), accuracy(y_hat, y), y.size().numel()
            )
        # 更新模型参数(如果未使用优化器,updater 不是优化器实例)
        else:
            # 对损失 l 进行求和,然后对求和后的损失进行反向传播。将计算损失关于模型参数的梯度,并将这些梯度存储在模型参数的 .grad 属性中。
            l.sum().backward()
            # 传入当前批次的样本数作为参数,手动更新模型参数。
            updater(X.shape[0])
            # 这次直接使用l.sum()(没有乘以len(y)),因为已经进行了求和。
            metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回整个训练周期的平均损失和平均准确率
    return metric[0] / metric[2], metric[1] / metric[2]


# 总训练函数
# net: 神经网络模型、train_iter: 训练数据集迭代器、test_iter: 测试数据集迭代器、loss: 损失函数、num_epochs: 训练的轮数(即整个数据集被遍历的次数)、updater: 一个用于更新模型参数的函数
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    # 使用 Animator 工具来绘制训练过程中的指标,设置了x轴标签为“epoch”,x轴的范围从1到num_epochs,y轴的范围从0.3到0.9
    # 曲线分别有训练损失、训练正确率和测试正确率。
    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):  # 变量num_epochs遍数据
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater) # 返回两个值,一个总损失、一个总正确率
        # 在测试数据集上评估模型的精度。这个函数只返回一个值:仅返回测试集上的总正确率。
        test_acc = evaluate_accuracy(net, test_iter)
        # 因为通常我们从第1个epoch开始计数,而不是从第0个,所以这里加了1
        # train_metrics+(test_acc,):这不是将两个正确率相加,而是将训练指标(损失和正确率)的元组与测试正确率合并成一个新的元组。
        animator.add(epoch+1, train_metrics+(test_acc,)) # train_metrics+(test_acc,) 仅将两个值的正确率相加,
    train_loss, train_acc = train_metrics
    animator.show()


# 初始化神经网络中线性层权重
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01) # 均值为0,标准差为0.01来初始化权重


batch_size = 256
# train_iter:是一个迭代器,用于遍历训练集中的数据。在每次迭代中,它会返回一个小批量的训练样本和对应的标签。
# test_iter:也是一个迭代器,它用于遍历测试集中的数据。在每次迭代中,它同样会返回一个小批量的测试样本和对应的标签。
# batch_size 是一个参数,它指定了每个小批量中应包含的样本数量。
# 例如,如果 batch_size 是 64,那么 train_iter 每次迭代都会返回 64 个训练样本和对应的 64 个标签。
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

# PyTorch不会隐式地调整输入的形状。
# 因此,我们定义了展平层(flatten)在线性层前调整网络输入的形状
# nn.Flatten() 是一个将多维张量展平为一维张量的模块。
# 假设其形状为 [batch_size, channels, height, width]则转换为 [batch_size, channels * height * width]
# 以便可以将其传递给线性层。
# 在Fashion-MNIST数据集中,每个图像都是 28x28 像素的灰度图(没有颜色通道,所以 channels = 1)。
# 因此,一个图像的形状是 [1, 28, 28]经过 nn.Flatten() 后,其形状变为 [784](因为 28*28=784)。
# nn.Linear(784, 10) 接收一个形状为 [784] 的输入(即展平后的图像),并输出一个形状为 [10] 的向量。
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))
# 用于将某个函数(这里是 init_weights)应用到神经网络 net 中的所有模块上。
net.apply(init_weights)
# 在交叉熵损失函数中传递未归一化的预测,并同时计算softmax及其对数,使得在训练分类模型时不需要在最后一层之后显式地添加 softmax 层。
loss = nn.CrossEntropyLoss()
# 使用学习率为0.1的小批量随机梯度下降作为优化算法
# torch.optim.SGD 是一个优化器
# 在PyTorch中,模型的参数(如线性层的权重和偏置)通常存储在 nn.Parameter 对象中,这些对象在模型被实例化时自动注册到模型的 parameters() 方法中。
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
# 调用之前定义的训练函数来训练模型
num_epochs = 10
train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
d2l.plt.show()

结果:
在这里插入图片描述




问题

1、softlabe训练策略、以及为什么有效。
一位有效表示一个标号,n类的话表示成一个很长的向量,只有正确那一类为1,剩下的所有变成0,然后用一个softmax去逼近这个纯0、1的分布,它的问题是用了指数的话很难用指数逼近1,因为指数变成1的话,你想要它完全变成1的话,你的输出必须几乎接近无穷大,而剩下的都很小,挺难用softmax逼近0和1的极端的数值。所以提出一个方案是说如果是正确的那一类计为0.9,剩下那些不正确的类就是0.1÷ 1 n \frac 1n n1,这就是softlabel。这样的好处是说使得你用softmax真的去完全拟合0.9和那些很小的数的时候是有可能的。

2、softmax回归和logistic回归分析是一样的吗?如果不一样的话,哪些地方不同?
可以认为是一样的,

3、为什么用交叉熵,不用相对熵等其他基于信息量的度量?
相对熵表示的是一个两个概率之间的区别,它比交叉熵的好处是说它是一个对称的关系。不用的原因是不好算。

4、ylog y ^ \hat{y} y^我们为什么只关心正确类,不关心不正确类呢,如果关心不正确类效果有没有可能更好呢?
其实,我们不是不关心不正确的类,是因为y_hat的编码把剩下类的概率变成了0,导致计算的时候可以忽略掉不正确的类,如果我们使用softlable,不正确的类也是有存在非0的概率的情况下,我们确实会关心不正确的类。

5、这样的n分类,对每一个类别来说,是不是可以认为只有1个正类,n-1个负类吗?会不会类别不平衡呢?
会有这样的情况,但相对来说,好处是可以看到损失函数,如果是0、1编码的话,并不关心别的类会怎么样,只关心当前类。其实不用关心类别平不平衡,要关心的是是不是存在一些类,这些类有没有足够多的样本。

6、似然函数是怎么得出来的?有什么参考意义?其他次优解是不是似然值也很高呢?
最小化的损失等价于最大化的一个似然函数,似然函数是指一个模型,在给定数据的情况下,这个所谓的模型就是我的权重,出现的概率有多大。

7、DaTALOADER()的num_workers是并行了吗?
是的,取决于你的实现,pytorch是用进程来实现的。

8、Pytorch训练好模型,测试的时候发现无论batchsize设为1还是更多,测试的总时间都差不多,但正常理解如果设为4不应该是设为1的4倍速度吗?
不是的,不管batch_size是多少,计算量是不会发生变化的,唯一发生变化的是并行度是不是能增加,执行的效率能不能增加。

9、为什么不在accuracy函数中把除以len(y)做完呢?
因为读一个batch的时候,最后那个batch可能读不满,会导致不正确。

10、在计算精度的时候,为什么需要使用new.eval()将模型设置成评估模式?
不设也没有关系,是一个好的习惯,设成eval模式的话是默认不计算梯度。

11、w、b怎么从模型中抽出来,放进updater的?
在trainer = torch.optim.SGD(net.parameters(), lr=0.1)中,net.parameters()是将模型的所有w b参数都放入到updater里面了。

12、在多次迭代之后如果测试精度出现上升后再下降是过拟合了吗?可以提前终止吗?
一直在下降,很可能过拟合了。有其他微调可以避免这些事情。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1689100.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端请求超时截断,axios timeout设置未生效情况记录

问题描述 前端请求超时截断&#xff0c;axios timeout设置未生效情况记录 timeout设置方式&#xff1a; 表现&#xff08;前端超过5min报错500&#xff0c;直接访问接口超过5min能够正常响应&#xff09;&#xff1a; 问题原因 上面的配置设置时间为1000min&#xff0c;明显…

Servlet的response对象

目录 HTTP响应报文协议 reponse继承体系 reponse的方法 响应行 public void setStatus(int sc) 响应头 public void setHeader(String name, String value) 响应体 public java.io.PrintWriter getWriter() public ServletOutputStream getOutputStream() 请求重定…

P2P服务端模型配合 Tool.net P2pServerAsync 类使用

Tool.Net 支持的 P2P 服务器模型实例 说明服务器部分相关代码相关调用实例Tcp版本Udp版本 最后附一张思维图 说明 当前文章&#xff0c;仅是Tool.Net 开源库的一个缩影。本次更新V5.0版本以上提供支持。可以提供简单实现P2P功能用于业务开发。 服务器部分相关代码 完整代码&…

基于Docker部署GitLab环境搭建

文件在D:\E\学习文档子目录压缩\专项进阶&#xff0c;如ngnix,webservice,linux,redis等\docker 建议虚拟机内存2G以上 1.下载镜像文件 docker pull beginor/gitlab-ce:11.0.1-ce.0 注意&#xff1a;一定要配置阿里云的加速镜像 创建GitLab 的配置 (etc) 、 日志 (log) 、数…

MQTT 异常断开(一)

分析问题总结&#xff1a; 前提&#xff1a;MQTT是基于TCP层再次封装&#xff0c;MQTT是不关心TCP层的实现与传输&#xff0c;但是如果TCP链路出现异常&#xff08;丢失TCP ACK&#xff0c;网络延时TCP ACK等&#xff09;一定会导致MQTT断开连接。 MQTT代理服务器存在如下问题&…

服务器没有图形界面没有显示器怎么办

可以用vnc。 vnc是开元的。什么是vnc&#xff1f; 使用vnc 下载vnc和vncserver命令。 每生成一个图形界面就叫做开启session会话。 vnc相关命令&#xff1a; start a new session: vncserver。 如果没有会话&#xff0c;一般从:1开始 端口5901 vncserver :2 #指定会话为:2 端…

如何让社区版IDEA变得好用

如何让社区版IDEA变得好用 背景 收费版的idea功能非常强大&#xff0c;但是费用高。社区版的免费&#xff0c;但是功能被阉割了。如何才能让社区版Idea变得好用&#xff0c;就需要各种插件支持了。经过全局配置编码&#xff0c;maven&#xff0c;jdk版本&#xff0c;在加上各…

MSI U盘重装系统

MSI U盘重装系统 1. 准备一块U盘 首先需要将U盘格式化&#xff0c;这个格式化并不是在文件管理中将U盘里面的所有东西都删干净就可以了&#xff0c;需要在磁盘管理中&#xff0c;将这块U盘格式化&#xff0c;如果这块U盘有分区的话&#xff0c;那将所有的分区都格式化并且删除…

大模型最新黑书:大模型应用解决方案: 基于GPT-3、ChatGPT、GPT-4等Transformer架构的自然语言处理 PDF

今天给大家推荐一本丹尼斯罗斯曼(Denis Rothman)编写的关于大语言模型&#xff08;LLM&#xff09;权威教程<<大模型应用解决方案> 基于GPT-3、ChatGPT、GPT-4等Transformer架构的自然语言处理>&#xff01;Google工程总监Antonio Gulli作序&#xff0c;这含金量不…

开源软件 | 一文彻底搞懂许可证的定义、起源、分类及八大主流许可证,让你选型不再头疼

为什么开源软件会存在许可证&#xff0c;许可证的起源与产生目的是为了解决什么问题&#xff1f;许可证的定义又是怎样的&#xff1f;什么是Copyleft&#xff0c;与Copyright有何区别&#xff1f;开源软件常见的许可证有哪些&#xff1f;这些许可证都有什么特点&#xff1f;接下…

RAC11G删除节点

删除节点步骤&#xff1a;删除实例、删除 DB 软件、删除 GI 软件 删除节点发生的场景 1、被删除节点一切保留&#xff0c;需要从RAC中剔除&#xff0c;例如因为要更换服务器。 2、被删除节点关于RAC的部分文件丢失&#xff0c;如GI、库软件误删除&#xff0c;需要重新安装GI…

英语学习笔记21+23——Which book?/Which glasses?

Which book?/Which glasses? 哪本书&#xff1f;/哪些杯子&#xff1f; 词汇 Vocabulary give v. 给 搭配&#xff1a;Give me five! 击掌庆祝 用法&#xff1a;give 人 东西     give 东西 to 人    把……东西给某人 例句&#xff1a;把这些苹果给 Bobby.   …

【高频】从输入URL到页面展示到底发生了什么?

一、相关衍生面试问题&#xff1a; 浏览器输入美团网站&#xff0c;从回车到浏览器展示经历了哪些过程 &#xff1f; http输入网页之后的流程&#xff1f; 百度搜索页面&#xff0c;从点开搜索框&#xff0c;到显示搜索页面经历了什么&#xff1f; 二、探究各个过程&#x…

起底震网病毒的来龙去脉

2010年&#xff0c;震网病毒被发现&#xff0c;引起世界哗然&#xff0c;在后续的10年间&#xff0c;陆陆续续有更多关于该病毒的背景和细节曝光。今年&#xff0c;《以色列时报》和《荷兰日报》又披露了关于此事件的更多信息&#xff0c;基于这些信息&#xff0c;我们重新梳理…

【数据结构】第七节:堆

个人主页&#xff1a; 深情秋刀鱼-CSDN博客 数据结构专栏&#xff1a;数据结构与算法 源码获取&#xff1a;数据结构: 上传我写的关于数据结构的代码 (gitee.com) ​ 目录 一、堆 1.堆的概念 2.堆的定义 二、堆的实现 1.初始化和销毁 2.插入 向上调整算法 3.删除 向下调整算法…

短剧系统源码解析与应用

在数字化时代&#xff0c;短剧作为一种新兴的娱乐形式&#xff0c;因其内容紧凑、节奏快速而受到广大年轻群体的喜爱。短剧系统源码的开发和应用&#xff0c;不仅为创作者提供了一个展示才华的平台&#xff0c;也为观众带来了全新的观看体验。本文将对短剧系统源码进行解析&…

get和post的区别,二者是幂等的吗?

一、什么是幂等 所谓幂等性通俗的将就是一次请求和多次请求同一个资源产生相同的副作用。 维基百科定义&#xff1a;幂等&#xff08;idempotent、idempotence&#xff09;是一个数学与计算机学概念&#xff0c;常见于抽象代数中。 在编程中一个幂等操作的特点是其任意多次执…

win32-鼠标消息、键盘消息、计时器消息、菜单资源

承接前文&#xff1a; win32窗口编程windows 开发基础win32-注册窗口类、创建窗口win32-显示窗口、消息循环、消息队列 本文目录 键盘消息键盘消息的分类WM_CHAR 字符消息 鼠标消息鼠标消息附带信息 定时器消息 WM_TIMER创建销毁定时器 菜单资源资源相关菜单资源使用命令消息的…

人类交互2 听觉处理和语言中枢

人类听觉概述 人类听觉是指通过耳朵接收声音并将其转化为神经信号&#xff0c;从而使我们能够感知和理解声音信息的能力。听觉是人类五种感觉之一&#xff0c;对我们的日常生活和交流至关重要。 听觉是人类交流和沟通的重要工具。通过听觉&#xff0c;我们能够听到他人的语言…

jwtcracker下载安装出现错误

1.jwtcracker 用于爆破jwt秘钥 2.下载 ubuntu/kali安装c-jwt-cracker及使用方法-CSDN博客 参考这个大佬写的 但是我在这里出现了这个问题 显示Cannot initialize the default message digest sha256, aborting 我实在找不出来哪里有问题&#xff0c;所以直接换成docker …