CapsNet

Я думаю, что большая часть моих читателей уже видела последнюю работу Хинтона – Dynamic Routing Between Capsules. (ещё есть вторая статья)

Интересно, пробовал ли кто-то разобраться и вникнуть. Я немного попробовал и вот что вышло:
Во-первых.
Зачем всё это нужно. Мне кажется, что это единственный кусок, который хорошо рассказывают в научно-популярных статьях про CapsNet (1,2,3) это кусок “Почему свёрточные сети не работают”. Это не относиться к самой статье. По сути тут ничего нового нет. Помню ещё года 2 назад в каком-то ресёрче составлял похожий список. Сам Хинтон говорит про это года с 2011ого.
1) Нестабильность к трансформациям, особенно к поворотам. Обучать поворотам достаточно долго, падает качество.
2) Пулинг очень сильно прореживает информацию. Именно в пулинге есть некоторая стабильность к поворотам, но зато такие картинки будут выглядеть куда ближе друг к другу, чем мы хотели бы:

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

И.т.д. и.т.п. Если хотите, то вот почти часовой спич Хинтона о том почему всё сломано в CNN.

Во-вторых.
Перейдём к делу. Что, собственно в статье предлагается и как с этим жить.
Роутинг. Нам нужно как-то проредить информацию между входом и выходом. По сути, чтобы отдельные части сети учили свой контекст, при этом активизируясь только когда его узнают. При этом в идеале лучше разнести эту информацию дальше друг от друга, чтобы она не была коррелирована.
Хинтон предлагает достаточно забавный механизм. Если честно, то из статьи я понял только концепт, зато код неплохо его проясняет:

    for r_iter in range(cfg.iter_routing):
        with tf.variable_scope('iter_' + str(r_iter)):
            # line 4:
            # => [1, 1152, 10, 1, 1]
            c_IJ = tf.nn.softmax(b_IJ, dim=2)
            c_IJ = tf.tile(c_IJ, [cfg.batch_size, 1, 1, 1, 1])
            assert c_IJ.get_shape() == [cfg.batch_size, 1152, 10, 1, 1]

            # line 5:
            # weighting u_hat with c_IJ, element-wise in the last two dims
            # => [batch_size, 1152, 10, 16, 1]
            s_J = tf.multiply(c_IJ, u_hat)
            # then sum in the second dim, resulting in [batch_size, 1, 10, 16, 1]
            s_J = tf.reduce_sum(s_J, axis=1, keep_dims=True)
            assert s_J.get_shape() == [cfg.batch_size, 1, 10, 16, 1]

            # line 6:
            # squash using Eq.1,
            v_J = squash(s_J)
            assert v_J.get_shape() == [cfg.batch_size, 1, 10, 16, 1]

            # line 7:
            # reshape & tile v_j from [batch_size ,1, 10, 16, 1] to [batch_size, 10, 1152, 16, 1]
            # then matmul in the last tow dim: [16, 1].T x [16, 1] => [1, 1], reduce mean in the
            # batch_size dim, resulting in [1, 1152, 10, 1, 1]
            v_J_tiled = tf.tile(v_J, [1, 1152, 1, 1, 1])
            u_produce_v = tf.matmul(u_hat, v_J_tiled, transpose_a=True)
            assert u_produce_v.get_shape() == [cfg.batch_size, 1152, 10, 1, 1]
            b_IJ += tf.reduce_sum(u_produce_v, axis=0, keep_dims=True)

    return(v_J)

Что тут происходит.
1) Роутинг у нас идёт между слоем l и слоём l+1 В примере с MNIST это слои “PrimaryCaps” и “DigitCaps”

2) b_IJ – Это все возможные пути между капсулами уровня l и l+1. Для каждого forward перед стартом роутинга мы инициализируем их нулями: b_IJ = tf.constant(np.zeros([1, input.shape[1].value, self.num_outputs, 1, 1], dtype=np.float32))
3) c_IJ – временная переменная. Она определяет какие из наших путей сейчас активируются. Видно, что при iter_routing=1 переменная c_IJ примет равномерное значение, все пути будут работать одинаково.
4) Выбрав пути мы рассчитываем ожидаемое значение уровня l+1 – s_J = tf.multiply(c_IJ, u_hat) Для MNIST это DigitCaps.
5) А теперь немного магии. Применяется новый вариант активации который предложил Хинтон. Он называется “squash”. Пока не понял его глобальное преимущество. Понятно, что активация вида мало-> ноль, много -> единица – нужна. Но чем лучше squash – не знаю.
6) Пришло время замкнуть обратную положительную связь. Выберем ту капсулу, которая привнесла максимальную энергетику в ответ: u_produce_v = tf.matmul(u_hat, v_J_tiled, transpose_a=True)
7) И сдвинем веса routing так, чтобы мы доверяли этой капсуле больше: b_IJ += tf.reduce_sum(u_produce_v, axis=0, keep_dims=True)
Кстати. Нашёл несколько примеров исходников для других фреймворков, там b_IJ иногда храниться в биасе. И не зануляется при новом форварде. При этом вроде как всё работает. Так что может там баги, а может я не всё понял.

Регуляризация. Забавно, что все обзорные статьи говорят только про роутинг и забывают вторую большую часть того что использует Хинтон:

Вообще то, что в сетках современных нет регуляризации ругаются многие. Сама регуляризация для некорректно поставленных задач это старая тема. До появления нейронных сеток её очень часто делали в задачах обработки изображений. Взять то же построение КТ.
Отсутствие регуляризации это нестабильность ответа для малых изменений входных данных. Ничего не напоминает?
Хинтон делает регуляризацию простым утверждением:
“Выходной информации сети должно быть достаточно чтобы полностью восстановить входную”. Для этого вторая сетка, которая обучается параллельно – пытается построить такое представление капсулей, которое было бы достаточно для восстановления полной информации.
Вообще эта тема уже поднималась в работах по VAE и по Conditional VAE. Но именно как регуляризационная функция такое применяется редко. Хотя я видел как так люди делали:)
Есть причины, почему для CapsNet регуляризация была более критична чем для обычных сетей. Но рассказывать их уже нет сил. Надеюсь, что Вася таки опубликует в скором времени свою статью на arxiv, а потом обзор на Хабр. Там он затронет эти вопросы с более практической и философской точки зрения.

В-третьих. 
Что это всё даёт. И как это применить. Даёт – увеличение стабильности к большому числу искажений. На MNIST  и smallNORB есть прирост качества по сравнению с некоторыми другими сетями.
Теоретически – очень классно что всё заработало. Хинтон показал подход который обходит много современных проблем. Скорее всего это не полное их решение, а лишь возможность выйти за границы текущих ограничений.
Практически – пока ничего. Сетки очень медленно работают, очень медленно обучаются. Применять на картинки размером больше чем 30*30 почти невозможно.

2 thoughts on “CapsNet”

  1. CapsNet напоминает PathNet

    Идея скрестить PathNet-подобную сеть с AlphaZero для получения сети с humanlike intelligence:
    К PathNet прикрутить tree search от AlphaZero. PolicyNet не просто делает свои очередные ходы в игре, а она генерирует игры tree search из самой себя с учетом связности, свежести (+немного случайности) (подробнее ниже). В качестве Value Network пусть выступает Input (из среды) пропущенный через PolicyNetwork (а базовые вознаграждения: за новизну и за улыбку задаем жеще. Детектор улыбки простой CNN, а с новизной сложнее).
    Если игра/задача/приложение/среда не такая как в го (у него нет доступа ко всему миру сразу), это не беда, т.к. у него полный доступ к своим играм которые он сам генерирует в tree search из самого себя, поэтому отличие от го непринципиальное. Из этих игр заэмерджентятся игры воображения.
    (Сборщик мусора еще понадобится. Прорежатель неиспользуемых весов.)
    (А также побольше сделать систему).
    (А на системе побольше побороть проблемы производительности).
    Потом еще шлифануть аутоэмэлом или “Hierarchial representatiions for efficient architecture search”
    Потом выяснится что ничего не работает и наступит 2-я зима ИИ, лопнет пузырь ИИ-стартапов, повлечет 3-ю экономическую депрессию, которая совместно с популяционным давлением спровоцируют войны и разруху. И тут прилетит терминатор в голубом звездолете и покажет нам кузькину мать, всех людей отсреляет и конечно заставит маму конора оцифровать!

    У PathNet точность хромает для размеченных дэйтасетов, но это ему не минус, т.к. основанный на нем AGI дэйтасеты если и будет изучать, то необязательно размеченные и не непосредственно изучать, а через tree search взятый от AlphaZero (к тому же PathNet будет значительно большего размера).
    (“But after the first task has been learned to this accuracy, learning the second task is faster, so when cSVHN
    and CIFAR are learned as the second task with PathNet, then accuracies of 35.7% and 39.8% are achieved respectively”)

    Уточню:
    PolictyNetwork сам генерирует экземляры игры из самого себя. Т.е. он генерирует не банальные дощечки го 19×19, а модели. А оценку дает среда (игра или что угодно еще). Input среды идет в PolicyNetwork, и посредством этой PolicyNetwork среда делает оценку.

    В описываемом концепте PolicyNetwork очень важна, т.к. нет такой детерминированности правил как в го. В концепте наоборот нет отдельной ValueNetwork

  2. Сходство CapsNet и PathNet конечно слишком уж отдаленное.
    Да и простое дерево поиска не годится для не perfect задач.
    Оказывается я пропустил, что дипмайндовцы летом сделали неплохой моделлер “мира” полностью на нейросетях, он в зачаточном состоянии, но автоматически выучивает модели для разных задач и при этом может этими моделями пользоваться для моделирования/воображения, это две работы:
    Imagination-Augmented Agents for Deep Reinforcement Learning. https://arxiv.org/abs/1707.06203
    Learning model-based planning from scratch. https://arxiv.org/abs/1707.06170
    Вторая мне больше понравилась. К ним бы еще привинтить Elastic Weight Consolidation и капсулы Хинтона и вместо LSTM для памяти использовать что-нибудь получше. И обучаемость с первого раза сделать благодаря автоматической генерации трансформаций и автоматический контроль оверфита обеспечить.
    Запись в блоге DeepMind: https://deepmind.com/blog/agents-imagine-and-plan/
    Иллюстрация с сокобаном обманчиво проста, походит на простое дерево поиска, но это не так.
    Придумал еще один концепт:
    AtomicRoutesNet

    Очень хорошо распарралеливается. Требовательна к количеству простых CPU (+local RAM) и к пропускной способности шин, не подходит для обычных GPU.
    Каждый атом:
    CNN 2×1, 1 output мэппится от 0 до 2^31 или до 2^63 (зависит от разрядности, которая зависит от количества атомов в системе). Output является нечетким id атома.
    Атомное поле опрашивается пучком вопросов от Input и от самого атомного поля. Вопросы о последовательностях: вопросы от Input о последовательностях пикселей во времени: например вопрос о последовательности пикселя цвета 0xeee кадр назад и пикселя 0xccc в текущем кадре. Количество одновременно заданных вопросов соответствует количеству пикселей в Input и оно же равно количеству входных маршрутов. (Вопросы от атомного поля рассмотрим чуть позже). Атомы (параллельно друг другу) распознают последовательности и отвечают на вопросы своими idшниками. При одновременном распознавании несколькими атомами одной последовательности выбирается рэндомно один из ответов. Если последовательность не распознана никем, то на ней just-in-time обучается один из атомов с автоматическим контролем оверфита.
    Из idшников-ответов складываются последовательности: маршруты. На эти последовательности опять отвечают атомы, тем самым опять складывая последовательности. И т.д.
    Прокладывание и ветвление маршрутов — это самовоспроизводимый процесс, который не прекращается даже при отключении Input.
    Ветвление маршрутов обеспечивает способность многомерного моделирования.
    Бурление маршрутов ограничивается доступной пропускной способностью шин данных.
    Маршрутный процессинг реализуется как экономическая деятельность суб-агентов на генетическом алгоритме, где геном это торговая стратегия. При выборе ветвления учитывается стоимость маршрутизации, текущая стоимость атома и поиск новизны. Стоимость зависит от загруженности шин данных, от текущего спроса на текущий атом. Ветвление происходит когда оно экономически обосновано для сегмента сети и есть несколько ответов на один вопрос, либо происходит рэндомное ветвление: рэндомные idшники близкие к текущему idшнику в маршруте регулярно генерируются и проверяются на наличие соответствующих атомов, что обеспечивает поиск новизны: новые атомы приносят прибыль суб-агентам. (А также рядом с Input происхдит ветвление обусловленное пространственным взаиморасположением входных маршрутов). Автоматическое экономическое регулирование спроса/предложения обеспечивает отсутстие бесконечно зацикленных маршрутов и (почти) отсутствие дублирующих маршрутов. В то же время среднесрочные/долгосрочные торговые стратегии суб-агентов даже вопреки краткосрочным высоким издержкам приносят им прибыль в среднесрочно-долгосрочной перспективе, что на более высоком уровне выглядит как повторное использование абстракций/концепций без их дублирования.
    Так работает планирование/воображение. Inputом воображению служат потоки воображения же.
    По несколько атомов выделено на активацию актуаторов: например актуаторы взгляда, актуаторы положения головы. Собственно attention работает как контроль взгляда.
    Потоки дотекшие до актуаторов создают иллюзию потока воображения и его запоминания, что является сознанием.
    Система длительно инициализируется, т.е. имеет детский период.
    Другие reward (кроме поиска новизны):
    При получении reward (например условное яблоко или срабатывание простого детектора улыбки и т.д. и т.п.) временно ослабляется интенсивность удаления редко используемых атомов и значительно интесифицируется генерация маршрутов, что укореняет текущие вихри марштутов, делая их более устойчивыми, укрепляя их в этой динамической системе.
    Reward тратится не дискретно: он получается дискретно и наполняет резервуар из которого тратится с помощью контроллера вознаграждения в соответствии с типом reward и функцией контроллера.

Comments are closed.