图片分类任务

这段代码定义了训练集和验证集的图像预处理流程,使用了 PyTorch 的 torchvision.transforms 模块。以下是这段代码的详细解释:

1. 训练集预处理 (train_transform)

Python复制

train_transform = transforms.Compose(
    [
        transforms.ToPILImage(),   # 将张量转换为 PIL 图像
        transforms.RandomResizedCrop(224),  # 随机裁剪并调整大小到 224x224
        transforms.RandomRotation(50),  # 随机旋转图像,角度范围为 -50 到 50 度
        transforms.ToTensor()  # 将 PIL 图像转换为张量
    ]
)
1.1 transforms.ToPILImage()
  • 作用: 将输入的张量(Tensor)转换为 PIL 图像。

  • 原因: 后续的图像变换操作(如 RandomResizedCropRandomRotation)需要 PIL 图像作为输入。

1.2 transforms.RandomResizedCrop(224)
  • 作用: 对图像进行随机裁剪,并将裁剪后的图像调整为 224x224 大小。

  • 参数: 224 表示目标大小为 224x224。

  • 目的: 增强数据多样性,提高模型的泛化能力。

1.3 transforms.RandomRotation(50)
  • 作用: 对图像进行随机旋转,旋转角度范围为 -50 到 50 度。

  • 参数: 50 表示旋转角度范围。

  • 目的: 增强数据多样性,提高模型对不同角度图像的鲁棒性。

1.4 transforms.ToTensor()
  • 作用: 将 PIL 图像转换为张量(Tensor),并归一化像素值到 [0, 1] 范围。

  • 目的: 模型需要张量作为输入,且归一化有助于加速训练。

2. 验证集预处理 (val_transform)

Python复制

val_transform = transforms.Compose(
    [
        transforms.ToPILImage(),   # 将张量转换为 PIL 图像
        transforms.ToTensor()  # 将 PIL 图像转换为张量
    ]
)
2.1 transforms.ToPILImage()
  • 作用: 将输入的张量(Tensor)转换为 PIL 图像。

  • 原因: 后续的图像变换操作需要 PIL 图像作为输入。

2.2 transforms.ToTensor()
  • 作用: 将 PIL 图像转换为张量(Tensor),并归一化像素值到 [0, 1] 范围。

  • 目的: 模型需要张量作为输入,且归一化有助于加速训练。

3. 总结

  • 训练集预处理: 包括数据增强操作(随机裁剪、随机旋转),以增加数据多样性,提高模型的泛化能力。

  • 验证集预处理: 不包括数据增强操作,只进行基本的转换和归一化,以确保验证结果的客观性。

通过这些预处理操作,可以确保输入模型的图像数据格式统一且适合模型训练和验证。

train_transform = transforms.Compose(
    [
        transforms.ToPILImage(),   #224, 224, 3模型  :3, 224, 224
        transforms.RandomResizedCrop(224),
        transforms.RandomRotation(50),
        transforms.ToTensor()
    ]
)

val_transform = transforms.Compose(
    [
        transforms.ToPILImage(),   #224, 224, 3模型  :3, 224, 224
        transforms.ToTensor()
    ]
)

os.listdir(path) 的作用

  • os.listdir(path): 这是 Python 标准库 os 中的一个函数,用于列出指定路径下的所有文件和文件夹。

  • 返回值: 返回一个列表,包含指定路径下的所有文件和文件夹的名称,不包括路径本身。

xi = np.zeros((len(file_list), HW, HW, 3), dtype=np.uint8)
  • np.zeros: 创建一个全零数组。

  • shape: 指定数组的形状,这里是一个 4 维数组:

    • len(file_list): 数组的第一个维度,表示文件的数量。

    • HW: 图像的高度和宽度,这里指定为 224。

    • 3: 图像的通道数,表示 RGB 三通道。

  • dtype=np.uint8: 指定数组的数据类型为无符号 8 位整数(范围是 0 到 255),适合存储图像像素值。

  • xi = np.zeros((len(file_list), HW, HW, 3), dtype=np.uint8) 的作用是创建一个形状为 (len(file_list), HW, HW, 3) 的全零数组,用于存储图像数据。这在图像处理和深度学习中非常常见,用于批量处理图像数据

Python复制

enumerate(iterable, start=0)
  • iterable: 需要遍历的可迭代对象。

  • start: 索引的起始值,默认为 0。

作用

enumerate() 的主要作用是简化循环中的索引管理,使代码更加清晰和简洁。

示例

假设我们有一个列表 file_list,包含文件名:

Python复制

file_list = ["image1.png", "image2.png", "image3.png"]

使用 enumerate() 遍历这个列表:

Python复制

for index, file_name in enumerate(file_list):
    print(f"索引: {index}, 文件名: {file_name}")

输出:

复制

索引: 0, 文件名: image1.png
索引: 1, 文件名: image2.png
索引: 2, 文件名: image3

假设 xi 的形状是 (3, 224, 224, 3),表示可以存储 3 张大小为 224x224 的 RGB 图像。img 是一张调整大小后的图像,形状为 (224, 224, 3)

Python复制

xi = np.zeros((3, 224, 224, 3), dtype=np.uint8)
img = np.random.randint(0, 256, (224, 224, 3), dtype=np.uint8)  # 创建一个随机图像

xi[0, ...] = img  # 将 img 存储到 xi 的第 0 个位置

执行后,xi 的第 0 个子数组将包含 img 的所有像素值。

1. tqdm 的作用

tqdm 是一个快速、可扩展的进度条库,可以在 Python 的长循环中添加一个进度提示信息。它通常用于显示循环的进度,帮助用户了解代码的执行情况。

2. 代码中的作用

Python复制

for i in tqdm(range(11)):
  • range(11): 生成一个从 0 到 10 的整数序列,表示循环的范围。

  • tqdm(range(11)): 将这个序列包装成一个 tqdm 对象,为循环添加一个进度条。

3. 进度条的显示

在循环执行过程中,tqdm 会在终端中显示一个进度条,实时更新当前的进度。进度条通常包括以下信息:

  • 当前进度(如 50%)。

  • 已完成的迭代次数。

  • 总迭代次数。

  • 每秒迭代次数(it/s)。

  • 剩余时间(如果可用)。

4. 示例

假设我们有一个简单的循环,使用 tqdm 来显示进度:

Python复制

from tqdm import tqdm
import time

for i in tqdm(range(11)):
    time.sleep(0.5)  # 模拟耗时操作

运行这段代码时,终端会显示类似以下的进度条:

100%|██████████| 11/11 [00:05<00:00, 2.00it/s]

5. 在代码中的具体作用

在你的代码中,tqdm 用于显示处理 11 个类别(range(11))的进度。这有助于用户了解代码的执行进度,尤其是在处理大量数据时。

6. 总结

tqdm 的作用是为循环提供一个可视化的进度条,帮助用户了解代码的执行进度。它在处理长循环或耗时操作时非常有用,可以提供直观的进度反馈。

如果你没有安装 tqdm,可以通过以下命令安装:

bash复制

pip install tqdm

如果你不需要进度条,也可以直接去掉 tqdm,代码仍然可以正常运行:

Python复制

for i in range(11):

但使用 tqdm 可以让代码的执行过程更加直观。

Python复制

file_dir = path + "/%02d" % i
  • path: 基础路径,例如 "./data"

  • i: 当前类别的索引,范围是 0 到 10。

  • "%02d" % i: 将 i 格式化为两位数字,不足两位时前面补零。

  • file_dir: 生成的完整路径,例如 "./data/00""./data/01""./data/02" 等。

在这段代码中,yiY 分别用于存储每个图像的类别标签。它们的作用是将图像与其对应的类别标签关联起来,以便在后续的训练和验证过程中使用。以下是详细解释:

1. yi 的作用

yi 是一个 NumPy 数组,用于存储当前类别 i 下所有图像的类别标签。它的形状是 (len(file_list),),即与当前类别下的图像数量一致。

Python复制

yi = np.zeros(len(file_list), dtype=np.uint8)
  • len(file_list): 当前类别下的图像数量。

  • dtype=np.uint8: 数据类型为无符号 8 位整数,适合存储类别标签。

在循环中,yi 的每个元素被赋值为当前类别的索引 i

Python复制

yi[j] = i
  • j: 当前图像的索引。

  • i: 当前类别的索引。

2. Y 的作用

Y 是一个 NumPy 数组,用于存储所有图像的类别标签。它的形状是 (总图像数量,),即与所有图像的总数一致。

在循环中,Y 通过 np.concatenate 不断扩展,将每个类别的标签数组 yi 追加到 Y 中:

Python复制

if i == 0:
    Y = yi
else:
    Y = np.concatenate((Y, yi), axis=0)
  • if i == 0: 如果是第一个类别,直接将 yi 赋值给 Y

  • else: 如果不是第一个类别,将 yi 追加到 Y 的末尾。

3. 代码的作用

这段代码的作用是:

  1. 遍历每个类别(i 从 0 到 10)。

  2. 对每个类别,读取其文件夹中的所有图像。

  3. 将每个图像的类别标签存储到 yi 中。

  4. 将所有类别的标签数组 yi 合并到一个大的数组 Y 中。

4. 示例

假设 path"./data",每个类别下有以下文件:

  • ./data/00: 包含 3 张图像。

  • ./data/01: 包含 2 张图像。

  • ./data/02: 包含 4 张图像。

执行代码后:

  • yi 的内容将是 [0, 0, 0](对于类别 0)、[1, 1](对于类别 1)、[2, 2, 2, 2](对于类别 2)。

  • Y 的内容将是 [0, 0, 0, 1, 1, 2, 2, 2, 2]

5. 完整代码片段

Python复制

for i in tqdm(range(11)):
    file_dir = path + "/%02d" % i
    file_list = os.listdir(file_dir)

    xi = np.zeros((len(file_list), HW, HW, 3), dtype=np.uint8)
    yi = np.zeros(len(file_list), dtype=np.uint8)

    for j, img_name in enumerate(file_list):
        img_path = os.path.join(file_dir, img_name)
        img = Image.open(img_path)
        img = img.resize((HW, HW))
        xi[j, ...] = img
        yi[j] = i

    if i == 0:
        X = xi
        Y = yi
    else:
        X = np.concatenate((X, xi), axis=0)
        Y = np.concatenate((Y, yi), axis=0)
print("读到了%d个数据" % len(Y))
return X, Y

6. 总结

  • yi: 存储当前类别下所有图像的类别标签。

  • Y: 存储所有图像的类别标签。

  • 这些标签用于后续的训练和验证过程,帮助模型学习图像与类别的映射关系。

X = np.concatenate((X, xi), axis=0) 的作用是将两个 NumPy 数组 Xxi 在指定的轴(axis=0)上进行拼接。具体来说,它会沿着数组的第一个维度(通常是行方向)将两个数组合并成一个更大的数组。以下是这段代码的详细解释:

1. np.concatenate 的作用

np.concatenate 是 NumPy 提供的一个函数,用于将多个数组沿着指定的轴拼接成一个更大的数组。它的语法如下:

Python复制

np.concatenate((array1, array2, ...), axis)
  • array1, array2, ...: 要拼接的数组,这些数组必须在拼接轴以外的维度上具有相同的形状。

  • axis: 指定拼接的轴。axis=0 表示沿着第一个维度(行方向)拼接。

2. 代码的作用

Python复制

X = np.concatenate((X, xi), axis=0)
  • X: 已经存在的数组,存储了之前类别的图像数据。

  • xi: 当前类别的图像数据。

  • axis=0: 指定沿着第一个维度(行方向)进行拼接。

3. 具体解释

假设 Xxi 的形状如下:

  • X 的形状是 (N1, H, W, C),表示已经存储了 N1 张图像。

  • xi 的形状是 (N2, H, W, C),表示当前类别有 N2 张图像。

执行 X = np.concatenate((X, xi), axis=0) 后,X 的形状将变为 (N1 + N2, H, W, C),即将 xi 的所有图像追加到 X 的末尾。

4. 示例

假设 Xxi 的形状如下:

  • X 的形状是 (3, 224, 224, 3),表示存储了 3 张图像。

  • xi 的形状是 (2, 224, 224, 3),表示当前类别有 2 张图像。

执行 X = np.concatenate((X, xi), axis=0) 后,X 的形状将变为 (5, 224, 224, 3),即将 xi 的 2 张图像追加到 X 的末尾。

self.mode == "semi"
  • self.mode: 表示数据集的模式,可以是 "train""val""semi"

  • self.X[item]: 获取数据集中的第 item 个图像数据。

  • self.transform(self.X[item]): 对图像数据应用预定义的变换(如数据增强)。

  • self.X[item]: 返回原始图像数据(未经过变换)。

  • 返回值: 返回一个元组 (transformed_image, original_image),其中 transformed_image 是经过变换的图像,original_image 是原始图像。

  • no_label_loader: 无标签数据的数据加载器。

  • model: 预训练的模型,用于生成伪标签。

  • device: 计算设备(cudacpu)。

  • thres: 置信度阈值,用于筛选伪标签。

  • self.get_label: 调用 get_label 方法,从无标签数据中生成伪标签。

  • self.flag: 标志变量,表示是否成功生成了半监督数据。

    • 如果 x == [],表示没有生成任何半监督数据,self.flag 设置为 False

    • 否则,self.flag 设置为 True,并将筛选出的数据和标签存储到 self.Xself.Y 中。

  • self.transform: 应用于图像的变换操作(如数据增强)

 

  • model.to(device): 将模型移动到指定设备。

  • pred_problabels: 用于存储每个预测的置信度和伪标签。

  • soft = nn.Softmax(): 使用 Softmax 函数将模型的输出转换为概率分布。

  • with torch.no_grad(): 禁用梯度计算,减少内存占用。

pred = model(bat_x) 是 PyTorch 中的一个常见操作,用于对输入数据 bat_x 进行前向传播,获取模型的预测结果。以下是这段代码的详细解释:

1. 代码的作用

Python复制

pred = model(bat_x)
  • model: 一个预训练的神经网络模型,用于对输入数据进行分类或回归预测。

  • bat_x: 输入数据,通常是一个张量,形状为 (batch_size, channels, height, width),表示一个批次的图像数据。

  • pred: 模型的输出,通常是未经归一化的 logits(分类任务)或回归值(回归任务)。

2. 前向传播

  • 前向传播 是神经网络中从输入到输出的计算过程。在这个过程中,输入数据通过网络的每一层,最终得到预测结果。

  • model(bat_x): 调用模型的 forward 方法,对输入数据 bat_x 进行前向传播,返回预测结果 pred

3. 预测结果的形状

假设 bat_x 的形状是 (batch_size, channels, height, width),模型是一个分类器,输出类别数为 num_classes,则 pred 的形状将是 (batch_size, num_classes)。每个元素表示输入样本属于某个类别的 logits(未经归一化的分数)。

4. 示例

假设 model 是一个预训练的分类模型,bat_x 是一个形状为 (4, 3, 224, 224) 的张量,表示 4 张 224x224 的 RGB 图像:

Python复制

pred = model(bat_x)
  • pred: 模型的输出,形状为 (4, num_classes),表示每个图像属于每个类别的 logits。

  • pred_soft.max(1): 获取每个预测的最大概率值和对应的类别索引。

  • pred_soft.max(1) 是 PyTorch 中的一个操作,用于计算张量 pred_soft 在指定维度(这里是维度 1)上的最大值及其对应的索引。具体来说,它返回两个张量:

  • 最大值: 每个样本的最大概率值。

  • 索引: 每个样本的最大概率值对应的类别索引。

  • 1. pred_soft 的形状

    假设 pred_soft 的形状是 (batch_size, num_classes),其中:

  • batch_size: 批量大小,表示每个批次中的样本数量。

  • num_classes: 类别数量,表示每个样本的类别概率分布。

  • 2. pred_soft.max(1) 的作用

    Python复制

    pred_max, pred_value = pred_soft.max(1)
  • pred_soft.max(1): 在维度 1 上计算每个样本的最大值及其对应的索引。

    • pred_max: 每个样本的最大概率值,形状为 (batch_size,)

    • pred_value: 每个样本的最大概率值对应的类别索引,形状为 (batch_size,)

  • pred_soft = torch.tensor([
        [0.1, 0.2, 0.7],
        [0.8, 0.1, 0.1],
        [0.3, 0.4, 0.3],
        [0.2, 0.6, 0.2]
    ])

    执行 pred_max, pred_value = pred_soft.max(1) 后:

  • pred_max: 每个样本的最大概率值,形状为 (4,)

    Python复制

    pred_max = torch.tensor([0.7, 0.8, 0.4, 0.6])
  • pred_value: 每个样本的最大概率值对应的类别索引,形状为 (4,)

    Python复制

    pred_value = torch.tensor([2, 0, 1, 1])
  • 3. 具体解释

    假设 pred_soft 是一个形状为 (4, 3) 的张量,表示 4 个样本的类别概率分布:

    Python复制

    pred_soft = torch.tensor([
        [0.1, 0.2, 0.7],
        [0.8, 0.1, 0.1],
        [0.3, 0.4, 0.3],
        [0.2, 0.6, 0.2]
    ])

    执行 pred_max, pred_value = pred_soft.max(1) 后:

  • pred_max: 每个样本的最大概率值,形状为 (4,)

    Python复制

    pred_max = torch.tensor([0.7, 0.8, 0.4, 0.6])
  • pred_value: 每个样本的最大概率值对应的类别索引,形状为 (4,)

    Python复制

    pred_value = torch.tensor([2, 0, 1, 1])
  • pred_prob.extendlabels.extend: 将预测的置信度和伪标签存储到列表中。

  • for index, prob in enumerate(pred_prob): 遍历所有预测的置信度。

    • 如果置信度大于阈值 thres,将对应的图像和伪标签存储到 xy 中。

  • return x, y: 返回筛选后的图像和伪标签。

1. DataLoader 的作用

DataLoader 是 PyTorch 中的一个工具,用于将数据集包装成一个可迭代的对象,方便批量加载数据。它支持多线程加载、数据打乱、批量处理等功能。

2. 代码的作用

Python复制

semi_loader = DataLoader(semiset, batch_size=16, shuffle=False)
  • semiset: 一个 semiDataset 类的实例,表示半监督数据集。

  • batch_size=16: 每个批次的大小为 16。

  • shuffle=False: 不打乱数据顺序。

3. semiset 的作用

semiset 是一个 semiDataset 类的实例,它包含从无标签数据中筛选出的置信度高于阈值 thres 的数据及其伪标签。semiset 的主要作用是:

  1. 使用预训练的模型对无标签数据进行预测。

  2. 筛选出置信度高于阈值的数据及其伪标签。

  3. 提供一个 PyTorch 数据集类,支持通过 DataLoader 加载这些数据。

4. DataLoader 的参数

  • semiset: 数据集对象,提供数据和标签。

  • batch_size=16: 每个批次的大小为 16。这意味着每次从数据集中加载 16 个样本。

  • shuffle=False: 不打乱数据顺序。在半监督学习中,通常不需要打乱数据顺序,因为数据已经通过置信度筛选过。

5. 返回值

  • semi_loader: 一个 DataLoader 对象,用于加载半监督数据集 semiset

6. 完整代码片段

Python复制

def get_semi_loader(no_label_loader, model, device, thres):
    semiset = semiDataset(no_label_loader, model, device, thres)
    if semiset.flag == False:
        return None
    else:
        semi_loader = DataLoader(semiset, batch_size=16, shuffle=False)
        return semi_loader

7. 总结

semi_loader = DataLoader(semiset, batch_size=16, shuffle=False) 的作用是创建一个 DataLoader,用于加载半监督数据集 semiset。这个 DataLoader 可以在训练过程中批量加载筛选后的数据及其伪标签,方便模型进行半监督学习。

self.bn1 = nn.BatchNorm2d(64) 的作用是创建一个二维批量归一化(Batch Normalization)层,用于对卷积层的输出进行归一化处理。批量归一化是一种常用的正则化和加速训练的技术。以下是详细解释:

1. 批量归一化的作用

批量归一化(Batch Normalization,简称 BN)是一种用于加速深度神经网络训练的技术,同时也有一定的正则化效果。它的主要作用包括:

  • 加速训练:通过减少内部协变量偏移(Internal Covariate Shift),使网络的每一层输入的分布更加稳定,从而加速训练过程。

  • 提高性能:有助于提高模型的泛化能力。

  • 简化超参数调整:减少了对学习率等超参数的敏感性。

2. nn.BatchNorm2d 的作用

nn.BatchNorm2d 是 PyTorch 中实现二维批量归一化的类。它对卷积层的输出进行归一化处理,确保每个特征图的均值为 0,标准差为 1。具体来说,它对每个特征图进行以下操作: BN(x)=γ(σx−μ​)+β 其中:

  • x 是输入特征图。

  • μ 是输入特征图的均值。

  • σ 是输入特征图的标准差。

  • γ 和 β 是可学习的参数,分别表示缩放因子和偏移量。

3. 代码的作用

Python复制

self.bn1 = nn.BatchNorm2d(64)
  • 64: 表示该批量归一化层处理的特征图数量(通道数)。这里假设前一层的输出通道数为 64。

  • self.bn1: 将这个批量归一化层存储为类的一个属性,以便在前向传播中使用。

4. 在模型中的应用

在你的模型中,self.bn1 通常位于卷积层和激活函数之间。例如:

Python复制

self.conv1 = nn.Conv2d(3, 64, 3, 1, 1)    # 64*224*224
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU()

在前向传播中,数据会依次通过这些层:

Python复制

x = self.conv1(x)  # 卷积层
x = self.bn1(x)    # 批量归一化层
x = self.relu(x)   # 激活函数

5. 示例

假设输入张量 x 的形状是 (batch_size, 3, 224, 224),经过 self.conv1 后,输出的形状变为 (batch_size, 64, 224, 224)。然后,self.bn1 会对这 64 个特征图进行归一化处理,确保每个特征图的均值为 0,标准差为 1。

6. 总结

self.bn1 = nn.BatchNorm2d(64) 的作用是创建一个二维批量归一化层,用于对卷积层的输出进行归一化处理。批量归一化有助于加速训练过程,提高模型的性能,并简化超参数调整。

1. torch.argmax() 的作用

torch.argmax() 的作用是返回张量中最大值的索引。它支持多维张量,并可以在指定维度上操作。

2. 语法

Python复制

torch.argmax(input, dim=None)
  • input: 输入张量。

  • dim: 指定维度。如果不指定 dim,则返回整个张量中最大值的索引。

3. 具体解释

假设 input 是一个形状为 (batch_size, num_classes) 的张量,表示每个样本的类别概率分布:

Python复制

input = torch.tensor([
    [0.1, 0.2, 0.7],
    [0.8, 0.1, 0.1],
    [0.3, 0.4, 0.3],
    [0.2, 0.6, 0.2]
])
3.1 在指定维度上操作

Python复制

pred_value = torch.argmax(input, dim=1)
  • dim=1: 在每个样本的类别概率分布中找到最大值的索引。

  • pred_value: 返回每个样本的最大概率值对应的类别索引,形状为 (batch_size,)

运行结果:

Python复制

pred_value = torch.tensor([2, 0, 1, 1])
3.2 不指定维度

Python复制

max_index = torch.argmax(input)
  • 不指定 dim: 返回整个张量中最大值的索引。

  • max_index: 返回一个整数,表示整个张量中最大值的索引。

运行结果:

Python复制

max_index = 0  # 对应第一个样本的第三个类别

在 PyTorch 中,optimizer.step()optimizer.zero_grad() 是训练神经网络时常用的两个操作,它们的作用分别是更新模型参数和清空梯度。以下是详细解释:

1. optimizer.step()

Python复制

optimizer.step()
  • 作用: 更新模型的参数。

  • 具体操作: 根据优化器的算法(如 SGD、Adam 等),使用当前计算的梯度来更新模型的参数。

  • 重要性: 这一步是训练过程中最关键的一步,它根据反向传播计算的梯度调整模型的权重,以最小化损失函数。

2. optimizer.zero_grad()

Python复制

optimizer.zero_grad()
  • 作用: 清空(归零)梯度。

  • 具体操作: 将所有参数的梯度手动设置为零。

  • 重要性: 在 PyTorch 中,梯度是累加的。如果不手动清空梯度,每次调用 backward() 时计算的梯度会累加到之前的梯度上,而不是替换它们。这会导致梯度值不断累积,最终导致训练不稳定或错误。

3. 为什么需要清空梯度

在 PyTorch 中,梯度是累加的,而不是自动清零。这意味着:

  • 每次调用 backward() 时,计算的梯度会加到之前的梯度上。

  • 如果不手动清空梯度,梯度会不断累积,导致训练不稳定或错误。

if epoch%3 == 0 and plt_val_acc[-1] > 0.6:
    semi_loader = get_semi_loader(no_label_loader, model, device, thres)

在训练过程中,plt_val_acc[-1] 表示最近一次验证的准确率。如果在某个 epoch 后 plt_val_acc[-1] > 0.6,并不意味着接下来的 epoch 中 plt_val_acc[-1] 会一直大于 0.6。验证集的准确率可能会因为多种因素而波动,例如模型的泛化能力、数据的复杂度、优化器的设置等。

loss = nn.CrossEntropyLoss() 的作用是定义一个交叉熵损失函数,用于衡量模型的预测输出与真实标签之间的差异。交叉熵损失函数常用于分类任务,特别是在多分类问题中。以下是详细解释:

1. 交叉熵损失函数的作用

交叉熵损失函数用于衡量模型预测的概率分布与真实标签的概率分布之间的差异。它鼓励模型的预测结果与真实标签一致,从而提高分类的准确性。

2. 数学公式

对于多分类问题,交叉熵损失函数的数学公式为: CrossEntropyLoss=−N1​∑i=1N​∑j=1C​yij​log(pij​) 其中:

  • N 是样本数量。

  • C 是类别数量。

  • yij​ 是第 i 个样本属于第 j 个类别的真实标签(0 或 1)。

  • pij​ 是模型预测的第 i 个样本属于第 j 个类别的概率。

3. 代码的作用

Python复制

loss = nn.CrossEntropyLoss()
  • nn.CrossEntropyLoss(): 这是 PyTorch 中的一个类,用于实现交叉熵损失函数。

  • loss: 将交叉熵损失函数赋值给变量 loss,以便在训练过程中使用。

4. 在训练中的应用

在训练过程中,交叉熵损失函数用于计算模型的预测输出与真实标签之间的损失。例如:

Python复制

pred = model(x)
train_bat_loss = loss(pred, target)
  • pred: 模型的输出,形状为 (batch_size, num_classes)

  • target: 真实标签,形状为 (batch_size,)

  • train_bat_loss: 计算的损失值,用于反向传播和参数更新。

5. 为什么选择交叉熵损失函数

  • 多分类问题: 交叉熵损失函数适用于多分类问题,能够有效衡量模型的预测结果与真实标签之间的差异。

  • 数值稳定: 交叉熵损失函数结合了 softmax 操作,数值计算更稳定。

  • 优化友好: 交叉熵损失函数的梯度计算简单,适合优化算法使用。

6. 与其他损失函数的区别

  • nn.CrossEntropyLoss: 内部结合了 softmax 操作,适用于多分类问题。

  • nn.NLLLoss: 需要输入是 log 概率,适用于已经应用了 softmax 的情况。

  • nn.BCELoss: 用于二分类问题,输入是单个概率值。

7. 总结

loss = nn.CrossEntropyLoss() 的作用是定义一个交叉熵损失函数,用于衡量模型的预测输出与真实标签之间的差异。它在多分类问题中非常常用,能够有效提高模型的分类性能。

optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-4) 的作用是定义一个优化器,用于更新模型的参数。以下是这段代码的详细解释:

1. torch.optim.AdamW

AdamW 是一种优化算法,结合了 Adam 的自适应学习率和权重衰减(L2 正则化)的优点。它通过将权重衰减与优化步骤分离,解决了传统 Adam 优化器在权重衰减处理上的不足,从而在训练过程中提供更好的收敛性和泛化性能。

2. 参数解释

  • model.parameters(): 模型的可训练参数,这些参数将在训练过程中被优化器更新。

  • lr=lr: 学习率(learning rate),控制参数更新的步长。学习率越大,参数更新的步长越大,训练可能越快收敛,但也可能导致训练不稳定。

  • weight_decay=1e-4: 权重衰减系数,用于 L2 正则化。权重衰减通过在损失函数中添加参数的 L2 范数,防止模型过拟合,提高泛化能力。

3. 代码的作用

Python复制

optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-4)
  • 作用: 创建一个 AdamW 优化器实例,用于在训练过程中更新模型的参数。

  • model.parameters(): 模型的可训练参数。

  • lr: 学习率,控制参数更新的步长。

  • weight_decay: 权重衰减系数,用于 L2 正则化。

4. 在训练中的应用

在训练过程中,优化器通过以下步骤更新模型参数:

  1. 前向传播: 计算模型的预测值和损失函数。

  2. 反向传播: 计算梯度。

  3. 参数更新: 使用优化器更新模型参数。

Python复制

for batch_x, batch_y in train_loader:
    x, target = batch_x.to(device), batch_y.to(device)
    pred = model(x)
    loss_value = loss(pred, target)
    loss_value.backward()
    optimizer.step()  # 更新参数
    optimizer.zero_grad()  # 清空梯度

5. 为什么选择 AdamW

  • 结合 Adam 和权重衰减: AdamW 解决了传统 Adam 优化器在权重衰减处理上的不足,提供了更好的收敛性和泛化性能。

  • 自适应学习率: AdamW 使用自适应学习率,能够根据参数的梯度自动调整学习率,提高训练效率。

  • 正则化效果: 权重衰减(L2 正则化)有助于防止过拟合,提高模型的泛化能力。

6. 与其他优化器的区别

  • Adam: 没有分离权重衰减,可能导致优化效果不如 AdamW。

  • SGD: 需要手动调整学习率,没有自适应学习率,训练可能更慢且不稳定。

  • RMSprop: 也是一种自适应学习率的优化器,但 AdamW 通常在实践中表现更好。

7. 总结

optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-4) 的作用是定义一个 AdamW 优化器,用于在训练过程中更新模型的参数。它结合了自适应学习率和权重衰减的优点,能够提高模型的训练效率和泛化性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值