Неожиданное поведение keras реализации измененной модели скипграммы - PullRequest
1 голос
/ 25 мая 2020

Мне поручено воссоздать результаты в документе: P2V-MAP: Отображение рыночных структур для большого розничного ассортимента .

Я реализовал измененную модель skip-gram, которая принимает в качестве входных данных целые числа, соответствующие продуктам в корзине, купленной покупателем. Затем, используя модель skip-gram, он должен создать векторы, соответствующие скрытым атрибутам продукта. В основном это word2ve c для продуктов, но изменилось то, что оценка сходства, используемая в word2ve c, расширена за счет добавления спецификаций центра c и контекста c смещения, изученного в модели *. 1005 *

Теперь о том, как я его программировал. Сначала данные: данные загружаются в хорошо известный стиль пары центральный контекст. Модели дается массив center_products, positive_context_products (продукты, которые попадают в корзину вместе) и, наконец, 2-мерный массив negative_context_products (продукты, которые не попадают в корзину вместе).

Ниже приведены последние 10 записей (кроме отрицательного контекста, в котором есть последние 2) массива в следующем порядке: center, positive_context и negative_context.

train_center[:10] = [11109 11109 11109 11109 11109 10246 10246 10246 10246 10246]
train_pos_context[:10] = [10246 49683 13176 47209 22035 11109 49683 13176 47209 22035]
train_neg_context[:2] = [[22227 45007  4814 15084 26041  7715 42658 38383 29675 49198 19613 32304
  28985  2295 44418 13166 24852  5068 13176 13170]
 [31506  3339 32478 40469 44990  9392 11512 46106 32478  8277  7461 21903
   7087 32433 13482  9092 23630 28881 42294 42625]]

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

labels[0] = [[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

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

from random import random, randint
import tensorflow as tf
from tensorflow.keras import Model
from tensorflow.keras.layers import Embedding, dot, Dense
import numpy as np
from tensorflow_core.python.keras.layers import Reshape


class P2VTensorFlow:
    def __init__(self, seed, num_epoch, train_center, train_pos_context, train_neg_context, num_n_samples, size_vector):
        self.num_epoch = num_epoch
        self.seed = seed
        self.n_products = max(max(x) for x in train_neg_context) + 1
        self.train_center = train_center
        self.train_pos_context = train_pos_context
        self.train_neg_context = train_neg_context
        self.num_n_samples = num_n_samples
        self.size_embedding = size_vector
        self.epoch_done = 0
        self.w_context = None
        self.w_center = None
        self.b_context = None
        self.b_center = None
        self.center_emb = None
        self.positive_emb = None
        self.negative_emb = None
        self.center_bias = None
        self.pos_bias = None
        self.neg_bias = None
        self.model = None

    def initialize_network(self):
        # Inputs needed for model
        center_vector = tf.keras.Input(shape=[1, ], name='center_input')
        positive_vector = tf.keras.Input(shape=[1, ], name='positive_context')
        negative_vector = tf.keras.Input(shape=[self.num_n_samples, ], name='negative_context')

        # Defining embedding layers needed in network
        self.w_context = Embedding(input_dim=self.n_products,
                                   output_dim=self.size_embedding,
                                   input_length=1,
                                   embeddings_initializer=tf.keras.initializers.TruncatedNormal(
                                       stddev=.08, seed=self.seed),
                                   name='context_embedding')
        self.w_center = Embedding(input_dim=self.n_products,
                                  output_dim=self.size_embedding,
                                  input_length=1,
                                  embeddings_initializer=tf.keras.initializers.TruncatedNormal(
                                      stddev=.08, seed=self.seed),
                                  name='center_embedding')
        self.b_context = Embedding(input_dim=self.n_products,
                                   output_dim=1,
                                   input_length=1,
                                   embeddings_initializer=tf.initializers.Constant(-1.5)
                                   , name='context_bias')
        self.b_center = Embedding(input_dim=self.n_products,
                                  output_dim=1,
                                  input_length=1,
                                  embeddings_initializer=tf.initializers.Constant(-1.5),
                                  name='center_bias')

        self.center_emb = self.w_center(center_vector)
        self.center_emb = Reshape((self.size_embedding,), name='Reshape_center_emb')(self.center_emb)
        self.center_bias = self.b_center(center_vector)
        self.center_bias = Reshape((1,), name='Reshape_center_bias')(self.center_bias)

        self.positive_emb = self.w_context(positive_vector)
        self.positive_emb = Reshape((self.size_embedding,), name='Reshape_positive_emb')(self.positive_emb)
        self.pos_bias = self.b_context(positive_vector)
        self.pos_bias = Reshape((1,), name='Reshape_positive_bias')(self.pos_bias)

        self.negative_emb = self.w_context(negative_vector)
        self.neg_bias = self.b_context(negative_vector)
        self.neg_bias = Reshape((self.num_n_samples,), name='Reshape_negative_bias')(self.neg_bias)

        context_emb = tf.concat([tf.expand_dims(self.positive_emb, axis=1), self.negative_emb], 1,
                                name='Context_emb_former')

        # Calculate similarity of center and context vector using dot product
        similarity = tf.einsum('ik,ijk->ij', self.center_emb, context_emb,
                               name='Calculating_similarity_context_center')

        # Calculate score by adding bias to similarity
        score = tf.add(similarity,
                       self.center_bias)
        score = tf.add(score,
                       tf.concat([self.pos_bias, self.neg_bias], axis=1))
        # Create output layer to get probabilities
        output = (Dense(1 + self.num_n_samples, activation='sigmoid', name='Calculate_probabilities')(score))

        # Create model
        self.model = Model([center_vector, positive_vector, negative_vector],
                           output,
                           name='product2vec')

        self.model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=False),
                           optimizer=tf.keras.optimizers.Adam(),
                           metrics=[tf.keras.metrics.Accuracy()])

    def train_network(self):
        tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=r".\logs", histogram_freq=1,
                                                              write_graph=True, write_images=True, embeddings_freq=1)
        labels = np.zeros((self.train_center.shape[0], self.num_n_samples + 1))
        for i in range(self.train_center.shape[0]):
            index = randint(0, self.num_n_samples)
            labels[i][0] = 1

        test_model = Model(inputs=self.model.input, outputs=self.model.get_layer(
            'Calculate_probabilities').output)
        output_test = test_model.predict(
            [self.train_center[:10], self.train_pos_context[:10], self.train_neg_context[:10]])
        print(output_test)
        print(self.train_center[:10])
        print(self.train_pos_context[:10])
        print(self.train_neg_context[:10])
        print(labels[:10])

        self.model.fit(x=[self.train_center, self.train_pos_context, self.train_neg_context],
                       y=[labels],
                       epochs=self.num_epoch,
                       shuffle=True,
                       batch_size=64,
                       validation_split=0.2,
                       callbacks=[tensorboard_callback])

        test_model = Model(inputs=self.model.input, outputs=self.model.get_layer(
            'Calculate_probabilities').output)
        output_test = test_model.predict(
            [self.train_center[:10], self.train_pos_context[:10], self.train_neg_context[:10]])
        print(output_test)

        self.model.save("model.h5")

        for i in range(self.num_epoch):
            pass


# Load small dataset from disk
center = np.loadtxt('small_center_products', delimiter=",", dtype=np.int32)
pos_context = np.loadtxt('small_positive_context_products', delimiter=",", dtype=np.int32)
neg_context = np.loadtxt('small_negative_context_products', delimiter=",", dtype=np.int32)

test = P2VTensorFlow(1000, 2, center, pos_context, neg_context, 20, 30)
test.initialize_network()
test.train_network()

Однако при обучении и отслеживании точности я получаю некоторые странные результаты. Во-первых, точность начинается с 0,0000e + 00 и очень медленно увеличивается до 0,0415. Результат обучения модели на моем полном наборе данных для 2 эпох: видно здесь Точность только кажется go выше, потому что выход плотного сигмовидного слоя активации выглядит следующим образом:

output =  [1.0000000e+00 2.8150848e-11 2.8790569e-11 2.5585041e-11 2.7863401e-11
  2.3193829e-11 3.2485150e-11 2.8669577e-11 2.5894085e-11 3.0460568e-11
  2.9323939e-11 2.8722720e-11 2.6484696e-11 2.7809879e-11 2.9465336e-11
  2.6407571e-11 2.9402460e-11 3.4942400e-11 2.7540987e-11 3.1028721e-11
  2.4985264e-11]

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

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

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