基于最小熵原理的NLP库:nlp zero
By 苏剑林 | 2018-05-31 | 101404位读者 |陆陆续续写了几篇最小熵原理的博客,致力于无监督做NLP的一些基础工作。为了方便大家实验,把文章中涉及到的一些算法封装为一个库,供有需要的读者测试使用。
由于面向的是无监督NLP场景,而且基本都是NLP任务的基础工作,因此命名为nlp zero。
地址 #
Github: https://github.com/bojone/nlp-zero
Pypi: https://pypi.org/project/nlp-zero/
可以直接通过
pip install nlp-zero==0.1.6
进行安装。整个库纯Python实现,没有第三方调用,支持Python2.x和3.x。
使用 #
默认分词 #
库内带了一个词典,可以作为一个简单的分词工具用
from nlp_zero import *
t = Tokenizer()
t.tokenize(u'扫描二维码,关注公众号')
自带的词典加入了一些通过新词发现挖掘出来的新词,并且经过笔者的人工优化,质量相对来说还是比较高的。
词库构建 #
通过大量的原始语料来构建词库。
首先我们需要写一个迭代容器,这样就不用一次性把所有语料加载到内存中了。迭代器的写法很灵活,比如我的数据存在MongoDB中,那就是:
import pymongo
db = pymongo.MongoClient().weixin.text_articles
class D:
def __iter__(self):
for i in db.find().limit(10000):
yield i['text']
如果数据存在文本文件中,大概就是
class D:
def __iter__(self):
with open('text.txt') as f:
for l in f:
yield l.strip() # python2.x还需要转编码
然后就可以执行了
from nlp_zero import *
import logging
logging.basicConfig(level = logging.INFO, format = '%(asctime)s - %(name)s - %(message)s')
f = Word_Finder(min_proba=1e-8)
f.train(D()) # 统计互信息
f.find(D()) # 构建词库
通过Pandas查看结果:
import pandas as pd
words = pd.Series(f.words).sort_values(ascending=False)
直接用统计出来的词库建立一个分词工具:
t = f.export_tokenizer()
t.tokenize(u'今天天气不错')
句模版构建 #
跟前面一样,同样要写一个迭代器,这里不再重复。
因为构建句模版是基于词来统计的,因此还需要一个分词函数,可以用自带的分词器,也可以用外部的,比如结巴分词。
from nlp_zero import *
import logging
logging.basicConfig(level = logging.INFO, format = '%(asctime)s - %(name)s - %(message)s')
tokenize = Tokenizer().tokenize # 使用自带的分词工具
# 通过 tokenize = jieba.lcut 可以使用结巴分词
f = Template_Finder(tokenize, window=3)
f.train(D())
f.find(D())
通过Pandas查看结果:
import pandas as pd
templates = pd.Series(f.templates).sort_values(ascending=False)
idx = [i for i in templates.index if not i.is_trivial()]
templates = templates[idx] # 筛选出非平凡的模版
每个模版已经被封装为一个类了。
层次分解 #
基于句模版来进行句子结构解析。
from nlp_zero import *
# 建立一个前缀树,并加入模版
# 模版可以通过tuple来加入,
# 也可以直接通过“tire[模版类]=10”这样来加入
trie = XTrie()
trie[(None, u'呢')] = 10
trie[(None, u'可以', None, u'吗')] = 9
trie[(u'我', None)] = 8
trie[(None, u'的', None, u'是', None)] = 7
trie[(None, u'的', None, u'是', None, u'呢')] = 7
trie[(None, u'的', None)] = 12
trie[(None, u'和', None)] = 12
tokenize = Tokenizer().tokenize # 使用自带的分词工具
p = Parser(trie, tokenize) # 建立一个解析器
p.parse(u'鸡蛋可以吃吗') # 对句子进行解析
"""输出:
>>> p.parse(u'鸡蛋可以吃吗')
+---> (鸡蛋)可以(吃)吗
| +---> 鸡蛋
| | +---> 鸡蛋
| +---> 可以
| +---> 吃
| | +---> 吃
| +---> 吗
"""
为了方便对结果进行调用以及可视化,输出结果已经被封装为一个SentTree类。这个类有三个属性:template(当前主模版)、content(当前主模版覆盖的字符串)、modules(语义块的list,每个语义块也是用SentTree来描述)。总的来说,就是按照《最小熵原理(三):“飞象过河”之句模版和语言结构》一文中我们对语言结构的假设来设计的。
待续 #
如果有必要,请阅读源码寻求答案~后续有更新会继续在这里演示。
转载到请包括本文地址:https://kexue.fm/archives/5597
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (May. 31, 2018). 《基于最小熵原理的NLP库:nlp zero 》[Blog post]. Retrieved from https://kexue.fm/archives/5597
@online{kexuefm-5597,
title={基于最小熵原理的NLP库:nlp zero},
author={苏剑林},
year={2018},
month={May},
url={\url{https://kexue.fm/archives/5597}},
}
June 1st, 2018
苏大神你这个库自带的词典也是这个库里的函数构建的吗
自带词典来源比较复杂。其中比较新的那部分词语是通过新词发现算法来召回的。
June 8th, 2018
为什么句模板的构建如此的慢,我300多句5个小时都没好
June 8th, 2018
句子不能太长,因为他用的是动太规划,你把句子长度限制一下就好了
@33|comment-9301
没错,句子太长的原因。
主要是因为需要抽取图上所有的路径,这个路径数量是指数级别的。
July 26th, 2018
用的你的nlp_zero,速度很快,但有一点不方便,新词发现之后很多单字,能够设置输出单词的字数就好了。
自己对输出结果筛选不更好?
August 1st, 2018
您好,之前有在github上请教您transformer相关的内容。我想将transformer用在ocr上,就是不太理解解码时target那一步怎么操作:(
transformer是啥呀?
就是google那篇attention is all you need里的结构呀。
还是没理解,怎么用在ocr上?你说说你的想法?
就是先用卷积提取图片特征,将transformer的输入换成图片的输入,label就是图片里面的文字。有一篇论文已经这样做了,"NRTR: A No-Recurrence Sequence-to-Sequence Model For Scene Text Recognition".我尝试实现了一下,但是效果不好,不知道是不是哪个结构没有用对。
那我就不清楚了,没折腾过~
我也尝试实现了一下,但是发现训练不出来,拟合不了训练集
June 3rd, 2019
请问一下,有没有办法在没有生成句模板的前提下,自动识别句子的套路呢?
本文不是已经包含了“层次分解”?
谢谢苏神回复,我参照了一下层次分解的内容,事先引入了一个前缀树并导入模板,假设在没有模板的前提下,我该如何识别呢?
模版理论上可以无监督生成啊。
这里想请教苏神一个问题,假设有一堆短文本样本,有什么好的办法识别出其中的模板文本呢?苏神方便留个QQ交流一下吗?
我不知道该怎么回复你。我写了好几篇文章讲新词发现、模版构建的,还开源了程序,然后你还来问我怎么识别出模版......如果你实在不愿意看我的文章、用我的程序,那我觉得我们也没有什么必要交流下去了。