代码步骤笔记
导入模块
import torch
from tensorboardX import SummaryWriter //可视化
设置参数
batch_size=64
works=4
epochs=20
train_path="train"
val_path="val"
数据预处理
流程:先定义数据集,再将定义的数据集导入数据载入器(Dataloader)来读取数据。
定义数据集有两种方式,一种是自定义Dataset包装类,和DataLoader类一样,它是torch.utils.data的里的一个类,另一种是直接调用ImageFolder函数,它是torchvision.datasets里的函数。
定义数据集
1.Dataset
Dataset是一个抽象类,可以自定义数据集,为了能够方便的读取,需要将要使用的数据包装为Dataset类。
自定义的Dataset需要继承它并且实现两个成员方法:
1.__getitem__():该方法定义用索引(0到len(self))获取一条数据或一个样本。
2.__len__()方法返回数据集的总长度。
模板如下:
import torch.utils.data
#定义一个数据集
class CaptionDataset(Dataset):
""" 数据集演示 """
def __init__(self,transform=None):
"""实现初始化方法,在初始化的时候将数据读载入"""
....(包括加载数据路径)
def __getitem__(self):
return self....
def __len__(self):
return len(...)
# 实例化这个类,然后我们就得到了Dataset类型的数据,记下来就将这个类传给DataLoader,就可以了。
train_data= CaptionDataset(transform=transform) #transform需自己定义(见下面torchvision.transforms)
2.ImageFolder
ImageFolder假设所有的文件按文件夹保存,每个文件夹下存储同一个类别的图片,文件夹名为类名,其构造函数如下:
import torchvision.datasets
ImageFolder(root, transform=None, target_transform=None, loader=default_loader)
各参数含义:
root:在root指定的路径下寻找图片
transform:对PIL Image进行的转换操作,transform的输入是使用loader读取图片的返回对象
target_transform:对label的转换
loader:给定路径后如何读取图片,默认读取为RGB格式的PIL Image对象
label:按照文件夹名顺序排序后存成字典,即{类名:类序号(从0开始)}
举例如下:
import torchvision.datasets
#此处transform需自己定义(见下面torchvision.transforms),其他参数为默认值
train_data=torchvision.datasets.ImageFolder(root=train_path,transform=transform)
加载数据集
DataLoader
DataLoader是一个数据加载器类,实现了对数据集进行随机采样和多轮次迭代的功能。在训练过程中,可以非常方便地实现多轮次小批量随机梯度下降训练。
常用参数有:Dataset数据集实例,batch_size(每个batch的大小,shuffle(是否进行搅乱操作),num_workers(加载数据的时候使用几个子进程),返回一个可迭代对象。
import torch.utils.data
train_loader = torch.utils.data.DataLoader(
CaptionDataset(train_data, transform=transform),
batch_size=batch_size, shuffle=True, num_workers=workers)
详细有关参数见博客:PyTorch 中的数据类型 torch.utils.data.DataLoader
torchvision–数据预处理要使用的库
torchvision是Pytorch中专门用来处理图像的库。
提供了常用图片数据集(datasets);
训练好的模型(models);
一般的图像转换操作类(transforms),
torchvision.datasets
torchvision.datasets可以理解为PyTorch团队自定义的dataset,这些dataset帮我们提前处理好了很多的图片数据集,我们拿来就可以直接使用:
- MNIST
- COCO
- Captions
- Detection
- LSUN
- ImageFolder
- Imagenet-12
- CIFAR
- STL10
- SVHN
- PhotoTour
以上我们可以直接用(其他的只能通过自己自定义数据集),示例如下:
import torchvision.datasets as datasets
trainset = datasets.MNIST(root='./data', # 表示 MNIST 数据的加载的目录
train=True, # 表示是否加载数据库的训练集,false的时候加载测试集
download=True, # 表示是否自动下载 MNIST 数据集
transform=None) # 表示是否需要对数据进行预处理,none为不进行预处理
torchvision.models
torchvision提供了训练好的模型,可以加载后直接使用(见下面代码),或者在进行迁移学习torchvision.models模块的子模块中包含以下模型结构:
- AlexNet
- VGG
- ResNet
- SqueezeNet
- DenseNet
#导入预训练模型
import torchvision.models
model = torchvision.models.vgg16(pretrained=True) #True代表已经训练好的模型
torchvision.transforms
transform模块提供了一般的图像转换操作类,用作数据处理和数据增强。
主要提供了对PIL Image对象和Tensor对象的常用操作。
对PIL Image对象的常用操作有:
- Resize:调整图片尺寸
- CenterCrop、RandomCrop、RandomSizedCrop:裁剪图片
- Pad:填充
- ToTensor:将PIL Image对象转成Tensor,会自动将[0,255]归一化至[0,1]
对Tensor对象的常用操作有:
- Normalize:标准化,即减均值,除以标准差
- ToPILImage:将Tensor转为PIL Image对象。
import torchvision.transforms as transforms
transform = transforms.Compose([
transforms.RandomCrop(32, padding=4), #先四周填充0,在把图像随机裁剪成32*32
transforms.RandomHorizontalFlip(), #图像一半的概率翻转,一半的概率不翻转
transforms.RandomRotation((-45,45)), #随机旋转
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.229, 0.224, 0.225)), #R,G,B每层的归一化用到的均值和方差
])
详细有关transforms的用法见博客:PyTorch 学习笔记(三):transforms的二十二个方法
训练网络参数
训练前的准备
设置指定的训练设备(GPU、CPU)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
定义损失函数
torch.nn模块中定义了很多标准地损失函数。
import torch.nn as nn
xentropy=nn.CrossEntropyLoss() #此处定义一个交叉熵损失函数对象,该对象可以调用backward()方法实现误差反向传播。
定义优化器
torch.optim模块提供了很多优化算法类,
比如:torch.optim.SGD,torch.optim.Adam,torch.optim.RMSprop。这里以SGD为例。
#import torch.optim
net=CNN().to(device) #使用gpu构造一个CNN对象
optimizer=torch.optim.SGD(params=net.parameters(),lr=0.01,momentum=0.9)
#上式参数依次为:需要网络模型的参数、学习率、动量参数
详细参数见博客:torch.optim.SGD()各参数的解释
训练过程
神经网络训练过程的一步迭代包含四个主要步骤:
- 前向运算,计算给定输入的预测结果
- 计算损失函数值
- 反向传播(BP),计算参数梯度(计算之前要先梯度清零)
- 使用梯度下降法更新参数值
详细代码如下:
def train(net,optimizer,loss_fn,num_epoch,data_loader,device):
'''参数分别为网络模型、损失函数(对应之前的xentropy)、epoch总次数、数据加载器、训练设备'''
net.train() #进入训练模型
for epoch in range(num_epoch):
print('Epoch {}/{}'.format(epoch+1, num_epochs))
running_loss=0
running_corrects=0
for i,data in enumerate(data_loader):
inputs=data[0].to(device) #输入
labels=data[1].to(device) #真实值标签
#下面优化过程
optimizer.zero_grad() #先把前一步的梯度清除,设置梯度值为0
outputs=net(inputs) #前向运算,计算网络模型在inputs上的输出outputs
loss=loss_fn(outputs,labels) #计算损失函数值
loss.backward() #进行反向传播,计算梯度
optimizer.step() #使用优化器的step()方法,进行梯度下降,更新模型参数
#可以输出两种loss,loss为每次迭代的loss,running_loss为每个epoch的loss,之后再取平均值。
running_loss+=loss.item() #计算每个epoch的loss总值
_, preds = torch.max(outputs, 1)
running_corrects += torch.sum(preds == labels).item()
epoch_loss=running__loss/len(train_data) #计算每个epoch的平均loss
epoch_acc = running_corrects / len(train_data)
print('{} Loss: {:.4f} Acc: {:.4f}'.format(
'train', epoch_loss, epoch_acc))
验证/测试过程
测试和验证集过程不用反向传播,也不用更新梯度。
def evaluate(net,loss_fn,data_load,device):
net.eval() #进入模型评估模式,验证和测试都是这个
running_loss=0
correct=0.0
total=0
for data in data_loader:
inputs=data[0].to(device) #输入
labels=data[1].to(device) #真实值标签
with torch.no_grad():
outputs=net(inputs)
loss=loss_fn(outputs,labels)
running_loss+=loss.item()
_,predicted=torch.max(outputs.data,1)
total+=labels.size(0) #另一种计算总数的方法
correct+=(predicted==labels).sum().item() #计算预测对的数
epoch_loss = running_loss/len(val_data)
acc=correct/total #计算准确率
print('{} Loss: {:.4f} Acc: {:.4f}'.format(
'valid', epoch_loss, acc))
运行
有两种方式:
- 1.设立一个主函数main(),将for epoch in epochs:以及train函数和test函数放到main()里运行就可以了。
- 2.将for epoch in epochs:和test函数放入train函数,再直接运行train()函数就可以了。
完整代码实例:pytorch实现图像分类代码实例