常用工具

常用工具

1 数据加载和预处理

import torch
torch.__version__
'1.3.1'

1.1 Dataset

Dataset是一个抽象类,为方便读取,需将要使用的数据包装为Dataset类。自定义的Dataset需要继承它并实现两个成员方法:

  1. getitem() 用索引(0到len(self))获取一条数据或一个样本。
    obj[index]等价于obj.getitem(index)
  2. len() 返回数据集总长度。len(obj)等价于obj.len()
# 以Kaggle的Dogs vs. Cat数据为例
# 引用
import torch
from torch.utils.data import Dataset
import os
from PIL import Image
import numpy as np

# 定义一个数据集
class DogCat(Dataset):
    """ 数据集演示 """
    def __init__(self, root):
        imgs = os.listdir(root)
        # 图片的绝对路径
        # 这里不实际加载图片,只是指定路径,当调用__getitem__时才读图
        self.imgs = [os.path.join(root, img) for img in imgs]
        
    def __len__(self):
        """返回imgs的长度"""
        return len(self.imgs)
    
    def __getitem__(self, idx):
        """根据idx返回一行数据和标签"""
        img_path = self.imgs[idx]
        # 标签 dog -> 1, cat -> 0
        label = 1 if 'dog' in img_path.split('/')[-1] else 0
        # 读入数据
        pil_img = Image.open(img_path)
        array = np.asarray(pil_img)
        data = torch.from_numpy(array)
        return data, label
dataset = DogCat('F:\Kaggle_data\competitions\dogs-vs-cats\data')
img, label = dataset[0] # 相当于调用dataset.__getitem__(0)
for img, label in dataset:
    print(img.size(), img.float().mean(), label)
torch.Size([374, 500, 3]) tensor(119.7826) 1
torch.Size([280, 300, 3]) tensor(71.6653) 1
torch.Size([499, 489, 3]) tensor(108.7773) 1
torch.Size([410, 431, 3]) tensor(110.0135) 1
torch.Size([396, 312, 3]) tensor(131.8400) 1
torch.Size([414, 500, 3]) tensor(156.6921) 1
torch.Size([375, 499, 3]) tensor(96.8243) 1
torch.Size([144, 175, 3]) tensor(166.6151) 1
torch.Size([303, 400, 3]) tensor(129.1319) 1
torch.Size([499, 495, 3]) tensor(90.0335) 1
torch.Size([345, 461, 3]) tensor(109.3431) 1
torch.Size([425, 320, 3]) tensor(158.8270) 1

可以看到,上面的数据集并不适合实际使用,因为样本的大小不一致,并且样本数值较大,未归一化至[-1,1]。
针对这种问题,pytorch提供了torchvision工具包,其中的transform模块提供了对PIL image对象和Tensor对象的常用操作。

对PIL Image的操作有:
· Scale:调整图片尺寸,长宽比不变
· CenterCrop、RandomCrop、RandomResizedCrop:裁剪图片
· Pad:填充
· ToTensor:将PIL Image转成Tensor,自动将[0,255]归一化为[0,1]

对Tensor的操作有:
· Normalize:标准化
· ToPILImage:Tensor转成PIL Image

import os 
from PIL import Image
import numpy as np
from torchvision import transforms as T

# 使用Compose函数将多个操作拼接起来
transform = T.Compose([
    T.Resize(224), # 缩放图片,保持长宽比不变,最短边为224像素
    T.CenterCrop(224), # 从图片中间切出224*224的图片
    T.ToTensor(), # 将图片转成Tensor,归一化至[0,1]
    T.Normalize(mean=[.5, .5, .5], std=[.5, .5, .5]) # 标准化至[-1,1],规定均值和标准差
])

class DogCat(Dataset):
    def __init__(self, root, transforms=None):
        imgs = os.listdir(root)
        self.imgs = [os.path.join(root, img) for img in imgs]
        self.transforms=transforms
        
    def __getitem__(self, index):
        img_path = self.imgs[index]
        label = 0 if 'dog' in img_path.split('/')[-1] else 1
        data = Image.open(img_path)
        if self.transforms:
            data = self.transforms(data)
        return data, label
    
    def __len__(self):
        return len(self.imgs)

dataset = DogCat('F:\Kaggle_data\competitions\dogs-vs-cats\data', transforms=transform)
img, label = dataset[0]
for img, label in dataset:
    print(img.size(), label)
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
torch.Size([3, 224, 224]) 0
1.1.1 torchvision中一个常用的Dataset——ImageFolder

ImageFolder假设所有文件按文件夹保存,每个文件夹存储同一类图片,文件夹名即为类名。

# 构造函数
# root 在root路径下寻找图片
# transform 对PIL Image的转换,
#           transform的输入是使用loader读取图片的返回对象
# target_transform 对label的转换
# loader 给定路径后如何读取图片,默认读取RGB格式的PIL Image对象
ImageFolder(root, transform=None, target_transform=None,
            loader=default_loader)
!tree /F F:\Kaggle_data\competitions\dogs-vs-cats\data
文件夹 PATH 列表
卷序列号为 1B42-6D96
F:\KAGGLE_DATA\COMPETITIONS\DOGS-VS-CATS\DATA
├─cat
│      cat.0.jpg
│      cat.1.jpg
│      cat.10.jpg
│      cat.11.jpg
│      cat.2.jpg
│      cat.3.jpg
│      cat.4.jpg
│      cat.5.jpg
│      cat.6.jpg
│      cat.7.jpg
│      cat.8.jpg
│      cat.9.jpg
│      
└─dog
        dog.0.jpg
        dog.1.jpg
        dog.2.jpg
        dog.3.jpg
        dog.4.jpg
        dog.5.jpg
        dog.6.jpg
        dog.7.jpg
from torchvision.datasets import ImageFolder
dataset = ImageFolder('F:\Kaggle_data\competitions\dogs-vs-cats\data')
# cat文件夹图片对应label 0,dog对应1
dataset.class_to_idx
{'cat': 0, 'dog': 1}
# 所有图片的路径和对应的label
dataset.imgs
[('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.0.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.1.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.10.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.11.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.2.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.3.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.4.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.5.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.6.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.7.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.8.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\cat\\cat.9.jpg', 0),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\dog\\dog.0.jpg', 1),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\dog\\dog.1.jpg', 1),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\dog\\dog.2.jpg', 1),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\dog\\dog.3.jpg', 1),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\dog\\dog.4.jpg', 1),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\dog\\dog.5.jpg', 1),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\dog\\dog.6.jpg', 1),
 ('F:\\Kaggle_data\\competitions\\dogs-vs-cats\\data\\dog\\dog.7.jpg', 1)]
# 没有任何的transform,因此返回的还是PIL Image对象
print(dataset[0][1]) # 第一维是第几张图,第二维为1返回label
print(dataset[0][0]) # 为0返回图片数据
0
<PIL.Image.Image image mode=RGB size=500x374 at 0xA0D7508>
# 加上transform
normalize = T.Normalize(mean=[0.4,0.4,0.4], std=[0.2,0.2,0.2])
transform = T.Compose([
    T.RandomResizedCrop(224),
    T.RandomHorizontalFlip(),
    T.ToTensor(),
    normalize,
])
dataset = ImageFolder('F:\Kaggle_data\competitions\dogs-vs-cats\data',
                     transform=transform)
# 深度学习中图片数据一般保存为 C*H*W(通道数*高*宽)
dataset[0][0].size()
torch.Size([3, 224, 224])
to_img = T.ToPILImage()
to_img((dataset[0][0]*0.2+0.4))

在这里插入图片描述

1.2 DataLoader

Dataset只负责数据的抽象,一次调用__getitem__只返回一个样本。前面提到过,在训练神经网络时,最好是对一个batch的数据进行操作,同时还需要对数据进行shuffle和并行加速等。对此,PyTorch提供了DataLoader帮助我们实现这些功能。

# 构造函数
# dataset 加载的数据集(Dataset对象)
# batch_size
# shuffle 是否打乱数据
# num_workers 使用多进程加载的进程数,0表不用多进程
# collate_fn 如何将多个样本拼接成一个batch,一般默认即可
# pin_memory 是否将数据保存至pin memory区,数据转到GPU会快一点
# drop_last dataset中的数据个数可能不是batch_size的整数倍,
#           drop_last为True会把多余数据丢弃
DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
          num_workers=0, collate_fn=default_collate, pin_memory=False,
          drop_last=False)
from torch.utils.data import DataLoader
dataloader = DataLoader(dataset, batch_size=3, shuffle=True, num_workers=0,
                       drop_last=False)
# dataloader是一个可迭代的对象,因此可以像迭代器一样使用它
dataiter = iter(dataloader)
imgs, labels = next(dataiter)
imgs.size() # batch_size, channel, height, weight
torch.Size([3, 3, 224, 224])

在数据处理中,有时会出现某个样本无法读取等问题,比如某张图片损坏。这时在__getitem__函数中将出现异常,此时最好的解决方案即是将出错的样本剔除。此外,还可以通过随机取一张图片代替原图的方式。

class NewDogCat(DogCat):
    def __getitem__(self, index):
        try:
            # 调用父类的获取函数 DogCat.__getitem__(self,index)
            return super(NewDogCat, self).__getitem__(index)
        except:
            new_index = random.randint(0, len(self)-1)
            return self[new_index]

2 可视化工具

深度学习中常用的可视化工具:Tensorboard和Visdom

2.1 Tensorboard

终端输入tensorboard --logdir <your_running_dir> --port <your_bind_port>,默认端口为6006,浏览器输入http://localhost:6006/ 即可看到web页面

from torch.utils.tensorboard import SummaryWriter
cat_img = Image.open('F:\Kaggle_data\competitions\dogs-vs-cats\data\cat\cat.0.jpg')
cat_img.size
(500, 374)
# 使用transform将图片转为224大小
cat_img_224 = transform(cat_img)

#将图片展示在tensorboard中
writer = SummaryWriter(log_dir='./logs', comment='cat_image')
writer.add_image('cat', cat_img_224)
writer.close() # 执行close立即刷新

在tensorboard的images界面即可看到图片。

# 更新损失函数,使用tensorboard的SCALAR界面
x = torch.FloatTensor([100])
y = torch.FloatTensor([500])

for epoch in range(30):
    x = x * 1.2
    y = y / 1.1
    loss = np.random.random()
    with SummaryWriter(log_dir='./logs', comment='train') as writer:
        # 使用with语法,自动调用close方法
        writer.add_histogram('his/x', x, epoch)
        writer.add_histogram('his/y', y, epoch)
        writer.add_scalar('data/x', x ,epoch)
        writer.add_scalar('data/y', y, epoch)
        writer.add_scalar('data/loss', loss, epoch)
        writer.add_scalars('data/data_group', {'x':x, 'y':y}, epoch)

在tensorboard的SCALAR页面即可看到曲线。

2.2 Visdom

在终端输入visdom或python -m visdom.server启动服务器,浏览器输入http://localhost:8097看到页面

Environments

Environments:对可视化区域进行分区,每个用户都有一个默认分区main,图表都放置在这里面。

Panes

Panes是作为每个图表的容器,可以对它进行拖放、删除、缩放等操作。一个Environment可以包含多个Panes。

使用
import math
import numpy as np
from visdom import Visdom
import time

env = Visdom()
assert env.check_connection() # 测试连接,链接错误会报错
Setting up a new session...
# 生成sin和cos曲线
y = np.linspace(0, 2 * math.pi, 70)
x = np.column_stack((np.sin(y), np.cos(y)))

# 使用茎叶图
env.stem(X=x, Y=y, opts=dict(legend=['sine','cosine']))
'window_37f702b5e31114'
# 更新损失函数
x,y = 0,0
env2 = Visdom()
pane1 = env2.line(X=np.array([x]),Y=np.array([y]),
                  opts=dict(title='loss'))
Setting up a new session...
for i in range(10):
    time.sleep(1) # 每隔一秒打印一次数据
    x += i
    y = (y+i)*1.5
    print(x, y)
    env2.line(X=np.array([x]),Y=np.array([y]),
             win=pane1, # win参数确认使用哪个pane
             update='append') # 追加
0 0.0
1 1.5
3 5.25
6 12.375
10 24.5625
15 44.34375
21 75.515625
28 123.7734375
36 197.66015625
45 309.990234375
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值