你以前听说过深度学习这个词吗? 或者你刚刚开始学习它?
在本文中,我将引导您构建自己的狗狗分类器。在这个项目的最后:
-
您的代码将接受任何用户提供的图像作为输入
-
如果一只狗在图像中被检测到,它将提供对该狗狗品种的预测
我会让它尽可能的简单~
实现步骤如下:
-
步骤0:导入数据集
-
步骤1:图像预处理
-
步骤2:选择迁移学习的模式
-
步骤3:更改预训练模型的分类器
-
步骤4:编写训练算法
-
步骤5:训练模型
-
步骤6:测试模型
-
步骤7:测试你自己的图
步骤0:导入数据集
你可以从下列网址下载你自己的数据集:https://www.kaggle.com/c/dog-breed-identification
然后解压缩文件!
由于图像处理在本地机器上需要大量的时间和资源,因此我将使用 colab 的 GPU 来训练我的模型。所以,如果你没有自己的 GPU,也可以切换到 colab 来跟进。
导入必要的库始终是一个良好的开始,下面代码展示了我们训练所需要的库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#Importing Libraries import numpy as np import pandas as pd import matplotlib.pyplot as plt from PIL import Image from PIL import ImageFile import cv2 # importing Pytorch model libraries import torch import torchvision.models as models import torchvision.transforms as transforms from torchvision import datasets import torch.nn as nn import torch.nn.functional as F import torch.optim as optim 在下列代码的圆括号的双引号中输入 dog_images 的路径。 dog_files = np.array(glob("/data/dog_images/*/*/*")) # print number of images in each dataset print('There are %d total dog images.' % len(dog_files)) Output : There are 8351 total dog images. |
1 2 3 4 5 6 7 8 9 |
#Applying Data Augmentation and Normalization on images train_transforms = transforms.Compose([transforms.RandomRotation(30),transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(),transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])]) valid_transforms = transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]) test_transforms = transforms.Compose([transforms.Resize(255), transforms.CenterCrop(224), transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]) |
将图像存储到数据加载器中现在我们需要将训练、验证和测试目录加载到数据加载器中。这将使我们能够将数据分成小批量。我们将数据以key-value的格式进行存储,这将有助于以后调用它们。
1 2 3 4 5 6 7 8 9 10 |
# TODO: Load the datasets with ImageFolder train_data = datasets.ImageFolder(train_dir, transform=train_transforms) valid_data = datasets.ImageFolder(valid_dir,transform=valid_transforms) test_data = datasets.ImageFolder(test_dir, transform=test_transforms) # TODO: Using the image datasets and the trainforms, define the dataloaders trainloader = torch.utils.data.DataLoader(train_data, batch_size=20, shuffle=True) testloader = torch.utils.data.DataLoader(test_data, batch_size=20,shuffle=False) validloader = torch.utils.data.DataLoader(valid_data, batch_size=20,shuffle=False) loaders_transfer = {'train':trainloader,'valid':validloader,'test':testloader} data_transfer = { 'train':trainloader} |
步骤2:选择迁移学习的模式
什么是预训练模型,我们为什么要使用它?
预训练模型是别人为解决类似问题而创建的模型。
-
与其从零开始构建模型来解决类似的问题,不如使用经过其他问题训练的模型作为起点
-
一个预训练模型在应用程序中可能不是100% 准确,但是它节省了重新发明轮子所需的巨大努力
-
迁移学习可以利用在一个问题上学到的特性,并在一个新的、类似的问题上利用它们。例如,一个已经学会识别小浣熊的模型的特征可能有助于开始训练一个用于识别猫咪的模型
你可以选择几个事先训练过的模特进行模特训练。例如 Densenet,Resnet,VGG 模型。我将使用 VGG-16进行模型训练。
1 2 |
#Loading vgg11 into the variable model_transfer model_transfer = models.vgg11(pretrained=True) |
步骤3:更改预训练模型的分类器
将采取以下步骤来改变预训练分类器:
-
从以前训练过的模型中取出网络层
-
冻结它们,以避免在以后的训练回合中破坏它们所包含的任何信息
-
在冻结层上添加一些新的、可训练的图层。他们将学习在新的数据集上将旧的特性转化为预测
-
在你的数据集上训练新的网络层
由于特征已经学会了预训练的模型,所以将冻结他们在训练期间对狗狗的形象。我们只会改变分类器的尺寸,只会训练它。
原来的分类器层有25088个维度,但是为了匹配我们的预处理图像大小,我们需要更改为4096。
1 2 3 4 5 6 7 8 |
要在 GPU 上训练模型,我们需要使用以下命令将其移动到 GPU-RAM 上。 #Freezing the parametersfor param in model_transfer.features.parameters(): param.requires_grad = False #Changing the classifier layermodel_transfer.classifier[6] = nn.Linear(4096,133,bias=True) #Moving the model to GPU-RAM spaceif use_cuda: model_transfer = model_transfer.cuda()print(model_transfer) |
现在我们需要选择一个损失函数和优化器。
利用损失函数计算模型预测值与实际图像的误差。如果你的预测完全错误,你的损失函数将输出更高的数字。如果它们非常好,那么输出的数字就会更低。本文将使用交叉熵损失。
优化器是用来改变神经网络属性的算法或方法,比如权重和学习速度,以减少损失。本文将使用 SGD 优化器。
1 2 3 4 |
0.001的学习率对于训练是有好处的,但是你也可以用其他的学习率进行实验。鼓励大家多多尝试不同的参数~ ### Loading the Loss-Functioncriterion_transfer = nn.CrossEntropyLoss() ### Loading the optimizeroptimizer_transfer = optim.SGD(model_transfer.parameters(), lr=0.001, momentum=0.9) |
步骤4:编写训练算法
接下来我们将编写训练函数。
我用它编写了验证代码行。所以在训练模型时,我们会同时得到两种损失。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
每次Loss减少时,我也会存储这个模型。这样我就不必在以后每次打开一个新实例时都要训练它。 def train(n_epochs, loaders, model, optimizer, criterion, use_cuda, save_path): """returns trained model""" # initialize tracker for minimum validation loss valid_loss_min = np.inf #---> Max Value (As the loss decreases and becomes less than this value it gets saved) for epoch in range(1, n_epochs+1): #Initializing training variables train_loss = 0.0 valid_loss = 0.0 # Start training the model model.train() for batch_idx, (data, target) in enumerate(loaders['train']): # move to GPU's memory space (if available) if use_cuda: data, target = data.cuda(), target.cuda() model.to('cuda') optimizer.zero_grad() output = model(data) loss = criterion(output,target) loss.backward() optimizer.step() train_loss = train_loss + ((1 / (batch_idx + 1)) * (loss.data - train_loss)) # validate the model # model.eval() for batch_idx, (data, target) in enumerate(loaders['valid']): accuracy=0 # move to GPU's memory space (if available) if use_cuda: data, target = data.cuda(), target.cuda() ## Update the validation loss logps = model(data) loss = criterion(logps, target) valid_loss += ((1 / (batch_idx + 1)) * (loss.data - valid_loss)) # print both training and validation losses print('Epoch: {} tTraining Loss: {:.6f} tValidation Loss: {:.6f}'.format(epoch, train_loss,valid_loss)) if valid_loss <= valid_loss_min: print('Validation loss decreased ({:.6f} --> {:.6f}). Saving model ...'.format( valid_loss_min, valid_loss)) #Saving the model torch.save(model.state_dict(), 'model_transfer.pt') valid_loss_min = valid_loss # return the trained model return model |
步骤5:训练模型
现在,我将开始通过在函数中提供参数来训练模型。我将训练10个epoch。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# train the model model_transfer = train(10, loaders_transfer, model_transfer, optimizer_transfer, criterion_transfer, use_cuda, 'model_transfer.pt') # load the model that got the best validation accuracy model_transfer.load_state_dict(torch.load('model_transfer.pt')) OUTPUT:Epoch: 1 Training Loss: 2.443815 Validation Loss: 0.801671Validation loss decreased (inf --> 0.801671). Saving model ...Epoch: 2 Training Loss: 1.440627 Validation Loss: 0.591050Validation loss decreased (0.801671 --> 0.591050). Saving model ...Epoch: 3 Training Loss: 1.310158 Validation Loss: 0.560950Validation loss decreased (0.591050 --> 0.560950). Saving model ...Epoch: 4 Training Loss: 1.200572 Validation Loss: 0.566340Epoch: 5 Training Loss: 1.160727 Validation Loss: 0.530196Validation loss decreased (0.560950 --> 0.530196). Saving model ...Epoch: 6 Training Loss: 1.088659 Validation Loss: 0.560774Epoch: 7 Training Loss: 1.060936 Validation Loss: 0.503829Validation loss decreased (0.530196 --> 0.503829). Saving model ...Epoch: 8 Training Loss: 1.010044 Validation Loss: 0.500608Validation loss decreased (0.503829 --> 0.500608). Saving model ...Epoch: 9 Training Loss: 1.054875 Validation Loss: 0.497319Validation loss decreased (0.500608 --> 0.497319). Saving model ...Epoch: 10 Training Loss: 1.000547 Validation Loss: 0.545735<All keys matched successfully> |
步骤6:测试模型
现在我将在模型以前从未见过的新图像上测试模型,并计算预测的准确性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
def test(loaders, model, criterion, use_cuda): # Initializing the variables test_loss = 0.correct = 0.total = 0. model.eval() #So that it doesn't change the model parameters during testing for batch_idx, (data, target) in enumerate(loaders['test']): # move to GPU's memory spave if available if use_cuda: data, target = data.cuda(), target.cuda() # Passing the data to the model (Forward Pass) output = model(data) loss = criterion(output, target) #Test Loss test_loss = test_loss + ((1 / (batch_idx + 1)) * (loss.data - test_loss)) # Output probabilities to the predicted class pred = output.data.max(1, keepdim=True)[1] # Comparing the predicted class to output correct += np.sum(np.squeeze(pred.eq(target.data.view_as(pred))).cpu().numpy()) total += data.size(0) print('Test Loss: {:.6f}n'.format(test_loss)) print('nTest Accuracy: %2d%% (%2d/%2d)' % ( 100. * correct / total, correct, total)) test(loaders_transfer, model_transfer, criterion_transfer, use_cuda) |
我已经训练了它10个 epoch,得到了83% 的准确率。并且,得到以下输出!
如何提高这个模型的准确性?
-
通过训练更多的 epoch(比较训练和验证损失)
-
通过改变学习率(比如0.01,0.05,0.1)
-
通过改变预训练模型(像稠密网络,但需要更多的训练时间)
-
通过对图像的进一步预处理
步骤7:测试你自己的图片
现在你已经训练和测试了你的模型。现在,这是最令人兴奋的部分。你能走到这一步真是太好了。
1.将要测试和保存的新图像加载到内存中
1 2 3 |
#Loading the new image directory dog_files_short = np.array(glob("/content/my_dogs/*")) #Loading the mode lmodel_transfer.load_state_dict(torch.load('model_transfer.pt')) |
2. 现在我们必须对图像进行预处理,并通过测试我们训练好的模型来预测类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def predict_breed_transfer(img_path): #Preprocessing the input image transform = transforms.Compose([transforms.Resize(255), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]) img = Image.open(img_path) img = transform(img)[:3,:,:].unsqueeze(0) if use_cuda: img = img.cuda() model_transfer.to('cuda') # Passing throught the model model_transfer.eval() # Checking the name of class by passing the index class_names = [item[4:].replace("_", " ") for item in data_transfer['train'].dataset.classes] idx = torch.argmax(model_transfer(img)) return class_names[idx] output = model_transfer(img) # Probabilities to class pred = output.data.max(1, keepdim=True)[1] return pred |

1 |
Output |
这只是一个开始,你可以用这个模型做更多的事情。你可以通过部署它来创建一个应用程序。我尝试从零开始创建自己的模型,没有使用迁移学习,但测试的准确率不超过13% 。你也可以尝试一下,因为这有助于理解概念。
数据科学与编程 » 基于迁移学习的狗狗分类器