- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章浅谈分词器Tokenizer由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
分词器的作用是将一串字符串改为“词”的列表,下面以“大学生活”这个输入为例进行讲解:
对“大学生活”这句话做分词,通常来说,一个分词器会分三步来实现:
(1)找到“大学生活”这句话中的全部词做为一个集合,即:[大、大学、大学生、学、学生、生、生活、活] 。
(2)在第一步中得到的集合中找到所有能组合成“大学生活”这句话的子集,即:
[大、学、生、活] 。
[大、学、生活] 。
[大、学生、活] 。
[大学、生、活] 。
[大学、生活] 。
[大学生、活] 。
(3)在第二步中产生的所有子集中挑选一个最有可能的作为最终的分词结果.
为了得到第1步需要的集合,通常我们需要一个词典。大部分的分词器都是基于词典去做分词的(也就是说也可以不基于词典来做分词,在此暂时不做讨论)。那么现在假设我们有一个小词典:[大学、大学生、学习、学习机、学生、生气、生活、活着]。首先要在“大学生活”这句话里面匹配到这个词典里面的全部词,有些同学脑中可能会出现这种过程:
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
|
public
class
demo1{
//加载词典中的所有词汇
static
set<string> dic =
new
hashset(){{
add(
"大学"
);
add(
"大学生"
);
add(
"学习"
);
add(
"学习机"
);
add(
"学生"
);
add(
"生气"
);
add(
"生活"
);
add(
"活着"
);
}};
//匹配句子中词典中存在的所有词汇
static
list<string> getallwordsmatched(string sentence){
list<string> wordlist =
new
arraylist<>();
for
(
int
index =
0
;index < sentence.length();index++){
for
(
int
offset = index+
1
; offset <= sentence.length();offset++){
string sub = sentence.substring(index,offset);
if
(dic.contains(sub)){
wordlist.add(sub);
}
}
}
return
wordlist;
}
public
static
void
main(string[] args){
string sentence =
"大学生活"
;
getallwordsmatched(sentence).foreach(system.out::println);
}
}
|
执行这段代码会输出:
大学 。
大学生 。
学生 。
生活 。
似乎到这里,我们已经完美地完成了在词典中找到词的任务。然而真实的分词器的词典往往有几十万甚至几百万的词汇量,使用上面这种算法性能太低了。高效地实现这种匹配的算法有很多,下面简单介绍一种:
ac自动机是一种常用的多模式匹配算法,基于字典树(trie树)的数据结构和kmp算法的失败指针的思想来实现,有不错的性能并且实现起来非常简单.
引用一下百度百科对于trie树的描述:trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高.
下面一个存放了[大学、大学生、学习、学习机、学生、生气、生活、活着]这个词典的trie树:
它可以看作是用每个词第n个字做第n到第n+1层节点间路径哈希值的哈希树,每个节点是实际要存放的词.
现在用这个树来进行“大学生活”的匹配。依然从“大”字开始匹配,如下图所示:从根节点开始,沿最左边的路径匹配到了大字,沿着“大”节点可以匹配到“大学”,继续匹配则可以匹配到“大学生”,之后字典中再没有以“大”字开头的词,至此已经匹配到了[大学、大学生]第一轮匹配结束.
继续匹配“学”字开头的词,方法同上步,可匹配出[学生] 。
继续匹配“生”和“活”字开头的词,这样“大学生活”在词典中的词全部被查出来.
可以看到,以匹配“大”字开头的词为例,第一种匹配方式需要在词典中查询是否包含“大”、“大学”、“大学”、“大学生活”,共4次查询,而使用trie树查询时当找到“大学生”这个词之后就停止了该轮匹配,减少了匹配的次数,当要匹配的句子越长,这种性能优势就越明显.
再来看一下上面的匹配过程,在匹配“大学生”这个词之后,由于词典中不存在其它以“大”字开头的词,本轮结束,将继续匹配以“学”字开头的词,这时,需要再回到根节点继续匹配,如果这个时候“大学生”节点有个指针可以直指向“学生”节点,就可以减少一次查询,类似地,当匹配完“学生”之后如果“学生”节点有个指针可以指向“生活”节点,就又可以减少一次查询。这种当下一层节点无法匹配需要进行跳转的指针就是失败指针,创建好失败指针的树看起来如下图:
图上红色的线就是失败指针,指向的是当下层节点无法匹配时应该跳转到哪个节点继续进行匹配.
失败指针的创建过程通常为:
1.创建好trie树.
2.bfs每一个节点(不能使用dfs,因为每一层节点的失败指针在创建时要确保上一层节点的失败指针全部创建完成).
3.根节点的子节点的失败指针指向根节点.
4.其它节点查找其父节点的失败指针指向的节点的子节点是否有和该节点字相同的节点,如果有则失败指针指向该节点,如果没有则重复刚才的过程直至找到字相同的节点或根节点.
查询过程如下:
执行这段代码会输出:
大学 。
大学生 。
学生 。
生活 。
在匹配到了词典中所有出现在句子中的词之后,继续第二步:在得到的集合中找到所有能组合成“大学生活”这个句子的子集。但是在这个地方遇到了一个小问题,上面查到的4个词中仅有“大学”和“生活”这两个词可以组成“大学生活”这个句子,而“大学生”和“生活”则无法在匹配到的词中找到能够与其连接的词汇。现实情况中,词典很难囊括所有词汇,所以这种情况时有发生。在这里,可以额外将单个字放到匹配到的词的集合中,这得到了一个新集合:
[大学、大学生、学生、生活]u[大、学、生、活] = [大学、大学生、学生、生活、大、学、生、活] 。
可以用一个有向图来表示这个集合的分词组合,从开始节点到结束节点的全部路径就是所有分词方式.
那么在这些可能的分词组合中,应该选取哪一种作为最终的分词结果呢?大部分分词器的主要差异也体现在这里,有些分词器可能有很多不同的分词策略供使用者选择。例如最少词策略,就是在有向图中选择能够达到结束节点的全部路径中最短(经过最少节点)的一条。对于上面这张有向图,最短路径有两条,分别是“大学,生活”与“大学生,活”最终的分词结束就在这两条路径中选择一条。这种选择方法最为简单,性能也很高,但是准确性较差。其实仔细考虑一下不难发现,无论使用哪种分词策略,其目的都是想要挑选出一条最可能正确的,也就是概率最大的一种。“大学生活”分词为[大、学、活]的概率为p(大)p(学|大)p(生|大,学)p(活|大,学,生),这就是说,想要计算其的概率,需要知道“大”的出现概率,“大”出现时“学”出现的概率,“大”、“学”同时出现时“生”的概率,“大”,“学”,“生”同时出现时出现“活”的概率。这些出现概率可以在一份由大量文章组成的文本库中统计得出,但是问题是,如果词典要记录任意n个词出现时出现词w的概率,一个存放m个词汇的词典需要存放m^n量级的关系数据,这个词典会太大,所以通常会限制n的大小,一般来说,n为2或者为3,计算条件概率时只考虑到它前面2到3个词,这是基于马尔可夫链做的简化。当n为2时称为二元模型,n为3时称为三元模型。一个有50万词的词典的二元模型需要50万*50万条关系,这也是相当大的一个量级,可以对其进行压缩或转化为其它近似形式,这部分相对比较复杂,在此不作讲解,这里使用更简单一些的形式,假设每个词的出现都是独立事件,令p(大,学,生,活)=p(大)p(学)p(生)p(活)。要计算这个概率,只需要知道每个词的出现概率,一个词的出现概率=词出现的次数/文本库中词总量。那么将之前使用的词典更新为[大学5、大学生4、学习6、学习机3、学生5、生气8、生活7、活着2] 后面的数字是这些词在文本库中出现的次数,文本库中词的问题就是这些词出现次数之和=5+4+6+3+5+8+7+2=40 。
那么p(大学,生活)=p(大学)p(生活)=5/40*7/40 。
p(大学生、活)=p(大学生)p(活)=4/40*0/40 在这个地方出现了问题,对于词典里不存在的词,它的概率是0,这将会导致整个乘积是0,这是不合理的,对于这种情况可以做平滑处理,简单地来说,可以设词典中不存在的词的出现次数为1,于是p(大学生、活)=p(大学生)p(活)=4/40*1/40 。
最终可以挑选出一条最有可能的分词组合。至此第三步结束.
以上就是浅谈分词器tokenizer的详细内容,更多关于分词器tokenizer的资料请关注我其它相关文章! 。
原文链接:https://www.cnblogs.com/zf-blog/p/12582533.html 。
最后此篇关于浅谈分词器Tokenizer的文章就讲到这里了,如果你想了解更多关于浅谈分词器Tokenizer的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
大家好,其实我快疯了,我竭尽全力解决这个简单的问题。 如您所见,狭窄空间中的简单标签导致单个单词“Verification”被分成两行,这当然是 Not Acceptable 。 我知道我只能将行数设
我正在尝试创建类似句子的东西,其中包含随机单词。具体来说,我会有类似的东西: "The weather today is [weather_state]." 并且能够做一些事情,比如找到 [brack
我希望我的导航栏 (.top-bar) 比现在更具响应性。目前,如果屏幕缩小太多,.top-bar-right 类只会下降到 .menu 类之下。我需要 .top-bar-right 来分割自己或打破
我正在尝试编写一个函数来将命令行参数解析为一个 vector 。问题是我似乎无法消除使用全局指针数组作为 vector 。 代码是: /** parse command line arguments
我正在做一些分词实验,如下所示。 lst是一个字符序列,output是所有可能的词。 lst = ['a', 'b', 'c', 'd'] def foo(lst): ... retu
我正在尝试解决 this问题。问题如下 给定一个输入字符串和一个单词字典,看看是否可以将输入字符串分割成以空格分隔的字典单词序列。 字典是一个字符串数组。 我的方法是以下递归 fn 并存储递归调用的结
我正在研究这个问题。似乎我找到了正确的答案并返回 true,但随后它被 false 覆盖。Java 新手,抱歉,如果这是一个虚拟问题。我如何返回 true?预先感谢您 问题给定一个字符串 s 和一本单
我正在使用 word-break css 属性,但即使是一个简单的示例似乎也无法让它工作。我的代码是: react : render() { return ( A very very lo
我正在尝试更改 word-break某些内联元素的属性,例如 和 以获得更好的页面内容流。 Firefox 似乎只识别显示为 block 的元素的分词属性(例如 ),而 Chrome 尊重分词的请求
我想标记用户输入的任何字符串。我的代码是这样的: #include #include #include int main(void) { char str; char *toke
有没有办法让单词正确对齐?我尝试添加 word-break 和 word-wrap 属性,但没有任何不同。 Subtotal S$42.50 Tota
如何防止 Bash 拆分子字符串中的单词?这是一个有点人为的例子来说明这个问题: touch file1 'foo bar' FILES="file1 'foo bar'" ls -la $FILES
我正在创建一个非常薄的页面(它被打印在收据纸上:56 毫米宽) 我正在尝试显示一些文本(在本例中为运送选择)。有时这个文本是正常的一些间隔单词,例如'Signed for 1st Class',有时是
我正在尝试弄清楚 IFS 如何影响 bash 中的分词。该行为依赖于上下文,其方式似乎与分词的直觉不符。 总体思路似乎很简单。引自 bash 手册页: The shell treats each ch
今天我 Handlebars 机升级到 iOS7,发现了一些奇怪的问题。 (博客.niwyclin.org)这是我网站的测试帖子页面 在桌面浏览器上它看起来不错。 我用Responsivator查了一
我在 jsfiddle 中有以下示例: https://jsfiddle.net/27L545rr/3/ Word-break should cause just the extra charact
我有一个应用程序,我需要解析或标记 XML 并保留原始文本(例如,不解析实体、不转换属性中的空格、保持属性顺序等)在 Java 程序中。 我今天花了几个小时尝试使用 StAX、SAX、XSLT、Tag
到目前为止,这是我的代码: ssssssssssssssssssssssssssssssssssssss 但是, word-wrap:break-word; word-br
我正在尝试使用 word-break打破一个长字符超过其父宽度的单词。 在这个例子中,我有一个 与 width:43px和里面的“玩”字。在 chrome 中,这个词很合适,但在 Firefox 中,
list(gensim.utils.simple_preprocess("i you he she I it we you they", deacc=True)) 给出结果: ['you', 'he'
我是一名优秀的程序员,十分优秀!