基于Pytorch的猫狗图片分类(pytorch 猫狗)
ztj100 2024-10-31 16:13 17 浏览 0 评论
转自:CSDN格兰芬多
译者 | 小圈 审校 | 方圆
猫狗分类来源于Kaggle上的一个入门竞赛—Dogs vs Cats 。为了加深对CNN的理解,基于 Pytorch 复现了LeNet,AlexNet,ResNet等经典CNN模型,项目大纲如下:
https://github.com/AuroraLHL/CatDog
文章目录
一、问题描述
二、数据集处理
1 损坏图片清洗
2 抽取图片形成数据集
三、图片预处理
(1)init 方法
(2)getitem方法
(3)len方法
(4)测试
四、模型
1 LeNet
2 AlexNet模型
五、训练
1 开始训练
2 tensorboard可视化
六、不同模型训练结果分析
1 LeNet模型
(1) 数据集数量=1000,无数据增强
(2) 数据集数量=4000,无数据增强
(3)数据集数量=4000,数据增强
(4)数据集=4000,数据增强
(5)使用dropout函数抑制过拟合
2 AlexNet模型
3 squeezeNet模型
4 resNet模型
总结
七、预测
一、问题描述
基于训练集数据,训练一个模型,利用训练好的模型预测未知图片中的动物是狗或者猫的概率。
训练集有25,000张图片,测试集12,500 张图片。
数据集下载地址:https://www.kaggle.com/datasets/shaunthesheep/microsoft-catsvsdogs-dataset
二、数据集处理
1 损坏图片清洗
在 01_clean.py 中,用多种方式来清洗损坏图片:
- 判断开头是否有JFIF
- 用imghdr库中的imghdr.what函数判断文件类型
- 用Image.open(filename).verify()验证图片是否损坏
结果如下:
2 抽取图片形成数据集
由于一万多张图片比较多,并且需要将Cat类和Dog类的图片合在一起并重新命名,方便获得每张图片的labels,我们将从原数据集文件夹复制 任意给定数量图片 到 train 的文件夹,并且重命名如下:
程序为:02_data_processing.py .
三、图片预处理
图片预处理部分需要完成:
- 对图片的裁剪:将大小不一的图片裁剪成神经网络所需的,我选择的是裁剪为(224x224)
- 转化为张量
- 归一化:三个方向归一化
- 图片数据增强
- 形成加载器:返回图片数据和对应的标签,利用Pytorch的Dataset包
在 dataset.py 中定义Mydata的类,继承pytorch的Dataset,定义如下三个方法:
(1)init 方法
读取图片路径,并拆分为数据集和验证集,完成对图片的处理(以下代码 仅体现结构,具体见源码 ):
class Mydata(data.Dataset):
"""定义自己的数据集"""
def __init__(self, root, Transforms=None, train=True):
"""进行数据集的划分"""
if train:
self.imgs = imgs[:int(0.8*imgs_num)] #80%训练集
else:
self.imgs = imgs[int(0.8*imgs_num):] #20%验证集
"""定义图片处理方式"""
if Transforms is None:
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
self.transforms = transforms.Compose(
[ transforms.CenterCrop(224),
transforms.Resize([224,224]),
transforms.ToTensor(), normalize])
(2)getitem方法
对图片处理,返回数据和标签:
def __getitem__(self, index):
return data, label
(3)len方法
返回数据集大小:
def __len__(self):
"""返回数据集中所有图片的个数"""
return len(self.imgs)
(4)测试
实例化数据加载器后,通过调用getitem方法,可以得到经过处理后的 3 × 244 × 244 3\times244\times244 3 × 244 × 244 的图片数据
if __name__ == "__main__":
root = "./data/train"
train = Mydata(root, train=True) #实例化加载器
img,label=train.__getitem__(5) #获取index为5的图片
print(img.dtype)
print(img.size(),label)
print(len(train)) #数据集大小
#输出
torch.float32
torch.Size([3, 224, 224]) 0
3200
裁剪处理后图片如下所示,大小为 224X224 :
四、模型
模型都放在 models.py 中,主要用了一些经典的CNN模型 :
- LeNet
- ResNet
- ResNet
- SqueezeNet
下面给出重点关注的LeNet模型和AlexNet模型:
1 LeNet
LeNet模型是一个早期用来识别手写数字图像的卷积神经网络,这个名字来源于LeNet论文的第一作者Yann LeCun。LeNet展示了通过梯度下降训练卷积神经网络可以达到手写数字识别在当时最先进的结果,LeNet模型结构图示如下所示:
由上图知,LeNet分为 卷积层块 和 全连接层块 两个部分,在本项目中我对LeNet模型做了 相应的调整 :
- 采用三个卷积层
- 三个全连接层
- ReLu作为激活函数
- 在卷积后正则化
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.relu = nn.ReLU()
self.sigmoid = nn.Sigmoid()
#三个卷积层
self.conv1 = nn.Sequential(
nn.Conv2d(
in_channels=3,
out_channels=16,
kernel_size=3,
stride=2,
),
nn.BatchNorm2d(16),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
)
self.conv2 = nn.Sequential(
nn.Conv2d(
in_channels=16,
out_channels=32,
kernel_size=3,
stride=2,
),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
)
self.conv3 = nn.Sequential(
nn.Conv2d(
in_channels=32,
out_channels=64,
kernel_size=3,
stride=2,
),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
)
#三个全连接层
self.fc1 = nn.Linear(3 * 3 * 64, 64)
self.fc2 = nn.Linear(64, 10)
self.out = nn.Linear(10, 2) #分类类别为2,
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = x.view(x.shape[0], -1)
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.out(x)
return x
调用torchsummary库,可以观察模型的结构、参数:
2 AlexNet模型
2012年,AlexNet横空出世,这个模型的名字来源于论文第一作者的姓名Alex Krizhevsky。AlexNet使用了 8层卷积神经网络 ,由5个卷积层和3个池化Pooling 层 ,其中还有3个全连接层构成。AlexNet 跟 LeNet 结构类似,但使?了更多的卷积层和更?的参数空间来拟合?规模数据集 ImageNet,它是浅层神经?络和深度神经?络的分界线。
特点:
- 在每个卷积后面添加了Relu激活函数,解决了Sigmoid的梯度消失问题,使收敛更快。
- 使用随机丢弃技术(dropout)选择性地忽略训练中的单个神经元,避免模型的过拟合(也使用数据增强防止过拟合)
- 添加了归一化LRN(Local Response Normalization,局部响应归一化)层,使准确率更高。
- 重叠最大池化(overlapping max pooling),即池化范围 z 与步长 s 存在关系 z>s 避免平均池化(average pooling)的平均效应
五、训练
训练在 main.py 中,主要是对获取数据、训练、评估、模型的保存等功能的整合,能够实现以下功能:
- 指定训练模型、epoches等基本参数
- 是否选用 预训练模型
- 接着从上次的中断的地方 继续训练
- 保存最好的模型 和最后一次训练的模型
- 对模型的评估:Loss和Accuracy
- 利用 TensorBoard可视化
1 开始训练
在 main.py 程序中,设置参数和模型( models.py 中可以查看有哪些模型):
在vscode中点击运行或在命令行中输入:python main.py
即可开始训练,开始训练后效果如下:
若程序中断,设置resume参数为True,可以接着上次的模型继续训练,可以非常方便的 任意训练多少次 。
2 tensorboard可视化
在vscode中打开tensorboard,或者在命令行中进入当前项目文件夹下输入
tensorboard --logdir runs
即可打开训练中的可视化界面,可以很方便的观察模型的效果:
如上图所示,可以非常方便的观察任意一个模型训练过程的效果!
六、不同模型训练结果分析
1 LeNet模型
在用LeNet模型训练的过程中,通过调整数据集数量、是否用数据增强等不同的方法,来训练模型,并观察模型的训练效果。
(1) 数据集数量=1000,无数据增强
通过Tensorboard可视化可以观察到:
- 验证集准确率(Accuracy)在上升,训练 30epoch 左右,达到最终 63% 左右的最好效果
- 但验证集误差(Loss)也在上升,训练集误差一直下降
- 训练集误差 接近于0
说明模型在训练集上效果好,验证集上效果不好,泛化能力差,可以推测出模型 过拟合 了。而这个原因也是比较好推测的,数据集比较少。
(2) 数据集数量=4000,无数据增强
同样 过拟合了 ,但是最后的准确率能达到 68% 左右,说明数据集增加有效果,用全部数据进行训练效果应该更好。
(3)数据集数量=4000,数据增强
这次数据集数量同上一个一样为4000,但采用了如下的数据增强:
- 水平翻转,概率为p=0.5
- 上下翻转,概率为p=0.1
我们可以看到这次一开始验证集误差是 下降的 ,说明一开始没有过拟合,但到15个epoch之后验证集误差开始上升了,说明已经开始过拟合了,但最后的准确率在 71% 左右,说明数据增强对扩大数据集有明显的效果。
(4)数据集=4000,数据增强
这次数据集数量为4000,但采用了如下的数据增强:
- 水平翻转,概率为p=0.5
- 上下翻转,概率为p=0.5
- 亮度变化
可以看到:
- 35个epoch之前,验证集误差呈 下降 趋势,准确率也一直上升,最高能到 75%
- 但在35个epoch之后,验证集误差开始上升,准确率也开始下降
说明使用了更强的数据增强之后,模型效果更好了。
(5)使用dropout函数抑制过拟合
本次数据集和数据增强方式同(4),但是在模型的第一个 全连接层 加入dropout函数。
dropout原理:
训练过程中随机丢弃掉一些参数。在前向传播的时候,让某个神经元的激活值以一定的概率p(伯努利分布)停止工作,这样可以使模型泛化性更强。
不使用dropout示意图 使用dropout示意图
这样相当于每次训练的是一个比较"瘦"的模型,更不容易过拟合
加入dropout函数后,训练85个epochs,可以观察到效果 十分显著 :
- 验证集的误差总体呈现下降趋势,且最后 没有反弹 !
- 训练集误差下降比较慢了!
- 准确率一直上升,最后可以达到 76% !
说明模型最后没有过拟合,并且效果还不错。
2 AlexNet模型
将AlexNet模型参数打印出来:
可以看到AlexNet相比LeNet,参数数目有数量级的上升,而在数据量比较小的情况下,很容易梯度消失,经过反复的调试:
- 要在卷积层加入正则化
- 优化器选择SGD
- 学习率不能过大
才能避免验证集的准确率一直在 50%
经过调试,较好的一次结果如下所示,最终准确率能达到 78%
3 squeezeNet模型
在后面两个模型中,使用迁移学习的方法。
迁移学习(Transfer Learning) 是机器学习中的一个名词,是指一种学习对另一种学习> 的影响,或习得的经验对完成其它活动的影响。迁移广泛存在于各种知识、技能与社会规范> 的学习中,将某个领域或任务上学习到的知识或模式应用到不同但相关的领域或问题中。
使用squeezeNet预训练模型,在迭代16个epoch后,准确率可以达到 93%
4 resNet模型
使用resnet50的预训练模型,训练25个epoch后,准确率可以达到 98%!
总结
模型 | 测试集预测准确率 |
LeNet(无数据增强) | 68% |
LeNet(数据增强) | 75% |
LeNet(采用Dropout) | 76% |
Alexnet | 78% |
squeezeNet(迁移学习) | 93% |
resNet | 98% |
七、预测
模型训练好后,可以打开 predict.py 对新图片进行预测,给定用来预测的模型和预测的图片文件夹:
model = LeNet1() # 模型结构
modelpath = "./runs/LeNet1_1/LeNet1_best.pth" # 训练好的模型路径
checkpoint = torch.load(modelpath)
model.load_state_dict(checkpoint) # 加载模型参数
root = "test_pics"
运行 predict.py 会将预测的图片储存在 output 文件夹中,如下图所示
会给出预测的类别和概率。
相关推荐
- Vue 技术栈(全家桶)(vue technology)
-
Vue技术栈(全家桶)尚硅谷前端研究院第1章:Vue核心Vue简介官网英文官网:https://vuejs.org/中文官网:https://cn.vuejs.org/...
- vue 基础- nextTick 的使用场景(vue的nexttick这个方法有什么用)
-
前言《vue基础》系列是再次回炉vue记的笔记,除了官网那部分知识点外,还会加入自己的一些理解。(里面会有部分和官网相同的文案,有经验的同学择感兴趣的阅读)在开发时,是不是遇到过这样的场景,响应...
- vue3 组件初始化流程(vue组件初始化顺序)
-
学习完成响应式系统后,咋们来看看vue3组件的初始化流程既然是看vue组件的初始化流程,咋们先来创建基本的代码,跑跑流程(在app.vue中写入以下内容,来跑流程)...
- vue3优雅的设置element-plus的table自动滚动到底部
-
场景我是需要在table最后添加一行数据,然后把滚动条滚动到最后。查网上的解决方案都是读取html结构,暴力的去获取,虽能解决问题,但是不喜欢这种打补丁的解决方案,我想着官方应该有相关的定义,于是就去...
- Vue3为什么推荐使用ref而不是reactive
-
为什么推荐使用ref而不是reactivereactive本身具有很大局限性导致使用过程需要额外注意,如果忽视这些问题将对开发造成不小的麻烦;ref更像是vue2时代optionapi的data的替...
- 9、echarts 在 vue 中怎么引用?(必会)
-
首先我们初始化一个vue项目,执行vueinitwebpackechart,接着我们进入初始化的项目下。安装echarts,npminstallecharts-S//或...
- 无所不能,将 Vue 渲染到嵌入式液晶屏
-
该文章转载自公众号@前端时刻,https://mp.weixin.qq.com/s/WDHW36zhfNFVFVv4jO2vrA前言...
- vue-element-admin 增删改查(五)(vue-element-admin怎么用)
-
此篇幅比较长,涉及到的小知识点也比较多,一定要耐心看完,记住学东西没有耐心可不行!!!一、添加和修改注:添加和编辑用到了同一个组件,也就是此篇文章你能学会如何封装组件及引用组件;第二能学会async和...
- 最全的 Vue 面试题+详解答案(vue面试题知识点大全)
-
前言本文整理了...
- 基于 vue3.0 桌面端朋友圈/登录验证+60s倒计时
-
今天给大家分享的是Vue3聊天实例中的朋友圈的实现及登录验证和倒计时操作。先上效果图这个是最新开发的vue3.x网页端聊天项目中的朋友圈模块。用到了ElementPlus...
- 不来看看这些 VUE 的生命周期钩子函数?| 原力计划
-
作者|huangfuyk责编|王晓曼出品|CSDN博客VUE的生命周期钩子函数:就是指在一个组件从创建到销毁的过程自动执行的函数,包含组件的变化。可以分为:创建、挂载、更新、销毁四个模块...
- Vue3.5正式上线,父传子props用法更丝滑简洁
-
前言Vue3.5在2024-09-03正式上线,目前在Vue官网显最新版本已经是Vue3.5,其中主要包含了几个小改动,我留意到日常最常用的改动就是props了,肯定是用Vue3的人必用的,所以针对性...
- Vue 3 生命周期完整指南(vue生命周期及使用)
-
Vue2和Vue3中的生命周期钩子的工作方式非常相似,我们仍然可以访问相同的钩子,也希望将它们能用于相同的场景。...
- 救命!这 10 个 Vue3 技巧藏太深了!性能翻倍 + 摸鱼神器全揭秘
-
前端打工人集合!是不是经常遇到这些崩溃瞬间:Vue3项目越写越卡,组件通信像走迷宫,复杂逻辑写得脑壳疼?别慌!作为在一线摸爬滚打多年的老前端,今天直接甩出10个超实用的Vue3实战技巧,手把...
- 怎么在 vue 中使用 form 清除校验状态?
-
在Vue中使用表单验证时,经常需要清除表单的校验状态。下面我将介绍一些方法来清除表单的校验状态。1.使用this.$refs...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Vue 技术栈(全家桶)(vue technology)
- vue 基础- nextTick 的使用场景(vue的nexttick这个方法有什么用)
- vue3 组件初始化流程(vue组件初始化顺序)
- vue3优雅的设置element-plus的table自动滚动到底部
- Vue3为什么推荐使用ref而不是reactive
- 9、echarts 在 vue 中怎么引用?(必会)
- 无所不能,将 Vue 渲染到嵌入式液晶屏
- vue-element-admin 增删改查(五)(vue-element-admin怎么用)
- 最全的 Vue 面试题+详解答案(vue面试题知识点大全)
- 基于 vue3.0 桌面端朋友圈/登录验证+60s倒计时
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)