合肥工业大学自然语言处理实验报告

工程报告
目录
1 研究背景 4
2 工程目标 7
2.1 工程一 7
2.2 工程二 7
2.3 工程三 7
2.4 工程四 7
3 实验环境与工具 7
4 模型方法 8
4.1 n-gram模型 8
4.2 模型的平滑 9
4.2.1 Add-one 9
4.2.2 Add-k 9
4.2.3 Backoff 10
4.2.4 Interpolation 10
4.2.5 Absolute discounting 10
4.2.6 Kneser-Ney 10
4.2.7 Modified Kneser-ney 11
4.3 最大匹配法 11
4.3.1 最大前向匹配 11
4.3.2 最大后向匹配 11
4.3.3 基本原则 12
4.4 隐马尔科夫模型 12
4.5 维特比算法 12
5 系统设计 13
5.1 宋词词频统计 13
5.2 宋词自动生成 14
5.3 中文词频统计 16
5.4 中文词法分析系统 18
6 系统演示 23
7 课程学习心得 25
参考文献 25
附录A:源代码 25

1 研究背景

自然语言理解,研究用电子计算机模拟人的语言交际过程,使计算机能理解和运用人类社会的自然语言如汉语、英语等,实现人机之间的自然语言通信,以代替人的部分脑力劳动,包括查询资料、解答问题、摘录文献、汇编资料以及一切有关自然语言信息的加工处理。这在当前新技术革命的浪潮中占有十分重要的地位。
自然语言处理的发展历程是一个艰辛且充满机遇的过程,从提出至今已有70多年的历史,但是现在仍处于期望膨胀期,且被评为了AI未来十年重点发展方向。在其发展过程中,可分为三个阶段:第一个阶段为符号主义与经验主义占主流的阶段;第二个阶段为理性主义与统计主义占主流的阶段;第三个阶段为连接主义占主流的阶段。在第三阶段连接主义时,可以从最初的神经元数学模型(MP模型)开始,到1960至1970年代的连接主义(如脑模型),再到Rumelhart等在1986年建立的反向传播算法[1],最终过渡到近几年的循环神经网络(Recurrent Neural Network,RNN)、长短期记忆网络(Long Short-Term Memory,LSTM)、词嵌入(word embedding)、注意力模型(Attention Model,AM)等,以时间为主线,理论与技术的发展为脉络进行介绍与分析。自然语言处理发展历程可从以下几个维度进行研究。
1.1 从不同学派的理论与技术看待自然语言处理发展历程
自然语言处理高度融合了数学,计算机等学科,研究方向中也包含了神经科学,生物学,心理学等。在自然语言处理发展的历史中,我们会看到不同学派之间的碰撞。正如哲学中的经验主义与理性主义一样,自然语言处理领域中的经验主义(统计主义)更看重的是证据,即所有理论和假设都必须被实验来检验。所以我们可以发现经验主义的方法都是统计的整个语料库中的概率,以此作为经验来验证我们的后续任务。而自然语言处理领域中的理性主义(符号主义),也如哲学中的理性主义,认为大部分的知识是来自感觉上的独立思考。就像正则表达式只提取我们需要的内容,而不会关心这个词在句子中是否是一个独立的个体。
在多个学派各自探索与研究的过程中,在初期时代是孤立的。可是随着学者们的不断前进,呈现出了高度综合化的趋势。自然语言处理在21世纪迎来发展的高潮,正是从图像与语音领域得到了启发,结合了深度学习才得到的成果。而这个高潮时代也离不开其他领域发展所带来的贡献,如果没有计算机科学、脑神经科学与认知科学不断发展而带来神经网络的高潮[2],也不会有连接主义的出现。随着学者们对自然语言处理研究的深入,不同学派之间也产生了融合。
1.2 从单一技术的发展看待自然语言处理发展历程
恩格斯的否定之否定规律揭示了事物发展的方向和道路[3]。在自然语言处理知识体系中,学者们对词嵌入技术的研究过程是一个不断螺旋上升的过程。学者们提出了一个新的词向量构建技术,后人在研究的过程中会发现其的不足,并加之完善。这样的过程伴随着词嵌入这项技术的发展。虽然在研究的过程中出现了许多问题,甚至谬误,但技术发展并未停滞,在学者们一次又一次地发现了不足后,加之完善,达到了更好的境地。可见词嵌入技术的发展历程是螺旋式、曲折式前进的过程。以否定之否定规律来看,词嵌入技术的发展历程可看成是技术内部矛盾引起的自我否定,内部矛盾转化,最终解决矛盾的过程[4]。而从整个词嵌入技术的发展过程来看,我们也可以推测出整个自然语言处理领域的发展是类似的情况,都是在前人工作中发现不足,并进行改正,不断完善理论的一个过程。在研究该领域的过程中,我们要从其历史进行科学的解读,针对出现的新问题,发现过去理论的不足,改正这些错误,并不断完善这个领域的理论。
1.3 从系统思维看待自然语言处理发展历程
系统思维是指将事物看成系统进行分析与处理[5]。在整个自然语言处理的过程中,自然语言处理可看作为一个系统,它是人工智能系统的一个子系统。系统理论与方法对自然语言处理技术的发展起重要的指导和促进作用。
系统理论着重从全局和整体两方面分析问题,注重事物之间的复杂联系,而不单方面地分析问题[6]。就像在自然语言处理的研究中,现阶段的工作都是基于文档、基于句子甚至是基于词语的,虽然我们能够将其看作为一个系统,但是人类在表达一句话的时候有可能会暗含许多信息(比如输入“520快乐”给计算机,计算机只能识别出一串数字“520”和一个词“快乐”,却不能够理解“520”暗指网络情人节)。这就表明了尽管现在自然语言处理正在蓬勃发展,但是却有一定的缺陷。近年来知识工程学科的研究就正在致力于解决计算机没有外部知识这一问题[7]。从这个方面来说,尽管现在对自然语言处理系统化的研究已经有了显著成果,但是依旧需要学者们站在一个更为宏观的角度来系统化看待这个问题。
1.4 从数学方法看待自然语言处理发展历程
数学是辩证的辅助手段和表达方式[8]。在学者研究自然语言处理的过程中,特别是统计主义学派与连接主义学派及其融合学派,通过发现自然语言的内在规律,对其进行数学建模,将其用数学公式形式化表达出来。正是由于数学作为基石,自然语言处理才能够稳步发展。计算机处理文字是一件复杂的事,正是由于词嵌入技术将文字映射到向量空间,才能够降低计算机运行的成本。在隐含马尔科夫模型中,通过双随机过程,能够实现为词语打标签的工作。数学无处不在,在发展过程中扮演举足轻重的作用,正是由学者将发现的规律进行简化并形式化表达为数学公式,才能够有层出不穷的新技术诞生。
1.5 从联系与发展看待自然语言处理发展历程
自然语言处理的发展历史上,离不开辩证法中联系与发展的两大特征。如语言模型[9],正是学者们发现了相邻的词语之间存在关联关系,并对这种内在规律建模,才有了相应的研究。同时,正因为其他学科的学者们在自然界等发现了相应的规律,才有学者能够将其引入到自然语言处理中。注意力机制,也正是学者们研究了人类的注意力机制,发现了其中的客观性与普遍性,才能够通过数学对其建模,而引入到计算机科学中,接着才能够应用到自然语言处理领域。从以上两个例子我们可以看出,学者们不仅对一句话中的词语之间的关联关系进行了研究,还将自然界中的客观现象引入到了自然语言处理中,这也反映了辩证法联系的思想。
从自然语言处理技术的发展路线我们可以发现,这种发展是具有普遍性的,并没有因为过程的曲折而致使技术停滞不前。且学者们通过分析总结前人的不足,并加之完善,这体现出了发展的实质:事物的前进与上升。尽管事物的前进方向是光明的,但是学者们用了几年甚至几十年才慢慢完善该学科,也就体现出了事物发展道路的曲折性。最后我们可以发现,如果不是之前学者们从one-hot编码到语言模型的提出,也不会有Word2Vec这种模型的创建。Word2Vec正是吸收了前面词嵌入技术的精华,而提炼出的一个全新技术,这正印证了辩证法发展性中量变引起质变的特性。
1.6 从学术前沿看待自然语言处理发展历程
自然语言处理中多学科交叉融合的优势越来越明显,在研究过程中会有更多的新思想、新技术融合到自然语言处理,自然语言处理的应用领域越来越广泛。自然语言处理的学术前沿代表当前学术界或工业界需解决的理论或技术问题,教学过程中适当地将学术前沿理论融入自然语言处理发展历程中进行教学,增强学生自主学习动力,激发学生的科研兴趣。比如,计算机在处理文本数据时,它并不能很好地识别出一个句子中包含的外部知识和真实情感,我们需要研究在知识指导下的自然语言处理技术,建立知识获取、知识产生、知识表达等与自然语言处理的方法与技术,使计算机能够智能认知和理解自然语言。同时,其他学科所产生的新理论、新技术为自然语言处理提供了良好的基石,可以引导学生尝试借鉴这些新理论与新技术构建出新模型、新技术,推动自然语言处理的新发展。

2 工程目标

2.1 工程一

宋词词频统计
语料库: ci.txt
要求: 编程序, 输入ci, 自动分析统计ci.txt, 统计宋词的单字词, 双字词等。统计后,输出的是单字词和双字词的词典文件。文件中包括相应的词和频度(次数)。

2.2 工程二

宋词自动生成
语料库:ci.txt
要求: 输入词牌,基于宋词的词典和宋词的词牌,可以随机或者按照语言模型,自动生成宋词。设计相应的Ui或者Web界面。

2.3 工程三

中文词频统计
语料库:1998-01-2003版-带音.txt
要求:输入txt文件,统计1元模型和2元模型,输出单词和词频文件,双词和词频文件。设计相应的接口,能够快速载入文件,并检索单词和双词。

2.4 工程四

中文词法分析系统
语料库:1998-01-2003版-带音.txt
要求:根据构建的单词词典和双词词典,用n-gram模型,或者前向最长匹配,或者后向最长匹配等算法,鼓励用更复杂一些的方法来进行,包括隐马尔科夫模型和条件随机场模型。

3 实验环境与工具

编辑工具:Visual Studio Code
编译工具:python 3.9.7
界面工具:PyQt5 designer
用到的库:re; collections; random; PyQt5 5.15.4;
版本工具:Git
操作系统:Windows10

4 模型方法

4.1 n-gram模型

N-gram模型属于概率语言模型,其要解决的问题其实很简单,那就是从是否符合自然语言的角度评估一个句子的质量。在这个问题中,句子W被建模成词的排列 W = w1, w2,…wn,所谓质量就就是句子出现的概率,也即该排列出现的联合概率:

但是要计算概率要么通过概率密度函数,要么用统计概率近似。自然语言的概率密度函数我们肯定是无法知道了,那么就要用统计概率,所以必须要有一个自然语言的样本库,里面包含了大量的自然语言的句子实例,在N-gram模型中用到的便是语料库。
那么怎么通过语料库来计算句子出现的概率呢?这就涉及到马尔科夫链的假设,即当前词出现的概率 P (wi)仅与前N-1个词有关,这里的N就对应N-gram里面的N。所以N-gram模型的构建只需要计算出所有的N以内的条件概率即可:

例如常用的当N=3时,句子W出现概率就简化为:

可以看到当N增大时,需要计算的概率将会大大增多,其计算量几乎是与N成指数关系的增长(因为每多考虑一阶,每个词都大约需要多考虑M个“新的概率”)。谷歌当初使用的N=4的语言模型需要500台以上的服务器进行存储,这是不可想象的。
而且即使N取的再大,也无法完全覆盖后文对前文的依赖关系,语言的上下文联系可以跨度很大,而N的增长对计算量增大带来的压力完全无法满足这种大跨度的上下文联系。
N-gram模型是基于对语料库的统计来进行训练的,举例来说,对于Bigram模型,我们要计算 p (like∣you),那么公式为:

其中 C(wi)表示语料库中单词wi出现的次数。
仍然以Bigram模型为例,我们的目标函数是:

由于又涉及到多个概率连乘问题,所以我们映射到对数空间,即

我们的任务是最大化这个对数似然,因此这种参数估计的方式就叫做MLE(Maximum Log Likelihood)

4.2 模型的平滑

4.2.1 Add-one

Add-one 是最简单、最直观的一种平滑算法,既然希望没有出现过的N-gram的概率不再是0,那就直接规定在训练时任何一个N-gram在训练预料至少出现一次(即规定没有出现的,在语料中也出现一次),因此:Countnew(n-gram) = countold(n-gram)+1; 于是对于n-gram的模型而言,假设V是所有可能的不同的N-gram的类型个数,那么根据贝叶斯公式有:

然这里的n-gram的可以相应的改成uingram和bigram表达式,并不影响。其中C(x)为x在训练中出现的次数,wi为给定的训练数据中第i个单词。
这样一来,训练语料库中出现的n-gram的概率不再为0,而是一个大于0的较小的概率值,Add-one平滑算法确实解决了我们的问题,但是显然它也并不完美,由于训练语料中未出现的n-gram数量太多,平滑后,所有未出现的占据了整个概率分布的一个很大的比例,因此,在自然语言处理中,Add-one给语料库中没有出现的n-gram分配了太多的概率空间。此外所有没有出现的概率相等是不是合理,这也是需要考虑的。

4.2.2 Add-k

由Add-one衍生出来的另一种算法就是Add-k,既然我们认为加1有点过了,那么我们可以选择一个小于1的正数k,概率计算公式就可以变成如下表达式:

它的效果通常会比Add-one好,但是依旧没有办法解决问题,至少在实践中,k必须认为的给定,而这个值到底多少该取多少都没有办法确定。

4.2.3 Backoff

回退模型,思路实际上是:当使用Trigram的时候,如果Count(trigram)满足条件就使用,否则使用Bigram,再不然就使用Unigram.公式如下,其中d,a和k分别为参数。k一般选择为0,但是也可以选其它的值。

4.2.4 Interpolation

Interpolation插值法和回退法的思想非常相似,设想对于一个trigram的模型,我们要统计语料库中“”“I like you”出现的次数,结果发现它没有出现,则计数为0,在回退策略中们将会试着用低阶的gram来进行替代,也就是用“like you”出现的次数来替代。在使用插值的时候,我们把不同阶层的n-gram的模型线性叠加组合起来之后再使用,简单的如trigram的模型,按照如下的方式进行叠加:

4.2.5 Absolute discounting

插值法使用的参数实际上没有特定的选择,如果将lamda参数根据上下文进行选择的话就会演变成Absolute discounting。对于这个算法的基本想法是,有钱的,每个人交固定的税D,建立一个基金,没有钱的根据自己的父辈有多少钱分这个基金。比如对于bigram的模型来说,有如下公式。

4.2.6 Kneser-Ney

这种算法是目前一种标准的而且是非常先进的平滑算法,它其实相当于前面讲过的几种算法的综合。它的思想实际上是:有钱的人,每个人交一个固定的税D,大家一起建立一个基金,没有钱的呢,根据自己的的父辈的“交际的广泛”的程度来分了这个基金。这里交际的广泛实际上是指它父辈会有多少种不同的类型,类型越多,这说明越好。其定义式为:

其中max(c(X)-D,0)的意思是要保证最后的计数在减去一个D后不会变成一个负数,D一般大于0小于1。这个公式递归的进行,直到对于Unigram的时候停止。而lamda是一个正则化的常量,用于分配之前的概率值(也就是从高频词汇中减去的准备分配给哪些未出现的低频词的概率值(分基金池里面的基金))。

4.2.7 Modified Kneser-ney

这一种方法是上一种方法的改进版,而且也是现在最优的方法。上一个方法,每一个有钱的人都交一个固定的锐,这个必然会出现问题,就像国家收税一样,你有100万和你有1个亿交税的量肯定不一样这样才是比较合理的,因此将上一种方法改进就是:有钱的每个人根据自己的收入不同交不同的税D,建立一个基金,没有钱的,根据自己的父辈交际的广泛程度来分配基金。

4.3 最大匹配法

4.3.1 最大前向匹配

设MaxLen表示最大词长,D为分词词典
(1) 从待切分语料中按正向取长度为MaxLen的字串str,令Len=MaxLen;
(2) 把str与D中的词从左往右相匹配;
(3) 若匹配成功,则认为该字串为词,指向待切分语料的指针向前移Len个汉字,返回到(1);
(4) 若不成功:如果Len>1,则将Len减1,从待切分语料中
取长度为Len的字串str,返回到(2)。否则,得到长度为2的单字词,指向待切分语料的指针向前移1个汉字,返回(1)。

4.3.2 最大后向匹配

设MaxLen表示最大词长,D为分词词典
(1) 从待切分语料中按正向取长度为MaxLen的字串str,令Len=MaxLen;
(2) 把str与D中的词从右往左相匹配;

(3) 若匹配成功,则认为该字串为词,指向待切分语料的指针向后移Len个汉字,返回到(1);
(4) 若不成功:如果Len>1,则将Len减1,从待切分语料中取长度为Len的字串str,返回到(2)。否则,得到长度为2的单字词,指向待切分语料的指针向后移1个汉字,返回(1)。

4.3.3 基本原则

颗粒度越大越好:用于进行语义分析的文本分词,要求分词结果的颗粒度越大,即单词的字数越多,所能表示的含义越确切。
切分结果中非词典词越少越好,单字字典词数越少越好,这里的“非词典词”就是不包含在词典中的单字,而“单字字典词”指的是可以独立运用的单字。

4.4 隐马尔科夫模型

存在一类重要的随机过程:如果一个系统有N个状态S1,S2,…,SN,随着时间的推移,该系统从某一状态转移到另一状态。如果用q 表示系统在时间t的状态变量,那么,t时刻的状态取值为S(1<=j<=N) 的概率取决于前t-1 个时刻(1,2,…,t-1)的状态,该概率为:
假设一:如果在特定情况下,系统在时间t 的状态只与其在时间t-1 的状态相关,则该系统构成一个离散的一阶马尔可夫链:

假设二:假设二:如果只考虑公式(14)独立于时间t的随机过程,即所谓的不动性假设,状态与时间无关,那么:

该随机过程称为马尔可夫模型。

4.5 维特比算法

(1) 从点S出发,对于第一个状态X的各个节点,不妨假定有n个,计算出S到它们的距离d(S,X),其中X代表任意状态1的节点。因为只有一步,所以这些距离都是S到它们各自的最短距离。
(2) 对于第二个状态X的所有节点,要计算出从S到它们的最短距离。对于特点的节点X,从S到它的路径可以经过状态1的n中任何一个节点X,对应的路径长度就是d(S,X) = d(S,X) + d(X,X)。由于j有n种可能性,我们要一一计算,找出最小值。
这样对于第二个状态的每个节点,需要n次乘法计算。假定这个状态有n个节点,把S这些节点的距离都算一遍,就有O(n·n)次计算。
(3) 接下来,类似地按照上述方法从第二个状态走到第三个状态,一直走到最后一个状态,就得到了整个网格从头到尾的最短路径。每一步计算的复杂度都和相邻两个状态S和S各自的节点数目n,n的乘积成正比,即O(n·n)
(4) 假设这个隐含马尔可夫链中节点最多的状态有D个节点,也就是说整个网格的宽度为D,那么任何一步的复杂度不超过O(D),由于网格长度是N,所以整个维特比算法的复杂度是O(N·D)。

5 系统设计

5.1 宋词词频统计

本工程模块划分:

函数名称 函数输入 函数输出
get_one_word() 宋词语料库 单字词词典文件
get_two_word() 宋词语料库 双字词词典文件
Main() 程序开始 程序结束

表 1 工程一模块划分
对于宋词词频的统计,主要是对单字词的统计和双字词的统计。在宋词词频统计的系统当中,利用两个词典word_dict,word_dict2,分别用于存储单字词的词频和双字词的词频信息。
合肥工业大学自然语言处理实验报告_第1张图片
图 1 统计单字词代码
单字词基本思想:对Ci.txt逐行读取每一个字,判断读取的每一个字的utf-8编码是不是在汉字编码值范围内,若在,则为单字词,将其放入列表。读取完毕后,用字典对列表中的所有字进行字数统计,若字典中已经存在该字,则直接将字典中该字对应的键值加一,否则,在字典中新増一个键,键为该字,键值为1。统计完毕后,将其排序写入单字词.txt中。其中,可设置一个特殊符号字典来去掉特殊符号,字典如下:
exclude_str = ",。!?、( )【 】 < > 《 》 = : + - * — “ ” … "+ ‘\n’
合肥工业大学自然语言处理实验报告_第2张图片
图 2 统计双字词代码
双字词基本思想:双字词的统计过程和单字词类似,因为统计的是双字词,所以判断条件变成了当前读取的字及其下一个字是否均为汉字。对Ci.txt逐行读取每一个字,判断读取的每一个字的utf-8编码及其下一个字的utf-8编码是不是都在汉字编码值范围内,若都在,则为双字词,将其放入列表。读取完毕后,用字典对列表中的所有词进行词数统计,若字典中已经存在该词,则直接将字典中该词对应的键值加一,否则,在字典中新増一个键,键为该词,键值为1。统计完毕后,将字典按字频排序后写入双字词.txt中。其中汉字的utf-8编码范围是’\u4e00’到’\u9fa5’。此处也可以用正则表达式去掉。
最后将统计好的字典按照键值对的大小排列,由大到小写入到文件中去。

5.2 宋词自动生成

本工程模块划分:

函数名称 函数输入 函数输出
main() 程序开始 程序结束
Ui_MainWindow() 初始化一个界面对象
Mywindow() 在界面上输入词牌名 将生成的词显示在界面
create_Songci() 词牌名 生成词

表 2 工程二模块划分
用PyQt5简单的设计了一个界面。
合肥工业大学自然语言处理实验报告_第3张图片
图 3 PyQt5设计的界面
程序第一次运行先将工程一中的保存好的“单字词.txt”和“双字词.txt”加载到内存中。然后再将宋词语料库加载到内存中。因为宋词的固定格式是根据词牌名来的,所以当用户输入词牌名时需要在语料库中寻找是否有对应的词牌名,没有则随机生成一个格式的词。若有相应的词牌名,程序会根据系统已有的词的格式加载到一个列表中,(格式如下:
[3, ‘,’, 5, ‘。’, 3, ‘,’, 5, ‘。’],意识是先随机三个字或词,然后逗号,五个词,句号等等)。寻找的词过程就是在工程一保存的文件接口中随机选择词。
起初我设计的代码是这样的:
合肥工业大学自然语言处理实验报告_第4张图片
图 4 第一次设计的代码
其中为了满足格式的要求,用了大量的if-else语句来符合题意,不适合读也不适合写,后来优化成了以下的方法:
合肥工业大学自然语言处理实验报告_第5张图片
图 5 第二次设计的代码
这样设计代码清晰明了,没事随机选择一个数,数的范围是1和2,若当前的值减去选择的随机数大于等于零,说明可以填一个词,那就填词,否则再次选择随机数。

5.3 中文词频统计

本工程模块划分:

函数名称 函数输入 函数输出
Main() 程序开始 程序结束
get_one_word() 1998-01-2003版-带音 单词和词频文件
get_two_word() 1998-01-2003版-带音 双词和词频文件

表 3 工程三模块划分
首先读取语料库:1998-01-2003版-带音.txt,用正则表达式将语料库中的词性和括号去掉:
合肥工业大学自然语言处理实验报告_第6张图片
图 6 用正则表达式去掉词性和括号
单词并不是一个字的意思,而是一个词或者一个字,分割之后不能表达原意的词,即分割失意的词。对得到的text列表逐行读取,把不是和的词元素加入到单词表,把相邻的两个词组成双词放入双词表中。
合肥工业大学自然语言处理实验报告_第7张图片
图 7 统计单词和双词
对单双字词统计词频完后,进行对一元\二元模型.txt.文件进行输出,填充文件。下一步则是实验四的内容,利用最大向前算法对句子进行切分,在此步骤前要对后续输入的句子进行分句处理,目的是以免效率过低带来麻烦。
设计思路是:首先在句首和句尾都添加一个索引,然后尾部的索引逐步往前移,同时判断索引间是否为文件中的单字词,直到索引相遇,如果词出现在了词典里,即索引间内容是一个词,则将其当成一个单字词,若这个词没有出现在词典里,则把这个字单独分开。随后首部索引将定位在我们已经划分过的词后面,尾部索引位置依旧是句尾,按上述操作循环进行,直到所有的字都被我们划入了字词块中。
最后程序输入要查询的词,在创建的接口中查询,若返回的个数不为0,则语料库中存在该词,输出该词的词性(单词或双词)。

5.4 中文词法分析系统

本工程模块划分:

函数名称 函数输入 函数输出
Main() 一个待分析的句子 两种分析结果
Get_max_forword_split_sentence() 一个待分析的句子 最大前向概率划分的句子
Get_max_backword_split_sentence() 一个待分析的句子 最大后向概率划分的句子

表 4 工程四模块划分
程序首先输入待分析的句子,然后将句子传递参数给分析的两个函数进行句子的划分:
最大前向匹配:
设MaxLen表示最大词长,D为分词词典
(1) 从待切分语料中按正向取长度为MaxLen的字串str,令Len=MaxLen;
(2) 把str与D中的词从左往右相匹配;
(3) 若匹配成功,则认为该字串为词,指向待切分语料的指针向前移Len个汉字,返回到(1);
(4) 若不成功:如果Len>1,则将Len减1,从待切分语料中
取长度为Len的字串str,返回到(2)。否则,得到长度为2的单字词,指向待切分语料的指针向前移1个汉字,返回(1)。
合肥工业大学自然语言处理实验报告_第8张图片
图 8 最大前向匹配流程图
合肥工业大学自然语言处理实验报告_第9张图片
图 9 最大前向匹配代码实现
最大后向匹配:
设MaxLen表示最大词长,D为分词词典
(1) 从待切分语料中按正向取长度为MaxLen的字串str,令Len=MaxLen;
(2) 把str与D中的词从右往左相匹配;
(3) 若匹配成功,则认为该字串为词,指向待切分语料的指针向后移Len个汉字,返回到(1);
(4) 若不成功:如果Len>1,则将Len减1,从待切分语料中取长度为Len的字串str,返回到(2)。否则,得到长度为2的单字词,指向待切分语料的指针向后移1个汉字,返回(1)。
逆向最大匹配法从被处理文档的末端开始匹配扫描,每次取最末端的m个字符(m为词典中最长词数作为匹配字段,若匹配失败,则去掉匹配字段最前面的一个字,继续匹配。相应地,它使用的分词词典是逆序词典,其中的每个词条都将按逆序方式存放。在实际处理时,先将文档进行倒排处理,生成逆序文档。然后,根据逆序词典,对逆序文档用正向最大匹配法处理即可。由于汉语中偏正结构较多,若从后向前匹配,可以适当提高精确度。所以,逆向最大匹配法比正向最大匹配法的误差要小
合肥工业大学自然语言处理实验报告_第10张图片
图 10 最大后向匹配流程图
合肥工业大学自然语言处理实验报告_第11张图片
图 11 最大后向匹配代码实现
双向最大匹配:
将正向最大匹配法得到的分词结果和逆向最大匹配法得到的结果进行比较,然后按照最大匹配原则,选取词数切分最少的作为结果。据研究表明,对于中文中90.0%左右的句子,正向最大匹配和逆向最大匹配的切分结果完全重合且正确,只有大概9.0%的句子采用两种切分方法得到的结果不一样,但其中必有一个是正确的(歧义检测成功),只有不到1.0%的句子,或者正向最大匹配和逆向最大匹配的切分结果虽重合却都是错的,或者正向最大匹配和逆向最大匹配的切分结果不同但两个都不对(歧义检测失败)。这正是双向最大匹配法在实用中文信息处理系统中得以广泛使用的原因所在。双向最大匹配的规则如下所示:
如果正反向分词结果词数不同,则取分词数量较少的那个结果如果分词结果词数相同,则:
分词结果相同,就说明没有歧义,可返回任意一个结果。分词结果不同,返回其中单字较少的那个。实现代码如下:
合肥工业大学自然语言处理实验报告_第12张图片
图 12 双向匹配代码实现

6 系统演示

运行工程一中的main.py,输出结果在同路径下的“单字词.txt”和“双字词.txt”
合肥工业大学自然语言处理实验报告_第13张图片
图 13 工程一结果截图

运行工程二中的main.py,在文本框中输入词牌名,系统自动在界面输出词的内容:如下图所示
合肥工业大学自然语言处理实验报告_第14张图片
图 14 工程二结果截图
运行工程三中的main.py, 系统单词语料库.txt文件,自动统计1元模型和2元模型,输出单词和词频文件,双词和词频文件。并设计相应的接口文件,能够快速载入文件,并检索单词和双词。输入词语返回它是单字词还是双字词并返回词频。
合肥工业大学自然语言处理实验报告_第15张图片
图 15 工程三结果截图
运行工程四中的main.py, 根据工程三中构建的单词词典和双词词典文件接口, 程序输入待划分的句子,系统自动利用用前向最长匹配,后向最长匹配和双向最长匹配,来划分语句。如下所示:
合肥工业大学自然语言处理实验报告_第16张图片
图 16工程四结果截图

7 课程学习心得

谷雨老师是我们自然语言处理课的老师,他上课非常生动有趣,我记忆最深刻的是有一堂课讲古诗如何翻译成英语,举了很多有名的句子,很有趣。我清楚的记得有 “浮世三千,吾爱有三。日,月与卿。日为朝,月为暮,卿为朝朝暮暮。”
课程的这四个实验都很有意思,自动生成宋词,自动断句,这些都让第一次接触自然语言理解的我倍感惊奇。通过这几个实验,使我更加深入的了解了自然语言这门课程。这几个实验的内容都很吸引人,并且这几个实验当中都用到了很多上课讲过的算法和思想,在做实验的过程当中,更加深刻地体会了这些算法的思想,和如何将他们转换成代码的形式呈现出来。通过实本次实验,我学习到了很多的东西。
通过对本门课的学习,我对NLP相关内容有了了解和认知。感受到了各类日常行为带来的信息量,同时也通过实验自己进行了操作,对统计规划方面打下基础。知道了这一领域的研究涉及自然语言,即人们日常使用的语言,所以它与语言学的研究有着密切的联系。希望在以后的学习路程中,借助学习本门课打下的基础,可以更熟练地探寻各类与语言学相关的联系,可以更好地发挥所学的知识,同时也希望在以后的教学里,可以更多地提供让我们亲自动手进行实验地步骤,增加书本知识与实际操作地结合时间,以达到更好的教学成果。

参考文献

  1. 中国互联网信息中心.第48次中国互联网发展状况统计报告[R].中国:CNNIC,2021.
  2. 陈艳平."自然语言处理"课程教学探索和实践[J].科教文汇(上旬刊) ,2020(12):135-136.
  3. 计算语言学中的语言模型[J]. 冯志伟,丁晓梅. 外语电化教学. 2021(06)
  4. 生成词向量的三种方法[J].冯志伟.外语电化教学.2021(01)
  5. Efficient Estimation of Word Representations in Vector Space[J].Tomas Mikolov,Kai Chen 0010,Greg Corrado,Jeffrey Dean.CoRR .2013
  6. 自然语言处理中的预训练范式[J]. 冯志伟,李颖. 外语研究. 2021(01)
  7. 神经网络、深度学习与自然语言处理[J].冯志伟.上海师范大学学报(哲学社会科学版).2021(02)
  8. 深度学习在自然语言处理领域的研究进展[J]. 江洋洋,金伯,张宝昌. 计算机工程与应用. 2021(22)
  9. A broad-coverage challenge corpus for sentence understanding through inference. Williams A,Nangia N,Bowman S R. . 2017

附录A:源代码

实验一

'''
Main.py
Author: Martin
Date: 2022-10-13 19:05:43

宋词词频统计
语料库: ci.txt
要求: 编程序, 输入ci, 自动分析统计ci.txt, 统计宋词的单字词, 双字词等。
统计后,输出的是单字词和双字词的词典文件。文件中包括相应的词和频度(次数)。
'''
file_path_in = r'D:\a表格\课件\大三上\自然语言处理\实验\Ci.txt'
file_path_out_1 = r'D:\a表格\课件\大三上\自然语言处理\实验\实验一\单字词.txt'
file_path_out_2 = r'D:\a表格\课件\大三上\自然语言处理\实验\实验一\双字词.txt'
exclude_str = ",。!?、( )【 】 < > 《 》 = : + - * — “ ” … " + '\n'

def get_one_word():
    '''
    写入文件 file_path_out_1
    '''
    word_dict = {} #{word:num}
    ret_word_dict = [] #[(word,num),(word,num)]
    # 读文件
    with open(file_path_in,'r',encoding='utf-8') as fp:
        # 字典统计赋值
        for line in fp:
            line = line.strip()
            if len(line) < 10:
                continue
            for char in line:
                if char not in exclude_str:
                    # 用字典统计每个字出现的个数和频率:
                    word_dict[char] = word_dict.get(char,0)+1
    # 对字典排序:变成[(word,num),(word,num)]
    ret_word_dict = sorted(word_dict.items(),key=lambda x : x[1],reverse=True)
    # print(ret_word_dict)

    with open(file_path_out_1,'w',encoding='utf-8') as fp:
        for item in ret_word_dict:
            fp.write(item[0])
            fp.write('\t')
            fp.write(str(item[1]))
            fp.write('\n')

def get_two_word():
    '''
    写入文件 file_path_out_2
    '''
    word_dict = {} #{words:num}
    ret_word_dict = [] #[(words,num),(words,num)]
    # 读文件
    with open(file_path_in,'r',encoding='utf-8') as fp:
        # 字典统计赋值
        for line in fp:
            line = line.strip()
            if len(line) < 10:
                continue
            for i in range(len(line)-1):
                # 确保是汉字编码
                if '\u4e00'<=line[i]<='\u9fa5' and '\u4e00'<=line[i+1]<='\u9fa5':
                    # 用字典统计每个字出现的个数和频率:
                    word_dict[line[i]+line[i+1]] = word_dict.get(line[i]+line[i+1],0)+1
    # 对字典排序:变成[(word,num),(word,num)]
    ret_word_dict = sorted(word_dict.items(),key=lambda x : x[1],reverse=True)
    # print(ret_word_dict)

    with open(file_path_out_2,'w',encoding='utf-8') as fp:
        for item in ret_word_dict:

            fp.write(item[0])
            fp.write('\t')
            fp.write(str(item[1]))
            fp.write('\n')

def main():
    get_one_word()
    get_two_word()
if __name__ == '__main__':
    main()

实验二:
Ui_test2.py

'''
Author: Martin
Date: 2022-10-17 20:58:36
'''
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'd:\a表格\课件\大三上\自然语言处理\实验\实验二\test2.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("2020210593李海跃")
        MainWindow.resize(683, 383)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(580, 40, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(240, 35, 171, 21))
        self.label.setObjectName("label")
        self.textEdit_1 = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit_1.setGeometry(QtCore.QRect(270, 70, 161, 31))
        self.textEdit_1.setObjectName("textEdit_1")
        self.textEdit_2 = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit_2.setGeometry(QtCore.QRect(110, 130, 481, 141))
        self.textEdit_2.setObjectName("textEdit_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 683, 26))
        self.menubar.setObjectName("menubar")
        self.menu2020210593 = QtWidgets.QMenu(self.menubar)
        self.menu2020210593.setObjectName("menu2020210593")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.menubar.addAction(self.menu2020210593.menuAction())

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "Create!"))
        self.label.setText(_translate("MainWindow", "        请输入词牌名"))
        self.menu2020210593.setTitle(_translate("MainWindow", "2020210593"))

main.py

'''
Author: Martin
Date: 2022-10-17 20:09:59

宋词自动生成
语料库:ci.txt
要求: 输入词牌,基于宋词的词典和宋词的词牌,可以随机或者按照语言模型,自动生成宋词。设计相应的Ui或者Web界面。

'''
import random
from Ui_test2 import Ui_MainWindow
import sys
from PyQt5 import QtWidgets
import re

# 主界面
class Mywindow(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.pushButton.clicked.connect(self.btn_action)

    def btn_action(self):
        Ci_pai_name = self.ui.textEdit_1.toPlainText()
        ret = create_Songci(Ci_pai_name)
        self.ui.textEdit_2.setText('') #写入空,清除作用
        self.ui.textEdit_2.setText(ret) #写入


def create_Songci(Ci_pai_name):
    file1 = open(r'D:\a表格\课件\大三上\自然语言处理\实验\实验一\单字词.txt', 'r', encoding='utf-8')
    file2 = open(r'D:\a表格\课件\大三上\自然语言处理\实验\实验一\双字词.txt', 'r', encoding='utf-8')
    file1 = file1.read().replace('\n','\t').split('\t') #['人', '13449', '风', '12875', '花', '11627', '一', '11502']
    file2 = file2.read().replace('\n','\t').split('\t')

    # 返回的字符串  \t\n控制输出格式
    ret_str = '\t'+'\t'+'    '+Ci_pai_name + '\n' +'\n'

    # 根据词牌名,查找输出格式。
    file3 = open(r'D:\a表格\课件\大三上\自然语言处理\实验\Ci.txt', 'r', encoding='utf-8')
    file3 = file3.read().split('\n')
    file3 = [x for x in file3 if x != '']
    # print(file3) #['酒泉子', '长忆钱塘,不是人寰是。']
    # 寻找词牌的位置
    try:
        id = file3.index(Ci_pai_name)
    except:
        #沒找到
        return '词库未找到该词牌名'
    # 词牌格式是id+1的位置
    item = file3[id+1]
    count_tmp = 0 #记录每个短句的个数
    Ci_form = [] # 词的格式[4, ',', 7, '。', 7, '。', 5, '。', 7, '。', 7, '。', 7, '。', 5, '。']
    for i in range(len(item)):
        if item[i] in ',。、( )':
            Ci_form.append(count_tmp)
            Ci_form.append(item[i])
            count_tmp = 0
        else:
            count_tmp += 1
    print('Ci_form',Ci_form)#[4, ',', 7, '。', 7, '。', 5, '。', 7, '。', 7, '。', 7, '。', 5, '。']
    

    # 按照格式随机选择生成词
    for item in Ci_form:
        print(item)
        if str(item) in ',。、()(){}::“” ':
            ret_str += item
        
        # 第一次写的废物代码,n个if
        # else:
        #     if item == 2:
        #         tmp = random.randint(0,2*len(file2))
        #         ret_str += file2[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
        #     # 三个字
        #     elif item == 3:
        #         tmp = random.randint(0,2*len(file1))
        #         ret_str += file1[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
        #         # 因为字典是#['人', '13449', '风', '12875', '花', '11627', '一', '11502']
        #         # 保证是汉字而不是数字,下面同理
        #         tmp = random.randint(0,2*len(file2))
        #         ret_str += file2[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]

        #     # 四个字
        #     elif item == 4:
        #         for i in range(2):
        #             tmp = random.randint(0,2*len(file2))
        #             ret_str += file2[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
        #     # 五个字
        #     elif item == 5:
        #         for i in range(3):
        #             if i==1:
        #                 tmp = random.randint(0,2*len(file1))
        #                 ret_str += file1[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
        #             else:
        #                 tmp = random.randint(0,2*len(file2))
        #                 ret_str += file2[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
        #     # 六个字
        #     elif item == 6:
        #         for i in range(3):
        #             tmp = random.randint(0,2*len(file2))
        #             ret_str += file2[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
        #     # 七个字
        #     elif item == 7:
        #         for i in range(4):
        #             if i==2:
        #                 tmp = random.randint(0,2*len(file1))
        #                 ret_str += file1[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
        #             else:
        #                 tmp = random.randint(0,2*len(file2))
        #                 ret_str += file2[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
        
        else:
            while item > 0:
                R_int = random.randint(1,3)
                if item - R_int >= 0: #可以填词
                    if R_int == 1: #加一个词
                        tmp = random.randint(0,2*len(file1))
                        ret_str += file1[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
                        item -= 1
                    else:
                        # 加两个词
                        tmp = random.randint(0,2*len(file2))
                        ret_str += file2[tmp//2 if (tmp//2)%2==0 else (tmp//2)-1]
                        item -= 2


    print('ret_str')
    print(ret_str)
    return ret_str

def main():
    create_Songci('酒泉子')

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mywindow = Mywindow()
    mywindow.show()
    sys.exit(app.exec_())
main()

实验三
Main.py

'''
Author: Martin
Date: 2022-10-17 21:18:40

中文词频统计
语料库:1998-01-2003版-带音.txt 
要求:输入txt文件,统计1元模型和2元模型,输出单词和词频文件,双词和词频文件。设计相应的接口,能够快速载入文件,并检索单词和双词。
'''
import re
import collections

file_path_in = r'D:\a表格\课件\大三上\自然语言处理\实验\199801.txt'
file_path_out_1 = r'D:\a表格\课件\大三上\自然语言处理\实验\实验三\单词.txt'
file_path_out_2 = r'D:\a表格\课件\大三上\自然语言处理\实验\实验三\双词.txt'

text = []
oneWords = []
twoWords = []
one_counter_list = []
two_counter_list = []

def get_one_and_two_word():
    '''
    读入文件
    写入文件
    '''
    global one_counter_list
    global two_counter_list
    global oneWords
    global twoWords
    
    # 读文件
    with open(file_path_in,'r',encoding='utf-8') as fp:
        line = fp.read().split('\n')

    # 去掉词性,存入列表
    for one in line:
        fil = re.compile("(/[a-z]*\\s{0,})")
        one = fil.split(one)
        one = one[1:] #去掉 时间
        one = one[1::2] #去掉 /m
        test = []
        test.append('')

        # 去掉括号
        for i in range(len(one)):
            word = one[i]
            fil = re.compile(r"\{.*?\}|\[.*?\]|\(.*?\)")
            word = fil.sub('', word)
            word = ''.join(word)
            if len(word) < 1:
                continue
            test.append(word)

        test.append('')

        # 除了,还有字
        if len(test) > 2:
            text.append(test)
    #print(text) #[[['', '迈向', '充满', '希望', '的', '新', '世纪', '——', '一九九八年', 
    # '新年', '讲话', '(', '附', '图片', '1', '张', ')', ''], ['', '中共中央']
    # 把单词和双词存入列表
    for line in text:
        for i in range(len(line)):
            if not line[i] == '' and not line[i] == '':
                oneWords.append(line[i])
            if i + 1 < len(line):
                twoWords.append(line[i] + line[i+1])

    global one_counter_list
    global two_counter_list

    one_counter_list = collections.Counter(oneWords)
    two_counter_list = collections.Counter(twoWords)
    one_counter_dict = dict(one_counter_list)
    two_counter_dict = dict(two_counter_list)

    # 对字典排序:变成[(word,num),(word,num)]
    tmp_dict = sorted(one_counter_dict.items(),key=lambda x : x[1],reverse=True)
    with open(file_path_out_1,'w',encoding='utf-8') as fp:
        for item in tmp_dict:
            fp.write(item[0])
            fp.write('\t')
            fp.write(str(item[1]))
            fp.write('\n')
   
    tmp_dict = sorted(two_counter_dict.items(),key=lambda x : x[1],reverse=True)
    with open(file_path_out_2,'w',encoding='utf-8') as fp:
        for item in tmp_dict:
            # print(item)
            fp.write(item[0])
            fp.write('\t')
            fp.write(str(item[1]))
            fp.write('\n')
def main():
    get_one_and_two_word()
    while True:
        name = input("请输入你要查找的词:")
        if (one_counter_list[name] == 0) and (two_counter_list[name] == 0):
            print("未找到该词,请重新输入")
        elif two_counter_list[name] != 0:
            print("该单词是双词")
            print("该词数量为:", two_counter_list[name])
        elif one_counter_list[name] != 0:
            print("该单词是单词")
            print("该词数量为:", one_counter_list[name])

if __name__ == '__main__':
    main()

实验四
main.py

'''
Author: Martin
Date: 2022-10-17 22:36:19
中文词法分析系统
语料库:1998-01-2003版-带音.txt
要求:根据构建的单词词典和双词词典,用n-gram模型,或者前向最长匹配,或者后向最长匹配等算法,
鼓励用更复杂一些的方法来进行,包括隐马尔科夫模型和条件随机场模型。
'''

file_path_in = r'D:\a表格\课件\大三上\自然语言处理\实验\199801.txt'
file_path_out_1 = r'D:\a表格\课件\大三上\自然语言处理\实验\实验三\单词.txt'
file_path_out_2 = r'D:\a表格\课件\大三上\自然语言处理\实验\实验三\双词.txt'

file1 = open(file_path_out_1, 'r', encoding='utf-8')
file2 = open(file_path_out_2, 'r', encoding='utf-8')
file1 = file1.read().replace('\n','\t').split('\t') #['人', '13449', '风', '12875', '花', '11627', '一', '11502']
file2 = file2.read().replace('\n','\t').split('\t')

file1_dict = dict(zip(file1[0::2],file1[1::2])) #{',': '73350', '的': '53572'}
file2_dict = dict(zip(file2[0::2],file2[1::2]))
# 合并两个词典
mix_file1_and_file2_dict = {**file1_dict,**file2_dict}
# print(mix_file1_and_file2_dict.keys())

#实验四,利用最大向前对句子进行切分
def Get_max_forword_split_sentence(texts):
    import re
    # 分句
    result_text = []
    texts = re.split('"(:|。|,| |?|!)"',texts)
    for text in texts:
        # 初始化索引
        text_begin = 0  #句子的开始
        text_end = len(text) #句子的结束
        while text_begin < len(text):
            if text_begin <= text_end:
                # 如果存在最长的匹配
                if text[text_begin:text_end] in mix_file1_and_file2_dict.keys():
                    #print(text[text_begin:text_end])
                    tmp_string = text[text_begin:text_end]
                    result_text.append(tmp_string)
                    text_begin += len(tmp_string)
                    text_end = len(text)
                
                # 如果不存在
                else:
                    text_end -= 1
            # 匹配完成
            else:
                # 就剩一个字了,语料库也没找到,直接入
                #result_text.append(text[text_end])
                text_begin += 1
    res = ''
    for word in result_text:
        res += word + '/'
    return res
#实验四,利用最大后向对句子进行切分
def Get_max_backword_split_sentence(texts):
    import re
    # 分句
    result_text = []
    texts = re.split('"(:|。|,| |?|!)"',texts)
    for text in texts:
        text_begin = 0
        text_end = len(text)
        while text_end > 0:
            if text_begin<=text_end:
                # 如果存在最长的匹配
                if text[text_begin:text_end] in mix_file1_and_file2_dict.keys():
                    # print(text[text_begin:text_end])
                    tmp_string = text[text_begin:text_end]
                    result_text.append(tmp_string)
                    text_end -= len(tmp_string)
                    text_begin = 0
                
                # 如果不存在
                else:
                    text_begin += 1
            # 匹配完成
            else:
                # 就剩一个字了,语料库也没找到,直接入
                #result_text.append(text[text_begin])
                text_end -= 1 
    res = ''
    for word in result_text[::-1]:
        res += word + '/'
    return res
def main():
    textsssssssss = ['中国的合作交流不断扩大,经济实力不断增强,要坚强']
    textsssssssss = ['我是一名计算机专业的学生']
    textsssssssss = ['双向最大匹配法是将正向最大匹配法得到的分词结果和逆向最大匹配法得到的结果进行比较,']
    for texts in textsssssssss:
        forword = Get_max_forword_split_sentence(texts)
        backword = Get_max_backword_split_sentence(texts)
        # backword = 'w /ni/我'·
        print('利用最大向前对句子进行切分: ',forword)
        print('利用最大后向对句子进行切分: ',backword)
        one_word_in_forword = len([x for x in forword.split('/') if len(x)==1])
        one_word_in_backword = len([x for x in backword.split('/') if len(x)==1])
        print(one_word_in_forword,one_word_in_backword)
        if one_word_in_forword<=one_word_in_backword:
            print('系统认为最大后向匹配较好')
            return forword
        print('系统认为最大前向匹配较好')
        return backword
    #Max_forword_split_sentence
if __name__ == '__main__':
    main()

你可能感兴趣的:(信息安全本科生实验和课设,#,自然语言处理,自然语言处理,人工智能)