目录
鄙人生医转码,道行浅薄,请多谅解~
感觉这章的内容超量,代码和详解都非常长,细嚼慢咽ing~
首先导入需要的库和上一章讲的训练和测试集MNIST(相比于原码我多加了一个库后面用)
import torch
import matplotlib.pyplot as plt
from IPython import display
from d2l import torch as d2l
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
一、初始化模型参数
num_inputs = 784#输入一个拉长的向量
num_outputs = 10#模型输出维度为10
#拉长向量会损失很多空间信息,这就是卷积神经网络要考虑的事情了
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)#定义权重
b = torch.zeros(num_outputs, requires_grad=True)#偏移长度同理
这一步和线性回归操作一样,但是我们需要明确w,b矩阵的大小关系,不然容易搞混,比如说w为了在和x相乘后得出的数据与分类模型的类别数相同,我们要其列数为10也就是output
二、定义softmax操作
#仅是回顾一下沿着特定维度求和的矩阵操作
#X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
#X.sum(0, keepdim=True), X.sum(1, keepdim=True)
回顾后我们来写softmax的代码实现:
def softmax(X):
X_exp = torch.exp(X)#输入x中每个元素的指数函数
partition = X_exp.sum(1, keepdim=True)#关键,计算出每个样本的所有类别分数指数和
#参数 keepdim=True 表示在加和操作后保持维度,结果仍然是二维张量,这在后面的广播机制中起作用
return X_exp / partition # 这里应用了广播机制
#由于 partition 是 [n, 1] 形状的张量,而 X_exp 是 [n, m] 形状的张量(n 是样本数量,m 是类别数量)
#广播机制会自动将 partition 扩展为 [n, m] 的形状,使得每行的每个元素都被对应的 partition 值除
#我感觉这里有一个比较容易搞混的地方?
#就是为什么sum函数的维度是1而不是0是因为类别分数的归一化是针对每个样本单独进行的
#而不是将所有样本的类别分数一起归一化
这里也再次强调了输出矩阵的格式,我感觉我第一次看李沐的时候没有太关注代码和这些具体实现,二刷的时候就很懵,我现在浅薄的想法就是,输入的样本数作为行数,然后和w相乘后以类别数作为列数。
三、定义模型
定义模型就很简单了直接导入上面的softmax操作:
def net(X):
return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
#reshape 是为了确保输入的形状与权重矩阵 W 的形状兼容,从而进行正确的矩阵运算
这里确保了输入的形状兼容性,但我寻思之前我们写w的正态分布不是已经规定好了吗?不懂
四、定义损失函数
需要先理解一个格式:
#利用y作为y_hat概率中的索引
#y = torch.tensor([0, 2])
#y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
#y_hat[[0, 1], y]
然后再写交叉熵损失函数:
#实现交叉熵损失函数
def cross_entropy(y_hat, y):
return - torch.log(y_hat[range(len(y_hat)), y])
#y_hat 是模型的输出概率分布,通常是经过 Softmax 处理后的张量,形状为 (num_samples, num_classes)
#y 是真实标签,通常是一个包含每个样本对应类别索引的一维张量,形状为 (num_samples,)
#这行代码通过索引选择 y_hat 中每个样本对应类别的预测概率。例如,如果 y 中的某个值是 2,