- 1.什么是llama-factory?
- 2.怎么下载模型文件?
- 3.怎么准备训练数据集?
- 4.怎么使用llama-factory进行LoRA和QLoRA微调?
- 5.怎么使用训练后的模型进行推理?
- 6.模型评测中常见的报错处理手段?
- 7.怎么合并LoRA模型并导出?
- 8.怎么量化模型并导出?
- 9.怎么把safetensors格式转化为GGUF格式?
- 10.怎么安装Ollama框架?
- 11.怎么启动ollama框架?
- 12.常见的Ollama命令有哪些?
- 13.怎么大模型的UI项目?
- 14.怎么调用Llama-factory的api服务?
- 15.怎么调用ollama本身的api服务?
- 16.怎么让ollama生成兼容openai的api?
- 1.什么是embedding?
- 2.什么是Word2Vec(早期模型)?
- 3.怎么构建自己的Word2Vec?
- 4.怎么使用llamaIndex微调embedding模型?
- 5.怎么评估embedding模型?
- 6.怎么使用auto-train微调embedding模型?
- 7.怎么通过MRR评测embedding模型?
(1) 基础定义
LLaMA Factory 是一个专为大型语言模型(LLM)设计的高效、易用的训练与微调平台。其核心目标是通过简化的流程,让用户无需编写代码即可在本地完成多种模型的微调与训练任务,支持丰富的模型类型、训练方法和优化技术。
(2)核心功能
-
模型种类:LLaMA、LLaVA、Mistral、Mixtral-MoE、Qwen、Yi、Gemma、Baichuan、ChatGLM、Phi 等等。
-
训练算法:(增量)预训练、(多模态)指令监督微调、奖励模型训练、PPO 训练、DPO 训练、KTO 训练、ORPO 训练等等。
-
运算精度:16 比特全参数微调、冻结微调、LoRA 微调和基于 AQLM/AWQ/GPTQ/LLM.int8/HQQ/EETQ 的 2/3/4/5/6/8 比特 QLoRA 微调。
-
优化算法:GaLore、BAdam、DoRA、LongLoRA、LLaMA Pro、Mixture-of-Depths、LoRA+、LoftQ 和 PiSSA。
-
加速算子:FlashAttention-2 和 Unsloth。
-
推理引擎:Transformers 和 vLLM。
-
实验监控:LlamaBoard、TensorBoard、Wandb、MLflow、SwanLab 等等。
(3)LLaMA Factory安装
- 硬件环境校验:
安装显卡驱动和CUDA,并使用nvidia-smi命令检验。
- 软件环境准备:
# 创建名为 llama_factory 的 Python 3.10 虚拟环境
conda create -n llama_factory python=3.10
# 激活虚拟环境
conda activate llama_factory
# 安装 PyTorch 2.3.1 + CUDA 12.1 版本(确保显卡驱动支持 CUDA 12.1)
conda install pytorch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 pytorch cuda=12.1 -c pytorch -c nvidia拉取LLaMA Factory代码并安装。
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factorypip install -e ".[torch,metrics]"安装模型量化的所需资源。
# QLoRA
pip install https://github.com/jllllll/bitsandbytes-windows-webui/releases/download/wheels/bitsandbytes-0.41.2.post2-py3-none-win_amd64.whl
# awq量化
pip install autoawq启动LLaMA Factory:
# 命令行目录查看
llamafactory-cli train -h
# Web唤醒 or CUDA_VISIBLE_DEVICES=0 llamafactory-cli web
llamafactory-cli webui通过Hugging Face下载或者魔搭社区ModelScope下载。以Meta-Llama3-8B-Instruct为例。
# Hugging Face
git clone https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct
# Model Scope
git clone https://www.modelscope.cn/LLM-Research/Meta-Llama-3-8B-Instruct.git(1)Hugging Face更多的下载方式
# Hugging Face下载
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
model_id = "meta-llama/Meta-Llama-3-8B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.bfloat16,
device_map="auto",
)(2)魔搭社区更多的下载方式
# 魔搭社区下载
from modelscope import snapshot_download
local_dir = ""
model_dir = snapshot_download('LLM-Research/Meta-Llama-3-8B-Instruct',local_dir=local_dir)(1)更多的模型推理方式:魔搭社区,Hugging Face
import transformers
import torch
model_id = "LLM-Research/Meta-Llama-3-8B-Instruct"
# 通过模型是否能正常推理验证模型是否下载成功。
pipeline = transformers.pipeline(
"text-generation",
model=model_id,
model_kwargs={"torch_dtype": torch.bfloat16},
device_map="auto",
)
messages = [
{"role": "system", "content": "You are a pirate chatbot who always responds in pirate speak!"},
{"role": "user", "content": "Who are you?"},
]
terminators = [
pipeline.tokenizer.eos_token_id,
pipeline.tokenizer.convert_tokens_to_ids("<|eot_id|>")
]
outputs = pipeline(
messages,
max_new_tokens=256,
eos_token_id=terminators,
do_sample=True,
temperature=0.6,
top_p=0.9,
)
print(outputs[0]["generated_text"][-1])(1)(增量)预训练数据集
# Alpaca格式
[
{"text": "预训练文本"},
{"text": "预训练文本"}
](2)监督微调数据集
- Alpaca格式:在模型微调时,instruction对应的内容会与input对应的内容拼接后作为人类指令,而output对应的内容作为模型回答。如有,system对应的内容为系统提示词,而history分别代表历史消息中每轮对话的指令和回答。
# Alpaca格式
[
{"instruction": "人类指令(必填)",
"input": "人类输入(必填)",
"output": "模型回答(必填)",
"system": "系统提示词(选填)",
"history": [
["第一轮指令(选填)", "第一轮回答(选填)"],
["第二轮指令(选填)", "第二轮回答(选填)"]]
}
]- sharegpt格式:sharegpt格式支持更多的角色种类,比如human,gpt,observation,function等。其中human和observation必须出现在奇数位置,gpt和function必须出现在偶数位置。
[
{
"conversations":[
{
"from": "human",
"value": "人类指令",
},
{
"from": "function_call",
"value": "工具参数",
},
{
"from": "observation",
"value": "工具结果",
},
{
"from": "gpt",
"value": "模型回答",
},
],
"system": "系统提示词(选填)",
"tools": "工具描述(选填)"
}
](3)偏好数据
- Alpaca格式:
[
{
"instruction": "人类指令(必填)",
"input": "人类输入(选填)",
"chosen": "优质回答(必填)",
"rejected": "劣质回答(必填)"
}
]- sharegpt格式
{
"conversations": [
{
"from": "human",
"value": "人类指令"
}
],
"chosen": {
"from": "gpt",
"value": "模型回答!"
},
"rejected": {
"from": "gpt",
"value": "模型回答"
}
}LLaMA Factory中的文件中包含了所有可用的数据集。如果使用自定义数据集,需要在dataset_info.json文件中添加数据集的描述。 dataset_info.json文件位于LLaMA Factory根目录的data文件下,即LLaMA-Factory\data。
(1)(增量)预训练数据集
"数据集名称": {
"file_name": "data.json",
"columns": {
"prompt": "text"
}
}(2)监督微调数据集
- Alpaca格式:
"数据集名称": {
"file_name": "data.json",
"columns": {
"prompt": "instruction",
"query": "input",
"response": "output",
"system": "system",
"history": "history"
}
}- sharegpt格式
"数据集名称": {
"file_name": "data.json",
"formatting": "sharegpt",
"columns": {
"messages": "conversations",
"system": "system",
"tools": "tools"
}
}(3)偏好微调
- Alpaca格式:
"数据集名称": {
"file_name": "data.json",
"ranking": true,
"columns": {
"prompt": "instruction",
"query": "input",
"chosen": "chosen",
"rejected": "rejected"
}
}- sharegpt格式
"数据集名称": {
"file_name": "data.json",
"formatting": "sharegpt",
"ranking": true,
"columns": {
"messages": "conversations",
"chosen": "chosen",
"rejected": "rejected"
}
}一般只需要修改数据集名称和file_name,其他参数为默认,可以写明。更多的训练数据格式和配置文件
LoRA:训练参数大约占完全参数的1%,非破坏性微调,可以任意切换或者组合。
QLoRA:相较于LoRA节省30%的训练内存,但多约30%的训练时长。
llamafactory-cli train \
--stage sft \ # 训练阶段/策略,可选:rm,pt,sft,PPO,DPO,KTO...
--do_train True \ # True用于训练,False用于评测
--model_name_or_path baichuan-inc/<model_name> \ # 模型名称/路径
--preprocessing_num_workers 16 \ # 用于数据预处理的工作线程数
--finetuning_type lora \ # 微调方法,可选:freeze,LoRA,full
--template <model_name> \ # 数据集模板,与模型一一对应
--flash_attn auto \
--dataset_dir llama-factory/data/<dataset_folder> \ # 数据集所在目录
--dataset <dataset_name> \ # 数据集名称,多个数据集用","隔开
--cutoff_len 1024 \ # 截断长度,表示输入序列的最大长度
--learning_rate 5e-05 \ # 学习率
--num_train_epochs 3.0 \ # 训练周期数
--max_samples 100000 \ # 用于训练的最大样本数
--per_device_train_batch_size 2 \ # 每个设备上训练的批次大小
--gradient_accumulation_steps 8 \ # 梯度累计步数
--lr_scheduler_type cosine \ # 学习率调度曲线,可选“linear”,“consince”...
--max_grad_norm 1.0 \
--logging_steps 5 \ # 日志保存步数
--save_steps 100 \
--warmup_steps 0 \
--optim adamw_torch \ # 优化器
--packing False \ # 是否启动数据打包
--report_to none \
--output_dir saves/<model_name>/lora/train \ # 保存路径
--fp16 True \ # True使用fp16
--plot_loss True \ # 是否绘制损失图
--ddp_timeout 180000000 \
--include_num_input_tokens_seen True \
--lora_rank 8 \ # LoRA的秩
--lora_alpha 16 \ # LoRA的alpha值,值越大,新数据对权重的影响越大。
--lora_dropout 0 \ # LoRA的dropout率,防止模型过拟合。
--lora_target all \ # 采用LoRA的目标模块,一般为all
--deepspeed cache/ds_z3_config.json # 使用deepspeed加速训练只有命令行格式
# 如果通过命令行控制,则模型为继续训练,训练集为从头训练(一般适配新数据叠加训练)
--resume_from_checkpoint /workspace/checkpoint/<model_name>/checkpoint-4000 # 不设置,脚本会自动到workspace里面寻找最新的checkpoint
--output_dir new_dir # 不设置,默认原来的output_dir
# 其他参数同训练训练结束后,在output_dir下,
(1)adaptor开头的就是LoRA保存的结果(主要结果)。
(2)training_loss和training_log等记录了训练的过程。
(3)其他是训练的其他备份。
(1)0-shot评测是指在没有任何针对特定任务的训练或者实例的情况下,直接评估模型在任务上的表现。
(2)5-shot评测是指在prompt中为模型提供几个示例,以增强模型的泛化能力。
(1)主流benchmark测评
llama-factory可以通过yaml文件和命令流两种方式进行测评
### model
model_name_or_path: meta-llama/Meta-Llama-3-8B-Instruct
adapter_name_or_path: saves/llama3-8b/lora/sft
trust_remote_code: true
### method
finetuning_type: lora
### dataset
task: mmlu_test # choices: [mmlu_test, ceval_validation, cmmlu_test]
template: fewshot
lang: en
n_shot: 5
### output
save_dir: saves/llama3-8b/lora/eval
### eval
batch_size: 4# YAML命令流
llamafactory-cli evalexamples/train_lora/llama3_lora_eval.yaml- 命令流
# chat模型
llamafactory-cli eval \ # eval表示评测 CUDA_VISIBLE_DEVICES=0
--model_name_or_path /llama3/Meta-Llama-3-8B-Instruct \ # 基础模型路径
--template llama3 \ # 提示词模版
--task mmlu_test \ # 评测任务集
--lang en \ # 语言
--n_shot 5 \ # 0shot,5shot等
--batch_size 1 # 评测是的batch size
# base模型
llamafactory-cli eval \ # eval表示评测 CUDA_VISIBLE_DEVICES=0
--model_name_or_path /llama3/Meta-Llama-3-8B-Instruct \ # 基础模型路径
--template fewshot \ # 提示词模版
--task mmlu_test \ # 评测任务集
--split validation \
--lang en \ # 语言
--n_shot 5 \ # 0shot,5shot等
--batch_size 1 # 评测是的batch size- 其他更多的开源评测项目:opencompass,EleutherAI
(2)垂直数据集测评
- 环境安装
pip install jieba #中文文本分词库
pip install rouge-chinesepip install nltk #自然语言处理工具包(Natural Language Toolkit)- 批量推理
llamafactory-cli train \ # CUDA_VISIBLE_DEVICES=0
--stage sft \ # 监督微调
--do_predict \ # 现在是预测模式
--model_name_or_path /llama3/Meta-Llama-3-8B-Instruct \ # 底模路径
--adapter_name_or_path ./saves/LLaMA3-8B/lora/sft \ # lora路径
--eval_dataset alpaca_gpt4_zh,identity,adgen_local \ # 评测数据集
--dataset_dir ./data \ # 数据集路径
--template llama3 \ # 提示词模版,比如llama3 ,qwen 和训练微调一样
--finetuning_type lora \ # 微调方式 lora
--output_dir ./saves/LLaMA3-8B/lora/predict \ # 评估预测输出文件夹
--overwrite_cache \
--overwrite_output_dir \
--cutoff_len 1024 \ # 提示词截断长度
--preprocessing_num_workers 16 \ # 预处理数据的线程数量
--per_device_eval_batch_size 1 \ # 每个设备评估时的batch size
--max_samples 20 \ # 每个数据集采样多少用于预测对比
--predict_with_generate True # 现在用于生成文本- 批量评测
# 采用blue和rouge分数进行评测
llamafactory-cli train \
--stage sft \
--model_name_or_path Qwen/Qwen2-7B-Instruct-AWQ \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--quantization_method bitsandbytes \ # 量化
--template qwen \
--flash_attn auto \
--dataset_dir data \
--eval_dataset <evaluation dataset> \
--cutoff_len 1024 \ # 提示词截断长度
--max_samples 100000 \
--per_device_eval_batch_size 2 \
--predict_with_generate True \
--max_new_tokens 512 \
--top_p 0.7 \
--temperature 0.95 \
--output_dir saves\Qwen2-7B-int4-Chat\lora\eval_2024-08-24-10-42-52 \
--do_predict True \
--adapter_name_or_path saves\Qwen2-7B-int4-Chat\lora\train_2024-08-18-14-43-59 \
--quantization_bit 4- 测评后结果
在output_dir下,存在:
all_results.json # 评测结果
generated_predictions.jsonl # 输入-输出
llamaboard_config.yaml # 评测的参数配置
trainer_log.jsonl # 训练日志
training_args.yaml # 训练的参数配置设置--eval_accumulation_steps=1(累计梯度)以及--per_device_eval_batch_size=1(批量大小)
比如F1score,recall,precision。需要在llama-factory根目录下,修改src\llamafactory\train\sft\metric.py和src\llamafactory\train\sft\workflow.py文件。
每个样本分开评测,一起评测得到的是平均分数。
检查提示词模板是否是对应的模板,如果仍然有问题选择default模板再次尝试。
如果想把训练的LoRA和原始大模型进行融合并输出一个完整的模型文件,可以使用以下命令。
llamafactory-cli export \ # CUDA_VISIBLE_DEVICES=0是一个环境变量,设定使用第一张GPU卡
--model_name_or_path /llama3/Meta-Llama-3-8B-Instruct \ # 底模路径
--adapter_name_or_path ./saves/LLaMA3-8B/lora/sft \ # lora路径
--template llama3 \ # 提示词模版
--finetuning_type lora \ # 微调方式lora
--export_dir megred-model-path \ # 导出路径
--export_size 2 \ # 导出每个分文件大小,2表示2G,比如一个4B权重的float16的模型,模型权重8G,就会分成4个文件保存
--export_device cpu \ # 使用cpu导出模型,而非模型后面是用cpu还是gpu运行
--export_legacy_format False # 是否使用旧格式导出,新格式默认是safetensors,旧格式就是pt,bin等格式- 其他可选参数
--export_quantization_bit: 量化位数,全精度导出时,不用填写
--export_quantization_dataset: 量化校准数据集(参见Q7)“模型路径”,“检查点路径”,“最大分块大小”,“导出目录”设置成功后开始导出。注意:**”导出量化等级“**设置为None,否则无法导出。
”模型路径“:改成合并后的模型路径;
”检查点路径“:取消选择;
”导出量化等级“:设置成需要的量化等级;
”导出路径“:新的保存路径。
Ollama框架可以帮助用户快速地使用本地的大语言模型,但只支持GGUF文件格式。其中,GGUF是llama cpp设计的大模型储存格式,可以对模型进行高效的压缩。
直接 pip 安装GGUF不是最新的版本,可能导致和最新的转换脚本会不兼容。建议直接从源码安装llama.cpp
# conda创建python=3.10版本的虚拟环境
conda create -n llama_cpp python=3.10
# 激活llama_cpp虚拟环境
conda activate llama_cpp
# torch和cuda安装等等(注意版本问题)
conda install pytorch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 pytorch-cuda=12.1 -c pytorch -c nvidia
# 拉取代码
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpppip install --editable .返回llama.cpp项目根目录,会有一个官方提供的convert-hf-to-gguf.py脚本,用于完成huggingface格式(safetensors)到gguf格式的转换。
python convert_hf_to_gguf.py <需要转换的模型所在的路径> # 需要转换的模型路径
# 转化后的模型保存在同一路径下。我们以ollama为例,下载地址:https://ollama.com/
ollama是go语言开发的开源项目,github地址:https://github.com/ollama/ollama
ollama文档参考:https://github.com/ollama/ollama/tree/main/docs
ollama支持的是GGUF文件格式,如果是其他文件格式需要转换成GGUF文件格式
# 注意驱动程序已经安装完成!
# 方法1:使用官方安装脚本(推荐)
curl -fsSL https://ollama.com/install.sh | sh
# curl -fsSL:安全下载脚本(-f 失败时静默,-s 静默模式,-S 显示错误,-L 跟随重定向);管道 | sh:直接执行下载的安装脚本
# 方法2:手动安装
sudo curl -L https://ollama.com/download/ollama-linux-amd64 -o /usr/bin/ollama
sudo chmod +x /usr/bin/ollama
#下载二进制文件到系统路径 /usr/bin/ollama
# 添加可执行权限
# 创建专用系统用户
sudo useradd -r -s /bin/false -m -d /usr/share/ollama ollama
# 创建 systemd 服务文件
cat > /etc/systemd/system/ollama.service <<EOF
[Unit]
Description=Ollama Service
After=network-online.target # 确保网络就绪后启动
[Service]
ExecStart=/usr/bin/ollama serve # 启动命令
User=ollama # 指定运行用户
Group=ollama # 指定用户组
Restart=always # 崩溃时自动重启
RestartSec=3 # 重启等待时间
[Install]
WantedBy=default.target # 关联到默认启动组
EOF
# 启动服务
sudo systemctl daemon-reload # 重新加载服务配置
sudo systemctl enable ollama # 设置开机自启
sudo systemctl start ollama # 立即启动服务
# 移除 ollama 服务
sudo systemctl stop ollama # 停止服务
sudo systemctl disable ollama # 禁用开机启动
sudo rm /etc/systemd/system/ollama.service # 删除服务文件不再需要Windows Subsystem for Linux!Ollama可以直接通过下载地址进行安装。
推荐系统的配置应包括windows 10及其以上版本,NVIDIA/AMD Radeon驱动程序。
# 下载好的ollama文件目录
# 程序文件目录
C:\Users\Administrator\AppData\Local\Programs\Ollama
#日志文件夹
C:\Users\Administrator\AppData\Local\Ollama
# 模型和数据文件夹,可以通过设置OLLAMA_MODELS环境变量更改
C:\Users\Administrator\.ollama安装 Ollama Windows 预览版后,Ollama 将在后台运行, ollama 命令行可以在 cmd/powershell 等终端中使用。
# 可以设置的环境变量:模型目录和ollama api服务的url
OLLAMA_MODELS <model folder> # 可以自定义模型目录
OLLAMA_BASE_URL http://127.0.0.1:11434 # 可以自定义端口,方便api调用# 启动ollama服务
./ollama serve
# 指定模型的版本。如果模型没有下载,会自动下OLLAMA_MODELS目录中。
./ollama run gemma2:2bOllama支持的模型列表:https://ollama.com/library
llama pull <model name> # 下载模型
ollama rm <model name> # 删除模型
ollama show <model name> # 显示模型信息
ollama list # 列出下载好的模型(1)llama支持的是GGUF文件格式,如果是其他文件格式需要转换成GGUF文件格式
(2)构建ModelFile文件(不同的模型建议搜索对应的ModelFile文件参照)
FROM llama3.1 # 官方支持的模型
# FROM <模型路径> # 必要参数:自定义支持的模型,路径加模型文件名
#非必要参数:温度参数,参数越大,分布曲线压的越平
PARAMETER temperature 1
# 非必要参数:设置系统提示词
SYSTEM """You are Mario from Super Mario Bros. Answer as Mario, the assistant, only."""(3)注册模型
ollama create <模型名称> -f <ModelFile文件的路径> # -f 后面接ModelFile文件的路径(4) 命令聊天示例
ollama run <模型名称> # 运行模型就可以输入命令
>>> <聊天命令>- Open-webui项目:方便本地部署大模型并且带UI界面,可以管理文档资料,也可以作为聊天AI使用。项目下载地址:https://github.com/open-webui/open-webui
- 访问连接后,可以通过设置更改模型和下载模型
训练好的模型想通过api调用,接入到langchain或者其他下游业务中,可以采用llama-factory的api服务。该服务采用OpenAI的标准,基于uvicorn服务框架开发。
CUDA_VISIBLE_DEVICES=0 API_PORT=8000 llamafactory-cli api \
--model_name_or_path /llama3/Meta-Llama-3-8B-Instruct \
--adapter_name_or_path ./saves/LLaMA3-8B/lora/sft \
--template llama3 \
--finetuning_type lora如果要加速推理可以采用vllm推理后端,不过vllm只支持linux系统,不支持windows系统。如果使用vllm推理框架,需要提前将LoRA模型进行merge,使用融合后的完整模型或者训练前的基座模型。
CUDA_VISIBLE_DEVICES=0 API_PORT=8000 llamafactory-cli api \
--model_name_or_path <模型目录> \ # 修改为融合后模型或者基座模型的目录
--template llama3 \
--infer_backend vllm \ # 新添加命令
--vllm_enforce_eager # 新添加命令服务启动后,按照openai 的API 进行远程访问(替换其中的base_url,指向所部署的机器url和端口号)。
# 基本调用代码
import os
from openai import OpenAI
from transformers.utils.versions import require_version
if __name__ == '__main__':
# 更换端口号
port = 8000
client = OpenAI(
api_key="0",
base_url="http://localhost:{}/v1".format(os.environ.get("API_PORT",8000)),
)
messages = []
messages.append({"role": "user", "content": "hello!"})
result = client.chat.completions.create(
messages=messages,
model="test"
)
print(result.choices[0].message)启动ollama服务后,可以通过api调用来进行推理。 api url可以通过环境变量OLLAMA_BASE_URL来指定,也可以直接使用默认 http://127.0.0.1:11434
该api端口支持在终端流式生成和非流式生成,详细api文档参见官网
(1)Generation complication:POST /api/generate
# 流式生成
curl http://localhost:11434/api/generate -d '{
"model": "llama3.2",
"prompt": "Why is the sky blue?"
}'
# 输出
{
"model": "llama3.2",
"created_at": "2023-08-04T08:52:19.385406455-07:00",
"response": "The",
"done": false
}# 非流式生成
curl http://localhost:11434/api/generate -d '{
"model": "llama3.2",
"prompt": "Why is the sky blue?",
"stream": false
}'
# 输出
{
"model": "llama3.2",
"created_at": "2023-08-04T19:22:45.499127Z",
"response": "The sky is blue because it is the color of the sky.",
"done": true,
"context": [1, 2, 3],
"total_duration": 5043500667,
"load_duration": 5025959,
"prompt_eval_count": 26,
"prompt_eval_duration": 325953000,
"eval_count": 290,
"eval_duration": 4709213000
}(2) Chat:POST /api/chat
# 流式生成
curl http://localhost:11434/api/chat -d '{
"model": "llama3.2",
"messages": [
{
"role": "user",
"content": "why is the sky blue?"
}
]
}'# 非流式生成
curl http://localhost:11434/api/chat -d '{
"model": "llama3.2",
"messages": [
{
"role": "user",
"content": "why is the sky blue?"
}
],
"stream": false
}'大多项目需要采用openai兼容的api进行搭建,因此ollama框架也开发了对应的api服务。只需要在原有python代码上更换模型和api的url地址即可。详情参加官网,以下是一个简单的案例
from openai import OpenAI
client = OpenAI(
base_url='http://localhost:11434/v1/',
# 必要参数,但是不会有用
api_key='ollama',
)
chat_completion = client.chat.completions.create(
messages=[
{
'role': 'user',
'content': 'Say this is a test',
}
],
model='llama3.2',
)
response = client.chat.completions.create(
model="llava",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "What's in this image?"},
{
"type": "image_url",
"image_url": "data:image/png;base64,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",
},
],
}
],
max_tokens=300,
)
completion = client.completions.create(
model="llama3.2",
prompt="Say this is a test",
)
list_completion = client.models.list()
model = client.models.retrieve("llama3.2")
embeddings = client.embeddings.create(
model="all-minilm",
input=["why is the sky blue?", "why is the grass green?"],
)Embedding(嵌入层),是一种将现实世界中的对象(如文本、图像等)映射为低维稠密向量的数学过程。
-
是一种低维稠密的向量化表示,能够高效表达大量信息。
-
相较于传统one-hot编码,具有更高的信息密度和更低的维度。
-
可将文字、图片、语音等现实数据转化为计算机可处理且信息不丢失的向量形式。
-
Embedding作为降维与升维工具,比如低维的数据过于笼统,通过升维实现特征分离;
-
表征非结构化数据在向量空间的位置关系,比如语义相近的词在向量空间中距离更近。
-
CBOW (Continuous bag of words): 根据上下文预测中心词,相当于做完形填空;
-
Skip-gram:根据中心词预测上下文
-
CBOW模型
(1) 输入构造示例 :上下文长度为4,句子为 "by a bus in <>",目标是预测中间词red;
(2)数据集格式输入:[by, a, bus, in], 标签:red;
(3)网络结构:输入层:V维(词汇表大小),隐藏层:N维(embedding维度,输出层:V维;
(4)输入处理方式:多个上下文词的one-hot向量分别乘以共享权重矩阵$W$($V \times N$),得到多个
$1 \times N$ 向量;(5)隐藏层计算:将多个上下文向量求和或求平均,得到最终隐藏层向量
$h$ ;(6)输出层计算: 隐藏层向量
$h$ 乘以输出权重矩阵$W'$ ($N \times V$),得$1 \times V$ 输出向量;(7)Softmax激活: 输出向量经Softmax函数转化为概率分布;
(8)损失函数:使用交叉熵损失函数比较预测分布与真实one-hot标签;
(9) 通过反向传播更新
$W$ $W'$ -
Skip-gram模型 (1)数据集构造方式:例如中心词为 note,上下文为 [short, salt, make, a],则生成四组样本:输入note,输出short,salt,make,a;
(2)与CBOW的关键区别:输入仅为单个中心词的one-hot向量,无需对多个向量进行求和或平均。
(3)前向传播过程:输入
$x$ ($1 \times V$)乘以输入权重矩阵$W$ ($V \times N$),得隐藏层$h = xW$ ($1 \times N$)。隐藏层$h$ 乘以输出权重矩阵$W'$ ($N \times V$),得输出$o = hW'$ ($1 \times V$)。输出经Softmax归一化为概率分布。
import torch
import numpy as np
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
import torch.utils.data as Data
# 设置数据类型和设备
dtype = torch.FloatTensor
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 模型相关参数
batch_size = 8
embedding_size = 2 # 词向量的维度
C = 2 # 上下文窗口大小
def prepare_data():
# 文本预处理
sentences = ["longge like dog", "longge like cat", "longge like animal",
"dog cat animal", "banana apple cat dog like", "dog fish milk like",
"dog cat animal like", "longge like apple", "apple like", "longge like banana",
"apple banana longge movie book music like", "cat dog hate", "cat dog like"]
# 将句子连接并分割成单词列表
word_sequence = " ".join(sentences).split() # ['longge', 'like', 'dog', 'longge', 'like', 'cat', 'animal',...]
# 构建词汇表(去重)
vocab = list(set(word_sequence))
word2idx = {w: i for i, w in enumerate(vocab)} # 单词到索引的映射
voc_size = len(vocab) # 词汇表大小
# 生成Skip-gram训练数据
skip_grams = []
print(word2idx) # 打印词汇映射
# 遍历文本序列,为每个中心词生成训练样本
for idx in range(C, len(word_sequence) - C):
center = word2idx[word_sequence[idx]] # 中心词的索引
# 上下文词的索引(左边C个词 + 右边C个词)
context_idx = list(range(idx - C, idx)) + list(range(idx + 1, idx + C + 1))
context = [word2idx[word_sequence[i]] for i in context_idx] # 上下文词的索引列表
# 为每个上下文词创建训练样本(中心词,上下文词)
for w in context:
skip_grams.append([center, w])
def make_data(skip_grams):
"""将Skip-gram数据转换为模型输入格式"""
input_data = []
output_data = []
for i in range(len(skip_grams)):
# 输入数据:中心词的one-hot编码
input_data.append(np.eye(voc_size)[skip_grams[i][0]])
# 输出数据:上下文词的索引
output_data.append(skip_grams[i][1])
return input_data, output_data
print(skip_grams) # 打印生成的训练样本
input_data, output_data = make_data(skip_grams)
print(input_data) # 打印输入数据
print(output_data) # 打印输出数据
# 转换为PyTorch张量
input_data, output_data = torch.Tensor(input_data), torch.LongTensor(output_data)
# 创建数据集和数据加载器
dataset = Data.TensorDataset(input_data, output_data)
loader = Data.DataLoader(dataset, batch_size, True)
return vocab, voc_size, loader
# 定义Word2Vec模型
class Word2Vec(nn.Module):
def __init__(self):
super(Word2Vec, self).__init__()
# 输入权重矩阵:词汇表大小 × 词向量维度
self.W_in = nn.Parameter(torch.randn(voc_size, embedding_size).type(dtype))
# 输出权重矩阵:词向量维度 × 词汇表大小
self.W_out = nn.Parameter(torch.randn(embedding_size, voc_size).type(dtype))
def forward(self, X):
# X : [batch_size, voc_size] one-hot编码的输入
# 隐藏层:输入与W_in相乘得到词向量 [batch_size, embedding_size]
hidden_layer = torch.matmul(X, self.W_in)
# 输出层:隐藏层与W_out相乘得到预测输出 [batch_size, voc_size]
output_layer = torch.matmul(hidden_layer, self.W_out)
return output_layer
def train_model():
# 初始化模型并移动到设备
model = Word2Vec().to(device)
# 使用交叉熵损失函数
criterion = nn.CrossEntropyLoss().to(device)
# 使用Adam优化器
optimizer = optim.Adam(model.parameters(), lr=1e-4)
# 训练循环
for epoch in range(2000):
for i, (batch_x, batch_y) in enumerate(loader):
batch_x = batch_x.to(device)
batch_y = batch_y.to(device)
# 前向传播
pred = model(batch_x)
# 计算损失
loss = criterion(pred, batch_y)
# 每1000个epoch打印损失
if (epoch + 1) % 1000 == 0:
print(epoch + 1, i, loss.item())
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 可视化词向量
# 获取模型参数
for i, label in enumerate(vocab):
W, WT = model.parameters()
# 获取每个词的词向量坐标
x, y = float(W[i][0]), float(W[i][1])
# 绘制散点图
plt.scatter(x, y)
# 添加文本标注
plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', ha='right', va='bottom')
plt.show()
# 主程序流程
# 准备数据
vocab, voc_size, loader = prepare_data()
# 开始训练
train_model()- 现在的embedding模型大都基于transformer架构构造,具有很强的表征能力。可以采用已有框架对现有模型进行训练。
# 使用conda创建一个名为'rag_embedding'的Python虚拟环境,并指定Python版本为3.10
# 使用虚拟环境可以隔离项目依赖,避免包版本冲突[citation:1]
conda create -n rag_embedding python=3.10
# 激活刚刚创建的'rag_embedding'虚拟环境
# 激活后,后续的安装命令只会影响当前环境[citation:1]
conda activate rag_embedding
# 安装PyTorch框架及其相关扩展库
# 安装特定版本(2.3.1)以确保兼容性,并指定对应的CUDA工具包版本(12.1)以支持GPU加速[citation:2]
conda install pytorch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 pytorch-cuda=12.1 -c pytorch -c nvidia
# 以下命令用于安装LlamaIndex及其相关组件,这些组件是构建RAG系统的核心[citation:5]
# 安装用于连接和调用OpenAI大语言模型(如GPT系列)的LlamaIndex集成包
# 如果你的微调数据需要通过OpenAI的API生成,或者你计划使用OpenAI的模型,就需要安装这个包[citation:3]
pip install llama-index-llms-openai
# 安装用于使用OpenAI的文本嵌入模型(如text-embedding-ada-002)的LlamaIndex集成包
# 嵌入模型负责将文本转换为向量,是语义检索的关键[citation:10]
pip install llama-index-embeddings-openai
# 安装用于连接本地部署的Ollama模型的LlamaIndex集成包
# Ollama允许你在本地运行多种开源大模型(如Llama、Mistral等)[citation:4]
pip install llama-index-llms-ollama
# 安装LlamaIndex的微调工具包
# 这个包提供了工具,允许你根据自己的数据对模型进行微调[citation:8]
pip install llama-index-finetuning
# 安装用于使用HuggingFace上的开源嵌入模型的LlamaIndex集成包
# 如果你希望使用免费、本地的嵌入模型(如sentence-transformers系列),就需要安装这个包[citation:5][citation:10]
pip install llama-index-embeddings-huggingface
# 安装或升级核心的LlamaIndex包至最新版本
# `-U`参数确保会升级到可用的最新版本。LlamaIndex是构建RAG系统的主框架[citation:5]
pip install -U llama-indeximport os
import json
from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.schema import MetadataMode
from llama_index.llms.openai import OpenAI
from llama_index.llms.ollama import Ollama
from llama_index.finetuning import generate_qa_embedding_pairs
from llama_index.core.evaluation import EmbeddingQAFinetuneDataset
from llama_index.finetuning import SentenceTransformersFinetuneEngine
from llama_index.core.evaluation import EmbeddingQAFinetuneDataset
# 定义基础目录和文件路径
BASE_DIR = ""
TRAIN_FILES = [BASE_DIR + "xxxx.pdf"] # 训练文件路径
VAL_FILES = [BASE_DIR + "xxxx.pdf"] # 验证文件路径
TRAIN_CORPUS_FPATH = BASE_DIR + "train_corpus.json" # 训练数据集保存路径
VAL_CORPUS_FPATH = BASE_DIR + "val_corpus.json" # 验证数据集保存路径
def load_corpus(files, verbose=False):
"""
加载语料库并解析为节点
参数:
files: 文件路径列表
verbose: 是否显示详细日志
返回:
nodes: 解析后的文本节点列表
"""
if verbose:
print(f"Loading files {files}")
# 使用SimpleDirectoryReader读取指定文件
reader = SimpleDirectoryReader(input_files=files)
docs = reader.load_data()
if verbose:
print(f"Loaded {len(docs)} docs")
# 使用句子分割器将文档拆分为更小的节点(块)
parser = SentenceSplitter()
nodes = parser.get_nodes_from_documents(docs, show_progress=verbose)
if verbose:
print(f"Parsed {len(nodes)} nodes")
return nodes
def mk_dataset():
"""
创建微调数据集
主要步骤:
1. 加载训练和验证语料
2. 使用本地Ollama模型生成问答对
3. 保存生成的数据集为JSON文件
"""
# 加载训练和验证语料节点
train_nodes = load_corpus(TRAIN_FILES, verbose=True)
val_nodes = load_corpus(VAL_FILES, verbose=True)
# 初始化本地Ollama模型
# 使用qwen2:7b-instruct-q4_0模型,设置较长的请求超时时间
ollm = Ollama(model="qwen2:7b-instruct-q4_0", request_timeout=120.0)
# 为训练节点生成问答对
# 这个函数会使用LLM为每个文本节点生成相关的问题
train_dataset = generate_qa_embedding_pairs(
llm=ollm, # 使用的语言模型
nodes=train_nodes # 训练节点
)
# 为验证节点生成问答对
val_dataset = generate_qa_embedding_pairs(
llm=ollm, # 使用的语言模型
nodes=val_nodes # 验证节点
)
# 将生成的数据集保存为JSON文件
train_dataset.save_json(TRAIN_CORPUS_FPATH)
val_dataset.save_json(VAL_CORPUS_FPATH)
# 执行数据集创建函数
mk_dataset()from llama_index.finetuning import SentenceTransformersFinetuneEngine
from llama_index.core.evaluation import EmbeddingQAFinetuneDataset
def finetune_embedding_model():
"""
微调嵌入模型的主函数
主要步骤:
1. 加载训练和验证数据集
2. 初始化微调引擎
3. 执行模型微调
4. 获取并返回微调后的模型
"""
# 1. 从JSON文件加载训练和验证数据集
# 这些数据集应该包含之前生成的问答对
train_dataset = EmbeddingQAFinetuneDataset.from_json(TRAIN_CORPUS_FPATH)
val_dataset = EmbeddingQAFinetuneDataset.from_json(VAL_CORPUS_FPATH)
# 2. 初始化句子转换器微调引擎
finetune_engine = SentenceTransformersFinetuneEngine(
train_dataset, # 训练数据集,包含问答对
model_id="BAAI/bge-small-zh-v1.5", # 基础预训练模型
# 使用BAAI开源的bge-small-zh-v1.5中文嵌入模型作为起点
# 这是一个轻量级的中文文本嵌入模型
model_output_path="zhengquan", # 微调后模型的保存路径
# 微调完成的模型将保存在当前目录下的"zhengquan"文件夹中
val_dataset=val_dataset, # 验证数据集,用于评估微调效果
# 在训练过程中会定期在验证集上评估模型性能
)
# 3. 执行模型微调
finetune_engine.finetune() # 开始微调过程
# 这个过程会根据训练数据调整模型参数,使其更适合特定领域(证券法)
# 微调时间取决于数据量、模型大小和硬件性能
# 4. 获取微调后的嵌入模型
embed_model = finetune_engine.get_finetuned_model()
# 返回微调后的模型实例,可以直接用于文本嵌入任务
# 打印模型信息(可选)
print(embed_model)
# 通常会显示模型的基本信息和配置
# 返回微调后的模型,便于后续使用
return embed_model
# 执行微调函数
finetune_embedding_model()- 评估标准:使用lama index自带的retrieval evaluation框架和命中率(Top_k检索到为1,没有检索到就是0)来评估模型的好坏。
from llama_index.core import VectorStoreIndex
from llama_index.core.schema import TextNode
from tqdm.notebook import tqdm # 用于显示进度条
import pandas as pd
def evaluate(
dataset,
embed_model,
top_k=5,
verbose=False,
):
"""
评估嵌入模型的检索性能
参数:
dataset: 包含语料、查询和相关文档的数据集
embed_model: 要评估的嵌入模型
top_k: 检索时返回的顶部K个结果
verbose: 是否显示详细日志
返回:
eval_results: 包含每个查询评估结果的列表
"""
# 从数据集中提取三个核心组件
corpus = dataset.corpus # 语料库:文档ID到文档内容的映射
queries = dataset.queries # 查询集:查询ID到查询文本的映射
relevant_docs = dataset.relevant_docs # 相关文档:查询ID到相关文档ID列表的映射
# 将语料库转换为TextNode对象,用于构建向量索引
# 每个节点包含文档ID和文本内容
nodes = [TextNode(id_=id_, text=text) for id_, text in corpus.items()]
# 使用指定的嵌入模型构建向量存储索引
# show_progress=True 会显示索引构建的进度条
index = VectorStoreIndex(
nodes,
embed_model=embed_model,
show_progress=True
)
# 创建检索器,设置返回最相似的top_k个结果
retriever = index.as_retriever(similarity_top_k=top_k)
# 存储所有查询的评估结果
eval_results = []
# 遍历所有查询,使用tqdm显示进度条
for query_id, query in tqdm(queries.items()):
# 使用检索器对当前查询进行检索
retrieved_nodes = retriever.retrieve(query)
# 提取检索到的文档节点ID
retrieved_ids = [node.node.node_id for node in retrieved_nodes]
# 从验证数据集中获取当前查询对应的标准答案文档ID
# 这里假设每个查询只有一个相关文档,所以取第一个
expected_id = relevant_docs[query_id][0]
# 检查标准答案文档是否在检索到的文档列表中(是否命中)
is_hit = expected_id in retrieved_ids
# 记录当前查询的评估结果
eval_result = {
"is_hit": is_hit, # 是否命中(True/False)
"retrieved": retrieved_ids, # 检索到的文档ID列表
"expected": expected_id, # 期望的文档ID
"query": query_id, # 查询ID
}
eval_results.append(eval_result)
# 返回所有查询的评估结果
return eval_results # 注意:原代码这里有错误,返回的是单个结果而不是列表- 只能怕针对sentence transformer兼容的模型,不支持OpenAI模型。
from sentence_transformers.evaluation import InformationRetrievalEvaluator
from sentence_transformers import SentenceTransformer
from pathlib import Path
def evaluate_st(
dataset,
model_id,
name,
):
"""
使用sentence-transformers内置的InformationRetrievalEvaluator评估嵌入模型
参数:
dataset: 包含语料、查询和相关文档的数据集
model_id: 要评估的嵌入模型ID或路径
name: 评估任务的名称,用于结果标识
返回:
评估结果(通常包含各种检索指标)
"""
# 从数据集中提取三个核心组件
corpus = dataset.corpus # 语料库:文档ID到文档内容的映射
queries = dataset.queries # 查询集:查询ID到查询文本的映射
relevant_docs = dataset.relevant_docs # 相关文档:查询ID到相关文档ID列表的映射
# 创建信息检索评估器
# 这个评估器会自动计算多个检索指标,如:
# - 召回率 (Recall@k)
# - 平均精度 (Mean Average Precision)
# - 归一化折损累计增益 (NDCG)
# - 精确率 (Precision@k)
evaluator_st = InformationRetrievalEvaluator(
queries, # 查询字典
corpus, # 语料字典
relevant_docs, # 相关文档字典
name=name # 评估名称,用于结果文件命名
)
# 加载要评估的嵌入模型
# model_id可以是HuggingFace模型ID或本地模型路径
model = SentenceTransformer(model_id)
# 创建结果输出目录(如果不存在)
output_path = "results/"
Path(output_path).mkdir(exist_ok=True, parents=True)
# exist_ok=True: 如果目录已存在不会报错
# parents=True: 自动创建父目录
# 执行评估并返回结果
# 评估器会在指定路径生成详细的评估报告
return evaluator_st(model, output_path=output_path)
evaluate_st(
dataset=val_dataset,
model_id="XXXX", # 微调后的模型路径
name="XXXX"
)from llama_index.embeddings.openai import OpenAIEmbedding
import pandas as pd
# 创建OpenAI的嵌入模型实例
# 默认使用OpenAI的text-embedding-ada-002模型
ada = OpenAIEmbedding()
# 使用之前定义的evaluate函数对验证集进行评估
# 使用OpenAI的嵌入模型在验证集上进行检索性能测试
ada_val_results = evaluate(val_dataset, ada)
# 将评估结果转换为pandas DataFrame,便于分析
df_ada = pd.DataFrame(ada_val_results)
# 计算命中率:对"is_hit"列求平均值
# 由于is_hit是布尔值(True=1, False=0),平均值就是命中率
hit_rate_ada = df_ada["is_hit"].mean()
# 打印OpenAI嵌入模型的命中率
print(hit_rate_ada)# conda创建 python=3.10版本的虚环境
conda create -n autotrain python=3.10
# 激活conda创建的名字叫autotrain的虚环境
conda activate autotrain
#torch安装
conda install pytorch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 pytorch cuda=12.1 -c pytorch -c nvidia
# 安装autotrain-advanced
pip install autotrain-advanced
# 安装git 和git-lfs
"""
git下载地址:https://git-scm.com/downloads
如果是windows系统,则不需要单独安装git-lfs,git安装包有带
git-lfs下载地址:https://github.com/git-lfs/git-lfs/releases
git-lfs安装教程:https://github.com/git-lfs/git-lfs/wiki/Installation
"""
# HF_TOKEN
"""
注册登录huggingface
获取HF_TOKEN:https://huggingface.co/settings/tokens
添加环境变量HF_TOKEN
"""环境变量HF_TOKEN
autotrain app --port 8080 --host 127.0.0.1-
autotrain微调embedding模型,选择ST(Sentence transformer)前缀的。
-
教程:https://huggingface.co/blog/abhishek/finetune-custom-embeddings-autotrain
- 训练过程中,界面上可以看日志,以及确认有没有任务运行。
Mean Reciprocal Rank (MRR)是一种用于评估信息检索系统,比如搜索引擎和推荐系统,性能的指标(特别是在首位返回相关结果的重要性较高的场景)。
# conda创建 python=3.10版本的虚环境
# conda create -n rag_embedding python=3.10
# 激活conda创建的名字叫rag_embedding的虚环境
conda activate rag_embedding
# torch安装 - 使用conda安装指定版本的PyTorch及其相关库,并指定CUDA 12.1版本
# conda install pytorch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 pytorch-cuda=12.1 -c pytorch -c nvidia
# 安装依赖 - 使用pip安装LlamaIndex与OpenAI集成的LLM和嵌入模型库
# 如果用openai模型来生成微调数据集
pip install llama-index-llms-openai
pip install llama-index-embeddings-openai
# 安装依赖 - 使用pip安装LlamaIndex与Ollama集成的库(用于本地部署模型)
# 如果是本地部署ollama
pip install llama-index-llms-ollama
# 安装依赖 - 使用pip安装LlamaIndex微调工具库
pip install llama-index-finetuning
# 安装依赖 - 使用pip安装LlamaIndex与HuggingFace嵌入模型集成的库
pip install llama-index-embeddings-huggingface
# 安装依赖 - 使用pip安装最新版本的llama-index核心库
pip install -U llama-index #要按照最新的llama-index
# 安装依赖 - 使用pip安装LlamaIndex的网页读取器
pip install llama-index-readers-web
# 评测模型有量化 - 使用pip安装加速库(用于优化模型推理)
pip install accelerate
# 评测模型有量化 - 使用pip安装Windows版的bitsandbytes库(用于模型量化)
# 注意:这是一个特定的Windows轮子文件,仅适用于Windows AMD64架构
# pip install https://github.com/jllllll/bitsandbytes-windows-webui/releases/download/wheels/bitsandbytes-0.41.2.post2-py3-none-win_amd64.whl# 导入所需的库
from llama_index.readers.web import SimpleWebPageReader # 网页读取器
from llama_index.core.node_parser import SentenceSplitter # 句子分割器
from llama_index.core import VectorStoreIndex # 向量存储索引
from llama_index.core.schema import TextNode # 文本节点
from tqdm.notebook import tqdm # 进度条显示
from llama_index.embeddings.openai import OpenAIEmbedding # OpenAI嵌入模型
import pandas as pd # 数据处理
import numpy as np # 数值计算
# from llama_index.llms import OpenAI # OpenAI模型(已注释,改用Ollama)
from llama_index.finetuning import generate_qa_embedding_pairs # 生成问答对
from llama_index.llms.ollama import Ollama # Ollama本地模型
from llama_index.core.evaluation import (
generate_question_context_pairs, # 生成问题和上下文对
EmbeddingQAFinetuneDataset, # 嵌入微调数据集
)
# 准备问答对数据集
def prepare_data():
"""
从欧盟法律网站下载指定语言的法律文档
将文档分割成较小的文本块
使用本地部署的Ollama模型为每个文本块生成问答对
返回用于训练和评估的问答对数据集
返回:
qa_dataset: 包含问题、上下文和相关文档的数据集对象
"""
# 法案文档下载地址(欧盟法规32024R1689)
# https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32024R1689
language = "EN" # 设置文档语言为英文
# 构建欧盟法律文档的URL,使用指定的语言和文档标识符
# CELEX:32024R1689 是欧盟法律文档的唯一标识符
url_doc = "https://eur-lex.europa.eu/legal-content/" + language + "/TXT/HTML/?uri=CELEX:32024R1689"
# 使用SimpleWebPageReader读取网页内容
# html_to_text=True 参数表示将HTML转换为纯文本
documents = SimpleWebPageReader(html_to_text=True).load_data([url_doc])
# 创建SentenceSplitter解析器,设置chunk_size=1000(每个文本块大小为1000个字符/标记)
# 注意:没有指定chunk_overlap参数,所以文本块之间没有重叠部分
parser = SentenceSplitter(chunk_size=1000) # 这儿没指定chunk_overlap重叠部分大小
# 使用解析器将文档分割成节点(文本块)
nodes = parser.get_nodes_from_documents(documents, show_progress=True)
# 定义提示词模板,用于指导模型生成问题
prompts = {}
prompts["EN"] = """Context information is below.
{context_str}
---------------------
Given the context information and not prior knowledge, generate only questions based on the below query.
You are a Teacher/Professor. Your task is to setup {num_questions_per_chunk} questions for an upcoming quiz/examination.
The questions should be diverse in nature across the document. Restrict the questions to the context information provided.
"""
# 初始化Ollama模型,使用qwen2:7b-instruct-q4_0模型,设置超时时间为120秒
# 注意:这里使用本地部署的Ollama服务,而不是OpenAI API
ollm = Ollama(model="qwen2:7b-instruct-q4_0", request_timeout=120.0)
# 生成问答对数据集
# 使用指定的LLM为每个文档块生成问题和答案
qa_dataset = generate_qa_embedding_pairs(
llm=ollm, # 使用Ollama本地部署的模型,而不是OpenAI模型
nodes=nodes, # 文档节点列表
qa_generate_prompt_tmpl=prompts[language], # 生成问题的提示词模板
num_questions_per_chunk=2 # 每个文档块生成2个问题
)
return qa_dataset # 返回生成的问答对数据集
# 自定义的MRR评估函数
def evaluate(dataset, embed_model, insert_batch_size=1000, top_k=5):
"""
使用MRR(平均倒数排名)指标评估嵌入模型的检索性能
对每个查询检索相关文档,计算相关文档在检索结果中的排名
返回平均MRR分数
参数:
dataset: 包含语料、查询和相关文档的数据集
embed_model: 要评估的嵌入模型
insert_batch_size: 批量插入大小,默认1000
top_k: 检索返回的top-k结果数量,默认5
返回:
mrr_score: 平均MRR分数
"""
# 从数据集中提取语料(文档内容)、查询和相关文档
corpus = dataset.corpus # 语料库:文档ID到文档内容的映射
queries = dataset.queries # 查询:查询ID到查询文本的映射
relevant_docs = dataset.relevant_docs # 相关文档:查询ID到相关文档ID列表的映射
# 创建文本节点,用于构建向量索引
nodes = [TextNode(id_=id_, text=text) for id_, text in corpus.items()]
# 使用要评估的嵌入模型创建向量存储索引
index = VectorStoreIndex(nodes, embed_model=embed_model)
# 创建检索器,设置返回top_k个最相似的结果
retriever = index.as_retriever(similarity_top_k=top_k)
# 准备收集评估结果
eval_results = []
# 对每个查询进行检索和评估
for query_id, query in tqdm(queries.items()):
# 检索出与查询最相关的top_k个文档
retrieved_nodes = retriever.retrieve(query)
# 提取检索到的文档ID列表
retrieved_ids = [node.node.node_id for node in retrieved_nodes]
# 获取查询对应的相关文档ID(取第一个相关文档)
expected_id = relevant_docs[query_id][0]
# 检查相关文档是否在检索结果中
is_hit = expected_id in retrieved_ids
# 如果命中,计算倒数排名;否则为0
if is_hit:
rank = retrieved_ids.index(expected_id) + 1 # 排名(从1开始)
mrr = 1 / rank # 倒数排名
else:
mrr = 0
eval_results.append(mrr)
# 返回平均MRR分数
return np.average(eval_results)
# 对多个模型和语言进行评估
def embedding_evaluation():
"""
评估多个OpenAI嵌入模型在不同语言数据集上的性能
包括不同维度的text-embedding-3-large、text-embedding-3-small和 text-embedding-ada-002模型
对英语、法语、捷克语和匈牙利语四种语言进行评估
将结果汇总到pandas DataFrame中并显示
流程:
1. 定义要评估的嵌入模型规格
2. 遍历所有语言
3. 遍历所有模型
4. 加载数据集并评估每个模型的MRR分数
5. 汇总结果并显示
"""
# 定义要评估的嵌入模型规格
embeddings_model_spec = {}
# OpenAI embedding-3-large模型,256维版本
embeddings_model_spec['OAI-Large-256'] = {'model_name': 'text-embedding-3-large', 'dimensions': 256}
# OpenAI embedding-3-large模型,3072维版本
embeddings_model_spec['OAI-Large-3072'] = {'model_name': 'text-embedding-3-large', 'dimensions': 3072}
# OpenAI embedding-3-small模型,1536维
embeddings_model_spec['OAI-Small'] = {'model_name': 'text-embedding-3-small', 'dimensions': 1536}
# OpenAI embedding-ada-002模型,默认维度
embeddings_model_spec['OAI-ada-002'] = {'model_name': 'text-embedding-ada-002', 'dimensions': None}
# 存储结果的列表
results = []
# 要评估的语言列表
languages = ["EN", "FR", "CS", "HU"]
# 遍历所有语言
for language in languages:
# 加载数据集(假设已保存为JSON文件)
file_name = language + "_dataset.json"
qa_dataset = EmbeddingQAFinetuneDataset.from_json(file_name)
# 遍历所有模型
for model_name, model_spec in embeddings_model_spec.items():
# 创建嵌入模型实例
embed_model = OpenAIEmbedding(
model=model_spec['model_name'],
dimensions=model_spec['dimensions']
)
# 评估嵌入模型的MRR分数
score = evaluate(qa_dataset, embed_model)
# 将结果添加到列表
results.append([language, model_name, score])
# 将结果转换为DataFrame以便查看
df_results = pd.DataFrame(results, columns=["Language", "Embedding model", "MRR"])
# 打印结果
print(df_results)
return df_results # 返回结果DataFrame
# 主程序
if __name__ == "__main__":
"""
程序主入口
执行步骤:
1. 准备数据集(生成问答对)
2. 对嵌入模型进行评估
"""
# 准备数据集
print("正在准备数据集...")
qa_dataset = prepare_data()
# 保存数据集到JSON文件(如果需要)
# qa_dataset.save_json("EN_dataset.json")
# 执行评估
print("开始评估嵌入模型...")
df_results = embedding_evaluation()











