推薦算法有不少,最基礎的就是協同過濾,前段時間對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