7、模型微调入门&&如何面对不断更新的大模型

本节介绍

前面讲 RAG 时,我们说过,它本质上是让大模型知道更多的东西,尤其是属于你自己业务领域的东西。当时我们就说,RAG 只是其中的一种选择,还有另外一种选择,就是模型微调。这一讲,我们就来使用llamafactory对qwen2.5:3b进行模型微调,然后ollama调用微调后的模型。再未来,我们会更深入的对微调进行研究。

什么是模型微调

模型微调,顾名思义,就是对模型微微做一些调整。为什么要做微调呢?如果可以的话,每个公司都想拥有一个属于自己的大模型。但是,现实比较残酷,训练一个大模型需要花太多的钱。按照一些大公司的说法,一个千亿参数的大模型,训练一次的成本大约需要几百万美元。这显然就超过一个普通公司承受的范围。虽然我们无法训练一个属于自己的大模型,但一个好消息是,我们可以做模型微调。

大模型是构建于神经网络基础之上的,神经网络可以理解成一个一个的神经元构建的网络。训练模型,就是在调整神经元之间的连接方式。一次完整的训练就相当于把所有的神经元连接都调整一遍,这个计算规模相当之大,是我们无法承受的。

所谓微调,就是把一个训练好的模型中的一部分连接重新调整。因为只做了一部分的调整,所以,规模就要小得多,训练成本也就要小得多。

在这里插入图片描述

微调和rag的对比

前面说过,RAG 和模型微调可以解决同样的问题,从本质上说,就是把核心业务数据放在提示词里,还是放在模型里。

两种做法各有优劣。放到提示词里,优势就是做法比较简单,但其问题的关键在于能否取到恰当的数据。如果不能取到恰当的数据,就可能会出现“幻觉”问题,也就是大模型会一本正经地胡说八道。放到模型里,优势是数据准确性会提高,但前提条件是在训练的时候,要准备高质量的数据,否则就是“垃圾进,垃圾出”了,而微调好一个模型,并不是一件很容易的事情。

在工程实践中,二者往往是结合使用的。模型微调不是时刻在进行,所以,一些团队的做法是,用 RAG 的方式提取新的业务数据,积累到一定阶段,用这些数据进行模型微调,把这些数据内置到模型中,再把新模型替换到业务系统中。这样一来,既保证数据的新鲜,又保证了基本的服务质量。

怎样微调模型

模型微调,需要我们先选定一个模型。如果选定的模型是像 OpenAI 这样的闭源模型,就需要使用其对应的模型微调服务。不过,使用这种服务也就意味着要把数据提供出去。所以,很多团队的实际做法是,基于开源模型的微调。

具体的模型微调通常分成几个步骤:

  • 准备训练数据
  • 训练模型
  • 评估结果
  • 使用模型

这其中最耗时耗力的,应该是准备数据了。首先,微调大模型需要多准备一些数据,数据量过少,可能看上去完全不起作用。毕竟大模型那么多参数,如果只改动几个,占比太低,就和没改动效果差不多。其次,数据也要精心挑选一下,还是那句话:垃圾进,垃圾出。所以,这个过程不是技术活,而是一个体力活。什么数据是好数据?最好由一个懂业务的人说了算。

顺便说一下,因为准备数据耗时耗力,有人把一些整理好的数据开源了出来,Hugging Face 上有大量这种数据集。如果站在学习的角度,我们可以把这些数据集拿过来直接用。

我们知道了要准备数据,但数据格式是什么样的呢?这取决于模型微调的具体方式,比如,qwen2.5 的模型微调需要把数据准备成下面这样(仅限于单轮对话):

[
  {
    "instruction": "识别并解释给定列表中的两个科学理论:细胞理论和日心说。",
    "input": "",
    "output": "细胞理论是生物科学的一个理论,它认为所有生命体都是由微小的基本单元——细胞所构成。这是生物学的一个基础理论,认为细胞是所有生物的基本结构和功能单位,所有的生物都是由一个或多个细胞组成,细胞只能通过细胞分裂产生新的细胞。这一理论由薛定谔、施瓦内和雪莱于1839年首次提出。\n\n日心说是指太阳是太阳系的中心,也就是说,行星围绕太阳旋转的理论。这个理论打破了传统的地心说观点,认为地球并不是宇宙的中心。日心说的提出者是尼古拉·哥白尼,他在16世纪初发表了他的著作《天体运行论》,阐述了太阳系行星围绕太阳运行的模型,为天文学的发展做出了巨大贡献。"
  },
  {
    "instruction": "输入三支篮球队的名称并生成一个适当的口号。",
    "input": "输入:俄克拉荷马城雷霆队,芝加哥公牛队,布鲁克林网队。",
    "output": "输出: \"雷霆,公牛和网队:各显神通,角逐群雄!\""
  },
  {
    "instruction": "比较以下两位著名人物,使用特征列表并以句子形式提供输出。",
    "input": "人物1:阿尔伯特·爱因斯坦\n人物2:斯蒂芬·霍金\n特征:智力、教育、对世界的影响",
    "output": "阿尔伯特·爱因斯坦和斯蒂芬·霍金都是拥有极其出色智力的人物。两人都取得过非常高的教育成就,他们推进了科学发展并在世界范围内产生了深远的影响。爱因斯坦以其相对论和质能关系公式而闻名,而霍金以其关于黑洞和宇宙的发现而著称。两位科学家都以其深厚的学识和非凡的贡献影响了世界。"
  }
]

接下来,我们就以 LLaMA Factory 为例具体看一下模型微调的过程。

使用 LLaMa Factory 微调模型

LLaMA Factory 是这样自我介绍的:它是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。它当然有很多优点,比如,支持很多不同的模型、支持很多训练方法,而且效率很高。如果你初次接触模型微调,它更真实的优点是,能够在本地运行,还有一个图形化的界面。

使用 LLaMa Factory 需要先把它的代码 clone 下来。

git clone https://github.com/hiyouga/LLaMA-Factory.git

然后就是安装相应的依赖。为了不把现有的 Python 环境搞乱,我们还可以创建一个虚拟环境。

cd LLaMA-Factory
conda create -n llama-factory python=3.12
conda activate llama-factory
pip install -e ".[torch,metrics]"

安装依赖之后,我们就可以运行下面的命令判断安装是否成功。

(llama-factory) zeng@zeng:~/llm/LLaMA-Factory$ llamafactory-cli version
----------------------------------------------------------
| Welcome to LLaMA Factory, version 0.9.2.dev0           |
|                                                        |
| Project page: https://github.com/hiyouga/LLaMA-Factory |
----------------------------------------------------------

接着,我们需要先准备一下训练数据。LlaMA Factory 支持 alpaca 格式和 sharegpt 格式,它们实际上都是 JSON 的格式。下面是一个 alpaca 格式的示例:

[
  {
    "instruction": "输入三支篮球队的名称并生成一个适当的口号。",
    "input": "输入:俄克拉荷马城雷霆队,芝加哥公牛队,布鲁克林网队。",
    "output": "输出: \"雷霆,公牛和网队:各显神通,角逐群雄!\""
  }
]

接下来是 sharegpt 格式的示例:

[
  {
    "conversations": [
      {
        "from": "human",
        "value": "你能解释一下算法分析中的大O记法概念吗?"
      },
      {
        "from": "gpt",
        "value": "是的,大O表示法是一种描述算法性能随输入数据大小增加的方法。它用于估计算法在最坏情况下的时间复杂度或空间复杂度。大O中的“O”代表“数量级”,这意味着随着输入大小的增加,算法所需的操作数以一定的速率增长。\n\n例如,如果我们有一个需要n步才能解决大小为n的问题的算法,那么时间复杂度可以表示为O(n),这意味着操作数随着输入大小线性增长。如果我们有一个需要n^2步才能解决大小为n的问题的算法,那么时间复杂度可以表示为O(n^2),这意味着操作数随着输入大小二次方增长。\n\n还有其他几种大O表示法,如O(log n)、O(n log n)、O(2^n)等,它们代表了随着输入大小增加的不同操作数增长速率。通过分析算法的大O表示法,我们可以比较不同算法的性能,并选择给定问题最有效的算法。\n\n您是否有关于大O表示法或算法分析的特定问题?"
      }
    ],
    "tools": "[]"
  }
]

从这两个示例我们不难看出,它们就是一个对话的结构,相当于一个人问了一个问题,大模型给出了一个回答。从简单直接的角度,sharegpt 格式更符合我们常规理解的会话形式。但是alpaca格式是开源数据中常见的数据。

确定了数据格式,我们需要把数据改写成这个格式,把它们存到一个文件里。然后,把这个文件放到 LLaMa Factory 的 data 目录里。接着,在 dataset_info.json 里加上这个文件的描述。dataset_info.json 相当于是一个元文件,用来描述在 LLaMa Factory 界面上可以看到的数据是什么样的。

下面是一个例子,我们指定了文件的名称和相应的文件格式,formatting如果是alpaca可以省略 (dreamhead.json里面的内容是我从identity.json复制出来,替换里面的{{name}}和{{author}}得到的,当然十分建议你尝试使用自己准备的数据,以便熟悉数据的准备过程)

"dreamhead": {
  "file_name": "dreamhead.json",
  "formatting": "alpaca"
}

准备好数据,我们就可以开始训练了。前面说了,LLaMa Factory 对新手最友好的地方是它提供了图形化界面,我们可以通过下面的命令,启动图形化界面。

llamafactory-cli webui

LLaMa Factory 的图形界面如下所示。

在这里插入图片描述

最终配置的参数如下,这里注意轮数因为我们数据量少,所以训练为10轮,同时将学利率调大一些,让其更快收敛

在这里插入图片描述

虽然说是一个图形界面,但这个界面上的参数之多,已经达到了眼花缭乱的地步。不过,我们并不需要一上来就把所有的参数搞清楚,要微调自己的模型,最核心的几个参数是:

  • 模型名称,也就是我们用于微调的基础模型。
  • 数据集,在这里可以找到前面准备好的数据。
  • 除此之外,我们还需要知道输出目录,毕竟,我们还是要结果的。

做好了最基础的配置,我们只要点击“开始”,就可以开始执行训练了,训练的时间会因为配置的差异有所不同。如果一切顺利,我们只需要等待这个过程结束。

这些界面上的各种配置,最终都会以命令行的方式在后台运行。点击“预览命令”,我们就可以看到在后台要执行的命令。我们完全可以自己在命令行里执行这个命令进行训练。

当训练结束,我们可以在输出目录中找到微调之后的模型。有了模型,我们怎么把它用起来呢?根据我们上一讲讲到的做法,最简单的方案就是把它接入到 Ollama 中。

测试模型

在页面中可以选chat验证模型,首先我们不加载检查点,看看当前的回答:

在这里插入图片描述

加载检查点后,再来看看当前的回答:

在这里插入图片描述

在 Ollama 中运行微调模型

怎样在 Ollama 中接入一个新模型呢?我们需要给 Ollama 提供一份模型描述文件,也就是它的 Modelfile。在 Ollama 上运行的每个模型都有自己的 Modelfile,我们可以通过命令查看它们的 Modelfile,比如:

ollama show qwen2.5:3b --modelfile

下面是我创建的 Modelfile:

FROM qwen2.5:3b
ADAPTER ./my_model.gguf

在这个 Modelfile 里,FROM 指向基础模型,而 ADAPTER 指向了微调过的文件。不过,你可能会发现在生成模型的目录下并没有一个 gguf 文件。GGUF 是一种模型文件的存储格式,它也是 Ollama 支持的文件格式。坏消息是,我们微调的模型并不是以这种格式存储的,好消息是,我们可以把自己的模型转换成 GGUF。

在微调过程中,我们采用了缺省的 LoRA(Low-Rank Adaptation),这是一种微调模型的技术。llama.cpp 这个开源项目就提供了一个转换程序,可以将 LoRA 的结果转换成 GGUF 格式。这个转换程序是一个单独的 Python 程序,我们可以在本地执行它,其主要的依赖就是 transformers这个库。安装好依赖之后,我们就执行这个转换程序,得到相应的 GGUF 文件。

# 拉取llama.cpp工程
git clone https://github.com/ggerganov/llama.cpp.git
cd llama
python ./convert_lora_to_gguf.py --base "/home/zeng/llm/model/Qwen2.5-3B-Instruct" "/home/zeng/llm/LLaMA-Factory/saves/Qwen2.5-3B-Instruct/lora/train_2025-02-11-15-56-41"

# 输出如下内容,表示成功
INFO:hf-to-gguf:blk.9.attn_v.weight.lora_b, torch.float32 --> F32, shape = {8, 256}
INFO:hf-to-gguf:Set meta model
INFO:hf-to-gguf:Set model parameters
INFO:hf-to-gguf:Set model tokenizer
INFO:hf-to-gguf:Set model quantization version
INFO:gguf.gguf_writer:Writing the following files:
INFO:gguf.gguf_writer:/home/zeng/llm/LLaMA-Factory/saves/Qwen2.5-3B-Instruct/lora/train_2025-02-11-15-56-41/train_2025-02-11-15-56-41-F16-LoRA.gguf: n_tensors = 504, total_size = 59.9M
Writing: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 59.9M/59.9M [00:00<00:00, 3.15Gbyte/s]
INFO:lora-to-gguf:Model successfully exported to /home/zeng/llm/LLaMA-Factory/saves/Qwen2.5-3B-Instruct/lora/train_2025-02-11-15-56-41/train_2025-02-11-15-56-41-F16-LoRA.gguf

在这个位置下,就能看到我们生成出来的.gguf,修改成my_model.gguf:

cd /home/zeng/llm/LLaMA-Factory/saves/Qwen2.5-3B-Instruct/lora/train_2025-02-11-15-56-41
mv train_2025-02-11-15-56-41-F16-LoRA.gguf my_model.gguf

在当前目录新建Modelfile:

FROM qwen2.5:3b
ADAPTER ./my_model.gguf

现在就就可以在 Ollama 中创建一个自己的模型了。

ollama create my_model -f ./my_model.modelfile

如果一切顺利,你就得到了一个可以在 Ollama 中运行的模型,然后,我们就像运行一个普通的模型一样运行它。

ollama run my_model

也可以先用ollama list,查看新建的模型

在这里插入图片描述

好,你现在可以测试你的模型了,评估微调之后大模型的效果,如果觉得不理想,就需要回到前面重新来过。正如上一讲所说,一个模型接入了 Ollama,它就能接入到 One-API 上,而接入了大模型代理,我们就可以在项目中访问它了。这样,我们就完成了一个完整的微调流程。

这是我的调用测试:

在这里插入图片描述

跟之前我们用来微调的数据是一致的:

在这里插入图片描述

如何面对不断更新的大模型

大模型领域本身就是一个发展迅速的领域:才说 GPT 3.5 好,又来了个更强大的 GPT 4,没过多久,GPT-o1 又展现出强大的推理能力;今天有个 Llama,明天出个 QWen,后天 Mistral、deepseek 也很强大;总而言之,只要你关注,总会有新的大模型以各种能力展现在你面前。这确实是一件令人焦虑的事情。

这一讲,我们就来谈谈,如何面对这些不断更新的大模型。

大模型的不变

虽说大模型领域是以“变”为主,不断推陈出新,但从做一个软件系统的角度,我想先谈谈大模型的不变。因为在软件开发中,我们一直在追求的就是将变与不变隔离开,让不变的东西尽可能稳定下来。

大模型的不变首先体现在 API 上。我在前面的内容说过,OpenAI API 在某种意义上已经成为了行业的事实标准,加之集中接入的引入,我们只要使用统一的 OpenAI 接口,几乎可以访问所有的模型。从这个角度上说,访问大模型的 API 就是统一的。

有了统一的大模型 API,也就意味着我们的代码可以通过相同的方式进行处理,无需考虑不同 API 之间的差异性。站在开发的角度看,这是代码稳定的前提条件。不同的模型之间的差异,主要体现在给 API 传入的模型参数的差异。

不过,前面我们也说过,大模型应用开发中,API 的影响是很低的,真正的核心内容都在于大模型的交互上。从我们的观感上看,各种新的大模型层出不穷,大模型有不变的东西吗?答案是有。

我们目前看到的这些进步基本上都是工程层面的进步,也就是说,一个大模型有的东西,另一些大模型很快就能跟上,比如,当 GPT 4 支持了图像识别,Llama 也很快有了一个 Vision 版本;GPT-o1 用提示工程让大模型的推理能力得到提升,不久,Qwen 就推出了 QwQ,它也具备不错的推理能力。

工程上的进步很重要,但这些都是量变,不是质变,所以,虽然想做好有难度,却并非遥不可及。一个新技术出现,追随者很快就能跟上。出现这种情况,也很好理解,毕竟大家处于同一个时代,底层技术都是公开的。

正是因为如此,大模型的能力都会处于一个类似的水平线上,而且,底线会越来越高。这些大模型的底线能力就是大模型里不变的东西。

我们在应用开发部分讲了很多内容,虽然我们使用的是 OpenAI 的 GPT 模型,但你也可以尝试着把这些应用运行在其它的模型上,最简单的方案就是使用我在前面几讲介绍过的 Ollama。你会发现,很多大模型的表现得都还不错。我们在这些应用里用到的能力几乎就是大模型中最基础的能力,比如,聊天、按格式输出内容、基本的推理能力等等。

讨论大模型中这些不变的东西,我想说的是,开发一个 AI 应用,核心点并不是选择一个更好的大模型,而是自己的业务。在现阶段,再好的大模型也不能帮我们把业务理顺,再好的大模型也只能起到一个辅助的作用,不要过分高估大模型所能起到的作用。我们要做的是,用大模型改造我们的业务流程,让 AI 嵌入到我们的业务流程中去。

由“不变”带来的设计

仔细分析一下,我们便不难发现,我们应用中,有一部分的工作属于简单的推理工作,比如我们介绍过的 ReAct,其中一个核心步骤就是确定调用哪个工具。如果我们搭建了一个本地的开源模型,这些简单的推理工作完全可以在本地完成,无需为此支付模型调用的费用,以此节省调用模型的成本。

一旦我们想清楚,不同的请求可以发给不同的模型,我们就可以在架构上做一个区分,一部分请求需要发给大模型服务获取更好的表现,一部分请求(比如简单推理)交给本地的模型,完成基本的操作。在这种思路下,整个的服务处理过程就会有多个不同的模型参与。既然可以引入一个额外的模型,我们完全可以再进一步,在处理过程中,引入多个模型,让不同的模型完成不同的工作。

在这里插入图片描述

在这个架构中,我们引入了一个 LLM Router,也就是大模型路由,它负责根据用途的差异,采用不同的模型。比如,聊天就用 Open AI 的服务,推理使用本地部署的开源模型。有了大模型路由,我们就可以把应用和使用不同模型隔离开来,以保证应用代码的稳定。

你可能会觉得这个结构有点类似于前面说过的大模型代理。二者不同的点在于,大模型代理是做一个独立的部分,它只提供标准的 OpenAI API,我们在调用时,需要指定模型,而大模型路由,则是要根据用途确定不同的大模型,请求方甚至都不用指定模型,因为我们到底使用哪个模型已经在路由内部配置好了。

之所以把用途与模型隔离开来,是因为评估什么模型适合用在什么地方,本质上是一个技术选型的工作。这点一旦确定,在一段时间内是可以保持稳定的。

大模型的变

从软件开发的角度来说,一个系统能够正常运作,我们也就没有必要经常动它。我们当然希望我们做好的系统能够长时间保持稳定。但作为一个有经验的程序员,我们知道,一个有生命力的系统不可能长时间稳定,总有一些新的需求会进来。有了大模型,这一点应该也是没有什么变化的——总会发现适合大模型的新场景,总要评估怎样的模型是合适的。

正如我们在各类新闻中看到的,新模型不断出现。作为技术人员,我们需要关注的这些模型的特点是什么,核心在于找到大模型中“变”的地方。这里的“关注”,首先是找出这些“变”的地方可以创造怎样的使用场景。举个例子,GPT-4V 的出现,让大模型具备了图像识别的功能,这就可以创造出很多不同的玩法,比如,根据手绘图生成代码。

其次,我们需要看看是否会有性能的大幅度提升,比如,GPT 3.5 到 4,虽然从功能上改变不大,但性能上有了大幅度提升。在这种情况下,我们就可以评估是否有替换模型的必要。我就曾经在一些项目中,把 GPT 3.5 用在了一些推理的场景上,而聊天的场景切换到 GPT 4 上。

再者,我们也可以关注使用成本。比如,2023 年的时候,GPT 4 在性能上可谓一骑绝尘,很多时候,我们想达到理想的效果,必须忍受其高昂的使用成本,但到了 2024 年,很多开源模型都已经达到了 GPT 4 的水准,我们完全可以用这些模型替代 GPT 4。当然,OpenAI 也是看到了这个趋势,GPT 4 新模型的使用成本也在逐渐降低。

前面说的是一些通用的考量,如果你的应用场景是比较特殊的,往往就需要关注针对这些场景的模型。比如,很多编程工具像 Cursor、Cline 会推荐使用 Claude 3.5 sonnect。这些模型针对特定的场景做了一些优化,在这些场景下,其表现会远远好于通用的模型。这些特定场景的模型还没有像通用大模型一样相对稳定,其变化还是值得开发者们关注。

前面我们说了大模型的“变”和“不变”,你可能会有些困惑,我到底该怎么应对呢?简单说,由于不变,我们可以把系统做稳定;应对变,我们可以抓住核心点,定期评估。大模型领域目前还是处于快速发展之中,我们普通程序员要做的,就是抓住大模型带给我们的机遇,做出更好的应用。

最后说一下

不变的是,大模型的 API 和基础能力。我们要利用这些能力构建自己的应用,而应用的核心是我们的业务,大模型是帮助我们提供更好的服务体验。

由于大模型基础能力的提高,我们可以在一个服务中使用多个不同的大模型,既可以节省成本,又可以找到最佳的表现。通过引入大模型路由,我们可以把其中的复杂度从应用中分离出来。

应对大模型的变,我们需要找到自己的核心关注点,是使用场景在增加,还是模型能力有提升,抑或是成本得到控制。只有找到自己的核心关注点,我们才不会在不断涌现的新模型中迷失。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值