n-gram语言模型——文本生成源码

  • n-gram模型的基本原理
  • 文本生成的步骤
  • 1. 准备和分词
  • 2. 构建n-gram模型
  • 3. 平滑技术的应用
  • 4. 生成文本
  • 源码


n-gram语言模型——文本生成源码_自然语言处理

  在自然语言处理的领域中,n-gram语言模型是一种基础而强大的工具。它通过考虑词汇的序列来预测文本内容,从而有效地用于文本生成任务。这篇博客中将探讨如何利用n-gram模型,特别是在处理中文文本时,使用jieba进行分词和nltk库进行模型构建。

  我在上一篇博客里讲解了n-gram的原理,参考:n-gram语言模型——句子概率分布计算与平滑。

n-gram模型的基本原理

  n-gram模型基于一个简单的假设:一个词的出现只与它前面的有限个词相关。这种模型可以分为不同的类型,如bigrams(二元模型)和trigrams(三元模型),取决于我们考虑前面多少个词。

  以bigram为例,可以近似认为一个词的出现概率仅依赖于它前面的一个词:

n-gram语言模型——文本生成源码_语言模型_02在i为1时有意义,我们通常会在句子开头添加一个开始标记(BOS),并在句子结尾添加一个结束标记(EOS),以此包含在概率计算中。例如,要计算Mark wrote a book的概率,我们会这样计算:

n-gram语言模型——文本生成源码_文本生成_03

n-gram语言模型——文本生成源码_语言模型_02,可以简单地计算在某一文本中单词w的频率,然后对其进行归一化。若用c表示在给定文本中的出现次数,我们可以使用如下公式:

n-gram语言模型——文本生成源码_语言模型_05

  上述公式即为最大似然估计(Maximum Likelihood Estimation, MLE)。对于更高阶的n-gram模型,这一公式同样适用。

文本生成的步骤

1. 准备和分词

  使用jieba对中文文本进行分词,这是处理中文n-gram模型的第一步。分词后的结果用来构建n-gram模型。

2. 构建n-gram模型

  利用nltk的ngrams函数,从分词结果中创建bigrams序列。然后使用这些bigrams来构建一个条件频率分布对象,用于后续的文本生成。

3. 平滑技术的应用

  在n-gram模型中,为了处理那些在训练数据中未出现的词组合,需要采用平滑技术。Lidstone平滑和Laplace平滑是两种常见的方法。这些方法通过添加一个小的非零值到词组合的计数中,避免了零概率问题,使模型更加健壮。

Lidstone平滑和Laplace平滑参考之前的博客 n-gram语言模型——句子概率分布计算与平滑

4. 生成文本

  文本生成的过程是从一个初始词开始,根据条件频率分布连续生成下一个词。这个过程重复进行,直到达到所需的词数或遇到停止条件。

源码

  下面是使用Laplace平滑的n-gram模型来成文本的示例:

import nltk
from nltk.probability import LidstoneProbDist, LaplaceProbDist
from nltk.corpus import brown
from nltk import FreqDist, ConditionalFreqDist
import random
import jieba

# 示例文本 读取ylk.txt
text = open("ylk.txt", encoding="utf-8").read()
# jieba分词
tokens = jieba.cut(text)
# 生成bigrams
bi_grams = list(nltk.ngrams(tokens, 2))
# 创建条件频率分布对象
cfd = ConditionalFreqDist(bi_grams)

# # 使用Lidstone平滑
# # gamma值小于1的Lidstone平滑
lidstone_cfd = {condition: LidstoneProbDist(cfd[condition], gamma=0.1) for condition in cfd.conditions()}

# 使用Laplace平滑
# Laplace平滑是gamma=1的特殊情况
laplace_cfd = {condition: LaplaceProbDist(cfd[condition]) for condition in cfd.conditions()}

def generate_text(initial_word, cfd, num_words=50):
    current_word = initial_word
    generated_text = [current_word]

    for _ in range(num_words - 1):
        if current_word not in cfd:
            break
        next_word = random.choices(
            population=list(cfd[current_word].samples()),
            weights=[cfd[current_word].prob(w) for w in cfd[current_word].samples()]
        )[0]
        generated_text.append(next_word)
        current_word = next_word

    return ''.join(generated_text)

# 示例:从"哈哈"开始生成文本
print(generate_text("方锐", laplace_cfd, 100))
print(generate_text("方锐", lidstone_cfd, 100))

  代码里我下了4M多的网络小说作为语料库。然后根据主角的名字开始生成,结果如下:

n-gram语言模型——文本生成源码_自然语言处理_06

  感觉lidstone效果更好一点。

  通过使用n-gram模型结合平滑技术,能够有效地生成符合语言规律的文本。这种方法虽然简单,但在许多应用场景下仍然非常有效,特别是在资源有限的情况下。

  随着深度学习技术的发展,出现了更复杂的语言模型,n-gram模型感觉在文本生成领域已经不行了~