Автоматическое обучение с использованием FastAPI, Pytorch и SerpApi

Это часть серии сообщений в блогах, посвященных внедрению искусственного интеллекта. Если вас интересует предыстория этой истории или ее развитие:

На предыдущих неделях мы изучали, как автоматически создать собственный набор данных изображений с помощью SerpApi’s Google Images Scraper API. На этой неделе мы будем использовать эти образы для автоматического обучения сети с помощью простого командного объекта, который будет передан в FastAPI.

Пользовательский CSV-файл

Нам нужно создать собственный CSV-файл с нужными нам изображениями. Для этого мы будем использовать библиотеку pandas. Вот требования:

Нам нужно создать список элементов, которые мы собрали из API-интерфейса SerpApi Google Image Scraper, задать имя создаваемого CSV-документа и определить дробь для обучающих данных. Что подразумевается под дробью здесь просто. Тестовые данные содержат все изображения, которые мы собрали, в то время как обучающие данные будут содержать только их часть. Для этого нам нужно создать объект, который мы можем передать в конечную точку:

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

Он принимает список аргументов, которые мы предоставили, которые представляют собой запросы, которые мы сделали к SerpApi's Google Images Scraper API, и создает CSV из каждого изображения в соответствующей папке. После того, как все изображения готовы, он берет часть выборки с перемешиванием и создает обучающий CSV.
Давайте определим функцию в main.py для вызова такого действия:

Эти классы необходимы для его вызова в main.py
Функция, отвечающая за действие:

Чтобы привести наглядный пример, если вы отправитесь в http://localhost:8000/docs и попробуете /create/ со следующими параметрами:

Вы создадите два CSV-файла в datasets/csv с именами apples_and_oranges.csv и apples_and_oranges_train.csv.

apples_and_oranges.csv будет тестовым CSV, будет упорядоченным, со всеми изображениями и будет выглядеть так:

apples_and_oranges_train.csv будет CSV поезда, будет перемешано, будет содержать 80% изображений и будет выглядеть так:

Эти два будут использоваться для создания элемента набора данных.

Пользовательские тренировочные команды

Нам нужен объект, чтобы указать детали операции обучения, и иметь совместное использование несколькими классами, чтобы избежать циклического импорта:

Давайте разберем элементы в этом объекте:

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

Пользовательский набор данных и пользовательский загрузчик данных

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

Затем давайте инициализируем наш класс набора данных:

Мы используем параметр type, чтобы определить, инициализируем ли мы базу данных train или базу данных test со следующей частью:

Чтобы определить список меток, которые будут использоваться при формировании модели, мы используем следующие строки:

Это дает нам словарь элементов, которые нужно классифицировать, каждый со своим уникальным целым числом:

Мы должны определить определенные преобразования для ввода и метки. Эти преобразования определяют, как они будут преобразованы в тензор для использования в обучении, и какие операции следует применять после их преобразования:

Давайте также определим функцию, которая дает нам количество изображений в данном наборе данных:

Наконец, нам нужно определить, что давать, тензор изображения и тензор метки:

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

Допустим, набор данных является обучающим набором данных, а индекс равен 0:

Это будет наш self.img_labels.iloc[0,0].
Причина, по которой в пути есть test, заключается в том, что мы пока храним все файлы в каталоге test. Однако изображение взято из кадра данных apples_and_oranges_train.csv. Так что это не вызовет проблем.
Следующие строки создадут один горячий вектор из индекса рассматриваемой метки:

Причина, по которой я прокомментировал [0., 0.], заключается в том, что в нашем примере есть 2 метки, а именно Apple и Orange, поэтому размер вектора будет определяться по ним. Мы преобразуем его в массив numpy, чтобы его можно было преобразовать в тензор.
Следующие строки будут использовать библиотеку PIL для чтения и изменения формы изображения до желаемых размеров:

Мы конвертируем изображение в RGB, чтобы получить трехмерный вектор, третий из которых является цветом. Затем мы изменяем его размер, используя метод ANTIALIAS, чтобы он оставался узнаваемым для глаза. Как я уже говорил, этого обычно недостаточно. Но мы пока поступим так.

Теперь идет пользовательский загрузчик данных:

Мы инициализируем в нем self.train_dataloader и self.test_dataloader, используя параметр type, как упоминалось ранее. Затем мы используем функцию DataLoader Pytorch для объявления загрузчика. Размер пакета определяет, сколько изображений будет извлечено при вызове загрузчика данных.
В итерации мы перебираем self.train_dataloader, чтобы получить изображение и соответствующую ему метку, используя пользовательский набор данных изображения, который мы определили при инициализации.
train_features будет пакетом тензоров изображений, которые мы извлекли из набора данных, а train_labels будет пакетом соответствующих меток этих изображений.

Автоматическое обучение

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

Давайте также объявим модель CNN, которую мы хотим использовать:

Здесь следует отметить, что выходной размер n_labels в нашем случае равен 2, поскольку мы классифицируем только между Apple и Orange. Необходимо сделать еще один расчет, полученный из размера встраивания, высоты и ширины изображения, которые я вычислил вручную. В целом, это довольно общая функция для классификации изображений. В следующие недели мы обсудим, как автоматически рассчитать размер, который я рассчитал вручную, а также как добавить дополнительные слои из TrainCommands для дальнейшей автоматизации процесса.
Теперь давайте определим функцию обучения, которая использует пользовательский набор данных, и пользовательский загрузчик данных:

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

Следующая строка объявляет модель (Сверточная нейронная сеть) с помощью обучающих команд:

За создание критерия отвечает следующая строка:

В нашем случае это эквивалентно torch.nn.CrossEntropyLoss().

Следующая строка предназначена для создания оптимизатора с нужными параметрами:

Это эквивалентно torch.optim.SGD(CNN.parameters(), lr=0.001, momentum=0.9)
В следующие недели у нас появится подход к предоставлению необязательных параметров с необязательными именами для заполнения оптимизатора и критерия без ограничений. Но пока этого достаточно.

Наконец, мы инициализируем количество эпох для запуска и имя модели, которую мы сохраним:

В следующих частях мы перебираем эпохи, удаляем потери и вызываем изображение и метку из набора данных с помощью функции enumerate:

Данные будут поступать в виде кортежей:

Затем обнуляем градиенты в оптимизаторе:

Запустите прогноз:

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

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

Затем мы запускаем обратное распространение для автоматического повторного накопления градиентов:

Шагаем оптимизатор:

Затем обновите running_loss:

Следующие строки предназначены для вывода процесса через каждые 5 шагов:

Наконец, мы сохраняем модель в нужном месте:

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

Конечная точка /train/ примет наши команды и автоматически обучит модель для нас.

Теперь, если вы отправитесь на localhost:8000/docs и попробуете наш /train/ со следующими параметрами:

Вы можете наблюдать за процессом обучения из терминала, так как мы объявили функции печати для эпох:

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

Заключение

Я благодарен гениальным людям из SerpApi за то, что они сделали возможным этот пост в блоге, и я благодарен читателю за его внимание. В следующие недели мы обсудим, как сделать некоторые части, упомянутые здесь, более эффективными и более настраиваемыми. Также мы обсудим асинхронную обработку FastAPI для всех процессов и одновременные вызовы SerpApi’s Google Images Scraper API.

Первоначально опубликовано на https://serpapi.com 2 июня 2022 г.