猫狗大战--使用VGG16迁移学习进行CIFAR10分类

本次实验主要使用VGG16模型迁移学习进行CIFAR10分类,将猫和狗的图片区分开,平台使用Google Colab平台。

VGG网络

VGG是由Simonyan 和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,是2014年的 ImageNet图像分类与定位挑战赛的第二名。VGG网络影响巨大,一直到今天仍然很多人在使用。猫狗大战--使用VGG16迁移学习进行CIFAR10分类_第1张图片
可以看到VGG网络是一个非常庞大的网络,训练难度也很高,我们本次采用迁移学习的方式,对原网络进行 fine-tune (即固定前面若干层,作为特征提取器,只重新训练最后两层),尝试提高实验效果。

一、数据集导入

研习社为我们提供了提供了训练以及测试的数据集,以及最终实验数据的检测。(目前比赛已经结束,但仍可做为练习赛每天提交测试结果)
由于训练的数据集,我们采用了偷懒的办法,只选取了训练集其中一部分进行训练。

训练集直接从网站链接导入

! wget http://fenggao-image.stor.sinaapp.com/dogscats.zip
! unzip dogscats.zip

测试集数据,下载完成后,解压并仅压缩test文件夹,上传到Google Drive中,然后在colab左侧按钮处连接Google Drive猫狗大战--使用VGG16迁移学习进行CIFAR10分类_第2张图片
然后,我们就能在drive目录中查看到我们上传的数据集的压缩包的具体位置。

训练集引入代码:

#具体位置根据自己的情况修改
from google.colab import drive
drive.mount('/content/drive')
!unzip "/content/drive/MyDrive/Colab Notebooks/test.zip"

二、训练模型

1.加载训练集数据

normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

vgg_format = transforms.Compose([
               transforms.CenterCrop(224),
               transforms.ToTensor(),
               normalize,
           ])

data_dir = './dogscats'

dsets = {x: datasets.ImageFolder(os.path.join(data_dir, x), vgg_format)
        for x in ['train']}

dset_sizes = {x: len(dsets[x]) for x in ['train']}
dset_classes = dsets['train'].classes
loader_train = torch.utils.data.DataLoader(dsets['train'], batch_size=256, shuffle=True, num_workers=6)

2.下载并加载模型

(1)下载模型

#下载模型
!wget https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json

(2)加载模型

#使用vgg16需要
model_vgg = models.vgg16(pretrained=True)
 
with open('./imagenet_class_index.json') as f:
    class_dict = json.load(f)
    
dic_imagenet = [class_dict[str(i)][1] for i in range(len(class_dict))]

model_vgg = model_vgg.to(device)

'''
可以看到结果为5行,1000列的数据,每一列代表对每一种目标识别的结果。
但是我也可以观察到,结果非常奇葩,有负数,有正数,
为了将VGG网络输出的结果转化为对每一类的预测概率,我们把结果输入到 Softmax 函数
'''

m_softm = nn.Softmax(dim=1)

def imshow(inp, title=None):
#   Imshow for Tensor.
    inp = inp.numpy().transpose((1, 2, 0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = np.clip(std * inp + mean, 0,1)
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
    plt.pause(0.001)  # pause a bit so that plots are updated

3.修改模型

主要修改:
(1)冻结VGG16的参数,不进行梯度下降。
(2)增加两个线性层,使用效果更好的参数为0.2的LeakyReLU函数

print(model_vgg)

model_vgg_new = model_vgg;

#冻结VGG16中的参数,不进行梯度下降
for param in model_vgg_new.parameters():
   param.requires_grad = False

#新增两个线性层,后期主要训练这两层
model_vgg_new.classifier._modules['6'] = nn.Linear(4096, 4096)
model_vgg_new.classifier._modules['7'] = nn.LeakyReLU(0.2, inplace=True)
model_vgg_new.classifier._modules['8'] = nn.Dropout(p=0.5,inplace=False)
model_vgg_new.classifier._modules['9'] = nn.Linear(4096, 2)
model_vgg_new.classifier._modules['10'] = torch.nn.LogSoftmax(dim=1)

model_vgg_new = model_vgg_new.to(device)

print(model_vgg_new.classifier)

4.训练模型

(1)优化函数由SGD改成了效果更好的Adam。
(2) 每一个epoch结束,都会计算成功率(acc)与目前最高的相比的哪个大,以便取到最好的模型(因为训练模型只是不断趋向于最优,曲线并不严格单调,所以不能保证最后一次的模型是最优解)

#训练模型
from tqdm import trange,tqdm
criterion = nn.NLLLoss()
lr = 0.001
optimizer_vgg = torch.optim.Adam(model_vgg_new.classifier[6].parameters(), lr=lr)
 
def train_model(model, dataloader, size, epochs=200, optimizer=None):
    model.train()
    max_acc = 0
    count = 0
    for epoch in range(epochs):
        running_loss = 0.0
        running_corrects = 0
        count = 0
        for inputs, classes in tqdm(dataloader):
            inputs = inputs.to(device)
            classes = classes.to(device)
            outputs = model(inputs)
            loss = criterion(outputs, classes)
            optimizer = optimizer
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            _, preds = torch.max(outputs.data, 1)
            # statistics
            running_loss += loss.data.item()
            running_corrects += torch.sum(preds == classes.data)
            count += len(inputs)
            #print('Training: No. ', count, ' process ... total: ', size)
        epoch_loss = running_loss / size
        epoch_acc = running_corrects.data.item() / size
        if epoch_acc>max_acc:
            max_acc = epoch_acc
            torch.save(model, '/content/drive/MyDrive/Colab Notebooks/model_best_new.pth')
            tqdm.write("\n Got A Nice Model Acc:{:.8f}".format(max_acc))
        tqdm.write('\nepoch: {} \tLoss: {:.8f} Acc: {:.8f}'.format(epoch,epoch_loss, epoch_acc))
        time.sleep(0.1)
 
    torch.save(model, '/content/drive/MyDrive/Colab Notebooks/model.pth')
    tqdm.write("Got A Nice Model")
 
 
# 模型训练
train_model(model_vgg_new, loader_train, size=dset_sizes["train"], epochs=100,
            optimizer=optimizer_vgg)

在训练之后,将最佳模型以pth格式保存在Google Drive上。

三、测试模型

由于网络问题,导致Colab有时会出现掉线的问题,有时会掉线导致训练模型丢失,所以采用将训练和测试分离的方法,通过pth文件读取传输模型数据。

1.加载测试数据和已训练好的模型

此处我们已经完成了数据的读取和解压,应该已经存在一个解压好的test文件夹。

import torch
import numpy as np
from torchvision import transforms,datasets
from tqdm import tqdm
device = torch.device("cuda:0" )
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
vgg_format = transforms.Compose([
                transforms.CenterCrop(224),
                transforms.ToTensor(),
                normalize,
            ])
dsets_mine = datasets.ImageFolder(r"test1", vgg_format)
 
loader_test = torch.utils.data.DataLoader(dsets_mine, batch_size=1, shuffle=False, num_workers=0)

model_vgg_new = torch.load(r'/content/drive/MyDrive/Colab Notebooks/model_best_new.pth')
model_vgg_new = model_vgg_new.to(device)

这里有一个小坑,如果在读取文件夹处使用test,就会报错Found 0 files in subfolders ,因为ImageFolder()的读取方式是,在该文件夹内的文件夹中读取文件。所以,我们此处在外层建立一个test1文件夹,把test放入test1文件夹中。

2.测试,获取数据,写入文件

(1)测试

dic = {}
def test(model,dataloader,size):
    model.eval()
    predictions = np.zeros(size)
    cnt = 0
    for inputs,_ in tqdm(dataloader):
        inputs = inputs.to(device)
        outputs = model(inputs)
        _,preds = torch.max(outputs.data,1)    
        #这里是切割路径,因为dset中的数据不是按1-2000顺序排列的
        key = dsets_mine.imgs[cnt][0].split("\\")[-1].split('.')[0]
        dic[key] = preds[0]
        cnt = cnt +1
test(model_vgg_new,loader_test,size=2000)

(2)写入文件

with open("result.csv",'a+') as f:
    for key in range(2000):
        f.write("{},{}\n".format(key,dic["test1/test/"+str(key)]))

根据研习社的测试要求,将测试结果写入csv文件夹

四、结果

在这里插入图片描述
研习社网站得到的测试结果为97.75,结果不错,但还有很大的进步空间,可以进一步改造模型,或者更直接的方法,多跑几遍,多试几遍的方法,提高正确率。

源码:
(1)训练:https://colab.research.google.com/drive/1W1KCgB0u05ZgvoivOHL68dzvlO3nDD7P#scrollTo=ZDj0OVrPnD1C
(2)测试:
https://colab.research.google.com/drive/1sYqR8fwUflqKPe5WwjAnJ-bgnK1L8vl6#scrollTo=VFhys8h3nrBd

参考:
[1]:https://github.com/dataflowr/notebooks/blob/master/CEA_EDF_INRIA/01_intro_DLDIY_colab.ipynb
[2]:https://blog.csdn.net/Vision_Tung/article/details/109697509#%E7%8C%AB%E7%8B%97%E5%A4%A7%E6%88%98–%E4%BD%BF%E7%94%A8%20%E2%80%9CVGG16%E8%BF%9B%E8%A1%8CCIFAR10%E5%88%86%E7%B1%BB%E2%80%9D%20%C2%A0%E8%BF%81%E7%A7%BB%E5%AD%A6%E4%B9%A0%E5%AE%9E%E7%8E%B0-toc

你可能感兴趣的:(人工智能,pytorch,深度学习)