1、torch.max
correct = 0
total = 0
for xb,yb in valid_dl:outputs = model(xb)_,predicted = torch.max(outputs.data,1)total += yb.size(0)  #yb.size(0) 返回的是张量 yb 在第 0 维的大小,也就是 yb 中的样本数量。correct += (predicted == yb).sum().item()  
print('正确率:%d %%'%(100 * correct /total))
def accuracy(predictions, labels):# predictions.data 是模型的预测结果# torch.max(predictions.data, 1) 返回每一行的最大值和对应的索引。 # [1] 表示我们只关心索引,即选择模型预测类别的索引。pred = torch.max(predictions.data, 1)[1]   # 取出每一行的最大值索引,得到预测类别# labels.data.view_as(pred) 将 labels 调整为与 pred 形状一致,方便比较# eq() 函数逐元素比较 pred 和 labels 的值,如果相等返回 True,不相等返回 Falserights = pred.eq(labels.data.view_as(pred)).sum()  # 统计预测正确的样本数'''pred.eq(labels.data.view_as(pred)):pred 是模型预测的类别(索引形式)。
labels.data.view_as(pred) 将标签(真实类别)转换为与 pred 相同的形状(通常是一个一维向量),这样就可以逐元素比较。
eq() 是 torch 中的一个方法,用于比较两个张量的元素,返回一个布尔张量,元素相等则返回 True,否则返回 False。'''# 返回正确的数量以及总样本数return rights, len(labels)
torch.max(input, dim):
- 输入:一个张量
input,以及一个维度dim。- 输出:返回一个元组 (values, indices):
values:沿着dim维度的最大值。
indices:最大值对应的索引。
torch.max(outputs.data, 1)与torch.max(outputs.data, 0)
如果
outputs的形状是[784, 10],这意味着你有 784 个样本,每个样本对应 10 个类的输出(比如 logits 或预测值)。在这种情况下,torch.max(outputs.data, 1)和torch.max(outputs.data, 0)的结果含义会有所不同,具体如下:1.
torch.max(outputs.data, 1)
维度:
dim=1
操作:沿着第二维(每个样本的类别)查找最大值。
输出:
- 返回一个张量,其中包含每个样本的最大值(即 logits 的最高值),以及一个索引张量,表示该最大值对应的类别。
- 输出形状为
[784]和[784](索引),表示每个样本的预测类别。
示例:
import torch# 假设 outputs 是 [784, 10] 的张量 outputs = torch.randn(784, 10) # 随机生成示例输出max_values, predicted = torch.max(outputs.data, 1) # 查找每个样本的最大值及其对应的索引。outputs.data[i][0]---outputs.data[i][9] print(predicted) # 输出:每个样本的预测类别(索引)2.
torch.max(outputs.data, 0)
维度:
dim=0
操作:沿着第一维(所有样本)查找每个类别的最大值。
输出:
- 返回一个张量,包含每个类别在所有样本中的最大值,以及对应的索引。
- 输出形状为
[10](最大值)和[784](索引),表示每个类别在 784 个样本中的最大值。
示例:
import torch# 假设 outputs 是 [784, 10] 的张量 outputs = torch.randn(784, 10) # 随机生成示例输出max_values, indices = torch.max(outputs.data, 0) # 查找每个类别的最大值及其对应的样本索引 print(max_values) # 输出:每个类别的最大值 print(indices) # 输出:每个最大值对应的样本索引总结
操作 维度 输出形状 作用 torch.max(outputs, 1)1 [784](最大值),[784](索引)返回每个样本的最大值及对应的类索引(预测类别) torch.max(outputs, 0)0 [10](最大值),[784](索引)返回每个类别在所有样本中的最大值及对应的样本索引 因此,当
outputs的形状是[784, 10]时,选择torch.max(outputs.data, 1)可以用来获取每个样本的预测类别,而torch.max(outputs.data, 0)可以用来分析每个类别在样本中的最大预测值。
2、Dropout
关于 Dropout(0.5) 的理解,我们来详细说明一下它的工作原理。
Dropout 的工作原理
-  定义: - Dropout(0.5)表示在每次前向传播中,每个神经元有 50% 的概率被“丢弃”。这意味着有 50% 的神经元在当前迭代中不会参与计算,输出值被设置为 0。
 
-  示例: - 假设你的输入张量形状为 [64, 784],这里64是批量大小,784是每个样本的特征数(例如,一个 28x28 的图像展平后得到的特征向量)。
- 在前向传播中,Dropout 会随机选择 50% 的神经元进行丢弃。重要的是,这并不意味着只会有 32 个神经元参与计算,而是对于每个样本(每一行),可能有一部分神经元会被丢弃。
 
- 假设你的输入张量形状为 
具体过程
-  对于输入的每个样本(每一行),Dropout 会独立地选择哪些神经元被丢弃。例如,假设我们有以下输入: Input (x): [64, 784] (64 个样本,每个样本 784 个特征)
-  ==在每次前向传播时,Dropout 会随机将某些特征(神经元)设置为 0。==比如说,某次前向传播可能会得到如下的输出: Output (x'): [64, 784] (仍然是 64 个样本,但某些特征被置为 0)Sample 1: [0.5, 0, 0.2, 0, 0.8, ...] # 一些神经元被丢弃 Sample 2: [0, 0.3, 0, 0.9, 0.7, ...] # 不同的丢弃情况
关键点
- 样本数量不变:输入张量的形状 [64, 784]在经过 Dropout 后仍然是[64, 784],而不是[32, 784]。每个样本都有 784 个特征(神经元)。
- 随机性:每次前向传播时丢弃的神经元都是随机的,不同的迭代可能会丢弃不同的神经元。
- 训练与推理:在训练时使用 Dropout,而在推理(测试)时,所有神经元都被使用,通常输出会根据 Dropout 的比例进行缩放(乘以保留的概率)。
总结
- Dropout 会随机丢弃一些神经元的输出,但并不改变输入的样本数量和形状。输入 [64, 784]经过 Dropout 处理后,仍然是[64, 784],只是某些神经元的输出被置为 0。
2、常用模块
这些包在 Python 中有各自的作用,通常用于处理图像、文件操作和时间管理等。下面我逐一解释这些包的功能及其典型用途。
1. imageio
 
-  作用: - imageio是一个用于读取和写入图像、视频、动图等多媒体文件的库。
- 它支持多种图像格式,如 PNG、JPEG、GIF、TIFF,以及视频格式,如 MP4、AVI 等。
- 常用于读取图片或视频帧、保存图片、生成 GIF 等。
 
-  常用方法: - imageio.imread():读取图像文件。
- imageio.imwrite():保存图像文件。
- imageio.mimwrite():保存多张图像作为 GIF 动图。
- imageio.get_reader()和- imageio.get_writer():用于读取或写入视频。
 
-  示例: import imageio img = imageio.imread('image.png') # 读取图片 imageio.imwrite('new_image.png', img) # 保存图片
2. time
 
-  作用: - time是 Python 的标准库,用于处理时间相关的任务。
- 它可以获取当前时间、暂停程序执行、格式化时间、计算时间差等。
 
-  常用方法: - time.time():返回当前时间的时间戳(从 1970 年 1 月 1 日以来的秒数)。
- time.sleep(seconds):暂停程序执行指定的秒数。
- time.localtime():将时间戳转换为结构化时间。
- time.strftime():格式化时间为字符串。
 
-  示例: import time print(time.time()) # 输出当前时间的时间戳 time.sleep(2) # 暂停程序执行 2 秒
3. copy
 
-  作用: - copy是 Python 的标准库,提供了对象的浅复制和深复制功能。
- 浅复制 只复制对象的引用,某些情况下修改副本会影响原对象。
- 深复制 递归复制对象中的所有内容,副本与原对象完全独立。
 
-  常用方法: - copy.copy():执行浅复制。
- copy.deepcopy():执行深复制。
 
-  示例: import copy original = [1, 2, [3, 4]] shallow_copy = copy.copy(original) # 浅复制 deep_copy = copy.deepcopy(original) # 深复制
4. PIL.Image(Pillow)
 
-  作用: - PIL(Python Imaging Library)是一个强大的图像处理库,后来被分支为- Pillow,用于处理图像文件的读取、操作和保存。
- 它支持多种图像格式(如 JPEG、PNG、BMP 等),并提供丰富的图像操作功能,如裁剪、缩放、旋转、滤镜处理等。
 
-  常用方法: - Image.open():打开图像文件。
- Image.save():保存图像文件。
- Image.resize():调整图像大小。
- Image.rotate():旋转图像。
- Image.convert():转换图像模式(如 RGB、灰度等)。
 
-  示例: from PIL import Image img = Image.open('image.jpg') # 打开图片 img = img.resize((100, 100)) # 调整图片大小 img.save('resized_image.jpg') # 保存图片
总结
- imageio:用于读取和保存多媒体文件(图像、视频、GIF等)。
- time:用于时间处理,如暂停、获取当前时间等。
- copy:用于对象的浅复制和深复制。
- PIL.Image:图像处理库,支持多种图像操作(读取、保存、裁剪、旋转等)。
这些库在图像处理、时间控制和数据复制中被广泛使用,通常会用于一些涉及图片、视频的应用中,如机器学习的预处理、生成 GIF、视频分析等。
3、datasets.ImageFolder
当你使用 torchvision.datasets.ImageFolder 给定一个 root 目录时,root 是一个包含子文件夹的主目录。ImageFolder 并不会直接加载 root 目录下的所有图片,而是会查找 root 目录下每个子文件夹,并将这些子文件夹中的图片归类到对应的类别。
具体行为如下:
-  子文件夹代表类别: ImageFolder会将root目录下的每一个子文件夹的名称作为类别标签。每个子文件夹内部的所有图片都被视为属于该类别。例如,假设你的 root目录结构如下:root/├── cat/│ ├── image1.jpg│ ├── image2.jpg└── dog/├── image1.jpg├── image2.jpg- 在这种情况下,cat文件夹中的图片会被分配标签0,dog文件夹中的图片会被分配标签1。
- 图片路径 root/cat/image1.jpg会被标记为类别0,而root/dog/image1.jpg会被标记为类别1。
 
- 在这种情况下,
-  图片的加载顺序: ImageFolder会递归地遍历每个类别文件夹下的所有图片,并按文件路径的顺序加载它们。
-  文件类型: ImageFolder只会加载文件扩展名符合常见图片格式的文件(如.jpg,.png,.bmp等)。
因此,ImageFolder 只加载 root 目录中的子文件夹内的图片,而不会直接加载 root 目录中的图片(如果 root 目录本身包含图片,这些图片将会被忽略)。
举个例子
假设 root 目录为 data/train/,结构如下:
data/train/├── class1/│   ├── img1.jpg│   ├── img2.jpg├── class2/│   ├── img3.jpg│   ├── img4.jpg
使用 ImageFolder 加载该数据集:
from torchvision import datasets, transforms# 定义图像预处理
transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor()
])# 加载数据集
dataset = datasets.ImageFolder(root='data/train/', transform=transform)# 输出类别和样本数
print(f'类别:{dataset.class_to_idx}')  # 类别标签映射 {'class1': 0, 'class2': 1}
print(f'数据集大小:{len(dataset)}')  # 数据集大小,应该是4张图片
总结:
- ImageFolder会遍历- root目录下的每个子文件夹,并将子文件夹中的图片分配到对应的类别。
- 如果 root目录下有图片但没有子文件夹,这些图片将会被忽略。
4、数据集
batch_size = 128image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'valid']}  #同时读入了数据和标签
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True) for x in ['train', 'valid']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'valid']}
class_names = image_datasets['train'].classes #标签名
这两行代码:
image, label = image_datasets['train'][0]  #train的第一张图片
print(image_datasets['train'])
print(image.shape,label)  
print(image[0])    #这是图像的第一个通道(通常是 RGB 图像的红色通道)
Dataset ImageFolderNumber of datapoints: 6552Root location: ./data/flower_data/trainStandardTransform
Transform: Compose(Resize(size=[96, 96], interpolation=bilinear, max_size=None, antialias=True)RandomRotation(degrees=[-45.0, 45.0], interpolation=nearest, expand=False, fill=0)CenterCrop(size=(64, 64))RandomHorizontalFlip(p=0.5)RandomVerticalFlip(p=0.5)ColorJitter(brightness=(0.8, 1.2), contrast=(0.9, 1.1), saturation=(0.9, 1.1), hue=(-0.1, 0.1))RandomGrayscale(p=0.025)ToTensor()Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]))
torch.Size([3, 64, 64]) 0
tensor([[-0.1828, -0.2342, -0.2856,  ..., -1.6213, -1.6213, -1.7069],[-0.1999, -0.2856, -0.3198,  ..., -1.5357, -1.6213, -1.7069],[-0.2856, -0.3541, -0.4226,  ..., -1.5014, -1.6213, -1.6898],...,[-1.2959, -1.3130, -1.3815,  ..., -1.1075, -1.1418, -0.8849],[-1.2959, -1.3302, -1.4158,  ..., -1.0219, -1.0904, -0.7308],[-1.2959, -1.3473, -1.4500,  ..., -0.9877, -1.0733, -0.6623]])
#print(image_datasets['train'][0].shape)
print(image_datasets['train'][0][0][0].shape) #训练集第一个文件夹的第一张图片的B通道有负数,因为被标准化了 #训练集第一个文件夹的第一张图片的B通道有负数,因为被标准化了
print(image_datasets['train'][0][0][0])
torch.Size([64, 64])
tensor([[-1.7925, -1.8097, -1.7925,  ..., -0.9877, -1.0733, -1.1075],[-1.7754, -1.7754, -1.8268,  ..., -0.9705, -1.0733, -1.1932],[-1.7412, -1.7754, -1.8097,  ..., -1.0390, -1.1589, -1.2274],...,[ 0.3309,  0.4166,  0.4679,  ..., -1.8953, -1.9124, -1.9124],[ 0.3823,  0.4679,  0.4337,  ..., -1.8782, -1.8953, -1.8953],[ 0.5364,  0.4679,  0.5022,  ..., -1.8268, -1.8782, -1.8953]]
-  image_datasets['train'][0]返回一个元组(image, label),其中:- image是经过数据变换后的图像张量。
- label是对应的标签(类别)。
 这里, image是一个三维张量(C, H, W),即通道、高度、宽度,label是一个整数,表示图像的类别。
image, label = image_datasets['train'][0]
这一行将元组解包为 image 和 label,分别表示图片张量和标签。然后你打印 image[0],即图像张量的第一个通道,以及标签。
在 Python 中,解包(unpacking) 指的是将一个包含多个元素的容器(如元组、列表等)中的各个元素分别赋值给多个变量。解包可以让你快速地从容器中提取元素,方便后续操作。
元组解包
假设你有一个包含两个元素的元组:
tup = (10, 20)现在,你可以使用解包将
tup中的两个元素分别赋值给变量a和b:a, b = tup print(a) # 输出 10 print(b) # 输出 20在这里,
a被赋值为元组中的第一个元素(10),b被赋值为第二个元素(20)。这种方式被称为解包。代码中的解包
在你的代码中:
image, label = image_datasets['train'][0]
image_datasets['train'][0]返回的其实是一个包含两个元素的元组(image, label):
image是图像张量。
label是图像对应的标签(类别)。通过解包,
image和label分别被赋值为元组中的第一个和第二个元素。相当于以下代码:
# 没有解包的情况下: tup = image_datasets['train'][0] # tup 是一个元组 (image, label) image = tup[0] # 获取第一个元素(图像) label = tup[1] # 获取第二个元素(标签)但是,通过解包,你可以直接用
image, label = image_datasets['train'][0]一步完成,而不需要额外的tup[0]和tup[1]访问操作,简化了代码。总结
解包 就是从元组(或其他容器)中将每个元素分别赋值给多个变量。例如,你可以从
image_datasets['train'][0]返回的元组中提取出图像张量image和对应的标签label,简化后续代码的处理。
5、优化器&加载保存模型
1. optimizer.param_groups
 
optimizer.param_groups 是 PyTorch 中优化器的一个属性,表示优化器管理的参数组。每个参数组是一个字典,包含与优化相关的信息,比如模型的参数(权重)、学习率等。
常见内容:
- params: 这是模型中要更新的参数列表(张量),通常是模型的权重和偏置。
- lr: 学习率,用于控制梯度下降时的步长。
- momentum: 动量参数,适用于带动量的优化算法(如 SGD)。
- weight_decay: 权重衰减(L2正则化)系数。
- dampening: 动量的抑制因子。
- nesterov: 是否使用 Nesterov 动量。
示例:
# 打印所有参数组信息
for param_group in optimizer.param_groups:print(param_group)
示例输出(一个带有动量的 SGD 优化器):
{'params': [tensor1, tensor2, ...], 'lr': 0.001, 'momentum': 0.9, 'weight_decay': 0.01, 'nesterov': True}
- 这里 params存储模型中的参数(如权重和偏置),其他则是用于调整这些参数的超参数。
2. model.state_dict()
 
model.state_dict() 是 PyTorch 中一个非常重要的函数,用于获取模型的参数和持久性状态,并以字典形式返回。这个字典的键是模型中各层的名称,值是这些层对应的张量(即权重和偏置)。
常见用途:
- 保存模型参数:可以通过 state_dict()来保存模型的所有参数,通常保存为.pt文件。
- 加载模型参数:通过加载保存的 state_dict()来恢复模型的参数。
示例:
# 打印模型的 state_dict
for param_tensor in model.state_dict():print(param_tensor, "\t", model.state_dict()[param_tensor].size())
示例输出:
fc.weight      torch.Size([10, 512])
fc.bias        torch.Size([10])
conv1.weight   torch.Size([64, 3, 7, 7])
conv1.bias     torch.Size([64])
- 输出中,fc.weight和fc.bias是全连接层的权重和偏置,conv1.weight和conv1.bias是卷积层的权重和偏置。
常用操作:
-  保存模型参数: torch.save(model.state_dict(), 'model_weights.pth')
-  加载模型参数: model.load_state_dict(torch.load('model_weights.pth'))
总结:
- optimizer.param_groups:包含优化器参数和超参数(如学习率、动量等),帮助控制模型权重的更新过程。
- model.state_dict():保存或加载模型的所有参数(权重、偏置等),通常用于模型的持久化和恢复。