GPT-2生成文本多格式导出实战:三步把结果变成JSON、Markdown和纯文本 GPT-2生成文本多格式导出实战三步把结果变成JSON、Markdown和纯文本【免费下载链接】gpt-2Code for the paper Language Models are Unsupervised Multitask Learners项目地址: https://gitcode.com/GitHub_Trending/gp/gpt-2晚上十点产品经理在群里我「这批用 GPT-2 生成的 200 条商品卖点明早要导进运营后台。」gpt-2仓库名 GitHub_Trending/gp/gpt-2是论文《Language Models are Unsupervised Multitask Learners》的官方实现核心能力很纯粹——给一句开头它就能续写出一整段通顺文本。听起来很美好直到我发现默认脚本只会把生成结果噼里啪啦打在终端里200 条文案靠复制、清洗、再粘进表格够我熬到天亮。如果你也正打算把 GPT-2 的生成结果接进业务系统、写进数据库或直接发布成文章这篇文章就是为你准备的。我会用一个真实的导出任务串起全过程带你给这个项目加一道「多格式输出」能力让生成结果一键变成 JSON、Markdown 或纯文本。先说说三个让人抓狂的细节改造之前我对着终端里的原始输出发了好一会儿呆问题可以归结成三条① 输出里全是「噪音」。每段文本前面都顶着 SAMPLE 1 这种分隔线交互模式下还会把输入提示词回显一遍真正要的内容混在里面连复制都要先眯着眼挑。② 格式没法直接入库。运营后台要的是结构化数据——至少得是「正文 生成参数」的配对。纯文本一行行贴过去不仅慢还容易贴错行、漏字段。③ 批量生成时终端直接刷屏。generate_unconditional_samples.py跑一次就是十几段终端根本存不下历史关了窗口等于白跑。这三个痛点的根源是同一个生成管线只做了「解码」没做「出口」。我们要补的正是这最后一公里。一句话方案给生成结果加一道「格式化出口」思路很朴素在enc.decode()拿到文本之后、打印之前插入一个可插拔的格式器让同一段文本按需变成三种形态。选哪种格式由命令行参数决定完全不动模型本身。整体数据流长这样prompt ──► enc.encode() ──► sample_sequence() ──► token 序列 │ ▼ enc.decode() 得到原始文本 │ ┌───────────────────┴───────────────────┐ ▼ ▼ ▼ PlainTextOutput JsonOutput MarkdownOutput │ │ │ ▼ ▼ ▼ 控制台 samples.json samples.md模型、采样逻辑src/sample.py里的sample_sequence一概不动我们只新增一个格式化模块再在两个生成脚本里各接几行代码。风险小回滚也容易。开工准备五分钟把环境跑起来先把仓库克隆下来并装好依赖项目基于 TensorFlow 1.x记得按官方说明装tensorflow1.12.0git clone https://gitcode.com/GitHub_Trending/gp/gpt-2 cd gpt-2 pip3 install -r requirements.txt pip3 install tensorflow1.12.0 python3 download_model.py 124M为了让中文输出在终端里正常显示Linux/macOS 上建议先设一下编码export PYTHONIOENCODINGUTF-8动手第一步搞清楚文本是从哪冒出来的打开src/generate_unconditional_samples.py找到生成主循环整个链路其实只有三句话那么长out sess.run(output) # 1. 跑一次采样拿到 token 序列 text enc.decode(out[i]) # 2. 用 BPE 解码器还原成文本 print(text) # 3. 直接打印无任何加工文本在enc.decode()之后就已经是干净的字符串了问题只出在第三步。所以我们不改前面任何东西只把第三步替换成「格式化 打印 可选写文件」。动手第二步新建一个 30 行的格式化模块在src/下新建src/formatter.py内容分两块三个格式器 一个注册表式工厂。# src/formatter.py —— 负责把一段生成文本变成不同格式 import json import os import re from datetime import datetime class OutputFormatter: 格式器基类所有格式统一实现 format(text, metadata) def format(self, text, metadataNone): raise NotImplementedError(子类必须实现 format 方法) class PlainTextOutput(OutputFormatter): 纯文本原样返回最省事 def format(self, text, metadataNone): return text class JsonOutput(OutputFormatter): JSON正文和生成参数打包成结构化数据方便下游程序消费 def format(self, text, metadataNone): payload {text: text, length: len(text), word_count: len(text.split())} if metadata: payload.update(metadata) return json.dumps(payload, ensure_asciiFalse, indent2) class MarkdownOutput(OutputFormatter): Markdown自动补标题、整理段落、附生成信息可直接发文章 def format(self, text, metadataNone): title (metadata or {}).get(title, GPT-2 Generated Text) lines [# title, ] # 按空行切段落再把段内的换行和多余空格收敛成单个空格 for para in re.split(r\n\s*\n, text.strip()): lines.append( .join(para.split())) if metadata: lines [, ## 生成信息] lines [f- **{k}**: {v} for k, v in metadata.items()] return \n\n.join(lines) class FormatterFactory: 注册表式工厂想加新格式只需在这里补一行 _registry { text: PlainTextOutput, json: JsonOutput, markdown: MarkdownOutput, } classmethod def get(cls, name): fmt_cls cls._registry.get(name) if fmt_cls is None: raise ValueError(不支持的格式: %s可选 text/json/markdown % name) return fmt_cls()几个设计上的小心思值得多说一句元数据单独走metadata参数正文和生成参数模型名、温度、时间戳等解耦以后想加字段只动一处。ensure_asciiFalse必须写否则中文会被转义成\uXXXXJSON 文件里全是天书。工厂用字典注册新增格式 写一个类 登记一行不用改任何调用方代码。接着写辅助函数拼元数据、以及把结果追加进文件。def build_metadata(sample_id, model_name, temperature, top_k, top_p, promptNone): 把生成时的环境信息打包随正文一起导出便于事后溯源 meta { sample_id: sample_id, model_name: model_name, temperature: temperature, top_k: top_k, top_p: top_p, timestamp: datetime.now().isoformat(timespecseconds), } if prompt is not None: meta[prompt] prompt return meta动手第三步让 JSON 文件从头到尾永远合法写文件有个坑如果每生成一条就往文件里追加一个独立 JSON 对象最终文件会变成「多个对象拼在一起」不是合法的 JSON任何解析器都读不了。解决办法是让文件始终是一个 JSON 数组——追加新样本时找到末尾的]把它替换成,新样本]。def append_sample(path, fmt, sample_id, formatted): 把一条结果写进文件JSON 模式下保证文件始终是合法数组 if fmt json: if not os.path.exists(path) or os.path.getsize(path) 0: # 新文件直接写一个只有单个元素的数组 with open(path, w, encodingutf-8) as f: f.write([\n formatted \n]) return with open(path, r, encodingutf-8) as f: f.seek(0, os.SEEK_END) f.seek(max(0, f.tell() - 1)) # 退回到最后一个 ] 之前 f.write(,\n formatted \n]) else: # 纯文本 / Markdown 直接追加加一条分隔线方便人看 with open(path, a, encodingutf-8) as f: f.write( * 40 SAMPLE str(sample_id) * 40 \n) f.write(formatted \n\n)这样无论跑了多少条samples.json打开永远是结构完整的数组直接json.load()就能读。动手第四步把格式器接到交互脚本上现在改src/interactive_conditional_samples.py。先给interact_model增加两个参数其余签名保持原样def interact_model( model_name124M, seedNone, nsamples1, batch_size1, lengthNone, temperature1, top_k0, top_p1, models_dirmodels, fmttext, # 新增输出格式 text / json / markdown outfileNone, # 新增导出文件路径为空则只打印到控制台 ):然后改生成循环——原来print(text)的地方换成「格式化 → 打印 → 按需写文件」三步# 在进入 while True 之前解析一次格式器别放在热循环里 formatter FormatterFactory.get(fmt) while True: raw_text input(Model prompt ) while not raw_text: print(Prompt should not be empty!) raw_text input(Model prompt ) context_tokens enc.encode(raw_text) generated 0 for _ in range(nsamples // batch_size): out sess.run(output, feed_dict{ context: [context_tokens for _ in range(batch_size)] })[:, len(context_tokens):] for i in range(batch_size): generated 1 text enc.decode(out[i]) # 关键一行把正文和元数据交给格式器 metadata build_metadata( generated, model_name, temperature, top_k, top_p, raw_text) formatted formatter.format(text, metadata) print( * 40 SAMPLE str(generated) * 40) print(formatted) if outfile: append_sample(outfile, fmt, generated, formatted) print( * 80)文件底部原本的fire.Fire(interact_model)不用动新增的两个参数会被 fire 自动识别成--fmt和--outfile。动手第五步批量脚本接线顺手修一个计数 Bugsrc/generate_unconditional_samples.py的改造逻辑几乎一样唯一区别是没有 prompt元数据里就不带输入。不过在抄代码之前先注意原脚本的一个小毛病for i in range(batch_size): generated batch_size # ← 原脚本写的是 batch_size当batch_size2时编号会从 1、2、4、6 这样跳着涨样本序号全乱了。接线时顺手改成generated 1编号就恢复连续generated 0 formatter FormatterFactory.get(fmt) while nsamples 0 or generated nsamples: out sess.run(output) for i in range(batch_size): generated 1 # 修复原脚本的计数问题 text enc.decode(out[i]) metadata build_metadata( generated, model_name, temperature, top_k, top_p) formatted formatter.format(text, metadata) print( * 40 SAMPLE str(generated) * 40) print(formatted) if outfile: append_sample(outfile, fmt, generated, formatted)不要忘记在文件头部加一行导入from formatter import FormatterFactory, build_metadata, append_sample两个脚本都在src/下运行和formatter.py同目录from formatter import ...直接可用。改造前后对比同一段输出两种待遇以「什么是人工智能」为提示词跑一次交互改造前的终端是这种画风 SAMPLE 1 什么是人工智能 人工智能Artificial Intelligence, AI是计算机科学的一个分支…… SAMPLE 2 什么是人工智能 另一段续写结果……改造后同一句话存成--fmtjson --outfilesamples.json文件里是这样[ { text: 人工智能Artificial Intelligence, AI是计算机科学的一个分支致力于模拟人类智能……, length: 1204, word_count: 218, sample_id: 1, model_name: 124M, temperature: 0.8, top_k: 40, top_p: 1, timestamp: 2026-08-13T16:44:57, prompt: 什么是人工智能 } ]这份数据可以直接喂给json.load()入库而 Markdown 模式产出的文件去掉## 生成信息一节就能当文章草稿发出去。把前后差异拉个表对比维度改造前改造后输出内容正文混着分隔线、提示词回显干净正文可选附带生成参数入库方式手工复制粘贴、人肉清洗一条命令产出合法 JSON直接入库批量留存终端滚动刷屏关窗即丢持续追加写文件随时可断点续存格式切换固定纯文本--fmt一行切换三种格式三种格式怎么选也给你一张速查表格式最大优势明显短板典型场景纯文本零依赖、人眼可读没有结构程序难以消费快速预览、日志、邮件草稿JSON结构清晰任何程序可解析直接阅读略费劲入库、API 响应、数据交换Markdown兼具结构与排版下游需要 Markdown 渲染器文档、博客草稿、README进阶玩法四个让导出更省心的姿势① 三行代码加一个新格式。比如想要 HTML继承基类、实现format、在注册表登记一行class HtmlOutput(OutputFormatter): def format(self, text, metadataNone): return h1GPT-2 生成结果/h1\np text.replace(\n, br) /p # 在 FormatterFactory._registry 里补一行即可生效 # html: HtmlOutput,调用方、命令行参数一行都不用改这就是注册表模式的好处。② 格式化失败时兜底。格式器理论上不会抛错但万一下游加了奇怪的自定义格式器主流程不该被它拖死try: formatted formatter.format(text, metadata) except Exception as e: print(f[formatter] 第 {generated} 条格式化失败回退纯文本: {e}) formatted text # 兜底保证生成主流程不中断③ 批量落盘时攒批写。样本量很大时每条都打开一次文件有点浪费可以在循环外把文件句柄打开复用或用列表攒够 N 条再统一写入。④ 元数据随你扩展。想把生成参数也留档只需在build_metadata里加字段JSON 和 Markdown 会自动带上不用碰格式器。新手高频疑问速答Q1我加了--fmtjson却没效果还是纯文本A先确认参数名写的是--fmt不是--output_format并且你改的是本仓库src/下的两个脚本。参数默认值是text没匹配上就退回纯文本这也保证了旧命令完全兼容。Q2JSON 文件写一半程序崩了还能用吗A不能用崩溃时文件可能停在非法状态。批量场景建议先跑几条小样本验证或者把落盘改成「内存攒批、结束时一次性写入」。Q3生成文本里有引号、换行会不会弄坏 JSONA不会json.dumps会自动转义。真正要留意的是 Markdown 的段落排版我们已用re.split按空行切段处理。Q4batch_size和nsamples是什么关系A一次sess.run会同时生成batch_size条交互脚本会循环跑nsamples // batch_size轮所以batch_size必须能整除nsamples。批量脚本没有这个限制但建议也保持一致便于预估输出条数。Q5提示module object has no attribute to_floatA原项目基于 TensorFlow 1.xtf.to_float在 TF2 里被移除了。请按DEVELOPERS.md安装 TF 1.12或在 TF2 下开启 compat 模式运行。Q6显存不够1558M 模型跑不动怎么办A先用 124M 把格式逻辑调通再换大模型也可以调小batch_size和length来降显存占用。写在最后你的下一步这一趟下来你只动了两个文件、新增了一个模块就给 gpt-2 补上了从「终端输出」到「可消费产物」的最后一公里JSON 直接入库Markdown 直接发稿纯文本随手存档而模型与采样逻辑始终没碰过一下。接下来你可以试试把 CSV 格式器补上对接 Excel把append_sample换成带缓冲的批量写入处理上万条样本或者给 Markdown 格式器加上自定义标题模板。想深入理解生成链路可以接着读src/sample.py里的采样实现和src/encoder.py的 BPE 编解码需要部署到不同环境项目里的Dockerfile.cpu和Dockerfile.gpu也值得翻一翻。去把那个周五深夜没做完的导出任务重新跑一遍吧。【免费下载链接】gpt-2Code for the paper Language Models are Unsupervised Multitask Learners项目地址: https://gitcode.com/GitHub_Trending/gp/gpt-2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考