首次用Colab完成AI研习社的题目,写的详细一点。
——AI研习社:猫狗大战—经典图像分类题
1.数据导入
首先从AI研习社下载数据集(下载链接 : https://static.leiphone.com/cat_dog.rar)
下载完后解压,然后做调整。
一般来说,数据集中包好两个文件夹,train和val,每个文件夹下面包含N个子文件夹,N是你的分类类别数,且每个子文件夹里面存放的就是这个类别的图像。解压完可以发现,这两个文件夹下都没有给你分类,所以需要用最笨的手工方法先分为两个类:cats和dogs。
train:
val:
分完类之后压缩数据集,压缩为.zip压缩包,上传到Google云端硬盘,记住你的路径。(过程巨慢巨卡)

至此准备工作完成,开始撸代码。
连上GPU。
import numpy as npimport matplotlib.pyplot as pltimport osimport torchimport torch.nn as nnimport torchvisionfrom torchvision import models, transforms, datasetsimport timeimport json# 判断是否存在GPU设备device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print('Using gpu: %s ' % torch.cuda.is_available())

解压数据集
!unzip '/content/drive/My Drive/app/cat_dog/cat_dog.zip'
2.数据处理
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 = './cat_dog'dsets = {x: datasets.ImageFolder(os.path.join(data_dir, x), vgg_format) for x in['train', 'val', 'test']}dset_sizes = {x: len(dsets[x]) for x in['train', 'val', 'test']}dset_classes = dsets['train'].classes
这里采用官方写好的torchvision.datasets.ImageFolder接口实现数据导入。这个接口需要你提供图像所在的文件夹,就是data_dir = ‘./cat_dog’。
这样torchvision.datasets.ImageFolder就会返回一个列表(比如下面代码中的image_datasets[‘train’]或者image_datasets[‘val’]),列表中的每个值都是一个tuple,每个tuple包含图像和标签信息。
通过下面代码可以查看 dsets 的一些属性
print(dsets['train'].classes)print(dsets['train'].class_to_idx)print(dsets['train'].imgs[:5])print('dset_sizes: ', dset_sizes)

前面torchvision.datasets.ImageFolder只是返回list,list是不能作为模型输入的,因此在PyTorch中需要用另一个类来封装list,那就是:torch.utils.data.DataLoader。torch.utils.data.DataLoader类可以将list类型的输入数据封装成Tensor数据格式,以备模型使用。
loader_train = torch.utils.data.DataLoader(dsets['train'], batch_size=64, shuffle=True, num_workers=6)loader_val = torch.utils.data.DataLoader(dsets['val'], batch_size=5, shuffle=False, num_workers=6)loader_test = torch.utils.data.DataLoader(dsets['test'], batch_size=5, shuffle=False, num_workers=6)'''val 数据一共有2000张图,每个batch是5张,因此,下面进行遍历一共会输出到 400同时,把第一个 batch 保存到 inputs_try, labels_try,分别查看'''count = 1for data in loader_val:if count == 1:inputs_try, labels_try = datacount += 1print(labels_try)print(inputs_try.shape)

# 显示图片的小程序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# 显示 labels_try 的5张图片,即valid里第一个batch的5张图片out = torchvision.utils.make_grid(inputs_try)imshow(out, title=[dset_classes[x] for x in labels_try])
3.创建VGG Model
torchvision中集成了很多在 ImageNet (120万张训练数据) 上预训练好的通用的CNN模型,可以直接下载使用。
在本课程中,我们直接使用预训练好的 VGG 模型。同时,为了展示 VGG 模型对本数据的预测结果,还下载了 ImageNet 1000 个类的 JSON 文件。
在这部分代码中,对输入的5个图片利用VGG模型进行预测,同时,使用softmax对结果进行处理,随后展示了识别结果。可以看到,识别结果是比较非常准确的。
!wget https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json

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))]inputs_try = inputs_try.to(device)labels_try = labels_try.to(device)model_vgg = model_vgg.to(device)outputs_try = model_vgg(inputs_try)print(outputs_try)print(outputs_try.shape)

可以看到结果为5行,1000列的数据,每一列代表对每一种目标识别的结果,有正数也有负数。
为了将VGG网络输出的结果转化为对每一类的预测概率,我们把结果输入到 Softmax 函数
m_softm = nn.Softmax(dim=1)probs = m_softm(outputs_try)vals_try, pred_try = torch.max(probs, dim=1)print( 'prob sum: ', torch.sum(probs,1))print( 'vals_try: ', vals_try)print( 'pred_try: ', pred_try)
4.修改最后一层,冻结前面层的参数
我们的目标是使用预训练好的模型,因此,需要把最后的 nn.Linear 层由1000类,替换为2类。为了在训练中冻结前面层的参数,需要设置 required_grad=False。这样,反向传播训练梯度时,前面层的权重就不会自动更新了。训练中,只会更新最后一层的参数。
#查看vgg16模型print(model_vgg)


不对网络详细说明了。训练的时候将更新最后一层的参数,并增加一层logsoftmax
model_vgg_new = model_vggfor param in model_vgg_new.parameters():param.requires_grad = Falsemodel_vgg_new.classifier._modules['6'] = nn.Linear(4096, 2)model_vgg_new.classifier._modules['7'] = torch.nn.LogSoftmax(dim=1)model_vgg_new = model_vgg_new.to(device)print(model_vgg_new.classifier)
5.训练并测试全连接层
包括三个步骤:第1步,创建损失函数和优化器;第2步,训练模型;第3步,测试模型。
'''第一步:创建损失函数和优化器损失函数 NLLLoss()的输入是一个对数概率向量和一个目标标签.它不会为我们计算对数概率,适合最后一层是log_softmax()的网络.'''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=1, optimizer=None):model.train()for epoch in range(epochs):running_loss = 0.0running_corrects = 0count = 0for inputs, classes in dataloader:inputs = inputs.to(device)classes = classes.to(device)outputs = model(inputs)loss = criterion(outputs, classes)optimizer = optimizeroptimizer.zero_grad()loss.backward()optimizer.step()_,preds = torch.max(outputs.data, 1)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 / sizeepoch_acc = running_corrects.data.item() / sizeprint('Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc))# 模型训练train_model(model_vgg_new, loader_train, size=dset_sizes['train'], epochs=1, optimizer=optimizer_vgg)

测试模型:
def test_model(model, dataloader, size):model.eval()predictions = np.zeros(size)all_classes = np.zeros(size)all_proba = np.zeros((size, 2))i = 0running_loss = 0.0running_corrects = 0for inputs, classes in dataloader:inputs = inputs.to(device)classes = classes.to(device)outputs = model(inputs)loss = criterion(outputs, classes)_,preds = torch.max(outputs.data, 1)running_loss += loss.data.item()running_corrects += torch.sum(preds == classes.data)predictions[i:i+len(classes)] = preds.to('cpu').numpy()all_classes[i:i+len(classes)] = classes.to('cpu').numpy()all_proba[i:i+len(classes)] = outputs.data.to('cpu').numpy()i += len(classes)print('Testing: No. ', i, ' process ... total: ', size)epoch_loss = running_loss / sizeepoch_acc = running_corrects.data.item() / sizeprint('Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc))return predictions, all_proba, all_classespredictions, all_proba, all_classes = test_model(model_vgg_new, loader_valid, size=dset_sizes['valid'])
踩坑指南——model.train()和model.eval()的作用:
如果模型中有BN层(Batch Normalization)和Dropout,需要在训练时添加model.train(),在测试时添加model.eval()。其中model.train()是保证BN层用每一批数据的均值和方差,而model.eval()是保证BN用全部训练数据的均值和方差;而对于Dropout,model.train()是随机取一部分网络连接来训练更新参数,而model.eval()是利用到了所有网络连接。
需要联系Batch Normalization和Dropout的原理之后去理解为何要这么做。
(总之,套用框架就是在训练开始之前写上model.train(),在测试时写上model.eval())
测试test数据:
def t_model(model, dataloader, size):model.eval()predictions = np.zeros(size)i = 0for inputs, classes in dataloader:inputs = inputs.to(device)classes = classes.to(device)outputs = model(inputs)loss = criterion(outputs, classes)_,preds = torch.max(outputs.data, 1)predictions[i:i+len(classes)] = preds.to('cpu').numpy()i += len(classes)print('Testing: No. ', i, ' process ... total: ', size)return predictionspredictions = t_model(model_vgg_new, loader_test, size=dset_sizes['test'])print(predictions)
6.可视化模型预测结果(主观分析)
主观分析就是把预测的结果和相对应的测试图像输出出来看看,一般有四种方式:
- 随机查看一些预测正确的图片
- 随机查看一些预测错误的图片
- 预测正确,同时具有较大的probability的图片
- 预测错误,同时具有较大的probability的图片
- 最不确定的图片,比如说预测概率接近0.5的图片
```python
单次可视化显示的图片个数
n_view = 8 correct = np.where(predictions==all_classes)[0] from numpy.random import random, permutation idx = permutation(correct)[:n_view] print(‘random correct idx: ‘, idx) loader_correct = torch.utils.data.DataLoader([dsets[‘val’][x] for x in idx],
for data in loader_correct: inputs_cor,labels_cor = databatch_size = n_view,shuffle=True)Make a grid from batch
out = torchvision.utils.make_grid(inputs_cor) imshow(out, title=[l.item() for l in labels_cor])
类似的思路,可以显示错误分类的图片,这里不再重复代码

<a name="eixpk"></a>
### 7.处理测试集
将结果导入到csv文件中
```python
import csv
with open('./cat_dog/cats_vs_dogs.csv','w',newline="")as f:
writer = csv.writer(f)
for index,cls in enumerate(predictions):
path = datasets.ImageFolder(os.path.join(data_dir,'test'),vgg_format).imgs[index][0]
l = path.split("/")
img_name = l[-1]
order = int(img_name.split(".")[0])
writer.writerow([order,int(predictions[index])])

下载文件cats_vs_dogs.csv,排序,提交到AI研习社
准确率还挺高的
参考博文:


