[Интерпретация диссертации] Общая структура кода Ali DIEN

глубокое обучение

0x00 сводка

DIEN — это аббревиатура Ali Deep Interest Evolution Network.

В этой статье будет проанализирована общая идея исходного кода DIEN. Поскольку DIEN развивается на основе DIN, код в основном дублируется.

В этой статье используетсяGitHub.com/Mouna99/Вниз…реализация в.

0x01 Знакомство с файлом

Файлы данных в основном включают:

  • uid_voc.pkl: словарь пользователя, идентификатор, соответствующий имени пользователя;
  • mid_voc.pkl: словарь фильмов, идентификатор, соответствующий элементу;
  • cat_voc.pkl: Словарь типов, идентификатор соответствует категории;
  • item-info: информация о категории, соответствующая элементу;
  • reviews-info: просмотреть метаданные в формате: userID, itemID, рейтинг, отметка времени, данные, используемые для отрицательной выборки;
  • local_train_splitByUser: обучающие данные, формат одной строки: метка, имя пользователя, целевой элемент, категория целевого элемента, исторический элемент, соответствующая категория исторического элемента;
  • local_test_splitByUser: тестовые данные, формат такой же, как у обучающих данных;

Кодекс в основном включает в себя:

  • rnn.py: Измените исходный rnn в tensorflow, чтобы объединить внимание с rnn.
  • vecAttGruCell.py: Измените исходный код GRU, обратите на него внимание и спроектируйте структуру AUGRU.
  • data_iterator.py: итератор данных для непрерывного ввода данных
  • utils.py: некоторые вспомогательные функции, такие как функции активации DICE, расчет оценки внимания и т. д.
  • model.py:Файл модели DIEN
  • train.py: Запись модели, используемая для обучения данных, сохранения модели и тестовых данных.

0x02 Общая архитектура

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

Сеть эволюции глубоких интересов разделена на несколько слоев снизу вверх:

  • Уровень поведения: основная функция состоит в том, чтобы преобразовать продукты, которые просматривал пользователь, в соответствующие вложения и отсортировать их в соответствии со временем просмотра, то есть преобразовать исходные функции последовательности поведения класса id в последовательности поведения встраивания;
  • Слой извлечения интересов: основная функция заключается в извлечении последовательности интересов пользователя на основе последовательности поведения путем имитации процесса миграции интересов пользователя;
  • Уровень развития интереса: основная функция заключается в моделировании процесса развития интереса, связанного с текущей целевой рекламой, путем добавления механизма внимания на основе уровня извлечения интереса и моделирования процесса развития интереса, связанного с целевым элементом;
  • Объедините представление интересов с векторами внедрения рекламы, профиля пользователя и контекста. Наконец, используйте MLP для завершения окончательного прогноза;

0x03 Общий код

Код DIEN взят из train.py. train.py сначала оценивает тестовый набор с исходной моделью, а затем вызывает train:

  • Получите обучающие данные и тестовые данные, которые являются итераторами данных для непрерывного ввода данных.
  • Создайте соответствующую модель в соответствии с model_type
  • В соответствии с пакетным обучением тестовый набор оценивается каждые 1000 раз.

код показывает, как показано ниже:

def train(
        train_file = "local_train_splitByUser",
        test_file = "local_test_splitByUser",
        uid_voc = "uid_voc.pkl",
        mid_voc = "mid_voc.pkl",
        cat_voc = "cat_voc.pkl",
        batch_size = 128,
        maxlen = 100,
        test_iter = 100,
        save_iter = 100,
        model_type = 'DNN',
        seed = 2,
):

    with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess:
        ## 训练数据
        train_data = DataIterator(train_file, uid_voc, mid_voc, cat_voc, batch_size, maxlen, shuffle_each_epoch=False)
        ## 测试数据
        test_data = DataIterator(test_file, uid_voc, mid_voc, cat_voc, batch_size, maxlen)
        n_uid, n_mid, n_cat = train_data.get_n()

        ......
        
        elif model_type == 'DIEN':
            model = Model_DIN_V2_Gru_Vec_attGru_Neg(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE)
            
        ......    

        sess.run(tf.global_variables_initializer())
        sess.run(tf.local_variables_initializer())

        iter = 0
        lr = 0.001
        for itr in range(3):
            loss_sum = 0.0
            accuracy_sum = 0.
            aux_loss_sum = 0.
            for src, tgt in train_data:
                uids, mids, cats, mid_his, cat_his, mid_mask, target, sl, noclk_mids, noclk_cats = prepare_data(src, tgt, maxlen, return_neg=True)
                loss, acc, aux_loss = model.train(sess, [uids, mids, cats, mid_his, cat_his, mid_mask, target, sl, lr, noclk_mids, noclk_cats])
                loss_sum += loss
                accuracy_sum += acc
                aux_loss_sum += aux_loss
                iter += 1
                if (iter % test_iter) == 0:
                    eval(sess, test_data, model, best_model_path)
                    loss_sum = 0.0
                    accuracy_sum = 0.0
                    aux_loss_sum = 0.0
                if (iter % save_iter) == 0:
                    model.save(sess, model_path+"--"+str(iter))
            lr *= 0.5

0x04 Базовый класс модели

Базовым классом модели является Model, а его конструктор__init__Это можно понимать как слой поведения: основная функция заключается в преобразовании продуктов, которые просматривал пользователь, в соответствующее встраивание и сортировке их в соответствии со временем просмотра, то есть преобразование исходных функций последовательности поведения класса id в поведение встраивания. последовательности.

4.1 Основная логика

Основная логика следующая:

  • В области «Входы» создайте различные переменные-заполнители;
  • В области «Embedding_layer» создайте таблицу поиска внедрения пользователя и элемента и преобразуйте входные данные в соответствующее внедрение;
  • Объединить различные векторы встраивания, такие как объединение встраивания, соответствующего идентификатору элемента, и встраивания cateid, соответствующего элементу, в качестве встраивания элемента;

4.2 Модульный анализ

Следующие B — это размер пакета, T — длина последовательности, а H — скрытый размер.Переменные инициализации в программе следующие:

EMBEDDING_DIM = 18
HIDDEN_SIZE = 18 * 2
ATTENTION_SIZE = 18 * 2
best_auc = 0.0

4.2.1 Переменные сборки

Во-первых, создать переменную-заполнитель.

with tf.name_scope('Inputs'):
    # shape: [B, T] #用户行为特征(User Behavior)中的 movie id 历史行为序列。T为序列长度
    self.mid_his_batch_ph = tf.placeholder(tf.int32, [None, None], name='mid_his_batch_ph')
    # shape: [B, T] #用户行为特征(User Behavior)中的 category id 历史行为序列。T为序列长度
    self.cat_his_batch_ph = tf.placeholder(tf.int32, [None, None], name='cat_his_batch_ph')
    # shape: [B],  user id 序列。 (B:batch size)
    self.uid_batch_ph = tf.placeholder(tf.int32, [None, ], name='uid_batch_ph')
    # shape: [B],  movie id 序列。 (B:batch size)
    self.mid_batch_ph = tf.placeholder(tf.int32, [None, ], name='mid_batch_ph')
    # shape: [B],  category id 序列。 (B:batch size)
    self.cat_batch_ph = tf.placeholder(tf.int32, [None, ], name='cat_batch_ph')
    self.mask = tf.placeholder(tf.float32, [None, None], name='mask')
    # shape: [B]; sl:sequence length,User Behavior中序列的真实序列长度(?)
    self.seq_len_ph = tf.placeholder(tf.int32, [None], name='seq_len_ph')
    # shape: [B, T], y: 目标节点对应的 label 序列, 正样本对应 1, 负样本对应 0
    self.target_ph = tf.placeholder(tf.float32, [None, None], name='target_ph')
    # 学习速率
    self.lr = tf.placeholder(tf.float64, [])
    self.use_negsampling =use_negsampling
    if use_negsampling:
        self.noclk_mid_batch_ph = tf.placeholder(tf.int32, [None, None, None], name='noclk_mid_batch_ph') #generate 3 item IDs from negative sampling.
        self.noclk_cat_batch_ph = tf.placeholder(tf.int32, [None, None, None], name='noclk_cat_batch_ph')

Конкретные формы см. в следующих переменных времени выполнения.

self = {Model_DIN_V2_Gru_Vec_attGru_Neg} 
 cat_batch_ph = {Tensor} Tensor("Inputs/cat_batch_ph:0", shape=(?,), dtype=int32)
 uid_batch_ph = {Tensor} Tensor("Inputs/uid_batch_ph:0", shape=(?,), dtype=int32)
 mid_batch_ph = {Tensor} Tensor("Inputs/mid_batch_ph:0", shape=(?,), dtype=int32)
  
 cat_his_batch_ph = {Tensor} Tensor("Inputs/cat_his_batch_ph:0", shape=(?, ?), dtype=int32)
 mid_his_batch_ph = {Tensor} Tensor("Inputs/mid_his_batch_ph:0", shape=(?, ?), dtype=int32)
  
 lr = {Tensor} Tensor("Inputs/Placeholder:0", shape=(), dtype=float64)
 mask = {Tensor} Tensor("Inputs/mask:0", shape=(?, ?), dtype=float32)
 seq_len_ph = {Tensor} Tensor("Inputs/seq_len_ph:0", shape=(?,), dtype=int32)
 target_ph = {Tensor} Tensor("Inputs/target_ph:0", shape=(?, ?), dtype=float32)

 noclk_cat_batch_ph = {Tensor} Tensor("Inputs/noclk_cat_batch_ph:0", shape=(?, ?, ?), dtype=int32)
 noclk_mid_batch_ph = {Tensor} Tensor("Inputs/noclk_mid_batch_ph:0", shape=(?, ?, ?), dtype=int32)

 use_negsampling = {bool} True

4.2.2 Встраивание сборки

Затем создается таблица поиска встраивания пользователя и элемента, и входные данные преобразуются в соответствующее вложение, которое должно преобразовывать разреженные функции в плотные функции. Что касается принципа и анализа кода слоя внедрения, в этой серии будет специальная статья для объяснения.

Последующая буква U — это размер хеш-ведра для user_id, I — это хеш-багет для item_id, а C — это хеш-багет для cat_id.

Обратите внимание, что такие переменные, как self.mid_his_batch_ph, сохраняют историческую последовательность поведения пользователя, размер — [B, T], поэтому при выполнении embedding_lookup выходной размер — [B, T, H/2];

# Embedding layer
with tf.name_scope('Embedding_layer'):
    # shape: [U, H/2], user_id的embedding weight. U是user_id的hash bucket size,即user count
    self.uid_embeddings_var = tf.get_variable("uid_embedding_var", [n_uid, EMBEDDING_DIM])
    # 从uid embedding weight 中取出 uid embedding vector
    self.uid_batch_embedded = tf.nn.embedding_lookup(self.uid_embeddings_var, self.uid_batch_ph)

    # shape: [I, H/2], item_id的embedding weight. I是item_id的hash bucket size,即movie count
    self.mid_embeddings_var = tf.get_variable("mid_embedding_var", [n_mid, EMBEDDING_DIM])
    # 从mid embedding weight 中取出 uid embedding vector
    self.mid_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, self.mid_batch_ph)
    # 从mid embedding weight 中取出 mid history embedding vector,是正样本
    # 注意 self.mid_his_batch_ph这样的变量 保存用户的历史行为序列, 大小为 [B, T],所以在进行 embedding_lookup 时,输出大小为 [B, T, H/2]; 
    self.mid_his_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, self.mid_his_batch_ph)
    # 从mid embedding weight 中取出 mid history embedding vector,是负样本
    if self.use_negsampling:
        self.noclk_mid_his_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, self.noclk_mid_batch_ph)

    # shape: [C, H/2], cate_id的embedding weight. C是cat_id的hash bucket size
    self.cat_embeddings_var = tf.get_variable("cat_embedding_var", [n_cat, EMBEDDING_DIM])
    # 从 cid embedding weight 中取出 cid history embedding vector,是正样本
    self.cat_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, self.cat_batch_ph)
    # 从 cid embedding weight 中取出 cid embedding vector,是正样本
    self.cat_his_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, self.cat_his_batch_ph)
    # 从 cid embedding weight 中取出 cid history embedding vector,是负样本
    if self.use_negsampling:
        self.noclk_cat_his_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, self.noclk_cat_batch_ph)

Конкретные формы см. в следующих переменных времени выполнения.

self = {Model_DIN_V2_Gru_Vec_attGru_Neg} 
 cat_embeddings_var = {Variable} <tf.Variable 'cat_embedding_var:0' shape=(1601, 18) dtype=float32_ref>
 uid_embeddings_var = {Variable} <tf.Variable 'uid_embedding_var:0' shape=(543060, 18) dtype=float32_ref>
 mid_embeddings_var = {Variable} <tf.Variable 'mid_embedding_var:0' shape=(367983, 18) dtype=float32_ref>
  
 cat_batch_embedded = {Tensor} Tensor("Embedding_layer/embedding_lookup_4:0", shape=(?, 18), dtype=float32)
 mid_batch_embedded = {Tensor} Tensor("Embedding_layer/embedding_lookup_1:0", shape=(?, 18), dtype=float32)
 uid_batch_embedded = {Tensor} Tensor("Embedding_layer/embedding_lookup:0", shape=(?, 18), dtype=float32)

 cat_his_batch_embedded = {Tensor} Tensor("Embedding_layer/embedding_lookup_5:0", shape=(?, ?, 18), dtype=float32)
 mid_his_batch_embedded = {Tensor} Tensor("Embedding_layer/embedding_lookup_2:0", shape=(?, ?, 18), dtype=float32)

 noclk_cat_his_batch_embedded = {Tensor} Tensor("Embedding_layer/embedding_lookup_6:0", shape=(?, ?, ?, 18), dtype=float32)
 noclk_mid_his_batch_embedded = {Tensor} Tensor("Embedding_layer/embedding_lookup_3:0", shape=(?, ?, ?, 18), dtype=float32)

4.2.3 Встраивание сплайсинга

Эта часть предназначена для объединения различных векторов встраивания, таких какitem的id对应的embeddingи яtem对应的cateid的embeddingСращивание, вместе как вложение элемента;

Примечание о формах:

  • Обратите внимание, что на предыдущем шаге такие переменные, как self.mid_his_batch_ph, сохраняют историческую последовательность поведения пользователя, размер которой равен [B, T], поэтому при выполнении embedding_lookup выходной размер равен [B, T, H/2].
  • Здесь объедините вложения Goods и Cate, чтобы получить размер [B, T, H]. Обратите внимание, что значение параметра оси в tf.concat равно 2.

Примечание по логике:

Первый шагself.item_eb = tf.concat([self.mid_batch_embedded, self.cat_batch_embedded], 1)То есть получить вложение, соответствующее целевому узлу в пакете, и сохранить его вi_emb, он объединяется с вложениями товаров и категорий. Согласно схеме архитектуры:

Второй шагself.item_his_eb = tf.concat([self.mid_his_batch_embedded, self.cat_his_batch_embedded], 2)логически да两个历史矩阵Для обработки эти две матрицы истории сохраняют историческую последовательность поведения пользователя, размер[B, T], поэтому при выполнении embedding_lookup выходной размер равен[B, T, H/2]. Затем объедините вложения Goods и Cate, чтобы получить[B, T, H]размер примечаниеtf.concatсерединаaxisЗначение параметра равно 2. Согласно схеме архитектуры:

Конкретный код выглядит следующим образом:

# 正样本的embedding拼接,正样本包括item和cate。即将目标节点对应的商品 embedding 和类目 embedding 进行 concatenation
self.item_eb = tf.concat([self.mid_batch_embedded, self.cat_batch_embedded], 1)
# 将 Goods 和 Cate 的 embedding 进行 concat, 得到   [B, T, H] 大小. 注意到 tf.concat 中的 axis 参数值为 2
self.item_his_eb = tf.concat([self.mid_his_batch_embedded, self.cat_his_batch_embedded], 2)
self.item_his_eb_sum = tf.reduce_sum(self.item_his_eb, 1)

# 负样本的embedding拼接,负样本包括item和cate。即将目标节点对应的商品 embedding 和类目 embedding 进行 concatenation
if self.use_negsampling:
    # 0 means only using the first negative item ID. 3 item IDs are inputed in the line 24.
    self.noclk_item_his_eb = tf.concat(   
        [self.noclk_mid_his_batch_embedded[:, :, 0, :], self.noclk_cat_his_batch_embedded[:, :, 0, :]], -1)
    # cat embedding 18 concate item embedding 18.
    self.noclk_item_his_eb = tf.reshape(self.noclk_item_his_eb,
                                        [-1, tf.shape(self.noclk_mid_his_batch_embedded)[1], 36])
    self.noclk_his_eb = tf.concat([self.noclk_mid_his_batch_embedded, self.noclk_cat_his_batch_embedded], -1)
    self.noclk_his_eb_sum_1 = tf.reduce_sum(self.noclk_his_eb, 2)
    self.noclk_his_eb_sum = tf.reduce_sum(self.noclk_his_eb_sum_1, 1)

Конкретные формы см. в следующих переменных времени выполнения.

self = {Model_DIN_V2_Gru_Vec_attGru_Neg} 
 item_eb = {Tensor} Tensor("concat:0", shape=(?, 36), dtype=float32)
 item_his_eb = {Tensor} Tensor("concat_1:0", shape=(?, ?, 36), dtype=float32)
 item_his_eb_sum = {Tensor} Tensor("Sum:0", shape=(?, 36), dtype=float32)
  
 noclk_item_his_eb = {Tensor} Tensor("Reshape:0", shape=(?, ?, 36), dtype=float32)
 noclk_his_eb = {Tensor} Tensor("concat_3:0", shape=(?, ?, ?, 36), dtype=float32)
 noclk_his_eb_sum = {Tensor} Tensor("Sum_2:0", shape=(?, 36), dtype=float32)
 noclk_his_eb_sum_1 = {Tensor} Tensor("Sum_1:0", shape=(?, ?, 36), dtype=float32)

0x05 Model_DIN_V2_Gru_Vec_attGru_Neg

Model_DIN_V2_Gru_Vec_attGru_Neg – это модель, соответствующая DIEN. История пользователя должна представлять собой временной ряд. Если она передается в RNN, можно считать, что последнее состояние содержит всю историческую информацию. Поэтому авторы используют двухуровневый GRU для моделирования интересов пользователей.

Model_DIN_V2_Gru_Vec_attGru_Neg__init__Функция строит этот двухуровневый GRU, и логика ее кода такова:

  • Первый уровень «rnn_1» соответствует желтой части диаграммы архитектуры, а именно уровню извлечения интересов.
  • Второй уровень Attention_layer_1 соответствует уровню развития интересов в красной части диаграммы архитектуры, а основным компонентом является AUGRU.

5.1 Первый слой «rnn_1»

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

Основная функция заключается в извлечении последовательности интересов пользователя на основе последовательности поведения путем имитации процесса миграции интересов пользователя. В динамический rnn (первый слой ГРУ) встраивается элемент истории поведения пользователя, при этом вычисляются вспомогательные потери, а на выходе - интерес пользователя в каждый момент времени.

# RNN layer(-s)
with tf.name_scope('rnn_1'):
    rnn_outputs, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=self.item_his_eb,
                                 sequence_length=self.seq_len_ph, dtype=tf.float32,
                                 scope="gru1")

aux_loss_1 = self.auxiliary_loss(rnn_outputs[:, :-1, :], self.item_his_eb[:, 1:, :],
                                 self.noclk_item_his_eb[:, 1:, :],
                                 self.mask[:, 1:], stag="gru")
self.aux_loss = aux_loss_1

5.1.1 GRU

ГРУ выглядит следующим образом, а РНН мы представим в другой статье.

5.1.2 Дополнительные потери

Расчет вспомогательных потерь фактически представляет собой двухклассовую модель, соответствующую статье:

В частности, он использует поведение b(t+1) в момент времени t в качестве контроля для изучения вектора скрытого слоя ht. Положительные и отрицательные выборки представляют собой t-й вектор встраивания элемента, по которому пользователь щелкнул/не щелкнул, соответственно.

  • Используйте реальное следующее поведение в качестве положительного образца;
  • Выбор отрицательных примеров либо случайным образом выбирается из элементов, с которыми пользователь не взаимодействовал, либо случайным образом выбирается из элементов, которые были показаны пользователю, но пользователь не щелкнул;

В коде используется tf.concat([h_states, click_seq], -1), где -1 означает увеличение предпоследнего измерения, а остальные остаются неизменными.

Например, (3,2,4) + (3,2,4) Если вы действуете в соответствии с -1, последнее измерение формы увеличивается до (3,2,8), что объединяет два тензора.

Конкретный код выглядит следующим образом:

def auxiliary_loss(self, h_states, click_seq, noclick_seq, mask, stag = None):
    mask = tf.cast(mask, tf.float32)
    # 倒数第一维度concat,其余不变
    click_input_ = tf.concat([h_states, click_seq], -1) 
    # 倒数第一维度concat,其余不变
    noclick_input_ = tf.concat([h_states, noclick_seq], -1)
    # 获取正样本最后一个y_hat
    click_prop_ = self.auxiliary_net(click_input_, stag = stag)[:, :, 0]
    # 获取正样本最后一个y_hat
    noclick_prop_ = self.auxiliary_net(noclick_input_, stag = stag)[:, :, 0]
    # 对数损失,并且mask出真实历史行为
    click_loss_ = - tf.reshape(tf.log(click_prop_), [-1, tf.shape(click_seq)[1]]) * mask
    noclick_loss_ = - tf.reshape(tf.log(1.0 - noclick_prop_), [-1, tf.shape(noclick_seq)[1]]) * mask
    loss_ = tf.reduce_mean(click_loss_ + noclick_loss_)
    return loss_

def auxiliary_net(self, in_, stag='auxiliary_net'):
    bn1 = tf.layers.batch_normalization(inputs=in_, name='bn1' + stag, reuse=tf.AUTO_REUSE)
    dnn1 = tf.layers.dense(bn1, 100, activation=None, name='f1' + stag, reuse=tf.AUTO_REUSE)
    dnn1 = tf.nn.sigmoid(dnn1)
    dnn2 = tf.layers.dense(dnn1, 50, activation=None, name='f2' + stag, reuse=tf.AUTO_REUSE)
    dnn2 = tf.nn.sigmoid(dnn2)
    dnn3 = tf.layers.dense(dnn2, 2, activation=None, name='f3' + stag, reuse=tf.AUTO_REUSE)
    y_hat = tf.nn.softmax(dnn3) + 0.00000001
    return y_hat

5.1.3 Роль маски

Что касается роли маски, поговорим о ней в связке с Трансформером:

mask представляет собой маску, которая маскирует определенные значения, чтобы они не влияли на обновления параметров. В модели Transformer используется два вида масок, а именно маска заполнения и маска последовательности. Среди них маска заполнения должна использоваться во всех масштабируемых скалярных произведениях внимания, а маска последовательности используется только при самостоятельном внимании декодера.

Padding Mask

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

Конкретный метод заключается в том, чтобы добавить очень большое отрицательное число (отрицательная бесконечность) к значению этих позиций, чтобы после softmax вероятность этих позиций была близка к 0! И наша маска заполнения на самом деле является тензором, каждое значение является логическим значением, а место, где значение равно false, — это то место, где мы хотим обработать.

Sequence mask

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

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

Для собственного внимания декодера внутри используется масштабированное скалярное внимание, а маска заполнения и маска последовательности требуются как ATTN_MASK.Конкретная реализация состоит в том, что две MASK добавляются как ATTN_MASK.

В других случаях attn_mask всегда равен маске заполнения.

DIN использует здесь заполняющую маску.

5.2 Второй уровень «Внимание_уровень_1»

Второй уровень Attention_layer_1 соответствует уровню развития интересов в красной части диаграммы архитектуры, а основным компонентом является AUGRU.

5.2.1 Механизм внимания

Механизм внимания: представьте, что составляющие элементы в источнике состоят из ряда пар данных . В это время, учитывая элемент Запрос в Цели, сходство или корреляция между Запросом и каждым Ключом вычисляется путем вычисления , получите весовой коэффициент каждого ключа, соответствующего значению, а затем выполните взвешенное суммирование значения, чтобы получить окончательное значение внимания. Итак, по сути, механизм Attention взвешивает и суммирует значения Value элементов в Source, а Query и Key используются для вычисления весового коэффициента соответствующего Value. То есть его существенную идею можно переписать в виде следующей формулы:

这里写图片描述

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

Другое понимание: механизм внимания также можно рассматривать как разновидность мягкой адресации (Soft Addressing): источником можно считать содержимое, хранящееся в памяти, элемент состоит из ключа адреса и значения значения, в настоящее время запрос Key=Query , целью которого является извлечение из памяти соответствующего значения Value, то есть значения Attention. Адресация выполняется путем сравнения сходства между Запросом и адресом элемента Ключ в памяти.Причина, по которой она называется мягкой адресацией, означает, что в отличие от общей адресации она находит только часть содержимого из сохраненного содержимого, но это возможно чтобы найти фрагмент контента из каждого адреса ключа. Контент будет удален, и важность извлечения контента будет определена в соответствии с сходством между запросом и ключом, а затем значение взвешено и суммировано, так что окончательный Значение значения, то есть значение Внимание, можно вынуть. Поэтому многие исследователи рассматривают механизм Attention как частный случай мягкой адресации, что также весьма разумно.

Что касается конкретного процесса расчета механизма «Внимание», то, если абстрагироваться от большинства текущих методов, его можно свести к двум процессам:

  • Первый процесс заключается в вычислении весового коэффициента на основе запроса и ключа;
  • Второй процесс выполняет взвешенное суммирование Values ​​согласно весовым коэффициентам.

Первый процесс можно разделить на два этапа:

  • Первый небольшой этап вычисляет сходство или корреляцию между ними на основе запроса и ключа;
  • Второй малый этап нормализует исходную оценку первого этапа;

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

这里写图片描述

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

这里写图片描述

Оценки, сгенерированные на первом этапе, имеют разные диапазоны значений в зависимости от конкретного метода генерации.

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

这里写图片描述

Результатом расчета ai второго этапа является весовой коэффициент, соответствующий Valuei, после чего путем взвешенного суммирования можно получить значение Attention:

这里写图片描述

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

5.2.2 Attention layer

В DIEN роль слоя «Attention_layer_1» заключается в моделировании процесса эволюции интереса, связанного с текущей целевой рекламой, путем добавления механизма «Внимание» на основе уровня извлечения интереса, а также в моделировании процесса эволюции интереса, связанного с текущим целевым рекламным объявлением. целевой элемент. Выходные данные первого уровня подаются в ГРУ второго уровня, а оценка внимания (рассчитанная на основе выходного вектора первого уровня и материала-кандидата) используется для управления воротами обновления ГРУ второго уровня. .

Сначала нам нужно рассчитать оценку внимания, которая будет введена позже в рамках ГРУ.

Соответствующая бумага здесь

код показывает, как показано ниже:

# Attention layer
with tf.name_scope('Attention_layer_1'):
    att_outputs, alphas = din_fcn_attention(self.item_eb, rnn_outputs, ATTENTION_SIZE, self.mask,
                                            softmax_stag=1, stag='1_1', mode='LIST', return_alphas=True)

Код получает распределение интереса пользователя с помощью следующих шагов.Можно понять, что когда приходит запрос, сначала вычисляется сходство по ключу (факту) этого запроса и ряда кандидатов, а затем вычисляется конкретное значение кандидаты по сходству:

  • Если time_major, он будет конвертировать: (T,B,D) => (B,T,D);
  • Преобразовать маску.
    • Используйте tf.ones_like(mask) для создания тензора той же размерности, что и маска, с элементами 1;
    • Используйте tf.equal для преобразования маски из int в bool. Функция tf.equal состоит в том, чтобы определить, равны ли два входа, истинно ли равенство и ложно ли неравенство;
  • Преобразуйте размерность запроса и измените запрос на ту же форму, что и факты B * T * H; здесь T меняется с каждыми конкретными обучающими данными, например, определенный временной ряд пользователя имеет длину 5, а другой временной ряд 15;
    • запрос [B, H], преобразованный в запросы с размерностью (B, T, H). Рассчитать веса для каждого элемента в последовательностях pos_item и поведения пользователя. здесь используетсяtf.tile(query, [1, tf.shape(facts)[1]]). Результатом tf.shape(keys)[1] является T, а запросом является [B, H].После плитки первое измерение расширяется в соответствии с T, чтобы получить [B, T * H];
    • Изменить форму запросов до размера фактов: [B, T, H];
  • Выполните больше операций, чтобы зафиксировать отношения между элементами поведения и элементами-кандидатами до MLP: сложение, вычитание, умножение и деление и т. д. Затем получил информацию от местного блока активации. То есть вставка, соответствующая рекламным запросам-кандидатам, вставка, соответствующая историческим фактам последовательности поведения пользователя, плюс перекрестные функции между ними, результат после объединения;
  • Внимание операция, цель состоит в том, чтобы вычислить степень корреляции между запросом и ключом. Вес каждого ключа в запросах и фактах получается через трехслойную нейронную сеть, а выходной узел этой сети DNN равен 1;
    • Форма последнего шага d_layer_3_all — [B, T, 1];
    • Затем изменение формы равно [B, 1, T], ось = 2. Это измерение представляет параметры веса, соответствующие T последовательностям поведения пользователя соответственно;
    • вывод внимания, [B, 1, T];
  • Получить значимый балл;
    • использоватьkey_masks = tf.expand_dims(mask, 1)Увеличьте размер маски с [B, T] до [B, 1, T];
    • Используйте tf.ones_like (оценки), построенный как измерение и оценки, все элементы тензора равны 1;
    • Маска заполнения заполняется небольшим отрицательным числом, чтобы при последующем вычислении softmax результат e^{x} был приблизительно равен 0;
    • Выполните операцию заполнения [B, 1, T]. Чтобы игнорировать влияние заполнения на заполнение, код использует tf.where для установки веса вектора заполнения (свободных элементов в каждой выборочной последовательности) на минимальное значение (-2 ** 32 + 1) вместо 0;
    • использоватьtf.where(key_masks, scores, paddings)получить действительно значимую оценку;
  • Масштабирование — стандартная операция внимания, после масштабирования оно отправляется в softmax для получения окончательного веса. Но эта часть в коде не используется и аннулируется;
  • После нормализации с помощью softmax получается нормализованный вес;
  • Здесь были получены правильные весовые оценки и исторические факты последовательности поведения пользователей, поэтому представление интересов конечного пользователя получено через взвешенную сумму;
    • Если это режим SUM, умножьте матрицу, чтобы получить представление интересов пользователя; в частности, размер оценок равен [B, 1, T], что представляет собой вес каждого исторического поведения, а факты представляют собой историческую последовательность поведения, и размер [B, T] , H], оба выполняются путем умножения матриц, и выходной результат равен [B, 1, H].
    • В противном случае код Хада умножается.
      • Сначала измените форму баллов с [B, 1, H] на «Пакетная * Время»;
      • И используйте expand_dims, чтобы увеличить баллы на одно измерение в конце;
      • Затем возьмите произведение кода Хада: [B, T, H] x [B, T, 1] = [B, T, H];
      • Наконец, измените форму на Пакет * Время * Скрытый размер;

Конкретный код выглядит следующим образом:

def din_fcn_attention(query, facts, attention_size, mask, stag='null', mode='SUM', softmax_stag=1, time_major=False, return_alphas=False, forCnn=False):
    '''
    query :候选广告,shape: [B, H], 即i_emb;
    facts :用户历史行为,shape: [B, T, H], 即h_emb,T是padding后的长度,每个长H的emb代表一个item;
    mask : Batch中每个行为的真实意义,shape: [B, H];    
    '''     
    if isinstance(facts, tuple):
        # In case of Bi-RNN, concatenate the forward and the backward RNN outputs.
        facts = tf.concat(facts, 2)
    if len(facts.get_shape().as_list()) == 2:
        facts = tf.expand_dims(facts, 1)

    if time_major:
        # (T,B,D) => (B,T,D)
        facts = tf.array_ops.transpose(facts, [1, 0, 2])
        
    # Trainable parameters
    mask = tf.equal(mask, tf.ones_like(mask))
    facts_size = facts.get_shape().as_list()[-1]  # D value - hidden size of the RNN layer
    querry_size = query.get_shape().as_list()[-1] # H,这里是36
    
    # 和DIN attention不同
    query = tf.layers.dense(query, facts_size, activation=None, name='f1' + stag)
    query = prelu(query)
    
    # 1. 转换query维度,变成历史维度T 
    # query是[B, H],转换到 queries 维度为(B, T, H),为了让pos_item和用户行为序列中每个元素计算权重
    # 此时query是 Tensor("concat:0", shape=(?, 36), dtype=float32)
    # tf.shape(keys)[1] 结果就是 T,query是[B, H],经过tile,就是把第一维按照 T 展开,得到[B, T * H] 
    queries = tf.tile(query, [1, tf.shape(facts)[1]]) # [B, T * H], 想象成贴瓷砖
    # 此时 queries 是 Tensor("Attention_layer/Tile:0", shape=(?, ?), dtype=float32)
    # queries 需要 reshape 成和 facts 相同的大小: [B, T, H]
    queries = tf.reshape(queries, tf.shape(facts)) # [B, T * H] -> [B, T, H]
    # 此时 queries 是 Tensor("Attention_layer/Reshape:0", shape=(?, ?, 36), dtype=float32) 
    
    # 2. 这部分目的就是为了在MLP之前多做一些捕获行为item和候选item之间关系的操作:加减乘除等。
    # 得到 Local Activation Unit 的输入。即 候选广告 queries 对应的 emb,用户历史行为序列 facts
    # 对应的 embed, 再加上它们之间的交叉特征, 进行 concat 后的结果
    din_all = tf.concat([queries, facts, queries-facts, queries*facts], axis=-1) # T*[B,H] ->[B, T, H]
    
    # 3. attention操作,通过几层MLP获取权重,这个DNN 网络的输出节点为 1
    d_layer_1_all = tf.layers.dense(din_all, 80, activation=tf.nn.sigmoid, name='f1_att' + stag)
    d_layer_2_all = tf.layers.dense(d_layer_1_all, 40, activation=tf.nn.sigmoid, name='f2_att' + stag)
    d_layer_3_all = tf.layers.dense(d_layer_2_all, 1, activation=None, name='f3_att' + stag)
  	# 上一层 d_layer_3_all 的 shape 为 [B, T, 1]
 	  # 下一步 reshape 为 [B, 1, T], axis=2 这一维表示 T 个用户行为序列分别对应的权重参数    
    d_layer_3_all = tf.reshape(d_layer_3_all, [-1, 1, tf.shape(facts)[1]])
    scores = d_layer_3_all # attention的输出, [B, 1, T]
    
    # 4. 得到有真实意义的score
    # key_masks = tf.sequence_mask(facts_length, tf.shape(facts)[1])   # [B, T]
    key_masks = tf.expand_dims(mask, 1) # [B, 1, T]
    # padding的mask后补一个很小的负数,这样后面计算 softmax 时, e^{x} 结果就约等于 0
    paddings = tf.ones_like(scores) * (-2 ** 32 + 1) # 注意初始化为极小值
    # [B, 1, T] padding操作,为了忽略了padding对总体的影响,代码中利用tf.where将padding的向量(每个样本序列中空缺的商品)权重置为极小值(-2 ** 32 + 1),而不是0
    if not forCnn:
        scores = tf.where(key_masks, scores, paddings)  # [B, 1, T]

    # 5. Scale # attention的标准操作,做完scaled后再送入softmax得到最终的权重。
    # scores = scores / (facts.get_shape().as_list()[-1] ** 0.5)

    # 6. Activation,得到归一化后的权重
    if softmax_stag:
        scores = tf.nn.softmax(scores)  # [B, 1, T]

    # 7. 得到了正确的权重 scores 以及用户历史行为序列 facts, 再进行矩阵相乘得到用户的兴趣表征
    # Weighted sum,
    if mode == 'SUM':
        # scores 的大小为 [B, 1, T], 表示每条历史行为的权重,
        # facts 为历史行为序列, 大小为 [B, T, H];
        # 两者用矩阵乘法做, 得到的结果 output 就是 [B, 1, H]
        # B * 1 * H 三维矩阵相乘,相乘发生在后两维,即 B * (( 1 * T ) * ( T * H ))
        # 这里的output是attention计算出来的权重,即论文公式(3)里的w,
        output = tf.matmul(scores, facts)  # [B, 1, H]
        # output = tf.reshape(output, [-1, tf.shape(facts)[-1]])
    else:
        # 从 [B, 1, H] 变化成 Batch * Time
        scores = tf.reshape(scores, [-1, tf.shape(facts)[1]]) 
        # 先把scores在最后增加一维,然后进行哈达码积,[B, T, H] x [B, T, 1] =  [B, T, H]
        output = facts * tf.expand_dims(scores, -1) 
        output = tf.reshape(output, tf.shape(facts)) # Batch * Time * Hidden Size
    return output

5.2.3 VecAttGRUCell

Далее идет структура AUGRU, где разработана новая структура VecAttGRUCell. Конкретный принцип RNN Cell будет обсуждаться в другой статье.

Классификация текста, основанная на глубоком обучении, также сталкивается с проблемой сжатия нескольких векторов слов в абзаце в один вектор для представления абзаца. Обычный метод состоит в том, чтобы передать несколько векторов слов в RNN, а выходной вектор RNN в последний момент представляет собой «объединенный» результат нескольких векторов слов.. DIEN позаимствовал эту идею и модифицировал структуру GRU, используя показатель внимания для управления воротами.

Модификация, которую Али сделал здесь, в основном представляет собой функцию вызова, которая является модификацией att_score:

u = (1.0 - att_score) * u
new_h = u * state + (1 - u) * c
return new_h, new_h    

Конкретный код:

def call(self, inputs, state, att_score=None):
    ......
    c = self._activation(self._candidate_linear([inputs, r_state]))
    u = (1.0 - att_score) * u  # 这里是新增加的
    new_h = u * state + (1 - u) * c # 这里是新增加的
    return new_h, new_h

5.2.4 Процесс эволюции вычислительных процентов

После проектирования новой ячейки ГРУ мы можем рассчитать эволюцию интереса, что и делает «rnn_2».

with tf.name_scope('rnn_2'):
    rnn_outputs2, final_state2 = dynamic_rnn(VecAttGRUCell(HIDDEN_SIZE), inputs=rnn_outputs,
                                             att_scores = tf.expand_dims(alphas, -1),
                                             sequence_length=self.seq_len_ph, dtype=tf.float32,
                                             scope="gru2")

5.2.5 Вычислить вход полносвязного слоя

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

inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, final_state2], 1)

0x06 Полностью подключенный слой

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

Так через многослойную нейронную сеть получается окончательное оценочное значение ctr, и эта часть представляет собой вызов функции.

# Fully connected layer
self.build_fcn_net(inp, use_dice=True)

В соответствии с бумагой:

Логика следующая:

  • Сначала выполните пакетную нормализацию;
  • Добавить полносвязный слойtf.layers.dense(bn1, 200, activation=None, name='f1');
  • Активировать с помощью кубиков или прелу;
  • Добавить полносвязный слойtf.layers.dense(dnn1, 80, activation=None, name='f2');
  • Активировать с помощью кубиков или прелу;
  • Добавить полносвязный слойtf.layers.dense(dnn2, 2, activation=None, name='f3');
  • получить выводy_hat = tf.nn.softmax(dnn3) + 0.00000001;
  • Выполнить перекрестную энтропию и инициализацию оптимизатора;
    • получить перекрестную энтропию- tf.reduce_mean(tf.log(self.y_hat) * self.target_ph);
    • Если есть отрицательная выборка, необходимо добавить дополнительные потери;
    • используйте AdamOptimizer;
  • Рассчитать точность;

Конкретный код выглядит следующим образом:

def build_fcn_net(self, inp, use_dice = False):
    bn1 = tf.layers.batch_normalization(inputs=inp, name='bn1')
    dnn1 = tf.layers.dense(bn1, 200, activation=None, name='f1')
    if use_dice:
        dnn1 = dice(dnn1, name='dice_1')
    else:
        dnn1 = prelu(dnn1, 'prelu1')

    dnn2 = tf.layers.dense(dnn1, 80, activation=None, name='f2')
    if use_dice:
        dnn2 = dice(dnn2, name='dice_2')
    else:
        dnn2 = prelu(dnn2, 'prelu2')
    dnn3 = tf.layers.dense(dnn2, 2, activation=None, name='f3')
    self.y_hat = tf.nn.softmax(dnn3) + 0.00000001

    with tf.name_scope('Metrics'):
        # Cross-entropy loss and optimizer initialization
        ctr_loss = - tf.reduce_mean(tf.log(self.y_hat) * self.target_ph)
        self.loss = ctr_loss
        if self.use_negsampling:
            self.loss += self.aux_loss
        tf.summary.scalar('loss', self.loss)
        self.optimizer = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(self.loss)

        # Accuracy metric
        self.accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.round(self.y_hat), self.target_ph), tf.float32))
        tf.summary.scalar('accuracy', self.accuracy)

    self.merged = tf.summary.merge_all()

0x07 Обучить модель

Используйте model.train для обучения модели.

Входные данные для model.train:

  • Идентификатор пользователя;
  • Идентификатор элемента цели;
  • cateid, соответствующий целевому элементу;
  • Список идентификаторов элементов исторического поведения пользователя;
  • Список идентификаторов категорий, соответствующих элементу исторического поведения пользователя;
  • историческая маска поведения;
  • целевое значение;
  • продолжительность исторического поведения;
  • скорость обучения;
  • Данные отрицательной выборки;

Код поезда следующий:

def train(self, sess, inps):
    if self.use_negsampling:
        loss, accuracy, aux_loss, _ = sess.run([self.loss, self.accuracy, self.aux_loss, self.optimizer], feed_dict={
            self.uid_batch_ph: inps[0],
            self.mid_batch_ph: inps[1],
            self.cat_batch_ph: inps[2],
            self.mid_his_batch_ph: inps[3],
            self.cat_his_batch_ph: inps[4],
            self.mask: inps[5],
            self.target_ph: inps[6],
            self.seq_len_ph: inps[7],
            self.lr: inps[8],
            self.noclk_mid_batch_ph: inps[9],
            self.noclk_cat_batch_ph: inps[10],
        })
        return loss, accuracy, aux_loss
    else:
        loss, accuracy, _ = sess.run([self.loss, self.accuracy, self.optimizer], feed_dict={
            self.uid_batch_ph: inps[0],
            self.mid_batch_ph: inps[1],
            self.cat_batch_ph: inps[2],
            self.mid_his_batch_ph: inps[3],
            self.cat_his_batch_ph: inps[4],
            self.mask: inps[5],
            self.target_ph: inps[6],
            self.seq_len_ph: inps[7],
            self.lr: inps[8],
        })
        return loss, accuracy, 0

В следующей статье будет представлена ​​RNN Cell, так что следите за обновлениями.

0xEE Личная информация

★★★★★★Думая о жизни и технологиях★★★★★★

Публичный аккаунт WeChat:мысли Росси

Если вы хотите получать своевременные новости о статьях, написанных отдельными лицами, или хотите видеть технические материалы, рекомендованные отдельными лицами, обратите внимание.

ссылка 0xFF

Ручная работа в широком и глубоком масштабе с помощью NumPy

Узнайте, как Google реализует модель Wide & Deep (1)

Узнайте, как Youtube использует глубокое обучение, чтобы давать рекомендации

Также прокомментируйте сеть Deep Interest Evolution Network

От DIN к DIEN, чтобы увидеть эволюцию алгоритма CTR Али

Глава 7 Искусственный интеллект, 7.6 Применение DNN в сценариях поиска (Автор: Жэнь Чжун)

#Paper Reading# Deep Interest Network for Click-Through Rate Prediction

【Чтение бумаги】 Сеть Deep Interest Evolution для прогнозирования кликабельности

Также прокомментируйте сеть Deep Interest Evolution Network

Текст статьи: «Сеть Deep Interest Evolution для прогнозирования кликабельности»

【Бумажные заметки】Сеть развития глубокого интереса (AAAI 2019)

[Заметки для чтения] Сеть Deep Interest Evolution для прогнозирования рейтинга кликов

DIN (Deep Interest Network): основная идея + комментарии по чтению исходного кода

Расчет рейтинга кликов по рекламе (5) — Ali Deep Interest Network Theory

Подробное объяснение принципа модели Deep Interest Network для оценки CTR.

LSTM, понятные каждому

Понимание RNN, LSTM и GRU из анимированных графиков

Машинное обучение Ли Хунъи Национального Тайваньского университета (1) — RNN и LSTM

Машинное обучение Ли Хунъи (2016)

Система рекомендаций соответствует глубокому обучению (24) - принцип и фактическая борьба за развитие сети глубокого интереса DIEN!

из google.protobuf.pyext import _message, ImportError: Ошибка загрузки DLL при использовании tensorflow

Знакомство с сетью глубокого интереса DIN и анализ исходного кода

CTR Prediction Paper Intensive Reading (8) — Deep Interest Network для прогнозирования кликабельности

Ali CTR оценивает трилогию (1): Deep Interest NetWork для ПРОГНОЗИРОВАНИЯ СКОРОСТИ КЛИКОВ

Ali CTR Prediction Trilogy (2): краткий анализ сети Deep Interest Evolution для прогнозирования кликабельности

Интерпретация сети Deep Interest

Сеть глубокого интереса (DIN, сеть глубокого интереса)

Анализ официальной реализации документа DIN

Как смоделировать последовательность пользователей в исходном коде Ali DIN (1): базовая схема

Как моделировать пользовательские последовательности в исходном коде Ali DIN (2): перспективы DIN и разработки функций

Перевод документа Ali Deep Interest Network (DIN)

Система рекомендаций соответствует глубокому обучению (24) - принцип и фактическая борьба за развитие сети глубокого интереса DIEN!

Система рекомендаций сочетается с глубоким обучением (18) — Изучение анализа и внедрения Ali’s Deep Interest Network (DIN)

[Введение в статью] Модель прогнозирования Ali CTR 2018 г. --- DIN (Deep Interest Network) с прикрепленным кодом воспроизведения TF2.0

[Введение в статью] Модель прогнозирования Ali CTR 2019 --- DIEN (сеть развития глубокого интереса)

Система рекомендаций --- Deep Interest Network DIN&DIEN

Механизм внимания в глубоком обучении

Преобразуйте подробное объяснение (супер подробное) Внимание - это все, что вам нужно, бумага