欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > 【PyTorch】迁移学习、数据增强

【PyTorch】迁移学习、数据增强

2025/2/23 14:50:50 来源:https://blog.csdn.net/u014608435/article/details/144839502  浏览:    关键词:【PyTorch】迁移学习、数据增强


PyTorch官网

介绍

PyTorch 是一个开源的机器学习库,由 Facebook 的人工智能研究实验室开发。它提供了两种主要的功能:张量计算(类似于 NumPy,但具有 GPU 加速)和基于动态计算图的深度学习工具。PyTorch 因其灵活性、易用性和强大的社区支持而广受欢迎,特别适合研究和原型设计。

PyTorch 的核心特性

  • 张量(Tensor):PyTorch 中的基本数据结构是 torch.Tensor,它可以表示多维数组,并且可以在 CPU 或 GPU 上运行。张量支持自动求导功能,这使得实现复杂的神经网络变得简单。
  • 自动求导(Autograd):PyTorch 的 torch.autograd 模块可以自动计算梯度,这对于训练神经网络至关重要。每个张量都可以跟踪其计算历史,并根据需要反向传播以计算梯度。
  • 神经网络模块(nn.Module):torch.nn 提供了构建神经网络所需的各种层和损失函数。你可以通过- 继承 nn.Module 类来定义自己的模型,并使用内置或自定义的层和激活函数。
  • 优化器(Optimizers):torch.optim 包含了多种常用的优化算法,如 SGD、Adam 等,可以帮助你轻松地设置训练过程中的参数更新规则。
  • 数据处理(Data Loading):torch.utils.data 提供了方便的数据加载和预处理工具,包括 Dataset 和 DataLoader 类,它们能够高效地管理批量数据并支持多线程读取。
  • 分布式训练(Distributed Training):PyTorch 支持单机多 GPU 和多机多 GPU 的分布式训练,可以通过 torch.distributed 和 torch.nn.parallel.DistributedDataParallel 实现高效的模型并行化。
  • 迁移学习(Transfer Learning):PyTorch 提供了许多预训练模型,可以直接用于新的任务或者作为基础模型进行微调。
  • 可视化(Visualization):结合 TensorBoard 或其他可视化工具,PyTorch 可以帮助开发者更好地理解模型的行为和性能。

PyTorch 生态系统

除了核心库外,PyTorch 还拥有丰富的生态系统,涵盖了从计算机视觉到自然语言处理等多个领域。例如:

  • torchvision:提供了一系列与图像相关的数据集、模型架构和常用变换。
  • torchaudio:专注于音频处理,包含数据集和预训练模型。
  • torchtext:为文本数据处理提供工具和支持。
  • transformers:Hugging Face 提供的库,包含了大量预训练的语言模型及其应用。

迁移学习

迁移学习(Transfer Learning)是一种机器学习方法,其中在一个任务上预训练的模型被重新用于另一个相关任务。这种方法特别有用,当新任务的数据量较少时,可以利用预训练模型中已经学到的特征表示,从而提高性能和减少过拟合的风险。

在 PyTorch 中进行迁移学习通常涉及以下几个步骤:

  • 选择预训练模型:PyTorch 提供了许多流行的深度学习架构(如 ResNet、VGG、DenseNet 等),这些模型已经在大规模数据集(如 ImageNet)上进行了预训练。你可以从 torchvision.models 模块中加载这些预训练模型。
  • 冻结或微调部分层:根据具体的应用场景,可以选择冻结预训练模型中的某些层(通常是前面的卷积层),只对后面的全连接层进行训练。这样做可以保留预训练模型中捕捉到的低级特征,同时调整高层特征以适应新的任务。如果目标任务与原始任务非常相似,则可以考虑微调整个网络。
  • 修改分类器:对于分类任务,通常需要替换预训练模型的最后一层(即分类器)。因为预训练模型的最后一层是针对原始任务设计的,而新任务可能有不同的类别数量。你需要根据新任务的需求构建一个新的分类器,并将其附加到预训练模型的顶部。
  • 训练模型:使用目标数据集来训练模型。由于你可能会冻结一些层,因此需要确保优化器只更新未冻结的参数。
  • 评估模型:在验证集上评估模型性能,必要时调整超参数或继续微调模型。

案例

导包

import torch 
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as pltimport torchvision
from torchvision import transforms

数据目录

base_dir = './dataset'
train_dir = os.path.join(base_dir, 'train')
test_dir = os.path.join(base_dir, 'test')
<IPython.core.display.Javascript object><IPython.core.display.Javascript object>

图像数据转换

transform = transforms.Compose([# 统一缩放到96 * 96transforms.Resize((96, 96)),transforms.ToTensor(),# 正则化transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])
train_ds = torchvision.datasets.ImageFolder(train_dir, transform=transform)
test_ds = torchvision.datasets.ImageFolder(test_dir, transform=transform)batch_size = 32
train_dl = torch.utils.data.DataLoader(train_ds, batch_size=batch_size, shuffle=True, drop_last=True)
test_dl = torch.utils.data.DataLoader(test_ds, batch_size=batch_size)

加载预训练好的模型(VGG16)

# 加载预训练好的模型
model = torchvision.models.vgg16(pretrained=True)
model
VGG((features): Sequential((0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(1): ReLU(inplace=True)(2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(3): ReLU(inplace=True)(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(6): ReLU(inplace=True)(7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(8): ReLU(inplace=True)(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(11): ReLU(inplace=True)(12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(13): ReLU(inplace=True)(14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(15): ReLU(inplace=True)(16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(18): ReLU(inplace=True)(19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(20): ReLU(inplace=True)(21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(22): ReLU(inplace=True)(23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(25): ReLU(inplace=True)(26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(27): ReLU(inplace=True)(28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(29): ReLU(inplace=True)(30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(avgpool): AdaptiveAvgPool2d(output_size=(7, 7))(classifier): Sequential((0): Linear(in_features=25088, out_features=4096, bias=True)(1): ReLU(inplace=True)(2): Dropout(p=0.5, inplace=False)(3): Linear(in_features=4096, out_features=4096, bias=True)(4): ReLU(inplace=True)(5): Dropout(p=0.5, inplace=False)(6): Linear(in_features=4096, out_features=1000, bias=True))
)
model.features.parameters()
<generator object Module.parameters at 0x000001F0515FBAF0>
for param in model.features.parameters():param.requires_grad = False

修改原网络中的输出层的结构

# 修改原网络中的输出层的结构.
model.classifier[-1].out_features = 4

另一种修改输出层的写法

# 另一种修改输出层的写法. 
model.classifier[-1] = torch.nn.Linear(model.classifier[-1], 4)
Linear(in_features=4096, out_features=4, bias=True)

拷到gpu上

# 拷到gpu上
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
model.to(device)
VGG((features): Sequential((0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(1): ReLU(inplace=True)(2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(3): ReLU(inplace=True)(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(6): ReLU(inplace=True)(7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(8): ReLU(inplace=True)(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(11): ReLU(inplace=True)(12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(13): ReLU(inplace=True)(14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(15): ReLU(inplace=True)(16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(18): ReLU(inplace=True)(19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(20): ReLU(inplace=True)(21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(22): ReLU(inplace=True)(23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)(24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(25): ReLU(inplace=True)(26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(27): ReLU(inplace=True)(28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))(29): ReLU(inplace=True)(30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False))(avgpool): AdaptiveAvgPool2d(output_size=(7, 7))(classifier): Sequential((0): Linear(in_features=25088, out_features=4096, bias=True)(1): ReLU(inplace=True)(2): Dropout(p=0.5, inplace=False)(3): Linear(in_features=4096, out_features=4096, bias=True)(4): ReLU(inplace=True)(5): Dropout(p=0.5, inplace=False)(6): Linear(in_features=4096, out_features=4, bias=True))
)

优化器和损失函数

optimizer = optim.Adam(model.parameters(), lr=0.001)
loss_fn = nn.CrossEntropyLoss()

定义训练函数

def fit(epoch, model, train_loader, test_loader):correct = 0total = 0running_loss = 0model.train()for x, y in train_loader:# 把数据放到GPU上去. x, y = x.to(device), y.to(device)y_pred = model(x)loss = loss_fn(y_pred, y)optimizer.zero_grad()loss.backward()optimizer.step()with torch.no_grad():y_pred = torch.argmax(y_pred, dim=1)correct += (y_pred == y).sum().item()total += y.size(0)running_loss += loss.item()epoch_loss = running_loss / len(train_loader.dataset)epoch_acc = correct / total# 测试过程test_correct = 0test_total = 0test_running_loss = 0model.eval()with torch.no_grad():for x, y in test_loader:x, y = x.to(device), y.to(device)y_pred = model(x)loss = loss_fn(y_pred, y)y_pred = torch.argmax(y_pred, dim=1)test_correct += (y_pred == y).sum().item()test_total += y.size(0)test_running_loss += loss.item()test_epoch_loss = test_running_loss / len(test_loader.dataset)test_epoch_acc = test_correct / test_totalprint('epoch: ', epoch,'loss: ', round(epoch_loss, 3),'accuracy: ', round(epoch_acc, 3),'test_loss: ', round(test_epoch_loss, 3),'test_accuracy: ', round(test_epoch_acc, 3))return epoch_loss, epoch_acc, test_epoch_loss, test_epoch_acc

训练

epochs = 10
train_loss = []
train_acc = []
test_loss = []
test_acc = []
for epoch in range(epochs):epoch_loss, epoch_acc, test_epoch_loss, test_epoch_acc = fit(epoch, model, train_dl, test_dl)train_loss.append(epoch_loss)train_acc.append(epoch_acc)test_loss.append(test_epoch_loss)test_acc.append(test_epoch_acc)
epoch:  0 loss:  0.079 accuracy:  0.709 test_loss:  0.012 test_accuracy:  0.933
epoch:  1 loss:  0.007 accuracy:  0.951 test_loss:  0.014 test_accuracy:  0.938
epoch:  2 loss:  0.012 accuracy:  0.942 test_loss:  0.014 test_accuracy:  0.942
epoch:  3 loss:  0.01 accuracy:  0.95 test_loss:  0.03 test_accuracy:  0.898
epoch:  4 loss:  0.009 accuracy:  0.96 test_loss:  0.014 test_accuracy:  0.956
epoch:  5 loss:  0.002 accuracy:  0.982 test_loss:  0.017 test_accuracy:  0.964
epoch:  6 loss:  0.003 accuracy:  0.991 test_loss:  0.026 test_accuracy:  0.933
epoch:  7 loss:  0.008 accuracy:  0.974 test_loss:  0.036 test_accuracy:  0.933
epoch:  8 loss:  0.019 accuracy:  0.96 test_loss:  0.04 test_accuracy:  0.951
epoch:  9 loss:  0.019 accuracy:  0.968 test_loss:  0.082 test_accuracy:  0.942
model.parameters()
<generator object Module.parameters at 0x000001F04BA67DB0>
plt.plot(range(1, epochs+1), train_loss, label='train_loss')
plt.plot(range(1, epochs+1), test_loss, label='test_loss')
plt.legend()
<matplotlib.legend.Legend at 0x1f09c7aad68>

在这里插入图片描述

plt.plot(range(1, epochs+1), train_acc, label='train_acc')
plt.plot(range(1, epochs+1), test_acc, label='test_acc')
plt.legend()
<matplotlib.legend.Legend at 0x1f062c532e8>

在这里插入图片描述

模型保存。state_dict, 是一个字典, 保存了训练模型

model.state_dict()
OrderedDict([('features.0.weight',tensor([[[[-5.5373e-01,  1.4270e-01,  5.2896e-01],[-5.8312e-01,  3.5655e-01,  7.6566e-01],[-6.9022e-01, -4.8019e-02,  4.8409e-01]],[[ 1.7548e-01,  9.8630e-03, -8.1413e-02],[ 4.4089e-02, -7.0323e-02, -2.6035e-01],[ 1.3239e-01, -1.7279e-01, -1.3226e-01]],....1.1987e-02, -2.5213e-02,  1.0111e-02,  3.8605e-02,  5.9450e-02,-2.3559e-02, -1.9590e-02, -1.3793e-03,  1.9122e-02, -2.9245e-02,-1.8233e-02,  1.8062e-02, -4.7170e-02,  2.3613e-03,  5.9311e-02,-2.2850e-02, -4.1937e-02, -9.4028e-02, -3.3440e-02, -8.7492e-03,2.1338e-02, -2.2048e-03, -2.3466e-02, -3.6288e-02, -3.4857e-02,-1.5877e-02, -1.4878e-02, -4.4460e-02,  4.1556e-04, -5.2920e-02,-3.3104e-02, -4.3998e-02, -8.0722e-02,  1.8624e-03, -8.0641e-02,-1.0499e-01, -3.8824e-02, -4.2339e-02, -2.5216e-02, -3.0640e-02,5.8134e-04, -1.6703e-02, -5.3450e-02, -2.9327e-02, -4.9636e-02,-1.7681e-02, -1.7017e-03, -2.4267e-02, -5.5113e-02, -5.1077e-02,5.5027e-02, -1.9267e-02,  1.8775e-02, -9.4351e-02,  8.4938e-03,-1.4278e-02,  4.2343e-02, -4.4048e-03, -5.8631e-02,  1.5991e-02,-9.0777e-03,  3.0489e-02,  2.1308e-02, -2.0702e-02,  4.4335e-03,9.7881e-03,  8.7422e-03,  5.1457e-02, -7.3706e-03, -5.8167e-02,-3.7386e-02, -4.3151e-02, -7.5032e-02, -9.4130e-03, -3.6172e-02,-5.3026e-02, -5.7748e-02, -5.2776e-02, -6.5282e-02, -3.8436e-02,-2.2584e-02, -5.9209e-02,  1.3151e-02, -4.4181e-02, -1.6432e-02],device='cuda:0'))])

保存参数

# 保存参数
path = './vgg16.pth'
torch.save(model.state_dict(), path)

数据增强

数据增强(Data Augmentation)是指通过对训练数据应用一系列随机变换,生成额外的训练样本,以此增加模型的泛化能力和鲁棒性。这有助于防止过拟合,尤其是在数据集较小的情况下。

在 PyTorch 中,数据增强主要通过 torchvision.transforms 模块实现。这个模块提供了多种图像转换操作,包括但不限于:

  • 几何变换:如水平翻转、垂直翻转、旋转、缩放和平移。
  • 颜色变换:如亮度调整、对比度调整、饱和度调整和色调调整。
  • 裁剪:如中心裁剪、随机裁剪和弹性变形。
  • 噪声添加:如高斯噪声和其他类型的噪声。
  • 标准化:将像素值归一化到特定范围,例如 [0, 1] 或 [-1, 1]。
  • 其他变换:如仿射变换、透视变换等。

这些变换可以单独使用,也可以组合起来形成一个复杂的增强管道。transforms.Compose 函数允许你按顺序定义多个转换,并一次性应用于输入数据。此外,还可以结合自定义函数来实现更高级别的增强逻辑。

数据增强不仅限于图像数据,也可以应用于其他类型的数据,比如文本(如词序扰动)、音频(如音调变化)等。对于不同的任务和数据类型,选择合适的数据增强策略非常重要,因为它可以直接影响模型的表现。

案例

  • 随机位置的裁剪 , CenterCrop 中间位置裁剪
  • 随机旋转
  • 水平翻转
  • 垂直翻转
  • 亮度
  • 对比度
  • 饱和度
  • 随机灰度化
transforms.RandomCrop    # 随机位置的裁剪 , CenterCrop 中间位置裁剪
transforms.RandomRotation # 随机旋转
transforms.RandomHorizontalFlip() # 水平翻转
transforms.RandomVerticalFlip() # 垂直翻转
transforms.ColorJitter(brightness) # 亮度
transforms.ColorJitter(contrast) # 对比度
transforms.ColorJitter(saturation) # 饱和度
transforms.ColorJitter(hue)
transforms.RandomGrayscale() # 随机灰度化.

数据增强只会加在训练数据上

# 数据增强只会加在训练数据上. 
train_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.RandomCrop(192), transforms.RandomHorizontalFlip(),transforms.RandomVerticalFlip(),transforms.RandomRotation(0.4),
#     transforms.ColorJitter(brightness=0.5),
#     transforms.ColorJitter(contrast=0.5),transforms.ToTensor(),# 正则化transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])
test_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(),# 正则化transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])

其他代码都一样,再看一下训练过程:
train_loss、test_loss
在这里插入图片描述
train_acc、test_acc
在这里插入图片描述
对比数据增强的前后结果,数据增强后,训练和测试损失变化明显比增强前要更收敛,效果更好。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词