Pytorch常用工具总结
数据处理
数据加载
- 自定义数据集
- 继承
Dataset
,实现python方法:__getitem__
: 返回一条数据/一个样本__len__
: 返回样本数量
- 继承
以猫狗识别为例:
文件结构
所有文件放在一个文件夹,根据前缀判断
1
2
3
4
5
6
7
8
9data/dogcat/
|-- cat.12484.jpg
|-- cat.12485.jpg
|-- cat.12486.jpg
|-- cat.12487.jpg
|-- dog.12496.jpg
|-- dog.12497.jpg
|-- dog.12498.jpg
`-- dog.12499.jpg在构造函数中获取图片路径
1
2
3
4
5def __init__(self, root):
# 图片文件夹路径
imgs = os.listdir(root)
# 遍历文件夹下所有文件 + 文件姐路径=> 构成绝对路径
self.imgs = [os.path.join(root, img) for img in imgs]定义
__getitem__
,这一步中真正读取图片,并根据文件名生成label1
2
3
4
5
6
7
8
9
10
11def __getitem__(self, index):
img_path = self.imgs[index]
# 根据文件名生成Label
label = 1 if 'dog' in img_path.split('/')[-1] else 0
# 加载图片
pil_img = Image.open(img_path)
# 图片转为numpy
array = np.asarray(pil_img)
# numpy转为tensor
data = t.from_numpy(array)
return data, bale还需要定义
__len__
,返回imgs数组的长度调用数据加载类
```python
dataset = DogCat(FILEPATH)访问某个元素
dataset[INDEX]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
### transforms
- 常见操作
- | 操作 | 说明 |
| ------------------------------------------------ | ------------------------------------------------ |
| `Scale` | 调整图片尺寸,长宽比 |
| `CenterCrop`, ``RandomCrop`, `RandomResizedCrop` | 裁剪 |
| `pad` | 填充 |
| `ToTensor` | 将PIL Image对象转成Tensor,将[0, 255]归一化[0,1] |
- 对Tensor的操作
- Normalize: 标准化
- `ToPILImage`: Tensor转PIL Image
- `Compose`: 拼接
- 实现
- ```python
transform = T.Compose([
T.Resize(224), # 缩放,保持长宽比,短边224
T.CenterCrop(224), # 中间切224*224
T.ToTensor(), # 归一化到[0, 1]
T.Normalize(mean = [.5, .5, .5], std = [.5, .5, .5]) # 表转化到[-1, 1]
])在数据集的类中定义transform,并在
__getitem__
方法中返回transforms处理过的data(图像数据).
自定义转换
- e.g.
trans=T.Lambda(lambda img: img.rotate(random()*360))
- e.g.
ImageFolder
假设文件按文件夹保存,每个文件夹存储同类别,文件夹名为类名。
1
ImageFolder(root, transform=None, target_transform=None, loader=default_loader)
root
: 路径transform
: 对图片的转换操作target_transform
: 对label转换loader
: 读取格式
属性
class_to_idx
- 类名和类下标的对应关系
图片保存:Channel x Height x Width
DataLoader
```python
DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, num_workers=0, collate_fn=default_collate, pin_memory=False, drop_last=False)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- `dataset` - Dataset对象
- `shuffle` - 打乱
- `sampler`
- `num_workers` - 多进程加载的进程数
- `collate_fn` - 多个样本数据拼成一个batch的方法,一般使用默认
- `pin_memory` - 是否将数据保存在`pin_memory`区,转到GPU快
- `drop_last` -师傅 将最后不足`batch_size`个数据丢弃
#### 可迭代
- 可以使用for循环
- ```python
for batch_datas, batch_labels in dataloader:可以使用迭代器
- ```python
dataiter = iter(dataloader)
batch_datas, batch_labels = next(dataiter)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
### 剔除出错样本
#### 返回None,并在拼合为batch的时候筛选掉
- 使用`try`, `except`
- 异常情况:`return None, None`
> 这里需要配合实现`Dataloader`的`collate_fn`,将None过滤
```python
from torch.utils.data.dataloader import default_collate # 导入默认的拼接方式
def my_collate_fn(batch):
# 过滤为None的数据
batch = list(filter(lambda x:x[0] is not None, batch))
if len(batch) == 0: return t.Tensor()
return default_collate(batch) # 用默认方式拼接过滤后的batch数据
- ```python
随机选取图片代替
- 优点:保证每个batch的数目仍是batch_size
注意事项
- 高负载的操作放在
__getitem__
,如加载图片 => 为实现并行加速 dataset
包含只读,避免修改 => 使用多进程加载,修改可能产生冲突
sampler
- 对数据采样,loader里面shuffle为True,调用随机采样器
RandomSampler
,打乱数据;默认使用SequentialSampler
,顺序采样
WeightedRandomSampler
- 根据每个样本的权重选取数据,比例不均衡问题中进行重采样
- 构建时提供每个样本的权重
weights
和选取样本总数num_samples
,可选replacement
- 权重越大被选中概率越大
replacement
决定是否可以重复选取某一样本
在指定了sampler的情况下,shuffle不再生效;一个epoch返回的图片总数取决于sampler.num_samples
计算机视觉工具包 torchvision
- models:提供网络结构以及预训练好的模型
- datasets:提供常用的数据集加载
- transforms:提供常用的数据预处理
Visdom
env
: 不同环境的可视化结果相互隔离,默认main
pane
: 窗格
安装和使用
```shell
$ pip install visdom
$ python -m visdom.server # 启动visdom服务
$ nohup python -m visdom.server & # 将服务放至后台运行1
2
3
4
5
6
7
8
9
10
11
> **注意:**
>
> 手动指定保存`env`,在web上或者程序里save,否则visdom服务重启后,信息会丢失。
#### 常用操作
- 新建连接客户端
- ```python
vis = visdom.Visdom(env=u'test1',use_incoming_socket=False)- 可以指定host, port..
画图函数: line, image, text, histogram, scatter, bar, pie…
支持tensor和narray的数据结构
常见参数
win
: 指定Pane的名字,两次操作指定的win相同,则覆盖需要更新数值且不覆盖,指定参数
update='append'
也可以使用
vis.updateTrace
更新图:增加新trace
1
2
3x = t.arange(0, 9, 0.1)
y = (x ** 2) / 9
vis.line(X=x, Y=y, win='polynomial', name='this is a new Trace',update='new')在原trace上追加
1
2
3
4
5for ii in range(0, 10):
# y = x
x = t.Tensor([ii])
y = x
vis.line(X=x, Y=y, win='polynomial', update='append' if ii>0 else None)
opts
: 选项,接收字典,用于pane的显示格式
text
- 支持html
使用GPU加速 cuda
注意事项
损失函数定义后也应该调用
criterion.cuda
转移到GPU推荐方法设置环境变量
CUDA_VISIBLE_DEVICES
,在运行py的命令行实现
在程序中:
- ```python
import os
os.environ[“CUDA_VISIBLE_DEVICES”] = “2”1
2
3
4
5
- 在Jupyter notebook中:
- ```shell
%env CUDA_VISIBLE_DEVICES=1,2
- ```python
持久化
- 可持久化的对象:
- Tensor
- Variable
- nn.Module
- Optimizer
- 都保存成Tensor,使用
t.save(OBJ, FILENAME)
和t.load(FILENAME)
可完成load
的时候可以指定加载的存储位置,设置map_location
参数
- 建议module和optimizer保存为
state_dict
module
- ```python
保存
t.save(model.state_dict(), FILENAME)加载
model.load_state_dict(t.load(FILENAME))1
2
3
4
5
6
7
8
9
10
11
12
13
14
- optimizer同样
#### 一起保存加载
- ```python
all_data = dict(
optimizer = optimizer.state_dict(),
model = model.state_dict(),
info = u'模型和优化器的所有参数'
)
t.save(all_data, 'all.pth')
all_data = t.load('all.pth')