├── README.md
└── kenlm_model.py
/README.md:
--------------------------------------------------------------------------------
1 | # py-kenlm-model
2 | python | 高效使用统计语言模型kenlm:新词发现、分词、智能纠错等
3 |
4 | 之前看到苏神[【重新写了之前的新词发现算法:更快更好的新词发现】](https://spaces.ac.cn/archives/6920)中提到了kenlm,之前也自己玩过,没在意,现在遇到一些大规模的文本问题,模块确实好用,前几天还遇到几个差点“弃疗”的坑,解决了之后,就想,不把kenlm搞明白,对不起我浪费的两天。。
5 |
6 | **kenlm的优点([关于kenlm工具训练统计语言模型](https://blog.csdn.net/HHTNAN/article/details/84231733)):**
7 | 训练语言模型用的是传统的“统计+平滑”的方法,使用kenlm这个工具来训练。它快速,节省内存,最重要的是,允许在开源许可下使用多核处理器。
8 | kenlm是一个C++编写的语言模型工具,具有速度快、占用内存小的特点,也提供了Python接口。
9 |
10 | 额外需要加载的库:
11 | ```
12 | kenlm
13 | pypinyin
14 | pycorrector
15 | ```
16 |
17 | 笔者的代码可见github,只是粗略整理,欢迎大家一起改:
18 | [mattzheng/py-kenlm-model](https://github.com/mattzheng/py-kenlm-model)
19 |
20 | 相关新词发现,fork了苏神的,进行了微调:
21 |
22 | [mattzheng/word-discovery](https://github.com/mattzheng/word-discovery)
23 |
24 | 博客链接:
25 |
26 | [python | 高效使用统计语言模型kenlm:新词发现、分词、智能纠错等](https://mattzheng.blog.csdn.net/article/details/101512616)
27 |
28 |
29 |
30 | ----------
31 |
32 |
33 |
34 | # 1 kenlm安装
35 |
36 | 在这里面编译:[kpu/kenlm](https://github.com/kpu/kenlm),下载库之后编译:
37 | ```python
38 | mkdir -p build
39 | cd build
40 | cmake ..
41 | make -j 4
42 | ```
43 | 一般编译完,很多有用的文件都存在`build/bin`之中,这个后面会用到:
44 | 
45 | python库的安装方式:
46 |
47 | ```python
48 | pip install https://github.com/kpu/kenlm/archive/master.zip
49 | ```
50 | 简单使用:
51 |
52 | ```python
53 | import kenlm
54 | model = kenlm.Model('lm/test.arpa')
55 | print(model.score('this is a sentence .', bos = True, eos = True))
56 | ```
57 |
58 | 坑点来了,笔者之前装在docker之中的,之前一不小心重启docker,kenlm就不灵了。。
59 | 当时并不知道该如何重新编译,就重新:`cmake ..` + `make -j 4`,但是这样出来,运行会报很多依赖没装:
60 |
61 | ```python
62 |
63 | libboost_program_options.so.1.54.0: cannot open shared object file: No such file or directory
64 | ```
65 | 笔者还假了嘛嘎的去ubuntu上拉下来装了,又报其他依赖错。。
66 |
67 | (此处省略N多次,无效尝试。。。)
68 |
69 | 如果出现:
70 |
71 | ```python
72 | -- Could NOT find BZip2 (missing: BZIP2_LIBRARIES BZIP2_INCLUDE_DIR)
73 | -- Could NOT find LibLZMA (missing: LIBLZMA_INCLUDE_DIR LIBLZMA_LIBRARY LIBLZMA_HAS_AUTO_DECODER LIBLZMA_HAS_EASY_ENCODER LIBLZMA_HAS_LZMA_PRESET)
74 |
75 | ```
76 | 需安装:
77 |
78 | ```python
79 | sudo apt install libbz2-dev
80 | sudo apt install liblzma-dev
81 | ```
82 |
83 | 之后实验发现,把`build`文件夹删了,重新来一遍`cmake ..` + `make -j 4`即可。
84 |
85 |
86 | ----------
87 |
88 | # 2 kenlm统计语言模型使用
89 |
90 | ## 2.1 kenlm的训练 `lmplz`
91 | ### 2.1.1 两种训练方式
92 | 训练是根据`build/bin/lmplz `来进行,一般来说有两种方式:
93 |
94 | 
95 |
96 | (1)管道的方式传递
97 |
98 | 数据print的方式,苏神之前的博客【[【中文分词系列】 5. 基于语言模型的无监督分词](https://spaces.ac.cn/archives/3956#%E5%AE%9E%E8%B7%B5%EF%BC%9A%E8%AE%AD%E7%BB%83)】中有提到:
99 |
100 | ```python
101 | python p.py|./kenlm/bin/lmplz -o 4 > weixin.arpa
102 | ```
103 | p.py为:
104 |
105 | ```python
106 | import pymongo
107 | db = pymongo.MongoClient().weixin.text_articles
108 |
109 | for text in db.find(no_cursor_timeout=True).limit(500000):
110 | print ' '.join(text['text']).encode('utf-8')
111 | ```
112 |
113 | (2)预先生成语料文本
114 |
115 | 直接命令行,数据保存
116 | ```python
117 | bin/lmplz -o 3 --verbose_header --text ../text-18-03/text_18-03-AU.txt --arpa MyModel/log.arpa
118 | ```
119 | 其中参数的大致意义:
120 |
121 | ```python
122 | -o n:最高采用n-gram语法
123 | -verbose_header:在生成的文件头位置加上统计信息
124 | --text text_file:指定存放预料的txt文件
125 | --arpa:指定输出的arpa文件
126 | -S [ --memory ] arg (=80%) Sorting memory内存预占用量
127 | --skip_symbols : Treat , , and as whitespace instead of throwing an exception
128 | ```
129 |
130 | 预先语料可以不加开头、结尾符号,其中, 需要特别介绍三个特殊字符。
131 | `、和`
132 | ``和``结对使用,模型在计算概率时对每句话都进行了处理,将该对标记加在一句话的起始和结尾。
133 | 这样就把开头和结尾的位置信息也考虑进来。
134 | 如`“我 喜欢 吃 苹果” --> " 我 喜欢 吃 苹果 "`
135 | ``表示unknown的词语,对于oov的单词可以用它的值进行替换。
136 |
137 | 可参考:
138 | 不带开头结尾:
139 | ```
140 | W h o o 后 拱 辰 享 水 水 妍 护 肤 套 装 整 套 质 地 都 比 较 清 爽
141 | 滋 润
142 | 侧 重 保 湿
143 | 适 合 各 种 肤 质
144 | 调 节 肌 肤 水 平 衡
145 | 它 还 具 有 修 复 功 效
146 | 提 亮 肤 色 我 是 油 性 肤 质 用 起 来 也 一 点 也 不 觉 得 油 腻
147 | 味 道 淡 淡 的 还 很 好 闻
148 | 也 很 好 吸 收
149 | 质 地 清 爽
150 | ```
151 | 带开头结尾的:
152 |
153 | ```python
154 | 3 乙 方 应 依 据 有 关 法 律 规 定
155 | 对 甲 方 为 订 立 和 履 行 本 合 同 向 乙 方 提 供 的 有 关 非 公 开 信 息 保 密
156 | 但 下 列 情 形 除 外
157 | 1 贷 款 人 有 权 依 据 有 关 法 律 法 规 或 其 他 规 范 性 文 件 的 规 定 或 金 融 监 管 机 构 的 要 求
158 | ```
159 |
160 | 具体的训练过程可见该博客:[图解N-gram语言模型的原理--以kenlm为例](https://blog.csdn.net/asrgreek/article/details/81979194)
161 |
162 | 
163 |
164 |
165 |
166 | ### 2.1.2 生成文件arpa的解释
167 | 来源:[语言模型kenlm的训练及使用](https://www.bbsmax.com/A/WpdKmENJVQ/)
168 | 其中生成的arpa文件有:
169 |
170 | ```python
171 |
172 |
173 | \1-grams:
174 | -6.5514092 0
175 | 0 -2.9842114
176 | -1.8586434 0
177 | -2.88382 ! -2.38764
178 | -2.94351 world -0.514311
179 | -2.94351 hello -0.514311
180 | -6.09691 guys -0.15553
181 |
182 | \2-grams:
183 | -3.91009 world ! -0.351469
184 | -3.91257 hello world -0.24
185 | -3.87582 hello guys -0.0312
186 |
187 | \3-grams:
188 | -0.00108858 hello world !
189 | -0.000271867 , hi hello !
190 |
191 | \end\
192 |
193 |
194 | ```
195 |
196 | 介绍该文件需要引入一个新的概念,back_pro【[language model](http://blog.csdn.net/visionfans/article/details/50131397)】
197 | 三个字段分别是:`Pro,word,back_pro `
198 | 注:arpa文件中给出的数值都是以10为底取对数后的结果
199 |
200 |
201 |
202 |
203 | ### 2.1.3 几个训练坑点解读
204 |
205 | 划重点来了,其中`-s` 非常重要,默认是`80%`,如果机器有20%被占了,笔者当时发现,10句话训练模型也能超内存,这不是瞎胡闹:
206 |
207 | ```python
208 | #34304 what(): /mnt/mNLP/kg/kenlm/util/scoped.cc:20 in void* util::{anonymous}::InspectAddr(void*, std::size_t, const char*) threw MallocException because `!addr && requested'.
209 | #Cannot allocate memory for 84881776616 bytes in malloc
210 | ```
211 | 需要额外设置内存占用量!当然还有挺多可能会产生意外的参数:
212 |
213 | 参数 | 解释
214 | -------- | :-----
215 | minimum_block arg (=8K) | Minimum block size to allow
216 | sort_block arg (=64M) | Size of IO operations for sort (determines arity)
217 | block_count arg (=2) | Block count (per order)
218 | interpolate_unigrams [=arg(=1)] (=1) | Interpolate the unigrams (default) as opposed to giving lots of mass to like SRI. If you want SRI's behavior with a large and the old lmplz default, use --interpolate_unigrams 0.
219 | discount_fallback [=arg(=0.5 1 1.5)] | The closed-form estimate for Kneser-Ney discounts does not work without singletons or doubletons.
220 | 。。。(还有不少) | 。。。
221 |
222 | 还有可能会报错:
223 |
224 | ```python
225 | Unigram tokens 153 types 116
226 | === 2/5 Calculating and sorting adjusted counts ===
227 | Chain sizes: 1:1392 2:10964970496 3:20559319040 4:32894910464
228 | /mnt/mNLP/kg/kenlm/lm/builder/adjust_counts.cc:52 in void lm::builder::{anonymous}::StatCollector::CalculateDiscounts(const lm::builder::DiscountConfig&) threw BadDiscountException because `s.n[j] == 0'.
229 | Could not calculate Kneser-Ney discounts for 1-grams with adjusted count 4 because we didn't observe any 1-grams with adjusted count 3; Is this small or artificial data?
230 | Try deduplicating the input. To override this error for e.g. a class-based model, rerun with --discount_fallback
231 | ```
232 | 报错码为:34304,主要是因为字数太少,所以训练的时候需要多加一些。
233 |
234 | ## 2.2 模型压缩二进制化`build_binary `
235 | 这边生成的arpa文件,可能会比较大,可以通过二进制化缩小文件大小:
236 |
237 | ```python
238 | bin/build_binary -s lm.arpa lm.bin
239 | ```
240 | 将arpa文件转换为binary文件,这样可以对arpa文件进行压缩,提高后续在python中加载的速度。
241 |
242 | 
243 | 虽然大小没有发生太大的变化,但是压缩后会大大提高Python加载的速度。
244 |
245 | 可能会报错,报错码为:256,原因如下:
246 |
247 | ```python
248 | No such file or directory while opening output/test2.arpa
249 | ```
250 |
251 |
252 |
253 | ## 2.3 利用kenlm的`count_ngrams`计算n-grams
254 | 苏神[【重新写了之前的新词发现算法:更快更好的新词发现】](https://spaces.ac.cn/archives/6920)中用的是这个。
255 | 这个库存在`build/bin/count_ngrams`
256 | ```python
257 | # Counts n-grams from standard input.
258 | # corpus count:
259 | # -h [ --help ] Show this help message
260 | # -o [ --order ] arg Order
261 | # -T [ --temp_prefix ] arg (=/tmp/) Temporary file prefix
262 | # -S [ --memory ] arg (=80%) RAM
263 | # --read_vocab_table arg Vocabulary hash table to read. This should
264 | # be a probing hash table with size at the
265 | # beginning.
266 | # --write_vocab_list arg Vocabulary list to write as null-delimited
267 | # strings.
268 | ```
269 | 其中也有该死的`-s`,要留意。
270 | 执行命令示例:
271 | ```python
272 | ./count_ngrams -S 50% -o 4 --write_vocab_list output/test2.chars