音乐推荐系统(二)

  • Post author:
  • Post category:其他




歌曲序列建模



NLP场景

我是中国人 => 我 是 中国 人

我 => [1,0,0,0] 1500

是 => [0,1,0,0] 1500



1*4 vector one-hot encoding

word => vector

distance similarity



从word2vec到song2vec

我们把歌曲的id序列取出来,类比于分完词后的句子,送到word2vec中去学习一下

#coding: utf-8
import multiprocessing
import gensim
import sys
from random import shuffle

def parse_playlist_get_sequence(in_line, playlist_sequence):
	song_sequence = []
	contents = in_line.strip().split("\t")
	# 解析歌单序列
	for song in contents[1:]:
		try:
			song_id, song_name, artist, popularity = song.split(":::")
			song_sequence.append(song_id)
		except:
			print "song format error"
			print song+"\n"
	for i in range(len(song_sequence)):
		shuffle(song_sequence)
		playlist_sequence.append(song_sequence)


def train_song2vec(in_file, out_file):
	#所有歌单序列
	playlist_sequence = []
	#遍历所有歌单
	for line in open(in_file):
		parse_playlist_get_sequence(line, playlist_sequence)
	#使用word2vec训练
	cores = multiprocessing.cpu_count()
	print "using all "+str(cores)+" cores"
	print "Training word2vec model..."
	model = gensim.models.Word2Vec(sentences=playlist_sequence, size=150, min_count=3, window=7, workers=cores)
	print "Saving model..."
	model.save(out_file)
song_sequence_file = "./popular.playlist"
model_file = "./song2vec.model"
train_song2vec(song_sequence_file, model_file)



进一步思考

所以我们用word2vec学会了哪些歌曲和哪些歌曲最接近。

我们来思考一些很现实同时又很难解决的问题。比如:



冷启动问题

我们经常会遇到冷启动的问题,比如没有任何信息的歌曲,我们如何对它做推荐呢?

如果是歌手发行的新歌曲,我们怎么进行推荐呢?

如果我听完(并收藏)了一首很冷门的歌,怎么进行推荐呢?

我们知道新歌(或者小众的歌)是非常难和其他的歌关联上的,我们有的信息太少了(很少有用户在它上面发生行为)。

1.1 一种解决办法当然是推荐热门的歌曲,但是其实没从个人兴趣出发,我们知道这并不是最好的办法,并没有太大的卵用。

1.2 我们把问题的

粒度放粗

一点,用同样的思路,比如一个可考虑的解决方案是,我们把歌曲的粒度上升到对应的歌手,把刚才的

song_list替换成artist_list

,重新用word2vec建模,这样我们可以得到和一个歌手最相关(接近)的歌手,再推荐这个歌手最热门的歌曲,相对1.1的方法针对性强一些。

商品 => 品类

品类list => 送到word2vec里面去学习

[上衣,上衣,上衣,牛仔裤,牛仔裤,连衣裙…]



用户兴趣预测问题

我们刚才完成的功能,类似酷狗音乐和网易音乐里针对一首歌的“相似音乐”,那么问题又来了,如果我们现在要对一个user用这套song2vec的方式推荐,我们怎么做呢?

每个人的兴趣都是有时效性的,这意味着说,3年前我喜欢王菲的歌,去年我喜欢五月天的歌,而今年我可能就改摇滚路线,喜欢汪峰的歌了。

每一首歌的热度也是不一样的,有一些热门的歌,如果用户能喜欢,当然是首选

那么,我们来做一个粗暴一点点的处理,把这2个维度拉进来,一起来针对一个用户做推荐。

**把每个用户喜欢(收藏)过的歌,沿着时间轴排好,同时由近到远给不同的衰减因子(比如最近一首歌是1,前一首是0.98,再前一首是0.98^2,以此类推…),同时我们针对不同的歌曲热度,给定不同的推荐因子(比如热度100的是1,热度80的是0.9…),每一首歌都可以拿回一个song2vec的推荐列表和对应的相似度,对相似度以时间衰减因子和热度权重进行加权,最后的结果排序后,展示给用户。



矩阵分解

在这里插入图片描述

LFM:把用户在item上打分的行为,看作是有内部依据的,认为和k个factor有关系

每一个user i会有一个用户的向量(k维),每一个item会有一个item的向量(k维)

SVD是矩阵分解的一种方式

在这里插入图片描述



推荐系统学习(TensorFlow)



获取数据

以movielens为例,数据格式为user item rating timestamp



数据处理部分

tensorflow搭建的模型,训练方式通常是一个batch一个batch训练的

from __future__ import absolute_import, division, print_function
import numpy as np
import pandas as pd


def read_data_and_process(filname, sep="\t"):
    col_names = ["user", "item", "rate", "st"]
    df = pd.read_csv(filname, sep=sep, header=None, names=col_names, engine='python')
    df["user"] -= 1
    df["item"] -= 1
    for col in ("user", "item"):
        df[col] = df[col].astype(np.int32)
    df["rate"] = df["rate"].astype(np.float32)
    return df


class ShuffleDataIterator(object):
    """
    随机生成一个batch一个batch数据
    """
    #初始化
    def __init__(self, inputs, batch_size=10):
        self.inputs = inputs
        self.batch_size = batch_size
        self.num_cols = len(self.inputs)
        self.len = len(self.inputs[0])
        self.inputs = np.transpose(np.vstack([np.array(self.inputs[i]) for i in range(self.num_cols)]))

    #总样本量
    def __len__(self):
        return self.len

    def __iter__(self):
        return self

    #取出下一个batch
    def __next__(self):
        return self.next()
    
    #随机生成batch_size个下标,取出对应的样本
    def next(self):
        ids = np.random.randint(0, self.len, (self.batch_size,))
        out = self.inputs[ids, :]
        return [out[:, i] for i in range(self.num_cols)]


class OneEpochDataIterator(ShuffleDataIterator):
    """
    顺序产出一个epoch的数据,在测试中可能会用到
    """
    def __init__(self, inputs, batch_size=10):
        super(OneEpochDataIterator, self).__init__(inputs, batch_size=batch_size)
        if batch_size > 0:
            self.idx_group = np.array_split(np.arange(self.len), np.ceil(self.len / batch_size))
        else:
            self.idx_group = [np.arange(self.len)]
        self.group_id = 0

    def next(self):
        if self.group_id >= len(self.idx_group):
            self.group_id = 0
            raise StopIteration
        out = self.inputs[self.idx_group[self.group_id], :]
        self.group_id += 1
        return [out[:, i] for i in range(self.num_cols)]



模型搭建

在这里插入图片描述

import tensorflow as tf

# 使用矩阵分解搭建的网络结构
def inference_svd(user_batch, item_batch, user_num, item_num, dim=5, device="/cpu:0"):
    #使用CPU
    with tf.device("/cpu:0"):
        # 初始化几个bias项
        global_bias = tf.get_variable("global_bias", shape=[])
        w_bias_user = tf.get_variable("embd_bias_user", shape=[user_num])
        w_bias_item = tf.get_variable("embd_bias_item", shape=[item_num])
        # bias向量
        bias_user = tf.nn.embedding_lookup(w_bias_user, user_batch, name="bias_user")
        bias_item = tf.nn.embedding_lookup(w_bias_item, item_batch, name="bias_item")
        w_user = tf.get_variable("embd_user", shape=[user_num, dim],
                                 initializer=tf.truncated_normal_initializer(stddev=0.02))
        w_item = tf.get_variable("embd_item", shape=[item_num, dim],
                                 initializer=tf.truncated_normal_initializer(stddev=0.02))
        # user向量与item向量
        embd_user = tf.nn.embedding_lookup(w_user, user_batch, name<



版权声明:本文为m0_49180275原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。