CapsNet

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

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

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

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

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

Что тут происходит.
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 почти невозможно.

Leave a Reply

Your email address will not be published. Required fields are marked *