Kaggle上的犬種識別(ImageNet Dogs)


Kaggle上的犬種識別(ImageNet Dogs)

Dog Breed Identification (ImageNet Dogs) on Kaggle

在本節中,將解決在Kaggle競賽中的犬種識別挑戰。比賽的網址是

             https://www.kaggle.com/c/dog-breed-identification             

在這場競賽中,試圖鑒別120種不同品種的狗。本次競賽中使用的數據集實際上是著名的ImageNet數據集的一個子集。與CIFAR-10數據集中的圖像不同,ImageNet數據集中的圖像更高更寬,尺寸不一致。             

圖1顯示了比賽網頁上的信息。為了提交結果,請先在Kaggle網站注冊一個帳戶。

 

Fig. 1  Dog breed identification competition website. The dataset for the competition can be accessed by clicking the “Data” tab.

首先,導入比賽所需的軟件包或模塊。

import collections

from d2l import mxnet as d2l

import math

from mxnet import autograd, gluon, init, npx

from mxnet.gluon import nn

import os

import time

npx.set_np()

1. Obtaining and Organizing the Dataset

比賽數據分為訓練集和測試集。訓練集包含10222幀圖像和測試集包含10357幀圖像。兩組圖像均為JPEG格式。這些圖像包含三個RGB通道(顏色),具有不同的高度和寬度。訓練集中有120種狗,包括拉布拉多犬、貴賓犬、臘腸犬、薩摩耶犬、哈士奇犬、吉娃娃犬和約克郡梗(Labradors, Poodles, Dachshunds, Samoyeds, Huskies, Chihuahuas, and Yorkshire Terriers)。

1.1. Downloading the Dataset

登錄Kaggle后,點擊圖1所示犬種識別比賽網頁上的“數據”選項卡,點擊“全部下載”按鈕下載數據集。在../data中解壓縮下載的文件后,將在以下路徑中找到整個數據集:

  • ../data/dog-breed-identification/labels.csv
  • ../data/dog-breed-identification/sample_submission.csv
  • ../data/dog-breed-identification/train
  • ../data/dog-breed-identification/test

可能已經注意到,上述結構與第13.13節中的CIFAR-10競賽非常相似,其中文件夾分別訓練/和測試/包含訓練和測試狗圖像,以及標簽.csv有訓練圖像的標簽。             

類似地,為了更容易開始,提供了上面提到的數據集的小規模樣本,“train_valid_test_tiny.zip”. 如果要為Kaggle競賽使用完整的數據集,還需要將下面的demo變量更改為False。

#@save

d2l.DATA_HUB['dog_tiny'] = (d2l.DATA_URL + 'kaggle_dog_tiny.zip',

                            '7c9b54e78c1cedaa04998f9868bc548c60101362')

# If you use the full dataset downloaded for the Kaggle competition, change

# the variable below to False

demo = True

if demo:

    data_dir = d2l.download_extract('dog_tiny')

else:

data_dir = os.path.join('..', 'data', 'dog-breed-identification')

1.2. Organizing the Dataset

組織數據集,即將驗證集與訓練集分離,並將圖像移動到按標簽分組的子文件夾中。             

下面的reorg_dog_data函數用於讀取訓練數據標簽、分割驗證集並組織訓練集。

def reorg_dog_data(data_dir, valid_ratio):

    labels = d2l.read_csv_labels(os.path.join(data_dir, 'labels.csv'))

    d2l.reorg_train_valid(data_dir, labels, valid_ratio)

    d2l.reorg_test(data_dir)

batch_size = 1 if demo else 128

valid_ratio = 0.1

reorg_dog_data(data_dir, valid_ratio)

2. Image Augmentation

此部分中的圖像的大小大於上一部分中的圖像。下面是一些可能有用的圖像增強操作。

transform_train = gluon.data.vision.transforms.Compose([

    # Randomly crop the image to obtain an image with an area of 0.08 to 1 of

    # the original area and height to width ratio between 3/4 and 4/3. Then,

    # scale the image to create a new image with a height and width of 224

    # pixels each

    gluon.data.vision.transforms.RandomResizedCrop(224, scale=(0.08, 1.0),

                                                   ratio=(3.0/4.0, 4.0/3.0)),

    gluon.data.vision.transforms.RandomFlipLeftRight(),

    # Randomly change the brightness, contrast, and saturation

    gluon.data.vision.transforms.RandomColorJitter(brightness=0.4,

                                                   contrast=0.4,

                                                   saturation=0.4),

    # Add random noise

    gluon.data.vision.transforms.RandomLighting(0.1),

    gluon.data.vision.transforms.ToTensor(),

    # Standardize each channel of the image

    gluon.data.vision.transforms.Normalize([0.485, 0.456, 0.406],

                                           [0.229, 0.224, 0.225])])

在測試過程中,只使用明確的圖像預處理操作。

transform_test = gluon.data.vision.transforms.Compose([

    gluon.data.vision.transforms.Resize(256),

    # Crop a square of 224 by 224 from the center of the image

    gluon.data.vision.transforms.CenterCrop(224),

    gluon.data.vision.transforms.ToTensor(),

    gluon.data.vision.transforms.Normalize([0.485, 0.456, 0.406],

                                           [0.229, 0.224, 0.225])])

3. Reading the Dataset

與上一節一樣,可以創建一個ImageFolderDataset實例來讀取包含原始圖像文件的數據集。

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')]

在這里,創建DataLoader實例。

train_iter, train_valid_iter = [gluon.data.DataLoader(

    dataset.transform_first(transform_train), batch_size, shuffle=True,

    last_batch='keep') for dataset in (train_ds, train_valid_ds)]

valid_iter, test_iter = [gluon.data.DataLoader(

    dataset.transform_first(transform_test), batch_size, shuffle=False,

    last_batch='keep') for dataset in (valid_ds, test_ds)]

4. Defining the Model

本次比賽的數據集是ImageNet數據集的一個子集。因此,選擇一個在整個ImageNet數據集上預先訓練的模型,並使用來提取圖像特征,以便輸入到定制的小規模輸出網絡中。Gluon提供了一系列預先訓練的模型。這里,將使用經過預先訓練的ResNet-34模型。由於競爭數據集是預訓練數據集的一個子集,因此只需重用預訓練模型輸出層的輸入,即提取的特征。然后,可以用一個可以訓練的小的定制輸出網絡來代替原來的輸出層,例如一系列中兩個完全連接的層。不重新訓練用於特征提取的預訓練模型。這減少了訓練時間和存儲模型參數梯度所需的內存。             

必須注意,在圖像增強過程中,使用整個ImageNet數據集的三個RGB通道的平均值和標准差進行標准化。這與預訓練模型的規范化是一致的。

def get_net(ctx):

    finetune_net = gluon.model_zoo.vision.resnet34_v2(pretrained=True)

    # Define a new output network

    finetune_net.output_new = nn.HybridSequential(prefix='')

    finetune_net.output_new.add(nn.Dense(256, activation='relu'))

    # There are 120 output categories

    finetune_net.output_new.add(nn.Dense(120))

    # Initialize the output network

    finetune_net.output_new.initialize(init.Xavier(), ctx=ctx)

    # Distribute the model parameters to the CPUs or GPUs used for computation

    finetune_net.collect_params().reset_ctx(ctx)

    return finetune_net

在計算損失時,首先利用成員變量特征來獲取預先訓練模型輸出層的輸入,即提取的特征。然后,使用這個特性作為小型定制輸出網絡的輸入並計算輸出。

loss = gluon.loss.SoftmaxCrossEntropyLoss()

def evaluate_loss(data_iter, net, ctx):

    l_sum, n = 0.0, 0

    for X, y in data_iter:

        y = y.as_in_ctx(ctx)

        output_features = net.features(X.as_in_ctx(ctx))

        outputs = net.output_new(output_features)

        l_sum += float(loss(outputs, y).sum())

        n += y.size

    return l_sum / n

5. Defining the Training Functions

將根據模型在驗證集上的性能來選擇模型並調整超參數。模型訓練功能訓練只訓練小型定制輸出網絡。

def train(net, train_iter, valid_iter, num_epochs, lr, wd, ctx, lr_period,

          lr_decay):

    # Only train the small custom output network

    trainer = gluon.Trainer(net.output_new.collect_params(), 'sgd',

                            {'learning_rate': lr, 'momentum': 0.9, 'wd': wd})

    for epoch in range(num_epochs):

        train_l_sum, n, start = 0.0, 0, time.time()

        if epoch > 0 and epoch % lr_period == 0:

            trainer.set_learning_rate(trainer.learning_rate * lr_decay)

        for X, y in train_iter:

            y = y.as_in_ctx(ctx)

            output_features = net.features(X.as_in_ctx(ctx))

            with autograd.record():

                outputs = net.output_new(output_features)

                l = loss(outputs, y).sum()

            l.backward()

            trainer.step(batch_size)

            train_l_sum += float(l)

            n += y.size

        time_s = "time %.2f sec" % (time.time() - start)

        if valid_iter is not None:

            valid_loss = evaluate_loss(valid_iter, net, ctx)

            epoch_s = ("epoch %d, train loss %f, valid loss %f, "

                       % (epoch + 1, train_l_sum / n, valid_loss))

        else:

            epoch_s = ("epoch %d, train loss %f, "

                       % (epoch + 1, train_l_sum / n))

        print(epoch_s + time_s + ', lr ' + str(trainer.learning_rate))

6. Training and Validating the Model

現在,可以訓練和驗證模型。可以調整以下超參數。例如,可以增加紀元的數量。由於lr_period and lr_decay分別設置為10和0.1,因此優化算法的學習率每10個周期將乘以0.1。

ctx, num_epochs, lr, wd = d2l.try_gpu(), 1, 0.01, 1e-4

lr_period, lr_decay, net = 10, 0.1, get_net(ctx)

net.hybridize()

train(net, train_iter, valid_iter, num_epochs, lr, wd, ctx, lr_period,

      lr_decay)

epoch 1, train loss 4.879428, valid loss 4.834594, time 8.79 sec, lr 0.01

7. Classifying the Testing Set and Submitting Results on Kaggle

在獲得滿意的模型設計和超參數后,使用所有訓練數據集(包括驗證集)對模型進行再訓練,然后對測試集進行分類。請注意,預測是由剛剛訓練的輸出網絡做出的。

net = get_net(ctx)

net.hybridize()

train(net, train_valid_iter, None, num_epochs, lr, wd, ctx, lr_period,

      lr_decay)

preds = []

for data, label in test_iter:

    output_features = net.features(data.as_in_ctx(ctx))

    output = npx.softmax(net.output_new(output_features))

    preds.extend(output.asnumpy())

ids = sorted(os.listdir(

    os.path.join(data_dir, 'train_valid_test', 'test', 'unknown')))

with open('submission.csv', 'w') as f:

    f.write('id,' + ','.join(train_valid_ds.synsets) + '\n')

    for i, output in zip(ids, preds):

        f.write(i.split('.')[0] + ',' + ','.join(

            [str(num) for num in output]) + '\n')

epoch 1, train loss 4.848448, time 10.14 sec, lr 0.01

執行上述代碼后,將生成一個“submission.csv “文件。此文件的格式符合Kaggle競賽要求。

8. Summary

  • We can use a model pre-trained on the ImageNet dataset to extract features and only train a small custom output network. This will allow us to classify a subset of the ImageNet dataset with lower computing and storage overhead.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM