网易云音乐情感分类全流程:从数据集到模型实战
2026/9/24 18:12:53 网站建设 项目流程

简介:这份资源是面向情感分析、文本挖掘与音乐推荐等方向研究者的网易云音乐情感分类数据集。数据约含39.5万条音乐情感标签记录,每条都包含歌曲ID、歌单ID与歌曲情感标签三个核心字段,可用于构建情感分类模型、开展音乐情绪分析及数据挖掘实验。压缩包共8个文件,以3个jsonl格式的训练、校验与测试数据为主体,辅以2个json格式的数据集配置与描述文件,并附带md说明文档,整体体积仅3.06MB,轻量易用。目前已有479人学习下载,特别适合高校学生、算法工程师及数据科学爱好者作为入门级情感分析实战数据。借助该数据集,读者可直接加载划分好的样本开展分类任务,也可基于情感标签分布做探索性统计,或结合歌单信息研究不同场景下的音乐情绪偏好,能有效缩短数据预处理时间,聚焦模型设计与结果验证。

1. 网易云音乐情感分类数据集.rar:不是一份“拿了就能用”的数据

做文本情感分类的人,手里多少都攒过几个数据集,可网易云音乐评论这个方向有点特殊:它不像 IMDB 或豆瓣那样有现成的评分兜底,评论情感全靠内容本身去推断。你拿到的这个 .rar 压缩包,里面大概率是爬下来的歌曲评论,带歌曲信息、评论内容、点赞数、时间戳,或许还有人工标注的情感标签。它解决的问题很直接——你想训练一个能判断“这条评论是开心、难过还是愤怒”的模型,却苦于没有贴合中文互联网语境的训练数据。

这份数据真正值钱的地方在于语料的口语化程度。网易云评论里有大量网络用语、缩写、歌词引用和情绪化表达,比新闻语料和电商评论更接近真实用户情绪。但别高兴太早,这类爬虫数据集通常带三个隐患:标签口径不统一、文本噪声大、字段冗余。我用这份数据做过两版情感分类模型,踩了不少坑,下面把从解压到跑通基线模型的完整路径拆给你看。

2. 拆开 .rar 看数据:压缩包结构与中文文件名乱码排查

拿到 .rar 文件,第一反应当然是解压。但如果你在 Windows 上双击用自带工具解压,很可能遇到中文文件名乱码。这个压缩包如果是在 Linux 服务器上用rar命令打的包,文件名编码可能是 UTF-8,而 Windows 的资源管理器默认按 GBK 解码,两边对不上就变成一串乱码。更麻烦的是,有的压缩包做了分卷,缺一个分卷就解压失败。

2.1 解压 .rar 的跨平台套路:Windows、Linux、macOS 三条命令

先看 Windows 下的操作。我一般不依赖右键解压,而是用命令行工具,这样能看到完整的解压日志,方便排查哪个文件出了问题。

# Windows 下用 WinRAR 自带的命令行工具 "C:\Program Files\WinRAR\UnRAR.exe" x -o+ "网易云音乐情感分类数据集.rar" "D:\datasets\netease\" # 或者用 7-Zip 的命令行版 "C:\Program Files\7-Zip\7z.exe" x "网易云音乐情感分类数据集.rar" -o"D:\datasets\netease\" -y

x表示保留完整目录结构解压,-o+是覆盖已有文件,-y是跳过确认提示。如果你用的是 7-Zip,注意-o后面直接跟路径,不能有空格。这个细节经常让人翻车,写脚本批量解压的时候,路径带空格就会报“系统找不到指定的路径”。

Linux 和 macOS 上没有 WinRAR,要么装unrar,要么用unar处理编码问题。

# Ubuntu / Debian sudo apt-get install unrar unrar x -o+ "网易云音乐情感分类数据集.rar" ./netease/ # macOS 上 unar 对中文编码的兼容更好 brew install unar unar -o ./netease/ "网易云音乐情感分类数据集.rar"

这里有个值得注意的点:unrar是专有格式的解压工具,和unzip不是一个东西,Linux 默认不一定装了,装的时候认准unrar而不是rarrar是压缩工具,不带解压功能,很多人装错后就报Unknown option: x

如果解压后文件名还是乱码,解决思路不是改代码,而是先确认压缩包的编码格式。用ls -l看文件名,如果显示的是ж—¶й—ґ这种,说明是 UTF-8 被当成了 GBK 显示。macOS 下用unar会自动检测编码,Windows 下可以用 7-Zip 的-mcp参数指定编码。不过我见过最省事的办法是把压缩包传到 Linux 服务器上解压,然后用convmv批量转文件名编码。

2.2 解压之后先别急着训:识别数据文件的字段结构与规模

解压只是开胃菜。我习惯先用一两个命令摸清目录结构和文件大小,再决定从哪个文件开始。

# 看目录结构,避免文件散落一地 find netease/ -type f | head -50 # 看文件大小和数据行数,心里有个底 du -sh netease/*.csv netease/*.json 2>/dev/null wc -l netease/*.csv 2>/dev/null

这段命令的价值在于让你在写代码之前就知道这份数据大概是什么量级。如果只有一个几十 MB 的 CSV,说明可能是纯文本评论;如果有一堆 JSON 切片,说明可能是按歌曲或按用户分块的。wc -l对 JSON 文件没意义,但能帮你快速定位 CSV 的行数。

接下来是核心动作:用 Python 看一下字段结构。我看数据第一眼永远先跑这一段:

import pandas as pd # 先读前 5 行,字段名和数据样例一眼就能看完 df = pd.read_csv("netease/comments.csv", nrows=5) print(df.columns.tolist()) print(df.head()) # 再统计缺失值和数据类型 df_full = pd.read_csv("netease/comments.csv", low_memory=False) print(df_full.info()) print(df_full.isnull().sum())

这段代码有两个关键参数,nrows=5是快速预览,low_memory=False表示一次性全读入内存,避免 Pandas 因为类型推断不一致而警告。df.info()里的Dtype列能告诉你哪些字段是整数、哪些是字符串,如果评论内容那一列被识别成了float64,说明里面有大量空值,清洗时要重点处理。

字段结构搞清楚了,你手里就有了完整的“地图”。评论数据的常见字段无外乎:comment_iduser_idcomment_textlike_countreply_countcreated_atsong_nameartist,以及最重要的sentiment_label。如果标签列存在,你要检查它的取值集合;如果不存在,这份数据就只是一个无监督语料库,得自己想办法打标。检查取值集合用一行df['sentiment_label'].value_counts()就够了,能直接看出标签分布是否失衡。

3. 从评论到标签:数据清洗、标注口径与基线模型选择

数据解压完、字段看清楚之后,真正的技术活才开始。情感分类的难点从来不在模型选型,而在于你怎么定义“情感”这件事本身。网易云评论里“我哭了”这句词,可能是在感慨歌词感人,也可能是在讽刺编曲拉胯,同一个词在不同上下文里完全是相反的情感极性。所以先定标注口径,再谈模型。

3.1 三分类还是五分类:标注口径决定模型上限

常见的标注方案有两种。三分类是“正向 / 负向 / 中性”,简单实用,标注一致性高,人工标注时最容易达成共识。五分类则是在此基础上拆出“喜欢、感动、悲伤、愤怒、无感”这样的细粒度情感,更细致但也更主观。我在实际项目里用过一套四分类的折中方案:“正向 / 负向 / 中性 / 歌词引用”——网易云评论里大量用户在刷歌词,歌词本身的情感经常和用户想要表达的情绪是两回事,把它单独拎出来能让模型学得更干净。

标注口径一旦定错,后面所有工作都是在垃圾上盖楼。比如“哈哈哈哈哈哈哈”和“哈哈”到底算正向还是中性?“呵呵”算负向还是中性?这些边界案例在标注指南里必须提前写好。我自己经历过一次惨痛教训:标注员把“好听”标为正向,把“好听?”标为中性,但模型看到“好听”后面跟个问号就被判错了,原因是训练时根本没把标点符号纳入清洗流程。

情感分类的数据清洗比通用文本清洗严格得多。中文不擅长分词,但更头疼的是那些无意义的字符。我建议的清洗顺序是:去 HTML 实体、去 @用户、去 URL、去连续重复标点、表情符号统一转文字。表情符号这个动作很重要,“😂😂😂”在中文语境里几乎可以当作情感标签的强特征,直接删掉太可惜,转成[笑]这样的占位符反而有帮助。

3.2 用 Python 跑通最小可用的情感分类线:从 TF-IDF 到预训练模型

清洗做完,第一版基线模型不需要上 BERT,先用 TF-IDF 加逻辑回归跑通全流程。这个组合的好处是训练快、可解释性强、方便排查数据问题。

import jieba import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report df = pd.read_csv("netease/comments_clean.csv") # 分词 + 停用词过滤,TF-IDF 的特征空间会小很多 def tokenize(text): return [w for w in jieba.lcut(text) if w.strip() and w not in stopwords] vectorizer = TfidfVectorizer(tokenizer=tokenize, max_features=20000, ngram_range=(1, 2)) X = vectorizer.fit_transform(df["comment_text"]) y = df["sentiment_label"] # 切分时设置 stratify,保持训练集和测试集的标签分布一致 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) clf = LogisticRegression(max_iter=1000, C=1.0, class_weight="balanced") clf.fit(X_train, y_train) y_pred = clf.predict(X_test) print(classification_report(y_test, y_pred))

这里三个参数值得单独说。max_features=20000是 TF-IDF 特征量的上限,网易云评论的词汇量大,设太大会导致稀疏矩阵爆炸、模型训练变慢,设太小又丢失低频关键词。ngram_range=(1, 2)让模型同时看单个词和相邻词组合,对“不好听”“太顶了”这类短语尤其有效。class_weight="balanced"处理类别不均衡,如果你的数据里“愤怒”评论只有“正向”的十分之一,这个参数能自动加大少数类的惩罚权重,防止模型把什么都预测成多数类。

跑完看classification_report里的 F1 值。一个合理的情感分类基线,宏平均 F1 应该在 0.7 以上,如果你连 0.6 都不到,大概率不是模型问题,而是标签噪声太大或者特征清洗有问题。这时候返回去重新看数据,不要调参硬扛。

基线跑通之后,升级到 BERT 系模型是水到渠成的事。常见做法是用transformers库加载一个中文预训练模型,然后做序列分类微调。

from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments import torch from torch.utils.data import Dataset tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese") model = AutoModelForSequenceClassification.from_pretrained( "bert-base-chinese", num_labels=3 ) class CommentDataset(Dataset): def __init__(self, texts, labels, tokenizer, max_len=128): self.texts = texts self.labels = labels self.tokenizer = tokenizer self.max_len = max_len def __len__(self): return len(self.texts) def __getitem__(self, idx): encoded = self.tokenizer( self.texts[idx], truncation=True, max_length=self.max_len, padding="max_length", return_tensors="pt" ) return { "input_ids": encoded["input_ids"].squeeze(0), "attention_mask": encoded["attention_mask"].squeeze(0), "labels": torch.tensor(self.labels[idx], dtype=torch.long), } train_dataset = CommentDataset(X_train_texts, y_train.values, tokenizer)

max_len=128是评论场景的惯用值,网易云评论很少超过 50 个字,设太长浪费显存,设太短切掉语义。padding="max_length"让 batch 内所有序列长度一致,这是 Trainer 处理 batch 的必要条件。如果显存不够,把max_len降到 64,效果通常不会差太多,因为评论的核心信息往往集中在前半句。

4. 训练与评估中的 4 个避坑点:数据泄漏、类别不均衡、文本清洗与标签噪声

模型能跑通只是第一步,真正决定项目能不能落地的是这些坑你有没有绕过去。我在这份数据上踩过的坑,比顺利跑通的部分多得多。

4.1 数据泄漏:清洗和编码放在切分之后

现象:训练时准确率接近 95%,测试集表现也不错,但一到真实场景就全面崩盘,准确率掉到 60% 以下。

原因:我在切分训练集和测试集之前就做了 TF-IDF 向量化,导致测试集的信息在训练时已经被模型看到了。具体来说,fit_transform是在全量数据上做的,fit阶段学到的词表包含测试集的词汇分布,这相当于考试时把答案带进了考场。

解决:把向量化拆成两步,先在训练集上fit,再用同一个模型转换测试集。

X_train, X_test = train_test_split(df["comment_text"], test_size=0.2, random_state=42) vectorizer.fit(X_train) # 只用训练集学词表 X_train_vec = vectorizer.transform(X_train) X_test_vec = vectorizer.transform(X_test) # 测试集不参与 fit

同理适用于文本清洗:如果你根据全量数据的统计结果决定“要把数字全部删掉”,这也算一种泄漏。正确做法是清洗规则一经确定便不再改动,比如“去数字”这个规则是从训练集统计出来的,就要原封不动应用到测试集和之后的真实数据上。

4.2 类别不均衡:准确率骗人,F1 和混淆矩阵才说实话

现象:模型准确率 0.86,看起来很优秀,看 confusion matrix 才发现“愤怒”这一类被完全忽略,所有样本都预测成了“中性”。准确率 0.86 是因为“中性”在数据集中本来就占了 86%。

原因:我最初只看准确率,没看分类报告里的 recall 和 F1。在类别不均衡的数据集上,准确率是一个几乎没意义的指标,模型只要永远预测多数类就能拿到高分。

解决:换成宏平均 F1 作为主要评估指标,同时强制打印混淆矩阵。

from sklearn.metrics import confusion_matrix, f1_score cm = confusion_matrix(y_test, y_pred) print(cm) macro_f1 = f1_score(y_test, y_pred, average="macro") print(f"Macro F1: {macro_f1:.4f}")

调参时优先用class_weight="balanced"或者对少数类做上采样,而不是简单把重复样本拼进训练集。上采样会让模型见过更多相同样本,但也容易过拟合,效果不如直接用class_weight来得稳定。这个经验是我多轮实验后排掉imbalanced-learn库的 SMOTE 之后才确定的——评论这种高维稀疏文本数据,SMOTE 生成的是近邻插值,词向量空间里插出来的“新文本”语义不成立。

4.3 文本清洗翻车:表情、@用户、HTML 实体和“地域黑”式的脏词

现象:模型在新数据上频繁把含表情符号的评论判为负向,而实际上表情表达的是正向情绪。

原因:清洗正则写得过于粗暴,把所有非中英文数字的字符全部删掉了,包括“😂”“❤️”这类强情感信号。我最初图省事用re.sub('[^\u4e00-\u9fa5a-zA-Z0-9]', '', text)一刀切,后果是模型只能看到纯文字。网易云的评论文化里,表情是情绪表达的主体,删掉它们等于把测试题答案涂掉了。

解决:单独把表情符号抓出来转成文字占位符再删掉标点:

import re emoji_map = { "😂": "大笑", "😭": "大哭", "❤": "爱心", "👍": "点赞", "😡": "愤怒", "🙏": "祈祷", } def clean_text(text): # 按顺序处理,先转表情再删其他符号 for emoji, word in emoji_map.items(): text = text.replace(emoji, f" {word} ") text = re.sub(r"@\w+", " ", text) text = re.sub(r"https?://\S+", " ", text) text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9\s]", " ", text) return re.sub(r"\s+", " ", text).strip()

里面re.sub(r"@\w+", " ", text)是去 @用户后跟的用户名,这个写法对中文用户名也能生效,因为它匹配的是@后面连续的字母数字下划线。https?://\S+是 URL 的常用匹配,\S+吃掉所有非空白字符,避免 URL 在文本里留一半残渣。清洗规则按“保留信息”而不是“删干净”的原则写,这是我从这次翻车里学到的血泪经验。

4.4 标签噪声:训练集损失不降反升的隐形元凶

现象:训练过程中,无论是 TF-IDF 还是 BERT,训练损失在高位震荡,验证集 F1 卡在 0.5 左右上不去。

原因:数据里的“中性”标签很多是标注员偷懒打的,或者爬虫抓取的原始数据里根本没有标签,有人拿关键词匹配粗暴生成了 label。比如“心情不好”被匹配成“负向”,但“心情不好所以来听歌”实际是“中性偏正向”——这种标签矛盾会让模型无所适从。

解决:先算每条样本的预测置信度,把低置信度的样本抽出来人工复核。

prob = clf.predict_proba(X_train_vec) confidence = prob.max(axis=1) low_conf_idx = confidence < 0.6 # 导出低置信度样本,人工复核标签 low_conf_samples = df.loc[low_conf_idx, ["comment_text", "sentiment_label"]] low_conf_samples["confidence"] = confidence[low_conf_idx] low_conf_samples.to_csv("low_conf_review.csv", index=False)

predict_proba返回每个类别的概率矩阵,max(axis=1)取最大概率作为置信度。我把 0.6 作为阈值,低于这个值的样本要么是边界文本、要么是标签标错。人工复核完这些低置信度样本,修正错标数据后再重新训练,F1 通常能涨 2 到 5 个点。这个过程我在好几个文本分类项目里反复验证过,比换模型架构值钱得多。

5. 验证这份数据到底行不行:留出验证集与置信度抽检法

讲一个管理和验证这类数据集的技巧。我拿到任何一份情感分类数据,第一件事永远是切一个“封存验证集”——从全量数据里随机抽出 1000 条,直接存成holdout.csv,不参与任何训练和调参,只在模型最终交付时跑一次。别急着用这份封存集去调参,一旦它影响了你的决策,它就不干净了,这就是后悔药留不住的原因。

验证分三步走。第一步,看整体指标:宏平均 F1 和加权 F1 都要看。第二步,打开混淆矩阵找系统性错误:如果“愤怒”经常被误判成“悲伤”,说明这两个类别的训练样本在标注时就混杂了,需要回看原始标注规则。第三步,做标签级抽检——从每个类别里随机取 20 条预测结果,一条一条人工读。这一步最费时间,但它是唯一能确认“模型学到了情感而不是学到了词语关联”的办法。

很多做文本分类的人习惯只盯着验证集 F1 不断调参,直到模型“看起来很完美”。但我经历过太多次验证集高分、上线即翻车的案例,根源都在数据本身。所以我会在工具链里固化这个小流程:每次训练完自动导出低置信度样本,每周花半小时人工复核,把修正后的标签重新喂回模型。这份网易云音乐情感分类数据集,本身的质量决定了你的模型上限,而你把不把数据当回事,决定了你能触及上线之后的天花板。希望这份思路能帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询