基于ML的红楼梦作者分析

《红楼梦》,原名《石头记》,中国古代章回体长篇小说,中国古典四大名著之一。其通行本共120回,一般认为前80回是清代作家曹雪芹所著,后40回作者为无名氏,整理者为程伟元、高鹗。《红楼梦》百度百科

我们基于聚类方法(K-means)无监督学习和Naive Bayes监督学习方法进行有关章节作者的探索。

1
2
3
4
5
6
7
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.cluster import AgglomerativeClustering
from sklearn.naive_bayes import MultinomialNB
from sklearn.preprocessing import MinMaxScaler # 归一化
import re

1. 数据处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 生成词频矩阵
big_xuci=['之','其','或','亦','方','于','即','皆','因','仍','故','尚','乃',
'呀','吗','咧','罢咧','啊','罢','罢了','么','呢',
'了','的','着','一','不','把','让','向','往','是','在','别','好',
'可','便','就','但','越','再','更','比','很','偏']
#xuci:18个常用虚词
#xuci=['而','何','乎','乃','其','且','若','所','为','焉','也','以','因','于','与','则','者','之']
f = open('《红楼梦》完整版.txt',encoding='utf-8')
s = f.read()

#章节划分,建立章节名的正则表达式
#rule = re.compile('第[一二三四五六七八九十百]+回 ')
rule = re.compile('第[零一二三四五六七八九十百]+回 ')
zj=rule.split(s)
zj=zj[1:]

#首选生成虚词统计矩阵
cdata=np.zeros([len(zj),len(big_xuci)])
for i in range(len(zj)):
for j in range(len(big_xuci)):
cdata[i,j]=zj[i].count(big_xuci[j])

#计算文章中,该虚词在该章出现次数占该章统计的全部虚词出现的次数,生成虚词频率矩阵
sdata=np.zeros([len(cdata),len(big_xuci)])
for i in range(len(cdata)):
for j in range(len(big_xuci)):
sdata[i,j]=cdata[i,j]/sum(cdata[i])

#数据归一化
scaler=MinMaxScaler().fit(sdata)
ndata=scaler.transform(sdata)

2. 聚类算法

1
2
3
# 数据检查
data=ndata
data.shape
(120, 45)
2.1 k-means聚类
1
2
3
4
5
6
7
# 1------kmeans聚类------
kmeans=KMeans(n_clusters=2).fit(data)
author = kmeans.labels_
print('------------K-Means聚类------------')
print('全章回结果:\n',author)
print('前80回结果:\n',author[:80])
print('后40回结果:\n',author[80:])
------------K-Means聚类------------
全章回结果:
 [1 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 0 0 1 1 0 1 1
 1 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1
 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0]
前80回结果:
 [1 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 0 0 1 1 0 1 1
 1 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1
 1 1 1 1 1 1]
后40回结果:
 [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0]
2.2 层次聚类
1
2
3
4
5
6
# #3------层次聚类------
# agg = AgglomerativeClustering(linkage='ward', n_clusters=2).fit(data)
# print('------------层次聚类------------')
# print('全章回结果:\n',agg.labels_)
# print('前80回结果:\n',agg.labels_[:80])
# print('后40回结果:\n',agg.labels_[80:])

效果不太好(´,,•ω•,,‘),我们选择淘汰此方法。

3. 朴素贝叶斯方法

用于多项式模型的朴素贝叶斯分类器

多项式朴素贝叶斯分类器适用于具有离散特征的分类(例如,用于文本分类的字数统计)。多项式分布通常需要整数特征计数。但是,实际上,小数计数(例如tf-idf)也可能起作用。

在使用贝叶斯分类之前,为了便于处理问题,我们进行如下的假设。首先,我们将问题定义为二分类问题,即假设前面某部分由曹雪芹所写,后面某部分由另一个人所写(如高鹗)。朴素贝叶斯方法为有监督的方法,需要先进行标注。我们基于前面的假设同时尽量可能充分地训练模型,将最前面(前40章)标注为作者0;将最后面(后20章)标注为作者1。因此第41-100章为待分类数据,从而划分训练数据和预测数据。

3.1 添加标签
1
2
3
4
5
6
7
# 添加标签
npdata=pd.DataFrame(cdata)

traindata0=npdata[:40]
traindata1=npdata[100:]
traindata0['label']=0;
traindata1['label']=1;
C:\Users\admin\AppData\Local\Temp/ipykernel_25524/1077375511.py:6: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  traindata0['label']=0;
C:\Users\admin\AppData\Local\Temp/ipykernel_25524/1077375511.py:7: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  traindata1['label']=1;
3.2 数据集划分
1
2
3
4
5
6
# 拆分训练集和测试集(待分类)
traindata = pd.concat([traindata0,traindata1])
testdata = pd.DataFrame(cdata[40:100])

X = traindata.iloc[:,:45]
Y = traindata['label']
3.3 训练与分类
1
2
3
mnb = MultinomialNB()
mnb.fit(X, Y)
label_predict = mnb.predict(testdata)

结果展示

1
2
3
print('------------Naive Bayes model------------')
print("41-80章节的贝叶斯分类结果为:")
label_predict[:40]
------------Naive Bayes model------------
41-80章节的贝叶斯分类结果为:





array([0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=int64)
1
2
print("81-100章节的贝叶斯分类结果为:")
label_predict[40:]
81-100章节的贝叶斯分类结果为:





array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
      dtype=int64)

虽然模型建立的依赖于我们的假设,且训练数据较少,但是分类的结果还是很理想的。:.゚ヽ(。◕‿◕。)ノ゚.:。+゚


基于ML的红楼梦作者分析
https://e-alan.github.io/2023/02/04/基于ML的红楼梦作者分析/
作者
Yubiao Wang
发布于
2023年2月4日
许可协议