AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


GGUF / llama.cpp 转换
发布日期:2024-06-07 06:15:37 浏览次数: 2338


背景

我们通常会看到XX大厂又发布base模型和chat模型时,这两种模型的有什么区别呢?

其实,所有的大语言模型(LLM)的工作方式都是接收一些文本,然后预测最有可能出现在其后面的文本。

base模型,也称为基础模型,是在大量各种文本上训练出来的预测后续文本的模型。这意味着生成的后续文本不一定是对指令或对话的响应。

chat模型,又称为对话模型,是在base模型的基础上,通过对话记录(指令-响应)进行进一步的微调和强化学习。这使得它能够在接收指令和用户对话时,生成符合指令和人类预期的assistant响应内容。简单来说,chat模型更关注对话生成和交互场景,能更好地模拟人类对话。


1、微调

微调,简单说就是给我们的开源 LLM 的 CKPT 增加更多数据,让它多掌握一些知识,或者改变它的一些初始生成结果。

微调会调整模型的权重,并能更好地控制模型的生成输出。与 few-shot prompting 方式相比,微调也能解决因 token 消费过多、模型响应速度慢以及上下文窗口不够带来的问题。

为啥需要微调大模型?

大模型通常是针对通用任务进行训练的,可能并不总是适合特定任务。

微调是调整大模型参数的过程,以使其在特定任务上的性能更好。

1、提高准确性

微调可以显着提高大模型在特定任务上的准确性。例如,在对新闻文章数据集进行微调后,大模型可能能够更准确地识别文章的情感。

2、提高效率

可以使大模型在特定任务.上更有效。例如,在对问答数据集进行微调后,大模型可能能够更快、更准确地回答问题。

3、提高泛化能力

可以提高大模型的泛化能力,这意味着它们可以更好地执行与训练数据中数据不同的任务。例如,在对不同类型的创意文本格式(如诗歌、代码、脚本、音乐作品、电子邮件、信件等)的数据集进行微调后,大模型可能能够生成我们用看件下微调大模型的优可以减少训练大模型所需的数据量。

什么情况下需要微调大模型?

1、在特定任务上实现高精度

当大模型如GPT、BERT等在预训练阶段并不完全适应特定领域或任务,比如法律文档分析、医疗信息处理,微调可以帮助模型学习特定领域的专业术语和知识。

2、行业的数据量有限

尽管大模型可能具有丰富的知识,但针对特定任务的训练数据可能较少。微调可以帮助模型更好地适应这些小规模数据,避免过拟合。

3、模型更新

新数据或新需求出现时,对模型进行微调可以确保模型始终是最新的。

4、隐私或安全考虑

如果数据敏感,内部组织可能更愿意使用微调而不是公开模型,以保护数据隐私。

2、模型量化

模型量化是一种优化技术,它通过减少模型中权重和激活值的表示精度来降低模型的存储需求和计算复杂度。

在深度学习中,量化通常指的是将浮点数(如32位浮点数)转换为较低精度的表示(如16位、8位甚至更低的整数)。

这种转换可以在不显著牺牲模型性能的情况下,大幅减少模型的内存占用和推理时间

主要类型包括

1、线性量化:这是最常见的量化方法,它将浮点数线性映射到整数范围。例如,一个8位量化会将浮点数映射到0到255或-128到127的整数范围。

2、非线性量化:这种方法使用非线性函数来映射浮点数到整数,通常用于特定类型的数据或模型。

3、对称量化:在对称量化中,量化范围是对称的,例如-127到127,适用于数据分布相对均匀的情况。

4、非对称量化:非对称量化允许量化范围是非对称的,例如0到255,适用于数据分布偏斜的情况。

好处有以下几点

1、减少内存占用:量化后的模型权重和激活值占用更少的内存,使得模型可以在内存受限的设备上运行。

2、加速推理:低精度计算通常比高精度计算更快,可以在硬件加速器(如GPU、FPGA、ASIC)上实现更快的推理速度。

3、降低功耗:减少计算精度可以降低能耗,对于移动设备和嵌入式系统尤其重要。

4、提高吞吐量:在服务器端,量化可以提高处理请求的速率,从而提高服务质量。

3、完整代码

你将学习如何进行数据预处理、训练、运行模型和保存模型(例如,为 Llama.cpp)。

Llama-3 8b 是在令牌数量为15万亿(trillion tokens)的数据集上训练的,而Llama-2只使用了2万亿令牌。

# 安装 Unsloth、Xformers(Flash Attention)和所有其他软件包
!pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
!pip install --no-deps "xformers<0.0.26" trl peft accelerate bitsandbytes
  • • 支持 Llama、Mistral、CodeLlama、TinyLlama、Vicuna、Open Hermes 等模型。 还有 Yi、Qwen(llamafied)、Deepseek,以及所有基于 Llama、Mistral 的架构。

  • • unsloth支持16位LoRA或4位QLoRA,速度提升两倍。

  • • max_seq_length可以设置为任何值,因为unsloth通过kaiokendev的方法进行自动RoPE缩放。

  • • [NEW] 通过PR 26037,unsloth支持下载4位模型的速度提升4倍!

from unsloth import FastLanguageModel
import torch
max_seq_length = 2048 # 选择想要的任意方法!内部已经实现了对RoPE(Reachable Policy Optimization for Efficient Exploration)缩放的自动支持。
dtype = None # 对于自动检测,请使用 "None";如果使用 Tesla T4 或 V100 加速器,请使用 Float16;如果使用 Ampere 架构的加速器,请使用 Bfloat16。
load_in_4bit = True # 使用 4 位量化(quantization)技术来减少内存使用。这可以是一个可选项

# unsloth支持 4 位预量化模型,这可以使得下载速度提高 4 倍,并且不会出现内存不足(Out of Memory,简称 OOM)的问题。
fourbit_models = [
    "unsloth/mistral-7b-bnb-4bit",
    "unsloth/mistral-7b-instruct-v0.2-bnb-4bit",
    "unsloth/llama-2-7b-bnb-4bit",
    "unsloth/gemma-7b-bnb-4bit",
    "unsloth/gemma-7b-it-bnb-4bit", # Instruct version of Gemma 7b
    "unsloth/gemma-2b-bnb-4bit",
    "unsloth/gemma-2b-it-bnb-4bit", # Instruct version of Gemma 2b
    "unsloth/llama-3-8b-bnb-4bit", # [NEW] 15 Trillion token Llama-3
] # More models at https://huggingface.co/unsloth

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/llama-3-8b-bnb-4bit",
    max_seq_length = max_seq_length,
    dtype = dtype,
    load_in_4bit = load_in_4bit,
)

我们现在添加了 LoRA(Layer-wise Relevance Analysis,即层次相关性分析)适配器,使得只需更新模型中的 1% 到 10% 的参数!

model = FastLanguageModel.get_peft_model(
    model,
    r = 16, # Choose any number > 0 ! Suggested 8, 16, 32, 64, 128
    target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
                      "gate_proj", "up_proj", "down_proj",],
    lora_alpha = 16,
    lora_dropout = 0, # Supports any, but = 0 is optimized
    bias = "none",    # Supports any, but = "none" is optimized
    # [NEW] "unsloth" uses 30% less VRAM, fits 2x larger batch sizes!
    use_gradient_checkpointing = "unsloth", # True or "unsloth" for very long context
    random_state = 3407,
    use_rslora = False,  # We support rank stabilized LoRA
    loftq_config = None, # And LoftQ
)

数据准备

我们现在使用 yahma 的 Alpaca 数据集,共包含 52K 条数据。

数据集如下:

alpaca_prompt = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.

### Instruction:
{}

### Input:
{}

### Response:
{}"""

EOS_TOKEN = tokenizer.eos_token # tokenized 输出中必须添加 EOS\_TOKEN(结束标记)。这是一个特殊的标记,用于告诉模型一个序列已结束
def formatting_prompts_func(examples):
    instructions = examples["instruction"]
    inputs       = examples["input"]
    outputs      = examples["output"]
    texts = []
    for instruction, input, output in zip(instructions, inputs, outputs):
        # 表示必须添加 EOS\_TOKEN(结束标记),否则生成的序列会无限延续下去
        text = alpaca_prompt.format(instruction, input, output) + EOS_TOKEN
        texts.append(text)
    return { "text" : texts, }
pass

from datasets import load_dataset
dataset = load_dataset("yahma/alpaca-cleaned", split = "train")
dataset = dataset.map(formatting_prompts_func, batched = True,)

模型训练

这里使用 60 步进行训练,加速处理,也可以设置 num_train_epochs=1 以进行完整的运行,并将 max_steps=None 关闭。

from trl import SFTTrainer
from transformers import TrainingArguments

trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = dataset,
    dataset_text_field = "text",
    max_seq_length = max_seq_length,
    dataset_num_proc = 2,
    packing = False, # 在短序列情况下,可以将训练速度提高 5 倍。
    args = TrainingArguments(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 4,
        warmup_steps = 5,
        max_steps = 60,
        learning_rate = 2e-4,
        fp16 = not torch.cuda.is_bf16_supported(),
        bf16 = torch.cuda.is_bf16_supported(),
        logging_steps = 1,
        optim = "adamw_8bit",
        weight_decay = 0.01,
        lr_scheduler_type = "linear",
        seed = 3407,
        output_dir = "outputs",
    ),
)
# 显示当前GPU的内存信息
gpu_stats = torch.cuda.get_device_properties(0)
start_gpu_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
max_memory = round(gpu_stats.total_memory / 1024 / 1024 / 1024, 3)
print(f"GPU = {gpu_stats.name}. Max memory = {max_memory} GB.")
print(f"已预留{start_gpu_memory}GB的内存")

trainer_stats = trainer.train()

60 步进行训练。

#@title 显示最终的内存和时间统计数据
used_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
used_memory_for_lora = round(used_memory - start_gpu_memory, 3)
used_percentage = round(used_memory         /max_memory*100, 3)
lora_percentage = round(used_memory_for_lora/max_memory*100, 3)
print(f"{trainer_stats.metrics['train_runtime']} seconds used for training.")
print(f"{round(trainer_stats.metrics['train_runtime']/60, 2)} minutes used for training.")
print(f"Peak reserved memory = {used_memory} GB.")
print(f"Peak reserved memory for training = {used_memory_for_lora} GB.")
print(f"Peak reserved memory % of max memory = {used_percentage} %.")
print(f"Peak reserved memory for training % of max memory = {lora_percentage} %.")

推理

让我们运行模型!您可以更改指令和输入 - 输出留空!

# alpaca_prompt = Copied from above
FastLanguageModel.for_inference(model) # Enable native 2x faster inference
inputs = tokenizer(
[
    alpaca_prompt.format(
        "Continue the fibonnaci sequence.", # instruction
        "1, 1, 2, 3, 5, 8", # input
        "", # output - leave this blank for generation!
    )
], return_tensors = "pt").to("cuda")

outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
tokenizer.batch_decode(outputs)

输出:

['<|begin_of_text|>Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.\n\n### Instruction:\nContinue the fibonnaci sequence.\n\n### Input:\n1, 1, 2, 3, 5, 8\n\n### Response:\n13, 21, 34, 55, 89, 144, 233, 377, 610, 987<|end_of_text|>']

您也可以使用TextStreamer进行连续推理 - 这样您可以看到每个词的生成,而不是等待整个过程结束!

# alpaca_prompt = Copied from above
FastLanguageModel.for_inference(model) # Enable native 2x faster inference
inputs = tokenizer(
[
    alpaca_prompt.format(
        "Continue the fibonnaci sequence.", # instruction
        "1, 1, 2, 3, 5, 8", # input
        "", # output - leave this blank for generation!
    )
], return_tensors = "pt").to("cuda")

from transformers import TextStreamer
text_streamer = TextStreamer(tokenizer)
_ = model.generate(**inputs, streamer = text_streamer, max_new_tokens = 128)

输出:

# alpaca_prompt = Copied from above
FastLanguageModel.for_inference(model) # Enable native 2x faster inference
inputs = tokenizer(
[
    alpaca_prompt.format(
        "Continue the fibonnaci sequence.", # instruction
        "1, 1, 2, 3, 5, 8", # input
        "", # output - leave this blank for generation!
    )
], return_tensors = "pt").to("cuda")

from transformers import TextStreamer
text_streamer = TextStreamer(tokenizer)
_ = model.generate(**inputs, streamer = text_streamer, max_new_tokens = 128)

保存、加载微调好的模型

要将最终模型保存为LoRA适配器,可以使用Huggingface的push_to_hub进行在线保存或save_pretrained进行本地保存。

[注意] 这只保存LoRA适配器,而不是完整模型。要保存为16位或GGUF,请向下滚动!

model.save_pretrained("lora_model") # Local saving
tokenizer.save_pretrained("lora_model")
# model.push_to_hub("your_name/lora_model", token = "...") # Online saving
# tokenizer.push_to_hub("your_name/lora_model", token = "...") # Online saving

输出:

('lora_model/tokenizer_config.json',
 'lora_model/special_tokens_map.json',
 'lora_model/tokenizer.json')

现在,如果你想加载我们刚刚保存的LoRA适配器用于推理,请将False设置为True

if False:
    from unsloth import FastLanguageModel
    model, tokenizer = FastLanguageModel.from_pretrained(
        model_name = "lora_model", # YOUR MODEL YOU USED FOR TRAINING
        max_seq_length = max_seq_length,
        dtype = dtype,
        load_in_4bit = load_in_4bit,
    )
    FastLanguageModel.for_inference(model) # Enable native 2x faster inference

# alpaca_prompt = You MUST copy from above!

inputs = tokenizer(
[
    alpaca_prompt.format(
        "What is a famous tall tower in Paris?", # instruction
        "", # input
        "", # output - leave this blank for generation!
    )
], return_tensors = "pt").to("cuda")

outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
tokenizer.batch_decode(outputs)

输出:

["<|begin_of_text|>Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.\n\n### Instruction:\nWhat is a famous tall tower in Paris?\n\n### Input:\n\n\n### Response:\nThe Eiffel Tower is a famous landmark in Paris, France. It is a wrought iron tower that was built in 1889 for the World's Fair. Standing at 324 meters tall, it is the tallest building in Paris and one of the most recognizable landmarks in the world.<|end_of_text|>"]

你也可以使用 Hugging Face 的 AutoModelForPeftCausalLM。只有在你没有安装 unsloth 的情况下,才应该使用它。因为 4bit 模型下载不支持,Unsloth 的 推理速度是 2 倍以上

if False:
    # I highly do NOT suggest - use Unsloth if possible
    from peft import AutoPeftModelForCausalLM
    from transformers import AutoTokenizer
    model = AutoPeftModelForCausalLM.from_pretrained(
        "lora_model", # YOUR MODEL YOU USED FOR TRAINING
        load_in_4bit = load_in_4bit,
    )
    tokenizer = AutoTokenizer.from_pretrained("lora_model")

将 VLLM 保存为 float16

也支持直接保存为 float16。选择 merged_16bit 以保存为 float16,或选择 merged_4bit 以保存为 int4。

另外还支持 lora 适配器作为备用。使用 push_to_hub_merged 将模型上传至你的 Hugging Face 账户!

# Merge to 16bit
if False: model.save_pretrained_merged("model", tokenizer, save_method = "merged_16bit",)
if False: model.push_to_hub_merged("hf/model", tokenizer, save_method = "merged_16bit", token = "")

# Merge to 4bit
if False: model.save_pretrained_merged("model", tokenizer, save_method = "merged_4bit",)
if False: model.push_to_hub_merged("hf/model", tokenizer, save_method = "merged_4bit", token = "")

# Just LoRA adapters
if False: model.save_pretrained_merged("model", tokenizer, save_method = "lora",)
if False: model.push_to_hub_merged("hf/model", tokenizer, save_method = "lora", token = "")

GGUF / llama.cpp 转换

为了将文件保存为 GGUF / llama.cpp 格式,unsloth现在原生支持这一功能!

克隆了 llama.cpp,并且默认将其保存为 q8_0 格式。unsloth支持所有方法,例如 q4_k_m

使用 save_pretrained_gguf 进行本地保存,使用 push_to_hub_gguf 进行上传到 Hugging Face。

一些受支持的量化方法(完整列表请参阅unsloth的 Wiki 页面):

  • • q8_0 - 快速转换。资源消耗较高,但通常可以接受。

  • • q4_k_m - 推荐使用。对于一半的 attention.wv 和 feed_forward.w2 张量使用 Q6_K,其他情况使用 Q4_K。

  • • q5_k_m - 推荐使用。对于一半的 attention.wv 和 feed_forward.w2 张量使用 Q6_K,其他情况使用 Q5_K。

# Save to 8bit Q8_0
if False: model.save_pretrained_gguf("model", tokenizer,)
if False: model.push_to_hub_gguf("hf/model", tokenizer, token = "")

# Save to 16bit GGUF
if False: model.save_pretrained_gguf("model", tokenizer, quantization_method = "f16")
if False: model.push_to_hub_gguf("hf/model", tokenizer, quantization_method = "f16", token = "")

# Save to q4_k_m GGUF
if False: model.save_pretrained_gguf("model", tokenizer, quantization_method = "q4_k_m")
if False: model.push_to_hub_gguf("hf/model", tokenizer, quantization_method = "q4_k_m", token = "")

现在,在 llama.cpp 或基于 UI 的系统如 GPT4All 中使用 model-unsloth.gguf 文件或 model-unsloth-Q4_K_M.gguf 文件。


53AI,企业落地应用大模型首选服务商

产品:大模型应用平台+智能体定制开发+落地咨询服务

承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询