Цель: создать модель прогнозирования машинного обучения, обслуживать ее с помощью HTTP-сервера на удаленном облачном сервере и управлять ею, как в производственной среде.

Это практический подход, при котором мы будем писать код с нуля с объяснением всего кода и жаргонов.
Окончательный код для справки можно найти здесь

Это часть 1 из 4 частей:

  1. Обучение очень простой модели машинного обучения (которая сообщает, есть ли пиво на данном изображении) с использованием keras (библиотеки глубокого обучения Python). Мы не будем углубляться в это, в основном на основе интуиции.
  2. Написание независимой функции прогнозирования, которая обслуживает модель, а затем выполнение прогноза с помощью запроса HTTP API POST (здесь).
  3. Докеризация API прогнозирования. Создание файла Docker и создание собственного образа докера. Развертывание докера прогнозирования на облачном компьютере. Мы сможем вызвать api прогнозирования из любого места и увидеть статус и журналы сервера для отладки. (Здесь)
  4. Настройка базового конвейера CI / CD, т. Е. Настройка нашей инфраструктуры таким образом, чтобы выполнение фиксации автоматически развертывало новый код в рабочей среде. (Здесь)

Структура проекта

.
├── data/                   # This folder contains the training data
  └── dataset/
    ├── train/ 
      ├── beer/
      └── not_beer/
    └── validate/
      ├── beer/
      └── not_beer/
├── scripts                # These contain the standalone scripts.
├── src                    # Source files
  ├── training
    └── train.py 
  ├── serving
    └── serve.py
  ├── prediction
    ├── predict.py
    └── run_predict_on_file.py
  └── config.py 
├── test                   # Automated tests
├── .gitignore             # files to be ignored in commits 
├── LICENSE
├── Makefile
├── README.md
├── serving-requirements.txt
└── training-requirements.txt

Начните с создания пустой папки mkdir to-beer-or-not-to-beer и вставьте в нее компакт-диск.

Управление зависимостями

Затем создайте среду песочницы, используя virtualenv (_ 12_). Это позволит нам поэкспериментировать с версиями пакетов pip, которые отличаются от пакетов pip, установленных в нашей системе. Создайте это с помощью virtualenv training-venv (создается папка с именем venv, которая будет содержать все ваши пакеты pip). Активируйте это с помощью source ./training-venv/bin/activate

Установите зависимости, которые потребуются нам для обучения
pip install numpy==1.17.4 tensorflow==1.14 keras==2.2.4 pillow==6.2.1 google-images-download==2.8.0

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

Чтобы отслеживать пакеты, которые вы используете, выполните эту команду pip freeze > training-requirements.txt. Это создаст новый файл «training-requirements.txt». В нем вы найдете установленные вами пакеты и их номера версий. Переустановить пакеты так же просто, как запустить pip install -r training-requirements.txt.

Сбор данных

Теперь приступим к сбору необходимых данных. Создайте каталог скриптов и давайте создадим веб-парсер, который будет загружать изображения из Google по ключевому слову. Используя это, вы можете загружать свое пиво, а не изображения пива. Я использовал этот скрипт для загрузки 1000 изображений «пива» и 1000 изображений «not_beer» для обучения и по 200 изображений для проверки. Убедитесь, что вы поместили данные в соответствующие папки, как показано в структуре проекта выше.

# ./scripts/scrapers/google/image_downloader.py
from google_images_download import google_images_download
def image_download(output_dir, keywords , number_images):
  """
    arguments:
    keywords : it refers to the keywords being searched
    number_images : Total number of images to be downloaded
"""
  response = google_images_download.googleimagesdownload()
  arguments = {"keywords":keywords,"limit":number_images,"print_urls":True, "output_directory": output_dir}   #creating list of arguments
  paths = response.download(arguments)   #passing args
  print(paths)   #printing absolute paths of the downloaded images
image_download('./data/datasets/train/beer', 'beer', 100)

Централизованное управление конфигурацией

Прежде чем мы перейдем к обучению, давайте определим файл config.py, который мы будем использовать для управления конфигурацией, связанной с моделью. Чтобы попробовать с параметрами, нам просто нужно настроить этот файл. Это поможет нам поддерживать чистый код.

# ./src/config.py
dataset_dir = "data/datasets/"
train_data_dir = "data/datasets/beer/train/"
validation_data_dir = "data/datasets/beer/validation/"
trained_model_name = "latest_model_ekdum_kadak"
trained_model_file = "trained_models/beer/latest.h5"
epochs = 50
img_width = 150
img_height = 150
test_img_dir = "data/datasets/beer/test_validation/"
test_img_dir_file = "data/datasets/beer/test_validation/beer.007.jpg"

Обучение модели

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

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

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

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

Приведенный ниже фрагмент кода - это наша первая модель, простой стек из 3 сверточных слоев с активацией ReLU и последующими слоями максимального объединения. Это очень похоже на архитектуры, которые Ян Лекун защищал в 1990-х годах для классификации изображений (за исключением ReLU).

from src import config
import os
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K
if __name__ == "__main__":
  print("Starting training using train_data_dir->", config.train_data_dir)
  train_data_dir = config.train_data_dir
  validation_data_dir = config.validation_data_dir
  nb_train_samples = 2000
  nb_validation_samples = 400
  epochs = config.epochs
  batch_size = 16
if K.image_data_format() == 'channels_first':
    input_shape = (3, config.img_width, config.img_height)
  else:
    input_shape = (config.img_width, config.img_height, 3)
model = Sequential()
  model.add(Conv2D(32, (3, 3), input_shape=input_shape))
  model.add(Activation('relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Conv2D(32, (3, 3)))
  model.add(Activation('relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Conv2D(64, (3, 3)))
  model.add(Activation('relu'))
  model.add(MaxPooling2D(pool_size=(2, 2)))
  model.add(Flatten())
  model.add(Dense(64))
  model.add(Activation('relu'))
  model.add(Dropout(0.5))
  model.add(Dense(1))
  model.add(Activation('sigmoid'))
  model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
# this is the augmentation configuration we will use for training
  train_datagen = ImageDataGenerator(rescale=1. / 255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
# this is the augmentation configuration we will use for testing:
  # only rescaling
  test_datagen = ImageDataGenerator(rescale=1. / 255)
  train_generator = train_datagen.flow_from_directory( train_data_dir, target_size=(config.img_width, config.img_height), batch_size=batch_size, class_mode='binary')
validation_generator = test_datagen.flow_from_directory(validation_data_dir, target_size=(config.img_width, config.img_height), batch_size=batch_size, class_mode='binary')
model.fit_generator(train_generator, steps_per_epoch=nb_train_samples // batch_size, epochs=epochs, validation_data=validation_generator, validation_steps=nb_validation_samples // batch_size)
model.save(config.trained_model_file)

Для предварительной обработки и увеличения данных мы используем следующие параметры - ›

  • rotation_range - значение в градусах (0–180), диапазон, в котором можно произвольно повернуть изображения.
  • width_shift и height_shift - это диапазоны (в виде доли от общей ширины или высоты), в пределах которых можно произвольно переводить изображения по вертикали или горизонтали.
  • rescale - это значение, на которое мы умножим данные перед любой другой обработкой. Наши исходные изображения состоят из коэффициентов RGB в диапазоне 0–255, но такие значения были бы слишком высокими для обработки нашими моделями (с учетом типичной скорости обучения), поэтому вместо этого мы нацелены на значения от 0 до 1 путем масштабирования с 1/255. фактор.
  • shear_range для произвольного применения сдвиговых преобразований
  • zoom_range - для произвольного увеличения изображения внутри
  • horizontal_flip предназначен для случайного переворачивания половины изображений по горизонтали - актуально, когда нет предположений о горизонтальной асимметрии (например, реальных изображений).
  • fill_mode - это стратегия, используемая для заполнения вновь созданных пикселей, которые могут появиться после поворота или сдвига ширины / высоты.

После завершения выполнения этого кода, если все пойдет хорошо, будет создан файл обученной модели в «./trained_models/beer/latest.h5». Сеть будет обучаться быстрее, если этот код будет запущен на машине с графическим процессором. Для прогнозов не нужен графический процессор. ему нужны только обученные веса модели и архитектуры. Таким образом, мы будем использовать этот сгенерированный файл, чтобы запускать прогнозы отдельно, независимо от нашего обучения.

Обратите внимание, что это всего лишь базовая модель, которая дает вам интуицию. Использование трансферного обучения на предварительно обученной модели может дать гораздо лучшую точность. Для начала этого достаточно. Мы всегда можем вернуться позже и обучить лучшую модель. В конце концов будет сгенерирован файл «‹something› .h5», который мы будем использовать для прогнозирования. Этот файл можно напрямую использовать в прогнозе, поскольку он содержит информацию об используемой сетевой архитектуре и весах нейронов модели.

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