数据通常保存在硬盘中,要训练模型,需要把数据从硬盘读入内存,并转换为符合模型要求的数据,需要考虑的问题有:
- .jpg或.png等格式的图像文件需要被解压并提取出原始图像数据(Extract)
- 硬盘中的数据量太大,而内存有限,需要一批一批(mini_batch)的载入内存
- 图像原始数据的数值范围为[0,255],模型要求的数值范围为[0,1]或[-1,1],即数据放缩(scale)
- 为了防止模型过拟合,学到了训练数据的顺序,加速模型收敛,需要在每个epoch对训练数据做shuffle
- 为了防止模型过拟合,加入数据增强(Data augmentation)处理。本文仅讨论数据增强单独作用于训练数据。
-
高效的把数据从内存送入显存
数据从硬盘到模型
Extract, Transform, and Load操作
PyTorch中的ETL实现。PyTorch为了解决上述ETL问题,提供了两个类:
- torch.utils.data.Dataset:从硬盘中载入图像数据和标签数据(Extract),并把数据和标签打包为一个sample;在载入的过程中,实现对数据和标签的转换(Transform)。
- torch.utils.data.DataLoader:在dataset的基础上,把训练数据batch化,并实现shuffle,方便模型使用(Load)。
范例代码:
import torch
from torch.utils.data import Dataset, DataLoader
from torchvision import datasets
from torchvision import transforms
from torchvision.transforms import ToTensor
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" # Solve the OMP: Error #15
train_dataset = datasets.FashionMNIST(root='data',train=True, download=True, transform=ToTensor())
test_dataset = datasets.FashionMNIST(root='data',train=False, download=True)
train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True)
train_batch_data, train_batch_labels = next(iter(train_dataloader))
print(f"train_batch_data shape: {train_batch_data.size()}")
print(f"train_batch_labels shape: {train_batch_labels.size()}")
print(train_batch_data[0])
train_batch_data shape: torch.Size([64, 1, 28, 28])
train_batch_labels shape: torch.Size([64])
tensor([[[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000],
...
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0039, 0.0000, 0.0000, 0.6784, 0.7216,
0.6431, 0.7804, 0.7608, 0.9647, 0.5647, 1.0000, 0.7725, 1.0000,
0.9059, 0.8980, 0.6235, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0039, 0.0000, 0.0000, 0.7686, 0.7294,
0.6510, 0.6784, 0.6353, 0.7451, 0.4588, 0.8314, 0.3804, 0.7176,
0.2627, 0.6314, 0.5294, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0039, 0.0078, 0.0000, 0.0000, 0.8314, 0.7490,
0.8667, 0.9961, 1.0000, 0.9961, 0.9647, 0.9882, 0.9922, 0.9647,
0.9098, 0.9255, 0.3843, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0196, 0.0000, 0.0667, 0.9294, 0.6588,
0.1333, 0.4824, 0.8784, 0.9569, 0.9961, 0.8706, 0.8314, 0.7843,
0.6902, 0.7569, 0.3686, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0157, 0.0000, 0.2039, 0.8039, 0.6549,
0.4902, 0.7216, 0.7608, 0.3137, 0.6078, 0.7176, 0.6549, 0.7373,
0.6431, 0.8039, 0.3804, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0078, 0.0000, 0.0000, 0.3725, 0.7765, 0.7137,
0.9098, 0.8980, 0.9765, 0.6118, 0.6667, 0.7569, 0.5961, 0.7804,
0.7686, 0.8000, 0.3922, 0.0000],
...
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000]]])
可以看到数据已经批次化,并转换到[0,1]了。 ToTensor把PIL Image或Numpy.ndarray对象转换为PyTorch的Tensor对象,并把数值范围从[0,255]转为[0,1.0]
TensorFlow中的ETL实现。TensorFlow为了解决上述ETL问题提供了:
-
tf.data
API,实现ETL输入Pipeline - tf.data提供了一个
tf.data.Dataset
类,用于抽象ETL中的所有操作
范例程序:
import tensorflow as tf
(training_data, training_labels) , (test_data, test_labels)= tf.keras.datasets.fashion_mnist.load_data()
dataset = tf.data.Dataset.from_tensor_slices(
(tf.cast(training_data[...,tf.newaxis]/255, tf.float32),
tf.cast(training_labels,tf.int64))
)
dataset = dataset.shuffle(1000).batch(64)
images, labels = next(iter(dataset.take(1)))
print(images.shape,images[0].shape)
print(tf.squeeze(images[0]).numpy())
(64, 28, 28, 1) (28, 28, 1)
[[0. 0. 0. 0. 0. 0.
...
[0. 0. 0. 0. 0.1764706 0.27450982
0.7411765 0. 0. 0.30980393 0.26666668 0.24705882
0.2627451 0.3137255 0.36862746 0.29803923 0.32156864 0.32156864
0.28627452 0.3254902 0.10196079 0. 0.69803923 0.18431373
0.21568628 0. 0. 0. ]
[0. 0. 0. 0. 0.05490196 0.21960784
0.56078434 0. 0. 0. 0.02745098 0.06666667
0.13333334 0.15294118 0.16078432 0.15686275 0.14901961 0.13725491
0.10980392 0.03921569 0. 0. 0.61960787 0.21568628
0.18039216 0. 0. 0. ]]
总结
- TensorFlow 和 PyTorch都有对应的ETL实现,PyTorch是使用torch.utils.data.Dataset和torch.utils.data.DataLoader, TensorFlow使用tf.data API和tf.data.Dataset类。
- 个人更加喜欢PyTorch的实现,因为层次清晰易懂。不管实现如何,最关键的是要理解背后的原理,即不执着于表象。
- PyTorch中的数据默认格式是:NCHW;TensorFlow中的数据默认格式是:NHWC
网友评论