news 2026/2/7 18:39:40

使用GPT2、FastAPI和ReactJS训练与部署AI格言生成器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用GPT2、FastAPI和ReactJS训练与部署AI格言生成器

如何训练与部署自定义AI生成的格言:使用GPT2、FastAPI和ReactJS

问题

好的格言能让我们更坚强。格言真正鼓舞人心的并非其语调或满足感,而是分享者所反映的、真正有益于他人的生活经验。上面这段关于格言的话(格言套娃)并不是我写的,而是我训练的一个AI模型生成的。而且它说得比我可能表达的更好。格言对不同的人意味着不同的事物。有时它们激励我们,鼓舞我们。另一些时候,它们让我们思考生活、宗教,有时它们只是让我们发笑。

那么,我们能否训练一个AI模型来生成更多格言,让我们思考、发笑或受到启发?这就是我开始这段旅程的动机。

太长不看版
我已经在具有特定风格的格言上微调了GPT2模型,风格包括励志/鼓舞人心、有趣以及严肃/哲学,并部署在一个可即用的网站上:AI格言生成器。

模型

2020年6月3日,某中心发布了GPT3,这是一个在570GB互联网文本上训练的巨型语言模型。人们将这个多才多艺的模型应用于各种场景,从创建应用设计、网站,到实现近乎魔法的Excel函数。但存在一个问题——模型权重从未公开。我们访问它的唯一方式是通过付费的API。

所以,让我们时光倒流到2019年11月,那时GPT-2发布了。GPT-2虽然不如GPT-3强大,但在文本生成领域引发了一场革命。我记得当我阅读模型生成的演示文本时,惊讶得下巴都要掉下来了——其连贯性和语法结构近乎完美。我对模型的要求不是成为魔术师,而是能够生成结构完美的英语句子。为此,GPT2已经绰绰有余。

数据集

首先我需要一个数据集。从网上爬取格言是一个选择,但在那之前我想看看是否已经有人做过这件事。果然!Quotes-500k是一个包含近50万条格言的数据集,所有格言都是从网上爬取的,并附有诸如知识、爱情、友谊等标签。

现在我希望模型能够根据特定主题生成格言。由于我计划使用预训练模型,条件性文本生成并不容易实现。PPLM是某机构发布的一个模型,或者更确切地说,是一种使用预训练模型的方法,旨在实现这一目标(一篇非常有趣的论文,一定要看看),但最终我选择了另一条路。我决定训练三个模型,每个都针对特定类型的格言进行微调。

我考虑了三种类型——励志型、严肃型和有趣型。我使用Quotes500k数据集及其标签,根据标签将格言分离到这三个类别中。对于励志数据集,我使用了诸如爱情、生活、鼓舞人心、励志、人生教训、梦想等标签。对于严肃型,我选择了哲学、生活、上帝、时间、信仰、恐惧等标签。最后,对于有趣型,我只用了幽默和搞笑这类标签。

预处理

这里没什么特别的;只是基本的清理工作。例如:

  • 转换为小写
  • 替换缩写,如将"wasn’t"替换为完整形式"was not"。
  • 移除HTML特殊实体(因为这是一个从网上抓取的语料库)
  • 移除多余的空格
  • 在单词和标点符号之间插入空格
  • 拼写检查和纠正(使用pyenchant)

你可能会有疑问:那停用词呢?词形还原呢?那些步骤在哪里?删除停用词和词形还原并不是你在每个NLP任务中都必须执行的强制性步骤。这真的取决于任务。如果我们使用TF-IDF这类模型进行文本分类,那么,是的,做所有这些是有意义的。但对于使用上下文(如神经网络模型或N-Gram模型)的文本分类来说,必要性就小一些。如果你在进行文本生成或机器翻译,那么删除停用词和词形还原实际上可能会损害性能,因为我们正在从语料库中丢失有价值的上下文信息。

句首/句尾标记与最大长度

我还希望生成的格言不要太长。所以我查看了数据集,绘制了格言长度的频率分布图,并决定了一个适当的截断长度。在励志语料库中,我丢弃了所有超过100个单词的格言。

格言长度频率(励志型)
另一个使格言简短的重要方面是模型预测句尾标记的能力。因此,我们用句首标记和句尾标记来包装每条格言。

tokenizer=AutoTokenizer.from_pretrained('gpt2')MAX_LEN=100train_m=""withopen(train_path,"r",encoding='utf-8')asf:forlineinf.readlines():iflen(line.split())>MAX_LEN:continuetrain_m+=(tokenizer.special_tokens_map['bos_token']+line.rstrip()+tokenizer.special_tokens_map['eos_token'])withopen(train_mod_path,"w",encoding='utf-8')asf:f.write(train_m)test_m=""withopen(test_path,"r",encoding='utf-8')asf:forlineinf.readlines():iflen(line.split())>MAX_LEN:continuetest_m+=(tokenizer.special_tokens_map['bos_token']+line.rstrip()+tokenizer.special_tokens_map['eos_token'])withopen(test_mod_path,"w",encoding='utf-8')asf:f.write(test_m)

训练

现在我们已经准备好并处理了数据集,让我们开始训练模型。来自某机构的Transformers库因其易于使用的API和惊人的预训练权重模型集合而成为显而易见的选择。得益于某机构,训练或微调模型是一件轻而易举的事。

我们首先加载GPT2的模型和分词器。

tokenizer=AutoTokenizer.from_pretrained('gpt2')model=AutoModelWithLMHead.from_pretrained('gpt2')

就这样。在你编码时,巨大的预训练模型的全部力量就掌握在你手中。

现在,让我们定义一个加载数据集的函数。

defload_dataset(train_path,test_path,tokenizer):train_dataset=TextDataset(tokenizer=tokenizer,file_path=train_path,block_size=128)test_dataset=TextDataset(tokenizer=tokenizer,file_path=test_path,block_size=128)data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer,mlm=False,)returntrain_dataset,test_dataset,data_collator train_dataset,test_dataset,data_collator=load_dataset(train_mod_path,test_mod_path,tokenizer)

现在我们有了数据集,让我们创建Trainer。这是处理所有训练过程的核心类。

fromtransformersimportTrainer,TrainingArguments training_args=TrainingArguments(output_dir="./storage/gpt2-motivational_v6",#输出目录overwrite_output_dir=True,#覆盖输出目录的内容num_train_epochs=10,#训练轮数per_gpu_train_batch_size=32,#训练批大小per_gpu_eval_batch_size=64,#评估批大小logging_steps=500,#两次评估之间的更新步数save_steps=500,#每#步后保存模型warmup_steps=500,#学习率调度器的预热步数)trainer=Trainer(model=model,args=training_args,data_collator=data_collator,train_dataset=train_dataset,eval_dataset=test_dataset,prediction_loss_only=True,)

到这个时候,我们已经拥有了开始训练所需的所有要素——模型、分词器和训练器。剩下要做的就是训练模型。训练完成后,我们只需要保存模型和分词器以备使用。

trainer.train()trainer.save_model("./storage/gpt2-motivational_v6")tokenizer.save_pretrained("./storage/gpt2-motivational_v6")

每个模型都在单个P5000 GPU上训练了约50轮,总共约12小时(不包括所有测试运行以及出现问题的运行)。

推理

我们已经训练并保存了模型。现在呢?为了进行推理,我们使用了某机构的另一个杰出特性——管道。这个很棒的特性让你只需几行代码就能将模型投入生产。

tokenizer=AutoTokenizer.from_pretrained("./storage/gpt2-motivational_v6")model=AutoModelWithLMHead.from_pretrained("./storage/gpt2-motivational_v6")gpt2_finetune=pipeline('text-generation',model=model,tokenizer=tokenizer)# gen_kwargs有不同的选项,如max_length、beam_search选项、top-p、top-k等gen_text=gpt2_finetune(seed,*gen_kwargs)

gen_kwargs配置文本生成。使用了k=50的top_k采样和p=0.95的top_p采样的混合方法。为了避免文本生成中的重复,使用了no_repeat_ngram_size = 3和repetition_penalty=1.2。

用户界面

现在我们已经训练好了核心模型,我们需要一种与它交互的方式。每次需要它生成内容时都写代码对用户不友好。所以,我们需要一个UI。我选择用ReactJS快速构建一个简单的UI。

虽然我无法将整个项目放到Github上,但我会将关键部分作为Github Gists发布。

出于UI的目的,我给三个模型起了代号——Inspiratobot(励志型)、Aristobot(严肃型)和FunnyBot(有趣型)。

UI的主要功能包括:

  • 能够在三种不同风格之间进行选择
  • 可以自己输入引语开头,也可以使用众多随机的起始种子之一(将自动填充)。如果不喜欢某个种子,点击随机按钮获取另一个种子。
  • 在高级设置中,可以调整引语的最小和最大长度。也可以调整多样性(或采样中的温度参数)
  • UI还使用了来自Unsplash的库存照片作为引语的背景,因为这是近来的潮流。它会从一系列适合当前生成引语风格的照片中进行选择。
  • 还可以让你以1到5的等级为引语评分。

UI项目的文件夹结构如下:

| +---public | favicon.ico | index.html | logo.png | logo.svg | manifest.json | ---src App.js customRatings.js # 心形评分组件 debug.log index.js quotes.css # 页面的CSS quotes.js # 核心页面 theme.js | | .gitignore | package-lock.json | package.json

后端API服务器

为了托管和服务模型,我们需要一个服务器。为此,我选择了FastAPI,这是一个直截了当的Web框架,专门用于构建API。

由于它的极致简单性,我强烈推荐FastAPI。一个非常简单的API示例(来自文档)如下:

fromtypingimportOptionalfromfastapiimportFastAPI app=FastAPI()@app.get("/")defread_root():return{"Hello":"World"}@app.get("/items/{item_id}")defread_item(item_id:int,q:Optional[str]=None):return{"item_id":item_id,"q":q}

不到10行代码就能为你的API搭建一个Web服务器,这听起来好得不像真的,但它确实如此。

不深入细节,API的关键部分是引语的生成,以下是相关代码。

defpostprocess_gen_text(gen_text):sentences=gen_text.split(".")fori,sentinenumerate(sentences):sent=". ".join([s.strip()forsinsent.split(".")]).capitalize().strip()sent=re.sub(r"\bi\b","I",sent)sent=re.sub(r"\bgod\b","God",sent)sent=re.sub(r"\bchrist\b","Christ",sent)sentences[i]=sentreturn". ".join(sentences).strip()defgenerate_quote(model_name:ModelName,seed:str,gen_kwargs:dict):globalmodelglobaltokenizerglobalcurrent_model_nameifmodel_name!=current_model_name:_load_model(model_name)gpt2_finetune=pipeline("text-generation",model=model,tokenizer=tokenizer,device=-1,)gen_text=gpt2_finetune(seed,**gen_kwargs)[0]["generated_text"]returnpostprocess_gen_text(gen_text)

项目的文件夹结构:

+---app | | api.py | | db_utils.py | | enums.py | | timer.py | | init.py | | +---data | funny_quotes.txt | motivational_quotes.txt | serious_quotes.txt | +---models | +---gpt2-funny | | | +---gpt2-motivational | | | |---gpt2-serious | | app_config.yml | logger.py | logging_config.json | main.py | memory_profiler.log | pipeline.log | requirements.txt

部署

将应用程序容器化,启动一个EC2实例并进行托管,这本来会很容易。但我想要一个便宜(如果不是免费的话)的方案。另一个挑战是每个模型大约500MB,将它们加载到内存中会使RAM消耗徘徊在1GB左右。所以EC2实例成本会很高。除此之外,还需要在云端存储三个模型,这也需要存储成本。最重要的是,还需要一个数据库来存储用户给出的评分。

带着这些需求,我开始寻找产品/服务。为了让事情更简单,我将应用分成了两部分——一个后端API服务器和一个内部调用后端API的前端UI。

在搜索过程中,我偶然发现了一个开发者可以使用的免费服务的超棒列表。在评估了几个选项之后,我最终确定了以下选项,以使部署的总成本尽可能低:

  • 后端API服务器 – Kintohub
  • 前端托管 – Firebase(免费)
  • 数据库 – MongoDB Atlas(免费)

KintoHub
“KintoHub是一个为全栈开发者设计的一体化部署平台”,主页上写道。如果成本不是问题,我本可以将整个应用程序部署在KintoHub上,因为这就是他们提供的服务——一个可以很好扩展的后端服务器、一个服务静态HTML文件的前端服务器以及一个数据库。在后台,他们通过一个非常易于使用的Web界面将应用程序容器化,并部署在选定配置的机器上。最棒的是,所有这些都可以通过Github完成。你将代码提交到Github仓库(公共或私有),并通过指向该仓库直接部署应用。

就必须要完成的最低限度设置而言,一个页面就足够了。

就是这样。当然还有更多可用设置,比如应用程序运行所需的内存等。虽然KintoHub有一个免费层,但我很快意识到,由于模型的内存消耗,我需要至少1.5GB才能使其运行而不崩溃。所以我转到了按需付费层,他们每月慷慨地赠送5美元信用额度。成本计算器显示应用程序托管将花费5.5美元,我可以接受(仍在等待月底看实际成本)。

Google Firebase
Firebase是很多东西的集合。它的主页上说它是一个完整的应用开发平台,事实也确实如此。但我们只对Firebase Hosting感兴趣,他们允许你免费托管单页网站。部署应用非常轻松。你需要做的就是:

  1. 安装firebase CLI
  2. 在你的React项目上运行npm build
  3. 从项目的根文件夹运行firebase init,并将源路径从public指向build
  4. 运行firebase deploy

只需要一个非常简短的教程就能完成。

MongoDB Atlas
MongoDB Atlas是一个云数据库即服务,在云上提供MongoDB,好处是他们提供了一个具有512MB存储空间的免费层。对于我们的用例来说,这绰绰有余。

创建一个新的集群是直截了当的,一旦解决了访问问题,就可以使用像pymongo这样的Python包装器来实现我们的数据库连接。

成果

所有这些工作的成果是一个网站,你可以在其中与模型互动,生成格言并假装是自己的作品 😄。

虽然模型并不完美,但它仍然能产生一些真正让你思考的好格言。这不就是你对任何格言的期望吗?玩得开心。

免责声明

本项目中提及的公司名称仅用于技术背景描述,不构成任何形式的商业推广。相关技术实现和部署方案的选择基于作者的个人评估和项目需求。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)或者 我的个人博客 https://blog.qife122.com/
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/6 21:06:05

Snap Hutao:4大核心突破解决原神玩家90%的管理难题

Snap Hutao:4大核心突破解决原神玩家90%的管理难题 【免费下载链接】Snap.Hutao 实用的开源多功能原神工具箱 🧰 / Multifunctional Open-Source Genshin Impact Toolkit 🧰 项目地址: https://gitcode.com/GitHub_Trending/sn/Snap.Hutao …

作者头像 李华
网站建设 2026/2/6 21:40:44

解锁Vue数据透视表:从入门到精通的实战指南

解锁Vue数据透视表:从入门到精通的实战指南 【免费下载链接】vue-pivot-table A vue component for pivot table 项目地址: https://gitcode.com/gh_mirrors/vu/vue-pivot-table Vue数据透视表是前端开发中处理多维数据分析的强大工具,能够帮助开…

作者头像 李华
网站建设 2026/2/5 21:01:58

突破性革新:Parquet文件处理的浏览器端革命

突破性革新:Parquet文件处理的浏览器端革命 【免费下载链接】parquet-viewer View parquet files online 项目地址: https://gitcode.com/gh_mirrors/pa/parquet-viewer Parquet文件处理长期以来受限于本地环境配置的复杂性,直到Parquet Viewer的…

作者头像 李华
网站建设 2026/2/6 22:25:41

如何从零开始RFSoC开发:面向新手的软件定义无线电实践指南

如何从零开始RFSoC开发:面向新手的软件定义无线电实践指南 【免费下载链接】RFSoC-Book Companion Jupyter Notebooks for the RFSoC-Book. 项目地址: https://gitcode.com/gh_mirrors/rf/RFSoC-Book 本文将帮助你从零开始学习RFSoC开发,通过软件…

作者头像 李华
网站建设 2026/2/6 19:50:45

Chandra OCR部署实操:NVIDIA驱动/vLLM/chandra-ocr三步环境配置

Chandra OCR部署实操:NVIDIA驱动/vLLM/chandra-ocr三步环境配置 1. 为什么Chandra OCR值得你花30分钟配好环境 你有没有遇到过这样的场景:手头堆着几十页扫描版合同、数学试卷PDF、带复选框的医疗表单,想快速转成结构化文本放进知识库&…

作者头像 李华
网站建设 2026/2/7 18:17:56

4×24GB显卡跑不动?Live Avatar多GPU部署问题全解析

424GB显卡跑不动?Live Avatar多GPU部署问题全解析 1. 真实困境:为什么5张4090也带不动一个数字人? 你是不是也遇到过这样的场景:手握4块甚至5块RTX 4090,每张卡24GB显存,信心满满地拉起Live Avatar镜像&a…

作者头像 李华