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

Каждый раз, когда я слышу «он учится» или «у него есть разум», я думаю: «Ну, это просто множество утверждений if / else, не так ли?». Ну, технически да, но совсем не так, как я думал. Чтобы решить свою дилемму, я решил изобрести велосипед и построить его с нуля. Было ли это полезно для какого-либо коммерческого решения? Конечно, нет. Был ли ваш первый «Hello World» коммерчески полезным? Ничего подобного, но я думаю, что было очень важно начать царапать верхушку айсберга. Думаю, здесь то же самое. Я хочу показать, что называется обучением и что на самом деле делает нейронная сеть. Спойлер: это всего лишь производные!

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

Он имеет в общей сложности 784 пикселя (28 * 28), каждый из которых находится между полностью черным или полностью белым. Представьте себе, что вы записываете некоторые if / else, чтобы проверить пиксель за пикселем, образуют ли они восьмерку: распознавать два замкнутых круга? Во-первых, что такое круг (в пикселях)? Что делать, если число немного наклонено? Как видно, восьмерка не совсем закрыта. Плохая 8 все равно 8, не так ли? Перевёрнутая тройка по-прежнему остается тройкой? А как насчет 6 против 9 и как поступать в таких случаях? Это момент, когда мы перестаем использовать обычную логику программирования и начинаем применять нейронную сеть для решения этой проблемы.

Прежде всего, давайте рассмотрим систему черного ящика, которая отвечает: «На этом изображении есть верхний замкнутый кружок?» и еще один, который отвечает так же о любом нижнем замкнутом круге. Оба они возвращают логическое значение. Я сейчас очень абстрактен и выполняю сложные задачи, и мне очень жаль, но, пожалуйста, продолжайте. Назовем их соответственно a1 и a2. Итак, чтобы определить число 8, у нас будет isThisAnEight = a1 && a2. Для большей гибкости давайте использовать число с плавающей запятой между 0 и 1 вместо логического, где 1 соответствует 100% истине, а 0 - полностью ложному значению. Теперь наши удивительные вымышленные машины a1 и a2 могут возвращать .6 и 1. Это все еще 8? А как насчет .7 и .9? Мы можем настроить, как мы ответим на эту дилемму, произвольно конкретизируя:

isThisAnEight = 1.2*a1 + .8*a2 + .4

Теперь мы добавляем больший вес к a1, а не к a2, чтобы решить, является ли это восьмеркой, и даже добавляем много смещения (.4), что означает что-то вроде мы уже ожидаем 8, давайте потребуем меньше уверенности от наших машин. . Чтобы быть уверенным, что это значение не взорвется до очень больших номеров модулей, мы можем инкапсулировать правую часть уравнения с помощью функции, которая принимает любое значение и выводит от 1 до 0, сохраняя нашу предыдущую логику о истинности или ложности. Назовем эти константы и инкапсулируем с помощью ограничивающей функции s:

w2*a2 + w1*a1 + b1 выводит любое неограниченное значение

isThisAnEight = s(w2*a2 + w1*a1 + b1), выводится от 0 до 1 из-за s

А теперь представьте, что мы имеем дело с числом 5. Сколько машин мы должны рассмотреть, чтобы определить число 5? А насчет числа 6? Независимо от того, сколько, идея все та же:

someOutput = s(w1*a1 + w2*a2 + ... + wn*an + b1), где у нас есть уникальный вес для каждого входа и одно смещение для каждого выхода. Загвоздка в том, что любой ввод может быть выводом другого выражения, состоящего из нескольких его собственных вводов! Таким образом, у нас могут быть слои, где первые представляют абстрактные линии (прямые и изогнутые), промежуточные - это круги и определенные углы (прямые углы и т. Д.), А последние - это ровно 10 возможных цифр (от 0 до 9). Еще одна загвоздка: мы можем повторно использовать a2 для определения нижнего круга 6 и a1 для определения верхнего круга 9. Это означает, что один и тот же входной сигнал может быть вставлен в разные выходы! Таким образом, все эти произвольные решения и связи с их весами и предубеждениями превратятся в уродливую сеть:

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

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

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

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

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

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

Переведем это в математику. Вызывая x ожидаемый результат, y фактический результат нейронной сети и C функцию стоимости, мы могли бы сказать:

  • C >= 0, то есть всегда ноль или положительный
  • y ~ x -> C ~ 0, то есть чем ближе y подходит к x, тем меньше становится C.

Не существует единственного правильного уравнения для отображения этой функции затрат. Вместо этого есть несколько вариантов со своими плюсами и минусами (которые я не собираюсь здесь обсуждать). А пока я хочу сосредоточиться на номере 2, чтобы найти способ изменить параметры на основе этой функции стоимости. Помните, мы хотим C~0, поэтому, рассматривая один фиксированный вход (например, число 8, как в начале), мы хотим найти набор весов и смещений, которые уменьшают C больше всего, что дает минимальное значение из стоимости. функция. Для этого мы можем использовать некоторые производные, чтобы добиться такого поведения:

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

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

Во-вторых, наша функция стоимости не является функцией одного параметра. Рассматривая сеть с 784, 30 и 10 нейронами на 3-х уровнях, мы фактически имеем 23860 параметров (23820 весов и 40 смещений). А пока давайте будем простыми, как на этом рисунке: двумерная функция, также называемая поверхностью.

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

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

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

Если это слишком абстрактно, представьте себе следующее: вы играете в GTA V, но с разделенным экраном на 3 полосы с одним главным персонажем на каждой полосе. Имея только одну клавиатуру и одну мышь (извините, мастер гонки ПК), вы управляете одновременно и в реальном времени всеми тремя персонажами, каждый из которых находится в разных частях карты. Теперь возникает проблема: ваша цель - сделать их высоту как можно меньшей после случайных начальных позиций. Но попытка спуститься с холма с Тревором посреди пустыни может привести Майкла вверх по Вайнвуду. Вместо трех движущихся точек представьте что-то близкое к размеру вашей мини-партии (5, 10, 100?), И вместо трехмерной поверхности, как любая физическая карта, представьте поверхность размером 23860 (даже слово поверхность здесь больше не имеет смысла ха-ха). Настоящая проблема в нашей сети такая же, только более громоздкая.

Итак, у нас есть:

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

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

И последнее, но не менее важное, мы говорили о:

  1. Что такое нейронная сеть
  2. Как измерить точность нейронной сети
  3. Как использовать эту меру, чтобы улучшить его

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

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

Любые допущенные мной ошибки, неясные пункты и дополнения, не стесняйтесь записывать или свяжитесь со мной!