тетрадь разработчика
Sparse Hash
Разреженное распределённое кодирование как основа архитектуры ИИ
"я просто щёлкаю тумблерами и смотрю, что зажжётся"
Материал по проекту выкладывается в виде черновика. Дополняется и редактируется в произвольном порядке.

Обсуждение проекта в Телеграм. Мой профиль на Хабре.

обновлено 05.03.21

2021, Алексей Тарасов

Основа
Определения
sdr
SDR коды — разреженные распределенные представления (Sparse Distributed Representations).

SDR — это бинарный вектор, где существует небольшое количество элементов с единичными значениями и они распределены по всему массиву без какой-либо структурной организации. Активным элементом считается элемент с единичным значением. Для краткости далее употребляется просто как код, или паттерн активности.

14 активных элементов в массиве размером 40:

[1,0,0,1,1,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,0,1,1,0,1,0,0,0,0,0,1,0,1,0,0,0]

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

|■  ■■   ■     ■■■■   ■   ■■ ■     ■ ■   |
термины
FPR - ложноположительные срабатывания
FRR - ложноотрицательные срабатывания
НЕЙРОН
Не формальный нейрон
Хеш-нейрон
модель
Модель искусственного нейрона построена в некотором приближении к биологическому прототипу. Данная модель нейрона далее называется хеш-нейроном.

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

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

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

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

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

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

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

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

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

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

Уравнение равновесия:

(N × Rexc) × Wexc = (N × (1 Rexc)) × Winh;

, где:
N — общее количество синапсов;
Rexc [0, 1] — количество возбуждающих синапсов по отношению к общему числу синапсов обоих типов;
Wexc — вес возбуждающего необученного синапса;
Winh — вес тормозящего синапса.

Тогда из уравнения (1) находим вес тормозного синапса Winh:

Winh = (N × Rexc) × Wexc (N × (1 Rexc));

или:

Winh = Nexc × Wexc Ninh;

, где:
Nexc = N × Rexc; — количество возбуждающих синапсов;
Ninh = (N × (1 Rexc); — количество тормозных синапсов.

Пример

Задаётся вес необученного возбуждающего синапса Wexc, доля возбуждающих синапсов Rexc и общее количество синапсов обоих типов N.
Из уравнения (2) находим вес тормозного синапса Winh.

N = 500;
Rexc = 0.9;
Wexc = 1.0;

Winh = (500 × 0.9) × 1.0 (500 × (1 0.9)) = 9.0;

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

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

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

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

Новый вес необученного возбуждающего синапса:

Wuntr_exc = ((N × (1 Rexc)) × Winh SWexc_tr) (N × Rexc Nexc_tr);

, где:
N — общее количество синапсов;
Nexc_tr — количество усиленных возбуждающих синапсов;
SWexc_tr — суммарный вес усиленных возбуждающих синапсов;
Wuntr_exc — вес необученного возбуждающего синапса.

или:

Wuntr_exc = (Ninh × Winh SWexc_tr) (Nexc Nexc_tr);

, где:
Nexc = N × Rexc; — количество возбуждающих синапсов;
Ninh = (N × (1 Rexc); — количество тормозных синапсов.

Пример

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

Задаётся суммарный вес усиленных/обученных возбуждающих синапсов SWexc_tr, вес тормозного синапса Winh, доля возбуждающих синапсов Rexc, количество необученных возбуждающих синапсов Nexc_untr и общее количество синапсов N.
Из уравнения (4) находим новый вес необученного возбуждающего синапса Wuntr_exc.

N = 500;
Rexc = 0.9;
Winh = 9.0;
Nexc_tr = 50;
SWexc_tr = 100.0; — 50 синапсов с весом равным 2.0

Wuntr_exc = ((500 × (1 0.9)) × 9.0 100.0) ((500 × 0.9) 50) = 0.875;
СЛОЙ
Кластер
Рассмотрим нейроны, одновременно участвующие в некоем общем процессе. Участие само собой подразумевает проявление нейронами активности. Назовём ансамбль из синхронно срабатывающих нейронов кластером.

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

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

Почему нейрон обязан отвечать на множество входных образов, а не на один?

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

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

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

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

Если взять случайную группу нейронов, то её средняя активность будет равна разреженности выхода нейрона. Легко подсчитать как часто случайная активность нейронов, входящих в кластер, будет соответствовать срабатыванию этого кластера. Вероятность случайного ложноположительного срабатывания кластера равна (1 S)K, где S — разреженность выхода нейрона, а K — размер кластера.
кластер-детектор
И здесь я прихожу к идее, что детектором некоего входного образа (фичи) в такой нейросети может выступать не отдельный настроенный на этот образ селективный нейрон, а группа из неселективных нейронов — кластер.

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

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

Зададим порог активности нейрона для отбора его в кластер и применим его к полученной средней активности нейронов. Чем выше задаётся порог, тем более селективные нейроны оказываться отобраны в кластер, но и тем меньшего размера сформируется кластер.

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

Для создания кластера обобщения берётся набор образов одного класса из обучающей выборки, хешируется и далее так же как и в случае одиночного образа в кластер отбираются самые активные нейроны. Так как количество образов может быть сотни и тысячи, а хеш-нейроны по своей природе неселективны, то для отбора их в кластер необходимо задавать меньший порог. Но это в свою очередь приводит к уменьшению селективности кластера.
РАСПОЗНАВАНИЕ
Классификация
При классификации наблюдаемый образ должен соответствовать одному единственному классу. Всё что для этого требуется, это выбрать кластер с максимальной активностью — применить принцип winner-take-all (WTA). Для понижения FRR можно задать порог, например, 0.98. Это поможет уменьшить ложноотрицательную ошибку, но приведёт к повышению ложноположительной.
повышение эффективности
Распознающая способность кластера обобщения зависит от качества обучающей выборки. Например, цифра один может иметь множество написаний: классическая с уголком, как вертикальная черта, или наклонённая черта вправо, влево. Для небольших выборок в 20-50 образцов на класс, если какое-то написание цифры не попало в выборку, то кластер, естественно, не сможет его распознать.

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

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

Вариант построения классификатора на выборке в ~50 образов на класс и 200-500 нейронов в слое демонстрирует FRR < 10% и FPR < 20%. Тест на обучающей выборке даёт ошибку FRR < 3% и FPR < 6%. В большинстве случаев FRR 0-1%.

Характер FRR и FPR разный. Слой по ложноотрицательной ошибке спотыкается на одних и тех же образах, они выбиваются из образов остальных образцов. Обычно это "крякозябры" или образы в виде тонких линий (так как предварительно изображения из MNIST бинаризуются, что делает некоторые цифры очень тонкими). Кластеры затрудняются построить устойчивое обобщение для таких "нестандартных" образов.

Ложноположительная ошибка же распределена случайно и часто равна удвоенному значению FRR.

Также я использовал итерационный способ создания кластеров-ключей. Для классов создавались первичные ключи и через них прогонялся обучающий набор. Те образы, что распознавались с ошибками формировали новый обучающий набор и так до полного устранения ошибок (обычно до 10 прогонов). Первичный ключ оставлял нераспознанными < 10% обучающих образов, — можно сказать, имел сжимающую способность в 5-15 раз. Все последующие ключи сжимали в 3 раза.

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

Дальнейшее повышение точности возможно путём сборки слоёв в кластеры, например, по три. Такое тройное резервирование позволит снизить оба типа ошибок. Можно даже не создавать новые хеш-слои, а ограничиться новыми наборами кластеров для одного и того же слоя.
РАСПОЗНАВАНИЕ
Кластеризация
самосборка кластеров
В прошлых примерах мы, можно сказать, вручную формировали кластеры из рассортированной по классам обучающей выборки. Новая задача — кластеры должны формироваться самостоятельно из немаркированного потока образов (без учителя).

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

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

С каждой итерацией такие перестраиваемые кластеры становятся всё более и более селективными.

Приведу несколько примеров самосборки кластеров на MNIST. Всего было задано 30 кластеров, каждый с памятью на 5-15 своих хешей. Первоначальная их селективная способность "никакая".

|                 ■     ■        |
|              ■        ■     ■  |
|    ■          ■ ■       ■  ■■  |
|    ■ ■  ■ ■  ■■ ■           ■ ■|
|                       ■        |
|                       ■     ■  |
|                       ■        |
|    ■    ■     ■■ ■ ■  ■  ■    ■|
|                 ■             ■|
|    ■ ■  ■■    ■ ■■          ■ ■|
|                 ■     ■ ■      |
|    ■ ■                ■        |
|    ■            ■       ■  ■■  |
|         ■     ■  ■             |
|    ■    ■■ ■ ■■  ■    ■ ■   ■  |
|                       ■        |
|               ■ ■           ■ ■|
|    ■     ■  ■        ■■     ■ ■|
|                             ■ ■|
|      ■  ■       ■           ■ ■|

Здесь показана активность 30 кластеров на предъявление 20 тестовых образов цифры 0.

Повторный тест после демонстрации 1000 образов всех цифр в случайном порядке и без повторений.

|              ■                 |
|              ■                 |
|            ■ ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |
|              ■                 |

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

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

|                             ■  |
|                             ■  |
|          ■                     |
|          ■                     |
|                             ■  |
|          ■                  ■  |
|          ■                  ■  |
|               ■             ■  |
|          ■                     |
|          ■                     |
|          ■                  ■  |
|               ■                |
|          ■                     |
|               ■                |
|          ■                  ■  |
|          ■                  ■  |
|                             ■  |
|          ■                     |
|          ■                     |
|          ■                     |

Как видим три кластера охватывают в тесте все инвариантные образы цифры 0.

Чтобы превратить кластеризатор в классификатор нам достаточно теперь просто связать эти три кластера с классом цифры 0.