- 背景
预训练模型使用的训练数据并非训练集,可能来自ImageNet数据库等
用于图像分类,像素语义分割,对象检测,实例分割,人员关键点检测和视频分类 - 重点
+特征提取:去掉输出层;仅提取分类的特征,为分类做准备
+采用预训练模型的结构:权重随机化
+训练特定层(比如分类层),冻结其他层:将模型起始的一些层的权重保持不变,重新训练后面的层,得到新的权重(冻结:不参与梯度的反向传播)
一、模块 & Data
1. 重点:迁移学习的模块
torchvision.models()
卷积层网络
- 前提:对输入图像的要求
+3通道RGB 维度:3224224(至少224个像素点)
+归一化(mean & std)
2. Python模块 & Data
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import torch
from torch import nn
from torch import optim
import torch.nn.functional as F
from torchvision import datasets, transforms models
data_dir = 'Cat_Dog_data' #本地文件的路径
#Define a transform to normalize the data
train_transforms = transforms.Compose([transforms.Resize(384),
transforms.CenterCrop(255),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])])
test_transforms = transforms.Compose([transforms.Resize(384),
transforms.CenterCrop(255),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])])
#Input data
train_data = datasets.ImageFolder(data_dir + '/train', transform=train_transforms)
test_data = datasets.ImageFolder(data_dir + '/test', transform=test_transforms)
#Dataloader
trainloader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
testloader = torch.utils.data.DataLoader(test_data, batch_size=64)
二、加载预训练模型
model = models.densenet121(pretrained=True)
-
特征层
应用:可以作为特征检测
-
分类层
全连接:1000个类别(需要修改:猫狗分类只有2个类别)
本质:迁移学习到的是特征(从权重信息可进一步抽象出特征)
三、建立模型
- 冻结参数:特征部分
冻结预训练模型中的所有参数
for param in model.parameters():
param.requires_grad = False
- 针对目标问题(猫狗分类),建立网络架构
注意:输入的维度与classifier.in_features
保持一致
from collections import OrderedDict
classifier = nn.Sequential(OrderedDict([
('fc1', nn.Linear(1024, 500)),
('relu', nn.ReLU()),
('fc2', nn.Linear(500, 2)),
('output', nn.LogSoftmax(dim=1))
]))
model.classifier = classifier
训练(过程省略)
测试(cpu VS cuda)
import time
for device in ['cpu', 'cuda']:
criterion = nn.NLLLoss()
#Only train the classifier parameters, feature parameters are frozen
optimizer = optim.Adam(model.classifier.parameters(), lr=0.001)
model.to(device)
for ii, (inputs, labels) in enumerate(trainloader):
inputs, labels - inputs.to(device), labels.to(device)
start = time.time()
outputs = model(inputs) #或者model.forward(inputs)??
loss = criterion(outputs, labels)
loss.backward() #反向传播
optimizer.step() #权重更新
if ii==3:
break
print(f"Device = {device}; Time per batch:{(time.time()-start)/3:.3f} seconds")
@me:同样的代码,我的GPU内存爆满
- 说明容量还是不够
- 要考虑代码优化
四、分析总结
- 知识点
对象:模型优化(更新权重)
optim.()
#optim.Adam()
optimizer = optim.Adam(model.classifier.parameters(), lr=0.001)
#optim.SGD()
optim.SGD([{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}], lr=1e-2, momentum=0.9)
#训练-内循环 梯度清零:梯度跟踪仅在反向传播阶段
optimizer.zero_grad()
对象:DataLoader
torch.utils.data.DataLoader类
本质:格式化数据
输出:迭代器
DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
batch_sampler=None, num_workers=0, collate_fn=None,
pin_memory=False, drop_last=False, timeout=0,
worker_init_fn=None)
#Dataset负责生产数据
#DataLoader负责数据的分批(batch_size)、采样(sampler)、传输
- 模型参数/属性
1. 模型
torch.nn.Module
nn.Sequential((nn.(), nn.(), ...))
,
nn.Sequential(OrderedDict([('', nn.()), ('', nn.()), ('', nn.()), ...]))
models.() 预训练模型:迁移学习
models.densenet121(pretrained=True)
2. 模型的层次
- 特征层
model.features
- 分类层
model.classifier
- 探索:模型层次的组合
#基于预训练模型
model_rechange = models.densenet121(pretrained=True)
#重构特征层
feature = nn.Sequential(OrderedDict([
('fc1', nn.Linear(1024, 500)),
('relu', nn.ReLU()),
('fc2', nn.Linear(500, 800)),
('output', nn.LogSoftmax(dim=1))
]))
model_rechange.features = feature #输出测试:特征层.features.parameters() 输出数据的维度是 torch.Size([800])
#保留分类层classifier
classifier = nn.Sequential(OrderedDict([
('fc1', nn.Linear(1024, 500)),
('relu', nn.ReLU()),
('fc2', nn.Linear(500, 2)),
('output', nn.LogSoftmax(dim=1))
]))
model_rechange.classifier = classifier
问题:feature层的输出是800,但classifier层的输入是1024,该模型是否可行?
3. 模型的具体参数
model.features.parameters()
model.classifier.parameters()
torch.nn.Parameter()