欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > 深度学习之迁移学习resnet18模型及调用模型预测

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

2025/2/22 16:45:03 来源:https://blog.csdn.net/m0_64588135/article/details/145689869  浏览:    关键词:深度学习之迁移学习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 = Falsein_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): #类的初始化,解析数据文件txtself.file_path = file_pathself.imgs = []self.labels = []self.transform = transformwith open(self.file_path) as f:#是把train.txt文件中图片的路径保存在 self.imgs,train.txt文件中标签保存在 self.labelssamples = [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,BGRif self.transform:                   #将pil图像数据转换为tensorimage = self.transform(image)    #图像处理为256*256,转换为tenorlabel = self.labels[idx]        #label还不是tensorlabel = torch.from_numpy(np.array(label,dtype = np.int64))  #label也转换为tensor,return image, labeltraining_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 = 1for X, y in dataloader:                 #其中batch为每一个数据的编号X, y = X.to(device), y.to(device)   #把训练数据集和标签传入cpu或GPUpred = 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 += 1best_acc = 0
def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval()    #测试模式test_loss, correct = 0, 0global best_accwith 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_batchescorrect /= sizeif correct > best_acc:best_acc = correctprint(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 nnresnet_model = models.resnet18(weights = models.ResNet18_Weights.DEFAULT)
for param in resnet_model.parameters():print(param)param.requires_grad = Falsedata_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 outmodel = 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): #类的初始化,解析数据文件txtself.file_path = file_pathself.imgs = []self.labels = []self.transform = transformwith open(self.file_path) as f:#是把train.txt文件中图片的路径保存在 self.imgs,train.txt文件中标签保存在 self.labelssamples = [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,BGRif self.transform:                   #将pil图像数据转换为tensorimage = self.transform(image)    #图像处理为256*256,转换为tenorlabel = self.labels[idx]        #label还不是tensorlabel = torch.from_numpy(np.array(label,dtype = np.int64))  #label也转换为tensor,return image, labeltraining_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 = 1for X, y in dataloader:                 #其中batch为每一个数据的编号X, y = X.to(device), y.to(device)   #把训练数据集和标签传入cpu或GPUpred = 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 += 1best_acc = 0
def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval()    #测试模式test_loss, correct = 0, 0global best_accwith 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_batchescorrect /= sizeif correct > best_acc:best_acc = correctprint(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 nndata_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): #类的初始化,解析数据文件txtself.file_path = file_pathself.imgs = []self.labels = []self.transform = transformwith open(self.file_path) as f:#是把train.txt文件中图片的路径保存在 self.imgs,train.txt文件中标签保存在 self.labelssamples = [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,BGRif self.transform:                   #将pil图像数据转换为tensorimage = self.transform(image)    #图像处理为256*256,转换为tenorlabel = self.labels[idx]        #label还不是tensorlabel = 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, 0global best_accwith 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_batchescorrect /= sizeprint(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)

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词