音乐推荐系统demo

一、推荐系统流程图
在这里插入图片描述

一、项目描述:

1.对三个数据进行预处理,合并用户与物品相关信息,数据字段包含itemid、userid、用户信息(年龄、性别、收入、地区)、物品信息(名字、描述、时长、标签)、用户行为数据(收听时长)等。
2.粗排召回阶段使用CB算法,基于内容进行jieba中文分词,计算itemid对应分词的tfidf分数,整理训练数据;使用mr 协同 过滤进行相关性计算,训练得到物品之间对应分数item-item;CF算法则通过协同过滤将UI矩阵转成II矩阵,格式化数据后将结果按k/v形式批量灌入redis数据库。
3.精排阶段利用LR进行推荐排序,得到权重w、b用于模型构建。结合用户与物品标签获取用户与物品特征训练数据。
4.推荐流程阶段加载特征数据及排序模型,检索redis数据库获取候选集,利用逻辑回归sigmoid函数打分并排序,最终利用可视化页面实现itemid->name进行top10评分相关推荐。

二、推荐系统思路详解

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),返回

数据集:
1.用户画像数据(user_profile.data)
字段:userid, gender, age, salary, location
userid,性别,年龄,收入,地域
002db7d2360562dd16828c4b91402000,女,46-100,5000-10000,云南
006a184749e3b3eb83e9eb516d522000,男,36-45,2000-5000,天津
00de61c1d635ad964eef2aefa8292000,女,19-25,2000-5000,内蒙古

2.物品元数据(music_meta)
字段:itemid, name, desc, total_timelen, location, tags
itemid,name,内容 ,时长,地域,标签
少女时代,鹿晗,韩国,exo综艺,最新回归,exo快乐大本营,吴世勋,fx组合,音悦台
0102209100韩国张力尹携手EXO成员CHEN《呼吸》中文版韩国张力尹携手
029900100徐颢菲《猫的借口》284国内

3.用户行为数据(user_watch_pref.sml)
userid,itemid,用户对item收听时长,点击时间(小时)
01e069ed67600f1914e64c0fe773094440903091011519
01d86fc1401b283d5828c293be290e0861928091017512
002f4b9c49be9a0b2c13e1c3c4f6a21c891510910138518

三、推荐系统实现

1、数据预处理:

总体思路:处理原始的数据,将用户画像数据 、物品元数据、用户行为数据,3份融合到一起,得到处理后merge_base.data,用于cb、cf算法进行计算。

python gen_base.py

#coding=utf-8

import sys

#找到三类原始数据文件,用户画像数据、物品元数据,用户行为数据
user_action_data = '../data/user_watch_pref.sml'
music_meta_data = '../data/music_meta'
user_profile_data = '../data/user_profile.data'

##将合并后的元数据放到新的文件里
output_file = '../data/merge_base.data'

# 将3份数据merge后的结果输出,供下游数据处理
ofile = open(output_file, 'w')

# step 1. 处理物品元数据,将处理后的结果放入字典里面,key是itemid,value为物品对应的信息,为最后写入做准备
item_info_dict = {
    
    }
with open(music_meta_data, 'r') as fd:
    for line in fd:
        ss = line.strip().split('\001')
        if len(ss) != 6:
            continue
        itemid, name, desc, total_timelen, location, tags = ss
        item_info_dict[itemid] = '\001'.join([name, desc, total_timelen, location, tags])

# step 2. 处理用户画像数据,将处理后的结果放入字典里面,key是用户id,value是用户信息
user_profile_dict = {
    
    }
with open(user_profile_data, 'r') as fd:
    for line in fd:
        ss = line.strip().split(',')
        if len(ss) != 5:
            continue
        userid, gender, age, salary, location = ss
        user_profile_dict[userid] = '\001'.join([gender, age, salary, location])

# step 3. 写入最后的信息,将用户行为数据进行处理,把step1和step2得到的数据一并归纳在文件里面
with open(user_action_data, 'r') as fd:
    for line in fd:
        ss = line.strip().split('\001')
        if len(ss) != 4:
            continue
        userid, itemid, watch_len, hour = ss

        if userid not in user_profile_dict:
            continue

        if itemid not in item_info_dict:
            continue

        ofile.write('\001'.join([userid, itemid, watch_len, hour, \
                user_profile_dict[userid], item_info_dict[itemid]]))
        ofile.write("\n")

ofile.close()

得到类似下面数据merge_base.data

01e3fdf415107cd6046a07481fbed499A6470209102A1635A21A男A36-45A20000-100000A内蒙古A黄家驹1993演唱会高清视频AA1969AA演唱会

2、【召回】CB算法

【注:CF、CB(只是数据形态不一样),都可以套用协同过滤模板得到ii矩阵】
CF:user,item,score -》item item sim
CB:token,item,score -》item item sim

总体思路:将初始化好的用户,物品,用户行为数据进行处理,目的是为了得到token,itemid,score,对于生成的数据里面的name,将itemName进行分词,得到tfidf权重,同时将desc进行分词,处理name和desc。

代码思路:元数据中tags已经分类好无需再次进行切分,只需要用idf词表查处权重即可,针对name、desc、tags三个分词结果,适当调整name权重比例,分别对这三类得出的分数再次进行分数权重划分,最后得到cb的初始数据。

(1)利用jieba分词,对item name进行中文分词,执行python gen_cb_train.py

#coding=utf-8

import sys
sys.path.append('../')
reload(sys)
sys.setdefaultencoding('utf-8')

import jieba
import jieba.posseg
import jieba.analyse


#读入初始数据
input_file = "../data/merge_base.data"

# 输出cb训练数据
output_file = '../data/cb_train.data'
outfile = open(output_file, 'w')

#定义三类的权重分数(大小可自行设定)
RATIO_FOR_NAME = 0.9
RATIO_FOR_DESC = 0.1
RATIO_FOR_TAGS = 0.05


#为tags读入idf权重值
idf_file = '../data/idf.txt'
idf_dict = {
    
    }
with open(idf_file, 'r') as fd:
    for line in fd:
        token, idf_score = line.strip().split(' ')
        idf_dict[token] = idf_score

#开始处理初始数据
itemid_set = set()
with open(input_file, 'r') as fd:
    for line in fd:
        ss = line.strip().split('\001')
        # 用户行为
        userid = ss[0].strip()
        itemid = ss[1].strip()
        watch_len = ss[2].strip()
        hour = ss[3].strip()
        # 用户画像
        gender = ss[4].strip()
        age = ss[5].strip()
        salary = ss[6].strip()
        user_location = ss[7].strip()
        # 物品元数据
        name = ss[8].strip()
        desc = ss[9].strip()
        total_timelen = ss[10].strip()
        item_location = ss[11].strip()
        tags = ss[12].strip()

        # 对item去重,相同的itemid不用再计算,因为都一样,这里用到continue特性,当不同的时候才继续执行下面的代码
        if itemid not in itemid_set:
            itemid_set.add(itemid)
        else:
            continue

        # 去掉重复后的itemid,然后我们进行分词,计算权重,放到字典里面
        token_dict = {
    
    }
        #对name统计
        for a in jieba.analyse.extract_tags(name, withWeight=True):
            token = a[0]
            score = float(a[1])
            token_dict[token] = score * RATIO_FOR_NAME

        #对desc进行分词,这里需要注意的是描述一般会含有name中的词,这里我们把有的词的分数进行相加,没有的放入
        for a in jieba.analyse.extract_tags(desc, withWeight=True):
            token = a[0]
            score = float(a[1])
            if token in token_dict:
                token_dict[token] += score * RATIO_FOR_DESC
            else:
                token_dict[token] = score * RATIO_FOR_DESC

        # 对tags 进行分数计算
        for tag in tags.strip().split(','):
            if tag not in idf_dict:
                continue
            else:
                if tag in token_dict:
                    token_dict[tag] += float(idf_dict[tag]) * RATIO_FOR_TAGS
                else:
                    token_dict[tag] = float(idf_dict[tag]) * RATIO_FOR_TAGS

        #循环遍历token_dict,输出toke,itemid,score
        for k, v in token_dict.items():
            token = k.strip()
            score = str(v)
            ofile.write(','.join([token, itemid, score]))
            ofile.write("\n")


outfile.close()

得到物品元数据:name,desc,tag
经过数据预处理,得到如下格式的cb训练数据:
哲,4090309101,0.896607562717
大连,4090309101,0.568628215367
舞曲,4090309101,0.713898826298
大美妞,4090309101,0.896607562717
网络,4090309101,0.465710816584
伤感,4090309101,0.628141853463
(最后一个不是传统的TF-IDF,因为分出的词在name,desc,tag里面权重不同,即切分单词在desc中重要性不同)
  
(2)用协同过滤算法跑出item-item数据
  
求相似度的II矩阵(相似的item配对,形成II矩阵)。

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

map阶段:

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


import sys
import re
 
for line in sys.stdin:
    ss = line.strip().split(',')
    if len(ss) != 3:
        continue
 
    r1 = 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阶段:

我们前面已经在pair reduce之前我们做过map操作,输出以token,item,score输出,所以排序是token排序好的, 这里我们相当于求的是II矩阵,所以是相同的token的item进行相似度计算

总体思路:
    1、进行user统计,若相同,把相同的user的item和score放入list里面
    2、不相同,开始进行两两配对,循环该list,进行两两配对,求出相似度
import  sys
import  math
 
cur_token = None
item_score_list = []
for line in sys.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矩阵

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

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

import sys

infile = '../data/cb.result'
outfile = '../data/cb_reclist.redis'

ofile = open(outfile, 'w')

MAX_RECLIST_SIZE = 100
PREFIX = 'CB_'

rec_dict = {
    
    }
with open(infile, 'r') as fd:
    for line in fd:
        itemid_A, itemid_B, sim_score = line.strip().split('\t')

        #判断itemA在不在该字典里面,若不在,创建一个key为itemA的列表,把与itemA相关联的itemB和score添加进去
        if itemid_A not in rec_dict:
            rec_dict[itemid_A] = []
        rec_dict[itemid_A].append((itemid_B, sim_score))

#循环遍历字典,格式化数据,把itemB和score中间以:分割,不同的itemB以_分割
for k, v in rec_dict.items():
    key_item = PREFIX + k

    #接下来格式化数据,将数据以从大到小排列后再格式化
    #排序,由于数据量大,我们只取100个
    #排好序后,我们来格式化数据
    reclist_result = '_'.join([':'.join([tu[0], str(round(float(tu[1]), 6))]) \
              for tu in sorted(v, key=lambda x: x[1], reverse=True)[:MAX_RECLIST_SIZE]])

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

ofile.close()

类似如下数据:

SET CB_5305109176 726100303:0.393048_953500302:0.393048_6193109237:0.348855

3.CF算法:

看我的这篇文章https://blog.csdn.net/qq_36816848/article/details/113184759

4.灌库(redis)

1.Centos中安装redis,本文下载的是redis-2.8.3,下载对应安装包并进行源码编译(需要C编译yum install gcc-c++ ),先执行make,然后进入src目录中,得到bin文件(redis-server 服务器,redis-cli 客户端)
  
2.启动redis server服务两种方法:
  ]# ./src/redis-server
  ]#后台方式启动 nohup ./redis-server &

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

  • 接下来灌数据(批量灌),需要安装unix2dos(yum install unix2dos)(进行格式转换),安装完后执行unix2dos cb_reclist.redis命令。

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

进入redis验证执行./src/redis-cli
  127.0.0.1:6379> get CB_5305109176
  “726100303:0.393048_953500302:0.393048_6193109237:0.348855”

4、LR训练模型的数据准备
  准备我们自己的训练数据
  进入pre_data_for_rankmodel目录:
  gen_samples.py

#coding=utf-8


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

import sys
sys.path.append('../')
reload(sys)
sys.setdefaultencoding('utf-8')

import jieba
import jieba.analyse
import jieba.posseg

merge_base_infile = '../data/merge_base.data'
output_file = '../data/samples.data'

#我们这里需要再生成两个文件,一个是用户样本和item样本,因为要对实时推荐的化,必须使用这两个样本
output_user_feature_file = '../data/user_feature.data'
output_item_feature_file = '../data/item_feature.data'

#这里生成个类似name和id对应的字典信息
output_itemid_to_name_file = '../data/name_id.dict'


#定义函数,来获取各类数据
def get_base_samples(infile):
    #放待处理样本数据
    ret_samples_list = []
    #放user用户数据
    user_info_set = set()
    #放物品数据
    item_info_set = set()
    item_name2id = {
    
    }
    item_id2name = {
    
    }

    with open(infile, 'r') as fd:
        for line in fd:
            ss = line.strip().split('\001')
            if len(ss) != 13:
                continue
            userid = ss[0].strip()
            itemid = ss[1].strip()
            #这两个时间为了计算label而使用
            watch_time = ss[2].strip()
            total_time = ss[10].strip()

            #用户数据
            gender = ss[4].strip()
            age = ss[5].strip()
            user_feature = '\001'.join([userid, gender, age])

            #物品数据
            name = ss[8].strip()
            item_feature = '\001'.join([itemid, name])

            #计算标签
            label = float(watch_time) / float(total_time)
            final_label = '0'

            if label >= 0.82:
                final_label = '1'
            elif label <= 0.3:
                final_label = '0'
            else:
                continue

            #接下来装在数据,并返回结果,首先我们装在itemid2name和itemname2id
            item_name2id[name] = itemid
            item_id2name[itemid] = name

            #装在待处理的标签数据
            ret_samples_list.append([final_label, user_feature, item_feature])

            user_info_set.add(user_feature)
            item_info_set.add(name)

    return ret_samples_list, user_info_set, item_info_set, item_name2id, item_id2name


#step 1 程序的入口,开始调用函数,开始处理文件,得到相应的数据
base_sample_list, user_info_set, item_info_set, item_name2id, item_id2name = \
    get_base_samples(merge_base_infile)


#step 2 抽取用户画像信息,用户标签转换,将年龄和age进行转换,用于样本使用
user_fea_dict = {
    
    }
for info in user_info_set:
    userid, gender, age = info.strip().split('\001')

    #设置标签idx,将男(1)和女(0)用数剧的形式表示,权重都设置为1
    idx = 0 # default 女
    if gender == '男':
        idx = 1
    #将标签和权重拼接起来
    gender_fea = ':'.join([str(idx), '1'])

    #性别设置完成,我们接下来设置年龄,将年龄进行划分,0-18,19-25,26-35,36-45
    idx = 0
    if age == '0-18':
        idx = 0
    elif age == '19-25':
        idx = 1
    elif age == '26-35':
        idx = 2
    elif age == '36-45':
        idx = 3
    else:
        idx = 4

    idx += 2

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

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

#step 3 抽取物品特征,这里我们要用到分词,将name进行分词,并且把分词后的token转换成id,这里就需要我们来做生成tokenid词表
token_set = set()
item_fs_dict = {
    
    }
for name in item_info_set:
    token_score_list = []
    for x,w in jieba.analyse.extract_tags(name,withWeight=True):
        token_score_list.append((x,w))
        token_set.add(x)
    item_fs_dict[name] = token_score_list

#进行token2id的转换
token_id_dict = {
    
    }
#这里我们要用到刚刚利用set去重过的token列表,生成tokenid的字典表
for s in enumerate(list(token_set)):
    token_id_dict[s[1]] = s[0]

#接下来,我们需要把第三步生成的item_fs_dict中name对应的token全部替换成id,然后当作字典,为下面的全量替换做准备
item_fea_dict = {
    
    }
user_feature_offset = 10
for name ,fea in item_fs_dict.items():
    token_score_list = []
    for (token,score) in fea:
        if token not in token_id_dict:
            continue
        token_id = token_id_dict[token] + user_feature_offset
        token_score_list.append(':'.join([str(token_id),str(score)]))

    #接下来输出到字典中
    item_fea_dict[name] = ' '.join(token_score_list)

#step 4 将第一步输出的样本数据整体替换并且替换user_feature和item_feature,并输出到文件中
ofile = open(output_file,'w')
for (label,userfea,itemfea) in base_sample_list:
    userid = userfea.strip().split('\001')[0]
    item_name = itemfea.strip().split('\001')[1]

    if userid not in user_fea_dict:
        continue
    if item_name not in item_fea_dict:
        continue

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

ofile.close()

#step 5 为了能够实时使用userfeatre,我们需要输出一下
out_put_file = open(output_user_feature_file,'w')
for userid,fea in user_fea_dict.items():
    out_put_file.write('\t'.join([userid,fea]))
    out_put_file.write('\n')
out_put_file.close()

#step 6 输出item_feature
out_file = open(output_item_feature_file,'w')
for name,fea in item_fea_dict.items():
    if name not in item_name2id:
        continue
    itemid = item_name2id[name]
    out_file.write('\t'.join([itemid,fea]))
    out_file.write('\n')

#step 7 输出id2name的对应的字典
o_file = open(output_itemid_to_name_file,'w')
for id,name in item_id2name.items():
    o_file.write('\t'.join([id,name]))
    o_file.write('\n')
o_file.close()

得到如下数据:

5、模型准备

lr.py

# -*- coding: UTF-8 -*-
'''
    思路:这里我们要用到我们的数据,就需要我们自己写load_data的部分,
         首先定义main,方法入口,然后进行load_data的编写
         其次调用该方法的到x训练x测试,y训练,y测试,使用L1正则化或是L2正则化使得到结果更加可靠
         输出wegiht,和b偏置
'''
import sys
import numpy as np
from scipy.sparse import csr_matrix

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

input_file = sys.argv[1]

def load_data():
    #由于在计算过程用到矩阵计算,这里我们需要根据我们的数据设置行,列,和训练的数据准备
    #标签列表
    target_list = []
    #行数列表
    fea_row_list = []
    #特征列表
    fea_col_list = []
    #分数列表
    data_list = []

    #设置行号计数器
    row_idx = 0
    max_col = 0

    with open(input_file,'r') as fd:
        for line in fd:
            ss = line.strip().split(' ')
            #标签
            label = ss[0]
            #特征
            fea = ss[1:]

            #将标签放入标签列表中
            target_list.append(int(label))

            #开始循环处理特征:
            for fea_score in fea:
                sss = fea_score.strip().split(':')
                if len(sss) != 2:
                    continue
                feature, score = sss
                #增加行
                fea_row_list.append(row_idx)
                #增加列
                fea_col_list.append(int(feature))
                #填充分数
                data_list.append(float(score))
                if int(feature) > max_col:
                    max_col = int(feature)

            row_idx += 1

    row = np.array(fea_row_list)
    col = np.array(fea_col_list)
    data = np.array(data_list)

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

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

    return x_train, x_test, y_train, y_test

def main():
    x_train,x_test,y_train,y_test = load_data()
    #用L2正则话防止过拟合
    model = LogisticRegression(penalty='l2')
    #模型训练
    model.fit(x_train,y_train)

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

    #写入训练出来的W
    for w_list in model.coef_:
        for w in w_list:
            print >> ff_w, "w: ", w
    # 写入训练出来的B
    for b in model.intercept_:
        print >> ff_b, "b: ", b
    print "precision: ", model.score(x_test, y_test)
    print "MSE: ", np.mean((model.predict(x_test) - y_test) ** 2)

if __name__ == '__main__':
    main()

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

#coding=utf-8
import web
import sys
import redis
import json
import math

urls = (
    '/', 'index',
    '/test', 'test',
)

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

# 加载user特征
user_fea_dict = {
    
    }
with open('../data/user_feature.data') as fd:
    for line in fd:
        userid, fea_list_str = line.strip().split('\t')
        user_fea_dict[userid] = fea_list_str


# 加载item特征
item_fea_dict = {
    
    }
with open('../data/item_feature.data') as fd:
    for line in fd:
        ss = line.strip().split('\t')
        if len(ss) != 2:
            continue
        itemid, fea_list_str = ss
        item_fea_dict[itemid] = fea_list_str

class index:
    def GET(self):
        r = redis.Redis(host='master', port=6379,db=0)
        # step 1 : 解析请求,上面我们已经得到userid,itemid
        params = web.input()
        userid = params.get('userid', '')
        req_itemid = params.get('itemid', '')

        # step 2 : 加载模型
        model_w_file_path = '../rankmodel/model.w'
        model_b_file_path = '../rankmodel/model.b'

        model_w_list = []
        model_b = 0.
        with open (model_w_file_path, 'r') as fd:
            for line in fd:
                ss = line.strip().split(' ')
                if len(ss) != 3:
                    continue
                model_w_list.append(float(ss[2].strip()))

        with open (model_b_file_path, 'r') as fd:
            for line in fd:
                ss = line.strip().split(' ')
                model_b = float(ss[2].strip())

        # step 3 : 检索候选(match),这里我们分两次,cb,cf
        #将检索回来的item全部放到recallitem列表里面
        rec_item_mergeall = []
        # 3.1 cf
        cf_recinfo = 'null'
        key = '_'.join(['CF', req_itemid])
        if r.exists(key):
            cf_recinfo = r.get(key)

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

        # 3.2 cb
        cb_recinfo = 'null'
        key = '_'.join(['CB', req_itemid])
        if r.exists(key):
            cb_recinfo = r.get(key)
        if len(cb_recinfo) > 6:
            for cb_iteminfo in cb_recinfo.strip().split('_'):
                item, score = cb_iteminfo.strip().split(':')
                rec_item_mergeall.append(item)

        # step 4: 获取用户特征,将获取的用户特征处理后放到字典里面,方便后续计算内积
        user_fea = ''
        if userid in user_fea_dict:
            user_fea = user_fea_dict[userid]

        u_fea_dict = {
    
    }
        for fea_idx in user_fea.strip().split(' '):
            ss = fea_idx.strip().split(':')
            if len(ss) != 2:
                continue
            idx = int(ss[0].strip())
            score = float(ss[1].strip())
            u_fea_dict[idx] = score

        # step 5: 获取物品的特征 ,循环遍历刚刚得到itemid,判断item是否在item特征中,若在开始进行处理
        rec_list = []
        for itemid in rec_item_mergeall:
            if itemid in item_fea_dict:
                item_fea = item_fea_dict[itemid]

                i_fea_dict = dict()
                for fea_idx in item_fea.strip().split(' '):
                    ss = fea_idx.strip().split(':')
                    if len(ss) != 2:
                        continue
                    idx = int(ss[0].strip())
                    score = float(ss[1].strip())
                    i_fea_dict[idx] = score

                #我们得到召回item对应的特征和用户的特征,我们接下来根据模型求出来的w,b,进行打分
                wx_score = 0.
                #这里我们求个内积,wx,然后做sigmoid,先将两个字典拼接起来,然后计算分数
                for fea, score in dict(u_fea_dict.items() + i_fea_dict.items()).items():
                    wx_score += (score * model_w_list[fea])

                #计算sigmoid: 1 / (1 + exp(-wx))
                final_rec_score = 1 / (1 + math.exp(-(wx_score + model_b)))
                #将itemid和分数放入列表中,方便后续排序
                rec_list.append((itemid, final_rec_score))

        # step 6 : 精排序(rank)
        rec_sort_list = sorted(rec_list, key=lambda x:x[1], reverse=True)

        # step 7 : 过滤(filter)
        rec_fitler_list = rec_sort_list[:10]

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

        item_dict = {
    
    }
        with open('../data/name_id.dict', 'r') as fd:
            for line in fd:
                raw_itemid, name = line.strip().split('\t')
                item_dict[raw_itemid] = name

        ret_list = []
        for tup in rec_fitler_list:
            req_item_name = item_dict[req_itemid]
            item_name = item_dict[tup[0]]
            item_rank_score = str(tup[1])
            ret_list.append('   ->   '.join([req_item_name, item_name, item_rank_score]))

        ret = '\n'.join(ret_list)

        return ret

class test:
    def GET(self):
        print web.input()
        return '222'

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

7.推荐测试:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_36816848/article/details/108383078
今日推荐