微调大模型笔记(unsloth框架)
很多注释都为直接转载,笔者也不明白参数意义,若碰到不明白含义的参数建议使用默认值。
原文地址:
10G显存,使用Unsloth微调Qwen2并使用Ollama推理
使用Unsloth微调Llama3-Chinese-8B-Instruct中文开源大模型
大模型的常用指令格式 --> ShareGPT 和 Alpaca (以 llama-factory 里的设置为例)
1 安装环境
-ubuntu22.04
-cuda12.1.0
-py310
-torch2.3.0-1.17.1
安装unsloth
pip install "unsloth[cu121-torch230] @ git+https://github.com/unslothai/unsloth.git"
2 下载模型
从此处开始所有代码皆为 python 语句,它的执行方式为:在有对应环境下输入 python 命令进入python命令行,输入命令回车直接运行。这点和其他编程语言固定编译运行的执行顺序不同。
from modelscope import snapshot_download
# 下载模型并设置目录,这里下载模型是从魔塔社区拉取的(如果从别的地方拉取可以查看对应平台的文档),会存入到 /root/models 中,如果不设置 cache_dir 参数则会存储在 .cache 目录中,调用时可以使用 model_dir 变量,也可以使用绝对路径
model_dir = snapshot_download("qwen/Qwen2-7B")
3 加载模型
from unsloth import FastLanguageModel
import torch
# 设置最大序列长度
max_seq_length = 2048
# 设置数据类型为 bfloat16
dtype = torch.bfloat16
# 是否加载 4bit 精度模型
load_in_4bit = True
# 加载模型和分词器
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = model_dir, # 模型路径,可以直接使用上文中的 model_dir变量,也可以用路径
max_seq_length = max_seq_length, # 这决定了模型的上下文窗口长度,比如Qwen2-7B的上下文长度为32K,并可以通过yarn拓展到128K。本文从测试的角度,设置上下文长度为2048。
dtype = dtype, # 根据A10的GPU选择torch.bfloat16(魔塔社区的机器为 A10 显卡,所以我用了这个)
load_in_4bit = load_in_4bit, # 采用 4 位量化进行微调。这样可将内存使用量减少 4 倍。4 位量化本质上将权重转换为一组有限的数字以减少内存使用量。这样做的缺点是准确度会下降 1-2%。
)
4 设置LoRA训练参数
LoRA (Low-Rank Adaptation)是一种大语言模型的低阶适配器技术,可在模型微调过程中,只更新整个模型参数的1%到10%左右,而不是全部参数。通过这种方式实现有效的模型微调和优化,提高了模型在特定任务上的性能。
model = FastLanguageModel.get_peft_model(
model,
r = 16, # 选择任何大于0的数字!建议使用8、16、32、64、128。。微调过程的rank。数值越大,占用的内存越多,速度越慢,但可以提高复杂任务的准确性。通常建议数值为 8(用于快速微调),最高可达 128。数值过大可能会导致过度拟合,从而损害模型的质量。
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",], #本文选择所有模块进行微调。您可以删除一些模块以减少内存使用量并加快训练速度,但强烈不建议这样做。
lora_alpha = 16, #微调的缩放因子。较大的数字将使微调更多地了解您的数据集,但可能会导致过度拟合。建议将其等于等级r,或将其加倍。
lora_dropout = 0, # 将其保留为 0 以加快训练速度!可以减少过度拟合,但效果不大。
bias = "none", # 将其保留为 none,以实现更快、更少的过度拟合训练!
# [NEW] "unsloth" 使用的VRAM减少30%,适用于2倍更大的批处理大小!
use_gradient_checkpointing = "unsloth", # True或"unsloth"适用于非常长的上下文。。选项包括True、False 和"unsloth"。本文建议这样做"unsloth",因为unsloth将内存使用量减少了 30%,并支持极长的上下文微调。
random_state = 3407, # 确定确定性运行的次数。训练和微调需要随机数,因此设置此数字可使实验可重复
use_rslora = False, # 支持排名稳定的LoRA,,高级功能可自动设置lora_alpha = 16。
loftq_config = None, # 高级功能可将 LoRA 矩阵初始化为权重的前 r 个奇异向量。可以在一定程度上提高准确度,但一开始会使内存使用量激增。
)
5 准备数据集
alpace 格式数据集(Alpaca 是基于 Meta 开源的 LLaMA 模型构建的一种微调数据集格式,特别用于 instruction-tuning,即指令微调。其数据格式的特点是提供了一个明确的任务描述(instruction)、输入(input)和输出(output)三部分)
[
{
"instruction": "人类指令(必填)",
"input": "人类输入(选填)",
"output": "模型回答(必填)",
"system": "系统提示词(选填)",
"history": [
["第一轮指令(选填)", "第一轮回答(选填)"],
["第二轮指令(选填)", "第二轮回答(选填)"]
]
}
]
示例:
[
{
"instruction": "基于输入的患者医案记录,直接给出你的证型诊断,无需给出原因。",
"input": "患者1年前无明显诱因反复出现胸闷心慌,劳累后加重,持续数5-6分钟,休息后可缓解,未予重视,未予治疗。1年来患者胸闷心慌不适反复发作。1周前患者无明显诱因自觉胸闷心慌较前加重,乏力,伴头痛不适,休息后症状未见明显缓解,遂今日至我院门诊就诊,为进一步治疗收入我科。入院时:患者胸闷心慌,伴头痛不适,倦怠乏力,无头晕,无胸痛放射痛,无恶心呕吐,无恶寒发热,无黒朦晕厥,无意识障碍,纳食可,二便尚调,夜寐尚可。",
"output": "气虚血瘀证"
},
{
"instruction": "基于输入的患者医案记录,直接给出你的证型诊断,无需给出原因。",
"input": "患者5年前无明显诱因出现腹泻,每日解7-8次黄色稀便,夹有少量粘液,无脓血,无明显腹痛,患者多次至我院及外院就诊,多次查肠镜示慢性结肠炎伴糜烂、结肠息肉,予调整肠道菌群、对症等治疗后症状时有好转,但停药易反复。后患者再次至我院就诊,查肠镜示直肠炎伴糜烂。予对症治疗后症状稍好转。现患者为求进一步系统诊治,遂来我院,由门诊收住入院。入院时:患者腹泻,每日解7-8次黄色稀便,夹有少量粘液,无脓血,无明显腹痛,无里急后重感,无肛门坠胀感,伴有嗳气反酸及上腹烧灼感,时有口酸,无口苦,食纳尚可,夜寐欠安,小便尚调,舌红,苔薄黄,脉弦细。",
"output": "大肠湿热证"
}
]
shareGPT 格式数据集(ShareGPT 格式来源于通过记录 ChatGPT 与用户对话的数据集,主要用于对话系统的训练。它更侧重于多轮对话数据的收集和组织,模拟用户与 AI 之间的交互)
与 alpaca 格式相比,sharegpt 格式支持更多的角色种类,如 human、gpt、observation、function 等。他们构成一个对象列表呈现在 conversations 列中。
[
{
"conversations": [
{
"from": "human",
"value": "I saw a dress that I liked. It was originally priced at $200 but it's on sale for 20% off. Can you tell me how much it will cost after the discount?"
},
{
"from": "function_call",
"value": "{\"name\": \"calculate_discount\", \"arguments\": {\"original_price\": 200, \"discount_percentage\": 20}}"
},
{
"from": "observation",
"value": "{\"discounted_price\": 160}"
},
{
"from": "gpt",
"value": "The dress will cost you $160 after the 20% discount."
}
],
"system": "系统提示词(选填)",
"tools": "[{\"name\": \"calculate_discount\", \"description\": \"Calculate the discounted price\", \"parameters\": {\"type\": \"object\", \"properties\": {\"original_price\": {\"type\": \"number\", \"description\": \"The original price of the item\"}, \"discount_percentage\": {\"type\": \"number\", \"description\": \"The percentage of discount\"}}, \"required\": [\"original_price\", \"discount_percentage\"]}}]"
}
]
function_call表示函数调用,什么是函数调用?其作用是什么?
由于大模型的数据一般都是截止于某个时间点之前的数据,不具备实时性。比如,我要问今天的天气,正常来说,由于模型参数的局限性,是不会知道的。但基于函数调用的功能,就解决了这个问题。
所谓的function_call,在某个程度来说,可以理解为API调用,这个API就是一个function,提供了某种功能。
observation表示观测结果,即function_call的执行结果。
tools表示工具,即对function_call的总结描述。
observation并不是新词汇,对于HMM模型如果有了解的话,在其模型算法的表述中,也有着observation的相关引用。
ShareGPT格式简单明了而且结构强大,不仅仅轻易的支持单轮对话、多轮对话;还引入了强大的函数调用,支持功能扩展。
示例:
{
"conversations": [
{
"from": "human",
"value": "你好,我出生于1990年5月15日。你能告诉我我今天几岁了吗?"
},
{
"from": "function_call",
"value": "{\"name\": \"calculate_age\", \"arguments\": {\"birthdate\": \"1990-05-15\"}}"
},
{
"from": "observation",
"value": "{\"age\": 31}"
},
{
"from": "gpt",
"value": "根据我的计算,你今天31岁了。"
}
],
"tools": "[{\"name\": \"calculate_age\", \"description\": \"根据出生日期计算年龄\", \"parameters\": {\"type\": \"object\", \"properties\": {\"birthdate\": {\"type\": \"string\", \"description\": \"出生日期以YYYY-MM-DD格式表示\"}}, \"required\": [\"birthdate\"]}}]"
}
{
"conversations": [
[
{
"from": "human",
"value": "Escribe un discurso que pueda recitar como padrino de la boda de mi mejor amigo."
},
{
"from": "gpt",
"value": "Queridos invitados, amigos y familiares,\n\nMe siento muy honrado de estar aquí hoy como padrino de bodas de mi mejor amigo [Nombre del novio].\n\nRecuerdo con cariño los días en los que [Nombre del novio] y yo nos conocimos, cuando éramos solo dos jóvenes llenos de sueños y esperanza. Ahora, aquí estamos, celebrando su boda con la persona que ama, [Nombre de la novia].\n\n[Nombre de la novia], te aseguro que [Nombre del novio] es una persona increíble, llena de amor y lealtad. Juntos, han formado un equipo invencible y estoy seguro de que su amor perdurará por siempre.\n\n[Nombre del novio], mi amigo, te deseo todo lo mejor en esta nueva etapa de tu vida. Espero que tu matrimonio esté lleno de amor, alegría y felicidad, y que [Nombre de la novia] sea siempre tu compañera de vida y tu mejor amiga.\n\nA [Nombre de la novia], te deseo una vida llena de amor, paz y felicidad. Espero que [Nombre del novio] sea para ti un marido cariñoso y amoroso, y que juntos encuentren la felicidad que merecen.\n\nPor último, quiero desearles a ambos mucha suerte y que Dios les bendiga en su matrimonio. Espero que esta boda sea solo el comienzo de una larga y feliz vida juntos.\n\n¡Felicidades a los novios!\n\nAtentamente,\n[Tu nombre]"
}
],
[
{
"from": "human",
"value": "Why are POSIX-shellscripts superior compared to Bash-shellscripts?"
},
{
"from": "gpt",
"value": "**POSIX-shellscripts** and **Bash-shellscripts** are both scripting languages that use the shell command-line interpreter. **POSIX** is a standard that defines the interface and behavior of many Unix-like operating systems, while **Bash** is a specific implementation of the shell that has many extensions and features beyond the POSIX specification.\n\nSome of the advantages of POSIX-shellscripts over Bash-shellscripts are:\n\n - They are more portable and can run on almost any Linux or Unix-like system, regardless of the shell implementation.\n - They are more compatible with other POSIX-compliant utilities and programs.\n - They are simpler and less prone to errors or unexpected behavior due to Bash-specific features.\n\nSome of the disadvantages of POSIX-shellscripts over Bash-shellscripts are:\n\n - They are less expressive and powerful than Bash-shellscripts, as they lack many features such as arrays, associative arrays, arithmetic expressions, extended pattern matching, etc.\n - They are less convenient and user-friendly than Bash-shellscripts, as they require more syntax and quoting rules.\n - They are less widely used and supported than Bash-shellscripts, as most GNU/Linux systems use Bash as the default shell.\n\nUltimately, the choice between POSIX-shellscripts and Bash-shellscripts depends on your needs and preferences. If you want to write scripts that are portable, compatible and simple, you may prefer POSIX-shellscripts. If you want to write scripts that are expressive, powerful and convenient, you may prefer Bash-shellscripts. However, you should also be aware of the differences and limitations of each language, and avoid using features that are not supported by the target shell."
},
{
"from": "human",
"value": "Why are POSIX-shellscripts less prone to errors than Bash-shellscripts?"
},
{
"from": "gpt",
"value": "Its not correct to state that POSIX-shellscripts are more or less prone to errors than Bash-shellscripts as either can be written with functions as well as commands that generate errors. Unlike POSIX-shellscripts, Bash-shellscripts may not be portable across unix platforms as readily as POSIX-shell scripts that provide the same purpose. POSIX-shellscripts are usually less complex and so less prone to errors than more complex Bash-shellscripts."
}
]
]
}
6 数据集处理
# 这里的是 alpaca 风格的数据集处理函数
alpaca_prompt = """下面是一项描述任务的说明,配有提供进一步背景信息的输入。写出一个适当完成请求的回应。
### Instruction:
{}
### Input:
{}
### Response:
{}"""
EOS_TOKEN = tokenizer.eos_token # Must add 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):
# Must add EOS_TOKEN, otherwise your generation will go on forever!
text = alpaca_prompt.format(instruction, input, output) + EOS_TOKEN
texts.append(text)
return { "text" : texts, }
# 从本地目录加载数据集
from datasets import load_dataset
dataset = load_dataset("json", data_files="./train.json", split = "train")
# 使用格式化函数对数据进行处理
dataset = dataset.map(formatting_prompts_func, batched = True,)
# ==================================================================================================
# 这里是 ShareGPT 风格的数据集处理函数
from unsloth.chat_templates import get_chat_template
# 获取聊天模板
tokenizer = get_chat_template(
tokenizer,
chat_template="chatml", # 支持 zephyr, chatml, mistral, llama, alpaca, vicuna, vicuna_old, unsloth 模式
mapping={
"role": "from", # 角色映射
"content": "value", # 内容映射
"user": "human", # 用户映射
"assistant": "gpt" # 助手映射
}
)
# 定义格式化函数
def formatting_prompts_func(examples):
convos = examples["conversations"]
texts = [
tokenizer.apply_chat_template(convo, tokenize=False, add_generation_prompt=False)
for convo in convos
]
return {"text": texts}
# 从 魔塔社区 中加载数据
from modelscope.msdatasets import MsDataset
dataset = MsDataset.load('OmniData/guanaco-sharegpt-style', split="train")
# 使用格式化函数对数据进行处理
dataset = dataset.map(formatting_prompts_func, batched=True)
7 训练参数配置
使用huggingface的trl库
from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported
# 使用 SFTTrainer 进行训练
trainer = SFTTrainer(
model=model, # 训练的模型(上文中的变量)
tokenizer=tokenizer, # 使用的 tokenizer (上文中的变量)
train_dataset=dataset, # 训练数据集(上文中的变量)
dataset_text_field="text", # 数据集中文本字段
max_seq_length=2048, # 最大序列长度
dataset_num_proc=2, # 使用的处理进程数
packing=False, # 是否启用数据打包(可以加速短序列的训练)
args=TrainingArguments(
per_device_train_batch_size=2, # 每个设备的训练批次大小:如果想更多地利用 GPU 的内存,请增加批处理大小。同时增加批处理大小可以使训练更加流畅,并使过程不会过度拟合。
gradient_accumulation_steps=4, # 梯度累积的步数:相当于将批量大小增加到自身之上,但不会影响内存消耗!如果您想要更平滑的训练损失曲线,通常建议增加这个值。
warmup_steps=5, # 预热步数
max_steps=60, # 最大训练步数:我们将步骤设置为 60 以加快训练速度。对于可能需要数小时的完整训练运行,请注释掉max_steps,并将其替换为num_train_epochs = 1。将其设置为 1 表示对数据集进行 1 次完整传递。通常建议传递 1 到 3 次,不要更多,否则您的微调会过度拟合。
learning_rate=2e-4, # 学习率:如果您想让微调过程变慢,但同时又最有可能收敛到更高精度的结果,请降低学习率。我们通常建议尝试 2e-4、1e-4、5e-5、2e-5 作为数字。
fp16=not is_bfloat16_supported(), # 如果不支持 bfloat16,则使用 fp16
bf16=is_bfloat16_supported(), # 如果支持 bfloat16,则使用 bfloat16
logging_steps=1, # 日志记录的步数间隔
optim="adamw_8bit", # 使用的优化器类型,支持 8-bit adamw
weight_decay=0.01, # 权重衰减(L2正则化)
lr_scheduler_type="linear", # 学习率调度器类型
seed=3407, # 随机种子
output_dir="outputs", # 输出目录(一开始我不知道这个目录具体在哪,后面发现这个相对路径为你打开python命令行的位置)
),
)
8 开始训练
trainer_stats = trainer.train()
9 数据统计
# 计算总的GPU使用内存(单位:GB)
used_memory = round(torch.cuda.max_memory_reserved() / 1024 / 1024 / 1024, 3)
# 计算LoRA模型使用的GPU内存(单位:GB)
used_memory_for_lora = round(used_memory - start_gpu_memory, 3)
# 计算总的GPU内存使用百分比
used_percentage = round(used_memory / max_memory * 100, 3)
# 计算LoRA模型的GPU内存使用百分比
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} %.")
10 推理模型(这里并不重要,我使用了第一种方式输出成功后就进行导出了,每个方法都莫名其妙失败或者成功,所以失败了就换一个)
三种推理都使用到了 model 变量与 tokenizer 变量,运用上文中加载模型的函数应该可以推理存储后的大模型。(未经过尝试)
# 这里的是 ShareGPT 风格的数据集的作者提供的
from unsloth.chat_templates import get_chat_template
# 获取用于聊天模板的tokenizer
tokenizer = get_chat_template(
tokenizer, # 传入的tokenizer
chat_template="chatml", # 支持的聊天模板类型:zephyr, chatml, mistral, llama, alpaca, vicuna, vicuna_old, unsloth
mapping={
"role": "from", # 映射角色字段
"content": "value", # 映射内容字段
"user": "human", # 用户角色
"assistant": "gpt", # 助手角色
}, # ShareGPT风格的映射
)
# 启用原生推理加速(使推理速度加快2倍)
FastLanguageModel.for_inference(model)
# 模拟人类提问
messages = [
{"from": "human", "value": "杭州的省会在哪里?"}, # 用户输入的问题
]
# 处理输入消息,生成模型输入
inputs = tokenizer.apply_chat_template(
messages, # 输入消息
tokenize=True, # 是否进行tokenize
add_generation_prompt=True, # 生成时必须添加生成提示符
return_tensors="pt", # 返回PyTorch张量
).to("cuda") # 将数据移动到GPU
# 使用模型生成输出
outputs = model.generate(
input_ids=inputs, # 输入ID
max_new_tokens=64, # 生成的最大token数
use_cache=True, # 使用缓存
)
# 解码输出的tokens
tokenizer.batch_decode(outputs) # 解码生成的输出
# ====================================================================================================
# 这里的是 ShareGPT 风格的数据集的作者提供的第二种方法(流式输出)
from unsloth.chat_templates import get_chat_template
from transformers import TextStreamer
# 获取用于聊天模板的tokenizer
tokenizer = get_chat_template(
tokenizer, # 传入的tokenizer
chat_template="chatml", # 支持的聊天模板类型:zephyr, chatml, mistral, llama, alpaca, vicuna, vicuna_old, unsloth
mapping={
"role": "from", # 映射角色字段
"content": "value", # 映射内容字段
"user": "human", # 用户角色
"assistant": "gpt", # 助手角色
}, # ShareGPT风格的映射
)
# 启用原生推理加速(使推理速度加快2倍)
FastLanguageModel.for_inference(model)
# 模拟人类提问
messages = [
{"from": "human", "value": "杭州的省会在哪里?"}, # 用户输入的问题
]
# 处理输入消息,生成模型输入
inputs = tokenizer.apply_chat_template(
messages, # 输入消息
tokenize=True, # 是否进行tokenize
add_generation_prompt=True, # 生成时必须添加生成提示符
return_tensors="pt", # 返回PyTorch张量
).to("cuda") # 将数据移动到GPU
# 创建TextStreamer对象,指定跳过提示符
text_streamer = TextStreamer(tokenizer, skip_prompt=True)
# 使用模型生成输出,使用streamer来逐步输出生成的文本
_ = model.generate(
input_ids=inputs, # 输入ID
streamer=text_streamer, # 使用streamer来控制输出流
max_new_tokens=128, # 生成的最大token数
use_cache=True, # 使用缓存
)
# ====================================================================================================
# 这里是 alpaca 风格的数据集作者提供的
FastLanguageModel.for_inference(model) # 启用原生推理速度快2倍
inputs = tokenizer(
[
alpaca_prompt.format(
"你好", # instruction
"", # input
"", # output
)
], return_tensors = "pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens = 64, use_cache = True)
tokenizer.batch_decode(outputs)
# ========================================================================================================
# GPT 给的结果
# 假设你已经正确加载了模型和tokenizer
# 定义 Alpaca prompt(确保格式化字符串中的占位符数量正确)
alpaca_prompt = "{0} # instruction {1} # input {2} # output"
# 正确格式化字符串并传入tokenizer
inputs = tokenizer(
[
alpaca_prompt.format("我肚子好疼", "", "") # 传入3个参数
], return_tensors="pt"
).to("cuda")
# 使用模型进行推理
outputs = model.generate(**inputs, max_new_tokens=64, use_cache=True)
# 解码输出
decoded_output = tokenizer.batch_decode(outputs)
print(decoded_output)
11 导出模型
将微调后的模型保存到一个名叫LoRA的100MB小文件。
加载刚保存的LoRA适配器用于推断,他将自动加载整个模型及LoRA适配器。adapter_config.json
定义了完整模型的路径。
# 保存模型到本地目录 "lora_model"
model.save_pretrained("lora_model") # 本地保存模型
# 保存tokenizer到本地目录 "lora_model"
tokenizer.save_pretrained("lora_model") # 本地保存tokenizer
保存完整模型
# 将模型和tokenizer合并为16位格式并保存到本地
model.save_pretrained_merged("models/Llama3", tokenizer, save_method = "merged_16bit",)
# 将合并后的模型和tokenizer推送到Hugging Face Hub (抱抱脸平台,这个平台是全球最大的大模型分享平台,它与魔塔社区的关系相当于GitHub与Gitee,,曾经上过央视,不过还是被墙了,很幽默)
model.push_to_hub_merged("hf/model", tokenizer, save_method = "merged_16bit", token = "")
## 将模型和tokenizer合并为4位格式并保存到本地
model.save_pretrained_merged("models/Llama3", tokenizer, save_method = "merged_4bit",)
# 将合并后的模型和tokenizer推送到Hugging Face Hub
model.push_to_hub_merged("hf/model", tokenizer, save_method = "merged_4bit", token = "")
保存GGUF格式(GGUF就是一种二进制格式文件的规范,原始的大模型预训练结果经过转换后变成GGUF格式可以更快地被载入使用,也会消耗更低的资源。原因在于GGUF采用了多种技术来保存大模型预训练结果,包括采用紧凑的二进制编码格式、优化的数据结构、内存映射等。),保存为GGUF后可以放到 ollama 平台运行,ollama 可以在没有显卡的机器上使用 CPU 推理大模型。
此处可能会有报错,因为可能还不太兼容,不过我执行过程非常顺利,遇到错误可以必应检索试试。
# 保存到 16bit GGUF 体积大
model.save_pretrained_gguf("model", tokenizer, quantization_method = "f16")
model.push_to_hub_gguf("hf/model", tokenizer, quantization_method = "f16", token = "")
# 保存到 8bit Q8_0 体积适中
model.save_pretrained_gguf("model", tokenizer,)
model.push_to_hub_gguf("hf/model", tokenizer, token = "")
# 保存到 q4_k_m GGUF 体积小
model.save_pretrained_gguf("model", tokenizer, quantization_method = "q4_k_m")
model.push_to_hub_gguf("hf/model", tokenizer, quantization_method = "q4_k_m", token = "")
12 导入到 Ollama
保存为 GGUF 格式后,编写一个Modelfile文件,即可用来创建大模型
参考官网文档 Ollama Modelfile
PARAMETER参数
我实测发现Modelfile貌似不能自己编写,我编写后大模型出现了胡乱输出的状况,可以使用以下Modelfile模版
FROM llama2
TEMPLATE """下面是一项描述任务的说明,配有提供进一步背景信息的输入。写出一个适当完成请求的回应。{{ if .Prompt }}
### Instruction:
{{.Prompt}}
{{ end }}### Response:
{{ .Response }}<|end_of_text|>"""
PARAMETER stop "<|start_header_id|>"
PARAMETER stop "<|eot_id|>"
PARAMETER stop "<|end_header_id|>"
PARAMETER stop "<|end_of_text|>"
PARAMETER stop "<|reserved_special_token_"
使用 ollama 创建模型
ollama create 模型名称 -f /mnt/workspace/model/Modelfile