Нейронная сеть, имеющая способность к самообучению
Всем привет! Недавно, когда я размышлял над работой памяти в мозге человека, мне пришла идея того, как можно сделать нейронную сеть, которая будет обладать способностью самообучаться. В этой статье я бы хотел представить свои размышления и идею, к которой я пришёл.
Скорее всего, нечто подобное уже придумывали и у подобной идеи есть проблемы, которые не позволяют использовать её на практике (их я тоже обсуждаю ниже), тем не менее, мне кажется, что многим может быть интересна данная тема.
Хочу сразу сказать, что я не профессионал в сфере AI, а лишь увлекаюсь ИИ как хобби — для меня это интересное дело, заниматься которым я могу часами, из‑за чего мне хотелось бы поделиться с вами своими мыслями на этот счёт. Поэтому прошу, не судите строго. Приятного прочтения!:)
Как работает память человека?
В человеческом мозге есть два вида памяти: кратковременная и долговременная. Чтобы ответить на вопрос: «Как работает память человека?» — можно попробовать поразмышлять: как именно мы заносим информацию в кратковременную и долговременную память. Кому это знать, как не самому человеку. Начнём с кратковременной.
Кратковременная память
Когда мозг воспринимает какую‑либо информацию или вспоминает что‑то, первым делом все эти данные попадают в кратковременную память, используя которую, человек может производить какие‑либо манипуляции — размышлять о чём‑то. Этот вид памяти очень быстрый и эффективный, так как работает за счёт хранения информации в виде текущей активности мозга. Тем не менее, он обладает огромным недостатоком — объём. Действительно, объём кратоквременный памяти очень маленький. Такой вид памяти можно сравнить с механизмом внимания в современных архитектурах нейронных сетей для обработки последовательности данных.
Долговременная память
Помимо кратковременной, в мозге человека есть ещё и долговременная память — с ней всё куда сложнее и запутанней. Попробуем выяснить, как именно происходит вспоминание и запоминание — для этого обратимся к тому, как мы это ощущаем.
Конечно, в действительности размышления в таком ключе не позволят понять, как именно работает долговременная память: в мозге человека всё устроено куда сложнее того, что можно узнать лишь из ощущений. Тем не менее, сейчас это не имеет значения — даже наши ощущения могут подсказать нам механизм, который будет отражать основную суть процесса, что сейчас нам и нужно. Подумаем для начала о процессе вспоминания.
Когда мы вспоминаем, что мы ели вчера на ужин, то начинаем поиск в памяти. Как он происходит? С самого начала мы задаём вопрос долговременной памяти «Мне понравился ужин вчера?», на что получаем ответ «Да», который записывается в кратковременную память. Затем мы, используя эту информацию, начинаем задавать следующий вопрос долговременной памяти «А какие блюда я люблю?», на что получаем ответ «блюдо 1, блюдо 2, блюдо 3», который также записывается в кратковременную память. И наконец, уже используя всю информацию, которую мы достали, спрашиваем «Ел ли я вчера блюдо 1, 2 или 3?», на что получаем долгожданный ответ: «Из этих трёх блюд вчера на ужин ты ел только блюдо 2».
Таким образом, процесс вспоминания информации очень напоминает общение двух отдельных нейронных сетей: памяти и сознания. Сознание — нейронная сеть, которая обладает кратковременной памятью и занимается обработкой информации, а память — сеть, единственная роль которой — хранить информацию. Сознание может задавать вопросы долговременной памяти, на что та будет выдавать какую‑либо информацию, которая может как помочь в текущих размышлениях, так и найти другую информацию (как это было в примере выше).
Хорошо, а как выглядит процесс запоминания информации? Это может происходить следующим образом. Допустим, вы размышляете и пытаетесь ответить на вопрос: «Сегодня стоит отдохнуть или позаниматься полезным делом?». Взвешивая все за и против, вы принимаете решение: сегодня половину дня лучше позаниматься, а остальную — отдыхать. В процессе размышления над этим вопросом вам приходилось держать в кратковременной памяти немало информации, тем не менее, вы сами решаете, что запомнить, а что забыть. В данном случае вам необходимо запомнить ответ на вопрос. Решение о запоминании ответа происходит незаметно для нас, но в конечном счёте его принимает именно сознание. Более явно это заметно на примере запоминания стиха, но суть остаётся та же.
Можно обратить внимание, что здесь, в процессе мышления человека, нет отдельного этапа «Обучение» как у нейронных сетей, после которого человек смог бы думать. Обучение человека происходит за счёт запоминания информации в долговременную память. То есть запоминание информации в долговременную память — это тоже самое, что и обучение. По этой причине нейронные сети, которые имеют подобный механизм кратковременной и долговременной памяти, могут быть способны к самообучению. Теперь можно попробовать придумать, как такой механизм мог бы работать в искусственных нейронных сетях.
Устройство нейронной сети с долговременной памятью
Для примера, пусть наша нейронная сеть выполняет задачу общения с человеком, как ChatGPT. Наша модель будет представлять собой две нейронные сети: память и сознание.
Сеть «память»
Сеть «память» может работать по принципу «ключ‑значение», то есть наподобие ассоциативного массива. Эта сеть будет принимать на вход ключ (некоторый вопрос, например «Что я ел вчера на ужин?»), а на выходе выдавать значение (ответ на вопрос, то есть как в примере «Из этих трёх блюд вчера на ужин ты ел только блюдо 2.»). При необходимости считать нужную информацию сеть‑сознание формирует ключ, по которому получает нужное значение.
Стоит отметить, что возможности такой сети‑памяти будут за рамками обычного ассоциативного массива, ведь каждый ключ и каждый ответ на него в процессе обучения с большой долей вероятности превратятся в вопросы и ответы, так как это наиболее эффективный способ общения между такими сетями.
Очевидно, что вопросы и ответы, которыми будут обмениться сети, не будут представлять собой слова на человеческом языке, а будут векторами скрытых состояний, в которых вся эта информация закодирована (вообще для интереса можно попробовать раскодировать эти векторы скрытых состояний, чтобы понять, о чём «разговаривают» сети «память» и «сознание»). Так как сети‑сознанию может потребоваться задать сразу несколько вопросов (передать за раз несколько векторов скрытого состояния), для сети-памяти целесообразно использовать архитектуру Transformer. Таким образом получается, что данная модель сети долговременной памяти также будет обладать кратковременной памятью, как бы странно это ни звучало. Тем не менее, вероятнее всего, так же происходит и в мозге человека: наша долговременная память обладает своей кратковременной.
Сеть «сознание»
Так как важной деталью сети‑сознания является наличие кратковременной памяти, сразу становится понятно, что она также должна обладать архитектурой Transformer. Именно сеть‑сознание будет выполнять основную деятельность, в нашем случае — отвечать на вопросы пользователя в процессе общения. То есть она получает на вход некоторую последовательность, в данном примере токенов, а на выходе выдаёт некоторую последовательность распределений вероятностей токенов (как ChatGPT — получает и выдаёт слова). Также у неё должна быть возможность задавать вопросы сети-памяти и получать от неё ответы. Поэтому на выходе сети‑сознания помимо распределения вероятностей токенов должен также выдаваться вектор скрытого состояния, который будет содержать вопрос для сети памяти. На входе помимо токенов сеть должна получать вектор скрытого состояния ответа сети памяти (конечно, всё это стоит воспринимать в рамках механизма внимания, т. е. все эти данные на входе доступны одновременно, как бы внутри кратковременной памяти).
Вот как можно проиллюстрировать модель, если собрать всё вместе:
Обучение
Для начала разберёмся с тем, как будет происходить процесс запоминания — это, как мне кажется, самая интересная часть статьи. Когда сети‑сознанию необходимо что‑нибудь запомнить, сначала она сигнализирует о начале процесса запоминания с помощью специального сигнального ключа — это некоторый опеределенный вектор, который сеть‑сознание подаёт на синий выход «Ключ» (если ориентироваться по иллюстрации выше). Получив вектор, который слабо отличается от сигнального ключа, мы понимаем, что сеть‑сознание хочет начать запись в память.
Чтобы записать в память информацию, которую сеть‑сознание хочет запомнить, она генерирует небольшую обучающую выборку для сети‑памяти из пар ключ‑значение (вопрос и ответ на него. Проще говоря, на какой вопрос и как сеть-память должна отвечать). Эту выборку сеть также может выдавать на синий выход «Ключ» в виде последовательности ключ‑значение, ключ‑значение,..., ключ‑значение. Размер этой выборки можно так же, как и сигнальный ключ, получать из выхода «Ключ» в сети‑сознании, но для простоты пусть размер выборки будет фиксированным.
Теперь, используя сгенерированную выборку, осталось с помощью обычного метода обратнего распространения ошибки дообучить сеть-память на сгенерированной сетью-сознанием выборке.
После этого можно сказать, что сеть‑память сохранила информацию, которую сеть‑сознание хотела запомнить. Процесс запоминания можно формально записать с помощью следующей функции потерь:
Где Loss(x) — функция потерь запоминания, memory() — сеть‑память, cons(x)_keys — ключи из сгенерированной сетью‑сознанием запоминаемой выборки, cons(x)_values — целевые значения из сгенерированной сетью‑сознанием выборки, x — данные, от которых зависит сеть-сознание (входные данные и её текущее скрытое состояние).
Вот как будет выглядеть процесс запоминания в целом:
Теперь, чтобы обучить всю сеть решать поставленную ей задачу (в нашем случае это языковая модель, поэтому её задача — общаться с пользователем), достаточно использовать обычный метод обратного распространения ошибки. То есть получается интересная ситуация: в процессе обучения с помощью метода обратного распространения ошибки внутри сети будет использоваться он же для занесения информации в память. Но на самом деле здесь нет никаких противоречий, ведь обучение с помощью градиентного спуска можно представить как часть нашей общей большой функции нейронной сети, которая вместе с самой нейронной сетью может обучаться.
Интенсивность запоминания
Вернёмся немного к запоминанию сети‑памяти. Стоит задаться вопросом: насколько сильно нужно уменьшать ошибку сети-памяти при обучении сгенерированной выборке? Или, другими словами, насколько сильно нужно запоминать текущую информацию, которую сгенерировала сеть‑сознание? Ведь чем сильнее необходимо запомнить новую информацию, тем больше сеть‑память будет забывать то, что она запомнила раньше и тем дольше будет происходить процесс запоминания. Нам явно необходима возможность контролировать интенсивность запоминания.
Эту задачу может решить сама сеть‑сознание; кому как не ей знать, насколько сильно нужно запомнить информацию, которую она хочет сохранить. Это можно сделать так: пусть первый вектор при генерации сохраняемой выборки, который сеть‑сознание генерирует на синем выходе «Ключ», будет на месте первого элемента хранить число от 0 до 1, показывающее важность сохраняемой далее информации. Чем больше это число, тем сильнее нужно сохранить информацию (ниже опустить значение ошибки при обучении сети‑памяти сгенерированной выборке).
Кстати, скорее всего, при обучении всей нейронной сети в функцию потерь стоит добавить член, заставляющий сеть делать значение этого коэффициента ближе к 0 так часто, как это возможно, ведь маленькое значение сильно увеличит скорость запоминания сети.
Проблема быстрого забывания
Может возникнуть проблема, когда сеть‑память при запоминании новой информации будет слишком быстро забывать старую. На самом деле, на небольших объёмах информации эта проблема не должна быть сильно выражена, и вот почему.
Допустим, сеть‑память уже запомнила некоторую информацию — это значит, что она имеет некоторую структуру, в которой та хранится. Внутри неё все нейроны или их часть обозначают некоторые абстрактные объекты. Так вот, при запоминании новой информации из‑за того, что это запоминание происходит с помощью градиентного спуска, для сохранения новой информации будут как можно сильнее использоваться уже созданные объекты, таким образом максимально сохраняя структуру сети.
Тем не менее, при запоминании новой информации что‑то забыться обязательно должно (ведь какие‑то веса обязательно должны поменять своё значение, что неизбежно приведёт к изменению ответа сети при получении предыдущих запомненных ключей). Из этого следует, что чем больше информации запомнила сеть, тем быстрее и значительнее она будет забывать старые данные.
Всё же одно можно сказать точно: изменение одних весов приведет к забыванию большей информации, чем изменение других. Можно попробовать идентифицировать веса, изменение которых слабо повлияет на уже запомненную информацию и записывать новую именно с помощью их изменения, а не тех, которые приведут к забыванию — это может позволить в некоторой степени уменьшить влияние проблемы быстрого забывания на работу сети‑памяти.
P. S. Далее идёт пояснение того, как можно реализовать эту идею, но, как мне кажется, вышло весьма объёмно, поэтому, если это будет кому‑то будет интересно, я могу подробнее расписать в другой статье.
Контроль запоминания
А как можно идентифицировать веса, которые слабо влияют на уже запомненную информацию? Возьмём простой пример. Допустим, сеть‑память получает на вход ключ X, а на выходе выдаёт значение Y. Наша цель — изменить веса так, чтобы при получении сетью‑памятью ключа X она с минимальными изменениями выдавала значение Y.
Попробуем немного изменить значение веса k и посмотрим, насколько сильно изменится ответ Y сети‑памяти — такое изменение показывает производная ответа Y по весу k — именно она нам и нужна. Она отражает, насколько сильно изменение веса повлияет на ответ сети‑памяти Y (предыдущую запомненную информацию). Как мы помним, нам необходимо найти такие веса, изменение которых слабо повлияет на уже запомненную информацию в сети, то есть на ответ Y. Такие веса будут иметь маленькое значение производной по ответу Y.
Так как ответ сети Y — это вектор, который имеет несколько значений, то для того чтобы полностью определить, как изменится ответ при изменении веса, мы можем просуммировать производные каждого элемента вектора ответа Y по весу k (при этом каждую эту производную необходимо возвести в квадрат, чтобы избавиться от знака):
Обозначим это буквой i — первая буква слова importance (важность веса для хранения ответа Y).
Таким образом, мы можем определить важность каждого веса для хранения информации, состоящей из ключа X и значения Y.
Можно для каждого веса создать некоторый дополнительный параметр s, который будет показывать, насколько важен конкретный вес для хранения всей предыдущей информации, а не только конкретного ответа Y. Как считать значение этого параметра s? Всё просто — чем чаще при получении информации из сети‑памяти вес k становится важным (важность определяется с помощью значения i, формулу которого я написал выше), тем более важен данный вес для хранения наиболее часто используемой информации. Исходя из этого, подсчитать параметр s можно так:
Здесь φ — это коэффициент скорости забывания (чем он больше, тем быстрее информация будет забываться), а m — коэффициент запоминания (чем он больше, тем больше уже сохранённой информации будет оставаться неизменной при новом запоминании, вплоть до того, что сеть‑память может вообще перестать меняться в целях запомнить старую информацию). Здесь важно то, что, исходя из формулы, значение параметра s может стать отрицательным, что нежелательно. Поэтому, когда значение s < 0, оно должно принимать значение 0.
Ну и теперь, когда у нас есть значение s для каждого из весов, осталось лишь модифицировать функцию потерь так, чтобы веса, для которых значение важности s больше, меняли своё значение в процессе запоминания меньше — для этого достаточно использовать регуляризацию. Наша цель — наказывать сеть за то, что она в процессе запоминания меняет веса, которые нельзя менять (у которых значение параметра s большое). То есть, чем сильнее сеть поменяла значение веса с начала момента обучения, тем больше мы её должны штрафовать, но только в том случае, если s этого веса имеет большое значение. Поэтому функцию потерь можно сделать такой:
Здесь E(x) — изначальная функция потерь, λ — коэффициент регуляризации (показывает, насколько сильно регуляризация должна влиять на процесс обучения), w_start — значение веса k в начале процесса запоминания, w_current — текущее значение веса k в момент запоминания, а сумма — член регуляризации.
Как можно заметить, здесь идёт суммирование значений для весов, каждое из которых представляет собой то, насколько сильно текущее значение веса отличается от его же значения в начале обучения. Всё это берётся в квадрат, чтобы избавиться от знака, а затем домножается на коэффециент s.
Таким образом, если вес неважный, то сколько бы сильно не менялось его значение, это не будет увеличивать ошибку: неважный вес можно менять как угодно для сохранения новой информации. Это происходит за счёт того, что параметр s для неважного веса будет стремиться к 0, из‑за чего значение штрафа для этого веса будет домножаться на 0, таким образом обнуляясь.
Если же вес важен, то значение s будет положительным числом, поэтому чем сильнее сеть в процессе запоминания изменит этот вес, тем сильнее увеличится ошибка, из‑за чего сеть не захочет менять этот вес и будет использовать для запоминания только неважные веса — это именно то, что нам и нужно.
Проблемы
Как я уже упомянул выше, у данной сети есть проблемы, по причине которых, скорее всего, подобное и не используется. Вот две основные, которые сразу бросаются в глаза:
Долгое время запоминания в долговременную память. В такой сети процесс запоминания в долговременную память происходит с помощью градиентного спуска, поэтому, вероятнее всего, оно будет достаточно долгим. Конечно, всё зависит от того, насколько часто сеть инициирует процесс запоминания, насколько большие выборки за раз она пытается запомнить, и насколько сильно она хочет запомнить информацию. Тем не менее, этот механизм всё равно с большой долей вероятности будет весьма долгим. Для уменьшения значения этой проблемы есть идея попробовать использовать обучение Хэбба, но не факт, что оно будет лучше.
Проблема быстрого забывания. Как я уже упомянал выше, в такой сети обязательно будет присутствовать непроизвольное забывание информации, причём количество и интенсивность забытой информации будет расти с количеством записанной в память. Предложенный метод решения этой проблемы может улучшить ситуацию, но неизвестно, насколько эффективно — нужно проверять. Можно попробовать также и другие методы, например, при каждом новом запоминании обучать сеть‑память не только на текущей сгенерированной выборке, но и на случайных предыдущих парах ключ‑значения, которые были сгенерированы ранее. Можно скомибинировать оба метода — всё это также требует проверки на практике.
Заключение
К сожалению, до практической реализации идеи я так и не дошёл, поэтому показать результаты пока что не могу. Тем не менее, если эта тема будет интересна, я могу попробовать реализовать подобную сеть и посмотреть, как она будет работать: насколько она эффективна, насколько долго обучается, насколько эффективно предложенное решение проблемы быстрого забывания сети-памяти.
Надеюсь вам понравилось, спасибо за внимание! ?