{ "cells": [ { "cell_type": "markdown", "id": "de53995b-32ed-4722-8cac-ba104c8efacb", "metadata": {}, "source": [ "# 导入环境" ] }, { "cell_type": "code", "execution_count": 1, "id": "52fac949-4150-4091-b0c3-2968ab5e385c", "metadata": { "tags": [] }, "outputs": [], "source": [ "from datasets import Dataset\n", "import pandas as pd\n", "from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig" ] }, { "cell_type": "code", "execution_count": 2, "id": "e098d9eb", "metadata": { "tags": [] }, "outputs": [], "source": [ "# 将JSON文件转换为CSV文件\n", "df = pd.read_json('./huanhuan.json')\n", "ds = Dataset.from_pandas(df)" ] }, { "cell_type": "code", "execution_count": 3, "id": "8ac92d42-efae-49b1-a00e-ccaa75b98938", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "{'instruction': ['小姐,别的秀女都在求中选,唯有咱们小姐想被撂牌子,菩萨一定记得真真儿的——',\n", " '这个温太医啊,也是古怪,谁不知太医不得皇命不能为皇族以外的人请脉诊病,他倒好,十天半月便往咱们府里跑。',\n", " '嬛妹妹,刚刚我去府上请脉,听甄伯母说你来这里进香了。'],\n", " 'input': ['', '', ''],\n", " 'output': ['嘘——都说许愿说破是不灵的。', '你们俩话太多了,我该和温太医要一剂药,好好治治你们。', '出来走走,也是散心。']}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ds[:3]" ] }, { "cell_type": "markdown", "id": "51d05e5d-d14e-4f03-92be-9a9677d41918", "metadata": {}, "source": [ "# 处理数据集" ] }, { "cell_type": "code", "execution_count": 4, "id": "74ee5a67-2e55-4974-b90e-cbf492de500a", "metadata": { "tags": [] }, "outputs": [], "source": [ "tokenizer = AutoTokenizer.from_pretrained('/root/autodl-tmp/Lucachen/gemma2b', use_fast=False, trust_remote_code=True)" ] }, { "cell_type": "code", "execution_count": 5, "id": "2503a5fa-9621-4495-9035-8e7ef6525691", "metadata": { "tags": [] }, "outputs": [], "source": [ "def process_func(example):\n", " MAX_LENGTH = 384 # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性\n", " input_ids, attention_mask, labels = [], [], []\n", " instruction = tokenizer(f\"system\\n现在你要扮演皇帝身边的女人--甄嬛\\nuser\\n{example['instruction'] + example['input']}\\nmodel\\n\", add_special_tokens=False)\n", " #response = tokenizer(f\"{example['output']}\", add_special_tokens=False)\n", " response = tokenizer(f\"{example['output']}model\", add_special_tokens=False)\n", " input_ids = instruction[\"input_ids\"] + response[\"input_ids\"]\n", " attention_mask = instruction[\"attention_mask\"] + response[\"attention_mask\"] + [1] # 因为eos token咱们也是要关注的所以 补充为1\n", " labels = [-100] * len(instruction[\"input_ids\"]) + response[\"input_ids\"]\n", " if len(input_ids) > MAX_LENGTH: # 做一个截断\n", " input_ids = input_ids[:MAX_LENGTH]\n", " attention_mask = attention_mask[:MAX_LENGTH]\n", " labels = labels[:MAX_LENGTH]\n", " return {\n", " \"input_ids\": input_ids,\n", " \"attention_mask\": attention_mask,\n", " \"labels\": labels\n", " }" ] }, { "cell_type": "code", "execution_count": 6, "id": "84f870d6-73a9-4b0f-8abf-687b32224ad8", "metadata": { "tags": [] }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b7e2b2803eb84c17b8c1716309b4c263", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Map: 0%| | 0/3729 [00:00system\n", "现在你要扮演皇帝身边的女人--甄嬛\n", "user\n", "小姐,别的秀女都在求中选,唯有咱们小姐想被撂牌子,菩萨一定记得真真儿的——\n", "model\n", "嘘——都说许愿说破是不灵的。model\n" ] } ], "source": [ "print(tokenizer.decode(tokenized_id[0]['input_ids']))" ] }, { "cell_type": "code", "execution_count": 8, "id": "97f16f66-324a-454f-8cc3-ef23b100ecff", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "'你们俩话太多了,我该和温太医要一剂药,好好治治你们。model'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tokenizer.decode(list(filter(lambda x: x != -100, tokenized_id[1][\"labels\"])))" ] }, { "cell_type": "markdown", "id": "424823a8-ed0d-4309-83c8-3f6b1cdf274c", "metadata": {}, "source": [ "# 创建模型" ] }, { "cell_type": "code", "execution_count": 9, "id": "170764e5-d899-4ef4-8c53-36f6dec0d198", "metadata": { "tags": [] }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3fe409299b30432d9387cb2387d829f1", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Loading checkpoint shards: 0%| | 0/2 [00:00, auto_mapping=None, base_model_name_or_path=None, revision=None, task_type=, inference_mode=False, r=8, target_modules={'gate_proj', 'q_proj', 'v_proj', 'down_proj', 'k_proj', 'o_proj', 'up_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": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from peft import LoraConfig, TaskType, get_peft_model\n", "\n", "config = LoraConfig(\n", " task_type=TaskType.CAUSAL_LM, \n", " target_modules=[\"q_proj\", \"k_proj\", \"v_proj\", \"o_proj\", \"gate_proj\", \"up_proj\", \"down_proj\"],\n", " inference_mode=False, # 训练模式\n", " r=8, # Lora 秩\n", " lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理\n", " lora_dropout=0.1# Dropout 比例\n", ")\n", "config" ] }, { "cell_type": "code", "execution_count": 13, "id": "2c2489c5-eaab-4e1f-b06a-c3f914b4bf8e", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "LoraConfig(peft_type=, auto_mapping=None, base_model_name_or_path='/root/autodl-tmp/Lucachen/gemma2b', revision=None, task_type=, inference_mode=False, r=8, target_modules={'gate_proj', 'q_proj', 'v_proj', 'down_proj', 'k_proj', 'o_proj', 'up_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: 9,805,824 || all params: 2,515,978,240 || trainable%: 0.3897420034920493\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/gemma2b\",\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 07:54, Epoch 2/3]\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StepTraining Loss
107.766300
205.784600
305.256000
404.794900
504.593100
604.573600
704.399200
804.482700
904.470100
1004.386200
1104.299400
1204.318600
1304.283000
1404.255000
1504.264900
1604.314900
1704.223300
1803.979600
1904.153200
2004.085700
2104.112500
2204.050800
2304.158200
2403.960000
2503.675600
2603.606900
2703.556100
2803.701300
2903.714200
3003.572500
3103.578600
3203.628900
3303.634500
3403.615700
3503.655000
3603.661400
3703.595800
3803.629100
3903.587600
4003.512100
4103.712600
4203.514200
4303.637700
4403.552600
4503.532300
4603.644300
4703.373200
4803.050600
4903.166200
5003.160500
5103.029400
5203.079600
5303.067800
5403.144000
5503.175200
5603.246800
5703.166300
5803.163200
5903.172500
6003.095900
6103.189200
6203.112100
6303.149100
6403.184200
6503.137400
6603.151700
6703.192500
6802.981800
6903.087900

" ], "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/Lucachen/gemma2b - 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/Lucachen/gemma2b - 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/Lucachen/gemma2b - 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/Lucachen/gemma2b - 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/Lucachen/gemma2b - 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/Lucachen/gemma2b - 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=3.7700800834295576, metrics={'train_runtime': 475.6661, 'train_samples_per_second': 23.519, 'train_steps_per_second': 1.47, 'total_flos': 1.318330043547648e+16, 'train_loss': 3.7700800834295576, 'epoch': 2.996784565916399})" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trainer.train()" ] }, { "cell_type": "markdown", "id": "f93a86ec", "metadata": {}, "source": [ "# 保存 LoRA 和 tokenizer 结果\n" ] }, { "cell_type": "code", "execution_count": 18, "id": "4e376229", "metadata": {}, "outputs": [ { "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/Lucachen/gemma2b - will assume that the vocabulary was not modified.\n", " warnings.warn(\n" ] }, { "data": { "text/plain": [ "('./gemma2b_lora/tokenizer_config.json',\n", " './gemma2b_lora/special_tokens_map.json',\n", " './gemma2b_lora/tokenizer.model',\n", " './gemma2b_lora/added_tokens.json')" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "peft_model_id=\"./gemma2b_lora\"\n", "trainer.model.save_pretrained(peft_model_id)\n", "tokenizer.save_pretrained(peft_model_id)" ] }, { "cell_type": "markdown", "id": "9823e3c7", "metadata": {}, "source": [ "# 加载 lora 权重推理" ] }, { "cell_type": "code", "execution_count": null, "id": "12dad881", "metadata": {}, "outputs": [], "source": [ "from transformers import AutoModelForCausalLM, AutoTokenizer\n", "import torch\n", "from peft import PeftModel\n", "\n", "mode_path = '/root/autodl-tmp/Lucachen/gemma2b'\n", "lora_path = './gemma2b_lora'\n", "\n", "# 加载tokenizer\n", "tokenizer = AutoTokenizer.from_pretrained(mode_path)\n", "\n", "# 加载模型\n", "model = AutoModelForCausalLM.from_pretrained(mode_path, device_map=\"auto\",torch_dtype=torch.bfloat16)\n", "\n", "# 加载lora权重\n", "model = PeftModel.from_pretrained(model, model_id=lora_path, config=config)\n", "\n", "prompt = \"你是谁?\"\n", "messages = [\n", " # {\"role\": \"system\", \"content\": \"现在你要扮演皇帝身边的女人--甄嬛\"},\n", " {\"role\": \"user\", \"content\": prompt}\n", "]\n", "\n", "text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)\n", "\n", "model_inputs = tokenizer([text], return_tensors=\"pt\").to('cuda')\n", "\n", "generated_ids = model.generate(\n", " model_inputs.input_ids,\n", " max_new_tokens=512\n", ")\n", "generated_ids = [\n", " output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)\n", "]\n", "\n", "response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]\n", "\n", "print(response)" ] } ], "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 }