首页
/ N-gram语言模型实战:基于ArXiv论文摘要的文本生成

N-gram语言模型实战:基于ArXiv论文摘要的文本生成

2025-07-06 06:24:30作者:蔡怀权

引言

在自然语言处理领域,语言模型是理解、生成和评估文本的基础工具。本文将带您深入了解N-gram语言模型的原理与实现,并通过ArXiv论文摘要数据集构建一个能够生成"学术论文"的文本生成模型。

数据准备

我们使用的数据集包含ArXiv论文的标题和摘要。首先需要进行数据预处理:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 加载数据
data = pd.read_json("./arxivData.json")

# 组合标题和摘要
lines = data.apply(lambda row: row['title'] + ' ; ' + row['summary'].replace("\n", ' '), axis=1).tolist()

文本分词

使用WordPunctTokenizer进行分词处理:

from nltk.tokenize import WordPunctTokenizer

tokenizer = WordPunctTokenizer()
lines = [' '.join(tokenizer.tokenize(line)) for line in lines]

N-gram语言模型原理

N-gram模型基于马尔可夫假设,认为当前词的概率仅依赖于前n-1个词。其数学表达式为:

P(w1,,wn)=tP(wtwtn+1,,wt1)P(w_1, \dots, w_n) = \prod_t P(w_t | w_{t-n+1}, \dots, w_{t-1})

其中,n=1时为unigram模型,n=2为bigram,n=3为trigram模型。

模型实现

1. N-gram计数

首先实现计数功能,统计每个词在特定上下文后出现的频率:

from collections import defaultdict, Counter

UNK, EOS = "_UNK_", "_EOS_"

def count_ngrams(lines, n):
    counts = defaultdict(Counter)
    for line in lines:
        tokens = line.split() + [EOS]
        for i in range(len(tokens)):
            prefix = tuple([UNK]*(n-1-len(tokens[:i])) + tuple(tokens[max(0,i-n+1):i])
            counts[prefix][tokens[i]] += 1
    return counts

2. 概率计算与模型构建

基于计数结果计算条件概率:

class NGramLanguageModel:
    def __init__(self, lines, n):
        self.n = n
        counts = count_ngrams(lines, self.n)
        self.probs = defaultdict(Counter)
        
        for prefix, counter in counts.items():
            total = sum(counter.values())
            for token, count in counter.items():
                self.probs[prefix][token] = count / total

文本生成

实现基于温度采样的文本生成方法:

def get_next_token(lm, prefix, temperature=1.0):
    next_tokens = lm.get_possible_next_tokens(prefix)
    if not next_tokens:
        return EOS
        
    tokens, probs = zip(*next_tokens.items())
    probs = np.array(probs)
    
    if temperature != 1.0:
        probs = np.power(probs, 1.0/temperature)
        probs /= probs.sum()
        
    return np.random.choice(tokens, p=probs)

模型评估:困惑度

困惑度(Perplexity)是评估语言模型的重要指标:

def perplexity(lm, lines, min_logprob=np.log(10**-50.)):
    logprob_total = 0
    total_tokens = 0
    
    for line in lines:
        tokens = line.split() + [EOS]
        for i in range(len(tokens)):
            prefix = ' '.join(tokens[max(0,i-lm.n+1):i])
            prob = lm.get_next_token_prob(prefix, tokens[i])
            logprob = max(np.log(prob), min_logprob)
            logprob_total += logprob
            total_tokens += 1
            
    return np.exp(-logprob_total / total_tokens)

实验结果

在不同n值下的困惑度对比:

  • Unigram模型(n=1): 318.21
  • Trigram模型(n=3): 1.52
  • 10-gram模型(n=10): 1.18

结果显示,随着n的增加,模型在训练数据上的困惑度降低,但需要注意过拟合问题。

文本生成示例

prefix = "deep learning"
for i in range(100):
    prefix += ' ' + get_next_token(lm, prefix, temperature=0.7)
    if prefix.endswith(EOS):
        break
        
print(prefix)

输出示例: "deep learning has become a popular approach in machine learning and artificial intelligence research . EOS"

总结

N-gram语言模型虽然简单,但在理解语言模型基本原理和评估方法方面具有重要意义。通过本教程,我们实现了:

  1. 完整的N-gram语言模型构建流程
  2. 基于温度采样的文本生成方法
  3. 困惑度评估指标的计算

这些技术为后续更复杂的神经网络语言模型奠定了基础。