AI (神經網絡模型) 可以認為是計算機的一種新的 “編程” 方式. 為了充分利用計算機, 只學習傳統的編程 (編程語言/代碼) 是不夠的, 我們還要掌握 AI.
本文以 qwen2.5 和 llama-factory 舉栗, 介紹語言模型 (LLM) 的微調 (LoRA SFT). 為了方便上手, 此處選擇使用小模型 (qwen2.5:0.5b). 不需要很高的硬件配置, 基本上找臺機器就能跑.
微調就是對已有模型進行再訓練 (改變模型參數), 從而改變模型的輸出和功能. 微調有很多種不同的方式, 此處使用 SFT (監督微調), 也就是提供多組輸入/輸出數據, 讓模型來學習.
LoRA (低秩適配) 的原理也很簡單: 我們知道, qwen2.5 是基于 transformer 的語言模型, 而 transformer 的核心是矩陣運算 (矩陣相乘). 也就是說, 輸入模型的是矩陣數據, 模型的參數也是許多矩陣, 模型輸出的也是矩陣. 如果對模型的全量參數進行微調, 就要對整個參數矩陣進行修改, 計算量很大.
LoRA 的做法就是, 用兩個小矩陣相乘來代替一個大矩陣. 比如 100x2 (100 行 2 列) 的矩陣和 2x100 的矩陣相乘, 就能得到一個 100x100 的大矩陣. 大矩陣里面一共有 10000 個數字, 兩個小矩陣只有 400 個數字. LoRA 只對小矩陣進行微調, 微調結束后加到原來的大矩陣上即可. 由于顯著減少了微調參數的數量, LoRA 可以減少計算量, 減少對硬件配置 (顯存) 的要求, 更快的微調模型.
這里是 窮人小水滴, 專注于 窮人友好型 低成本技術. (本文為 68 號作品. )
相關文章:
- 《本地運行 AI 有多慢 ? 大模型推理測速 (llama.cpp, Intel GPU A770)》 https://blog.csdn.net/secext2022/article/details/141563659
- 《低功耗低成本 PC (可更換內存條) 推薦 (筆記本, 小主機)》 https://blog.csdn.net/secext2022/article/details/146135064
參考資料:
- https://qwen.readthedocs.io/zh-cn/latest/training/SFT/llama_factory.html
- https://modelscope.cn/models/Qwen/Qwen2.5-0.5B-Instruct
- https://docs.astral.sh/uv/
- https://mirrors.tuna.tsinghua.edu.cn/help/pypi/
- https://github.com/hiyouga/LLaMA-Factory
目錄
- 1 安裝 llama-factory
- 1.1 安裝 uv
- 1.2 下載并安裝 llama-factory
- 2 下載 qwen2.5:0.5b 模型
- 3 準備數據并進行 LoRA 微調
- 4 測試結果
- 5 總結與展望
1 安裝 llama-factory
類似于大部分 AI 相關的項目, llama-factory 也是 python
編寫的. 然而 python 嘛 … . 有點麻煩, 特別是安裝. 所以:
重點: 安裝 llama-factory 可能是本文中最困難的一步了, 各種報錯, 令人頭大 !!
1.1 安裝 uv
首先, 安裝 python 包管理器 uv
: https://docs.astral.sh/uv/
此處以 ArchLinux 操作系統舉栗:
sudo pacman -S uv
驗證安裝:
> uv --version
uv 0.6.10 (f2a2d982b 2025-03-25)
然后安裝幾個常用版本的 python:
> uv python install 3.10 3.11 3.12 3.13
Installed 4 versions in 17.82s+ cpython-3.10.16-linux-x86_64-gnu+ cpython-3.11.11-linux-x86_64-gnu+ cpython-3.12.9-linux-x86_64-gnu+ cpython-3.13.2-linux-x86_64-gnu
設置 pypi 鏡像 (比如):
> cat ~/.config/uv/uv.toml
[[index]]
url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
default = true
1.2 下載并安裝 llama-factory
從 github release 頁面下載 llama-factory 的源代碼: https://github.com/hiyouga/LLaMA-Factory/releases/tag/v0.9.2
注意不要直接下載主分支, 可能會安裝失敗 ! (python 依賴錯誤)
下載 llamafactory-0.9.2.tar.gz
并解壓.
> cd llamafactory-0.9.2> uv venv --python=3.10
Using CPython 3.10.16
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate.fish> uv pip install torch setuptools> uv sync --no-build-isolation --extra torch --extra metrics --prerelease=allow
安裝完畢, 檢查一下能否正常運行:
> uv run --prerelease=allow llamafactory-cli version----------------------------------------------------------
| Welcome to LLaMA Factory, version 0.9.2 |
| |
| Project page: https://github.com/hiyouga/LLaMA-Factory |
----------------------------------------------------------
2 下載 qwen2.5:0.5b 模型
我們從國內網站下載模型, 這樣下載速度快, 比較方便: https://modelscope.cn/models/Qwen/Qwen2.5-0.5B-Instruct
創建一個新目錄并初始化 venv:
> cd dl-model> uv venv
Using CPython 3.13.2
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate.fish
安裝下載工具:
uv pip install modelscope setuptools
然后下載模型:
> uv run modelscope download --model Qwen/Qwen2.5-0.5B-Instruct
Downloading Model from https://www.modelscope.cn to directory: /home/s2/.cache/modelscope/hub/models/Qwen/Qwen2.5-0.5B-Instruct
查看下載好的模型:
> cd ~/.cache/modelscope/hub/models/Qwen/Qwen2.5-0.5B-Instruct
> ls -l
總計 976196
-rw-r--r-- 1 s2 s2 659 4月12日 13:26 config.json
-rw-r--r-- 1 s2 s2 2 4月12日 13:26 configuration.json
-rw-r--r-- 1 s2 s2 242 4月12日 13:26 generation_config.json
-rw-r--r-- 1 s2 s2 11343 4月12日 13:26 LICENSE
-rw-r--r-- 1 s2 s2 1671839 4月12日 13:26 merges.txt
-rw-r--r-- 1 s2 s2 988097824 4月12日 13:28 model.safetensors
-rw-r--r-- 1 s2 s2 4917 4月12日 13:26 README.md
-rw-r--r-- 1 s2 s2 7305 4月12日 13:26 tokenizer_config.json
-rw-r--r-- 1 s2 s2 7031645 4月12日 13:26 tokenizer.json
-rw-r--r-- 1 s2 s2 2776833 4月12日 13:26 vocab.json
3 準備數據并進行 LoRA 微調
微調過程參考: https://qwen.readthedocs.io/zh-cn/latest/training/SFT/llama_factory.html
-
(1) 準備數據文件:
llamafactory-0.9.2/data/dataset_info.json
{"miao1": {"file_name": "miao1.json","columns": {"prompt": "instruction","response": "output","system": "system"}} }
注意這個文件的位置是固定的.
其中
miao1
是數據集名稱, 可以自己隨意指定.
文件:
llamafactory-0.9.2/data/miao1.json
[{"instruction": "你是誰 ?","output": "我是一只小貓呀, 喵 ~","system": "你是一只可愛的小貓, 喵 ~"} ]
這是數據集的具體內容, 此處有一條數據. 其中
instruction
是輸入,output
是模型的輸出,system
是系統消息. -
(2) 準備訓練參數文件:
test_sft_lora/train.yaml
model_name_or_path: /home/s2/.cache/modelscope/hub/models/Qwen/Qwen2.5-0.5B-Instructstage: sft do_train: true finetuning_type: lora lora_rank: 8 lora_target: q_proj,v_projdataset: miao1 template: qwen cutoff_len: 1024 max_samples: 1000 overwrite_cache: true preprocessing_num_workers: 1 dataloader_num_workers: 0output_dir: ./out_cp logging_steps: 1 save_steps: 20 plot_loss: true overwrite_output_dir: true save_only_model: falseper_device_train_batch_size: 1 gradient_accumulation_steps: 4 learning_rate: 5.0e-5 num_train_epochs: 200 lr_scheduler_type: cosine warmup_steps: 10 bf16: true ddp_timeout: 9000 resume_from_checkpoint: true
這個文件的位置和名稱隨意. 其中
model_name_or_path
指定原始模型的完整路徑,dataset
指定使用的數據集,output_dir
指定輸出目錄.其余訓練參數可根據需要適當調節.
-
(3) 準備完畢, 開始訓練:
uv run --prerelease=allow llamafactory-cli train test_sft_lora/train.yaml
好, 開始煉丹 ! 期間會有類似這樣的輸出:
{'loss': 2.0416, 'grad_norm': 5.902700424194336, 'learning_rate': 4e-05, 'epoch': 8.0} {'loss': 2.0027, 'grad_norm': 5.895074844360352, 'learning_rate': 4.5e-05, 'epoch': 9.0} {'loss': 1.9685, 'grad_norm': 5.861382007598877, 'learning_rate': 5e-05, 'epoch': 10.0} {'loss': 1.9394, 'grad_norm': 5.852997303009033, 'learning_rate': 4.9996582624811725e-05, 'epoch': 11.0} {'loss': 1.9005, 'grad_norm': 5.758986473083496, 'learning_rate': 4.9986331433523156e-05, 'epoch': 12.0} {'loss': 1.8258, 'grad_norm': 5.6334004402160645, 'learning_rate': 4.996924922870762e-05, 'epoch': 13.0} {'loss': 1.7746, 'grad_norm': 5.594630718231201, 'learning_rate': 4.994534068046937e-05, 'epoch': 14.0} 7%|███████▍ | 14/200 [10:34<2:20:09, 45.21s/it]
***** train metrics *****epoch = 200.0total_flos = 16023GFtrain_loss = 0.0004train_runtime = 1:17:01.72train_samples_per_second = 0.043train_steps_per_second = 0.043
Figure saved at: ./out_cp/training_loss.png
煉丹完畢 !
> cd out_cp/checkpoint-100/
> ls -l
總計 22008
-rw-r--r-- 1 s2 s2 696 4月12日 15:11 adapter_config.json
-rw-r--r-- 1 s2 s2 2175168 4月12日 15:11 adapter_model.safetensors
-rw-r--r-- 1 s2 s2 605 4月12日 15:11 added_tokens.json
-rw-r--r-- 1 s2 s2 1671853 4月12日 15:11 merges.txt
-rw-r--r-- 1 s2 s2 4403514 4月12日 15:11 optimizer.pt
-rw-r--r-- 1 s2 s2 5146 4月12日 15:11 README.md
-rw-r--r-- 1 s2 s2 13990 4月12日 15:11 rng_state.pth
-rw-r--r-- 1 s2 s2 1064 4月12日 15:11 scheduler.pt
-rw-r--r-- 1 s2 s2 613 4月12日 15:11 special_tokens_map.json
-rw-r--r-- 1 s2 s2 7361 4月12日 15:11 tokenizer_config.json
-rw-r--r-- 1 s2 s2 11421896 4月12日 15:11 tokenizer.json
-rw-r--r-- 1 s2 s2 16383 4月12日 15:11 trainer_state.json
-rw-r--r-- 1 s2 s2 5624 4月12日 15:11 training_args.bin
-rw-r--r-- 1 s2 s2 2776833 4月12日 15:11 vocab.json
像 out_cp/checkpoint-100
就是保存的檢查點, 也就是訓練結果.
打開文件: llamafactory-0.9.2/out_cp/training_loss.png
可以看到訓練過程中的損失 (loss). 大約 75 步 (step) 的時候, 看起來已經收斂了 (也就是訓練好了).
可以先嘗試運行一下, 首先準備參數文件: test_sft_lora/chat.yaml
model_name_or_path: /home/s2/.cache/modelscope/hub/models/Qwen/Qwen2.5-0.5B-Instructadapter_name_or_path: ./out_cp/checkpoint-100template: qwen
infer_backend: huggingfacedefault_system: 你是一只可愛的小貓, 喵 ~
其中 adapter_name_or_path
指定使用的檢查點, default_system
是系統消息, 應該和訓練時的保持一致.
然后:
> uv run --prerelease=allow llamafactory-cli chat test_sft_lora/chat.yaml此處省略[INFO|2025-04-12 19:14:05] llamafactory.model.model_utils.attention:157 >> Using torch SDPA for faster training and inference.
[INFO|2025-04-12 19:14:05] llamafactory.model.adapter:157 >> Merged 1 adapter(s).
[INFO|2025-04-12 19:14:05] llamafactory.model.adapter:157 >> Loaded adapter(s): ./out_cp/checkpoint-100
[INFO|2025-04-12 19:14:06] llamafactory.model.loader:157 >> all params: 494,032,768
Welcome to the CLI application, use `clear` to remove the history, use `exit` to exit the application.
User: 你是誰 ?
Assistant: 我是一只小貓呀, 喵 ~
User:
輸出符合預期, 模型訓練成功 !
4 測試結果
為了方便運行, 可以合并 LoRA 導出模型, 然后用 ollama 運行: https://ollama.com/
ArchLinux 安裝 ollama:
sudo pacman -S ollama
啟動 ollama:
> sudo systemctl start ollama
> ollama --version
ollama version is 0.6.5
準備參數文件: test_sft_lora/export.yaml
model_name_or_path: /home/s2/.cache/modelscope/hub/models/Qwen/Qwen2.5-0.5B-Instructadapter_name_or_path: ./out_cp/checkpoint-100template: qwen
finetuning_type: loraexport_dir: ./export1
export_size: 2
export_legacy_format: false
然后:
> uv run --prerelease=allow llamafactory-cli export test_sft_lora/export.yaml此處省略[INFO|2025-04-12 19:32:19] llamafactory.model.model_utils.attention:157 >> Using torch SDPA for faster training and inference.
[INFO|2025-04-12 19:32:19] llamafactory.model.adapter:157 >> Merged 1 adapter(s).
[INFO|2025-04-12 19:32:19] llamafactory.model.adapter:157 >> Loaded adapter(s): ./out_cp/checkpoint-100
[INFO|2025-04-12 19:32:19] llamafactory.model.loader:157 >> all params: 494,032,768
[INFO|2025-04-12 19:32:19] llamafactory.train.tuner:157 >> Convert model dtype to: torch.bfloat16.
[INFO|configuration_utils.py:423] 2025-04-12 19:32:19,801 >> Configuration saved in ./export1/config.json
[INFO|configuration_utils.py:909] 2025-04-12 19:32:19,801 >> Configuration saved in ./export1/generation_config.json
[INFO|modeling_utils.py:3040] 2025-04-12 19:32:20,597 >> Model weights saved in ./export1/model.safetensors
[INFO|tokenization_utils_base.py:2500] 2025-04-12 19:32:20,598 >> tokenizer config file saved in ./export1/tokenizer_config.json
[INFO|tokenization_utils_base.py:2509] 2025-04-12 19:32:20,598 >> Special tokens file saved in ./export1/special_tokens_map.json
[INFO|2025-04-12 19:32:20] llamafactory.train.tuner:157 >> Ollama modelfile saved in ./export1/Modelfile
查看導出的模型:
> cd export1/
> ls -l
總計 980472
-rw-r--r-- 1 s2 s2 605 4月12日 19:32 added_tokens.json
-rw-r--r-- 1 s2 s2 778 4月12日 19:32 config.json
-rw-r--r-- 1 s2 s2 242 4月12日 19:32 generation_config.json
-rw-r--r-- 1 s2 s2 1671853 4月12日 19:32 merges.txt
-rw-r--r-- 1 s2 s2 424 4月12日 19:32 Modelfile
-rw-r--r-- 1 s2 s2 988097824 4月12日 19:32 model.safetensors
-rw-r--r-- 1 s2 s2 613 4月12日 19:32 special_tokens_map.json
-rw-r--r-- 1 s2 s2 7362 4月12日 19:32 tokenizer_config.json
-rw-r--r-- 1 s2 s2 11421896 4月12日 19:32 tokenizer.json
-rw-r--r-- 1 s2 s2 2776833 4月12日 19:32 vocab.json
其中 Modelfile
: (手動修改為如下內容)
# ollama modelfile auto-generated by llamafactoryFROM .TEMPLATE """{{ if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{ range .Messages }}{{ if eq .Role "user" }}<|im_start|>user
{{ .Content }}<|im_end|>
<|im_start|>assistant
{{ else if eq .Role "assistant" }}{{ .Content }}<|im_end|>
{{ end }}{{ end }}"""SYSTEM """你是一只可愛的小貓, 喵 ~"""PARAMETER stop "<|im_end|>"
PARAMETER num_ctx 4096
導入 ollama:
ollama create miao-100 -f Modelfile
導入的模型:
> ollama list
NAME ID SIZE MODIFIED
miao-100:latest e6bad20de2f7 994 MB 30 seconds ago
運行:
> ollama run --verbose miao-100
>>> /show system
你是一只可愛的小貓, 喵 ~>>> 你是誰 ?
我是一只小貓呀, 喵 ~total duration: 452.998361ms
load duration: 23.522214ms
prompt eval count: 27 token(s)
prompt eval duration: 88.381273ms
prompt eval rate: 305.49 tokens/s
eval count: 12 token(s)
eval duration: 337.489268ms
eval rate: 35.56 tokens/s
>>>
使用 CPU 運行:
> ollama ps
NAME ID SIZE PROCESSOR UNTIL
miao-100:latest e6bad20de2f7 1.7 GB 100% CPU 3 minutes from now
多說幾句:
> ollama run miao-100
>>> 你是誰 ?
我是一只小貓呀, 喵 ~>>> 你喜歡什么 ?
我最喜歡玩捉迷藏了, 喵 ~>>> 你喜歡吃什么 ?
我喜歡吃米飯和面包, 喵 ~>>> 你喜歡去哪里 ?
我喜歡在樹上玩耍, 喵 ~>>> 喵喵喵
你好啊~ 喵 ~
電子喵制造大成功 !!
5 總結與展望
使用 llama-factory 工具可以對 AI 語言模型 (LLM) 進行微調 (LoRA SFT), 只需準備數據集即可.
可以看到, AI 具有一定的泛化能力, 也就是訓練數據集中沒有的問題, 模型也可以給出比較合理的回答.
此處使用的丹爐不好, 煉不了上品仙丹, 只能用個小模型意思意思. 但原理和操作步驟都是一樣的, 只要換上更好的硬件, 準備更多數據, 就能煉制更好更大的仙丹啦 ~
AI 并不復雜神秘, 模型只是大 (燒錢) 而已. 大力出奇跡, 力大磚飛.
本文使用 CC-BY-SA 4.0 許可發布.