, inference_mode=False, r=8, target_modules={'o_proj', 'gate_proj', 'q_proj', 'k_proj', 'up_proj', 'down_proj', 'v_proj'}, lora_alpha=32, lora_dropout=0.1, fan_in_fan_out=False, bias='none', use_rslora=False, modules_to_save=None, init_lora_weights=True, layers_to_transform=None, layers_pattern=None, rank_pattern={}, alpha_pattern={}, megatron_config=None, megatron_core='megatron.core', loftq_config={}, use_dora=False, layer_replication=None)"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "model = get_peft_model(model, config)\n",
+ "config"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "ebf5482b-fab9-4eb3-ad88-c116def4be12",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "trainable params: 20,185,088 || all params: 7,635,801,600 || trainable%: 0.26434798934534914\n"
+ ]
+ }
+ ],
+ "source": [
+ "model.print_trainable_parameters()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ca055683-837f-4865-9c57-9164ba60c00f",
+ "metadata": {},
+ "source": [
+ "# 配置训练参数"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "7e76bbff-15fd-4995-a61d-8364dc5e9ea0",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "args = TrainingArguments(\n",
+ " output_dir=\"./output/Qwen2.5_instruct_lora\",\n",
+ " per_device_train_batch_size=4,\n",
+ " gradient_accumulation_steps=4,\n",
+ " logging_steps=10,\n",
+ " num_train_epochs=3,\n",
+ " save_steps=100, \n",
+ " learning_rate=1e-4,\n",
+ " save_on_each_node=True,\n",
+ " gradient_checkpointing=True\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "f142cb9c-ad99-48e6-ba86-6df198f9ed96",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "trainer = Trainer(\n",
+ " model=model,\n",
+ " args=args,\n",
+ " train_dataset=tokenized_id,\n",
+ " data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "aec9bc36-b297-45af-99e1-d4c4d82be081",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`...\n",
+ "/root/miniconda3/lib/python3.10/site-packages/torch/utils/checkpoint.py:429: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
+ " warnings.warn(\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ " \n",
+ " \n",
+ "
\n",
+ " [699/699 10:02, Epoch 2/3]\n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | Step | \n",
+ " Training Loss | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 10 | \n",
+ " 3.759900 | \n",
+ "
\n",
+ " \n",
+ " | 20 | \n",
+ " 3.096600 | \n",
+ "
\n",
+ " \n",
+ " | 30 | \n",
+ " 3.063300 | \n",
+ "
\n",
+ " \n",
+ " | 40 | \n",
+ " 2.917600 | \n",
+ "
\n",
+ " \n",
+ " | 50 | \n",
+ " 2.900500 | \n",
+ "
\n",
+ " \n",
+ " | 60 | \n",
+ " 2.953600 | \n",
+ "
\n",
+ " \n",
+ " | 70 | \n",
+ " 2.912600 | \n",
+ "
\n",
+ " \n",
+ " | 80 | \n",
+ " 3.013900 | \n",
+ "
\n",
+ " \n",
+ " | 90 | \n",
+ " 3.045900 | \n",
+ "
\n",
+ " \n",
+ " | 100 | \n",
+ " 2.933000 | \n",
+ "
\n",
+ " \n",
+ " | 110 | \n",
+ " 2.927600 | \n",
+ "
\n",
+ " \n",
+ " | 120 | \n",
+ " 2.942600 | \n",
+ "
\n",
+ " \n",
+ " | 130 | \n",
+ " 2.921200 | \n",
+ "
\n",
+ " \n",
+ " | 140 | \n",
+ " 2.876200 | \n",
+ "
\n",
+ " \n",
+ " | 150 | \n",
+ " 2.984700 | \n",
+ "
\n",
+ " \n",
+ " | 160 | \n",
+ " 2.968600 | \n",
+ "
\n",
+ " \n",
+ " | 170 | \n",
+ " 2.955000 | \n",
+ "
\n",
+ " \n",
+ " | 180 | \n",
+ " 2.809300 | \n",
+ "
\n",
+ " \n",
+ " | 190 | \n",
+ " 2.859800 | \n",
+ "
\n",
+ " \n",
+ " | 200 | \n",
+ " 2.913100 | \n",
+ "
\n",
+ " \n",
+ " | 210 | \n",
+ " 2.815100 | \n",
+ "
\n",
+ " \n",
+ " | 220 | \n",
+ " 2.853700 | \n",
+ "
\n",
+ " \n",
+ " | 230 | \n",
+ " 2.863900 | \n",
+ "
\n",
+ " \n",
+ " | 240 | \n",
+ " 2.807300 | \n",
+ "
\n",
+ " \n",
+ " | 250 | \n",
+ " 2.647200 | \n",
+ "
\n",
+ " \n",
+ " | 260 | \n",
+ " 2.501700 | \n",
+ "
\n",
+ " \n",
+ " | 270 | \n",
+ " 2.542600 | \n",
+ "
\n",
+ " \n",
+ " | 280 | \n",
+ " 2.585100 | \n",
+ "
\n",
+ " \n",
+ " | 290 | \n",
+ " 2.608100 | \n",
+ "
\n",
+ " \n",
+ " | 300 | \n",
+ " 2.556000 | \n",
+ "
\n",
+ " \n",
+ " | 310 | \n",
+ " 2.517500 | \n",
+ "
\n",
+ " \n",
+ " | 320 | \n",
+ " 2.585400 | \n",
+ "
\n",
+ " \n",
+ " | 330 | \n",
+ " 2.566300 | \n",
+ "
\n",
+ " \n",
+ " | 340 | \n",
+ " 2.512500 | \n",
+ "
\n",
+ " \n",
+ " | 350 | \n",
+ " 2.631100 | \n",
+ "
\n",
+ " \n",
+ " | 360 | \n",
+ " 2.518600 | \n",
+ "
\n",
+ " \n",
+ " | 370 | \n",
+ " 2.568600 | \n",
+ "
\n",
+ " \n",
+ " | 380 | \n",
+ " 2.528400 | \n",
+ "
\n",
+ " \n",
+ " | 390 | \n",
+ " 2.564100 | \n",
+ "
\n",
+ " \n",
+ " | 400 | \n",
+ " 2.423300 | \n",
+ "
\n",
+ " \n",
+ " | 410 | \n",
+ " 2.631800 | \n",
+ "
\n",
+ " \n",
+ " | 420 | \n",
+ " 2.402200 | \n",
+ "
\n",
+ " \n",
+ " | 430 | \n",
+ " 2.598100 | \n",
+ "
\n",
+ " \n",
+ " | 440 | \n",
+ " 2.533700 | \n",
+ "
\n",
+ " \n",
+ " | 450 | \n",
+ " 2.479900 | \n",
+ "
\n",
+ " \n",
+ " | 460 | \n",
+ " 2.486800 | \n",
+ "
\n",
+ " \n",
+ " | 470 | \n",
+ " 2.390800 | \n",
+ "
\n",
+ " \n",
+ " | 480 | \n",
+ " 2.299600 | \n",
+ "
\n",
+ " \n",
+ " | 490 | \n",
+ " 2.225800 | \n",
+ "
\n",
+ " \n",
+ " | 500 | \n",
+ " 2.185200 | \n",
+ "
\n",
+ " \n",
+ " | 510 | \n",
+ " 2.116700 | \n",
+ "
\n",
+ " \n",
+ " | 520 | \n",
+ " 2.199900 | \n",
+ "
\n",
+ " \n",
+ " | 530 | \n",
+ " 2.308900 | \n",
+ "
\n",
+ " \n",
+ " | 540 | \n",
+ " 2.233000 | \n",
+ "
\n",
+ " \n",
+ " | 550 | \n",
+ " 2.086700 | \n",
+ "
\n",
+ " \n",
+ " | 560 | \n",
+ " 2.243200 | \n",
+ "
\n",
+ " \n",
+ " | 570 | \n",
+ " 2.195300 | \n",
+ "
\n",
+ " \n",
+ " | 580 | \n",
+ " 2.232300 | \n",
+ "
\n",
+ " \n",
+ " | 590 | \n",
+ " 2.163100 | \n",
+ "
\n",
+ " \n",
+ " | 600 | \n",
+ " 2.155500 | \n",
+ "
\n",
+ " \n",
+ " | 610 | \n",
+ " 2.266100 | \n",
+ "
\n",
+ " \n",
+ " | 620 | \n",
+ " 2.220200 | \n",
+ "
\n",
+ " \n",
+ " | 630 | \n",
+ " 2.120700 | \n",
+ "
\n",
+ " \n",
+ " | 640 | \n",
+ " 1.994400 | \n",
+ "
\n",
+ " \n",
+ " | 650 | \n",
+ " 2.347600 | \n",
+ "
\n",
+ " \n",
+ " | 660 | \n",
+ " 2.120000 | \n",
+ "
\n",
+ " \n",
+ " | 670 | \n",
+ " 2.190900 | \n",
+ "
\n",
+ " \n",
+ " | 680 | \n",
+ " 2.190100 | \n",
+ "
\n",
+ " \n",
+ " | 690 | \n",
+ " 2.143500 | \n",
+ "
\n",
+ " \n",
+ "
"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/root/miniconda3/lib/python3.10/site-packages/peft/utils/save_and_load.py:154: UserWarning: Could not find a config file in /root/autodl-tmp/qwen/Qwen2.5-7B-Instruct - will assume that the vocabulary was not modified.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/torch/utils/checkpoint.py:429: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/peft/utils/save_and_load.py:154: UserWarning: Could not find a config file in /root/autodl-tmp/qwen/Qwen2.5-7B-Instruct - will assume that the vocabulary was not modified.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/torch/utils/checkpoint.py:429: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/peft/utils/save_and_load.py:154: UserWarning: Could not find a config file in /root/autodl-tmp/qwen/Qwen2.5-7B-Instruct - will assume that the vocabulary was not modified.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/torch/utils/checkpoint.py:429: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/peft/utils/save_and_load.py:154: UserWarning: Could not find a config file in /root/autodl-tmp/qwen/Qwen2.5-7B-Instruct - will assume that the vocabulary was not modified.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/torch/utils/checkpoint.py:429: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/peft/utils/save_and_load.py:154: UserWarning: Could not find a config file in /root/autodl-tmp/qwen/Qwen2.5-7B-Instruct - will assume that the vocabulary was not modified.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/torch/utils/checkpoint.py:429: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/peft/utils/save_and_load.py:154: UserWarning: Could not find a config file in /root/autodl-tmp/qwen/Qwen2.5-7B-Instruct - will assume that the vocabulary was not modified.\n",
+ " warnings.warn(\n",
+ "/root/miniconda3/lib/python3.10/site-packages/torch/utils/checkpoint.py:429: UserWarning: torch.utils.checkpoint: please pass in use_reentrant=True or use_reentrant=False explicitly. The default value of use_reentrant will be updated to be False in the future. To maintain current behavior, pass use_reentrant=True. It is recommended that you use use_reentrant=False. Refer to docs for more details on the differences between the two variants.\n",
+ " warnings.warn(\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "TrainOutput(global_step=699, training_loss=2.5707558644176043, metrics={'train_runtime': 603.8093, 'train_samples_per_second': 18.527, 'train_steps_per_second': 1.158, 'total_flos': 4.578125275274342e+16, 'train_loss': 2.5707558644176043, 'epoch': 3.0})"
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "trainer.train()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8abb2327-458e-4e96-ac98-2141b5b97c8e",
+ "metadata": {},
+ "source": [
+ "# 合并加载模型"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "bd2a415a-a9ad-49ea-877f-243558a83bfc",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "e6dc6e1f8180464aa17ad958188e3aff",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Loading checkpoint shards: 0%| | 0/4 [00:00, ?it/s]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "WARNING:root:Some parameters are on the meta device device because they were offloaded to the cpu.\n",
+ "WARNING:root:Some parameters are on the meta device device because they were offloaded to the cpu.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "我是甄嬛,一个在后宫中挣扎求存的女子。\n"
+ ]
+ }
+ ],
+ "source": [
+ "from transformers import AutoModelForCausalLM, AutoTokenizer\n",
+ "import torch\n",
+ "from peft import PeftModel\n",
+ "\n",
+ "mode_path = '/root/autodl-tmp/qwen/Qwen2.5-7B-Instruct/'\n",
+ "lora_path = './output/Qwen2.5_instruct_lora/checkpoint-10' # 这里改称你的 lora 输出对应 checkpoint 地址\n",
+ "\n",
+ "# 加载tokenizer\n",
+ "tokenizer = AutoTokenizer.from_pretrained(mode_path, trust_remote_code=True)\n",
+ "\n",
+ "# 加载模型\n",
+ "model = AutoModelForCausalLM.from_pretrained(mode_path, device_map=\"auto\",torch_dtype=torch.bfloat16, trust_remote_code=True).eval()\n",
+ "\n",
+ "# 加载lora权重\n",
+ "model = PeftModel.from_pretrained(model, model_id=lora_path)\n",
+ "\n",
+ "prompt = \"你是谁?\"\n",
+ "inputs = tokenizer.apply_chat_template([{\"role\": \"user\", \"content\": \"假设你是皇帝身边的女人--甄嬛。\"},{\"role\": \"user\", \"content\": prompt}],\n",
+ " add_generation_prompt=True,\n",
+ " tokenize=True,\n",
+ " return_tensors=\"pt\",\n",
+ " return_dict=True\n",
+ " ).to('cuda')\n",
+ "\n",
+ "\n",
+ "gen_kwargs = {\"max_length\": 2500, \"do_sample\": True, \"top_k\": 1}\n",
+ "with torch.no_grad():\n",
+ " outputs = model.generate(**inputs, **gen_kwargs)\n",
+ " outputs = outputs[:, inputs['input_ids'].shape[1]:]\n",
+ " print(tokenizer.decode(outputs[0], skip_special_tokens=True))"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.8"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/models/Qwen2-vl-2B/04-Qwen2-vl-2B Lora 微调.md b/models/Qwen2-vl-2B/04-Qwen2-vl-2B Lora 微调.md
new file mode 100644
index 0000000..72fcf09
--- /dev/null
+++ b/models/Qwen2-vl-2B/04-Qwen2-vl-2B Lora 微调.md
@@ -0,0 +1,221 @@
+# Qwen2-vl 2B Lora 微调
+
+本节我们简要介绍如何基于 transformers、peft 等框架,对 Qwen2-vl 2B Lora 模型进行 Lora 微调。Lora 是一种高效微调方法,深入了解其原理可参见博客:[知乎|深入浅出 Lora](https://zhuanlan.zhihu.com/p/650197598)。
+
+这个教程会在同目录下给大家提供一个 [nodebook](./05-Qwen2-7B-Instruct%20Lora.ipynb) 文件,来让大家更好的学习。
+
+## 环境配置
+
+在完成基本环境配置和本地模型部署的情况下,你还需要安装一些第三方库,可以使用以下命令:
+
+```bash
+python -m pip install --upgrade pip
+# 更换 pypi 源加速库的安装
+pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
+
+pip install modelscope==1.18.0
+pip install transformers==4.44.2
+pip install streamlit==1.24.0
+pip install sentencepiece==0.2.0
+pip install accelerate==0.34.2
+pip install datasets==2.20.0
+pip install peft==0.11.1
+
+```
+
+
+
+> 注意:flash-attn 安装会比较慢,大概需要十几分钟。
+
+在本节教程里,我们将微调数据集放置在根目录 [/dataset](../dataset/huanhuan.json)。
+
+## 模型下载
+使用 `modelscope` 中的 `snapshot_download` 函数下载模型,第一个参数为模型名称,参数 `cache_dir` 为模型的下载路径。
+
+新建 `model_download.py` 文件输入以下代码,并运行 `python model_download.py` 执行下载。
+
+此处使用 `modelscope` 提供的 `snapshot_download` 函数进行下载,该方法对国内的用户十分友好。
+
+在 /root/autodl-tmp 路径下新建 model_download.py 文件并在其中输入以下内容,粘贴代码后请及时保存文件,如下图所示。并运行 `python /root/autodl-tmp/model_download.py` 执行下载,模型大小为 15GB,下载模型大概需要 5 分钟。
+
+```python
+import torch
+from modelscope import snapshot_download, AutoModel, AutoTokenizer
+import os
+model_dir = snapshot_download('qwen/Qwen2.5-7B-Instruct', cache_dir='/root/autodl-tmp', revision='master')
+```
+
+## 指令集构建
+
+LLM 的微调一般指指令微调过程。所谓指令微调,是说我们使用的微调数据形如:
+
+```json
+{
+ "instruction": "回答以下用户问题,仅输出答案。",
+ "input": "1+1等于几?",
+ "output": "2"
+}
+```
+
+其中,`instruction` 是用户指令,告知模型其需要完成的任务;`input` 是用户输入,是完成用户指令所必须的输入内容;`output` 是模型应该给出的输出。
+
+即我们的核心训练目标是让模型具有理解并遵循用户指令的能力。因此,在指令集构建时,我们应针对我们的目标任务,针对性构建任务指令集。例如,在本节我们使用由笔者合作开源的 [Chat-甄嬛](https://github.com/KMnO4-zx/huanhuan-chat) 项目作为示例,我们的目标是构建一个能够模拟甄嬛对话风格的个性化 LLM,因此我们构造的指令形如:
+
+```json
+{
+ "instruction": "你是谁?",
+ "input": "",
+ "output": "家父是大理寺少卿甄远道。"
+}
+```
+
+我们所构造的全部指令数据集在根目录下。
+
+## 数据格式化
+
+`Lora` 训练的数据是需要经过格式化、编码之后再输入给模型进行训练的,如果是熟悉 `Pytorch` 模型训练流程的同学会知道,我们一般需要将输入文本编码为 input_ids,将输出文本编码为 `labels`,编码之后的结果都是多维的向量。我们首先定义一个预处理函数,这个函数用于对每一个样本,编码其输入、输出文本并返回一个编码后的字典:
+
+```python
+def process_func(example):
+ MAX_LENGTH = 384 # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性
+ input_ids, attention_mask, labels = [], [], []
+ instruction = tokenizer(f"<|im_start|>system\n现在你要扮演皇帝身边的女人--甄嬛<|im_end|>\n<|im_start|>user\n{example['instruction'] + example['input']}<|im_end|>\n<|im_start|>assistant\n", add_special_tokens=False) # add_special_tokens 不在开头加 special_tokens
+ response = tokenizer(f"{example['output']}", add_special_tokens=False)
+ input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
+ attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1] # 因为eos token咱们也是要关注的所以 补充为1
+ labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id]
+ if len(input_ids) > MAX_LENGTH: # 做一个截断
+ input_ids = input_ids[:MAX_LENGTH]
+ attention_mask = attention_mask[:MAX_LENGTH]
+ labels = labels[:MAX_LENGTH]
+ return {
+ "input_ids": input_ids,
+ "attention_mask": attention_mask,
+ "labels": labels
+ }
+```
+
+`Qwen2` 采用的 `Prompt Template`格式如下:
+
+```text
+<|im_start|>system
+You are a helpful assistant.<|im_end|>
+<|im_start|>user
+你是谁?<|im_end|>
+<|im_start|>assistant
+我是一个有用的助手。<|im_end|>
+```
+
+## 加载 tokenizer 和半精度模型
+
+模型以半精度形式加载,如果你的显卡比较新的话,可以用 `torch.bfolat`形式加载。对于自定义的模型一定要指定 `trust_remote_code`参数为 `True`。
+
+```python
+tokenizer = AutoTokenizer.from_pretrained('/root/autodl-tmp/qwen/Qwen2.5-7B-Instruct/', use_fast=False, trust_remote_code=True)
+
+model = AutoModelForCausalLM.from_pretrained('/root/autodl-tmp/qwen/Qwen2.5-7B-Instruct/', device_map="auto",torch_dtype=torch.bfloat16)
+```
+
+## 定义 LoraConfig
+
+`LoraConfig`这个类中可以设置很多参数,但主要的参数没多少,简单讲一讲,感兴趣的同学可以直接看源码。
+
+- `task_type`:模型类型
+- `target_modules`:需要训练的模型层的名字,主要就是 `attention`部分的层,不同的模型对应的层的名字不同,可以传入数组,也可以字符串,也可以正则表达式。
+- `r`:`lora`的秩,具体可以看 `Lora`原理
+- `lora_alpha`:`Lora alaph`,具体作用参见 `Lora` 原理
+
+`Lora`的缩放是啥嘞?当然不是 `r`(秩),这个缩放就是 `lora_alpha/r`, 在这个 `LoraConfig`中缩放就是 4 倍。
+
+```python
+config = LoraConfig(
+ task_type=TaskType.CAUSAL_LM,
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
+ inference_mode=False, # 训练模式
+ r=8, # Lora 秩
+ lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理
+ lora_dropout=0.1# Dropout 比例
+)
+```
+
+## 自定义 TrainingArguments 参数
+
+`TrainingArguments`这个类的源码也介绍了每个参数的具体作用,当然大家可以来自行探索,这里就简单说几个常用的。
+
+- `output_dir`:模型的输出路径
+- `per_device_train_batch_size`:顾名思义 `batch_size`
+- `gradient_accumulation_steps`: 梯度累加,如果你的显存比较小,那可以把 `batch_size` 设置小一点,梯度累加增大一些。
+- `logging_steps`:多少步,输出一次 `log`
+- `num_train_epochs`:顾名思义 `epoch`
+- `gradient_checkpointing`:梯度检查,这个一旦开启,模型就必须执行 `model.enable_input_require_grads()`,这个原理大家可以自行探索,这里就不细说了。
+
+```python
+args = TrainingArguments(
+ output_dir="./output/Qwen2.5_instruct_lora",
+ per_device_train_batch_size=4,
+ gradient_accumulation_steps=4,
+ logging_steps=10,
+ num_train_epochs=3,
+ save_steps=100,
+ learning_rate=1e-4,
+ save_on_each_node=True,
+ gradient_checkpointing=True
+)
+```
+
+## 使用 Trainer 训练
+
+```python
+trainer = Trainer(
+ model=model,
+ args=args,
+ train_dataset=tokenized_id,
+ data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
+)
+trainer.train()
+```
+
+## 加载 lora 权重推理
+
+训练好了之后可以使用如下方式加载 `lora`权重进行推理:
+
+```python
+from transformers import AutoModelForCausalLM, AutoTokenizer
+import torch
+from peft import PeftModel
+
+model_path = '/root/autodl-tmp/qwen/Qwen2.5-7B-Instruct/'
+lora_path = 'lora_path'
+
+# 加载tokenizer
+tokenizer = AutoTokenizer.from_pretrained(model_path)
+
+# 加载模型
+model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto",torch_dtype=torch.bfloat16)
+
+# 加载lora权重
+model = PeftModel.from_pretrained(model, model_id=lora_path, config=config)
+
+prompt = "你是谁?"
+messages = [
+ {"role": "system", "content": "现在你要扮演皇帝身边的女人--甄嬛"},
+ {"role": "user", "content": prompt}
+]
+
+text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
+
+model_inputs = tokenizer([text], return_tensors="pt").to('cuda')
+
+generated_ids = model.generate(
+ model_inputs.input_ids,
+ max_new_tokens=512
+)
+generated_ids = [
+ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
+]
+
+response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
+
+print(response)
+```