天天看點

使用Mini-ImageNet訓練分類網絡

文章目錄

    • 資料集下載下傳連結
    • 資料集簡介
    • 制作新的train以及val檔案
    • 訓練自己的網絡

資料集下載下傳連結

百度網盤下載下傳:

連結: https://pan.baidu.com/s/1Uro6RuEbRGGCQ8iXvF2SAQ 密碼: hl31

資料集簡介

提到Imagenet大家都知道,是一個非常大型、有名的開源資料集。一般設計一個新的分類網絡就會在Imagenet1000類的資料上進行訓練以及驗證。包括常見的目标檢測網絡等,所使用的backbone一般都會先基于Imagenet進行預訓練。但對于普通研究員或者開發者而言,這個資料集太大了(全部下載下傳大概有100GB左右),而且訓練對硬體要求也非常高,通常都是很多塊高端顯示卡并行訓練,即使是這樣的配置通常還要訓練好幾天的時間。是以讓很多人望而卻步(我就是其中之一,關鍵太大,而且國内下載下傳很慢)。

2016年google DeepMind團隊從Imagnet資料集中抽取的一小部分(大小約3GB)制作了Mini-Imagenet資料集,共有100個類别,每個類别都有600張圖檔,共60000張(都是

.jpg

結尾的檔案),而且圖像的大小并不是固定的。

使用Mini-ImageNet訓練分類網絡
使用Mini-ImageNet訓練分類網絡
使用Mini-ImageNet訓練分類網絡
使用Mini-ImageNet訓練分類網絡

資料集的結構為:

├── mini-imagenet: 資料集根目錄
     ├── images: 所有的圖檔都存在這個檔案夾中
     ├── train.csv: 對應訓練集的标簽檔案
     ├── val.csv: 對應驗證集的标簽檔案
     └── test.csv: 對應測試集的标簽檔案
           

Mini-Imagenet資料集中還包含了

train.csv

val.csv

以及

test.csv

三個檔案。需要注意的是,當時作者制作這個資料集時主要是針對小樣本學習領域的,而且提供的标簽檔案并不是從每個類别中進行采樣的。我自己用

pandas

包分析了下每個标簽檔案。

train.csv

包含38400張圖檔,共64個類别。

val.csv

包含9600張圖檔,共16個類别。

test.csv

包含12000張圖檔,共20個類别。

每個标簽檔案之間的圖像以及類别都是互相獨立的,即共60000張圖檔,100個類。

pandas

讀取的

csv

檔案資料格式如下,每一行對應一張圖檔的名稱和所屬類别:

filename      label
0  n0153282900000005.jpg  n01532829
1  n0153282900000006.jpg  n01532829
2  n0153282900000007.jpg  n01532829
3  n0153282900000010.jpg  n01532829
4  n0153282900000014.jpg  n01532829
           

至于每個類别對應的實際物體名稱,可檢視這個json檔案,這個檔案是Imagenet1000類資料中對應的标簽檔案。

{"0": ["n01440764", "tench"], 
 "1": ["n01443537", "goldfish"], 
 "2": ["n01484850", "great_white_shark"],
 ...
}
           

制作新的train以及val檔案

根據上面分析的,如果想用Mini-Imgenet資料集直接去訓練自己的分類網絡是不可行的,因為

train.csv

val.csv

并不是從每個類别中進行采樣的,是以我們需要自己去建構一個新的

train.csv

val.csv

檔案。下面是我自己寫的一個建構

train.csv

val.csv

标簽檔案的腳本,該腳本會從這100個類别中按給定的比例去劃分訓練集和驗證集。

import os
import json

import pandas as pd
from PIL import Image
import matplotlib.pyplot as plt


def read_csv_classes(csv_dir: str, csv_name: str):
    data = pd.read_csv(os.path.join(csv_dir, csv_name))
    # print(data.head(1))  # filename, label

    label_set = set(data["label"].drop_duplicates().values)

    print("{} have {} images and {} classes.".format(csv_name,
                                                     data.shape[0],
                                                     len(label_set)))
    return data, label_set


def calculate_split_info(path: str, label_dict: dict, rate: float = 0.2):
    # read all images
    image_dir = os.path.join(path, "images")
    images_list = [i for i in os.listdir(image_dir) if i.endswith(".jpg")]
    print("find {} images in dataset.".format(len(images_list)))

    train_data, train_label = read_csv_classes(path, "train.csv")
    val_data, val_label = read_csv_classes(path, "val.csv")
    test_data, test_label = read_csv_classes(path, "test.csv")

    # Union operation
    labels = (train_label | val_label | test_label)
    labels = list(labels)
    labels.sort()
    print("all classes: {}".format(len(labels)))

    # create classes_name.json
    classes_label = dict([(label, [index, label_dict[label]]) for index, label in enumerate(labels)])
    json_str = json.dumps(classes_label, indent=4)
    with open('classes_name.json', 'w') as json_file:
        json_file.write(json_str)

    # concat csv data
    data = pd.concat([train_data, val_data, test_data], axis=0)
    print("total data shape: {}".format(data.shape))

    # split data on every classes
    num_every_classes = []
    split_train_data = []
    split_val_data = []
    for label in labels:
        class_data = data[data["label"] == label]
        num_every_classes.append(class_data.shape[0])

        # shuffle
        shuffle_data = class_data.sample(frac=1, random_state=1)
        num_train_sample = int(class_data.shape[0] * (1 - rate))
        split_train_data.append(shuffle_data[:num_train_sample])
        split_val_data.append(shuffle_data[num_train_sample:])

        # imshow
        imshow_flag = False
        if imshow_flag:
            img_name, img_label = shuffle_data.iloc[0].values
            img = Image.open(os.path.join(image_dir, img_name))
            plt.imshow(img)
            plt.title("class: " + classes_label[img_label][1])
            plt.show()

    # plot classes distribution
    plot_flag = False
    if plot_flag:
        plt.bar(range(1, 101), num_every_classes, align='center')
        plt.show()

    # concatenate data
    new_train_data = pd.concat(split_train_data, axis=0)
    new_val_data = pd.concat(split_val_data, axis=0)

    # save new csv data
    new_train_data.to_csv(os.path.join(path, "new_train.csv"))
    new_val_data.to_csv(os.path.join(path, "new_val.csv"))


def main():
    data_dir = "/home/wz/mini-imagenet/"  # 指向資料集的根目錄
    json_path = "./imagenet_class_index.json"  # 指向imagenet的索引标簽檔案

    # load imagenet labels
    label_dict = json.load(open(json_path, "r"))
    label_dict = dict([(v[0], v[1]) for k, v in label_dict.items()])

    calculate_split_info(data_dir, label_dict)


if __name__ == '__main__':
    main()
           

訓練自己的網絡

項目位址:https://github.com/WZMIAOMIAO/deep-learning-for-image-processing

pytorch_classification

->

mini-imagenet

檔案夾中,裡面提供了兩個訓練腳本,一個是針對單GPU的,一個是針對多GPU的。在這個項目中是以訓練ShuffleNetv2為例進行講解的。訓練了100個epoch,達到了78%的準确率。

使用Mini-ImageNet訓練分類網絡

接着,我拿這個預訓練權重去做遷移學習,訓練其他的小資料集,确實也有一定幫助。在我測試過程中,如果不使用預訓練權重,訓練自己的資料集能達到80%的準确率,如果使用預訓練權重能達到90%的準确率。當然基于Mini-Imagenet的預訓練權重和基于Imagenet的預訓練權重還有一些差距,畢竟資料量擺在這。之前使用基于Imagenet的預訓練權重準确率可以達到94%。

當然,對于自己新搭的網絡,如果想快速驗證一下,Mini-Imagenet也是一個不錯的選擇。

繼續閱讀