Привет ребята,

Недавно я участвовал в своем самом первом хакатоне по глубокому обучению, который проводил Analytics Vidhya. Здесь я расскажу, как я начал с самого начала, какие подходы я использовал, как я утвердил свои оценки и, наконец, достиг 95% точности тестирования.

Фон

Изначально, когда я начал свой путь к машинному обучению, я в значительной степени ограничился только проблемами регрессии и классификации. Раньше меня очень пугали модели компьютерного зрения из-за продвинутого характера темы. Я думал, что слишком наивен, чтобы разбираться в архитектуре нейронных сетей и достижениях в этой области.

Но когда я наконец решил начать изучать эту тему, я постепенно заинтересовался, и тема стала действительно увлекательной. Чтобы реализовать свои практические занятия по учебникам, я сначала выбрал тему классификации изображений и попробовал некоторые очень простые задачи, такие как классификация рукописных цифр MNIST, классификация кошек и собак и т. Д. Это было мое первое взаимодействие с Keras, и я использовал Tensorflow в качестве бэкэнда. . Честно говоря, я обнаружил, что за Керасом очень легко следить новичкам.

Если вы такой же новичок, как я, я предлагаю вам сначала начать с Кераса.

Заявление о проблеме

Обнаружение судов или судов имеет широкий спектр применений в областях безопасности на море, управления рыболовством, загрязнения моря, обороны и безопасности на море, защиты от пиратства, нелегальной миграции и т. Д.

Есть 5 классов кораблей, которые нужно обнаружить, а именно:

Нам было предоставлено около 8932 изображений размером около 212 * 128 пикселей, которые менялись от изображения к изображению. Из них 6252 изображения будут использоваться в качестве обучающего набора и 2680 изображений будут использоваться в качестве тестового набора для оценки таблицы лидеров.

Подход - 1) Использование самоопределяемой архитектуры CNN

Как и любой новичок, начинающий использовать keras, в первом подходе я создал свою собственную архитектуру CNN и использовал ее для прогнозирования классов кораблей.

Здесь я объясняю код -

# Importing Necessary Libraries
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten,BatchNormalization
from keras.layers import Conv2D, MaxPooling2D,GlobalAveragePooling2D
from keras.utils import to_categorical
from keras.preprocessing import image
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from tqdm import tqdm
%matplotlib inline
# Reading the csv file which contains which images to use as Training Set and the corresponding Labels
path = "/Users/bdiplus/Desktop/Dan/Analytics_Vidya/GameOfDeepLearning/"
train = pd.read_csv(path+"/train.csv")    # reading the csv file
# printing first five rows of the file
train.head()

# Training Data Preprocessing
# Loading each image as numpy array and converting into uniform size (128, 128, 3); the images were colored so used 3 for RGB
train_image = []
for i in tqdm(range(train.shape[0])):
    img = image.load_img(path+'images/'+train['image'][i],target_size=(128,128,3))
    img = image.img_to_array(img)
    img = img/255
    train_image.append(img)
X = np.array(train_image)
X.shape #(6252, 128, 128, 3) 
# meaning 6252 color images each having size (128,128)
# Note - Dividing the pixel values by 255, to get normalized input features between 0 and 1
plt.imshow(X[1])

# Labels are given from 1 to 5, but algorithm requires labels starting from 0, so substracting 1
y=train[‘category’].values-1
# One hot Encoding the labels, e.g. converts class 1 to [0 1 0 0 0] 
y = to_categorical(y)
# Splitting the training set into 80% training + 20% validation sets
X_train, X_val, y_train, y_val = train_test_split(X, y, random_state=42, test_size=0.2)

Распределение классов в данных проверки было обнаружено так же, как и в данных обучения. Хорошо для нас. Также обратите внимание, что, хотя количество изображений не одинаково для всех классов, его все же можно принять как сбалансированный набор данных, поскольку они имеют один и тот же порядок (3: 1). Это не то же самое, что набор данных с перекосом, в котором порядок распределения классов будет сильно несбалансированным (~ 1000: 1).

Определение архитектуры модели CNN

# Model Architecture
model = Sequential()
model.add(Conv2D(32, (3, 3),activation='relu',input_shape=(128,128,3)))
model.add(Conv2D(32, (3, 3),activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(5, activation='softmax'))
# Compiling the Model
model.compile(loss='categorical_crossentropy',optimizer='Adam',metrics=['accuracy'])
# Fitting the model
model.fit(X_train, y_train, epochs=15, validation_data=(X_val, y_val))

Предварительная обработка тестовых данных

test = pd.read_csv(path+"/test.csv")    # reading the csv file
test.head()      # printing first five rows of the file
test_image = []
for i in tqdm(range(test.shape[0])):
    img = image.load_img(path+'images/'+test['image'][i],target_size=(128,128,3))
    img = image.img_to_array(img)
    img = img/255
    test_image.append(img)
X_test = np.array(test_image)

Прогнозирование на основе тестовых данных

prediction = model.predict_classes(X_test)
# creating submission file
sample = pd.read_csv('sample_submission_ns2btKE.csv')
sample['category'] = prediction + 1
sample.to_csv('sample_cnn.csv', header=True, index=False)

Пришло время посмотреть рейтинг таблицы лидеров с помощью подхода 1. С помощью этой очень поверхностной архитектуры и без использования каких-либо дополнительных методов улучшения модели мы могли бы достичь точности 64% !!!

Подход 2) Использование той же архитектуры CNN вместе с методами увеличения данных и обратными вызовами

Затем я хотел углубиться в архитектуру нейронных сетей, чтобы улучшить производительность моей модели. Но я знал, что глубокие сети являются синонимом переобучения, которое может дать мне хорошую точность обучения, но моя точность проверки пострадает. Итак, перед тем, как использовать глубокие модели, я решил сделать паузу и попробовать некоторые техники. На этапе обучения я узнал, что есть несколько методов, которые можно разумно использовать для повышения производительности модели при одновременном сокращении переобучения. Итак, в моем следующем подходе я попробовал следующие методы, используя ту же сеть CNN, которую мы построили в подходе 1.

  1. Расширение данных. Если в нейронные сети поступает больше данных, переобучение уменьшится. Но в соревнованиях мы не имеем права получать новые данные. Итак, мы можем искусственно создавать новые изображения, используя технику увеличения данных. Мы определяем объект генератора данных изображения, который принимает изображения в качестве входных данных и применяет случайное преобразование, например. перевод, поворот, отражение, масштабирование и т. д. для создания новых дополненных изображений, которые различаются по ориентации, но по сути являются одним и тем же изображением. Поэтому мы хотим, чтобы алгоритм рассматривал его как одно и то же изображение.
# Training Generator
train_datagen = ImageDataGenerator(
    rotation_range=40,
    width_shift_range=0.3,
    height_shift_range=0.3,
    horizontal_flip=True,
    fill_mode="nearest",
    shear_range=0.1,
    zoom_range=0.4
)

2) Обратные вызовы. Я использовал 3 обратных вызова для управления поведением моей модели.

а) Снижение скорости обучения. Мы знаем, что использование более низкой скорости обучения полезно для правильного обучения алгоритма, но модель становится очень медленной. Мы также не можем выбрать очень большую скорость обучения, иначе модель может никогда не сойтись. Итак, компромисс заключается в том, что мы позволяем алгоритму работать в течение нескольких эпох со скоростью обучения по умолчанию. Если точность проверки повысится, хорошо, и мы не изменим скорость обучения. Но если точность начинает падать, мы в какой-то раз снижаем скорость обучения. Для этого мы используем объект ReduceLROnPlateau, где мы можем указать аргумент patience, чтобы указать, сколько эпох, мы можем позволить снизить точность проверки.

б) Ранняя остановка - нейронные сети начинают переобучаться, если вы оставите это на несколько эпох. Итак, мы можем использовать EarlyStopping, чтобы остановить дальнейшее обучение, если точность проверки начнет снижаться. Опять же, мы можем использовать аргумент patience, чтобы указать, сколько эпох, мы можем позволить снизить точность проверки.

c) Контрольные точки модели. В течение эпох, в которых работает модель, точность модели может повышаться и понижаться. Итак, мы можем оставить контрольную точку, чтобы выбрать лучшую модель.

## Callbacks For Reducing Overfitting
from keras.callbacks import EarlyStopping, ReduceLROnPlateau,ModelCheckpoint
# Model CheckPointing
model_checkpoint = ModelCheckpoint('godr_mdl_wts_xcp2.hdf5', save_best_only=True, monitor='val_acc', mode='max')
# Early Stopping
earlystop = EarlyStopping(patience=10,verbose=1)
# Learning Rate Reduction
learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',patience=1,verbose=1,factor=0.5,min_lr=0.00001)
callbacks = [earlystop, learning_rate_reduction, model_checkpoint]

3) Размер пакета. Я пробовал разные размеры пакета: 32, 64, 128, но в итоге остановился на 64, поскольку он давал хорошую точность.

4) Заполнение. Я сохранил то же количество слоев, но добавил padding=same. Обратите внимание, что я использовал ту же модель CNN, как определено в Подходе 1.

# Model Architecture
model = Sequential()
model.add(Conv2D(32, (3, 3),activation='relu',input_shape=(128,128,3)))
model.add(Conv2D(32, (3, 3),activation='relu',padding='same'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu',padding='same'))
model.add(Conv2D(64, (3, 3), activation='relu',padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(5, activation='softmax'))
# Compiling the Model
model.compile(loss='categorical_crossentropy',optimizer='Adam',metrics=['accuracy'])

Используя четыре вышеупомянутых метода, мы готовы снова подобрать модель.

Обратите внимание, на этот раз я использовал генератор обучающих данных train_datagen, который будет принимать изображения в качестве входных данных и применять определенные преобразования. Точно так же я использовал дополнительные аргументы, чтобы указать batch_size и callbacks

batch_size = 64
# Fitting the Model
history = model.fit_generator(train_datagen.flow(X_train, y_train, batch_size=batch_size),
validation_data=(X_val, y_val), 
steps_per_epoch=X_train.shape[0] // batch_size,
epochs=15,
callbacks=callbacks)

С помощью этих методов мы смогли повысить нашу оценку до 84% точности.

Подход 3) Использование трансферного обучения с моделью Xception

Я никогда раньше не использовал трансферное обучение, поэтому немного колебался. Но после изучения онлайн-литературы и статистики соревнований я убедился, что для того, чтобы хорошо выступить в этом соревновании, я должен использовать некоторую предварительно обученную модель, которая стабильно дает хорошие результаты на широком диапазоне изображений. Я рассматривал модели VGG16, VGG19, Resnet и InceptionV3. Но из-за их глубокой архитектуры, они занимали невероятно много времени, чтобы закончить (~ 6 часов на 1 эпоху, думаю, 15 эпох, к тому времени само соревнование было бы окончено, ха-ха !!!). Недостаток оперативной памяти моей системы не выдержал этого, поскольку она работала на полную мощность непрерывно в течение 2 дней.

Итак, я решил поискать альтернативные модели, которые, с одной стороны, предлагают архитектуру с меньшим объемом памяти и хорошую производительность. Тогда я столкнулся с моделью Xception на Керасе.

Здесь я даю весь свой код, который использует передачу обучения из модели Xception -

Входные параметры -

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O
#from keras.preprocessing.image import ImageDataGenerator, load_img
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
from keras.utils import to_categorical
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import random
import os
print(os.listdir("/Users/bdiplus/Desktop/Dan/Analytics_Vidya/GameOfDeepLearning/"))
root_path = "/Users/bdiplus/Desktop/Dan/Analytics_Vidya/GameOfDeepLearning/"
image_path = "/Users/bdiplus/Desktop/Dan/Analytics_Vidya/GameOfDeepLearning/images/"
files = os.listdir("/Users/bdiplus/Desktop/Dan/Analytics_Vidya/GameOfDeepLearning/images/")
batch_size=64
input_shape=(210,210,3)

Обратные вызовы

## Callbacks For Reducing Overfitting
from keras.callbacks import EarlyStopping, ReduceLROnPlateau,ModelCheckpoint
# Model CheckPoiting
model_checkpoint = ModelCheckpoint('godr_mdl_wts_xcp2.hdf5', save_best_only=True, monitor='val_acc', mode='max')
# Early Stopping
earlystop = EarlyStopping(patience=10,verbose=1)
# Learning Rate Reduction
learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',patience=1,verbose=1,factor=0.5,min_lr=0.00001)
callbacks = [earlystop, learning_rate_reduction, model_checkpoint]

Модель Xception

#import inception with pre-trained weights. do not include fully #connected layers
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense, Activation, BatchNormalization
from keras.applications.xception import Xception
Xception_base = Xception(weights='imagenet', include_top=False, input_shape = input_shape, pooling='max')
model = Sequential()
model.add(Xception_base)
# model.add(Flatten())
# model.add(Dense(512, activation='relu'))
model.add(Dense(5, activation='softmax'))
model.summary()
# Compiling model
model.compile(loss='categorical_crossentropy',optimizer="Adam",metrics=['accuracy'])

Предварительная обработка данных обучения

train = pd.read_csv(root_path+"/train.csv")    # reading the csv file
from tqdm import tqdm
from keras.applications.xception import preprocess_input
# from keras.applications.inception_v3 import preprocess_input
train_image = []
for i in tqdm(range(train.shape[0])):
    img = image.load_img(root_path+'images/'+train['image'][i],target_size=input_shape)
    img = image.img_to_array(img)
    img = img/255
    train_image.append(img)
X = np.array(train_image)
y=train['category'].values-1
y = to_categorical(y)
X_train, X_val, y_train, y_val = train_test_split(X, y, random_state=42, test_size=0.2)

Увеличение данных

## Using Data Augmentation To Reduce Overfitting - Increases Data by randomly transforming the image
# Training Generator
train_datagen = ImageDataGenerator(
    rotation_range=40,
    width_shift_range=0.3,
    height_shift_range=0.3,
    horizontal_flip=True,
    fill_mode="nearest",
    shear_range=0.1,
    zoom_range=0.4
)

Подгонка модели

history = model.fit_generator(train_datagen.flow(X_train, y_train, batch_size=batch_size),validation_data=(X_val, y_val), steps_per_epoch=X_train.shape[0] // batch_size,epochs=15,
                        callbacks=callbacks)

# Visualize the model loss and Accuracy
epochs = 15
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 12))
ax1.plot(history.history['loss'], color='b', label="Training loss")
ax1.plot(history.history['val_loss'], color='r', label="validation loss")
ax1.set_xticks(np.arange(1, epochs, 1))
ax1.set_yticks(np.arange(0, 1, 0.1))
ax2.plot(history.history['acc'], color='b', label="Training accuracy")
ax2.plot(history.history['val_acc'], color='r',label="Validation accuracy")
ax2.set_xticks(np.arange(1, epochs, 1))
legend = plt.legend(loc='best', shadow=True)
plt.tight_layout()
plt.show()

Кривые потерь и точности

Как мы видим, с этой моделью мы достигли ~ 98% точности обучения и 94% точности проверки. Пришло время проверить производительность нашей модели на тестовых данных.

Предварительная обработка тестовых данных

test = pd.read_csv(path+"/test.csv")    # reading the csv file
test.head()      # printing first five rows of the file
test_image = []
for i in tqdm(range(test.shape[0])):
    img = image.load_img(path+'images/'+test['image'][i],target_size=(100,100,3))
    img = image.img_to_array(img)
    img = img/255
    test_image.append(img)
X_test = np.array(test_image)

Прогнозы на основе тестовых данных

prediction = model.predict_classes(X_test)
# creating submission file
sample = pd.read_csv('sample_submission_ns2btKE.csv')
sample['category'] = prediction + 1
sample.to_csv('sample_cnn.csv', header=True, index=False)

Как видно из вышеизложенного, мы достигли 95,6% точности в общедоступной таблице лидеров и 95% точности в частной таблице лидеров.

Заключение

Этот хакатон стал для меня чрезвычайно обогащающим опытом и предоставил мне сложную платформу для проверки моих недавно приобретенных навыков. Я хотел бы поблагодарить Analytics Vidhya за организацию этого мероприятия и их процветающее сообщество Data Science за предоставление такого качественного контента, которым мы дорожим. С нетерпением жду еще одного вызова !!!