鸽了好久的迁移学习篇学习终于打算更新,这次我们来学习一个机器学习中经典常用的简单快速提高网络指标的trick,迁移学习,迁移学习本身是机器学习中的一个trick,但是近些年在深度学习中应用广泛。之前我在学习迁移学习的时候想做到随便迁移任何一个网络但是我又看不太懂CNN的代码,然后就很懵,这篇博客的目的在于让大家只需要简单修改代码即可实现各种经典CNN网络的迁移。当然迁移学习是一门很大的学科,我们这里只介绍微调的方法。
一般来说不管针对任何数据集,使用迁移学习的方法一定是比不使用迁移学习要效果好的,记住我这里说的是
一定,一定,一定!!!重要的事情说三遍。
好的,废话少说,我们来简单介绍一下迁移学习的概念,迁移学习的思想可以理解成为,我们人类的学习,简单来说如果一个人需要学习python编程语言,但是他从来没有学过任何编程语言,这样他重新开始学编程语言就会学的很慢,但是如果他之前就学过C语言,或者C++,亦或者matlab的脚本语言,那么学python的时候就会很快,对,没错,我们基于已有的知识去重新学习新知识的过程便是迁移学习。
不论是深度学习模型还是各种神经网络,我们都可以理解成人类的大脑,大脑如果最开始一片空白(即模型还未开始训练,没有任何信息),然后开始学习一个知识(识别一个简单的目标)会学的非常慢,因为我们都知道从0开始学习是非常慢的,但是如果这个人已经学过数学,比如说高等数学,那么我们再开始学习线性代数,概率论,我们学习的速度一定是要快于什么都没学过的人的(假设双方智力相等),而且我们也会学的比没学过的人好。
对,如果我们的模型在训练之前,不是新的模型,是已经训练过可以识别其他对象的模型,那么我们在这个的基础上再次训练这个模型识别一个新对象的时候我们也许简单跑几个epoch就能达到很高的识别率。
好的,到这里我想我们经对迁移学习的概念有所了解,所谓迁移学习,就是我们在已经学会旧事物的基础上去学习类似的新事物,这样会让我们学的更快更好。
事实上这也是为什么我们经常将迁移学习用在数据量较少的数据上,因为我们完全可以将模型在数据量较大的数据上进行集中训练,训练出很好的效果,对于我们目标数据集量很少的情况,我们只需要迁移学习一下,很少的数据就能达到很好的效果。
说到这里,我们就需要介绍一下Imagenet数据集了(详细的自行百度),身为最庞大的开源数据集之一,他包含1000类数据,这也是没什么我们从他上面下载的预训练模型的结构的输出都是1000的原因。
好,到这里我们又提出了预训练模型的概念,所谓预训练模型是个比较专业的叫法,其实说白了就是我们提到的已经训练过的模型。但是面临一个问题,每当我们需要训练模型的时候,需要采用迁移学习方法的时候我们需要去哪里寻找好的预训练模型呢?
这个时候Imagenet数据集的重要性已经体现了出来,按照我们经常使用的TensorFlow和pytorch来说,这些深度学习框架里面已经在Imagenet数据集上训练好了各种常见的CNN模型的预训练模型,我们只需要在代码上稍作修改,就可以在训练的时候下载相应的预训练模型,并且在他的最后再加一层全连接层,全连接层的输出改成我们数据集的种类,即可得到我们想要的结果。
例如我们常见的Resnet模型的迁移学习,
这就是Resnet预训练模型的下载过程
下面以alexnet为例:预训练模型下载代码
alexnet1 = torchvision.models.alexnet(pretrained=True) #下载预训练模型,pretrained=True则是下载预训练模型,如果下载好了就设置为False #这里是下载的alexnet预训练模型,你想下载那个,就把这个alexnet修改成相应模型的名字即可 num_calss = 5 #数据种类,你有几种数据,就设置为几 alexnet1.add_module("linear", nn.Linear(1000, num_calss)) #我们一般需要在模型的最后添加一个全连接层,然后这里的
下面附上完整的基于迁移学习的alexnet,VGG,Googlenet,Resnet代码:
import torch import torchvision import torchvision.models from matplotlib import pyplot as plt from tqdm import tqdm from torch import nn from torch.utils.data import DataLoader from torchvision.transforms import transforms data_transform = { "train": transforms.Compose([transforms.RandomResizedCrop(120), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]), "val": transforms.Compose([transforms.Resize((120, 120)), # cannot 224, must (224, 224) transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])} def main(): train_data = torchvision.datasets.ImageFolder(root = "./data/train" , transform = data_transform["train"]) #导入自己的数据,自己的数据放在跟代码相同的文件夹下新建一个data文件夹,data文件夹里的新建一个train文件夹用于放置训练集的图片。同理新建一个val文件夹用于放置测试集的图片。 traindata = DataLoader(dataset= train_data , batch_size= 32 , shuffle= True , num_workers=0 ) # 将训练数据以每次32张图片的形式抽出进行训练 test_data = torchvision.datasets.ImageFolder(root = "./data/val" , transform = data_transform["val"]) train_size = len(train_data) # 训练集的长度 test_size = len(test_data) # 测试集的长度 print(train_size) # 输出训练集长度看一下,相当于看看有几张图片 print(test_size) # 输出测试集长度看一下,相当于看看有几张图片 testdata = DataLoader(dataset = test_data , batch_size= 32 , shuffle= True , num_workers=0 )# 将训练数据以每次32张图片的形式抽出进行测试 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print("using {} device.".format(device)) # class alexnet(nn.Module): # def __init__(self): # super(alexnet , self).__init__() # self.model = nn.Sequential( # # nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 120, 120] output[48, 55, 55] # # nn.ReLU(inplace=True), # nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27] # nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27] # # nn.ReLU(inplace=True), # nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13] # nn.Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13] # # nn.ReLU(inplace=True), # nn.Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13] # # nn.ReLU(inplace=True), # nn.Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13] # # nn.ReLU(inplace=True), # nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6] # nn.Flatten(), # nn.Dropout(p=0.5), # nn.Linear(512, 2048), # nn.ReLU(inplace=True), # nn.Dropout(p=0.5), # nn.Linear(2048, 1024), # nn.ReLU(inplace=True), # nn.Linear(1024, 7), # # ) # def forward(self , x): # x = self.model(x) # return x alexnet1 = torchvision.models.alexnet(pretrained=True) #下载预训练模型,pretrained=True则是下载预训练模型,如果下载好了就设置为False #这里是下载的alexnet预训练模型,你想下载那个,就把这个alexnet修改成相应模型的名字即可 num_calss = 5 #数据种类,你有几种数据,就设置为几 alexnet1.add_module("linear", nn.Linear(1000, num_calss)) #我们一般需要在模型的最后添加一个全连接层,然后这里的 print(alexnet1) #打印模型结构,看看模型的构成 alexnet1.to(device) test1 = torch.ones(64, 3, 120, 120) #输出一个测试数据看看模型的输出 test1 = alexnet1(test1.to(device)) print(test1.shape) epoch = 15 # 迭代次数即训练次数 learning = 0.001 # 学习率 optimizer = torch.optim.Adam(alexnet1.parameters(), lr=learning) # 使用Adam优化器-写论文的话可以具体查一下这个优化器的原理 loss = nn.CrossEntropyLoss() # 损失计算方式,交叉熵损失函数 train_loss_all = [] # 存放训练集损失的数组 train_accur_all = [] # 存放训练集准确率的数组 test_loss_all = [] # 存放测试集损失的数组 test_accur_all = [] # 存放测试集准确率的数组 for i in range(epoch): # 开始迭代 train_loss = 0 # 训练集的损失初始设为0 train_num = 0.0 # train_accuracy = 0.0 # 训练集的准确率初始设为0 alexnet1.train() # 将模型设置成 训练模式 train_bar = tqdm(traindata) # 用于进度条显示,没啥实际用处 for step, data in enumerate(train_bar): # 开始迭代跑, enumerate这个函数不懂可以查查,将训练集分为 data是序号,data是数据 img, target = data # 将data 分位 img图片,target标签 optimizer.zero_grad() # 清空历史梯度 outputs = alexnet1(img.to(device)) # 将图片打入网络进行训练,outputs是输出的结果 loss1 = loss(outputs, target.to(device)) # 计算神经网络输出的结果outputs与图片真实标签target的差别-这就是我们通常情况下称为的损失 outputs = torch.argmax(outputs, 1) # 会输出10个值,最大的值就是我们预测的结果 求最大值 loss1.backward() # 神经网络反向传播 optimizer.step() # 梯度优化 用上面的abam优化 train_loss += loss1.item() # 将所有损失的绝对值加起来 accuracy = torch.sum(outputs == target.to(device)) # outputs == target的 即使预测正确的,统计预测正确的个数,从而计算准确率 train_accuracy = train_accuracy + accuracy # 求训练集的准确率 train_num += img.size(0) # print("epoch:{} , train-Loss:{} , train-accuracy:{}".format(i + 1, train_loss / train_num, # 输出训练情况 train_accuracy / train_num)) train_loss_all.append(train_loss / train_num) # 将训练的损失放到一个列表里 方便后续画图 train_accur_all.append(train_accuracy.double().item() / train_num) # 训练集的准确率 test_loss = 0 # 同上 测试损失 test_accuracy = 0.0 # 测试准确率 test_num = 0 alexnet1.eval() # 将模型调整为测试模型 with torch.no_grad(): # 清空历史梯度,进行测试 与训练最大的区别是测试过程中取消了反向传播 test_bar = tqdm(testdata) for data in test_bar: img, target = data outputs = alexnet1(img.to(device)) loss2 = loss(outputs, target.to(device)) outputs = torch.argmax(outputs, 1) test_loss = test_loss + loss2.item() accuracy = torch.sum(outputs == target.to(device)) test_accuracy = test_accuracy + accuracy test_num += img.size(0) print("test-Loss:{} , test-accuracy:{}".format(test_loss / test_num, test_accuracy / test_num)) test_loss_all.append(test_loss / test_num) test_accur_all.append(test_accuracy.double().item() / test_num) # 下面的是画图过程,将上述存放的列表 画出来即可 plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(range(epoch), train_loss_all, "ro-", label="Train loss") plt.plot(range(epoch), test_loss_all, "bs-", label="test loss") plt.legend() plt.xlabel("epoch") plt.ylabel("Loss") plt.subplot(1, 2, 2) plt.plot(range(epoch), train_accur_all, "ro-", label="Train accur") plt.plot(range(epoch), test_accur_all, "bs-", label="test accur") plt.xlabel("epoch") plt.ylabel("acc") plt.legend() plt.show() torch.save(alexnet1, "alexnet-transfer-learning.pth") print("模型已保存") if __name__ == '__main__': main()
完整的基于迁移学习的VGG代码:
import torch import torchvision import torchvision.models from matplotlib import pyplot as plt from tqdm import tqdm from torch import nn from torch.utils.data import DataLoader from torchvision.transforms import transforms data_transform = { "train": transforms.Compose([transforms.RandomResizedCrop(120), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]), "val": transforms.Compose([transforms.Resize((120, 120)), # cannot 224, must (224, 224) transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])} def main(): train_data = torchvision.datasets.ImageFolder(root = "./data/train" , transform = data_transform["train"]) #导入自己的数据,自己的数据放在跟代码相同的文件夹下新建一个data文件夹,data文件夹里的新建一个train文件夹用于放置训练集的图片。同理新建一个val文件夹用于放置测试集的图片。 traindata = DataLoader(dataset= train_data , batch_size= 32 , shuffle= True , num_workers=0 ) # 将训练数据以每次32张图片的形式抽出进行训练 test_data = torchvision.datasets.ImageFolder(root = "./data/val" , transform = data_transform["val"]) train_size = len(train_data) # 训练集的长度 test_size = len(test_data) # 测试集的长度 print(train_size) # 输出训练集长度看一下,相当于看看有几张图片 print(test_size) # 输出测试集长度看一下,相当于看看有几张图片 testdata = DataLoader(dataset = test_data , batch_size= 32 , shuffle= True , num_workers=0 )# 将训练数据以每次32张图片的形式抽出进行测试 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print("using {} device.".format(device)) VGG = torchvision.models.vgg16(pretrained=True) #下载预训练模型,pretrained=True则是下载预训练模型,如果下载好了就设置为False #这里是下载的alexnet预训练模型,你想下载那个,就把这个alexnet修改成相应模型的名字即可 num_calss = 5 #数据种类,你有几种数据,就设置为几 VGG.add_module("linear", nn.Linear(1000, num_calss)) #我们一般需要在模型的最后添加一个全连接层,然后这里的 print(VGG) #打印模型结构,看看模型的构成 VGG.to(device) test1 = torch.ones(64, 3, 120, 120) #输出一个测试数据看看模型的输出 test1 = VGG(test1.to(device)) print(test1.shape) epoch = 15 # 迭代次数即训练次数 learning = 0.0001 # 学习率 optimizer = torch.optim.Adam(VGG.parameters(), lr=learning) # 使用Adam优化器-写论文的话可以具体查一下这个优化器的原理 loss = nn.CrossEntropyLoss() # 损失计算方式,交叉熵损失函数 train_loss_all = [] # 存放训练集损失的数组 train_accur_all = [] # 存放训练集准确率的数组 test_loss_all = [] # 存放测试集损失的数组 test_accur_all = [] # 存放测试集准确率的数组 for i in range(epoch): # 开始迭代 train_loss = 0 # 训练集的损失初始设为0 train_num = 0.0 # train_accuracy = 0.0 # 训练集的准确率初始设为0 VGG.train() # 将模型设置成 训练模式 train_bar = tqdm(traindata) # 用于进度条显示,没啥实际用处 for step, data in enumerate(train_bar): # 开始迭代跑, enumerate这个函数不懂可以查查,将训练集分为 data是序号,data是数据 img, target = data # 将data 分位 img图片,target标签 optimizer.zero_grad() # 清空历史梯度 outputs = VGG(img.to(device)) # 将图片打入网络进行训练,outputs是输出的结果 loss1 = loss(outputs, target.to(device)) # 计算神经网络输出的结果outputs与图片真实标签target的差别-这就是我们通常情况下称为的损失 outputs = torch.argmax(outputs, 1) # 会输出10个值,最大的值就是我们预测的结果 求最大值 loss1.backward() # 神经网络反向传播 optimizer.step() # 梯度优化 用上面的abam优化 train_loss += loss1.item() # 将所有损失的绝对值加起来 accuracy = torch.sum(outputs == target.to(device)) # outputs == target的 即使预测正确的,统计预测正确的个数,从而计算准确率 train_accuracy = train_accuracy + accuracy # 求训练集的准确率 train_num += img.size(0) # print("epoch:{} , train-Loss:{} , train-accuracy:{}".format(i + 1, train_loss / train_num, # 输出训练情况 train_accuracy / train_num)) train_loss_all.append(train_loss / train_num) # 将训练的损失放到一个列表里 方便后续画图 train_accur_all.append(train_accuracy.double().item() / train_num) # 训练集的准确率 test_loss = 0 # 同上 测试损失 test_accuracy = 0.0 # 测试准确率 test_num = 0 VGG.eval() # 将模型调整为测试模型 with torch.no_grad(): # 清空历史梯度,进行测试 与训练最大的区别是测试过程中取消了反向传播 test_bar = tqdm(testdata) for data in test_bar: img, target = data outputs = VGG(img.to(device)) loss2 = loss(outputs, target.to(device)) outputs = torch.argmax(outputs, 1) test_loss = test_loss + loss2.item() accuracy = torch.sum(outputs == target.to(device)) test_accuracy = test_accuracy + accuracy test_num += img.size(0) print("test-Loss:{} , test-accuracy:{}".format(test_loss / test_num, test_accuracy / test_num)) test_loss_all.append(test_loss / test_num) test_accur_all.append(test_accuracy.double().item() / test_num) # 下面的是画图过程,将上述存放的列表 画出来即可 plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(range(epoch), train_loss_all, "ro-", label="Train loss") plt.plot(range(epoch), test_loss_all, "bs-", label="test loss") plt.legend() plt.xlabel("epoch") plt.ylabel("Loss") plt.subplot(1, 2, 2) plt.plot(range(epoch), train_accur_all, "ro-", label="Train accur") plt.plot(range(epoch), test_accur_all, "bs-", label="test accur") plt.xlabel("epoch") plt.ylabel("acc") plt.legend() plt.show() torch.save(VGG, "VGG-transfer-learning.pth") print("模型已保存") if __name__ == '__main__': main()
完整的基于迁移学习的Googlenet代码:
import torch import torchvision import torchvision.models from matplotlib import pyplot as plt from tqdm import tqdm from torch import nn from torch.utils.data import DataLoader from torchvision.transforms import transforms data_transform = { "train": transforms.Compose([transforms.RandomResizedCrop(120), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]), "val": transforms.Compose([transforms.Resize((120, 120)), # cannot 224, must (224, 224) transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])} def main(): train_data = torchvision.datasets.ImageFolder(root = "./data/train" , transform = data_transform["train"]) #导入自己的数据,自己的数据放在跟代码相同的文件夹下新建一个data文件夹,data文件夹里的新建一个train文件夹用于放置训练集的图片。同理新建一个val文件夹用于放置测试集的图片。 traindata = DataLoader(dataset= train_data , batch_size= 32 , shuffle= True , num_workers=0 ) # 将训练数据以每次32张图片的形式抽出进行训练 test_data = torchvision.datasets.ImageFolder(root = "./data/val" , transform = data_transform["val"]) train_size = len(train_data) # 训练集的长度 test_size = len(test_data) # 测试集的长度 print(train_size) # 输出训练集长度看一下,相当于看看有几张图片 print(test_size) # 输出测试集长度看一下,相当于看看有几张图片 testdata = DataLoader(dataset = test_data , batch_size= 32 , shuffle= True , num_workers=0 )# 将训练数据以每次32张图片的形式抽出进行测试 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print("using {} device.".format(device)) Googlenet = torchvision.models.googlenet(pretrained=True) #下载预训练模型,pretrained=True则是下载预训练模型,如果下载好了就设置为False #这里是下载的alexnet预训练模型,你想下载那个,就把这个alexnet修改成相应模型的名字即可 num_calss = 5 #数据种类,你有几种数据,就设置为几 Googlenet.add_module("linear", nn.Linear(1000, num_calss)) #我们一般需要在模型的最后添加一个全连接层,然后这里的 print(Googlenet) #打印模型结构,看看模型的构成 Googlenet.to(device) test1 = torch.ones(64, 3, 120, 120) #输出一个测试数据看看模型的输出 test1 = Googlenet(test1.to(device)) print(test1.shape) epoch = 15 # 迭代次数即训练次数 learning = 0.0001 # 学习率 optimizer = torch.optim.Adam(Googlenet.parameters(), lr=learning) # 使用Adam优化器-写论文的话可以具体查一下这个优化器的原理 loss = nn.CrossEntropyLoss() # 损失计算方式,交叉熵损失函数 train_loss_all = [] # 存放训练集损失的数组 train_accur_all = [] # 存放训练集准确率的数组 test_loss_all = [] # 存放测试集损失的数组 test_accur_all = [] # 存放测试集准确率的数组 for i in range(epoch): # 开始迭代 train_loss = 0 # 训练集的损失初始设为0 train_num = 0.0 # train_accuracy = 0.0 # 训练集的准确率初始设为0 Googlenet.train() # 将模型设置成 训练模式 train_bar = tqdm(traindata) # 用于进度条显示,没啥实际用处 for step, data in enumerate(train_bar): # 开始迭代跑, enumerate这个函数不懂可以查查,将训练集分为 data是序号,data是数据 img, target = data # 将data 分位 img图片,target标签 optimizer.zero_grad() # 清空历史梯度 outputs = Googlenet(img.to(device)) # 将图片打入网络进行训练,outputs是输出的结果 loss1 = loss(outputs, target.to(device)) # 计算神经网络输出的结果outputs与图片真实标签target的差别-这就是我们通常情况下称为的损失 outputs = torch.argmax(outputs, 1) # 会输出10个值,最大的值就是我们预测的结果 求最大值 loss1.backward() # 神经网络反向传播 optimizer.step() # 梯度优化 用上面的abam优化 train_loss += loss1.item() # 将所有损失的绝对值加起来 accuracy = torch.sum(outputs == target.to(device)) # outputs == target的 即使预测正确的,统计预测正确的个数,从而计算准确率 train_accuracy = train_accuracy + accuracy # 求训练集的准确率 train_num += img.size(0) # print("epoch:{} , train-Loss:{} , train-accuracy:{}".format(i + 1, train_loss / train_num, # 输出训练情况 train_accuracy / train_num)) train_loss_all.append(train_loss / train_num) # 将训练的损失放到一个列表里 方便后续画图 train_accur_all.append(train_accuracy.double().item() / train_num) # 训练集的准确率 test_loss = 0 # 同上 测试损失 test_accuracy = 0.0 # 测试准确率 test_num = 0 Googlenet.eval() # 将模型调整为测试模型 with torch.no_grad(): # 清空历史梯度,进行测试 与训练最大的区别是测试过程中取消了反向传播 test_bar = tqdm(testdata) for data in test_bar: img, target = data outputs = Googlenet(img.to(device)) loss2 = loss(outputs, target.to(device)) outputs = torch.argmax(outputs, 1) test_loss = test_loss + loss2.item() accuracy = torch.sum(outputs == target.to(device)) test_accuracy = test_accuracy + accuracy test_num += img.size(0) print("test-Loss:{} , test-accuracy:{}".format(test_loss / test_num, test_accuracy / test_num)) test_loss_all.append(test_loss / test_num) test_accur_all.append(test_accuracy.double().item() / test_num) # 下面的是画图过程,将上述存放的列表 画出来即可 plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(range(epoch), train_loss_all, "ro-", label="Train loss") plt.plot(range(epoch), test_loss_all, "bs-", label="test loss") plt.legend() plt.xlabel("epoch") plt.ylabel("Loss") plt.subplot(1, 2, 2) plt.plot(range(epoch), train_accur_all, "ro-", label="Train accur") plt.plot(range(epoch), test_accur_all, "bs-", label="test accur") plt.xlabel("epoch") plt.ylabel("acc") plt.legend() plt.show() torch.save(Googlenet, "Googlenet-transfer-learning.pth") print("模型已保存") if __name__ == '__main__': main()
完整的基于迁移学习的Resnet代码:
import torch import torchvision import torchvision.models from matplotlib import pyplot as plt from tqdm import tqdm from torch import nn from torch.utils.data import DataLoader from torchvision.transforms import transforms data_transform = { "train": transforms.Compose([transforms.RandomResizedCrop(120), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]), "val": transforms.Compose([transforms.Resize((120, 120)), # cannot 224, must (224, 224) transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])} def main(): train_data = torchvision.datasets.ImageFolder(root = "./data/train" , transform = data_transform["train"]) #导入自己的数据,自己的数据放在跟代码相同的文件夹下新建一个data文件夹,data文件夹里的新建一个train文件夹用于放置训练集的图片。同理新建一个val文件夹用于放置测试集的图片。 traindata = DataLoader(dataset= train_data , batch_size= 32 , shuffle= True , num_workers=0 ) # 将训练数据以每次32张图片的形式抽出进行训练 test_data = torchvision.datasets.ImageFolder(root = "./data/val" , transform = data_transform["val"]) train_size = len(train_data) # 训练集的长度 test_size = len(test_data) # 测试集的长度 print(train_size) # 输出训练集长度看一下,相当于看看有几张图片 print(test_size) # 输出测试集长度看一下,相当于看看有几张图片 testdata = DataLoader(dataset = test_data , batch_size= 32 , shuffle= True , num_workers=0 )# 将训练数据以每次32张图片的形式抽出进行测试 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print("using {} device.".format(device)) Resnet = torchvision.models.resnet18(pretrained=True) #下载预训练模型,pretrained=True则是下载预训练模型,如果下载好了就设置为False #这里是下载的alexnet预训练模型,你想下载那个,就把这个alexnet修改成相应模型的名字即可 num_calss = 5 #数据种类,你有几种数据,就设置为几 Resnet.add_module("linear", nn.Linear(1000, num_calss)) #我们一般需要在模型的最后添加一个全连接层,然后这里的 print(Resnet) #打印模型结构,看看模型的构成 Resnet.to(device) test1 = torch.ones(64, 3, 120, 120) #输出一个测试数据看看模型的输出 test1 = Resnet(test1.to(device)) print(test1.shape) epoch = 15 # 迭代次数即训练次数 learning = 0.0001 # 学习率 optimizer = torch.optim.Adam(Resnet.parameters(), lr=learning) # 使用Adam优化器-写论文的话可以具体查一下这个优化器的原理 loss = nn.CrossEntropyLoss() # 损失计算方式,交叉熵损失函数 train_loss_all = [] # 存放训练集损失的数组 train_accur_all = [] # 存放训练集准确率的数组 test_loss_all = [] # 存放测试集损失的数组 test_accur_all = [] # 存放测试集准确率的数组 for i in range(epoch): # 开始迭代 train_loss = 0 # 训练集的损失初始设为0 train_num = 0.0 # train_accuracy = 0.0 # 训练集的准确率初始设为0 Resnet.train() # 将模型设置成 训练模式 train_bar = tqdm(traindata) # 用于进度条显示,没啥实际用处 for step, data in enumerate(train_bar): # 开始迭代跑, enumerate这个函数不懂可以查查,将训练集分为 data是序号,data是数据 img, target = data # 将data 分位 img图片,target标签 optimizer.zero_grad() # 清空历史梯度 outputs = Resnet(img.to(device)) # 将图片打入网络进行训练,outputs是输出的结果 loss1 = loss(outputs, target.to(device)) # 计算神经网络输出的结果outputs与图片真实标签target的差别-这就是我们通常情况下称为的损失 outputs = torch.argmax(outputs, 1) # 会输出10个值,最大的值就是我们预测的结果 求最大值 loss1.backward() # 神经网络反向传播 optimizer.step() # 梯度优化 用上面的abam优化 train_loss += loss1.item() # 将所有损失的绝对值加起来 accuracy = torch.sum(outputs == target.to(device)) # outputs == target的 即使预测正确的,统计预测正确的个数,从而计算准确率 train_accuracy = train_accuracy + accuracy # 求训练集的准确率 train_num += img.size(0) # print("epoch:{} , train-Loss:{} , train-accuracy:{}".format(i + 1, train_loss / train_num, # 输出训练情况 train_accuracy / train_num)) train_loss_all.append(train_loss / train_num) # 将训练的损失放到一个列表里 方便后续画图 train_accur_all.append(train_accuracy.double().item() / train_num) # 训练集的准确率 test_loss = 0 # 同上 测试损失 test_accuracy = 0.0 # 测试准确率 test_num = 0 Resnet.eval() # 将模型调整为测试模型 with torch.no_grad(): # 清空历史梯度,进行测试 与训练最大的区别是测试过程中取消了反向传播 test_bar = tqdm(testdata) for data in test_bar: img, target = data outputs = Resnet(img.to(device)) loss2 = loss(outputs, target.to(device)) outputs = torch.argmax(outputs, 1) test_loss = test_loss + loss2.item() accuracy = torch.sum(outputs == target.to(device)) test_accuracy = test_accuracy + accuracy test_num += img.size(0) print("test-Loss:{} , test-accuracy:{}".format(test_loss / test_num, test_accuracy / test_num)) test_loss_all.append(test_loss / test_num) test_accur_all.append(test_accuracy.double().item() / test_num) # 下面的是画图过程,将上述存放的列表 画出来即可 plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(range(epoch), train_loss_all, "ro-", label="Train loss") plt.plot(range(epoch), test_loss_all, "bs-", label="test loss") plt.legend() plt.xlabel("epoch") plt.ylabel("Loss") plt.subplot(1, 2, 2) plt.plot(range(epoch), train_accur_all, "ro-", label="Train accur") plt.plot(range(epoch), test_accur_all, "bs-", label="test accur") plt.xlabel("epoch") plt.ylabel("acc") plt.legend() plt.show() torch.save(Resnet, "Resnet-transfer-learning.pth") print("模型已保存") if __name__ == '__main__': main()