科大讯飞AI大赛(大模型技术方向)
科大讯飞AI大赛(大模型技术方向)
Task1 用AI做带货视频评论分析
目标 获取当前市场情况
一条有效的用户评论 ≈ 一份市场情报。
本项目将教你用AI把散落各处的‘用户吐槽’‘种草心声’自动整理成商品改进清单和带货指南针。
我们将聚焦在讯飞「基于带货视频评论的用户洞察挑战赛」的赛事项目实践。
1 环境
Step2:启动魔搭Notebook!(点击跳转)
git lfs install
git clone https://www.modelscope.cn/datasets/Datawhale/AISumerCamp_video_comments_insights_baseline.git
Task2 读baseline
数据加载与预处理
首先,代码导入了所需的库,并读取视频数据和评论数据:video_data = pd.read_csv("origin_videos_data.csv")
和 comments_data = pd.read_csv("origin_comments_data.csv")
。这里使用的是 Pandas 库来加载CSV文件。接着打印了部分数据以查看样子,比如 video_data.sample(5)
随机抽取了5行视频数据查看(包括video_id
、video_desc
、video_tags
、product_name
等列),而 comments_data.head()
则显示了评论数据的前5行(包括video_id
、comment_id
、comment_text
、以及情感和聚类主题等列)。
然后,代码把视频数据表中的“视频描述”和“视频标签”两列合并成一个新的“text”列:
video_data["text"] = video_data["video_desc"].fillna("") + " " + video_data["video_tags"].fillna("")
这行代码的含义是:如果“视频描述”或“视频标签”有缺失,就用空字符串代替(fillna("")
),然后在两者之间加一个空格拼接成一个长文本。这样做可以把视频的标题、描述和标签当成一个整体文本来分析。为了提高质量,可以考虑在拼接时把标签字符串中的分隔符(比如“;;”)替换为空格,让每个标签成为一个独立词。优化建议: 可以在这一步使用更细致的预处理,例如使用正则表达式去掉不必要的符号,或者将video_tags
里的“;;”替换成空格,确保标签被正确拆分成单个词汇。
商品识别模型
接下来是商品识别(Product Identification)任务,这里使用了机器学习的流水线(pipeline)来构建模型。代码首先导入了结巴分词库和Scikit-learn中的相关模块:
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import SGDClassifier
from sklearn.pipeline import make_pipeline
- 结巴分词(jieba):主要用于把中文文本切分成词语列表。
- TfidfVectorizer:是一个文本向量化器,它会对文本做TF-IDF处理,把每段文字转换成数值特征blog.csdn.net。TF-IDF是一种常用的文本特征提取方法,它会计算每个词在当前文本中的频率(TF)以及该词在所有文本中的逆频率(IDF),从而衡量每个词的重要性blog.csdn.net。
- SGDClassifier:随机梯度下降分类器,是一种线性分类模型scikit-learn.org.cn。它可以实现线性支持向量机(默认loss='hinge'时)或逻辑回归(loss='log'时)等。SGDClassifier通过逐一更新样本的梯度来学习模型,是训练大规模线性模型的一种常用方法scikit-learn.org.cn。
代码通过以下方式构建了一个二步流水线:
product_name_predictor = make_pipeline(
TfidfVectorizer(tokenizer=jieba.lcut, max_features=50),
SGDClassifier()
)
- 第一步:
TfidfVectorizer(tokenizer=jieba.lcut, max_features=50)
。这一步会对文本进行切词和提取TF-IDF特征。其中tokenizer=jieba.lcut
表示使用结巴的lcut
方法来把文本切成词语列表;max_features=50
表示最多只保留50个最重要的词作为特征。简而言之,这一步就像“从文本中选出50个最有代表性的词”scikit-learn.cnblog.csdn.net。 - 第二步:
SGDClassifier()
,使用随机梯度下降分类器对特征进行训练。也就是说,模型会学到一个权重向量,根据那50个词出现的频率来判断视频推广的商品名称。就像“根据视频中出现的关键词来猜视频里卖的是什么商品”。
接着用已标注的样本训练模型:
product_name_predictor.fit(
video_data[~video_data["product_name"].isnull()]["text"],
video_data[~video_data["product_name"].isnull()]["product_name"],
)
其中 video_data[~video_data["product_name"].isnull()]
表示只选择那些product_name
非空的行作为训练样本。换句话说,只用已有商品标签的视频来训练模型。
训练完成后,代码用训练好的模型对所有视频的文本进行预测,并将预测结果覆盖写回原来的 video_data["product_name"]
列:
video_data["product_name"] = product_name_predictor.predict(video_data["text"])
这一步会把模型预测的商品名称(比如“Xfaiyx Smart Translator”等)填入所有视频数据中,包括之前标注为空的测试集部分。
说明与建议: 以上商品识别流程的思路是“先用文本提取特征,再用分类器预测商品”。但这里有几个地方可以优化:
- 词汇量(max_features)过小:原代码只保留了50个词,这在文本信息丰富时可能不够。建议增大特征数量,比如设置为几千(
max_features=1000
或更多),同时使用ngram_range=(1,2)
来包括二词组合,这样可以捕捉更多信息scikit-learn.cn。 - 不同语言的处理:样本中有英文、法文、德文、日文等内容,用结巴分词可能不适合英文。可以考虑只对中文进行jieba分词,对英文则使用简单分隔符或英文分词库(如NLTK)处理,或者直接使用
TfidfVectorizer
默认的英文分词,避免用一个分词器处理所有语言。 - 其他模型尝试:SGDClassifier效果可能一般,可以尝试其它分类器如
LogisticRegression
(逻辑回归)、LinearSVC
(线性SVM)甚至集成模型(如随机森林RandomForestClassifier
或梯度提升GradientBoostingClassifier
)。这些模型在文本分类中也很常用,可能带来性能提升。同时可以对模型的超参数进行网格搜索调参scikit-learn.org.cn。 - 保留原始标注:目前预测结果会直接覆盖原有标注,可能会丢失原始信息。实际比赛中,如果要比较标注和预测的准确率,建议先将预测写入新列,比如
predicted_product_name
,以免覆盖原始数据。
评论情感分析模型
接下来的代码部分对评论数据进行多维度情感分类,共四个标签:sentiment_category
(情感类别)、user_scenario
(用户场景)、user_question
(用户问题)、user_suggestion
(用户建议)。每个标签都是分类问题(sentiment_category
有 5 类,后三个标签是二分类)。代码使用了一个循环,对这四个标签分别训练一个模型:
for col in ['sentiment_category', 'user_scenario', 'user_question', 'user_suggestion']:
predictor = make_pipeline(
TfidfVectorizer(tokenizer=jieba.lcut),
SGDClassifier()
)
predictor.fit(
comments_data[~comments_data[col].isnull()]["comment_text"],
comments_data[~comments_data[col].isnull()][col],
)
comments_data[col] = predictor.predict(comments_data["comment_text"])
- 对每个标签
col
,首先创建一个流水线:文本向量化 + SGD分类(与商品识别类似,但是这里没有限制max_features
,默认提取所有词)。 - 只用标签不为空的评论(训练集)来训练这个分类器。
- 再用训练好的模型预测所有评论的标签,并将结果填回相应的列。
逐句解释:
for col in [...]
: 循环每一个标签名。TfidfVectorizer(tokenizer=jieba.lcut)
: 同样使用结巴分词提取TF-IDF特征。由于注释中没有设置max_features
,默认会保留所有词(实际上是由内存和词表自动决定)。SGDClassifier()
: 使用同样的随机梯度下降分类器。默认loss='hinge',相当于线性SVM。.fit(...)
: 训练分类器。左边的输入是comment_text
文本,右边是对应标签列的值(比如情感类别1~5,或0/1)。通过~comments_data[col].isnull()
过滤掉标签为空的行。.predict(...)
: 预测所有评论的标签。这里将预测结果赋值给原数据的相应列,所以原先空的测试集标签会被填上预测值。
说明与建议: 这种做法是典型的“词袋+线性分类”方案。但它也有改进空间:
- 特征工程:目前只用了一种特征(TF-IDF)。可以尝试加上
ngram_range=(1,2)
提取1-2元组词特征,或者使用停用词列表去除无意义词。另外,可以考虑将视频层面的信息(如对应的视频product_name
或video_desc
)作为特征的一部分,帮助分类器更好地理解评论内容。 - 不同模型比较:对于多类别的情感分类,除了SGD(线性SVM),可以试试逻辑回归(
LogisticRegression
)或多层感知机(MLPClassifier
)。二分类的场景、问题、建议预测,也可以尝试如RandomForestClassifier
、GradientBoostingClassifier
等模型,根据交叉验证结果选择性能最好的。 - 数据不平衡与正则化:情感类别有“正面、负面、中性”等5类,样本可能不平衡(如正面评论更多)。可以在模型中设置
class_weight='balanced'
或对样本做过采样/欠采样等操作来改善。此外,SGDClassifier默认正则化为L2,也可以调整为L1或ElasticNet试试。 - 训练-验证分离:当前代码直接用全部标注数据训练,没有留出验证集。建议在优化时使用交叉验证(如
cross_val_score
)评估模型效果,避免过拟合。
评论聚类
最后的部分是对评论进行聚类,并提取每个簇的主题词。根据任务要求,需要对满足不同条件的评论分别聚类(正面、负面、用户场景、问题、建议),并找出每个簇的关键词。基线代码依次用了五个类似的块,只是过滤条件和赋值列名不同。例如,对“正面倾向”评论(情感标签为1或3)执行了:
kmeans_predictor = make_pipeline(
TfidfVectorizer(tokenizer=jieba.lcut),
KMeans(n_clusters=2)
)
kmeans_predictor.fit(comments_data[comments_data["sentiment_category"].isin([1, 3])]["comment_text"])
kmeans_cluster_label = kmeans_predictor.predict(comments_data[comments_data["sentiment_category"].isin([1, 3])]["comment_text"])
kmeans_top_word = []
tfidf_vectorizer = kmeans_predictor.named_steps['tfidfvectorizer']
kmeans_model = kmeans_predictor.named_steps['kmeans']
feature_names = tfidf_vectorizer.get_feature_names_out()
cluster_centers = kmeans_model.cluster_centers_
for i in range(kmeans_model.n_clusters):
top_feature_indices = cluster_centers[i].argsort()[::-1]
top_word = ' '.join([feature_names[idx] for idx in top_feature_indices[:top_n_words]])
kmeans_top_word.append(top_word)
comments_data.loc[comments_data["sentiment_category"].isin([1, 3]), "positive_cluster_theme"] = [kmeans_top_word[x] for x in kmeans_cluster_label]
- 首先构建了一个TF-IDF + KMeans流水线,将正面评论的文本输入KMeans进行聚类,其中
n_clusters=2
表示划分成2个簇。 - 使用
.fit()
在符合条件的评论集合上做聚类。然后用.predict()
得到每个评论所在的簇标签(这里也是在正面评论集合上)。 - 接下来,通过
kmeans_model.cluster_centers_
获取聚类中心的TF-IDF权重向量,然后找出每个中心向量中值最大的前top_n_words
个特征索引。通过把这些索引映射回词语(feature_names[idx]
),合并成一句话top_word
,得到每个簇的“主题词”。 - 最后,根据聚类标签将这些主题词赋给对应评论的“positive_cluster_theme”列。形式是:每个评论对应的簇编号x,取
kmeans_top_word[x]
作为那条评论所在簇的主题词。
基线针对负面(sentiment_category
为2或3)、用户场景(user_scenario
为1)、疑问(user_question
为1)、建议(user_suggestion
为1)分别重复了类似的过程,每次都取n_clusters=2
,并赋给不同的列(negative_cluster_theme
、scenario_cluster_theme
、question_cluster_theme
、suggestion_cluster_theme
)。
说明与建议: 这里聚类的思路是“先筛选出相关评论,再做KMeans”。需要注意:
- 聚类数目过少:题目要求是聚类数在5~8之间,而基线代码只用了2簇,这明显偏少,难以发现更多不同主题。实际可以尝试
n_clusters=5
或6等,然后使用轮廓系数(Silhouette Coefficient)等方法评估最佳簇数[zhuanlan.zhihu.com](https://zhuanlan.zhihu.com/p/530944459#:~:text=2 Silhouette Coefficient Index 所谓轮廓系数(Silhouette,1],它本质上衡量的是每个样本点到其簇内样本的距离与其最近簇结构之间距离的比值。 如果该比值越)。 - 主题词提取:直接用聚类中心的TF-IDF权重挑词,这种方法是一个常见做法[scikit-learn.cn](https://scikit-learn.cn/stable/auto_examples/text/plot_document_clustering.html#:~:text=由于 TfidfVectorizer 可以反转,我们可以识别聚类中心,这提供了 每个聚类 中最具影响力的词的直观理解。有关与,每个目标类别 最具预测性的词的比较,请参阅示例脚本 使用稀疏特征对文本文档进行分类。)。可以优化的是,将
top_n_words
设成较大的值,比如10或20,方便覆盖更多关键词;或者使用mini_df
和max_df
参数在TF-IDF中过滤掉出现太少或太多(几乎所有文档都有)的词,避免得到无意义的词。 - 分词和预处理:同样地,如果评论包含多种语言和符号,切分词的策略也需要优化。可以先对评论文本做清洗(去掉HTML标签或特殊符号),再分词。对于英文评论,可能不需要结巴,直接用英文分词或
TfidfVectorizer
的默认行为。 - 其他聚类算法:KMeans 假设簇大小相近且形状球状,对文本有时候表现一般。可以尝试其他算法,如层次聚类(AgglomerativeClustering)、DBSCAN、或主题模型(LDA)来发现评论主题。此外,还可以使用更先进的文本聚类方法,比如基于词嵌入和神经网络的聚类模型(如BERTopic),效果可能更好。
- 包括测试集:题目要求聚类时使用训练集和测试集的所有满足条件的评论样本。基线代码确实对整个
comments_data
(含测试集)用.predict()
,这符合要求。但评估时只计算那些情感和场景都正确的评论。建议按说明严格包含训练和测试数据做聚类。
优化后的完整代码
下面给出综合上述优化建议后的示例代码。我们在向量化时使用了更多特征(包括1-2元词),对分类器加入了常见的模型选择与参数设置,同时在每一步加了注释说明。可以根据实际需要进一步调整参数,并确保执行环境中已安装相应库。代码假设使用了原始数据文件名与目录结构一致,并且代码是可重复执行的。
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import SGDClassifier, LogisticRegression
from sklearn.svm import LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.cluster import KMeans
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import classification_report
import jieba
# 读取数据
video_data = pd.read_csv("origin_videos_data.csv")
comments_data = pd.read_csv("origin_comments_data.csv")
# 预处理视频文本:拼接描述和标签,清洗标签分隔符
video_data["video_desc"] = video_data["video_desc"].fillna("")
video_data["video_tags"] = video_data["video_tags"].fillna("").str.replace(";;", " ")
video_data["text"] = video_data["video_desc"] + " " + video_data["video_tags"]
# 商品识别任务:提取文本特征并分类
# 使用更丰富的TF-IDF:包括1-2元词,特征数量设大一些
product_vectorizer = TfidfVectorizer(
tokenizer=jieba.lcut,
ngram_range=(1, 2),
max_features=1000,
norm='l2'
)
# 尝试多个分类器(这里示例使用线性SVC)
product_classifier = LinearSVC()
product_pipeline = make_pipeline(product_vectorizer, product_classifier)
# 训练:仅使用有标注的商品数据
train_idx = ~video_data["product_name"].isnull()
X_train_prod = video_data.loc[train_idx, "text"]
y_train_prod = video_data.loc[train_idx, "product_name"]
product_pipeline.fit(X_train_prod, y_train_prod)
# 预测:覆盖整个数据集的商品名称
video_data["product_name"] = product_pipeline.predict(video_data["text"])
# 评论情感和相关属性分类任务
# 定义用于分类的TF-IDF向量器(中英文混合处理:这里继续用结巴分词,但对其他语言可能效果一般)
comment_vectorizer = TfidfVectorizer(tokenizer=jieba.lcut, ngram_range=(1, 2), max_features=2000)
# 我们可以对每个任务选择不同的分类器或调参,这里示例使用Logistic回归(更稳健)
label_classifiers = {
'sentiment_category': LogisticRegression(max_iter=1000, class_weight='balanced'),
'user_scenario': LogisticRegression(),
'user_question': LogisticRegression(),
'user_suggestion': LogisticRegression(),
}
for col, classifier in label_classifiers.items():
# 取出有标注的样本进行训练
mask = ~comments_data[col].isnull()
X_train = comments_data.loc[mask, "comment_text"]
y_train = comments_data.loc[mask, col].astype(int)
# 对文本特征做拟合转换
X_feat = comment_vectorizer.fit_transform(X_train)
# 训练分类器
classifier.fit(X_feat, y_train)
# 对所有评论进行预测(使用相同的向量器transform)
X_all_feat = comment_vectorizer.transform(comments_data["comment_text"])
comments_data[col] = classifier.predict(X_all_feat)
# 评论聚类任务
# 函数:给定过滤条件和目标列,进行聚类并写入主题词
def cluster_and_assign(mask, feature_col, n_clusters, theme_col):
# 1. 向量化
X = comment_vectorizer.fit_transform(comments_data.loc[mask, feature_col])
# 2. 训练KMeans
kmeans = KMeans(n_clusters=n_clusters, random_state=42)
kmeans.fit(X)
labels = kmeans.labels_
# 3. 提取簇中心的主题词
terms = comment_vectorizer.get_feature_names_out()
centers = kmeans.cluster_centers_
top_words = []
for center in centers:
top_indices = center.argsort()[::-1][:10] # 取前10个词
words = [terms[idx] for idx in top_indices]
top_words.append(" ".join(words))
# 4. 写回结果:为每条评论赋对应簇的主题词
comments_data.loc[mask, theme_col] = [top_words[label] for label in labels]
# 应用到各个条件
# 正面情感:sentiment_category=1或3
mask_pos = comments_data["sentiment_category"].isin([1, 3])
cluster_and_assign(mask_pos, "comment_text", n_clusters=5, theme_col="positive_cluster_theme")
# 负面情感:sentiment_category=2或3
mask_neg = comments_data["sentiment_category"].isin([2, 3])
cluster_and_assign(mask_neg, "comment_text", n_clusters=5, theme_col="negative_cluster_theme")
# 用户场景:user_scenario=1
mask_scn = comments_data["user_scenario"] == 1
cluster_and_assign(mask_scn, "comment_text", n_clusters=5, theme_col="scenario_cluster_theme")
# 用户疑问:user_question=1
mask_q = comments_data["user_question"] == 1
cluster_and_assign(mask_q, "comment_text", n_clusters=5, theme_col="question_cluster_theme")
# 用户建议:user_suggestion=1
mask_sug = comments_data["user_suggestion"] == 1
cluster_and_assign(mask_sug, "comment_text", n_clusters=5, theme_col="suggestion_cluster_theme")
# 保存结果
video_data[["video_id", "product_name"]].to_csv("submit/submit_videos.csv", index=False)
comments_data[[
"video_id", "comment_id", "sentiment_category",
"user_scenario", "user_question", "user_suggestion",
"positive_cluster_theme", "negative_cluster_theme",
"scenario_cluster_theme", "question_cluster_theme",
"suggestion_cluster_theme"
]].to_csv("submit/submit_comments.csv", index=False)
注释说明:
- 我们扩大了
max_features
和ngram_range
,以提高TF-IDF特征的表达能力scikit-learn.cnblog.csdn.net。 - 对分类器使用了
LogisticRegression
(可选),并在情感分类中加入了class_weight='balanced'
来应对类别不平衡。scikit-learn.org.cn - 聚类时将簇数设为5(在5~8之间)以满足题目要求,并提取了前10个主题词。
- 代码保证可重复运行,只要文件路径正确,输出结果就是
submit_videos.csv
和submit_comments.csv
,可用于提交和验证。
以上优化和重构旨在增强模型的准确性和鲁棒性。例如,更大词汇量、更丰富的模型选择及更多的调参空间都可能带来更好的性能。同时,这些改进也符合常见的文本分类和聚类最佳实践scikit-learn.cnscikit-learn.org.cn。
Task2.2
优化一
在原 Notebook 中加入了以下内容:
- 清洗
comment_text
字段。 - 用
TfidfVectorizer + RandomForestClassifier
替代原先的简单分类器。 - 自动输出
classification_report
以便查看分类效果。
优化二
✅ 使用 MultiOutputClassifier
实现了同时预测
sentiment_category
user_scenario
user_question
user_suggestion
✅ 模型结构:Tfidf + GradientBoosting
✅ 自动输出每个标签的 classification_report