Туториал: нейронная сеть на JavaScript в 30 строк кода

12 декабря 2018

Туториал: нейронная сеть на JavaScript в 30 строк кода

В этой статье будет показано, как создать и обучить нейронную сеть на JavaScript, используя Synaptic.js. Этот пакет позволяет реализовывать глубокое обучение в Node.js и в браузере. Будет создана простейшая возможная…

В этой статье будет показано, как создать и обучить нейронную сеть на JavaScript, используя Synaptic.js. Этот пакет позволяет реализовывать глубокое обучение в Node.js и в браузере. Будет создана простейшая возможная нейронная сеть — та, которой удается выполнить XOR операцию.

Перевод статьи «How to create a Neural Network in JavaScript in only 30 lines of code». Автор — Per Harald Borgen. Ссылка на оригинал — в подвале статьи. 

Вы можете видеть созданный мною интерактивный Scrimba туториал:

нейронная сеть на javascript

В Scribma туториале вы можете поиграться с сетью. Но перед тем, как посмотреть на код, давайте вспомним основы нейронных сетей.

Нейроны и синапсы

Самый первый строительный элемент любой нейронной сети — нейрон. Он похож на функцию — берет несколько входных значений и возвращает результат алгебраических действий над ними.

Существует множество типов нейронов. Наша сеть использует нейрон с сигмоидной функцией активации, которая принимает любое число и трансформирует его в значение, лежащее в диапазоне от 0 до 1.

Круг ниже показывает сигмоидный нейрон. На входе — 5, на выходе — 1. Стрелочки называются синапсами, они соединяют нейрон с другими слоями в нейросети.

сигмоидный нейрон

Почему красное число это 5? Это сумма из трех синапсов, которые соединены с нейроном. Это показано тремя стрелочками слева от нейрона. Поясню сказанное.

Слева можно видеть два плюс одно значения. Зеленые числа — значения 1 и 0. Параметр сдвига -2 — коричневое число. Сначала входные числа умножаются на соответствующие им веса 7 и 3 соответственно. Значения весов представляют синие числа.

Наконец мы добавляем параметр сдвига и получаем итоговой результат 5 — красное число. Это и есть вход для нашего искусственного нейрона.

веса и смещения - нейронная сеть JS

Такак мы работаем с сигмоидным нейроном, который переводит любое входное значение на отрезок от 0 до 1, выход преобразуется в 1.

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

нейронная сеть

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

Во время тренировки мы просто показываем сети большое количество примеров — рукописных символов или просто картинок. На основе этого сеть предсказывает ответ.

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

Подробное техническое рассмотрение обратного распространения ошибки.

Код нейросети JavaScript

Вы получили базовое представление о нейронных сетях, теперь самое время посмотреть на реализацию. Первое, что необходимо сделать, это создать слои. Это делается при помощи функции new Layer в synaptic. Число в скобках определяет, сколько каждый слой должен содержать нейронов.

Если вам трудно представить, что такое слой, посмотрите еще на скринкаст.

const { Layer, Network } = window.synaptic;
var inputLayer = new Layer(2);
var hiddenLayer = new Layer(3);
var outputLayer = new Layer(1);

Далее соединяем эти слои вместе и создаем инстанс новой сети, как показано ниже:

inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
var myNetwork = new Network({
 input: inputLayer,
 hidden: [hiddenLayer],
 output: outputLayer
});

У нас получилась сеть с 2-3-1 нейронами в входном, скрытом и выходном слое, соответственно. Архитектура этой сети показана ниже:

XOR функция

Теперь давайте обучим нашу сеть:

// train the network - learn XOR
var learningRate = .3;
for (var i = 0; i < 20000; i++) {
  // 0,0 => 0
  myNetwork.activate([0,0]);
  myNetwork.propagate(learningRate, [0]);
  // 0,1 => 1
  myNetwork.activate([0,1]);
  myNetwork.propagate(learningRate, [1]);
  // 1,0 => 1
  myNetwork.activate([1,0]);
  myNetwork.propagate(learningRate, [1]);
  // 1,1 => 0
  myNetwork.activate([1,1]);
  myNetwork.propagate(learningRate, [0]);
}

Запускаем сеть 20 000 раз. Каждый раз распространяемся в прямом и обратном направлении четыре раза, проходя через четыре возможных входа для этой сети: [0,0], [0,1], [1,0], [1,1].

Начинаем с команды myNetwork.activate([0,0]), где [0,0] — данные, которые посылаются в сеть. Это прямое распространение, которое также называется активизацией сети. После каждого прямого распространения требуется сделать и обратное, тогда сеть обновит свои веса и свдиг.

Обратное распротстранение осуществляется командой myNetwork.propagate(learningRate, [0]). Параметр learningRate — константа, которая говорит сети, насколько каждый раз нужно изменять веса. Второй параметр — 0 представляет корректный ответ для заданного входа [0,0].

Далее сеть сравнивает свое предсказание с правильной меткой. На этом шаге определяется, было ли предсказание сделано правильно.

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

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

console.log(myNetwork.activate([0,0])); 
-> [0.015020775950893527]
console.log(myNetwork.activate([0,1]));
->[0.9815816381088985]
console.log(myNetwork.activate([1,0]));
-> [0.9871822457132193]
console.log(myNetwork.activate([1,1]));
-> [0.012950087641929467]

Если округлить эти значения до ближайшего целого числа, получим корректные ответы для XOR операций!

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

Наконец, когда выучите что-нибудь новое, обязательно поделитесь звоими знаниями с помощью скринкаста Scrimba и написания статьи!

Сверточная нейронная сеть на PyTorch: пошаговое руководство

26 октября 2018

Сверточная нейронная сеть на PyTorch: пошаговое руководство

В предыдущем вводном туториале по нейронным сетям была создана трехслойная архитектура для классификации рукописных символов датасета MNIST. В конце туториала была показана точность приблизительно 86%. Для простого датасета, как MNIST,…

В предыдущем вводном туториале по нейронным сетям была создана трехслойная архитектура для классификации рукописных символов датасета MNIST. В конце туториала была показана точность приблизительно 86%. Для простого датасета, как MNIST, это плохое качество. Дальнейшая оптимизация смогла улучшить результат плотно соединенной сети до 97-98% точности. Это уже намного лучше, но всё еще не достаточно для MNIST. Требуется более современный метод, который может действительно называться глубоким обучением. В данном туториале представлен такой метод — сверточная нейронная сеть (Convolutional Neural Network, CNN), который достигает высоких результатов в задачах классификации картинок. В частности, будет рассмотрена и теория, и практика реализации CNN при помощи PyTorch.

PyTorch — набирающий популярность мощный фреймворк глубокого машинного обучения. Его особенность — он чувствует себя как дома в Python, а прототипирование осуществляется очень быстро. Туториал не предполагает больших знаний PyTorch. Весь код для сверточной нейронной сети, рассмотренной здесь, находится в этом репозитории. Давайте приступим.

Особенности CNN

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

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

Принцип работы CNN

Свертка — фактически главное, что необходимо понять о сверточных нейронных сетях. Этот замысловатый математический термин нужен для движущегося окна или фильтра по исследуемому изображению. Перемещающееся окно применяется к определенному участку узлов, как показано ниже. Где примененный фильтр — ( 0.5 * значение в узле):

 

 

 

 

 

 

 

На диаграмме показаны только два выходных значения, каждое из которых отображает входной квадрат размера 2×2. Вес отображения для каждого входного квадрата, как ранее упоминалось, равен 0.5 для всех четырех входов (inputs). Поэтому выход может быть посчитан так:

 

 

 

 

 

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

 

 

 

 

 

 

 

 

 

 

 

Первое положение связей движущегося фильтра показано синей линией, второе — зеленой. Веса для каждых таких соединений равны 0.5.

Вот несколько вещей в сверточном шаге, которые ускоряют процесс тренировки, сокращая количество параметров, весов:

  • Редкие связи — не каждый узел в первом (входном) слое соединен с каждым узлом во втором слое. Этим отличается архитектура CNN от полностью связанной нейронной сети, где каждый узел соединен со всем другими в следующем слое.
  • Постоянные параметры фильтра. Другими словами, при движении фильтра по изображению одинаковые веса применяются для каждого 2 х 2 набора узлов. Каждый фильтр может быть обучен для выполнения специфичных трансформаций входного пространства. Следовательно, каждый фильтр имеет определенный набор весов, которые применяются для каждой операции свертки. Этот процесс уменьшает количество параметров. Нельзя говорить, что любой вес постоянен внутри отдельного фильтра. В примере выше веса были [0.5, 0.5, 0.5, 0.5], но ничего не мешало им быть и [0.25, 0.1, 0.8, 0.001]. Выбор конкретных значений зависит от обучения каждого фильтра.

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

Следующий шаг в структуре CNN — прохождение выхода операции свертки через нелинейную активационную функцию. Речь идет о некотором подвиде ReLU, который обеспечивает известное нелинейное поведение этой нейронной сети.

Процесс, использующийся в сверточном блоке, называется признаковым отображением (feature mapping). Название основано на идее, что каждый сверточный фильтр может быть обучен для поиска различных признаков в изображении, которые затем могут быть использованы в классификации. Перед разговором о следующем свойстве CNN, называемом объединением (pooling), рассмотрим идею признакового отображения и каналов.

Отображение признаков и мультиканальность

Поскольку веса отдельных фильтров остаются постоянными, будучи примененными на входных узлах, они могут обучаться выбирать определенные признаки из входных данных. В случае изображений, архитектура способна учиться различать общие геометрические  объекты — линии, грани и другие формы исследуемого объекта. Вот откуда взялось определение признакового отображения. Из-за этого любой сверточный слой нуждается в множестве фильтров, которые тренируются детектировать различные признаки. Следовательно, необходимо дополнить предыдущую диаграмму движущегося фильтра следующим образом:

 

 

 

 

 

 

 

Теперь в правой части рисунка можно видеть несколько сложенных (stacked) выходов операции свертки. Их несколько, потому что существует несколько обучаемых фильтров, каждый из которых производит собственный 2D выход (в случае 2D изображения). Такое множество фильтров часто в глубоком обучении часто называют каналами. Каждый канал должен обучаться для выделения на изображении определенного ключевого признака. Выход сверточного слоя для черно-белого изображения, как в датасете MNIST, имеет 3 измерения — 2D для каждого из каналов и еще одно для их числа.

Если входной объект мультиканальный, то в случае цветного RGB изображения (один канал для каждого цвета) выход будет четырехмерным. К счастью, любая библиотека глубокого обучения, включая PyTorch, легко справляется с отображением. Наконец, не стоит забывать, что операция свертки проходит через активационную функцию в каждом узле.

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

Объединение (Pooling)

Основными преимуществами для пулинга в сверточной нейронной сети являются:

  • Уменьшение количества параметров в вашей модели благодаря процессу даунсемплинга (down-sampling).
  • Детектирование признаков становится более правильным при изменении ориентации или размера объекта.

Пулинг — другой тип техники скользящего окна, где вместо применения обучаемых весов используется статистическая функция некоторого типа по содержимому этого окна. Наиболее частый тип пулинга — max pooling, который применяет функцию max(). Есть и другие варианты — mean pooling (который применяет функцию усреднения по содержимому окна), которые применяются в особых случаях. В этом туториале мы будем концентрироваться на max pooling. На рисунке ниже показано, как работает операция max pooling:

 

 

 

 

 

 

 

Давайте пройдемся по некоторым пунктам, связанным с диаграммой:

Основы

На диаграмме можно наблюдать действие max pooling. Для первого окна голубого цвета max pooling выдает значение 3.0, которое является максимальным значением узла в 2х2 окне. Таким же образом зеленое окно выводит максимальное значение, равно 5.0, а для красного окна максимальное значение — 7.0. Здесь всё просто и понятно.

Шаги и даунсемплинг

На диаграмме сверху можно заметить, что пулинговое окно каждый раз перемещается на 2 места. Можем говорить, что шаг равен 2. На диаграмме показаны шаги только вдоль оси x,  но для задачи предотвращения перекрытия окна, шаг должен быть также равен 2 и в направлении y. Другими словами, шаг обозначается как [2,2]. Следует упомянуть, если во время пулинга шаг больше 1, тогда размер выхода будет уменьшен. Как можно видеть на диаграмме, входной объект размера 5×5 уменьшается до 3х3 на выходе. И это хорошо — такое явление называется даунсемплингом и уменьшает количество обучаемых параметров в модели.

Padding

Важно отметить также, что в пулинговой диаграмме есть дополнительный столбец и строка, добавленные к входу размера 5х5, делающие эффективный размер пулингового пространства равным 6х6. Это делается для того, чтобы пулинговое окно размером 2х2 корректно работало с шагом [2,2]. Такой прием называется padding. Padding-узлы зачастую фиктивные, так как значения на них равны 0, и операция max pooling их не видит. Этот факт нужно будет учитывать при создании нашей сверточной сети на PyTorch.

Теперь мы разобрались в механизме работы пулинга в CNN,  выяснили его полезность в осуществлении даунсемплинга. Рассмотрим еще его некоторые функции и ответим на вопрос, почему max pooling используется так часто.

Использование пулинга в сверточных нейронных сетях

В дополнении к функции даунсемплинга пулинг используется в CNN, чтобы детектировать определенные признаки, инвариантные к изменениям размера или ориентации. Другой способ представить действие пулинга  — он обобщает низкоуровневую, сложно структурированную информацию. Представим случай, когда у нас есть сверточный фильтр, который во время тренировки обучается распознавать знак «9» в различных положениях на входном изображении. Чтобы сверточная сеть научилась корректно классифицировать появление «9» на картинке, требуется каким-то образом активировать сеть каждый раз, когда эта цифра появляется на изображении независимо от размера и ориентации (кроме случая, когда «9» напоминает «9»). Пулинг может помочь в такой задаче выбора высокоуровневых, обобщенных признаков. Этот процесс иллюстрирован ниже:

 

 

 

 

 

 

 

Диаграмма — стилизованное представление операции пулинга. Если мы считаем, что маленький участок входного изображения содержит цифру 9 (зеленый квадрат), и предполагаем, что пытаемся детектировать эту цифру на изображении. В таком случае несколько сверточных фильтров обучаются активироваться с помощью ReLU функции каждый раз, когда они видят «9» на картинке. Однако, они будут активироваться более или менее сильно в зависимости от того, как она расположена. Мы хотим научить сеть обнаруживать цифру на изображении независимо от ее ориентации. Здесь наступает черед пулинга. Он«смотрит» на выходы трех фильтров и берет настолько высокое значение, насколько высокий порог функций активации этих фильтров.

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

Общий взгляд

Ниже представлено изображение из Википедии, которое показывает структуру полностью разработанной сверточной нейронной сети:

 

 

 

 

 

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

Полносвязный слой

Ранее обсуждалось, сверточная нейронная сеть берет данные высокого разрешения и эффективно трансформирует их в представления объектов. Полносвязный слой может рассматриваться как стандартный классификатор над богатым информацией выходом нейросети для интерпретации и финального предсказания результата классификации. Для прикрепления полносвязного слоя к сети измерения выхода CNN необходимо «расплющить».

Рассматривая предыдущую диаграмму, на выходе имеем несколько каналов x x y тензоров/матриц. Эти каналы необходимо привести к одному (N x 1) тензору. Возьмем пример: имеем 100 каналов с 2х2 матрицами, отображающими выход последней пулинг операции в сети. В PyTorch можно легко осуществить преобразование в 2х2х100 = 400 строк, как будет показано ниже.

Теперь, когда основы сверточных нейронных сетей заложены, настало время реализовать CNN с помощью PyTorch.

Реализация CNN на PyTorch

Любой достойный фреймворк глубокого обучения может с легкостью справиться с операциями сверточной нейросети. PyTorch является таким фреймворком. В данном разделе будет показано, как создавать CNN с помощью PyTorch шаг за шагом. В идеале вы должны обладать некоторым представлением о PyTorch, но это не обязательно. Мы хотим разработать нейронную сеть для классификации символов в датасете MNIST. Полный код к этому туториалу находится в этом репозитории на GitHub.

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

 

 

 

 

 

 

В самом начале на вход подаются черно-белые представления символов размером 28х28 пикселей каждое. Первый слой состоит из 32 каналов сверточных фильтров размера 5х5 + активационная функция ReLU, затем идет 2х2 max pooling с даунсемплингом с шагом 2 (этот слой выводит данные размером 14х14). На следующий слой подается выход с первого слоя размера 14х14, который сканируется снова 5х5 сверточными фильтрами с 64 каналов, затем следует 2х2 max pooling с даунсемплингом для генерирования выхода размером 7х7.

После сверточной части сети следует:

  • операция выравнивания, которая создает 7х7х64=3164 узлов
  • средний слой из 1000 полносвязных улов
  • операция softmax над крайними 10 узлами для генерирования вероятностей классов.

Эти слои представлены в выходном классификаторе.

Загрузка датасета

В PyTorch уже интегрирован датасет MNIST, который мы можем использовать при помощи функции DataLoader. В этом подразделе выясним, как установить загрузчик данных для датасета MNIST. Но для начала нужно определить предварительные переменные:

num_epochs = 5 
num_classes = 10 
batch_size = 100 
learning_rate = 0.001
DATA_PATH = 'C:\\Users\Andy\PycharmProjects\MNISTData'
MODEL_STORE_PATH = 'C:\\Users\Andy\PycharmProjects\pytorch_models\\'

Прежде всего, устанавливаем тренировочные гиперпараметры. Далее идет спецификация пути папки для хранения датасета MNIST (PyTorch автоматически загрузит датасет в эту папку) и локации для тренировочных параметров модели после того, как обучение будет завершено.

Далее задаем преобразование для применения к MNIST и переменные для данных:

trans = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) 

train_dataset = torchvision.datasets.MNIST(root=DATA_PATH, train=True, transform=trans, download=True) 
test_dataset = torchvision.datasets.MNIST(root=DATA_PATH, train=False, transform=trans)

Первое, на что необходимо обратить внимание в коде сверху, это функция transform.Compose(). Функция находится в  пакете torchvision. Она позволяет разработчику делать различные манипуляции с указанным датасетом. Численные трансформации могут быть соединены вместе в список при использовании функции Compose(). В этом случае сначала устанавливается преобразование, которое конвертирует входной датасет в PyTorch тензор. PyTorch тензор — особый тип данных, используемый в библиотеке для всех различных операций с данными и весами внутри нейросети. По сути, такой тензор — простая многомерная матрица. Но в любом случае, PyTorch требует преобразования датасета в тензор таким образом, что его можно использовать для тренировки и тестирования сети.

Следующий аргумент в списке Compose() — нормализация. Нейронная сеть обучается лучше, когда входные данные нормализованы так, что их значения находятся в диапазоне от -1 до 1 или от 0 до 1. Чтобы это сделать с помощью нормализации PyTorch, необходимо указать среднее и стандартное отклонение MNIST датасета, которые в этом случае равны 0.1307 и 0.3081 соответственно. Отметим, среднее значение и стандартное отклонение должны быть установлены для каждого входного канала. У MNIST есть только один канал, но уже для датасета CIFAR c 3 каналами (по одному на каждый цвет из RGB спектра) надо указывать среднее и стандартное отклонение для каждого.

Далее необходимо создать объекты  train_dataset и test_dataset, которые будут последовательно проходить через загрузчик данных. Чтобы создать такие датасеты из данных MNIST, требуется задать несколько аргументов. Первый — путь до папки, где хранится файл с данными для тренировки и тестирования. Логический аргумент train показывает, какой файл из train.pt или  test.pt стоит брать в качестве тренировочного сета. Следующий аргумент — transform, в котором мы указываем ранее созданный объект trans, который осуществляет преобразования. Наконец, аргумент загрузки просит функцию датасета MNIST загрузить при необходимости данные из онлайн источника.

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

train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size,shuffle=True) 
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

Объект загрузчик данных в PyTorch обеспечивает несколько полезных функций при использовании тренировочных данных:

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

Как можно видеть, требуется задать три простых аргумента. Первый — данные, которые вы хотите загрузить; второй — желаемый размер партии; третий — перемешивать ли случайным образом датасет. Загрузчик может использоваться в качестве итератора. Поэтому для извлечения данных мы можем просто воспользоваться стандартным итератором Python, например, перечислением. Такая возможность будет позже продемонстрирована на практике в туториале.

Создание модели

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

class ConvNet(nn.Module): 
     def __init__(self): 
         super(ConvNet, self).__init__() 
         self.layer1 = nn.Sequential( nn.Conv2d(1, 32, kernel_size=5, stride=1, padding=2), 
            nn.ReLU(), nn.MaxPool2d(kernel_size=2, stride=2)) 
         self.layer2 = nn.Sequential( nn.Conv2d(32, 64, kernel_size=5, stride=1, padding=2), 
            nn.ReLU(), nn.MaxPool2d(kernel_size=2, stride=2)) 
         self.drop_out = nn.Dropout() 
         self.fc1 = nn.Linear(7 * 7 * 64, 1000) 
         self.fc2 = nn.Linear(1000, 10)

Это то место, где определяется модель. Наиболее простой способ создания структуры нейронной сети в PyTorch — создание наследственного класса от материнского nn.Module. Класс nn.Module очень полезен в PyTorch, он содержит всё необходимое для конструирования типичной глубокой нейронной сети. Кроме этого, класс имеет удобные функции: способы перемещения переменных и операций на GPU или обратно на CPU, применение рекурсивных функций через все свойства в классе (например, перенастройка всех весовых переменных), создание оптимизированных интерфейсов для тренировки и тому подобное. Все доступные методы можно посмотреть здесь.

Первый шаг — создание нескольких объектов последовательного слоя с помощью функции class _init_. Сначала создаем слой 1 (self.layer1) через создание объекта nn.Sequential. Этот метод позволяет создавать упорядоченные слои в сети и является удобным способом создания последовательности свертка + ReLu + пулинг. Как можно видеть, первый элемент в определении этой последовательности — метод Conv2d, который создает набор сверточных фильтров. В этом методе первый аргумент — количество входных каналов; в нашем случае изображения MNIST черно-белые и количество каналов равно 1. Второй аргумент в Conv2d — количество выходных каналов; как показано на диаграмме архитектуры модели, первый слой сверточных фильтров состоит из 32 каналов, поэтому значение второго аргумента равно 32.

Аргумент kernel_size отвечает за размер сверточного фильтра, в нашем случае мы хотим фильтр размеров 5х5, поэтому аргумент равен 5. Если бы мы хотели фильтры с разными размерными формами по оси х и y, необходимо было указать параметры в виде python tuple (размер по х, размер по y). Наконец, мы хотим установить padding, что делается несколько сложнее. Размер измерения на выходе от операции  сверточной фильтрации или пулинга может быть посчитан с помощью уравнения:

 

 

Где W(in) — ширина выхода, F — размер фильтра, P — паддинг, S — шаг. Такая же формула применима для расчета высоты.  В нашем случае изображение и фильтрация симметричны, поэтому формула применима и к ширине и к высоте. Если хотим оставить входные и выходные измерения без изменений с размером фильтра 5 и шагом 1, то исходя из верхней формулы паддинг нужно задать равным 2. Таким образом, аргумент для паддинга в Conv2d равен 2.

Следующий аргумент в последовательности — простая ReLU функция активации. Последний элемент в последовательном определении для self.layer1 — операция max pooling. Первый аргумент — размер объединения, который равен 2х2; следовательно, аргумент равен 2. Во втором аргументе мы хотим взять подвыборку из данных через уменьшение эффективного размера изображения с факторов 2. Чтобы это сделать используя формулу выше, устанавливаем шаг равным 2 и паддинг равным 0. Следовательно, шаговый аргумент равен 2. Паддинг аргумент по умолчанию равен 0, если не указано другое значение, что сделано в коде сверху. Из этих вычислений теперь понятно, что выход слоя self.layer1 имеет 32 канала и “изображения” размером 14х14.

Второй слой, self.layer2, определяется таким же образом, как и первый. Единственное отличие здесь — вход в функцию Conv2d теперь 32 канальный, а выход — 64 канальный. Следуя такой же логике и учитывая пулинг и даунсемплинг, выход из self.layer2 представляет из себя 64 канала изображения размера 7х7.

Далее определяем отсеивающий слой для предотвращения переобучения модели. В конце создаем два полносвязных слоя. Первый слой имеет размер 7х7х64 узла и соединяется со вторым слоем с 1000 узлами. Чтобы создать полносвязный слой в PyTorch, используем метод nn.Linear. Первый аргумент метода — число узлов в данном слое, второй аргумент — число узлов в следующем слое.

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

def forward(self, x): 
     out = self.layer1(x) 
     out = self.layer2(out) 
     out = out.reshape(out.size(0), -1) 
     out = self.drop_out(out) 
     out = self.fc1(out) 
     out = self.fc2(out) 
     return out

Здесь важно назвать функцию “forward”, так как она будет использовать базовую функцию прямого распространения в nn.Module и позволит всему функционалу nn.Module работать корректно. Как можно видеть, функция принимает на вход аргумент х, представляющий собой данные, которые должны проходить через модель (например, партия данных). Направляем эти данные в первый слой (self.layer1) и возвращаем выходные данные как out. Эти выходные данные поступают на следующий слой и так далее. Отметим, после self.layer2 применяем функцию преобразования формы к out, которая разглаживает размеры данных с 7х7х64 до 3164х1. После двух полносвязных слоев применяется dropout-слой и из функции возвращается финальное предсказание.

Мы определили, что такое сверточная нейронная сеть, и как она работает. Настало время обучить нашу модель.

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

Перед тренировкой модели мы должны сначала создать экземпляр (instance) нашего класса ConvNet(), определить функцию потерь и оптимизатор:

model = ConvNet()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

Экземпляр класса ConvNet() создается под названием  model. Далее определяем операцию над потерями, которая будет использоваться для подсчета потерь. В нашем случае используем доступную в PyTorch функцию CrossEntropyLoss(). Вы уже могли заметить, что мы еще не задали активационную функцию SoftMax для последнего слоя, выполняющего классификацию. Это сделано потому, что функция  CrossEntropyLoss() объединяет и SoftMax и кросс-энтропийную функцию потерь в единую функцию. Далее определяем оптимизатор Adam. Первым аргументом функции являются параметры, которые мы хотим обучить оптимизатором. Это легко сделать с помощью класса nn.Module, из которого вы получается ConvNet. Всё что нужно сделать — снабдить функцию аргументом model.parameters() и PyTorch будет отслеживать все параметры нашей модели, которые необходимо обучить. Последним определяется скорость обучения.

Тренировочный цикл выглядит следующим образом:

total_step = len(train_loader)
loss_list = []
acc_list = []
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):
        # Прямой запуск
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss_list.append(loss.item())

        # Обратное распространение и оптимизатор
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # Отслеживание точности
        total = labels.size(0)
        _, predicted = torch.max(outputs.data, 1)
        correct = (predicted == labels).sum().item()
        acc_list.append(correct / total)

        if (i + 1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}, Accuracy: {:.2f}%'
                  .format(epoch + 1, num_epochs, i + 1, total_step, loss.item(),
                          (correct / total) * 100))

Самыми важными частями для начала являются два цикла. Первый цикл проходит по количеству эпох, в нём итерации проходят по train_loader используя перечисление. Во внутреннем цикле сначала считаются выходы прямого прохождения изображений (которые представляют собой партии нормализованных MNIST изображений из train_loader). Отметим, нам не требуется вызывать model.forward(images), так как nn.Module знает, что нужно вызывать forward при выполнении model(images).

Следующий шаг — отправление выходов модели и настоящих меток в функцию CrossEntropyLoss, определенную как criterion. Потери добавляются в список, который будет использован позже для отслеживания прогресса обучения. Затем — выполнение обратного распространения ошибки и оптимизированного шага тренировки. Сначала градиенты должны быть обнулены, что легко делается вызовом zero_grad() на оптимизаторе. Далее вызываем функцию .backward() на переменной loss для выполнения обратного распространения. Теперь, когда градиенты посчитаны при обратном распространении, просто вызываем optimizer.step() для выполнения шага обучения оптимизатора Adam. PyTorch делает процесс обучения модели очень легким и интуитивным.

Следующий набор строк отвечает за отслеживание точности на тренировочном сете. Предсказания модели могут быть определены с помощью функции torch.max(), которая возвращает индекс максимального значения в тензоре. Первый аргумент этой функции — тензор, который мы хотим исследовать; второй — ось, по которой определяется максимум. Выходящий из модели тензор должен иметь размер (batch_size,10). Чтобы определить предсказание модели, необходимо для каждого примера в партии найти максимальные значения из 10 выходных узлов. Каждый из этих узлов будет соответствовать одному рукописному символу (например, выход 2 равен символу «2» и так далее). Выходной узел с наибольшим значением и будет предсказанием модели. Следовательно, надо задать второй аргумент в функции torch.max() равным 1, что указывает функции максимума проверить ось выходного узла (axis = 0 соответствует размерности batch_size).

На этом шаге возвращается список целочисленных предсказаний модели, а следующая строка сравнивает эти предсказания с настоящими метками (predicted == labels) и суммирует правильные предсказания. Отметим, что на этом шаге выход функции sum() всё еще тензор, поэтому для оценки его значений требуется вызвать .item(). Делим количество правильных предсказаний на размер партии batch_size (эквивалентно labels.size(0)) для подсчета точности. Наконец, во время тренировки после каждых 100 итераций внутреннего цикла выводим прогресс.

Результаты тренировки будут выглядеть следующим образом:

Epoch [1/6], Step [100/600], Loss: 0.2183, Accuracy: 95.00%
Epoch [1/6], Step [200/600], Loss: 0.1637, Accuracy: 95.00%
Epoch [1/6], Step [300/600], Loss: 0.0848, Accuracy: 98.00%
Epoch [1/6], Step [400/600], Loss: 0.1241, Accuracy: 97.00%
Epoch [1/6], Step [500/600], Loss: 0.2433, Accuracy: 95.00%
Epoch [1/6], Step [600/600], Loss: 0.0473, Accuracy: 98.00%
Epoch [2/6], Step [100/600], Loss: 0.1195, Accuracy: 97.00%

Теперь напишем код для определения точности на тестовом наборе.

Тестирование

Для тестирования модели используем следующий код:

model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print('Test Accuracy of the model on the 10000 test images: {} %'.format((correct / total) * 100))

# Сохраняем модель и строим график
torch.save(model.state_dict(), MODEL_STORE_PATH + 'conv_net_model.ckpt')

В первой строке устанавливаем режим оценки используя model.eval(). Это удобная функция запрещает любые исключения или слои нормализации партии в модели, которые будут мешать объективной оценке. Конструкция torch.no_grad() выключает функцию автоградиента в модели, так как она не нужна при тестировании/оценке модели и замедляет вычисления. Остальная часть кода совпадает с вычислением точности во время тренировки, за исключением того, что в этом случае итерации проходят по test_loader.

Наконец, результат выводится в консоль, а модель сохраняется при помощи функции torch.save().

В последней части кода в этом репозитории на GitHub дополнительно построен график отслеживания точности и потерь, используя библиотеку для рисования Bokeh. Финальный результат выглядит так:

Точность на тестовой выборке на 10000 картинках составляет 99.03%

 

 

 

 

 

 

 

 

 

 

 

Можно видеть, сеть достаточно быстро достигает высокого уровня точности на тренировочном сете. На тестовом наборе после 6 эпох модель показывает точность 99%, что очень хорошо. Этот результат определенно лучше точности базовой полносвязной нейронной сети.

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

 


Интересные статьи:

Azure ML туториал: создание простой модели машинного обучения

23 октября 2018
azure ml туториал что это

Azure ML туториал: создание простой модели машинного обучения

Перед вами пошаговое руководство по созданию модели машинного обучения с использованием Microsoft Azure ML, перевод статьи «Tutorial – Build a simple Machine Learning Model using AzureML«. Ссылка на оригинал — в…

Перед вами пошаговое руководство по созданию модели машинного обучения с использованием Microsoft Azure ML, перевод статьи «Tutorial – Build a simple Machine Learning Model using AzureML«. Ссылка на оригинал — в подвале статьи.

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

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

Что такое Azure ML?

azure ml туториал

Хорошая новость — освоить машинное обучение сегодня стало намного проще, чем, например, в 2014 году. Как новичок в этом деле, ты можешь начать познавать машинное обучение с помощью фреймворка Microsoft Azure ML.

Azure ML представляет собой реализацию алгоритма машинного обучения через графический интерфейс от Microsoft.

Какие ресурсы доступны на AzureML

Давайте познакомимся с арсеналом этого инструмента.

  1. Примеры датасетов: мне нравится тестировать инструменты, которые имеют множество встроенных наборов данных. Это упрощает процесс тестирования мощности инструмента. Azure ML поставляется с длинным списком встроенных наборов данных. Список датасетов.
  2. Средства машинного обучения: Azure ML имеет в наличии почти все популярные алгоритмы машинного обучения и формулы оценочных показателей.
  3. Преобразование данных: в Azure ML есть все параметры фильтрации, параметры преобразования, параметры суммирования и варианты расчета матрицы.
  4. Параметры преобразования формата данных: А  что если вы хотите добавить свой собственный набор данных? Azure ML имеет несколько опций для добавления наборов данных из вашей локальной системы. Вот эти варианты:

формат экспорта данных из azure ml

Создание модели

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

Здесь вы начинаете — нажмите на “Создать новый эксперимент”.

Вы получаете пустую таблицу экспериментов:

поле для экспериментов

Теперь вы можете выбрать pallete:

список датасетов azure ml

Шаг 1. Выберите набор данных. Это могут быть ваши примеры данных или же вы можете загрузить их. В этом уроке я буду использовать «Данные о раке молочной железы» из встроенных наборов данных. Просто перетащите эти данные в главное окно.

машинное обучение с azure ml

Шаг 2. Выберите инструмент деления. Вы можете использовать опцию поиска в палитре, чтобы найти «split data». Поместите «split data» под свой набор данных и присоедините.

датасеты

Теперь вы видите две точки на ячейке «split data». Это означает, что у вас есть два набора данных, готовых к работе. С правой стороны у вас есть возможность выбрать тип деления.

Шаг 3. Обучите модель машинного обучения: Для этого вам понадобятся два узла. Во-первых, это будет тип модели, которую вы хотите построить. Во-вторых, это будет узел тренировки модели. Вы можете обратиться к следующему рисунку:

датасет

Вы можете заметить восклицательный знак в узле тренировки модели. Это означает, что вам нужно указать целевую переменную. Выберем целевую переменную, щелкнув по ней. Теперь вы увидите окно с правой стороны. Выберите «Launch column slector».

тренировка модели

Я выбрал «Класс» в качестве целевой переменной.

Шаг 4. Теперь вы оцениваете модель: см. Следующий рисунок

model training azure ml

Шаг 5. Наконец, производите вычисления

Запускаете модель!

Визуализация датасета и вывода

Чтобы визуализировать любой узел, вы просто переходите к узлу, нажимаете на него правой кнопкой мыши, затем выбираете “визуализировать”.

Вот как выглядят визуальные данные в нашем случае:

визуализация данных в azure ml

Как видно, переменная Класс имеет только два значения. Этот инструмент аккуратно рисует распределение для каждой переменной и позволяет проверить нормальность.

Так выглядит подсчитанная модель:

azure машинное обучение

Как видно, оцененные вероятности в основном близки к нулю и к единице. Функция распределение имеет почти постоянное значение между нулем и единицей.Таким образом, модель выдает сильно разбросанные значения.

Наконец, вот как выглядят графики:

azure ml туториал

azure ml туториал

Заключение

Как можно видеть, модель оказалась очень эффективной, и мне потребовалось меньше минуты, чтобы построить и исполнить задачу. Вычисляемые расчетные матрицы являются довольно полными и, вероятно, содержат значение, которое вы искали. Мне понравился инструмент своей временной эффективностью и удобством для пользователя.

Считаете ли вы эту статью полезной ? Поделитесь с нами своим опытом работы с Azure ML.


Интересные статьи по теме:

Туториал по PyTorch: от установки до готовой нейронной сети

22 октября 2018
pytorch туториал

Туториал по PyTorch: от установки до готовой нейронной сети

Если вы уже пробовали создавать собственные глубокие нейронные сети с помощью TensorFlow и Keras, то, вероятно, знакомы с чувством разочарования при отлаживании этих библиотек. Хотя они имеют API на Python,…

Если вы уже пробовали создавать собственные глубокие нейронные сети с помощью TensorFlow и Keras, то, вероятно, знакомы с чувством разочарования при отлаживании этих библиотек. Хотя они имеют API на Python, всё еще трудно выяснить, что именно пошло не так при ошибке. Они также плохо работают вместе с библиотеками numpy, scipy, scikit-learn, Cython и другими. Библиотека глубокого обучения PyTorch имеет заявленное преимущество — хорошо работает с Python и создана для апологетов Python. Кроме того, приятное свойство PyTorch — построение вычислительного динамического графа, противоположно статическим вычислительным графам, представленным в TensorFlow и Keras. PyTorch сейчас находится на подъеме и используется в разработке Facebook, Twitter, NVIDIA и другими компаниями. Давайте обратимся к туториалу по использованию PyTorch.

цукерберг pytorch facebook

Перед вами перевод статьи «A PyTorch tutorial – deep learning in Python», ссылка на оригинал — в подвале статьи.

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

Основы PyTorch

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

Полносвязная нейронная сеть
Полносвязная нейронная сеть

Установка на Windows

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

Вычислительные графы

Первое, что необходимо понять о любой библиотеке глубокого обучения — идея вычислительных графов. Вычислительный граф — набор вычислений, которые называются узлами, и которые соединены в прямом порядке вычислений. Другими словами, выбранный узел зависит от узлов на входе, который в свою очередь производит вычисления для других узлов. Ниже представлен простой пример вычислительного графа для вычисления выражения a = (b + c) * (c + 2). Можно разбить вычисление на следующие шаги:

Вычислительный графы
Простой вычислительный граф

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

Тензоры

Тензоры — подобные матрице структуры данных, которые являются неотъемлемыми компонентами в библиотеках глубокого обучения и используются для эффективных вычислений. Графические процессоры (GPU) эффективны при вычислении операций между тензорами, что стимулировало волну возможностей в глубоком обучении. В PyTorch тензоры могут определяться несколькими способами:

import torch
x = torch.Tensor(2, 3)

Этот код создает тензор размера (2,3), заполненный нулями. В данном примере первое число — количество рядов, второе — количество столбцов:

0 0 0
0 0 0
[torch.FloatTensor of size 2x3]

Мы также можем создать тензор, заполненный случайными float-значениями:

x = torch.rand(2, 3)

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

x = torch.ones(2,3)
y = torch.ones(2,3) * 2
x + y

Код возвращает:

3 3 3
3 3 3
[torch.FloatTensor of size 2x3]

Также доступна работа с функцией slice в numpy. Например y[:,1]:

y[:,1] = y[:,1] + 1

Которая возвращает:

2 3 2
2 3 2
[torch.FloatTensor of size 2x3]

Теперь вы знаете, как создавать тензоры и работать с ними в PyTorch. Следующим шагом туториала будет обзор более сложных конструкций в библиотеке.

Автоматическое дифференцирование в PyTorch

В библиотеках глубокого обучения есть механизмы вычисления градиента ошибки и обратного распространения ошибки через вычислительный граф. Этот механизм, называемый автоградиентом в PyTorch, легко доступен и интуитивно понятен. Переменный класс — главный компонент автоградиентной системы в PyTorch. Переменный класс обертывает тензор и позволяет автоматически вычислять градиент на тензоре при вызове функции .backward(). Объект содержит данные из тензора, градиент тензора (единожды посчитанный по отношению к некоторому другому значению, потеря) и содержит также ссылку на любую функцию, созданную переменной (если это функция созданная пользователем, ссылка будет пустой).

Создадим переменную из простого тензора:

x = Variable(torch.ones(2, 2) * 2, requires_grad=True)

В объявлении переменной используется двойной тензор размера 2х2 и дополнительно указывается, что переменной необходим градиент. При использовании этой переменной в нейронных сетях, она становится способна к обучению. Если последний параметр будет равен False, то переменная не может использоваться для обучения. В этом простом примере мы ничего не будем тренировать, но хотим запросить градиент для этой переменной, как будет показано ниже.

Далее, давайте создадим новую переменную на основе x.

z = 2 * (x * x) + 5 * x

Чтобы вычислить градиент этой операции по x, dz/dx, можно аналитически получить 4x + 5. Если все элементы x — двойки, то градиент dz/dx — тензор размерности (2,2), заполненный числами 13. Однако, сначала необходимо запустить операцию обратного распространения .backwards(), чтобы вычислить градиент относительно чего-либо. В нашем случае инициализируется единичный тензор (2,2), относительно которого считаем градиент. В таком случаем вычисление — просто операция d/dx:

z.backward(torch.ones(2, 2))
print(x.grad)

Результатом кода является следующее:

Variable containing:
13 13
13 13
[torch.FloatTensor of size 2x2]

Заметим, это в точности то, что мы предсказывали вначале. Отметим, градиент хранится в переменной x в свойстве .grad.

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

Создание нейронной сети в PyTorch

Этот раздел — основной в туториале. Полный код туториала лежит в этом репозитории на GitHub. Здесь мы создадим простую нейронную сеть с 4 слоями, включая входной и два скрытых слоя, для классификации рукописных символов в датасете MNIST. Архитектура, которую мы будем использовать, показана на картинке:

Архитектура полносвязной нейронной сети
Архитектура полносвязной нейронной сети

Входной слой состоит из 28 х 28 = 784 пикселей с оттенками серого, которые составляют входные данные в датасете MNIST. Входные данные далее проходят через два скрытых слоя, каждый из которых содержит 200 узлов, использующих линейную выпрямительную функцию активации (ReLU). Наконец, мы имеем выходной слой с десятью узлами, соответствующими десяти рукописным цифрам от 0 до 9. Для такой задачи классификации будем использовать выходной softmax-слой.

Класс для построения нейронной сети

Чтобы создать нейронную сеть в PyTorch, используется класс nn.Module. Чтобы им воспользоваться, также необходимо наследование, что позволит использовать весь функционал базового класса nn.Module, но при этом еще имеется возможность переписать базовый класс для конструирования модели или прямого прохождения через сеть. Представленный ниже код поможет объяснить сказанное:

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
   def __init__(self):
       super(Net, self).__init__()
       self.fc1 = nn.Linear(28 * 28, 200)
       self.fc2 = nn.Linear(200, 200)
       self.fc3 = nn.Linear(200, 10)

В таком определении можно видеть наследование базового класса nn.Module. В первой строке инициализации класса def __init__(self)  мы имеем требуемую super() функцию языка Python, которая создает объект базового класса. В следующих трех строках создаем полностью соединенные слои как показано на диаграмме архитектуры. Полностью соединенный слой нейронной сети представлен объектом nn.Linear, в котором первый аргумент — определение количества узлов в i-том слое, а второй — количество узлов в i+1 слое. Из кода видно, первый слой принимает на входе 28×28 пикселей и соединяется с первым скрытым слоем с 200 узлами. Далее идет соединение с другим скрытым слоем с 200 узлами. И, наконец, соединение последнего скрытого слоя с выходным слоем с 10 узлами.

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

def forward(self, x):
   x = F.relu(self.fc1(x))
   x = F.relu(self.fc2(x))
   x = self.fc3(x)
   return F.log_softmax(x)

Для метода forward() берем входные данные x в качестве основного аргумента. Далее, загружаем всё в в первый полностью соединенный слой self.fc1(x) и применяем активационную функцию ReLU для узлов в этом слое, используя F.relu(). Из-за иерархической природы этой нейронной сети, заменяем x на каждой стадии и отправляем на следующий слой. Делаем эту процедуру на трех соединенных слоях, за исключением последнего. На последнем слое возвращаем не ReLU, а логарифмическую softmax активационную функцию. Это, в комбинации с функцией потери отрицательного логарифмического правдоподобия, дает многоклассовую на основе кросс-энтропии функцию потерь, которую мы будет использовать для тренировки сети.

Мы определили нейронную сеть. Следующим шагом будет создание экземпляра (instance) этой архитектуры:

net = Net()
print(net)

При выводе экземпляра класса Net получаем следующее:

Net (
(fc1): Linear (784 -> 200)
(fc2): Linear (200 -> 200)
(fc3): Linear (200 -> 10)
)

Что очень удобно, так как подтверждает структуру нашей нейронной сети.

Тренировка сети

Далее необходимо задать метод оптимизации и критерий качества:

# Осуществляем оптимизацию путем стохастического градиентного спуска
optimizer = optim.SGD(net.parameters(), lr=learning_rate, momentum=0.9)
# Создаем функцию потерь
criterion = nn.NLLLoss()

В первой строке создаем оптимизатор на основе стохастического градиентного спуска,  устанавливая скорость обучения (learning rate; в нашем случае определим этот показатель на уровне 0.01) и momentum. Еще в оптимизаторе необходимо определить все остальные параметры сети, но это делается легко в PyTorch благодаря методу .parameters() в базовом классе nn.Module, который наследуется из него в новый класс Net.

Далее устанавливается метрика контроля качества — функция потерь отрицательного логарифмического правдоподобия. Такой вид функции в комбинации с логарифмической softmax-функцией на выходе нейронной сети дает эквивалентную кросс-энтропийную потерю для 10 классов задачи классификации.

Настало время тренировать нейронную сеть. Во время тренировки данные будут извлекаться из объекта загрузки данных, который включен в модуль PyTorch. Здесь не будут рассмотрены детали этого способа, но вы можете найти код в этом репозитории на GitHub. Из загрузчика будут поступать партиями входные и целевые данные, которые будут подаваться в нашу нейронную сеть и функцию потерь, соответственно. Ниже представлен полный код для тренировки:

# запускаем главный тренировочный цикл
for epoch in range(epochs):
   for batch_idx, (data, target) in enumerate(train_loader):
       data, target = Variable(data), Variable(target)
# изменим размер с (batch_size, 1, 28, 28) на (batch_size, 28*28)
       data = data.view(-1, 28*28)
       optimizer.zero_grad()
       net_out = net(data)
       loss = criterion(net_out, target)
       loss.backward()
       optimizer.step()
       if batch_idx % log_interval == 0:
           print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                   epoch, batch_idx * len(data), len(train_loader.dataset),
                          100. * batch_idx / len(train_loader), loss.data[0]))

Внешний тренировочный цикл проходит по количеству эпох, а внутренний тренировочный цикл проходит через все тренировочные данные в партиях, размер которых задается в коде как batch_size. На следующей линии конвертируем данные и целевую переменную в переменные PyTorch. Входной датасет MNIST, который находится в пакете torchvision (который вам необходимо установить при помощи pip), имеет размер (batch_size, 1, 28, 28) при извлечении из загрузчика данных. Такой четырехмерный тензор больше подходит для архитектуры сверточной нейронной сети, чем для нашей полностью соединенной сети. Тем не менее, необходимо уменьшить размерность данных с (1,28,28)  до одномерного случая для 28 х 28 = 784 входных узла.

Функция .view() работает с переменными PyTorch и преобразует их форму. Если мы точно не знаем размерность данного измерения, можно использовать ‘-1’ нотацию в определении размера. Поэтому при использование data.view(-1,28*28) можно сказать, что второе измерение должно быть равно 28 x 28, а первое измерение должно быть вычислено из размера переменной оригинальных данных. На практике это означает, что данные теперь будут размера (batch_size, 784). Мы можем пропустить эту партию входных данных в нашу нейросеть, и магический PyTorch сделает за нас тяжелую работу, эффективно выполняя необходимые вычисления с тензорами.

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

net_out = net(data)
loss = criterion(net_out, target)

Первая строка, в которой подаем порцию данных на вход нашей модели, вызывает метод forward() в классе Net. После запуска строки переменная net_out будет иметь логарифмический softmax-выход из нашей нейронной сети для заданной партии данных.  Это одна из самых замечательных особенностей PyTorch, так как можно активировать любой стандартный отладчик Python, который вы обычно используете, и мгновенно узнать, что происходит в нейронной сети. Это противоположно другим библиотекам глубокого обучения, TensorFlow и Keras, в которых требуется  производить сложные отладочные действия, чтобы узнать, что ваша нейронная сеть действительно создает. Надеюсь, вы поиграете с кодом для этого туториала и поймете, насколько в PyTorch удобный отладчик.

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

Давайте посмотрим на следующие две строки:

loss.backward()
optimizer.step()

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

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

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

if batch_idx % log_interval == 0:
   print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                   epoch, batch_idx * len(data), len(train_loader.dataset),
                          100. * batch_idx / len(train_loader), loss.data[0]))

Эта функция выводит наш прогресс на протяжении эпох тренировки и показывает ошибку нейросети в этот момент. Отметим, что доступ к потерям находится в свойстве .data у переменной PyTorch, которая в данном случае будет массивом с единственным значением. Получаем скалярную потерю используя loss.data[0].

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

Train Epoch: 9 [52000/60000 (87%)] Loss: 0.015086

Train Epoch: 9 [52000/60000 (87%)] Loss: 0.015086

Train Epoch: 9 [54000/60000 (90%)] Loss: 0.030631

Train Epoch: 9 [56000/60000 (93%)] Loss: 0.052631

Train Epoch: 9 [58000/60000 (97%)] Loss: 0.052678

После 10 эпох, значение потери по величине должно получиться меньше 0.05.

Тестирование сети

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

test_loss = 0
correct = 0
for data, target in test_loader:
   data, target = Variable(data, volatile=True), Variable(target)
   data = data.view(-1, 28 * 28)
   net_out = net(data)
# Суммируем потери со всех партий
   test_loss += criterion(net_out, target).data[0]
   pred = net_out.data.max(1)[1]  # получаем индекс максимального значения
   correct += pred.eq(target.data).sum()

test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
       test_loss, correct, len(test_loader.dataset),
       100. * correct / len(test_loader.dataset)))

Этот цикл совпадает с тренировочным циклом до строки test_loss. Здесь мы извлекаем потери сети используя свойство .data[0] как и раньше, но только все в одной строке. Далее в строке pred используется метод data.max(1), который возвращает индекс наибольшего значения в определенном измерении тензора. Теперь выход нашей нейронной сети будет иметь размер (batch_size, 10), где каждое значение из второго измерения длины 10 — логарифмическая вероятность, которую нейросеть приписывает каждому выходному классу (то есть это логарифмическая вероятность принадлежности картинки к символу от 0 до 9). Поэтому для каждого входного образца в партии net_out.data будет выглядеть следующим образом:

[-1.3106e+01, -1.6731e+01, -1.1728e+01, -1.1995e+01, -1.5886e+01, -1.7700e+01, -2.4950e+01, -5.9817e-04, -1.3334e+01, -7.4527e+00]

Значение с наибольшей логарифмической вероятностью — цифра от 0 до 9, которую нейронная сеть распознает на входной картинке. Иначе говоря, это лучшее предсказание для заданного входного объекта. В примере net_out.data таким лучшим предсказанием является значение -5.9817e-04, которое соответствует цифре “7”. Поэтому для этого примера нейросеть предскажет знак  “7”. Функция .max(1) определяет это максимальное значение во втором пространстве (если мы хотим найти максимум в первом пространстве, мы должны аргумент функции изменить с 1 на 0) и возвращает сразу и максимальное найденное значение, и индекс ему соответствующий. Поэтому эта конструкция имеет размер (batch_size, 2). В данном случае, нас интересует индекс максимального найденного значения, к которому мы получаем доступ с помощью вызова .max(1)[1].

Теперь у нас есть предсказание нейронной сети для каждого примера в определенной партии входных данных, и можно сравнить его с настоящей меткой класса из тренировочного датасета. Это используется для подсчета количества правильных ответов. Чтобы сделать это в PyTorch, необходимо воспользоваться функцией .eq(), которая сравнивает значения в двух тензорах и при совпадении возвращает единицу. В противном случае, функция возвращает 0:

correct += pred.eq(target.data).sum()

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

test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
       test_loss, correct, len(test_loader.dataset),
       100. * correct / len(test_loader.dataset)))

После тренировки сети за 10 эпох получаем следующие результаты на тестовой выборке:

Test set: Average loss: 0.0003, Accuracy: 9783/10000 (98%)

Мы получили точность 98%. Весьма неплохо!

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


Интересные статьи:

Как создать нейронную сеть c библиотекой Keras на Python: пример

9 октября 2018
keras нейронаня сеть на python

Как создать нейронную сеть c библиотекой Keras на Python: пример

Keras — популярная библиотека глубокого обучения, которая внесла большой вклад в коммерциализацию глубокого обучения. Библиотека Keras проста в использовании и позволяет создавать нейронные сети с помощью лишь нескольких строк кода…

Keras — популярная библиотека глубокого обучения, которая внесла большой вклад в коммерциализацию глубокого обучения. Библиотека Keras проста в использовании и позволяет создавать нейронные сети с помощью лишь нескольких строк кода Python.

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

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

Содержание:

  • Что такое Keras?
  • Что такое анализ настроений?
  • Датасет IMDB
  • Импорт зависимостей и получение данных
  • Изучение данных
  • Подготовка данных
  • Создание и обучение модели

Что такое Keras?

Keras — это библиотека для Python с открытым исходным кодом, которая позволяет легко создавать нейронные сети. Библиотека совместима с TensorFlow, Microsoft Cognitive Toolkit, Theano и MXNet. Tensorflow и Theano являются наиболее часто используемыми численными платформами на Python для разработки алгоритмов глубокого обучения, но они довольно сложны в использовании.

deep learning frameworks
Оценка популярности фреймворков машинного обучения по 7 категориям

Читайте: TensorFlow туториал. Часть 1: тензоры и векторы

Keras, наоборот, предоставляет простой и удобный способ создания моделей глубокого обучения. Ее создатель, François Chollet, разработал ее для того, чтобы максимально ускорить и упростить процесс создания нейронных сетей. Он сосредоточил свое внимание на расширяемости, модульности, минимализме и поддержке Python. Keras можно использовать с GPU и CPU; она поддерживает как Python 2, так и Python 3. Keras компании Google внесла большой вклад в коммерциализацию глубокого обучения и искусственного интеллекта, поскольку она содержит cовременные алгоритмы глубокого обучения, которые ранее были не только недоступными, но и непригодными для использования.

Что такое анализ настроений (сентимент-анализ)?

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

keras нейронная сеть анализ настроений
Пример шкалы анализа настроений

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

Датасет IMDb

imdb reviews sentiment
Рецензии на сайте IMDb

Датасет IMDb состоит из 50 000 обзоров фильмов от пользователей, помеченных как положительные (1) и отрицательные (0).

  • Рецензии предварительно обрабатываются, и каждая из них кодируется последовательностью индексов слов в виде целых чисел.
  • Слова в обзорах индексируются по их общей частоте появления в датасете. Например, целое число «2» кодирует второе наиболее частое используемое слово.
  • 50 000 обзоров разделены на два набора: 25 000 для обучения и 25 000 для тестирования.

Датасет был создан исследователями Стэнфордского университета и представлен в статье 2011 года, в котором достигнутая точность предсказаний была равна 88,89%. Датасет также использовался в рамках конкурса сообщества Keggle «Bag of Words Meets Bags of Popcorn» в 2011 году.

Импорт зависимостей и получение данных

Начнем с импорта необходимых зависимостей для предварительной обработки данных и построения модели.

%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from keras.utils import to_categorical
from keras import models
from keras import layers

Загрузим датесет IMDb, который уже встроен в Keras. Поскольку мы не хотим иметь данные обучения и тестирования в пропорции 50/50, мы сразу же объединим эти данные после загрузки для последующего разделения в пропорции 80/20:

from keras.datasets import imdb
(training_data, training_targets), (testing_data, testing_targets) = imdb.load_data(num_words=10000)
data = np.concatenate((training_data, testing_data), axis=0)
targets = np.concatenate((training_targets, testing_targets), axis=0)

Изучение данных

Изучим наш датасет:

print("Categories:", np.unique(targets))
print("Number of unique words:", len(np.unique(np.hstack(data))))
Categories: [0 1]
Number of unique words: 9998
length = [len(i) for i in data]
print("Average Review length:", np.mean(length))
print("Standard Deviation:", round(np.std(length)))
Average Review length: 234.75892
Standard Deviation: 173.0

Можно видеть, что все данные относятся к двум категориям: 0 или 1, что представляет собой настроение обзора. Весь датасет содержит 9998 уникальных слов, средний размер обзора составляет 234 слова со стандартным отклонением 173.

Рассмотрим простой способ обучения:

print("Label:", targets[0])
Label: 1
print(data[0])
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]

Здесь вы видите первый обзор из датасета, который помечен как положительный (1). Нижеследующий код производит обратное преобразование индексов в слова, чтобы мы могли их прочесть. В нем каждое неизвестное слово заменяется на «#». Это делается с помощью функции get_word_index ().

index = imdb.get_word_index()
reverse_index = dict([(value, key) for (key, value) in index.items()]) 
decoded = " ".join( [reverse_index.get(i - 3, "#") for i in data[0]] )
print(decoded) 

# this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert # is an amazing actor and now the same being director # father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for # and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also # to the two little boy's that played the # of norman and paul they were just brilliant children are often left out of the # list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all

Подготовка данных

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

def vectorize(sequences, dimension = 10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1
return results
 
data = vectorize(data)
targets = np.array(targets).astype("float32")

Разделим датасет на обучающий и тестировочный наборы. Обучающий набор будет состоять из 40 000 обзоров, тестировочный — из 10 000.

test_x = data[:10000]
test_y = targets[:10000]
train_x = data[10000:]
train_y = targets[10000:]

Создание и обучение модели

Теперь можно создать простую нейронную сеть. Начнем с определения типа модели, которую мы хотим создать. В Keras доступны два типа моделей: последовательные и с функциональным API.

Затем нужно добавить входные, скрытые и выходные слои. Для предотвращения переобучения будем использовать между ними исключение («dropout»). Обратите внимание, что вы всегда должны использовать коэффициент исключения в диапазоне от 20% до 50%. На каждом слое используется функция «dense» для полного соединения слоев друг с другом. В скрытых слоях будем используем функцию активации «relu», потому это практически всегда приводит к удовлетворительным результатам. Не бойтесь экспериментировать с другими функциями активации. На выходном слое используем сигмоидную функцию, которая выполняет перенормировку значений в диапазоне от 0 до 1. Обратите внимание, что мы устанавливаем размер входных элементов датасета равным 10 000, потому что наши обзоры имеют размер до 10 000 целых чисел. Входной слой принимает элементы с размером 10 000, а выдает — с размером 50.

Наконец, пусть Keras выведет краткое описание модели, которую мы только что создали.

# Input - Layer
model.add(layers.Dense(50, activation = "relu", input_shape=(10000, )))
# Hidden - Layers
model.add(layers.Dropout(0.3, noise_shape=None, seed=None))
model.add(layers.Dense(50, activation = "relu")
model.add(layers.Dropout(0.2, noise_shape=None, seed=None))
model.add(layers.Dense(50, activation = "relu"))
# Output- Layer
model.add(layers.Dense(1, activation = "sigmoid"))model.summary()
model.summary()

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 50)                500050    
_________________________________________________________________
dropout_1 (Dropout)          (None, 50)                0         
_________________________________________________________________
dense_2 (Dense)              (None, 50)                2550      
_________________________________________________________________
dropout_2 (Dropout)          (None, 50)                0         
_________________________________________________________________
dense_3 (Dense)              (None, 50)                2550      
_________________________________________________________________
dense_4 (Dense)              (None, 1)                 51        
=================================================================
Total params: 505,201
Trainable params: 505,201
Non-trainable params: 0
_________________________________________________________________

Теперь нужно скомпилировать нашу модель, то есть, по существу, настроить ее для обучения. Будем использовать оптимизатор «adam». Оптимизатор — это алгоритм, который изменяет веса и смещения во время обучения. В качестве функции потерь используем бинарную кросс-энтропию (так как мы работаем с бинарной классификацией), в качестве метрики оценки — точность.

model.compile(
 optimizer = "adam",
 loss = "binary_crossentropy",
 metrics = ["accuracy"]
)

Теперь можно обучить нашу модель. Мы будем делать это с размером партии 500 и только двумя эпохами, поскольку я выяснил, что модель начинает переобучаться, если тренировать ее дольше. Размер партии определяет количество элементов, которые будут распространяться по сети, а эпоха — это один проход всех элементов датасета. Обычно больший размер партии приводит к более быстрому обучению, но не всегда — к быстрой сходимости. Меньший размер партии обучает медленнее, но может быстрее сходиться. Выбор того или иного варианта определенно зависит от типа решаемой задачи, и лучше попробовать каждый из них. Если вы новичок в этом вопросе, я бы посоветовал вам сначала использовать размер партии 32, что является своего рода стандартом.

results = model.fit(
 train_x, train_y,
 epochs= 2,
 batch_size = 500,
 validation_data = (test_x, test_y)
)

Train on 40000 samples, validate on 10000 samples
Epoch 1/2
40000/40000 [==============================] - 5s 129us/step - loss: 0.4051 - acc: 0.8212 - val_loss: 0.2635 - val_acc: 0.8945
Epoch 2/2
40000/40000 [==============================] - 4s 90us/step - loss: 0.2122 - acc: 0.9190 - val_loss: 0.2598 - val_acc: 0.8950

Проведем оценку работы модели:

print(np.mean(results.history["val_acc"]))
0.894750000536

Отлично! Наша простая модель уже побила рекорд точности в статье 2011 года, упомянутой в начале поста. Смело экспериментируйте с параметрами сети и количеством слоев.

Полный код модели приведен ниже:

import numpy as np
from keras.utils import to_categorical
from keras import models
from keras import layers
from keras.datasets import imdb
(training_data, training_targets), (testing_data, testing_targets) = imdb.load_data(num_words=10000)
data = np.concatenate((training_data, testing_data), axis=0)
targets = np.concatenate((training_targets, testing_targets), axis=0)
def vectorize(sequences, dimension = 10000):
 results = np.zeros((len(sequences), dimension))
 for i, sequence in enumerate(sequences):
  results[i, sequence] = 1
 return results
 
data = vectorize(data)
targets = np.array(targets).astype("float32")
test_x = data[:10000]
test_y = targets[:10000]
train_x = data[10000:]
train_y = targets[10000:]
model = models.Sequential()
# Input - Layer
model.add(layers.Dense(50, activation = "relu", input_shape=(10000, )))
# Hidden - Layers
model.add(layers.Dropout(0.3, noise_shape=None, seed=None))
model.add(layers.Dense(50, activation = "relu"))
model.add(layers.Dropout(0.2, noise_shape=None, seed=None))
model.add(layers.Dense(50, activation = "relu"))
# Output- Layer
model.add(layers.Dense(1, activation = "sigmoid"))
model.summary()
# compiling the model
model.compile(
 optimizer = "adam",
 loss = "binary_crossentropy",
 metrics = ["accuracy"]
)
results = model.fit(
 train_x, train_y,
 epochs= 2,
 batch_size = 500,
 validation_data = (test_x, test_y)
)
print("Test-Accuracy:", np.mean(results.history["val_acc"]))

Итоги

Вы узнали, что такое анализ настроений и почему Keras является одной из наиболее популярных библиотек глубокого обучения.

Мы создали простую нейронную сеть с шестью слоями, которая может вычислять настроение авторов кинорецензий с точностью 89%. Теперь вы можете использовать эту модель для анализа бинарных настроений в других источниках, но для этого вам придется сделать их размер равным 10 000 или изменить параметры входного слоя.

Эту модель (с небольшими изменениями) можно применить и для решения других задач машинного обучения.


Интересные статьи:

TensorFlow туториал. Часть 4: глубокое обучение

1 октября 2018
Глубокое обучение с Tensorflow

TensorFlow туториал. Часть 4: глубокое обучение

Теперь, когда вы изучили и подготовили свои данные, пришло время создать архитектуру нейронной сети с помощью пакета TensorFlow! Предыдущие статьи: TensorFlow туториал. Часть 1: тензоры и векторы TensorFlow туториал. Часть…

Теперь, когда вы изучили и подготовили свои данные, пришло время создать архитектуру нейронной сети с помощью пакета TensorFlow!

Предыдущие статьи:

Моделирование нейронной сети

Пришло время построить нейронную сеть, слой за слоем.

Если вы еще этого не сделали, импортируйте tensorflow в рабочую область под названием tf. Затем инициализируйте Graph с помощью функции Graph(). Используйте эту функцию для определения вычислений. Обратите внимание, что с помощью Graph вы ничего не вычисляете, поскольку она не принимает на вход никаких переменных. Graph лишь определяет операции, которые вы хотите выполнить позже.

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

Теперь можно добавлять операции к графу. Нужно создать модель нейронной сети, а затем при ее компиляции определить функцию потерь, оптимизатор и метрику. Когда вы работаете с TensorFlow, это делается за один шаг:

  • Во-первых, вы задаете плейсхолдеры для входных данных и меток, потому что пока что не задаете «настоящие» данные. Помните, что плейсхолдеры — это неинициализированные переменные, которые будут инициализированы сессией при ее запуске. Когда вы, наконец, запустите сессию, эти плейсхолдеры получат значения датасета, которые вы передадите в функцию run()!
  • Затем вы создаете сеть. Сначала нужно выполнить сглаживание входных данных с помощью функции ыхflatten(), которая создаст массив размерности [None, 784] вместо массива размерности [None, 28, 28], который является исходным массивом ваших изображений в оттенках серого.
  • После того, как вы произвели сглаживание данных, нужно создать полностью подключенный слой, который генерирует логиты размера [None, 62]. Логиты — это функции, работающие с немасштабированным выходным результатом предыдущих слоев и использующие относительную шкалу для проверки линейности единиц измерения.
  • После построения многослойного персептрона вы можете определить функцию потерь. Выбор функции потерь зависит от задачи, которую вы решаете. В нашем случае используйте эту:
sparse_softmax_cross_entropy_with_logits()
  • Эта функция вычисляет разреженную кросс-энтропию softmax между логитами и метками. Другими словами, она измеряет вероятность ошибки в дискретных задачах классификации, в которых классы являются взаимоисключающими. Это означает, что каждый элемент данных принадлежит только одному классу. В нашем случае, например, дорожный знак содержит только одну метку. Помните, хотя регрессия используется для предсказания непрерывных значений, классификация используется для предсказания дискретных значений или классов элементов данных. Вы выполняете свертку этой функции с reduce_mean(), которая вычисляет среднее значение элементов вдоль измерений тензора.
  • Также вам нужно установить оптимизатор обучения. Одни из наиболее популярных алгоритмов оптимизации — Stochastic Gradient Descent (SGD), ADAM и RMSprop. В зависимости от выбранного вами алгоритма необходимо настроить параметры, например, скорость обучения. В этом случае вы выбираете оптимизатор ADAM, для которого скорость обучения выбирается равной 0,001.
  • Наконец, вы инициализируете операции, выполняемые перед началом обучения.
# Импорт `tensorflow` 

import tensorflow as tf

# Инициализация плейсхолдеров 

x = tf.placeholder(dtype = tf.float32, shape = [None, 28, 28])

y = tf.placeholder(dtype = tf.int32, shape = [None])

# Сглаживание входных данных

images_flat = tf.contrib.layers.flatten(x)

# Полностью подключенный слой 

logits = tf.contrib.layers.fully_connected(images_flat, 62, tf.nn.relu)

# Определение функции потерь

losstf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels = y, logits = logits))

# Определение оптимизатора 

train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)

# Конвертация логитов в индексы меток

correct_pred = tf.argmax(logits, 1)

# Определение метрики точности

accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

Вы только что успешно создали свою первую нейронную сеть с помощью TensorFlow!

Если хотите, также можно вывести значения (большинства) переменных, чтобы получить краткую сводку того, что вы только что запрограммировали:

print("images_flat: ", images_flat)

print("logits: ", logits)

print("loss: ", loss)

print("predicted_labels: ", correct_pred)

Совет. Если вы видите ошибку module ‘pandas’ has no attribute ‘computation’, обновите пакеты dask, запустив команду pip install —upgrade dask в командной строке.

Запуск нейронной сети

Теперь, когда вы собрали модель cети слой за слоем, пришло время запустить ее! Для этого сначала инициализируйте сессию с помощью Session(), которой вы передаете граф graph, определенный в предыдущем разделе. Затем вы можете запустить сессию с помощью run(), которой вы передаете инициализированные операции в форме переменной init, которую вы также определили в предыдущем разделе.

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

Как было показано в разделе об основах TensorFlow, нет необходимости закрывать сессию вручную; это уже сделано для вас. Хотите опробовать другую конфигурацию? Вам, вероятно, понадобится сделать это с помощью sess.close(), если вы определили свою сессию как sess, как в блоке ниже:

tf.set_random_seed(1234)

sess = tf.Session()

sess.run(tf.global_variables_initializer())

for i in range(201):

print('EPOCH', i)

_, accuracy_val = sess.run([train_op, accuracy], feed_dict={x: images28, y: labels})

if i % 10 == 0:

print("Loss: ", loss)

print('DONE WITH EPOCH')

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

tf.set_random_seed(1234)

with tf.Session() as sess:

sess.run(tf.global_variables_initializer())

for i in range(201):

_, loss_value = sess.run([train_op, loss], feed_dict={x: images28, y: labels})

if i % 10 == 0:

print("Loss: ", loss)

Обратите внимание, что используется функция global_variables_initializer(), потому что функция initialize_all_variables() устарела.

Вы успешно обучили свою модель! Это было не слишком сложно, не так ли?

Оценка вашей нейронной сети

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

Давайте воспользуемся matplotlib и выведем дорожные знаки для визуального сравнения:

# Импорт `matplotlib`

import matplotlib.pyplot as plt

import random

# Выбор 10 случайных изображений

sample_indexes = random.sample(range(len(images28)), 10)

sample_images = [images28[i] for i in sample_indexes]

sample_labels = [labels[i] for i in sample_indexes]

# Запуск операции "correct_pred"

predicted = sess.run([correct_pred], feed_dict={x: sample_images})[0]

# Вывод истинных и предсказанных меток

print(sample_labels) print(predicted)

# Вывод предсказанных и истинных изображений

fig = plt.figure(figsize=(10, 10))

for i in range(len(sample_images)):

truth = sample_labels[i]

prediction = predicted[i]

plt.subplot(5, 2,1+i)

plt.axis('off') color='green' if truth == prediction else 'red'

plt.text(40, 10, "Truth: {0}\nPrediction: {1}".format(truth, prediction), fontsize=12, color=color)

plt.imshow(sample_images[i], cmap="gray")

plt.show()

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

Обратите внимание, что используется функция load_data(), которая была определена в начале этого руководства.

# Импорт`skimage`

from skimage import transform

# Загрузка данных проверки

test_images, test_labels = load_data(test_data_directory)

# Преобразование изображений в 28 х 28 пикселей

test_images28 = [transform.resize(image, (28, 28)) for image in test_images]

# Конвертация в оттенки серого

from skimage.color import rgb2gray

test_images28 = rgb2gray(np.array(test_images28))

# Вывод предсказаний и полного набора данных проверки

predicted = sess.run([correct_pred], feed_dict={x: test_images28})[0]

# Вычисление совпадений 

match_count = sum([int(y == y_) for y, y_ in zip(test_labels, predicted)])

# Вычисление точности

accuracy = match_count / len(test_labels)

# Вывод точности

print("Accuracy: {:.3f}".format(accuracy))

Не забудьте закрыть сессию помощью sess.close(), если вы не использовали команду with tf.Session() as sess:, чтобы начать сеанс TensorFlow.

Что делать дальше с моделью?

Если вы хотите продолжить работу с этим датасетом и моделью, которую вы построили в этом туториале, попробуйте выполнить следующее:

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

Если знаете английский язык, обязательно ознакомьтесь с книгой Machine Learning With TensorFlow, написанной Nishant Shukla.


Интересные статьи:

TensorFlow туториал. Часть 3: работа с данными

26 сентября 2018
tensorflow анализ и работа с данными

TensorFlow туториал. Часть 3: работа с данными

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

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


Перед прочтением статьи рекомендуем изучить:


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

  • Текст на дорожных знаках в Бельгии обычно приведен на голландском и французском языках. Это полезно знать, но для датасета, с которым вы будете работать, это не слишком важно!
  • В Бельгии шесть категорий дорожных знаков: предупреждающие знаки, знаки приоритета, запрещающие знаки, предписывающие знаки, знаки, связанные с парковкой и стоянкой у дорог, и, наконец, обозначения.
  • 1 января 2017 года с бельгийских дорог было снято более 30 000 дорожных знаков. Это были запрещающие знаки, ограничивающие максимальную скорость вождения.
  • Снятие знаков связано с длительной дискуссией в Бельгии (и во всем Европейском Союзе) о чрезмерном количестве дорожных знаков.

Загрузка датасета

Теперь, когда вы знакомы со спецификой бельгийского трафика, пришло время скачать датасет. Вы должны скачать два zip-файла в разделе «BelgiumTS for Classification (cropped images)» с названиями «BelgiumTSC_Training» и «BelgiumTSC_Testing».

Совет. Если вы скачали файлы или сделаете это после прочтения туториала, взгляните на структуру папок, которые вы загрузили! Вы увидите, что папки тестирования и обучения содержат 61 подпапку, соответствующую 61 типам дорожных знаков, которые вы будете использовать для классификации в этом туториале. Кроме того, вы обнаружите, что файлы имеют расширение .ppm или Portable Pixmap Format. Вы скачали изображения дорожных знаков!

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

  • Сначала установите ROOT_PATH. Этот путь к каталогу, в котором находятся данные для обучения и тестирования.
  • Затем добавьте пути к вашему ROOT_PATH с помощью функции join(). Сохраните эту пути в train_data_directory и test_data_directory.
  • Вы увидите, что после этого можно вызвать функцию load_data() и передать в нее train_data_directory.
  • Теперь сама функция load_data() запускается путем сбора всех подкаталогов, присутствующих в train_data_directory; она делает это с помощью спискового включения, что является естественным способом составления списков — то есть, если вы найдете что-то в train_data_directory, вы проверяете, папка ли это и, если так и есть, добавляете ее в свой список. Помните, что каждый подкаталог представляет собой метку.
  • Затем вам нужно перебрать подкаталоги. Сначала вы инициализируете два списка, labels и images. Затем вы собираете пути подкаталогов и имена файлов изображений, которые хранятся в этих подкаталогах. После этого можно собрать данные в двух списках с помощью функции append().
def load_data(data_directory):

directories = [d for d in os.listdir(data_directory)

if os.path.isdir(os.path.join(data_directory, d))]

labels = []

images = [] for d in directories:

label_directory = os.path.join(data_directory, d)

file_names = [os.path.join(label_directory, f)

for f in os.listdir(label_directory)

if f.endswith(".ppm")]

for f in file_names:

images.append(skimage.data.imread(f))

labels.append(int(d))

return images, labels

ROOT_PATH = "/your/root/path"

train_data_directory = os.path.join(ROOT_PATH, "TrafficSigns/Training")

test_data_directory = os.path.join(ROOT_PATH, "TrafficSigns/Testing")

images, labels = load_data(train_data_directory)

Обратите внимание, что в приведенном выше блоке данные для обучения и тестирования находятся в папках с названиями «training» и «testing», которые являются подкаталогами другого каталога «TrafficSigns». На компьютере это может выглядеть примерно так: «/Users /Name /Downloads /TrafficSigns», а затем две подпапки под названием «training» и «testing».

Исследование данных

Когда данные загружены, пришло время для их исследования! Для начала можно провести элементарный анализ с помощью атрибутов ndim и size массива images:

Обратите внимание, что переменные images и labels являются списками, поэтому вам, возможно, придется воспользоваться np.array() для преобразования переменных в массив в рабочей области. Но здесь это уже сделано для вас!

# Вывести размерность 'images'
print(images.ndim)

# Вывести количество элементов в 'images'
print(images.size)

# Вывести первое значение 'images'
images[0]

Обратите внимание, что значение images[0], которое вы вывели, на самом деле представляют собой одно изображение, представленное массивами в массиве! Это может показаться нелогичным, но вы привыкните к этому, когда будете работать с изображениями в проектах машинного или глубокого обучения.

Далее, обратимся к labels, но на этот раз никаких сюрпризов не будет:

# Вывести размерность 'labels'
print(labels.ndim)

# Вывести число элементов в 'labels'
print(labels.size)

# Вывести длину массива 'labels'
print(len(set(labels)))

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

Совет. Попробуйте добавить в свои массивы следующие атрибуты, чтобы получить дополнительную информацию о распределении памяти: длину одного элемента массива в байтах и ​​общее количество байтов, занимаемых элементами массива: flags, itemsize и nbytes.

Затем можно изучить распределение дорожных знаков по типам:

# Импортировать модуль 'pyplot'
import matplotlib.pyplot as plt

# Построить гистограмму с 64 точками - значениями 'labels'
plt.hist(labels, 62)

# Вывести график
plt.show()

Отлично! Давайте посмотрим на полученную гистограмму:

изучение данных в tensorflow

Хорошо видно, что не все типы дорожных знаков одинаково представлены в датасете. Это то, с чем вы столкнетесь позже, когда будете работать с данными, прежде чем приступать к моделированию нейронной сети. На первый взгляд видно: количество знаков типов 22, 32, 38 и 61 определенно преобладает над остальными. Запомните это, в следующем разделе вам пригодится эта информация.

Визуализация данных

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

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

  • Во-первых, убедитесь, что вы импортируете модуль pyplot пакета matplotlib под общепринятым названием plt.
  • Затем создайте список из 4 случайных чисел. Они будут использоваться для выбора дорожных знаков из массива images, загруженного в прошлом разделе. В нижеследующем примере это будут числа 300, 2250, 3650 и 4000.
  • Затем для каждого элемента этого списка, от 0 до 4, вы создаете графики без осей (чтобы они не мешали сосредоточиться на изображениях). На этих графиках вы увидите конкретные изображения из массива images, которые соответствует номеру индекса i. На первом шаге цикла вы получите i = 300, во втором — 2250 и так далее. Наконец, нужно расположить графики так, чтобы между ними было достаточно пространства.
  • Последнее — выведите ваши графики с помощью функции show()!

Код:

# Импорт модуля 'pyplot' 'matplotlib`

import matplotlib.pyplot as plt

# Задание (случайных) номеров изображений, которые вы хотите вывести

traffic_signs = [300, 2250, 3650, 4000]

# Заполнение графиков изображениями

for i in range(len(traffic_signs)):

plt.subplot(1, 4, i+1)

plt.axis('off')

plt.imshow(images[traffic_signs[i]])

plt.subplots_adjust(wspace=0.5)

plt.show()

Как можно догадаться, знаки каждого из 62 типов отличаются друг от друга.

Но что еще можно заметить? Взглянем на изображения ниже:

работа с данными

Эти четыре изображения имеют разный размер!

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

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

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

# Импорт 'matplotlib'

import matplotlib.pyplot as plt

# Задание (случайных) номеров изображений, которые вы хотите вывести

traffic_signs = [300, 2250, 3650, 4000]

# Заполнение графиков изображениями и вывод размеров

for i in range(len(traffic_signs)):

plt.subplot(1, 4, i+1)

plt.axis('off')

plt.imshow(images[traffic_signs[i]])

plt.subplots_adjust(wspace=0.5)

plt.show()

print("shape: {0}, min: {1}, max: {2}".format(images[traffic_signs[i]].shape, images[traffic_signs[i]].min(), images[traffic_signs[i]].max()))

Обратите внимание на то, как вы используется метод format() в строке «shape: {0}, min: {1}, max: {2}», чтобы заполнить аргументы {0}, {1} и {2}.

визуализация данных tensorflow

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

# Импорт модуля 'pyplot' 'matplotlib'

import matplotlib.pyplot as plt

# Задание типов

unique_labels = set(labels)

# Инициализация графика

plt.figure(figsize=(15, 15))

# Задание счетчика

i = 1

# Для каждого типа:

for label in unique_labels:

# Выбирается первое изображение каждого типа:

image = images[labels.index(label)]

# Задание 64 графиков

plt.subplot(8, 8, i)

# Выключение осей

plt.axis('off')

# Добавление заголовка каждому графику

plt.title("Label {0} ({1})".format(label, labels.count(label)))

# Увеличить значение счетчика на 1

i += 1

# Вывод первого изображения

plt.imshow(image)

# Вывод всего графика

plt.show()

Обратите внимание, что даже если вы определяете 64 графика, не на всех из них будут изображения (так как есть всего 62 типа знаков!). Обратите также внимание на то, что опять же, вы не выводите оси, чтобы не отвлекаться на них.

анализ данных в Tensorflow

Как было видно из гистограммы, количество фотографий знаков с типами 22, 32, 38 и 61 значительно больше остальных. Эта видно из из графика выше: есть 375 снимков с меткой 22, 316 снимков с меткой 32, 285 снимков с меткой 38 и, наконец, 282 снимка с меткой 61.

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

Давайте рассмотрим более подробно: видно, что метки 22 и 32 являются запретительными знаками, но метки 38 и 61 являются указательными знаками и знаками приоритета, соответственно. Это означает, что между этими четырьмя знаками нет непосредственной связи, за исключением того факта, что половина знаков, наиболее широко представленных в датасетах, являются запрещающими.

Извлечение признаков

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

  • Изображения имеют разный размер;
  • Есть 62 метки (помним, что нумерация меток начинаются с 0 и заканчиваются на 61);
  • Распределение типов знаков трафика довольно неравномерно; между знаками, которые в большом количестве присутствовали в наборе данных, нет никакой связи.

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

Масштабирование изображений

Чтобы сделать размеры изображений одинаковыми, можно воспользоваться функцией skimage или библиотекой Scikit-Image, которая представляет собой набор алгоритмов для обработки изображений.

Во втором случае случае будет полезен модуль transform, так как в нем есть функция resize(); она снова использует списковое включение, чтобы сделать разрешение снимков равным 28×28 пикселей. Повторюсь: вы увидите, что фактически составляете список — для каждого изображения в массиве image вы выполните операцию преобразования, которую позаимствуете из библиотеки skimage. Наконец, вы сохраняете результат в переменной images28:

# Импорт модуля 'transform' из 'skimage'

import transform

# Масштабирование изображений в 'image'

array images28 = [transform.resize(image, (28, 28)) for image in images]

Выглядит очень просто, не так ли?

Обратите внимание, что изображения теперь четырехмерны: если вы конвертируете images28 в массив и привязываете атрибут shape, видно, что размеры imeges28 равны (4575, 28, 28, 3). Изображения 784-мерные (потому что ваши изображения имеют размер 28 на 28 пикселей).

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

Результат:

результат визуализации данных tensorflow

Обратите внимание, что, поскольку вы изменили масштаб, значения min и max также изменились; сейчас они все лежат в одном диапазоне, что действительно здорово, потому что теперь вам не нужно производить нормировку данных!

Преобразование изображений в оттенки серого

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

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

Как и при масштабировании, вы можете использовать библиотеку Scikit-Image; в этом случае вам понадобится модуль color с функцией rgb2gray().

Это будет просто!

Однако не забудьте преобразовать переменную images28 в массив, так как функция rgb2gray() в качестве аргумента принимает именно массивы.

# Импорт `rgb2gray` из`skimage.color`

from skimage.color import rgb2gray

# Конвертация `images28` в массив

images28 = np.array(images28)

# Конвертация `images28` в оттенки серого

images28 = rgb2gray(images28)

Проверьте результат вашего преобразования в оттенки серого, выведя некоторые из изображений; для этого используйся несколько модифицированный код:

import matplotlib.pyplot as plt

traffic_signs = [300, 2250, 3650, 4000]

for i in range(len(traffic_signs)):

plt.subplot(1, 4, i+1)

plt.axis('off')

plt.imshow(images28[traffic_signs[i]], cmap="gray") plt.subplots_adjust(wspace=0.5)

# Вывод графика

plt.show()

Обратите внимание, что вам обязательно нужно указать цветовую карту или cmap и выставить значение ‘gray’ для вывода изображений в оттенках серого. Это связано с тем, что imshow() по умолчанию использует тепловую цветовую карту.


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

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

В следующей частиглубокое обучение c TensorFlow.


Интересные статьи:

TensorFlow туториал. Часть 2: установка и начальная настройка

24 сентября 2018
установка Tensorflow

TensorFlow туториал. Часть 2: установка и начальная настройка

Теперь, когда вы получше узнали TensorFlow, пора приступить к работе с ним и установить библиотеку. Важно знать, что TensorFlow предоставляет API для Python, C ++, Haskell, Java, Go, Rust. Также…

Теперь, когда вы получше узнали TensorFlow, пора приступить к работе с ним и установить библиотеку. Важно знать, что TensorFlow предоставляет API для Python, C ++, Haskell, Java, Go, Rust. Также существует сторонний пакет для R.

Читайте также Часть 1: тензоры и векторы

После прочтения этого туториала вы сможете скачать версию TensorFlow, которая позволит вам написать код для проекта по глубокому обучению на Python. На странице установки TensorFlow представлены некоторые из наиболее распространенных способов установки (и советы по ним) с использованием virtualenv, pip, Docker, а также несколько других способов инсталляции.

Примечание. Если вы работаете на Windows, можно установить TensorFlow с помощью Conda. Однако, поскольку существует сообщество для помощи при инсталляции TensorFlow, лучше изучить официальные инструкции по установке.

Проверим, что TensorFlow установлен корректно. Для этого его нужно импортировать в рабочую область под названием tf:

import tensorflow as tf

Обратите внимание, что название, использованное в данной строчке кода, является своего рода соглашением. Оно общепринято как в сообществе разработчиков, использующих TensorFlow в своих проектах Data Science, так и в TensorFlow-проектах с открытым исходным кодом.

Начало работы с TensorFlow

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

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

Во-первых, импортируйте библиотеку tensorflow под названием tf, как было показано в предыдущем разделе. Затем инициализируйте две переменные-константы. Подайте массив из четырех чисел в функцию constant().

Обратите внимание, что можно также подать целое число, но чаще всего вы будете работать с массивами. Как вы увидели во введении, тензоры — это и есть массивы! Так что убедитесь, что передаете массив. Затем можно использовать multiply() для перемножения двух переменных. Сохраните результат в переменной result. Окончательно, выведите результат с помощью функции print().

# Import `tensorflow`
import tensorflow as tf

# Initialize two constants
x1 = tf.constant([1,2,3,4])
x2 = tf.constant([5,6,7,8])

# Multiply
result = tf.multiply(x1, x2)

# Print the result
print(result)

Обратите внимание, что для запуска интерактивной сессии можно использовать нижеследующий код. Запустите result и автоматически закройте сессию после вывода output:

# Import `tensorflow`
import tensorflow as tf

# Initialize two constants
x1 = tf.constant([1,2,3,4])
x2 = tf.constant([5,6,7,8])

# Multiply
result = tf.multiply(x1, x2)

# Initialize Session and run `result`
with tf.Session() as sess:
output = sess.run(result)
print(output)

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

Например, если добавить

config= tf.ConfigProto (log_device_placement = True)

к сессии, вы убедитесь, что зарегистрировали устройство GPU или CPU, назначенное для этой операции. Затем вы получите информацию о том, какие устройства используются в сессии для каждой операции. Если вы используете мягкие ограничения для размещения устройств, можно использовать следующую конфигурацию сессии:

congif= tf.ConfigProto (allow_soft_placement = True)

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

В следующей частиработа с данными в TensorFlow.

Архитектура Turing от Nvidia: что она дает и как получить максимум производительности

24 сентября 2018
Turing SDK

Архитектура Turing от Nvidia: что она дает и как получить максимум производительности

Архитектура Turing – одно из главных достижений в компьютерной графике за 20 лет, по заявлению самой Nvidia. Turing SDK теперь доступен для разработчиков. Дизайнеры и художники могут создавать контент с улучшенной…

Архитектура Turing – одно из главных достижений в компьютерной графике за 20 лет, по заявлению самой Nvidia. Turing SDK теперь доступен для разработчиков. Дизайнеры и художники могут создавать контент с улучшенной графикой, фотореалистичным рендерингом, использовать AI для обработки изображений и видео в режиме реального времени с помощью NVIDIA RTX. Рассказываем о разработках Nvidia, которые помогут исследователям раскрыть все возможности новой архитектуры.

CUDA 10

CUDA 10 включает поддержку Turing GPUs, оптимизированных по производительности библиотек, новую асинхронную модель программирования графов, улучшенную интеграцию CUDA и графического API и новые инструменты разработки. CUDA 10 также предоставляет все составляющие, необходимые для создания серверных платформ NVIDIA для AI и нагрузок высокопроизводительных вычислений (HPC), как локально (DGX-2), так и в облаке (HGX-2).

TensorRT 5 (доступен с 20 сентября)

TensorRT 5 показывает до 40 раз производительность вывода по сравнению с CPUs благодаря новым оптимизациям, APIs и поддержке Turing GPUs. Он оптимизирует точность в рекомендательные системах, машинном переводе, обработке естественного языка. Основными особенностями TensorRT 5 являются INT8 APIs, предлагающие новые гибкие схемы работы, оптимизация для глубокой свертки, поддержка платформ NVIDIA Drive на основе Xavier и акселератор NVIDIA DLA. Кроме того, TensorRT 5 поддерживает операционные системы Windows и CentOS. Смотрите туториал.

cuDNN 7.3

Главные особенности фреймворка cuDNN 7.3 — улучшенная сгруппированная свертка для формата данных NHWC и расширенная производительность сверток для ResNet50, DeepSpeech2 и Wavenet.

NCCL 2.3

Фреймворки, использующие NCCL 2.3 и более поздние версии, заточены на работу с архитектурами Volta и Turing для обеспечения высокой производительности многоузловых и многопроцессорных вычислений глубокого обучения. Новые функции включают улучшенные алгоритмы с низкой задержкой для сообщений небольших размеров и удобная регулировка при использовании графических процессоров Direct P2P и RDMA.

CUTLASS 1.1

CUTLASS 1.1 позволяет использовать Tensor Cores Turing для высокопроизводительного умножения матриц в CUDA C ++. Новые особенности включают поддержку CUDA 10 и новых матричных функций для доступа к Turing.

NVIDIA RTX — NGX SDK

Архитектура Turing заточена под новейшее GPU — Nvidia RTX. Стоимость новых видеокарт — от $599 до $1199. NGX — новый технологический стек, в котором реализованы алгоритмы, которые ускоряют и улучшают графику, обработку изображений и видео непосредственно в приложениях. NGX SDK использует предварительно подготовленные сети, что позволяет разработчикам легко интегрировать алгоритмы.

SDK будет доступен в ближайшее время.

VRWorks Graphics 3.0

Функциональные особенности VRWorks Graphics предлагают новый уровень визуальной точности, производительности и скорости реагирования в виртуальной реальности. Эта версия, объединенная с Turing на базе GPU, включает в себя переменную скорость раскраски (Variable Rate Shading) и визуализация в нескольких проекциях (Multi-View Rendering). Variable Rate Shading — новая технология визуализации, которая выделяет больше мощности GPU для обработки полутонов детализированных областей сцены и меньшую для менее подробных участков.

Multi-view рендеринг
Multi-view рендеринг

Multi-View Rendering расширяет возможности Single Pass Stereo, увеличивая количество проекционных центров или представлений для одного прохода с двух до четырех. Все четыре вида, доступные за один проход, теперь не зависят от положения и перемещаются вдоль любой оси в проекционном пространстве, что позволяет поддерживать новые конфигурации дисплея.

Инструменты разработчика

Nsight Compute 1.0

NVIDIA Nsight Compute — это инструмент нового поколения, который обеспечивает интерактивную отладку CUDA API и профилирование ядра. Эта версия Nsight Compute предлагает быстрый сбор данных о показателях производительности и отладке API с помощью пользовательского интерфейса и командной строки.

Nsight Systems 2018.2

NVIDIA Nsight Systems — это инструмент для анализа производственных характеристик, позволяющий разработчики оптимизировать программное обеспечение, например, выявлять узкие места в процессорах и графических процессорах. Обновления в Nsight Systems 2018.2 включают поддержку CUDA 10, обновление интерфейса командной строки для реализации новых сценариев использования, улучшенную совместимость и удобство использования.

Nsight Graphics 2018.5

Nsight Graphics — автономный инструмент разработки, позволяющий отлаживать, профилировать и экспортировать фреймы, созданные с помощью популярных графических API. Версия 2018.5 обеспечивает публичную доступность GPU Trace, поддерживает расширения Direct3D 12 DXR и Vulkan Ray Tracing, расширяет функционал для DirectX 12 и поддерживает Windows RS3 DirectX 12 SDK.

Nsight VSE 6.0

NVIDIA Nsight Visual Studio Edition — это среда разработки приложений для GPUs, которая позволяет создавать, отлаживать, профилировать и отслеживать набор приложений. Обновления в Nsight VSE 6.0 включают графическую отладку с поддержкой трассировки лучей, расширенную отладку и анализ с поддержкой CUDA 10.


Интересные статьи: