音乐频道推荐业务,支持各个产品业务和策略。这里我先使用CB+CF+LR实现推荐部分,下面具体展开:
一、推荐系统流程图
CB,CF算法在召回阶段使用,推荐出来的item是粗排的,利用LR算法,可以将CB,CF召回来的item进行精排,然后选择分数最高,给用户推荐出来。后续我们可以采用矩阵分解、聚类、深度学习算法来实现对候选集合的召回。
二、推荐系统思路详解
话不多说,这里先放上代码思路:
1、数据预处理(用户画像数据、物品元数据、用户行为数据)
2、召回(CB、CF算法)
3、LR训练模型的数据准备,即用户特征数据,物品特征数据
4、模型准备,即通过LR算法训练模型数据得到w,b
5、推荐系统流程:
(1)解析请求:userid,itemid
(2)加载模型:加载排序模型(model.w,model.b)
(3)检索候选集合:利用cb,cf去redis里面检索数据库,得到候选集合
(4)获取用户特征:userid
(5)获取物品特征:itemid
(6)打分(逻辑回归,深度学习),排序
(7)top-n过滤
(8)数据包装(itemid->name),返回
三、推荐系统实现
3.1、数据预处理
(1)用户画像数据:user_profile.data
userid,性别,年龄,收入,地域
(2)物品(音乐)元数据:music_metaitemid,name,desc,时长,地域,标签
(3)用户行为数据:user_watch_pref.sml
userid,itemid,该用户对该物品的收听时长,点击时间(小时)
首先,将3份数据融合到一份数据中
执行python gen_base.py
1 #coding=utf-8
2
3 '''
4 总体思路:处理原始的数据:1、用户画像数据 2、物品元数据 3、用户行为数据5 把三类数据统一到一个文件里面,供后面cb、cf算法进行计算权重6 '''
7
8 importsys9
10 #找到三类原始数据文件,用户画像数据、物品元数据,用户行为数据
11 user_action_data = '../data/user_watch_pref.sml'
12 music_meta_data = '../data/music_meta'
13 user_profile_data = '../data/user_profile.data'
14
15 #将三类处理后的元数据放到新的文件里面,这里我们需要定一个文件名,路径
16 output_file = '../data/merge_base.data'
17
18 #将3份数据merge后的结果输出,供下游数据处理
19 ofile = open(output_file, 'w')20
21 #step 1. 处理物品元数据,将处理后的结果放入字典里面,key是itemid,value为物品对应的信息,为最后写入做准备
22 item_info_dict ={}23 with open(music_meta_data, 'r') as fd:24 for line infd:25 ss = line.strip().split('\001')26 if len(ss) != 6:27 continue
28 itemid, name, desc, total_timelen, location, tags =ss29 item_info_dict[itemid] = '\001'.join([name, desc, total_timelen, location, tags])30
31 #step 2. 处理用户画像数据,将处理后的结果放入字典里面,key是用户id,value是用户信息
32 user_profile_dict ={}33 with open(user_profile_data, 'r') as fd:34 for line infd:35 ss = line.strip().split(',')36 if len(ss) != 5:37 continue
38 userid, gender, age, salary, location =ss39 user_profile_dict[userid] = '\001'.join([gender, age, salary, location])40
41 #step 3. 写入最后的信息,将用户行为数据进行处理,把step1和step2得到的数据一并归纳在文件里面
42 with open(user_action_data, 'r') as fd:43 for line infd:44 ss = line.strip().split('\001')45 if len(ss) != 4:46 continue
47 userid, itemid, watch_len, hour =ss48
49 if userid not inuser_profile_dict:50 continue
51
52 if itemid not initem_info_dict:53 continue
54
55 ofile.write('\001'.join([userid, itemid, watch_len, hour, \56 user_profile_dict[userid], item_info_dict[itemid]]))57 ofile.write("\n")58
59 ofile.close()
得到类似下面数据merge_base.data
01e3fdf415107cd6046a07481fbed499^A6470209102^A1635^A21^A男^A36-45^A20000-100000^A内蒙古^A黄家驹1993演唱会高清视频^A^A1969^A^A演唱会
3.2、【召回】CB算法
(1)以token itemid score形式整理训练数据利用jieba分词,对item name进行中文分词
python gen_cb_train.py
1 #coding=utf-8
2
3 '''
4 总体思路:将初始化好的用户,物品,用户行为数据进行处理,目的是为了得到token,itemid,score,我们知道生成的数据里面的name,5 将itemName进行分词,得到tfidf权重,同时将desc进行分词,处理name和desc,我们在元数据中还有已经分类好的tags,tags已经切分好6 了没必要再次进行切分,只需要用idf词表查处权重即可,但是对于name、desc、tags这三个分词结果,我们对name的结果应该更加偏重一7 点,所以分别对这三类得出的分数再次进行分数权重划分,最后得到cb的初始数据8 '''
9
10 importsys11 sys.path.append('../')12 reload(sys)13 sys.setdefaultencoding('utf-8')14
15 importjieba16 importjieba.posseg17 importjieba.analyse18
19
20 #读入初始数据
21 input_file = "../data/merge_base.data"
22
23 #输出cb训练数据
24 output_file = '../data/cb_train.data'
25 ofile = open(output_file, 'w')26
27 #定义三类的权重分数
28 RATIO_FOR_NAME = 0.9
29 RATIO_FOR_DESC = 0.1
30 RATIO_FOR_TAGS = 0.05
31
32
33 #为tags读入idf权重值
34 idf_file = '../data/idf.txt'
35 idf_dict ={}36 with open(idf_file, 'r') as fd:37 for line infd:38 token, idf_score = line.strip().split(' ')39 idf_dict[token] =idf_score40
41 #开始处理初始数据
42 itemid_set =set()43 with open(input_file, 'r') as fd:44 for line infd:45 ss = line.strip().split('\001')46 #用户行为
47 userid =ss[0].strip()48 itemid = ss[1].strip()49 watch_len = ss[2].strip()50 hour = ss[3].strip()51 #用户画像
52 gender = ss[4].strip()53 age = ss[5].strip()54 salary = ss[6].strip()55 user_location = ss[7].strip()56 #物品元数据
57 name = ss[8].strip()58 desc = ss[9].strip()59 total_timelen = ss[10].strip()60 item_location = ss[11].strip()61 tags = ss[12].strip()62
63 #对item去重,相同的itemid不用再计算,因为都一样,这里用到continue特性,当不同的时候才继续执行下面的代码
64 if itemid not initemid_set:65 itemid_set.add(itemid)66 else:67 continue
68
69 #去掉重复后的itemid,然后我们进行分词,计算权重,放到字典里面
70 token_dict ={}71 #对name统计
72 for a in jieba.analyse.extract_tags(name, withWeight=True):73 token =a[0]74 score = float(a[1])75 token_dict[token] = score *RATIO_FOR_NAME76
77 #对desc进行分词,这里需要注意的是描述一般会含有name中的词,这里我们把有的词的分数进行相加,没有的放入
78 for a in jieba.analyse.extract_tags(desc, withWeight=True):79 token =a[0]80 score = float(a[1])81 if token intoken_dict:82 token_dict[token] += score *RATIO_FOR_DESC83 else:84 token_dict[token] = score *RATIO_FOR_DESC85
86 #对tags 进行分数计算
87 for tag in tags.strip().split(','):88 if tag not inidf_dict:89 continue
90 else:91 if tag intoken_dict:92 token_dict[tag] += float(idf_dict[tag]) *RATIO_FOR_TAGS93 else:94 token_dict[tag] = float(idf_dict[tag]) *RATIO_FOR_TAGS95
96 #循环遍历token_dict,输出toke,itemid,score
97 for k, v intoken_dict.items():98 token =k.strip()99 score =str(v)100 ofile.write(','.join([token, itemid, score]))101 ofile.write("\n")102
103
104 ofile.close()
得到如下数据:
翻译,4090309101,0.561911164569(最后一个是一个不是传统的TF-IDF,因为分出的词在name,desc,tag里面他的重要性是不一样的)
(2)用协同过滤算法跑出item-item数据
相似的item配对,II矩阵的形成。相似度计算,我们要用到MapReduce的框架来进行,只要是用到shuffle阶段,对map出来的结果排序,reduce进行两两配对,这里就是主要的wordcount逻辑,主要说下注意