Классификация изображений - одна из старейших и разрабатываемых задач компьютерного зрения и глубокого обучения. Фактически, разработано настолько, что любой, у кого есть некоторый опыт программирования, может собирать свои собственные данные, обучать модели с приличной точностью и развертывать их с помощью нескольких строк кода. Что оказало значительное влияние, так это доступность больших наборов данных, таких как ImageNet и CIFAR10, и инструментов разработки, таких как Pytorch и fastai, которые упрощают создание прототипов моделей и их обучение. Мы можем легко использовать сложные модели, обученные на больших наборах данных, и передать эти возможности другим меньшим наборам данных с аналогичной задачей. Этот подход называется трансферным обучением. Этой осенью я участвую в MOOC fast.ai Live part1 v3 (читайте об этом здесь), и в этом сообщении блога я проиллюстрирую несколько идей из первых двух уроков и покажу, как легко мы можем использовать переносное обучение в обучить остаточную сеть (статья здесь) на настраиваемом наборе данных

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

Для обучения модели нам нужны 3 вещи: данные, архитектура и функция потерь.

Данные:
Набор данных был извлечен из Картинок Google. На нем размещены изображения 14 достопримечательностей Токио. Изображения разделены на папки, и каждая папка представляет собой класс, содержащий свои изображения (этот формат называется форматом ImageNet из-за того, как структурирован набор данных ImageNet)

Поскольку у меня были некоторые несвязанные изображения, нам нужно их очистить. fastai предоставляет отличный виджет для просмотра изображений в Jupyter Notebook и удаления плохих / неправильно помеченных.

Архитектура:
Архитектура, которую я буду использовать для задачи классификации, - это остаточная сеть (Resnet). Это сверточная нейронная сеть (CNN) с остаточными связями. Подробнее об этом позже. Мы будем использовать Resnet, обученный на наборе данных ImageNet.

Функция потерь: поскольку это задача классификации нескольких классов, мы будем использовать потерю перекрестной энтропии. Перекрестная потеря энтропии может быть упрощена до: cross_entropy = sum(-log(y_pred) for y_pred in y_preds), где y_pred - вероятность целевого класса, предсказанная моделью. Это хорошая функция потерь для классификации, потому что, если вероятность правильного класса низкая, значение потерь увеличивается, а если оно велико, значение потерь уменьшается.

  • Первый шаг: начать кодирование
    Теперь, когда мы собрали все части, мы можем взглянуть на код:
###
ImageDataBunch is a fastai class that 
creates an iterable which grabs the batch-sized 
images from disk, does the transformations necessary
(resizing, rotating, zooming,cropping etc.) at every `next` call. 
###
data = ImageDataBunch.from_folder(path, train=".", ds_tfms=get_transforms(),valid_pct=0.1, size=224, num_workers=4)
#Create a resnet34 model(i.e a resnet with 34 layers)
learn = create_cnn(data, models.resnet34, metrics=error_rate)
#train the last few layers
learn.fit_one_cycle(4)

#Train again after freeing all the weights
learn.unfreeze()
learn.fit_one_cycle(4, max_lr=slice(1e-5,2e-4))

Таким образом, у нас есть модель, которая классифицирует 14 различных точек в Токио с частотой ошибок 7,6%. Это было бы невозможно, если бы мы не использовали предварительно активированные / предварительно тренированные веса.

Вот тетрадь со всеми вышеперечисленными процессами подробно.

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

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

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

Слишком высокая / слишком низкая скорость обучения:
Если скорость обучения слишком высока, потери при проверке начинают расти, а если они слишком низкие, потери уменьшаются очень медленно. К счастью, fastai имеет функцию, которая позволяет нам выполнить начальный запуск с разной скоростью обучения, чтобы увидеть, на каком интервале потери уменьшаются быстрее всего.

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

Но фастай и Пайторч позволили нам открыть эти черные ящики и посмотреть, что внутри.

# Opening up the Black Box:
class ResLayer(nn.Module):
 “Resnet style `ResLayer`”
 def __init__(self, ni:int):
 “create ResLayer with `ni` inputs”
 super().__init__()
 self.conv1=conv_layer(ni, ni//2, ks=1)
 self.conv2=conv_layer(ni//2, ni, ks=3)
def forward(self, x): 
 return x + self.conv2(self.conv1(x))
#Credit: Code from fastai 1.0 lib

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

  • Второй шаг: интуиция
    Какие эти функции усвоила модель?
    Свойства, которым усвоила модель, на самом деле более значимы, чем просто набор чисел. Визуализируя особенности слоев, мы видим, что результаты кажутся довольно очевидными. Первоначальные слои содержат основную информацию (например, края, углы, узоры), а последние содержат более конкретную информацию (например, нос, глаза и т. Д., В зависимости от того, на чем они были обучены).

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

Почему мы добавляем промежуточные выходы?
Интуиция в Resnets заключается в том, что, поскольку сложнее учиться на глубоких полносвязных слоях, мы используем несколько более мелких слоев и переносим промежуточные функции. Используя остаток, оставленный промежуточными слоями, отдельные слои могут учиться на предыдущих слоях, создавая сеть, которую не только легче обучать, но и захватывать сложные объекты.

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

Превращаем модель в веб-приложение:

Теперь, когда у нас есть обученная модель, мы можем использовать ее в приложении. Приложение было создано с использованием Starlette, который представляет собой фреймворк / инструментарий ASGI. Вот пример простого приложения, которое принимает URL-адрес изображения и классифицирует изображение:

Тестирование веб-приложения на этом изображении:

Предоставлено:
Идеи основаны на уроках fast.ai part1 v3. Код, используемый для извлечения изображений из Google Images, обучения и развертывания модели, также был заимствован из материалов курса.