Понимание 'tf.nn.nce_loss()' в тензорном потоке

Я пытаюсь понять функцию потери NCE в Tensorflow. Потеря NCE используется для задачи word2vec, например:

# Look up embeddings for inputs.
embeddings = tf.Variable(
    tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
embed = tf.nn.embedding_lookup(embeddings, train_inputs)

# Construct the variables for the NCE loss
nce_weights = tf.Variable(
    tf.truncated_normal([vocabulary_size, embedding_size],
                        stddev=1.0 / math.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))

# Compute the average NCE loss for the batch.
# tf.nce_loss automatically draws a new sample of the negative labels each
# time we evaluate the loss.
loss = tf.reduce_mean(
    tf.nn.nce_loss(weights=nce_weights,
                   biases=nce_biases,
                   labels=train_labels,
                   inputs=embed,
                   num_sampled=num_sampled,
                   num_classes=vocabulary_size))

более подробную информацию, пожалуйста, обратитесь к Tensorflow word2vec_basic.py

  1. Каковы матрицы ввода и вывода в функции NCE?

В модели word2vec мы заинтересованы в построении представлений для слов. В процессе обучения, учитывая скользкое окно, каждое слово будет иметь два вложения: 1) когда слово является центральным словом; 2) когда слово является контекстным словом. Эти два вложения называются входными и выходными векторами, соответственно. (больше объяснений входных и выходных матриц)

На мой взгляд, входная матрица - это embeddings а выходная матрица - nce_weights. Это правильно?

  1. Каково окончательное вложение?

Согласно сообщению s0urcer, также относящемуся к nce, он говорит, что конечная матрица внедрения - это только входная матрица. В то время как некоторые другие говорят, final_embedding=input_matrix+output_matrix. Что является правильным/более распространенным?

Ответ 1

Давайте посмотрим на относительный код в примере word2vec (examples/tutorials/word2vec).

embeddings = tf.Variable(
    tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
embed = tf.nn.embedding_lookup(embeddings, train_inputs)

Эти две строки создают представления вложения. embeddings - это матрица, в которой каждая строка представляет слово вектор. embedding_lookup - это быстрый способ получить векторы, соответствующие train_inputs. В примере word2vec train_inputs состоит из некоторого числа int32, представляющего id целевых слов. В основном, это может быть размещено скрытым слоем.

# Construct the variables for the NCE loss
nce_weights = tf.Variable(
    tf.truncated_normal([vocabulary_size, embedding_size],
                        stddev=1.0 / math.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))

Эти две строки создают параметры. Они будут обновляться оптимизатором во время обучения. Мы можем использовать tf.matmul(embed, tf.transpose(nce_weights)) + nce_biases чтобы получить итоговый результат. Другими словами, последний слой внутреннего продукта в классификации может быть заменен им.

loss = tf.reduce_mean(
      tf.nn.nce_loss(weights=nce_weights,     # [vocab_size, embed_size]
                   biases=nce_biases,         # [embed_size]
                   labels=train_labels,       # [bs, 1]
                   inputs=embed,              # [bs, embed_size]
                   num_sampled=num_sampled, 
                   num_classes=vocabulary_size))

Эти строки создают nce loss, @garej дал очень хорошее объяснение. num_sampled относится к числу отрицательных выборок в алгоритме nce.


Чтобы проиллюстрировать использование nce, мы можем применить его в примере mnist (examples/tutorials/mnist/mnist_deep.py), выполнив следующие 2 шага:

1. Замените вставку на скрытый слой. Размер скрытого слоя - 1024 а num_output - 10. Минимальное значение num_sampled равно 1. Не забудьте удалить последний слой внутреннего продукта в deepnn().

y_conv, keep_prob = deepnn(x)                                            

num_sampled = 1                                                          
vocabulary_size = 10                                                     
embedding_size = 1024                                                    
with tf.device('/cpu:0'):                                                
  embed = y_conv                                                         
  # Construct the variables for the NCE loss                             
  nce_weights = tf.Variable(                                             
      tf.truncated_normal([vocabulary_size, embedding_size],             
                          stddev=1.0 / math.sqrt(embedding_size)))       
  nce_biases = tf.Variable(tf.zeros([vocabulary_size])) 

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

loss = tf.reduce_mean(                                   
    tf.nn.nce_loss(weights=nce_weights,                           
                   biases=nce_biases,                             
                   labels=y_idx,                                  
                   inputs=embed,                                  
                   num_sampled=num_sampled,                       
                   num_classes=vocabulary_size))                  

output = tf.matmul(y_conv, tf.transpose(nce_weights)) + nce_biases
correct_prediction = tf.equal(tf.argmax(output, 1), tf.argmax(y_, 1))

Когда мы установим num_sampled=1, точность val будет около 98.8%. И если мы установим num_sampled=9, мы сможем получить почти ту же точность val, что и тренируемая softmax. Но учтите, что nce отличается от softmax.

Полный код обучения mnist по nce можно найти здесь. Надеюсь, это полезно.

Ответ 2

embeddings Tensor - это ваша окончательная матрица вывода. Он сопоставляет слова векторам. Используйте это в графе предсказания слов.

train_input матрица представляет собой партию centre-word: context-word пары centre-word: context-word (train_input и train_label соответственно), сформированные из текста обучения.

Хотя точная работа nce_loss op еще не известна мне, основная идея заключается в том, что она использует однослойную сеть (параметры nce_weights и nce_biases) для сопоставления входного вектора (выбранного из embeddings с использованием embed op) в выходное слово, а затем сравнивает результат с обучающей меткой (смежным словом в тексте обучения), а также случайным num_sampled (num_sampled) всех других слов в vocab, а затем изменяет входной вектор (сохраняется в embeddings) и параметры сети, чтобы свести к минимуму ошибку.

Ответ 3

Каковы матрицы ввода и вывода в функции NCE?

Возьмем, например, пропущенную грамматическую модель для этого предложения:

the quick brown fox jumped over the lazy dog

входные и выходные пары:

(quick, the), (quick, brown), (brown, quick), (brown, fox),...

Для получения дополнительной информации обратитесь к учебнику.

Каково окончательное вложение?

Конечным вложением, которое вы должны извлечь, обычно является {w} между входным и скрытым слоями.

enter image description here

Чтобы проиллюстрировать более интуитивно взглянуть на следующее изображение: enter image description here

Один горячий вектор [0, 0, 0, 1, 0] является входным слоем в вышеприведенном графике, выход является вложением слова [10, 12, 19], а W (на графике выше) является матрицей в между.

Подробное объяснение читайте в этом уроке.

Ответ 4

1) Короче говоря, это правильно в целом, но только частично верно для рассматриваемой функции. Смотрите учебник:

Потеря оценки, сопоставимая с шумом, определяется с помощью модели логистической регрессии. Для этого нам необходимо определить весовые коэффициенты и смещения для каждого слова в словаре (также называемые output weights в отличие от input embeddings).

Таким образом, входные данные для функции nce_loss представляют собой output weights и небольшую часть nce_loss input embeddings, среди прочего.

2) "Окончательное" встраивание (иначе говоря, векторы Word, а также векторные представления слов) - это то, что вы называете input matrix. Вложения - это строки (векторы) этой матрицы, соответствующие каждому слову.

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

То же самое относится и к весам - они также должны быть инициализированы. Это происходит в следующей строке:

nce_weights = tf.Variable(
        tf.truncated_normal([50000, 128], stddev=1.0 / math.sqrt(128)))

Каждый вектор вложения может быть умножен на вектор из матрицы весов (в виде строки в столбец). Таким образом, мы получим скаляр в output layer NN. Норма этого скаляра интерпретируется как вероятность того, что целевое слово (из входного слоя) будет сопровождаться словом метки [или контекста], соответствующим месту скаляра в output layer.


Итак, если мы говорим о входных данных (аргументах) для функции, то обе матрицы таковы: весовые коэффициенты и пакетное извлечение из вложений:

tf.nn.nce_loss(weights=nce_weights,            # Tensor of shape(50000, 128)
               biases=nce_biases,              # vector of zeros; len(128)
               labels=train_labels,            # labels == context words enums
               inputs=embed,                   # Tensor of shape(128, 128)
               num_sampled=num_sampled,        # 64: randomly chosen negative (rare) words
               num_classes=vocabulary_size))   # 50000: by construction

Эта функция nce_loss выводит вектор batch_size - в примере TensorFlow тензор shape(128,). Затем reduce_mean() уменьшает этот результат до скалярного значения, взятого из этих 128 значений, что фактически является целью для дальнейшей минимизации.

Надеюсь это поможет.

Ответ 5

Из статьи Эффективное вложение слов вложения с шумо-контрастной оценкой:

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

Мы могли бы выяснить, что при вложении слов НСЕ является отрицательной выборкой. (Разницу между этими двумя, см. Документ: Примечания по оценке контрастности шума и отрицательной выборке)

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