pytorch数据处理工作箱
数据下载和预处理是机器学习,深度学习项目种耗时有重要的任务,数据预处理关系到数据质量和模型性能,pytorch提供了专门的数据下载,数据处理包。
概述
数据处理(数据装载,数据预处理,数据增强)主要工具包和相互关系:
左边是 torch.utils.data 工具包,包括四个类:
- Dataset,一个抽象类,其他数据要继承这个类,并且覆写_getitem__ ,len 的构造方法。
- DataLoader: 定义一个新的迭代器,实现批量(batch)读取,大量数据(shuffle)并提供并行加速等功能。
- random_split: 把数据集随机拆分为给定长度的非重叠的新数据集
- *sampler: 多种采样函数。
中间的 torchvision 是可视化处理工具,是一个视觉处理包,需要额外安装。主要功能:
- datasets 提供常用的数据加载,继承自 utils.data.Dataset,比如 COCO, MMIST等。
- models: 提供深度学习种各种经典的网络结构和已经训练好的模型(芜湖,pretrained=True),包括 AlexNet,VGG ,ResNet等。
- transforms,常用的数据处理操作,包括对 tensor,PIL Image的操作。
- utils 包括两个函数,make_grid,将多张图片拼接在一个网络种,一个 save_img, 将 tensor 保存成图片。
utils.data 简介
utils.data 包括 Dataset and DataLoader ,torch.utils.data.Dataset 为抽象类。自定义的数据集要继承这个类,并构造 len, getitem ,两个函数,前者提供数据的大小(size), 后者通过给定索引获得数据和标签。getitem 一次获得一个数据,需要通过 DataLoader 定义迭代器,实现batch 读取。
import torch
from torch.utils import data
import numpy as np
# 定义获取数据的类
class TestDataset(data.Dataset):#继承Dataset
def __init__(self):
self.Data=np.asarray([[1,2],[3,4],[2,1],[3,4],[4,5]])#一些由2维向量表示的数据集
self.Label=np.asarray([0,1,0,1,2])#这是数据集对应的标签
def __getitem__(self, index):
#把numpy转换为Tensor
txt=torch.from_numpy(self.Data[index])
label=torch.tensor(self.Label[index])
return txt,label
def __len__(self):
return len(self.Data)
Test=TestDataset()
print(Test[2]) #相当于调用__getitem__(2)
print(Test.__len__())
#輸出:
#(tensor([2, 1]), tensor(0)) # 只返回一个样本,批量处理要同时进行打乱和并行加速的操作
#5 # 可以使用DataLoader
data.DataLoader(
dataset, # 加载的数据集
batch_size=1, # 批大小
shuffle=False, # 是否打乱数据
sampler=None, # 样本抽样
batch_sample=None,
num_workers=0, # 多进程加载的进程数,0表示不使用
collate_fn=<function default_collate at 0x7f108ee01620>,
pin_memory=False, # 是否将内存保存在pin memory区,这样转到GPU 更快一些
drop_last=False, # 将不足一个batch的数据丢弃,为True时。
timeout=0,
worker_init_fn=None
)
test_loader = data.DataLoader(Test,batch_size=2,shuffle=False)
for i,traindata in enumerate(test_loader):
print('i:',i)
Data,Label=traindata
print('data:',Data)
print('Label:',Label)
>>>
i: 0
data: tensor([[1, 2],
[3, 4]])
Label: tensor([0, 1])
i: 1
data: tensor([[2, 1],
[3, 4]])
Label: tensor([0, 1])
i: 2
data: tensor([[4, 5]]) # 只有5个数据
Label: tensor([2])
dataiter=iter(test_loader) # 也可以转换成迭代器在读取
imgs,labels=next(dataiter)
#imgs.size()
一般用dataset 处理同一个目录下的数据,如果数据在不同的目录下,就不是很方便,这就可以使用 torchvision ,可以自动获取标签,还提供了很多数据处理,增强的转换函数。
torchvision 简介
transforms
提供了对 PIL Image对象和Tensor对象的常用操作
- 对PIL Image:
- Scale/Resize,调整尺寸,长宽比保持不变
- CenterCrop, RandomCrop ,RandomSizedCrop, 剪裁图片,前两个是固定size, 后一个则是random size 。
- Pad 填充
- ToTensor ,将一个取值为 [0,255] 的PIL.Image转成Tensor ,形状为(H,W,C) 的nudarray 转换成 【C,H,W】 取值为【0,1】 的torch.FloatTensor
- RandomHorizontalFlip 图像随机水平翻转,翻转概率为 0.5。
- RandomVerticalFlip ,图像随机垂直翻转。
- ColorJitter,修改两队,对比度,和饱和度
- 对Tensor :
- Normalize,标准化,减去平均值,除以标准差。
- ToPILImage 将Tensor 转为PIL Image
对数据集进行对各操作,就通过Compose将这些操作拼接。
import torch
import torchvision
import torchvision.transforms as transforms
transforms.Compose([
#将给定的 PIL.Image 进行中心切割,得到给定的 size,
#size 可以是 tuple,(target_height, target_width)。
#size 也可以是一个 Integer,在这种情况下,切出来的图片形状是正方形。
transforms.CenterCrop(10),
#切割中心点的位置随机选取
transforms.RandomCrop(20, padding=0),
#把一个取值范围是 [0, 255] 的 PIL.Image 或者 shape 为 (H, W, C) 的 numpy.ndarray,
#转换为形状为 (C, H, W),取值范围是 [0, 1] 的 torch.FloatTensor
transforms.ToTensor(),
#规范化到[-1,1]
transforms.Normalize(mean = (0.5, 0.5, 0.5), std = (0.5, 0.5, 0.5)), # 三维就要有三个
transforms.Lambda(lambda x:x.add(10)) # 自定个 lambda 表达式,每个像素加10
])
ImageFolder
当文件依据标签处于不同文件下时,如:
─── data
├── zhangliu
│ ├── 001.jpg
│ └── 002.jpg
├── wuhua
│ ├── 001.jpg
│ └── 002.jpg
.................
可以利用 torchvision.datasets.ImageFolder 来直接构造 dataset
loader = datasets.ImageFolder(path) # 将目录中的文件名自动转换为序列,
loader = data.DataLoader(dataset) # 载入后,标签自动就是整数序列了
from torchvision import transforms, utils
from torchvision import datasets
import torch
import matplotlib.pyplot as plt
# %matplotlib inline
my_trans=transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor()
])
train_data = datasets.ImageFolder('./data/torchvision_data', transform=my_trans) # 读取数据然后进行处理
train_loader = data.DataLoader(train_data,batch_size=8,shuffle=True,)
for i_batch, img in enumerate(train_loader):
if i_batch == 0:
print(img[1]) # 标签
fig = plt.figure()
grid = utils.make_grid(img[0])
plt.imshow(grid.numpy().transpose((1, 2, 0)))
plt.show()
utils.save_image(grid,'test01.png')
break
可视化工具
Tensorboard 是 tensorflow 的可视化工具,可以记录训练数据,评估数据,网格结构,图像等。可以在 web 上展示,对于观察神经网络训练的过程非常有帮助,虽然也可以采用 tensorboard_logger , visdom 等工具,但这些方法比较复杂,所以推出了更厉害的 tensorboardX.
tensorboardX 简介
支持 scalar,image,figure,histogram,audio,text,graph ,onnx_graph,,,等可视化方式。
使用步骤:
from tensorboardX import SummaryWriter
write = SumaryWriter(log_dir='log') # 指定日志存放路径
# SummaryWriter(log_dir=, comment='', **kwargs) comment 文件名后的后缀,也可以不指定路径自动创建一个runs 目录
writer.add_xxx() # 调用实例
# add_xxx(tag=name,object,iteration=number) 标签,记录的对象,迭代次数
writer.close() # 关闭
# 启动tensorboard服务
到日志的目录,输入:
tensorboard --logdir=logs
然后再浏览器中打开返回的目录就行 http://localhost:port
用tensorboardX可视化神经网络
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from tensorboardX import SummaryWriter
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5) # 暂时看不懂,,,
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
self.bn = nn.BatchNorm2d(20)
def forward(self, x):
x = F.max_pool2d(self.conv1(x), 2)
x = F.relu(x) + F.relu(-x)
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = self.bn(x)
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
x = F.softmax(x, dim=1)
return x
#定义输入
input = torch.rand(32, 1, 28, 28)
#实例化神经网络
model = Net()
#将model保存为graph
with SummaryWriter(log_dir='logs',comment='Net') as w:
w.add_graph(model, (input, ))
。。我画出来就三个块。。。说是要降低一下版本。。。以后在搞吧。。看来这玩意都不能下最新版的啊。。。
用tensorboardX可视化损失值
一层全连接神经网络,训练一个一元二次函数的参数。
import torch
import torch.nn as nn
from tensorboardX import SummaryWriter
import numpy as np
input_size = 1
output_size = 1
num_epoches = 60
learning_rate = 0.01
dtype = torch.FloatTensor
writer = SummaryWriter(log_dir='logs',comment='Linear')
np.random.seed(100)
x_train = np.linspace(-1, 1, 100).reshape(100,1)
y_train = 3*np.power(x_train, 2) +2+ 0.2*np.random.rand(x_train.size).reshape(100,1) # 加噪声
model = nn.Linear(input_size, output_size)
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
for epoch in range(num_epoches):
inputs = torch.from_numpy(x_train).type(dtype)
targets = torch.from_numpy(y_train).type(dtype)
output = model(inputs)
loss = criterion(output, targets)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 保存loss的数据与epoch数值
writer.add_scalar('训练损失值', loss, epoch)
浏览器中,如果没有立刻出图的话可以等一下刷新试试。。。玄学
用 tensorboardX 可视化特征图
不同卷积层的特征图的抽取程度是不一样的
import torch
import torchvision
import torchvision.transforms as transforms
from tensorboardX import SummaryWriter
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 可以百度下一个cifar10数据集,更快。。
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
import torch.nn as nn
import torch.nn.functional as F
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
class CNNNet(nn.Module): # 这就来CNN 了吗
def __init__(self):
super(CNNNet,self).__init__()
self.conv1 = nn.Conv2d(in_channels=3,out_channels=16,kernel_size=5,stride=1)
self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)
self.conv2 = nn.Conv2d(in_channels=16,out_channels=36,kernel_size=3,stride=1)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.fc1 = nn.Linear(1296,128)
self.fc2 = nn.Linear(128,10)
def forward(self,x):
x=self.pool1(F.relu(self.conv1(x)))
x=self.pool2(F.relu(self.conv2(x)))
#print(x.shape)
x=x.view(-1,36*6*6)
x=F.relu(self.fc2(F.relu(self.fc1(x))))
return x
net = CNNNet()
net=net.to(device)
import torch.optim as optim
LR=0.001
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9)
#初始化数据
for m in net.modules():
if isinstance(m,nn.Conv2d): # 是否是卷积层
nn.init.normal_(m.weight)
nn.init.xavier_normal_(m.weight)
nn.init.kaiming_normal_(m.weight) #卷积层参数初始化
nn.init.constant_(m.bias, 0)
elif isinstance(m,nn.Linear):
nn.init.normal_(m.weight) #全连接层参数初始化,,以后会看明白的对吧。。。
#训练模型
for epoch in range(2):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# 获取训练数据
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
# 权重参数梯度清零
optimizer.zero_grad()
# 正向及反向传播
outputs = net(inputs) # 都是自动正向传播的
loss = criterion(outputs, labels)
loss.backward() # 损失反向
optimizer.step()
# 显示损失值
running_loss += loss.item()
if i % 2000 == 1999: # 2000个批量打印一次损失
print('[%d, %5d] loss: %.3f' %(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
import torchvision.utils as vutils
writer = SummaryWriter(log_dir='logs',comment='feature map')
for i, data in enumerate(trainloader, 0):
# 获取训练数据
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
x=inputs[0].unsqueeze(0) # torch.Size([1, 3, 32, 32])
break
img_grid = vutils.make_grid(x, normalize=True, scale_each=True, nrow=2)
net.eval()
for name, layer in net._modules.items():
# 为fc层预处理x
x = x.view(x.size(0), -1) if "fc" in name else x
print(x.size())
x = layer(x)
print(f'{name}')
# 查看卷积层的特征图
if 'layer' in name or 'conv' in name:
x1 = x.transpose(0, 1) # C,B, H, W ---> B,C, H, W
img_grid = vutils.make_grid(x1, normalize=True, scale_each=True, nrow=4) # normalize进行归一化处理
writer.add_image(f'{name}_feature_maps', img_grid, global_step=0)
出现的问题
我的是 1.7.0 的报错 Output 0 of UnbindBackward is a view and is being modified inplace。。。
解决方法就是 脑补链接 ,将报错的 utils.py 里的 74行改成:
绝了。。。