0. 引言:什么是马尔可夫链?
在人工智能和自然语言处理(NLP)的世界里,马尔可夫链(Markov Chain)是一个既经典又强大的工具。简单来说,马尔可夫链是一种数学模型,用于描述一个系统从一个状态转移到另一个状态的过程。它的核心思想非常简单:“未来只取决于现在,与过去无关”。
在文本生成中,这意味着我们预测下一个词语时,只需要考虑当前这个词语,而不用管它之前的词语。这个简单的规则,却能让我们生成听起来“合理”的句子。
这篇教程将带你通过一个简单的 Python 脚本,一步步实现一个基于马尔可夫链的中文文本生成器。
1. 准备工作
在开始之前,我们需要安装一个重要的中文分词库:jieba
。
pip install jieba
然后,你需要准备一个用于训练的文本文件。例如,你可以下载《三国演义》的白话文版本,并将其命名为 三国演义白话文.txt
,放在与你的 Python 脚本相同的目录下。
通过网盘分享的文件:三国演义白话文.txt
链接: https://pan.baidu.com/s/1ExuQ_1D9e1kKMWUS1wlVsA?pwd=ve8a 提取码: ve8a
--来自百度网盘超级会员v3的分享
2. 核心代码解析
我们将分步解析这个文本生成脚本,让你清楚地了解每个部分的作用。
2.1 文本预处理 (preprocess_text
)
文本预处理是所有 NLP 项目的第一步。在这里,我们主要做两件事:读取文本和中文分词。
jieba.cut()
会将一整段文本分割成一个一个的词语。我们还需要过滤掉分词后可能产生的空白字符。
def preprocess_text(filepath):
"""
读取文本文件,进行分词和清洗,并返回一个词语列表。
"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
text = f.read()
except FileNotFoundError:
print(f"错误:找不到文件 '{filepath}'。请确保文件存在。")
return []
# 使用jieba库进行分词,得到一个生成器
words = jieba.cut(text)
# 过滤空格,并将生成器转换为列表
words_list = [word for word in words if word.strip()]
return words_list
2.2 构建马尔可夫模型 (build_markov_model
)
这是整个项目的核心部分。我们需要遍历分词后的词语列表,来构建一个能够表示词语间关系的转移矩阵。这个矩阵实际上是一个嵌套的字典结构,它会记录每个词语后面可能出现哪些词,以及出现的次数。
让我们用一个简单的例子来理解这个过程。假设我们的文本是:“我 喜欢 编程,我 喜欢 学习。”
第一步:统计词语的跟随关系
我们遍历分词后的词语列表 ["我", "喜欢", "编程", "我", "喜欢", "学习"]
。
-
当
current_word
是 "我" 时,next_word
是 "喜欢"。模型会记录:model["我"]["喜欢"]
的计数加 1。 -
当
current_word
是 "喜欢" 时,next_word
是 "编程"。模型会记录:model["喜欢"]["编程"]
的计数加 1。 -
...依此类推,直到遍历完整个列表。
经过遍历,我们的 model
字典会是这样的:
{
"我": {
"喜欢": 2
},
"喜欢": {
"编程": 1,
"学习": 1
},
"编程": {
"我": 1
}
}
第二步:将计数转换为概率
仅仅有计数是不够的,我们需要将这些计数转换为概率,这样在生成文本时才能进行随机选择。
-
对于词语 "我":它后面只出现过 "喜欢" 2 次,所以 "我" 到 "喜欢" 的概率是 2/2=1.0。
-
对于词语 "喜欢":它后面出现过 "编程" 1 次和 "学习" 1 次。总次数是 1+1=2。
-
"喜欢" 到 "编程" 的概率是 1/2=0.5。
-
"喜欢" 到 "学习" 的概率是 1/2=0.5。
-
最终,我们的模型会变成一个带有概率的字典:
{
"我": {
"喜欢": 1.0
},
"喜欢": {
"编程": 0.5,
"学习": 0.5
},
"编程": {
"我": 1.0
}
}
这个转换后的模型,就是我们用来生成文本的马尔可夫链。
def build_markov_model(words_list):
"""
根据词语列表构建马尔可夫链模型。
"""
model = {}
for i in range(len(words_list) - 1):
current_word = words_list[i]
next_word = words_list[i+1]
# 统计下一个词语出现的次数
if current_word not in model:
model[current_word] = {}
if next_word not in model[current_word]:
model[current_word][next_word] = 0
model[current_word][next_word] += 1
# 将计数转换为概率
prob_model = {}
for word, transitions in model.items():
total = sum(transitions.values())
prob_model[word] = {
next_word: count / total
for next_word, count in transitions.items()
}
return prob_model
2.3 生成文本 (generate_text
)
模型构建好后,文本生成就变得很简单了。这个过程就像一个寻宝游戏:我们从一个起点出发,然后根据地图(模型)上的指引,一步步随机地走向下一个地点。
第一步:选择起始词
-
你可以指定一个词作为起点,比如"曹操"。
-
如果没有指定,程序会从你训练好的模型中随机选择一个词作为句子的开头。
第二步:根据概率选择下一个词
-
有了当前词后,我们从模型中找到所有可能跟随它的词语和对应的概率。
-
使用
random.choices()
函数,它会根据这些概率,随机选择一个词。概率越高,被选中的机会就越大。
第三步:重复这个过程
-
我们将新选择的词添加到结果中,然后让它成为新的“当前词”。
-
重复第二步,直到生成的文本达到我们设定的长度(例如,50个词)。
第四步:拼接成最终文本
-
最后,我们将生成的词语列表拼接成一个完整的字符串,一个由马尔可夫链“创作”的句子就完成了!
def generate_text(model, start_word=None, length=50):
"""
根据马尔可夫链模型生成一段文本。
"""
if not model:
return "模型为空,无法生成文本。"
# 选择起始词
if start_word is None or start_word not in model:
start_word = random.choice(list(model.keys()))
generated_text = [start_word]
current_word = start_word
# 循环生成文本
for _ in range(length - 1):
if current_word not in model:
break
choices = list(model[current_word].keys())
probabilities = list(model[current_word].values())
# 随机选择下一个词
next_word = random.choices(choices, weights=probabilities, k=1)[0]
generated_text.append(next_word)
current_word = next_word
return "".join(generated_text)
2.4 运行与演示
现在,我们把所有的函数放在一起,并添加一个主程序入口来运行它。将以下代码保存为 markov_chain_generator.py
,并确保 三国演义白话文.txt
在同一目录下。
import jieba
import os
import re
import random
def preprocess_text(filepath):
"""
读取文本文件,进行分词和清洗,并返回一个词语列表。
"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
text = f.read()
except FileNotFoundError:
print(f"错误:找不到文件 '{filepath}'。请确保文件存在。")
return []
# 1. 文本清洗:去除标点符号和特殊字符
# 只保留中文、英文、数字和空格
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', text)
# 2. 中文分词
# 使用jieba库进行分词,得到一个生成器
words = jieba.cut(text)
# 3. 过滤空格,并将生成器转换为列表
# 确保每个元素都是一个有效的词语
words_list = [word for word in words if word.strip()]
return words_list
def build_markov_model(words_list):
"""
根据词语列表构建马尔可夫链模型。
"""
model = {}
for i in range(len(words_list) - 1):
current_word = words_list[i]
next_word = words_list[i+1]
# 如果当前词语不在模型中,则创建一个新的字典
if current_word not in model:
model[current_word] = {}
# 统计下一个词语出现的次数
if next_word not in model[current_word]:
model[current_word][next_word] = 0
model[current_word][next_word] += 1
# 将计数转换为概率
prob_model = {}
for word, transitions in model.items():
total = sum(transitions.values())
prob_model[word] = {
next_word: count / total
for next_word, count in transitions.items()
}
return prob_model
def generate_text(model, start_word=None, length=50):
"""
根据马尔可夫链模型生成一段文本。
"""
if not model:
return "模型为空,无法生成文本。"
# 1. 选择起始词
# 如果没有指定起始词,则从模型中随机选择一个
if start_word is None or start_word not in model:
start_word = random.choice(list(model.keys()))
generated_text = [start_word]
current_word = start_word
# 2. 循环生成文本
for _ in range(length - 1):
# 检查当前词是否在模型中,如果不在,则结束或随机选择一个新词
if current_word not in model:
break
# 获取所有可能的下一个词和它们的概率
choices = list(model[current_word].keys())
probabilities = list(model[current_word].values())
# 随机选择下一个词
next_word = random.choices(choices, weights=probabilities, k=1)[0]
generated_text.append(next_word)
current_word = next_word
# 3. 将词语列表拼接成句子
return "".join(generated_text)
if __name__ == "__main__":
file_path = "三国演义白话文.txt"
print("开始预处理文本并构建模型...")
words = preprocess_text(file_path)
if words:
model = build_markov_model(words)
print("\n--- 模型构建完成 ---")
# 生成文本
generated_sentence = generate_text(model, start_word="曹操", length=10)
print("\n--- 生成文本 ---")
print(generated_sentence)
运行结果如下所示:
开始预处理文本并构建模型...
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\ADMINI~1\AppData\Local\Temp\jieba.cache
Loading model cost 0.465 seconds.
Prefix dict has been built successfully.
--- 模型构建完成 ---
--- 生成文本 ---
曹操命令武士赶走使者从湖口夜袭吴军连战连
3. 总结与展望
通过这篇教程,你已经学会了如何使用简单的马尔可夫链模型来生成文本。这个模型虽然简单,但它的核心思想——基于概率的序列生成——在更复杂的深度学习模型中也得到了体现,例如在循环神经网络(RNN)中。
如果你想让生成的文本更流畅、更像人写的那样,可以尝试以下几个方向:
-
高阶马尔可夫链:在预测下一个词时,考虑前两个甚至更多的词。这将捕捉更长的依赖关系,使生成的句子更具连贯性。
-
使用更复杂的模型:探索基于神经网络的语言模型,如 LSTM、Transformer 等。这些模型能够学习更复杂的语言模式,生成高质量的文本。
-
更多样化的数据集:尝试用不同类型和风格的文本来训练模型,例如诗歌、歌词、甚至代码,看看马尔可夫链能生成什么样的有趣结果。
希望这份教程能为你打开文本生成的大门,让你在探索 AI 世界的道路上更进一步。