Мне поручено воссоздать результаты в документе: 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]
Я в тупике, модель во-первых, похоже, не отслеживает точность должным образом. Я искал на этом сайте похожие проблемы, но единственные люди, у которых была странная точность, использовали линейную активацию. Во-вторых, модель, кажется, идеально предсказывает, находятся ли продукты вместе в корзине или нет. Кажется, очень маловероятно, что это не ошибка или ошибка с моей стороны.
Наконец, если у вас есть какие-либо комментарии по макету, качеству кода или странному коду, пожалуйста, скажите об этом, я всегда хочу научиться делать лучше код.