LLM模型finetune_量化_部署

       众所周知,大语言模型(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 GenerationThe 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 论文笔记

  1. QLoRA | 48G内存训练24小时,改进版4-bit量化技术微调650亿参数的模型达到chatgpt99.3%的效果
  2. Johnson7788:QLORA:LLM的高效量化微调

>QA-Lora

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,二进制文件./serverllama.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 原理笔记 - 知乎

4、一些 Llama3 微调工具以及如何在 Ollama 中运行 - AIGC

5、使用llama.cpp量化部署LLM - AIGC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaomu_347

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值