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,不相等返回 False
rights = 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 = 128
image_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 ImageFolder
Number of datapoints: 6552
Root location: ./data/flower_data/train
StandardTransform
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()
:保存或加载模型的所有参数(权重、偏置等),通常用于模型的持久化和恢复。
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 深度学习代码学习笔记2
发表评论 取消回复