python音乐推荐系统_音乐推荐系统

音乐频道推荐业务,支持各个产品业务和策略。这里我先使用CB+CF+LR实现推荐部分,下面具体展开:

一、推荐系统流程图

python音乐推荐系统_音乐推荐系统_第1张图片

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,性别,年龄,收入,地域

python音乐推荐系统_音乐推荐系统_第2张图片

(2)物品(音乐)元数据:music_metaitemid,name,desc,时长,地域,标签

python音乐推荐系统_音乐推荐系统_第3张图片

(3)用户行为数据:user_watch_pref.sml

userid,itemid,该用户对该物品的收听时长,点击时间(小时)

python音乐推荐系统_音乐推荐系统_第4张图片

首先,将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

python音乐推荐系统_音乐推荐系统_第5张图片

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()

得到如下数据:

python音乐推荐系统_音乐推荐系统_第6张图片

翻译,4090309101,0.561911164569(最后一个是一个不是传统的TF-IDF,因为分出的词在name,desc,tag里面他的重要性是不一样的)

(2)用协同过滤算法跑出item-item数据

相似的item配对,II矩阵的形成。相似度计算,我们要用到MapReduce的框架来进行,只要是用到shuffle阶段,对map出来的结果排序,reduce进行两两配对,这里就是主要的wordcount逻辑,主要说下注意的部分:我们需要把两两分数的过滤掉,或是把itemA和itemB相同的item过滤掉,因为这部分数据没有任何意义

map阶段:

#!usr/bin/python#-*- coding: UTF-8 -*-

'''总体思路:这里需要把初始化后的结果进行map排序,为了后续两两取 pair对,所以这里我们需要进行map,其实什么也不用操作输出即可'''

importsysimportrefor line insys.stdin:

ss= line.strip().split(',')if len(ss) != 3:continuer1= u'[a-zA-Z0-9’!"#$%&\'()*+,-./:;<=>?@,。?★、…【】《》?“”‘’![\\]^_`{|}~]+'ss[0]= re.sub(r1,'',ss[0])if len(ss[0]) ==0:continue

print ','.join([ss[0], ss[1], ss[2]])

reduce阶段:

#!usr/bin/python#-*- coding: UTF-8 -*

'''我们前面已经在pair reduce之前我们做过map操作,输出以token,item,score输出,所以排序是token排好的序

这里我们相当于求的是II矩阵,所以是根相同的token的item进行相似度计算

思路:

1、进行user统计,若相同,把相同的user的item和score放入list里面

2、不相同,开始进行两两配对,循环该list,进行两两配对,求出相似度'''

importsysimportmath

cur_token=None

item_score_list=[]for line insys.stdin:

ss= line.strip().split(',')

itemid= ss[1]

score= float(ss[2])if len(ss) != 3:continue

if cur_token ==None:

cur_token=ss[0]if cur_token !=ss[0]:#这里需要注意的是range的区间前闭后开,同时注意range中即使前闭后开,刚开始是从0即列表里面的第一个,循环到列表最后一个的前一个

for i in range(0,len(item_score_list)-1):for j in range(i+1,len(item_score_list)):

item_a,score_a=item_score_list[i]

item_b,score_b=item_score_list[j]#score = float(score_a * score_b)/float(math.sqrt(pow(score_a,2))*math.sqrt(pow(score_b,2)))

#输出两遍的目的是为了形成II矩阵的对称

score = float(score_a*score_b)if item_a ==item_b:continue

if score < 0.08:continue

print "%s\t%s\t%s" %(item_a, item_b, score)print "%s\t%s\t%s" %(item_b, item_a, score)

cur_token=ss[0]

item_score_list=[]

item_score_list.append((itemid,float(score)))for i in range(0, len(item_score_list) - 1):for j in range(i + 1, len(item_score_list)):

item_a, score_a=item_score_list[i]

item_b, score_b=item_score_list[j]#score = (score_a * score_b) / (math.sqrt(pow(score_a, 2)) * math.sqrt(pow(score_b, 2))

#输出两遍的目的是为了形成II矩阵的对称

score = float(score_a *score_b)if item_a ==item_b:continue

if score < 0.08:continue

print "%s\t%s\t%s" %(item_a, item_b, score)print "%s\t%s\t%s" % (item_b, item_a, score)

最后得到基于cb的ii矩阵

python音乐推荐系统_音乐推荐系统_第7张图片

python音乐推荐系统_音乐推荐系统_第8张图片

(3)对数据格式化,item-> item list形式,整理出KV形式python gen_reclist.py

1 #coding=utf-8

2 '''

3 思路:我们已经通过CB算法得到itemA,itemB,score,然后我们需要把放入到redis库,存入的方法,4 我们以itemA为key与itemA有相似度的itemB,和分数,以value的形式存入内存库5 1、创建一个字典,将key放入itemA,value 放入与A对应的不同b和分数6 2、循环遍历字典,将key加上前缀CB,value以从大到小的分数进行排序,并且相同的item以——分割,item和score间用:分割7 '''

8

9 importsys10

11 infile = '../data/cb.result'

12 outfile = '../data/cb_reclist.redis'

13

14 ofile = open(outfile, 'w')15

16 MAX_RECLIST_SIZE = 100

17 PREFIX = 'CB_'

18

19 rec_dict ={}20 with open(infile, 'r') as fd:21 for line infd:22 itemid_A, itemid_B, sim_score = line.strip().split('\t')23

24 #判断itemA在不在该字典里面,若不在,创建一个key为itemA的列表,把与itemA相关联的itemB和score添加进去

25 if itemid_A not inrec_dict:26 rec_dict[itemid_A] =[]27 rec_dict[itemid_A].append((itemid_B, sim_score))28

29 #循环遍历字典,格式化数据,把itemB和score中间以:分割,不同的itemB以_分割

30 for k, v inrec_dict.items():31 key_item = PREFIX +k32

33 #接下来格式化数据,将数据以从大到小排列后再格式化

34 #排序,由于数据量大,我们只取100个

35 #排好序后,我们来格式化数据

36 reclist_result = '_'.join([':'.join([tu[0], str(round(float(tu[1]), 6))]) \37 for tu in sorted(v, key=lambda x: x[1], reverse=True)[:MAX_RECLIST_SIZE]])38

39 ofile.write(' '.join(['SET', key_item, reclist_result]))40 ofile.write("\n")41

42 ofile.close()

类似如下数据:

python音乐推荐系统_音乐推荐系统_第9张图片

SET CB_5305109176 726100303:0.393048_953500302:0.393048_6193109237:0.348855

(4)灌库(redis)

下载redis-2.8.3.tar.gz安装包

进行源码编译(需要C编译yum install

gcc-c++ ),执行make,然后会在src目录中,得到bin文件(redis-server

服务器,redis-cli 客户端)

启动redis

server服务两种方法:

]# ./src/redis-server

]#后台方式启动 nohup

./redis-server &

python音乐推荐系统_音乐推荐系统_第10张图片

然后换一个终端执行:]# ./src/redis-cli,连接服务

接下来灌数据(批量灌):

需要安装unix2dos(yum install unix2dos)(格式转换)

]# cat cb_reclist.redis |

/usr/local/src/redis-2.8.3/src/redis-cli --pipe 这样是会报大量异常,所以需要用下面的方式去做,完了再使用管道插入(注意redis安装目录)

unix2dos cb_reclist.redis

5e1b7aaa60e97459c99554bf21a12961.png

cat cb_reclist.redis | /usr/local/src/redis/redis-2.8.3/src/redis-cli --pipe

6d31d0d45254a5023edbc2fe5dfd4820.png

验证:]# ./src/redis-cli

执行:127.0.0.1:6379> get CB_5305109176

"726100303:0.393048_953500302:0.393048_6193109237:0.348855"

374d387fcf49d82246115e17548c0356.png

3.3、【召回】CF算法

(1)以userid itemid score形式整理训练数据

python gen_cf_train.py

1 #coding=utf-8

2 '''

3 总体思路:首先和cb一样,对处理完的用户元数据,物品元数据,行为数据进行cf数据准备工作,我们的目的事输出:4 user,item score,其中主要是的到用户对item的score,这里score怎么算呢,当然是用户收听的音乐的时常和总的时5 长相除的到6 '''

7

8 importsys9

10 input_file = "../data/merge_base.data"

11

12 #输出cf训练数据

13 output_file = '../data/cf_train.data'

14 ofile = open(output_file, 'w')15

16 key_dict ={}17 with open(input_file, 'r') as fd:18 for line infd:19 ss = line.strip().split('\001')20 #用户行为

21 userid =ss[0].strip()22 itemid = ss[1].strip()23 watch_len = ss[2].strip()24 hour = ss[3].strip()25 #用户画像

26 gender = ss[4].strip()27 age = ss[5].strip()28 salary = ss[6].strip()29 user_location = ss[7].strip()30 #物品元数据

31 name = ss[8].strip()32 desc = ss[9].strip()33 total_timelen = ss[10].strip()34 item_location = ss[11].strip()35 tags = ss[12].strip()36 #拼接key,为了将同一个用户对相同物品的时长全部得到,需要做个聚合

37 key = '_'.join([userid, itemid])38 if key not inkey_dict:39 key_dict[key] =[]40 key_dict[key].append((int(watch_len), int(total_timelen)))41

42 #循环处理相同用户对相同item的分数

43 for k, v inkey_dict.items():44 t_finished =045 t_all =046 #对为key进行分数聚合

47 for vv inv:48 t_finished +=vv[0]49 t_all += vv[1]50

51 #得到userid对item的最终分数

52 score = float(t_finished) /float(t_all)53 userid, itemid = k.strip().split('_')54

55

56 ofile.write(','.join([userid, itemid, str(score)]))57 ofile.write("\n")58

59 ofile.close()

得到如下数据:python音乐推荐系统_音乐推荐系统_第11张图片

(2)用协同过滤算法跑出item-item数据

II矩阵数据准备,归一化,取pair对,计算总和

这里我们准备redis数据分为这么几个部分,我们来一一解析一下,当然这部分的数据需要利用到MapReduce框架,进行map和reduce排序。

归一化

归一化阶段我们主要是将相同的item进行单位模计算,因为后续我们要用到cos相似度计算公式,将相同的item的分数进行平方和再开根号,最后进行单位化。

map阶段,只要将转数据换成item,user,score ,因为我们要在reduce阶段进行相同item单位化,要充分用到shuffle阶段的排序。

1 #!usr/bin/python

2 #-*- coding: UTF-8 -*-

3 '''

4 思路:转换成i,u,s的矩阵5 '''

6 importsys7

8 for line insys.stdin:9 ss = line.strip().split(',')10 if len(ss) != 3:11 continue

12 u , i , s =ss13 print '\t'.join([i,u,s])

reduce阶段,我们需要将相同item平方和相加开根号,然后再单位化计算,最后输出。

1 #!usr/bin/python

2 #-*- coding: UTF-8 -*-

3 '''

4 在map的基础上将每个item进行归一化,map已经将相同的item排好序,这里我们根据map的结果进行给先平方再开根号:5 思路 :6 1、截取字符串,取出item,user,socre7 2、在for循环中进行判断,当前的item和下一个是否相同,要是相同,将相同的放到列表(user,score)列表里面,否则往下执行8 3、若不相同,循环user和score列表,计算模计算,然后再次循环,进行单位化计算9 '''

10

11 importsys12 importmath13

14 cur_item =None15 user_score_list =[]16 for line insys.stdin:17 ss = line.strip().split('\t')18 if len(ss) != 3:19 continue

20

21 item =ss[0]22 userid = ss[1]23 score = ss[2]24

25 #wordcount判断,当前和下一个是否相同,相同添加到列表,不相同进行归一化计算

26 if cur_item ==None:27 cur_item =item28 if cur_item !=item:29 #定义sum

30 sum = 0.0

31 #循环列表进行模向量计算

32 for ss inuser_score_list:33 user,s =ss34 sum += pow(s,2)35 sum =math.sqrt(sum)36

37 #单位化计算

38 for touple inuser_score_list:39 u,s =touple40 #进行单位化完成后,我们输出重置成原来的user-item-score输出

41 print "%s\t%s\t%s" % (u, cur_item, float(s /sum))42

43 #初始化这两个变量

44 cur_item =item45 user_score_list =[]46

47 user_score_list.append((userid,float(score)))48

49 #定义sum

50 sum = 0.0

51 #循环列表进行模向量计算

52 for ss inuser_score_list:53 user,s =ss54 sum += pow(s,2)55 sum =math.sqrt(sum)56 #单位化计算

57 for touple inuser_score_list:58 u,s =touple59 #进行单位化完成后,我们输出重置成原来的user-item-score输出

60 print "%s\t%s\t%s" % (u, cur_item, float(s / sum))

两两取pair对

两两取pair对,我们在map阶段,其实什么都不用做,保证输出user,itemid,score即可。

map阶段

1 #!usr/bin/python

2 #-*- coding: UTF-8 -*-

3

4 #在进行pair取对之前,什么都不需要做,输出就行

5

6 importsys7

8 for line insys.stdin:9 u, i, s = line.strip().split('\t')10 print "%s\t%s\t%s" % (u, i, s)

reduce阶段,我们需要将同一个用户下面的item进行两两取对,因为我们要形成II矩阵,就必须以user为参考单位,相反形成uu矩阵,就必须以item参考,所以将同一个用户下的item进行两两取对,并将分数相乘,就得到临时这个相似度,因为还没有对相同pair对的分数相加,这个是最后一步要做的。

1 #!usr/bin/python

2 #-*- coding: UTF-8 -*-

3

4 '''

5 思路:进行map排好序之后,我们的会得到相同user对应的不同item和score,这里我们主要的思路是进行相同用户两两取pair6 1、进行判断,当前用户和下一个用户是不是一样,若是不一样,我们进行两两取对,形成ii矩阵7 2、若是相同,我们将不同的item和score放入list里面8 '''

9

10 importsys11

12 cur_user =None13 item_score_list =[]14 for line insys.stdin:15 user,item,score = line.strip().split('\t')16

17 if cur_user ==None:18 cur_user=user19

20 if cur_user !=user:21

22 #进行两两pair,利用range函数

23 for i in range(0,len(item_score_list)-1):24 for j in range(i+1,len(item_score_list)):25 item_a, score_a =item_score_list[i]26 item_b, score_b =item_score_list[j]27 #输出两遍的目的是为了形成II矩阵的对称

28 print "%s\t%s\t%s" % (item_a, item_b, score_a *score_b)29 print "%s\t%s\t%s" % (item_b, item_a, score_a *score_b)30

31 cur_user =user32 item_score_list =[]33

34 item_score_list.append((item,float(score)))35

36 #进行两两pair,利用range函数

37 for i in range(0,len(item_score_list)-1):38 for j in range(i+1,len(item_score_list)):39 item_a, score_a =item_score_list[i]40 item_b, score_b =item_score_list[j]41 #输出两遍的目的是为了形成II矩阵的对称

42 print "%s\t%s\t%s" % (item_a, item_b, score_a *score_b)43 print "%s\t%s\t%s" % (item_b, item_a, score_a * score_b)

进行最终分数求和

我们最后的阶段是要将相同pair的分数相加才能得到两个item的相似度

map阶段,这里我们因为要将相同item对排序到一起,就要将pair组成一个key进行排序,将同一个partition后数据放倒一个reduce桶中,再说一下MapReduce框架中国年shuffle阶段,key只是做排序,partition只是做分区,不要搞混了。

1 #!usr/bin/python

2 #-*- coding: UTF-8 -*-

3

4 '''

5 sum的map中,我们需要把相同的itemA,itemB组成key,为了使相同的key能够在shuffle阶段分配到同一个reduce中,6 因为是计算item的相似度,要把相同的相加7 '''

8

9 importsys10

11 for line insys.stdin:12 item_a,item_b,score = line.strip().split('\t')13 key = '#'.join([item_a,item_b])14 print '%s\t%s' %(key,score)

reduce阶段主要任务就是将相同的item的pair对相加.

#!usr/bin/python#-*- coding: UTF-8 -*-

'''思路:将相同的item的分数进行相加,得到最后的相似度'''

importsys

cur_item=None

score= 0.0

for line insys.stdin:

item, s= line.strip().split('\t')if notcur_item:

cur_item=itemif cur_item !=item:

ss= item.split("#")if len(ss) != 2:continueitem_a, item_b=ssprint "%s\t%s\t%s" %(item_a, item_b, score)

cur_item=item

score= 0.0score+=float(s)

ss= item.split("#")if len(ss) != 2:

sys.exit()

item_a, item_b=ssprint "%s\t%s\t%s" % (item_a, item_b, score)

最后得到基于cf的ii矩阵

python音乐推荐系统_音乐推荐系统_第12张图片

python音乐推荐系统_音乐推荐系统_第13张图片

(3)对数据格式化,item-> item list形式,整理出KV形式

python gen_reclist.py

1 #coding=utf-8

2 '''

3 思路:这个处理的逻辑和CB中完全一样,不一样的是redis的key是CF开头4 '''

5

6 importsys7

8 infile = '../data/cf.result'

9 outfile = '../data/cf_reclist.redis'

10

11 ofile = open(outfile, 'w')12

13 MAX_RECLIST_SIZE = 100

14 PREFIX = 'CF_'

15

16 rec_dict ={}17 with open(input_file,'r') as fd:18 for line infd:19 itemid_A, itemid_B, score = line.strip().split('\t')20

21 #判断itemA在不在该字典里面,若不在,创建一个key为itemA的列表,把与itemA相关联的itemB和score添加进去

22 if itemid_A not inrec_dict:23 rec_dict[itemid_A] =[]24 rec_dict[itemid_A].append((itemid_B, score))25

26 #循环遍历字典,格式化数据,把itemB和score中间以:分割,不同的itemB以_分割

27 for k,v inrec_dict.items():28 key = PREFIX+k29 #接下来格式化数据,将数据以从大到小排列后再格式化

30 #排序,由于数据量大,我们只取100个

31 list = sorted(v,key=lambda x:x[1],reverse=True)[:MAX_RECLIST_SIZE]32 #拍好序后,我们来格式化数据

33 result = '_'.join([':'.join([str(val[0]),str(round(float(val[1]),6))]) for val inlist])34

35 ofile.write(' '.join(['SET',key,result]))36 ofile.write("\n")37

38 ofile.close()

类似如下数据:

python音乐推荐系统_音乐推荐系统_第14张图片

(4)灌库

unix2dos cf_reclist.redis

cat cf_reclist.redis | /usr/local/src/redis-2.8.3/src/redis-cli --pipe

验证:

521f589511302ffd64467882d34d7cdc.png

3.4、LR训练模型的数据准备

准备我们自己的训练数据

进入pre_data_for_rankmodel目录:

gen_samples.py

1 #coding=utf-8

2

3

4 '''

5 思路:这里我们经过cb,cf算法,将数据已经放到内存库,召回部分已经完成,接下来我们需要做排序模型,为逻辑回归准备样本数据6 1、处理第一次将用户元数据,物品元数据,用户行为数据一起归并的数据,也就是merge_base.data,我们在这里需要得到用户画像7 数据,用户信息数据,标签数据8 2、收取样本,标签,用户画像信息,物品信息9 3、抽取用户画像信息,对性别和年龄生成样本数据10 4、抽取item特征信息,分词获得token,score,做样本数据11 5、拼接样本,生成最终的样本信息,作为模型进行训练12 '''

13

14 importsys15 sys.path.append('../')16 reload(sys)17 sys.setdefaultencoding('utf-8')18

19 importjieba20 importjieba.analyse21 importjieba.posseg22

23 merge_base_infile = '../data/merge_base.data'

24 output_file = '../data/samples.data'

25

26 #我们这里需要再生成两个文件,一个是用户样本和item样本,因为要对实时推荐的化,必须使用这两个样本

27 output_user_feature_file = '../data/user_feature.data'

28 output_item_feature_file = '../data/item_feature.data'

29

30 #这里生成个类似name和id对应的字典信息

31 output_itemid_to_name_file = '../data/name_id.dict'

32

33

34 #定义函数,来获取各类数据

35 defget_base_samples(infile):36 #放待处理样本数据

37 ret_samples_list =[]38 #放user用户数据

39 user_info_set =set()40 #放物品数据

41 item_info_set =set()42 item_name2id ={}43 item_id2name ={}44

45 with open(infile, 'r') as fd:46 for line infd:47 ss = line.strip().split('\001')48 if len(ss) != 13:49 continue

50 userid =ss[0].strip()51 itemid = ss[1].strip()52 #这两个时间为了计算label而使用

53 watch_time = ss[2].strip()54 total_time = ss[10].strip()55

56 #用户数据

57 gender = ss[4].strip()58 age = ss[5].strip()59 user_feature = '\001'.join([userid, gender, age])60

61 #物品数据

62 name = ss[8].strip()63 item_feature = '\001'.join([itemid, name])64

65 #计算标签

66 label = float(watch_time) /float(total_time)67 final_label = '0'

68

69 if label >= 0.82:70 final_label = '1'

71 elif label <= 0.3:72 final_label = '0'

73 else:74 continue

75

76 #接下来装在数据,并返回结果,首先我们装在itemid2name和itemname2id

77 item_name2id[name] =itemid78 item_id2name[itemid] =name79

80 #装在待处理的标签数据

81 ret_samples_list.append([final_label, user_feature, item_feature])82

83 user_info_set.add(user_feature)84 item_info_set.add(name)85

86 returnret_samples_list, user_info_set, item_info_set, item_name2id, item_id2name87

88

89 #step 1 程序的入口,开始调用函数,开始处理文件,得到相应的数据

90 base_sample_list, user_info_set, item_info_set, item_name2id, item_id2name =\91 get_base_samples(merge_base_infile)92

93

94 #step 2 抽取用户画像信息,用户标签转换,将年龄和age进行转换,用于样本使用

95 user_fea_dict ={}96 for info inuser_info_set:97 userid, gender, age = info.strip().split('\001')98

99 #设置标签idx,将男(1)和女(0)用数剧的形式表示,权重都设置为1

100 idx = 0 #default 女

101 if gender == '男':102 idx = 1

103 #将标签和权重拼接起来

104 gender_fea = ':'.join([str(idx), '1'])105

106 #性别设置完成,我们接下来设置年龄,将年龄进行划分,0-18,19-25,26-35,36-45

107 idx =0108 if age == '0-18':109 idx =0110 elif age == '19-25':111 idx = 1

112 elif age == '26-35':113 idx = 2

114 elif age == '36-45':115 idx = 3

116 else:117 idx = 4

118

119 idx += 2

120

121 age_fea = ':'.join([str(idx), '1'])122

123 user_fea_dict[userid] = ' '.join([gender_fea, age_fea])124

125 #step 3 抽取物品特征,这里我们要用到分词,将name进行分词,并且把分词后的token转换成id,这里就需要我们来做生成tokenid词表

126 token_set =set()127 item_fs_dict ={}128 for name initem_info_set:129 token_score_list =[]130 for x,w in jieba.analyse.extract_tags(name,withWeight=True):131 token_score_list.append((x,w))132 token_set.add(x)133 item_fs_dict[name] =token_score_list134

135 #进行token2id的转换

136 token_id_dict ={}137 #这里我们要用到刚刚利用set去重过的token列表,生成tokenid的字典表

138 for s inenumerate(list(token_set)):139 token_id_dict[s[1]] =s[0]140

141 #接下来,我们需要把第三步生成的item_fs_dict中name对应的token全部替换成id,然后当作字典,为下面的全量替换做准备

142 item_fea_dict ={}143 user_feature_offset = 10

144 for name ,fea initem_fs_dict.items():145 token_score_list =[]146 for (token,score) infea:147 if token not intoken_id_dict:148 continue

149 token_id = token_id_dict[token] +user_feature_offset150 token_score_list.append(':'.join([str(token_id),str(score)]))151

152 #接下来输出到字典中

153 item_fea_dict[name] = ' '.join(token_score_list)154

155 #step 4 将第一步输出的样本数据整体替换并且替换user_feature和item_feature,并输出到文件中

156 ofile = open(output_file,'w')157 for (label,userfea,itemfea) inbase_sample_list:158 userid = userfea.strip().split('\001')[0]159 item_name = itemfea.strip().split('\001')[1]160

161 if userid not inuser_fea_dict:162 continue

163 if item_name not initem_fea_dict:164 continue

165

166 ofile.write(' '.join([label,user_fea_dict[userid],item_fea_dict[item_name]]))167 ofile.write('\n')168

169 ofile.close()170

171 #step 5 为了能够实时使用userfeatre,我们需要输出一下

172 out_put_file = open(output_user_feature_file,'w')173 for userid,fea inuser_fea_dict.items():174 out_put_file.write('\t'.join([userid,fea]))175 out_put_file.write('\n')176 out_put_file.close()177

178 #step 6 输出item_feature

179 out_file = open(output_item_feature_file,'w')180 for name,fea initem_fea_dict.items():181 if name not initem_name2id:182 continue

183 itemid =item_name2id[name]184 out_file.write('\t'.join([itemid,fea]))185 out_file.write('\n')186

187 #step 7 输出id2name的对应的字典

188 o_file = open(output_itemid_to_name_file,'w')189 for id,name initem_id2name.items():190 o_file.write('\t'.join([id,name]))191 o_file.write('\n')192 o_file.close()

得到如下数据:

python音乐推荐系统_音乐推荐系统_第15张图片

3.5、模型准备

1 #-*- coding: UTF-8 -*-

2 '''

3 思路:这里我们要用到我们的数据,就需要我们自己写load_data的部分,4 首先定义main,方法入口,然后进行load_data的编写5 其次调用该方法的到x训练x测试,y训练,y测试,使用L1正则化或是L2正则化使得到结果更加可靠6 输出wegiht,和b偏置7 '''

8 importsys9 importnumpy as np10 from scipy.sparse importcsr_matrix11

12 from sklearn.model_selection importtrain_test_split13 from sklearn.linear_model importLogisticRegression14

15 input_file = sys.argv[1]16

17 defload_data():18 #由于在计算过程用到矩阵计算,这里我们需要根据我们的数据设置行,列,和训练的数据准备

19 #标签列表

20 target_list =[]21 #行数列表

22 fea_row_list =[]23 #特征列表

24 fea_col_list =[]25 #分数列表

26 data_list =[]27

28 #设置行号计数器

29 row_idx =030 max_col =031

32 with open(input_file,'r') as fd:33 for line infd:34 ss = line.strip().split(' ')35 #标签

36 label =ss[0]37 #特征

38 fea = ss[1:]39

40 #将标签放入标签列表中

41 target_list.append(int(label))42

43 #开始循环处理特征:

44 for fea_score infea:45 sss = fea_score.strip().split(':')46 if len(sss) != 2:47 continue

48 feature, score =sss49 #增加行

50 fea_row_list.append(row_idx)51 #增加列

52 fea_col_list.append(int(feature))53 #填充分数

54 data_list.append(float(score))55 if int(feature) >max_col:56 max_col =int(feature)57

58 row_idx += 1

59

60 row =np.array(fea_row_list)61 col =np.array(fea_col_list)62 data =np.array(data_list)63

64 fea_datasets = csr_matrix((data, (row, col)), shape=(row_idx, max_col + 1))65

66 x_train, x_test, y_train, y_test = train_test_split(fea_datasets, s, test_size=0.2, random_state=0)67

68 returnx_train, x_test, y_train, y_test69

70 defmain():71 x_train,x_test,y_train,y_test =load_data()72 #用L2正则话防止过拟合

73 model = LogisticRegression(penalty='l2')74 #模型训练

75 model.fit(x_train,y_train)76

77 ff_w = open('model.w', 'w')78 ff_b = open('model.b', 'w')79

80 #写入训练出来的W

81 for w_list inmodel.coef_:82 for w inw_list:83 print >> ff_w, "w:", w84 #写入训练出来的B

85 for b inmodel.intercept_:86 print >> ff_b, "b:", b87 print "precision:", model.score(x_test, y_test)88 print "MSE:", np.mean((model.predict(x_test) - y_test) ** 2)89

90 if __name__ == '__main__':91 main()

好了,所有的一切都准备好了,我们下来就进行推荐系统的实现

3.6、推荐系统实现

推荐系统demo流程

(1)解析请求:userid,itemid

(2)加载模型:加载排序模型(model.w,model.b)

(3)检索候选集合:利用cb,cf去redis里面检索数据库,得到候选集合

(4)获取用户特征:userid

(5)获取物品特征:itemid

(6)打分(逻辑回归,深度学习),排序

(7)top-n过滤

(8)数据包装(itemid->name),返回

推荐系统的实现主要就是我们前面说的这几部分,思路很明确,需要大家细细看下代码。

main.py

1 #coding=utf-8

2 importweb3 importsys4 importredis5 importjson6 importmath7

8 urls =(9 '/', 'index',10 '/test', 'test',11 )12

13 app =web.application(urls, globals())14

15 #加载user特征

16 user_fea_dict ={}17 with open('../data/user_feature.data') as fd:18 for line infd:19 userid, fea_list_str = line.strip().split('\t')20 user_fea_dict[userid] =fea_list_str21

22

23 #加载item特征

24 item_fea_dict ={}25 with open('../data/item_feature.data') as fd:26 for line infd:27 ss = line.strip().split('\t')28 if len(ss) != 2:29 continue

30 itemid, fea_list_str =ss31 item_fea_dict[itemid] =fea_list_str32

33 classindex:34 defGET(self):35 r = redis.Redis(host='master', port=6379,db=0)36 #step 1 : 解析请求,上面我们已经得到userid,itemid

37 params =web.input()38 userid = params.get('userid', '')39 req_itemid = params.get('itemid', '')40

41 #step 2 : 加载模型

42 model_w_file_path = '../rankmodel/model.w'

43 model_b_file_path = '../rankmodel/model.b'

44

45 model_w_list =[]46 model_b =0.47 with open (model_w_file_path, 'r') as fd:48 for line infd:49 ss = line.strip().split(' ')50 if len(ss) != 3:51 continue

52 model_w_list.append(float(ss[2].strip()))53

54 with open (model_b_file_path, 'r') as fd:55 for line infd:56 ss = line.strip().split(' ')57 model_b = float(ss[2].strip())58

59 #step 3 : 检索候选(match),这里我们分两次,cb,cf

60 #将检索回来的item全部放到recallitem列表里面

61 rec_item_mergeall =[]62 #3.1 cf

63 cf_recinfo = 'null'

64 key = '_'.join(['CF', req_itemid])65 ifr.exists(key):66 cf_recinfo =r.get(key)67

68 if len(cf_recinfo) > 6:69 for cf_iteminfo in cf_recinfo.strip().split('_'):70 item, score = cf_iteminfo.strip().split(':')71 rec_item_mergeall.append(item)72

73 #3.2 cb

74 cb_recinfo = 'null'

75 key = '_'.join(['CB', req_itemid])76 ifr.exists(key):77 cb_recinfo =r.get(key)78 if len(cb_recinfo) > 6:79 for cb_iteminfo in cb_recinfo.strip().split('_'):80 item, score = cb_iteminfo.strip().split(':')81 rec_item_mergeall.append(item)82

83 #step 4: 获取用户特征,将获取的用户特征处理后放到字典里面,方便后续计算内积

84 user_fea = ''

85 if userid inuser_fea_dict:86 user_fea =user_fea_dict[userid]87

88 u_fea_dict ={}89 for fea_idx in user_fea.strip().split(' '):90 ss = fea_idx.strip().split(':')91 if len(ss) != 2:92 continue

93 idx =int(ss[0].strip())94 score = float(ss[1].strip())95 u_fea_dict[idx] =score96

97 #step 5: 获取物品的特征 ,循环遍历刚刚得到itemid,判断item是否在item特征中,若在开始进行处理

98 rec_list =[]99 for itemid inrec_item_mergeall:100 if itemid initem_fea_dict:101 item_fea =item_fea_dict[itemid]102

103 i_fea_dict =dict()104 for fea_idx in item_fea.strip().split(' '):105 ss = fea_idx.strip().split(':')106 if len(ss) != 2:107 continue

108 idx =int(ss[0].strip())109 score = float(ss[1].strip())110 i_fea_dict[idx] =score111

112 #我们得到召回item对应的特征和用户的特征,我们接下来根据模型求出来的w,b,进行打分

113 wx_score =0.114 #这里我们求个内积,wx,然后做sigmoid,先将两个字典拼接起来,然后计算分数

115 for fea, score in dict(u_fea_dict.items() +i_fea_dict.items()).items():116 wx_score += (score *model_w_list[fea])117

118 #计算sigmoid: 1 / (1 + exp(-wx))

119 final_rec_score = 1 / (1 + math.exp(-(wx_score +model_b)))120 #将itemid和分数放入列表中,方便后续排序

121 rec_list.append((itemid, final_rec_score))122

123 #step 6 : 精排序(rank)

124 rec_sort_list = sorted(rec_list, key=lambda x:x[1], reverse=True)125

126 #step 7 : 过滤(filter)

127 rec_fitler_list = rec_sort_list[:10]128

129 #step 8 : 返回+包装(return),进行将itemid转换成name

130

131 item_dict ={}132 with open('../data/name_id.dict', 'r') as fd:133 for line infd:134 raw_itemid, name = line.strip().split('\t')135 item_dict[raw_itemid] =name136

137 ret_list =[]138 for tup inrec_fitler_list:139 req_item_name =item_dict[req_itemid]140 item_name =item_dict[tup[0]]141 item_rank_score = str(tup[1])142 ret_list.append('->'.join([req_item_name, item_name, item_rank_score]))143

144 ret = '\n'.join(ret_list)145

146 returnret147

148 classtest:149 defGET(self):150 printweb.input()151 return '222'

152

153 if __name__ == "__main__":154 app.run()

验证:

192.168.150.10:9999/?userid=00370d83b51febe3e8ae395afa95c684&itemid=3880409156

python音乐推荐系统_音乐推荐系统_第16张图片

840f8e1f0252e9e1cbfbb8532a6e9c76.png

python音乐推荐系统_音乐推荐系统_第17张图片

python音乐推荐系统_音乐推荐系统_第18张图片

python音乐推荐系统_音乐推荐系统_第19张图片

下面附上一张完整的音乐推荐系统流程结构图:

python音乐推荐系统_音乐推荐系统_第20张图片

你可能感兴趣的:(python音乐推荐系统)