本文是基于博客PyTorch学习之路(level1)——训练一个图像分类模型并结合所查资料及自己的理解整理出来的,目的是作为python基础知识备忘,侵删。
torchvision.transforms
的功能为:
PIL.image
/numpy.ndarray
与Tensor
相互转化
Tensor
归一化对
PIL.image
裁剪、缩放等通常,在使用
torchvision.transforms{}
,我们通常使用transforms.Compose{}
将transforms组合在一起
利用pytorch封装好的
torchvision.datasets.ImageFolder
接口,默认训练数据按照一个类别对应一个文件夹的情形data_dir = '/data' # 数据所在路径 image_datasets = {x: datasets.ImageFolder( os.path.join(data_dir, x), data_transforms[x]), # 可用于resize crop等的字典 for x in ['train', 'val']}
torchvision.datasets.ImageFolder
会返回一个list,list中的每个值都是一个tuple,每个tuple包含图片路径和标签两个对象。data_transforms = { 'train': transforms.Compose([ transforms.RandomSizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), 'val': transforms.Compose([ transforms.Scale(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), }
data_transforms={}
是一个字典,功能是图片预处理,如resize、crop等,python实现的时候采用pytorch封装好的torchvision.transforms
模块,例如:
torchvision.transforms.RandomSizedCrop
——————图片裁剪,输入是PIL image(用pytorch的PIL库读取的图片内容)Crop是从图像中移除不需要的信息,只保留需要的部分*[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aciTgmmI-1573464364817)(E:\Markdown工具-Typora\images-saved\1573454072668.png)]
transforms.ToTensor
——————将PIL.Image
/numpy.ndarray
数据进转化为torch.FloatTensor
transforms.Normalize()
————————输入是Tensor,对其归一化,公式为channel=(channel-mean)/std
dataloders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4, shuffle=True, num_workers=4) for x in ['train', 'val']}
torchvision.datasets.ImageFolder
只是返回list,而list不能作为模型的输入,因此需要另一个类来封装list,那就是torch.utils.data.DataLoader
.
torch.utils.data.DataLoader
——————将list类型的输入数据封装成Tensor数据格式,以备模型使用。⚠️注意:图片和标签分别封装为一个Tensor
torch.utils.data.Dataset
是一个抽象类,pytorch中所有和数据相关的类都要继承这个类实现。所以当你的数据不是按照一个类别一个文件夹这种方式存储时,你就要自定义一个类来读取数据,自定义的这个类必须继承自
troch.utils.data.Dataset
这个基类,最后同样用torch.data.DataLoader封装成Tensor。
for data in dataloders['train']: inputs, labels = data if use_gpu: inputs = Variable(inputs.cuda()) labels = Variable(labels.cuda()) else: inputs, labels = Variable(inputs), Variable(labels)
dataloader
——————字典;
dataloader['train']
——————载入训练数据;
for
循环——————从dataloaders[‘train’]中读取batch_size
个数据,data里面包含图像input tensor和标签label tensor
torch.autograd.Variable
——————将tensor封装成模型可以使用的variable数据类型为什么要封装成variable类型呢?
Variable可以看成是tensor的一种包装,其不仅包含了tensor的内容,还包含了梯度等信息,因此在神经网络中常常用Variable数据结构。那么怎么从一个Variable类型中取出tensor呢?也很简单,比如下面封装后的inputs是一个Variable,那么
inputs.data
就是对应的tensor。
因为预训练网络一般是在1000类的ImageNet数据集上进行的,所以要迁移到你自己数据集的2分类,需要替换最后的全连接层为你所需要的输出。
# models模块导入resnet18网络,然后获取全连接层的输入channel个数,用这个channel个数和你要做的分类类别数(这里是2)替换原来模型中的全连接层。 model = models.resnet18(pretrained=True) # 导入模型 num_ftrs = model.fc.in_features model.fc = nn.Linear(num_ftrs, 2)
torchvision.models
——————导入模型,如VGG,ResNet,DenseNet等
在PyTorch中采用torch.nn模块来定义网络的所有层,比如卷积、降采样、损失层等等,这里采用交叉熵函数,因此可以这样定义:
critertion=nn.CrossEntropyLoss()
- 定义优化函数:
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 虽然写的是SGD但是由于有动量,所以是Adam
torch.optim
————SGD
lr
——————learning rate学习率
momentum
——————动量
- 定义学习率变化策略:
scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
采用的是
torch.optim.lr_scheduler
模块的StepLR类,表示每隔step_size个epoch就将学习率降为原来的gamma倍。
- 更新学习率
scheduler.step()
- 设置模型为训练状态
model.train(True)
- 将网络中所有梯度置0
optimizer.zero_grad()
- 前向传播
outputs = model(inputs)
- 损失函数
loss = criterion(outputs, labels)
由于网络的输入为variable数据类型,所以
output
也是variable。
- 分类
_, preds = torch.max(outputs.data, 1)
得到输出后(网络的全连接层的输出)还希望能得到模型预测该样本属于哪个类别的信息,这里采用
torch.max
。**torch.max()
的第一个输入是tensor格式,所以用outputs.data而不是outputs作为输入;**第二个参数1是代表dim的意思,也就是取每一行的最大值,其实就是我们常见的取概率最大的那个index;第三个参数loss也是torch.autograd.Variable格式。
torch.max(a,1)
——————返回每一行中元素的最大值及其索引拓展:
torch.max()[0]
————只返回最大值的每个数
troch.max()[1]
————只返回最大值的每个索引
torch.max()[1].data
————只返回variable中的数据部分(去掉Variable containing:)
torch.max()[1].data.numpy()
————把数据转化成numpy ndarry
torch.max()[1].data.numpy().squeeze()
————把数据条目中维度为1 的删除掉
torch.max(tensor1,tensor2) element-wise
————比较tensor1 和tensor2 中的元素,返回较大的那个值
- 回传损失backward
training时才有bp,test时只有forward
loss.backward()
- 计算梯度并不断更新
optimizer.step()
optimizer.step
————更新参数,可从optimizer.param_groups[0]['params']
里面看到各个层的梯度和权值信息
- GPU操作
use_gpu = torch.cuda.is_available()
判断是否有gpu可以用,如果有则use_gpu是true。
optimizer.step()
```optimizer.step```————更新参数,可从```optimizer.param_groups[0]['params']```里面看到各个层的梯度和权值信息 - **GPU操作** ```python use_gpu = torch.cuda.is_available()
判断是否有gpu可以用,如果有则use_gpu是true。
以上就是一个batch数据的训练过程,不断重复这个过程就可以达到训练效果。