目錄
- 前言
- 總體設計
- 系統整體結構圖
- 系統流程圖
- 運行環境
- Python 環境
- MySQL環境
- VUE環境
- 模塊實現
- 1. 數據請求和儲存
- 2. 數據處理
- 計算歌曲、歌手、用戶相似度
- 計算用戶推薦集
- 3. 數據存儲與后臺
- 4. 數據展示
- 系統測試
- 工程源代碼下載
- 其它資料下載
前言
本項目以豐富的網易云音樂數據為基礎,運用協同過濾和內容推薦算法作為核心方法,旨在為不同用戶量身定制音樂推薦。
首先,我們充分利用網易云音樂的大量用戶數據,包括用戶的收聽歷史、喜好歌手、喜歡的歌曲等信息。通過協同過濾算法,我們可以分析不同用戶之間的相似性,找到具有相近音樂口味的用戶群體。
其次,我們引入內容推薦算法,從音樂的特征、流派、歌手風格等方面進行深入分析。這種算法能夠更精準地為用戶推薦符合其喜好和興趣的音樂作品。
綜合協同過濾和內容推薦的結果,我們為每位用戶打造個性化的音樂推薦列表。這樣,不同用戶將能夠在網易云音樂平臺上獲得與其音樂喜好相符的歌曲,從而提升他們的音樂體驗。
本項目的目標是充分發揮大數據分析和智能推薦算法的優勢,為網易云音樂的用戶提供更加個性化、多樣化的音樂推薦服務。這將為用戶帶來更多的音樂發現和享受,同時也促進了音樂平臺的發展和用戶滿意度的提升。
總體設計
本部分包括系統整體結構圖和系統流程圖。
系統整體結構圖
系統整體結構如圖所示。
系統流程圖
系統流程如圖所示。
運行環境
本部分包括 Python 環境、MySQL 環境和 VUE 環境。
Python 環境
需要Python 3.6以上運行環境,推薦使用PyCharm IDE。Python 包和對應的版本MusicRecSys/MusicRec/z-others/files/requirement.txt
文件中,安裝命令為:
pip install -r requirement.txt
需要安裝的依頼包為: Django 2.1、PyMySQL 0.9.2、jieba 0.39、xlrd 1.1.0、gensim 3.6.0
查看本機的IP地址,修改MusicRecSys/MusicRec/Music Rec/settings.py文件中的ALLOWED_HOSTS
為本地IP地址和MySQL配置信息。
進入MusicRecSys/MusicRec, 執行pythonmanage.pyrunserver0.0.0.0: 8000
。
Django后臺訪問地址為http://127.0.0.1:8000/admin/(admin, admin)
MySQL環境
安裝MySQL最新版本和Navicat可視化工具,在命令行中創建并連接本機用戶的數據庫,下載地址為https://pan.baidu.com/s/1dYtKQxnoSZywuRfgCOfPRQ, 提取碼為:qw8k
新建musicrec數據庫,導入文件MusicRecSys/MusicRec/z-others/fles/musicrec.sql
VUE環境
安裝node.js 10.13以上版本和npm包管理器(可以安裝淘寶鏡像cnpm提高速度),推薦使用VSCODEIDE。
修改MusicRecSys/MusicRec-Vue/config/index.js
中的serverUrl為本機IP地址。
修改MusicRecSys/MusicRec-Vue/src/assets/js/linkBase.js
中的serverUrl為本機IP地址。
進入MusicRecSys/MusicRec-Vue
,執行npminstall/npmrundev
自動安裝所需要的依賴包并用webpack打包運行。
在瀏覽器中輸入http://127.0.0.1:8001, 訪問項目界面。
模塊實現
本項目包括包括4個模塊:數據請求及存儲、數據處理、數據存儲與后臺、數據展示,下面分別介紹各模塊的功能及相關代碼。
1. 數據請求和儲存
通過請求獲取音樂和用戶相關的所有數據。網易云API地址為https://api.imjad.cn。選擇歌單數據作為出發點,是因為歌單與用戶、歌曲、歌手都有聯系,包含數據的維度最廣,并且是用戶的主觀行為。歌單URL如下所示。
https://music163.com/playlist?id=2308644764
https://music163.com/playlist?id=2470855457
https://music163.com/playlist?id=2291941158
https://music163.com/playlist?id=2452844647
通過URL處理得到歌單ID,請求所需要的數據,存儲每一步請求失敗的數據,在后續數據處理時跳過這些請求失敗的數據URL。
1)歌單信息
歌單信息如圖所示。
歌單信息包含歌單ID、創建者ID、名字、創建時間、更新時間,包含音樂數、播放次數、分享次數、評論次數、收藏次數、標簽和歌單封面等。
2)創建者信息
創建者信息如圖所示。
創建者信息包含用戶ID、昵稱、生日、性別、省份、城市、類型、標簽、頭像鏈接、用戶狀態、賬號狀態、djStatus、vipStatus、 簽名。
3)歌曲音樂信息
歌曲ID信息如圖所示。
歌曲信息如圖所示。
歌曲信息包含歌曲ID、歌名、專輯ID、出版時間、歌手信息、總評論數、熱門評論數、大小、歌曲鏈接。
4)歌曲對應的歌手信息
歌手信息如圖所示。
歌手信息包含歌手ID、歌名、音樂作品數、MV作品數、專輯數、頭像鏈接等,其數據文件結構如圖所示。
最終獲得所需的基礎數據和請求失敗的數據,獲取歌單信息并存儲的相關代碼如下:
import requests
import traceback
#獲取每個歌單的信息類
class PlayList:def __init__(self):self.playlist_file = "./data/playlist_url/playlist_id_name_all.txt"#獲取出錯的歌單ID保存文件self.error_id_file = "./data/error_playlist_ids.txt"#歌單創造者信息self.creator_mess = "./data/user_mess/"#每個歌單的json信息self.playlist_mess = "./data/playlist_mess/"#歌單包含的歌曲ID信息self.trackid_mess = "./data/song_mess/"self.ids_list = self.getIDs()self.url = "https://api.imjad.cn/cloudmusic/?type=playlist&id="#獲得的歌單信息出錯的歌單IDself.error_id = list()#由歌單url 獲取歌單IDdef getIDs(self):print("根據歌單鏈接獲取歌單ID ...")ids_list = list()for line in open(self.playlist_file,"r",encoding="utf-8").readlines():try:id = line.strip().split("\t")[0].split("id=")[1]ids_list.append(id)except Exception as e:print(e)passprint("獲取歌單ID完成 ...")return ids_list
#獲取每個歌單的具體信息url #https://api.imjad.cn/cloudmusic/?type=playlist&id=2340739428def getEveryPlayListMess(self):print("獲取每個歌單的具體信息")i = 0while self.ids_list.__len__() !=0 :i += 1id = self.ids_list.pop()url = self.url + str(id)try:print("%s - 歌單ID為:%s" % (i,id))r = requests.get(url)#解析信息self.getFormatPlayListMess(r.json())except Exception as e:#將出錯ID寫入記錄及寫入文件,出錯時進行跳過print(e)traceback.print_exc()print("歌單ID為:%s 獲取出錯,進行記錄" % id)self.error_id.append(id)pass#breakself.writeToFile(self.error_id_file,",".join(self.error_id))print("歌單信息獲取完畢,寫入文件: %s" % self.playlist_mess)#每個歌單的內容進行格式化處理寫入文件#需要獲取的信息: 歌單信息、創建者信息、歌單音樂信息def getFormatPlayListMess(self,json_line):#創建者信息:用戶ID、昵稱、生日、性別、省份、城市、類型、標簽、頭像鏈接、用戶狀態、賬號狀態、djStatus,vipStatus、簽名creator = json_line["playlist"]["creator"]c_list = (str(creator["userId"]),str(creator["nickname"]),str(creator["birthday"]),str(creator["gender"]),str(creator["province"]),str(creator["city"]),str(creator["userType"]),str(creator["expertTags"]),str(creator["avatarUrl"]),str(creator["authStatus"]),str(creator["accountStatus"]),str(creator["djStatus"]),str(creator["vipType"]),str(creator["signature"]).replace("\n",""))self.writeToFile(self.creator_mess + "user_mess_all.txt"," |=| ".join(c_list))#歌單信息#歌單ID、創建者ID、名字、創建時間、更新時間、播放次數、分享次數、評論次數、收藏次數、標簽、歌單封面、描述playlist = json_line["playlist"]p_list = [str(playlist["id"]),str(playlist["userId"]),str(playlist["name"]).replace("\n",""),str(playlist["createTime"]),str(playlist["updateTime"]),str(playlist["trackCount"]),str(playlist["playCount"]),str(playlist["shareCount"]),str(playlist["commentCount"]),str(playlist["subscribedCount"]),str(playlist["tags"]),str(playlist["coverImgUrl"]),str(playlist["description"]).replace("\n","")]self.writeToFile(self.playlist_mess + "pl_mess_all.txt"," |=| ".join(p_list))#歌單包含的歌曲信息t_list = list()trackids = json_line["playlist"]["trackIds"]for one in trackids:t_list.append(str(one["id"]))self.writeToFile(self.trackid_mess + "ids_all1.txt",str(playlist["id"])+"\t"+",".join(t_list))#寫入文件def writeToFile(self,filename,one):fw = open(filename,"a",encoding="utf8")fw.write(str(one) + "\n")fw.close()
if __name__ == "__main__": #主函數print("開始獲取歌單信息 ..")pl = PlayList()pl.getEveryPlayListMess()print("歌單信息獲取完畢 ... Bye !")
2. 數據處理
本部分包含計算歌曲、歌手、用戶相似度和計算用戶推薦集。
計算歌曲、歌手、用戶相似度
用戶在創建歌單時指定了標簽,系統認為用戶對該標簽有偏好,遍歷用戶創建的所有歌單,會給出標簽向量。
例如,若系統中有3個標簽(日語、嘻哈、沉默),用戶張三在歌單中使用的標簽為日語和嘻哈,則其對應的標簽向量為[1,1,0],根據用戶的標簽向量使用杰卡德距離算法計算用戶相似度。歌單、歌手、歌曲的相似計算邏輯與用戶相似度的計算邏輯相同。相關代碼如下:
#計算用戶相似度,全量用戶存儲數據量大,所以這里只存儲了20個用戶,并且要求相似度大于0.8
def getUserSim(self):sim = dict()if os.path.exists("./data/user_sim.json"): #路徑sim = json.load(open("./data/user_sim.json","r",encoding="utf-8"))else:i = 0for use1 in self.userTags.keys():sim[use1] = dict()for use2 in self.userTags.keys():if use1 != use2:j_len = len (self.userTags[use1] & self.userTags[use2] )if j_len !=0:result = j_len / len(self.userTags[use1] | self.userTags[use2])if sim[use1].__len__() < 20 or result > 0.8:sim[use1][use2] = resultelse:#找到最小值并刪除minkey = min(sim[use1], key=sim[use1].get)del sim[use1][minkey]i += 1print(str(i) + "\t" + use1)json.dump(sim, open("./data/user_sim.json","w",encoding="utf-8"))print("用戶相似度計算完畢!")return sim
#將計算出的相似度轉成導入mysql的格式
def transform(self):fw = open("./data/user_sim.txt","a",encoding="utf-8")for u1 in self.sim.keys():for u2 in self.sim[u1].keys():fw.write(u1 + "," + u2 + "," + str(self.sim[u1][u2]) + "\n")fw.close()print("Over!")
計算用戶推薦集
本部分主要介紹用戶的協同過濾算法,為用戶產生歌曲推薦,與歌單、用戶、歌手的推薦算法相似。
(1)創建RecSong類
相關代碼如下:
class RecSong:def __init__(self):self.playlist_mess_file = "../tomysql/data/pl_mess_all.txt"self.playlist_song_mess_file = "../tomysql/data/pl_sing_id.txt"self.song_mess_file = "../tomysql/data/song_mess_all.txt"
# 在__init__(self)中指定了所使用的文件
2)構建用戶和歌曲的對應關系
用戶創建了歌單,歌單中包含歌曲。當用戶將一首歌曲歸檔到歌單中,則認為該首歌曲的評分值為1;如果對同一首歌多次歸檔,則每次歸檔評分值加1。相關代碼如下:
#加載數據 =>用戶對歌曲的對應關系
def load_data(self):#所有用戶user_list = list()#歌單和歌曲對應關系playlist_song_dict = dict()for line in open(self.playlist_song_mess_file, "r", encoding="utf-8"):#歌單 \t 歌曲splaylist_id, song_ids = line.strip().split("\t")playlist_song_dict.setdefault(playlist_id, list())for song_id in song_ids.split(","):playlist_song_dict[playlist_id].append(song_id)#print(playlist_sing_dict)print("歌單和歌曲對應關系!")#用戶和歌曲對應關系user_song_dict = dict()for line in open(self.playlist_mess_file, "r", encoding="utf-8"):pl_mess_list = line.strip().split(" |=| ")playlist_id, user_id = pl_mess_list[0], pl_mess_list[1]if user_id not in user_list:user_list.append(user_id)user_song_dict.setdefault(user_id, {})for song_id in playlist_song_dict[playlist_id]:user_song_dict[user_id].setdefault(song_id, 0)user_song_dict[user_id][song_id] += 1#print(user_song_dict)print("用戶和歌曲對應信息統計完畢 !")return user_song_dict, user_list
3)計算用戶相似度
為用戶推薦歌曲采用的是基于協同過濾算法,需要計算出用戶相似度。計算分為兩步:構建倒排表和構建相似度矩陣,計算公式為:
w u v = ∑ i ∈ N ( u ) ∩ N ( v ) 1 lg ? ( 1 + ∣ N ( i ) ∣ ) ∣ N ( u ) ∥ N ( v ) ∣ {w_{u v}}=\frac{\sum_{i \in N(u) \cap N(v)} \frac{1}{\lg (1+|N(i)|)}}{\sqrt{|N(u) \| N(v)|}} wuv?=∣N(u)∥N(v)∣?∑i∈N(u)∩N(v)?lg(1+∣N(i)∣)1??
相關代碼如下:
#計算用戶之間的相似度,采用懲罰熱門商品和優化復雜度的算法
def UserSimilarityBest(self):#得到每個item被哪些user評價過tags_users = dict()for user_id, tags in self.user_song_dict.items():for tag in tags.keys():tags_users.setdefault(tag,set())if self.user_song_dict[user_id][tag] > 0:tags_users[tag].add(user_id)#構建倒排表C = dict()N = dict()for tags, users in tags_users.items():for u in users:N.setdefault(u,0)N[u] += 1C.setdefault(u,{})for v in users:C[u].setdefault(v, 0)if u == v:continueC[u][v] += 1 / math.log(1+len(users))#構建相似度矩陣W = dict()for u, related_users in C.items():W.setdefault(u,{})for v, cuv in related_users.items():if u==v:continueW[u].setdefault(v, 0.0)W[u][v] = cuv / math.sqrt(N[u] * N[v])print("用戶相似度計算完成!")return W
4)計算用戶對歌曲的可能偏好
遍歷所有的相似用戶,對于未產生過歸檔行為的歌曲,計算用戶對他們的偏好。相關代碼如下:
#為每個用戶推薦歌曲
def recommend_song(self):#記錄用戶對歌手的評分user_song_score_dict = dict()if os.path.exists("./data/user_song_prefer.json"):user_song_score_dict = json.load(open("./data/user_song_prefer.json", "r", encoding="utf-8"))print("用戶對歌手的偏好從文件加載完畢!")return user_song_score_dictfor user in self.user_song_dict.keys():print(user)user_song_score_dict.setdefault(user, {})#遍歷所有用戶for user_sim in self.user_sim[user].keys():if user_sim == user:continuefor song in self.user_song_dict[user_sim].keys():user_song_score_dict[user].setdefault(song,0.0)user_song_score_dict[user][song] += self.user_sim[user][user_sim] * self.user_song_dict[user_sim][song]json.dump(user_song_score_dict, open("./data/user_song_prefer.json", "w", encoding="utf-8"))print("用戶對歌曲的偏好計算完成!")return user_song_score_dict
5)寫入文件
對每首歌曲的偏好進行排序,將用戶最可能產生歸檔行為的前100首歌曲寫入文件,便于導入數據庫,供系統使用。相關代碼如下:
#寫入文件
def write_to_file(self):fw = open("./data/user_song_prefer.txt","a",encoding="utf-8")for user in self.user_song_score_dict.keys():sort_user_song_prefer = sorted(self.user_song_score_dict[user].items(), key=lambda one:one[1], reverse=True)for one in sort_user_song_prefer[:100]:fw.write(user+','+one[0]+','+str(one[1])+'\n')fw.close()print("寫入文件完成")
user_song_prefer.txt
文件內容如圖所示。
同樣,歌單、歌手、用戶的推薦結果也通過類似的方式進行計算。
3. 數據存儲與后臺
在PyCharm中創建新的Django項目及5個模板,即主頁、歌單、歌手、歌曲和用戶。模板為文本文件,用于分離表現形式和內容。下面以歌單模板為例,介紹各文件用途。Django項目結構如圖所示。
部分文件數據大,使用Navicat軟件工具導入,其余部分使用Python代碼連接數據庫導入。以歌單信息導入數據庫為例,Django中創建的Model層相關代碼如下:
#歌單信息:歌單ID、創建者ID、名字、創建時間、更新時間、包含音樂數
#播放次數、分享次數、評論次數、收藏次數、標簽、歌單封面、描述
class PlayList(models.Model):pl_id = models.CharField(blank=False, max_length=64, verbose_name="ID", unique=True)pl_creator = models.ForeignKey(User, related_name="創建者信息", on_delete=False)pl_name = models.CharField(blank=False, max_length=64, verbose_name="歌單名字")pl_create_time = models.DateTimeField(blank=True, verbose_name="創建時間")pl_update_time = models.DateTimeField(blank=True, verbose_name="更新時間")pl_songs_num = models.IntegerField(blank=True,verbose_name="包含音樂數")pl_listen_num = models.IntegerField(blank=True,verbose_name="播放次數")pl_share_num = models.IntegerField(blank=True,verbose_name="分享次數")pl_comment_num = models.IntegerField(blank=True,verbose_name="評論次數")pl_follow_num = models.IntegerField(blank=True,verbose_name="收藏次數")pl_tags = models.CharField(blank=True, max_length=1000, verbose_name="歌單標簽")pl_img_url = models.CharField(blank=True, max_length=1000, verbose_name="歌單封面")pl_desc = models.TextField(blank=True, verbose_name="歌單描述")def __str__(self):return self.pl_idclass Meta:db_table = 'playList'verbose_name_plural = "歌單信息"
#歌單信息寫入數據庫
def playListMessToMysql(self):i=0for line in open("./data/pl_mess_all.txt", "r", encoding="utf-8"):pl_id, pl_creator, pl_name, pl_create_time, pl_update_time, pl_songs_num, pl_listen_num, \pl_share_num, pl_comment_num, pl_follow_num, pl_tags, pl_img_url, pl_desc = line.split(" |=| ")try:user = User.objects.filter(u_id=pl_creator)[0]except:user = User.objects.filter(u_id=pl_creator)[0]pl = PlayList(pl_id = pl_id,pl_creator = user,pl_name = pl_name,pl_create_time = self.TransFormTime(int(pl_create_time)/1000),pl_update_time = self.TransFormTime(int(pl_update_time)/1000),pl_songs_num = int (pl_songs_num),pl_listen_num = int( pl_listen_num ),pl_share_num = int( pl_share_num) ,pl_comment_num = int (pl_comment_num),pl_follow_num = int(pl_follow_num),pl_tags = str(pl_tags).replace("[","").replace("]","").replace("\'",""),pl_img_url = pl_img_url,pl_desc = pl_desc)pl.save()i+=1print(i)
執行完畢后,在數據庫可視化管理軟件Navicat和Django自帶的后臺管理中可查看對應的數據表,如圖1和圖2所示。
最終得到全部數據表,如下兩圖所示。
4. 數據展示
前端實現的功能包括:用戶登錄和選擇偏好歌曲、歌手;為你推薦(用戶行為不同,推薦也不同) ;進入各頁面時基于內容的推薦算法為用戶推薦歌單,協同過濾算法為用戶推薦歌曲、歌手;單擊時獲取詳細信息,提供單個歌單、歌曲、歌手、用戶的推薦;個性化排行榜(將相似度由大到小排序);我的足跡,展示用戶在站內的行為(單擊時記錄)。
(1)Django后臺處理前端請求獲取推薦標簽,View層相關代碼。
#首頁推薦標簽
"""由于標簽個數原因,且歌單、歌手、歌曲共用一套標簽,這里標簽推薦基于1)用戶進入系統時的選擇2)用戶在站內產生的單擊行為3)熱門標簽進行補數
"""
def GetRecTags(request, base_click):#從接口中獲取傳入的歌手和歌曲IDsings = request.session["sings"].split(",")songs = request.session["songs"].split(",")#歌手標簽sings_tags = getSingRecTags(sings, base_click)#歌曲標簽songs_tags,pl_tags = getSongAndPlRecTags(songs, base_click)return {"code": 1,"data": {"playlist": {"cateid": 2, "tags": list(pl_tags)},"song": {"cateid": 3, "tags": list(songs_tags)},"sing": {"cateid": 4, "tags": list(sings_tags)},}}
#獲得歌曲、歌單標簽推薦
def getSongAndPlRecTags(songs, base_click):song_tags = list()pl_tags = list()#base_click =1 表示用戶是在站內產生行為后返回推薦,此時用戶行為對象對應的標簽排序在前#否則基于用戶選擇的標簽排序在前if base_click == 1: #表示前端是基于單擊行為進入為你推薦模塊click_songs = UserBrowse.objects.filter(click_cate="3").values("click_id")if click_songs.__len__() != 0:for one in click_songs:filter_one = SongTag.objects.filter(song_id=one["click_id"])if filter_one.__len__() != 0 and filter_one[0].tag not in song_tags:song_tags.append(filter_one[0].tag)#歌單tagpl_one = PlayListToSongs.objects.filter( song_id=filter_one[0].song_id )if pl_one.__len__() !=0:for pl_tag_one in PlayListToTag.objects.filter(pl_id=pl_one[0].song_id):if pl_tag_one.tag not in pl_tags:pl_tags.append(pl_tag_one.tag)if songs.__len__() != 0: #表示前端選擇了相關歌曲for sing in songs:choose_one = SongTag.objects.filter(song_id=sing)if choose_one.__len__() != 0 and choose_one[0].tag not in song_tags:song_tags.append(choose_one[0].tag)#歌單tagpl_one= layListToSongs.objects.filter(song_id=choose_one[0].song_id)if pl_one.__len__() != 0:for pl_tag_one in PlayListToTag.objects.filter(pl_id=pl_one[0].song_id):if pl_tag_one.tag not in pl_tags:pl_tags.append(pl_tag_one.tag)#print("songs_tags_by_click %s" % songs_tags_by_click)#print("pl_tags_by_click %s" % pl_tags_by_click)else: #表示用戶是首次進入為你推薦模塊if songs.__len__() != 0: #表示前端選擇了相關歌曲for sing in songs:choose_one = SongTag.objects.filter(song_id=sing)if choose_one.__len__() != 0 and choose_one[0].tag not in song_tags:song_tags.append(choose_one[0].tag)#歌單tagpl_one = PlayListToSongs.objects.filter(song_id=choose_one[0].song_id)if pl_one.__len__() != 0:for pl_tag_one in PlayListToTag.objects.filter(pl_id=pl_one[0].song_id):if pl_tag_one.tag not in pl_tags:pl_tags.append(pl_tag_one.tag)#print("songs_tags_by_choose: %s" % songs_tags_by_choose)#print("pl_tags_by_choose: %s" % pl_tags_by_choose)#如果click和choose的tag不夠以hot來補充if song_tags.__len__() < 15:hot_tag_dict = dict()for one in SongTag.objects.all():hot_tag_dict.setdefault(one.tag, 0)hot_tag_dict[one.tag] += 1tag_dict_song = sorted(hot_tag_dict.items(), key=lambda k: k[1], reverse=True)[:15-song_tags.__len__()]for one in tag_dict_song:if one[0] not in song_tags:song_tags.append(one[0])#print("songs_tags_by_hot: %s" % songs_tags_by_hot)#如果 click 和 choose的tag不夠,以 hot來補充if pl_tags.__len__() < 15:hot_tag_dict = dict()for one in PlayListToTag.objects.all():hot_tag_dict.setdefault(one.tag, 0)hot_tag_dict[one.tag] += 1tag_dict_pl = sorted(hot_tag_dict.items(), key=lambda k: k[1], reverse=True)[:15-pl_tags.__len__()]for one in tag_dict_pl:if one[0] not in pl_tags:pl_tags.append(one[0])#print("pl_tags_by_hot: %s" % pl_tags_by_hot)return song_tags,pl_tags
(2)進入各頁面是基于內容的推薦算法給用戶推薦歌單、協同過濾算法推薦歌曲、歌手,這里以歌單為例:
def rec_right_playlist(request): #推薦歌單user = request.GET.get("username")u_id = User.objects.filter(u_name=user)[0].u_idrec_all = UserPlayListRec.objects.filter(user=u_id).order_by("-sim")[:12]_list = list()for rec in rec_all:one = PlayList.objects.filter(pl_id=rec.related)[0]_list.append({"pl_id": one.pl_id,"pl_creator": one.pl_creator.u_name,"pl_name": one.pl_name,"pl_img_url": one.pl_img_url})return {"code": 1,"data": {"recplaylist": _list}}
(3)單擊時獲取詳細信息,并基于標簽進行單個歌單、歌曲、歌手、用戶的推薦,這里以用戶為例:
def all(request):#接口傳入的tag參數tag = request.GET.get("tag")#接口傳入的page參數_page_id = int(request.GET.get("page"))print("Tag : %s, page_id: %s" % (tag,_page_id))_list = list()#全部用戶if tag == "all":sLists = User.objects.all().order_by("-u_id")#拼接用戶信息for one in sLists[(_page_id - 1) * 30:_page_id * 30]:_list.append({"u_id": one.u_id,"u_name": one.u_name,"u_img_url": one.u_img_url})#指定標簽下的用戶else:sLists = UserTag.objects.filter(tag=tag).values("user_id").order_by("user_id")for sid in sLists[(_page_id - 1) * 30:_page_id * 30]:one = User.objects.filter(u_id=sid["user_id"])if one.__len__() == 1:one = one[0]else:continue_list.append({"u_id": one.u_id,"u_name": one.u_name,"u_img_url": one.u_img_url})total = sLists.__len__()return {"code": 1,"data": {"total": total,"sings": _list,"tags": getAllUserTags()}}
#獲取所有用戶標簽
def getAllUserTags():tags = set()for one in UserTag.objects.all().values("tag").distinct().order_by("user_id"):tags.add(one["tag"])return list(tags)
def one(request): #處理用戶請求u_id = request.GET.get("id")one = User.objects.filter(u_id=u_id)[0]
wirteBrowse(user_name=request.GET.get("username"),click_id=u_id,click_cate="5", user_click_time=getLocalTime(), desc="查看用戶")return JsonResponse({"code": 1,"data": [{"u_id": one.u_id,"u_name": one.u_name,"u_birthday":one.u_birthday,"u_gender":one.u_gender,"u_province":one.u_province,"u_city":one.u_city,"u_tags":one.u_tags,"u_img_url": one.u_img_url,"u_sign":one.u_sign,"u_rec": getRecBasedOne(u_id),"u_playlist":getUserCreatePL(u_id)}]})
#獲取單個用戶的推薦
def getRecBasedOne(u_id):result = list()sim_users = UserSim.objects.filter(user_id=u_id).order_by("-sim").values("sim_user_id")[:10]for user in sim_users:one = User.objects.filter(u_id= user["sim_user_id"])[0]result.append({"id": one.u_id,"name": one.u_name,"img_url": one.u_img_url,"cate":"5"})return result
#獲取用戶創建的歌單
def getUserCreatePL(uid):pls = PlayList.objects.filter(pl_creator__u_id=uid)result = list()for one in pls:result.append({"pl_id": one.pl_id,"pl_name":one.pl_name,"pl_creator": one.pl_creator.u_name,"pl_create_time": one.pl_create_time,"pl_img_url": one.pl_img_url,"pl_desc":one.pl_desc})return result
#用戶瀏覽信息進行記錄
"""user_name = models.CharField(blank=False, max_length=64, verbose_name="用戶名")click_id = models.CharField(blank=False, max_length=64, verbose_name="ID")click_cate = models.CharField(blank=False, max_length=64, verbose_name="類別")user_click_time = models.DateTimeField(blank=False, verbose_name="瀏覽時間")desc = models.CharField(blank=False, max_length=1000, verbose_name="備注",default="Are you ready!")
"""
def wirteBrowse(user_name="",click_id="",click_cate="",user_click_time="",desc=""):if "12797496" in click_id: click_id = "12797496"UserBrowse(user_name=user_name,click_id=click_id,click_cate=click_cate,user_click_time = user_click_time,desc=desc).save()print("用戶【 %s 】的行為記錄【 %s 】寫入數據庫" % (user_name, desc))
#獲取當前格式化的系統時間
def getLocalTime():return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
系統測試
本部分包括系統流程和測試效果。
(1)選擇用戶進入系統。每次隨機從數據庫中返回部分作為系統用戶,使用不同的用戶是為了區分行為偏好,如圖所示。
(2)選擇歌手、歌曲(3個及以上,可以跳過)。用戶與系統交互的過程,解決系統的冷啟動。當然用戶也可以不選擇歌手,直接跳過,此時系統中“為你推薦歌手標簽部分為熱度標簽數據。界面如下兩圖所示。
(3)根據用戶創建歌單的偏好,推薦用戶偏好的歌單、歌曲的。單擊標簽可以查看相應標簽下的所有歌曲,分別進入歌單、歌曲、歌手推薦頁面,如圖所示。
(4)歌單推薦頁面。左側為按照標簽分類的全部歌單,右側為基于內容的推薦算法為用戶推薦的歌單,如圖所示。
(5) 歌單詳情頁。包含歌單的詳細信息和歌單內的歌曲,右側為基于標簽相似度提供的歌單推薦,如圖所示。
(6) 歌曲推薦頁面。左側為按照標簽分類的全部歌曲,右側為基于協同過濾算法為用戶推薦的歌曲,如圖所示。
(7)歌曲詳情頁。包含歌曲的信息和歌詞,右側為基于標簽相似度提供的歌單推薦,如圖所示 。
(8)歌手推薦頁面。左側為按照標簽分類的全部歌手,右側為基于協同過濾算法為用戶推薦的歌曲,如圖所示。
(9)歌手詳情頁。包含歌手的信息和歌曲,右側為基于標簽相似度提供的歌手推薦,如圖所示。
(10)用戶推薦頁面。左側為按照標簽分類的全部用戶,右側為基于協同過濾算法為用戶推薦的用戶,如圖所示。
(11) 用戶詳情頁。包含用戶的信息和創建的歌單,右側為基于標簽相似度提供的推薦,如圖所示 。
(12) 個性化排行榜。基于用戶的偏好程度(協同過濾算法計算的結果)進行排序展示,不同用戶看到的顯示界面不同,如圖5~圖8所示。
(13) 我的足跡。瀏覽歌單、歌曲、歌手時用戶在系統中產生的行為記錄,如圖所示。
工程源代碼下載
詳見本人博客資源下載頁
其它資料下載
如果大家想繼續了解人工智能相關學習路線和知識體系,歡迎大家翻閱我的另外一篇博客《重磅 | 完備的人工智能AI 學習——基礎知識學習路線,所有資料免關注免套路直接網盤下載》
這篇博客參考了Github知名開源平臺,AI技術平臺以及相關領域專家:Datawhale,ApacheCN,AI有道和黃海廣博士等約有近100G相關資料,希望能幫助到所有小伙伴們。