- Ollama 是一个全新的本地可运行的大模型框架,可以本地运行的 “ChatGPT”。这节课我们还会用到 GPT-SoVITS。这是一个 TTS(Text-to-Speech,文本转语音)大模型,也可以在本地运行。你可能也都听说过多模态这个词,GPT-SoVITS 就是一个语音类的多模态模型。咱们这节课的任务,就是结合 Ollama 的文本能力和 GPT-SoVITS 的语音能力,开发一个可以本地运行的实时语音聊天助手,实现一个类似 ChatGPT 的语音助手。 Ollama 是一套构建和运行大模型的开发框架,它采用的模型量化技术进一步降低了大模型对显存的需求。
- 我们用 Ollama 的 Python 接口来定制自己的大模型。这里面有一个 Modelfile,它是 Ollama 大模型的配置文件,你可以修改各种配置,然后运行接口程序。比如我就自己配置了一个基于 Llama2 的大模型,设置了温度,token 数量和系统提示词。可以使用 Hugging Face 的 transformers 库结合上述数据进行微调, 这样就可以让微调后的大模型学习到小助理日常的对话方式和常见的知识问答。把微调完成后生成新的模型命名为 fine_tuned_llama。在此基础上修改 Python 代码里的模型名称,就可以实现小助理专用模型的调用了。
- 多模态模型和语言模型一样本质就是一个序列化模型。因此多模态只是语言大模型的扩展。
- TTS 大模型:要实现一个语音小助手,最核心的能力当然是语音能力,那语音能力如何跟 Ollama 大模型文本能力对接呢?这里就要用到 TTS 技术。TTS 是 Text-to-Speech 的缩写,指的是文本转语音技术。通过 TTS,用户可以输入文字,让计算机生成自然语音,从而实现语音提示、有声书、语音助手之类的功能。GPT-SoVITS 就是一个可以实现语音克隆的 TTS 大模型,最大的特点是只需要 5s 左右的语音输入样本,就可以实现语音克隆,之后还可以用我们训练好的模型实现 TTS 文本转语音操作,音色、音调的还原度也很高。
- 微调:将一部分专业私有数据加入大模型,整个大模型就具备了某项专业能力。如果你想完全避免私有数据的幻觉问题,可以用向量数据库结合大模型开发。向量数据库 VS 微调在工程实践经验看来,向量数据库往往比微调效果还好。
- 专有模型开发:你如果对全能数据库本身不满意,还可以基于开源大模型二次开发,也就是我们说的专有模型开发,例如专门实现代码开发能力的大模型。这个基于开源大模型的二次开发,又分为两种情况,偏向行业数据的行业大模型开发,以及偏向大模型能力扩展的大模型插件开发。专有模型相当于全能数据库的二次开发,以及全能数据库自定义函数的开发。最终的应用往往需要在专有 LLM 或公有 LLM 基础上结合私有数据微调,同时结合已有的应用接口整合出一个可靠的 AI 应用,避免大模型那些出错的“幻觉”,留下我们需要的创造性。
- 微信 AI 聊天功能需要解决两个问题。首先需要一个支持 RPA 架构(Robotic Process Automation,机器人流程自动化)的微信机器人,实现微信消息的代理收发,以及一些微信界面的自动化操作。其次,AI 软件需要对接大模型,实现对用户聊天的代理。比如上述交互中的 @AI广告 这个消息前缀,目的就是让 RPA 机器人在群聊中识别到用户想调用 AI 能力,从而路由到对应的 AI 广告机器人,让机器人处理、回复这条消息。
- 我们选用了支持微信 RPA 的Python 库 WXAuto。这个 Python 库支持所有的微信代理操作,比如搜索群名搜索、读取群列表、读取群消息列表、模拟按键操作等。
- 挑战 1,轮询效率问题:由于 RPA 的基本原理是模拟界面操作,根据之前的流程,为了尽快的处理消息,即使群里完全没有新消息,RPA 也要定时轮询群消息。但是,一旦群的数量增加,这种轮询的效率会越来越低。这时候,如何高效轮询,如何减少轮询的次数,就成为了最关键的问题。如果轮询时段内没有消息,则只要读第一个群就停止了,如果有消息则正好读完有消息的群就停止,做到了性能和效率最大化。
- 挑战 2,消息可靠性问题:微信 AI 这个项目里,我们通过 RPA 读取群消息列表完成了消息读取。其中最大的问题就是微信消息列表不提供消息 ID 和时间戳,这让从消息列表里分解出最新消息变得非常困难。如果存储和比对整个消息列表,那对计算量和数据的要求都会过高。最终我们采用了一个折中的方案。我们注意到微信消息列表中一段时间的消息会有一个总的时间戳,它本身也是一条消息,我们利用了这个时间戳消息,存储消息列表里最新时间戳之后的消息,并逐个比对,这样就得到了可靠的最新消息。
- 自有模型训练:我们基于 ChatGLM-6B 模型搭建的 AI 营销客户问答互动自动化系统已经完全够用。在工程实践上,一开始我们也采用了现有产品库信息来微调 ChatGLM-6B 模型,后期则将产品知识库数据输入到大模型 AI 中,让大模型在会话过程中学习产品知识库。
- 结果:主要用户在运营部门。原来很多操作需要登录各个内部系统,最终这些操作都被我们集中到了微信 AI 里,实现了 10-20 倍的效率提升。典型的操作有广告类数据报表、广告异常数据报警、企业知识库内部检索、AI 内部数据分析等。目前我们运营团队几乎所有日常操作都在微信 AI 上完成,这个结果可以说是无心插柳柳成荫。本以为场景在客户营销侧,最终发现真正有价值的在内部运营侧。
- 营销文章功能,他其实是整个 AI 营销系统的核心功能。如何将产品信息整理成问答的数据集,也就是一个数据工程问题。
- 大模型微调可以采用 LLaMA-Factory 大模型微调框架,LLaMA-Factory 支持上百种大模型的微调,也支持简单的界面操作,比较好上手。基础模型注意选择 ChatGLM,微调方式选择 LoRA 微调。LoRA 训练成本较低,也更容易训练成功。在微调过程中注意观察 loss 损失曲线,损失曲线下降并趋于平稳就可以了。
- 提示词 + 知识库的模式,解决产品数据更新的问题。创建向量数据库,把产品信息存到向量数据库中。
- 我们选用的是开源向量数据库 Milvus。Milvus 是一个查询比较高效的向量数据库,开源版支持很好的私有性,适合我们的私有化场景,还具备分布式扩展能力,将来的扩展性很好。Milvus 数据库的 Collection 就相当于传统数据库的表,因为 Milvus 数据库是非结构化的,因此可以把产品信息分成若干段结构存入Milvus。collection_name = "product_info" 可以理解为表名称,schema = CollectionSchema 则是表结构。collection.insert(entities) 就是往数据表写数据。输出 Distance 表示这次查询的输入向量和结果向量的相似度,数字越小表示越相似。
- 第一步是根据用户提问的问题,用分词技术提取出产品名,可以用 Python 的分词库 jieba 库,提取专有名词,也就是产品名称。最后把名字提取和向量数据库查询结合起来,再把整个结果组织成大模型提示词就可以了。
- 迭代:规则引擎方案,输出文章:先由人工提供一个文章架构,再由 AI 写具体的模块内容,实际上这也是目前用 AI 做内容的标准方法。营销文章生成过程分为规则引擎 + 产品信息 + ChatGLM 改写。
- 第一步,设计 Agent(助理)的系统提示词。如果把 Agent 类比为一个公司的前台的话,这一步就是告诉前台整体的工作职责和对客户的态度,或者说,限定大模型的交互边界。Agent 平台提供了统一的界面,也就是助理聊天界面。
- 工作流和插件的低代码开发模式,也就是 Agent(助理)设计的第三步,复用现有业务和复用现有云计算能力,这些都非常方便,至少有 10-20 倍的业务开发速度提升。而且你开发的工作流可以直接分享,进一步提高了别人的开发效率。、
- 挑战:其一,数据私有化这是企业的常见需求,利用现有 Agent 平台开发营销 AI 2.0 平台,不可避免地要将私有数据和现有 Agent 平台打通,企业客户不一定能接受这个方案。其二,在能力复用方面,我们现有的营销平台功能非常多,如果用现有 Agent 平台来开发,就需要适配 Agent 平台重新开发一遍,开发成本很高。想要搭建一个完整的营销平台 2.0 版本,会有很大的成本问题。用现有的 Agent 平台本身需要成本,使用它代理的大模型也需要 token 成本。因此,我们要从底层实现一套类似 Coze 平台的架构。
- Agent 智能体在平台的核心其实就两个,一是用于限定机器人交互的系统提示词,二是用于实现业务逻辑的工作流。工作流开发在系统插件、LLM 能力以及知识库、Memory 系统记忆等大量系统能力的加持下,能够提升不小的开发效率。
黄焖鸡餐馆菜单的数据为例,我们看一下私有知识库的搭建流程
- 第一步,将菜单数据整理成固定的输入格式
- 第二步,将菜单向量化,将类似 item["description"] 这样的本字段转化为向量表示。这一步核心是 get_embedding(menu_descriptions) 这个逻辑。它实现的是文本向量的转化,只有转化为向量数据格式才能在向量数据库做后续操作。
- 第三步,存储和读取向量数据库,这里以 Milvus向量数据库 为例,注意要将菜单信息和向量信息一起存储。整个模块最核心的一行代码其实就是这句 FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768)。你可以把它理解为传统数据库里的索引。加上这个字段后,后续工作流中需要用到菜单时都可以用向量查询,具体查询类似下面的伪代码。用户的输入例如 “喜欢辣味的菜品” 可能每个人表述不一样。但是同样需求下,通过向量查询就可以找到相似的菜品信息,这正是向量数据库的核心作用。如果单独针对一个应用开发这样一个知识库是没问题的,但是我们要做的是让所有助理可以复用一套知识库逻辑,因此在实战中,我们会针对这类逻辑开发一个统一的插件。
- query_knowledge_base 是刚才说的插件能力,在用户上传自己的菜单之后可以直接使用,filter_spicy_dishes 则是我们内部针对菜单这个行业场景开发的菜品推荐逻辑,它可以是基于 LLM 开发的。看到这个编排工作流的 yaml 配置,你可能意识到它没办法和我们的 Agent 底层 Demo 直接对接到一起。实际上我们还需要一个统一的、基于 yaml 配置调度具体工作流逻辑的框架。
- 我们分析了 Agent 平台的操作逻辑,设计并实现了一套自有的 Agent 代理层核心。基于这套核心,我们把原有营销平台的功能全部转化开发了 Agent 平台的插件能力。实现营销 Agent 平台的核心是实现 Agent 代理层的逻辑,而 Agent 代理层的核心有两点,一是基于上下文的用户意图识别,二是调用自定义的工作流完成业务逻辑。而最好的上下文意图识别的方案就是大模型。
- QuestionAnswerChain 是一个自定义的 Chain,而 Chain 就是 LangChain 框架的核心概念,你可以理解为一个 Chain 就是一个自定义的具备单独能力的智能体。针对用户搜索的输入,通过 self.llm(input_text) 来感知到用户搜索的关键词,这一步是大模型微完成的。第二步用 GoogleSearchTool 查找信息,实际上是我们编程做的决策和规划。最后我们利用了大模型的语言能力来做输出的执行,也就是 self.llm(search_results)。
- 一个基于 Chain 的简单智能体,其感知、决策、规划、执行,每一步都有我们人类的设定和参与,就能很好的完成这个小任务。类似任务其实可以用更加智能的处理方式,让大模型做更多决策。在 LangChain 框架中,意图识别和工具选择可以依赖大模型的理解和预定义的代理逻辑。
- LangChain 的核心概念就是例子中的 Chain 和 Agent,实际上 Chain 就是一个人类可以自定义决策和规划执行逻辑的智能体,Agent 则在更高层,可以根据上下文自主决策和规划调用这些智能体。LangChain 框架还有其他概念,都是辅助的智能体工作的。比如 llm 是大模型,PromptTemplate 是处理提示词的,memory 是处理会话消息历史的,Message 表示各种消息,tool 是传统工具。
- RAG 方案的本质是通过向量查询确定抽取的信息。但是,如果用户上下文复杂,这种类似模糊查询的方法,不能准确定位用户真实意图,很可能查询到的产品信息是不精确的。实际上,基于提示词 + RAG 的方案已经可以实现 80% 的问题用智能客服替代,剩下的问题还是可以用人工坐席,如果想提高这个比例怎么办呢?这时候,就需要一个可以精确识别用户意图的方法了。真实的智能客服项目的思路是用大模型先做用户意图识别,把问题分类,不同的问题用不同的方案。简单的问题用规则库和提示词解决,产品信息查询和知识类用 RAG 的方案,复杂用户问题识别和信息提取才需要微调,微调大模型能力之后,还是继续结合提示词 + RAG 解决问题。
- 选用 Dify 智能体开发框架,因为它同时具备定制的能力和拖拽式的编排界面,很方便。Agent 智能体的核心逻辑就是意图识别和 ReAct 推理。意图识别简单来说就是根据上下文做问题分类,ReAct 是一种推理框架。其中,Re=推理(Reasoning),Act=行动(Acting),Re 推理可以简单地理解为在特定的问题分类下,准确地识别用户的上下文,获取解决这个问题需要的参数,Act 行动则是具体解决这个问题的工作流程序逻辑。在智能客服项目里,怎么让大模型准确的识别出问题分类和用户参数呢?只需要使用 Dify 框架下的 Chatflow。为了解决自然语言输入中用户意图识别的复杂性,Chatflow 提供了问题理解类节点,我们把问题分类编辑好之后,就可以借助大模型完成用户意图识别了。
- 预制了 3 个问题分类:售后相关的问题,产品操作使用相关的问题,其他问题。定义这些具体问题分类的描述,实际上就是在告诉大模型怎么做意图识别。每个问题分类后续工作流的输入参数定义,实际上就是在告诉大模型 Re 推理应该从上下文提取哪些用户参数。最后工作流的具体逻辑就是实现 Act 行动。
- 由于意图识别和 ReAct 在特定环境下能力不足,我们才需要微调大模型适配这些场景。这里面一个很重要的场景就是自我认知的微调。以我们客服项目里的电脑配件场景为例,如果直接使用通用模型进行客服服务,可能会因为原模型对技术性内容的理解不足,无法提供准确的解答。而经过微调后的模型,就像一个精通硬件的专家,可以为用户提供精准的产品建议和技术支持。
- 解决一类问题的微调数据要准备多少,才能让大模型具备比较好的泛化能力呢?我的经验是,要让智能客服处理复杂会话且准确提取参数,每一个细化的场景需要 1000-10000 条训练数据,大量的数据才能把大模型微调到我们想要的方向上。好,现在我们解决另一个问题,如果一个问题的良好问答数据例子极少,提示词无法很好回答,怎么办呢?答案是,数字孪生。数字孪生数字孪生的根本目的,就是根据一个数据自动造出一类数据。让大模型具备泛化能力,实际上就是让大模型还可以识别这条数据的另外两个变种。
- 要实现数字孪生,可以用 Jinja2 和 Faker 这两个 Python 库分别实现句式变种和数据变种。Facker 用于制作数据变种(表 1),Jinja2 模版表示句式变种(表 2),而且都可以全自动完成。首先来看如何利用 Faker 生成模拟数据。在下面的实现代码中,fake.phone_number() 这个系列的函数是最关键的,它们用于生成逼真的数据。在 Faker 模拟数据脚本基础上,结合特定的 Jinja2 模版批量制造训练数据。这个数据里的 human 表示用户的输入,bot 表示机器人回复,现在整个数字孪生工程就完成了。客服场景下泛化不足的细分问题,都可以通过数字孪生生成数据。
- 大部分人可能觉得大模型微调是写代码,做训练,实际上大模型微调中 80% 的工作是准备数据。而当你真的做一个项目的时候,你会发现,最大的问题是这个场景下根本没有数据,怎么办呢?这时候就要用数字孪生技术了,数字孪生可以在只有一条数据的情况下,孪生出上万条数据。技术上 Jinja2 库做不同表述方式的数据模版,再结合 Faker 库制作虚拟数据,针对地址信息的问题就可以制作上万条真实的假数据。注意,大模型微调中,往往要用 json 格式表达这些数据,所以最终的数据集都是 json 格式的。你可能也注意到了,在数字孪生里,最核心的工作是设计数据模版。当我们在一个客服场景中遇到需要设计数据模版的对话,可以把所有可变量设计为模版的变量,再结合 Jinja2 和 Facker,就能完成这个微调的数据准备。
- 普通的 RAG 查询只能根据商品种类查询库存,没办法结合时间信息进一步筛选,何况这个场景下 今晚 这个时间信息通过标准的 ReAct 推理很可能是获取不到的。换一个说法,可以把这个细分的场景需求总结为:根据特定送达时间获取产品需求。在这个需求下,用户的会话场景可以有更多的形式,但是要提取的参数是相同的。所以,我们可以针对这个特定场景微调训练这个信息的提取能力。用 Faker 库制作模拟数据,用 GPT 扩写模版,最后用数字孪生技术生产训练数据。当大模型意图识别到 根据特定送达时间获取产品需求 这个细分场景需求的时候,我们的工作流实际上是根据精确的输入参数在做匹配和计算。
- 选择ChatGLM作为基座大模型
- 微调方法,一共有 3 个常见的微调技术,分别是全参微调,LoRA 微调和 P-tuning v2 微调。全参微调和 LoRA 微调更倾向于给大模型微调让其具备某些能力,而 P-tuning v2 微调更倾向于微调大模型让其扮演某个角色,所以,针对电商客服场景,我们选择 P-tuning v2 微调。
- 选用更适合大模型场景的训练器 Seq2SeqTrainer,它擅长大模型场景下的序列化数据训练。trainer.train() 开启训练后,并不是一次性地训练整个数据集,而是分批分步训练。所以,搞清楚一个批次的训练流程,也就理解了整个模型训练过程,而最重要的几个 training_args 训练参数也都体现在批次流程里。
- 批次大小:表示每一批训练的数据大小。学习率:表示每一次训练对数据的学习强度。损失率:表示调整完参数后模型对数据预测失败的程度,分为训练损失率和验证损失率。如果把整个模型训练看做学习一本书,那一个批次训练就像学习一页书,学习率就是这个人的学习程度,训练损失率就是学完这一页书之后马上测验的错题数,验证损失率就是单元测验的错题数。
- 数据集需要拆分成训练集、验证集、测试集。如果我们把大模型训练比作读一本书的话,那么训练集就是用来学习的整本书,而验证集就是单元测试题,测试集就是期末考试题。大模型训练器内部的数据格式是一个序列化的张量,所以首先要做 token 化,把 JSON 格式的数据转为 token 列表。如果想要方便做损失率计算,就要标明输入的 token 列表哪些 token 是输入,哪些是输出。你可以用一个 loss_mask 列表来标明。
- 如何高效处理大规模训练数据?具体场景:在模型训练过程中,如果训练语料特别大(如 1TB 的数据),一次性加载到内存会导致内存不足。解决方案:使用 JSONL 格式(每行一个 JSON),采用流式加载。这样可以根据需要加载数据,避免内存占用过大。
- 如何保证数据加载的随机性?具体场景:JSONL 格式默认按行加载数据,每次加载顺序几乎是固定的,需要在每轮训练中,确保加载的数据样本具有随机性和多样性,避免模型过拟合。解决方案:设置数据加载的随机种子,并一次性加载足够大的数据集(例如 10 万条),然后打乱数据顺序。
- JSONL 格式常见陷阱有哪些?陷阱问题:如果 JSONL 文件的最后一行为空,或者数据量少于 32 条,可能导致 Hugging Face 的 PyTorch 版本报错或解析失败。
- 怎么灵活使用 Py 文件加载数据?具体场景:当模型语料不便于转换为 JSONL 格式(如原始数据是 Excel、CSV 或来自数据库),或者语录里有 JSON 数据,不方便转义。解决方案:编写 Py 脚本将数据转换为适合模型训练的格式,利用 PyTorch 的 DataLoader 加载 Py 文件中的数据集。Py 文件中的类应继承自 Dataset 类。陷阱问题:PyTorch 会将 Py 数据文件写入 /tmp/cache/,如果 import 的包不规范或路径不正确,可能导致加载失败。所以要避免使用相对路径的跨目录引入,一定要用绝对路径确保数据文件正确加载。
- 批次大小结合当前资源情况设定。学习率结合批次大小设定。在多次训练过程中按不同情况调参。就拿学习率而言,如果把学习率调得很高,大模型会出现类似我们学习书本过程中死记硬背的问题。
- 怎么在模型训练过程中方便地观察训练情况?**我们选用 wandb 这个工具,wandb 可以方便地上传和监控模型训练的基础日志,也可以自定义相应的参数,并且提供网页实时查看数据,使用非常方便。
- 过拟合,就是模型在训练数据上表现很好,但在验证数据上表现不好。类似学习一本书出现死记硬背的情况。每一页书可以完全背出来,甚至得满分(损失率为 0),但是单元测试时出现新的题型就不会做了。所以,我们可以调整数据和设定正则化参数,继续实验。这里的正则化是指,通过在损失函数中添加权重参数的平方和乘以一个系数来惩罚大权重值。类似学习一本书过程中让学生多做举一反三,不要把平时学习内容的权重设为 100%。若训练损失小于验证损失,也就是训练集效果很好,验证集效果不好,则是过拟合。若训练损失大于验证损失,有可能是验证集太多了,神经网络还没收敛。一个解决方案是需要调整训练过程,去找中间的点,比如在 Step 的 5 或 6 停下来,才能取得相对较好效果。
- 一般实验多少次,经验参数是多少?一般要做至少 10 次的实验,通过这个过程工程师可以了解业务数据的特性,找到合适的参数范围,再进行更大数据量的模型训练。一般经验的学习率参数为 2×10{−5}~5×10{−5}。
- 测评和训练的关系不是先训练再测评,而是在模型训练过程中分别评分,随时根据结果调整模型训练过程。
- 测评工具可以用 LM-Evaluation-Harness 框架,LM-Evaluation-Harness 是一个用于评估和比较语言模型(Language Models, LMs)性能的工具包。lm-evaluation-harness 框架可以直接测评各种开源的大模型,也可以使用各种公开数据集来测评。在我们客服项目中,被测评的模型选择私有的大模型,而数据集选择 mmlu。LM-Evaluation-Harness 实际上提供一套测评框架,可以自定义测评任务,在电商客服中,可以理解为:将一些我们要通过微调完成的任务,写出测评任务,这些任务就是私有 task,用框架重点测评这些 task,评估我们模型的微调的效果。
- 评分算法:最简单的算法就是完全匹配算法,如果回答和 answer 完全一样记 100 分,否则 0 分。当然针对大模型这个场景,使用更灵活的评估方法,而不是简单的字符串匹配。常见的方法包括使用 相似度评估(例如 BLEU、ROUGE、BERTScore 等)或 模糊匹配。我们选用的是 BLEU 算法,下面的 bleu 和 agg_bleu 分别是单个数据的 bleu 计算和整体的 bleu 评分。
- 只有当大模型遇到问题的时候才需要微调,因此评估的重点是看微调后的大模型是否具备新的特性,其方法是人工测评 + 私有 task 测评,其中核心参考人工测评的结果,私有 task 可以完全实现自动化,其评分作为辅助参考。另外一方面,大模型微调之后,原有基础能力不能下降,如何评估呢?可以通过通用测评数据集的测评来完成,这一步和私有 task 一样,都可以通过 LM-Evaluation-Harness 测评框架自动化完成。
- 之所以要训练 JSON 原子能力,是因为大模型需要和智能体配合才能达到较高的可靠性,而 JSON 是最适合跟现有编程体系对接的数据格式。数据格式设计本质,就是准确利用好大模型的基础 NLP 能力,提升复杂对话场景下的数据提取能力。而在智能客服复杂对话场景下设计数据格式,就需要训练大模型的原子能力。数据孪生最本质的地方就在于数据格式设计,也就是引导大模型去思考,处理数据。
- 在真实的智能电商客服项目中,还需要加入基础的电商领域数据微调,让大模型具备电商领域基础能力。举个例子,一个电商客服如果连基础的电商平台和规则都不清楚,实际上是没有资格做专有场景客服的。所以,我们需要收集大量电商平台过程数据。这些数据包括电商平台的商品信息、用户评论、订单详情、客户地址等。将这些格式化数据按照字段等组合成完整语义的上下文,再组合成 JSON 和 Markdown 格式的数据。通过电商客服模型专用语料的训练,就可以强化模型对电商数据的理解力。
- 数据工程在这一过程中至关重要,大模型通过少量的例子就能泛化出新的 NLP 能力。同时,具体数据格式的设计要参考大模型现有的 NLP 能力,这样才能在现有能力上进一步加强。最后我想说,模型的微调并非单纯的代码开发,而是基于数据工程的持续优化。
- 要自己实现一个评估专家,可以分为两个层次思考,首先在战略设计上可以直接参考 ChatGPT 的论文。实际上就是 RLHF 强化学习的三个阶段。在第一阶段,预训练的大语言模型首先使用人类标注的高质量数据集进行监督微调,第二阶段就是之前分析的基于人类专家数据标注的奖励模型训练,第三阶段是采用 PPO 算法让大模型在奖励模型评分的基础上保持稳定的数据质量。
- 然而,ChatGPT 的技术报告不包含技术细节,大方向有参考性,但是可操作性低。其中模型结构、反馈结构、使用方法有很高的参考价值。总之,ChatGPT 的论文只能作为一种指导思想,我这里的目的也不是分析论文,我们要从一个更落地的评估专家模型入手,也就是我们思考的第二个层次,从战术上找一个可对标的开源评估专家。
- Llama Guard 3模型:典型的评估专家模型,他的作用是给输出内容做安全评估和分类。在具体实现上,Llama Guard 3 其实就是一个经过微调的大模型,它是基于 Llama 3.1 8B 小模型微调训练的。
- 3 类数据集的区别,KTO 的输出是绝对的 0-1 这样二值判断,RLHF 则是类似 ChatGPT 输出的排序评估,DPO 也很简单,是对 A-B 两种结果的倾向性评估。RLHF 输出是多个输出对比排序,也就是 ChatGPT 奖励模型的数据格式,自不必多说。那 KTO 训练和 DPO 训练有什么不同?训练过程中,KTO 方法需要对输出结果进行二元判断,也就是输出:符合预期 / 不符合预期,所以其收集的数据为 Prompt+Chosen 或 Rejected;DPO 训练依赖人类反馈,需要对输出结果进行偏好性判断,输出是两个结果的倾向程度,所以其收集的数据为 Prompt+Chosen 和 Rejected。
- KTO(Knowledge Trained Optimization)更适合客观问题,因为它依赖于对输出结果进行明确的二元判断(符合预期或不符合预期)。这意味着它的应用场景主要是有明确标准答案的领域,例如数学问题、技术操作或事实陈述等。在这些场景中,输出结果要么正确,要么错误,没有太多的主观性。
- DPO(Direct Preference Optimization) 则更加适合主观问题,因为它依赖人类的偏好进行相对判断(两个输出的倾向性)。在这类问题中,可能不存在绝对的“正确答案”,而是更多取决于人类的判断和偏好,例如内容创作、文学作品、设计审美等。在这些场景中,输出的好坏并不取决于客观标准,而是根据用户的偏好来选择更优的输出。
- 工程经验:在数据孪生的数据质量判断中,需要评估专家按照业务使用场景对数据进行快速精选(也就是二义分类 KTO),只需要用小数据集就可以做得很好。KTO 作为优化方法,会根据这些预定义的质量标准(如数据的准确性、完整性、合理范围等)来判断数据是否为“坏数据”。而且判断的结果不是基于偏好,而是明确的“对”或“错”,“适合”或“不适合”的判断。最后还要说一个工程经验,做评估专家模型的时候,无论如何,不要重头开始训练一个新模型,而是基于预训练模型如 Llama 3 ,ChatGLM 3 来训练,这样才能更好利用这些模型已经内化的能力。
- 奖励模型的难点主要集中在如何提升偏好数据的质量以及奖励函数的泛化能力。
- 为了解决数据噪声问题,可以采取标签翻转、损失函数平滑处理以及数据区分度调整等方法来提高数据质量。
- 数据质量是第一个难点,另外一个难点则是奖励函数的泛化程度。泛化能力是指奖励模型能否有效地处理训练数据之外的情况。然而,很多奖励函数在不同任务之间的泛化能力较差。为了提升泛化能力,可以通过对比学习的方式来优化奖励函数。通过对比学习,奖励函数能够更好地区分不同回复之间的细微差别,从而提升其泛化能力。此外,还可以通过改进训练过程中的梯度更新策略,确保奖励函数在给定的分布下能够有效区分不同的回复。这不仅能够提升奖励模型在当前任务中的表现,也能提升其在新任务中的适应性。
- 奖励模型负责对大语言模型的输出打分,提供反馈信号。PPO 通过这些反馈信号稳定地优化模型策略,确保训练过程平稳有效。
- 训练奖励模型:TRL 支持用户在其数据集和模型上进行定制的奖励建模,允许对奖励模型进行个性化训练和优化。其底层核心是两个类,RewardTrainer 和 RewardConfig。在准备好数据集后,你可以像使用 Transformers 的 Trainer 类一样使用 RewardTrainer。你需要将一个 AutoModelForSequenceClassification 模型传递给 RewardTrainer,同时还需要提供 RewardConfig,用于配置训练的超参数。训练一个奖励模型也需要做 loss 评估,你可以通过向数据集添加一个“margin”列来将边际添加到损失函数中。奖励处理器将会自动传递该列,并根据传递的列计算损失。模型会对两个候选的输出进行打分,一个对应“优选”(score_chosen),另一个对应“拒绝”(score_rejected)。通过比较这两个分值的差异,模型能够模拟人类的偏好,从而进行训练和优化。模型的目标是通过这种分值差异,使自己的预测与标注数据中的偏好更为一致,从而降低损失函数。
- 数据集完全可以做到排序。注意看上述的数据中,chosen 和 rejected 只表示某个问题的回答倾向性。当 role 为 user 时,chosen 和 rejected 数据的 content 字段内容是完全一样的,当 role 为 assistant 时 content 才不一样。这其实代表了同一个问题不同输出的倾向性选择。具体的倾向性大小通过 score_chosen 和 score_rejected 的大小来表示,通过多条数据的倾向性数据,就能做到排序。要实现数字孪生的数据质量对错判断,还是要借鉴上述数据集格式,那具体怎么做呢?我们的任务是做二值分类,因此 score_chosen 一定是 1,而 score_rejected 一定是 0,不做倾向性也不做排序。在这种情况下,输入的是地址信息,奖励模型将对多个候选地址进行评分和选择,帮助判断哪个地址更符合标准。构建完数据集就可以直接训练评估模型了。注意要在实战中采用TRL已经封装好的训练脚本,而不是自己编写程序。
- 模型训练:要自己训练一个评估专家模型可以用开源的 TRL 库,在 TRL 库基础上开发一个用于训练模型的程序即可,但在实战中,则可以直接利用 TRL 提供的训练程序 reward_modeling.py 。reward_modeling.py 是一个完整的训练脚本,下面分别是利用这个脚本做全量训练和 Lora 训练的两个例子。这个脚本最主要的参数有两个,一个是 Qwen/Qwen2-0.5B-Instruct 表示用于训练评估专家模型的基模型,trl-lib/ultrafeedback_binarized 表示用于训练评估专家的数据集。TRL 提供一个完整的训练脚本 reward_modeling.py。提供全量训练和Lora训练两种模式。
- 总结:一个完整的 RLHF 训练包含 3 个步骤,分别是数据预训练,奖励模型训练,大模型的 PPO 稳定性调整,这是大模型领域难度最高的一个过程了。如果要自己训练一个评估专家模型,我们可以基于 TRL 项目的开源训练脚本就可以完成,其核心还是整理数据集,这里需要注意 TRL 标准的数据集分为 chosen 和 rejected 两个部分,但是每条数据还可以打分,这样就能实现一个完整的具有排序能力的评估专家模型了。在数字孪生的数据质量检测中,我们也要将数据集整理成标准的格式,需要注意两点,第一点是 assistant 的回复用正确,错误字样来表示二值分类,第二点,具体的 score 分数填为 1 和 0。训练数据集要保持一定的多样性,否则容易导致准确率不足。
- 评估长文本客观题的场景下,要用到文本距离算法。用大模型来评估,其实就是将问题,答案和 LLM 输出一起提交给大模型做出评价。
- 对于客观问题短文本,评估可以基于答案的明确性,例如通过语义距离算法或 KTO 类方法进行二值评分(1 或 0)。这类问题通常有标准答案,评估的自动化相对容易。在工程实战中,则可以采用 xFinder 模型来对答案做关键信息抽取,然后再进行评分,这样准确率能提高到 90% 以上。
- 对于客观问题长文本,由于答案复杂度的增加,传统的二值评分方法不适用。这里通常采用余弦相似度或者 BERT 之类的语义匹配算法,通过计算模型输出和标准答案之间的文本距离,来评估输出的准确性。
- 对于主观问题,例如写作类任务,倾向性评估更为适合。可以使用 DPO 或 RLHF 等方法,通过比较多个模型输出,选择最符合预期的答案。这类问题的评估主观性较强,因而需要更复杂的评估算法来模拟专家评估的过程。在工程实战中,往往采用比训练的大模型更强的大模型来做这类问题的评估专家,效果也足够好。
- 开发一个行业专用的大模型,需要在数据和评估上投入更多资源,尤其是数据的标注与获取。在开发路线方面,从小数据量和小模型开始训练,逐步评估大模型的能力,以此扩展到更复杂的应用场景。并不是一开始就用大参数的基模型进行训练,而是先从小参数,小量数据开始训练,在评估完之后,确定了基模型可用性和大模型能力提升之后,再用大参数和大数据放大这个能力。
- BibiGPT 是怎么减少 Token 费用的。BibiGPT 处理 B 站视频内容时,内容量是固定的,比如每天免费用户产生 10 万条内容,实际上大部分内容只要处理过一次,后续使用者就能直接拿结果而不需要再走一次大模型付出 token 费用。
- 再看 BibiGPT 另外一个核心逻辑。在用户使用 BibiGPT 做总结的过程中,BibiGPT 可以把视频内容转为文本内容,这相当于大模型生成的第一次内容。这个内容除了给用户使用,BibiGPT 还会把内容做成一个关键词的网页,这个内容是可以被搜素引擎 SEO 收录的。这个逻辑下,免费用户每使用一次 BibiGPT 实际上都在帮 BibiGPT 生产营销内容,完美地利用了大模型生成式。
- 在这个运营模式之下,因为免费用户基数可以足够大,即使收费率再低,也可以把项目持续运营下去。总结一下,如果可以做到大模型生产的内容可以复用,甚至可以带来新的用户,这部分成本自然被抵消,也就做到了和传统互联网项目一样,边际成本为零,这才是 AI 创业项目真正的完美模式。
