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

Ожидается, что после обучения встраивание будет кодировать важную информацию для твитов и пользователей.

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

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

Почему? Скажем, если они находятся в одном пространстве. Теперь подумайте о том, как мы могли бы использовать вложение. Запрос близлежащих элементов для данного — довольно распространенное использование любого встраивания. В случае совместного встраивания пользователей твита могут быть следующие четыре типа запросов:

  1. найти пользователей, похожих на данного пользователя
  2. найти твиты, похожие на заданный твит
  3. найти релевантные твиты для данного пользователя
  4. найти релевантных пользователей для данного твита

Последние два в порядке. Но 1 и 2 могут вас подвести. Вот как:

При написании запроса почти всегда нужен порог. Таким образом, для данного пользователя u_0 вы хотите, чтобы все другие пользователи, чье расстояние до u_0 было меньше некоторого положительного числа r. Как показано ниже:

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

Теперь вернемся к обучению ко-встраивания. Мы знаем, что обучение нейронной сети — это просто поиск оптимизированного решения в функциональном пространстве, а градиентный спуск обычно заканчивается на каком-то локальном минимуме. Это означает, что если мы обозначим совместное встраивание, которое мы обучили, как U и T, где U — функция встраивания пользователя, а T — функция встраивания твитов, то функция потерь для обучения сети, описанной выше, будет локально минимизирована.

Вот ключевой прием: если я определяю новую пару ко-вложений: U*c и T/c, где c — некоторое положительное число, то скалярное произведение новых вложений будет таким же. как скалярное произведение исходных U и T. Поскольку

<U*c, T/c> = c*1/c*<U, T> = <U, T>

где ‹x, y› — скалярное произведение, или x^T*y.

Таким образом, эта новая пара также локально минимизирует функцию потерь для обучения сети. И это верно для любого c≠0. Итак, вы не уверены, какое решение вы получили во время тренировки: это может быть 2U и T/2, или U/2 и 2T и так далее.

Итак, что произойдет с вашим запросом, если (стохастический) градиентный спуск закончится на 2U и T/2? Ваше состояние станет

u such that <2U(u),2U(u_0)> <= r

i.e.

u such that <U(u),U(u_0)> <= r/4

Итак, вы запрашиваете гораздо меньшую окрестность u_0:

На этот раз по соседству не было других пользователей.

Но почему это не проблема для последних двух типов запросов? Потому что в этом случае условие для вашего запроса

t such that <cU(u_0),T(t)/c> = <U(u_0),T(t)> <= r

для данного пользователя u_0; или

u such that <cU(u),T(t_0)/c> = <U(u),T(t_0)> <= r

для данного твита t_0.

Здесь c и 1/c сокращаются, так что вы все еще смотрите на окрестности с тем же радиусом, даже если нейронная сеть сошлась к другому локальному минимуму, совместно встраивающему cU, T/c.

Вот почему не следует думать, что векторы вложения U(u) и T(t) лежат в одном и том же пространстве. В противном случае вы можете быть удивлены результатами вашего запроса.

На самом деле два вложения ко-вложения лежат в двойственных пространствах. Вот математическое определение двойственного пространства: для векторного пространства V его двойственное пространство V* — это пространство всех линейных функций над V. Слишком абстрактно, не так ли? Но если предположить, что V имеет конечную размерность, и думать о нем как о конкретном R^n, так что элементы в нем представляют собой просто n-кортеж (x_1,…,x_n), тогда V* также может можно рассматривать как R^n, потому что любая линейная функция над V может быть выражена* как

(a_1,…,a_n)^T • (x_1,…,x_n) = a_1*x_1+…+a_n*x_n

Хотя они оба изоморфны R^n как векторному пространству, это более тонко, когда у вас есть внутренний продукт g‹.,.› на V. Внутренний продукт на V естественным образом определяет скалярный продукт g*‹.,.› на V*. Я не буду приводить здесь детали определения. Но вы можете сослаться на эти конспекты лекций, если они не звенят в вашей голове. Затем с помощью того же трюка, что и выше, вы можете проверить, что если вы измените скалярное произведение g на V на коэффициент c, так что оно станет cg‹.,.›, то индуцированное скалярное произведение на V* станет g*‹ .,.›/c . Один становится больше, а его дуал меньше.

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

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