Skip to content

自然语言处理与语言模型

一、自然语言处理 NLP

1、通俗解释

每种动物都有自己的语言,机器也是!

自然语言处理(Natural Language Processing,NLP)就是在机器语言和人类语言之间沟通的桥梁,以实现人机交流的目的。

人类通过语言来交流,狗通过汪汪叫来交流。机器也有自己的交流方式,那就是数字信息。

不同的语言之间是无法沟通的,比如说人类就无法听懂狗叫,甚至不同语言的人类之间都无法直接交流,需要翻译才能交流。

而计算机更是如此,为了让计算机之间互相交流,人们让所有计算机都遵守一些规则,计算机的这些规则就是计算机之间的语言。

既然不同人类语言之间可以有翻译,那么人类和机器之间是否可以通过“翻译”的方式来直接交流呢?

NLP 就是人类和机器之间沟通的桥梁!

2、发展历史

自然语言处理的发展可追溯到 20 世纪 50 年代,当时计算机科学家开始尝试通过计算机程序来实现对自然语言的理解和生成。

50 多年来 NLP 的历史发展可以分为三个浪潮,前两波以理性主义和经验主义的形式出现,为当前的深度学习浪潮铺平了道路。

(1)20 世纪 50 年代到 70 年代——采用基于规则的方法

在第一个浪潮中,NLP 的实验持续了很长一段时间,可以追溯到 20 世纪 50 年代。

1950 年,阿兰.图灵提出了图灵测试,以评估计算机表现出与人类无法区分的智能行为的能力。这项测试是基于人类和计算机之间的自然语言对话,旨在生成类似人类的反应。

图灵提出的“图灵测试”,这一般被认为是自然语言处理思想的开端。

20 世纪 50 年代到 70 年代自然语言处理主要采用基于规则的方法,研究人员们认为自然语言处理的过程和人类学习认知一门语言的过程是类似的,所以大量的研究员基于这个观点来进行研究,这时的自然语言处理停留在理性主义思潮阶段,以基于规则的方法为代表。

但是,基于规则的方法具有不可避免的缺点,首先规则不可能覆盖所有语句,其次这种方法对开发者的要求极高,开发者不仅要精通计算机还要精通语言学,因此,这一阶段虽然解决了一些简单的问题,但是无法从根本上将自然语言理解实用化。

(2)20 世纪 70 年代到 21 世纪初——采用基于统计的方法

第二波 NLP 浪潮的特点是利用语料库数据以及基于(浅层)机器学习、统计学等来利用这些数据(Manning and Schtze 1999)。

由于许多自然语言的结构和理论都被贬低或抛弃,而倾向于数据驱动的方法,这个时代发展的主要方法被称为经验或务实的方法(ChurchandMercer 1993;Church 2014)。

NLP 的一个主要会议甚至被命名为“自然语言处理的经验方法(Empirical Methods in Natural Language Processing)(EMNLP)”,最直接地反映了 NLP 研究人员在那个时代对经验方法的强烈积极情绪。

70 年代以后随着互联网的高速发展,丰富的语料库成为现实以及硬件不断更新完善,自然语言处理思潮由经验主义向理性主义过渡,基于统计的方法逐渐代替了基于规则的方法。

贾里尼克和他领导的 IBM 华生实验室是推动这一转变的关键,他们采用基于统计的方法,将当时的语音识别率从 70%提升到 90%。在这一阶段,自然语言处理基于数学模型和统计的方法取得了实质性的突破,从实验室走向实际应用。

(3)2008 年至今——深度学习的 RNN、LSTM、Transformer

在第二波浪潮中开发的 NLP 系统,包括语音识别、语言理解和机器翻译,表现得比在第一波浪潮时更好,鲁棒性更高,但它们远远没有达到人的水平,而这留下了很多需求。除了少数例外,NLP 的(浅层)机器学习模型通常没有足够的容量来吸收大量的训练数据。此外,学习算法、方法和基础设施也都不够强大。所有这一切都在几年前发生了变化,而这导致了第三波 NLP 浪潮,这股浪潮是由深层机器学习或深度学习的新范式推动的。

从 2008 年到现在,在图像识别和语音识别领域的成果激励下,人们也逐渐开始引入深度学习来做自然语言处理研究,由最初的词向量到 2013 年的 word2vec,将深度学习与自然语言处理的结合推向了高潮,并在机器翻译、问答系统、阅读理解等领域取得了一定成功。深度学习是一个多层的神经网络,从输入层开始经过逐层非线性的变化得到输出。从输入到输出做端到端的训练。把输入到输出对的数据准备好,设计并训练一个神经网络,即可执行预想的任务。

基于深度神经网络的模型,如循环神经网络(RNN)、长短时记忆网络(LSTM)和Transformer等,这些技术大大提高了自然语言处理的效率和准确性。

在国内,自然语言处理研究和产业发展也取得了丰硕的成果。目前,国内的自然语言处理研究机构和企业有很多,如中科院计算所、清华大学、百度、腾讯等,其中百度的 ERNIE、阿里巴巴的 BERT 等预训练模型在多种中文自然语言处理任务上表现出色。同时,许多国内公司也已经将自然语言处理技术应用于智能客服、搜索引擎、推荐系统等场景。

在国际上,谷歌、Facebook、OpenAI 等科技巨头在自然语言处理领域也取得了一系列重要的突破。例如,谷歌推出的 BERT 模型和 OpenAI 的 GPT 系列模型,都在多个自然语言处理任务上取得了超过人类水平的表现。

3、定义

**自然语言处理(Natural Language Processing,NLP)**是计算机科学、人工智能和语言学领域的一个交叉学科,主要研究如何让计算机能够理解、处理、生成和模拟人类语言的能力,从而实现与人类进行自然对话的能力

通过自然语言处理技术,可以实现机器翻译、文本校对、语音合成、问答系统、情感分析、文本摘要等多种应用。

自然语言处理就是要计算机理解自然语言,自然语言处理机制涉及两个核心的任务:

  • 自然语言理解(Natural Language Understand, NLU):是指计算机能够理解自然语言文本的意义
  • 自然语言生成(Natural Language Generate, NLG):是指能以自然语言文本来表达给定的意图

过去,我们对自然语言理解研究得较多,而对自然语言生成研究得较少,但这种状况现在已有所改变。

二、自然语言理解 NLU

1、概述

自然语言理解就是希望机器像人一样,具备正常人的语言理解能力,由于自然语言在理解上有很多难点,所以 NLU 是至今还远不如人类的表现。

2015 年开始开始,对话系统开始在互联网领域迅速出圈,主要是因为一个技术的普及:机器学习特别是深度学习带来的语音识别和 NLU(自然语言理解)——主要解决的是识别人讲的话。

2、技术实现

(1)规则理解

比如,一个旅行 APP 用户需要订机票,他会有多种说法:

  • 订机票
  • 买去上海的航班吗?
  • 定下周二去纽约的飞机
  • 要出差,帮我看下机票
  • ......

基于规则的理解,就是系统内置关键词,即出现「订机票」字样就认为用户需要订机票,然后打开订票界面。

但是,关键词的规则匹配会出现这种问题,比如:

  • 我要出差(系统不识别 ❌)
  • 我要退订机票(系统识别为订机票 ❌)

所以,为了适用于各种场景,我们需要不断设计各种规则,包括各种边缘案例(Corner Case)。

最终,我们可以归纳出一个模板:

sh
 .*?[地名]{到|||飞往}[地名].*?{机票|飞机票|航班}.*?

(2)意图识别

旨在确定用户输入的语句中所表达的意图或目的。简单来说,意图识别就是对用户的话语进行语义理解,以便更好地回答用户的问题或提供相关的服务。

在意图识别中,通常会包含两个任务:

  • 意图识别: Intent Recognition,分类任务。
  • 槽填充: Slot Filing,基于这个意图所获取的有效信息。

假设订票系统听到用户说:“我想在 6 月 1 日抵达上海。”,系统有一些槽(slot):目的地和到达时间,系统要自动知道这边的每一个单词是属于哪一个槽,比如:

  • 意图识别:订机票
  • 槽位:上海属于目的地槽
  • 槽位:6 月 1 日属于到达时间槽。

假如有如下两句话:

  • 用户 1:我想在 6 月 1 号抵达上海(「上海」是目的地)
  • 用户 2:我想在 6 月 1 号离开上海(「上海」是出发地)

**「上海」**一词,出现在这句中的位置都是一样的,但是第二个是出发地,我们改如何操作呢?

这个时候,就可以使用语法和模式匹配,出发地和目的地通常具有一定的语法结构或模式,可以通过定义语法规则或正则表达式来识别这些模式。例如,可以定义包括“从 A 到 B”、“去 C”等模式,然后通过匹配这些模式来确定出发地和目的地槽。

3、应用场景

机器翻译

基于规则的翻译效果经常不太好,所以如果想提升翻译的效果,必须建立在对内容的理解之上。比如,翻译这句话:

  • 原文:I like apple, it’s so fast!
  • 机器:我喜欢苹果,它太快了!
  • 实际:我喜欢苹果(手机),它太快了!

机器客服

如果想实现问答,就要建立在多轮对话的理解基础之上,自然语言理解是必备的能力。

智能家居

智能家居中,NLU 也是重要的一个环节。很多语音交互都是很短的短语,设备不但需要能否识别用户在说什么话,更要理解用户的意图。

  • 天猫精灵,升起晾衣架
  • 小爱同学,我有点冷(空调调高 1 度)
  • 小度小度,我要睡觉了(拉起窗帘,关闭灯光,播放睡前 FM)
  • ......

4、应用难点

下面先列举一些机器不容易理解的案例:

sh
校长说衣服上除了校徽别别别的。
过几天天天天气不好。
看见西门吹雪点上了灯,叶孤城冷笑着说:“我也想吹吹吹雪吹过的灯”,然后就吹灭了灯。
今天多得谢逊出手相救,在这里我想真心感谢“谢谢谢逊大侠出手”。
灭霸把美队按在地上一边摩擦一边给他洗脑,被打残的钢铁侠说:灭霸爸爸叭叭叭叭儿的在那叭叭啥呢。
姑姑你估估我鼓鼓的口袋里有多少谷和菇!
“你看到王刚了吗?” “王刚刚刚刚走”
张杰陪俩女儿跳格子:俏俏我们不要跳跳跳跳过的格子啦!

那么对于机器来说,NLU 难点大致可以归为 5 类:

难点 1:语言的多样性

自然语言没有什么通用的规律,你总能找到很多例外的情况。

另外,自然语言的组合方式非常灵活,字、词、短语、句子、段落......

不同的组合可以表达出很多的含义。例如:

  • 我要听《夜曲》
  • 播放《夜曲》
  • 放周杰伦的《夜曲》
  • ......

难点 2:语言的歧义性

如果不联系上下文,缺少环境的约束,语言有很大的歧义性。

  • 抽屉没有锁。(抽屉到底锁没锁?)
  • 这个人好说话。(这人是脾气好,还是话痨?)
  • 这张照片里是小明和小刚的爸爸。(照片里有谁?)
  • 这是小明的照片。(照片是小明的,还是照片里是小明?)
  • 他刚转到这所学校,很多人都不认识。(谁不认识谁?)
  • 我今天要做手术。(是病人接受手术,还是医生实施手术?)
  • 这儿的人多半是大学生。(多半是超过一半,还是“大概、也许”?)
  • 人走了。(是走了,还是走了?)
  • 小心地滑。(小心 de 滑?小心 di 滑?)
  • ......

TIP

「我想起来了」这句话有几个意思?(摘自某地区语文竞赛题)

  • 第一个意思:我想起某一件事情
  • 第二个意思:我想起身
  • 第三个意思:「起来」是一个人名

难点 3:语言的鲁棒性

自然语言在输入的过程中,尤其是通过语音识别获得的文本,会存在多字、少字、错字、噪音等干扰问题。例如:

  • 我寻思我妹有口音啊!
  • 最近一部电影很好看,叫什么山的?(复仇者联盟 3)
  • ......

难点 4:语言的知识依赖

语言是对世界的符号化描述,语言天然连接着世界知识,例如:

  • 鸭梨大:表示水果大,也指压力大
  • 7 天:可以表示时间,也指某酒店
  • 百度:指公司,也指各种制度;多次
  • ......

难点 5:语言的上下文

上下文的概念包括很多种:对话的上下文、设备的上下文、应用的上下文、用户画像…

  • 小明:买张火车票
  • AI:请问你要去哪里?
  • 小明:宁夏
  • AI:已为您购买去宁夏的车票
  • 小明:来首歌听
  • AI:请问你想听什么歌?
  • 小明:宁夏
  • AI:好的,已为您播放梁静茹的《宁夏》。

三、自然语言生成 NLG

NLG 是为了跨越人类和机器之间的沟通鸿沟,将非语言格式的数据转换成人类可以理解的语言格式,如文章、报告等。

以智能音箱为例,当用户说“几点了?”,首先需要利用 NLU 技术判断用户意图,理解用户想要什么,然后利用 NLG 技术说出“现在是 8 点 50 分”。

自然语言生成有 2 种方式:

  • text – to – text:文本到语言的生成
  • data – to – text :数据到语言的生成

1、技术实现

数据合并

自然语言处理的简化形式,这将允许将数据转换为文本(通过类似 Excel 的函数)。为了关联,以邮件合并(MS Word mailmerge)为例,其中间隙填充了一些数据,这些数据是从另一个源(例如 MS Excel 中的表格)中检索的。

模板化文本

这种形式的 NLG 使用模板驱动模式来显示输出。以足球比赛得分板为例。数据动态地保持更改,并由预定义的业务规则集(如 if / else 循环语句)生成。

统计语言模型

在语言模型中,通常会使用一些概率模型来表示文本的生成概率,叫做统计语言模型,如 n-gram 模型、隐马尔可夫模型(HMM)和条件随机场(CRF)等。

大语言模型

大型语言模型(LLM)在自然语言生成方面也有着强大的能力。

2、应用场景

NLG 的不管如何应用,大部分都是下面的 3 种目的:

  • 能够大规模的产生个性化内容;
  • 帮助人类洞察数据,让数据更容易理解;
  • 加速内容生产。

下面给大家列一些比较典型的应用:

自动写新闻

某些领域的新闻是有比较明显的规则的,比如体育新闻。目前很多新闻已经借助 NLG 来完成了。

聊天机器人

大家了解聊天机器人都是从 Siri 开始的,最近几年又出现了智能音箱的热潮。

除了大家日常生活中很熟悉的领域,客服工作也正在被机器人替代,甚至一些电话客服也是机器人。

BI 解读和报告生成

几乎各行各业都有自己的数据统计和分析工具。这些工具可以产生各式各样的图表,但是输出结论和观点还是需要依赖人。

NLG 的一个很重要的应用就是解读这些数据,自动的输出结论和观点。

总之,NLG 是 NLP 的重要组成部分,他的主要目的是降低人类和机器之间的沟通鸿沟,将非语言格式的数据转换成人类可以理解的语言格式。

3、应用难点

难点 1:伦理问题与偏见问题

算法歧视,一直是 AI 的重要难题。

难点 2:幻觉问题

有时候,机器给出的回答并不是我们想要的答案,机器在胡说八道。

难点 3:数据隐私问题

对于用户的隐私数据,也是亟待解决的问题。

四、统计语言模型

语言模型是自然语言处理中最重要的概念之一,它用于计算给定文本序列的概率。语言模型可以基于规则、统计或深度学习等方法构建。

在语言模型中,通常会使用一些概率模型来表示文本的生成概率,叫做统计语言模型,如 n-gram 模型、隐马尔可夫模型(HMM)和条件随机场(CRF)等。

1、n-gram 语言模型

n-gram 模型是一种基于统计语言学的模型,它用于预测文本中下一个词或字符的可能性。

n-gram 模型是一种条件概率模型,它通过计算一个文本序列中的 n 个连续词或字符的概率来预测下一个词或字符的可能性。

在 n-gram 模型中,n 表示一个文本序列中包含 n 个连续的词或字符。例如:

  • 当 n=2 时,n-gram 模型会计算文本中每个相邻的词对出现的概率;
  • 当 n=3 时,n-gram 模型会计算文本中每个相邻的三个词组成的三元组出现的概率;
  • 以此类推......

如果我们有 4 个词组成的序列(或者说一个句子)为w1, w2, w3, w4,我们希望算得概率 p(w1, w2, w3, w4),根据链式法则(概率论知识),可得:

一般的,对于有 T 个词组成的文本序列,我们得到的一般公式为:

其中,条件概率 P 计算过程为:

大数定律

大数定律(Law of Large Numbers)是概率论中的一个重要定理,它描述了在独立重复试验中,随着试验次数的增加,样本平均值将趋向于收敛到其期望值。在条件概率中,大数定律表明,随着样本数量的增加,经验概率会越来越接近真实概率,即事件出现的频率约等于概率。

经验概率是根据观察到的实际样本数据计算得出的概率,它表示某个事件在一组观察数据中出现的频率。经验概率通常用来估计真实概率,尤其是当我们无法直接获得真实概率时。

在统计语言模型中,我们可以使用大数定律来解释 N-gram 模型中的频率计数。随着我们观察到的文本数量的增加,单词序列的出现频率将越来越接近真实的语言使用情况,从而提高了模型的准确性和可靠性。

假设我们有以下文本语料库:

sh
I like to eat apples.
I like to eat bananas.
I like to eat oranges.

如果我们想要计算在给定 I like 的情况下下一个单词是 to 的概率,我们统计文本中出现 I like to 的次数,以及后面紧跟着 to 的次数。然后,我们将后者除以前者,即可得到概率。

当序列非常长的时候,计算和存储多个词共同出现的概率的复杂度会呈指数级别增长。

而且数据稀疏十分严重,没有足够大的预料能够满足多个词的共现,即不满足大数定律而导致概率失真。

并且,由 Zipf 定律(Zipf's Law)知道在自然语言中大部分词都是低频词,很难通过增加数据集来避免数据稀疏问题。因此,多个词共同出现的次数在语料库中可能为 0,导致序列概率直接为 0。

如何简化语言模型的计算呢?这里便引入了马尔可夫假设:一个词的出现只与前面 n 个词相关,即n 阶马尔可夫链

这样,当前这个词仅仅跟前面 n 个有限的词相关,因此也就不必追溯到最开始的那个词,这样便可以大幅缩减上述算式的长度。

2、模型分类

1、一元模型

当 n=1 时,是一个一元模型(uni-gram model),即为:

例如,长度为 4 的序列 w1, w2, w3, w4 ,其按顺序组成的概率为:

举个例子,假设我们有以下文本:

sh
I like to eat apples.
I like to eat bananas.
I like to eat oranges.

我们可以使用一元 N-gram 模型来计算每个单词的出现频率。

下面是每个单词的出现频率:

在一元 N-gram 模型中,我们不考虑单词之间的顺序关系,因此我们只计算每个单词在文本中出现的次数。

一元 N-gram 模型虽然简单,但在某些情况下也有其用处,特别是在一些基本的文本分析任务中,如文本分类、情感分析等。

2、二元模型

当 n=2 时,是一个一元模型(uni-gram model),即为:

例如,长度为 4 的序列 w1, w2, w3, w4 ,其按顺序组成的概率为:

举个例子,假设我们有以下文本:

sh
I like to eat apples.
I like to eat bananas.
I like to eat oranges.

我们希望使用二元(bi-gram)模型来预测下一个单词。

在这个模型中,我们将文本分解为一系列的连续的两个单词组成的片段。

首先,我们将文本转换成 bigram 序列:

sh
("I", "like"), ("like", "to"), ("to", "eat"), ("eat", "apples"), ("apples", "I"), ("I", "like"), ("like", "to"), ("to", "eat"), ("eat", "bananas"), ("bananas", "I"), ("I", "like"), ("like", "to"), ("to", "eat"), ("eat", "oranges")

接下来,我们可以构建一个简单的马尔可夫链模型来表示这些转换。

假设我们想要预测下一个单词在给定前一个单词的情况下的概率,这就是一个一阶马尔可夫链。

对于我们的例子,我们可以得到以下转移概率:

这些概率表示了在给定前一个单词的情况下,下一个单词出现的可能性。

这就是一个简单的马尔可夫链模型。

3、三元模型

当 n=3 时,是一个一元模型(uni-gram model),即为:

例如,长度为 4 的序列 w1, w2, w3, w4 ,其按顺序组成的概率为:

举个例子,假设我们有以下文本:

sh
I like to eat apples.
I like to eat bananas.
I like to eat oranges.

我们希望使用三元(tri-gram)模型来预测下一个单词。

我们首先将文本分解为三元组,每个三元组包含三个连续的单词。例如:

sh
("I", "like", "to")
("like", "to", "eat")
("to", "eat", "apples")
("eat", "apples", "I")
("apples", "I", "like")
...

然后,我们计算每个三元组在文本中出现的频率,并计算给定前两个单词的情况下,下一个单词出现的条件概率。

例如,如果我们想要计算在给定 I like 的情况下下一个单词是 to 的概率,我们统计文本中出现 I like to 的次数,以及后面紧跟着 to 的次数。

然后,我们将后者除以前者,即可得到概率。

3、判断句子合理性

其实,上述的计算过程仍然有问题。因为,严格来说,对于二元模型和三元模型来说,这样的计算有偏差,因为 w1 虽然作为开头,也应该计算条件概率。

这是,我们就需要给句子加上开始和结束的标志。

下面,我们以二元模型为例,来进行计算。

我们有如下三个文本数据:

sh
<s>I am Tom</s>
<s>Tom I am</s>
<s>I do not like cat</s>

其中,表示开始标志,表示结束标志。

根据条件概率计算公式:

那么,我们可以得到如下概率计算结果:

因此,我们可以进行依次计算。

I am Tom生成的概率是:

Tom I am生成的概率是:

所以,根据生成的概率值,我们可以知道I am Tom要比Tom I am通顺的多,也更加合理。

但是在实际情况下,如果要严格计算语言模型,需要对语料进行分句,然后在句首和句尾添加起始符和结束符,这样比较麻烦。

所以在很多情况下我们会使用上面说的计算方法来近似,可能也是因为这个原因我们很少看见加起始符和结束符的计算操作,只要句子计算的标准保持一致,还是能够准确判断概率大小的。

4、应用场景

n-gram 的应用场景,主要是在搜索引擎(Google 或者 Baidu)、或者输入法的猜想或者提示。你在用谷歌时,输入一个或几个词,搜索框通常会以下拉菜单的形式给出几个像下图一样的备选,这些备选其实是在猜想你想要搜索的那个词串

再者,当你用输入法输入一个汉字的时候,输入法通常可以联系出一个完整的词,例如我输入一个“刘”字,通常输入法会提示我是否要输入的是“刘备”。

这其实是以 n-gram 模型为基础来实现的。

那么原理是什么呢?也就是我打入“我们”的时候,后面的“不一样”,”的爱“这些是怎么出来的,怎么排序的?

实际上是根据语言模型得出。假如使用的是二元语言模型预测下一个单词。

排序的过程就是:

这些概率值的求法和上面提到的完全一样,数据的来源可以是用户搜索的记录。

5、代码示例

通过 n-gram 模型,可以训练程序写歌词。

我们给一首歌词进行训练。

python
# -*- coding: utf-8 -*-
from collections import Counter
from jieba import lcut
from random import choice

corpus = '''
这一生原本一个人,你坚持厮守成我们,却小小声牵着手在默认。
感动的眼神说愿意,走进我的人生。
进了门开了灯一家人,盼来生依然是一家人。
确认过眼神,我遇上对的人。
我挥剑转身,而鲜血如红唇。
前朝记忆渡红尘,伤人的不是刀刃,是你转世而来的魂。
青石板上的月光照进这山城,我一路的跟你轮回声,我对你用情极深。
谁在用琵琶弹奏一曲东风破,枫叶将故事染色,结局我看透。
篱笆外的古道我牵着你走过,荒烟漫草的年头,就连分手都很沉默。
'''

def get_model(corpus):
    # 将语料按照\n切分为句子
    corpus = corpus.strip().split('\n')
    # 将句子切分为词序列
    corpus = [lcut(line) for line in corpus]  # 分词
    # 提取所有单词,构建单词列表
    words = [word for words in corpus for word in words]
    # 构造一个存储每个词统计的字典{'你':count(), '我':count()}
    bigram = {w: Counter() for w in words}
    # 根据语料库进行统计信息填充
    for sentence in corpus:
        for i in range(len(sentence) - 1):
            bigram[sentence[i]][sentence[i + 1]] += 1
    return bigram

# 按照语言模型生成文本
def generate_text(bigram, first_word, free=4):
    # 如果第一个词不在词典中 随机选一个
    if first_word not in bigram.keys():
        first_word = choice(bigram.keys())
    text = first_word
    # 将候选词按照出现概率进行降序排列
    next_words = sorted(bigram[first_word], key=lambda w: bigram[first_word][w], reverse=True)
    while True:
        if not next_words:
            break
        # 候选词前free个中随机选一个
        next_word = choice(next_words[:free])
        text += next_word
        if text.endswith('。'):
            print('生成文本:', text)
            break
        next_words = sorted(bigram[next_word], key=lambda w: bigram[next_word][w], reverse=True)


if __name__ == "__main__":
    bigram = get_model(corpus)
    first_words = ['你', '我们', '确认']
    for word in first_words:
        generate_text(bigram, word, free=2)

程序输出的结果如下所示:

Released under the MIT License.