[AliDIN] Общая структура кода анализа исходного кода сети с глубоким интересом

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

0x00 сводка

Сеть Deep Interest Network (DIN) была предложена группой точного направленного поиска и базового алгоритма Alimama в июне 2017 года. Его оценка CTR для индустрии электронной коммерции сосредоточена на полном использовании/извлечении информации из исторических данных о поведении пользователей.

Эта статья является третьей в серии, и в ней будет проанализирована общая идея исходного кода DIN. используетсяGitHub.com/Mouna99/Вниз…реализация в.

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

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: Некоторые вспомогательные функции, такие как функция активации игральных костей, расчет оценки внимания и т. д.
  • model.py:Файл модели DIEN
  • train.py: Запись модели, используемая для обучения данных, сохранения модели и тестовых данных.

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

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

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

  • В Deep Interest Network реализованы следующие новшества:

    1. Для разнообразия:Для широкого круга интересов пользователей DIN используетan interest distributionДля представления, то есть использования Пулинга (взвешенной суммы) для моделирования Разнообразия (моделирования разнообразных интересов пользователей).
    2. Для локальной активации:Используйте механизм внимания для реализации локальной активации, динамического изучения вектора внедрения интереса пользователя из исторического поведения пользователя и создания различных абстрактных представлений пользователя для различных рекламных объявлений, чтобы добиться более точного захвата текущего интереса пользователя в условиях определенное измерение данных. Историческое поведение пользователя обрабатывается по-разному. Для разных рекламных объявлений взвешивание исторического поведения пользователя несовместимо. То есть для текущего кандидата Ad делокализованная активация (Local Activate) соответствующая историческая интересная информация. Чем выше историческое поведение с текущим объявлением-кандидатом, тем вышеattention score, который будет доминировать в этом прогнозе.
    3. CTRОсобенности редкие и многомерные, обычно используют L1, L2, Dropout и другие средства для предотвращения переобучения. Поскольку традиционная регуляризация L2 вычисляет все параметры, параметры модели сцены предсказания CTR часто исчисляются сотнями миллионов. DIN предлагает метод регуляризации, который дает разные веса регуляризации функциям с разной частотой в каждой итерации мини-пакета;
    4. Из-за традиционногофункция активации, например, Relu выводит 0, когда вход меньше 0, что приведет к замедлению скорости итерации многих сетевых узлов. Хотя PRelu ускоряет итерацию, его точка разделения по умолчанию равна 0. На самом деле точка разделения должна определяться данными. Поэтому DIN предлагает динамическую адаптивную функцию активации данных Dice.
    5. Обучение модели на крупномасштабных разреженных данных:Когда глубина DNN относительно глубокая (с большим количеством параметров) и входные данные очень редки, ее легко переобучить. DIN предлагаетAdaptive regularizaionЧтобы предотвратить переоснащение, эффект значителен.

0x03 Общий код

Код DIN взят из 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 == 'DIN':
            model = Model_DIN(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE)
        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,是正样本
    # 比如cat_embeddings_var 是(1601, 18),cat_batch_ph 是(?,),则cat_batch_embedded 就是 (?, 18)
    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а такжеitem对应的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, он объединяется с вложениями товаров и категорий. Например[[mid1, mid2] , [mid3, mid4]]и[[cid1, cid2], [cid3, cid4]], соединенный, чтобы получить[[mid1, mid2,cid1, cid2] , [mid3, mid4,cid3, cid4]].

Согласно схеме архитектуры:

Второй шаг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. Например[[[mid1, mid2]]]и[[[cid1, cid2]]], соединенный, чтобы получить[[[mid1, mid2, cid1, cid2]]].

Согласно схеме архитектуры:

Третий шаг — использовать tf.reduce_sum(self.item_his_eb, 1) для суммирования по первому измерению, что уменьшит размерность.

Например[[[mid1, mid2,cid1, cid2] , [mid3, mid4,cid3, cid4]]]получить[[mid1 + mid3, mid2 + mid4, cid1 + cid3, cid2 + cid4]].

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

# 正样本的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)
# 举例如下,item_eb是 (128, 36),item_his_eb是(128, ?, 36),这个是从真实数据读取出来的,比如可能是 (128, 6, 36)。

# 负样本的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

Model_DIN — это модель, реализованная DIN.

class Model_DIN(Model):
    def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False):
        super(Model_DIN, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE,
                                           ATTENTION_SIZE,
                                           use_negsampling)

        # Attention layer
        with tf.name_scope('Attention_layer'):
            attention_output = din_attention(self.item_eb, self.item_his_eb, ATTENTION_SIZE, self.mask)
            att_fea = tf.reduce_sum(attention_output, 1)

        inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, att_fea], -1)
        # Fully connected layer
        self.build_fcn_net(inp, use_dice=True)

Общая идея относительно проста:

  • Attention layer
  • Fully connected layer

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

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

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

这里写图片描述

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

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

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

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

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

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

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

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

5.2 Реализация внимания

В DIN для последовательности поведения пользователя после объединения всех признаков поля каждого элемента для формирования временного внедрения элемента это уже не просто объединение сумм для всех временных вложений элемента в последовательности, а для каждого элемента. Emb вычисляет вес корреляции с элементом emb, то есть с модулем блока активации.

Эта часть функции реализована во внимании:

5.2.1 Вызов

Как позвонить:

attention_output = din_attention(self.item_eb, self.item_his_eb, ATTENTION_SIZE, self.mask)

Среди них соответствующие параметры и т. д.:

  • query : вложение, соответствующее рекламному объявлению-кандидату, форма: [B, H], т.е. i_emb;
  • факты: вложение, соответствующее историческому поведению пользователя, форма: [B, T, H], т.е. h_emb;
  • маска : реальное значение каждого поведения в пакете, форма: [B, H], потому что последовательности поведения пользователя в пакете не обязательно одинаковы, но размерность входных ключей фиксирована (все это максимальная длина исторического поведения). ), его реальная длина хранится в self.sl, поэтому были сгенерированы маски для выбора реального исторического поведения, чтобы сообщить модели, какие поведения бесполезны, а какие используются для расчета распределения интересов пользователей;
  • B: размер пакета, T: максимальная длина исторической последовательности поведения пользователя, H: размер встраивания;
  • внимание_выход : вывод представляет собой представление интереса пользователя;

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

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)
 mask = {Tensor} Tensor("Inputs/mask:0", shape=(?, ?), dtype=float32)

5.2.2 Роль маски

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

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

Padding Mask

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

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

Sequence mask

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

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

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

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

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

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

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

  • Если 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], а размерность axis=2 представляет весовые параметры, соответствующие T последовательностям поведения пользователя;
    • вывод внимания, [B, 1, T];
  • Получить значимый балл;
    • использоватьkey_masks = tf.expand_dims(mask, 1)Увеличьте размер маски с [B, T] до [B, 1, T];
    • Используйте tf.ones_like(scores) для создания тензора с той же размерностью, что и scores, и все элементы равны 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_attention(query, facts, attention_size, mask, stag='null', mode='SUM', softmax_stag=1, time_major=False, return_alphas=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)
        print ("querry_size mismatch")
        query = tf.concat(values = [
        query,
        query,
        ], axis=1)

    if time_major:
        # (T,B,D) => (B,T,D)
        facts = tf.array_ops.transpose(facts, [1, 0, 2])  
      
    # 转换mask  
    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
    
    # 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
    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

Переменные при запуске программы следующие:

attention_size = {int} 36
d_layer_1_all = {Tensor} Tensor("Attention_layer/f1_attnull/Sigmoid:0", shape=(?, ?, 80), dtype=float32)
d_layer_2_all = {Tensor} Tensor("Attention_layer/f2_attnull/Sigmoid:0", shape=(?, ?, 40), dtype=float32)
d_layer_3_all = {Tensor} Tensor("Attention_layer/Reshape_1:0", shape=(?, 1, ?), dtype=float32)
din_all = {Tensor} Tensor("Attention_layer/concat:0", shape=(?, ?, 144), dtype=float32)
facts = {Tensor} Tensor("concat_1:0", shape=(?, ?, 36), dtype=float32)
facts_size = {int} 36
key_masks = {Tensor} Tensor("Attention_layer/ExpandDims:0", shape=(?, 1, ?), dtype=bool)
mask = {Tensor} Tensor("Attention_layer/Equal:0", shape=(?, ?), dtype=bool)
mode = {str} 'SUM'
output = {Tensor} Tensor("Attention_layer/MatMul:0", shape=(?, 1, 36), dtype=float32)
paddings = {Tensor} Tensor("Attention_layer/mul_1:0", shape=(?, 1, ?), dtype=float32)
queries = {Tensor} Tensor("Attention_layer/Reshape:0", shape=(?, ?, 36), dtype=float32)
querry_size = {int} 36
query = {Tensor} Tensor("concat:0", shape=(?, 36), dtype=float32)
return_alphas = {bool} False
scores = {Tensor} Tensor("Attention_layer/Reshape_3:0", shape=(?, 1, ?), dtype=float32)
softmax_stag = {int} 1
stag = {str} 'null'
time_major = {bool} False

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

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

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

# Attention layers
with tf.name_scope('Attention_layer'):
  attention_output = din_attention(self.item_eb, self.item_his_eb, ATTENTION_SIZE, self.mask)
  att_fea = tf.reduce_sum(attention_output, 1)
  tf.summary.histogram('att_fea', att_fea)
  inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, att_fea], -1)
# 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,tf.train.AdamOptimizer(learning_rate=self.lr).minimize(self.loss), так что последующая оптимизация будет производиться через этот минимум;
  • Рассчитать точность;

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

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

0x08 AUC

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

def calc_auc(raw_arr):
    arr = sorted(raw_arr, key=lambda d:d[0], reverse=True)
    pos, neg = 0., 0.
    for record in arr: # 先计算正样本,负样本个数
        if record[1] == 1.:
            pos += 1
        else:
            neg += 1

    fp, tp = 0., 0.
    xy_arr = []
    for record in arr:
        if record[1] == 1.:
            tp += 1
        else:
            fp += 1
        xy_arr.append([fp/neg, tp/pos])

    auc = 0.
    prev_x = 0.
    prev_y = 0.
    # 就是计算auc面积,y + prev_y = prev_y + prev_y + (y - prev_y)
    # y + prev_y 再乘以 delta_x,就是 2 * delta_x * prev_y + 2 * delta_x * prev_y
    # 再除以 2,正好就是梯形面积 
    for x, y in xy_arr: 
        if x != prev_x:
            auc += ((x - prev_x) * (y + prev_y) / 2.)
            prev_x = x
            prev_y = y

    return auc

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 Prediction Trilogy (1): краткий анализ сети Deep Interest для прогнозирования кликабельности

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 (сеть развития глубокого интереса)

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

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