文章目录
- 前言:数据集介绍
- 0.准备工作:首先导入相关包,设置参数等
- 1.数据预处理之增强(transforms等)
- 2.数据的读取(Dataset&Dataloader)
- 3.模型的搭建(nn.model)
- 4.开始训练(loss函数,优化器,训练epoch)
- 先定义损失函数,优化器等
- 训练集上开始训练
- 测试集上计算loss及准确率
- 验证测试模型(没有标签的测试图片)
前言:数据集介绍
在学习完深度学习的理论之后,就要开始代码实战,基本上分为数据的读取,数据预处理(增强),模型的搭建及训练
首先介绍一下数据集,此次采用的数据集是CIFAR10,是一个经典的10分类彩色图片数据集。
- 一共包含 10 个类别的 RGB 彩色图 片:飞机( airplane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。
- 图片的尺寸为 32×32
- 数据集中一共有 50000 张训练图片和 10000 张测试图片
- 官方地址:https://www.cs.toronto.edu/~kriz/cifar.html 可以通过官网直接下载,有三个版本的(python,matlab,c);也可以直接在代码中下载(pytorch已封装,下文就采用这种方法)
从官网下载的是cifar-10-python.tar.gz,解压之后会得到相应文件夹,里面的文件如下图所示,显然并不是直接以图片的形式存放。由于pytorch的torchvision.datasets包中已经写好了CIFAR10的类,就说明可以直接调用再进行处理,所以不需要关心怎么转化成普通的图片类型(当然也可以通过代码将其转换成jpg或png)
0.准备工作:首先导入相关包,设置参数等
0-4: trainer.py
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import argparse
from torch import nn
parser=argparse.ArgumentParser()
parser.add_argument("--n_epochs", type=int , default=10, help="the number of epochs")
parser.add_argument("--batch_size", type=int, default=64)
parser.add_argument("--lr", type=float, default=0.001, help="learning rate")
args=parser.parse_args()
print(args)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
Namespace(batch_size=64, lr=0.001, n_epochs=10)
cuda
torch.device可以选择使用GPU训练,可以选择上述写法(更方便广泛),也可以像下面这样直接指定:
device=torch.device("cpu")
device=torch.device("cuda")
device=torch.device("cuda:0")
设置好device之后,就需要把网络模型,数据,loss函数等放到这个设备上,使用的时.to(device)
,具体可以看下面步骤的操作。
再说一下argparse包,这是python自带的命令行参数解析包,可以用来方便地读取命令行参数。如果代码需要频繁地修改参数的时候,使用这个工具可以将参数和代码分离开来,让代码更简洁,适用范围更广。
比如我们执行代码时,一般是python train.py
,此时程序就会执行默认参数;如果需要改变参数可以这样python train.py --batch_size 32
主要讲一下add_argument()函数的参数
参数 | 描述 |
---|
dest | 默认的变量名是–或-后面的字符串,也可以通过dest=xxx来设置参数的变量名。在代码中用args.xxx来获取参数的值。 |
default | 没有设置值情况下的默认参数 |
type | 参数类型(int,float,string……) |
required | 表示这个参数是否一定需要设置 |
choices | 参数值只能从几个选项里面选择 |
help | 指定参数的说明信息 |
action | 相当于把参数设成了一个“开关”,不需要给这个开关传递具体的值(常用在参数为true或false的情况) |
更多argparse信息:http://vra.github.io/2017/12/02/argparse-usage/
1.数据预处理之增强(transforms等)
一般来说只需要对训练集进行transforms操作,测试集一般只需转换为tensor
train_tfm=transforms.Compose([transforms.Resize((32,32)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor()])
主要是设置transforms来对训练集进行数据增强,常用的还有很多,这边只是进行了resize(由于图片本身就是32x32规则,所以这个操作也可以不用),随机翻转和转化为Tensor。更多transforms操作可参见:常用transforms大集合
2.数据的读取(Dataset&Dataloader)
"""
数据加载,将数据写进Dataset和DataLoader中
"""
train_data=datasets.CIFAR10(root="./dataset",train=True,
transform=train_tfm,download=True)
test_data = datasets.CIFAR10(root="./dataset", train=False,
transform=transforms.ToTensor(),download=True)
train_loader= DataLoader(train_data,batch_size = args.batch_size,
shuffle = True,num_workers = 0)
test_loader=DataLoader(test_data,batch_size = args.batch_size,
shuffle = False,num_workers = 0)
train_len=len(train_data)
test_len=len(test_data)
print("训练集的大小是:{}".format(train_len))
print("测试集的大小是:{}".format(test_len))
Files already downloaded and verified
Files already downloaded and verified
训练集的大小是:50000
测试集的大小是:10000
采用的是官网提供的datasets类来获取数据集,基本上比较清晰。更多Dataset&DataLoader用法(如想读取自己的数据集)可以参见:click here
3.模型的搭建(nn.model)
参照的网络模型是下图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/900e6dbe1b364b979a25d538c85b034c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbmFueWlkZXY=,size_20,color_FFFFFF,t_70,g_se,x_16)
为增加模型的泛化能力和减小误差,在原模型的基础上,加了几个非线性层(激活函数),每一层的输入输出和上图保持一致
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.module=nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32,
kernel_size=5, stride=1, padding=2),
nn.RelU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Flatten(),
nn.ReLU(),
nn.Linear(1024, 10))
def forward(self,x):
x = self.module(x)
return x
卷积网络各个层的详细用法和作用可以参见:click here
注意:torch.nn 只支持小批量输入,而不支持单个样本。例如,nn.Conv2d
接受一个 4 维的张量,每一维分别是 sSamples x nChannels x Height x Width
(样本数 x 通道数 x 高 x 宽),直接输入一张图片(三维)是不行的
如果是单个样本:
需使用 input.unsqueeze(0)
或者reshape(input,(1,3,224,224))
来添加其它的维数,具体可以参见下文选取一张图片进行验证的代码写法。
4.开始训练(loss函数,优化器,训练epoch)
先定义损失函数,优化器等
if __name__ == '__main__':
my_module = MyModule()
my_module = my_module.to(device)
criterion = nn.CrossEntropyLoss()
criterion=criterion.to(device)
optim = torch.optim.SGD(my_module.parameters(), args.lr)
训练集上开始训练
for i in range(args.n_epochs):
my_module.train()
print("第{}轮训练开始:".format(i+1))
for train_step,(imgs,targets) in enumerate(train_loader):
imgs = imgs.to(device)
targets = targets.to(device)
output = my_module(imgs)
loss = criterion(output, targets)
optim.zero_grad()
loss.backward()
optim.step()
if (train_step+1) % 100 == 0:
print("训练次数:{},loss:{}".format(train_step,loss.item()))
测试集上计算loss及准确率
test_loss = 0
total_correct = 0
my_module.eval()
with torch.no_grad():
for imgs,targets in test_loader:
imgs=imgs.to(device)
targets=targets.to(device)
output=my_module(imgs)
loss=criterion(output,targets)
test_loss=loss.item()+test_loss
correct = (output.argmax(1) == targets).sum()
total_correct = correct + total_correct
"""
# 可以保存每一次训练的模型pth
torch.save(my_module,"my_model{}.pth".format(i+1))
"""
print("测试集上的loss:{}".format(test_loss))
print("测试集的准确率:{}".format(total_correct/test_len))
torch.save(my_module,"my_module.pth")
print("模型已保存")
输出:
完整的训练到这里就结束了,下一步就是优化模型,让test loss更小——炼丹开始
验证测试模型(没有标签的测试图片)
tester.py
:选取几张数据集中没有的照片,如下:
img_path="./pic/dog.jpg"
img_path2="./pic/airplane.jpg"
from PIL import Image
from torch import nn
from torchvision import transforms
import torch
from trainer import MyModule
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
img_path="./pic/airplane.jpg"
image = Image.open(img_path)
tfm = transforms.Compose([transforms.Resize((32,32)),
transforms.ToTensor()])
img_tfm = tfm(image)
"""
就是上文的模型,如果不导入from trainer import MyModule,就需要把模型再写一遍
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.module = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32,
kernel_size=5, stride=1, padding=2),
nn.RelU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Flatten(),
nn.ReLU(),
nn.Linear(1024, 10))
def forward(self, x):
x = self.module(x)
return x
"""
model = torch.load("my_module.pth")
print(model)
img = torch.reshape(img_tfm,(1,3,32,32))
img=img.to(device)
output = model(img)
print(output)
print(output.argmax(1))
输出如下图所示,可以看出模型判断其为0类(查看最开始数据集介绍,第一个就是0类,airplane)
![在这里插入图片描述](https://img-blog.csdnimg.cn/c40ab490aa524975832bdd5f285a470c.png)
用官方提供的预训练模型——VGG16
VGG16是用ImageNet训练的,他的输入一般是224x224,这里我们也resize以下。一共有1000个类别,我们修改以下模型让他用于十分类
from PIL import Image
from torchvision import transforms,models
img_path="./pic/dog.jpeg"
image = Image.open(img_path)
tfm = transforms.Compose([transforms.Resize((224,224)),transforms.ToTensor()])
img_tfm = tfm(image)
vgg16_true = models.vgg16(pretrained=True)
vgg16_true.add_module("linear",nn.Linear(1000,10))
print(vgg16_true)
img = torch.reshape(img_tfm,(1,3,224,224))
output = vgg16_true(img)
print("输出的标签是:",output.argmax(1))
输出的标签是: tensor([208])
查一下ImageNet千分类对应的标签:
![在这里插入图片描述](https://img-blog.csdnimg.cn/f2ae717c8689465fb46762216cf78fdb.png)
只能说,千分类太细了,我也不太懂狗的品种……
参考链接:https://www.bilibili.com/video/BV1hE411t7RN?
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)