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

目录

  • 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 d2ld2l.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 d2ldef 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 d2ldef 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 transformsdef 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 d2ldef 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:continueprint(f'{timer.stop():.2f}sec')

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




softmax回归的从零实现

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

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

import torchnum_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 torchdef 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 torchdef 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 torchdef 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 torchdef 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 torchdef 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) == yreturn 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 torchdef accuracy(y_hat, y):"""计算预测正确的数量"""# 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1if 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])求和就是1return 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 transformsclass Accumulator:"""在n个变量上累加"""def __init__(self, n):self.data = [0, 0] * ndef 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 4def 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()))  # 测试集通常不shuffledef 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 = 256train_iter, test_iter = load_data_fashion_mnist(batch_size) num_inputs = 784num_outputs = 10w = 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] * ndef 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()))  # 测试集通常不shuffledef 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):"""计算预测正确的数量"""# 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1if 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和yfor 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 = 256train_iter, test_iter = load_data_fashion_mnist(batch_size) # 返回训练集、测试集的迭代器# 28×28=784,数据集有10个类别num_inputs = 784num_outputs = 10w = 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_hatl = loss(y_hat, y) # 通过交叉熵损失函数来算出预测值 y_hat 和实际值 y 之间的误差l# 更新模型参数(如果使用优化器,updater 是一个 PyTorch 的优化器实例)if isinstance(updater, torch.optim.Optimizer):# 先把梯度设成0updater.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 d2lclass 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, fmtsdef add(self, x, y):if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif 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] * ndef 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, fmtsdef add(self, x, y):if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif 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()))  # 测试集通常不shuffledef 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):"""计算预测正确的数量"""# 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1if 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和yfor 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_hatl = loss(y_hat, y) # 通过交叉熵损失函数来算出预测值 y_hat 和实际值 y 之间的误差l# 更新模型参数(如果使用优化器,updater 是一个 PyTorch 的优化器实例)if isinstance(updater, torch.optim.Optimizer):# 先把梯度设成0updater.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_metricsanimator.show()lr = 0.1def updater(batch_size):# 调用sgd函数来更新参数w和breturn d2l.sgd([w, b], lr, batch_size)if __name__ == '__main__':batch_size = 256train_iter, test_iter = load_data_fashion_mnist(batch_size) # 返回训练集、测试集的迭代器# 28×28=784,数据集有10个类别num_inputs = 784num_outputs = 10w = 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 = 10train_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] * ndef 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, fmtsdef add(self, x, y):if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif 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()))  # 测试集通常不shuffledef 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):"""计算预测正确的数量"""# 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1if 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和yfor 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_hatl = loss(y_hat, y) # 通过交叉熵损失函数来算出预测值 y_hat 和实际值 y 之间的误差l# 更新模型参数(如果使用优化器,updater 是一个 PyTorch 的优化器实例)if isinstance(updater, torch.optim.Optimizer):# 先把梯度设成0updater.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_metricsanimator.show()def predict_ch3(net, test_iter, n=6):# net: 神经网络模型# test_iter: 测试数据集迭代器# n: 需要显示的图像数量,默认为6"""预测标签"""# 取一次数据就结束,因为有breakfor 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.1def updater(batch_size):# 调用sgd函数来更新参数w和breturn d2l.sgd([w, b], lr, batch_size)if __name__ == '__main__':batch_size = 256train_iter, test_iter = load_data_fashion_mnist(batch_size) # 返回训练集、测试集的迭代器# 28×28=784,数据集有10个类别num_inputs = 784num_outputs = 10w = 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] * ndef 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, fmtsdef add(self, x, y):if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif 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):"""计算预测正确的数量"""# 第一个判断张量是否大于一维,第二个是判断张量的第二个维度是否大于1if 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和yfor 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_hatl = loss(y_hat, y) # 通过交叉熵损失函数来算出预测值 y_hat 和实际值 y 之间的误差l# 更新模型参数(如果使用优化器,updater 是一个 PyTorch 的优化器实例)if isinstance(updater, torch.optim.Optimizer):# 先把梯度设成0updater.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_metricsanimator.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.mzph.cn/diannao/14514.shtml

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

相关文章

RabbitMQ---交换机-Fanout-Direct

Publisher&#xff1a;生产者&#xff0c;不再发送消息到队列中&#xff0c;而是发给交换机Exchange&#xff1a;交换机&#xff0c;一方面&#xff0c;接收生产者发送的消息。另一方面&#xff0c;知道如何处理消息&#xff0c;例如递交给某个特别队列、递交给所有队列、或是将…

如何一键生成多个文本二维码?excel表格批量生码的方法

现在很多人会将文本信息做成二维码来展示&#xff0c;当有同类型内容生成大量二维码时&#xff0c;可以使用将文本导入excel表格的方式&#xff0c;将表格中的每条数据批量生成二维码&#xff0c;可以有效提升二维码制作的速度和效率。下面就让小编来将具体的操作步骤分享给大家…

二叉树顺序结构及链式结构

一.二叉树的顺序结构 1.定义&#xff1a;使用数组存储数据&#xff0c;一般使用数组只适合表示完全二叉树&#xff0c;此时不会有空间的浪费 注&#xff1a;二叉树的顺序存储在逻辑上是一颗二叉树&#xff0c;但是在物理上是一个数组&#xff0c;此时需要程序员自己想清楚调整…

手动安装maven依赖到本地仓库

使用mvn install命令安装jar包到指定的仓库。 命令如下&#xff1a; mvn install:install-file -Dmaven.repo.localC:\Users\liyong.m2\repository -DgroupIdcom.aspose -DartifactIdwords -Dversion18.4 -Dpackagingjar -DfileC:\Users\liyong\Desktop\jar\words-18.4.jar 解释…

grafana + Prometheus + node-exporter + pushgateway + alertmanager的监控解决方案

业内比较著名的监控解决方案&#xff0c;据笔者所知&#xff0c;大概是三套&#xff1a; 一个是zabbix的解决方案&#xff0c;一个是prometheusgrafana&#xff0c;一个是ELK zabbix比较重&#xff0c;而且原生支持监控SNMP&#xff0c;自带一个仪表盘&#xff0c;不需要额外…

docker redis 持久化

1、拉取redis镜像 docker pull redis:latest 2、 mkdir /data/redis 3、填充redis.conf文件及根据需求修改相应的配置 •通过官网地址找到对应版本的配置文件 •将配置信息复制到redis.conf中 •常见的修改配置 https://redis.io/docs/latest/operate/oss_and_stack/managem…

高仿果汁导航模板

参考原文&#xff1a;果汁导航风格模板_1234FCOM专注游戏工具及源码例子分享 极速云

基于springboot的毕业设计系统的开发源码

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的毕业设计系统的开发。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 毕业设计系统能够实现…

学习通高分免费刷课实操教程

文章目录 概要整体架构流程详细步骤云上全平台登录步骤小结 概要 我之前提到过一个通过浏览器的三个脚本就可以免费高分刷课的文章&#xff0c;由于不方便拍视频进行实操演示&#xff0c;然后写下了这个实操教程&#xff0c;之前的三个脚本划到文章末尾 整体架构流程 整体大…

窗口函数 | rows between …… and ……

ROWS BETWEEN ... AND ... 是 SQL 窗口函数中的一个子句&#xff0c;用于定义窗口函数操作的行范围。窗口函数允许用户对一组相关的记录执行计算&#xff0c;这些记录被称为窗口。 基本语法 <窗口函数> OVER ( [PARTITION BY <列名>] ORDER BY <列名> [AS…

华为云之Zabbix监控平台部署实践

华为云之Zabbix监控平台部署实践 一、本次实践介绍1.1 实践环境简介1.3 本次实践完成目标 二、 相关服务介绍2.1 华为云ECS云服务器介绍2.2 Zabbix介绍 三、环境准备工作3.1 预置实验环境3.2 查看预置环境信息 四、登录华为云4.1 登录华为云4.2 查看ECS状态4.3 连接ECS弹性云服…

力扣HOT100 - 287. 寻找重复数

解题思路&#xff1a; 快慢指针 第一步&#xff0c;慢指针每次移动一步&#xff0c;快指针每次移动两步&#xff0c;直到它们相遇。这一步保证了它们在环中相遇。 接下来&#xff0c;将其中一个指针&#xff08;快指针或慢指针&#xff09;重置到起点&#xff08;即数组的第一…

SpringBoot实现邮箱验证码

自行创建一个SpringBoot项目 导入SpringBoot所需要的邮箱验证码的包 <!--邮件发送--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId><version>2.6.1</version>…

前后端部署笔记

windows版&#xff1a; 如果傻呗公司让用win电脑部署&#xff0c;类似于我们使用笔记本做局域网服务器&#xff0c;社内使用。 1.安装win版的nginx、mysql、node、jdk等 2.nginx开机自启参考Nginx配置及开机自启动&#xff08;Windows环境&#xff09;_nginx开机自启动 wind…

UPPAAL使用方法

UPPAAL使用方法 由于刚开始学习时间自动机及其使用方法&#xff0c;对UPPAAL使用不太熟悉&#xff0c;网上能找到的教程很少&#xff0c;摸索了很久终于成功实现一个小例子&#xff0c;所以记录一下详细教程。 这里用到的例子参考【UPPAAL学习笔记】1&#xff1a;基本使用示例…

专业级润滑油,一站式批发服务

要为机械设备提供持久稳定的动力保障吗&#xff1f;选择我们的专业级润滑油&#xff0c;让您的设备运转更顺畅&#xff0c;效率更高。 我们专业从事润滑油批发多年&#xff0c;以优质的产品、合理的价格和完善的服务赢得了广大客户的信赖。无论是汽车、机械还是工业设备&#x…

【Vue3】env环境变量的配置和使用(区分cli和vite)

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 文章目录 前言一、env文件二、vue3cli加载env1..env配置2..dev配置&#xff08;其他环境参考&#xff09;3.package.json文件4.使用 三、vue3vite加载e…

【html5】03-新表单元素及属性

目录 1 引言 2 智能表单控件-type 3 表单属性 form input 5 答疑--解决required自定义提示信息 1 引言 HTML5引入了一系列新的表单输入类型&#xff0c;如email、url、number、range、date、time、datetime-local、month、week、search、color和tel等。这些新类型增强了表…

FFmpeg源码:bytestream_get_byte函数解析

一、引言 FFmpeg源码中经常使用到bytestream_get_byte这个函数&#xff0c;比如使用FFmpeg对BMP图片进行解析&#xff0c;其源码会调用函数bmp_decode_frame&#xff0c;而该函数内部会通过bytestream_get_byte读取BMP 的header。本文讲解函数bytestream_get_byte的作用和内部…

Spark SQL 中DataFrame DSL的使用

在上一篇文章中已经大致说明了DataFrame APi,下面我们具体介绍DataFrame DSL的使用。DataFrame DSL是一种命令式编写Spark SQL的方式&#xff0c;使用的是一种类sql的风格语法。 文章链接&#xff1a; 一、单词统计案例引入 import org.apache.spark.sql.{DataFrame, SaveMod…