前言:出于种种原因,总是不自觉把爱好和工作相互结合起来,每每感叹于曹雪芹构思的巧妙,语言的精炼,情节的感人……于是蹦出想法,看机器能否读懂“宝黛”之间的爱情。
- 数据处理
数据当然是伟大的《红楼梦》本身了,下载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个词向量)训练出来结果如何呢? 篇幅有限直接上结果吧,