众所周知,大语言模型(LLM)正在飞速发展, 尤其是llama系列开源后,各行业都有了自己的大模型。其中,大模型微调技术在此过程中起到了非常关键的作用,它提升了模型的生成效率和适应性,使其能够在多样化的应用场景中发挥更大的价值,下面所有内容都是基于llama系列模型进行阐述的。
一、微调方法
下面对常见的llm微调技术进行总结
(1)adapter
Parameter-Efficient Transfer Learning for NLP --Adapter方法的思路是用一个较小的神经网络模块插入到模型的不同层, 如下图右侧的Adapter Layer 由两个线性映射层和一个非线性变换层组成,先降维再升维,两个线性映射的可学习参数量比原模型的attention和feed-forward 中的参数少的多。模型迁移微调的时候,只训练Adapter Layer的相关参数和原始模型中的norm层和最后的分类层。
(2)p-tuning
通过对LLM的Embedding层进行改造,在微调时固定其他层的weight,只对Embedding层进行重训练 ,常见的Prompt Tuning方法有: Prefix-Tuning:Optimizing Continuous Prompts for Generation、The Power of Scale for Parameter-Efficient Prompt Tuning等
(3)LST
LST: Ladder Side-Tuning for Parameter and Memory Efficient Transfer Learning--LST
LST直接在原模型的推理旁路上加了一个新的分支,也是固定原模型中的参数,将原模型各层输出与新建的旁路分支结合得到输出。
(4)LORA
>LoRA: Low-Rank Adaptation of Large Language Models -- LoRA方法应该是目前针对大语言模型中微调效果最好的一种方法,该方法的示意图如下,具体来说就是固定原始模型权重,然后定义两个低秩矩阵作为新增weight参与运算,并将两条链路的结果求和后作为本层的输出,而在微调时,只梯度下降新增的两个低秩矩阵。
关于代码阅读可以直接去看微软的官方LoRA源码https://github.com/microsoft/LoRA,但是我更推荐Lightning-AI的源码https://github.com/Lightning-AI/lit-llama ,它在官方LoRA源码的基础上做了详细的代码注释。
>QLoRA 实际上是 Quantize+LoRA 技术,简单的说就是把大模型(Base Model)在训练的时候从 16bit 压缩到 4bit。从而降低训练的显存。关键技术:
- 4 位 NormalFloat,QLoRA 使用 NF4(Normal Float 4)bit 来量化压缩预训练模型。这是一种优化的 4 位量化方法,它针对神经网络权重通常遵循零中心正态分布的特性进行优化。使用标准正态分布函数将权重缩放到[-1, 1]的范围内。相比传统的 4 位量化,它的权重信息损失少,从而提高了模型量化的整体精度。
- 双重量化,双重量化是一种内存优化策略,它对量化所使用的常数进行二次量化,进一步减小内存占用。这意味着我们可以在保持精度的同时,降低了内存需求。
- Page Optimizer,这是一种内存管理技术,利用了 NVIDIA 的统一内存特性,在 CPU 和 GPU 之间进行自动 page 对 page 传输,它在 GPU 内存不足时,可以将一部分数据暂时移到 CPU 内存,需要时再移回。这降低了在大型模型训练时由于内存不足而造成的问题。
QLoRA 论文笔记
QA-LoRA的核心思想是进行分组操作,这既用于量化也用于低秩适应,找到一个平衡点,能够在这两者之间自由切换或调整。QA-LoRA不仅实施起来相对简单,还能广泛应用到各种基础模型和NLP中,而且无论是在微调阶段还是在推理阶段,它在计算上都展现出了高效性。
与LoRA相比,QA-LoRA在时间和内存消耗方面具有优势。与QLoRA相比,QA-LoRA需要额外的存储空间用于对缩放和零点因子,但将的参数数量从减小到 ——由于我们通常设,上述变化是可以忽略的。与QLoRA相比,QA-LoRA的主要优势在于推理阶段,它更快、更准确。
二、微调工具
目前有许多团队在做微调工具,他们的贡献提高了我们的效率、减少失误。除了支持的上述各种sft策略的huggingface自研peft外,下面介绍如何使用这几个工具进行微调(其实很多都是基于peft做的一些封装和可视化),以及如何在Ollama中安装运行微调后的模型。
(1)MLX-LM
MLX团队一直在不懈地努力改进MLX-LM库在模型微调工具方面的能力。使用MLX-LM微调llama3十分简单。可以参考相关例子:https://github.com/ml-explore/mlx-examples/tree/main/llms/llama
大致步骤如下:
1.准备训练数据
glaiveai/glaive-function-calling-v2是一个专门用于训练大语言模型处理函数调用方面的数据集。我们可以下载这个数据集,并将数据转换为适合Llama3对话的格式,并保存到"/data"目录下。
数据下载地址:https://huggingface.co/datasets/glaiveai/glaive-function-calling-v2
数据格式转换的脚本如下:
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments,BitsAndBytesConfig
from datasets import load_dataset
import json
model_name ="meta-llama/Meta-Llama-3-8B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_name)
dataset = load_dataset("glaiveai/glaive-function-calling-v2",split="train")
def cleanup(input_string):
arguments_index = input_string.find('"arguments"')
if arguments_index == -1:
return input_string
start_quote = input_string.find("'", arguments_index)
if start_quote == -1:
return input_string
end_quote = input_string.rfind("'")
if end_quote == -1 or end_quote <= start_quote:
return input_string
arguments_value = input_string[start_quote+1:end_quote]
output_string = input_string[:start_quote] + arguments_value + input_string[end_quote+1:]
return output_string
def formatting_prompts_func(example):
output_texts = []
for i in range(len(example['system'])):
messages = [
{
"role": "system",
"content": example['system'][i][len("SYSTEM:"):].strip(),
},
]
conversations = example['chat'][i].split("<|endoftext|>")
for message in conversations:
continue_outer = False
message = message.strip()
if message:
if "USER:" in message:
user_content = message.split("ASSISTANT:")[0].strip()
messages.append({"role": "user", "content": user_content[5:].strip()})
if "ASSISTANT:" in message:
assistant_content = message.split("ASSISTANT:")[1].strip()
if "<functioncall>" in assistant_content:
text = assistant_content.replace("<functioncall>","").strip()
json_str = cleanup(text)
try:
data = json.loads(json_str)
except json.JSONDecodeError as e:
print(f"0 - Failed to decode JSON: {json_str} - {assistant_content}")
continue_outer = True
break
new_func_text = "<functioncall> "+ json_str
messages.append({"role": "assistant", "content": new_func_text})
else:
messages.append({"role": "assistant", "content": assistant_content})
elif message.startswith("FUNCTION RESPONSE:"):
function_response = message[18:].strip()
if "ASSISTANT:" in function_response:
function_content, assistant_content = function_response.split("ASSISTANT:")
try:
data = json.loads(function_content.strip())
except json.JSONDecodeError as e:
print(f"1 - Failed to decode JSON: {function_content}")
continue_outer = True
break
messages.append({"role": "user", "content": function_content.strip()})
messages.append({"role": "assistant", "content": assistant_content.strip()})
else:
try:
data = json.loads(function_response.strip())
except json.JSONDecodeError as e:
print(f"2 - Failed to decode JSON: {function_response}")
continue_outer = True
break
messages.append({"role": "user", "content": function_response.strip()})
elif message.startswith("ASSISTANT:"):
assistant_content = message.split("ASSISTANT:")[1].strip()
if "<functioncall>" in assistant_content:
text = assistant_content.replace("<functioncall>","").strip()
json_str = cleanup(text)
try:
data = json.loads(json_str)
except json.JSONDecodeError as e:
print(f"3 - Failed to decode JSON: {json_str} - {assistant_content}")
continue_outer = True
break
new_func_text = "<functioncall> "+ json_str
messages.append({"role": "assistant", "content": new_func_text})
if continue_outer:
continue
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
output_texts.append(text)
del example['system']
del example['chat']
return {"text": output_texts}
dataset = dataset.map(formatting_prompts_func, batched=True)
2.安装mlx-lm包
pip install mlx-lm
这个库为微调LLM提供了一个友好的用户交互方式,省去了许多麻烦,并实现更好的效果。
3.创建LoRA配置
通过配置LoRA来微调Llama3 8B模型。更改一些关键参数以优化性能:
- 使用fp16代替qlora,以避免由于量化和解量化而导致的潜在性能下降。
- 将lora_layers设置为32,并使用全线性层,以获得与全微调相媲美的结果。
以下是lora_config.yaml文件的示例:
# The path to the local model directory or Hugging Face repo.
model: "meta-llama/Meta-Llama-3-8B-Instruct"
# Whether or not to train (boolean)
train: true
# Directory with {train, valid, test}.jsonl files
data: "data"
# The PRNG seed
seed: 0
# Number of layers to fine-tune
lora_layers: 32
# Minibatch size.
batch_size: 1
# Iterations to train for.
iters: 6000
# Number of validation batches, -1 uses the entire validation set.
val_batches: 25
# Adam learning rate.
learning_rate: 1e-6
# Number of training steps between loss reporting.
steps_per_report: 10
# Number of training steps between validations.
steps_per_eval: 200
# Load path to resume training with the given adapter weights.
resume_adapter_file: null
# Save/load path for the trained adapter weights.
adapter_path: "adapters"
# Save the model every N iterations.
save_every: 1000
# Evaluate on the test set after training
test: false
# Number of test set batches, -1 uses the entire test set.
test_batches: 100
# Maximum sequence length.
max_seq_length: 8192
# Use gradient checkpointing to reduce memory use.
grad_checkpoint: true
# LoRA parameters can only be specified in a config file
lora_parameters:
# The layer keys to apply LoRA to.
# These will be applied for the last lora_layers
keys: ['mlp.gate_proj', 'mlp.down_proj', 'self_attn.q_proj', 'mlp.up_proj', 'self_attn.o_proj','self_attn.v_proj', 'self_attn.k_proj']
rank: 128
alpha: 256
scale: 10.0
dropout: 0.05
# Schedule can only be specified in a config file, uncomment to use.
# lr_schedule:
# name: cosine_decay
# warmup: 100 # 0 for no warmup
# warmup_init: 1e-7 # 0 if not specified
# arguments: [1e-6, 1000, 1e-7] # passed to scheduler
4.执行微调
在数据准备和LoRA配置就绪后,就可以开始微调Llama3 8B了,只需要运行以下命令。
mlx_lm.lora --config lora_config.yaml
5.模型融合发布
LoRa模型是无法单独完成推理的,需要和原生Llama结合才能运行。因为它freeze了原来的模型,单独加了一些层,后续的训练都在这些层上做,所以需要进行模型融合。
可以使用mlx_lm.fuse将训练过的适配器与原始的Llama3 8B模型以HF格式融合:
mlx_lm.fuse --model meta-llama/Meta-Llama-3-8B-Instruct
(2)PyReft
ReFT方法的出发点是基于干预模型可解释性的概念,该概念强调改变表示而不是权重。这个概念基于线性表示假设,该假设指出概念被编码在神经网络的线性子空间中。项目源码:https://github.com/stanfordnlp/pyreft,PyReFT是一个基于ReFT方法的库,支持通过可训练的干预来调整内部语言模型的表示。PyReFT具有更少的微调参数和更强的鲁棒性,可以提高微调效率、降低微调成本,同时也为研究自适应参数的可解释性打开了大门。
PyReft支持:
- 微调发布在HuggingFace上任何预训练大模型
- 可配置ReFT超参数
- 轻松将微调后的结果分享到HuggingFace
1.安装依赖库
使用Pip安装最新版本的transformers以支持llama3。此外,还需要安装bitsandbytes库。
!pip install -q git+https://github.com/huggingface/transformers
!pip install -q bitsandbytes
2.安装或导入pyreft
安装Pyreft库。如果已经安装则将导入pyreft。
try:
import pyreft
except ModuleNotFoundError:
!pip install git+https://github.com/stanfordnlp/pyreft.git
3.加载模型
在加载模型之前需要确保登陆到huggingface,以便于访问Llama3模型,可以使用下面的代码片段:
from huggingface_hub import notebook_login
notebook_login()
接下来就是设置用于训练的提示词模板。由于我们将使用基础模型,因此需要添加特殊的标记,以便模型能够学会停止并且不继续生成文本。下面的代码片段用于执行加载模型和标记器。
import torch, transformers, pyreft
device = "cuda"
prompt_no_input_template = """<|begin_of_text|><|start_header_id|>user<|end_header_id|>%s<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
model_name_or_path = "meta-llama/Meta-Llama-3-8B"
model = transformers.AutoModelForCausalLM.from_pretrained(
model_name_or_path, torch_dtype=torch.bfloat16, device_map=device, trust_remote_code=True)
# # get tokenizer
tokenizer = transformers.AutoTokenizer.from_pretrained(
model_name_or_path, model_max_length=2048,
padding_side="right", use_fast=False)
tokenizer.pad_token = tokenizer.eos_token
接着,设置pyreft配置,然后使用pyreft.get_reft_model()方法准备好模型。
# get reft model
reft_config = pyreft.ReftConfig(representations={
"layer": 8, "component": "block_output",
"low_rank_dimension": 4,
"intervention": pyreft.LoreftIntervention(embed_dim=model.config.hidden_size,
low_rank_dimension=4)})
reft_model = pyreft.get_reft_model(model, reft_config)
reft_model.set_device("cuda")
reft_model.print_trainable_parameters()
4.准备数据集
下面以OpenHermes—2.5数据集为例。由于Reft Trainer的数据需要采用特定格式,因此我们使用:
pyreft.make_last_position_supervised_data_module()
来准备数据。
dataset_name = "teknium/OpenHermes-2.5"
from datasets import load_dataset
dataset = load_dataset(dataset_name, split="train")
dataset = dataset.select(range(10_000))
data_module = pyreft.make_last_position_supervised_data_module(
tokenizer, model, [prompt_no_input_template % row["conversations"][0]["value"] for row in dataset],
[row["conversations"][1]["value"] for row in dataset])
5.执行训练
为pyreft.ReftTrainerForCausalLM()设置训练参数。可以根据自己的使用情况和计算资源进行更改。下面的代码参数设置只训练1个epoch。
# train
training_args = transformers.TrainingArguments(
per_device_train_batch_size = 4,
gradient_accumulation_steps = 8,
warmup_steps = 100,
num_train_epochs = 1,
learning_rate = 5e-4,
bf16 = True,
logging_steps = 1,
optim = "paged_adamw_32bit",
weight_decay = 0.0,
lr_scheduler_type = "cosine",
output_dir = "outputs",
report_to=[]
)
trainer = pyreft.ReftTrainerForCausalLM(model=reft_model, tokenizer=tokenizer, args=training_args, **data_module)
_ = trainer.train()
训练完成后,将干预块保存到reft_to_share目录中。
reft_model.save(
save_directory="./reft_to_share",
)
6.发布与推理
模型微调训练完成后要进行推理。需要加载基本模型,并通过合并干预块来准备reft模型。然后将reft模型转移到cuda。
import torch, transformers, pyreft
device = "cuda"
model_name_or_path = "meta-llama/Meta-Llama-3-8B"
model = transformers.AutoModelForCausalLM.from_pretrained(
model_name_or_path, torch_dtype=torch.bfloat16, device_map=device)
reft_model = pyreft.ReftModel.load(
"Syed-Hasan-8503/Llama-3-openhermes-reft", model, from_huggingface_hub=True
)
reft_model.set_device("cuda")
接着进行推理测试:
instruction = "A rectangular garden has a length of 25 feet and a width of 15 feet. If you want to build a fence around the entire garden, how many feet of fencing will you need?"
# tokenize and prepare the input
prompt = prompt_no_input_template % instruction
prompt = tokenizer(prompt, return_tensors="pt").to(device)
base_unit_location = prompt["input_ids"].shape[-1] - 1 # last position
_, reft_response = reft_model.generate(
prompt, unit_locations={"sources->base": (None, [[[base_unit_location]]])},
intervene_on_prompt=True, max_new_tokens=512, do_sample=True,
eos_token_id=tokenizer.eos_token_id, early_stopping=True
)
print(tokenizer.decode(reft_response[0], skip_special_tokens=True))
(3)litgpt
LitGPT是一个可以用于微调预训练模型的命令行工具,支持20多个LLM的评估、部署。它为世界上最强大的开源大型语言模型(LLM)提供了高度优化的训练配方。源代码:https://github.com/Lightning-AI/litgpt
1.安装
pip install 'litgpt[all]'
2.评估测试
选择一个模型并执行:下载、对话、微调、预训练以及部署等。
# ligpt [action] [model]
litgpt download meta-llama/Meta-Llama-3-8B-Instruct
litgpt chat meta-llama/Meta-Llama-3-8B-Instruct
litgpt finetune meta-llama/Meta-Llama-3-8B-Instruct
litgpt pretrain meta-llama/Meta-Llama-3-8B-Instruct
litgpt serve meta-llama/Meta-Llama-3-8B-Instruct
例如:使用微软的phi-2进行对话评估。
# 1) Download a pretrained model
litgpt download --repo_id microsoft/phi-2
# 2) Chat with the model
litgpt chat \
--checkpoint_dir checkpoints/microsoft/phi-2
>> Prompt: What do Llamas eat?
3.微调模型
下面是在phi-2基础上进行微调的命令。
# 1) Download a pretrained model
litgpt download --repo_id microsoft/phi-2
# 2) Finetune the model
curl -L https://huggingface.co/datasets/ksaw008/finance_alpaca/resolve/main/finance_alpaca.json -o my_custom_dataset.json
litgpt finetune \
--checkpoint_dir checkpoints/microsoft/phi-2 \
--data JSON \
--data.json_path my_custom_dataset.json \
--data.val_split_fraction 0.1 \
--out_dir out/custom-model
# 3) Chat with the model
litgpt chat \
--checkpoint_dir out/custom-model/final
除此外,还可以基于自己的数据进行训练。详细参考GitHub。
4.部署
通过下面的部署命令,启动模型服务。
# locate the checkpoint to your finetuned or pretrained model and call the `serve` command:
litgpt serve --checkpoint_dir path/to/your/checkpoint/microsoft/phi-2
# Alternative: if you haven't finetuned, download any checkpoint to deploy it:
litgpt download --repo_id microsoft/phi-2
litgpt serve --checkpoint_dir checkpoints/microsoft/phi-2
通过Http API访问服务。
# Use the server (in a separate session)
import requests, json
response = requests.post(
"http://127.0.0.1:8000/predict",
json={"prompt": "Fix typos in the following sentence: Exampel input"}
)
print(response.json()["output"])
(4)LLaMA-Factory
LLaMA-Factory 是一个开源项目,它提供了一套全面的工具和脚本,用于微调、部署和基准测试LLaMA模型。源代码:https://github.com/hiyouga/LLaMA-Factory/,LLaMA-Factory 提供以下功能,使得我们可以轻松地使用LLaMA模型,具体操作可以参考链接GitHub - echonoshy/cgft-llm: Practice to LLM.:
- 数据预处理和标记化的脚本
- 用于微调 LLaMA 模型的训练流程
- 使用经过训练的模型生成文本的推理脚本
- 评估模型性能的基准测试工具
- 用于交互式测试的 Gradio Web UI
使用LLaMA-Factory 进行微调的步骤如下:
1.数据准备
LLaMA-Factory要求训练数据的格式如下:
[
{
"instruction": "What is the capital of France?",
"input": "",
"output": "Paris is the capital of France."
},
...
]
每个 JSON 对象代表一个训练示例,其中包含以下字段:
- instruction:任务指令或提示
- input:任务的附加上下文(可以为空)
- output:目标完成或响应
2.下载安装依赖包
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -r requirements.txt
3.执行微调
支持使用Python进行微调也支持图形化界面的方式。
下面是执行python脚本进行微调:
python finetune.py \
--model_name llama-7b \
--data_path data/alpaca_data_tokenized.json \
--output_dir output/llama-7b-alpaca \
--num_train_epochs 3 \
--batch_size 128 \
--learning_rate 2e-5 \
--fp16
该脚本将加载预训练的LLaMA模型,准备训练数据集,并使用指定的超参数运行微调脚步。微调后的模型检查点将保存在 中output_dir。
主要参数设置如下:
- model_name:要微调的基础 LLaMA 模型,例如llama-7b
- data_path:标记数据集的路径
- output_dir:保存微调模型的目录
- num_train_epochs:训练周期数
- batch_size:训练的批次大小
- learning_rate:优化器的学习率
- fp16:使用 FP16 混合精度来减少内存使用量
接着使用微调后的结果进行推理测试:
python generate.py \
--model_path output/llama-7b-alpaca \
--prompt "What is the capital of France?"
当然,微调过程也可以在可视化界面上进行。首先需要启动GUI界面。
python web_ui.py
4.基准测试
LLaMA-Factory 包含了基于各种评估数据集进行基准测试的脚本:
benchmark.py
例如:
python benchmark.py \
--model_path output/llama-7b-alpaca \
--benchmark_datasets alpaca,hellaswag
这个Python命令将加载经过微调的模型并评估其在指定方面的表现。benchmark_datasets参数指明使用哪些数据集进行评估。评估报告包括:准确度、困惑度和 F1分数等指标。还可以使用DatasetBuilder实现一个类并将其注册到基准脚本来添加您自己的评估数据集。
三、量化
这里以llama.cpp工具为例,介绍模型量化并在本地部署的详细步骤。这里使用 Meta最新开源的 Llama3-8B 模型。官方链接:
https://github.com/ggerganov/llama.cpp.git
(1)编译
基于make对llama.cpp项目进行编译,生成./main
(用于推理)和./quantize
(用于量化)二进制文件。Windows/Linux用户如需启用GPU推理,则推荐与BLAS(或cuBLAS如果有GPU)一起编译,可以提高prompt处理速度。以下是和cuBLAS
一起编译的命令,适用于NVIDIA相关GPU。参考:llama.cpp#blas-build
make LLAMA_CUBLAS=1
macOS用户无需额外操作,llama.cpp已对ARM NEON做优化,并且已自动启用BLAS。M系列芯片推荐使用Metal启用GPU推理,显著提升速度。只需将编译命令改为:LLAMA_METAL=1 make
,参考llama.cpp#metal-build
LLAMA_METAL=1 make
(2)生成量化版本模型
目前llama.cpp
已支持.pth
文件以及huggingface格式.bin
的转换。将完整模型权重转换为GGML的FP16格式,生成文件路径为Meta-Llama-3-8B-hf/ggml-model-f32.gguf
。进一步对FP32模型进行4-bit
量化,生成量化模型文件路径为Meta-Llama-3-8B-hf/ggml-model-q4_0.gguf
python convert.py Meta-Llama-3-8B-hf/ --vocab-type bpe
./quantize ./Meta-Llama-3-8B-hf/ggml-model-f16.gguf ./Meta-Llama-3-8B-hf/ggml-model-q4_0.gguf q4_0
可以-h 查看脚本的一些超参数
(3)加载并启动模型
运行./main二进制文件,-m命令指定 Q4量化模型(也可加载ggml-FP16的模型)。
# run the inference 推理
./main -m ./Meta-Llama-3-8B-hf/ggml-model-q4_0.gguf -n 128
./main -m ./Meta-Llama-3-8B-hf/ggml-model-f16.gguf -n 128
#以交互式对话
./main -m ./Meta-Llama-3-8B-hf/ggml-model-q4_0.gguf --color -f prompts/alpaca.txt -ins -c 2048 --temp 0.2 -n 256 --repeat_penalty 1.3
#chat with bob
./main -m ./Meta-Llama-3-8B-hf/ggml-model-q4_0.gguf -n 256 --repeat_penalty 1.0 --color -i -r "User:" -f prompts/chat-with-bob.txt
如果想用GPU加速,放在GPU上,需要更换编译 llama.cpp 的方式
GPU推理:通过Metal编译则只需在./main中指定-ngl 1;cuBLAS编译需要指定offload层数,例如-ngl 40表示offload 40层模型参数到GPU
在支持 Metal 的情况下,可以使用 --gpu-layers|-ngl 命令行参数启用 GPU 推理。任何大于 0 的值都会将计算卸载到 GPU
比较重要的参数:
-ins 启动类ChatGPT的对话交流模式 -f 指定prompt模板,alpaca模型请加载prompts/alpaca.txt 指令模板 -c 控制上下文的长度,值越大越能参考更长的对话历史(默认:512) -n 控制回复生成的最大长度(默认:128) –repeat_penalty 控制生成回复中对重复文本的惩罚力度 –temp 温度系数,值越低回复的随机性越小,反之越大 –top_p, top_k 控制解码采样的相关参数 -b 控制batch size(默认:512) -t 控制线程数量(默认:8),可适当增加
(4)API 方式调用, 架设server
llama.cpp
还提供架设server
的功能,用于API调用、架设简易demo
等用途。
运行以下命令启动server
,二进制文件./server
在llama.cpp
根目录,服务默认监听127.0.0.1:8080
。这里指定模型路径、上下文窗口大小。如果需要使用GPU解码,也可指定-ngl
参数。
./server -m ./Meta-Llama-3-8B-hf/ggml-model-q4_0.gguf -c 4096 -ngl 999
服务启动后,即可通过多种方式进行调用,例如利用curl命令。以下是一个示例脚本(同时存放在scripts/llamacpp/server_curl_example.sh
),将Alpaca-2的模板进行包装并利用curl命令进行API访问。
# server_curl_example.sh
SYSTEM_PROMPT='You are a helpful assistant. 你是一个乐于助人的助手。'
# SYSTEM_PROMPT='You are a helpful assistant. 你是一个乐于助人的助手。请你提供专业、有逻辑、内容真实、有价值的详细回复。' # Try this one, if you prefer longer response.
INSTRUCTION=$1
ALL_PROMPT="[INST] <<SYS>>\n$SYSTEM_PROMPT\n<</SYS>>\n\n$INSTRUCTION [/INST]"
CURL_DATA="{\"prompt\": \"$ALL_PROMPT\",\"n_predict\": 128}"
curl --request POST \
--url http://localhost:8080/completion \
--header "Content-Type: application/json" \
--data "$CURL_DATA"
给出一个示例指令。
bash server_curl_example.sh '请列举5条文明乘车的建议'
稍后返回响应结果。
{
"content": " 以下是五个文明乘车的建议:1)注意礼貌待人,不要大声喧哗或使用不雅用语;2)保持车厢整洁卫生,丢弃垃圾时要及时处理;3)不影响他人休息和正常工作时间,避免在车厢内做剧烈运动、吃零食等有异味的行为;4)遵守乘车纪律,尊重公共交通工具的规则和制度;5)若遇到突发状况或紧急情况,请保持冷静并配合相关部门的工作。这些建议旨在提高公民道德水平和社会文明程度,共同营造一个和谐、舒适的乘坐环境。",
"generation_settings":
{
"frequency_penalty": 0.0,
"ignore_eos": false,
"logit_bias": [],
"mirostat": 0,
"mirostat_eta": 0.10000000149011612,
"mirostat_tau": 5.0,
"model": "zh-alpaca2-models/7b/ggml-model-q6_k.gguf",
"n_ctx": 4096,
"n_keep": 0,
"n_predict": 128,
"n_probs": 0,
"penalize_nl": true,
"presence_penalty": 0.0,
"repeat_last_n": 64,
"repeat_penalty": 1.100000023841858,
"seed": 4294967295,
"stop": [],
"stream": false,
"temp": 0.800000011920929,
"tfs_z": 1.0,
"top_k": 40,
"top_p": 0.949999988079071,
"typical_p": 1.0
},
"model": "zh-alpaca2-models/7b/ggml-model-q6_k.gguf",
"prompt": " [INST] <<SYS>>\nYou are a helpful assistant. 你是一个乐于助人的助手。\n<</SYS>>\n\n请列举5条文明乘车的建议 [/INST]",
"stop": true,
"stopped_eos": true,
"stopped_limit": false,
"stopped_word": false,
"stopping_word": "",
"timings":
{
"predicted_ms": 3386.748,
"predicted_n": 120,
"predicted_per_second": 35.432219934875576,
"predicted_per_token_ms": 28.2229,
"prompt_ms": 0.0,
"prompt_n": 120,
"prompt_per_second": null,
"prompt_per_token_ms": 0.0
},
"tokens_cached": 162,
"tokens_evaluated": 43,
"tokens_predicted": 120,
"truncated": false
}
补充:
上面是官方提供的量化方式,你还可以尝试一下AWQ量化(是一种基于激活值分布挑选显著权重进行量化的方法。它不依赖于反向传播或重建,能够很好地保持模型在不同领域和模式上的泛化能力,而不会过拟合到校准集)。其对比gptq而言,AWQ量化精度比GPTQ高一点,并且AWQ比GPTQ更容易实现,计算性能更高。
相比AWQ采用heuristic的方法来寻找最佳的scale和clip系数,新的OminiQuant则采用训练的方式来获得相应的系数,论文数据比AWQ获得更高的量化准确度。
AWQ的原理非常简单,就是计算一个scale系数tensor,shape为[k],k为矩阵乘的权重reduce的维度大小。对激活除以该tensor,并对矩阵乘的权重乘以该tensor,这降低了权重量化的难度,使得权重可以采用常规的group量化(直接根据最大最小值计算scale, zero point)。AWQ的核心技术一是这个对激活和权重应用scale的方法,另外就是如何计算这个scale tensor。因为激活是fp16不量化,对激活进行scale一般不会牺牲精度,因此可以对权重进行一些处理降低量化的难度。
虽然AWQ与GPTQ两者都采用group量化,对shape为[k, n]的矩阵乘权重都生成(k/group) * n套量化系数。但是GPTQ通常采用act_order=True选项,这个导致每一个group并非使用一组相同的scale和zero point系数,而是每个k位置对应的向量都对应不同的scale和zero point(不同k位置共享一组系数,但是这个位置是随机的),每读取一个元素都要读取scale和zero point,导致反量化效率很低。而act_order=False时,每一个向量group size元素都共享同一组scale和zero point系数,这样反量化只需要每隔group size个元素才需要重新读取一次scale和zero point,反量化效率很高。AWQ反量化跟GPTQ act_order=False是一样的,因此计算效率比较高。
另外AWQ虽然要对激活乘以一个scale tensor,但是这个tensor通常可以合并到前面的RMS NORM上面,使得这个操作不会引入额外计算。
pip install autoawq
# 需要有較新版本的transformers, 在colab更新後可能需要restart
pip install transformers==4.37.0
pip install flash_attn
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_path = 'MediaTek-Research/Breeze-7B-Instruct-64k-v0_1'
quant_path = 'Breeze-7B-Instruct-64k-v0_1-AWQ'
quant_config = { "zero_point": True, "q_group_size": 128, "w_bit": 4, "version": "GEMM", "modules_to_not_convert": []}
# Load model
model = AutoAWQForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
# Quantize
model.quantize(tokenizer, quant_config=quant_config)
# Save quantized model
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)
四、与ollama整合
对于ollama的基本操作这里不做过多赘述,可以参考前面的博客笔记,下面主要针对微调后的llm模型与ollama整合起来。按照 Ollama modelfile ADAPTER 的说明,Ollama 支持 ggml 格式的 LoRA,所以我们需要把微调生成的 LoRA 转换成ggml格式。为此,我们需要使用到 Llama.cpp 的格式转换脚本:“conver-lora-to-ggml.py”。
./conver-lora-to-ggml.py /output/llama3_cn_01 llama
执行完命令后,将在 /output/llama3_cn_01 下生成 ggml-adapter-model.bin 文件。这个文件就是 Ollama 所需要的ggml格式LoRA文件。接下来就是创建一个modelfile,然后生成一个整合后的模型即可。比如创建一个llama3.modelfile,其内容如下
# set the base model
FROM llama3:8b
# set custom parameter values
PARAMETER temperature 1
PARAMETER num_keep 24
PARAMETER stop <|start_header_id|>
PARAMETER stop <|end_header_id|>
PARAMETER stop <|eot_id|>
PARAMETER stop <|reserved_special_token
# set the model template
TEMPLATE """
{{ if .System }}<|
start_header_id
|>system<|
end_header_id
|>
{{ .System }}<|
eot_id
|>{{ end }}{{ if .Prompt }}<|
start_header_id
|>user<|
end_header_id
|>
{{ .Prompt }}<|
eot_id
|>{{ end }}<|
start_header_id
|>assistant<|
end_header_id
|>
{{ .Response }}<|
eot_id
|>
"""
# set the system message
SYSTEM You are llama3 from Meta, customized and hosted @ HY's Blog (https://blog.yanghong.dev).
# set Chinese lora support
ADAPTER /root/.ollama/models/lora/ggml-adapter-model.bin
接着使用Ollama命令以及modelfile来创建自定义模型:
ollama create llama3:c01 -f llama3.modelfile
查看模型列表:
ollama list
运行模型:
ollama run llama3:c01
参考链接:
1、2024年大语言模型(LLM)微调方法最全总结!_大模型微调-CSDN博客
2、LLM微调(Finetune) 技术--LoRA - 知乎
3、[大模型微调技术] LoRA、QLoRA、QA-LoRA 原理笔记 - 知乎