三十二-用影视剧字幕语料库生成词向量

利用搜索引擎的原理实现的聊天机器人效果差强人意,为了对庞大的语料库进一步运用,打算利用LSTM-RNN来做训练,第一步需要把中文语料转成算法可以识别的向量形式,最强大的word embedding工具莫过于word2vec了,本节跟大家分享我是怎么用三千万影视剧字幕语料库生成词向量的

对语料库切词

因为word2vec的输入需要是切好词的文本文件,但我们的影视剧字幕语料库是回车换行分隔的完整句子,所以我们先对其做切词,有关中文切词的方法请见《教你成为全栈工程师(Full Stack Developer) 三十四-基于python的高效中文文本切词》,为了对影视剧字幕语料库切词,我们来创建word_segment.py文件,内容如下:

 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
# coding:utf-8

import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )

import jieba
from jieba import analyse

def segment(input, output):
    input_file = open(input, "r")
    output_file = open(output, "w")
    while True:
        line = input_file.readline()
        if line:
            line = line.strip()
            seg_list = jieba.cut(line)
            segments = ""
            for str in seg_list:
                segments = segments + " " + str
            output_file.write(segments)
        else:
            break
    input_file.close()
    output_file.close()

if __name__ == '__main__':
    if 3 != len(sys.argv):
        print "Usage: ", sys.argv[0], "input output"
        sys.exit(-1)
    segment(sys.argv[1], sys.argv[2]);

使用方法:

1
python word_segment.py subtitle/raw_subtitles/subtitle.corpus segment_result

生成的segment_result文件内容就像下面的样子:

1
2
3
……
这件 事 对不起 我 并 不 直率 只有 在 梦 中 才能 表白 在 思绪 错乱 之前 现在 只想 马上 见到 你 夜晚 在 月光 下 哭泣 深夜里 无法 打电话 给 你 纯情 的 我 该 怎么办 心 就 如 万花筒 一般 月亮 的 光芒 引导 着 我 与 你 相会 无数次 星座 闪耀 的 瞬间 来 占卜 恋爱 的 方向 同样 都 是 在 地球 上 出生 的 奇迹 的 罗曼史 我 相信 奇迹 的 罗曼史 小小 兔 在 哪 你 在 哪里 找到 了 吗 到处 都 没 看到 难道 被 敌人 … 总之 分头 寻找 继续 找 吧 暗黑 女王   Black   Lady 的 诞生 不 愉快 的 往日 回忆 会 在 内心深处 留下 创伤 想起 坏心肠 的 妈妈 及 冷酷 的 父亲 吧 是 青蛙 会 跌倒 的 谁 叫 你 不 听 妈妈 的话 不 可以 哭 妈妈 最 讨厌 了 爸爸   拉 我 起来 自己 站 起来 不向 你 伸出手 的 父母 就是 不爱 你 的 证据 是 你 自己 跌倒 不好 站 起来 快 回想起来 那些 更 可恨 的 事情 吧 怎么 一个 人站 着 发呆 今天 是 我 的 生日 生日 ? 对 呀 可是 爸
……

统计一下这个文件如下:

1
2
3
4
[root@centos $] ls -lh segment_result
-rw-r--r-- 1 lichuang staff 1.1G 10  9 18:59 segment_result
[root@centos $] wc segment_result
         0  191925623 1093268485 segment_result

0行是因为没有在行尾加回车符,一共是191925623列,1093268485个字符

用word2vec生成词向量

想了解word2vec的原理请见《自己动手做聊天机器人 二十五-google的文本挖掘深度学习工具word2vec的实现原理》,如果因为被墙获取不到word2vec可以从https://github.com/warmheartli/ChatBotCourse/tree/master/word2vec获取,直接make编译好后会生成一些二进制文件,我要用到的是word2vec

执行

1
2
3
4
5
./word2vec -train ../segment_result -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15
Starting training using file ../segment_result
Vocab size: 260499
Words in train file: 191353657
Alpha: 0.039254  Progress: 21.50%  Words/thread/sec: 96.67k

生成的vectors.bin就是我们想要的词向量,只不过是二进制格式,这时我们可以用word2vec自带的distance工具来验证一下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
./distance vectors.bin
Enter word or sentence (EXIT to break): 漂亮

Word: 漂亮  Position in vocabulary: 722

Word       Cosine distance
------------------------------------------------------------------------
很漂亮     0.532610
厉害      0.440603
身材      0.430269
beautiful       0.413831
棒       0.410241
帅呆了     0.409414
干得      0.407550
不错      0.402978
好美      0.401329
可爱      0.399667
猕猴桃     0.391512
好       0.388109
配当      0.387999
真棒      0.387924
太棒了     0.384184
真不错     0.377484
……

词向量二进制文件的格式及加载

word2vec生成的词向量二进制格式是这样的:

词数目(空格)向量维度 第一个词(空格)词向量(大小为200sizeof(float))第二个词(空格)词向量(大小为200sizeof(float))…… 所以写了一个加载词向量二进制文件的python脚本,后面会用到,如下:

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# coding:utf-8

import sys
import struct
import math
import numpy as np

reload(sys)
sys.setdefaultencoding( "utf-8" )

max_w = 50
float_size = 4

def load_vectors(input):
    print "begin load vectors"

    input_file = open(input, "rb")

    # 获取词表数目及向量维度
    words_and_size = input_file.readline()
    words_and_size = words_and_size.strip()
    words = long(words_and_size.split(' ')[0])
    size = long(words_and_size.split(' ')[1])
    print "words =", words
    print "size =", size

    word_vector = {}

    for b in range(0, words):
        a = 0
        word = ''
        # 读取一个词
        while True:
            c = input_file.read(1)
            word = word + c
            if False == c or c == ' ':
                break
            if a < max_w and c != '\n':
                a = a + 1
        word = word.strip()

        # 读取词向量
        vector = np.empty([200])
        for index in range(0, size):
            m = input_file.read(float_size)
            (weight,) = struct.unpack('f', m)
            vector[index] = weight

        # 将词及其对应的向量存到dict中
        word_vector[word.decode('utf-8')] = vector

    input_file.close()

    print "load vectors finish"
    return word_vector

if __name__ == '__main__':
    if 2 != len(sys.argv):
        print "Usage: ", sys.argv[0], "vectors.bin"
        sys.exit(-1)
    d = load_vectors(sys.argv[1])
    print d[u'真的']

运行方式如下:

1
python word_vectors_loader.py vectors.bin

效果如下:

 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
32
33
34
35
36
37
38
begin load vectors
words = 49804
size = 200
load vectors finish
[-1.09570336  2.03501272  0.3151325   0.17603125  0.30261561  0.15273243
 -0.6409803   0.06317     0.20631203  0.22687016  0.59229285 -1.10883808
  1.12569952  0.16838464  1.27895844 -1.18480754  1.6270808  -2.62790298
  0.43835989 -0.21364243  0.05743926 -0.77541786 -0.19709823  0.33360079
  0.43415883 -1.28643405 -0.95402282  0.01350032 -0.20490573  0.80880177
 -1.47243023 -0.09673293  0.05514769  1.00915158 -0.11268988  0.68446255
  0.08493964  0.27009442  0.33748865 -0.03105624 -0.19079798  0.46264866
 -0.53616458 -0.35288206  0.76765436 -1.0328685   0.92285776 -0.97560757
  0.5561474  -0.05574715 -0.1951212   0.5258466  -0.07396954  1.42198348
  1.12321162  0.03646624 -1.54316568  0.34798017  0.64197171 -0.57232529
  0.14402699  1.75856864 -0.72602183 -1.37281013  0.73600221  0.4458617
 -1.32631493  0.25921029 -0.97459841 -1.4394536   0.18724895 -0.74114919
  1.50315142  0.56819481  0.37238419 -0.0501433   0.36490002 -0.14456141
 -0.15503241 -0.04504468  1.18127966  1.465729   -0.13834922 -0.1232961
 -0.14927664  0.67862391  2.46567917 -1.10682511  0.71275675  1.04118025
  0.23883103 -1.99175942  0.40641201  0.73883104 -0.37824577  0.88882846
 -0.87234962  0.71112823  0.33647302 -1.2701565  -1.15415645  1.41575384
 -2.01556969 -0.85669023 -0.0378141  -0.60975027  0.0738821   0.19649875
  0.02519603 -0.78310513  0.40809572  0.55079561  1.79861426 -0.01188554
 -0.14823757 -0.97098011 -2.75159121  1.52366722 -0.41585007  0.78664345
  0.43792239  1.03834045  1.18758595  0.18793568 -1.44434023 -1.55205989
  0.24251698  1.05706048 -1.52376628 -0.60226047 -0.41849345 -0.30082899
 -1.32461691  0.29701442  0.36680841 -0.72046149 -0.16455257 -0.02307599
 -0.74143982  0.10319671 -0.5436908  -0.85527682 -0.81110024 -1.14968359
 -1.45617366  0.57568634 -1.10673392 -0.48830599  1.38728273 -0.46238521
  1.40288961 -0.92997569  0.90154368  0.09381612 -0.61220604 -0.40820527
  1.2660408  -1.02075434  0.98662543  0.81696391  0.06962785  0.83282673
 -0.12462004  1.16540051  0.10254569  1.03875697  0.05073663  1.50608146
  0.49252063  0.09693919  0.38897502 -0.0673333  -0.30629408 -2.1759603
  0.5477249  -1.46633601  1.54695141 -0.83080739 -0.49649978  1.05921662
 -0.60124737 -0.72645563 -1.44115663 -0.6903789   0.38817915 -0.11854757
 -0.18087701 -0.41152322 -0.98559368 -1.46712041  1.63777673 -0.64418262
 -0.56800991  1.79656076 -0.80431151  0.99533188  0.06813133 -0.73489577
 -0.67567319  0.64855355]

总结

考虑到运行时间的关系,以上示例中多数是我抽取了部分语料数据,因此你真正执行时数值可能会不一样。至此,我们已经做到用影视剧字幕语料库(获取方式《自己动手做聊天机器人 二十九-重磅:近1GB的三千万聊天语料供出》)生成词向量并能够通过python加载起来,下一步就是如何运用了,敬请关注