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

Введение

Искусственные нейронные сети (ИНС) существуют уже давно (с середины прошлого века!), но приобрели популярность в конце 80-х, а совсем недавно — с появлением глубокого обучения.

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

Основная способность нейронной сети заключается в том, чтобы делать «вывод» о том, что ей скармливают.

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

Сеть делает вывод, изображена ли на картинке кошка или собака.

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

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

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

Строительные блоки: нейроны

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

Его функция очень проста: он принимает некоторые входные данные xᵢ и выдает результат o в соответствии со значением входных данных.

Первая математическая запись

Давайте попробуем использовать математическую нотацию, чтобы представить это. Что такого в математике, что принимает какие-то входные данные и производит результат? Функция!

Таким образом, мы можем легко представить нейрон как функцию f некоторых входов x₁, …, xn:

f(x₁​, x₂​,…,xn)

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

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

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

f(x₁, x₂, …, xn) = w₁ x₁ + w₂ x₂ + … + wn xnₙ

Или с более компактной записью:

f(x₁, x₂, …, xn) = ∑ᵢwᵢ xᵢ

Где wᵢ - веса для каждого входа xᵢ

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

Ну, пока немного, это просто то, что в математике называется «линейной комбинацией».

Пример И

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

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

If condition1 is true AND condition2 is true then cross the road

Каждое условие состоит в том, что с этой стороны дороги не едет машина.

Это то, что называется логическим И.

Обозначим истину 1, а ложь 0.

Чтобы перейти дорогу, мне нужна функция, которая дает мне 1, если два входа равны 1, и 0 в противном случае.

Давайте напишем так называемую «таблицу истинности» для нашего вентиля И, чтобы понять, какими должны быть выходные данные для каждой комбинации входных данных:

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

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

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

Конечно, я мог бы установить для каждого w что-то вроде 0,3, чтобы иметь 0,3 (что ближе к 0), если активен только один вход, и 0,6 (что ближе к 1), если активны оба входа. Таким образом, если я смогу округлить вывод до ближайшего целого числа, нейрон выдаст ожидаемый результат.

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

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

Эта часть называется функцией активации.

Функция активации

Как следует из названия, функция активации «активируется», когда ее вход достигает определенного значения.

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

Сигмовидная функция принимает входные данные в ]-inf, +inf[ и отображает их в ]0, 1[

Итак, функция нашего нейрона теперь будет выглядеть так:

f (x₁, x₂) = сигмоид (w₁x₁ + w₂x₂)

Наша обновленная модель нейрона будет выглядеть так:

Где σ — активационная функция нейрона.

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

Кроме того, сигмоид центрируется вокруг 0, так что сигмоид (0) = 0,5. В нашем примере это не сработает, так как мы хотим, чтобы f(0,5) было как можно ближе к 0.

предвзятость

Давайте представим последнюю недостающую часть, «предвзятость».

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

f (x₁, x₂) = сигмоид (w₁x₁ + w₂x₂ + b)

Где b — наш уклон.

Наша окончательная модель нейрона будет выглядеть так:

В математике ввод сигмоиды w₁x₁ + w₂x₂ + b по-прежнему представляет собой просто линейную комбинацию.

Теперь мы можем установить наши веса на что-то высокое, например 10, чтобы мы могли насытить нашу сигмовидную диаграмму и сделать ее выход как можно ближе к 1 или 0, а также сдвинуть входную сигмовидную диаграмму назад на 15, чтобы получить что-то ближе к границам:

Это сработало! Нам удалось воспроизвести таблицу И с искусственным нейроном.

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

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

Обучение нейронной сети — это то, о чем я пока не буду вдаваться в подробности в этом посте.

Но вернемся к нашему примеру: почему так важно, чтобы наш единственный нейрон научился выполнять операцию И?

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

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

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

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

С этой функцией гораздо проще рассуждать (но на практике она не используется для многих задач, таких как недифференцируемость и исчезновение градиентов — пока не беспокойтесь об этом!).

Это очень простая функция, которая выводит 1, если вход = 0, и 0 в противном случае.

Это означает, что наш нейрон И может быть просто:

f (x₁, x₂) = хевисайд (x₁ + x₂–1,5)

Давайте теперь построим все возможные значения x₁ и x₂ на декартовой плоскости:

Я выделил красным точку (1, 1), которая активирует логический элемент И, дающий на выходе 1, и синим цветом все остальные точки, которые дают на выходе 0.

Помните уравнение линии: y = ax + b, где a определяет наклон, а b — насколько сильно смещена линия от начала координат.

Теперь давайте представим линейную комбинацию, происходящую в нашем нейроне И, как линию:

x₁ + x₂–1,5 на самом деле можно записать как x₂ = -x₁ + 1,5, чтобы было понятно, что это линия с отрицательным наклоном, сдвинутая на 1,5:

Ты видишь, что там происходит? Наша линейная комбинация входных данных соответствует линии, которая отделяет интересующую нас точку (1, 1), которая является единственной, удовлетворяющей соотношению И, от трех других точек.

Применение функции Хевисайда означает, что все, что выше этой линии, будет равно 1, а все, что ниже, будет равно 0.

Вам понадобятся три оси, чтобы визуализировать это, поскольку две оси — это x1 и x2, а третья — это выходное значение функции Хевисайда. Но так как это значение равно 0 или 1, мы можем просто использовать два разных цвета и сохранить нашу красивую двухмерную плоскость, чтобы визуализировать происходящее:

Именно поэтому я не использовал сигмовидную функцию. Было бы трудно визуализировать в 2-D. Вместо этого вы бы увидели трехмерную версию сигмоиды, что-то вроде этого:

Классификация против регрессии

Таким образом, линия, параметризованная весами и смещением, действует как разделитель между «хорошими» точками (теми, которые мы хотим присвоить 1, чтобы они проходили) и «плохими» точками.

Задачи такого типа, когда вы хотите разделить некоторые точки на два «класса», также называются проблемами бинарной классификации: классификацией, поскольку мы хотим, чтобы точки принадлежали к «классу», бинарными, поскольку мы есть только хорошие и плохие баллы (0 против 1), поэтому два класса.

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

Примером проблемы классификации может быть различение изображений кошек и собак. У вас есть только два класса, класс «кошка» и класс «собака». Конечно, вы в конечном итоге будете представлять кошек и собак числом, например, 0 и 1.

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

Итак, подытожим: веса и смещение действуют на эту разделительную линию, как и следовало ожидать из уравнения линии: веса определяют наклон, а смещение — смещение.

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

Очевидно, что в нашем случае есть несколько решений.

Проблема зашумленных данных

В реальных примерах есть еще одна проблема: данные, которые у вас есть, зашумлены. В нашей задаче И мы могли передать нейрону только 4 примера, 4 пары двоичных входов. Но представьте, если бы вы не могли быть уверены, едет машина или нет. У вас будут реальные числа вместо нулей и единиц для моделирования неопределенности.

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

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

Немного об обобщении

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

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

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

Я расскажу больше об обобщении в следующем посте, когда подробно расскажу, как обучать сеть, и представлю такие понятия, как переобучение, проверка и другие.

Пример XOR — полная нейронная сеть

Давайте попробуем более сложный пример, вентиль XOR.

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

По сути, он выведет 1, если только один из двух входных данных верен. Но если они оба истинны, он выведет 0.

XOR можно уменьшить с помощью OR, AND и NOT, как показано ниже:

x1 XOR x2 = (x1 OR x2) AND NOT(x1 AND x2)

Мы уже знаем, как выглядит И-нейрон:

и(x₁​, x₂​)=h(x₁​+x₂​–1,5)

Где h обозначает функцию Хевисайда.

Нейрон ИЛИ можно выразить так (вы можете написать таблицу истинности, если не верите мне):

or(x₁​,x₂​)=h(x₁​+x₂​–0.5)

Нам нужно инвертировать первое И, чтобы включить НЕ (формируя то, что называется вентилем И-НЕ), но мы можем сделать это, установив веса на -1 и смещение на 1,5:

nand(x₁​,x₂​)=h(−x₁​–x₂​+1,5)

Складывая вышеуказанные функции вместе, мы получаем:

xor(x₁, x₂) = h(h(x₁+x₂–0,5)+h(-x₁-x₂+1,5)-1,5)

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

Первый нейрон — это вентиль ИЛИ, второй — И-НЕ, а последний — И.

Если вы напишете таблицу истинности вышеизложенного, вы увидите, что она соответствует таблице XOR!

Но перейдем на декартову плоскость:

Теперь нам нужно, чтобы наша функция (выход сети) была равна 1 только для точек (1, 0) и (0, 1).

Следовательно, нам нужен способ отделить эти две точки от двух оставшихся.

Но подождите секунду... это невозможно с помощью всего одной строки!

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

Давайте еще раз посмотрим на структуру нашей сети:

Существуют разные номенклатуры и способы подсчета слоев, но можно сказать, что эта сеть имеет 2 слоя: первый слой (с нейронами ИЛИ и НЕ-И), также называемый «скрытым» слоем, поскольку он спрятан между входами и выходами, и выходной слой (И).

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

Можно ли было сделать это всего одним слоем? Ну не совсем. В противном случае, как вы могли бы объединить результаты вместе?

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

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

x₁x₂x₃+-x₂x₄+x₅x₆

Однако легче обучать более «глубокие» сети (созданные из большего количества слоев), поскольку они могут легче захватывать сложные структуры (каждый слой будет обнаруживать шаблоны в шаблонах шаблонов и т. д.).

Я пока оставлю интуицию в покое, но вернусь к ней позже.

Нейронные сети и линейные преобразования

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

Давайте рассмотрим первый слой нашей двухслойной сети, которая решает проблему XOR: у нас есть набор входных данных, x₁ и x₂, и два веса для каждого нейрона, w₁₁ и w₁₂ для первого нейрона и w₂₁ и w₂₂ для второго.

Каждый нейрон выдает выходные данные o₁ и o₂, которые затем передаются в последний слой.

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

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

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

Следовательно, выход первого слоя [o₁ o₂] становится:

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

Но ⃗x W — это не что иное, как применение линейного преобразования, определяемое W, к входному вектору ⃗x.

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

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

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

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

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

Что это обозначает? Что ж, давайте посмотрим на пример XOR.

Первый слой преобразует ввод [x₁, x₂] в [o₁, o₂], применяя операции ИЛИ и НЕ-И:

Помните, одной строки было недостаточно, чтобы отделить точки (x₁, x₂), соответствующие XOR, от остальных?

Давайте теперь построим точки в новом пространстве (o₁, o₂):

Две точки схлопнулись друг на друга в (1, 1), и это точки XOR! Так что теперь можно отделить XOR от других точек всего лишь линией. Вот что будет делать последний слой: он найдет линию, разделяющую эти три точки! Эта строка в нашем случае соответствует И.

Еще одна перспектива

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

Таким образом, в нашей сети XOR первый нейрон нашел полезную связь входных данных (отношение ИЛИ), второй нейрон нашел отношение И-НЕ, а третий нейрон нашел отношение между этими отношениями (отношение И). .

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

Помните, что эти отношения обнаруживаются путем обучения сети (я не объяснил, как), что, в свою очередь, означает поиск параметров w и b для каждого нейрона.

Другие типы сетей

В этом посте я ограничился простой полносвязной сетью с прямой связью (сеть, в которой все нейроны слоя связаны с нейронами следующего слоя, и соединения только прямые).

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

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

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

Но встраивание в сеть «структуры» (например, свертки) облегчит обучение, поскольку обучающему алгоритму не придется самостоятельно выяснять, что такое свертка. Это также может быть контрпродуктивным в тех случаях, когда свертки могут не очень помочь (например, принцип локальности неприменим), но это тема для другого поста!

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

Первоначально опубликовано на http://www.pietrocavallo.it/blog 29 августа 2020 г.