.. _sec_kaggle_cifar10: Classificação de Imagens (CIFAR-10) no Kaggle ============================================= Até agora, temos usado o pacote ``data`` do Gluon para obter diretamente conjuntos de dados de imagem no formato tensor. Na prática, entretanto, os conjuntos de dados de imagem geralmente existem no formato de arquivos de imagem. Nesta seção, começaremos com os arquivos de imagem originais e organizaremos, leremos e converteremos os arquivos para o formato tensor passo a passo. Realizamos um experimento no conjunto de dados CIFAR-10 em :numref:`sec_image_augmentation`. Este é um dado importante definido no campo de visão do computador. Agora, vamos aplicar o conhecimento que aprendemos em as seções anteriores para participar da competição Kaggle, que aborda problemas de classificação de imagens CIFAR-10. O endereço da competição na web é https://www.kaggle.com/c/cifar-10 :numref:`fig_kaggle_cifar10` mostra as informações na página da competição. Para enviar os resultados, primeiro registre uma conta no site do Kaggle. .. _fig_kaggle_cifar10: .. figure:: ../img/kaggle-cifar10.png :width: 600px Informações da página da web do concurso de classificação de imagens CIFAR-10. O conjunto de dados da competição pode ser acessado clicando na guia “Dados”. Primeiro, importe os pacotes ou módulos necessários para a competição. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python import collections import math import os import shutil import pandas as pd from mxnet import gluon, init, npx from mxnet.gluon import nn from d2l import mxnet as d2l npx.set_np() .. raw:: html
.. raw:: html
.. code:: python import collections import math import os import shutil import pandas as pd import torch import torchvision from torch import nn from d2l import torch as d2l .. raw:: html
.. raw:: html
Obtendo e Organizando o *Dataset* --------------------------------- Os dados da competição são divididos em um conjunto de treinamento e um conjunto de teste. O conjunto de treinamento contém :math:`50.000` imagens. O conjunto de teste contém :math:`300.000` imagens, das quais :math:`10.000` imagens são usadas para pontuação, enquanto as outras :math:`290.000` imagens sem pontuação são incluídas para evitar a rotulagem manual do conjunto de teste e o envio dos resultados da rotulagem. Os formatos de imagem em ambos os conjuntos de dados são PNG, com alturas e larguras de 32 pixels e três canais de cores (RGB). As imagens cobrem categorias de :math:`10`: aviões, carros, pássaros, gatos, veados, cães, sapos, cavalos, barcos e caminhões. O canto superior esquerdo de :numref:`fig_kaggle_cifar10` mostra algumas imagens de aviões, carros e pássaros no conjunto de dados. Baixando o Dataset ~~~~~~~~~~~~~~~~~~ Após fazer o login no Kaggle, podemos clicar na guia “Dados” na página da competição de classificação de imagens CIFAR-10 mostrada em :numref:`fig_kaggle_cifar10` e baixar o conjunto de dados clicando no botão “*Download All*”. Após descompactar o arquivo baixado em ``../data`` e descompactar ``train.7z`` e ``test.7z`` dentro dele, você encontrará o conjunto de dados inteiro nos seguintes caminhos: - ../data/cifar-10/train/[1-50000].png - ../data/cifar-10/test/[1-300000].png - ../data/cifar-10/trainLabels.csv - ../data/cifar-10/sampleSubmission.csv Aqui, as pastas ``train`` e\ ``test`` contêm as imagens de treinamento e teste, respectivamente, ``trainLabels.csv`` tem rótulos para as imagens de treinamento e ``sample_submission.csv`` é um exemplo de envio. Para facilitar o início, fornecemos uma amostra em pequena escala do conjunto de dados: ele contém as primeiras :math:`1000` de imagens de treinamento e :math:`5` de imagens de teste aleatórias. Para usar o conjunto de dados completo da competição Kaggle, você precisa definir a seguinte variável ``demo`` como ``False``. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python #@save d2l.DATA_HUB['cifar10_tiny'] = (d2l.DATA_URL + 'kaggle_cifar10_tiny.zip', '2068874e4b9a9f0fb07ebe0ad2b29754449ccacd') # If you use the full dataset downloaded for the Kaggle competition, set # `demo` to False demo = True if demo: data_dir = d2l.download_extract('cifar10_tiny') else: data_dir = '../data/cifar-10/' .. parsed-literal:: :class: output Downloading ../data/kaggle_cifar10_tiny.zip from http://d2l-data.s3-accelerate.amazonaws.com/kaggle_cifar10_tiny.zip... .. raw:: html
.. raw:: html
.. code:: python #@save d2l.DATA_HUB['cifar10_tiny'] = (d2l.DATA_URL + 'kaggle_cifar10_tiny.zip', '2068874e4b9a9f0fb07ebe0ad2b29754449ccacd') # If you use the full dataset downloaded for the Kaggle competition, set # `demo` to False demo = True if demo: data_dir = d2l.download_extract('cifar10_tiny') else: data_dir = '../data/cifar-10/' .. parsed-literal:: :class: output Downloading ../data/kaggle_cifar10_tiny.zip from http://d2l-data.s3-accelerate.amazonaws.com/kaggle_cifar10_tiny.zip... .. raw:: html
.. raw:: html
Organizando o Dataset ~~~~~~~~~~~~~~~~~~~~~ Precisamos organizar conjuntos de dados para facilitar o treinamento e teste do modelo. Vamos primeiro ler os rótulos do arquivo csv. A função a seguir retorna um dicionário que mapeia o nome do arquivo sem extensão para seu rótulo. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python #@save def read_csv_labels(fname): """Read fname to return a name to label dictionary.""" with open(fname, 'r') as f: # Skip the file header line (column name) lines = f.readlines()[1:] tokens = [l.rstrip().split(',') for l in lines] return dict(((name, label) for name, label in tokens)) labels = read_csv_labels(os.path.join(data_dir, 'trainLabels.csv')) print('# training examples:', len(labels)) print('# classes:', len(set(labels.values()))) .. parsed-literal:: :class: output # training examples: 1000 # classes: 10 .. raw:: html
.. raw:: html
.. code:: python #@save def read_csv_labels(fname): """Read fname to return a name to label dictionary.""" with open(fname, 'r') as f: # Skip the file header line (column name) lines = f.readlines()[1:] tokens = [l.rstrip().split(',') for l in lines] return dict(((name, label) for name, label in tokens)) labels = read_csv_labels(os.path.join(data_dir, 'trainLabels.csv')) print('# training examples:', len(labels)) print('# classes:', len(set(labels.values()))) .. parsed-literal:: :class: output # training examples: 1000 # classes: 10 .. raw:: html
.. raw:: html
Em seguida, definimos a função ``reorg_train_valid`` para segmentar o conjunto de validação do conjunto de treinamento original. O argumento ``valid_ratio`` nesta função é a razão entre o número de exemplos no conjunto de validação e o número de exemplos no conjunto de treinamento original. Em particular, seja :math:`n` o número de imagens da classe com menos exemplos e :math:`r` a proporção, então usaremos :math:`\max(\lfloor nr\rfloor,1)` imagens para cada classe como o conjunto de validação. Vamos usar ``valid_ratio = 0.1`` como exemplo. Como o conjunto de treinamento original tem :math:`50.000` imagens, haverá :math:`45.000` imagens usadas para treinamento e armazenadas no caminho “``train_valid_test/train``” ao ajustar hiperparâmetros, enquanto as outras :math:`5.000` imagens serão armazenadas como conjunto de validação no caminho “``train_valid_test / valid``”. Depois de organizar os dados, as imagens da mesma turma serão colocadas na mesma pasta para que possamos lê-las posteriormente. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python #@save def copyfile(filename, target_dir): """Copy a file into a target directory.""" os.makedirs(target_dir, exist_ok=True) shutil.copy(filename, target_dir) #@save def reorg_train_valid(data_dir, labels, valid_ratio): # The number of examples of the class with the least examples in the # training dataset n = collections.Counter(labels.values()).most_common()[-1][1] # The number of examples per class for the validation set n_valid_per_label = max(1, math.floor(n * valid_ratio)) label_count = {} for train_file in os.listdir(os.path.join(data_dir, 'train')): label = labels[train_file.split('.')[0]] fname = os.path.join(data_dir, 'train', train_file) # Copy to train_valid_test/train_valid with a subfolder per class copyfile(fname, os.path.join(data_dir, 'train_valid_test', 'train_valid', label)) if label not in label_count or label_count[label] < n_valid_per_label: # Copy to train_valid_test/valid copyfile(fname, os.path.join(data_dir, 'train_valid_test', 'valid', label)) label_count[label] = label_count.get(label, 0) + 1 else: # Copy to train_valid_test/train copyfile(fname, os.path.join(data_dir, 'train_valid_test', 'train', label)) return n_valid_per_label .. raw:: html
.. raw:: html
.. code:: python #@save def copyfile(filename, target_dir): """Copy a file into a target directory.""" os.makedirs(target_dir, exist_ok=True) shutil.copy(filename, target_dir) #@save def reorg_train_valid(data_dir, labels, valid_ratio): # The number of examples of the class with the least examples in the # training dataset n = collections.Counter(labels.values()).most_common()[-1][1] # The number of examples per class for the validation set n_valid_per_label = max(1, math.floor(n * valid_ratio)) label_count = {} for train_file in os.listdir(os.path.join(data_dir, 'train')): label = labels[train_file.split('.')[0]] fname = os.path.join(data_dir, 'train', train_file) # Copy to train_valid_test/train_valid with a subfolder per class copyfile(fname, os.path.join(data_dir, 'train_valid_test', 'train_valid', label)) if label not in label_count or label_count[label] < n_valid_per_label: # Copy to train_valid_test/valid copyfile(fname, os.path.join(data_dir, 'train_valid_test', 'valid', label)) label_count[label] = label_count.get(label, 0) + 1 else: # Copy to train_valid_test/train copyfile(fname, os.path.join(data_dir, 'train_valid_test', 'train', label)) return n_valid_per_label .. raw:: html
.. raw:: html
A função ``reorg_test`` abaixo é usada para organizar o conjunto de testes para facilitar a leitura durante a previsão. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python #@save def reorg_test(data_dir): for test_file in os.listdir(os.path.join(data_dir, 'test')): copyfile(os.path.join(data_dir, 'test', test_file), os.path.join(data_dir, 'train_valid_test', 'test', 'unknown')) .. raw:: html
.. raw:: html
.. code:: python #@save def reorg_test(data_dir): for test_file in os.listdir(os.path.join(data_dir, 'test')): copyfile(os.path.join(data_dir, 'test', test_file), os.path.join(data_dir, 'train_valid_test', 'test', 'unknown')) .. raw:: html
.. raw:: html
Finalmente, usamos uma função para chamar as funções ``read_csv_labels``, ``reorg_train_valid`` e ``reorg_test`` previamente definidas. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python def reorg_cifar10_data(data_dir, valid_ratio): labels = read_csv_labels(os.path.join(data_dir, 'trainLabels.csv')) reorg_train_valid(data_dir, labels, valid_ratio) reorg_test(data_dir) .. raw:: html
.. raw:: html
.. code:: python def reorg_cifar10_data(data_dir, valid_ratio): labels = read_csv_labels(os.path.join(data_dir, 'trainLabels.csv')) reorg_train_valid(data_dir, labels, valid_ratio) reorg_test(data_dir) .. raw:: html
.. raw:: html
Definimos apenas o tamanho do lote em :math:`4` para o conjunto de dados de demonstração. Durante o treinamento e os testes reais, o conjunto de dados completo da competição Kaggle deve ser usado e ``batch_size`` deve ser definido como um número inteiro maior, como :math:`128`. Usamos :math:`10\%` dos exemplos de treinamento como o conjunto de validação para ajustar os hiperparâmetros. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python batch_size = 4 if demo else 128 valid_ratio = 0.1 reorg_cifar10_data(data_dir, valid_ratio) .. raw:: html
.. raw:: html
.. code:: python batch_size = 4 if demo else 128 valid_ratio = 0.1 reorg_cifar10_data(data_dir, valid_ratio) .. raw:: html
.. raw:: html
Aumento de Imagem ----------------- Para lidar com o *overfitting*, usamos o aumento da imagem. Por exemplo, adicionando ``transforms.RandomFlipLeftRight()``, as imagens podem ser invertidas aleatoriamente. Também podemos realizar a normalização para os três canais RGB de imagens coloridas usando ``transforma.Normalize()``. Abaixo, listamos algumas dessas operações que você pode escolher para usar ou modificar, dependendo dos requisitos. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python transform_train = gluon.data.vision.transforms.Compose([ # Magnify the image to a square of 40 pixels in both height and width gluon.data.vision.transforms.Resize(40), # Randomly crop a square image of 40 pixels in both height and width to # produce a small square of 0.64 to 1 times the area of the original # image, and then shrink it to a square of 32 pixels in both height and # width gluon.data.vision.transforms.RandomResizedCrop(32, scale=(0.64, 1.0), ratio=(1.0, 1.0)), gluon.data.vision.transforms.RandomFlipLeftRight(), gluon.data.vision.transforms.ToTensor(), # Normalize each channel of the image gluon.data.vision.transforms.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010])]) .. raw:: html
.. raw:: html
.. code:: python transform_train = torchvision.transforms.Compose([ # Magnify the image to a square of 40 pixels in both height and width torchvision.transforms.Resize(40), # Randomly crop a square image of 40 pixels in both height and width to # produce a small square of 0.64 to 1 times the area of the original # image, and then shrink it to a square of 32 pixels in both height and # width torchvision.transforms.RandomResizedCrop(32, scale=(0.64, 1.0), ratio=(1.0, 1.0)), torchvision.transforms.RandomHorizontalFlip(), torchvision.transforms.ToTensor(), # Normalize each channel of the image torchvision.transforms.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010])]) .. raw:: html
.. raw:: html
Para garantir a certeza da saída durante o teste, realizamos apenas normalização na imagem. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python transform_test = gluon.data.vision.transforms.Compose([ gluon.data.vision.transforms.ToTensor(), gluon.data.vision.transforms.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010])]) .. raw:: html
.. raw:: html
.. code:: python transform_test = torchvision.transforms.Compose([ torchvision.transforms.ToTensor(), torchvision.transforms.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010])]) .. raw:: html
.. raw:: html
Lendo o *Dataset* ----------------- Em seguida, podemos criar a instância ``ImageFolderDataset`` para ler o conjunto de dados organizado contendo os arquivos de imagem originais, onde cada exemplo inclui a imagem e o rótulo. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python train_ds, valid_ds, train_valid_ds, test_ds = [ gluon.data.vision.ImageFolderDataset( os.path.join(data_dir, 'train_valid_test', folder)) for folder in ['train', 'valid', 'train_valid', 'test']] .. raw:: html
.. raw:: html
.. code:: python train_ds, train_valid_ds = [torchvision.datasets.ImageFolder( os.path.join(data_dir, 'train_valid_test', folder), transform=transform_train) for folder in ['train', 'train_valid']] valid_ds, test_ds = [torchvision.datasets.ImageFolder( os.path.join(data_dir, 'train_valid_test', folder), transform=transform_test) for folder in ['valid', 'test']] .. raw:: html
.. raw:: html
Especificamos a operação de aumento de imagem definida em ``DataLoader``. Durante o treinamento, usamos apenas o conjunto de validação para avaliar o modelo, portanto, precisamos garantir a certeza do resultado. Durante a previsão, treinaremos o modelo no conjunto de treinamento combinado e no conjunto de validação para fazer uso completo de todos os dados rotulados. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python train_iter, train_valid_iter = [gluon.data.DataLoader( dataset.transform_first(transform_train), batch_size, shuffle=True, last_batch='discard') for dataset in (train_ds, train_valid_ds)] valid_iter = gluon.data.DataLoader( valid_ds.transform_first(transform_test), batch_size, shuffle=False, last_batch='discard') test_iter = gluon.data.DataLoader( test_ds.transform_first(transform_test), batch_size, shuffle=False, last_batch='keep') .. raw:: html
.. raw:: html
.. code:: python train_iter, train_valid_iter = [torch.utils.data.DataLoader( dataset, batch_size, shuffle=True, drop_last=True) for dataset in (train_ds, train_valid_ds)] valid_iter = torch.utils.data.DataLoader(valid_ds, batch_size, shuffle=False, drop_last=True) test_iter = torch.utils.data.DataLoader(test_ds, batch_size, shuffle=False, drop_last=False) .. raw:: html
.. raw:: html
Definindo o Modelo ------------------ Aqui, construímos os blocos residuais com base na classe ``HybridBlock``, que é ligeiramente diferente da implementação descrita em :numref:`sec_resnet`. Isso é feito para melhorar a eficiência de execução. .. raw:: html
mxnet
.. raw:: html
.. code:: python class Residual(nn.HybridBlock): def __init__(self, num_channels, use_1x1conv=False, strides=1, **kwargs): super(Residual, self).__init__(**kwargs) self.conv1 = nn.Conv2D(num_channels, kernel_size=3, padding=1, strides=strides) self.conv2 = nn.Conv2D(num_channels, kernel_size=3, padding=1) if use_1x1conv: self.conv3 = nn.Conv2D(num_channels, kernel_size=1, strides=strides) else: self.conv3 = None self.bn1 = nn.BatchNorm() self.bn2 = nn.BatchNorm() def hybrid_forward(self, F, X): Y = F.npx.relu(self.bn1(self.conv1(X))) Y = self.bn2(self.conv2(Y)) if self.conv3: X = self.conv3(X) return F.npx.relu(Y + X) .. raw:: html
.. raw:: html
A seguir, definimos o modelo ResNet-18. .. raw:: html
mxnet
.. raw:: html
.. code:: python def resnet18(num_classes): net = nn.HybridSequential() net.add(nn.Conv2D(64, kernel_size=3, strides=1, padding=1), nn.BatchNorm(), nn.Activation('relu')) def resnet_block(num_channels, num_residuals, first_block=False): blk = nn.HybridSequential() for i in range(num_residuals): if i == 0 and not first_block: blk.add(Residual(num_channels, use_1x1conv=True, strides=2)) else: blk.add(Residual(num_channels)) return blk net.add(resnet_block(64, 2, first_block=True), resnet_block(128, 2), resnet_block(256, 2), resnet_block(512, 2)) net.add(nn.GlobalAvgPool2D(), nn.Dense(num_classes)) return net .. raw:: html
.. raw:: html
O desafio de classificação de imagens CIFAR-10 usa 10 categorias. Vamos realizar a inicialização aleatória de Xavier no modelo antes do início do treinamento. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python def get_net(devices): num_classes = 10 net = resnet18(num_classes) net.initialize(ctx=devices, init=init.Xavier()) return net loss = gluon.loss.SoftmaxCrossEntropyLoss() .. raw:: html
.. raw:: html
.. code:: python def get_net(): num_classes = 10 # PyTorch doesn't have the notion of hybrid model net = d2l.resnet18(num_classes, 3) return net loss = nn.CrossEntropyLoss(reduction="none") .. raw:: html
.. raw:: html
Definindo as Funções de Treinamento ----------------------------------- Selecionaremos o modelo e ajustaremos os hiperparâmetros de acordo com o desempenho do modelo no conjunto de validação. Em seguida, definimos a função de treinamento do modelo ``treinar``. Registramos o tempo de treinamento de cada época, o que nos ajuda a comparar os custos de tempo de diferentes modelos. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python def train(net, train_iter, valid_iter, num_epochs, lr, wd, devices, lr_period, lr_decay): trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': lr, 'momentum': 0.9, 'wd': wd}) num_batches, timer = len(train_iter), d2l.Timer() animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], legend=['train loss', 'train acc', 'valid acc']) for epoch in range(num_epochs): metric = d2l.Accumulator(3) if epoch > 0 and epoch % lr_period == 0: trainer.set_learning_rate(trainer.learning_rate * lr_decay) for i, (features, labels) in enumerate(train_iter): timer.start() l, acc = d2l.train_batch_ch13( net, features, labels.astype('float32'), loss, trainer, devices, d2l.split_batch) metric.add(l, acc, labels.shape[0]) timer.stop() if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1: animator.add(epoch + (i + 1) / num_batches, (metric[0] / metric[2], metric[1] / metric[2], None)) if valid_iter is not None: valid_acc = d2l.evaluate_accuracy_gpus(net, valid_iter, d2l.split_batch) animator.add(epoch + 1, (None, None, valid_acc)) if valid_iter is not None: print(f'loss {metric[0] / metric[2]:.3f}, ' f'train acc {metric[1] / metric[2]:.3f}, ' f'valid acc {valid_acc:.3f}') else: print(f'loss {metric[0] / metric[2]:.3f}, ' f'train acc {metric[1] / metric[2]:.3f}') print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec ' f'on {str(devices)}') .. raw:: html
.. raw:: html
.. code:: python def train(net, train_iter, valid_iter, num_epochs, lr, wd, devices, lr_period, lr_decay): trainer = torch.optim.SGD(net.parameters(), lr=lr, momentum=0.9, weight_decay=wd) scheduler = torch.optim.lr_scheduler.StepLR(trainer, lr_period, lr_decay) num_batches, timer = len(train_iter), d2l.Timer() animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], legend=['train loss', 'train acc', 'valid acc']) net = nn.DataParallel(net, device_ids=devices).to(devices[0]) for epoch in range(num_epochs): net.train() metric = d2l.Accumulator(3) for i, (features, labels) in enumerate(train_iter): timer.start() l, acc = d2l.train_batch_ch13(net, features, labels, loss, trainer, devices) metric.add(l, acc, labels.shape[0]) timer.stop() if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1: animator.add(epoch + (i + 1) / num_batches, (metric[0] / metric[2], metric[1] / metric[2], None)) if valid_iter is not None: valid_acc = d2l.evaluate_accuracy_gpu(net, valid_iter) animator.add(epoch + 1, (None, None, valid_acc)) scheduler.step() if valid_iter is not None: print(f'loss {metric[0] / metric[2]:.3f}, ' f'train acc {metric[1] / metric[2]:.3f}, ' f'valid acc {valid_acc:.3f}') else: print(f'loss {metric[0] / metric[2]:.3f}, ' f'train acc {metric[1] / metric[2]:.3f}') print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec ' f'on {str(devices)}') .. raw:: html
.. raw:: html
Treinamento e Validação do Modelo --------------------------------- Agora podemos treinar e validar o modelo. Os hiperparâmetros a seguir podem ser ajustados. Por exemplo, podemos aumentar o número de épocas. Como ``lr_period`` e\ ``lr_decay`` são definidos como 50 e 0,1 respectivamente, a taxa de aprendizado do algoritmo de otimização será multiplicada por 0,1 a cada 50 épocas. Para simplificar, treinamos apenas uma época aqui. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python devices, num_epochs, lr, wd = d2l.try_all_gpus(), 5, 0.1, 5e-4 lr_period, lr_decay, net = 50, 0.1, get_net(devices) net.hybridize() train(net, train_iter, valid_iter, num_epochs, lr, wd, devices, lr_period, lr_decay) .. parsed-literal:: :class: output loss nan, train acc 0.102, valid acc 0.100 131.6 examples/sec on [gpu(0), gpu(1)] .. figure:: output_kaggle-cifar10_42a34e_132_1.svg .. raw:: html
.. raw:: html
.. code:: python devices, num_epochs, lr, wd = d2l.try_all_gpus(), 5, 0.1, 5e-4 lr_period, lr_decay, net = 50, 0.1, get_net() train(net, train_iter, valid_iter, num_epochs, lr, wd, devices, lr_period, lr_decay) .. parsed-literal:: :class: output loss 2.522, train acc 0.105, valid acc 0.100 116.4 examples/sec on [device(type='cuda', index=0), device(type='cuda', index=1)] .. figure:: output_kaggle-cifar10_42a34e_135_1.svg .. raw:: html
.. raw:: html
Classificando o Conjunto de Testes e Enviando Resultados no Kaggle ------------------------------------------------------------------ Depois de obter um design de modelo satisfatório e hiperparâmetros, usamos todos os conjuntos de dados de treinamento (incluindo conjuntos de validação) para treinar novamente o modelo e classificar o conjunto de teste. .. raw:: html
mxnetpytorch
.. raw:: html
.. code:: python net, preds = get_net(devices), [] net.hybridize() train(net, train_valid_iter, None, num_epochs, lr, wd, devices, lr_period, lr_decay) for X, _ in test_iter: y_hat = net(X.as_in_ctx(devices[0])) preds.extend(y_hat.argmax(axis=1).astype(int).asnumpy()) sorted_ids = list(range(1, len(test_ds) + 1)) sorted_ids.sort(key=lambda x: str(x)) df = pd.DataFrame({'id': sorted_ids, 'label': preds}) df['label'] = df['label'].apply(lambda x: train_valid_ds.synsets[x]) df.to_csv('submission.csv', index=False) .. parsed-literal:: :class: output loss nan, train acc 0.102 138.0 examples/sec on [gpu(0), gpu(1)] .. figure:: output_kaggle-cifar10_42a34e_141_1.svg .. raw:: html
.. raw:: html
.. code:: python net, preds = get_net(), [] train(net, train_valid_iter, None, num_epochs, lr, wd, devices, lr_period, lr_decay) for X, _ in test_iter: y_hat = net(X.to(devices[0])) preds.extend(y_hat.argmax(dim=1).type(torch.int32).cpu().numpy()) sorted_ids = list(range(1, len(test_ds) + 1)) sorted_ids.sort(key=lambda x: str(x)) df = pd.DataFrame({'id': sorted_ids, 'label': preds}) df['label'] = df['label'].apply(lambda x: train_valid_ds.classes[x]) df.to_csv('submission.csv', index=False) .. parsed-literal:: :class: output loss 2.501, train acc 0.097 133.7 examples/sec on [device(type='cuda', index=0), device(type='cuda', index=1)] .. figure:: output_kaggle-cifar10_42a34e_144_1.svg .. raw:: html
.. raw:: html
Após executar o código acima, obteremos um arquivo “submit.csv”. O formato deste arquivo é consistente com os requisitos da competição Kaggle. O método para enviar resultados é semelhante ao método em :numref:`sec_kaggle_house`. Resumo ------ - Podemos criar uma instância ``ImageFolderDataset`` para ler o conjunto de dados contendo os arquivos de imagem originais. - Podemos usar redes neurais convolucionais, aumento de imagens e programação híbrida para participar de uma competição de classificação de imagens. Exercícios ---------- 1. Use o conjunto de dados CIFAR-10 completo para a competição Kaggle. Altere o ``batch_size`` e o número de épocas ``num_epochs`` para 128 e 100, respectivamente. Veja qual precisão e classificação você pode alcançar nesta competição. 2. Que precisão você pode alcançar quando não está usando o aumento de imagem? 3. Digitalize o código QR para acessar as discussões relevantes e trocar ideias sobre os métodos usados e os resultados obtidos com a comunidade. Você pode sugerir técnicas melhores? .. raw:: html
mxnetpytorch
.. raw:: html
`Discussões `__ .. raw:: html
.. raw:: html
`Discussões `__ .. raw:: html
.. raw:: html
.. raw:: html