第四次作业:猫狗大战挑战赛
经过了漫长的研究,终于理解到了Colab如何完成一道简单的AI研习社上的题......
下面开始(傻瓜式博客教学)作业内容
0.写在前面
首先第一步利用谷歌浏览器搜索colab,然后点击左上角文件新建一个笔记本。
然后点击修改->笔记本设置->硬件加速器->GPU->保存
如下代码是检查是否存在GPU
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import numpy as np import matplotlib.pyplot as plt import os import torch import torch.nn as nn import torchvision from torchvision import models,transforms,datasets import time import json # 判断是否存在GPU设备 device = torch.device( "cuda:0" if torch.cuda.is_available() else "cpu" ) print( 'Using gpu: %s ' % torch.cuda.is_available()) |
运行结果:
1.下载数据
为什么用这个数据集简单理解就是colab如果直接跑JH给的数据集(下载链接)会吃不消,所以我们采用重新整理的2000张图的数据集
1 2 | ! wget http: //fenggao-image.stor.sinaapp.com/dogscats.zip ! unzip dogscats.zip |
此时文件结构如图:
当然我们最终是要测试官方的test所以我们需要先从正式比赛官网网页下载测试集
下载后解压,将test再压缩成test.zip。复制test.zip上传到colab文件中
右键点击上传。
等到右边进度条转完之后上传完毕
通过新建文件夹构建出这样的文件目录
利用Linux命令,将test文件解压到/dogscats/test/test目录之中
1 | !unzip -d ./dogscats/test/test test.zip |
解压完成后的文件结构
2.数据处理
datasets 是 torchvision 中的一个包,可以用做加载图像数据。它可以以多线程(multi-thread)的形式从硬盘中读取数据,使用 mini-batch 的形式,在网络训练中向 GPU 输送。在使用CNN处理图像时,需要进行预处理。图片将被整理成 的大小,同时还将进行归一化处理。
torchvision 支持对输入数据进行一些复杂的预处理/变换 (normalization, cropping, flipping, jittering 等)。具体可以参照 torchvision.tranforms 的官方文档说明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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' , 'valid' , 'test' ]} dset_sizes = {x: len(dsets[x]) for x in [ 'train' , 'valid' , 'test' ]} dset_classes = dsets[ 'train' ].classes |
1 2 3 4 5 6 | # 通过下面代码可以查看 dsets 的一些属性 print (dsets[ 'train' ].classes) print (dsets[ 'train' ].class_to_idx) print (dsets[ 'train' ].imgs[: 5 ]) print ( 'dset_sizes: ' , dset_sizes) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | loader_train = torch.utils.data.DataLoader(dsets[ 'train' ], batch_size = 64 , shuffle = True , num_workers = 6 ) loader_valid = torch.utils.data.DataLoader(dsets[ 'valid' ], batch_size = 5 , shuffle = False , num_workers = 6 ) loader_test = torch.utils.data.DataLoader(dsets[ 'test' ], batch_size = 5 , shuffle = False , num_workers = 6 ) ''' valid 数据一共有2000张图,每个batch是5张,因此,下面进行遍历一共会输出到 400 同时,把第一个 batch 保存到 inputs_try, labels_try,分别查看 ''' count = 1 for data in loader_valid: print (count, end = '\n' ) if count = = 1 : inputs_try,labels_try = data count + = 1 print (labels_try) print (inputs_try.shape) |
1 2 3 4 5 6 7 8 9 10 11 12 | # 显示图片的小程序 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 |
1 2 3 | # 显示 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对结果进行处理,随后展示了识别结果。可以看到,识别结果是比较非常准确的。
1 | !wget https: / / s3.amazonaws.com / deep - learning - models / image - models / imagenet_class_index.json |
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 | 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 , labels_try = inputs_try.to(device), 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) print ([dic_imagenet[i] for i in pred_try.data]) imshow(torchvision.utils.make_grid(inputs_try.data.cpu()), title = [dset_classes[x] for x in labels_try.data.cpu()]) |
4. 修改最后一层,冻结前面层的参数
VGG 模型如下图所示,注意该网络由三种元素组成:
- 卷积层(CONV)是发现图像中局部的 pattern
- 全连接层(FC)是在全局上建立特征的关联
- 池化(Pool)是给图像降维以提高特征的 invariance
我们的目标是使用预训练好的模型,因此,需要把最后的 nn.Linear 层由1000类,替换为2类。为了在训练中冻结前面层的参数,需要设置 required_grad=False。这样,反向传播训练梯度时,前面层的权重就不会自动更新了。训练中,只会更新最后一层的参数。
1 2 3 4 5 6 7 8 9 10 11 12 | print (model_vgg) model_vgg_new = model_vgg; for param in model_vgg_new.parameters(): param.requires_grad = False model_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步,测试模型。
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 44 45 46 47 48 49 | ''' 第一步:创建损失函数和优化器 损失函数 NLLLoss() 的 输入 是一个对数概率向量和一个目标标签. 它不会为我们计算对数概率,适合最后一层是log_softmax()的网络. ''' criterion = nn.NLLLoss() # 学习率 lr = 0.001 # 随机梯度下降 optimizer_vgg = torch.optim.SGD(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.0 running_corrects = 0 count = 0 for inputs,classes in 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 print ( '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) |
对分类编号好的结果数据集预测
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 | def test_model(model,dataloader,size): model. eval () predictions = np.zeros(size) all_classes = np.zeros(size) all_proba = np.zeros((size, 2 )) i = 0 running_loss = 0.0 running_corrects = 0 for 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 ) # statistics 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 / size epoch_acc = running_corrects.data.item() / size print ( 'Loss: {:.4f} Acc: {:.4f}' . format ( epoch_loss, epoch_acc)) return predictions, all_proba, all_classes predictions, all_proba, all_classes = test_model(model_vgg_new,loader_valid,size = dset_sizes[ 'valid' ]) |
对比赛给的test数据集预测,此时不用关心打印出的Acc(准确率),因为比赛给的test没有标记
1 | predictions, all_proba, all_classes = test_model(model_vgg_new,loader_test,size = dset_sizes[ 'test' ]) |
结果导出在文件csv文件中
1 2 3 4 5 6 7 8 9 | import csv with open ( './dogscats/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文件,右键文件->下载下来
左侧一栏是id右侧一栏是预测结果,我们得到的文档不是按照id从小到大递增的。所以需要排序。
ctrl + A 全选->右键点击排序->自定义排序->按照下图设置点击确定。
排序后
6. 可视化模型预测结果(主观分析)
主观分析就是把预测的结果和相对应的测试图像输出出来看看,一般有四种方式:
- 随机查看一些预测正确的图片
- 随机查看一些预测错误的图片
- 预测正确,同时具有较大的probability的图片
- 预测错误,同时具有较大的probability的图片
- 最不确定的图片,比如说预测概率接近0.5的图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 单次可视化显示的图片个数 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[ 'valid' ][x] for x in idx], batch_size = n_view,shuffle = True ) for data in loader_correct: inputs_cor,labels_cor = data # Make a grid from batch out = torchvision.utils.make_grid(inputs_cor) imshow(out, title = [l.item() for l in labels_cor]) # 类似的思路,可以显示错误分类的图片,这里不再重复代码 |
7. 提交答案得到返回结果
啊这,预测得分居然只有95.6。后续再优化一下模型
出处:https://www.cnblogs.com/lightac/
联系:
Email: dzz@stu.ouc.edu.cn
QQ: 1171613053
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法