[笔记]推荐系统实战第三课


12月2日,主要是继续上节课
的内容,进一步介绍了airbnb利用word2vec的思想,对适合的业务场景进行建模的思想。其次,课程内容围绕youtube和facebook在推荐领域的应用,介绍了youtube的深度深度模型的match阶段。

请输入图片名称

通过对不同特征进行embeding输入网络后,对于看过的视频做多分类,对最后一个隐层的输出作为用户的隐向量,而对于视频向量文章没有详细说是如何产生的。不过通过这种思路,观察这个结构图,可以发现,其本身与word2vec的结构图“相似性”很高,只不过在输入层部分,可以输入更多embeding的特征。

请输入图片名称

课程后半段,介绍了在线的Match服务的一般实现思路

请输入图片名称

通过给对于日志,可以获取到用户行为,用户偏好,根据用户行为和用户偏好生成不同的商品候选列表,达到在线的match解决方案的基本思路。在这里面,我们可以发现,数据是以一种key-value对的形式组织起来。如何可以发现与value最相似的更多value的集合,这里面引入了KNN(K-Neares-Neighbors search)的基本实现思路。如何更快的得到结果?这个“更快”就是工业界产品要实现的“痛点”。

请输入图片名称

这里面介绍了几种精确搜索策略和近似搜索策略。其中前三种属于精确获取到与当前最相似的候选集合。后面三种便是采取近似的方式。(这个部分需要进一步学习一下)

代码实践,主要是讲解了word2vec的一种实现方案(不涉及霍夫曼树),实现代码分为:

CBOW/SKIP-GRAM两个版本。这两个版本的最大区别是输入和标签的区别。

其中CBOW的输入除目标外的窗口内的其他单词,标签就是目标词。

标签:【目标词】

输入:【词,词,目标词,词,词】 - 【目标词】

SKIP-GRAM的输入是当前的目标词,输出是当前窗口内随机选择N个作为标签。

输入:【目标词】

标签: 随机选择(【词,词,目标词,词,词】 - 【目标词】)

代码:实现两种样本的生成方式
def generate_batch(batch_size, num_skips = 2, window = 2,is_cbow = False):
    global data_index
    # 窗口大小
    span = 2 * window + 1
    buffer = collections.deque(maxlen=span)
    for _ in range(span):
        buffer.append(data[data_index])
        data_index = (data_index + 1) % len(data)

    if is_cbow:
        # cbow continuie bow
        batch = np.ndarray(shape=(batch_size,span-1), dtype=np.int32)
        labels = np.ndarray(shape=(batch_size,1), dtype=np.int32)
        for i in range(batch_size):
            target = window
            targets_to_avoid = [target]
            while target in targets_to_avoid:
                target = random.randint(0, span - 1)
            targets_to_avoid.append(target)
            labels[i, 0] = buffer[window]
            _index_ = 0
            for k in range(span):
                if k == window:
                    pass
                else:
                    batch[i,_index_] = buffer[k]
                    _index_ = _index_ + 1
            buffer.append(data[data_index])
            data_index = (data_index + 1) % len(data)
    else:
        # skip gram
        batch = np.ndarray(shape=(batch_size),dtype=np.int32)
        labels = np.ndarray(shape=(batch_size,1),dtype=np.int32)

        for i in range(batch_size // num_skips):
            target = window  #
            targets_to_avoid = [target]
            # 重复使用一个单词的次数
            for j in range(num_skips):
                # 选择当前词的上下文内容
                while target in targets_to_avoid:
                    target = random.randint(0, span - 1)
                targets_to_avoid.append(target)
                batch[i * num_skips + j] = buffer[window]
                labels[i * num_skips + j, 0] = buffer[target]
            buffer.append(data[data_index])
            data_index = (data_index + 1) % len(data)
    return batch, labels


完整代码:

coding:utf-8

import tensorflow as tf
import zipfile
import collections
import numpy as np
import random
import math

读取数据

def read_data(filename):
    with zipfile.ZipFile(filename) as f:
        data = tf.compat.as_str(f.read(f.namelist()[0])).split()
    return data

words = read_data('./text8.zip')

vocabulary_size = 5000

def build_dataset(words):
    count = [['UNK', -1]]
    #每个词出现的次数
    count.extend(collections.Counter(words).most_common(vocabulary_size - 1))
    dictionary = dict()
    #单词到数字的映射
    for word, _ in count:
        dictionary[word] = len(dictionary)
    data = list()
    unk_count = 0
    for word in words:
        if word in dictionary:
            index = dictionary[word]
        else:
            index = 0
            unk_count = unk_count + 1
        data.append(index)
    count[0][1] = unk_count
    #数字到单词的映射
    reverse_dictionary = dict(zip(dictionary.values(), dictionary.keys()))
    return data, count, dictionary, reverse_dictionary

data, count, dictionary, reverse_dictionary = build_dataset(words)

重点部分样本生成

batch size 频次大小

num skips 重复用一个单词的次数

skip windows 上下文个数

data_index = 0

def generate_batch(batch_size, num_skips = 2, window = 2,is_cbow = False):
    global data_index
    # 窗口大小
    span = 2 * window + 1
    buffer = collections.deque(maxlen=span)
    for _ in range(span):
        buffer.append(data[data_index])
        data_index = (data_index + 1) % len(data)

    if is_cbow:
        # cbow continuie bow
        batch = np.ndarray(shape=(batch_size,span-1), dtype=np.int32)
        labels = np.ndarray(shape=(batch_size,1), dtype=np.int32)
        for i in range(batch_size):
            target = window
            targets_to_avoid = [target]
            while target in targets_to_avoid:
                target = random.randint(0, span - 1)
            targets_to_avoid.append(target)
            labels[i, 0] = buffer[window]
            _index_ = 0
            for k in range(span):
                if k == window:
                    pass
                else:
                    batch[i,_index_] = buffer[k]
                    _index_ = _index_ + 1
            buffer.append(data[data_index])
            data_index = (data_index + 1) % len(data)
    else:
        # skip gram
        batch = np.ndarray(shape=(batch_size),dtype=np.int32)
        labels = np.ndarray(shape=(batch_size,1),dtype=np.int32)

        for i in range(batch_size // num_skips):
            target = window  #
            targets_to_avoid = [target]
            # 重复使用一个单词的次数
            for j in range(num_skips):
                # 选择当前词的上下文内容
                while target in targets_to_avoid:
                    target = random.randint(0, span - 1)
                targets_to_avoid.append(target)
                batch[i * num_skips + j] = buffer[window]
                labels[i * num_skips + j, 0] = buffer[target]
            buffer.append(data[data_index])
            data_index = (data_index + 1) % len(data)
    return batch, labels

batch_size = 128
is_cbow = True
window = 2
num_skips = 2
valid_size = 16
valid_window = 100
embedding_size = 128
num_sampled = 64
valid_examples = np.array(random.sample(range(valid_window), valid_size))

建立网络

with tf.Session() as sess:

    if is_cbow:
        train_input = tf.placeholder(tf.int32,shape=[batch_size,2 * window])
    else:
        train_input = tf.placeholder(tf.int32, shape=[batch_size])

    train_labels = tf.placeholder(tf.int32,shape=[batch_size,1])
    valid_dataset = tf.constant(valid_examples, dtype=tf.int32)

    embeddings = tf.Variable(tf.random_normal([vocabulary_size,embedding_size],-1,1))

    softmax_weights = tf.Variable(tf.truncated_normal([vocabulary_size, embedding_size],stddev=1.0 / math.sqrt(embedding_size)))
    softmax_biases = tf.Variable(tf.zeros([vocabulary_size]))

    if is_cbow:
        embeds = None
        embed_i = None
        for i in range(2 * window):
            embed_i = tf.nn.embedding_lookup(embeddings, train_input[:,i])
            emb_x, emb_y = embed_i.get_shape().as_list()
            if embeds is None:
                embeds = tf.reshape(embed_i, [emb_x, emb_y, 1])
            else:
                embeds = tf.concat([embeds, tf.reshape(embed_i, [emb_x, emb_y, 1])], 2)
        avg_embed = tf.reduce_mean(embeds, 2, keep_dims=False)
    else:
        avg_embed = tf.nn.embedding_lookup(embeddings, train_input)

    loss = tf.reduce_mean(
        tf.nn.sampled_softmax_loss(weights=softmax_weights, biases=softmax_biases, inputs=avg_embed,
                                   labels=train_labels, num_sampled=num_sampled, num_classes=vocabulary_size))

    optimizer = tf.train.AdagradOptimizer(1.0).minimize(loss)

    # 归一化
    norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))
    normalized_embeddings = embeddings / norm
    # 用已有embedding计算valid的相似次
    valid_embeddings = tf.nn.embedding_lookup(normalized_embeddings, valid_dataset)

    similarity = tf.matmul(valid_embeddings, normalized_embeddings, transpose_b=True)

    tf.global_variables_initializer().run()

    #
    average_loss = 0
    for step in range(10000 + 1):
        if is_cbow:
            batch_data, batch_labels = generate_batch(batch_size=batch_size, window=window, is_cbow=is_cbow)
        else:
            batch_data, batch_labels = generate_batch(batch_size=batch_size, num_skips=num_skips, window=window,is_cbow=is_cbow)

        feed_dict = {train_input: batch_data, train_labels: batch_labels}
        _, l = sess.run([optimizer, loss], feed_dict=feed_dict)
        average_loss += l
        # 2000次打印loss
        if step % 2000 == 0:
            if step > 0:
                average_loss = average_loss / 2000
            print('Average loss at step %d: %f' % (step, average_loss))
            average_loss = 0
        # 打印valid效果
        if step % 2000 == 0:
            sim = similarity.eval()
            for i in range(valid_size):
                valid_word = reverse_dictionary[valid_examples[i]]
                top_k = 5  # 相似度最高的5个词
                nearest = (-sim[i, :]).argsort()[1:top_k + 1]
                log = 'Nearest to %s:' % valid_word
                for k in range(top_k):
                    close_word = reverse_dictionary[nearest[k]]
                    log = '%s %s,' % (log, close_word)
                print(log)
    final_embeddings = normalized_embeddings.eval()
已邀请:

要回复问题请先登录注册

返回顶部