фото для обучения нейронной сети
Нейронные сети в картинках: от одного нейрона до глубоких архитектур
Многие материалы по нейронным сетям сразу начинаются с демонстрации довольно сложных архитектур. При этом самые базовые вещи, касающиеся функций активаций, инициализации весов, выбора количества слоёв в сети и т.д. если и рассматриваются, то вскользь. Получается начинающему практику нейронных сетей приходится брать типовые конфигурации и работать с ними фактически вслепую.
В статье мы пойдём по другому пути. Начнём с самой простой конфигурации — одного нейрона с одним входом и одним выходом, без активации. Далее будем маленькими итерациями усложнять конфигурацию сети и попробуем выжать из каждой из них разумный максимум. Это позволит подёргать сети за ниточки и наработать практическую интуицию в построении архитектур нейросетей, которая на практике оказывается очень ценным активом.
Иллюстративный материал
Популярные приложения нейросетей, такие как классификация или регрессия, представляют собой надстройку над самой сетью, включающей два дополнительных этапа — подготовку входных данных (выделение признаков, преобразование данных в вектор) и интерпретацию результатов. Для наших целей эти дополнительные стадии оказываются лишними, т.к. мы смотрим не на работу сети в чистом виде, а на некую конструкцию, где нейросеть является лишь составной частью.
Фреймворк
Для демонстрации конфигураций и результатов предлагаю взять популярный фреймворк Keras, написанный на Python. Хотя вы можете использовать любой другой инструмент для работы с нейросетями — чаще всего различия будут только в наименованиях.
Самая простая нейросеть
Самой простой из возможных конфигураций нейросетей является один нейрон с одним входом и одним выходом без активации (или можно сказать с линейной активацией f(x) = x):
N.B. Как видите, на вход сети подаются два значения — x и единица. Последняя необходима для того, чтобы ввести смещение b. Во всех популярных фреймворках входная единица уже неявно присутствует и не задаётся пользователем отдельно. Поэтому здесь и далее будем считать, что на вход подаётся одно значение.
Несмотря на свою простоту эта архитектура уже позволяет делать линейную регрессию, т.е. приближать функцию прямой линией (часто с минимизацией среднеквадратического отклонения). Пример очень важный, поэтому предлагаю разобрать его максимально подробно.
Как видите, наша простейшая сеть справилась с задачей приближения линейной функции линейной же функцией на ура. Попробуем теперь усложнить задачу, взяв более сложную функцию:
Опять же, результат вполне достойный. Давайте посмотрим на веса нашей модели после обучения:
Первое число — это вес w, второе — смещение b. Чтобы убедиться в этом, давайте нарисуем прямую f(x) = w * x + b:
Усложняем пример
Хорошо, с приближением прямой всё ясно. Но это и классическая линейная регрессия неплохо делала. Как же захватить нейросетью нелинейность аппроксимируемой функции?
Давайте попробуем накидать побольше нейронов, скажем пять штук. Т.к. на выходе ожидается одно значение, придётся добавить ещё один слой к сети, который просто будет суммировать все выходные значения с каждого из пяти нейронов:
И… ничего не вышло. Всё та же прямая, хотя матрица весов немного разрослась. Всё дело в том, что архитектура нашей сети сводится к линейной комбинации линейных функций:
f(x) = w1′ * (w1 * x + b1) +… + w5′ (w5 * x + b5) + b
Т.е. опять же является линейной функцией. Чтобы сделать поведение нашей сети более интересным, добавим нейронам внутреннего слоя функцию активации ReLU (выпрямитель, f(x) = max(0, x)), которая позволяет сети ломать прямую на сегменты:
Максимальное количество сегментов совпадает с количеством нейронов на внутреннем слое. Добавив больше нейронов можно получить более точное приближение:
Дайте больше точности!
Уже лучше, но огрехи видны на глаз — на изгибах, где исходная функция наименее похожа на прямую линию, приближение отстаёт.
В качестве стратегии оптимизации мы взяли довольно популярный метод — SGD (стохастический градиентный спуск). На практике часто используется его улучшенная версия с инерцией (SGDm, m — momentum). Это позволяет более плавно поворачивать на резких изгибах и приближение становится лучше на глаз:
Усложняем дальше
Синус — довольно удачная функция для оптимизации. Главным образом потому, что у него нет широких плато — т.е. областей, где функция изменяется очень медленно. К тому же сама функция изменяется довольно равномерно. Чтобы проверить нашу конфигурацию на прочность, возьмём функцию посложнее:
Увы и ах, здесь мы уже упираемся в потолок нашей архитектуры.
Дайте больше нелинейности!
Давайте попробуем заменить служивший нам в предыдущих примерах верой и правдой ReLU (выпрямитель) на более нелинейный гиперболический тангенс:
Инициализация весов — это важно!
Приближение стало лучше на сгибах, но часть функции наша сеть не увидела. Давайте попробуем поиграться с ещё одним параметром — начальным распределением весов. Используем популярное на практике значение ‘glorot_normal’ (по имени исследователя Xavier Glorot, в некоторых фреймворках называется XAVIER):
Уже лучше. Но использование ‘he_normal’ (по имени исследователя Kaiming He) даёт ещё более приятный результат:
Как это работает?
Давайте сделаем небольшую паузу и разберёмся, каким образом работает наша текущая конфигурация. Сеть представляет из себя линейную комбинацию гиперболических тангенсов:
f(x) = w1′ * tanh(w1 * x + b1) +… + w5′ * tanh(w5 * x + b5) + b
На иллюстрации хорошо видно, что каждый гиперболический тангенс захватил небольшую зону ответственности и работает над приближением функции в своём небольшом диапазоне. За пределами своей области тангенс сваливается в ноль или единицу и просто даёт смещение по оси ординат.
За границей области обучения
Давайте посмотрим, что происходит за границей области обучения сети, в нашем случае это [-3, 3]:
Как и было понятно из предыдущих примеров, за границами области обучения все гиперболические тангенсы превращаются в константы (строго говоря близкие к нулю или единице значения). Нейронная сеть не способна видеть за пределами области обучения: в зависимости от выбранных активаторов она будет очень грубо оценивать значение оптимизируемой функции. Об этом стоит помнить при конструировании признаков и входных данный для нейросети.
Идём в глубину
До сих пор наша конфигурация не являлась примером глубокой нейронной сети, т.к. в ней был всего один внутренний слой. Добавим ещё один:
Можете сами убедиться, что сеть лучше отработала проблемные участки в центре и около нижней границы по оси абсцисс:
N.B. Слепое добавление слоёв не даёт автоматического улучшения, что называется из коробки. Для большинства практических применений двух внутренних слоёв вполне достаточно, при этом вам не придётся разбираться со спецэффектами слишком глубоких сетей, как например проблема исчезающего градиента. Если вы всё-таки решили идти в глубину, будьте готовы много экспериментировать с обучением сети.
Количество нейронов на внутренних слоях
Просто поставим небольшой эксперимент:
Начиная с определённого момента добавление нейронов на внутренние слои не даёт выигрыша в оптимизации. Неплохое практическое правило — брать среднее между количеством входов и выходов сети.
Создаём нейронную сеть InceptionV3 для распознавания изображений
Привет, Хабр! Под катом пойдёт речь о реализации свёрточной нейронной сети архитектуры InceptionV3 с использованием фреймворка Keras. Статью я решил написать после ознакомления с туториалом «Построение мощных моделей классификации с использованием небольшого количества данных». С одобрения автора туториала я немного изменил содержание своей статьи. В отличие от предложенной автором нейронной сети VGG16, мы будем обучать гугловскую глубокую нейронную сеть Inception V3, которая уже предустановлена в Keras.
1. Данные:
В нашем примере мы будем использовать изображения с соревнования по машинному обучению под названием «Dogs vs Cats» на kaggle.com. Данные будут доступны после регистрации. Набор включает в себя 25000 изображений: 12500 кошек и 12500 собак. Класс 1 соответствует собакам, класс 0- кошкам. После загрузки архивов, разместите по 1000 изображений каждого класса в директориях следующим образом:
Вам ничего не мешает пользоваться всем набором данных. Я, также как и автор оригинальной статьи, решил использовать ограниченную выборку, чтобы проверить эффективность работы сети с малым набором изображений.
У нас есть три проблемы:
При ограниченном объёме изображения высока вероятность переобучения. Для борьбы с этим нужно:
2. Создадим модель InceptionV3, загрузим в неё изображения, сохраним их:
В библиотеке Keras находится несколько подготовленных нейронных сетей.
include_top: включать или не включать верхнюю часть сети, которая представляет из полносвязный слой с 1000 выходов.
Нам он без надобности, поэтому ставим: include_top=False;
weights: загружать/не загружать обученные веса. Если None, веса инициализируются случайно. Если «imagenet», будут загружены веса, тренированные на данных ImageNet.
В нашей модели веса нужны, поэтому weights=«imagenet»;
input_tensor: данный аргумент удобен, если мы используем слой Input для нашей модели.
Мы его не будем трогать.
input_shape : в этом аргументе мы задаём размер нашего изображения. Его указывают, если отсоединяется верхний слой (include_top=False). Если мы загрузили модель с верхним слоем, сто размер изображения должен быть только (3, 299, 299).
Мы убрали верхний слой и хотим анализировать изображения меньшего размера (3, 150, 150). Поэтому кажем: input_shape=()
Создаём нашу модель:
Теперь сделаем аугментацию данных. Для этого в Keras предусмотрены так называемые ImageDataGenerator. Они будут брать изображения прямо из папок и проводить над ними все необходимые трансформации.
Хочу выделить важный момент. Мы указали shuffle=False. То есть, изображения из разных классов не будут перемешиваться. Сначала будут поступать изображения из первой папки, а когда они все закончатся, из второй. Зачем это нужно, увидите позже.
Прогоним аугментированные изображения через обученную Inception и сохраним вывод в виде numpy массивов:
Процесс займёт некоторое время.
3. Создадим верхнюю часть модели, загрузим в неё данные, сохраним их:
В оригинальном посте автор использовал один слой сети с 256 нейронами, однако я буду использовать два слоя по 64 нейрона в каждом и Dropout слой со значением 0.5. Это изменение я был вынужден внести из-за того, когда я обучал готовую модель (которую мы сделаем в следующем шаге), мой компьютер зависал и перезагружался.
Загрузим массивы:
Обратите внимание, ранее мы указывали shuffle=False. А теперь мы легко укажем labels. Поскольку в каждом классе у нас 2000 изображений и все изображения поступали по очереди, у нас будет по 1000 нулей и 1000 единиц для тренировочной и для тестовой выборок.
Создадим модель FFN сети, скомпилируем её:
Загрузим в неё наши массивы:
Теперь мы загружаем данные не из папок, поэтому используем обычный метод fit.
Оценим точность модели:
Наша модель удовлетворительно справляется со своей задачей. Но принимает она только numpy массивы. Это нас не устраивает. Для того, чтобы получить полноценную модель, которая принимает на вход изображения, мы соединим две наши модели и обучим их ещё раз.
4. Создадим итоговую модель, загрузим в неё аугментированные данные, сохраним веса:
Загрузим в неё веса:
Скажу честно, я не заметил никакой разницы между эффективностью обучения модели с загрузкой весов или без неё. Но оставил этот раздел, потому что он описывает, как загрузить веса в определённые слои по имени (by_name=True).
Заблокируем с 1 по 205 слои Inception:
Скомпилируем модель:
Сделаем так, чтобы в процессе обучения сохранялись только веса с наибольшей точностью на тестовой выборке:
Создадим новые генераторы изображений для обучения полной модели. Мы будем трансформировать только учебную выборку. Тестовую трогать не будем.
pred_generator мы используем позже для демонстрации работы модели.
Загрузим изображения в модель:
У меня на каждую эпоху уходило по 210-220 секунд. 200 эпох обучения заняли около 12 часов.
5. Оценим точность модели
Вот нам и пригодился pred_generator. Обратите внимание, val_samples должны соответствовать величине batch_size в генераторе!
Точность 91,7%. Учитывая ограниченность выборки, возьму на себя смелость сказать, что это неплохая точность.
Проиллюстрируем работу модели
Просто смотреть на % правильных ответов и величину ошибки нам не интересно. Давайте посмотрим, сколько правильных и неправильных ответов дала модель для каждого класса:
pred_generator.next() удобная штука. Она загружает изображения в переменную и присваивает лейблы.
Количество изображений каждого класса будет различным при каждой генерации:
Сколько изображений каждого класса модель предсказала правильно?
pd.crosstab(labels,rounded_pred)
Col_0 | 0.0 | 1.0 |
---|---|---|
Row_0 | ||
0.0 | 47 | 4 |
1.0 | 8 | 41 |
Для модели было загружено 100 случайных изображений: 51 изображение кошек и 49 собак. Из 51 кошки модель правильно распознала 47. Из 50 собак правильно были распознаны 41. Общая точность модели на этой узкой выборке составила 88%.
Посмотрим, какие фотографии были распознаны неправильно:
Синие числа-истинный класс изображений. Красные числа- предсказанные моделью (если красное число меньше 0.5, модель считает, что на фото кот, если больше 0.5, то собака). Чем больше число приближается к нулю, тем сеть больше уверена, что перед ней кот. Интересно, что много ошибок с изображениями собак содержат породы небольшого размера или щенков.
Посмотрим первые 20 изображений, которые модель предсказала правильно:
Видно, что модель прилично справляется с задачей распознавания изображений на сравнительно небольших выборках.
Я надеюсь, что пост был вам полезен. С удовольствием выслушаю ваши вопросы или пожелания.
Подготовка данных для настройки и обучения нейросети по детекции объектов
Подготовка данных для настройки и обучения нейросети по детекции объектов.
Продолжаем тему детекции объектов. Для того, чтобы алгоритм различал объекты или был способен выявлять отклонения в процессе, его необходимо обучить на наборе данных, который соответствует общей картине анализируемых видеоматериалов. Подготовка обучающего набора является очень трудоемким процессом, от которого зависит как скорость обучения, так и качество работы алгоритма. Рассмотрим применение нескольких инструментов, которые помогут правильно подготовить данные.
Первый этап подготовки заключается в составлении первоначального набора данных. Как правило, чем больше объем данных и выше разнообразие анализируемого объекта, тем выше качество алгоритма. Если предполагается анализ видео с видеорегистраторов, то составление набора данных предпочтительней делать скрин-снимками с имеющихся видеоматериалов, т.к. в данном случае потребуется меньше данных и меньше времени на переобучение. В других случаях, набор данных составляется с учетом угла обзора, с которого запечатлен объект, его размера и т.д., насколько объекты с обучающего набора похожи на объекты с материалов, которые будут анализироваться алгоритмом. Набор данных необходимо составлять из различных вариаций, т.е. на которых будет отображен как один объект, так и несколько, например, человек и телефон в одном кадре.
Если составление набора данных осуществлялось при помощи фотоаппарата или телефона, вероятнее всего изображения будут иметь высокое разрешение, что негативно влияет на процесс обучения. Основной проблемой при высоком разрешении изображений будет ошибка «Out of memory», даже при малом значении параметра batch_size. Чтобы уменьшить вероятность возникновения данной ошибки необходимо уменьшить размер изображений. В этом нам поможет программа IrfanView. Данная программа позволяет пакетно обрабатывать изображения, т.е. нужно лишь указать путь к исходными изображениям, задать параметры преобразования и запустить процесс преобразования. В результате получим «облегченные» изображения в указанной папке. Для этого в стартовом окне программы нажмите клавишу B (русская раскладка «И») или во вкладке File выберите пункт «Batch Conversion/Rename», появится окно следующего вида:
Далее нам необходимо:
Таким образом, если исходное изображение было 4160 х 3120, то результирующее изображение станет 416 х 312 пикселей.
Формирование обучающей выборки достаточно утомительный процесс, будь то формирование скрин-снимков из видеоматериалов или съемка на фотоаппарат и телефон. Чтобы в разы увеличить объем набора данных применяют различные методы аугментации. Разберем несколько таких методов, реализованных в языке программирования Python с помощью библиотеки Pillow. Одним из самых популярных методов аугментации является «зеркальное отражение».
Для начала импортируем необходимые библиотеки:
Укажем путь расположения директории с исходными изображениями и путь для результата преобразования:
Напишем функцию для преобразования изображения в «зеркальное отражение»:
Еще одним популярным методом аугментации является поворот изображения. Стоит учитывать при полноразмерных объектах, что при данном методе аугментации теряется часть изображения. Напишем функцию для поворота изображения:
Значение (-30) – это угол поворота изображения, который подбирается исходя из типа объекта. Например, поворот на 180 градусов, если на изображении телефон, то это неплохой вариант аугментации, но если на изображении человек, то 180 градусов для данного изображения плохой пример, т.к. навряд ли вы встретите видео, на котором люди ходят вверх ногами.
И, наконец, проведем 2 типа аугментации для каждого изображения:
Пример аугментации «зеркальное отражение»:
Пример аугментации «поворот изображения»:
Существует множество различных методов аугментации (добавление шума, изменение контрастности и т.д.), все зависит от типа обрабатываемых данных. Таким образом, применив два метода аугментации, мы увеличили размер обучающих данных в три раза. Формирование обучающего набора завершено, осталось провести разметку объектов на изображениях.
Для разметки будем использовать программу LabelImage, так как на мой взгляд она достаточно проста в использовании и многофункциональна. Данная программа способна возвращать результат разметки в двух форматах (PascalVOC и YOLO). Первый формат представлен в виде XML – файла, в котором обозначены параметры изображения, а также координаты прямоугольников, которые описывают местоположение объектов на изображении:
Такой XML – файл формируется для каждого изображения, в котором описаны все размеченные объекты. Имя файла разметки совпадает с именем изображения. Формат YOLO представлен в виде текстового файла, в котором обозначен класс объекта и координаты прямоугольников, описанные значениями x,y,w,h в нормализованном виде:
Помимо текстового файла с разметкой для формата YOLO, также формируется файл classes.txt, в котором указаны имена объектов. Порядковые номера имен объектов зависят от порядка появления при разметке изображений. Стоит отметить, что файл classes.txt может быть перезаписан при повторном открытии приложения, поэтому необходимо следить за последовательностью внесения имен в программе, в противном случае произойдет несогласованность координат. Разметка объектов происходит в следующей последовательности:
Нейронная сеть с использованием TensorFlow: классификация изображений
Привет, Хабр! Представляю вашему вниманию перевод статьи «Train your first neural network: basic classification».
Это руководство по обучению модели нейронной сети для классификации изображений одежды, таких как кроссовки и рубашки. Для создания нейронной сети используем python и библиотеку TensorFlow.
Установка TensorFlow
Для работы нам понадобятся следующие библиотеки:
Официальное руководство по установке TensorFlow (на англ.)
Запускаем Jupyter. Для запуска в командной строке пишем jupyter notebook.
Начало работы
В этом руководстве используется набор данных Fashion MNIST, который содержит 70 000 изображений в оттенках серого в 10 категориях. На изображениях показаны отдельные предметы одежды с низким разрешением (28 на 28 пикселей):
Мы будем использовать 60 000 изображений для обучения сети и 10 000 изображений, чтобы оценить, насколько точно сеть научилась классифицировать изображения. Вы можете получить доступ к Fashion MNIST непосредственно из TensorFlow, просто импортировав и загрузив данные:
Загрузка набора данных возвращает четыре массива NumPy:
Изображения представляют собой NumPy массивы 28×28, значения пикселей которых находятся в диапазоне от 0 до 255. Метки (labels) представляют собой массив целых чисел от 0 до 9. Они соответствуют классу одежды:
Метка | Класс |
0 | T-shirt (Футболка) |
1 | Trouser (Брюки) |
2 | Pullover (Свитер) |
3 | Dress (Платье) |
4 | Coat (Пальто) |
5 | Sandal (Сандали) |
6 | Shirt (Рубашка) |
7 | Sneaker (Кроссовки) |
8 | Bag (Сумка) |
9 | Ankle boot (Ботильоны) |
Имена классов не включены в набор данных, поэтому прописываем сами:
Исследование данных
Рассмотрим формат набора данных перед обучением модели.
Предварительная обработка данных
Перед подготовкой модели данные должны быть предварительно обработаны. Если вы проверите первое изображение в тренировочном наборе, вы увидите, что значения пикселей находятся в диапазоне от 0 до 255:
Мы масштабируем эти значения до диапазона от 0 до 1:
Отобразим первые 25 изображений из тренировочного набора и покажем имя класса под каждым изображением. Убедимся, что данные находятся в правильном формате.
Построение модели
Построение нейронной сети требует настройки слоев модели.
Основным строительным блоком нейронной сети является слой. Большая часть глубокого обучения состоит в объединении простых слоев. Большинство слоев, таких как tf.keras.layers.Dense, имеют параметры, которые изучаются во время обучения.
Первый слой в сети tf.keras.layers.Flatten преобразует формат изображений из 2d-массива (28 на 28 пикселей) в 1d-массив из 28 * 28 = 784 пикселей. У этого слоя нет параметров для изучения, он только переформатирует данные.
Следующие два слоя это tf.keras.layers.Dense. Это плотно связанные или полностью связанные нейронные слои. Первый слой Dense содержит 128 узлов (или нейронов). Второй (и последний) уровень — это слой с 10 узлами tf.nn.softmax, который возвращает массив из десяти вероятностных оценок, сумма которых равна 1. Каждый узел содержит оценку, которая указывает вероятность того, что текущее изображение принадлежит одному из 10 классов.
Скомпилирование модели
Прежде чем модель будет готова к обучению, ей потребуется еще несколько настроек. Они добавляются во время этапа компиляции модели:
Обучение модели
Обучение модели нейронной сети требует следующих шагов:
Чтобы начать обучение, вызовите метод model.fit:
При моделировании модели отображаются показатели потерь (loss) и точности (acc). Эта модель достигает точности около 0,88 (или 88%) по данным обучения.
Оценка точности
Сравним, как модель работает в тестовом наборе данных:
Оказывается, точность в тестовом наборе данных немного меньше точности в тренировочном наборе. Этот разрыв между точностью обучения и точностью тестирования является примером переобучения. Переобучение — это когда модель машинного обучения хуже работает с новыми данными, чем с данными обучения.
Прогнозирование
Используем модель для прогнозирования некоторых изображений.
Здесь модель предсказала метку для каждого изображения в тестовом наборе. Давайте посмотрим на первое предсказание:
Предсказание представляет собой массив из 10 чисел. Они описывают «уверенность» модели в том, что изображение соответствует каждому из 10 разных предметов одежды. Мы можем видеть, какая метка имеет наибольшее доверительное значение:
Таким образом, модель наиболее уверенна в том, что это изображение — Ankle boot (Ботильоны), или class_names [9]. И мы можем проверить тестовую метку, чтобы убедиться, что это правильно:
Напишем функции для визуализации этих предсказаний
Давайте посмотрим на 0-е изображение, предсказания и массив предсказаний.
Построим несколько изображений с их прогнозами. Правильные метки прогноза — синие, а неправильные метки прогноза — красные. Обратите внимание, что это может быть неправильно, даже когда он очень уверен.
Наконец, используем обученную модель, чтобы сделать предсказание об одном изображении.
Модели tf.keras оптимизированы для того, чтобы делать прогнозы на пакеты (batch) или коллекции (collection). Поэтому, хотя мы используем одно изображение, нам нужно добавить его в список:
Прогноз для изображения:
Как и прежде, модель предсказывает метку 9.
Если есть вопросы, пишите в комментариях или в личные сообщения.