3.5 Набор данных для классификации изображений
Набор данных MNIST (LeCun et al., 1998) — один из самых широко используемых наборов для классификации изображений, но как эталон для сравнения методов он слишком прост. Поэтому мы будем использовать похожий, но более сложный набор — Fashion-MNIST (Xiao et al., 2017).
MXNet:
%matplotlib inline
from d2l import mxnet as d2l
from mxnet import gluon
import sys
d2l.use_svg_display()
PyTorch:
%matplotlib inline
from d2l import torch as d2l
import torch
import torchvision
from torchvision import transforms
from torch.utils import data
d2l.use_svg_display()
TensorFlow:
%matplotlib inline
from d2l import tensorflow as d2l
import tensorflow as tf
d2l.use_svg_display()
PaddlePaddle:
%matplotlib inline
from d2l import paddle as d2l
import warnings
warnings.filterwarnings("ignore")
import sys
import paddle
from paddle.vision import transforms
d2l.use_svg_display()
3.5.1 Чтение набора данных
Мы можем загрузить набор Fashion-MNIST в память с помощью встроенных функций фреймворка.
MXNet:
mnist_train = gluon.data.vision.FashionMNIST(train=True)
mnist_test = gluon.data.vision.FashionMNIST(train=False)
PyTorch:
# Преобразование ToTensor переводит изображения из формата PIL в 32-битные числа
# с плавающей точкой и делит их на 255, чтобы значения всех пикселей лежали в диапазоне от 0 до 1
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(
root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(
root="../data", train=False, transform=trans, download=True)
TensorFlow:
mnist_train, mnist_test = tf.keras.datasets.fashion_mnist.load_data()
PaddlePaddle:
trans = transforms.ToTensor()
mnist_train = paddle.vision.datasets.FashionMNIST(mode="train",
transform=trans)
mnist_test = paddle.vision.datasets.FashionMNIST(mode="test", transform=trans)
Набор Fashion-MNIST состоит из изображений десяти классов. Каждый класс представлен 6000 изображениями в обучающем наборе (train dataset) и 1000 изображениями в тестовом наборе (test dataset). Таким образом, обучающий и тестовый наборы содержат 60 000 и 10 000 изображений соответственно. Тестовый набор не используется для обучения, а только для оценки качества модели.
MXNet, PyTorch, PaddlePaddle:
len(mnist_train), len(mnist_test)
TensorFlow:
len(mnist_train[0]), len(mnist_test[0])
Высота и ширина каждого входного изображения составляют 28 пикселей. Набор состоит из изображений в оттенках серого, поэтому число каналов равно 1. Для краткости в этой книге форму изображения высотой пикселей и шириной пикселей будем записывать как или (, ).
mnist_train[0][0].shape
Набор Fashion-MNIST содержит 10 классов: футболка (t-shirt), брюки (trouser), пуловер (pullover), платье (dress), пальто (coat), сандалия (sandal), рубашка (shirt), кроссовок (sneaker), сумка (bag) и ботильон (ankle boot). Следующая функция преобразует числовые метки в текстовые названия классов.
def get_fashion_mnist_labels(labels): #@save
"""Возвращает текстовые метки набора Fashion-MNIST"""
text_labels = ['футболка', 'брюки', 'пуловер', 'платье', 'пальто',
'сандалия', 'рубашка', 'кроссовок', 'сумка', 'ботильон']
return [text_labels[int(i)] for i in labels]
Теперь создадим функцию, которая визуализирует эти примеры.
MXNet, TensorFlow:
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
"""Вывод списка изображений"""
figsize = (num_cols * scale, num_rows * scale)
_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
axes = axes.flatten()
for i, (ax, img) in enumerate(zip(axes, imgs)):
ax.imshow(d2l.numpy(img))
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
if titles:
ax.set_title(titles[i])
return axes
PyTorch:
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
"""Вывод списка изображений"""
figsize = (num_cols * scale, num_rows * scale)
_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
axes = axes.flatten()
for i, (ax, img) in enumerate(zip(axes, imgs)):
if torch.is_tensor(img):
# Тензор изображения
ax.imshow(img.numpy())
else:
# Изображение PIL
ax.imshow(img)
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
if titles:
ax.set_title(titles[i])
return axes
PaddlePaddle:
#@save
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
"""Вывод списка изображений"""
figsize = (num_cols * scale, num_rows * scale)
_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
axes = axes.flatten()
for i, (ax, img) in enumerate(zip(axes, imgs)):
if paddle.is_tensor(img):
# Тензор изображения
ax.imshow(img.numpy())
else:
# Изображение PIL
ax.imshow(img)
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
if titles:
ax.set_title(titles[i])
return axes
Ниже показаны изображения и соответствующие метки первых нескольких примеров обучающего набора.
MXNet:
X, y = mnist_train[:18]
print(X.shape)
show_images(X.squeeze(axis=-1), 2, 9, titles=get_fashion_mnist_labels(y));
PyTorch:
X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y));
TensorFlow:
X = tf.constant(mnist_train[0][:18])
y = tf.constant(mnist_train[1][:18])
show_images(X, 2, 9, titles=get_fashion_mnist_labels(y));
PaddlePaddle:
X, y = next(iter(paddle.io.DataLoader(mnist_train, batch_size=18)))
show_images(X.reshape([18, 28, 28]), 2, 9, titles=get_fashion_mnist_labels(y));
3.5.2 Чтение минибатчей
Чтобы упростить чтение обучающих и тестовых наборов, мы используем встроенные итераторы данных, а не создаём их с нуля.
Напомним, что на каждой итерации загрузчик данных читает минибатч размера batch_size.
Встроенный итератор перемешивает все примеры, поэтому минибатчи читаются без смещения.
MXNet:
batch_size = 256
def get_dataloader_workers(): #@save
"""На платформах, отличных от Windows, для чтения данных используются 4 процесса"""
return 0 if sys.platform.startswith('win') else 4
# Преобразование ToTensor переводит изображения из формата uint8 в 32-битные числа
# с плавающей точкой и делит их на 255, чтобы значения всех пикселей лежали в диапазоне от 0 до 1
transformer = gluon.data.vision.transforms.ToTensor()
train_iter = gluon.data.DataLoader(mnist_train.transform_first(transformer),
batch_size, shuffle=True,
num_workers=get_dataloader_workers())
PyTorch:
batch_size = 256
def get_dataloader_workers(): #@save
"""Для чтения данных используются 4 процесса"""
return 4
train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
num_workers=get_dataloader_workers())
TensorFlow:
batch_size = 256
train_iter = tf.data.Dataset.from_tensor_slices(
mnist_train).batch(batch_size).shuffle(len(mnist_train[0]))
PaddlePaddle:
batch_size = 256
def get_dataloader_workers(): #@save
"""Для чтения данных используются 4 процесса"""
return 4
train_iter = paddle.io.DataLoader(dataset=mnist_train,
batch_size=batch_size,
shuffle=True,
return_list=True,
num_workers=get_dataloader_workers())
Посмотрим, сколько времени занимает чтение обучающих данных.
timer = d2l.Timer()
for X, y in train_iter:
continue
f'{timer.stop():.2f} сек'
3.5.3 Объединение всех компонентов
Теперь определим функцию load_data_fashion_mnist, которая загружает набор Fashion-MNIST и читает его.
Функция возвращает итераторы данных для обучающих и проверочных наборов.
Кроме того, она принимает необязательный параметр resize, с помощью которого можно изменить размер изображений.
MXNet:
def load_data_fashion_mnist(batch_size, resize=None): #@save
"""Загрузка набора Fashion-MNIST и чтение его в память"""
dataset = gluon.data.vision
trans = [dataset.transforms.ToTensor()]
if resize:
trans.insert(0, dataset.transforms.Resize(resize))
trans = dataset.transforms.Compose(trans)
mnist_train = dataset.FashionMNIST(train=True).transform_first(trans)
mnist_test = dataset.FashionMNIST(train=False).transform_first(trans)
return (gluon.data.DataLoader(mnist_train, batch_size, shuffle=True,
num_workers=get_dataloader_workers()),
gluon.data.DataLoader(mnist_test, batch_size, shuffle=False,
num_workers=get_dataloader_workers()))
PyTorch:
def load_data_fashion_mnist(batch_size, resize=None): #@save
"""Загрузка набора Fashion-MNIST и чтение его в память"""
trans = [transforms.ToTensor()]
if resize:
trans.insert(0, transforms.Resize(resize))
trans = transforms.Compose(trans)
mnist_train = torchvision.datasets.FashionMNIST(
root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(
root="../data", train=False, transform=trans, download=True)
return (data.DataLoader(mnist_train, batch_size, shuffle=True,
num_workers=get_dataloader_workers()),
data.DataLoader(mnist_test, batch_size, shuffle=False,
num_workers=get_dataloader_workers()))
TensorFlow:
def load_data_fashion_mnist(batch_size, resize=None): #@save
"""Загрузка набора Fashion-MNIST и чтение его в память"""
mnist_train, mnist_test = tf.keras.datasets.fashion_mnist.load_data()
# Все значения делятся на 255, чтобы значения пикселей лежали в диапазоне от 0 до 1,
# в конец добавляется измерение батча, а метки приводятся к типу int32.
process = lambda X, y: (tf.expand_dims(X, axis=3) / 255,
tf.cast(y, dtype='int32'))
resize_fn = lambda X, y: (
tf.image.resize_with_pad(X, resize, resize) if resize else X, y)
return (
tf.data.Dataset.from_tensor_slices(process(*mnist_train)).batch(
batch_size).shuffle(len(mnist_train[0])).map(resize_fn),
tf.data.Dataset.from_tensor_slices(process(*mnist_test)).batch(
batch_size).map(resize_fn))
PaddlePaddle:
#@save
def load_data_fashion_mnist(batch_size, resize=None):
"""Загрузка набора Fashion-MNIST и чтение его в память"""
trans = [transforms.ToTensor()]
if resize:
trans.insert(0, transforms.Resize(resize))
trans = transforms.Compose(trans)
mnist_train = paddle.vision.datasets.FashionMNIST(mode="train",
transform=trans)
mnist_test = paddle.vision.datasets.FashionMNIST(mode="test",
transform=trans)
return (paddle.io.DataLoader(dataset=mnist_train,
batch_size=batch_size,
shuffle=True,
return_list=True,
num_workers=get_dataloader_workers()),
paddle.io.DataLoader(dataset=mnist_test,
batch_size=batch_size,
return_list=True,
shuffle=True,
num_workers=get_dataloader_workers()))
Проверим работу функции load_data_fashion_mnist с изменением размера изображений, передав параметр resize.
train_iter, test_iter = load_data_fashion_mnist(32, resize=64)
for X, y in train_iter:
print(X.shape, X.dtype, y.shape, y.dtype)
break
Теперь всё готово к работе с набором Fashion-MNIST: в следующих разделах мы будем использовать его для оценки различных алгоритмов классификации.
Резюме
- Fashion-MNIST — набор данных с изображениями одежды десяти классов. Мы будем использовать его в следующих главах для оценки различных алгоритмов классификации.
- Форму изображения высотой и шириной пикселей будем записывать как или (, ).
- Итераторы данных — ключевой компонент для высокой производительности. Хорошо реализованные итераторы, использующие возможности высокопроизводительных вычислений, не дают замедлять обучение.
Упражнения
- Повлияет ли уменьшение
batch_size(например, до 1) на производительность чтения данных? - Производительность итераторов данных очень важна. Достаточно ли быстра текущая реализация? Исследуйте различные способы её улучшить.
- Посмотрите онлайн-документацию API фреймворка. Какие ещё наборы данных доступны?
MXNet: Обсуждение
PyTorch: Обсуждение
TensorFlow: Обсуждение
PaddlePaddle: Обсуждение