前言:出于种种原因,总是不自觉把爱好和工作相互结合起来,每每感叹于曹雪芹构思的巧妙,语言的精炼,情节的感人……于是蹦出想法,看机器能否读懂“宝黛”之间的爱情。
-
数据处理
数据当然是伟大的《红楼梦》本身了,下载txt文件。
然后进行编码转化“utf-8”。
然后进行分词,去除其中大量的空格和标点,然后有两种方法进行词向量的构建,分别是n-gram模型训练和word2vect
用n-gramma生成词向量
把数据转化为如下格式:
也就是N=2
上述操作代码如下:
with open("红楼梦.txt",encoding='utf-8') as f:
text = f.read()
# print(text)
temp = jieba.lcut(text)
words = []
# print(temp)
for i in temp:
#过滤掉所有的标点符号
i = re.sub("[\s+\.\!\/_,$%^*(+\"\'””《》]+|[+——!,。?、~@#¥%……&*():]+", "", i)
if len(i) > 0:
words.append(i)
# print(len(words))
# print(words)
trigrams = [([words[i], words[i + 1]], words[i + 2]) for i in range(len(words) - 2)]
# 打印出前三个元素看看
print(trigrams[:3])
然后建立词汇表
# 得到词汇表
vocab = set(words)
print(len(vocab))
# 两个字典,一个根据单词索引其编号,一个根据编号索引单词
#word_to_idx中的值包含两部分,一部分为id,另一部分为单词出现的次数
#word_to_idx中的每一个元素形如:{w:[id, count]},其中w为一个词,id为该词的编号,count为该单词在words全文中出现的次数
word_to_idx = {}
idx_to_word = {}
ids = 0
#对全文循环,构件这两个字典
for w in words:
cnt = word_to_idx.get(w, [ids, 0])
if cnt[1] == 0:
ids += 1
cnt[1] += 1
word_to_idx[w] = cnt
idx_to_word[ids] = w
然后就是模型构建和参数训练,模型结构如下:
1、输入层:embedding层,这一层的作用是:先将输入单词的编号映射为一个one hot编码的向量,形如:001000,维度为单词表大小。 然后,embedding会通过一个线性的神经网络层映射到这个词的向量表示,输出为embedding_dim
2、线性层,从embedding_dim维度到128维度,然后经过非线性ReLU函数
3、线性层:从128维度到单词表大小维度,然后log softmax函数,给出预测每个单词的概率
具体代码可见:https://github.com/dctongsheng/n-grama-and-wordvect-Analysis-of-the-text
迭代训练
word2vect进行模型训练
训练的时候需要把数据重新进行调整,变成句子进行输入
如下:
['因此', '大家', '议定', '每日', '轮流', '做', '晚饭', '之主'], ['天天', '宰猪', '割羊', '屠鹅', '杀鸭', '好似', '临潼斗宝', '的', '一般', '都', '要', '卖弄', '自己', '家里', '的', '好', '厨役', '好', '烹调'],
数据处理的代码如下:
f = open("红楼梦.txt", encoding='utf-8')
f = f.read().split("。")
print(list(f))
lines = []
for line in f:
temp = jieba.lcut(line)
words = []
for i in temp:
#过滤掉所有的标点符号
i = re.sub("[\s+\.\!\/_,$%^*(+\"\'””《》]+|[+——!,\- 。?、~@#¥%……&*():;‘]+", "", i)
if len(i) > 0:
words.append(i)
if len(words) > 0:
lines.append(words)
print(lines)
然后输入模型,将图像转化为二维空间进行展示
model = Word2Vec(lines, size = 20, window = 2 , min_count = 0)
renwu = model.wv.most_similar('林黛玉', topn = 20)
print(renwu)
rawWordVec = []
word2ind = {}
for i, w in enumerate(model.wv.vocab):
rawWordVec.append(model[w])
word2ind[w] = i
rawWordVec = np.array(rawWordVec)
X_reduced = PCA(n_components=2).fit_transform(rawWordVec)
然后绘制图像,并且展示把几个特殊人物,在图像中展现出来'贾宝玉', '林黛玉', '香菱', '贾政', '晴雯', '妙玉', '袭人', '薛宝钗', '王熙凤', '平儿', '贾母', '探春'
# 绘制所有单词向量的二维空间投影
fig = plt.figure(figsize = (15, 10))
ax = fig.gca()
ax.set_facecolor('black')
ax.plot(X_reduced[:, 0], X_reduced[:, 1], '.', markersize = 1, alpha = 0.3, color = 'white')
# 绘制几个特殊单词的向量
words = ['贾宝玉', '林黛玉', '香菱', '贾政', '晴雯', '妙玉', '袭人', '薛宝钗', '王熙凤', '平儿', '贾母', '探春']
# 设置中文字体,否则无法在图形上显示中文
zhfont1 = matplotlib.font_manager.FontProperties(fname='./华文仿宋.ttf', size=16)
for w in words:
if w in word2ind:
ind = word2ind[w]
xy = X_reduced[ind]
plt.plot(xy[0], xy[1], '.', alpha =1, color = 'red')
plt.text(xy[0], xy[1], w, fontproperties = zhfont1, alpha = 1, color = 'yellow')
plt.show()
展示结果如下:
可以看到宝黛钗凤很相近,几乎都分不清了他们的名字了,然后放大之后,震惊……
结论:原来机器也“读懂”了“宝黛”爱情。
补充:由于以上用的数据仅仅来自于《红楼梦》文本本身,如果有另外巨大的语料(微博、人民日报、上海热线、汽车之家等,包含1366130个词向量)训练出来结果如何呢?
篇幅有限直接上结果吧,
以上内容纯属个人娱乐,与君共勉,不喜请喷