推荐算法有不少,最基础的就是协同过滤,前段时间对KTV数据比较感兴趣,你们去唱歌也只是唱熟悉的歌,那是否是有办法给你们一些建议拓展一下唱歌的宽度呢。KTV推荐可能要考虑不少因素,好比唱歌者的音域,年龄,地区,喜爱,等等。初版算法暂时只从item base的角度出发去给用户推荐。因为是我的兴趣,因此没有模型反馈迭代的过程,有兴趣的能够本身实现。python
协同过滤又叫行为类似召回,其实就是基于共现的一种类似度计算。 Item Base的协同过滤算法有几个关键概念:算法
类似度计算有不少种:共现类似度,欧几里得距离,皮尔逊相关系数,等等这里使用的是共现类似度,公式以下:elasticsearch
其中N(i)为喜欢i歌曲的用户数,一样N(j)为喜欢j歌曲的用户数,分子为同时喜欢i,j的用户数。该公式为改良公式,分子中加入了N(j)对类似度进行惩罚。这里不细讲。ui
寻找兴趣类似的用户,而后将偏好相同的用户的歌曲推荐给被推荐用户,表中发现A和C用户都喜欢i和k歌曲因此两个用户类似,因此将C用户的歌曲l推荐给A用户。若是用共现的方式去表述就是。这里细节计算的时候会涉及到用户打分和类似用户数据排序汇总。我这里都是概述。3d
用户/歌曲 | 歌曲i | 歌曲j | 歌曲k | 歌曲l |
---|---|---|---|---|
用户A | 1 | 1 | 推荐 | |
用户B | 1 | |||
用户C | 1 | 1 | 1 |
与UserBase相似,计算类似的时候使用的是歌曲矩阵找到类似的歌曲,而后根据用户历史数据进行推荐,大概原理以下表。表中发现i,k歌曲同事被A,B两个用户喜欢,因此i,k类似,若是C用户喜欢i歌曲那么他应该也喜欢类似的k歌曲.code
用户/歌曲 | 歌曲i | 歌曲j | 歌曲k |
---|---|---|---|
用户A | 1 | 1 | |
用户B | 1 | 1 | 1 |
用户C | 1 | 推荐 |
这里使用的是ItemBaseblog
若是用户喜欢i歌曲则排序
获得推荐歌曲为k歌曲索引
import elasticsearch import elasticsearch.helpers import re import numpy as np import operator def trim_song_name(song_name): """ 处理歌名,过滤掉无用内容和空白 """ song_name = song_name.strip() song_name = re.sub("-?【.*?】", "", song_name) song_name = re.sub("-?(.*?)", "", song_name) song_name = re.sub("-?(.*?)", "", song_name) return song_name def get_data(size=0): """ 获取uid=>做品名list的字典 """ cur_size=0 ret = {} es_client = elasticsearch.Elasticsearch() search_result = elasticsearch.helpers.scan( es_client, index="ktv_works", doc_type="ktv_works", scroll="10m", query={} ) all_songs_list = [] all_songs_set = set() for hit_item in search_result: cur_size += 1 if size>0 and cur_size>size: break item = hit_item['_source'] work_list = item['item_list'] ret[item['uid']] = [trim_song_name(item['songname']) for item in work_list] return ret def get_uniq_song_sort_list(song_dict): """ 合并重复歌曲并按歌曲名排序 """ return sorted(list(set(np.concatenate(list(song_dict.values())).tolist())))
import math # 共现数矩阵 col_show_count_matrix = np.zeros((song_count, song_count)) one_trik_matrix = np.zeros(song_count) for i in range(song_count): for j in range(song_count): if i>j: # 对角矩阵只计算一半的矩阵 one_trik_matrix = np.zeros(song_count) one_trik_matrix[i] = 1 one_trik_matrix[j] = 1 ret_m = user_song_one_hot_matrix.dot(one_trik_matrix.T) col_show_value = len([ix for ix in ret_m if ix==2]) col_show_count_matrix[i,j] = col_show_value col_show_count_matrix[j,i] = col_show_value # 类似度矩阵 col_show_rate_matrix = np.zeros((song_count, song_count)) # 歌曲count N(i)矩阵 song_count_matrix = np.zeros(song_count) for i in range(song_count): song_col = user_song_one_hot_matrix[:,i] song_count_matrix[i] = len([ix for ix in song_col if ix>=1]) # 类似度矩阵计算 for i in range(song_count): for j in range(song_count): if i>j: # 对角矩阵只计算一半的矩阵 # 类似度计算 N(i)nN(j)/sqart(N(i)*N(j)) rate_value = col_show_count_matrix[i,j]/math.sqrt(song_count_matrix[i]*song_count_matrix[j]) col_show_rate_matrix[i,j] = rate_value col_show_rate_matrix[j,i] = rate_value
import operator def get_songs_from_recommand(col_recommand_matrix): return [(int_to_song[k],r_value) for k,r_value in enumerate(col_recommand_matrix) if r_value>0] input_song = "十年" # 构造被推荐矩阵 one_trik_matrix = np.zeros(song_count) one_trik_matrix[song_to_int[input_song]] = 1 col_recommand_matrix = col_show_rate_matrix.dot(one_trik_matrix.T) recommand_array = get_songs_from_recommand(col_recommand_matrix) sorted_x = sorted(recommand_array, key=lambda k:k[1], reverse=True) # 获取推荐结果 print(sorted_x)
[('三生三世', 0.5773502691896258), ('下个路口见', 0.5773502691896258), ('不分手的恋爱', 0.5773502691896258),...]ip