新闻详情

新闻详情

首页 / 资讯中心 / 详情

TensorFlow 2.0 RNN唐诗生成实战:从语料到写诗

发布时间:2026/10/1 13:11:05来源:尧图网络
TensorFlow 2.0 RNN唐诗生成实战:从语料到写诗
简介这份资源面向深度学习与自然语言处理方向的初学者及课程实践者围绕循环神经网络LSTM构建唐诗生成模型解决从零搭建文本生成项目的入门难题。压缩包共12个文件约20.18MB包含6个Python源码文件、2个文本数据文件以及模型结构图、答辩幻灯片、训练权重和实验报告PDF覆盖数据加载、模型定义、训练与评估的完整流程。实验以“日、红、山、夜、湖、海、月”等词作为起始词生成诗句帮助读者理解序列建模与文本生成的核心思路。目前已有1537人学习下载。读者可获得可直接运行的代码工程、配套答辩PPT与实验报告既能对照代码逐行理解LSTM实现细节也能借助报告梳理实验目的与结果分析适合课程设计、毕业设计或答辩展示场景使用。1. RNN唐诗写作用TensorFlow 2.0把「深度学习实战」跑成自己的东西很多人第一次接触 RNN 循环神经网络都是被「用深度学习写唐诗」这个场景吸引的。它不像图像识别那样需要显卡堆料也不像大模型微调那样动辄几十 G 显存一台普通笔记本、一个 TensorFlow 2.0 环境就能把「深度学习实战」这四个字从课本拽到终端里。这个标题真正要解决的问题是给定一批唐诗语料怎么用 RNN 学出「平仄、押韵、意象搭配」的隐式规律再让模型一个字一个字地生成新句子。适合谁适合刚学完深度学习基础、想找一个能跑通、能改参数、能看见输出变化的实战项目的人也适合想理解「序列建模到底在学什么」的从业者。它不承诺写出李白级别的诗但能让你把 embedding、RNN、softmax、温度采样这条链路完整走一遍并且每一步都能自己调。2. 唐诗语料怎么变成 RNN 能吃的张量从原始文本到 tf.data 管道2.1 为什么选字符级建模而不是词级唐诗的难点在于总字数有限常用字集中但组合方式极多。如果按词切分分词器本身就会引入误差而且唐诗里单字成词的情况太普遍词表反而不好控制。字符级建模把每个汉字当成一个 token词表大小通常在一万以内embedding 维度设 128 或 256 就够用。常见做法是先统计语料中所有出现过的汉字按频次排序保留前 N 个剩下的统一映射到unk。这样做的代价是序列变长但唐诗本身五言、七言居多序列长度可控RNN 完全吃得下。我一般会先把语料整理成一行一首的纯文本标点统一成中文全角去掉注释和标题。然后做三件事建立字表、把每首诗转成 id 序列、用 padding 对齐到固定长度。注意padding 不要用 0 直接混进去最好单独留一个padid并在 loss 里 mask 掉否则模型会学会「预测 padding」生成时容易输出空白。2.2 用 TensorFlow 2.0 构建 tf.data 输入管道TensorFlow 2.0 的tf.data是处理序列数据的标准入口。下面这段代码把「一行一首诗」的文本文件转成(input_seq, target_seq)对其中 target 是 input 向右移一位。逻辑说明先按字符切分再查表转 id最后用window做滑动窗口。参数说明seq_len控制单次输入长度唐诗建议 32 到 48batch_size在 64 到 128 之间显存小就降到 32buffer_size设为语料行数即可不用太大。import tensorflow as tf # 读取语料每行一首诗 with open(tangshi.txt, r, encodingutf-8) as f: lines [line.strip() for line in f if line.strip()] # 建立字表 chars sorted(set(.join(lines))) char2idx {c: i 1 for i, c in enumerate(chars)} # 0 留给 padding idx2char {i: c for c, i in char2idx.items()} vocab_size len(char2idx) 1 # 转 id 序列 sequences [[char2idx[c] for c in line] for line in lines] # 统一长度短的补 0长的截断 seq_len 40 padded tf.keras.preprocessing.sequence.pad_sequences( sequences, maxlenseq_len, paddingpost, truncatingpost, value0 ) # 构造输入和目标目标为输入右移一位 dataset tf.data.Dataset.from_tensor_slices((padded[:, :-1], padded[:, 1:])) dataset dataset.shuffle(10000).batch(64).prefetch(tf.data.AUTOTUNE)这段代码跑完你可以打印vocab_size看看字表规模一般唐诗选集在 6000 到 9000 之间。如果超过一万说明语料里混入了太多生僻字或异体字建议先做一轮清洗。pad_sequences的value0对应 padding id后面在 loss 里要忽略掉。2.3 语料清洗的三个硬指标第一去掉长度小于 10 个字的残句这些多半是标题或注释混进来了。第二统一标点把英文逗号句号替换成中文全角否则字表里会多出几十个无意义符号。第三检查重复行重复率超过 5% 说明语料有大量复制粘贴会导致模型过拟合到某几首。我一般会写一个简单的统计脚本输出总行数、平均长度、字表大小、重复行数四个数字都正常了再进模型。3. 用 TensorFlow 2.0 搭一个能写诗的 RNNEmbedding GRU Dense 的最小实现3.1 模型结构选型SimpleRNN、LSTM 还是 GRUTensorFlow 2.0 里tf.keras.layers.RNN家族有三个常用成员SimpleRNN、LSTM、GRU。SimpleRNN 最容易出现梯度消失写出来的诗容易重复同一句LSTM 参数最多收敛稳但训练慢GRU 是折中方案参数量比 LSTM 少三分之一效果在短序列上几乎持平。唐诗生成这个场景序列长度 40 左右GRU 是性价比最高的选择。如果你用 SimpleRNN 跑建议把学习率降到 1e-3 以下否则 loss 会震荡。层数方面单层 GRU 加 256 隐藏单元已经能出像样的句子。想再提升可以堆两层但第二层要加return_sequencesTrue并且第一层要加dropout。注意dropout 不要设太大0.2 到 0.3 之间太大模型会学不动生成结果偏保守。3.2 完整模型代码与参数说明下面这个模型是「embedding → GRU → GRU → Dense → softmax」的结构。逻辑说明embedding 把 id 映射成稠密向量GRU 逐时间步处理最后一层 Dense 输出每个字在词表上的概率。参数说明embedding_dim256是常见起点语料小于 5 万行可以降到 128gru_units256对应隐藏状态维度dropout0.2防止过拟合learning_rate1e-3配合 Adam 优化器。import tensorflow as tf vocab_size 8000 # 按实际字表调整 embedding_dim 256 gru_units 256 model tf.keras.Sequential([ tf.keras.layers.Embedding(vocab_size, embedding_dim, mask_zeroTrue), tf.keras.layers.GRU(gru_units, return_sequencesTrue, dropout0.2), tf.keras.layers.GRU(gru_units, return_sequencesTrue, dropout0.2), tf.keras.layers.Dense(vocab_size, activationsoftmax) ]) model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), losstf.keras.losses.SparseCategoricalCrossentropy(from_logitsFalse), metrics[accuracy] ) model.summary()mask_zeroTrue很关键它让 embedding 层自动忽略 padding 位置后面的 GRU 和 loss 都会继承这个 mask。如果你忘了加模型会把 padding 当成正常字符学生成时开头容易输出一串空白。from_logitsFalse是因为 Dense 已经带了 softmax如果去掉 softmax 改成from_logitsTrue数值稳定性会更好但这里为了直观先保留。3.3 训练循环与回调什么时候该停训练用model.fit就行但有几个细节。第一epochs先设 50看 loss 曲线如果 20 轮后还在明显下降可以加到 100。第二加EarlyStopping监控losspatience5防止过拟合。第三加ModelCheckpoint只保存验证 loss 最低的权重后面生成时加载这个权重不要用最后一轮的。callbacks [ tf.keras.callbacks.EarlyStopping(monitorloss, patience5, restore_best_weightsTrue), tf.keras.callbacks.ModelCheckpoint(best_model.h5, monitorloss, save_best_onlyTrue) ] history model.fit(dataset, epochs50, callbackscallbacks)训练时观察 loss如果从 8 降到 3 左右说明模型已经学到字频和常见搭配降到 2 以下生成的诗会开始有「像诗」的感觉但也会出现重复。如果 loss 降到 1 以下大概率过拟合了生成结果会大量照抄训练集。我一般会在 loss 降到 2.5 到 3.5 之间停下来这个区间的生成结果最有「创作感」。4. 生成阶段温度采样、重复惩罚与「写诗」的玄学调参4.1 温度参数到底在调什么模型输出的是每个位置的概率分布直接取 argmax 会得到最保守的句子读起来像顺口溜。温度参数T用来缩放 logitsT越小分布越尖锐生成越保守T越大分布越平坦生成越随机。唐诗生成的经验值T0.6到0.8之间比较平衡T1.0会开始出现不搭的意象T1.5以上基本是乱码。我一般会同时跑三个温度各生成十首人工挑一挑再决定最终用哪个。import numpy as np def generate(model, start_char, char2idx, idx2char, length40, temperature0.7): input_seq [char2idx.get(start_char, 0)] generated [] for _ in range(length): input_tensor tf.expand_dims(input_seq, 0) predictions model(input_tensor)[0, -1, :] / temperature predicted_id tf.random.categorical(tf.expand_dims(predictions, 0), num_samples1)[0, 0].numpy() input_seq.append(predicted_id) generated.append(idx2char.get(predicted_id, )) return .join(generated)tf.random.categorical按概率采样比 argmax 灵活。input_seq只保留最近的部分也可以但 GRU 状态会累积短序列影响不大。注意如果生成的 id 是 0padding直接跳过不要输出。4.2 重复惩罚让模型别老写「春风」RNN 生成有个通病同一个字或同一个意象反复出现。解决办法是在 logits 上做重复惩罚对已经生成过的 token把对应 logit 减去一个惩罚值。惩罚值设 1.0 到 2.0 之间太大句子会不通顺。这个技巧在 TensorFlow 里手动实现就行不需要改模型结构。def generate_with_penalty(model, start_char, char2idx, idx2char, length40, temperature0.7, penalty1.5): input_seq [char2idx.get(start_char, 0)] generated_ids [] for _ in range(length): input_tensor tf.expand_dims(input_seq, 0) logits model(input_tensor)[0, -1, :] / temperature for gid in set(generated_ids): logits tf.tensor_scatter_nd_sub(logits, [[gid]], penalty) predicted_id tf.random.categorical(tf.expand_dims(logits, 0), num_samples1)[0, 0].numpy() input_seq.append(predicted_id) generated_ids.append(predicted_id) return .join(idx2char.get(i, ) for i in generated_ids)tensor_scatter_nd_sub在指定位置减去惩罚值。注意惩罚只对已经生成过的 id 生效不要对 padding 做惩罚。如果发现句子开始出现生僻字把penalty降到 1.0 以下。4.3 起始字符的选择与「藏头诗」玩法起始字符决定生成方向。用「春」开头模型会往「春风吹又生」这类意象走用「山」开头容易出「山高水长」。如果你想做藏头诗就把每句的第一个字固定逐句生成。注意每句生成完后把上一句的结尾作为下一句的起始这样句子之间会有连贯性。我试过用「月」开头生成五言十次里有三次能出「月照寒江雪」这种像样的句子剩下的七次需要手动挑。5. 避坑与排查RNN 写诗翻车的五个血泪现场5.1 现象生成结果全是「的的的的的」——原因padding 没 mask这是最常见的翻车。模型把 padding id 当成高频字符学因为 padding 在序列里占比很大。解决embedding 层加mask_zeroTrueloss 用SparseCategoricalCrossentropy并确保 mask 传递下去。如果已经训练完了加载权重后重新生成不要重新训练。5.2 现象loss 降到 1.0 以下生成的诗和训练集一模一样——原因过拟合语料太少或模型太大。解决先看语料行数少于 5000 行就减层把两层 GRU 改成一层gru_units从 256 降到 128。再加 dropout 到 0.3。如果还不行用EarlyStopping在 loss 降到 2.5 左右就停。5.3 现象训练时 loss 震荡不下降——原因学习率太大或 batch 太小Adam 默认 1e-3如果 batch 小于 32梯度噪声大loss 会跳。解决把 batch 加到 64 或 128学习率降到 5e-4。如果还震荡检查语料里有没有空行或超长行超长行截断到 40 后如果只剩几个字直接丢掉。5.4 现象生成时速度极慢一首诗要几十秒——原因逐字符调用模型没有缓存状态RNN 每次调用都重新计算整个序列长度 40 时还好但如果做藏头诗连续生成时间会累积。解决用tf.function装饰生成函数或者手动维护 GRU 状态每次只输入上一个字符。TensorFlow 2.0 里可以用model.layers[1].reset_states()配合statefulTrue但要注意 batch 维度。5.5 现象换了一台机器加载模型报错——原因TensorFlow 版本不一致TensorFlow 2.0 到 2.15 之间tf.keras的保存格式有变化。解决训练和生成用同一个环境保存时用model.save(model.keras)而不是.h5加载时用tf.keras.models.load_model。如果跨版本导出权重再重新搭结构加载。6. 把 RNN 写诗推到「能看」三个进阶技巧与我的固定习惯第一个技巧是「预训练 embedding」。如果你手头有更大的中文语料比如新闻或小说可以先训练一个字符级 embedding再迁移到唐诗模型。TensorFlow 2.0 里把 embedding 层设为trainableFalse先冻结几轮再解冻微调。这样字表覆盖更全生僻字也能有合理向量。第二个技巧是「双模型对比」。同时训一个 LSTM 和一个 GRU生成时各跑十首人工挑。GRU 通常更「敢写」LSTM 更「稳」两者混着用能覆盖不同风格。第三个技巧是「后处理过滤」。生成结果里如果出现连续三个相同字符或者出现训练集里没见过的标点组合直接丢弃重生成。这个过滤规则写起来简单但能挡掉八成废句。我自己的固定习惯是每次训练完先不急着调温度而是用T0.7生成 20 首打印出来用笔圈出「像诗」的句子统计比例。如果比例低于 20%说明模型还没学好回去检查语料和 loss如果高于 40%说明可以开始调温度和惩罚了。这个习惯帮我省了很多「盲目调参」的时间。另外我从不追求 loss 降到最低因为写诗这件事太准了反而无趣。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

IEC104从站模拟器选型与调试实战指南 2026/10/1 17:04:35

IEC104从站模拟器选型与调试实战指南

做电力远动联调的工程师,基本都碰过这种场景:现场IEC104主站还没完全就绪,站端测控装置却已经上电,调度电话一个接一个地催,后台监控页面上却一个遥测都刷不出来。这时候你最需要的并不是什么高深的算法,而…

阅读更多 →
台式机接Type-C触摸屏显示器:DP Alt Mode与触摸校准排障 2026/10/1 17:04:35

台式机接Type-C触摸屏显示器:DP Alt Mode与触摸校准排障

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Django+ECharts城市PM2.5空气质量数据可视化分析实战 2026/10/1 17:04:35

Django+ECharts城市PM2.5空气质量数据可视化分析实战

简介:这是一套基于Python与Django框架的城市PM2.5空气质量数据可视化分析项目源码,面向计算机相关专业学生、课程设计或期末大作业开发者,也适合希望入门Django与数据分析的小白实战练习。资源包共64个文件,约12.38MB,…

阅读更多 →
Switch大气层系统跑PC游戏实战:Linux+Wine方案与性能边界 2026/10/1 17:04:35

Switch大气层系统跑PC游戏实战:Linux+Wine方案与性能边界

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
模具导柱导套磨损、卡滞、异响?从业近20年,别只怪配件质量 2026/10/1 17:04:28

模具导柱导套磨损、卡滞、异响?从业近20年,别只怪配件质量

#我在恒通兴做模具配件快二十年,对接过数不清的模具厂。很多师傅遇到导柱导套拉伤、模具开合模卡顿、运行异响,第一反应就是配件买差了,直接换新的导柱导套。结果装上跑不了多久,老问题又重新出现。 实际现场看下来,导…

阅读更多 →
PowerShell中使用where与where.exe查找文件:区别与实战 2026/10/1 17:04:28

PowerShell中使用where与where.exe查找文件:区别与实战

在 PowerShell 里想查个文件,很多人会下意识敲出where xxxx.log,然后看着满屏红色报错一脸懵。这个“where”到底怎么回事?其实在 PowerShell 里至少有俩“where”在打架:一个是原生命令Where-Object(别名就是where&am…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉