【深度学习笔记】-代码解读6-迁移学习

转载「深度学习一遍过」必修6:利用迁移学习快速提升模型性能_荣仔的博客-CSDN博客

迁移学习的概念 

  • 神经网络需要用数据来训练,它从数据中获得信息,进而把它们转换成相应的权重
  • 这些权重能够被提取出来,迁移到其他的神经网络中,
  • 即“迁移”了这些学来的特征,就不需要从零开始训练一个神经网络了 。

迁移学习就像站在巨人的肩膀上,借助已有的高性能模型训练自己的算法,它可以为你节省大量功夫,使得自己模型性能快速飙升。

案例学习

1 加载ResNet18

pretrain_model = resnet18(pretrained=False) # 加载ResNet
print(pretrain_model)
  • 只需要网络结构,不需要用预训练模型的参数来初始化时 pretrained = False

2 修改模型结构

num_ftrs = pretrain_model.fc.in_features    # 获取全连接层的输入
pretrain_model.fc = nn.Linear(num_ftrs, 5)  # 全连接层改为不同的输出
print(pretrain_model)
# 加载预先训练好的模型
pretrained_dict = torch.load('./resnet18_pretrain.pth')
print(pretrained_dict)

事实上,就是实现一个 y = kx + b 的操作,其中:

  • x:图片对应输入
  • y:整个模型输出
  • k:权重
  • b:偏置
# 修改resnet18的最后一层
# 弹出fc层参数,为后面修改fc层权重做准备
pretrained_dict.pop('fc.weight')
pretrained_dict.pop('fc.bias')
print(pretrained_dict)
model_dict = pretrain_model.state_dict()
print(model_dict)
# 去除不需要的参数
{k: v for k, v in pretrained_dict.items() if k in model_dict}
#模型参数更新
model_dict.update(pretrained_dict)
print(model_dict)
# 冻结部分层——将满足条件的参数的 requires_grad 属性设置为 False。
# requires_grad 为 true 进行更新,为 False 时权重和偏置不进行更新。
for name, value in pretrain_model.named_parameters():
    if (name != 'fc.weight') and (name != 'fc.bias'):
        value.requires_grad = False

# 将模型中属性 requires_grad = True 的参数选出来(要更新的参数在 parms_conv 当中)
filter(lambda p: p.requires_grad, pretrain_model.parameters())

# 定义损失函数(分类常用交叉熵),计算相差多少
loss_fn = nn.CrossEntropyLoss()

# 控制优化器只更新需要更新的层,这里使用随机梯度下降优化器
optimizer = torch.optim.SGD(params_conv, lr=1e-3)  

3 整体概览 

  • Create Dateset
'''
生成训练集和测试集,保存在txt文件中
'''
# 相当于模型的输入。后面做数据加载器dataload的时候从里面读他的数据
import os
import random#打乱数据用的
 
# 百分之60用来当训练集
train_ratio = 0.6
 
# 用来当测试集
test_ratio = 1-train_ratio
 
rootdata = r"data"#数据的根目录
 
train_list, test_list = [],[]#读取里面每一类的类别
data_list = []
 
#生产train.txt和test.txt
class_flag = -1
for a,b,c in os.walk(rootdata):
    print(a)
    for i in range(len(c)):
        data_list.append(os.path.join(a,c[i]))
 
    for i in range(0,int(len(c)*train_ratio)):
        train_data = os.path.join(a, c[i])+'\t'+str(class_flag)+'\n'
        train_list.append(train_data)
 
    for i in range(int(len(c) * train_ratio),len(c)):
        test_data = os.path.join(a, c[i]) + '\t' + str(class_flag)+'\n'
        test_list.append(test_data)
 
    class_flag += 1
 
print(train_list)
random.shuffle(train_list)#打乱次序
random.shuffle(test_list)
 
with open('train.txt','w',encoding='UTF-8') as f:
    for train_img in train_list:
        f.write(str(train_img))
 
with open('test.txt','w',encoding='UTF-8') as f:
    for test_img in test_list:
        f.write(test_img)
  •  加载预训练模型
'''
    加载预训练模型,冻结层
'''
import torch
from torch import nn
from torch.utils.data import DataLoader
from utils import LoadData
from torchvision.models import resnet18
 
# 定义训练函数,需要
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    # 从数据加载器中读取batch(一次读取多少张,即批次数),X(图片数据),y(图片真实标签)。
    for batch, (X, y) in enumerate(dataloader):
        # 将数据存到显卡
        X, y = X.cuda(), y.cuda()
 
        # 得到预测的结果pred
        pred = model(X)
 
        # 计算预测的误差
        loss = loss_fn(pred, y)
 
        # 反向传播,更新模型参数
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
 
        # 每训练100次,输出一次当前信息
        if batch % 10 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
 
 
def test(dataloader, model):
    size = len(dataloader.dataset)
    print("size = ", size)
    # 将模型转为验证模式
    model.eval()
    # 初始化test_loss 和 correct, 用来统计每次的误差
    test_loss, correct = 0, 0
    # 测试时模型参数不用更新,所以no_gard()
    # 非训练, 推理期用到
    with torch.no_grad():
        # 加载数据加载器,得到里面的X(图片数据)和y(真实标签)
        for X, y in dataloader:
            # 将数据转到GPU
            X, y = X.cuda(), y.cuda()
            # 将图片传入到模型当中就,得到预测的值pred
            pred = model(X)
            # 计算预测值pred和真实值y的差距
            test_loss += loss_fn(pred, y).item()
            # 统计预测正确的个数
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= size
    correct /= size
    print("correct = ", correct)
    print(f"Test Error: \n Accuracy: {(100 * correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
 
 
if __name__ == '__main__':
    batch_size = 8
 
    # 给训练集和测试集分别创建一个数据集加载器
    train_data = LoadData("train.txt", True)
    valid_data = LoadData("test.txt", False)
 
    train_dataloader = DataLoader(dataset=train_data, num_workers=4, pin_memory=True, batch_size=batch_size, shuffle=True)
    test_dataloader = DataLoader(dataset=valid_data, num_workers=4, pin_memory=True, batch_size=batch_size)
 
    # 如果显卡可用,则用显卡进行训练
    device = "cuda" if torch.cuda.is_available() else "cpu"
    print(f"Using {device} device")
 
    '''
            修改ResNet18模型的最后一层
    '''
    pretrain_model = resnet18(pretrained=False)   # 加载ResNet
    num_ftrs = pretrain_model.fc.in_features      # 获取全连接层的输入
    pretrain_model.fc = nn.Linear(num_ftrs, 5)    # 全连接层改为不同的输出
 
    pretrained_dict = torch.load('./resnet18_pretrain.pth')
 
    # 弹出fc层的参数
    pretrained_dict.pop('fc.weight')
    pretrained_dict.pop('fc.bias')
 
    # 自己的模型参数变量,在开始时里面参数处于初始状态,所以很多0和1
    model_dict = pretrain_model.state_dict()
    
    # 去除一些不需要的参数
    pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
    
    # 模型参数列表进行参数更新,加载参数
    model_dict.update(pretrained_dict)
 
    # 改进过的预训练模型结构,加载刚刚的模型参数列表
    pretrain_model.load_state_dict(model_dict)
 
    '''
        冻结部分层
    '''
    # 将满足条件的参数的 requires_grad 属性设置为False
    for name, value in pretrain_model.named_parameters():
        if (name != 'fc.weight') and (name != 'fc.bias'):
            value.requires_grad = False
            
    # filter 函数将模型中属性 requires_grad = True 的参数选出来
    params_conv = filter(lambda p: p.requires_grad, pretrain_model.parameters())    # 要更新的参数在parms_conv当中
    model = pretrain_model.to(device)
 
    # # 定义损失函数,计算相差多少,交叉熵,
    loss_fn = nn.CrossEntropyLoss()
 
    '''   控制优化器只更新需要更新的层  '''
    optimizer = torch.optim.SGD(params_conv, lr=1e-3)  # 初始学习率
    
    # 一共训练5次
    epochs = 5
    for t in range(epochs):
        print(f"Epoch {t + 1}\n-------------------------------")
        train(train_dataloader, model, loss_fn, optimizer)
        test(test_dataloader, model)
    print("Done!")
 
    # 保存训练好的模型
    torch.save(model.state_dict(), "model_resnet18.pth")
    print("Saved PyTorch Model Success!")
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值