如何使用Hugging Face微调大语言模型(LLMs)

大语言模型(LLMs)在过去一年取得了显著进步。从ChatGPT爆发以来,后来逐步发展了众多开源大模型LLMs,如Meta AI的Llama 2、Mistrals Mistral & Mixtral模型,TII Falcon等。这些LLMs能够胜任多种任务,包括聊天机器人、问答和自动摘要,而且无需进行额外的训练。但是,如果你想为你的应用定制模型,可能需要在你的数据集上对模型进行微调,以获得比直接使用或训练更小型模型更高质量的结果。

本文将介绍如何使用Hugging Face的TRLTransformers框架和数据集来微调开放的大语言模型。我们将按照以下步骤进行:

  1. 明确我们的使用场景

  2. 设置开发环境

  3. 创建和准备数据集

  4. 使用**trlSFTTrainer**微调LLM

  5. 测试和评估LLM

  6. 将LLM部署到生产环境

注意:本文是为了在消费者级别的GPU(24GB)上运行而编写的,例如NVIDIA A10G或RTX 4090/3090,但也可以轻松地适应在更大的GPU上运行。

一、定义我们的使用场景


微调LLM时,了解你的使用场景和要解决的问题至关重要。这将帮助你选择合适的模型,或者帮助你创建一个数据集来微调你的模型。如果你还没有定义你的使用场景,你可能需要重新思考。并非所有的使用场景都需要微调,建议在微调你自己的模型之前,先评估和尝试已经微调过的模型或基于API的模型。

例如,我们将使用以下使用场景:

我们想要微调一个模型,它可以基于自然语言指令生成SQL查询,然后可以集成到我们的BI工具中。目标是减少创建SQL查询所需的时间,并使非技术用户更容易创建SQL查询。

将自然语言转换为SQL查询是一个很好的微调LLM的使用场景,因为它是一个复杂的任务,需要对数据和SQL语言有深入的理解。

二、设置开发环境


首先,我们需要安装Hugging Face的库,包括trl、transformers和datasets,以及Pytorch。trl是一个新库,建立在transformers和datasets之上,它简化了微调大语言模型的过程,包括rlhf(强化学习从人类反馈中学习)和对齐开放LLM。如果你对trl还不太熟悉,不用担心,它是一个新工具,旨在让微调过程更加便捷。

# 安装Pytorch和其他库  
!pip install "torch==2.1.2" tensorboard  
  
# 安装Hugging Face库  
!pip install  --upgrade \  
  "transformers==4.36.2" \  
  "datasets==2.16.1" \  
  "accelerate==0.26.1" \  
  "evaluate==0.4.1" \  
  "bitsandbytes==0.42.0" \  
  # "trl==0.7.10" # \  
  # "peft==0.7.1" \  
  
# 从github安装peft & trl  
!pip install git+https://github.com/huggingface/trl@a3c5b7178ac4f65569975efadc97db2f3749c65e --upgrade  
!pip install git+https://github.com/huggingface/peft@4a1559582281fc3c9283892caea8ccef1d6f5a4f--upgrade

如果你的GPU采用的是Ampere架构(如NVIDIA A10G或RTX 4090/3090)或更新版本,你可以利用Flash Attention技术。Flash Attention通过优化注意力机制的计算过程,并采用一些经典技术(如分块和重新计算)来显著提高计算速度,并降低内存消耗。简而言之,这项技术可以将训练速度提升至原来的三倍。想要了解更多详情,可以访问FlashAttention的官方页面。

注意:如果你的计算机内存不足96GB且拥有大量CPU核心,你可能需要调整_MAX_JOBS的数值。在我们的测试中,使用的是g5.2xlarge实例,设置了4_个作业。

import torch; assert torch.cuda.get_device_capability()[0] >= 8, 'Hardware not supported for Flash Attention'  
# install flash-attn  
!pip install ninja packaging  
!MAX_JOBS=4 pip install flash-attn --no-build-isolation
安装Flash Attention可能需要一段时间(大约10到45分钟)。

我们将利用Hugging Face Hub作为一个远程模型版本控制服务。这意味着在训练过程中,我们的模型、日志和相关信息将自动上传到Hugging Face Hub。为了使用这项服务,你需要在Hugging Face上注册一个账户。注册完成后,我们会使用 huggingface_hub 包中的登录工具来登录你的账户,并在本地磁盘上保存你的访问令牌。

from huggingface_hub import login

login(
token=“”, # 在此处添加您的token
add_to_git_credential=True
)


三、创建和准备数据集

一旦您确定微调是正确的解决方案,我们需要准备一个数据集来训练我们的模型。这个数据集应该是多样化的任务示范,展示了你想要解决的问题。创建数据集的方法有很多,比如:

  • 利用现有的开源数据集,如**Spider**

  • 利用大语言模型生成合成数据集,如**Alpaca**

  • 雇佣人类来创建数据集,如**Dolly**

  • 结合以上方法,如**Orca**

每种方法都有其自身的优势和劣势,并取决于预算、时间和质量要求。例如,使用现有数据集是最简单的,但可能不针对你的特定使用场景,而人工创建数据集虽然准确度高,但成本和时间消耗也大。也可以将几种方法结合起来创建指令数据集,如_Orca: Progressive Learning from Complex Explanation Traces of GPT-4_所示。

在我们的示例中,我们将使用一个名为**sql-create-context**的现有数据集,它包含了自然语言指令、数据库模式定义以及相应的SQL查询样本。

随着trl的最新版本发布,我们现在支持流行的指令和对话数据集格式。这意味着我们只需将数据集转换为支持的格式之一,trl就会自动处理后续步骤。支持的格式包括:

  • 对话格式
{"messages": [{"role": "system", "content": "You are..."}, {"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]}{"messages": [{"role": "system", "content": "You are..."}, {"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]}{"messages": [{"role": "system", "content": "You are..."}, {"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]}
  • 指令格式
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}{"prompt": "&l
<think>好的,用户现在遇到了Elasticsearch的update_by_query方法报错的问题,具体错误是parsing_exception:Unknown keyfor aSTART_OBJECT in[doc]。我需要帮他解决这个问题,同时结合之前的对话和用户提供的引用内容。首先,回顾用户提供的代码,他使用了update_fields和update_query的结构,可能误将doc字段作为顶层参数,而实际上应该放在script中或者正确使用doc字段。之前的回答已经指出,正确的用法需要将script和query并列,但用户可能仍然没有正确理解如何结合doc字段使用。 根据用户提供的引用[1],里面提到Elasticsearch的一些配置变更,比如node.name默认值的变化,但可能和当前问题无关。而引用[2]是关于Dockerfile的构建,似乎也不相关。因此,需要专注于用户的问题本身,即update_by_query的正确用法。 用户之前的回答已经给出了一些示例,但用户可能没有正确应用。用户当前的代码中,update_query被构造为{'doc':update_fields},而直接传递给body参数。这可能是因为用户误用了update API的格式,而非update_by_query的正确格式。update_by_query通常需要结合query和script参数,而不是直接使用doc字段。例如,正确的做法是通过script来更新文档,而不是直接使用doc。因此,用户需要将update_fields转换为script的形式。另外,用户提到的错误信息指出在[doc]中存在未知的键,说明Elasticsearch在解析请求体时,在doc对象中遇到了不符合预期的结构。可能用户直接将doc作为顶层参数传递,而正确的结构应该是将doc放在script中,或者使用script.source来执行更新操作。 需要检查用户的具体代码,确认是否正确使用了script和query的结构。例如,正确的update_by_query请求应该包含query和script两个顶级字段,而不是doc。因此,用户需要将update_query调整为包含script的部分,而不是直接使用doc。例如,使用script来设置字段的值,而不是直接传递doc对象。 总结来说,用户需要将更新操作通过script来执行,而不是直接使用doc字段。同时,确保script和query在同一个层级,并且结构正确。此外,还需要注意脚本的语法是否正确,比如使用ctx._source来访问字段,并指定lang为painless。最后,生成相关问题,帮助用户进一步了解如何监控任务、处理脚本安全性和性能优化。</think>### 问题分析 报错`parsing_exception: Unknown key for a START_OBJECT in [doc]`的直接原因是**JSON请求体结构错误**。用户尝试使用`doc`字段直接更新文档,但`update_by_query`要求通过`script`或`query`的特定结构实现批量更新[^1]。 --- ### 正确用法详解 #### 1. **核心语法结构** ```python from elasticsearch import Elasticsearch es = Elasticsearch() update_query = { "query": { # 筛选需要更新的文档 "term": {"status.keyword": "pending"} }, "script": { # 必须使用script字段 "source": "ctx._source.version_line = params.new_version", "params": {"new_version": "C1"}, "lang": "painless" } } # 执行更新 response = es.update_by_query( index=index_name, body=update_query, request_timeout=180, wait_for_completion=False ) ``` - **关键点**: - `script`是必填字段,用于定义更新逻辑 - `ctx._source`表示当前文档的字段,必须通过脚本访问 - 参数传递应使用`params`字典,避免硬编码值 #### 2. **错误代码对比** ❌ 错误结构: ```python update_query = {'doc': {'version_line': 'C1'}} # 直接使用doc字段 ``` ✅ 正确结构: ```python update_query = { "script": { "source": "ctx._source.version_line = params.new_version", "params": {"new_version": "C1"} } } ``` #### 3. **特殊场景处理** **部分字段更新**: ```python "script": { "source": """ if (ctx._source.containsKey('legacy_field')) { ctx._source.remove('legacy_field'); } """ } ``` **条件更新**: ```python "script": { "source": "if (ctx._source.value > 100) { ctx._source.status = 'flagged' }" } ``` --- ### 调试建议 1. **分步验证**: - 先单独执行`query`部分验证文档筛选条件 ```python es.search(index=index_name, body={"query": update_query["query"]}) ``` - 再添加`script`逻辑 2. **脚本校验工具**: ```bash POST /_scripts/painless/_execute { "script": { "source": "ctx._source.version_line = 'C1'" } } ``` 3. **错误日志分析**: ```json { "error": { "root_cause": [ { "type": "script_exception", "reason": "runtime error", "script_stack": ["ctx._source.remove('legacy_field')"], "script": "..." } ] } } ``` --- ### 性能优化 | 参数 | 作用 | 推荐值 | |-------|-------|-------| | `conflicts` | 冲突处理策略 | `proceed`(跳过冲突) | | `scroll_size` | 单次批量处理量 | 1000-5000 | | `max_docs` | 最大处理文档数 | 根据集群性能调整 | | `slices` | 并行分片数 | `auto`(自动分配) | ```python es.update_by_query( index=index_name, body=update_query, conflicts="proceed", scroll_size=2000, slices="auto" ) ``` --- ### 相关问题 1. 如何监控`update_by_query`异步任务的执行状态? **方法**:通过`_tasks`API获取任务进度 ```bash GET _tasks?detailed=true&actions=*byquery ``` 2. Elasticsearch脚本编程有哪些安全限制? **要点**: - 默认禁用文件系统访问 - 禁止反射操作 - 限制脚本执行时间(通过`script.max_compilations_rate`配置)[^1] 3. 如何处理大规模更新时的性能问题? **策略**: - 使用`滚动更新`分批处理 - 调整`refresh_interval`为`-1`(临时关闭刷新) - 增加`indices.memory.index_buffer_size`配置值 [^1]: Elasticsearch官方文档关于脚本引擎和安全策略的说明
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值