KTV歌曲推荐-深入浅出协同过滤
前言
推薦算法有很多,最基礎(chǔ)的就是協(xié)同過濾,前段時間對KTV數(shù)據(jù)比較感興趣,大家去唱歌也只是唱熟悉的歌,那是不是有辦法給大家一些建議拓展一下唱歌的寬度呢。KTV推薦可能要考慮很多因素,比如唱歌者的音域,年齡,地區(qū),喜好,等等。第一版算法暫時只從item base的角度出發(fā)去給用戶推薦。由于是個人興趣,所以沒有模型反饋迭代的過程,有興趣的可以自己實(shí)現(xiàn)。
協(xié)同過濾算法
協(xié)同過濾又叫行為相似召回,其實(shí)就是基于共現(xiàn)的一種相似度計(jì)算。 Item Base的協(xié)同過濾算法有幾個關(guān)鍵概念:
相似度計(jì)算
相似度計(jì)算有很多種:共現(xiàn)相似度,歐幾里得距離,皮爾遜相關(guān)系數(shù),等等這里使用的是共現(xiàn)相似度,公式如下:
其中N(i)為喜歡i歌曲的用戶數(shù),同樣N(j)為喜歡j歌曲的用戶數(shù),分子為同時喜歡i,j的用戶數(shù)。該公式為改良公式,分子中加入了N(j)對相似度進(jìn)行懲罰。這里不細(xì)講。
ItemBase和UserBase
UserBase
尋找興趣相似的用戶,然后將偏好相同的用戶的歌曲推薦給被推薦用戶,表中發(fā)現(xiàn)A和C用戶都喜歡i和k歌曲所以兩個用戶相似,所以將C用戶的歌曲l推薦給A用戶。如果用共現(xiàn)的方式去表述就是。這里細(xì)節(jié)計(jì)算的時候會涉及到用戶打分和相似用戶數(shù)據(jù)排序匯總。我這里都是概述。
| 用戶A | 1 | ? | 1 | 推薦 |
| 用戶B | ? | 1 | ? | ? |
| 用戶C | 1 | ? | 1 | 1 |
ItemBase
與UserBase類似,計(jì)算相似的時候使用的是歌曲矩陣找到相似的歌曲,然后根據(jù)用戶歷史數(shù)據(jù)進(jìn)行推薦,大概原理如下表。表中發(fā)現(xiàn)i,k歌曲同事被A,B兩個用戶喜歡,所以i,k相似,如果C用戶喜歡i歌曲那么他應(yīng)該也喜歡相似的k歌曲.
| 用戶A | 1 | ? | 1 |
| 用戶B | 1 | 1 | 1 |
| 用戶C | 1 | ? | 推薦 |
這里使用的是ItemBase
算法實(shí)現(xiàn)
得到用戶對歌曲的one hot矩陣
- 將歌曲去重,按歌名排序
- 得到歌曲和索引的轉(zhuǎn)換字典
計(jì)算得到歌曲對歌曲的共現(xiàn)度矩陣
- 計(jì)算共現(xiàn)矩陣
- 計(jì)算單個歌曲的出現(xiàn)次數(shù)
- 計(jì)算共現(xiàn)率值公式計(jì)算共現(xiàn)度
推薦
如果用戶喜歡i歌曲則
得到推薦歌曲為k歌曲
代碼實(shí)現(xiàn)
獲取數(shù)據(jù)
import elasticsearch import elasticsearch.helpers import re import numpy as np import operatordef trim_song_name(song_name):"""處理歌名,過濾掉無用內(nèi)容和空白"""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_namedef get_data(size=0):"""獲取uid=>作品名list的字典"""cur_size=0ret = {}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 += 1if size>0 and cur_size>size:breakitem = hit_item['_source']work_list = item['item_list']ret[item['uid']] = [trim_song_name(item['songname']) for item in work_list]return retdef get_uniq_song_sort_list(song_dict):"""合并重復(fù)歌曲并按歌曲名排序"""return sorted(list(set(np.concatenate(list(song_dict.values())).tolist())))相似度計(jì)算
import math# 共現(xiàn)數(shù)矩陣 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: # 對角矩陣只計(jì)算一半的矩陣one_trik_matrix = np.zeros(song_count)one_trik_matrix[i] = 1one_trik_matrix[j] = 1ret_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_valuecol_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])# 相似度矩陣計(jì)算 for i in range(song_count):for j in range(song_count):if i>j: # 對角矩陣只計(jì)算一半的矩陣# 相似度計(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_valuecol_show_rate_matrix[j,i] = rate_value推薦
import operatordef 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 = "十年" # 構(gòu)造被推薦矩陣 one_trik_matrix = np.zeros(song_count) one_trik_matrix[song_to_int[input_song]] = 1col_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)# 獲取推薦結(jié)果 print(sorted_x)結(jié)果
[('三生三世', 0.5773502691896258), ('下個路口見', 0.5773502691896258), ('不分手的戀愛', 0.5773502691896258),...]
總結(jié)
以上是生活随笔為你收集整理的KTV歌曲推荐-深入浅出协同过滤的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 介绍一下Druild,并从Twitter
- 下一篇: 你真的了解Maven pom.xml 的