记录softmax
向量backward需要指定维度:x torch.tensor([1.0, 2.0, 3.0], requires_gradTrue) y x * 2 # y [2.0, 4.0, 6.0] # ✅ 向量需要指定梯度参数 y.backward(torch.tensor([1.0, 1.0, 1.0])) print(fx的梯度: {x.grad}) # 输出: tensor([2., 2., 2.])读取数据集:# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式 # 并除以255使得所有像素的数值均在01之间 # 创建数据转换管道将PIL图像转换为PyTorch张量 # 这个转换会自动将像素值从[0, 255]范围缩放到[0.0, 1.0]范围 # 同时将图像维度从(H, W, C)调整为(C, H, W)格式符合PyTorch的输入要求 trans transforms.ToTensor() # 下载并加载Fashion-MNIST训练数据集 # root../data: 指定数据集存储的根目录为上级目录中的data文件夹 # trainTrue: 表示加载训练集共60,000张图片 # transformtrans: 对每张图片应用上面定义的转换 # downloadTrue: 如果数据集不存在则自动从互联网下载 mnist_train torchvision.datasets.FashionMNIST( root../data, trainTrue, transformtrans, downloadTrue) # 下载并加载Fashion-MNIST测试数据集 # trainFalse: 表示加载测试集共10,000张图片 # 其他参数含义与训练集相同 mnist_test torchvision.datasets.FashionMNIST( root../data, trainFalse, transformtrans, downloadTrue)例子:创建迭代器dataloader# 创建DataLoader设置批次大小为18 loader data.DataLoader(mnist_train, batch_size18) # iter(loader) 创建迭代器 # next() 获取第一个批次的数据 X, y next(iter(loader))dataloader迭代机制:重要# 查看DataLoader的内部实现 train_loader DataLoader(mnist_train, batch_size4, shuffleTrue) print(DataLoader的迭代机制:) print(*50) # 方法1: 使用for循环最常用 print(方法1: 使用for循环内部会自动调用iter) count 0 for batch_idx, (images, labels) in enumerate(train_loader): print(f 批次 {batch_idx}: images.shape{images.shape}) count 1 if count 2: # 只显示前2个批次 break print() # 方法2: 手动使用iter()和next() print(方法2: 手动使用iter()和next()) train_iter iter(train_loader) # 创建迭代器 batch1 next(train_iter) # 获取第一个批次 batch2 next(train_iter) # 获取第二个批次 print(f 批次1: images.shape{batch1[0].shape}) print(f 批次2: images.shape{batch2[0].shape}) print() # 方法3: 使用iter()和next()的组合 print(方法3: 使用iter()和next()的组合一次性) # 这是你代码中的写法 single_batch next(iter(train_loader)) print(f 单个批次: images.shape{single_batch[0].shape})Python 迭代器和可迭代对象的区别# 简单示例 numbers [1, 2, 3, 4, 5] print(列表是可迭代对象:) print(f hasattr(numbers, __iter__): {hasattr(numbers, __iter__)}) print(f hasattr(numbers, __next__): {hasattr(numbers, __next__)}) print() # 但列表不是迭代器 try: next(numbers) except TypeError as e: print(f直接对列表使用next()会报错: {e}) print() # 需要先用iter()创建迭代器 numbers_iter iter(numbers) print(迭代器:) print(f numbers_iter类型: {type(numbers_iter)}) print(f hasattr(numbers_iter, __next__): {hasattr(numbers_iter, __next__)}) print() # 现在可以使用next() print(使用迭代器:) print(f next(numbers_iter): {next(numbers_iter)}) print(f next(numbers_iter): {next(numbers_iter)}) print(f next(numbers_iter): {next(numbers_iter)})总结必须使用iter()DataLoader是可迭代对象但不是迭代器。必须先用iter()将其转换为迭代器然后才能用next()获取数据。next(iter(loader))的工作原理iter(loader)创建一个新的迭代器next(...)从该迭代器获取第一个批次这个组合每次都会创建新的迭代器获取第一批数据遍历数据集的正确方式使用for batch in loader:循环或创建一个迭代器iterator iter(loader)然后用next(iterator)手动控制你的代码中的写法是合理的当你只想快速查看一个批次的数据时next(iter(loader))是最简洁的写法。所以记住一定要先用iter()创建迭代器然后才能用next()获取数据enumerate# 2.1 普通for循环 print(普通for循环) for item in [a, b, c]: print(f item{item}) # 这里item也不需要预先定义 # 2.2 enumerate自动创建计数器 print(\nenumerate的工作原理) my_list [x, y, z] # enumerate返回一个迭代器每次迭代返回(索引, 值) enumerator enumerate(my_list) print(f enumerate(my_list)类型: {type(enumerator)}) # 查看enumerate返回的内容 for pair in enumerator: print(f enumerate返回: {pair} (类型: {type(pair)}))高级用法:# 6.1 自定义起始索引 print(enumerate自定义起始索引) for batch_idx, (images, labels) in enumerate(loader, start1): print(f 批次{batch_idx}: 标签{labels.tolist()}) if batch_idx 2: break print() # 6.2 同时获取多个元素 print(同时解包多个元素) data_list [(a, 1), (b, 2), (c, 3)] for idx, (letter, number) in enumerate(data_list): print(f idx{idx}, letter{letter}, number{number}) print() # 6.3 在DataLoader中的嵌套循环 print(DataLoader嵌套循环示例) for epoch in range(2): # 2个epoch print(fEpoch {epoch}:) for batch_idx, (images, labels) in enumerate(loader): if batch_idx 2: # 每个epoch只显示前2个批次 print(f 批次{batch_idx}: 标签{labels.tolist()}) print()softmax实现# PyTorch不会隐式地调整输入的形状。因此 # 我们在线性层前定义了展平层flatten来调整网络输入的形状 # 定义一个简单的神经网络模型 # nn.Sequential 是一个顺序容器模块将按照它们在构造函数中传入的顺序被添加到计算图中 net nn.Sequential( nn.Flatten(), # 展平层将多维输入张量展平为一维 nn.Linear(784, 10) # 全连接层输入784个特征输出10个类别 ) # 定义权重初始化函数 def init_weights(m): 初始化网络权重 参数: m: 网络中的一个模块 功能: 如果是线性层(nn.Linear)则使用正态分布初始化其权重 均值为0标准差为0.01 if type(m) nn.Linear: # 检查模块类型是否为线性层 # 使用正态分布初始化权重 # nn.init.normal_ 是PyTorch的原地初始化函数 # 参数: # tensor: 要初始化的张量这里是线性层的权重 # mean: 均值默认为0 # std: 标准差这里设置为0.01 nn.init.normal_(m.weight, std0.01) # 注意这里没有初始化偏置(bias)因为PyTorch默认偏置初始化为0 # 如果需要初始化偏置可以添加nn.init.constant_(m.bias, 0) # 应用权重初始化函数到网络的每一层 # net.apply() 会递归地对网络中的每个模块应用指定的函数 net.apply(init_weights)训练# 定义损失函数 # nn.CrossEntropyLoss 是交叉熵损失函数常用于多分类问题 # reductionnone 表示不进行降维操作返回每个样本的损失值 # 如果不设置reduction默认是mean即返回批次损失的平均值 loss nn.CrossEntropyLoss(reductionnone) # 定义优化器 # torch.optim.SGD 是随机梯度下降优化器 # 参数: # net.parameters(): 获取网络中所有可训练参数 # lr0.1: 学习率设置为0.1 trainer torch.optim.SGD(net.parameters(), lr0.1) # 设置训练轮数 num_epochs 10 # 调用训练函数 # d2l.train_ch3 是d2l库中封装好的训练函数 # 参数: # net: 神经网络模型 # train_iter: 训练数据迭代器 # test_iter: 测试数据迭代器 # loss: 损失函数 # num_epochs: 训练轮数 # trainer: 优化器 d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): #save 训练模型定义见第3章 参数: net: 神经网络模型 train_iter: 训练数据迭代器 test_iter: 测试数据迭代器 loss: 损失函数 num_epochs: 训练轮数 updater: 优化器 # 创建动画器用于可视化训练过程 # xlabel: x轴标签为epoch # xlim: x轴范围从1到num_epochs # ylim: y轴范围从0.3到0.9预期准确率范围 # legend: 图例显示三条曲线训练损失、训练准确率、测试准确率 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): # 训练一个epoch返回训练损失和训练准确率 train_metrics train_epoch_ch3(net, train_iter, loss, updater) # 在测试集上评估准确率 test_acc evaluate_accuracy(net, test_iter) # 将当前epoch的指标添加到动画器中 # 注意train_metrics是一个元组通常包含(train_loss, train_acc) # 所以 train_metrics (test_acc,) 创建包含三个值的元组 animator.add(epoch 1, train_metrics (test_acc,)) # 获取最后一个epoch的训练指标 train_loss, train_acc train_metrics # 断言确保训练结果在合理范围内 # 1. 训练损失应该小于0.5 assert train_loss 0.5, train_loss # 2. 训练准确率应该在0.7到1.0之间 assert train_acc 1 and train_acc 0.7, train_acc # 3. 测试准确率应该在0.7到1.0之间 assert test_acc 1 and test_acc 0.7, test_acctrain封装的函数太多了需要自己看原文件(softmax-regression-scratch)