做网站备案是承诺书是啥,手机网站根目录,网络科技公司网站源码,广州网站优化运营目录 Softmax回归回归 VS 分类Kaggle上的分类问题 从回归到多类分类回归分类从回归到多类分类-均方损失从回归到多类分类-无校验比例从回归到多类分类-校验比例 Softmax和交叉熵损失总结损失函数均方损失绝对值损失函数鲁棒损失 图像分类数据集通过框架中内置函数将FashionMNIS… 目录 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 loss、L1 loss、Huber’s Robust loss
均方损失 ① 蓝色曲线为当y0时变换预测值y所获得的曲线。 ② 绿色曲线为当y0时变换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, trainTrue, transformtrans, downloadTrue)
mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)
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, titlesNone, scale1.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, figsizefigsize)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, trainTrue, transformtrans, downloadTrue)
mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)
X, y next(iter(data.DataLoader(mnist_train, batch_size18))) # Xy 为仅抽取一次的18个样本的图片、以及对应的标签值
#show_images用来显示一批图像。.reshape(18, 28, 28) 意味着 X 被重新塑形为一个包含 18 个图像的数据集每个图像的大小是 28x28 像素。
#2, 9: 这两个参数指定了如何在一个网格中布局显示的图像。在一个 2 行 9 列的网格中显示图像。因此总共会显示 2x918 个图像与 X.reshape(18, 28, 28) 中的图像数量相匹配。
show_images(X.reshape(18, 28, 28), 2, 9, titlesget_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, titlesNone, scale1.5):Plot a list of images.figsize (num_cols * scale, num_rows * scale) # 传进来的图像尺寸scale 为放缩比例因子_, axes d2l.plt.subplots(num_rows, num_cols, figsizefigsize)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, trainTrue, transformtrans, downloadTrue)
mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)
X, y next(iter(data.DataLoader(mnist_train, batch_size18))) # Xy 为仅抽取一次的18个样本的图片、以及对应的标签值
show_images(X.reshape(18, 28, 28), 2, 9, titlesget_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, titlesNone, scale1.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, figsizefigsize)#将二维数组转换为一维数组。可以更容易地遍历所有的子图而不需要考虑它们的原始二维布局。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, trainTrue, transformtrans, downloadTrue)
mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)
# batch_size用于图像显示
X, y next(iter(data.DataLoader(mnist_train, batch_size18))) # Xy 为仅抽取一次的18个样本的图片、以及对应的标签值
# show_images用来显示一批图像。.reshape(18, 28, 28) 意味着 X 被重新塑形为一个包含 18 个图像的数据集每个图像的大小是 28x28 像素。
# 2, 9: 这两个参数指定了如何在一个网格中布局显示的图像。在一个 2 行 9 列的网格中显示图像。因此总共会显示 2x918 个图像与 X.reshape(18, 28, 28) 中的图像数量相匹配。
show_images(X.reshape(18, 28, 28), 2, 9, titlesget_fashion_mnist_labels(y))batch_size 256 #用于模型训练
train_iter data.DataLoader(mnist_train, batch_size, shuffleTrue, num_workersget_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, resizeNone):下载Fashion-MNIST数据集然后将其加载到内存中trans [transforms.ToTensor()]if resize:trans.insert(0, transforms.Resize(resize))trans transforms.Compose(trans)mnist_train torchvision.datasets.FashionMNIST(root../data, trainTrue, transformtrans, downloadTrue)mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)return (data.DataLoader(mnist_train, batch_size, shuffleTrue, num_workersget_dataloader_workers()),data.DataLoader(mnist_test, batch_size, shuffleFalse, num_workersget_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×28784所以softmax回归的输入是一个784的向量
② 因为数据集有10个类别所以网络输出维度为10.
import torchnum_inputs 784
num_outputs 10
w torch.normal(0, 0.01, size(num_inputs, num_outputs), requires_gradTrue)
b torch.zeros(num_outputs, requires_gradTrue)
print(w.shape)
print(b.shape)
① 给定一个矩阵X可以对所有元素求和。
import torch# 给定一个矩阵X2*3的矩阵我们可以按照维度进行元素求和
X torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
#X.sum(0, keepdimTrue)其中0是按照第一维度行求和X.sum(1, keepdimTrue)按照第二维度列求和
#keepdimTrue保持是二维矩阵这个参数用于指定在归并操作后是否保持原始张量的维度。
print(X.sum(0, keepdimTrue), X.sum(1, keepdimTrue))
print(------------------分开查看看的更明白------------------)
print(X.sum(0, keepdimTrue))
print(X.sum(1, keepdimTrue))结果 ②实现softmax import torchdef softmax(X):X_exp torch.exp(X) # 每个都进行指数运算partition X_exp.sum(1, keepdimTrue)#按照第二维度列方向求和就是对一行求和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(axis1) 按行求和概率和为1结果
详细分析代码
import torchdef softmax(X):X_exp torch.exp(X) # 每个都进行指数运算print(X_exp.shape) #(batch_size, num_classes)partition X_exp.sum(1, keepdimTrue)#按照第二维度列方向求和就是对一行求和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(axis1) 按行求和概率和为1结果 实现softmax回归模型
import torchdef softmax(X):X_exp torch.exp(X) # 每个都进行指数运算partition X_exp.sum(1,keepdimTrue)return X_exp / partition # 这里应用了广播机制def net(X):#需要一个批量大小*输入维数的矩阵reshape成一个2D的矩阵-1表示自动计算其实就是批量大小batch_size256而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_gradTrue)
b torch.zeros(num_outputs, requires_gradTrue)
# 生成模拟输入数据
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),atol1e-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 yi1因为只有当it的时 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}[行号][列号] y−hat[行号][列号])
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.30262.3026 -ln0.5≈--0.69310.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(axis1)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(axis1)为求每行数值最大的那列最大的预测概率的索引号y_hat是预测分类的类别y_hat y_hat.argmax(axis1)# 输出查看一下是否正确不出意外索引都是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 # 先判断逻辑运算符再赋值给cmpcmp为布尔类型的数据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, resizeNone):下载Fashion-MNIST数据集然后将其加载到内存中trans [transforms.ToTensor()]if resize:trans.insert(0, transforms.Resize(resize))trans transforms.Compose(trans)mnist_train torchvision.datasets.FashionMNIST(root../data, trainTrue, transformtrans, downloadTrue)mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)return (data.DataLoader(mnist_train, batch_size, shuffleTrue, num_workersget_dataloader_workers()),data.DataLoader(mnist_test, batch_size, shuffleFalse, num_workersget_dataloader_workers())) # 测试集通常不shuffledef softmax(X):X_exp torch.exp(X)partition X_exp.sum(1,keepdimTrue)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(axis1)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_gradTrue)b torch.zeros(num_outputs, requires_gradTrue)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, resizeNone):下载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, trainTrue, transformtrans, downloadTrue)mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)# 使用 data.DataLoader 创建一个数据加载器用于加载训练集。参数 batch_size 指定批次大小shuffleTrue 表示在训练时打乱数据顺序# num_workersget_dataloader_workers() 指定用于数据加载的子进程数return (data.DataLoader(mnist_train, batch_size, shuffleTrue, num_workersget_dataloader_workers()),data.DataLoader(mnist_test, batch_size, shuffleFalse, num_workersget_dataloader_workers())) # 测试集通常不shuffledef softmax(X):X_exp torch.exp(X) # 每个都进行指数运算partition X_exp.sum(1, keepdimTrue)return X_exp / partition # 这里应用了广播机制def net(X):#需要一个批量大小*输入维数的矩阵reshape成一个2D的矩阵-1表示自动计算其实就是批量大小batch_size256而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(axis1)为求每行数值最大的那列最大的预测概率的索引号y_hat是预测分类的类别y_hat y_hat.argmax(axis1)cmp y_hat.type(y.dtype) y # 先判断逻辑运算符再赋值给cmpcmp为布尔类型的数据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×28784,数据集有10个类别num_inputs 784num_outputs 10w torch.normal(0, 0.01, size(num_inputs, num_outputs), requires_gradTrue)b torch.zeros(num_outputs, requires_gradTrue)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, xlabelNone, ylabelNone, legendNone, xlimNone,ylimNone, xscalelinear, yscalelinear,fmts(-, m--, g-., r:), nrows1, ncols1,figsize(3.5, 2.5)):if legend is None:legend []d2l.use_svg_display()self.fig, self.axes d2l.plt.subplots(nrows, ncols, figsizefigsize)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(waitTrue) 轮次总训练函数
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, xlabelNone, ylabelNone, legendNone, xlimNone,ylimNone, xscalelinear, yscalelinear,fmts(-, m--, g-., r:), nrows1, ncols1,figsize(3.5, 2.5)):if legend is None:legend []d2l.use_svg_display()self.fig, self.axes d2l.plt.subplots(nrows, ncols, figsizefigsize)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(waitTrue)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, resizeNone):下载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, trainTrue, transformtrans, downloadTrue)mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)# 使用 data.DataLoader 创建一个数据加载器用于加载训练集。参数 batch_size 指定批次大小shuffleTrue 表示在训练时打乱数据顺序# num_workersget_dataloader_workers() 指定用于数据加载的子进程数return (data.DataLoader(mnist_train, batch_size, shuffleTrue, num_workersget_dataloader_workers()),data.DataLoader(mnist_test, batch_size, shuffleFalse, num_workersget_dataloader_workers())) # 测试集通常不shuffledef softmax(X):X_exp torch.exp(X) # 每个都进行指数运算partition X_exp.sum(1, keepdimTrue)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_size256而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(axis1)为求每行数值最大的那列最大的预测概率的索引号y_hat是预测分类的类别y_hat y_hat.argmax(axis1)cmp y_hat.type(y.dtype) y # 先判断逻辑运算符再赋值给cmpcmp为布尔类型的数据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_epochsy轴的范围从0.3到0.9# 曲线分别有训练损失、训练正确率和测试正确率。animator Animator(xlabelepoch, 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(epoch1, 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×28784,数据集有10个类别num_inputs 784num_outputs 10w torch.normal(0, 0.01, size(num_inputs, num_outputs), requires_gradTrue)b torch.zeros(num_outputs, requires_gradTrue)# 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, xlabelNone, ylabelNone, legendNone, xlimNone,ylimNone, xscalelinear, yscalelinear,fmts(-, m--, g-., r:), nrows1, ncols1,figsize(3.5, 2.5)):if legend is None:legend []d2l.use_svg_display()self.fig, self.axes d2l.plt.subplots(nrows, ncols, figsizefigsize)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(waitTrue)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, resizeNone):下载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, trainTrue, transformtrans, downloadTrue)mnist_test torchvision.datasets.FashionMNIST(root../data, trainFalse, transformtrans, downloadTrue)# 使用 data.DataLoader 创建一个数据加载器用于加载训练集。参数 batch_size 指定批次大小shuffleTrue 表示在训练时打乱数据顺序# num_workersget_dataloader_workers() 指定用于数据加载的子进程数return (data.DataLoader(mnist_train, batch_size, shuffleTrue, num_workersget_dataloader_workers()),data.DataLoader(mnist_test, batch_size, shuffleFalse, num_workersget_dataloader_workers())) # 测试集通常不shuffledef softmax(X):X_exp torch.exp(X) # 每个都进行指数运算partition X_exp.sum(1, keepdimTrue)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_size256而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(axis1)为求每行数值最大的那列最大的预测概率的索引号y_hat是预测分类的类别y_hat y_hat.argmax(axis1)cmp y_hat.type(y.dtype) y # 先判断逻辑运算符再赋值给cmpcmp为布尔类型的数据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_epochsy轴的范围从0.3到0.9# 曲线分别有训练损失、训练正确率和测试正确率。animator Animator(xlabelepoch, 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(epoch1, train_metrics(test_acc,)) # train_metrics(test_acc,) 仅将两个值的正确率相加train_loss, train_acc train_metricsanimator.show()def predict_ch3(net, test_iter, n6):# net: 神经网络模型# test_iter: 测试数据集迭代器# n: 需要显示的图像数量默认为6预测标签# 取一次数据就结束因为有breakfor X, y in test_iter:break# 获取真实的标签trues d2l.get_fashion_mnist_labels(y)# 使用神经网络模型net对取出的X进行预测net(X) 返回的是每个类别的得分使用argmax(axis1)找到得分最高的类别索引即预测的标签preds d2l.get_fashion_mnist_labels(net(X).argmax(axis1))# 创建一个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, titlestitles[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×28784,数据集有10个类别num_inputs 784num_outputs 10w torch.normal(0, 0.01, size(num_inputs, num_outputs), requires_gradTrue)b torch.zeros(num_outputs, requires_gradTrue)# 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, xlabelNone, ylabelNone, legendNone, xlimNone,ylimNone, xscalelinear, yscalelinear,fmts(-, m--, g-., r:), nrows1, ncols1,figsize(3.5, 2.5)):if legend is None:legend []d2l.use_svg_display()self.fig, self.axes d2l.plt.subplots(nrows, ncols, figsizefigsize)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(waitTrue)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(axis1)为求每行数值最大的那列最大的预测概率的索引号y_hat是预测分类的类别y_hat y_hat.argmax(axis1)cmp y_hat.type(y.dtype) y # 先判断逻辑运算符再赋值给cmpcmp为布尔类型的数据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_epochsy轴的范围从0.3到0.9# 曲线分别有训练损失、训练正确率和测试正确率。animator Animator(xlabelepoch, 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(epoch1, 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, std0.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*28784。
# 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(), lr0.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(), lr0.1)中net.parameters()是将模型的所有w b参数都放入到updater里面了。
12、在多次迭代之后如果测试精度出现上升后再下降是过拟合了吗可以提前终止吗 一直在下降很可能过拟合了。有其他微调可以避免这些事情。