赞
踩
项目完整源码:https://github.com/cdfmlr/murecom-verse-1
你什么时候听音乐?快乐的时候,悲伤的时候,兴奋的时候,失落的时候…时时刻刻。
你听什么音乐?欢快的音乐,治愈的音乐,浪漫的音乐,伤感的音乐…因时而异,因心情而定。
传统的推荐系统做了什么?忠实记录你历史上任意时刻的所有听歌记录,拿去和别的用户比较,推荐他们喜欢的歌给你。。。不管你现在什么心情,不管你想听什么类型。这种东西往往不太懂我。
我是个情绪复杂的人,我早上醒来可能任沉浸在梦里,异常低落,我需要 Sasha Sloan;开始做事情要让脑子活过来,我需要 ZUTOMAYO;中午的疲惫下让 Evan Call 调整心情;午后的阳光里让 Bach 带我创作;日落时分的脑子被思念塞满,这时的 Halsey 也许不错;深夜凉爽的霓虹散发迷离的美,这时的爵士与嘻哈绝配,给我来一段 Nujabes。
不同时刻,不同场景,不同心情,给你听不同的歌,这就是一个基于心情的推荐系统。
(我认为时刻与场景作用于心情从而影响人,所以基于心情,而不是场景或时刻等外在因素)
如何基于心情推荐?
首先,我们需要知道一首歌对应什么心情——(也许是)最简单的方法:分析歌名、歌词、以及热心网友的精彩评论。从文本获取情感,可以用比较简单的实现:每个词会对应一些既定的情感,只要当地新华书店购买一本「情感词典」就容易获取一个句子的情感。
然后,我们需要知道用户目前的心情。可以让用户输入一段话、一首诗、一篇文学大作。析文本中的情感,和前面处理歌曲如出一辙。“但我就听个歌诶,你还要让我写作,我讨厌语文,我不是诗人。”那么,对于这种不浪漫的用户,我们假设他情感比较直接,开心还是忧伤,全写在脸上,一看便知——考虑从人像识别情感。
最后,用户的心情与数据库中歌曲的心情一比较,找出最接近的,推荐出来,完成。
想法很简单,但能实现嘛?先看看我们需要做什么。
所以说这个系统还是比较简单的。
在开始实现之前,我们还需要讨论一点细节。大连理工情感词典(以下简称 DLUT)把情感分成了 7 大类,21 小类,而那个外国的图像情感识别(以下简称 Emotic) 把情感分成了 26 类。我们需要胶合一下,把二者对应起来。这里我们选择以 DLUT 为主,将 Emotic 的分类映射到 DLUT:
DLUT | Emotic | |||
---|---|---|---|---|
编号 | 情感大类 | 情感类 | 例词 | emotion categories with definitions |
1 | 乐 | 快乐(PA) | 喜悦、欢喜、笑眯眯、欢天喜地 | 17. Happiness: feeling delighted; feeling enjoyment or amusement 20. Pleasure: feeling of delight in the senses |
2 | 安心(PE) | 踏实、宽心、定心丸、问心无愧 | 6. Confidence: feeling of being certain; conviction that an outcome will be favorable; encouraged; proud 19. Peace: well being and relaxed; no worry; having positive thoughts or sensations; satisfied |
|
3 | 好 | 尊敬(PD) | 恭敬、敬爱、毕恭毕敬、肃然起敬 | 13. Esteem: feelings of favourable opinion or judgement; respect; admiration; gratefulness |
4 | 赞扬(PH) | 英俊、优秀、通情达理、实事求是 | 14. Excitement: feeling enthusiasm; stimulated; energetic | |
5 | 相信(PG) | 信任、信赖、可靠、毋庸置疑 | 4. Anticipation: state of looking forward; hoping on or getting prepared for possible future events 12. Engagement: paying attention to something; absorbed into something; curious; intereste |
|
6 | 喜爱(PB) | 倾慕、宝贝、一见钟情、爱不释手 | 1. Affection: fond feelings; love; tenderness | |
7 | 祝愿(PK) | 渴望、保佑、福寿绵长、万寿无疆 | 4. Anticipation: state of looking forward; hoping on or getting prepared for possible future events | |
8 | 怒 | 愤怒(NA) | 气愤、恼火、大发雷霆、七窍生烟 | 2. Anger: intense displeasure or rage; furious; resentful |
9 | 哀 | 悲伤(NB) | 忧伤、悲苦、心如刀割、悲痛欲绝 | 21. Sadness: feeling unhappy, sorrow, disappointed, or discouraged 23. Suffering: psychological or emotional pain; distressed; an- guished 22. Sensitivity: feeling of being physically or emotionally wounded; feeling delicate or vulnerable |
10 | 失望(NJ) | 憾事、绝望、灰心丧气、心灰意冷 | 5. Aversion: feeling disgust, dislike, repulsion; feeling hate 21. Sadness: feeling unhappy, sorrow, disappointed, or discouraged |
|
11 | 疚(NH) | 内疚、忏悔、过意不去、问心有愧 | 25. Sympathy: state of sharing others emotions, goals or troubles; supportive; compassionate | |
12 | 思(PF) | 思念、相思、牵肠挂肚、朝思暮想 | 15. Fatigue: weariness; tiredness; sleepy | |
13 | 惧 | 慌(NI) | 慌张、心慌、不知所措、手忙脚乱 | 18. Pain: physical suffering 3. Annoyance: bothered by something or someone; irritated; impa- tient; frustrated |
14 | 恐惧(NC) | 胆怯、害怕、担惊受怕、胆颤心惊 | 16. Fear: feeling suspicious or afraid of danger, threat, evil or pain; horror | |
15 | 羞(NG) | 害羞、害臊、面红耳赤、无地自容 | 11. Embarrassment: feeling ashamed or guilty | |
16 | 恶 | 烦闷(NE) | 憋闷、烦躁、心烦意乱、自寻烦恼 | 9. Disquietment: nervous; worried; upset; anxious; tense; pres- sured; alarmed 8. Disconnection: feeling not interested in the main event of the surrounding; indifferent; bored; distracted |
17 | 憎恶(ND) | 反感、可耻、恨之入骨、深恶痛绝 | 5. Aversion: feeling disgust, dislike, repulsion; feeling hate 7. Disapproval: feeling that something is wrong or reprehensible; contempt; hostile |
|
18 | 贬责(NN) | 呆板、虚荣、杂乱无章、心狠手辣 | 3. Annoyance: bothered by something or someone; irritated; impa- tient; frustrated |
|
19 | 妒忌(NK) | 眼红、吃醋、醋坛子、嫉贤妒能 | 26. Yearning: strong desire to have something; jealous; envious; lust | |
20 | 怀疑(NL) | 多心、生疑、将信将疑、疑神疑鬼 | 10. Doubt/Confusion: difficulty to understand or decide; thinking about different options | |
21 | 惊 | 惊奇(PC) | 奇怪、奇迹、大吃一惊、瞠目结舌 | 24. Surprise: sudden discovery of something unexpected |
(这个映射我随便写的,有待商榷)
没有任何难点,直接撸代码。
在 ncm
目录下,我们从网易云获取了一些数据:
和我们上一篇文章获取 Spotify 的数据不同,网易云有个特点——歌单里面曲目多,所以我们获取了不到 1 万张列表,就得到了 20 万首歌曲,100 万条热门评论。
ncm=# select count(*) from playlists;
8426
ncm=# select count(*) from tracks;
219038
ncm=# select count(*) from comments;
1052112
获取数据的过程如下图所示:
(在 git commit message 中有开发每一步更详细的说明)
这里使用了大量 Master/Worker 模式:
Master | Worker | Worker工作 |
---|---|---|
main | Master | 按照配置,启动 Task |
Master | Task | 一个 Task 完成一组特定分类的播放列表收集 |
Task | FetchTopPlaylists | 获取播放列表 |
Task | PlaylistWorks | 完善一个播放列表及其中曲目的完整信息,并保存 |
PlaylistWorks | FetchTracks | 获取一个播放列表中的全部曲目 |
PlaylistWorks | TrackWorks | 完善一首歌曲的完整信息 |
TrackWorks | FetchLyrics | 获取一首歌的歌词 |
TrackWorks | FetchComments | 获取一首歌的热门评论 |
这些各种 Worker 都是一个单独的 Goroutine,全在并发运行,靠 channel 传数据。
以及一些 C/S 模式:
Client Caller | Server | 工作 |
---|---|---|
PlaylistWorks | DB(GORM):PostgreSQL | 保存数据 |
FetchXxx | ncmapi | 完成网络请求,获取数据 |
数据库和网络作为数据入口/出口,以 C/S 模式来访问,各自集中维护自己的链接池。
(ncm
是个实验性的程序,效率并不高。我只是想尝试在编程时去面向对象化,尝试回归比较纯粹的数据驱动、面向过程、函数式,就像 Rob Pike 的代码那样。)
在 emotext
中,实现了利用大连理工大学情感本体库进行中文文本情感分析。
从 DLUT 的网站下载到情感词典:http://ir.dlut.edu.cn/info/1013/1142.htm
它给的是 Excel 表格,为了方便,我们将其重新导出为 CSV 格式,得到的文件形如:
词语,词性种类,词义数,词义序号,情感分类,强度,极性,辅助情感分类,强度,极性
脏乱,adj,1,1,NN,7,2,,,
糟报,adj,1,1,NN,5,2,,,
战祸,noun,1,1,ND,5,2,NC,5,2
招灾,adj,1,1,NN,5,2,,,
接下来,要把这个大表读到程序里。我们把「词语 + 情感」视为一个 Word 对象,如果一个词有「辅助情感分类」则把它看成两个 Word:
class Word:
word: str
emotion: str
intensity: int # 情感强度: 分为 1, 3, 5, 7, 9 五档,9 表示强度最大,1 为强度最小。
polarity: Polarity
再写一个 Emotions 类来放所有的这些 Word 即对应情感。用一个 self.words
dict,把每种情感的 Word 分开放。
class Emotions:
def __init__(self):
self.words = {
emo: [] for emo in emotions} # {"emotion": [words...]}
with open('/path/to/dict.csv') as f:
self._read_dict(f)
现在给定一个词汇,只需在表中查找,若存在,就到的了情感与对应强度(Word 对象);若不存在,就认为这个词没有感情,直接忽略。
def _find_word(self, w: str) -> List[Word]:
result = []
for emotion, words_of_emotion in self.words.items():
ws = list(map(lambda x: x.word, words_of_emotion))
if w in ws:
result.append(words_of_emotion[ws.index(w)])
return result
而给定一个句子,则先进行分词,取出句子中的前 20 个关键词,做前面的查表分析,将所有得到的关键词情感累加,就得到了句子的情感:
def emotion_count(self, text) -> Emotions:
emotions = empty_emotions()
keywords = jieba.analyse.extract_tags(text, withWeight=True)
for word, weight in keywords:
for w in self._find_word(word):
emotions[w.emotion] += w.intensity * weight
return emotions
如果你不喜欢看文字叙述,也不爱阅读代码,那么可以数学一下。这里我们使用 TF-IDF 算法抽取关键词:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。