ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python诗歌生成器:从NLP词库构建到规则引擎的创意编程实践

Python诗歌生成器:从NLP词库构建到规则引擎的创意编程实践 1. 项目概述从代码到诗意的跨越“Python-Poem-Maker”这个名字听起来就很有意思。它不是一个严肃的算法库也不是一个复杂的Web框架而是一个带着点浪漫和趣味色彩的项目。简单来说这就是一个用Python来“写诗”的程序。但别误会它可不是让你去写“Hello World”或者打印九九乘法表那种入门练习。这个项目的核心在于探索如何让冰冷的代码逻辑去模拟、生成甚至创作出具有美感和一定结构的文本——诗歌。我最初接触这个想法是源于对自然语言处理和创意编程的兴趣。市面上有很多教你怎么用Python做数据分析、爬虫或者自动化的教程但很少有人系统地聊聊怎么用这门语言去触碰一下“艺术”的边。Python-Poem-Maker恰恰填补了这个空白。它适合的人群很广对于Python新手这是一个绝佳的、有趣的综合练习项目能串联起字符串处理、列表操作、文件读写、随机选择甚至一些简单的算法对于有一定基础的开发者它可以作为一个进入NLP自然语言处理和文本生成领域的敲门砖理解词库构建、概率模型和生成规则甚至对于纯粹的诗文爱好者它也能提供一个全新的视角看看AI是如何“理解”和“创作”诗歌的。这个项目的价值远不止于生成几行押韵的句子。它背后涉及的是语言模型、数据结构设计、美学规则的量化等一系列有趣的问题。你会思考一首诗需要什么样的结构五言还是七言押韵的规则怎么用代码实现如何让生成的句子不只是随机词的堆砌而是有一定的意境连贯性解决这些问题的过程本身就是一次深刻的编程思维训练。接下来我就把自己搭建一个Python诗歌生成器的完整思路、踩过的坑和最终实现方案详细地分享给你。2. 核心思路与架构设计2.1 诗歌生成的基本原理拆解在动手写代码之前我们必须想清楚一首诗特别是中文古诗是怎么被“制造”出来的。我们不能指望程序真的拥有情感和灵感所以必须把创作过程分解成可计算的规则。经过分析和实践我总结出几个核心要素词汇库这是原料。程序需要知道哪些词可以用来写诗。我们需要按词性名词、动词、形容词等和主题山水、情感、季节等对词汇进行分类存储。格律规则这是模具。主要指平仄和押韵。对于入门项目我们可以先从简化规则开始比如只考虑句子的字数五言、七言和末尾字的押韵。平仄规则比较复杂初期可以忽略或做极大简化。生成算法这是生产线。如何从词汇库中选取词语按照格律规则组装成句子再进一步组合成有意义的篇章常见的思路有基于模板的填充、基于马尔可夫链的随机漫步或者使用简单的递归神经网络RNN。对于我们的“Python-Poem-Maker”我建议采用一种**“规则模板随机填充简单优化”**的混合策略。这样既能保证生成的诗句符合基本形式又具有一定的随机性和趣味性实现难度也适中。2.2 技术栈选型与项目结构我们完全使用Python标准库来实现核心功能以保证项目的纯净和可移植性。主要用到的模块包括random用于从词库中随机选择词语。json或pickle用于存储和加载我们构建好的结构化词库。re正则表达式可选用于更复杂的文本处理和韵律检查。对于想要更深入、实现更复杂生成逻辑的朋友后期可以引入jieba进行分词或者用numpy、pandas来管理更庞大的词库数据但这属于进阶内容我们基础版先不涉及。项目目录结构可以这样规划poem_maker/ ├── data/ # 数据目录 │ ├── word_lib.json # 核心词库文件 │ └── templates.json # 诗歌模板文件 ├── src/ # 源代码目录 │ ├── __init__.py │ ├── word_library.py # 词库构建与管理模块 │ ├── poem_generator.py # 诗歌生成核心逻辑 │ └── utils.py # 工具函数如押韵检查 └── main.py # 主程序入口这种结构清晰地将数据、逻辑和入口分离方便后续维护和扩展。2.3 词库设计诗歌的“食材仓库”词库是项目的基石它的质量直接决定生成诗歌的“口感”。我们不能直接用现代汉语词典因为古诗用词凝练、典雅。我的做法是从《全唐诗》等公开的电子文本中通过程序提取高频且意境优美的词汇并进行人工筛选和分类。一个简单的词库数据结构可以是这样的用JSON格式存储{ nouns: { nature: [山, 水, 风, 月, 云, 松, 竹, 梅, 江, 海], time: [春, 秋, 朝, 夕, 夜, 岁, 时], emotion: [心, 情, 思, 愁, 梦, 泪] }, verbs: { action: [望, 听, 登, 临, 归, 宿, 飞, 落], link: [是, 在, 有, 如, 似] }, adjectives: [青, 幽, 明, 静, 寒, 远, 孤, 独] }在实际构建时分类可以更细致。例如名词还可以按“天文”、“地理”、“植物”、“器物”等进一步划分。动词可以区分及物和不及物。形容词可以标注感情色彩积极/消极。注意构建词库是一个需要耐心和一定文学素养的过程。初期可以从小规模词库开始比如每个类别20-30个词确保生成的诗句基本通顺。之后可以像滚雪球一样通过分析生成结果的好坏不断补充和优化词库。一个常见的坑是某些动词和名词的搭配在逻辑上是不成立的如“吃月亮”需要在词库设计或生成规则中加以限制。3. 核心模块实现详解3.1 词库管理模块的实现我们首先实现word_library.py。这个模块负责加载词库、提供按类别随机取词的接口以及可能的词库扩展功能。import json import random import os class WordLibrary: def __init__(self, lib_pathdata/word_lib.json): 初始化词库 :param lib_path: 词库JSON文件的路径 self.lib_path lib_path self.word_dict self._load_library() def _load_library(self): 加载词库文件 if not os.path.exists(self.lib_path): # 如果词库文件不存在创建一个极简的示例词库 default_lib { nouns: {nature: [山, 水, 风, 月], time: [春, 秋]}, verbs: {action: [望, 听], link: [如]}, adjectives: [青, 幽] } os.makedirs(os.path.dirname(self.lib_path), exist_okTrue) with open(self.lib_path, w, encodingutf-8) as f: json.dump(default_lib, f, ensure_asciiFalse, indent2) return default_lib else: with open(self.lib_path, r, encodingutf-8) as f: return json.load(f) def get_random_word(self, category, subcategoryNone): 随机获取一个词语 :param category: 大类别如 nouns, verbs :param subcategory: 子类别如 nature, action。如果为None则从该大类的所有词中随机选 :return: 一个随机的词语字符串 if category not in self.word_dict: raise ValueError(f词库中不存在类别: {category}) target_collection self.word_dict[category] if subcategory: if subcategory not in target_collection: raise ValueError(f类别 {category} 中不存在子类: {subcategory}) word_list target_collection[subcategory] else: # 如果未指定子类则将该大类下所有子类的词合并到一个列表中 word_list [] for sub in target_collection.values(): if isinstance(sub, list): word_list.extend(sub) if not word_list: return None return random.choice(word_list) if word_list else None def add_word(self, category, subcategory, word): 向词库中添加一个新词仅内存中需调用save方法持久化 if category not in self.word_dict: self.word_dict[category] {} if subcategory not in self.word_dict[category]: self.word_dict[category][subcategory] [] if word not in self.word_dict[category][subcategory]: self.word_dict[category][subcategory].append(word) return True return False def save(self): 将内存中的词库保存到文件 with open(self.lib_path, w, encodingutf-8) as f: json.dump(self.word_dict, f, ensure_asciiFalse, indent2)这个类提供了词库的基础CRUD操作。关键点在于get_random_word方法它是生成诗句时取词的唯一入口。通过分离词库管理逻辑主生成程序会变得非常清晰。3.2 诗歌生成引擎的核心逻辑接下来是重头戏poem_generator.py。这里我们将实现不同的生成策略。策略一基于固定模板的填充这是最简单的方法。我们预先定义好一些诗句的“骨架”比如一个五言句的模板可能是[形容词][名词][动词][方位名词]对应生成“青松立岩畔”。我们需要先定义一套模板。# 在 poem_generator.py 中 class TemplatePoemGenerator: def __init__(self, word_lib): self.word_lib word_lib # 定义一些简单的句子模板用{}占位符表示需要填充的词性 self.templates [ {adj}{noun}{verb}{place}, {noun}{verb}{adj}{noun}, {adj}{noun}{adj}{noun}, {place}{verb}{adj}{noun} ] # 映射模板占位符到词库中的类别 self.category_map { adj: (adjectives, None), noun: (nouns, nature), verb: (verbs, action), place: (nouns, place) # 假设词库中有place子类 } def generate_line(self): 生成一行诗 template random.choice(self.templates) line template for placeholder, (cat, sub_cat) in self.category_map.items(): if f{{{placeholder}}} in line: word self.word_lib.get_random_word(cat, sub_cat) if word: line line.replace(f{{{placeholder}}}, word) else: # 如果取不到词用默认词替代避免生成失败 line line.replace(f{{{placeholder}}}, 空) return line def generate_poem(self, lines4): 生成一首诗默认四句 poem_lines [] for _ in range(lines): poem_lines.append(self.generate_line()) return \n.join(poem_lines)这种方法优点是速度快结构稳定。缺点是生成的诗句容易显得呆板、重复因为模板是有限的。策略二基于简单语法树的随机生成我们可以定义更灵活的生成规则模仿一个简单的上下文无关文法。例如定义一个“诗句”可以由“主语部分谓语部分”构成“主语部分”可以是“形容词名词”等。class GrammarPoemGenerator: def __init__(self, word_lib): self.word_lib word_lib def _generate_phrase(self, grammar_rule): 根据语法规则生成一个短语 :param grammar_rule: 例如 [adj, noun] 或 [noun, verb, noun] phrase [] for elem in grammar_rule: if elem adj: word self.word_lib.get_random_word(adjectives) elif elem noun_nature: word self.word_lib.get_random_word(nouns, nature) elif elem verb_action: word self.word_lib.get_random_word(verbs, action) else: word [?] phrase.append(word if word else [空]) return .join(phrase) def generate_line(self, word_count5): 生成指定字数的诗句通过组合不同的语法规则来实现 # 定义一些基本语法规则组合来凑字数 # 例如五言诗可以是 23 或 32 结构 grammar_patterns [ [[adj, noun_nature], [verb_action, noun_nature]], # 23 [[noun_nature, verb_action], [adj, noun_nature]], # 32 ] pattern random.choice(grammar_patterns) line_parts [] for part_rule in pattern: line_parts.append(self._generate_phrase(part_rule)) line .join(line_parts) # 简单修剪或补全到指定字数这里逻辑较简单实际需要更精细控制 return line[:word_count] if len(line) word_count else line 。*(word_count-len(line))这种方法比纯模板更灵活能生成更多样的句子结构。但如何设计一套合理且能生成优美诗句的语法规则需要大量的调试和语言学知识。3.3 押韵功能的集成对于诗歌来说押韵是灵魂。我们可以实现一个简单的押韵检查模块。一个取巧的方法是预先建立一个“押韵字表”。例如将所有押“ong”韵的字如“空”、“同”、“中”、“红”放在一个集合里。在生成每一行末尾字时从指定的韵部中选取。首先在utils.py中创建一个韵部工具# utils.py class RhymeChecker: def __init__(self): # 这里只是一个极简示例实际需要庞大的押韵字典 self.rhyme_groups { ong: [空, 同, 中, 红, 风, 浓, 穹, 穷], an: [山, 间, 还, 颜, 关, 寒, 难, 闲], i: [里, 起, 意, 地, 期, 时, 思, 迟] } # 反向映射字 - 韵部 self.char_to_rhyme {} for rhyme, chars in self.rhyme_groups.items(): for char in chars: self.char_to_rhyme[char] rhyme def get_rhyme_group(self, char): 获取一个字的韵部 return self.char_to_rhyme.get(char, None) def is_rhyme_with(self, char1, char2): 判断两个字是否押韵 return self.get_rhyme_group(char1) self.get_rhyme_group(char2) and self.get_rhyme_group(char1) is not None def get_random_char_in_rhyme(self, rhyme_group): 从指定韵部随机取一个字 if rhyme_group in self.rhyme_groups: return random.choice(self.rhyme_groups[rhyme_group]) return None然后在生成诗歌时特别是在生成绝句四句时我们可以规定第二、四句的末尾字必须押同一个韵。在生成过程中先随机选定一个韵部然后在生成第二、四句时确保句尾字从该韵部中选取。这需要对之前的生成逻辑进行改造在组词成句的最后一步替换或选择符合押韵要求的字。这会增加算法的复杂度但能显著提升生成诗歌的“像样”程度。实操心得押韵功能的集成是项目从“玩具”升级到“像样作品”的关键一步。但初期不要追求完美的古韵平水韵那太复杂了。可以从现代汉语拼音的韵母相同开始实现一个简化版的押韵效果已经足够让人眼前一亮。构建押韵字典是一个体力活可以从开源的古诗数据库中提取高频押韵字来构建。4. 项目集成与功能拓展4.1 主程序与用户交互有了核心模块我们需要一个main.py来把它们串起来并提供友好的命令行界面。# main.py import argparse from src.word_library import WordLibrary from src.poem_generator import TemplatePoemGenerator, GrammarPoemGenerator from src.utils import RhymeChecker def main(): parser argparse.ArgumentParser(descriptionPython Poem Maker - 你的AI诗人) parser.add_argument(--style, choices[template, grammar], defaulttemplate, help诗歌生成风格template(模板填充), grammar(语法生成)) parser.add_argument(--lines, typeint, default4, help诗歌的行数) parser.add_argument(--rhyme, actionstore_true, help是否启用押韵功能) parser.add_argument(--count, typeint, default1, help生成诗歌的数量) args parser.parse_args() # 初始化组件 print(正在加载词库...) word_lib WordLibrary() rhyme_checker RhymeChecker() if args.rhyme else None # 选择生成器 if args.style template: generator TemplatePoemGenerator(word_lib) else: generator GrammarPoemGenerator(word_lib) # 如果启用押韵我们需要一个支持押韵的生成器包装类 # 这里为了示例假设我们有一个新的支持押韵的生成器类 RhymedPoemGenerator # 实际开发中需要将押韵逻辑嵌入到上述某个生成器中或创建新的子类 if args.rhyme: print(警告押韵功能需要更复杂的生成器示例中暂未实现完整集成。) print(将使用基础生成器并尝试在生成后简单调整韵脚效果可能不理想。) # 此处应调用 RhymedPoemGenerator print(f\n生成 {args.count} 首{args.lines}行诗风格{args.style}...\n) for i in range(args.count): poem generator.generate_poem(linesargs.lines) print(f【诗 {i1}】) print(poem) print(- * 20) if __name__ __main__: main()这样用户就可以通过命令行参数来控制生成诗歌的风格、行数和是否押韵体验更完整。4.2 进阶功能探索一个基础的诗歌生成器完成后有很多方向可以深入让项目变得更有挑战性和实用性引入机器学习使用RNN、LSTM或更现代的Transformer模型如GPT-2的微调来训练一个真正的诗歌生成模型。这需要大量的唐诗宋词作为训练语料。你可以使用tensorflow或pytorch来实现。这将使生成的诗歌在连贯性和意境上有一个质的飞跃。主题控制让用户输入一个关键词如“秋天”、“离别”然后生成符合该主题的诗歌。这需要在词库中为词语打上主题标签并在生成时提高相关主题词语的选取概率。格律精细化实现真正的平仄规则。你需要为汉字标注平仄古音并在生成句子时按照“平平仄仄平”这样的格律模板来选字。这是一个巨大的工程但做出来会非常酷。Web可视化界面使用Flask或Streamlit快速搭建一个网页应用用户点击按钮就能生成诗歌并配上自动生成的山水画背景可以调用一些AI绘画的API体验感十足。对联生成诗歌生成的一个变种。对联要求上下联对仗工整平仄相对意境相关。可以看作是一个约束条件更严格的文本生成问题非常适合作为下一个练手项目。4.3 工程化与部署思考如果想让你的“Python-Poem-Maker”成为一个可以持续运行、提供服务的项目就需要考虑工程化问题性能如果词库很大每次生成都加载整个JSON文件可能效率不高。可以考虑使用数据库如SQLite来存储和管理词库或者将词库加载到内存中常驻对于Web服务。配置化将诗歌风格、模板、生成规则等参数都放到配置文件中如YAML这样不用修改代码就能调整生成逻辑。日志与监控记录每次生成请求的参数和结果便于分析哪种风格更受欢迎或者排查生成异常的问题。API化将核心生成功能封装成RESTful API方便其他应用调用。使用FastAPI可以非常快速地实现。5. 常见问题与调试心得在开发过程中我遇到了不少典型问题这里列出来供你参考问题1生成的句子不通顺搭配怪异。原因词库分类不够精细或者生成规则允许了不合理的词性组合如形容词直接接动词。解决细化词库分类。不仅是名词、动词还要考虑词语的及物性、常用搭配。例如“吹”这个动词常与“风”、“笛”搭配而不与“石头”搭配。可以在词库中建立简单的搭配关系表。优化生成模板或语法规则。避免出现“形容词动词”这类不符合中文习惯的结构。多参考真实古诗的句式。引入“n-gram”概率模型。从大量古诗中统计两个词、三个词连续出现的概率在生成时优先选择概率高的组合。这能极大提升通顺度。问题2诗歌缺乏整体意境每句诗之间毫无关联。原因生成算法是逐句独立随机的没有考虑上下文。解决主题贯穿在生成一首诗前先随机确定一个核心主题如“送别”然后在生成每一句时都倾向于从与该主题相关的词库子集中选词。状态记忆让生成器记住上一句用了哪些意象如“月亮”、“江水”下一句可以尝试使用与之相关的意象如“倒影”、“行舟”形成简单的呼应。使用序列模型这是根本解决方法。使用RNN/LSTM等模型它们天生就会考虑上文信息来预测下一个字。问题3押韵功能导致诗句生硬为了押韵凑字。原因押韵是在句子生成完成后强行替换末尾字破坏了句子本身的自然度。解决将押韵约束提前到生成过程中。例如在采用语法树生成时先确定韵脚然后在生成句末短语时只从符合该韵脚的字集中选择。这需要生成算法与押韵模块深度耦合设计难度更高但效果更好。问题4程序运行慢生成一首诗要好几秒。原因可能是词库文件过大加载慢或者是生成算法中存在低效的循环或搜索。解决对于词库使用json加载后可以将其转换为Python字典常驻内存而不是每次生成都读文件。对于算法审视核心循环。例如随机选词时如果某个词库子集是列表random.choice是O(1)操作很快。但如果需要频繁判断“这个词是否押某韵脚”而押韵判断是通过遍历列表实现的就会慢。此时应建立字典索引例如{韵母: [字1 字2...]}实现O(1)查询。对于复杂模型如神经网络生成速度慢是正常的需要考虑使用GPU加速或模型量化。调试技巧实录设置随机种子在调试时在程序开头使用random.seed(42)固定随机数种子。这样每次运行程序生成的诗歌都是一样的便于你反复调整参数和逻辑观察变化。分步输出不要一次性生成整首诗。在生成器中加入调试标志让它打印出中间过程比如“当前选择的模板是XXX”、“从‘nature’类别中随机选取了‘山’”、“尝试押韵‘ong’找到字‘空’”。这能帮你精准定位问题出在哪一环。人工评估与迭代生成几百首诗快速浏览把读起来特别通顺、有意境的句子和特别怪异的句子都标记出来。分析好句子为什么好用了什么词、什么结构坏句子为什么坏。用这些洞见反过来优化你的词库和生成规则。这个过程是提升模型质量最有效的方法没有之一。最后我想说Python-Poem-Maker项目的魅力在于它介于严格的工程和自由的创作之间。你写的每一行代码都在定义一种“诗意”的算法。它可能永远也写不出李白的豪迈或杜甫的沉郁但当你看到程序第一次生成出一句勉强可读、甚至偶有亮眼的句子时那种跨越逻辑与情感边界的成就感是其他纯工具性项目难以给予的。不妨就从构建一个不到100个词的小词库开始运行你的第一个生成器看看这位“AI诗人”的处女作吧。
返回列表