Google Colab - отличное место для практики машинного обучения, а Kaggle - одно из лучших мест для получения набора данных. В этой записи блога мы увидим, как легко:

  • Загрузите набор данных из Kaggle прямо в Google Colab
  • Свяжите Google Colab с Диском, чтобы сохранять веса моделей прямо в папку диска.
  • Создайте простой двоичный классификатор, построив небольшую сверточную нейронную сеть в TensorFlow Keras.
  • Используйте класс Image Data Generator из keras для упрощения работы с данными обучения и тестирования.

Итак, приступим!

Настроить Kaggle

Установите библиотеку Kaggle

! pip install kaggle

Создайте каталог .kaggle

!mkdir .kaggle

Добавьте свой json-файл токена в каталог .kaggle.

import json

token = {"username":"your-username","key":"your-key"}

with open('/content/.kaggle/kaggle.json', 'w') as file:
    json.dump(token, file)
  • username: ваше имя пользователя на Kaggle
  • key: ваш токен API из kaggle, вы можете получить его из Edit Profile - ›Account -› API - ›Create new API token

Скопируйте файл kaggle.json из папки содержимого в его местоположение в каталоге kaggle внутри корня.

!cp /content/.kaggle/kaggle.json ~/.kaggle/kaggle.json
#Configure the file
!kaggle config set -n path -v{/content}

Заблокируйте свой Kaggle API с помощью chmod 600, чтобы убедиться, что он не виден другим пользователям в системе.

!chmod 600 /root/.kaggle/kaggle.json

Теперь вы готовы !!

Вы можете перечислить наборы данных kaggle

!kaggle datasets list

Вы можете перечислить определенную категорию наборов данных, а также следующие

!kaggle datasets list -s sentiment

Скачать и настроить данные

Чтобы загрузить zip-файл набора данных, вам понадобится команда, относящаяся к конкретному набору данных. Что вы можете легко получить на странице Kaggle набора данных, который вы хотите загрузить. Просто нажмите Копировать команду API и вставьте ее в ячейку colab прямо, чтобы загрузить набор данных.

Загрузите zip-файл набора данных

!kaggle datasets download -d iarunava/cell-images-for-detecting-malaria -p /content

Используйте библиотеку zipfile для извлечения содержимого из zip-файла в папку содержимого на colab.

import os
import zipfile

local_zip = '/content/cell-images-for-detecting-malaria.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('/content/cell-images-for-detecting-malaria')
zip_ref.close()

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

# # Directory with our training unifected pictures
infected_dir = os.path.join('/content/cell-images-for-detecting-malaria/cell_images/Uninfected')
# Directory with our training infected or parasitized pictures
uninfected_dir = os.path.join('/content/cell-images-for-detecting-malaria/cell_images/Parasitized')
print(len(os.listdir(infected_dir)))
print(len(os.listdir(uninfected_dir)))

Теперь нам нужно структурировать наши данные по папкам. Нам нужна папка для обучения и папка для тестирования. И у каждого из них будет незараженная папка (содержащая изображения незараженных ячеек) и зараженная папка (содержащая изображения инфицированных ячеек).

У нас будет только два класса: зараженный и незараженный, поэтому у нас будет по две папки внутри каждой из папок для обучения и тестирования.

Итак, мы создадим каталоги в соответствии с этим. Сначала создадим каталог для обучения и тестирования. А затем для папок внутри каждого. Нам нужна эта структура, потому что мы будем использовать генератор данных изображения для передачи наших данных из каталогов в модель во время обучения. Генератор данных возьмет каждую подпапку внутри обучения и тестирования как один класс. Итак, в следующей структуре у нас будет папка для «зараженных» и «незараженных» изображений; тогда это будут два класса, что приведет к проблеме бинарной классификации.

try:
    os.mkdir('/content/cell-images-for-detecting-malaria/cell_images/training')
    os.mkdir('/content/cell-images-for-detecting-malaria/cell_images/testing')
    os.mkdir('/content/cell-images-for-detecting-malaria/cell_images/training/uninfected')
    os.mkdir('/content/cell-images-for-detecting-malaria/cell_images/training/infected')
    os.mkdir('/content/cell-images-for-detecting-malaria/cell_images/testing/uninfected')
    os.mkdir('/content/cell-images-for-detecting-malaria/cell_images/testing/infected')
except OSError as e:
    print('error:', e)

Иногда набор данных, который вы загружаете, уже структурирован в разные папки тестирования и обучения и классы внутри каждой. Но этот набор данных состоял всего из двух папок с изображениями инфицированных и неинфицированных клеток. Он не был разделен на обучение, проверку и тестирование. Так что нам придется разделить данные через код самостоятельно. Мы перенаправим наши данные из извлеченного содержимого zip-папки в определенные папки для обучения и тестирования после разделения. Следующий фрагмент кода выполнит эту работу. После этого у нас будут каталоги, которые мы создали ранее, заполненные необходимыми данными для обучения и тестирования.

import random 
from shutil import copyfile

def split_data(SOURCE, TRAINING, TESTING, SPLIT_SIZE):
    files = []
    for filename in os.listdir(SOURCE):
        file = SOURCE + filename
        if os.path.getsize(file) > 0:
            files.append(filename)
        else:
            print(filename + " is zero length, so ignoring.")

    training_length = int(len(files) * SPLIT_SIZE)
    testing_length = int(len(files) - training_length)
    shuffled_set = random.sample(files, len(files))
    training_set = shuffled_set[0:training_length]
    testing_set = shuffled_set[-testing_length:]

    for filename in training_set:
        this_file = SOURCE + filename
        destination = TRAINING + filename
        copyfile(this_file, destination)

    for filename in testing_set:
        this_file = SOURCE + filename
        destination = TESTING + filename
        copyfile(this_file, destination)


INFECTED_SOURCE_DIR = "/content/cell-images-for-detecting-malaria/cell_images/Parasitized/"
TRAINING_INFECTED_DIR = "/content/cell-images-for-detecting-malaria/cell_images/training/infected/"
TESTING_INFECTED_DIR = "/content/cell-images-for-detecting-malaria/cell_images/testing/infected/"
UNINFECTED_SOURCE_DIR = "/content/cell-images-for-detecting-malaria/cell_images/Uninfected/"
TRAINING_UNINFECTED_DIR = "/content/cell-images-for-detecting-malaria/cell_images/training/uninfected/"
TESTING_UNINFECTED_DIR = "/content/cell-images-for-detecting-malaria/cell_images/testing/uninfected/"

split_size = .9
split_data(INFECTED_SOURCE_DIR, TRAINING_INFECTED_DIR, TESTING_INFECTED_DIR, split_size)
split_data(UNINFECTED_SOURCE_DIR, TRAINING_UNINFECTED_DIR, TESTING_UNINFECTED_DIR, split_size)

Мы можем перечислить содержимое каталогов поездов для каждого из классов / папок следующим образом.

train_infected_names = os.listdir(TRAINING_INFECTED_DIR)
print(train_infected_names[:10])
train_uninfected_names = os.listdir(TRAINING_UNINFECTED_DIR)
print(train_uninfected_names[:10])

То же самое можно сделать и с тестовым каталогом.

test_infected_names = os.listdir(TESTING_INFECTED_DIR)
print(test_infected_names[:10])

test_uninfected_names = os.listdir(TESTING_UNINFECTED_DIR)
print(test_uninfected_names[:10])

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

%matplotlib inline

import matplotlib.pyplot as plt
import matplotlib.image as mpimg

# Parameters for our graph; we'll output images in a 4x4 configuration
nrows = 4
ncols = 4

# Index for iterating over images
pic_index = 0

Перебирайте изображения в обучающих зараженных и незараженных каталогах для отображения изображений. После того, как вышеуказанный блок был запущен, нижеследующий блок можно запускать повторно, чтобы каждый раз отображать разные наборы изображений (поскольку мы каждый раз увеличиваем pic_index на 8). Каждый раз мы будем отображать 8 инфицированных и 8 незараженных изображений клеток.

# Set up matplotlib fig, and size it to fit 4x4 pics
fig = plt.gcf()
fig.set_size_inches(ncols * 4, nrows * 4)

pic_index += 8
next_infected_pix = [os.path.join(TRAINING_INFECTED_DIR, 
fname) for fname in train_infected_names[pic_index-8:pic_index]]
next_uninfected_pix = [os.path.join(TRAINING_UNINFECTED_DIR, fname) for fname in train_uninfected_names[pic_index-8:pic_index]]

for i, img_path in enumerate(next_infected_pix+next_uninfected_pix):
  # Set up subplot; subplot indices start at 1
  sp = plt.subplot(nrows, ncols, i + 1)
  sp.axis('Off') # Don't show axes (or gridlines)

  img = mpimg.imread(img_path)
  plt.imshow(img)

plt.show()

Отображаемый результат:

Подключите Google Colab к Диску

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

!apt-get install -y -qq software-properties-common python-software-properties module-init-tools
!add-apt-repository -y ppa:alessandro-strada/ppa 2>&1 > /dev/null
!apt-get update -qq 2>&1 > /dev/null
!apt-get -y install -qq google-drive-ocamlfuse fuse
from google.colab import auth
auth.authenticate_user()
from oauth2client.client import GoogleCredentials
creds = GoogleCredentials.get_application_default()
import getpass
!google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret} < /dev/null 2>&1 | grep URL
vcode = getpass.getpass()
!echo {vcode} | google-drive-ocamlfuse -headless 
-id={creds.client_id} -secret={creds.client_secret}

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

!mkdir -p drive
!google-drive-ocamlfuse drive

Создайте модель CNN для обнаружения малярии

Обратный вызов контрольной точки

Давайте сначала импортируем тензорный поток. Во-вторых, мы определим каталог контрольных точек, в котором будем сохранять веса модели. Затем мы будем использовать функцию обратного вызова keras для создания обратных вызовов для сохранения веса каждые 3 эпохи.

  • обратный вызов: обратный вызов - это набор функций, которые должны применяться на определенных этапах процедуры обучения. Вы можете использовать обратные вызовы, чтобы получить представление о внутренних состояниях и статистике модели во время обучения. Затем обратный вызов может быть передан методу fit () для вызова функции на каждом этапе обучения. Мы можем использовать обратные вызовы, например, для сохранения модели после определенного количества эпох или после каждой эпохи или для ранней остановки на основе точности и потерь. В нашем коде мы будем использовать обратный вызов для сохранения веса каждые 3 эпохи.
import tensorflow as tf
checkpoint_path = "drive/app/malaria_detection/checkpoints/training.ckpt"
checkpoint_dir = os.path.dirname(checkpoint_path)

# Create checkpoint callback
cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, 
                                          save_weights_only=True,
                                                 verbose=1,
                                                 period=3)

Давайте немного разберем параметры функции обратного вызова контрольной точки:

  • checkpoint_path: каталог для сохранения веса модели
  • save_weights_only: мы определим это как True, потому что мы хотим сохранить только веса, а не всю модель, но другие параметры могут использоваться для других настроек этого
  • подробный: чтобы определить, сколько отображать в журнале
  • период: чтобы определить количество эпох, после которых мы хотим сохранить, поэтому мы будем сохранять веса только после каждых 3 эпох в каталоге на диске, определенном параметром checkpoint_path

Модельная архитектура

Теперь давайте определимся с архитектурой модели. Поскольку наши изображения не очень большие и сложные, мы можем использовать всего несколько слоев CNN и получить хорошие результаты. Следовательно, наша модель будет следующей:

  • Первый сверточный слой с входной формой, определенной как форма изображений, за которым следует максимальное объединение
  • Второй сверточный слой, за которым следует максимальное объединение
  • Третий слой свертки, за которым следует максимальное объединение
  • Сглаживание слоя, чтобы сгладить вывод третьего сверточного слоя и передать его на плотный слой из 128 узлов активации / нейронов.
  • Выведите плотный слой с одним элементом / нейроном, потому что у нас всего два класса; проблема бинарной классификации. Мы будем использовать сигмовидную функцию активации, поскольку нам нужно всего 0,1 в качестве выходных данных, чтобы разделить на два класса: инфицированные и неинфицированные.
model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(80, 80, 3)),
    tf.keras.layers.MaxPooling2D(2, 2),
    # The second convolution
    tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # The third convolution
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    # Flatten the results to feed into a DNN
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    # Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class (uninfected) and 0 class (infected)
    tf.keras.layers.Dense(1, activation='sigmoid')
]
)

Мы можем распечатать сводку модели, чтобы отобразить форму вывода на каждом уровне архитектуры и количество параметров, которые необходимо изучить на каждом этапе.

model.summary()

from tensorflow.keras.optimizers import RMSprop

model.compile(loss='binary_crossentropy',
              optimizer=RMSprop(lr=0.001),
              metrics=['acc'])

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

  • Функция потерь: математический способ измерения того, насколько ошибочны ваши прогнозы. Чем меньше убыток, тем лучше прогнозы. В процессе обучения мы настраиваем и изменяем параметры (веса) нашей модели, чтобы попытаться минимизировать эту функцию потерь и сделать наши прогнозы как можно более правильными. Существуют разные виды функций потерь, мы будем использовать binary_crossentorpy, потому что мы имеем дело с проблемой двоичной классификации; только два выходных класса, 0 или 1.
  • Оптимизатор: оптимизатор - это то, что настраивает параметры модели, чтобы уменьшить функцию потерь. «Функция потерь - это путеводитель по местности, сообщающий оптимизатору, движется ли он в правильном или неправильном направлении. Вы можете представить себе туриста, пытающегося спуститься с горы с завязанными глазами. Невозможно знать, в каком направлении двигаться, но есть одна вещь, которую она может знать: идет ли она вниз (прогрессирует) или поднимается (теряет прогресс). В конце концов, если она будет продолжать делать шаги, ведущие вниз, она доберется до базы ».
  • RMSProp: Существуют разные виды алгоритмов оптимизатора; RMSProp является одним из них.
  • lr: скорость обучения оптимизатора, проще говоря, определяет, насколько параметры должны быть изменены в каждом цикле. Если мы слишком быстро изменяем наши веса, добавляя или вычитая слишком много, мы можем помешать модели минимизировать функцию потерь. Мы не хотим делать такой большой прыжок, чтобы пропустить оптимальное значение для данного веса. В то же время, поддержание слишком низкой скорости laerning приведет к вычислительным затратам и времени.

Генератор графических данных

from tensorflow.keras.preprocessing.image import ImageDataGenerator
training_directory = '/content/cell-images-for-detecting-malaria/cell_images/training/'
train_datagen = ImageDataGenerator(rescale=1/255)

train_generator = train_datagen.flow_from_directory(
        training_directory,
        target_size=(80, 80),
        batch_size=256,
        class_mode='binary')
  • rescale: все изображения будут масштабированы на 1./255
  • training_directory: это исходный каталог для обучающих изображений
  • target_size: все изображения будут изменены до 80x80
  • batch_size: количество изображений в одном пакете цикла потерь оптимизатора, train_generator будет передавать обучающие изображения пакетами по 256
  • class_mode: мы сохраним это как binary, поскольку мы будем использовать binary_crossentropy loss, нам нужны двоичные метки

Осталось одно главное - тренировка. Подберем модель и передадим ей аргументы:

  • train_generator: наши обучающие изображения будут передаваться в модель
  • steps_per_epoch: общее количество шагов (пакетов выборок) за одну эпоху. Поскольку у нас 13780 * 2 = 27560 обучающих изображений и размер пакета 256, нам потребуется 27560/256 ~ 100 шагов на эпоху.
  • эпохи: сколько раз мы хотим, чтобы модель просматривала весь набор данных
  • подробный: определяет, что мы хотим видеть в журнале; Режим детализации. 0 = без звука, 1 = индикатор выполнения, 2 = одна строка на эпоху.
history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,  
      epochs=15,
      verbose=1)

Начало обучения:

Визуализируйте точность и потери

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

Вы можете получить информацию о точности и потерях из history.history, который будет иметь ключи acc и loss.

%matplotlib inline
import matplotlib.image  as mpimg
import matplotlib.pyplot as plt
#-----------------------------------------------------------
# Retrieve a list of list results on training and test data
# sets for each training epoch
#-----------------------------------------------------------
acc=history.history['acc']
loss=history.history['loss']
epochs=range(len(acc)) # Get number of epochs
#------------------------------------------------
# Plot training accuracy per epoch
#------------------------------------------------
plt.plot(epochs, acc, 'r', "Training Accuracy")
plt.title('Training accuracy')
plt.figure()
#------------------------------------------------
# Plot training loss per epoch
#------------------------------------------------
plt.plot(epochs, loss, 'r', "Training Loss")
plt.figure()

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

Теперь мы воспользуемся моделью, чтобы проверить точность наших тестовых данных. Мы можем использовать аналогичный объект класса генератора для прогнозирования тестовых данных следующим образом:

testing_directory = '/content/cell-images-for-detecting-malaria/cell_images/testing/'
test_datagen = ImageDataGenerator(rescale=1./255)
test_generator = test_datagen.flow_from_directory(
        testing_directory,
        target_size=(80, 80),
        color_mode="rgb",
        shuffle = False,
        class_mode='binary',
        batch_size=1)

filenames = test_generator.filenames
nb_samples = len(filenames)

predict = model.predict_generator(test_generator,steps = nb_samples, verbose=1)
  • rescale: все изображения будут масштабированы на 1./255
  • каталог тестирования: это исходный каталог для тестирования изображений
  • target_size: все изображения будут изменены до 80x80
  • batch_size: количество изображений в одном пакете, мы будем передавать изображения по одному, как batch_size = 1
  • shuffle: мы можем выбрать перемешивание изображений в каталоге, мы сохраняем его как False, по умолчанию True
  • color_mode: один из оттенков серого или rgb; будут ли изображения преобразованы в 1 или 3 канала
  • class_mode: мы сохраним это как binary, поскольку мы будем использовать binary_crossentropy loss, нам нужны двоичные метки

Поскольку мы использовали генератор данных изображения, мы, соответственно, будем использовать функцию Assessment_generator, чтобы получить потери и точность на наших тестовых образцах.

  • test_generator: для передачи изображений из каталога test через модель для предсказания
loss, acc = model.evaluate_generator(test_generator, steps=nb_samples)

Напечатаем наши результаты по потерям и точности

loss

0.14026699966237605

acc

0.95174164

Мы видим, что наша точность тестового набора данных составляет около 95 процентов, что довольно хорошо. Его можно улучшить и дальше, но мы закончим публикацию в блоге на достигнутой главной цели, как легко сделать все в colab от kaggle до обучения модели. Вы также можете видеть, что мы не использовали какой-либо набор проверки. Мы можем разделить данные на три набора вместо двух и использовать третий в качестве набора для проверки, чтобы лучше понять процесс обучения. А пока мы на этом закончим. :)

Ваше здоровье!

Вы можете найти Github Gist здесь:

Использованная литература: