让 AI 看懂你的心情,并推荐应景的音乐,以一种简单的实现

一种(简单的)基于心情的音乐推荐系统

项目完整源码:https://github.com/cdfmlr/murecom-verse-1

你什么时候听音乐?快乐的时候,悲伤的时候,兴奋的时候,失落的时候…时时刻刻。

你听什么音乐?欢快的音乐,治愈的音乐,浪漫的音乐,伤感的音乐…因时而异,因心情而定。

传统的推荐系统做了什么?忠实记录你历史上任意时刻的所有听歌记录,拿去和别的用户比较,推荐他们喜欢的歌给你。。。不管你现在什么心情,不管你想听什么类型。这种东西往往不太懂我。

我是个情绪复杂的人,我早上醒来可能任沉浸在梦里,异常低落,我需要 Sasha Sloan;开始做事情要让脑子活过来,我需要 ZUTOMAYO;中午的疲惫下让 Evan Call 调整心情;午后的阳光里让 Bach 带我创作;日落时分的脑子被思念塞满,这时的 Halsey 也许不错;深夜凉爽的霓虹散发迷离的美,这时的爵士与嘻哈绝配,给我来一段 Nujabes。

不同时刻,不同场景,不同心情,给你听不同的歌,这就是一个基于心情的推荐系统。

(我认为时刻与场景作用于心情从而影响人,所以基于心情,而不是场景或时刻等外在因素)

想法

如何基于心情推荐?

首先,我们需要知道一首歌对应什么心情——(也许是)最简单的方法:分析歌名、歌词、以及热心网友的精彩评论。从文本获取情感,可以用比较简单的实现:每个词会对应一些既定的情感,只要当地新华书店购买一本「情感词典」就容易获取一个句子的情感。

然后,我们需要知道用户目前的心情。可以让用户输入一段话、一首诗、一篇文学大作。析文本中的情感,和前面处理歌曲如出一辙。“但我就听个歌诶,你还要让我写作,我讨厌语文,我不是诗人。”那么,对于这种不浪漫的用户,我们假设他情感比较直接,开心还是忧伤,全写在脸上,一看便知——考虑从人像识别情感。

最后,用户的心情与数据库中歌曲的心情一比较,找出最接近的,推荐出来,完成。

设计

想法很简单,但能实现嘛?先看看我们需要做什么。

  • 音乐数据(包括歌词、评论等):我们可以从网易云音乐获取:网易云拥有“丰富”的曲库,“完善”的歌词,“精彩”的评论;
  • 文本 => 情感:前面提到需要一个情感词典,大连理工大学的大佬们做了这样的一个情感词汇本体词典,心怀感激地拿来用:http://ir.dlut.edu.cn/info/1013/1142.htm
  • 图像 => 情感:也有大佬做过这个:Context Based Emotion Recognition using Emotic Dataset,有开源的实现,可以心怀感激地拿来用:https://github.com/Tandon-A/emotic
  • 心情比较、推荐:KNN,这个简单,Sk-learn 随便做,当然也是心怀感激地。

所以说这个系统还是比较简单的。

在开始实现之前,我们还需要讨论一点细节。大连理工情感词典(以下简称 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 目录下,我们从网易云获取了一些数据:

db

和我们上一篇文章获取 Spotify 的数据不同,网易云有个特点——歌单里面曲目多,所以我们获取了不到 1 万张列表,就得到了 20 万首歌曲,100 万条热门评论。

ncm=# select count(*) from playlists;
  8426

ncm=# select count(*) from tracks;
 219038

ncm=# select count(*) from comments;
 1052112

获取数据的过程如下图所示:

ncm

(在 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 算法抽取关键词:

  • TF(term frequency, 词频):字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降: t f ( t , d ) = f t , d ∑ t ′ ∈ d f t ′ , d {\displaystyle \mathrm {tf} (t,d)={\frac {f_{t,d}}{\sum _{t'\in d}{f_{t',d}}}}} tf(t,
  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值