数据来源:B站观察者网、观视频工作室、央视新闻、小央视频、环球网、环球时报2020年1月到5月所有与新冠疫情相关的投稿视频的弹幕与评论,爬取视频共计13902个,爬取弹幕共计825869条,爬取评论共计16901540条。
朴素贝叶斯分类是一种常用的有监督机器学习算法,我使用的是scikit-learn提供的朴素贝叶斯分类模型,sklearn提供两种朴素贝叶斯分类,一种是高斯朴素贝叶斯,一种是多项式朴素贝叶斯,其中,后者常用于文本的分类。
因为需要对中文进行贝叶斯分类,所以在训练模型之前要对中文文本进行分词并去掉停用词,这里我是用的分词工具是jieba,停用词表是从网上随便搜的。
因为贝叶斯是有监督的机器学习算法,所以我需要手动标记出一个训练集,这里我选择随机抽取500条弹幕和500条评论进行手动标记(可以看到相对整体数据,我使用的训练集很小,但是似乎没有更好的方案)。
为了将分词后的文本数据进行合理的量化,这里使用sklearn提供的TfidfVectorizer工具计算词频-逆文档频率。
为了方便进行分类,我将所有数据转化为了pandas的DataFrame对象,并使用python内置的pickle模块储存到硬盘上,需要使用时直接从硬盘加载(代码中的dms和cts对象)。
dms和cts的content列分别表示弹幕和评论的分词后的结果。
我将训练集(500条弹幕+500条评论)储存为了csv文件,分类时加载并用于模型训练。
对于最后的训练结果,我同样将其使用pickle模块储存至硬盘。
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
from datetime import datetime
import pandas as pd
import csv
import jieba
import pickle
def __apply_jiebe(ser: pd.Series) -> pd.Series:
tmp = jieba.cut(ser['content'])
res = ''
for i in tmp:
if i not in stop_words:
res = i if res == '' else res + ' ' + i
ser['content'] = res
return ser
def __load_stop_words():
f = open(file='./stop_words.txt', mode='r', encoding='utf-8')
res = set()
while lin := f.readline():
res.add(lin[:-1])
return res
def load_train_data(file1: str, file2: str) -> (pd.Series, pd.Series):
w1 = csv.reader(open(file=file1, mode='r', encoding='utf-8', newline=''))
w2 = csv.reader(open(file=file2, mode='r', encoding='utf-8', newline=''))
content = []
in_out = []
for i in w1:
tmp = jieba.cut(i[0])
res = ''
for j in tmp:
if j not in stop_words:
res = j if res == '' else res + ' ' + j
content.append(res)
in_out.append(int(i[1]))
for i in w2:
tmp = jieba.cut(i[0])
res = ''
for j in tmp:
if j not in stop_words:
res = j if res == '' else res + ' ' + j
content.append(res)
in_out.append(int(i[1]))
return pd.Series(content), pd.Series(in_out)
stop_words = __load_stop_words()
if __name__ == '__main__':
begin = datetime.now()
# 加载数据
dms = pickle.load(file=open(file='../PickledFile/dm_split.pickled', mode='rb')) # type: pd.DataFrame
cts = pickle.load(file=open(file='../PickledFile/comments_split.pickled', mode='rb')) # type: pd.DataFrame
# 加载训练集
train_data, train_target = load_train_data(file1='./弹幕sample.csv', file2='./评论sample.csv')
print('数据加载与分词结束………………………………………………………………………………………………………………………………………………………………………………………………………………')
model = make_pipeline(TfidfVectorizer(), MultinomialNB())
model.fit(train_data, train_target)
print('模型训练结束……………………………………………………………………………………………………………………………………………………………………………………………………………………………')
print('开始预测……………………………………………………………………………………………………………………………………………………………………………………………………………………………………')
dms['res'] = model.predict(dms['content'])
cts['res'] = model.predict(cts['content'])
dms = dms.drop(labels=['content'], axis=1)
cts = cts.drop(labels=['content'], axis=1)
end = datetime.now()
print(end - begin)
# 储存分类结果
pickle.dump(obj=dms, file=open(file='../PickledFile/dm_predicted(3).pickled', mode='wb'))
pickle.dump(obj=cts, file=open(file='../PickledFile/comments_predicted (3).pickled', mode='wb'))
这里,我将数据分为3类:内容聚焦于国内情况(图中蓝色)、内容聚焦于国外情况(途中黄色)、内容没有明显关注倾向(图中未画出),结果如下图所示:
可以发现,分类结果基本与实际疫情的发展相符。
这里我将数据分为4类:内容带有调侃倾向(图中蓝色)、内容带有鼓励倾向(图中黄色)、内容带有严肃的事实讨论(图中绿色)、其他(图中未画出)。
可以发现,分类结果能够与实际疫情的发展阶段相符。