IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> Pytorch+CNN+猫狗分类实战 -> 正文阅读

[人工智能]Pytorch+CNN+猫狗分类实战

0.前言

同为计算机视觉任务,最经典的MNIST手写数字识别在文章Pytorch+CNN+MNIST实战已经讲得非常详细了,所以对于代码,长篇大论都是模板照套MNIST,故而只需要阐明与MNIST区别,升华境界。

1.猫狗分类数据集

1.1数据集下载(可选部分)

这个是原版的最全数据集,即共25000张图片,没有划分训练集和测试集。我做完一遍之后,划分出了一个精简版的,2000张图片,而且划分好了测试集和训练集。我比较建议用我的,一开始我用的是最全的那个数据集,但是坑:

  1. 那个里面的图片有3张是打不开的,导致报错,后来删除自己补了3张。
  2. 你是在用普通电脑训练还是大型机器?如果用的是普通电脑,我训练2000张图片的时候,10趟我测了需要3~4分钟。那么25000张图片我推测需要训练10趟(EPOCH)有可能需要50-60分钟。而且10趟并不一定保证训练得好,你是否确定你得机器吃得消。当然了,大型机器比如服务器忽略,应该比较快。

如果选择2000张的,下载地址见:https://download.csdn.net/download/qq_43391414/20023207(我设置了不需要积分),下载完成后,解压,然后直接进入章节2.2。

要全部数据集的下载地址见:https://www.microsoft.com/en-us/download/details.aspx?id=54765
在这里插入图片描述
下载好了解压之后,其文件夹的样子为(原封不动):
在这里插入图片描述
点开cat如下:
在这里插入图片描述
点开dog也是类似,这里不做展示了。

1.2数据集分析

先贴几张图片

图1 猫
图2 猫
图3 狗
图4 狗
我们发现:
  1. 猫狗分类是彩色图片,所以是3个channel,MNIST是1个。
  2. 猫狗分类的图片大小不一,有长的,方的。而MNIST非常规整,都是28*28。这就导致了猫狗分类的输入大小是各不相同的,但是CNN的输入是要求固定大小的。这是一个麻烦,后面会给出解决办法。

2.猫狗分类数据集预处理

2.1训练集和测试集划分

建立好如下的文件夹
在这里插入图片描述
检查一下你下载的图片是否有打不开的图片,我有3个(不知道什么原因,估计是见鬼了。),其中两个是猫文件夹的666.jpg,和狗文件夹下的11702.jpg。
在这里插入图片描述在这里插入图片描述

必须解决,否则后面读取错误。解决办法见https://blog.csdn.net/qq_43391414/article/details/118464005

按照8:2划分训练集和测试集。

import os,shutil

def mymovefile(srcfile,dstfile):
    if not os.path.isfile(srcfile):
        print("src not exist!")
    else:
        fpath,fname=os.path.split(dstfile)    #分离文件名和路径
        if not os.path.exists(fpath):
            os.makedirs(fpath)                #创建路径
        shutil.move(srcfile,dstfile)          #移动文件
test_rate=0.2#训练集和测试集的比例为8:2。
img_num=12500
test_num=int(img_num*test_rate)

import random
test_index = random.sample(range(0, img_num), test_num)
file_path=r"D:\Download\kagglecatsanddogs_3367a\PetImages"
tr="train"
te="test"
cat="Cat"
dog="Dog"

#将上述index中的文件都移动到/test/Cat/和/test/Dog/下面去。
for i in range(len(test_index)):
    #移动猫
    srcfile=os.path.join(file_path,tr,cat,str(test_index[i])+".jpg")
    dstfile=os.path.join(file_path,te,cat,str(test_index[i])+".jpg")
    mymovefile(srcfile,dstfile)
    #移动狗
    srcfile=os.path.join(file_path,tr,dog,str(test_index[i])+".jpg")
    dstfile=os.path.join(file_path,te,dog,str(test_index[i])+".jpg")
    mymovefile(srcfile,dstfile)

运行以上代码,发现我们的test文件夹下已经有了2*test_num个测试文件。
帮助:

  1. 上面就是一个移动文件的过程,然后随机从train中选取一些图片到test中即可,完成划分。划分比例8:2你可以自己改。

2.2训练集和测试集读取

如果是从2000张图片过来的,执行下面代码(全数据集的忽略):

tr="train"
te="test"
file_path=r"D:\lbq\lang\pythoncode\data\catsdogs"#路径换一下,换成你的解压目录,精确到train和test的上一级目录。

我们还发现一个区别:
2. MNIST被很多官方的库收录,并直接提供下载和预处理(torchvision.datasets.MNIST),所以相对简单,而猫狗分类不具备这个特点,需要我们单独对数据集进行预处理。

然而,其实猫狗分类的数据集预处理同样很简单,也有一个函数(torchvision.datasets.ImageFolder)可以直接搞定。

import numpy as np
from torchvision import transforms,datasets

#定义transforms
transforms = transforms.Compose(
[

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

train_data = datasets.ImageFolder(os.path.join(file_path,tr), transforms)
test_data=datasets.ImageFolder(os.path.join(file_path,te), transforms)

帮助:

  1. 其中那个RandomResizedCrop(150)是用来把图片的每一个channel大小都变成(150,150)
  2. mean=[0.485, 0.456, 0.406],有3个数的原因是猫狗分类是彩色图片,所以有3个channel,所以每一个channel上都有一个平均值。你可以翻一翻MNIST的transforms定义,其mean只有一个,因为只有一个channel。
  3. 同理,std也是。
  4. 上面的data已经把猫狗的图片都囊括了,而且标签已经自动变成了0和1。这就是ImageFolder的威力。

一些操作:

在这里插入图片描述
train_data的数据类型是DataSet,这个已经强调多遍,因为只有DataSet才可以被后面的DataLoader操作。

在这里插入图片描述

训练数据一共有20000张,10000张狗,10000张猫;测试数据有5000张,2500张狗,2500张猫。
在这里插入图片描述
第一张[0]训练图片的具体形况,前面是图片[0],后面是标签[1]。标签0代表猫,1代表狗。这是由于在file_path/train的文件夹下Cat在Dog的前面,所以前者是0,后者是1。
在这里插入图片描述
每一张图片都是(3,150,150),3表示3个channel,猫狗分类的图片是彩色的。

3.剩余代码

网络架构:

图1 卷积层
图2 全连接层

剩余代码:

from torch.utils import data
batch_size=32
train_loader = data.DataLoader(train_data,batch_size=batch_size,shuffle=True,pin_memory=True)
test_loader = data.DataLoader(test_data,batch_size=batch_size)
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
#架构会有很大的不同,因为28*28-》150*150,变化挺大的,这个步长应该快一点。
class CNN(nn.Module):
    def __init__(self):
        super(CNN,self).__init__()
        self.conv1=nn.Conv2d(3,20,5,5)#和MNIST不一样的地方,channel要改成3,步长我这里加快了,不然层数太多。
        self.conv2=nn.Conv2d(20,50,4,1)
        self.fc1=nn.Linear(50*6*6,200)
        self.fc2=nn.Linear(200,2)#这个也不一样,因为是2分类问题。
    def forward(self,x):
        #x是一个batch_size的数据
        #x:3*150*150
        x=F.relu(self.conv1(x))
        #20*30*30
        x=F.max_pool2d(x,2,2)
        #20*15*15
        x=F.relu(self.conv2(x))
        #50*12*12
        x=F.max_pool2d(x,2,2)
        #50*6*6
        x=x.view(-1,50*6*6)
        #压扁成了行向量,(1,50*6*6)
        x=F.relu(self.fc1(x))
        #(1,200)
        x=self.fc2(x)
        #(1,2)
        return F.log_softmax(x,dim=1)
    
              
lr=1e-4
device=torch.device("cuda" if torch.cuda.is_available() else "cpu" )
model=CNN().to(device)
optimizer=optim.Adam(model.parameters(),lr=lr)
def train(model,device,train_loader,optimizer,epoch,losses):
    model.train()
    for idx,(t_data,t_target) in enumerate(train_loader):
        t_data,t_target=t_data.to(device),t_target.to(device)
        pred=model(t_data)#batch_size*2
        loss=F.nll_loss(pred,t_target)
        
        #Adam
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if idx%10==0:
            print("epoch:{},iteration:{},loss:{}".format(epoch,idx,loss.item()))
            losses.append(loss.item())

        
def test(model,device,test_loader):
    model.eval()
    correct=0#预测对了几个。
    with torch.no_grad():
        for idx,(t_data,t_target) in enumerate(test_loader):
            t_data,t_target=t_data.to(device),t_target.to(device)
            pred=model(t_data)#batch_size*2
            pred_class=pred.argmax(dim=1)#batch_size*2->batch_size*1           
            correct+=pred_class.eq(t_target.view_as(pred_class)).sum().item()
    acc=correct/len(test_data)
    print("accuracy:{},average_loss:{}".format(acc,average_loss))


num_epochs=10
losses=[]
from time import *
begin_time=time()
for epoch in range(num_epochs):
    train(model,device,train_loader,optimizer,epoch,losses)
# test(model,device,test_loader)
end_time=time()                       

训练过程(截取部分):
在这里插入图片描述

test(model,device,test_loader)  

测试结果:63.5%(最好的时候是,可能会有波动,我也只做了几次)。至于怎么调参,仿照开头那篇文章。

4.总结

正如开头说的那样,这一篇只是一个续集,和MNIST手写数字识别没有很大的不同。

  1. 我们修改了数据预处理的方法,因为官方数据集并没有猫狗分类,所以需要自己处理,划分训练和测试集(不过我的2000张已经划分好了)。
  2. 我们需要resize,因为有些图片大,有些图片小,不像MNIST那么规整。
  3. 我们需要修改网络架构,因为这里是彩色的,有3个channel,而且由于我们的训练资源有限,我们增大了卷积的步长,就是那个self.conv1=nn.Conv2d(3,20,5,5)的最后一个参数5,即卷一次,移动5个格子,不写默认是1格。这样做,可以快速把图片缩小,原来是150*150的图片,这样可以变成30*30.
  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-07-05 20:20:08  更:2021-07-05 20:20:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/27 7:03:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码