kaggle 圖像分類競賽實戰(一):數據集下載和清洗

前言

本文集以 Kaggle 網站真實競賽《dogs-vs-cats-redux-kernels-edition》爲主線,講解如何使用深度學習技術解決圖像分類問題。本文做爲文集第一篇,講解圖像數據集的下載和清洗。python

1. kaggle 命令行

不熟悉 kaggle 命令行接口的讀者可參考專欄 《Kaggle 命令行工具查看 AI 最新競賽和下載數據集》git

經過關鍵詞搜索競賽:github

$ kaggle competitions list -s "dog cat"
ref                                 deadline             category       reward  teamCount  userHasEntered  
----------------------------------  -------------------  ----------  ---------  ---------  --------------  
dogs-vs-cats                        2014-02-01 23:59:00  Playground       Swag        215           False  
dogs-vs-cats-redux-kernels-edition  2017-03-02 23:59:00  Playground  Knowledge       1314            True  
複製代碼

下載競賽數據:redux

$ kaggle competitions download -c dogs-vs-cats-redux-kernels-edition
Downloading dogs-vs-cats-redux-kernels-edition.zip to ~/dogs-vs-cats
100%|███████████████████████████████████████| 814M/814M [01:48<00:00, 7.37MB/s]
100%|███████████████████████████████████████| 814M/814M [01:48<00:00, 7.86MB/s]

複製代碼

查看數據:微信

$ ls -l | grep .zip
-rw-r--r--    1 ken  staff  853083283 10  6 10:32 dogs-vs-cats-redux-kernels-edition.zip
複製代碼

解壓數據:網絡

$ unzip dogs-vs-cats-redux-kernels-edition.zip -d datasets
Archive:  dogs-vs-cats-redux-kernels-edition.zip
  inflating: datasets/train.zip      
  inflating: datasets/sample_submission.csv  
  inflating: datasets/test.zip
複製代碼

查看解壓數據:app

$ cd datasets && ls
sample_submission.csv	test.zip		train.zip
複製代碼

2. 數據處理

2.1 查看數據集

2.1.1 解壓數據集

$ unzip train.zip && unzip test.zip
複製代碼

2.1.2 查看訓練集和測試集圖片數量

# 訓練集數量
$ ls train | wc -l
25000

# 測試集數量
$ ls test | wc -l
12500
複製代碼

2.1.3 加載訓練集和測試集

import os

def load_datasets():
    def load(d):
        datasets = []
        for fn in os.listdir(d):
            fp = os.path.join(d, fn)
            if os.path.isfile(fp):
                datasets.append(fp)
        return datasets
    return load('datasets/train'), load('datasets/test')

train_datasets, test_datasets = load_datasets()
複製代碼

2.1.4 隨機查看數據集

import random
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline

def show_img(img, ax=None, title=None):
    if ax is not None:
        ax.imshow(img)
        ax.axis('off')
        if title is not None:
            ax.set_title(title)
    else:
        plt.title(title)
        plt.imshow(img)
        plt.axis('off')

def show_img_by_path(imgpath, **argkw):
    show_img(Image.open(imgpath), title=os.path.basename(imgpath), **argkw)

def show_imgsets(imgsets, shape=(2, 5), **argkw):
    if not isinstance(imgsets, list):
        return

    rows, cols = shape
    size = rows * cols
    imgsets = np.array(imgsets[:size]).reshape(shape)

    fig, axs = plt.subplots(rows, cols, **argkw)

    for row in range(rows):
        for col in range(cols):
            show_img_by_path(imgsets[row, col], ax=axs[row, col])

def randsample(imgsets, shape=(2, 5), **argkw):
    imgsets = random.choices(imgsets, k=shape[0]*shape[1])

    show_imgsets(imgsets, shape=shape, **argkw)
複製代碼
randsample(train_datasets, shape=(10, 10), figsize=(16, 16))
複製代碼

2.2 訓練集圖片清洗

2.2.1 搭建模型

from keras.preprocessing import image
from keras.applications.resnet50 import preprocess_input
from keras.applications.resnet50 import ResNet50

class ImgModel:
    def __init__(self, mc):
        self.model = mc(weights='imagenet')
    def _totensor(self, img_path):
        img = image.load_img(img_path, target_size=(224, 224))
        x = image.img_to_array(img)
        tensor = np.expand_dims(x, axis=0)
        return preprocess_input(tensor)
    def predict(self, img_path):
        tensor = self._totensor(img_path)
        predict_label = self.model.predict(tensor)
        return np.argmax(predict_label)

model = ImgModel(ResNet50)
複製代碼

2.2.2 找出模型沒法識別出是狗和貓的圖片

根據 ImageNet 圖像標籤 ,標籤 151-268 是狗,標籤 281-285 是貓。dom

def is_dog_or_cat(img_path):
    prediction = model.predict(img_path)
    if (prediction >= 151) and (prediction <= 268):
        return True
    if (prediction >= 281) and (prediction <= 285):
        return True
    return False

def find_unknown(datasets):
    unknown = list()
    for img_path in datasets:
        if not is_dog_or_cat(img_path):
            unknown.append(img_path)
    return unknown
複製代碼
train_unknown = find_unknown(train_datasets)
複製代碼
randsample(train_unknown, shape=(10, 10), figsize=(16, 16))
複製代碼

通過模型的初步篩選,在原有 25,000 張圖片的訓練集中,有 3,430 張圖片模型未識別出是狗或者貓。但經過對這些未識別出來的圖片隨機抽樣觀察,發現存在不少明顯是貓的圖,以及少許是狗的圖片。說明咱們前面的設定的標籤範圍並未涵蓋全部貓狗的分類。爲了進一步找出這些標籤,咱們不妨看看這些未識別的出來的標籤是否真的是咱們須要的。函數

def find_unknown_labels(unknown):
    return [model.predict(img_path) for img_path in unknown]
unknown_labels = find_unknown_labels(train_unknown)
複製代碼

爲了能直觀查看圖片和標籤,咱們須要對前文查看圖片的函數 show_img_by_path() 稍做修改,以便在查看圖片的同時,在標題欄顯示標籤。工具

def show_img_by_path2(imgpath, **argkw):
    show_img(Image.open(imgpath), **argkw)
複製代碼

編寫一個生成器,實現依次查看未識別出來的圖像標籤。

def unknown_iter():
    for img_path, label in zip(train_unknown, unknown_labels):
        yield img_path, label

unknown = unknown_iter()

def show_unknown(unknown, **argkw):
    img_path, label = next(unknown)
    show_img_by_path2(img_path, title=label, **argkw)
複製代碼

從第一張未識別圖片開始,每調用一次函數 show_unknown(),依次顯示下一張圖片,直到看完全部圖片。

show_unknown(unknown)
複製代碼

若是以爲一次看一張圖片效率過低,能夠再編寫一個函數實現一次看多張圖片。注意,爲了從第一張開始看,咱們須要從新初始化 unknown 變量。

def show_unknowns(unknown, shape=(10, 10), **argkw):
    rows, cols = shape
    fig, axs = plt.subplots(rows, cols, **argkw)
    for row in range(rows):
        for col in range(cols):
            show_unknown(unknown, ax=axs[row, col])

unknown = unknown_iter()
複製代碼

每調用一次函數 show_unknowns() 順序顯示下一百張圖片。

show_unknowns(unknown, figsize=(16, 16))
複製代碼

若是對數字標籤不滿意,想看文本分類名稱(只有英文)。能夠參考專欄 《Keras 手動搭建 VGG 卷積神經網絡識別 ImageNet 1000 種常見分類》第 3.5 小節 。這裏直接使用相關代碼獲取數字標籤到文本標籤的映射字典。

def get_imagenet_classes():
    import pickle

    with open('imagenet1000_clsid_to_human.pkl', 'rb') as f:
        classes = pickle.load(f)

    return classes

imagenet_classes = get_imagenet_classes()

def unknown_iter2():
    labels = [str(i)+'-'+imagenet_classes[i] for i in unknown_labels]
    for img_path, label in zip(train_unknown, labels):
        yield img_path, label

unknown2 = unknown_iter2()
複製代碼

一次顯示 20 張圖片。

show_unknowns(unknown2, shape=(5, 4), figsize=(16, 16))
複製代碼

2.2.3 改進篩選模型

經過上一小節的觀察,咱們發現,簡單對圖片預測單一分類(機率最高的),會由於圖片中存在其餘物體干擾,而致使沒法識別出貓和狗。所以,咱們須要改進一一下模型,在機率最高的前10個分類中,查看是否有貓或者狗。

ImgModel 基本同樣,僅僅在 predict() 方法返回時,不是返回機率最大的標籤,而是按機率倒序排序,即機率最大的排在前面,返回標籤列表。

class ImgModel2:
    def __init__(self, mc):
        self.model = mc(weights='imagenet')
    def _totensor(self, img_path):
        img = image.load_img(img_path, target_size=(224, 224))
        x = image.img_to_array(img)
        tensor = np.expand_dims(x, axis=0)
        return preprocess_input(tensor)
    def predict(self, img_path):
        tensor = self._totensor(img_path)
        predict_labels = self.model.predict(tensor)
        sort_labels = sorted(list(zip(range(1000), predict_labels.flatten())), key=lambda x:x[1], reverse=True)
        return [label[0] for label in sort_labels]

model2 = ImgModel2(ResNet50)
複製代碼

改進了模型,同時也要對應調整一下函數 is_dog_or_cat2() 和函數 find_unknown2()

def is_dog_or_cat2(img_path):
    predictions = model2.predict(img_path)[:10]
    for prediction in predictions:
        if (prediction >= 151) and (prediction <= 268):
            return True
        if (prediction >= 281) and (prediction <= 285):
            return True
    return False

def find_unknown2(datasets):
    unknown = list()
    for img_path in datasets:
        if not is_dog_or_cat2(img_path):
            unknown.append(img_path)
    return unknown
複製代碼

在前文未識別出貓狗的 3,430 張圖片中,使用改進的模型從新篩選不能識別的圖片。

train_unknown2 = find_unknown2(train_unknown)
複製代碼
len(train_unknown2)
複製代碼
281
複製代碼

通過第二次改進模型後的篩選,由原來不能識別 3,430 張圖片降到 281 張。再來看看這剩下的 281 張圖片究竟是「何方神聖」,爲什麼如此難以識別。

randsample(train_unknown2, shape=(10, 10), figsize=(16, 16))
複製代碼

Fuck ! (我想你和我同樣,已經忍不住破口而出了...)不難看出,這些圖片確實狗糟糕的。簡直無 fuck 說,我有100個理由把這些圖片排除在訓練集以外。

3. 將不能識別出來的圖片移出訓練集

import shutil
複製代碼
def move_train_unknown(train_unknown):
    train_unknown_dir = 'datasets/train_unknown'
    if not os.path.isdir(train_unknown_dir):
        os.makedirs(train_unknown_dir)
    for img_path in train_unknown:
        shutil.move(img_path, train_unknown_dir)
    print('{} pictures have been moved.'.format(len(train_unknown)))
複製代碼
move_train_unknown(train_unknown2)
複製代碼
281 pictures have been moved.
複製代碼

從新加載訓練集

train_datasets = load_datasets()[0]

print('{} images left after cleaning.'.format(len(train_datasets)))

randsample(train_datasets, shape=(10, 10), figsize=(16, 16))
複製代碼
24719 images left after cleaning.
複製代碼

能夠看到,通過清洗後的訓練集,圖片「乾淨」了很多,這爲後續模型的訓練提供了良好的開始。


微信掃描二維碼 獲取最新技術原創

相關文章
相關標籤/搜索