readme.md
1 # 大模型训练指南 2 3 ## 一、导出聊天记录 4 5 导出json格式的聊天记录。 6 7  8 9 如果你想合并多个联系人的数据,可以直接运行下面的代码合并 10 11 ```python 12 import json 13 import os 14 15 data_dir = r'E:\Project\Python\MemoTrace\data\聊天记录' 16 17 dev_res = [] 18 train_res = [] 19 20 for filepath, dirnames, filenames in os.walk(data_dir): 21 for filename in filenames: 22 if filename.endswith('.json'): 23 print(filename, filepath) 24 filepath_ = os.path.join(filepath, filename) 25 with open(filepath_, 'r', encoding='utf-8') as f: 26 data = json.load(f) 27 if data: 28 if filename.endswith('train.json'): 29 train_res += data 30 else: 31 dev_res += data 32 33 with open('train.json', 'w', encoding='utf-8') as f: 34 json.dump(train_res, f, ensure_ascii=False, indent=4) 35 36 with open('dev.json', 'w', encoding='utf-8') as f: 37 json.dump(dev_res, f, ensure_ascii=False, indent=4) 38 39 ``` 40 41 你现在应该有两个文件,dev.json(验证集)和train.json(训练集) 42 43 ## 二、下载ChatGLM3-6B模型 44 45 下载地址:[https://github.com/THUDM/ChatGLM3](https://github.com/THUDM/ChatGLM3) 46 47 ## 使用方式 48 49 ### 环境安装 50 51 首先需要下载本仓库: 52 53 ```shell 54 git clone https://github.com/THUDM/ChatGLM3 55 cd ChatGLM3 56 ``` 57 58 然后使用 pip 安装依赖: 59 60 ``` 61 pip install -r requirements.txt 62 ``` 63 64 + 为了保证 `torch` 的版本正确,请严格按照 [官方文档](https://pytorch.org/get-started/locally/) 的说明安装。 65 + **如果遇到问题,请参照ChatGLM3项目的解决方案,不要在本项目中提问** 66 67 ## 三、ChatGLM3-6B 微调 68 69 本目录提供 ChatGLM3-6B 模型的微调示例,包括全量微调和 P-Tuning v2。格式上,提供多轮对话微调样例和输入输出格式微调样例。 70 71 如果将模型下载到了本地,本文和代码中的 `THUDM/chatglm3-6b` 字段均应替换为相应地址以从本地加载模型。 72 73 运行示例需要 `python>=3.10`,除基础的 `torch` 依赖外,示例代码运行还需要依赖。 74 75 76 ```bash 77 pip install -r requirements.txt 78 ``` 79 80 ## 测试硬件标准 81 82 我们仅提供了单机多卡/多机多卡的运行示例,因此您需要至少一台具有多个 GPU 的机器。本仓库中的**默认配置文件**中,我们记录了显存的占用情况: 83 84 + SFT 全量微调: 4张显卡平均分配,每张显卡占用 `48346MiB` 显存。 85 + P-TuningV2 微调: 1张显卡,占用 `18426MiB` 显存。 86 + LORA 微调: 1张显卡,占用 `14082MiB` 显存。 87 88 > 请注意,该结果仅供参考,对于不同的参数,显存占用可能会有所不同。请结合你的硬件情况进行调整。 89 90 > 请注意,我们仅仅使用英伟达 Hopper(代表显卡:H100) 和 Ampère(代表显卡:A100) 架构和系列显卡做过测试。如果您使用其他架构的显卡,可能会出现 91 > 1. 未知的训练问题 / 显存占用与上述有误差。 92 > 2. 架构过低而不支持某些特性。 93 > 3. 推理效果问题。 94 > 以上三种情况为社区曾经遇到过的问题,虽然概率极地,如果您遇到了以上问题,可以尝试在社区中解决。 95 96 ## 多轮对话格式 97 98 多轮对话微调示例采用 ChatGLM3 对话格式约定,对不同角色添加不同 `loss_mask` 从而在一遍计算中为多轮回复计算 `loss`。 99 100 对于数据文件,样例采用如下格式 101 102 如果您仅希望微调模型的对话能力,而非工具能力,您应该按照以下格式整理数据。 103 104 ```json 105 [ 106 { 107 "conversations": [ 108 { 109 "role": "system", 110 "content": "<system prompt text>" 111 }, 112 { 113 "role": "user", 114 "content": "<user prompt text>" 115 }, 116 { 117 "role": "assistant", 118 "content": "<assistant response text>" 119 }, 120 // ... Muti Turn 121 { 122 "role": "user", 123 "content": "<user prompt text>" 124 }, 125 { 126 "role": "assistant", 127 "content": "<assistant response text>" 128 } 129 ] 130 } 131 // ... 132 ] 133 ``` 134 135 **请注意,这种方法在微调的step较多的情况下会影响到模型的工具调用功能** 136 137 - `system` 角色为可选角色,但若存在 `system` 角色,其必须出现在 `user` 138 角色之前,且一个完整的对话数据(无论单轮或者多轮对话)只能出现一次 `system` 角色。 139 140 ## 数据集格式示例 141 142 这里以 AdvertiseGen 数据集为例, 143 您可以从 [Google Drive](https://drive.google.com/file/d/13_vf0xRTQsyneRKdD1bZIr93vBGOczrk/view?usp=sharing) 144 或者 [Tsinghua Cloud](https://cloud.tsinghua.edu.cn/f/b3f119a008264b1cabd1/?dl=1) 下载 AdvertiseGen 数据集。 145 将解压后的 AdvertiseGen 目录放到 `data` 目录下并自行转换为如下格式数据集。 146 147 > 请注意,现在的微调代码中加入了验证集,因此,对于一组完整的微调数据集,必须包含训练数据集和验证数据集,测试数据集可以不填写。或者直接用验证数据集代替。 148 149 ``` 150 {"conversations": [{"role": "user", "content": "类型#裙*裙长#半身裙"}, {"role": "assistant", "content": "这款百搭时尚的仙女半身裙,整体设计非常的飘逸随性,穿上之后每个女孩子都能瞬间变成小仙女啦。料子非常的轻盈,透气性也很好,穿到夏天也很舒适。"}]} 151 ``` 152 153 ## 配置文件 154 155 微调配置文件位于 `config` 目录下,包括以下文件: 156 157 1. `ds_zereo_2 / ds_zereo_3.json`: deepspeed 配置文件。 158 2. `lora.yaml / ptuning.yaml / sft.yaml`: 模型不同方式的配置文件,包括模型参数、优化器参数、训练参数等。 部分重要参数解释如下: 159 + data_config 部分 160 + train_file: 训练数据集的文件路径。 161 + val_file: 验证数据集的文件路径。 162 + test_file: 测试数据集的文件路径。 163 + num_proc: 在加载数据时使用的进程数量。 164 + max_input_length: 输入序列的最大长度。 165 + max_output_length: 输出序列的最大长度。 166 + training_args 部分 167 + output_dir: 用于保存模型和其他输出的目录。 168 + max_steps: 训练的最大步数。 169 + per_device_train_batch_size: 每个设备(如 GPU)的训练批次大小。 170 + dataloader_num_workers: 加载数据时使用的工作线程数量。 171 + remove_unused_columns: 是否移除数据中未使用的列。 172 + save_strategy: 模型保存策略(例如,每隔多少步保存一次)。 173 + save_steps: 每隔多少步保存一次模型。 174 + log_level: 日志级别(如 info)。 175 + logging_strategy: 日志记录策略。 176 + logging_steps: 每隔多少步记录一次日志。 177 + per_device_eval_batch_size: 每个设备的评估批次大小。 178 + evaluation_strategy: 评估策略(例如,每隔多少步进行一次评估)。 179 + eval_steps: 每隔多少步进行一次评估。 180 + predict_with_generate: 是否使用生成模式进行预测。 181 + generation_config 部分 182 + max_new_tokens: 生成的最大新 token 数量。 183 + peft_config 部分 184 + peft_type: 使用的参数有效调整类型(如 LORA)。 185 + task_type: 任务类型,这里是因果语言模型(CAUSAL_LM)。 186 + Lora 参数: 187 + r: LoRA 的秩。 188 + lora_alpha: LoRA 的缩放因子。 189 + lora_dropout: 在 LoRA 层使用的 dropout 概率 190 + P-TuningV2 参数: 191 + num_virtual_tokens: 虚拟 token 的数量。 192 193 ## 开始微调 194 195 通过以下代码执行 **单机多卡/多机多卡** 运行,这是使用 `deepspeed` 作为加速方案的,您需要安装 `deepspeed`。 196 197 ```angular2html 198 cd finetune_demo 199 OMP_NUM_THREADS=1 torchrun --standalone --nnodes=1 --nproc_per_node=8 finetune_hf.py data/AdvertiseGen/ THUDM/chatglm3-6b configs/lora.yaml configs/ds_zero_2.json 200 ``` 201 202 通过以下代码执行 **单机单卡** 运行。 203 204 ```angular2html 205 cd finetune_demo 206 python finetune_hf.py data/AdvertiseGen/ THUDM/chatglm3-6b configs/lora.yaml 207 ``` 208 209 ## 从保存点进行微调 210 211 如果按照上述方式进行训练,每次微调都会从头开始,如果你想从训练一半的模型开始微调,你可以加入第四个参数,这个参数有两种传入方式: 212 213 1. `yes`, 自动从最后一个保存的 Checkpoint开始训练 214 2. `XX`, 断点号数字 例 `600` 则从序号600 Checkpoint开始训练 215 216 例如,这就是一个从最后一个保存点继续微调的示例代码 217 218 ```angular2html 219 cd finetune_demo 220 python finetune_hf.py data/AdvertiseGen/ THUDM/chatglm3-6b configs/lora.yaml yes 221 ``` 222 223 ## 使用微调后的模型 224 225 ### 在 inference_hf.py 中验证微调后的模型 226 227 您可以在 `finetune_demo/inference_hf.py` 中使用我们的微调后的模型,仅需要一行代码就能简单的进行测试。 228 229 ```angular2html 230 python inference_hf.py your_finetune_path --prompt your prompt 231 ``` 232 233 这样,得到的回答就微调后的回答了。 234 235 ### 在本仓库的其他 demo 或者外部仓库使用微调后的模型 236 237 您可以在任何一个 demo 内使用我们的 `lora` 和 全参微调的模型。这需要你自己按照以下教程进行修改代码。 238 239 1. 使用`finetune_demo/inference_hf.py`中读入模型的方式替换 demo 中读入模型的方式。 240 241 > 请注意,对于 LORA 和 P-TuningV2 我们没有合并训练后的模型,而是在`adapter_config.json` 242 > 中记录了微调型的路径,如果你的原始模型位置发生更改,则你应该修改`adapter_config.json`中`base_model_name_or_path`的路径。 243 244 ```python 245 def load_model_and_tokenizer( 246 model_dir: Union[str, Path], trust_remote_code: bool = True 247 ) -> tuple[ModelType, TokenizerType]: 248 model_dir = _resolve_path(model_dir) 249 if (model_dir / 'adapter_config.json').exists(): 250 model = AutoPeftModelForCausalLM.from_pretrained( 251 model_dir, trust_remote_code=trust_remote_code, device_map='auto' 252 ) 253 tokenizer_dir = model.peft_config['default'].base_model_name_or_path 254 else: 255 model = AutoModelForCausalLM.from_pretrained( 256 model_dir, trust_remote_code=trust_remote_code, device_map='auto' 257 ) 258 tokenizer_dir = model_dir 259 tokenizer = AutoTokenizer.from_pretrained( 260 tokenizer_dir, trust_remote_code=trust_remote_code 261 ) 262 return model, tokenizer 263 ``` 264 265 2. 读取微调的模型,请注意,你应该使用微调模型的位置,例如,若你的模型位置为`/path/to/finetune_adapter_model` 266 ,原始模型地址为`path/to/base_model`,则你应该使用`/path/to/finetune_adapter_model`作为`model_dir`。 267 3. 完成上述操作后,就能正常使用微调的模型了,其他的调用方式没有变化。 268 269 ### 提示 270 271 1. 微调代码在开始训练前,会先打印首条训练数据的预处理信息(默认已经注释,可以解除注释),显示为 272 273 ```log 274 Sanity 275 Check >> >> >> >> >> >> > 276 '[gMASK]': 64790 -> -100 277 'sop': 64792 -> -100 278 '<|system|>': 64794 -> -100 279 '': 30910 -> -100 280 '\n': 13 -> -100 281 'Answer': 20115 -> -100 282 'the': 267 -> -100 283 'following': 1762 -> -100 284 ... 285 'know': 683 -> -100 286 'the': 267 -> -100 287 'response': 3010 -> -100 288 'details': 3296 -> -100 289 '.': 30930 -> -100 290 '<|assistant|>': 64796 -> -100 291 '': 30910 -> 30910 292 '\n': 13 -> 13 293 'I': 307 -> 307 294 'need': 720 -> 720 295 'to': 289 -> 289 296 'use': 792 -> 792 297 ... 298 << << << << << << < Sanity 299 Check 300 ``` 301 302 字样,每行依次表示一个 detokenized string, token_id 和 target_id。其中,`target_id`为`token_id`在模型词表中的索引,`-100`表示该 303 token 不参与 `loss` 计算。 304 305 2. `_prepare_model_for_training` 的作用是遍历模型的所有可训练参数,并确保它们的数据类型为`torch.float32`。 306 这在某些情况下是必要的,因为混合精度训练或其他操作可能会更改模型参数的数据类型。该代码默打开,可以注释,但是如果使用 307 `half` 格式训练出现问题,可以切换回这个代码,显存可能增加。 308 3. 在我们的[Huggingface模型代码](https://huggingface.co/THUDM/chatglm3-6b/blob/main/modeling_chatglm.py)中,有以下内容: 309 ```python 310 if self.gradient_checkpointing and self.training: 311 layer_ret = torch.utils.checkpoint.checkpoint( 312 layer, 313 hidden_states, 314 attention_mask, 315 rotary_pos_emb, 316 kv_caches[index], 317 use_cache, 318 use_reentrant=False 319 ) 320 ``` 321 这可能导致训练的时候显存增加,因此,如果您的显存不足,可以尝试将``` use_reentrant``` 修改为`True`。 322 4. 微调后的模型可以使用任何支持 `peft` 载入的模型加速框架,在这里,我们没有提供demo。 323 5. 本仓库的微调数据集格式与 API 微调数据集格式有一定区别 324 + ZhipuAI API 微调数据集中的 `messages` 字段在本仓库为 `conversation` 字段。 325 + ZhipuAI API 中的微调文件为 `jsonl`, 在本仓库,需要简单的将文件名改为 `json`。 326 327 > 以上内容来自ChatGLM3项目 328 329 ## 微调示例 330 331 配置文件 332 333 config/lora.yaml 334 335 ```yaml 336 data_config: 337 train_file: train.json 338 val_file: dev.json 339 test_file: dev.json 340 num_proc: 10 341 max_input_length: 512 342 max_output_length: 128 343 training_args: 344 # see `transformers.Seq2SeqTrainingArguments` 345 output_dir: ./output03-24 346 max_steps: 100000 347 # settings for data loading 348 per_device_train_batch_size: 4 349 dataloader_num_workers: 10 350 remove_unused_columns: false 351 # settings for saving checkpoints 352 save_strategy: steps 353 save_steps: 2000 354 # settings for logging 355 log_level: info 356 logging_strategy: steps 357 logging_steps: 10 358 # settings for evaluation 359 per_device_eval_batch_size: 4 360 evaluation_strategy: steps 361 eval_steps: 5200 362 # settings for optimizer 363 # adam_epsilon: 1e-6 364 # uncomment the following line to detect nan or inf values 365 # debug: underflow_overflow 366 predict_with_generate: yes 367 # see `transformers.GenerationConfig` 368 generation_config: 369 max_new_tokens: 256 370 # set your absolute deepspeed path here 371 #deepspeed: ds_zero_2.json 372 # set to true if train with cpu. 373 use_cpu: false 374 peft_config: 375 peft_type: LORA 376 task_type: CAUSAL_LM 377 r: 8 378 lora_alpha: 32 379 lora_dropout: 0.1 380 ``` 381 382 硬件配置:4090 24G、64G内存、CPU 14700KF 20核28线程 383 384 你需要根据你的硬件配置修改上述参数,各个参数含义上面有说 385 386 微调命令(需要指定数据集路径和ChatGLM3基础大模型的路径) 387 388 ```shell 389 python finetune_hf.py data/ E:\\Project\\Python\\Langchain-Chatchat\\chatglm3-6b configs/lora.yaml yes 390 ``` 391 392 ## 部署 393 394 在api_server.py修改微调保存路径 395 ```python 396 model, tokenizer = load_model_and_tokenizer( 397 r'E:\Project\Python\ChatGLM3\finetune_demo\output03-24\checkpoint-224000' 398 ) 399 ``` 400 401 直接运行即可 402 403 ```shell 404 python api_server.py 405 ``` 406 407 调用示例(你可以在任意一个支持ChatGPT的应用中使用它): 408 409 ```python 410 from openai import OpenAI 411 412 base_url = "http://127.0.0.1:8002/v1/" 413 client = OpenAI(api_key="EMPTY", base_url=base_url) 414 415 def simple_chat(use_stream=True): 416 messages = [ 417 { 418 "role": "user", 419 "content": "你好啊" 420 } 421 ] 422 response = client.chat.completions.create( 423 model="chatglm3-6b", 424 messages=messages, 425 stream=use_stream, 426 max_tokens=256, 427 temperature=0.8, 428 presence_penalty=1.1, 429 top_p=0.8) 430 if response: 431 if use_stream: 432 for chunk in response: 433 print(chunk.choices[0].delta.content, end='') 434 else: 435 content = response.choices[0].message.content 436 print(content) 437 else: 438 print("Error:", response.status_code) 439 440 if __name__ == "__main__": 441 simple_chat(use_stream=True) 442 ``` 443 444 ## 体验地址 445 446 [https://chat.memotrace.cn/](https://chat.memotrace.cn/) 447 448  449 450 