• 背景
    预训练模型使用的训练数据并非训练集,可能来自ImageNet数据库
    用于图像分类,像素语义分割,对象检测,实例分割,人员关键点检测和视频分类
  • 重点
    +特征提取:去掉输出层;仅提取分类的特征,为分类做准备
    +采用预训练模型的结构:权重随机化
    +训练特定层(比如分类层),冻结其他层:将模型起始的一些层的权重保持不变,重新训练后面的层,得到新的权重(冻结:不参与梯度的反向传播)

一、模块 & Data

1. 重点:迁移学习的模块

torchvision.models() 卷积层网络

  • 前提:对输入图像的要求
    +3通道RGB 维度:3_224_224(至少224个像素点)
    +归一化(mean & std)

    2. Python模块 & Data

  1. %matplotlib inline
  2. %config InlineBackend.figure_format = 'retina'
  3. import matplotlib.pyplot as plt
  4. import torch
  5. from torch import nn
  6. from torch import optim
  7. import torch.nn.functional as F
  8. from torchvision import datasets, transforms models
  9. data_dir = 'Cat_Dog_data' #本地文件的路径
  10. #Define a transform to normalize the data
  11. train_transforms = transforms.Compose([transforms.Resize(384),
  12. transforms.CenterCrop(255),
  13. transforms.ToTensor(),
  14. transforms.Normalize(mean=[0.485, 0.456, 0.406],
  15. std=[0.229, 0.224, 0.225])])
  16. test_transforms = transforms.Compose([transforms.Resize(384),
  17. transforms.CenterCrop(255),
  18. transforms.ToTensor(),
  19. transforms.Normalize(mean=[0.485, 0.456, 0.406],
  20. std=[0.229, 0.224, 0.225])])
  21. #Input data
  22. train_data = datasets.ImageFolder(data_dir + '/train', transform=train_transforms)
  23. test_data = datasets.ImageFolder(data_dir + '/test', transform=test_transforms)
  24. #Dataloader
  25. trainloader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
  26. testloader = torch.utils.data.DataLoader(test_data, batch_size=64)

二、加载预训练模型

model = models.densenet121(pretrained=True)

  • 特征层
    应用:可以作为特征检测
    PyTorch | 迁移学习 - 图1
  • 分类层
    PyTorch | 迁移学习 - 图2
    全连接:1000个类别(需要修改:猫狗分类只有2个类别)

本质:迁移学习到的是特征(从权重信息可进一步抽象出特征)

三、建立模型

1. 冻结参数:特征部分

冻结预训练模型中的所有参数

  1. for param in model.parameters():
  2. param.requires_grad = False

2. 针对目标问题(猫狗分类),建立网络架构

注意:输入的维度与classifier.in_features保持一致

  1. from collections import OrderedDict
  2. classifier = nn.Sequential(OrderedDict([
  3. ('fc1', nn.Linear(1024, 500)),
  4. ('relu', nn.ReLU()),
  5. ('fc2', nn.Linear(500, 2)),
  6. ('output', nn.LogSoftmax(dim=1))
  7. ]))
  8. model.classifier = classifier

3. 训练(过程省略)

4. 测试(cpu VS cuda)

  1. import time
  2. for device in ['cpu', 'cuda']:
  3. criterion = nn.NLLLoss()
  4. #Only train the classifier parameters, feature parameters are frozen
  5. optimizer = optim.Adam(model.classifier.parameters(), lr=0.001)
  6. model.to(device)
  7. for ii, (inputs, labels) in enumerate(trainloader):
  8. inputs, labels - inputs.to(device), labels.to(device)
  9. start = time.time()
  10. outputs = model(inputs) #或者model.forward(inputs)??
  11. loss = criterion(outputs, labels)
  12. loss.backward() #反向传播
  13. optimizer.step() #权重更新
  14. if ii==3:
  15. break
  16. print(f"Device = {device}; Time per batch:{(time.time()-start)/3:.3f} seconds")

PyTorch | 迁移学习 - 图3

@me:同样的代码,我的GPU内存爆满

  • 说明容量还是不够
  • 要考虑代码优化

四、分析总结

1. 知识点

  • 对象:模型优化(更新权重) optim.()
  1. #optim.Adam()
  2. optimizer = optim.Adam(model.classifier.parameters(), lr=0.001)
  3. #optim.SGD()
  4. optim.SGD([{'params': model.base.parameters()},
  5. {'params': model.classifier.parameters(), 'lr': 1e-3}], lr=1e-2, momentum=0.9)
  6. #训练-内循环 梯度清零:梯度跟踪仅在反向传播阶段
  7. optimizer.zero_grad()
  • 对象:DataLoader
    torch.utils.data.DataLoader类
    本质:格式化数据
    输出:迭代器
  1. DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
  2. batch_sampler=None, num_workers=0, collate_fn=None,
  3. pin_memory=False, drop_last=False, timeout=0,
  4. worker_init_fn=None)
  5. #Dataset负责生产数据
  6. #DataLoader负责数据的分批(batch_size)、采样(sampler)、传输

2. 模型参数/属性

  1. 模型
  • torch.nn.Module
    nn.Sequential((nn.(), nn.(), ...)),
    nn.Sequential(OrderedDict([('', nn.()), ('', nn.()), ('', nn.()), ...]))
  • models.() 预训练模型:迁移学习
    models.densenet121(pretrained=True)
    PyTorch | 迁移学习 - 图4
  1. 模型的层次
  • 特征层 model.features
  • 分类层 model.classifier
  • 探索:模型层次的组合
  1. #基于预训练模型
  2. model_rechange = models.densenet121(pretrained=True)
  3. #重构特征层
  4. feature = nn.Sequential(OrderedDict([
  5. ('fc1', nn.Linear(1024, 500)),
  6. ('relu', nn.ReLU()),
  7. ('fc2', nn.Linear(500, 800)),
  8. ('output', nn.LogSoftmax(dim=1))
  9. ]))
  10. model_rechange.features = feature #输出测试:特征层.features.parameters() 输出数据的维度是 torch.Size([800])
  11. #保留分类层classifier
  12. classifier = nn.Sequential(OrderedDict([
  13. ('fc1', nn.Linear(1024, 500)),
  14. ('relu', nn.ReLU()),
  15. ('fc2', nn.Linear(500, 2)),
  16. ('output', nn.LogSoftmax(dim=1))
  17. ]))
  18. model_rechange.classifier = classifier

PyTorch | 迁移学习 - 图5
问题:feature层的输出是800,但classifier层的输入是1024,该模型是否可行?

  1. 模型的具体参数
  • model.features.parameters()
  • model.classifier.parameters()
  • torch.nn.Parameter()