.. _sec_fashion_mnist:
O *Dataset* de Classificação de Imagens
=======================================
Um dos *datasets* amplamente usados para classificação de imagens é o
conjunto de dados MNIST :cite:`LeCun.Bottou.Bengio.ea.1998`. Embora
tenha tido uma boa execução como um conjunto de dados de referência,
mesmo os modelos simples pelos padrões atuais alcançam uma precisão de
classificação acima de 95%, tornando-o inadequado para distinguir entre
modelos mais fortes e mais fracos. Hoje, o MNIST serve mais como
verificação de sanidade do que como referência. Para aumentar um pouco a
aposta, concentraremos nossa discussão nas próximas seções no *dataset*
Fashion-MNIST, qualitativamente semelhante, mas comparativamente
complexo :cite:`Xiao.Rasul.Vollgraf.2017`, que foi lançado em 2017.
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import sys
from mxnet import gluon
from d2l import mxnet as d2l
d2l.use_svg_display()
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l
d2l.use_svg_display()
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import tensorflow as tf
from d2l import tensorflow as d2l
d2l.use_svg_display()
.. raw:: html
.. raw:: html
Lendo o Dataset
---------------
Nós podemos baixar e ler o *dataset* Fashion-MNIST na memória por meio
das funções integradas na estrutura.
.. raw:: html
.. raw:: html
.. code:: python
mnist_train = gluon.data.vision.FashionMNIST(train=True)
mnist_test = gluon.data.vision.FashionMNIST(train=False)
.. raw:: html
.. raw:: html
.. code:: python
# `ToTensor` converts the image data from PIL type to 32-bit floating point
# tensors. It divides all numbers by 255 so that all pixel values are between
# 0 and 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)
.. raw:: html
.. raw:: html
.. code:: python
mnist_train, mnist_test = tf.keras.datasets.fashion_mnist.load_data()
.. raw:: html
.. raw:: html
O Fashion-MNIST consiste em imagens de 10 categorias, cada uma
representada por 6.000 imagens no conjunto de dados de treinamento e por
1.000 no conjunto de dados de teste. Um *dataset de teste* (ou *conjunto
de teste*) é usado para avaliar o desempenho do modelo e não para
treinamento. Consequentemente, o conjunto de treinamento e o conjunto de
teste contém 60.000 e 10.000 imagens, respectivamente.
.. raw:: html
.. raw:: html
.. code:: python
len(mnist_train), len(mnist_test)
.. parsed-literal::
:class: output
(60000, 10000)
.. raw:: html
.. raw:: html
.. code:: python
len(mnist_train), len(mnist_test)
.. parsed-literal::
:class: output
(60000, 10000)
.. raw:: html
.. raw:: html
.. code:: python
len(mnist_train[0]), len(mnist_test[0])
.. parsed-literal::
:class: output
(60000, 10000)
.. raw:: html
.. raw:: html
A altura e a largura de cada imagem de entrada são 28 pixels. Observe
que o *dataset* consiste em imagens em tons de cinza, cujo número de
canais é 1. Para resumir, ao longo deste livro armazenamos a forma de
qualquer imagem com altura :math:`h` largura :math:`w` pixels como
:math:`h \times w` or (:math:`h`, :math:`w`).
.. raw:: html
.. raw:: html
.. code:: python
mnist_train[0][0].shape
.. parsed-literal::
:class: output
(28, 28, 1)
.. raw:: html
.. raw:: html
.. code:: python
mnist_train[0][0].shape
.. parsed-literal::
:class: output
torch.Size([1, 28, 28])
.. raw:: html
.. raw:: html
.. code:: python
mnist_train[0][0].shape
.. parsed-literal::
:class: output
(28, 28)
.. raw:: html
.. raw:: html
As imagens no Fashion-MNIST estão associadas às seguintes categorias:
t-shirt, calças, pulôver, vestido, casaco, sandália, camisa, tênis,
bolsa e bota. A função a seguir converte entre índices de rótulos
numéricos e seus nomes em texto.
.. code:: python
def get_fashion_mnist_labels(labels): #@save
"""Return text labels for the Fashion-MNIST dataset."""
text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
return [text_labels[int(i)] for i in labels]
Agora podemos criar uma função para visualizar esses exemplos.
.. raw:: html
.. raw:: html
.. code:: python
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
"""Plot a list of images."""
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(img.asnumpy())
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
if titles:
ax.set_title(titles[i])
return axes
.. raw:: html
.. raw:: html
.. code:: python
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
"""Plot a list of images."""
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):
# Tensor Image
ax.imshow(img.numpy())
else:
# PIL Image
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
.. raw:: html
.. raw:: html
.. code:: python
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
"""Plot a list of images."""
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(img.numpy())
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
if titles:
ax.set_title(titles[i])
return axes
.. raw:: html
.. raw:: html
Aqui estão as imagens e seus *labels correspondentes (no texto) para os
primeiros exemplos no*\ dataset\* de treinamento.
.. raw:: html
.. raw:: html
.. code:: python
X, y = mnist_train[:18]
print(X.shape)
show_images(X.squeeze(axis=-1), 2, 9, titles=get_fashion_mnist_labels(y));
.. parsed-literal::
:class: output
(18, 28, 28, 1)
.. figure:: output_image-classification-dataset_e45669_65_1.svg
.. raw:: html
.. raw:: html
.. code:: python
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));
.. figure:: output_image-classification-dataset_e45669_68_0.svg
.. raw:: html
.. raw:: html
.. code:: python
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));
.. figure:: output_image-classification-dataset_e45669_71_0.svg
.. raw:: html
.. raw:: html
Lendo um *Minibatch*
--------------------
Para tornar nossa vida mais fácil ao ler os conjuntos de treinamento e
teste, usamos o iterador de dados integrado em vez de criar um do zero.
Lembre-se de que a cada iteração, um carregador de dados lê um minibatch
de dados com tamanho ``batch_size`` cada vez. Também misturamos
aleatoriamente os exemplos para o iterador de dados de treinamento.
.. raw:: html
.. raw:: html
.. code:: python
batch_size = 256
def get_dataloader_workers(): #@save
"""Use 4 processes to read the data except for Windows."""
return 0 if sys.platform.startswith('win') else 4
# `ToTensor` converts the image data from uint8 to 32-bit floating point. It
# divides all numbers by 255 so that all pixel values are between 0 and 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())
.. raw:: html
.. raw:: html
.. code:: python
batch_size = 256
def get_dataloader_workers(): #@save
"""Use 4 processes to read the data."""
return 4
train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
num_workers=get_dataloader_workers())
.. raw:: html
.. raw:: html
.. code:: python
batch_size = 256
train_iter = tf.data.Dataset.from_tensor_slices(
mnist_train).batch(batch_size).shuffle(len(mnist_train[0]))
.. raw:: html
.. raw:: html
Vejamos o tempo que leva para ler os dados de treinamento.
.. raw:: html
.. raw:: html
.. code:: python
timer = d2l.Timer()
for X, y in train_iter:
continue
f'{timer.stop():.2f} sec'
.. parsed-literal::
:class: output
'2.02 sec'
.. raw:: html
.. raw:: html
.. code:: python
timer = d2l.Timer()
for X, y in train_iter:
continue
f'{timer.stop():.2f} sec'
.. parsed-literal::
:class: output
'1.96 sec'
.. raw:: html
.. raw:: html
.. code:: python
timer = d2l.Timer()
for X, y in train_iter:
continue
f'{timer.stop():.2f} sec'
.. parsed-literal::
:class: output
'0.17 sec'
.. raw:: html
.. raw:: html
Juntando Tudo
-------------
Agora definimos a função ``load_data_fashion_mnist`` que obtém e lê o
*dataset* Fashion-MNIST. Ele retorna os iteradores de dados para o
conjunto de treinamento e o conjunto de validação. Além disso, ele
aceita um argumento opcional para redimensionar imagens para outra
forma.
.. raw:: html
.. raw:: html
.. code:: python
def load_data_fashion_mnist(batch_size, resize=None): #@save
"""Download the Fashion-MNIST dataset and then load it into memory."""
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()))
.. raw:: html
.. raw:: html
.. code:: python
def load_data_fashion_mnist(batch_size, resize=None): #@save
"""Download the Fashion-MNIST dataset and then load it into memory."""
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()))
.. raw:: html
.. raw:: html
.. code:: python
def load_data_fashion_mnist(batch_size, resize=None): #@save
"""Download the Fashion-MNIST dataset and then load it into memory."""
mnist_train, mnist_test = tf.keras.datasets.fashion_mnist.load_data()
# Divide all numbers by 255 so that all pixel values are between
# 0 and 1, add a batch dimension at the last. And cast label to 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))
.. raw:: html
.. raw:: html
Abaixo testamos o recurso de redimensionamento de imagem da função
``load_data_fashion_mnist`` especificando o argumento ``resize``.
.. raw:: html
.. raw:: html
.. code:: python
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
.. parsed-literal::
:class: output
(32, 1, 64, 64) (32,)
.. raw:: html
.. raw:: html
.. code:: python
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
.. parsed-literal::
:class: output
torch.Size([32, 1, 64, 64]) torch.float32 torch.Size([32]) torch.int64
.. raw:: html
.. raw:: html
.. code:: python
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
.. parsed-literal::
:class: output
(32, 64, 64, 1) (32,)
.. raw:: html
.. raw:: html
Agora estamos prontos para trabalhar com o *dataset* Fashion-MNIST nas
seções a seguir.
Resumo
------
- Fashion-MNIST é um *dataset* de classificação de vestuário que
consiste em imagens que representam 10 categorias. Usaremos esse
conjunto de dados nas seções e capítulos subsequentes para avaliar
vários algoritmos de classificação.
- Armazenamos a forma de qualquer imagem com altura :math:`h` largura
:math:`w` pixels como :math:`h \times w` or (:math:`h`, :math:`w`).
- Os iteradores de dados são um componente chave para um desempenho
eficiente. Conte com iteradores de dados bem implementados que
exploram a computação de alto desempenho para evitar desacelerar o
ciclo de treinamento.
Exercícios
----------
1. A redução de ``batch_size`` (por exemplo, para 1) afeta o desempenho
de leitura?
2. O desempenho do iterador de dados é importante. Você acha que a
implementação atual é rápida o suficiente? Explore várias opções para
melhorá-lo.
3. Verifique a documentação online da API do *framework*. Quais outros
conjuntos de dados estão disponíveis?
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
.. raw:: html