{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lora 实战" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step1 导入相关包" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/xhr/anaconda3/envs/llm/lib/python3.9/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", " from .autonotebook import tqdm as notebook_tqdm\n" ] }, { "data": { "text/plain": [ "'/CV/xhr/xhr_project/LLM_learn/transformers-code-master/self-llm'" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from datasets import Dataset\n", "from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer\n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step2 加载数据集" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 将JSON文件转换为CSV文件\n", "df = pd.read_json('../../dataset/huanhuan.json')\n", "ds = Dataset.from_pandas(df)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "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", "metadata": {}, "source": [ "## Step3 数据集预处理" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ChatGLMTokenizer(name_or_path='/CV/xhr/xhr_project/LLM_learn/transformers-code-master/model/chatglm3-6b', vocab_size=64798, model_max_length=1000000000000000019884624838656, is_fast=False, padding_side='left', truncation_side='right', special_tokens={'eos_token': '', 'unk_token': '', 'pad_token': ''}, clean_up_tokenization_spaces=False), added_tokens_decoder={\n", "\t\n", "}" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tokenizer = AutoTokenizer.from_pretrained(\"/root/autodl-tmp/ZhipuAI/chatglm3-6b\", trust_remote_code=True)\n", "tokenizer" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[64790, 64792, 35182, 55671, 31123, 34752, 55276, 54740, 32595, 54806, 54538, 54878, 31123, 37963, 35662, 36028, 54695, 54732, 60136, 49127, 31123, 34856, 31781, 33498, 54792, 54792, 40972, 16747]\n" ] } ], "source": [ "print(tokenizer.encode(ds[0]['instruction']))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "([64794, 30910, 13, 42579, 34526, 34975, 33690, 32587, 35524, 621, 52339],\n", " '<|system|> \\n 现在你要扮演皇帝身边的女人--甄嬛')" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "demo_token = tokenizer.build_single_message('system', \"\", \"现在你要扮演皇帝身边的女人--甄嬛\")\n", "demo_token, tokenizer.decode(demo_token)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(64790, '')" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tokenizer.get_command(\"[gMASK]\"), tokenizer._convert_id_to_token(+tokenizer.eos_token_id)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 调用api处理数据" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instruction = \"\\n\".join([ds[0][\"instruction\"], ds[0][\"input\"]]).strip() # query\n", "instruction = tokenizer.build_chat_input(instruction, history=[], role=\"user\")\n", "response = tokenizer(\"\\n\" + ds[0][\"output\"], add_special_tokens=False)\n", "input_ids = instruction[\"input_ids\"][0].numpy().tolist() + response[\"input_ids\"] + [tokenizer.eos_token_id]\n", "tokenizer.decode(input_ids)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 手动拆解数据处理" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "prompt = [tokenizer.get_command(\"<|system|>\")] + tokenizer.encode(\"现在你要扮演皇帝身边的女人--甄嬛\\n \", add_special_tokens=False)\n", "instruction_ = [tokenizer.get_command(\"<|user|>\")] + tokenizer.encode(\"\\n \" + \"\\n\".join([ds[0][\"instruction\"], ds[0][\"input\"]]).strip(), add_special_tokens=False,max_length=512) + [tokenizer.get_command(\"<|assistant|>\")]\n", "instruction = tokenizer.encode(prompt + instruction_)\n", "response = tokenizer.encode(\"\\n\" + ds[0][\"output\"], add_special_tokens=False)\n", "input_ids = instruction + response + [tokenizer.eos_token_id]\n", "tokenizer.decode(input_ids)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def process_func(example):\n", " MAX_LENGTH = 512\n", " input_ids, labels = [], []\n", " prompt = [tokenizer.get_command(\"<|system|>\")] + tokenizer.encode(\"现在你要扮演皇帝身边的女人--甄嬛\\n \", add_special_tokens=False)\n", " instruction_ = [tokenizer.get_command(\"<|user|>\")] + tokenizer.encode(\"\\n \" + \"\\n\".join([example[\"instruction\"], example[\"input\"]]).strip(), add_special_tokens=False,max_length=512) + [tokenizer.get_command(\"<|assistant|>\")]\n", " instruction = tokenizer.encode(prompt + instruction_)\n", " response = tokenizer.encode(\"\\n\" + example[\"output\"], add_special_tokens=False)\n", " input_ids = instruction + response + [tokenizer.eos_token_id]\n", " labels = [tokenizer.pad_token_id] * len(instruction) + response + [tokenizer.eos_token_id]\n", " pad_len = MAX_LENGTH - len(input_ids)\n", " # print()\n", " input_ids += [tokenizer.pad_token_id] * pad_len\n", " labels += [tokenizer.pad_token_id] * pad_len\n", " labels = [(l if l != tokenizer.pad_token_id else -100) for l in labels]\n", "\n", " return {\n", " \"input_ids\": input_ids,\n", " \"labels\": labels\n", " }" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Map: 100%|██████████| 3729/3729 [00:00<00:00, 4034.25 examples/s]\n" ] }, { "data": { "text/plain": [ "Dataset({\n", " features: ['input_ids', 'labels'],\n", " num_rows: 3729\n", "})" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tokenized_ds = ds.map(process_func, remove_columns=ds.column_names)\n", "tokenized_ds" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tokenizer.decode(tokenized_ds[1][\"input_ids\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tokenizer.decode(list(filter(lambda x: x != -100, tokenized_ds[1][\"labels\"])))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step4 创建模型" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Loading checkpoint shards: 100%|██████████| 7/7 [00:03<00:00, 1.96it/s]\n" ] } ], "source": [ "model = AutoModelForCausalLM.from_pretrained(\"/root/autodl-tmp/ZhipuAI/chatglm3-6b\", trust_remote_code=True, low_cpu_mem_usage=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lora" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PEFT Step1 配置文件" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- target_modules也可以传入正则项,比如以h.1结尾的query_key_value:\".*\\.1.*query_key_value\" \n", "- modules_to_save指定的是除了拆成lora的模块,其他的模块可以完整的指定训练。" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "LoraConfig(peft_type=, auto_mapping=None, base_model_name_or_path=None, revision=None, task_type=, inference_mode=False, r=8, target_modules={'query_key_value'}, lora_alpha=32, lora_dropout=0.0, fan_in_fan_out=False, bias='none', modules_to_save=None, init_lora_weights=True, layers_to_transform=None, layers_pattern=None, rank_pattern={}, alpha_pattern={})" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from peft import LoraConfig, TaskType, get_peft_model\n", "\n", "# model = AutoModelForCausalLM.from_pretrained(\"/root/autodl-tmp/ZhipuAI/chatglm3-6b\", low_cpu_mem_usage=True)\n", "config = LoraConfig(task_type=TaskType.CAUSAL_LM, target_modules={\"query_key_value\"}, r=8, lora_alpha=32)\n", "config" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PEFT Step2 创建模型" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "model = get_peft_model(model, config)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "trainable params: 1,949,696 || all params: 6,245,533,696 || trainable%: 0.031217444255383614\n" ] } ], "source": [ "model.print_trainable_parameters()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step5 配置训练参数" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Data collator\n", "data_collator = DataCollatorForSeq2Seq(\n", " tokenizer,\n", " model=model,\n", " label_pad_token_id=-100,\n", " pad_to_multiple_of=None,\n", " padding=False\n", ")" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "args = TrainingArguments(\n", " output_dir=\"./huanhuan\",\n", " per_device_train_batch_size=1,\n", " gradient_accumulation_steps=8,\n", " logging_steps=20,\n", " num_train_epochs=1\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step6 创建训练器" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "trainer = Trainer(\n", " model=model,\n", " args=args,\n", " train_dataset=tokenized_ds,\n", " data_collator=data_collator,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step7 模型训练" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "trainer.train()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step8 模型推理" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'[gMASK]sop <|system|>\\n现在你要扮演皇帝身边的女人--甄嬛\\n<|user|>\\n 你是谁?<|assistant|>\\n 我是甄嬛,家父是大理寺少卿甄远道。'" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model = model.cuda()\n", "ipt = tokenizer(\"<|system|>\\n现在你要扮演皇帝身边的女人--甄嬛\\n<|user|>\\n {}\\n{}\".format(\"你是谁?\", \"\").strip() + \"<|assistant|>\\n\", return_tensors=\"pt\").to(model.device)\n", "tokenizer.decode(model.generate(**ipt, max_length=128, do_sample=True)[0], skip_special_tokens=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "transformers", "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.9.18" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }