发现了一篇比较漂亮的resnet代码,借鉴,学习。原文
迁移学习
迁移学习的两种方式
- 微调。从线上下载以训练完毕的模型,利用本地数据集进行参数的微调,更新的是所有参数
- 用作特征提取器。外加一层全连接,只训练全连接部分的参数
1. 训练阶段
值得参考的tricks有:
- 训练过程中保存最优模型的参数,在每一个epochs中,若验证的准确率有所提升则更新参数
- 每个epoch中都包含训练和验证两个阶段。注意写法
# train
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
def train_model(model, criterion, optimizer, scheduler, num_epochs=25): # 括号中的参数是模型,损失函数标准,优化器,学习速率衰减方式,训练次数
best_model_wts = copy.deepcopy(model.state_dict()) # 先深拷贝一份当前模型的参数(wts=weights),后面迭代过程中若遇到更优模型则替换
best_acc = 0.0 # 最佳正确率,用于判断是否替换best_model_wts
for epoch in range(num_epochs): # 开启每一个epoch
print('Epoch {}/{}'.format(epoch + 1, num_epochs))
for phase in ['train', 'val']: # 每个epoch中都包含训练与验证两个阶段
if phase == 'train':
model.train()
else:
model.eval()
# 与train不同的是,test过程中没有batch-normalization与dropout,因此要区别对待。
# batchnorm是针对minibatch的,测试过程中每个样本单独测试,不存在minibatch
running_loss = 0.0
running_corrects = 0
for inputs, labels in dataloaders[phase]:
inputs = inputs.to(device)
labels = labels.to(device)
optimizer.zero_grad()
with torch.set_grad_enabled(phase == 'train'): # torch.set_grad_enabled(False/True)是上下文管理器,用于确定是否对with下的所有语句设计的参数求导,如果设置为false则新节点不可求导
outputs = model(inputs) # 网络模型的前向传播,就是为了从输入得到输出
_, preds = torch.max(outputs, 1) # 在维度1(行方向)查找最大值
loss = criterion(outputs, labels) # 输出结果与label相比较
if phase == 'train':
loss.backward() # 误差反向传播,计算每个w与b的更新值
optimizer.step() # 将这些更新值施加到模型上
# train, val都一样
running_loss += loss.item() * inputs.size(0) # 计算当前epoch过程中,所有batch的损失和
running_corrects += torch.sum(preds == labels.data) # 判断正确的样本数
if phase == 'train': # 完成本次epoch所有样本的训练与验证之后,就对学习速率进行修正
scheduler.step() # 在训练过程中,要根据损失的情况修正学习速率
epoch_loss = running_loss / dataset_sizes[phase] # 当前epoch的损失值是loss总和除以样本数
epoch_acc = running_corrects.double() / dataset_sizes[phase] # 当前epoch的正确率
print('{} Loss: {:.4f} Acc: {:.4f}'.format( # 输出train/test,损失、正确率
phase, epoch_loss, epoch_acc))
# deep copy the model
if phase == 'val' and epoch_acc > best_acc: # 如果是val阶段,并且当前epoch的acc比best acc大
best_acc = epoch_acc # 就替换best acc为当前epoch的acc
best_model_wts = copy.deepcopy(model.state_dict()) # 将best_model替换为当前模型
print('Best val Acc: {:4f}'.format(best_acc))
# 将最佳模型的相关参数加载到model中
model.load_state_dict(best_model_wts)
return model
2. 迁移学习部分
对模型所有层的所有参数都进行目标域的训练
model_ft = models.resnet18(pretrained=True) # 加载预训练参数
num_ftrs = model_ft.fc.in_features # 全连接层的输入的特征数
class_dim = 12
# 修改下载的model的全连接层的输入和输出维度
model_ft.fc = nn.Linear(num_ftrs, class_dim) # 利用线性映射将原来的num_ftrs转换为总特征数
# 将最后一个全连接由(512, 1000)改为(512, 12) 因为原网络是在1000类的ImageNet数据集上训练的
model_ft = model_ft.to(device) # 设置计算采用的设备,GPU还是CPU
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数
# 优化器,对加载的模型中所有参数都进行优化
optimizer_ft = torch.optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9) # 选用 较小的学习率
# 学习率每step_size个周期 降低 gamma倍
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler,num_epochs=25) # 模型训练