当前位置: 首页 > news >正文

深度学习之迁移学习resnet18模型及调用模型预测

迁移学习resnet18模型及调用模型预测

目录

  • 迁移学习resnet18模型及调用模型预测
    • 1 迁移学习
      • 1.1 概念
      • 1.2 主要思想
      • 1.3 优点
      • 1.4 迁移学习的步骤
    • 2 模型迁移和调整
      • 2.1 ResNet18模型
      • 2.2 新数据
      • 2.3 冻结参数
      • 2.4 微调层
      • 2.5 新增层
      • 2.6 数据预处理
    • 3 代码测试
      • 3.1 微调模型代码测试及保存模型
      • 3.2 新增层模型训练与测试
      • 3.3 调用模型预测新数据

1 迁移学习


1.1 概念

迁移学习是指利用已经训练好的模型,在新的任务上进行微调。迁移学习可以加快模型训练速度,提高模型性能,并且在数据稀缺的情况下也能很好地工作。

1.2 主要思想

利用已有的知识来帮助解决新的问题。在深度学习中,这通常意味着使用在大型数据集上预训练的神经网络作为起点,然后针对特定任务进行微调(fine-tuning)。

1.3 优点

  • 减少数据需求:对于新任务,不需要大量标注数据来从头开始训练模型。
  • 提高性能:预训练模型已经学习了通用的特征表示,这有助于新任务上的性能提升。
  • 节省计算资源:迁移学习可以减少训练时间和计算资源的需求。

1.4 迁移学习的步骤

  • 1.选择预训练的模型和适当的层:通常,我们会选择在大规模图像数据集(如lmageNet)上预训练的模型,如VGG、ResNet等。然后,根据新数据集的特点,选择需要微调的模型层。对于低级特征的任务(如边缘检测),最好使用浅层模型的层,而对于高级特征的任务(如分类),则应选择更深层次的模型。
  • 2.冻结预训练模型的参数:保持预训练模型的权重不变,只训练新增加的层或者微调一些层,避免因为在数据集中过拟合导致预训练模型过度拟合。
  • 3.在新数据集上训练新增加的层:在冻结预训练模型的参数情况下,训练新增加的层。这样,可以使新模型适应新的任务,从而获得更高的性能。
  • 4.微调预训练模型的层:在新层上进行训练后,可以解冻一些已经训练过的层,并且将它们作为微调的目标。这样做可以提高模型在新数据集上的性能。
  • 5.评估和测试:在训练完成之后,使用测试集对模型进行评估。如果模型的性能仍然不够好,可以尝试调整超参数或者更改微调层。

2 模型迁移和调整


2.1 ResNet18模型

在这里插入图片描述

2.2 新数据

新数据保存在train.txt和test.txt里,内容如下,是食物图片地址和食物图片的类别,一共有20种类别。
在这里插入图片描述

2.3 冻结参数

保持预训练模型的权重不变,模型的参数中有requires_grad,为True时权重参数会变化,为False时不会,所以对预训练模型所有参数冻结可指定:
model.parameters().requires_grad=False

for param in resnet_model.parameters():
    print(param)
    param.requires_grad = False

2.4 微调层

由于模型的输出为1000,而我们新数据的结果类别为20个,所以需要更改微调输出层。
更改输出层的输出,输入不变,需要先获取输入再重新定义输出层,如下,

## 获取输入
in_features = resnet_model.fc.in_features
# 重构输出
resnet_model.fc = nn.Linear(in_features,20)

2.5 新增层

需要保存原模型的权重参数不变,再新增一层输入输出分别为原模型的输出、新数据特征类别数。需要重新定义模型,可以先调用resnet18模型,再将其输出的1000,及新数据的20作为输入输出重新定义

class Resnet_add(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc_add = torch.nn.Linear(1000, 20)

    def forward(self, x):
        x = resnet_model(x)
        out = self.fc_add(x)
        return out

2.6 数据预处理

由于模型的输入图片是224*224,所以需要对我们的新数据进行处理,同时也进行了数据增强

data_transforms = {     #字典
    'train':
        transforms.Compose([        #  对图片做预处理的。组合,
        transforms.Resize([300,300]),   #数据进行改变大小[256,256]
        transforms.RandomRotation(45),
        transforms.CenterCrop(224),
        transforms.RandomHorizontalFlip(p=0.5),
        transforms.RandomVerticalFlip(0.5),
        transforms.RandomGrayscale(0.1),
        transforms.ToTensor(),          #数据转换为tensor,默认把通道维度放在前面
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),
    ]),
    'valid':
        transforms.Compose([
        transforms.Resize([224,224]),
        transforms.ToTensor(),
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),
    ]),
}

3 代码测试


3.1 微调模型代码测试及保存模型

模型保存:torch.save(model.state_dict(),‘best_rnt18.pth’)

代码展示:

import torch
from torch.utils.data import Dataset,DataLoader     #用于处理数据集的
import numpy as np
from PIL import Image       #
from torchvision import transforms      #对数据进行处理工具  转换
import torchvision.models as models
from torch import nn
resnet_model = models.resnet18(weights = models.ResNet18_Weights.DEFAULT)
for param in resnet_model.parameters():
    print(param)
    param.requires_grad = False

in_features = resnet_model.fc.in_features
resnet_model.fc = nn.Linear(in_features,20)
params_to_update = []
for param in resnet_model.parameters():
    if param.requires_grad == True:
        params_to_update.append(param)


data_transforms = {     #字典
    'train':
        transforms.Compose([        #  对图片做预处理的。组合,
        transforms.Resize([300,300]),   #数据进行改变大小[256,256]
        transforms.RandomRotation(45),
        transforms.CenterCrop(224),
        transforms.RandomHorizontalFlip(p=0.5),
        transforms.RandomVerticalFlip(0.5),
        transforms.RandomGrayscale(0.1),
        transforms.ToTensor(),          #数据转换为tensor,默认把通道维度放在前面
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),
    ]),
    'valid':
        transforms.Compose([
        transforms.Resize([224,224]),
        transforms.ToTensor(),
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),
    ]),
}#数组增强,
#Dataset是用来处理数据的。
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
model = resnet_model.to(device)
class food_dataset(Dataset):       #   food_dataset是自己创建的类名称,可以改为你需要的名称
    def __init__(self, file_path,transform=None): #类的初始化,解析数据文件txt
        self.file_path = file_path
        self.imgs = []
        self.labels = []
        self.transform = transform
        with open(self.file_path) as f:#是把train.txt文件中图片的路径保存在 self.imgs,train.txt文件中标签保存在 self.labels
            samples = [x.strip().split(' ') for x in f.readlines()]
            for img_path, label in samples:
                self.imgs.append(img_path)  #图像的路径
                self.labels.append(label)   #标签,还不是tensor
#初始化:把图片目录加载到self,
    def __len__(self):  #类实例化对象后,可以使用len函数测量对象的个数
        return len(self.imgs)
    #training_data[1]
    def __getitem__(self, idx): #关键,可通过索引的形式获取每一个图片数据及标签
        image = Image.open(self.imgs[idx])   #读取到图片数据,还不是tensor,BGR
        if self.transform:                   #将pil图像数据转换为tensor
            image = self.transform(image)    #图像处理为256*256,转换为tenor

        label = self.labels[idx]        #label还不是tensor
        label = torch.from_numpy(np.array(label,dtype = np.int64))  #label也转换为tensor,
        return image, label

training_data = food_dataset(file_path = './train_test/train.txt',transform = data_transforms['train'])  #
test_data = food_dataset(file_path = './train_test/test.txt',transform = data_transforms['valid'])
# test_data = food_dataset(file_path = './test_true.txt',transform = data_transforms['valid'])

#training_data需要具备索引的功能,还要确保数据是tensor
train_dataloader = DataLoader(training_data, batch_size=64,shuffle=True)#64张图片为一个包,
test_dataloader = DataLoader(test_data, batch_size=64,shuffle=True)


loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(params_to_update, lr=0.001)

def train(dataloader, model, loss_fn, optimizer):
    model.train()
#pytorch提供2种方式来切换训练和测试的模式,分别是:model.train() 和 model.eval()。
# 一般用法是:在训练开始之前写上model.trian(),在测试时写上 model.eval() 。
    batch_size_num = 1
    for X, y in dataloader:                 #其中batch为每一个数据的编号
        X, y = X.to(device), y.to(device)   #把训练数据集和标签传入cpu或GPU
        pred = model.forward(X)             #自动初始化 w权值
        loss = loss_fn(pred, y)             #通过交叉熵损失函数计算损失值loss
        # Backpropagation 进来一个batch的数据,计算一次梯度,更新一次网络
        optimizer.zero_grad()               #梯度值清零
        loss.backward()                     #反向传播计算得到每个参数的梯度值
        optimizer.step()                    #根据梯度更新网络参数
        loss = loss.item()                  #获取损失值
        if batch_size_num %1 == 0:
            print(f"loss: {loss:>7f}  [number:{batch_size_num}]")
        batch_size_num += 1

best_acc = 0
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()    #测试模式
    test_loss, correct = 0, 0
    global best_acc
    with torch.no_grad():   #一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()的时候。这可以减少计算所用内存消耗。
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model.forward(X)
            test_loss += loss_fn(pred, y).item() #
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
            a = (pred.argmax(1) == y)  #dim=1表示每一行中的最大值对应的索引号,dim=0表示每一列中的最大值对应的索引号
            b = (pred.argmax(1) == y).type(torch.float)
    test_loss /= num_batches
    correct /= size
    if correct > best_acc:
        best_acc = correct
        print(model.state_dict().keys())
        torch.save(model.state_dict(),'best_rnt18.pth')
        print(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")

scheduler = torch.optim.lr_scheduler.StepLR(optimizer,step_size=5,gamma=0.5)

epochs = 20
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    scheduler.step()
    test(test_dataloader, model, loss_fn)
print(f"Done!,best: {best_acc}")
model.load_state_dict(torch.load('best_rnt18.pth'))

运行结果:

在这里插入图片描述

3.2 新增层模型训练与测试

代码展示:

import torch
from torch.utils.data import Dataset,DataLoader     #用于处理数据集的
import numpy as np
from PIL import Image       #
from torchvision import transforms      #对数据进行处理工具  转换
import torchvision.models as models
from torch import nn

resnet_model = models.resnet18(weights = models.ResNet18_Weights.DEFAULT)
for param in resnet_model.parameters():
    print(param)
    param.requires_grad = False

data_transforms = {     #字典
    'train':
        transforms.Compose([        #  对图片做预处理的。组合,
        transforms.Resize([300,300]),   #数据进行改变大小[256,256]
        transforms.RandomRotation(45),
        transforms.CenterCrop(224),
        transforms.RandomHorizontalFlip(p=0.5),
        transforms.RandomVerticalFlip(0.5),
        transforms.RandomGrayscale(0.1),
        transforms.ToTensor(),          #数据转换为tensor,默认把通道维度放在前面
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),
    ]),
    'valid':
        transforms.Compose([
        transforms.Resize([224,224]),
        transforms.ToTensor(),
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),
    ]),
}#数组增强,
#Dataset是用来处理数据的。
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"


class Resnet_add(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc_add = torch.nn.Linear(1000, 20)

    def forward(self, x):
        x = resnet_model(x)
        out = self.fc_add(x)
        return out

model = Resnet_add().to(device)
params_to_update = []
for param in model.parameters():
    if param.requires_grad == True:
        params_to_update.append(param)
# model = models.resnet18(weights = models.ResNet18_Weights.DEFAULT)
# model = resnet_model.to(device)
class food_dataset(Dataset):       #   food_dataset是自己创建的类名称,可以改为你需要的名称
    def __init__(self, file_path,transform=None): #类的初始化,解析数据文件txt
        self.file_path = file_path
        self.imgs = []
        self.labels = []
        self.transform = transform
        with open(self.file_path) as f:#是把train.txt文件中图片的路径保存在 self.imgs,train.txt文件中标签保存在 self.labels
            samples = [x.strip().split(' ') for x in f.readlines()]
            for img_path, label in samples:
                self.imgs.append(img_path)  #图像的路径
                self.labels.append(label)   #标签,还不是tensor
#初始化:把图片目录加载到self,
    def __len__(self):  #类实例化对象后,可以使用len函数测量对象的个数
        return len(self.imgs)
    #training_data[1]
    def __getitem__(self, idx): #关键,可通过索引的形式获取每一个图片数据及标签
        image = Image.open(self.imgs[idx])   #读取到图片数据,还不是tensor,BGR
        if self.transform:                   #将pil图像数据转换为tensor
            image = self.transform(image)    #图像处理为256*256,转换为tenor

        label = self.labels[idx]        #label还不是tensor
        label = torch.from_numpy(np.array(label,dtype = np.int64))  #label也转换为tensor,
        return image, label

training_data = food_dataset(file_path = './train_test/train.txt',transform = data_transforms['train'])  #
test_data = food_dataset(file_path = './train_test/test.txt',transform = data_transforms['valid'])
# test_data = food_dataset(file_path = './test_true.txt',transform = data_transforms['valid'])

#training_data需要具备索引的功能,还要确保数据是tensor
train_dataloader = DataLoader(training_data, batch_size=64,shuffle=True)#64张图片为一个包,
test_dataloader = DataLoader(test_data, batch_size=64,shuffle=True)


loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(params_to_update, lr=0.001)

def train(dataloader, model, loss_fn, optimizer):
    model.train()
#pytorch提供2种方式来切换训练和测试的模式,分别是:model.train() 和 model.eval()。
# 一般用法是:在训练开始之前写上model.trian(),在测试时写上 model.eval() 。
    batch_size_num = 1
    for X, y in dataloader:                 #其中batch为每一个数据的编号
        X, y = X.to(device), y.to(device)   #把训练数据集和标签传入cpu或GPU
        pred = model.forward(X)             #自动初始化 w权值
        loss = loss_fn(pred, y)             #通过交叉熵损失函数计算损失值loss
        # Backpropagation 进来一个batch的数据,计算一次梯度,更新一次网络
        optimizer.zero_grad()               #梯度值清零
        loss.backward()                     #反向传播计算得到每个参数的梯度值
        optimizer.step()                    #根据梯度更新网络参数
        loss = loss.item()                  #获取损失值
        if batch_size_num %1 == 0:
            print(f"loss: {loss:>7f}  [number:{batch_size_num}]")
        batch_size_num += 1

best_acc = 0
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()    #测试模式
    test_loss, correct = 0, 0
    global best_acc
    with torch.no_grad():   #一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()的时候。这可以减少计算所用内存消耗。
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model.forward(X)
            test_loss += loss_fn(pred, y).item() #
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
            a = (pred.argmax(1) == y)  #dim=1表示每一行中的最大值对应的索引号,dim=0表示每一列中的最大值对应的索引号
            b = (pred.argmax(1) == y).type(torch.float)
    test_loss /= num_batches
    correct /= size
    if correct > best_acc:
        best_acc = correct
        print(model.state_dict().keys())
        torch.save(model.state_dict(),'best_rnt18_add.pth')
    print(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")

scheduler = torch.optim.lr_scheduler.StepLR(optimizer,step_size=5,gamma=0.5)

epochs = 20
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    scheduler.step()
    test(test_dataloader, model, loss_fn)
print(f"Done!,best: {best_acc}")

运行结果:
在这里插入图片描述

3.3 调用模型预测新数据

需要注意,调用保存的模型时,该模型的框架也需要先搭建好,才能加载训练好的模型参数,再预测

代码展示:

import torch
from torch.utils.data import Dataset,DataLoader     #用于处理数据集的
import numpy as np
from PIL import Image       #
from torchvision import transforms      #对数据进行处理工具  转换
import torchvision.models as models
from torch import nn

data_transforms = {     #字典
    'train':
        transforms.Compose([        #  对图片做预处理的。组合,
        transforms.Resize([300,300]),   #数据进行改变大小[256,256]
        transforms.RandomRotation(45),
        transforms.CenterCrop(224),
        transforms.RandomHorizontalFlip(p=0.5),
        transforms.RandomVerticalFlip(0.5),
        transforms.RandomGrayscale(0.1),
        transforms.ToTensor(),          #数据转换为tensor,默认把通道维度放在前面
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),
    ]),
    'valid':
        transforms.Compose([
        transforms.Resize([224,224]),
        transforms.ToTensor(),
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]),
    ]),
}#数组增强,
#Dataset是用来处理数据的。
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
# model = resnet_model.to(device)
class food_dataset(Dataset):       #   food_dataset是自己创建的类名称,可以改为你需要的名称
    def __init__(self, file_path,transform=None): #类的初始化,解析数据文件txt
        self.file_path = file_path
        self.imgs = []
        self.labels = []
        self.transform = transform
        with open(self.file_path) as f:#是把train.txt文件中图片的路径保存在 self.imgs,train.txt文件中标签保存在 self.labels
            samples = [x.strip().split(' ') for x in f.readlines()]
            for img_path, label in samples:
                self.imgs.append(img_path)  #图像的路径
                self.labels.append(label)   #标签,还不是tensor
#初始化:把图片目录加载到self,
    def __len__(self):  #类实例化对象后,可以使用len函数测量对象的个数
        return len(self.imgs)
    #training_data[1]
    def __getitem__(self, idx): #关键,可通过索引的形式获取每一个图片数据及标签
        image = Image.open(self.imgs[idx])   #读取到图片数据,还不是tensor,BGR
        if self.transform:                   #将pil图像数据转换为tensor
            image = self.transform(image)    #图像处理为256*256,转换为tenor

        label = self.labels[idx]        #label还不是tensor
        label = torch.from_numpy(np.array(label,dtype = np.int64))  #label也转换为tensor,
        return image, label
test_data = food_dataset(file_path = './test_true.txt',transform = data_transforms['valid'])
test_dataloader = DataLoader(test_data, batch_size=64,shuffle=True)
resnet_model = models.resnet18()
in_features = resnet_model.fc.in_features
resnet_model.fc = nn.Linear(in_features,20)
model = resnet_model
model.load_state_dict(torch.load('best_rnt18.pth'))
loss_fn = nn.CrossEntropyLoss()
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    model.eval()    #测试模式
    test_loss, correct = 0, 0
    global best_acc
    with torch.no_grad():   #一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()的时候。这可以减少计算所用内存消耗。
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model.forward(X)
            test_loss += loss_fn(pred, y).item() #
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
            a = (pred.argmax(1) == y)  #dim=1表示每一行中的最大值对应的索引号,dim=0表示每一列中的最大值对应的索引号
            b = (pred.argmax(1) == y).type(torch.float)
    test_loss /= num_batches
    correct /= size
    print(f'true:{y}')
    print(f'pre:{pred.argmax(1)}')
    print(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")

test(test_dataloader, model, loss_fn)

运行结果:
在这里插入图片描述

相关文章:

  • debezium专栏文章目录
  • 供应链管理:折旧、作业成本、分摊
  • nginx 部署前端vue项目
  • 基于Flask框架的食谱数据可视化分析系统的设计与实现
  • Mac M3/M4 本地部署Deepseek并集成vscode
  • 编写dockercompose脚本,管理redis,activemq,mysql5.7
  • spring boot知识点5
  • 大数据治理之solr的体现
  • 前端如何把SEO优化做到极致✅
  • vxe-table 如何实现跟 Excel 一样的数值或金额的负数自动显示红色字体
  • 【QT常用技术讲解】国产Linux桌面系统+window系统通过窗口句柄对窗口进行操作
  • web网络安全:跨站脚本攻击(XSS)
  • Windchill开发-Windchill REST
  • ETL工具: Kettle入门(示例从oracle到oracle的数据导入)
  • 【第二节】C++设计模式(创建型模式)-抽象工厂模式
  • 让win11右键默认显示更多选项
  • 【论文笔记】MambaGlue: Fast and Robust Local Feature Matching With Mamba
  • 通过FOFA进行DeepSeek仿冒资产发现实战
  • Python爬虫实战:获取腾牛网高清壁纸图片
  • OpenCV机器学习(5)逻辑回归算法cv::ml::LogisticRegression
  • 泰安钢管网站建设/凡客建站
  • 那些平台可以给网站做外链/seo网站关键词优化工具
  • 南昌做网站要多少钱/百度搜索使用方法
  • php培训学校网站源码/seo研究中心南宁线下
  • 湛江电子商务网站建设/手机百度云电脑版入口
  • 怎么做网站黑链/太原百度seo排名软件