基于Transformer与GPT-2的惠特曼风格诗歌生成器实践

发布时间:2026/6/1 12:00:09

基于Transformer与GPT-2的惠特曼风格诗歌生成器实践 1. 项目概述当代码遇见草叶几年前我在一个旧书店里翻到一本泛黄的《草叶集》随手一读就被惠特曼那种磅礴、自由、充满生命力的诗句击中了。他写“我辽阔广大我包罗万象”写“我赞美我自己歌唱我自己”那种打破传统格律、拥抱万物生灵的笔触让我这个理工科出身的人第一次感受到了诗歌作为“数据”的另一种魅力——它不是冰冷的结构化信息而是一种情感与意象的流动模型。于是一个念头冒了出来能不能用现代的技术比如机器学习去学习、模仿甚至“续写”这种独特的诗风这就是“惠特曼诗歌生成器”这个项目的起点。本质上这是一个基于深度学习的文本生成应用。它不是一个简单的随机词句拼接器而是试图让机器理解惠特曼诗歌的韵律感、意象群、句法结构和那股子精神气儿然后创作出具有类似风格的新诗。最终的产品形态是一个轻量级的Web应用用户可能只需要点击一个按钮或者输入一个关键词如“自我”、“道路”、“草叶”就能得到一首独一无二、带有惠特曼印记的生成诗。这个项目适合对自然语言处理NLP和创意编程感兴趣的开发者尤其是那些想跨越技术与人文边界用算法探索创意可能性的人。它不要求你已经是NLP专家但需要你有耐心处理文本数据理解模型训练的基本逻辑并享受将抽象模型转化为可交互应用的过程。2. 核心思路与技术选型为何是“他”与“它”构建一个诗歌生成器技术路径有很多。为什么选择惠特曼又为什么选择特定的技术栈这背后是一系列权衡。2.1 选择惠特曼数据与风格的确定性首先从数据角度看惠特曼是一个理想的学习对象。他的主要作品《草叶集》版本相对集中文本总量适中约几十万单词对于个人开发者和小型项目而言数据获取和处理的成本可控。更重要的是他的诗歌风格极其鲜明且一致大量使用自由体无固定韵律、排比句、列举法、第一人称视角、对自然和身体的歌颂、超长的句子结构以及自创的复合词。这种高辨识度的风格为机器学习模型提供了清晰、稳定的学习目标。模型要学习的不是“通用的好诗”而是一个非常具体的“惠特曼范式”。相比之下学习一个风格多变的诗人或者涵盖多个诗人的通用诗歌模型难度会呈指数级上升且容易生成风格模糊、四不像的作品。2.2 模型选型从RNN到Transformer的演进早期这类序列生成任务的首选是循环神经网络RNN及其变体LSTM、GRU。它们能很好地处理文本的时序依赖关系。我最初也尝试过用LSTM来训练效果尚可模型能学会使用一些惠特曼的标志性词汇和句首结构比如“我歌唱……”、“看哪……”。但LSTM模型存在一些固有局限一是生成长文本时容易遗忘前面的内容导致诗歌主题漂移二是训练速度相对较慢对长距离依赖的捕捉能力有限。因此当前项目的核心转向了Transformer架构特别是其解码器部分这几乎是现代文本生成的标配。Transformer依靠自注意力机制能让模型在生成每一个新词时“看到”并权衡输入序列中所有词的重要性无论它们距离多远。这对于生成惠特曼那种意象绵延、前后呼应的长诗行至关重要。具体来说我选择了GPT-2的轻量级版本作为基础模型。选择GPT-2而非更新更大的模型如GPT-3/4主要基于以下几点考量资源友好GPT-2 Small或Medium参数量在数亿级别在消费级GPU甚至强大的CPU上可以进行微调Fine-tuning推理速度也足够快适合部署成Web应用。开源与可控模型架构、权重完全开源训练和部署流程透明便于调试和定制。能力足够GPT-2在通用语言建模上已经表现出色以其作为“底子”再用惠特曼的诗集进行领域微调足以捕捉到独特的诗风。我们不需要一个通晓万物的模型只需要一个精通惠特曼的“专家”。注意直接使用未经微调的原始GPT-2生成的内容会是普通的新闻或故事文体。微调是关键步骤相当于让一个博学的通用学者变成一位专研惠特曼的诗歌学者。2.3 应用层选型轻量、快速与可分享模型训练好后需要封装成用户可交互的应用。目标是轻量、快速响应且易于分享。我排除了开发原生移动App成本高而选择了Streamlit作为前端框架。Streamlit是一个专为数据科学和机器学习构建应用的Python库其优势在于极简开发用纯Python脚本即可创建交互式Web界面无需了解HTML、CSS、JavaScript。快速原型几行代码就能添加滑块、按钮、文本框和实时更新的输出区域非常适合本项目的“输入关键词-生成诗歌-显示”场景。易于部署可以方便地部署到Streamlit Community Cloud、Hugging Face Spaces或任何支持Python的云服务器。后端则基于简单的Python服务核心是加载微调好的模型执行文本生成推理。整个技术栈体现了“敏捷”思想用成熟、高效的工具快速实现从数据到模型再到产品的闭环。3. 数据准备与模型微调喂养模型的“草叶”模型的表现七分靠数据。处理惠特曼的诗歌数据是一个细致且关键的过程。3.1 数据收集与清洗我使用的核心文本是《草叶集》的最终版“临终版”的纯文本文件来源于古登堡计划等开源数字图书馆。原始数据不能直接使用需要经过以下清洗步骤去除元数据删除书籍的标题、版权页、章节标题、编者注等非诗歌正文内容。标准化文本将全角字符转换为半角统一引号、破折号格式。惠特曼善用长破折号这是他风格的一部分予以保留。处理分行与分段诗歌的分行是重要的节奏信息。我将每个诗节stanza视为一个段落诗节内的换行保留。在输入模型时用特殊的标记如[SEP]来分隔不同的诗用换行符\n来保留诗节内的分行。构建训练格式将清洗后的所有诗歌文本连接成一个超长字符串作为训练语料。在微调GPT-2这类自回归语言模型时任务本质是“给定前文预测下一个词”。因此数据不需要额外的标签文本本身即是训练样本。3.2 分词与微调策略GPT-2使用其自身的**Byte-Pair Encoding (BPE)**分词器。BPE能有效处理罕见词和复合词这对处理惠特曼的自创词如“omniscene”很有帮助。我们直接使用transformers库中的GPT-2分词器。微调过程是在预训练的GPT-2权重基础上用我们的惠特曼诗歌语料继续进行训练。关键参数设置如下学习率选择较小的学习率如3e-5到5e-5因为预训练模型已经具备强大的语言能力我们只是引导它适应新风格太大学习率会“冲掉”原有知识。序列长度设置为512或1024以容纳惠特曼的长句。超过长度的文本会被截断但诗歌通常不会超过这个长度。训练轮数通常3-5个epoch整个数据集训练一遍为一个epoch就足够了。需要监控验证集上的损失loss防止过拟合——即模型完美“背诵”了训练集诗歌却失去了生成新诗的能力。提示Prompt工程为了让生成更有指向性我会在微调时给部分诗歌样本加上类似“惠特曼风格的诗歌”这样的前缀提示。在推理时用户输入的关键词如“海洋”也会被构造成类似的提示如“惠特曼风格诗歌关于海洋”这样模型更容易进入“创作状态”。# 微调代码结构示例使用Hugging Face Transformers库 from transformers import GPT2LMHeadModel, GPT2Tokenizer, Trainer, TrainingArguments # 加载预训练模型和分词器 model_name gpt2 # 或 gpt2-medium model GPT2LMHeadModel.from_pretrained(model_name) tokenizer GPT2Tokenizer.from_pretrained(model_name) # 设置分词器的填充标记 tokenizer.pad_token tokenizer.eos_token # 加载并分词处理好的惠特曼诗歌文本数据集 # ... (数据加载和分词代码) # 定义训练参数 training_args TrainingArguments( output_dir./whitman-poet, overwrite_output_dirTrue, num_train_epochs4, per_device_train_batch_size2, # 根据GPU内存调整 save_steps500, save_total_limit2, prediction_loss_onlyTrue, logging_dir./logs, ) # 创建Trainer并开始微调 trainer Trainer( modelmodel, argstraining_args, train_datasettokenized_datasets, data_collatorlambda data: {input_ids: torch.stack([f[input_ids] for f in data]), attention_mask: torch.stack([f[attention_mask] for f in data]), labels: torch.stack([f[input_ids] for f in data])} ) trainer.train()实操心得在微调时我发现加入一个简单的“风格分类”任务作为辅助多任务学习能稍微提升生成风格的一致性。即同时让模型判断一段文本是否是“惠特曼风格”。但这增加了复杂性对于初版项目专注于语言建模的微调已经能取得很不错的效果。4. 应用构建与交互设计让诗歌流淌出来模型训练完成后就到了将其包装成应用的阶段。目标是打造一个极简、专注能让用户瞬间感受到诗歌生成魅力的界面。4.1 使用Streamlit搭建前端Streamlit的核心理念是“将脚本变为App”。下面是一个核心界面的构建示例import streamlit as st import torch from transformers import GPT2LMHeadModel, GPT2Tokenizer # 设置页面标题和图标 st.set_page_config(page_title惠特曼诗歌生成器, page_icon) # 加载微调好的模型和分词器使用缓存避免重复加载 st.cache_resource def load_model(): model GPT2LMHeadModel.from_pretrained(./saved_whitman_model) tokenizer GPT2Tokenizer.from_pretrained(./saved_whitman_model) tokenizer.pad_token tokenizer.eos_token return model, tokenizer model, tokenizer load_model() # 应用标题和描述 st.title( 惠特曼诗歌生成器) st.markdown(输入一个词或一句话让AI模仿沃尔特·惠特曼的风格为你创作一首诗。) # 用户输入区域 with st.sidebar: st.header(创作参数) user_input st.text_input(灵感种子例如自我 道路 草叶:, 草叶) length st.slider(生成诗歌的最大长度单词数:, min_value50, max_value300, value150) temperature st.slider(创造性温度参数:, min_value0.5, max_value1.2, value0.9, help值越高生成越随机、有创意值越低生成越保守、可预测。) generate_button st.button(生成诗歌) # 诗歌生成与展示区域 if generate_button: if user_input: with st.spinner(AI正在草叶间漫步寻找诗句...): # 构建提示词 prompt f惠特曼风格诗歌关于{user_input} inputs tokenizer.encode(prompt, return_tensorspt) # 生成文本 outputs model.generate( inputs, max_lengthlength len(inputs[0]), # 总长度包含提示词 temperaturetemperature, do_sampleTrue, # 启用采样而非贪婪搜索 top_p0.92, # 核采样nucleus sampling参数保留概率质量最高的部分词 pad_token_idtokenizer.eos_token_id, no_repeat_ngram_size3, # 避免重复的3-gram减少循环 ) # 解码并后处理生成的文本 generated_text tokenizer.decode(outputs[0], skip_special_tokensTrue) # 移除提示词部分只保留生成的诗歌 poem generated_text.replace(prompt, ).strip() # 展示结果 st.success(诗歌生成完成) st.subheader(f为你而作《关于{user_input}》) st.text_area(生成的诗句, poem, height300) # 添加一些交互选项 col1, col2 st.columns(2) with col1: if st.button(再生成一首): st.rerun() # Streamlit 1.28 支持 rerun with col2: st.download_button( label下载诗歌, datapoem, file_namefwhitman_poem_{user_input}.txt, mimetext/plain ) else: st.warning(请输入一些灵感种子吧)这个界面包含了核心要素一个输入灵感的文本框、控制生成长度和随机性的滑块、一个触发生成的按钮以及一个优雅展示诗歌并支持下载的区域。侧边栏的设计保持了主显示区的整洁。4.2 生成参数调优控制AI的“诗兴”在model.generate()函数中几个参数对输出质量影响巨大temperature温度这是最重要的创意旋钮。温度低如0.5模型更倾向于选择概率最高的词输出稳定、保守但可能单调。温度高如1.2选择更随机输出更出人意料、有创意但也可能不合逻辑。对于诗歌我通常设置在0.8-1.0之间平衡可读性与创造性。top_p核采样与温度配合使用。它设定一个概率阈值如0.9模型只从累积概率超过该阈值的最小词集合中采样。这能有效避免选择那些概率极低、可能出错的词比单纯的top_k采样更灵活。no_repeat_ngram_size设置为3或4可以防止模型陷入重复短语的循环比如反复出现“我歌唱我歌唱我歌唱”。repetition_penalty略微大于1的值如1.1可以对已出现过的词进行惩罚进一步促进多样性。这些参数没有绝对的最佳值需要根据你的模型和审美进行多次调试。我通常会生成几十个样本从中挑选感觉最“对味”的参数组合。5. 生成结果评估与调优何为“好”的生成诗评估生成的诗歌是主观的但我们可以从多个维度建立一些客观和主观的评估标准。5.1 客观评估指标困惑度Perplexity在保留的验证集未参与训练的惠特曼诗歌上计算困惑度。它衡量模型对“真诗”的惊讶程度值越低说明模型越适应惠特曼的分布。但这只是一个参考低困惑度不代表生成的诗更有“诗意”。词汇多样性统计生成诗歌的独特词数占总词数的比例Type-Token Ratio。惠特曼的词汇丰富生成文本也应保持一定的多样性避免词汇贫乏。风格标记词频检查惠特曼的标志性词汇如“celebrate”, “sing”, “body”, “soul”, “democratic”, “cosmos”在生成文本中出现的频率是否在合理范围内既不能没有也不能泛滥成灾。5.2 主观评估与人工筛选这是更重要的环节。我会邀请一些了解或不了解惠特曼的朋友来阅读生成的诗歌并询问他们的感受风格辨识度读起来像惠特曼吗有没有那种自由奔放、包罗万象的感觉连贯性与语法句子是否基本通顺意象之间是否有断裂诗意与惊喜是否有那么一两个句子或意象让你觉得有趣、有美感甚至深刻失败模式分析收集生成效果差的样本如胡言乱语、无限重复、完全偏离主题分析其对应的输入和生成参数反向指导模型调整和提示词优化。一个成功的生成样本可能如下输入种子“道路” 我歌唱那延伸的道路那未曾被地图标记的路径 尘土与草叶覆盖的车轮与足迹交错的 通向喧闹城市也通向寂静森林的脉管。 每一个旅人都携带着他自己的宇宙行走 商贩的货物移民的希望恋人的心跳 都压入这柔软的地壳成为道路记忆的一部分。 哦道路你不是两点之间的直线 你是所有方向的总和是所有可能性的展开 在每一个岔路口都诞生一个新的世界。这段生成文本模仿了惠特曼的排比“通向...也通向...”、列举“商贩的货物移民的希望...”、对寻常事物的升华“道路记忆”、“所有方向的总和”以及第一人称的歌唱式开头。5.3 迭代调优策略基于评估迭代是必不可少的数据层面如果生成诗歌过于晦涩或语法错误多可能需要检查并进一步清洗训练数据或增加数据量谨慎引入其他来源的惠特曼相关文本或书信。模型层面尝试不同的模型大小GPT2-Small vs Medium调整微调的超参数学习率、训练轮数。推理层面精细调整temperature、top_p等生成参数并优化提示词模板。例如将“惠特曼风格诗歌关于{主题}”改为“以沃尔特·惠特曼的视角和风格写一首关于{主题}的诗”可能会引导出更沉浸式的生成。6. 部署上线与性能考量从本地到云端让应用在互联网上可访问是项目的最后一步也决定了用户体验。6.1 部署选项对比部署平台优点缺点适用场景Streamlit Community Cloud完全免费与Streamlit无缝集成一键部署。有资源限制内存、CPU应用公开。个人项目演示快速分享。Hugging Face Spaces免费专为ML应用设计易于嵌入模型卡片。资源限制可能需要配置环境。展示AI/ML项目社区曝光度高。云服务器 (如AWS EC2, GCP VM)完全控制资源可扩展可部署私有应用。需要自行管理服务器、安全和网络有成本。生产级应用需要高可用性或处理敏感数据。容器化部署 (Docker 云服务)环境一致易于迁移和扩展。配置复杂度增加。需要标准化部署和微服务架构。对于本项目我首选Hugging Face Spaces。它提供免费的GPU资源有限时长非常适合模型推理应用。部署流程大致是将应用代码、微调好的模型文件注意.gitignore大文件或使用Hugging Face Model Hub、依赖文件requirements.txt推送到一个GitHub仓库然后在Spaces上关联该仓库并创建应用。Hugging Face会自动构建环境并运行你的Streamlit应用。6.2 性能优化技巧模型量化使用PyTorch的量化工具将模型权重从FP32转换为INT8可以显著减少模型内存占用和加速推理而对精度影响很小非常适合在线部署。缓存与异步加载在Streamlit中使用st.cache_resource装饰器缓存加载的模型避免每次用户交互都重新加载这是性能提升的关键。响应式设计在生成诗歌时使用st.spinner()显示加载动画提升用户体验。设置合理的max_length避免生成过程过长导致请求超时。错误处理在代码中妥善处理可能出现的异常如模型加载失败、输入过长、生成内容为空等给用户友好的错误提示而不是崩溃的页面。7. 常见问题与故障排除实录在开发和部署过程中我遇到了不少典型问题这里记录下排查思路和解决方案。7.1 生成内容重复或退化现象诗歌不断重复同一句话或词或者生成无意义的字符序列。原因通常是生成参数设置不当或模型在训练时遇到了重复模式的数据。解决调整repetition_penalty(1.0) 和no_repeat_ngram_size(如3或4)。提高temperature值增加随机性。检查训练数据中是否有大量重复段落并进行清洗。尝试不同的top_p值如0.9。7.2 生成诗歌风格不鲜明现象生成的诗歌读起来像普通散文缺乏惠特曼的标志性特征。原因微调不充分或者预训练模型的通用语言模式过强压制了风格学习。解决增加微调的epoch数并监控训练损失是否持续下降。尝试在提示词中更加强调风格例如“以下是一首沃尔特·惠特曼式的、充满磅礴列举和自由韵律的诗歌”考虑使用更小的学习率进行更长时间的微调让模型更精细地调整权重。7.3 部署后应用加载慢或崩溃现象在Hugging Face Spaces上应用启动时间很长或运行时内存不足OOM崩溃。原因模型文件过大或Streamlit应用占用了过多内存。解决务必使用量化后的模型进行部署。确保在Hugging Face Spaces的配置文件中正确设置了Python版本和依赖。在Spaces的硬件设置中选择带有GPU的硬件如“GPU Basic”虽然免费使用时长有限但推理速度快。精简Streamlit应用移除不必要的依赖和计算。7.4 如何处理用户“不良”输入现象用户输入了无关或恶意的关键词。解决在应用前端或后端逻辑中加入简单的输入过滤。例如检查输入是否为空、是否过长如超过50字符或者建立一个简单的允许词列表虽然这有损开放性。更重要的是理解模型本身不具备“意识”它只是根据统计规律生成文本。对于任何生成内容都应视为一种无意识的文本组合实验而非真正的创作或表达。这个项目从灵感到上线的全过程是一次将经典文学与当代AI技术融合的实践。它让我深刻体会到技术不仅是工具也可以是通向人文理解的一座新桥。最终生成的诗句或许永远无法媲美惠特曼原作的精神深度但这个过程本身就像用代码在数字世界里播种了一片“草叶”每一次生成都是对那种自由、包容精神的一次独特回响。如果你也感兴趣不妨从收集一本《草叶集》的文本开始亲手训练属于你自己的诗歌生成器感受算法与诗意碰撞的奇妙火花。

相关新闻