Kaggle比賽NCFM圖像分類任務簡介

爲了保護和監控海洋環境及生態平衡,大天然保護協會(The Nature Conservancy)邀請Kaggle社區的參賽者們開發可以出機器學習算法,自動分類和識別遠洋捕撈船上的攝像頭拍攝到的圖片中魚類的品種,例如不一樣種類的吞拿魚和鯊魚。大天然保護協會一共提供了3777張標註的圖片做爲訓練集,這些圖片被分爲了8類,其中7類是不一樣種類的海魚,剩餘1類則是不含有魚的圖片,每張圖片只屬於8類中的某一類別。算法

圖片中待識別的海魚所佔整張圖片的一小部分,這就給識別帶來了很大的挑戰性。此外,爲了衡量算法的有效性,還提供了額外的1000張圖片做爲測試集,參賽者們須要設計出一種圖像識別的算法,儘量地識別出這1000張測試圖片屬於8類中的哪一類別。Kaggle平臺爲每個競賽都提供了一個榜單(Leaderboard),識別的準確率越高的競賽者在榜單上的排名越靠前。app

split_train_val.pydom

import os
import numpy as np
import shutil

np.random.seed(2016)

root_train = 'data\\train_split'
root_val = 'data\\val_split'

root_total = 'data\\train'

FishNames = ['ALB', 'BET', 'DOL', 'LAG', 'NoF', 'OTHER', 'SHARK', 'YFT']

nbr_train_samples = 0
nbr_val_samples = 0
# 訓練集所佔比例
split_proportion = 0.8

for fish in FishNames:
    if not os.path.exists(root_train):
        os.mkdir(root_train)
    if not os.path.exists(root_val):
        os.mkdir(root_val)

    # 創建各個類別的文件夾
    if fish not in os.listdir(root_train):
        os.mkdir(os.path.join(root_train, fish))
    # 當前類別全部的圖片
    total_images = os.listdir(os.path.join(root_total, fish))
    # 訓練集中圖片的數量
    nbr_train = int(len(total_images) * split_proportion)
    # 打亂數據集
    np.random.shuffle(total_images)
    # 劃分出訓練集
    train_images = total_images[:nbr_train]
    # 劃分出測試集
    val_images = total_images[nbr_train:]

    for img in train_images:
        # 從train文件夾將圖片拷貝至train_split文件夾下
        source = os.path.join(root_total, fish, img)
        target = os.path.join(root_train, fish, img)
        shutil.copy(source, target)
        nbr_train_samples += 1

    if fish not in os.listdir(root_val):
        os.mkdir(os.path.join(root_val, fish))

    for img in val_images:
        # 從train文件夾將圖片拷貝至val_split文件夾下
        source = os.path.join(root_total, fish, img)
        target = os.path.join(root_val, fish, img)
        shutil.copy(source, target)
        nbr_val_samples += 1

print('Finish splitting train and val images!')
print('# training samples: {}, # val samples: {}'.format(nbr_train_samples, nbr_val_samples))

ImageDataGenerator()是keras.preprocessing.image模塊中的圖片生成器,同時也能夠在batch中對數據進行加強,擴充數據集大小,加強模型的泛化能力。好比進行旋轉,變形,歸一化等等。機器學習

參數:ide

  • rescale: rescaling factor. Defaults to None.If None or 0, no rescaling is applied,otherwise we multiply the data by the value provided
  • featurewise_center: Boolean. 對輸入的圖片每一個通道減去每一個通道對應均值。
  • samplewise_center: Boolan. 每張圖片減去樣本均值, 使得每一個樣本均值爲0。
  • featurewise_std_normalization(): Boolean()
  • samplewise_std_normalization(): Boolean()
  • zca_epsilon(): Default 12-6
  • zca_whitening: Boolean. 去除樣本之間的相關性
  • rotation_range(): 旋轉範圍
  • width_shift_range(): 水平平移範圍
  • height_shift_range(): 垂直平移範圍
  • shear_range(): float, 透視變換的範圍
  • zoom_range(): 縮放範圍
  • fill_mode: 填充模式, constant, nearest, reflect
  • cval: fill_mode == 'constant'的時候填充值
  • horizontal_flip(): 水平反轉
  • vertical_flip(): 垂直翻轉
  • preprocessing_function(): user提供的處理函數
  • data_format(): channels_first或者channels_last
  • validation_split(): 多少數據用於驗證集

方法:函數

  • apply_transform(x, transform_parameters):根據參數對x進行變換
  • fit(x, augment=False, rounds=1, seed=None): 將生成器用於數據x,從數據x中得到樣本的統計參數, 只有featurewise_center, featurewise_std_normalization或者zca_whitening爲True才須要
  • flow(x, y=None, batch_size=32, shuffle=True, sample_weight=None, seed=None, save_to_dir=None, save_prefix='', save_format='png', subset=None) ):按batch_size大小從x,y生成加強數據
  • flow_from_directory()從路徑生成加強數據,和flow方法相比最大的優勢在於不用一次將全部的數據讀入內存當中,這樣減少內存壓力,這樣不會發生OOM,血的教訓。
  • get_random_transform(img_shape, seed=None): 返回包含隨機圖像變換參數的字典
  • random_transform(x, seed=None): 進行隨機圖像變換, 經過設置seed能夠達到同步變換。
  • standardize(x): 對x進行歸一化

train.py學習

from keras.applications.inception_v3 import InceptionV3
from keras.layers import Flatten, Dense, AveragePooling2D
from keras.models import Model
from keras.optimizers import RMSprop, SGD
from keras.callbacks import ModelCheckpoint
from keras.preprocessing.image import ImageDataGenerator

# 超參數
learning_rate = 0.0001
img_width = 299
img_height = 299
nbr_train_samples = 3019
nbr_validation_samples = 758
# nbr_epochs = 25
nbr_epochs = 1
batch_size = 32

# 訓練集和測試集路徑
train_data_dir = 'data\\train_split'
val_data_dir = 'data\\val_split'

# 類別
FishNames = ['ALB', 'BET', 'DOL', 'LAG', 'NoF', 'OTHER', 'SHARK', 'YFT']

# 加載InceptionV3模型
print('Loading InceptionV3 Weights ...')
InceptionV3_notop = InceptionV3(include_top=False, weights='imagenet', input_tensor=None, input_shape=(299, 299, 3))
# Note that the preprocessing of InceptionV3 is: (x / 255 - 0.5) x 2

# 添加平均池化層和Softmax輸出層
print('Adding Average Pooling Layer and Softmax Output Layer ...')
# Shape: (8, 8, 2048)
output = InceptionV3_notop.get_layer(index=-1).output
output = AveragePooling2D((8, 8), strides=(8, 8), name='avg_pool')(output)
output = Flatten(name='flatten')(output)
output = Dense(8, activation='softmax', name='predictions')(output)

InceptionV3_model = Model(InceptionV3_notop.input, output)
print(InceptionV3_model.summary())

# 使用梯度降低優化模型
optimizer = SGD(lr=learning_rate, momentum=0.9, decay=0.0, nesterov=True)
# 模型編譯
InceptionV3_model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])
# 自動保存最佳模型
best_model_file = "./weights.h5"
best_model = ModelCheckpoint(best_model_file, monitor='val_acc', verbose=1, save_best_only=True)

# 訓練集數據擴增配置
train_datagen = ImageDataGenerator(
    rescale=1. / 255,
    shear_range=0.1,
    zoom_range=0.1,
    rotation_range=10.,
    width_shift_range=0.1,
    height_shift_range=0.1,
    horizontal_flip=True)

# 驗證集數據擴增配置
val_datagen = ImageDataGenerator(rescale=1. / 255)

train_generator = train_datagen.flow_from_directory(
    train_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    shuffle=True,
    classes=FishNames,
    class_mode='categorical')

validation_generator = val_datagen.flow_from_directory(
    val_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    shuffle=True,
    classes=FishNames,
    class_mode='categorical')

InceptionV3_model.fit_generator(
    train_generator,
    samples_per_epoch=nbr_train_samples,
    nb_epoch=nbr_epochs,
    validation_data=validation_generator,
    nb_val_samples=nbr_validation_samples,
    callbacks=[best_model])

predict.py測試

from keras.models import load_model
import os
from keras.preprocessing.image import ImageDataGenerator
import numpy as np

# 超參數
img_width = 299
img_height = 299
batch_size = 32
nbr_test_samples = 1000

# 類別
FishNames = ['ALB', 'BET', 'DOL', 'LAG', 'NoF', 'OTHER', 'SHARK', 'YFT']

# 模型文件路徑
weights_path = os.path.join('weights.h5')

# 測試集路徑
test_data_dir = os.path.join('data/test_stg1/')
if not os.path.exists(test_data_dir):
    os.mkdir(test_data_dir)

# 測試集數據生成器
test_datagen = ImageDataGenerator(rescale=1. / 255)

test_generator = test_datagen.flow_from_directory(
    test_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    shuffle=False,  # Important !!!
    classes=None,
    class_mode=None)

test_image_list = test_generator.filenames

# 加載模型
print('Loading model and weights from training process ...')
InceptionV3_model = load_model(weights_path)

# 預測
print('Begin to predict for testing data ...')
predictions = InceptionV3_model.predict_generator(test_generator, nbr_test_samples)

# 保存預測結果
np.savetxt(os.path.join('predictions.txt'), predictions)

# 寫入提交文件
print('Begin to write submission file ..')
f_submit = open(os.path.join('submit.csv'), 'w')
f_submit.write('image,ALB,BET,DOL,LAG,NoF,OTHER,SHARK,YFT\n')
for i, image_name in enumerate(test_image_list):
    pred = ['%.6f' % p for p in predictions[i, :]]
    if i % 100 == 0:
        print('{} / {}'.format(i, nbr_test_samples))
    f_submit.write('%s,%s\n' % (os.path.basename(image_name), ','.join(pred)))

f_submit.close()

print('Submission file successfully generated!')

predict_average_augmentation.py優化

from keras.models import load_model
import os
from keras.preprocessing.image import ImageDataGenerator
import numpy as np

os.environ["CUDA_VISIBLE_DEVICES"] = "0"

# 超參數
img_width = 299
img_height = 299
batch_size = 32
nbr_test_samples = 1000
nbr_augmentation = 5

# 類別
FishNames = ['ALB', 'BET', 'DOL', 'LAG', 'NoF', 'OTHER', 'SHARK', 'YFT']

# 模型文件路徑
weights_path = os.path.join('weights.h5')

# 測試集文件路徑
test_data_dir = os.path.join('data/test_stg1/')

# 測試集數據生成器
test_datagen = ImageDataGenerator(
    rescale=1. / 255,
    shear_range=0.1,
    zoom_range=0.1,
    width_shift_range=0.1,
    height_shift_range=0.1,
    horizontal_flip=True)

# 加載模型
print('Loading model and weights from training process ...')
InceptionV3_model = load_model(weights_path)

for idx in range(nbr_augmentation):
    print('{}th augmentation for testing ...'.format(idx))
    random_seed = np.random.random_integers(0, 100000)

    test_generator = test_datagen.flow_from_directory(
        test_data_dir,
        target_size=(img_width, img_height),
        batch_size=batch_size,
        shuffle=False,  # Important !!!
        seed=random_seed,
        classes=None,
        class_mode=None)

    test_image_list = test_generator.filenames
    # print('image_list: {}'.format(test_image_list[:10]))
    print('Begin to predict for testing data ...')
    if idx == 0:
        predictions = InceptionV3_model.predict_generator(test_generator, nbr_test_samples)
    else:
        predictions += InceptionV3_model.predict_generator(test_generator, nbr_test_samples)

# 同一個模型,平均多個測試樣例
predictions /= nbr_augmentation

# 寫入提交文件
print('Begin to write submission file ..')
f_submit = open(os.path.join('submit.csv'), 'w')
f_submit.write('image,ALB,BET,DOL,LAG,NoF,OTHER,SHARK,YFT\n')
for i, image_name in enumerate(test_image_list):
    pred = ['%.6f' % p for p in predictions[i, :]]
    if i % 100 == 0:
        print('{} / {}'.format(i, nbr_test_samples))
    f_submit.write('%s,%s\n' % (os.path.basename(image_name), ','.join(pred)))

f_submit.close()

print('Submission file successfully generated!')
相關文章
相關標籤/搜索