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

И. Введение

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

Прежде чем продолжить обсуждение, давайте посмотрим демо проекта ниже:

Поиграть можно с демо — ЗДЕСЬ,

Доступна ссылка проекта на GitHub — ЗДЕСЬ

Схема статьи:

  • Фон
  • Руководство по проекту
    А. Модель сборки и обучения (Build_AND_Save_DL_model.ipynb)
    B. Приложение Streamlit (streamlit.py)
    C. Развертывание
  • Распространенные ошибки и устранение неполадок
  • Заключение и будущая работа

II. Фон

Построение модели машинного обучения в блокноте Jupyter — это одно, а развертывание модели — другое, требующее создания службы, к которой другой пользователь может получить доступ через Интернет. Существует множество методов и инструментов для развертывания модели машинного обучения, например создание REST API с облегченной веб-платформой, такой как Flask или Django. API можно вызвать из веб-приложения или мобильного приложения, чтобы получить прогноз по модели. Кроме того, вы можете развертывать модели машинного обучения как бессерверные функции, используя такие платформы, как Google Cloud Functions или AWS Lambda. Кроме того, вы можете упаковать модель машинного обучения и зависимости в контейнер Docker и развернуть его на определенной платформе управления, такой как Docker Swarm или Kubernetes. Варианты развертывания зависят от потребностей проекта, таких как бюджет, масштабируемость и производительность. Исходя из этих потребностей, разработчик должен выбирать методы. В нашем случае мы строим модель машинного обучения, используя Streamlit, который представляет собой фреймворк Python для создания веб-приложений для обработки данных. Мы также будем использовать ту же бесплатную платформу Streamlit для его размещения.

III. Руководство по проекту

Репозиторий проекта содержит следующие файлы/папки.

Два важных файла:

A. Build_AND_Save_DL_model.ipynb -> обучить и сохранить модель машинного обучения.

B. Streamlit.py —> Создавайте интерактивные веб-приложения с помощью Streamlit и делайте собственные прогнозы.

Другие файлы/папки:

  • data -> В папке хранятся пользовательские наборы данных и названия пород собак (обучающий набор данных большой, вы можете скачать его напрямую с Kaggle)
  • pretrained_models -> Сохраняет предварительно обученную и точно настроенную модель efficiencynet3.
  • README.md -> Текст о информации о проекте
  • custom_prediction.ipynb -> (необязательно) блокнот Experimental Jupiter, который делает пользовательские выводы на предварительно обученной модели.
  • requirements.txt -> Требования к пакету, чтобы сделать вывод на веб-сайте Streamlit.

A. Build_AND_Save_DL_model.ipynb

  1. Импортировать библиотеки и вспомогательные функции.

Я использовал Google Colab, в котором уже установлены библиотеки Python.

# I use tensorflow 2.9.1 version to train model
!pip install tensorflow==2.9.1

import pandas as pd
import pickle
from PIL import Image
from google.colab import drive
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import tensorflow as tf
import numpy as np
import zipfile
import os
import warnings
import shutil
import seaborn as sns
import random
from tensorflow.keras import layers
from tensorflow.keras.layers.experimental import preprocessing
warnings.filterwarnings("ignore")
drive.mount('/content/gdrive')
print(tf.__version__)

def unzip_data(filename):
  """
  Unzips filename into the current working directory.
  Args:
    filename (str): a filepath to a target zip folder to be unzipped.
  """
  zip_ref = zipfile.ZipFile(filename, "r")
  zip_ref.extractall()
  zip_ref.close()

def plot_value_count(df, column):
 """
 plots value count of dataframe column
 """
 sns.set(style='darkgrid')
 plt.figure(figsize= (20,10))
 sns.countplot(x=column, data=df, order = df[column].value_counts().index)
 plt.xticks(rotation=90)
 plt.show()

unzip_data("/content/gdrive/MyDrive/dog-breed-identification/dog-breed-identification.zip")

Если вы пытаетесь обучить модель локально, убедитесь, что вы создали новую виртуальную среду Python с помощью пакетного сценария:

python -m venv /path/to/new/virtual/environment

а затем установите библиотеки, чтобы не возникало проблем с зависимостями пакетов.

2. Изучите данные

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

labels_df=pd.read_csv('labels.csv')

train_images = os.listdir("/content/train/")
test_image = os.listdir("/content/test/")

# how many dog breed? 
target_labels = sorted(labels_df['breed'].unique().tolist())

print('train label shape:', labels_df.shape)
print('train dataset and test dataset sizes:',len(train_images), len(test_image))
print('total number of dog breeds: ',len(target_labels))
# lets plot distribution of dog breeds in training data
plot_value_count(labels_df, 'breed')

3. Создать структурированную информацию

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

# 1.create class folders
parent_folder = "data"
datasets = ["all_data"]
for i in datasets:
  for j in target_labels:
      os.makedirs(parent_folder + "/" + i + "/" + j,  exist_ok=True)

# all data
for _, name, label in labels_df.itertuples():
  original_path = "/content/train/" + name +".jpg"
  dest_path = "/content/data/all_data/" + label
  shutil.copy(original_path, dest_path)

4. Визуализируйте случайные изображения

Здесь я просто визуализирую тренировочные случайные тренировочные изображения.

all_data_dir = "/content/data/all_data/"

def view_25_random_image(target_dir, target_classes):
  plt.figure(figsize=(18, 12))
  random_images = []
  for i, target_class in enumerate(target_classes):
    # setup the target directory
    target_folder = target_dir + target_class

    # get the random image path
    random_image = random.sample(os.listdir(target_folder),1)
    img = mpimg.imread(target_folder + "/" + random_image[0])
    #print(target_folder + "/" + random_image[0])
    #print(random_image)
    plt.subplot(5,5,i+1)
    plt.imshow(img)
    plt.title(f"{target_class}")
    plt.axis("off");
    random_images.append(img)
    #print(f"image shape: {img.shape}")
  return random_images

random_images = view_25_random_image(target_dir=all_data_dir,
                                target_classes = random.sample(target_labels, 25))

5. Получить данные

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

tf.random.set_seed(42)
IMG_SIZE  = (256,256)
IMG_SHAPE = IMG_SIZE +(3,)
BATCH_SIZE = 32
data_all = tf.keras.preprocessing.image_dataset_from_directory(all_data_dir,
                                                              label_mode = "categorical",
                                                              image_size=IMG_SIZE,
                                                              batch_size=BATCH_SIZE,
                                                               )
print(f"all_data batches: {len(data_all)}")

6. Обучить модель

Я использую несколько методов увеличения данных, таких как RandomFlip, RandomCrop, Randomrotation и т. д. Кроме того, я использую два обратных вызова, например обратный вызов уменьшения скорости обучения, который снижает скорость обучения, когда точность проверки перестает улучшаться, и раннюю остановку, которая останавливает обучение после проверки. точность больше не улучшается. Уменьшение скорости обучения помогает модели медленнее сходиться и, следовательно, потенциально находить лучшее и более универсальное решение. Принимая во внимание, что ранняя остановка помогает модели избежать переобучения и прекратить обучение раньше. Что касается архитектуры модели ML, я использовал предварительно обученную модель EfficientNetB3, а затем точно настроил наши новые данные. EfficientNetB3 — это архитектура сверточной нейронной сети, предложенная в 2019 году Tan et al. которые достигли современной точности при использовании меньшего количества параметров и меньших вычислительных ресурсов, чем другие модели ML.

#data augmentation
data_augmentation_layer = tf.keras.models.Sequential([
                                                preprocessing.RandomFlip("horizontal"),
                                                preprocessing.RandomCrop(height=224, width=224),
                                                preprocessing.RandomRotation(0.2),
                                                preprocessing.RandomZoom(0.1),
                                                preprocessing.RandomContrast(factor=0.1),
                                                ])


# EarlyStopping Callback (stop training in val accuracy not improves)
early_stopping = tf.keras.callbacks.EarlyStopping(monitor="val_accuracy", 
                                                  patience=5,
                                                  restore_best_weights=True)

#ReduceLROnPlateau Callback (Creating learning rate)
reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor="val_accuracy",  
                                                 factor=0.2, # multiply the learning rate by 0.2 (reduce by 5x)
                                                 patience=2,
                                                 verbose=1, # print out when learning rate goes down 
                                                 min_lr=1e-5)

baseline_model = tf.keras.applications.EfficientNetB3(include_top=False)
# trainable freeze
baseline_model.trainable = False
inputs = tf.keras.Input(shape = IMG_SIZE+(3,), name = "input_layer")
#x = data_augmentation_layer(inputs)
x = baseline_model(inputs, training=False) # weights whhich need to stay frozen, stay frozen
x = layers.GlobalAveragePooling2D(name="global_average_pool_layer")(x)
x = layers.Dense(len(data_all.class_names))(x)
outputs = layers.Activation("softmax", dtype=tf.float32)(x)
model_0 = tf.keras.Model(inputs, outputs)

train_size = int(0.9 * len(data_all))
print(train_size)

model_0.compile(optimizer=tf.keras.optimizers.Adam(),
                              loss="categorical_crossentropy",
                              metrics = ["accuracy"])

history_0 = model_0.fit(data_all.take(train_size),
                                      epochs=5,
                                      validation_data = data_all.skip(train_size),
                                      callbacks = [
                                                    early_stopping,
                                                    reduce_lr
                                                  ])

7. Сохраните имена моделей и классов

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

#save model
model_0.save('/content/gdrive/MyDrive/dog-breed-identification/EfficientNetB3_Model.h5')

breed_names = data_all.class_names
breed_names = [n.replace('_',' ').capitalize() for n in breed_names]
print(breed_names[0:3])

#save dog breed names
with open('/content/gdrive/MyDrive/dog-breed-identification/class_names', 'wb') as fp:
    pickle.dump(breed_names, fp)

Б. Streamlit.py

  1. Импортировать библиотеки, предварительно обученные модели и названия классов
import streamlit as st
import tensorflow as tf
from tensorflow.keras.preprocessing.image import load_img
from tensorflow.keras.preprocessing.image import img_to_array
import pickle
import numpy as np
import pathlib
import matplotlib.pyplot as plt
from PIL import Image

IMG_SIZE  = (256, 256)
IMG_SHAPE = IMG_SIZE +(3,)

#1. load model
@st.cache_resource
def load_model():
    return tf.keras.models.load_model('pretrained_models/EfficientNetB3_Model.h5')

loaded_model = load_model()

#2. load class names
with open ('data/class_names', 'rb') as fp:
    class_names = pickle.load(fp)

2. Создать веб-приложение

Я создаю интерактивный веб-интерфейс с помощью скриптов Streamlit. Я создаю заголовок веб-объекта и объекта для загрузки файлов.

st.header("Dog Identification Application")

uploaded_file = st.file_uploader("Upload an image of your dog and identify its breed...", type=['jpg', 'jpeg', 'png'])

3. Сделать прогноз

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

# If an image is uploaded, display it and make a prediction
if uploaded_file is not None:
    img = Image.open(uploaded_file).resize(IMG_SIZE)
    img = np.array(img, dtype=np.float32)
    img_g = np.expand_dims(img, axis=0)
    custom_predict = loaded_model.predict(img_g)

    # Display the uploaded image and the predictions
    # st.write(f"{class_names[np.argmax(custom_predict[0])]}  ({round(np.max(custom_predict[0]*100))}% confidence)")
    if round(np.max(custom_predict[0]*100))>50:
        st.write(f'<p style="font-size:26px; color:green;"> {class_names[np.argmax(custom_predict[0])]}  ({round(np.max(custom_predict[0]*100))}% confidence)</p> ', unsafe_allow_html=True)
    else:
        argsorts = np.argsort(custom_predict[0])
        sorts = np.sort(custom_predict[0])
        st.write(f'<p style="font-size:26px; color:red;"> {class_names[argsorts[-1]]}  ({round(sorts[-1]*100)}% confidence) OR {class_names[argsorts[-2]]}  ({round(sorts[-2]*100)}% confidence)</p> ', unsafe_allow_html=True)

    st.image(img/255,  use_column_width=True)
    st.write('Notes:')
    st.write('1. The model is a first simplest baseline version, the prediction result will be improved in later versions')
    st.write('2. The model can identify up to 120 types of dog breeds')
    option = st.selectbox(
    'All Breeds',
    class_names)

Вы можете визуализировать результат на локальном хосте, выполнив команду bash

streamlit run Streamlit.py

C. Развертывание

Поскольку весь проект создается локально, пришло время развернуть его в Интернете. Этот шаг настолько прост.

  1. Загрузить на GitHub

Сначала мы загружаем следующие файлы/папки:

  • Streamlit.py,
  • предварительно обученные_модели/EfficientNetB3_Model.h5
  • данные/имя_класса
  • требования.txt

в репозитории GitHub.

2. Подключить репозиторий GitHub к Streamlit.

После этого авторизуйтесь на сайте Streamlit, создайте учетную запись и нажмите Новое приложение.

и мы просто следуем инструкциям

IV. Распространенные ошибки и устранение неполадок

Вот некоторые наиболее вероятные ошибки и предлагаемые решения при создании проекта.

  1. Проблема зависимости упаковки: это может произойти, если вы обучаете модель локально, обязательно создайте виртуальную среду Python, а затем установите библиотеки.
  2. Ошибки упаковки: при использовании Google Colab в некоторых версиях Tensorflow возникали проблемы при сохранении модели, поэтому используйте версию 2.9.1 Tesnorflow в Google Colab.
  3. Приложение Streamlit не работает после подключения учетной записи GitHub: в основном из-за отсутствия пакетов. Убедитесь, что у вас есть файл requirements.txt, загруженный на GitHub с определенными пакетами. Streamlit автоматически найдет файл и установит все необходимые пакеты для запуска приложения.

V. Заключение и будущая работа

Таким образом, я разработал простую модель глубокого обучения, которая идентифицирует собак из 120 пород. Кроме того, я разместил модель в Интернете с помощью Streamlit. Модель достигает точности около 94% на данных проверки, что является довольно приличным результатом. Несмотря на то, что модель работает хорошо, у нее есть некоторые недостатки. Например, показатель достоверности модели не откалиброван, что означает, что показатель достоверности не представляет реальную вероятность предсказания. Кроме того, модель знает только 120 пород собак, поэтому не может идентифицировать все породы собак. Кроме того, модель не знает, когда она не знает. Например, если вы загрузите изображение самолета, модель не сможет сказать вам, что это не собака. Я постараюсь решить эти проблемы в более поздней версии приложения.

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

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