目 录

  • 作者介绍
  • 编程实战指南
  • 比赛数据集介绍(Dogs vs cats)
  • 环境配置
  • 模型定义
  • 数据加载
  • 训练和测试
  • 结果展示
  • 参考

作者介绍

周新龙,男,西安工程大学电子信息学院,2019级研究生,张宏伟人工智能课题组
研究方向:机器视觉与人工智能
电子邮件:402850713@qq.com

编程实战指南

通过前面课程的学习,相信同学们已经掌握了Pytorch中大部分的基础知识,本节课将结合之前讲的内容,带领同学们从头实现一个完整的深度学习项目。本节课的重点不在于如何使用Pytorch的接口,而在于合理的设计程序的结构,使得程序更具可读性、更易用。
在学习某个深度学习框架时,掌握其基本知识和接口固然重要,但如何合理地组织代码,使代码具有良好的可读性和可扩展性也很关键。在做大多数深度学习研究时,程序都需要实现以下几个功能。

  • 模型定义
  • 数据处理和加载
  • 训练模型(Train&Validate)
  • 训练过程的可视化(Train&Validate)
  • 测试(Test/Inference)

另外,程序还应该满足以下几个要求:模型具有高度可配置性,便于修改参数、修改模型和反复实验;代码应该具有良好的组织结构,使人一目了然;代码应具有良好的说明,使其他人能够理解。

比赛数据集介绍(Dogs vs cats)


Dogs vs. Cats(猫狗大战)来源于Kaggle上的一个竞赛题,任务为给定一个数据集,其中训练集有25000张,猫狗各占一半,测试集12500张,没有标定是猫还是狗。要求设计一种算法对测试集中的猫狗图片进行判别,目前排名前十的算法均达到了99.8%,猫狗大战实际上是一个传统的二分类问题。

环境配置

建议使用Pytorch1.4版本进行该实例的学习,之前配置的Pytorch1.1也可以实现,Pytorch1.4的GPU版本安装代码如下。
python=3.6 pytorch1.4.0 torchvision0.5.0 cudatoolkit9.2

# CUDA 9.2
conda install pytorch==1.4.0 torchvision==0.5.0 cudatoolkit=9.2 -c pytorch
# CPU Only
conda install pytorch==1.4.0 torchvision==0.5.0 cpuonly -c pytorch

如果已经换成清华源的话可以直接去掉 -c pytorch
代码以及猫狗大战数据集都已经为大家打包好啦!
链接: 代码以及数据集下载链接 .
提取码:izzy

模型定义

目前已经有很多成熟的分类算法,本次课程中选择了较为简单的CNN结构为大家进行讲解,具体网络结构如下图所示。

Input:图像尺寸为200×200像素,由于训练集和测试集中的图片大小尺寸多样,因此在送入网络前,须将图片调整至200×200像素
conv1:卷积核的规模为[3×3×3×16],size大小3×3,深度3,数量16
第一次卷积结果:16个卷积图像(feature map),200×200像素
Pooling:第一次池化,size大小2×2,Max pooling
第一次池化结果:图像缩小为100×100像素
conv2:卷积核的规模为[3×3×16×16],size大小3×3,深度16,数量16
第二次卷积结果:16个卷积图像(feature map),100×100像素
Pooling:第二次池化,size大小2×2,Max pooling
第二次池化结果:图像缩小为50×50像素
FC1:第一次全连接,输入节点数为50×50×16= 40000,输出节点数为128,输出数据为[128×1]
FC2:第二次全连接,输入节点数为128,输出节点数为64,输出数据为[64×1]
FC3:第三次全连接,输入节点数为64,输出节点数为2,即两个数值,分别表示猫和狗的概率(通过softmax方法进行了转换)
以下是network代码实现部分

import torch
import torch.nn as nn
import torch.utils.data
import torch.nn.functional as Fclass Net(nn.Module):                                       # 新建一个网络类,就是需要搭建的网络,必须继承PyTorch的nn.Module父类def __init__(self):                                     # 构造函数,用于设定网络层super(Net, self).__init__()                         # 标准语句self.conv1 = torch.nn.Conv2d(3, 16, 3, padding=1)   # 第一个卷积层,输入通道数3,输出通道数16,卷积核大小3×3,padding大小1,其他参数默认self.conv2 = torch.nn.Conv2d(16, 16, 3, padding=1)  # 第二个卷积层,输入通道数16,输出通道数16,卷积核大小3×3,padding大小1,其他参数默认self.fc1 = nn.Linear(50*50*16, 128)                 # 第一个全连层,线性连接,输入节点数50×50×16,输出节点数128self.fc2 = nn.Linear(128, 64)                       # 第二个全连层,线性连接,输入节点数128,输出节点数64self.fc3 = nn.Linear(64, 2)                         # 第三个全连层,线性连接,输入节点数64,输出节点数2def forward(self, x):                   # 重写父类forward方法,即前向计算,通过该方法获取网络输入数据后的输出值x = self.conv1(x)                   # 第一次卷积x = F.relu(x)                       # 第一次卷积结果经过ReLU激活函数处理x = F.max_pool2d(x, 2)              # 第一次池化,池化大小2×2,方式Max poolingx = self.conv2(x)                   # 第二次卷积x = F.relu(x)                       # 第二次卷积结果经过ReLU激活函数处理x = F.max_pool2d(x, 2)              # 第二次池化,池化大小2×2,方式Max poolingx = x.view(x.size()[0], -1)         # 由于全连层输入的是一维张量,因此需要对输入的[50×50×16]格式数据排列成[40000×1]形式x = F.relu(self.fc1(x))             # 第一次全连,ReLU激活x = F.relu(self.fc2(x))             # 第二次全连,ReLU激活y = self.fc3(x)                     # 第三次激活,ReLU激活return y

数据加载

Kaggle提供的数据包括训练集和测试集,因此,在数据加载阶段,需要做的就是把训练集中所有的数据整理成[输入, 给定输出]的形式,在Dogs vs. Cats中,输入为一张张猫狗图片(input),给定输出是对应的猫或者狗信息(label)。

import os
import torch.utils.data as data
from PIL import Image
import numpy as np
import torch
import torchvision.transforms as transforms# 默认输入网络的图片大小
IMAGE_SIZE = 200# 定义一个转换关系,用于将图像数据转换成PyTorch的Tensor形式
dataTransform = transforms.Compose([transforms.Resize(IMAGE_SIZE),                          # 将图像按比例缩放至合适尺寸transforms.CenterCrop((IMAGE_SIZE, IMAGE_SIZE)),        # 从图像中心裁剪合适大小的图像transforms.ToTensor()   # 转换成Tensor形式,并且数值归一化到[0.0, 1.0],同时将H×W×C的数据转置成C×H×W,这一点很关键
])class DogsVSCatsDataset(data.Dataset):      # 新建一个数据集类,并且需要继承PyTorch中的data.Dataset父类def __init__(self, mode, dir):          # 默认构造函数,传入数据集类别(训练或测试),以及数据集路径self.mode = modeself.list_img = []                  # 新建一个image list,用于存放图片路径,注意是图片路径self.list_label = []                # 新建一个label list,用于存放图片对应猫或狗的标签,其中数值0表示猫,1表示狗self.data_size = 0                  # 记录数据集大小self.transform = dataTransform      # 转换关系if self.mode == 'train':            # 训练集模式下,需要提取图片的路径和标签dir = dir + '/train/'           # 训练集路径在"dir"/train/for file in os.listdir(dir):    # 遍历dir文件夹self.list_img.append(dir + file)        # 将图片路径和文件名添加至image listself.data_size += 1                     # 数据集增1name = file.split(sep='.')              # 分割文件名,"cat.0.jpg"将分割成"cat",".","jpg"3个元素# label采用one-hot编码,"1,0"表示猫,"0,1"表示狗,任何情况只有一个位置为"1",在采用CrossEntropyLoss()计算Loss情况下,label只需要输入"1"的索引,即猫应输入0,狗应输入1if name[0] == 'cat':self.list_label.append(0)         # 图片为猫,label为0else:self.list_label.append(1)         # 图片为狗,label为1,注意:list_img和list_label中的内容是一一配对的elif self.mode == 'test':           # 测试集模式下,只需要提取图片路径就行dir = dir + '/test/'            # 测试集路径为"dir"/test/for file in os.listdir(dir):self.list_img.append(dir + file)    # 添加图片路径至image listself.data_size += 1self.list_label.append(2)       # 添加2作为label,实际未用到,也无意义else:print('Undefined Dataset!')def __getitem__(self, item):            # 重载data.Dataset父类方法,获取数据集中数据内容if self.mode == 'train':                                        # 训练集模式下需要读取数据集的image和labelimg = Image.open(self.list_img[item])                       # 打开图片label = self.list_label[item]                               # 获取image对应的labelreturn self.transform(img), torch.LongTensor([label])       # 将image和label转换成PyTorch形式并返回elif self.mode == 'test':                                       # 测试集只需读取imageimg = Image.open(self.list_img[item])return self.transform(img)                                  # 只返回imageelse:print('None')def __len__(self):return self.data_size               # 返回数据集大小

训练和测试

train.py
在这里还是想再为大家讲述几个基本概念:.
(1)batchsize:批大小。在深度学习中,一般采用SGD训练,即每次训练在训练集中取batchsize个样本训练;

(2)iteration:1个iteration等于使用iteration个样本训练一次;

(3)epoch:1个epoch等于使用训练集中的全部样本训练一次,通俗的讲epoch的值就是整个数据集被轮几次。

比如训练集有500个样本,batchsize = 10 ,那么训练完整个样本集:iteration=50,epoch=1.

batch.: 深度学习每一次参数的更新所需要损失函数并不是由一个数据获得的,而是由一组数据加权得到的,这一组数据的数量就是batchsize.。

batchsize.最大是样本总数N,此时就是Full batch learning;最小是1,即每次只训练一个样本,这就是在线学习(Online Learning)。当我们分批学习时,每次使用过全部训练数据完成一次Forword运算以及一次BP运算,成为完成了一次epoch.。

from getdata import DogsVSCatsDataset as DVCD
from torch.utils.data import DataLoader as DataLoader
from network import Net
import torch
from torch.autograd import Variable
import torch.nn as nndataset_dir = './data/'                                                  # 数据集路径model_cp = './model/'               # 网络参数保存位置
workers = 10                        # PyTorch读取数据线程数量
batch_size = 16                     # batch_size大小
lr = 0.0001                         # 学习率
nepoch = 10device = torch.device("cuda:0" if (torch.cuda.is_available()) else "cpu")
# .to(device)def train():datafile = DVCD('train', dataset_dir)                                                           # 实例化一个数据集dataloader = DataLoader(datafile, batch_size=batch_size, shuffle=True, num_workers=workers, drop_last=True)     # 用PyTorch的DataLoader类封装,实现数据集顺序打乱,多线程读取,一次取多个数据等效果print('Dataset loaded! length of train set is {0}'.format(len(datafile)))model = Net()                       # 实例化一个网络model = model.to(device)               # 网络送入GPU,即采用GPU计算,如果没有GPU加速,可以去掉".cuda()"# model = model# model = nn.DataParallel(model)model.train()                       # 网络设定为训练模式,有两种模式可选,.train()和.eval(),训练模式和评估模式,区别就是训练模式采用了dropout策略,可以放置网络过拟合optimizer = torch.optim.Adam(model.parameters(), lr=lr)         # 实例化一个优化器,即调整网络参数,优化方式为adam方法criterion = torch.nn.CrossEntropyLoss()                         # 定义loss计算方法,cross entropy,交叉熵,可以理解为两者数值越接近其值越小cnt = 0             # 训练图片数量for epoch in range(nepoch):# 读取数据集中数据进行训练,因为dataloader的batch_size设置为16,所以每次读取的数据量为16,即img包含了16个图像,label有16个for img, label in dataloader:                                           # 循环读取封装后的数据集,其实就是调用了数据集中的__getitem__()方法,只是返回数据格式进行了一次封装img, label = Variable(img).to(device), Variable(label).to(device)           # 将数据放置在PyTorch的Variable节点中,并送入GPU中作为网络计算起点out = model(img)                                                    # 计算网络输出值,就是输入网络一个图像数据,输出猫和狗的概率,调用了网络中的forward()方法loss = criterion(out, label.squeeze())      # 计算损失,也就是网络输出值和实际label的差异,显然差异越小说明网络拟合效果越好,此处需要注意的是第二个参数,必须是一个1维Tensorloss.backward()                             # 误差反向传播,采用求导的方式,计算网络中每个节点参数的梯度,显然梯度越大说明参数设置不合理,需要调整optimizer.step()                            # 优化采用设定的优化方法对网络中的各个参数进行调整optimizer.zero_grad()                       # 清除优化器中的梯度以便下一次计算,因为优化器默认会保留,不清除的话,每次计算梯度都回累加cnt += 1print('Epoch:{0},Frame:{1}, train_loss {2}'.format(epoch, cnt*batch_size, loss/batch_size))          # 打印一个batch size的训练结果torch.save(model.state_dict(), '{0}/model.pth'.format(model_cp))            # 训练所有数据后,保存网络的参数if __name__ == '__main__':train()

test.py
测试的时候需要计算样本为猫或者狗的概率,需要加载训练好的模型和数据。

from getdata import DogsVSCatsDataset as DVCD
from network import Net
import torch
from torch.autograd import Variable
import numpy as np
import torch.nn.functional as F
import torch.nn as nn
import matplotlib.pyplot as plt
from PIL import Image
import random
import os
import getdatadataset_dir = './data/test/'                    # 数据集路径
model_file = './model/model.pth'                # 模型保存路径
N = 10device = torch.device("cuda:0" if (torch.cuda.is_available()) else "cpu")
# .to(device)
# # old version
# def test():
#
#     model = Net()                                       # 实例化一个网络
#     model.cuda()                                        # 送入GPU,利用GPU计算
#     model = nn.DataParallel(model)
#     model.load_state_dict(torch.load(model_file))       # 加载训练好的模型参数
#     model.eval()                                        # 设定为评估模式,即计算过程中不要dropout
#
#     datafile = DVCD('test', dataset_dir)                # 实例化一个数据集
#     print('Dataset loaded! length of train set is {0}'.format(len(datafile)))
#
#     index = np.random.randint(0, datafile.data_size, 1)[0]      # 获取一个随机数,即随机从数据集中获取一个测试图片
#     img = datafile.__getitem__(index)                           # 获取一个图像
#     img = img.unsqueeze(0)                                      # 因为网络的输入是一个4维Tensor,3维数据,1维样本大小,所以直接获取的图像数据需要增加1个维度
#     img = Variable(img).cuda()                                  # 将数据放置在PyTorch的Variable节点中,并送入GPU中作为网络计算起点
#     print(img)
#     out = model(img)                                            # 网路前向计算,输出图片属于猫或狗的概率,第一列维猫的概率,第二列为狗的概率
#     out = F.softmax(out, dim=1)                                        # 采用SoftMax方法将输出的2个输出值调整至[0.0, 1.0],两者和为1
#     print(out)                      # 输出该图像属于猫或狗的概率
#     if out[0, 0] > out[0, 1]:                   # 猫的概率大于狗
#         print('the image is a cat')
#     else:                                       # 猫的概率小于狗
#         print('the image is a dog')
#
#     img = Image.open(datafile.list_img[index])      # 打开测试的图片
#     plt.figure('image')                             # 利用matplotlib库显示图片
#     plt.imshow(img)
#     plt.show()# new version
def test():# setting modelmodel = Net()                                       # 实例化一个网络model.to(device)                                       # 送入GPU,利用GPU计算model.load_state_dict(torch.load(model_file))       # 加载训练好的模型参数model.eval()                                        # 设定为评估模式,即计算过程中不要dropout# get datafiles = random.sample(os.listdir(dataset_dir), N)   # 随机获取N个测试图像imgs = []           # imgimgs_data = []      # img datafor file in files:img = Image.open(dataset_dir + file)            # 打开图像img_data = getdata.dataTransform(img).to(device)           # 转换成torch tensor数据   ++imgs.append(img)                                # 图像listimgs_data.append(img_data)                      # tensor listimgs_data = torch.stack(imgs_data)                  # tensor list合成一个4D tensor# calculationout = model(imgs_data)                              # 对每个图像进行网络计算out = F.softmax(out, dim=1)                         # 输出概率化out = out.data.cpu().numpy()                        # 转成numpy数据# pring results         显示结果for idx in range(N):plt.figure()if out[idx, 0] > out[idx, 1]:plt.suptitle('cat:{:.1%},dog:{:.1%}'.format(out[idx, 0], out[idx, 1]))else:plt.suptitle('dog:{:.1%},cat:{:.1%}'.format(out[idx, 1], out[idx, 0]))plt.imshow(imgs[idx])plt.show()if __name__ == '__main__':test()

结果展示

参考

[1] CNN入门+猫狗大战(Dogs vs. Cats)+PyTorch入门.
[2] 深度学习实战—猫狗大战(pytorch实现).
[3] 深度学习中的batch、epoch、iteration的含义.

Python分类实例之猫狗大战相关推荐

  1. python 二分类的实例_keras分类之二分类实例(Cat and dog)

    1. 数据准备 在文件夹下分别建立训练目录train,验证目录validation,测试目录test,每个目录下建立dogs和cats两个目录,在dogs和cats目录下分别放入拍摄的狗和猫的图片,图 ...

  2. Python机器学习(二):Logistic回归建模分类实例——信用卡欺诈监测(上)

    Logistic回归建模分类实例--信用卡欺诈监测 现有一个creditcard.csv(点此下载)数据集,其中包含不同客户信用卡的特征数据(V1.V2--V28.Amount)和标签数据(Class ...

  3. Python爬虫实例(3)--BeautifulSoup的CSS选择器

    Python爬虫实例 紧接着上一讲的内容. 我们初步了解了bs4这个解析库. 但是bs4难道只有find,find_all了吗? 如果层次比较深,相似的元素比较多,和可能会写的比较长. 最主要的是很难 ...

  4. Python爬虫实例 wallhaven网站高清壁纸爬取。

    文章目录 Python爬虫实例 wallhaven网站高清壁纸爬取 一.数据请求 1.分析网页源码 2.全网页获取 二.数据处理 1.提取原图所在网页链接 2.获取高清图片地址及title 三.下载图 ...

  5. python决策树实例_Python机器学习之决策树算法实例详解

    本文实例讲述了Python机器学习之决策树算法.分享给大家供大家参考,具体如下: 决策树学习是应用最广泛的归纳推理算法之一,是一种逼近离散值目标函数的方法,在这种方法中学习到的函数被表示为一棵决策树. ...

  6. Python 分类导航

    Python 分类导航 /Python 爬虫 Python爬虫教程-01-爬虫介绍 Python爬虫教程-02-使用urlopen Python爬虫教程-03-使用 chardet 检测编码 Pyth ...

  7. 机器学习应用篇(五)——决策树分类实例

    机器学习应用篇(五)--决策树分类实例 文章目录 机器学习应用篇(五)--决策树分类实例 一.数据集 二.实现过程 1 数据特征分析 2 利用决策树模型在二分类上进行训练和预测 3 利用决策树模型在多 ...

  8. python数据分析实例:客户流失预警模型

    python数据分析实例:客户流失预警模型 客户流失是电信行业最重要的服务方面之一.客户流失的广义说法是因为客户自己或运营商违反服务协议而终止客户服务的行为. 流失预测流程一共分为四个步骤,分别为(1 ...

  9. 用python实现视频换脸_超简单使用Python换脸实例

    换脸! 这段时间,deepfakes搞得火热,比方说把<射雕英雄传>里的朱茵换成了杨幂,看下面的图!毫无违和感! 其实早在之前,基于AI换脸的技术就得到了应用,比方说<速度与激情7& ...

最新文章

  1. 积神经网络的参数优化方法——调整网络结构是关键!!!你只需不停增加层,直到测试误差不再减少....
  2. Softmax vs. SoftmaxWithLoss 推导过程
  3. Java内部类详解(使用场景和好处、相关内部类的笔试面试题)
  4. 中国工程院院士:物联网市场须走出碎片化
  5. java类与对象实验报告心得体会_Java类与对象实验报告.doc
  6. 全局修改elementui message 右边弹出_ElementUI 只允许 $message 提示一次
  7. “not a single-group group function”
  8. WORD设置别人只可阅读,不可更改文档?
  9. python之event【事件】
  10. C#中 JSON 序列化 与 反序列化
  11. 开发常用在线协作工具推荐
  12. R软件本地安装GitHub下载的R包
  13. Assembler如何把跳转汇编变成机器码的(四)
  14. 发布一个太空大战小游戏
  15. 视频配音怎么制作?手把手教你配音视频制作
  16. windows端的MarginNote:BookxNote
  17. oppo微信皮肤主题怎么设置
  18. 数据分析实战之RMF模型(用户精准运营)
  19. 双系统 Win10下安装Linux(单/双硬盘)
  20. 怎么用js抽取身份证号中的年份?

热门文章

  1. Unreal Engine4开篇
  2. STM32系统时钟设置,采用外部有源晶振相关配置问题
  3. 如何让 keil MDK v5 支持 ARM7/9 设备
  4. C语言中文网Socket学习笔记
  5. Spring Boot使用Spring DeferredResult实现长轮询,纵享新丝滑让你体验丝滑般的感觉 - 第414篇
  6. linux mysql5.7.36 离线安装使用全教程(含安装包)
  7. 【学员心得】我的HCIE-DC学习成长旅程
  8. leetcode:515. 在每个树行中找最大值
  9. Java学习之阶段小结
  10. 记:《洛克菲勒留给儿子的38封信》-- 37