自然语言理解工程

实验一 宋词词频统计

研究背景

统计宋词的单字词,双字词等。统计后,输出的是单字词和双字词的词典文件。文件中包括相应的词和频度(次数)。将这些词频统计结果作为实验二宋词生成的语料。

这里我打算使用从GitHub上下载的,由网友整理搜集的宋词数据库作为数据来源。这些数据用数据库进行存储,包括词牌名、诗人等信息,结构比较清晰。

模型方法

实验一内容较简单,使用Python读取数据库内容,使用Python
Dict类型的变量,利用其HashMap的特性对数据进行处理和存储,加快处理速度。最后,将词频字典以Json的形式存储到磁盘当中,方便实验二载入这些词频数据。

系统设计

详细代码文件置于model/main.py文件下,其中single_dic()函数为生成单字词频统计函数,double_dic()为双字词频统计函数。两个函数执行后分别生成single_dic.json以及double_dic.json文件。

使用Python
3自带的sqlite3数据包连接sqlite3数据文件。通过数据库语言提取出所有词的内容。关键代码如下图所示

conn = sqlite3.connect(‘ci.db’)

cousor = conn.cursor()

cousor.modelecute(‘select content from ci’)

values = cousor.fetchall()

在提取这些数据时,发现有些词的内容乱码,于是增加了is_chinese()函数对内容进行判别清洗。该函数通过utf8编码来判断是否为中文汉字。

系统演示与分析

两个词频统计文件内容如下:

实验二 宋词自动生成

研究背景

利用实验一生成的词频文件,输入词牌,基于宋词的词典和宋词的词牌,可以随机或者按照语言模型,自动生成宋词。设计相应的Ui或者Web界面。

模型方法

为了使生成的宋词更加符合实际宋词的韵脚与情感。这里使用RNN循环神经网络对现有宋词进行学习。

RNN的目的使用来处理序列数据。在传统的神经网络模型中,是从输入层到隐含层再到输出层,层与层之间是全连接的,每层之间的节点是无连接的。但是这种普通的神经网络对于很多问题却无能无力。例如,你要预测句子的下一个单词是什么,一般需要用到前面的单词,因为一个句子中前后单词并不是独立的。

RNN一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。利用RNN的这一特性,便可以进行宋词的生成。

系统设计

系统主要分为两个模块:/model下存储模型的训练以及生成模块,/poem_gene文件是前端展示模块。两个模块通过redis数据库进行生成宋词数据的传递。大致的工作模式如下:

前端展示页面接收到用户请求之后,从redis数据库中取得用户请求词牌名的消息队列队首的古诗词并返回给用户,然后利用redis的pub/sub向位于/model/sub.py文件夹下面的监听函数发送信息,告知消耗了一条数据,监听函数触发监听事件之后,判断redis数据库中宋词的余量,如果余量小于某一阈值之后,便新开一进程进行宋词的生成。

这一设计的原因是:宋词生成是一项需要大量时间的运算,如果用户请求发送过来在生成一首,需要等待大量的时间,利用这种方式,每个页面的返回时间大约能够降低到5ms以内,加快的访问速度。并且将宋词生成划分为独立的进程,防止因为生成进程崩溃导致前端主进程同时崩溃。

宋词生成模块使用tmodeltgenrnn库,利用其内置的训练函数构建深度学习模型完成训练。通过数据库语言统计所有词牌名的宋词数量:

发现浣溪沙、水调歌头、鹧鸪天、满江红这几首词牌名的宋词数量比较多,适合进行深度神经网络的训练。由于这些词存在格式不标准的问题,分别对这四种宋词的格式利用re表达式进行筛选,统一格式后再为给模型训练。

在第一次训练时,由于我把训练的轮数epoch设置为了50,导致模型出现了过度拟合的现象,生成的宋词很多都是原文,并不能创作出新的宋词。降低训练轮数epoch至20后,问题得到了解决。

利用RNN生成相应宋词之后,还需要再次进行re表达式匹配,匹配成功的宋词存入redis数据库中。

前端展示框架采用Python Flask + Bootstrap 4
完成,MVC架构,由于整个网页也比较简单。这里不再论述。

系统演示与分析

将项目部署到了我的腾讯云服务器上,可以登入http://zouanning.cn:800访问。截图如下:

一个有意思的问题如下:如果一直点击生成,当生成速度过快时,机器来不及生成时,会怎么样?

答:由于redis数据库队列操作的取出操作是阻塞式的,当无宋词可以从队列中弹出时,会一直阻塞,直至宋词生成模块生成相应的宋词并存入redis中,取出操作立即返回,并将结果返回给用户。

实验三 中文词频统计

研究背景

输入txt文件,统计1元模型和2元模型,输出单词和词频文件,双词和词频文件。设计相应的接口,能够快速载入文件,并检索单词和双词。

模型方法

没什么好说的,和实验一类似的要求,使用Python读取<1998-01-2003版-带音.txt>,随后使用split函数对字符串进行划分和解析,将分好的词存入至每一个list当中。随后对这些list循环遍历,统计输出结果。这里需要注意到是:这里的单词和双词和实验一中的并不一样。单词指分好词后的一个词汇,双词指单词连接起来的词。

系统设计

本实验代码详见/hmm/main.py,这里将主要代码逻辑放在了if name ==
main’之中,get_lines_list()函数为源文本解析函数,将源文本无用的信息取出,并将词已list储存。

系统演示与分析

单词和双词的词频统计结果如上图所示,这里用’+’号来表示两个词连在一起。

实验四 中文词法分析系统

研究背景

根据构建的单词词典和双词词典,用n-gram模型,或者前向最长匹配,或者后向最长匹配等算法,鼓励用更复杂一些的方法来进行,包括隐马尔科夫模型和条件随机场模型。

模型方法

这一实验主要目的是对文本进行分词。其可以使用的模型多种多样,首先,我使用了较为简单的前向后向匹配来完成分词任务,之后我又使用了隐马尔可夫模型对文本进行分词。

前向后向匹配原理比较简单,规定一个词的最大长度,每次扫描的时候寻找当前开始的这个长度的词来和字典中的词匹配,如果没有找到,就缩短长度继续寻找,直到找到或者成为单字。

隐马尔可夫模型的模型这里主要参考了这篇文章:

https://www.jianshu.com/p/f140c3a44ab6

隐马尔可夫模型用来描述一个含有隐含未知参数的马尔可夫过程。它的状态不能直接观察到,但能通过观测向量序列观察到,每个观测向量都是通过某些概率密度分布表现为各种状态,每一个观测向量是由一个具有相应概率密度分布的状态序列产生。

隐马尔可夫模型中,我们不知道模型经过的状态序列,只知道状态的概率函数,即,观察到的事件是状态的随机函数,因此,该模型是一个双重的随机过程。其中,模型的状态转换过程是不可观察的,即隐蔽的,可观察事件的随机过程是隐蔽的观察状态转换过程的随机函数。隐马尔可夫模型可以用五个元素来描述,包括2个状态集合和三个概率矩阵:

(1) 隐含状态 S

这些状态之间满足马尔可夫性质,是马尔可夫模型中实际所隐含的状态。这些状态通常无法通过直接观测而得到。(例如S1,S2,S3等等)

(2)可观测状态 O

在模型中与隐含状态相关联,可通过直接观测而得到。(例如O1,O2,O3等等,可观测状态的数目不一定要和隐含状态的数目一致。

(3)初始状态概率矩阵 π

表示隐含状态在初始时刻t=1的概率矩阵,(例如t=1时,P(S1)=p1,P(S2)=P2,P(S3)=p3,则初始状态概率矩阵
π=[ p1 p2 p3 ]

(4)隐含状态转移概率矩阵A

描述了HMM模型中各个状态之间的转移概率。其中Aij=P(Sj|Si),1≤i,,j≤N, 表示在 t
时刻、状态为 Si 的条件下,在 t+1 时刻状态是 Sj 的概率

(5) 观测状态转移概率矩阵B

令N代表隐含状态数目,M代表可观测状态数目,则 Bij=P(Oi|Sj),1≤i≤M,1≤j≤N 表示在 t
时刻、隐含状态是 sj条件下,观察状态为 Oi 的概率。

给一个汉语句子作为输入,以“BEMS”组成的序列串作为输出,然后再进行切词,进而得到输入句子的划分。其中,B代表该字是词语中的起始字,M代表是词语中的中间字,E代表是词语中的结束字,S则代表是单字成词。构建初始状态概率向量、状态转移概率矩阵、观测概率矩阵之后,利用维特比算法求出最优的分词结果。

系统设计

本次实验的代码详见/hmm/main.py以及/hmm/seg.py,/hmm/main.py的get_init_status()、trans_prob_mat()两个函数分别获取了初始状态概率向量、状态转移概率矩阵、观测概率矩阵,并以json文件的方式进行储存。随后,利用维特比算法,使用动态规划减少运算量,避免穷举所有的分词可能,从而加快分词的速度。维特比算法的实现主要参考了这一篇文章:

https://blog.csdn.net/youfefi/article/details/74276546

系统演示与分析

运行seg.py,结果如下:

git@git.dev.tencent.com:peacefullion/poem_gene.git

git@git.dev.tencent.com:peacefullion/hmm.git