From caf4fa46e0dccc229427ff760124a434708d30d1 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Fri, 1 Dec 2023 22:53:15 +0800 Subject: [PATCH 01/28] patch modelscope Former-commit-id: 8888cf53f040f5a2d8c0e59cddf79b252449bf58 --- README.md | 37 +-- README_zh.md | 37 +-- src/llmtuner/extras/constants.py | 392 +++++++++++++++++------------ src/llmtuner/extras/misc.py | 21 ++ src/llmtuner/hparams/model_args.py | 4 +- src/llmtuner/model/loader.py | 23 +- src/llmtuner/webui/common.py | 20 +- 7 files changed, 312 insertions(+), 222 deletions(-) diff --git a/README.md b/README.md index 7a4c8ee1..bc20c1ce 100644 --- a/README.md +++ b/README.md @@ -44,17 +44,23 @@ Compared to ChatGLM's [P-Tuning](https://github.com/THUDM/ChatGLM2-6B/tree/main/ ![benchmark](assets/benchmark.svg) +
Definitions + - **Training Speed**: the number of training samples processed per second during the training. (bs=4, cutoff_len=1024) - **Rouge Score**: Rouge-2 score on the development set of the [advertising text generation](https://aclanthology.org/D19-1321.pdf) task. (bs=4, cutoff_len=1024) - **GPU Memory**: Peak GPU memory usage in 4-bit quantized training. (bs=1, cutoff_len=1024) - We adopt `pre_seq_len=128` for ChatGLM's P-Tuning and `lora_rank=32` for LLaMA-Factory's LoRA tuning. +
+ ## Changelog -[23/12/01] We supported **[ModelScope Hub](https://www.modelscope.cn/models)** to accelerate model downloading. Add environment variable `USE_MODELSCOPE_HUB=1` to your command line, then you can use the model-id of ModelScope Hub. +[23/12/01] We supported downloading pre-trained models from the **[ModelScope Hub](https://modelscope.cn/models)** for Chinese mainland users. See [this tutorial](#use-modelscope-models-optional) for usage. [23/10/21] We supported **[NEFTune](https://arxiv.org/abs/2310.05914)** trick for fine-tuning. Try `--neft_alpha` argument to activate NEFTune, e.g., `--neft_alpha 5`. +
Full Changelog + [23/09/27] We supported **$S^2$-Attn** proposed by [LongLoRA](https://github.com/dvlab-research/LongLoRA) for the LLaMA models. Try `--shift_attn` argument to enable shift short attention. [23/09/23] We integrated MMLU, C-Eval and CMMLU benchmarks in this repo. See [this example](#evaluation) to evaluate your models. @@ -79,6 +85,8 @@ Compared to ChatGLM's [P-Tuning](https://github.com/THUDM/ChatGLM2-6B/tree/main/ [23/06/03] We supported quantized training and inference (aka **[QLoRA](https://github.com/artidoro/qlora)**). Try `--quantization_bit 4/8` argument to work with quantized models. +
+ ## Supported Models | Model | Model size | Default module | Template | @@ -231,31 +239,26 @@ If you want to enable the quantized LoRA (QLoRA) on the Windows platform, you wi pip install https://github.com/jllllll/bitsandbytes-windows-webui/releases/download/wheels/bitsandbytes-0.39.1-py3-none-win_amd64.whl ``` -### Use ModelScope Models +### Use ModelScope Models (optional) -If you have trouble with downloading models from HuggingFace, we have supported ModelScope Hub. To use LLaMA-Factory together with ModelScope, please add a environment variable: +If you have trouble with downloading models from Hugging Face, you can use LLaMA-Factory together with ModelScope in the following manner. -```shell -export USE_MODELSCOPE_HUB=1 +```bash +export USE_MODELSCOPE_HUB=1 # `set USE_MODELSCOPE_HUB=1` for Windows ``` -> [!NOTE] -> -> Please use integers only. 0 or not set for using HuggingFace hub. Other values will be treated as use ModelScope hub. +Then you can train the corresponding model by specifying a model ID of the ModelScope Hub. (find a full list of model IDs at [ModelScope Hub](https://modelscope.cn/models)) -Then you can use LLaMA-Factory with ModelScope model-ids: - -```shell +```bash python src/train_bash.py \ - --model_name_or_path ZhipuAI/chatglm3-6b \ - ... other arguments -# You can find all model ids in this link: https://www.modelscope.cn/models + --model_name_or_path modelscope/Llama-2-7b-ms \ + ... # arguments (same as above) ``` -Web demo also supports ModelScope, after setting the environment variable please run with this command: +LLaMA Board also supports using the models on the ModelScope Hub. -```shell -CUDA_VISIBLE_DEVICES=0 python src/train_web.py +```bash +CUDA_VISIBLE_DEVICES=0 USE_MODELSCOPE_HUB=1 python src/train_web.py ``` ### Train on a single GPU diff --git a/README_zh.md b/README_zh.md index 6a68ce30..0aa8ac0f 100644 --- a/README_zh.md +++ b/README_zh.md @@ -44,17 +44,23 @@ https://github.com/hiyouga/LLaMA-Factory/assets/16256802/6ba60acc-e2e2-4bec-b846 ![benchmark](assets/benchmark.svg) +
变量定义 + - **Training Speed**: 训练阶段每秒处理的样本数量。(批处理大小=4,截断长度=1024) - **Rouge Score**: [广告文案生成](https://aclanthology.org/D19-1321.pdf)任务验证集上的 Rouge-2 分数。(批处理大小=4,截断长度=1024) - **GPU Memory**: 4 比特量化训练的 GPU 显存峰值。(批处理大小=1,截断长度=1024) - 我们在 ChatGLM 的 P-Tuning 中采用 `pre_seq_len=128`,在 LLaMA-Factory 的 LoRA 微调中采用 `lora_rank=32`。 +
+ ## 更新日志 -[23/12/01] 我们支持了 **[魔搭ModelHub](https://www.modelscope.cn/models)** 进行模型下载加速。在启动命令前环境变量中增加 `USE_MODELSCOPE_HUB=1` 即可开启。 +[23/12/01] 我们支持了从 **[魔搭社区](https://modelscope.cn/models)** 下载预训练模型。详细用法请参照 [此教程](#使用魔搭社区可跳过)。 [23/10/21] 我们支持了 **[NEFTune](https://arxiv.org/abs/2310.05914)** 训练技巧。请使用 `--neft_alpha` 参数启用 NEFTune,例如 `--neft_alpha 5`。 +
展开日志 + [23/09/27] 我们针对 LLaMA 模型支持了 [LongLoRA](https://github.com/dvlab-research/LongLoRA) 提出的 **$S^2$-Attn**。请使用 `--shift_attn` 参数以启用该功能。 [23/09/23] 我们在项目中集成了 MMLU、C-Eval 和 CMMLU 评估集。使用方法请参阅[此示例](#模型评估)。 @@ -79,6 +85,8 @@ https://github.com/hiyouga/LLaMA-Factory/assets/16256802/6ba60acc-e2e2-4bec-b846 [23/06/03] 我们实现了 4 比特的 LoRA 训练(也称 **[QLoRA](https://github.com/artidoro/qlora)**)。请使用 `--quantization_bit 4` 参数进行 4 比特量化微调。 +
+ ## 模型 | 模型名 | 模型大小 | 默认模块 | Template | @@ -231,31 +239,26 @@ pip install -r requirements.txt pip install https://github.com/jllllll/bitsandbytes-windows-webui/releases/download/wheels/bitsandbytes-0.39.1-py3-none-win_amd64.whl ``` -### 使用魔搭的模型 +### 使用魔搭社区(可跳过) -如果下载HuggingFace模型存在问题,我们已经支持了魔搭的ModelHub,只需要添加一个环境变量: +如果您在 Hugging Face 模型的下载中遇到了问题,可以通过下述方法使用魔搭社区。 -```shell -export USE_MODELSCOPE_HUB=1 +```bash +export USE_MODELSCOPE_HUB=1 # Windows 使用 `set USE_MODELSCOPE_HUB=1` ``` -> [!NOTE] -> -> 该环境变量仅支持整数,0或者不设置代表使用HuggingFace,其他值代表使用ModelScope +接着即可通过指定模型名称来训练对应的模型。(在[魔搭社区](https://modelscope.cn/models)查看所有可用的模型) -之后就可以在命令行中指定魔搭的模型id: - -```shell +```bash python src/train_bash.py \ - --model_name_or_path ZhipuAI/chatglm3-6b \ - ... other arguments -# 在这个链接中可以看到所有可用模型: https://www.modelscope.cn/models + --model_name_or_path modelscope/Llama-2-7b-ms \ + ... # 参数同上 ``` -Web demo目前也支持了魔搭, 在设置环境变量后即可使用: +LLaMA Board 同样支持魔搭社区的模型下载。 -```shell -CUDA_VISIBLE_DEVICES=0 python src/train_web.py +```bash +CUDA_VISIBLE_DEVICES=0 USE_MODELSCOPE_HUB=1 python src/train_web.py ``` ### 单 GPU 训练 diff --git a/src/llmtuner/extras/constants.py b/src/llmtuner/extras/constants.py index 7e66d1b3..a36102a1 100644 --- a/src/llmtuner/extras/constants.py +++ b/src/llmtuner/extras/constants.py @@ -1,6 +1,7 @@ -import os +from enum import Enum from collections import defaultdict, OrderedDict -from typing import Dict, Optional, Union +from typing import Dict, Optional + CHOICES = ["A", "B", "C", "D"] @@ -20,8 +21,6 @@ SUBJECTS = ["Average", "STEM", "Social Sciences", "Humanities", "Other"] SUPPORTED_MODELS = OrderedDict() -ALL_OFFICIAL_MODELS = OrderedDict() - TRAINING_STAGES = { "Supervised Fine-Tuning": "sft", "Reward Modeling": "rm", @@ -30,9 +29,13 @@ TRAINING_STAGES = { "Pre-Training": "pt" } +class DownloadSource(str, Enum): + DEFAULT = "hf" + MODELSCOPE = "ms" + def register_model_group( - models: Dict[str, Union[str, Dict[str, str]]], + models: Dict[str, Dict[DownloadSource, str]], module: Optional[str] = None, template: Optional[str] = None ) -> None: @@ -42,14 +45,7 @@ def register_model_group( prefix = name.split("-")[0] else: assert prefix == name.split("-")[0], "prefix should be identical." - - ALL_OFFICIAL_MODELS[name] = [path] if isinstance(path, str) else list(path.values()) - if not int(os.environ.get('USE_MODELSCOPE_HUB', '0')): - # If path is a string, we treat it as a huggingface model-id by default. - SUPPORTED_MODELS[name] = path["hf"] if isinstance(path, dict) else path - elif isinstance(path, dict) and "ms" in path: - # Use ModelScope modelhub - SUPPORTED_MODELS[name] = path["ms"] + SUPPORTED_MODELS[name] = path if module is not None: DEFAULT_MODULE[prefix] = module if template is not None: @@ -59,16 +55,16 @@ def register_model_group( register_model_group( models={ "Baichuan-7B-Base": { - "hf": "baichuan-inc/Baichuan-7B", - "ms": "baichuan-inc/baichuan-7B", + DownloadSource.DEFAULT: "baichuan-inc/Baichuan-7B", + DownloadSource.MODELSCOPE: "baichuan-inc/baichuan-7B" }, "Baichuan-13B-Base": { - "hf": "baichuan-inc/Baichuan-13B-Base", - "ms": "baichuan-inc/Baichuan-13B-Base", + DownloadSource.DEFAULT: "baichuan-inc/Baichuan-13B-Base", + DownloadSource.MODELSCOPE: "baichuan-inc/Baichuan-13B-Base" }, "Baichuan-13B-Chat": { - "hf": "baichuan-inc/Baichuan-13B-Chat", - "ms": "baichuan-inc/Baichuan-13B-Base", + DownloadSource.DEFAULT: "baichuan-inc/Baichuan-13B-Chat", + DownloadSource.MODELSCOPE: "baichuan-inc/Baichuan-13B-Chat" } }, module="W_pack", @@ -79,20 +75,20 @@ register_model_group( register_model_group( models={ "Baichuan2-7B-Base": { - "hf": "baichuan-inc/Baichuan2-7B-Base", - "ms": "baichuan-inc/Baichuan2-7B-Base", + DownloadSource.DEFAULT: "baichuan-inc/Baichuan2-7B-Base", + DownloadSource.MODELSCOPE: "baichuan-inc/Baichuan2-7B-Base" }, "Baichuan2-13B-Base": { - "hf": "baichuan-inc/Baichuan2-13B-Base", - "ms": "baichuan-inc/Baichuan2-13B-Base", + DownloadSource.DEFAULT: "baichuan-inc/Baichuan2-13B-Base", + DownloadSource.MODELSCOPE: "baichuan-inc/Baichuan2-13B-Base" }, "Baichuan2-7B-Chat": { - "hf": "baichuan-inc/Baichuan2-7B-Chat", - "ms": "baichuan-inc/Baichuan2-7B-Chat", + DownloadSource.DEFAULT: "baichuan-inc/Baichuan2-7B-Chat", + DownloadSource.MODELSCOPE: "baichuan-inc/Baichuan2-7B-Chat" }, "Baichuan2-13B-Chat": { - "hf": "baichuan-inc/Baichuan2-13B-Chat", - "ms": "baichuan-inc/Baichuan2-13B-Chat", + DownloadSource.DEFAULT: "baichuan-inc/Baichuan2-13B-Chat", + DownloadSource.MODELSCOPE: "baichuan-inc/Baichuan2-13B-Chat" } }, module="W_pack", @@ -103,16 +99,16 @@ register_model_group( register_model_group( models={ "BLOOM-560M": { - "hf": "bigscience/bloom-560m", - "ms": "AI-ModelScope/bloom-560m", + DownloadSource.DEFAULT: "bigscience/bloom-560m", + DownloadSource.MODELSCOPE: "AI-ModelScope/bloom-560m" }, "BLOOM-3B": { - "hf": "bigscience/bloom-3b", - "ms": "AI-ModelScope/bloom-3b", + DownloadSource.DEFAULT: "bigscience/bloom-3b", + DownloadSource.MODELSCOPE: "AI-ModelScope/bloom-3b" }, "BLOOM-7B1": { - "hf": "bigscience/bloom-7b1", - "ms": "AI-ModelScope/bloom-7b1", + DownloadSource.DEFAULT: "bigscience/bloom-7b1", + DownloadSource.MODELSCOPE: "AI-ModelScope/bloom-7b1" } }, module="query_key_value" @@ -122,16 +118,16 @@ register_model_group( register_model_group( models={ "BLOOMZ-560M": { - "hf": "bigscience/bloomz-560m", - "ms": "AI-ModelScope/bloomz-560m", + DownloadSource.DEFAULT: "bigscience/bloomz-560m", + DownloadSource.MODELSCOPE: "AI-ModelScope/bloomz-560m" }, "BLOOMZ-3B": { - "hf": "bigscience/bloomz-3b", - "ms": "AI-ModelScope/bloomz-3b", + DownloadSource.DEFAULT: "bigscience/bloomz-3b", + DownloadSource.MODELSCOPE: "AI-ModelScope/bloomz-3b" }, "BLOOMZ-7B1-mt": { - "hf": "bigscience/bloomz-7b1-mt", - "ms": "AI-ModelScope/bloomz-7b1-mt", + DownloadSource.DEFAULT: "bigscience/bloomz-7b1-mt", + DownloadSource.MODELSCOPE: "AI-ModelScope/bloomz-7b1-mt" } }, module="query_key_value" @@ -141,12 +137,12 @@ register_model_group( register_model_group( models={ "BlueLM-7B-Base": { - "hf": "vivo-ai/BlueLM-7B-Base", - "ms": "vivo-ai/BlueLM-7B-Base", + DownloadSource.DEFAULT: "vivo-ai/BlueLM-7B-Base", + DownloadSource.MODELSCOPE: "vivo-ai/BlueLM-7B-Base" }, "BlueLM-7B-Chat": { - "hf": "vivo-ai/BlueLM-7B-Chat", - "ms": "vivo-ai/BlueLM-7B-Chat", + DownloadSource.DEFAULT: "vivo-ai/BlueLM-7B-Chat", + DownloadSource.MODELSCOPE: "vivo-ai/BlueLM-7B-Chat" } }, template="bluelm" @@ -156,8 +152,8 @@ register_model_group( register_model_group( models={ "ChatGLM2-6B-Chat": { - "hf": "THUDM/chatglm2-6b", - "ms": "ZhipuAI/chatglm2-6b", + DownloadSource.DEFAULT: "THUDM/chatglm2-6b", + DownloadSource.MODELSCOPE: "ZhipuAI/chatglm2-6b" } }, module="query_key_value", @@ -168,12 +164,12 @@ register_model_group( register_model_group( models={ "ChatGLM3-6B-Base": { - "hf": "THUDM/chatglm3-6b-base", - "ms": "ZhipuAI/chatglm3-6b-base", + DownloadSource.DEFAULT: "THUDM/chatglm3-6b-base", + DownloadSource.MODELSCOPE: "ZhipuAI/chatglm3-6b-base" }, "ChatGLM3-6B-Chat": { - "hf": "THUDM/chatglm3-6b", - "ms": "ZhipuAI/chatglm3-6b", + DownloadSource.DEFAULT: "THUDM/chatglm3-6b", + DownloadSource.MODELSCOPE: "ZhipuAI/chatglm3-6b" } }, module="query_key_value", @@ -184,59 +180,105 @@ register_model_group( register_model_group( models={ "ChineseLLaMA2-1.3B": { - "hf": "hfl/chinese-llama-2-1.3b", - "ms": "AI-ModelScope/chinese-llama-2-1.3b", + DownloadSource.DEFAULT: "hfl/chinese-llama-2-1.3b", + DownloadSource.MODELSCOPE: "AI-ModelScope/chinese-llama-2-1.3b" }, "ChineseLLaMA2-7B": { - "hf": "hfl/chinese-llama-2-7b", - "ms": "AI-ModelScope/chinese-llama-2-7b", + DownloadSource.DEFAULT: "hfl/chinese-llama-2-7b", + DownloadSource.MODELSCOPE: "AI-ModelScope/chinese-llama-2-7b" }, "ChineseLLaMA2-13B": { - "hf": "hfl/chinese-llama-2-13b", - "ms": "AI-ModelScope/chinese-llama-2-13b", + DownloadSource.DEFAULT: "hfl/chinese-llama-2-13b", + DownloadSource.MODELSCOPE: "AI-ModelScope/chinese-llama-2-13b" }, "ChineseLLaMA2-1.3B-Chat": { - "hf": "hfl/chinese-alpaca-2-1.3b", - "ms": "AI-ModelScope/chinese-alpaca-2-1.3b", + DownloadSource.DEFAULT: "hfl/chinese-alpaca-2-1.3b", + DownloadSource.MODELSCOPE: "AI-ModelScope/chinese-alpaca-2-1.3b" }, "ChineseLLaMA2-7B-Chat": { - "hf": "hfl/chinese-alpaca-2-7b", - "ms": "AI-ModelScope/chinese-alpaca-2-7b", + DownloadSource.DEFAULT: "hfl/chinese-alpaca-2-7b", + DownloadSource.MODELSCOPE: "AI-ModelScope/chinese-alpaca-2-7b" }, "ChineseLLaMA2-13B-Chat": { - "hf": "hfl/chinese-alpaca-2-13b", - "ms": "AI-ModelScope/chinese-alpaca-2-13b", + DownloadSource.DEFAULT: "hfl/chinese-alpaca-2-13b", + DownloadSource.MODELSCOPE: "AI-ModelScope/chinese-alpaca-2-13b" } }, template="llama2_zh" ) +register_model_group( + models={ + "DeepseekLLM-7B-Base": { + DownloadSource.DEFAULT: "deepseek-ai/deepseek-llm-7b-base", + DownloadSource.MODELSCOPE: "deepseek-ai/deepseek-llm-7b-base" + }, + "DeepseekLLM-67B-Base": { + DownloadSource.DEFAULT: "deepseek-ai/deepseek-llm-67b-base", + DownloadSource.MODELSCOPE: "deepseek-ai/deepseek-llm-67b-base" + }, + "DeepseekLLM-7B-Chat": { + DownloadSource.DEFAULT: "deepseek-ai/deepseek-llm-7b-chat", + DownloadSource.MODELSCOPE: "deepseek-ai/deepseek-llm-7b-chat" + }, + "DeepseekLLM-67B-Chat": { + DownloadSource.DEFAULT: "deepseek-ai/deepseek-llm-67b-chat", + DownloadSource.MODELSCOPE: "deepseek-ai/deepseek-llm-67b-chat" + } + }, + template="deepseek" +) + + +register_model_group( + models={ + "DeepseekCoder-6.7B-Base": { + DownloadSource.DEFAULT: "deepseek-ai/deepseek-coder-6.7b-base", + DownloadSource.MODELSCOPE: "deepseek-ai/deepseek-coder-6.7b-base" + }, + "DeepseekCoder-33B-Base": { + DownloadSource.DEFAULT: "deepseek-ai/deepseek-coder-33b-base", + DownloadSource.MODELSCOPE: "deepseek-ai/deepseek-coder-33b-base" + }, + "DeepseekCoder-6.7B-Chat": { + DownloadSource.DEFAULT: "deepseek-ai/deepseek-coder-6.7b-instruct", + DownloadSource.MODELSCOPE: "deepseek-ai/deepseek-coder-6.7b-instruct" + }, + "DeepseekCoder-33B-Chat": { + DownloadSource.DEFAULT: "deepseek-ai/deepseek-coder-33b-instruct", + DownloadSource.MODELSCOPE: "deepseek-ai/deepseek-coder-33b-instruct" + } + }, + template="deepseekcoder" +) + + register_model_group( models={ "Falcon-7B": { - "hf": "tiiuae/falcon-7b", - "ms": "AI-ModelScope/falcon-7b", + DownloadSource.DEFAULT: "tiiuae/falcon-7b", + DownloadSource.MODELSCOPE: "AI-ModelScope/falcon-7b" }, "Falcon-40B": { - "hf": "tiiuae/falcon-40b", - "ms": "AI-ModelScope/falcon-40b", + DownloadSource.DEFAULT: "tiiuae/falcon-40b", + DownloadSource.MODELSCOPE: "AI-ModelScope/falcon-40b" }, "Falcon-180B": { - "hf": "tiiuae/falcon-180B", - "ms": "AI-ModelScope/falcon-180B", + DownloadSource.DEFAULT: "tiiuae/falcon-180b", + DownloadSource.MODELSCOPE: "modelscope/falcon-180B" }, "Falcon-7B-Chat": { - "hf": "tiiuae/falcon-7b-instruct", - "ms": "AI-ModelScope/falcon-7b-instruct", + DownloadSource.DEFAULT: "tiiuae/falcon-7b-instruct", + DownloadSource.MODELSCOPE: "AI-ModelScope/falcon-7b-instruct" }, "Falcon-40B-Chat": { - "hf": "tiiuae/falcon-40b-instruct", - "ms": "AI-ModelScope/falcon-40b-instruct", + DownloadSource.DEFAULT: "tiiuae/falcon-40b-instruct", + DownloadSource.MODELSCOPE: "AI-ModelScope/falcon-40b-instruct" }, "Falcon-180B-Chat": { - "hf": "tiiuae/falcon-180B-chat", - "ms": "AI-ModelScope/falcon-180B-chat", + DownloadSource.DEFAULT: "tiiuae/falcon-180b-chat", + DownloadSource.MODELSCOPE: "modelscope/falcon-180B-chat" } }, module="query_key_value", @@ -247,20 +289,20 @@ register_model_group( register_model_group( models={ "InternLM-7B": { - "hf": "internlm/internlm-7b", - "ms": "Shanghai_AI_Laboratory/internlm-7b", + DownloadSource.DEFAULT: "internlm/internlm-7b", + DownloadSource.MODELSCOPE: "Shanghai_AI_Laboratory/internlm-7b" }, "InternLM-20B": { - "hf": "internlm/internlm-20b", - "ms": "Shanghai_AI_Laboratory/internlm-20b", + DownloadSource.DEFAULT: "internlm/internlm-20b", + DownloadSource.MODELSCOPE: "Shanghai_AI_Laboratory/internlm-20b" }, "InternLM-7B-Chat": { - "hf": "internlm/internlm-chat-7b", - "ms": "Shanghai_AI_Laboratory/internlm-chat-7b", + DownloadSource.DEFAULT: "internlm/internlm-chat-7b", + DownloadSource.MODELSCOPE: "Shanghai_AI_Laboratory/internlm-chat-7b" }, "InternLM-20B-Chat": { - "hf": "internlm/internlm-chat-20b", - "ms": "Shanghai_AI_Laboratory/internlm-chat-20b", + DownloadSource.DEFAULT: "internlm/internlm-chat-20b", + DownloadSource.MODELSCOPE: "Shanghai_AI_Laboratory/internlm-chat-20b" } }, template="intern" @@ -270,8 +312,8 @@ register_model_group( register_model_group( models={ "LingoWhale-8B": { - "hf": "deeplang-ai/LingoWhale-8B", - "ms": "DeepLang/LingoWhale-8B", + DownloadSource.DEFAULT: "deeplang-ai/LingoWhale-8B", + DownloadSource.MODELSCOPE: "DeepLang/LingoWhale-8B" } }, module="qkv_proj" @@ -281,20 +323,20 @@ register_model_group( register_model_group( models={ "LLaMA-7B": { - "hf": "huggyllama/llama-7b", - "ms": "skyline2006/llama-7b", + DownloadSource.DEFAULT: "huggyllama/llama-7b", + DownloadSource.MODELSCOPE: "skyline2006/llama-7b" }, "LLaMA-13B": { - "hf": "huggyllama/llama-13b", - "ms": "skyline2006/llama-13b", + DownloadSource.DEFAULT: "huggyllama/llama-13b", + DownloadSource.MODELSCOPE: "skyline2006/llama-13b" }, "LLaMA-30B": { - "hf": "huggyllama/llama-30b", - "ms": "skyline2006/llama-30b", + DownloadSource.DEFAULT: "huggyllama/llama-30b", + DownloadSource.MODELSCOPE: "skyline2006/llama-30b" }, "LLaMA-65B": { - "hf": "huggyllama/llama-65b", - "ms": "skyline2006/llama-65b", + DownloadSource.DEFAULT: "huggyllama/llama-65b", + DownloadSource.MODELSCOPE: "skyline2006/llama-65b" } } ) @@ -303,28 +345,28 @@ register_model_group( register_model_group( models={ "LLaMA2-7B": { - "hf": "meta-llama/Llama-2-7b-hf", - "ms": "modelscope/Llama-2-7b-ms", + DownloadSource.DEFAULT: "meta-llama/Llama-2-7b-hf", + DownloadSource.MODELSCOPE: "modelscope/Llama-2-7b-ms" }, "LLaMA2-13B": { - "hf": "meta-llama/Llama-2-13b-hf", - "ms": "modelscope/Llama-2-13b-ms", + DownloadSource.DEFAULT: "meta-llama/Llama-2-13b-hf", + DownloadSource.MODELSCOPE: "modelscope/Llama-2-13b-ms" }, "LLaMA2-70B": { - "hf": "meta-llama/Llama-2-70b-hf", - "ms": "modelscope/Llama-2-70b-ms", + DownloadSource.DEFAULT: "meta-llama/Llama-2-70b-hf", + DownloadSource.MODELSCOPE: "modelscope/Llama-2-70b-ms" }, "LLaMA2-7B-Chat": { - "hf": "meta-llama/Llama-2-7b-chat-hf", - "ms": "modelscope/Llama-2-7b-chat-ms", + DownloadSource.DEFAULT: "meta-llama/Llama-2-7b-chat-hf", + DownloadSource.MODELSCOPE: "modelscope/Llama-2-7b-chat-ms" }, "LLaMA2-13B-Chat": { - "hf": "meta-llama/Llama-2-13b-chat-hf", - "ms": "modelscope/Llama-2-13b-chat-ms", + DownloadSource.DEFAULT: "meta-llama/Llama-2-13b-chat-hf", + DownloadSource.MODELSCOPE: "modelscope/Llama-2-13b-chat-ms" }, "LLaMA2-70B-Chat": { - "hf": "meta-llama/Llama-2-70b-chat-hf", - "ms": "modelscope/Llama-2-70b-chat-ms", + DownloadSource.DEFAULT: "meta-llama/Llama-2-70b-chat-hf", + DownloadSource.MODELSCOPE: "modelscope/Llama-2-70b-chat-ms" } }, template="llama2" @@ -334,12 +376,12 @@ register_model_group( register_model_group( models={ "Mistral-7B": { - "hf": "mistralai/Mistral-7B-v0.1", - "ms": "AI-ModelScope/Mistral-7B-v0.1", + DownloadSource.DEFAULT: "mistralai/Mistral-7B-v0.1", + DownloadSource.MODELSCOPE: "AI-ModelScope/Mistral-7B-v0.1" }, "Mistral-7B-Chat": { - "hf": "mistralai/Mistral-7B-Instruct-v0.1", - "ms": "AI-ModelScope/Mistral-7B-Instruct-v0.1", + DownloadSource.DEFAULT: "mistralai/Mistral-7B-Instruct-v0.1", + DownloadSource.MODELSCOPE: "AI-ModelScope/Mistral-7B-Instruct-v0.1" } }, template="mistral" @@ -349,8 +391,8 @@ register_model_group( register_model_group( models={ "OpenChat3.5-7B-Chat": { - "hf": "openchat/openchat_3.5", - "ms": "myxiongmodel/openchat_3.5", + DownloadSource.DEFAULT: "openchat/openchat_3.5", + DownloadSource.MODELSCOPE: "myxiongmodel/openchat_3.5" } }, template="openchat" @@ -360,8 +402,8 @@ register_model_group( register_model_group( models={ "Phi1.5-1.3B": { - "hf": "microsoft/phi-1_5", - "ms": "allspace/PHI_1-5", + DownloadSource.DEFAULT: "microsoft/phi-1_5", + DownloadSource.MODELSCOPE: "allspace/PHI_1-5" } }, module="Wqkv" @@ -370,37 +412,69 @@ register_model_group( register_model_group( models={ + "Qwen-1.8B": { + DownloadSource.DEFAULT: "Qwen/Qwen-1_8B", + DownloadSource.MODELSCOPE: "qwen/Qwen-1_8B" + }, "Qwen-7B": { - "hf": "Qwen/Qwen-7B", - "ms": "qwen/Qwen-7B", + DownloadSource.DEFAULT: "Qwen/Qwen-7B", + DownloadSource.MODELSCOPE: "qwen/Qwen-7B" }, "Qwen-14B": { - "hf": "Qwen/Qwen-14B", - "ms": "qwen/Qwen-14B", + DownloadSource.DEFAULT: "Qwen/Qwen-14B", + DownloadSource.MODELSCOPE: "qwen/Qwen-14B" + }, + "Qwen-72B": { + DownloadSource.DEFAULT: "Qwen/Qwen-72B", + DownloadSource.MODELSCOPE: "qwen/Qwen-72B" + }, + "Qwen-1.8B-Chat": { + DownloadSource.DEFAULT: "Qwen/Qwen-1_8B-Chat", + DownloadSource.MODELSCOPE: "qwen/Qwen-1_8B-Chat" }, "Qwen-7B-Chat": { - "hf": "Qwen/Qwen-7B-Chat", - "ms": "qwen/Qwen-7B-Chat", + DownloadSource.DEFAULT: "Qwen/Qwen-7B-Chat", + DownloadSource.MODELSCOPE: "qwen/Qwen-7B-Chat" }, "Qwen-14B-Chat": { - "hf": "Qwen/Qwen-14B-Chat", - "ms": "qwen/Qwen-14B-Chat", + DownloadSource.DEFAULT: "Qwen/Qwen-14B-Chat", + DownloadSource.MODELSCOPE: "qwen/Qwen-14B-Chat" + }, + "Qwen-72B-Chat": { + DownloadSource.DEFAULT: "Qwen/Qwen-72B-Chat", + DownloadSource.MODELSCOPE: "qwen/Qwen-72B-Chat" + }, + "Qwen-1.8B-int8-Chat": { + DownloadSource.DEFAULT: "Qwen/Qwen-1_8B-Chat-Int8", + DownloadSource.MODELSCOPE: "qwen/Qwen-1_8B-Chat-Int8" + }, + "Qwen-1.8B-int4-Chat": { + DownloadSource.DEFAULT: "Qwen/Qwen-1_8B-Chat-Int4", + DownloadSource.MODELSCOPE: "qwen/Qwen-1_8B-Chat-Int4" }, "Qwen-7B-int8-Chat": { - "hf": "Qwen/Qwen-7B-Chat-Int8", - "ms": "qwen/Qwen-7B-Chat-Int8", + DownloadSource.DEFAULT: "Qwen/Qwen-7B-Chat-Int8", + DownloadSource.MODELSCOPE: "qwen/Qwen-7B-Chat-Int8" }, "Qwen-7B-int4-Chat": { - "hf": "Qwen/Qwen-7B-Chat-Int4", - "ms": "qwen/Qwen-7B-Chat-Int4", + DownloadSource.DEFAULT: "Qwen/Qwen-7B-Chat-Int4", + DownloadSource.MODELSCOPE: "qwen/Qwen-7B-Chat-Int4" }, "Qwen-14B-int8-Chat": { - "hf": "Qwen/Qwen-14B-Chat-Int8", - "ms": "qwen/Qwen-14B-Chat-Int8", + DownloadSource.DEFAULT: "Qwen/Qwen-14B-Chat-Int8", + DownloadSource.MODELSCOPE: "qwen/Qwen-14B-Chat-Int8" }, "Qwen-14B-int4-Chat": { - "hf": "Qwen/Qwen-14B-Chat-Int4", - "ms": "qwen/Qwen-14B-Chat-Int4", + DownloadSource.DEFAULT: "Qwen/Qwen-14B-Chat-Int4", + DownloadSource.MODELSCOPE: "qwen/Qwen-14B-Chat-Int4" + }, + "Qwen-72B-int8-Chat": { + DownloadSource.DEFAULT: "Qwen/Qwen-72B-Chat-Int8", + DownloadSource.MODELSCOPE: "qwen/Qwen-72B-Chat-Int8" + }, + "Qwen-72B-int4-Chat": { + DownloadSource.DEFAULT: "Qwen/Qwen-72B-Chat-Int4", + DownloadSource.MODELSCOPE: "qwen/Qwen-72B-Chat-Int4" } }, module="c_attn", @@ -411,8 +485,8 @@ register_model_group( register_model_group( models={ "Skywork-13B-Base": { - "hf": "Skywork/Skywork-13B-base", - "ms": "skywork/Skywork-13B-base", + DownloadSource.DEFAULT: "Skywork/Skywork-13B-base", + DownloadSource.MODELSCOPE: "skywork/Skywork-13B-base" } } ) @@ -421,12 +495,12 @@ register_model_group( register_model_group( models={ "Vicuna1.5-7B-Chat": { - "hf": "lmsys/vicuna-7b-v1.5", - "ms": "AI-ModelScope/vicuna-7b-v1.5", + DownloadSource.DEFAULT: "lmsys/vicuna-7b-v1.5", + DownloadSource.MODELSCOPE: "Xorbits/vicuna-7b-v1.5" }, "Vicuna1.5-13B-Chat": { - "hf": "lmsys/vicuna-13b-v1.5", - "ms": "Xorbits/vicuna-13b-v1.5", + DownloadSource.DEFAULT: "lmsys/vicuna-13b-v1.5", + DownloadSource.MODELSCOPE: "Xorbits/vicuna-13b-v1.5" } }, template="vicuna" @@ -436,24 +510,24 @@ register_model_group( register_model_group( models={ "XVERSE-7B": { - "hf": "xverse/XVERSE-7B", - "ms": "xverse/XVERSE-7B", + DownloadSource.DEFAULT: "xverse/XVERSE-7B", + DownloadSource.MODELSCOPE: "xverse/XVERSE-7B" }, "XVERSE-13B": { - "hf": "xverse/XVERSE-13B", - "ms": "xverse/XVERSE-13B", + DownloadSource.DEFAULT: "xverse/XVERSE-13B", + DownloadSource.MODELSCOPE: "xverse/XVERSE-13B" }, "XVERSE-65B": { - "hf": "xverse/XVERSE-65B", - "ms": "xverse/XVERSE-65B", + DownloadSource.DEFAULT: "xverse/XVERSE-65B", + DownloadSource.MODELSCOPE: "xverse/XVERSE-65B" }, "XVERSE-7B-Chat": { - "hf": "xverse/XVERSE-7B-Chat", - "ms": "xverse/XVERSE-7B-Chat", + DownloadSource.DEFAULT: "xverse/XVERSE-7B-Chat", + DownloadSource.MODELSCOPE: "xverse/XVERSE-7B-Chat" }, "XVERSE-13B-Chat": { - "hf": "xverse/XVERSE-13B-Chat", - "ms": "xverse/XVERSE-13B-Chat", + DownloadSource.DEFAULT: "xverse/XVERSE-13B-Chat", + DownloadSource.MODELSCOPE: "xverse/XVERSE-13B-Chat" } }, template="xverse" @@ -463,12 +537,12 @@ register_model_group( register_model_group( models={ "Yayi-7B": { - "hf": "wenge-research/yayi-7b-llama2", - "ms": "AI-ModelScope/yayi-7b-llama2", + DownloadSource.DEFAULT: "wenge-research/yayi-7b-llama2", + DownloadSource.MODELSCOPE: "AI-ModelScope/yayi-7b-llama2" }, "Yayi-13B": { - "hf": "wenge-research/yayi-13b-llama2", - "ms": "AI-ModelScope/yayi-13b-llama2", + DownloadSource.DEFAULT: "wenge-research/yayi-13b-llama2", + DownloadSource.MODELSCOPE: "AI-ModelScope/yayi-13b-llama2" } }, template="yayi" @@ -478,20 +552,20 @@ register_model_group( register_model_group( models={ "Yi-6B": { - "hf": "01-ai/Yi-6B", - "ms": "01ai/Yi-6B", + DownloadSource.DEFAULT: "01-ai/Yi-6B", + DownloadSource.MODELSCOPE: "01ai/Yi-6B" }, "Yi-34B": { - "hf": "01-ai/Yi-34B", - "ms": "01ai/Yi-34B", + DownloadSource.DEFAULT: "01-ai/Yi-34B", + DownloadSource.MODELSCOPE: "01ai/Yi-34B" }, "Yi-34B-Chat": { - "hf": "01-ai/Yi-34B-Chat", - "ms": "01ai/Yi-34B-Chat", + DownloadSource.DEFAULT: "01-ai/Yi-34B-Chat", + DownloadSource.MODELSCOPE: "01ai/Yi-34B-Chat" }, "Yi-34B-int8-Chat": { - "hf": "01-ai/Yi-34B-Chat-8bits", - "ms": "01ai/Yi-34B-Chat-8bits", + DownloadSource.DEFAULT: "01-ai/Yi-34B-Chat-8bits", + DownloadSource.MODELSCOPE: "01ai/Yi-34B-Chat-8bits" } }, template="yi" @@ -501,12 +575,12 @@ register_model_group( register_model_group( models={ "Zephyr-7B-Alpha-Chat": { - "hf": "HuggingFaceH4/zephyr-7b-alpha", - "ms": "AI-ModelScope/zephyr-7b-alpha", + DownloadSource.DEFAULT: "HuggingFaceH4/zephyr-7b-alpha", + DownloadSource.MODELSCOPE: "AI-ModelScope/zephyr-7b-alpha" }, "Zephyr-7B-Beta-Chat": { - "hf": "HuggingFaceH4/zephyr-7b-beta", - "ms": "modelscope/zephyr-7b-beta", + DownloadSource.DEFAULT: "HuggingFaceH4/zephyr-7b-beta", + DownloadSource.MODELSCOPE: "modelscope/zephyr-7b-beta" } }, template="zephyr" diff --git a/src/llmtuner/extras/misc.py b/src/llmtuner/extras/misc.py index 4f754c5c..33efb7d2 100644 --- a/src/llmtuner/extras/misc.py +++ b/src/llmtuner/extras/misc.py @@ -23,6 +23,7 @@ except ImportError: if TYPE_CHECKING: from transformers import HfArgumentParser + from llmtuner.hparams import ModelArguments class AverageMeter: @@ -117,3 +118,23 @@ def torch_gc() -> None: if torch.cuda.is_available(): torch.cuda.empty_cache() torch.cuda.ipc_collect() + + +def try_download_model_from_ms(model_args: "ModelArguments") -> None: + if not use_modelscope() or os.path.exists(model_args.model_name_or_path): + return + + try: + from modelscope import snapshot_download # type: ignore + revision = "master" if model_args.model_revision == "main" else model_args.model_revision + model_args.model_name_or_path = snapshot_download( + model_args.model_name_or_path, + revision=revision, + cache_dir=model_args.cache_dir + ) + except ImportError: + raise ImportError("Please install modelscope via `pip install modelscope -U`") + + +def use_modelscope() -> bool: + return bool(int(os.environ.get("USE_MODELSCOPE_HUB", "0"))) diff --git a/src/llmtuner/hparams/model_args.py b/src/llmtuner/hparams/model_args.py index ebf6cafa..07903b37 100644 --- a/src/llmtuner/hparams/model_args.py +++ b/src/llmtuner/hparams/model_args.py @@ -8,8 +8,8 @@ class ModelArguments: Arguments pertaining to which model/config/tokenizer we are going to fine-tune. """ model_name_or_path: str = field( - metadata={"help": "Path to pretrained model or model identifier " - "from huggingface.co/models or modelscope.cn/models."} + metadata={"help": "Path to pretrained model or model identifier from \ + huggingface.co/models or modelscope.cn/models."} ) cache_dir: Optional[str] = field( default=None, diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index 122cd7f2..87bad577 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -1,6 +1,4 @@ import math -import os - import torch from types import MethodType from typing import TYPE_CHECKING, Literal, Optional, Tuple @@ -23,8 +21,8 @@ try: except ImportError: # https://github.com/huggingface/transformers/releases/tag/v4.33.1 from transformers.deepspeed import is_deepspeed_zero3_enabled -from llmtuner.extras.logging import reset_logging, get_logger -from llmtuner.extras.misc import count_parameters, get_current_device, infer_optim_dtype +from llmtuner.extras.logging import get_logger +from llmtuner.extras.misc import count_parameters, get_current_device, infer_optim_dtype, try_download_model_from_ms from llmtuner.extras.packages import is_flash_attn2_available from llmtuner.extras.patches import llama_patch as LlamaPatches from llmtuner.hparams import FinetuningArguments @@ -58,6 +56,8 @@ def load_model_and_tokenizer( Support both training and inference. """ + try_download_model_from_ms(model_args) + config_kwargs = { "trust_remote_code": True, "cache_dir": model_args.cache_dir, @@ -65,8 +65,6 @@ def load_model_and_tokenizer( "token": model_args.hf_hub_token } - try_download_model_from_ms(model_args) - tokenizer = AutoTokenizer.from_pretrained( model_args.model_name_or_path, use_fast=model_args.use_fast_tokenizer, @@ -232,16 +230,3 @@ def load_model_and_tokenizer( logger.info("This IS expected that the trainable params is 0 if you are using model for inference only.") return model, tokenizer - - -def try_download_model_from_ms(model_args): - if int(os.environ.get('USE_MODELSCOPE_HUB', '0')) and not os.path.exists(model_args.model_name_or_path): - try: - from modelscope import snapshot_download - revision = model_args.model_revision - if revision == 'main': - revision = 'master' - model_args.model_name_or_path = snapshot_download(model_args.model_name_or_path, revision) - except ImportError as e: - raise ImportError(f'You are using `USE_MODELSCOPE_HUB=1` but you have no modelscope sdk installed. ' - f'Please install it by `pip install modelscope -U`') from e diff --git a/src/llmtuner/webui/common.py b/src/llmtuner/webui/common.py index dabfab16..ab2502e1 100644 --- a/src/llmtuner/webui/common.py +++ b/src/llmtuner/webui/common.py @@ -11,18 +11,17 @@ from transformers.utils import ( ADAPTER_SAFE_WEIGHTS_NAME ) - from llmtuner.extras.constants import ( DEFAULT_MODULE, DEFAULT_TEMPLATE, SUPPORTED_MODELS, - ALL_OFFICIAL_MODELS, - TRAINING_STAGES + TRAINING_STAGES, + DownloadSource ) +from llmtuner.extras.misc import use_modelscope from llmtuner.hparams.data_args import DATA_CONFIG - DEFAULT_CACHE_DIR = "cache" DEFAULT_DATA_DIR = "data" DEFAULT_SAVE_DIR = "saves" @@ -66,10 +65,15 @@ def save_config(lang: str, model_name: Optional[str] = None, model_path: Optiona def get_model_path(model_name: str) -> str: user_config = load_config() - cached_path = user_config["path_dict"].get(model_name, None) - if cached_path in ALL_OFFICIAL_MODELS.get(model_name, []): - cached_path = None - return cached_path or SUPPORTED_MODELS.get(model_name, "") + path_dict: Dict[DownloadSource, str] = SUPPORTED_MODELS.get(model_name, []) + model_path = user_config["path_dict"].get(model_name, None) or path_dict.get(DownloadSource.DEFAULT, "") + if ( + use_modelscope() + and path_dict.get(DownloadSource.MODELSCOPE) + and model_path == path_dict.get(DownloadSource.DEFAULT) + ): # replace path + model_path = path_dict.get(DownloadSource.MODELSCOPE) + return model_path def get_prefix(model_name: str) -> str: From 7d758e223200a67a3fa2f5a6dcc93b80388da720 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Fri, 1 Dec 2023 22:55:41 +0800 Subject: [PATCH 02/28] fix #1703 Former-commit-id: eee2e9abf6df345c5471e8ca7639293543ba720c --- src/llmtuner/extras/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llmtuner/extras/misc.py b/src/llmtuner/extras/misc.py index 33efb7d2..e1ae7d9f 100644 --- a/src/llmtuner/extras/misc.py +++ b/src/llmtuner/extras/misc.py @@ -73,7 +73,7 @@ def get_current_device() -> str: if accelerate.utils.is_xpu_available(): return "xpu:{}".format(os.environ.get("LOCAL_RANK", "0")) elif accelerate.utils.is_npu_available() or torch.cuda.is_available(): - return os.environ.get("LOCAL_RANK", "0") + return "cuda:{}".format(os.environ.get("LOCAL_RANK", "0")) else: return "cpu" From dc62b998b9f124bb5576e7fd9383f8233ccb5172 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Fri, 1 Dec 2023 22:58:29 +0800 Subject: [PATCH 03/28] update readme Former-commit-id: a0a9408e11f6b4cfb39af3f28402353b7cf48fa6 --- README.md | 2 +- README_zh.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc20c1ce..c31196e9 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ export USE_MODELSCOPE_HUB=1 # `set USE_MODELSCOPE_HUB=1` for Windows Then you can train the corresponding model by specifying a model ID of the ModelScope Hub. (find a full list of model IDs at [ModelScope Hub](https://modelscope.cn/models)) ```bash -python src/train_bash.py \ +CUDA_VISIBLE_DEVICES=0 python src/train_bash.py \ --model_name_or_path modelscope/Llama-2-7b-ms \ ... # arguments (same as above) ``` diff --git a/README_zh.md b/README_zh.md index 0aa8ac0f..6cc7a628 100644 --- a/README_zh.md +++ b/README_zh.md @@ -250,7 +250,7 @@ export USE_MODELSCOPE_HUB=1 # Windows 使用 `set USE_MODELSCOPE_HUB=1` 接着即可通过指定模型名称来训练对应的模型。(在[魔搭社区](https://modelscope.cn/models)查看所有可用的模型) ```bash -python src/train_bash.py \ +CUDA_VISIBLE_DEVICES=0 python src/train_bash.py \ --model_name_or_path modelscope/Llama-2-7b-ms \ ... # 参数同上 ``` From 3f4a3971058b7fc9936c6a8da92be5498acf3e56 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Fri, 1 Dec 2023 23:34:14 +0800 Subject: [PATCH 04/28] fix gptq model inference Former-commit-id: f7da9a87cb48cacb7d56322817b05d6f471f6508 --- src/llmtuner/model/loader.py | 13 ++++++++----- src/llmtuner/model/utils.py | 6 +++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index 87bad577..530869d5 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -146,22 +146,25 @@ def load_model_and_tokenizer( else: logger.warning("Current model does not support shift short attention.") + # Quantization configurations (using gptq or awq) + if getattr(config, "quantization_config", None): + if model_args.quantization_bit is not None: # remove bnb quantization + model_args.quantization_bit = None + config_kwargs["device_map"] = {"": get_current_device()} + quantization_config = getattr(config, "quantization_config", None) + logger.info("Loading {}-bit quantized model.".format(quantization_config.get("bits", -1))) + # Quantization configurations (using bitsandbytes library) if model_args.quantization_bit is not None: - if getattr(config, "quantization_config", None): - raise ValueError("Remove `quantization_bit` if you are using a quantized model.") - if is_deepspeed_zero3_enabled(): raise ValueError("DeepSpeed ZeRO-3 is incompatible with quantization.") if model_args.quantization_bit == 8: require_version("bitsandbytes>=0.37.0", "To fix: pip install bitsandbytes>=0.37.0") - config_kwargs["load_in_8bit"] = True config_kwargs["quantization_config"] = BitsAndBytesConfig(load_in_8bit=True) if model_args.quantization_bit == 4: require_version("bitsandbytes>=0.39.0", "To fix: pip install bitsandbytes>=0.39.0") - config_kwargs["load_in_4bit"] = True config_kwargs["quantization_config"] = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=model_args.compute_dtype, diff --git a/src/llmtuner/model/utils.py b/src/llmtuner/model/utils.py index 08eea563..1eab538d 100644 --- a/src/llmtuner/model/utils.py +++ b/src/llmtuner/model/utils.py @@ -22,7 +22,11 @@ def dispatch_model(model: "PreTrainedModel") -> "PreTrainedModel": Dispatches a pre-trained model to GPUs with balanced memory. Borrowed from: https://github.com/huggingface/transformers/blob/v4.31.0/src/transformers/modeling_utils.py#L2803 """ - if getattr(model, "is_loaded_in_8bit", False) or getattr(model, "is_loaded_in_4bit", False): # do nothing + if ( + getattr(model, "is_loaded_in_8bit", False) # bnb + or getattr(model, "is_loaded_in_4bit", False) # bnb + or getattr(model.config, "quantization_config", None) # gptq or awq + ): # already set on current device return model if torch.cuda.device_count() > 1: From c58714b9b0c50d88fbcc807657101103a604ad0c Mon Sep 17 00:00:00 2001 From: hiyouga Date: Fri, 1 Dec 2023 23:37:10 +0800 Subject: [PATCH 05/28] tiny fix Former-commit-id: fd2782a06ba4efa76cacbb49eb76a05de8d8aca6 --- src/llmtuner/model/utils.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/llmtuner/model/utils.py b/src/llmtuner/model/utils.py index 1eab538d..12a45445 100644 --- a/src/llmtuner/model/utils.py +++ b/src/llmtuner/model/utils.py @@ -22,11 +22,7 @@ def dispatch_model(model: "PreTrainedModel") -> "PreTrainedModel": Dispatches a pre-trained model to GPUs with balanced memory. Borrowed from: https://github.com/huggingface/transformers/blob/v4.31.0/src/transformers/modeling_utils.py#L2803 """ - if ( - getattr(model, "is_loaded_in_8bit", False) # bnb - or getattr(model, "is_loaded_in_4bit", False) # bnb - or getattr(model.config, "quantization_config", None) # gptq or awq - ): # already set on current device + if getattr(model, "quantization_method", None): # already set on current device return model if torch.cuda.device_count() > 1: From d46ce885198e05ae55ed872001a707057f066a43 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sat, 2 Dec 2023 00:27:15 +0800 Subject: [PATCH 06/28] fix gptq training Former-commit-id: bec58e3dc575aa4247e563881a456328ee5ef496 --- src/llmtuner/extras/misc.py | 4 +++- src/llmtuner/model/adapter.py | 2 +- src/llmtuner/model/utils.py | 16 ++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/llmtuner/extras/misc.py b/src/llmtuner/extras/misc.py index e1ae7d9f..10f5799e 100644 --- a/src/llmtuner/extras/misc.py +++ b/src/llmtuner/extras/misc.py @@ -72,7 +72,9 @@ def get_current_device() -> str: import accelerate if accelerate.utils.is_xpu_available(): return "xpu:{}".format(os.environ.get("LOCAL_RANK", "0")) - elif accelerate.utils.is_npu_available() or torch.cuda.is_available(): + elif accelerate.utils.is_npu_available(): + return "npu:{}".format(os.environ.get("LOCAL_RANK", "0")) + elif torch.cuda.is_available(): return "cuda:{}".format(os.environ.get("LOCAL_RANK", "0")) else: return "cpu" diff --git a/src/llmtuner/model/adapter.py b/src/llmtuner/model/adapter.py index 9a3fb3f6..53dfd6ea 100644 --- a/src/llmtuner/model/adapter.py +++ b/src/llmtuner/model/adapter.py @@ -87,7 +87,7 @@ def init_adapter( if is_trainable and checkpoint_to_resume is None: # create new lora weights while training if len(finetuning_args.lora_target) == 1 and finetuning_args.lora_target[0] == "all": - target_modules = find_all_linear_modules(model, model_args.quantization_bit) + target_modules = find_all_linear_modules(model) else: target_modules = finetuning_args.lora_target diff --git a/src/llmtuner/model/utils.py b/src/llmtuner/model/utils.py index 12a45445..a9138e7e 100644 --- a/src/llmtuner/model/utils.py +++ b/src/llmtuner/model/utils.py @@ -42,18 +42,18 @@ def dispatch_model(model: "PreTrainedModel") -> "PreTrainedModel": return model.cuda() -def find_all_linear_modules( - model: "PreTrainedModel", - quantization_bit: Optional[int] = None -) -> List[str]: +def find_all_linear_modules(model: "PreTrainedModel") -> List[str]: r""" Finds all available modules to apply lora. """ - if quantization_bit is not None: - import bitsandbytes as bnb - linear_cls = bnb.nn.Linear4bit if quantization_bit == 4 else bnb.nn.Linear8bitLt - else: + quantization_method = getattr(model, "quantization_method", None) + if quantization_method is None: linear_cls = torch.nn.Linear + elif quantization_method == "bitsandbytes": + import bitsandbytes as bnb + linear_cls = bnb.nn.Linear4bit if getattr(model, "is_loaded_in_4bit", False) else bnb.nn.Linear8bitLt + else: + raise ValueError("Finding linear modules for {} models is not supported.".format(quantization_method)) output_layer_names = ["lm_head"] if model.config.model_type == "chatglm": From f7cdc0b54cc7155382c49b79e71d04cbec810905 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sat, 2 Dec 2023 00:35:29 +0800 Subject: [PATCH 07/28] add xuanyuan models Former-commit-id: 1dfa9de3723550cddf24bbc0739cad6207731212 --- src/llmtuner/data/template.py | 17 +++++++++++++++++ src/llmtuner/extras/constants.py | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/llmtuner/data/template.py b/src/llmtuner/data/template.py index ebb633c5..d9e83326 100644 --- a/src/llmtuner/data/template.py +++ b/src/llmtuner/data/template.py @@ -650,6 +650,23 @@ register_template( ) +register_template( + name="xuanyuan", + prefix=[ + "{{system}}" + ], + prompt=[ + "Human: {{query}} Assistant:" + ], + system=( + "以下是用户和人工智能助手之间的对话。用户以Human开头,人工智能助手以Assistant开头," + "会对人类提出的问题给出有帮助、高质量、详细和礼貌的回答,并且总是拒绝参与与不道德、" + "不安全、有争议、政治敏感等相关的话题、问题和指示。\n" + ), + sep=[] +) + + register_template( name="xverse", prefix=[ diff --git a/src/llmtuner/extras/constants.py b/src/llmtuner/extras/constants.py index a36102a1..56d7ebb8 100644 --- a/src/llmtuner/extras/constants.py +++ b/src/llmtuner/extras/constants.py @@ -507,6 +507,25 @@ register_model_group( ) +register_model_group( + models={ + "XuanYuan-70B": { + DownloadSource.DEFAULT: "Duxiaoman-DI/XuanYuan-70B" + }, + "XuanYuan-70B-Chat": { + DownloadSource.DEFAULT: "Duxiaoman-DI/XuanYuan-70B-Chat" + }, + "XuanYuan-70B-int8-Chat": { + DownloadSource.DEFAULT: "Duxiaoman-DI/XuanYuan-70B-Chat-8bit" + }, + "XuanYuan-70B-int4-Chat": { + DownloadSource.DEFAULT: "Duxiaoman-DI/XuanYuan-70B-Chat-4bit" + } + }, + template="xuanyuan" +) + + register_model_group( models={ "XVERSE-7B": { From 823a75ef22600928a9c1cf34d93fc8e1449d261b Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sat, 2 Dec 2023 00:37:53 +0800 Subject: [PATCH 08/28] fix #1642 Former-commit-id: 11be28201f688ac21cf94135067d37e9aa7ab0a1 --- src/llmtuner/model/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llmtuner/model/utils.py b/src/llmtuner/model/utils.py index a9138e7e..42bef35b 100644 --- a/src/llmtuner/model/utils.py +++ b/src/llmtuner/model/utils.py @@ -25,7 +25,7 @@ def dispatch_model(model: "PreTrainedModel") -> "PreTrainedModel": if getattr(model, "quantization_method", None): # already set on current device return model - if torch.cuda.device_count() > 1: + if torch.cuda.device_count() > 1 and getattr(model.config, "model_type", None) != "chatglm": from accelerate import dispatch_model from accelerate.utils import infer_auto_device_map, get_balanced_memory From a58d846a291b497a3881136fe62cefc7b7045ef4 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sat, 2 Dec 2023 01:31:24 +0800 Subject: [PATCH 09/28] add logo Former-commit-id: 597894ad31c186120335252ccc0cc48fcea701b4 --- README.md | 2 +- README_zh.md | 2 +- assets/logo.png | Bin 0 -> 56849 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 assets/logo.png diff --git a/README.md b/README.md index c31196e9..386e9a2f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# LLaMA Factory: Training and Evaluating Large Language Models with Minimal Effort +![# LLaMA Factory](assets/logo.png) [![GitHub Repo stars](https://img.shields.io/github/stars/hiyouga/LLaMA-Factory?style=social)](https://github.com/hiyouga/LLaMA-Factory/stargazers) [![GitHub Code License](https://img.shields.io/github/license/hiyouga/LLaMA-Factory)](LICENSE) diff --git a/README_zh.md b/README_zh.md index 6cc7a628..c6758424 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,4 +1,4 @@ -# LLaMA Factory: 轻松的大模型训练与评估 +![# LLaMA Factory](assets/logo.png) [![GitHub Repo stars](https://img.shields.io/github/stars/hiyouga/LLaMA-Factory?style=social)](https://github.com/hiyouga/LLaMA-Factory/stargazers) [![GitHub Code License](https://img.shields.io/github/license/hiyouga/LLaMA-Factory)](LICENSE) diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb3dd569342ca3cd30a582fd664145bd88b360c GIT binary patch literal 56849 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 z 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\TWa&x~Ao>M$QqWRV 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 Date: Sun, 3 Dec 2023 11:33:12 +0800 Subject: [PATCH 10/28] fix #1707 #1710 Former-commit-id: 243a596518ad69cf1eec20a082534b9e94353ce4 --- src/llmtuner/extras/misc.py | 12 ------------ src/llmtuner/model/loader.py | 7 ++++--- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/llmtuner/extras/misc.py b/src/llmtuner/extras/misc.py index 10f5799e..2c659993 100644 --- a/src/llmtuner/extras/misc.py +++ b/src/llmtuner/extras/misc.py @@ -68,18 +68,6 @@ def count_parameters(model: torch.nn.Module) -> Tuple[int, int]: return trainable_params, all_param -def get_current_device() -> str: - import accelerate - if accelerate.utils.is_xpu_available(): - return "xpu:{}".format(os.environ.get("LOCAL_RANK", "0")) - elif accelerate.utils.is_npu_available(): - return "npu:{}".format(os.environ.get("LOCAL_RANK", "0")) - elif torch.cuda.is_available(): - return "cuda:{}".format(os.environ.get("LOCAL_RANK", "0")) - else: - return "cpu" - - def get_logits_processor() -> "LogitsProcessorList": r""" Gets logits processor that removes NaN and Inf logits. diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index 530869d5..e5075e37 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -1,3 +1,4 @@ +import os import math import torch from types import MethodType @@ -22,7 +23,7 @@ except ImportError: # https://github.com/huggingface/transformers/releases/tag/v from transformers.deepspeed import is_deepspeed_zero3_enabled from llmtuner.extras.logging import get_logger -from llmtuner.extras.misc import count_parameters, get_current_device, infer_optim_dtype, try_download_model_from_ms +from llmtuner.extras.misc import count_parameters, infer_optim_dtype, try_download_model_from_ms from llmtuner.extras.packages import is_flash_attn2_available from llmtuner.extras.patches import llama_patch as LlamaPatches from llmtuner.hparams import FinetuningArguments @@ -150,7 +151,7 @@ def load_model_and_tokenizer( if getattr(config, "quantization_config", None): if model_args.quantization_bit is not None: # remove bnb quantization model_args.quantization_bit = None - config_kwargs["device_map"] = {"": get_current_device()} + config_kwargs["device_map"] = {"": int(os.environ.get("LOCAL_RANK", "0"))} quantization_config = getattr(config, "quantization_config", None) logger.info("Loading {}-bit quantized model.".format(quantization_config.get("bits", -1))) @@ -172,7 +173,7 @@ def load_model_and_tokenizer( bnb_4bit_quant_type=model_args.quantization_type ) - config_kwargs["device_map"] = {"": get_current_device()} + config_kwargs["device_map"] = {"": int(os.environ.get("LOCAL_RANK", "0"))} logger.info("Quantizing model to {} bit.".format(model_args.quantization_bit)) # Load pre-trained models (without valuehead) From 74851e70335d70894cbd55cbe7f5aeaea407afae Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sun, 3 Dec 2023 20:52:54 +0800 Subject: [PATCH 11/28] implement rm server #1543 Former-commit-id: 2e5bb6888c86079493456c2ddd525f8c52b9963e --- src/llmtuner/api/app.py | 18 +++++++++- src/llmtuner/api/protocol.py | 13 +++++++ src/llmtuner/chat/chat_model.py | 45 +++++++++++++++++++++++-- src/llmtuner/hparams/finetuning_args.py | 4 +-- src/llmtuner/model/loader.py | 8 +++-- src/llmtuner/train/dpo/workflow.py | 6 ++-- src/llmtuner/train/ppo/workflow.py | 4 +-- src/llmtuner/train/pt/workflow.py | 2 +- src/llmtuner/train/rm/workflow.py | 4 +-- src/llmtuner/train/sft/workflow.py | 4 +-- src/llmtuner/train/utils.py | 20 +++++++---- 11 files changed, 104 insertions(+), 24 deletions(-) diff --git a/src/llmtuner/api/app.py b/src/llmtuner/api/app.py index c01fa0df..856b936e 100644 --- a/src/llmtuner/api/app.py +++ b/src/llmtuner/api/app.py @@ -15,7 +15,9 @@ from llmtuner.api.protocol import ( ChatCompletionStreamResponse, ChatCompletionResponseChoice, ChatCompletionResponseStreamChoice, - ChatCompletionResponseUsage + ChatCompletionResponseUsage, + ScoreEvaluationRequest, + ScoreEvaluationResponse ) from llmtuner.chat import ChatModel from llmtuner.extras.misc import torch_gc @@ -68,6 +70,9 @@ def create_app(chat_model: "ChatModel") -> "FastAPI": @app.post("/v1/chat/completions", response_model=ChatCompletionResponse, status_code=status.HTTP_200_OK) async def create_chat_completion(request: ChatCompletionRequest): + if not chat_model.can_generate: + raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED, detail="Not allowed") + if len(request.messages) == 0 or request.messages[-1].role != Role.USER: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid request") @@ -156,6 +161,17 @@ def create_app(chat_model: "ChatModel") -> "FastAPI": yield to_json(chunk) yield "[DONE]" + @app.post("/v1/score/evaluation", response_model=ScoreEvaluationResponse, status_code=status.HTTP_200_OK) + async def create_score_evaluation(request: ScoreEvaluationRequest): + if chat_model.can_generate: + raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED, detail="Not allowed") + + if len(request.messages) == 0: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid request") + + scores = chat_model.get_scores(request.messages, max_length=request.max_length) + return ScoreEvaluationResponse(model=request.model, scores=scores) + return app diff --git a/src/llmtuner/api/protocol.py b/src/llmtuner/api/protocol.py index 6b99da40..a5b5c81d 100644 --- a/src/llmtuner/api/protocol.py +++ b/src/llmtuner/api/protocol.py @@ -81,3 +81,16 @@ class ChatCompletionStreamResponse(BaseModel): created: Optional[int] = Field(default_factory=lambda: int(time.time())) model: str choices: List[ChatCompletionResponseStreamChoice] + + +class ScoreEvaluationRequest(BaseModel): + model: str + messages: List[str] + max_length: Optional[int] = None + + +class ScoreEvaluationResponse(BaseModel): + id: Optional[str] = "scoreeval-default" + object: Optional[str] = "score.evaluation" + model: str + scores: List[float] diff --git a/src/llmtuner/chat/chat_model.py b/src/llmtuner/chat/chat_model.py index 9966a813..500009fe 100644 --- a/src/llmtuner/chat/chat_model.py +++ b/src/llmtuner/chat/chat_model.py @@ -1,4 +1,5 @@ import torch +import tiktoken from dataclasses import dataclass from typing import Any, Dict, Generator, List, Literal, Optional, Tuple from threading import Thread @@ -22,8 +23,11 @@ class ChatModel: def __init__(self, args: Optional[Dict[str, Any]] = None) -> None: model_args, data_args, finetuning_args, self.generating_args = get_infer_args(args) - self.model, self.tokenizer = load_model_and_tokenizer(model_args, finetuning_args) - self.tokenizer.padding_side = "left" + self.can_generate = (finetuning_args.stage == "sft") + self.model, self.tokenizer = load_model_and_tokenizer( + model_args, finetuning_args, is_trainable=False, add_valuehead=(not self.can_generate) + ) + self.tokenizer.padding_side = "left" if self.can_generate else "right" self.model = dispatch_model(self.model) self.template = get_template_and_fix_tokenizer(data_args.template, self.tokenizer) self.system_prompt = data_args.system_prompt @@ -130,3 +134,40 @@ class ChatModel: thread.start() yield from streamer + + @torch.inference_mode() + def get_scores( + self, + batch_input: List[str], + **input_kwargs + ) -> List[float]: + if isinstance(getattr(self.tokenizer, "tokenizer", None), tiktoken.Encoding): # for tiktoken tokenizer (Qwen) + kwargs = dict(allowed_special="all") + else: + kwargs = dict(add_special_tokens=True) + + max_length = input_kwargs.pop("max_length", None) + device = getattr(self.model.pretrained_model, "device", "cuda") + + inputs = self.tokenizer( + batch_input, + padding=True, + truncation=True, + max_length=max_length or getattr(self.model.config, "max_position_embeddings", 1024), + pad_to_multiple_of=8, + return_tensors="pt", + **kwargs + ).to(device) + + input_ids: torch.Tensor = inputs["input_ids"] + _, _, values = self.model(**inputs, output_hidden_states=True, return_dict=True) + + if getattr(self.model.config, "model_type", None) == "chatglm": + values = torch.transpose(values, 0, 1) + + scores = [] + for i in range(input_ids.size(0)): + length = (input_ids[i] != self.tokenizer.pad_token_id).nonzero()[-1] + 1 + scores.append(values[i, length-1].nan_to_num().item()) + + return scores diff --git a/src/llmtuner/hparams/finetuning_args.py b/src/llmtuner/hparams/finetuning_args.py index cf60676a..06e5b2c1 100644 --- a/src/llmtuner/hparams/finetuning_args.py +++ b/src/llmtuner/hparams/finetuning_args.py @@ -118,9 +118,9 @@ class RLHFArguments: default=None, metadata={"help": "The number of bits to quantize the reward model."} ) - reward_model_type: Optional[Literal["lora", "full"]] = field( + reward_model_type: Optional[Literal["lora", "full", "api"]] = field( default="lora", - metadata={"help": "The checkpoint type of the reward model. The lora type only supports lora training."} + metadata={"help": "The type of the reward model in PPO training. Lora model only supports lora training."} ) diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index e5075e37..1f29abb2 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -49,7 +49,7 @@ def load_model_and_tokenizer( model_args: "ModelArguments", finetuning_args: "FinetuningArguments", is_trainable: Optional[bool] = False, - stage: Optional[Literal["pt", "sft", "rm", "ppo"]] = "sft" + add_valuehead: Optional[bool] = False ) -> Tuple[PreTrainedModel, "PreTrainedTokenizer"]: r""" Loads pretrained model and tokenizer. @@ -205,10 +205,9 @@ def load_model_and_tokenizer( # Initialize adapters model = prepare_model_for_training(model=model, finetuning_args=finetuning_args) if is_trainable else model model = init_adapter(model, model_args, finetuning_args, is_trainable) - model = model.train() if is_trainable else model.eval() # Prepare model with valuehead for RLHF - if stage in ["rm", "ppo"]: + if add_valuehead: model: "AutoModelForCausalLMWithValueHead" = AutoModelForCausalLMWithValueHead.from_pretrained(model) setattr(model, "_keys_to_ignore_on_save", [name for name, _ in model.named_parameters() if "pretrained_model" in name]) setattr(model, "tie_weights", MethodType(lambda _: None, model)) # use empty method @@ -224,6 +223,9 @@ def load_model_and_tokenizer( if not is_trainable: model.requires_grad_(False) # fix all model params model = model.to(model_args.compute_dtype) if model_args.quantization_bit is None else model + model.eval() + else: + model.train() trainable_params, all_param = count_parameters(model) logger.info("trainable params: {:d} || all params: {:d} || trainable%: {:.4f}".format( diff --git a/src/llmtuner/train/dpo/workflow.py b/src/llmtuner/train/dpo/workflow.py index 6b5a222d..7ce2d44c 100644 --- a/src/llmtuner/train/dpo/workflow.py +++ b/src/llmtuner/train/dpo/workflow.py @@ -25,11 +25,11 @@ def run_dpo( callbacks: Optional[List["TrainerCallback"]] = None ): dataset = get_dataset(model_args, data_args) - model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train, stage="sft") + model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train) dataset = preprocess_dataset(dataset, tokenizer, data_args, training_args, stage="rm") data_collator = DPODataCollatorWithPadding( tokenizer=tokenizer, - pad_to_multiple_of=4, + pad_to_multiple_of=8, label_pad_token_id=IGNORE_INDEX if data_args.ignore_pad_token_for_loss else tokenizer.pad_token_id ) @@ -37,7 +37,7 @@ def run_dpo( if finetuning_args.ref_model is None and (not training_args.do_train): # use the model itself ref_model = model else: - ref_model = create_ref_model(model_args, finetuning_args, stage="dpo") + ref_model = create_ref_model(model_args, finetuning_args) # Update arguments training_args_dict = training_args.to_dict() diff --git a/src/llmtuner/train/ppo/workflow.py b/src/llmtuner/train/ppo/workflow.py index 88d5e49d..933f69db 100644 --- a/src/llmtuner/train/ppo/workflow.py +++ b/src/llmtuner/train/ppo/workflow.py @@ -28,14 +28,14 @@ def run_ppo( callbacks: Optional[List["TrainerCallback"]] = None ): dataset = get_dataset(model_args, data_args) - model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train, stage="ppo") + model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train, add_valuehead=True) dataset = preprocess_dataset(dataset, tokenizer, data_args, training_args, stage="ppo") tokenizer.padding_side = "left" # use left-padding in generation while using right-padding in training data_collator = DataCollatorWithPadding(tokenizer=tokenizer) # Create reference model and reward model - ref_model = create_ref_model(model_args, finetuning_args, stage="ppo") + ref_model = create_ref_model(model_args, finetuning_args, add_valuehead=True) reward_model = create_reward_model(model, model_args, finetuning_args) # Create ppo config diff --git a/src/llmtuner/train/pt/workflow.py b/src/llmtuner/train/pt/workflow.py index eadfa001..27a6d2c4 100644 --- a/src/llmtuner/train/pt/workflow.py +++ b/src/llmtuner/train/pt/workflow.py @@ -22,7 +22,7 @@ def run_pt( callbacks: Optional[List["TrainerCallback"]] = None ): dataset = get_dataset(model_args, data_args) - model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train, stage="pt") + model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train) dataset = preprocess_dataset(dataset, tokenizer, data_args, training_args, stage="pt") data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False) diff --git a/src/llmtuner/train/rm/workflow.py b/src/llmtuner/train/rm/workflow.py index ecc409b7..944024ab 100644 --- a/src/llmtuner/train/rm/workflow.py +++ b/src/llmtuner/train/rm/workflow.py @@ -25,9 +25,9 @@ def run_rm( callbacks: Optional[List["TrainerCallback"]] = None ): dataset = get_dataset(model_args, data_args) - model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train, stage="rm") + model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train, add_valuehead=True) dataset = preprocess_dataset(dataset, tokenizer, data_args, training_args, stage="rm") - data_collator = PairwiseDataCollatorWithPadding(tokenizer, pad_to_multiple_of=4) + data_collator = PairwiseDataCollatorWithPadding(tokenizer, pad_to_multiple_of=8) # Update arguments training_args_dict = training_args.to_dict() diff --git a/src/llmtuner/train/sft/workflow.py b/src/llmtuner/train/sft/workflow.py index 4e504903..94a81151 100644 --- a/src/llmtuner/train/sft/workflow.py +++ b/src/llmtuner/train/sft/workflow.py @@ -26,7 +26,7 @@ def run_sft( callbacks: Optional[List["TrainerCallback"]] = None ): dataset = get_dataset(model_args, data_args) - model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train, stage="sft") + model, tokenizer = load_model_and_tokenizer(model_args, finetuning_args, training_args.do_train) dataset = preprocess_dataset(dataset, tokenizer, data_args, training_args, stage="sft") if training_args.predict_with_generate: @@ -34,7 +34,7 @@ def run_sft( data_collator = DataCollatorForSeq2Seq( tokenizer=tokenizer, - pad_to_multiple_of=4 if tokenizer.padding_side == "right" else None, # for shift short attention + pad_to_multiple_of=8 if tokenizer.padding_side == "right" else None, # for shift short attention label_pad_token_id=IGNORE_INDEX if data_args.ignore_pad_token_for_loss else tokenizer.pad_token_id ) diff --git a/src/llmtuner/train/utils.py b/src/llmtuner/train/utils.py index 6b40f33b..61700b53 100644 --- a/src/llmtuner/train/utils.py +++ b/src/llmtuner/train/utils.py @@ -1,5 +1,5 @@ import torch -from typing import TYPE_CHECKING, Literal, Union +from typing import TYPE_CHECKING, Optional, Union from llmtuner.extras.logging import get_logger from llmtuner.hparams import ModelArguments, FinetuningArguments @@ -35,7 +35,7 @@ def create_modelcard_and_push( def create_ref_model( model_args: "ModelArguments", finetuning_args: "FinetuningArguments", - stage: Literal["ppo", "dpo"] + add_valuehead: Optional[bool] = False ) -> Union["PreTrainedModel", "AutoModelForCausalLMWithValueHead"]: r""" Creates reference model for PPO/DPO training. Evaluation mode is not supported. @@ -51,13 +51,17 @@ def create_ref_model( )) ref_model_args = ModelArguments(**ref_model_args_dict) ref_finetuning_args = FinetuningArguments(finetuning_type="lora") - ref_model, _ = load_model_and_tokenizer(ref_model_args, ref_finetuning_args, is_trainable=False, stage=stage) + ref_model, _ = load_model_and_tokenizer( + ref_model_args, ref_finetuning_args, is_trainable=False, add_valuehead=add_valuehead + ) logger.info("Created reference model from {}".format(finetuning_args.ref_model)) else: if finetuning_args.finetuning_type == "lora": ref_model = None else: - ref_model, _ = load_model_and_tokenizer(model_args, finetuning_args, is_trainable=False, stage=stage) + ref_model, _ = load_model_and_tokenizer( + model_args, finetuning_args, is_trainable=False, add_valuehead=add_valuehead + ) logger.info("Created reference model from the model itself.") return ref_model @@ -71,7 +75,9 @@ def create_reward_model( r""" Creates reward model for PPO training. """ - if finetuning_args.reward_model_type == "lora": + if finetuning_args.reward_model_type == "api": + raise NotImplementedError + elif finetuning_args.reward_model_type == "lora": model.pretrained_model.load_adapter(finetuning_args.reward_model, "reward") for name, param in model.named_parameters(): # https://github.com/huggingface/peft/issues/1090 if "default" in name: @@ -93,7 +99,9 @@ def create_reward_model( )) reward_model_args = ModelArguments(**reward_model_args_dict) reward_finetuning_args = FinetuningArguments(finetuning_type="lora") - reward_model, _ = load_model_and_tokenizer(reward_model_args, reward_finetuning_args, is_trainable=False, stage="ppo") + reward_model, _ = load_model_and_tokenizer( + reward_model_args, reward_finetuning_args, is_trainable=False, add_valuehead=True + ) logger.info("Load full weights of reward model from {}".format(finetuning_args.reward_model)) logger.warning("Please ensure the ppo model and reward model share SAME tokenizer and vocabulary.") return reward_model From 3d200af1d217e4092b381b2ef3d5f0a98b43d64d Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sun, 3 Dec 2023 21:38:51 +0800 Subject: [PATCH 12/28] ppo support rm server Former-commit-id: 20b0edf16f5b42cb2c4a795674647afb68cb3a4a --- src/llmtuner/chat/chat_model.py | 5 +++-- src/llmtuner/extras/packages.py | 5 +++++ src/llmtuner/train/ppo/trainer.py | 30 ++++++++++++++++++++---------- src/llmtuner/train/ppo/utils.py | 16 +++++++++++++++- src/llmtuner/train/utils.py | 6 ++++-- 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/llmtuner/chat/chat_model.py b/src/llmtuner/chat/chat_model.py index 500009fe..6e4c28e7 100644 --- a/src/llmtuner/chat/chat_model.py +++ b/src/llmtuner/chat/chat_model.py @@ -167,7 +167,8 @@ class ChatModel: scores = [] for i in range(input_ids.size(0)): - length = (input_ids[i] != self.tokenizer.pad_token_id).nonzero()[-1] + 1 - scores.append(values[i, length-1].nan_to_num().item()) + end_indexes = (input_ids[i] != self.tokenizer.pad_token_id).nonzero() + end_index = end_indexes[-1].item() if len(end_indexes) else 0 + scores.append(values[i, end_index].nan_to_num().item()) return scores diff --git a/src/llmtuner/extras/packages.py b/src/llmtuner/extras/packages.py index 22cab732..22d725c2 100644 --- a/src/llmtuner/extras/packages.py +++ b/src/llmtuner/extras/packages.py @@ -18,6 +18,7 @@ _flash_attn2_available = is_package_available("flash_attn") and get_package_vers _jieba_available = is_package_available("jieba") _matplotlib_available = is_package_available("matplotlib") _nltk_available = is_package_available("nltk") +_requests_available = is_package_available("requests") _rouge_available = is_package_available("rouge_chinese") _starlette_available = is_package_available("sse_starlette") _uvicorn_available = is_package_available("uvicorn") @@ -43,6 +44,10 @@ def is_nltk_available(): return _nltk_available +def is_requests_available(): + return _requests_available + + def is_rouge_available(): return _rouge_available diff --git a/src/llmtuner/train/ppo/trainer.py b/src/llmtuner/train/ppo/trainer.py index b81aa7ff..ade5a41c 100644 --- a/src/llmtuner/train/ppo/trainer.py +++ b/src/llmtuner/train/ppo/trainer.py @@ -3,9 +3,9 @@ import sys import math import torch from tqdm import tqdm -from typing import TYPE_CHECKING, List, Optional, Tuple +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple -from transformers import BatchEncoding, GenerationConfig, Trainer, TrainerState, TrainerControl +from transformers import GenerationConfig, Trainer, TrainerState, TrainerControl from transformers.utils import WEIGHTS_NAME, SAFE_WEIGHTS_NAME from transformers.trainer_utils import PREFIX_CHECKPOINT_DIR from transformers.trainer_pt_utils import remove_dummy_checkpoint @@ -16,7 +16,7 @@ from trl.core import PPODecorators, logprobs_from_logits from llmtuner.extras.callbacks import LogCallback, SavePeftModelCallback from llmtuner.extras.logging import get_logger from llmtuner.extras.misc import AverageMeter, count_parameters, get_logits_processor -from llmtuner.train.ppo.utils import dump_layernorm, restore_layernorm, replace_model +from llmtuner.train.ppo.utils import dump_layernorm, get_rewards_from_server, restore_layernorm, replace_model if TYPE_CHECKING: from transformers import Seq2SeqTrainingArguments, TrainerCallback @@ -200,7 +200,7 @@ class CustomPPOTrainer(PPOTrainer, Trainer): ) @torch.no_grad() - def get_inputs(self, batch: BatchEncoding) -> Tuple[List[torch.Tensor], List[torch.Tensor]]: + def get_inputs(self, batch: Dict[str, torch.Tensor]) -> Tuple[List[torch.Tensor], List[torch.Tensor]]: r""" Generates model's responses given queries. """ @@ -208,7 +208,7 @@ class CustomPPOTrainer(PPOTrainer, Trainer): layernorm_params = dump_layernorm(self.model) unwrapped_model: "AutoModelForCausalLMWithValueHead" = self.accelerator.unwrap_model(self.model) - response: torch.Tensor = unwrapped_model.generate( + generate_output: torch.Tensor = unwrapped_model.generate( generation_config=self.generation_config, logits_processor=get_logits_processor(), **batch @@ -217,7 +217,8 @@ class CustomPPOTrainer(PPOTrainer, Trainer): if self.finetuning_args.upcast_layernorm: restore_layernorm(self.model, layernorm_params) - query, response = batch["input_ids"].detach().cpu(), response[:, batch["input_ids"].size(-1):].detach().cpu() + query = batch["input_ids"].detach().cpu() + response = generate_output[:, batch["input_ids"].size(-1):].detach().cpu() queries, responses = [], [] for i in range(len(query)): query_length = (query[i] != self.tokenizer.pad_token_id).nonzero()[0].item() @@ -242,17 +243,26 @@ class CustomPPOTrainer(PPOTrainer, Trainer): ) -> List[torch.Tensor]: r""" Computes scores using given reward model. + + Both inputs and outputs are put on CPU. """ - if self.reward_model is None: + if self.finetuning_args.reward_model_type == "api": + token_ids = [torch.cat((q, r), dim=-1).tolist() for q, r in zip(queries, responses)] + messages = self.tokenizer.batch_decode(token_ids, skip_special_tokens=True) + return get_rewards_from_server(self.reward_model, messages) + + if self.finetuning_args.reward_model_type == "lora": replace_model(unwrapped_model, target="reward") + reward_model = self.model + else: + reward_model = self.reward_model batch = self.prepare_model_inputs(queries, responses) with torch.cuda.amp.autocast(dtype=self.model_args.compute_dtype): # support bf16 - reward_model = self.reward_model if self.reward_model is not None else self.model _, _, values = reward_model(**batch, output_hidden_states=True, return_dict=True) - if getattr(unwrapped_model.config, "model_type", None) == "chatglm": + if getattr(unwrapped_model.config, "model_type", None) == "chatglm": # assume same architecture values = torch.transpose(values, 0, 1) rewards = [] @@ -261,7 +271,7 @@ class CustomPPOTrainer(PPOTrainer, Trainer): end_index = end_indexes[-1].item() if len(end_indexes) else 0 rewards.append(values[i, end_index].float().detach().cpu()) # use fp32 type - if self.reward_model is None: + if self.finetuning_args.reward_model_type == "lora": replace_model(unwrapped_model, target="default") return rewards diff --git a/src/llmtuner/train/ppo/utils.py b/src/llmtuner/train/ppo/utils.py index 74453a39..12e9bfcb 100644 --- a/src/llmtuner/train/ppo/utils.py +++ b/src/llmtuner/train/ppo/utils.py @@ -1,10 +1,24 @@ +import json import torch -from typing import TYPE_CHECKING, Dict, Literal, Optional +from typing import TYPE_CHECKING, Dict, List, Literal, Optional + +from llmtuner.extras.packages import is_requests_available if TYPE_CHECKING: from transformers import PreTrainedModel from trl import AutoModelForCausalLMWithValueHead +if is_requests_available(): + import requests + + +def get_rewards_from_server(server_url: str, messages: List[str]) -> List[torch.Tensor]: + headers = {"Content-Type": "application/json"} + payload = {"model": "model", "messages": messages} + response = requests.post(server_url, json=payload, headers=headers) + rewards = json.loads(response.text)["scores"] + return torch.Tensor(rewards) + def replace_model(model: "AutoModelForCausalLMWithValueHead", target: Literal["default", "reward"]) -> None: if target == "reward": # save default head temporarily diff --git a/src/llmtuner/train/utils.py b/src/llmtuner/train/utils.py index 61700b53..e7fc279b 100644 --- a/src/llmtuner/train/utils.py +++ b/src/llmtuner/train/utils.py @@ -76,7 +76,9 @@ def create_reward_model( Creates reward model for PPO training. """ if finetuning_args.reward_model_type == "api": - raise NotImplementedError + assert finetuning_args.reward_model.startswith("http"), "Please provide full url." + logger.info("Use reward server {}".format(finetuning_args.reward_model)) + return finetuning_args.reward_model elif finetuning_args.reward_model_type == "lora": model.pretrained_model.load_adapter(finetuning_args.reward_model, "reward") for name, param in model.named_parameters(): # https://github.com/huggingface/peft/issues/1090 @@ -102,6 +104,6 @@ def create_reward_model( reward_model, _ = load_model_and_tokenizer( reward_model_args, reward_finetuning_args, is_trainable=False, add_valuehead=True ) - logger.info("Load full weights of reward model from {}".format(finetuning_args.reward_model)) + logger.info("Loaded full weights of reward model from {}".format(finetuning_args.reward_model)) logger.warning("Please ensure the ppo model and reward model share SAME tokenizer and vocabulary.") return reward_model From 3c667217be863d02026abd42aeb39c0217c52bf7 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sun, 3 Dec 2023 21:40:40 +0800 Subject: [PATCH 13/28] fix bug Former-commit-id: 2fd7a8fc3134af66193a5e8db8fea35025f82de9 --- src/llmtuner/train/ppo/trainer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llmtuner/train/ppo/trainer.py b/src/llmtuner/train/ppo/trainer.py index ade5a41c..f02dbdc3 100644 --- a/src/llmtuner/train/ppo/trainer.py +++ b/src/llmtuner/train/ppo/trainer.py @@ -66,7 +66,7 @@ class CustomPPOTrainer(PPOTrainer, Trainer): if self.args.max_steps > 0: logger.info("max_steps is given, it will override any value given in num_train_epochs") - if reward_model is not None: + if finetuning_args.reward_model_type == "full": if self.is_deepspeed_enabled: if not ( getattr(reward_model.pretrained_model, "is_loaded_in_8bit", False) From 2e4f938b8ac7396bbc8345fe79c554d3e32e711c Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sun, 3 Dec 2023 21:59:45 +0800 Subject: [PATCH 14/28] release v0.3.3 Former-commit-id: 72ddb5fcce1649599671de214667d8d899ef5203 --- src/llmtuner/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llmtuner/__init__.py b/src/llmtuner/__init__.py index 27b96d01..b45c0054 100644 --- a/src/llmtuner/__init__.py +++ b/src/llmtuner/__init__.py @@ -7,4 +7,4 @@ from llmtuner.train import export_model, run_exp from llmtuner.webui import create_ui, create_web_demo -__version__ = "0.3.2" +__version__ = "0.3.3" From cf70644f8fe8020e2302221287827ae40a9697ec Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sun, 3 Dec 2023 22:35:47 +0800 Subject: [PATCH 15/28] fix #1715 Former-commit-id: 3f9192dbbbafdc2171d2eb80282d5cae47565b7b --- src/llmtuner/extras/misc.py | 14 ++++++++++++++ src/llmtuner/model/loader.py | 11 ++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/llmtuner/extras/misc.py b/src/llmtuner/extras/misc.py index 2c659993..b8424d62 100644 --- a/src/llmtuner/extras/misc.py +++ b/src/llmtuner/extras/misc.py @@ -68,6 +68,20 @@ def count_parameters(model: torch.nn.Module) -> Tuple[int, int]: return trainable_params, all_param +def get_current_device() -> torch.device: + import accelerate + if accelerate.utils.is_xpu_available(): + device = "xpu:{}".format(os.environ.get("LOCAL_RANK", "0")) + elif accelerate.utils.is_npu_available(): + device = "npu:{}".format(os.environ.get("LOCAL_RANK", "0")) + elif torch.cuda.is_available(): + device = "cuda:{}".format(os.environ.get("LOCAL_RANK", "0")) + else: + device = "cpu" + + return torch.device(device) + + def get_logits_processor() -> "LogitsProcessorList": r""" Gets logits processor that removes NaN and Inf logits. diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index 1f29abb2..faba1ee2 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -2,7 +2,7 @@ import os import math import torch from types import MethodType -from typing import TYPE_CHECKING, Literal, Optional, Tuple +from typing import TYPE_CHECKING, Optional, Tuple from transformers import ( AutoConfig, @@ -23,7 +23,7 @@ except ImportError: # https://github.com/huggingface/transformers/releases/tag/v from transformers.deepspeed import is_deepspeed_zero3_enabled from llmtuner.extras.logging import get_logger -from llmtuner.extras.misc import count_parameters, infer_optim_dtype, try_download_model_from_ms +from llmtuner.extras.misc import count_parameters, get_current_device, infer_optim_dtype, try_download_model_from_ms from llmtuner.extras.packages import is_flash_attn2_available from llmtuner.extras.patches import llama_patch as LlamaPatches from llmtuner.hparams import FinetuningArguments @@ -151,7 +151,7 @@ def load_model_and_tokenizer( if getattr(config, "quantization_config", None): if model_args.quantization_bit is not None: # remove bnb quantization model_args.quantization_bit = None - config_kwargs["device_map"] = {"": int(os.environ.get("LOCAL_RANK", "0"))} + config_kwargs["device_map"] = {"": get_current_device()} quantization_config = getattr(config, "quantization_config", None) logger.info("Loading {}-bit quantized model.".format(quantization_config.get("bits", -1))) @@ -173,7 +173,7 @@ def load_model_and_tokenizer( bnb_4bit_quant_type=model_args.quantization_type ) - config_kwargs["device_map"] = {"": int(os.environ.get("LOCAL_RANK", "0"))} + config_kwargs["device_map"] = {"": get_current_device()} logger.info("Quantizing model to {} bit.".format(model_args.quantization_bit)) # Load pre-trained models (without valuehead) @@ -209,7 +209,8 @@ def load_model_and_tokenizer( # Prepare model with valuehead for RLHF if add_valuehead: model: "AutoModelForCausalLMWithValueHead" = AutoModelForCausalLMWithValueHead.from_pretrained(model) - setattr(model, "_keys_to_ignore_on_save", [name for name, _ in model.named_parameters() if "pretrained_model" in name]) + ignore_modules = [name for name, _ in model.named_parameters() if "pretrained_model" in name] + setattr(model, "_keys_to_ignore_on_save", ignore_modules) setattr(model, "tie_weights", MethodType(lambda _: None, model)) # use empty method vhead_path = ( model_args.checkpoint_dir[-1] if model_args.checkpoint_dir is not None else model_args.model_name_or_path From b7cdc1d4e95bdd1d4b3036a3f6c358725a7b2439 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Mon, 4 Dec 2023 11:02:29 +0800 Subject: [PATCH 16/28] update readme Former-commit-id: d3c46cb126a9182be765341fe31c860d71430712 --- README.md | 3 +++ README_zh.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 386e9a2f..4ac05577 100644 --- a/README.md +++ b/README.md @@ -475,6 +475,9 @@ python src/export_model.py \ --export_dir path_to_export ``` +> [!WARNING] +> Merging LoRA weights into a GPTQ quantized model is not supported. + ### API Demo ```bash diff --git a/README_zh.md b/README_zh.md index c6758424..2b0ec298 100644 --- a/README_zh.md +++ b/README_zh.md @@ -475,6 +475,9 @@ python src/export_model.py \ --export_dir path_to_export ``` +> [!WARNING] +> 尚不支持 GPTQ 量化模型的 LoRA 权重合并及导出。 + ### API 服务 ```bash From 7250a6db54e6ec0e67640ba586036998547e1da3 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Mon, 4 Dec 2023 11:22:01 +0800 Subject: [PATCH 17/28] update readme Former-commit-id: a15f8cf19cac42acfb9917a2d7c9fa36a838b360 --- README.md | 2 +- README_zh.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ac05577..0140f614 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ huggingface-cli login | Method | Bits | 7B | 13B | 30B | 65B | | ------ | ---- | ----- | ----- | ----- | ------ | -| Full | 16 | 140GB | 240GB | 520GB | 1200GB | +| Full | 16 | 160GB | 320GB | 600GB | 1200GB | | Freeze | 16 | 20GB | 40GB | 120GB | 240GB | | LoRA | 16 | 16GB | 32GB | 80GB | 160GB | | QLoRA | 8 | 10GB | 16GB | 40GB | 80GB | diff --git a/README_zh.md b/README_zh.md index 2b0ec298..ce8e99ac 100644 --- a/README_zh.md +++ b/README_zh.md @@ -208,7 +208,7 @@ huggingface-cli login | 训练方法 | 精度 | 7B | 13B | 30B | 65B | | ------- | ---- | ----- | ----- | ----- | ------ | -| 全参数 | 16 | 140GB | 240GB | 520GB | 1200GB | +| 全参数 | 16 | 160GB | 320GB | 600GB | 1200GB | | 部分参数 | 16 | 20GB | 40GB | 120GB | 240GB | | LoRA | 16 | 16GB | 32GB | 80GB | 160GB | | QLoRA | 8 | 10GB | 16GB | 40GB | 80GB | From 28ed4cb3f4fc2379483440af0f7e5f986bf4cf41 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Mon, 4 Dec 2023 19:00:19 +0800 Subject: [PATCH 18/28] fix ppo trainer save logic Former-commit-id: 5e70c41e4e12a1109570b0ff56346fe212c028ed --- src/llmtuner/train/ppo/trainer.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/llmtuner/train/ppo/trainer.py b/src/llmtuner/train/ppo/trainer.py index f02dbdc3..40129840 100644 --- a/src/llmtuner/train/ppo/trainer.py +++ b/src/llmtuner/train/ppo/trainer.py @@ -8,7 +8,6 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Tuple from transformers import GenerationConfig, Trainer, TrainerState, TrainerControl from transformers.utils import WEIGHTS_NAME, SAFE_WEIGHTS_NAME from transformers.trainer_utils import PREFIX_CHECKPOINT_DIR -from transformers.trainer_pt_utils import remove_dummy_checkpoint from trl import PPOTrainer from trl.core import PPODecorators, logprobs_from_logits @@ -361,9 +360,13 @@ class CustomPPOTrainer(PPOTrainer, Trainer): self._save(output_dir, state_dict=self.accelerator.get_state_dict(self.model)) except ValueError: logger.warning( - " stage3_gather_16bit_weights_on_model_save=false. Saving the full checkpoint instead, use" - " zero_to_fp32.py to recover weights" + " stage3_gather_16bit_weights_on_model_save=false. Saving the full checkpoint instead," + " use zero_to_fp32.py to recover weights" ) self._save(output_dir, state_dict={}) - remove_dummy_checkpoint(self.args.should_save, output_dir, [WEIGHTS_NAME, SAFE_WEIGHTS_NAME]) + for filename in [WEIGHTS_NAME, SAFE_WEIGHTS_NAME]: # remove dummy checkpoint + file = os.path.join(output_dir, filename) + if os.path.isfile(file): + os.remove(file) + self.model.save_checkpoint(output_dir) # wrapped model From ad0069e5a2b963626198f84448f4b286e86df1f8 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Wed, 6 Dec 2023 13:33:18 +0800 Subject: [PATCH 19/28] add models Former-commit-id: 758ae7937a41a95016e70180fb343011763c1b67 --- src/llmtuner/data/template.py | 3 +++ src/llmtuner/extras/constants.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/llmtuner/data/template.py b/src/llmtuner/data/template.py index d9e83326..4f8d6a51 100644 --- a/src/llmtuner/data/template.py +++ b/src/llmtuner/data/template.py @@ -724,6 +724,9 @@ register_template( sep=[ "<|im_end|>\n" ], + stop_words=[ + "<|im_end|>" + ], efficient_eos=True ) diff --git a/src/llmtuner/extras/constants.py b/src/llmtuner/extras/constants.py index 56d7ebb8..ee506b65 100644 --- a/src/llmtuner/extras/constants.py +++ b/src/llmtuner/extras/constants.py @@ -578,10 +578,18 @@ register_model_group( DownloadSource.DEFAULT: "01-ai/Yi-34B", DownloadSource.MODELSCOPE: "01ai/Yi-34B" }, + "Yi-6B-Chat": { + DownloadSource.DEFAULT: "01-ai/Yi-6B-Chat", + DownloadSource.MODELSCOPE: "01ai/Yi-6B-Chat" + }, "Yi-34B-Chat": { DownloadSource.DEFAULT: "01-ai/Yi-34B-Chat", DownloadSource.MODELSCOPE: "01ai/Yi-34B-Chat" }, + "Yi-6B-int8-Chat": { + DownloadSource.DEFAULT: "01-ai/Yi-6B-Chat-8bits", + DownloadSource.MODELSCOPE: "01ai/Yi-6B-Chat-8bits" + }, "Yi-34B-int8-Chat": { DownloadSource.DEFAULT: "01-ai/Yi-34B-Chat-8bits", DownloadSource.MODELSCOPE: "01ai/Yi-34B-Chat-8bits" From 14f7ea118564b7770d5b634fe1f1c4f155dfc782 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Fri, 8 Dec 2023 16:26:20 +0800 Subject: [PATCH 20/28] fix #1771 and temporarily fix #1764 Former-commit-id: d0e5a5d604e16c2fe0035b0ac1d54dc3625d4da3 --- requirements.txt | 4 ++-- src/llmtuner/model/loader.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index ca876344..bbd1f89d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ torch>=1.13.1 transformers>=4.31.0,<4.35.0 -datasets>=2.14.0 +datasets>=2.14.3 accelerate>=0.21.0 -peft>=0.6.0 +peft==0.6.0 trl>=0.7.4 gradio>=3.38.0,<4.0.0 scipy diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index faba1ee2..728472da 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -39,9 +39,9 @@ logger = get_logger(__name__) require_version("transformers>=4.31.0,<4.35.0", "To fix: pip install \"transformers>=4.31.0,<4.35.0\"") -require_version("datasets>=2.14.0", "To fix: pip install datasets>=2.14.0") +require_version("datasets>=2.14.3", "To fix: pip install datasets>=2.14.3") require_version("accelerate>=0.21.0", "To fix: pip install accelerate>=0.21.0") -require_version("peft>=0.6.0", "To fix: pip install peft>=0.6.0") +require_version("peft==0.6.0", "To fix: pip install peft==0.6.0") require_version("trl>=0.7.4", "To fix: pip install trl>=0.7.4") From 8149cee8907356f3a31cb35e752300a35760541e Mon Sep 17 00:00:00 2001 From: hiyouga Date: Sat, 9 Dec 2023 20:53:18 +0800 Subject: [PATCH 21/28] fix #1784 Former-commit-id: 4e1af5a5d39d9e2f374c1372e2d67120c63fea09 --- data/README.md | 3 ++- data/README_zh.md | 5 +++-- data/dataset_info.json | 5 +++-- src/llmtuner/data/loader.py | 21 +++++++++++---------- src/llmtuner/hparams/data_args.py | 2 ++ 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/data/README.md b/data/README.md index 9010fb64..15d108da 100644 --- a/data/README.md +++ b/data/README.md @@ -4,9 +4,10 @@ If you are using a custom dataset, please provide your dataset definition in the "dataset_name": { "hf_hub_url": "the name of the dataset repository on the Hugging Face hub. (if specified, ignore below 3 arguments)", "script_url": "the name of the directory containing a dataset loading script. (if specified, ignore below 2 arguments)", - "file_name": "the name of the dataset file in the this directory. (required if above are not specified)", + "file_name": "the name of the dataset file in this directory. (required if above are not specified)", "file_sha1": "the SHA-1 hash value of the dataset file. (optional, does not affect training)", "subset": "the name of the subset. (optional, default: None)", + "folder": "the name of the folder of the dataset repository on the Hugging Face hub. (optional, default: None)", "ranking": "whether the dataset is a preference dataset or not. (default: false)", "formatting": "the format of the dataset. (optional, default: alpaca, can be chosen from {alpaca, sharegpt})", "columns": { diff --git a/data/README_zh.md b/data/README_zh.md index 740e27db..a6790f70 100644 --- a/data/README_zh.md +++ b/data/README_zh.md @@ -2,11 +2,12 @@ ```json "数据集名称": { - "hf_hub_url": "Hugging Face 上的项目地址(若指定,则忽略下列三个参数)", + "hf_hub_url": "Hugging Face 的仓库地址(若指定,则忽略下列三个参数)", "script_url": "包含数据加载脚本的本地文件夹名称(若指定,则忽略下列两个参数)", "file_name": "该目录下数据集文件的名称(若上述参数未指定,则此项必需)", - "file_sha1": "数据集文件的SHA-1哈希值(可选,留空不影响训练)", + "file_sha1": "数据集文件的 SHA-1 哈希值(可选,留空不影响训练)", "subset": "数据集子集的名称(可选,默认:None)", + "folder": "Hugging Face 仓库的文件夹名称(可选,默认:None)", "ranking": "是否为偏好数据集(可选,默认:False)", "formatting": "数据集格式(可选,默认:alpaca,可以为 alpaca 或 sharegpt)", "columns": { diff --git a/data/dataset_info.json b/data/dataset_info.json index 2b3f4eb7..1896d94d 100644 --- a/data/dataset_info.json +++ b/data/dataset_info.json @@ -274,10 +274,11 @@ "prompt": "content" } }, - "starcoder": { + "starcoder_python": { "hf_hub_url": "bigcode/starcoderdata", "columns": { "prompt": "content" - } + }, + "folder": "python" } } diff --git a/src/llmtuner/data/loader.py b/src/llmtuner/data/loader.py index 8e9053ca..d5a7a588 100644 --- a/src/llmtuner/data/loader.py +++ b/src/llmtuner/data/loader.py @@ -24,27 +24,27 @@ def get_dataset( for dataset_attr in data_args.dataset_list: logger.info("Loading dataset {}...".format(dataset_attr)) + data_path, data_name, data_dir, data_files = None, None, None, None if dataset_attr.load_from == "hf_hub": data_path = dataset_attr.dataset_name data_name = dataset_attr.subset - data_files = None + data_dir = dataset_attr.folder elif dataset_attr.load_from == "script": data_path = os.path.join(data_args.dataset_dir, dataset_attr.dataset_name) data_name = dataset_attr.subset - data_files = None elif dataset_attr.load_from == "file": - data_path, data_name = None, None - data_files: List[str] = [] - if os.path.isdir(os.path.join(data_args.dataset_dir, dataset_attr.dataset_name)): # is directory - for file_name in os.listdir(os.path.join(data_args.dataset_dir, dataset_attr.dataset_name)): - data_files.append(os.path.join(data_args.dataset_dir, dataset_attr.dataset_name, file_name)) + data_files = [] + local_path: str = os.path.join(data_args.dataset_dir, dataset_attr.dataset_name) + if os.path.isdir(local_path): # is directory + for file_name in os.listdir(local_path): + data_files.append(os.path.join(local_path, file_name)) if data_path is None: data_path = EXT2TYPE.get(file_name.split(".")[-1], None) else: assert data_path == EXT2TYPE.get(file_name.split(".")[-1], None), "file types are not identical." - elif os.path.isfile(os.path.join(data_args.dataset_dir, dataset_attr.dataset_name)): # is file - data_files.append(os.path.join(data_args.dataset_dir, dataset_attr.dataset_name)) - data_path = EXT2TYPE.get(dataset_attr.dataset_name.split(".")[-1], None) + elif os.path.isfile(local_path): # is file + data_files.append(local_path) + data_path = EXT2TYPE.get(local_path.split(".")[-1], None) else: raise ValueError("File not found.") @@ -56,6 +56,7 @@ def get_dataset( dataset = load_dataset( path=data_path, name=data_name, + data_dir=data_dir, data_files=data_files, split=data_args.split, cache_dir=model_args.cache_dir, diff --git a/src/llmtuner/hparams/data_args.py b/src/llmtuner/hparams/data_args.py index cea89198..da9be11b 100644 --- a/src/llmtuner/hparams/data_args.py +++ b/src/llmtuner/hparams/data_args.py @@ -15,6 +15,7 @@ class DatasetAttr: dataset_sha1: Optional[str] = None system_prompt: Optional[str] = None subset: Optional[str] = None + folder: Optional[str] = None ranking: Optional[bool] = False formatting: Optional[Literal["alpaca", "sharegpt"]] = "alpaca" @@ -173,6 +174,7 @@ class DataArguments: dataset_attr.content = dataset_info[name]["columns"].get("content", None) dataset_attr.subset = dataset_info[name].get("subset", None) + dataset_attr.folder = dataset_info[name].get("folder", None) dataset_attr.ranking = dataset_info[name].get("ranking", False) dataset_attr.formatting = dataset_info[name].get("formatting", "alpaca") dataset_attr.system_prompt = prompt_list[i] From 02b5f23266635e49c267476c2b2c37b064c314b7 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Mon, 11 Dec 2023 17:13:40 +0800 Subject: [PATCH 22/28] use peft 0.7.0, fix #1561 #1764 Former-commit-id: 423947bd58aa50da8785b8ceca1e7e288447a9da --- requirements.txt | 2 +- src/llmtuner/model/adapter.py | 3 +++ src/llmtuner/model/loader.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index bbd1f89d..da0b2d88 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ torch>=1.13.1 transformers>=4.31.0,<4.35.0 datasets>=2.14.3 accelerate>=0.21.0 -peft==0.6.0 +peft>=0.7.0 trl>=0.7.4 gradio>=3.38.0,<4.0.0 scipy diff --git a/src/llmtuner/model/adapter.py b/src/llmtuner/model/adapter.py index 53dfd6ea..82fa8c7b 100644 --- a/src/llmtuner/model/adapter.py +++ b/src/llmtuner/model/adapter.py @@ -102,6 +102,9 @@ def init_adapter( ) model = get_peft_model(model, lora_config) + for param in filter(lambda p: p.requires_grad, model.parameters()): + param.data = param.data.to(torch.float32) + if model_args.checkpoint_dir is not None: logger.info("Loaded fine-tuned model from checkpoint(s): {}".format(",".join(model_args.checkpoint_dir))) diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index 728472da..2434016e 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -41,7 +41,7 @@ logger = get_logger(__name__) require_version("transformers>=4.31.0,<4.35.0", "To fix: pip install \"transformers>=4.31.0,<4.35.0\"") require_version("datasets>=2.14.3", "To fix: pip install datasets>=2.14.3") require_version("accelerate>=0.21.0", "To fix: pip install accelerate>=0.21.0") -require_version("peft==0.6.0", "To fix: pip install peft==0.6.0") +require_version("peft>=0.7.0", "To fix: pip install peft>=0.7.0") require_version("trl>=0.7.4", "To fix: pip install trl>=0.7.4") From 51de180a2e93ab41ea401b63345c4d9810a42d9e Mon Sep 17 00:00:00 2001 From: hiyouga Date: Mon, 11 Dec 2023 17:50:02 +0800 Subject: [PATCH 23/28] support resize embeddings #1786 Former-commit-id: 368a41bd3c6a04f869083058d9165954fbdad105 --- src/llmtuner/model/loader.py | 5 ++++- src/llmtuner/model/utils.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index 2434016e..3df33c70 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -28,7 +28,7 @@ from llmtuner.extras.packages import is_flash_attn2_available from llmtuner.extras.patches import llama_patch as LlamaPatches from llmtuner.hparams import FinetuningArguments from llmtuner.model.adapter import init_adapter -from llmtuner.model.utils import load_valuehead_params, prepare_model_for_training +from llmtuner.model.utils import load_valuehead_params, prepare_model_for_training, resize_embedding_layer if TYPE_CHECKING: from transformers import PreTrainedTokenizer @@ -185,6 +185,9 @@ def load_model_and_tokenizer( **config_kwargs ) + # Resize token embeddings + resize_embedding_layer(model, tokenizer) + # Disable custom generate method (for Qwen and Baichuan2) if isinstance(model, PreTrainedModel) and "GenerationMixin" not in str(model.generate.__func__): model.generate = MethodType(PreTrainedModel.generate, model) diff --git a/src/llmtuner/model/utils.py b/src/llmtuner/model/utils.py index 42bef35b..b52582f5 100644 --- a/src/llmtuner/model/utils.py +++ b/src/llmtuner/model/utils.py @@ -11,6 +11,7 @@ from llmtuner.hparams import ModelArguments, FinetuningArguments if TYPE_CHECKING: from transformers.modeling_utils import PreTrainedModel + from transformers.tokenization_utils import PreTrainedTokenizer from llmtuner.hparams import DataArguments @@ -181,3 +182,14 @@ def prepare_model_for_training( output_layer.register_forward_hook(fp32_forward_post_hook) return model + + +def resize_embedding_layer(model: "PreTrainedModel", tokenizer: "PreTrainedTokenizer") -> None: + r""" + Resize token embeddings. + """ + old_vocab_size = model.get_input_embeddings().weight.size(0) + new_vocab_size = len(tokenizer) + if new_vocab_size != old_vocab_size: + model.resize_token_embeddings(new_vocab_size, pad_to_multiple_of=64) + logger.info("Resized embedding tokens from {} to {}.".format(old_vocab_size, new_vocab_size)) From 903cc3dcc1e026e3d78dc2662ca9f78f4620bcd4 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Mon, 11 Dec 2023 18:09:40 +0800 Subject: [PATCH 24/28] tiny fix Former-commit-id: 1f839fc4f278c2a258df22899241fc66a2cca682 --- src/llmtuner/model/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/llmtuner/model/utils.py b/src/llmtuner/model/utils.py index b52582f5..4be84dce 100644 --- a/src/llmtuner/model/utils.py +++ b/src/llmtuner/model/utils.py @@ -189,7 +189,7 @@ def resize_embedding_layer(model: "PreTrainedModel", tokenizer: "PreTrainedToken Resize token embeddings. """ old_vocab_size = model.get_input_embeddings().weight.size(0) - new_vocab_size = len(tokenizer) - if new_vocab_size != old_vocab_size: - model.resize_token_embeddings(new_vocab_size, pad_to_multiple_of=64) - logger.info("Resized embedding tokens from {} to {}.".format(old_vocab_size, new_vocab_size)) + if len(tokenizer) != old_vocab_size: + model.resize_token_embeddings(len(tokenizer), pad_to_multiple_of=64) + new_vocab_size = model.get_input_embeddings().weight.size(0) + logger.info("Resized token embeddings from {} to {}.".format(old_vocab_size, new_vocab_size)) From d75327e7862f6eae01a40e39dc17edf8d8a9bce2 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Mon, 11 Dec 2023 20:55:50 +0800 Subject: [PATCH 25/28] fix baichuan resize Former-commit-id: 66956d13074a9bc74d7a737b9476f38361a7764a --- src/llmtuner/model/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/llmtuner/model/utils.py b/src/llmtuner/model/utils.py index 4be84dce..d897c849 100644 --- a/src/llmtuner/model/utils.py +++ b/src/llmtuner/model/utils.py @@ -188,6 +188,10 @@ def resize_embedding_layer(model: "PreTrainedModel", tokenizer: "PreTrainedToken r""" Resize token embeddings. """ + if not isinstance(model.get_output_embeddings(), torch.nn.Linear): + logger.warning("Current model does not support resizing token embeddings.") + return + old_vocab_size = model.get_input_embeddings().weight.size(0) if len(tokenizer) != old_vocab_size: model.resize_token_embeddings(len(tokenizer), pad_to_multiple_of=64) From 82592501be6d3cb2842558e1e67b228eb95a2900 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Tue, 12 Dec 2023 11:39:04 +0800 Subject: [PATCH 26/28] support mixtral Former-commit-id: 75b5b8e36ab1933b2625f11b645f56cbc805fd85 --- README.md | 19 +++++++++++-------- README_zh.md | 19 +++++++++++-------- requirements.txt | 2 +- src/llmtuner/data/template.py | 4 +--- src/llmtuner/extras/constants.py | 16 ++++++++++++++++ src/llmtuner/model/loader.py | 31 ++++++++++++------------------- 6 files changed, 52 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 0140f614..665c1426 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,11 @@ Compared to ChatGLM's [P-Tuning](https://github.com/THUDM/ChatGLM2-6B/tree/main/ ## Changelog +[23/12/12] We supported fine-tuning the latest MoE model **[Mixtral 8x7B](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1)** in our framework. + [23/12/01] We supported downloading pre-trained models from the **[ModelScope Hub](https://modelscope.cn/models)** for Chinese mainland users. See [this tutorial](#use-modelscope-models-optional) for usage. -[23/10/21] We supported **[NEFTune](https://arxiv.org/abs/2310.05914)** trick for fine-tuning. Try `--neft_alpha` argument to activate NEFTune, e.g., `--neft_alpha 5`. +[23/10/21] We supported **[NEFTune](https://arxiv.org/abs/2310.05914)** trick for fine-tuning. Try `--neftune_noise_alpha` argument to activate NEFTune, e.g., `--neftune_noise_alpha 5`.
Full Changelog @@ -101,6 +103,7 @@ Compared to ChatGLM's [P-Tuning](https://github.com/THUDM/ChatGLM2-6B/tree/main/ | [LLaMA](https://github.com/facebookresearch/llama) | 7B/13B/33B/65B | q_proj,v_proj | - | | [LLaMA-2](https://huggingface.co/meta-llama) | 7B/13B/70B | q_proj,v_proj | llama2 | | [Mistral](https://huggingface.co/mistralai) | 7B | q_proj,v_proj | mistral | +| [Mixtral](https://huggingface.co/mistralai) | 8x7B | q_proj,v_proj | mistral | | [Phi-1.5](https://huggingface.co/microsoft/phi-1_5) | 1.3B | Wqkv | - | | [Qwen](https://github.com/QwenLM/Qwen) | 1.8B/7B/14B/72B | c_attn | qwen | | [XVERSE](https://github.com/xverse-ai) | 7B/13B/65B | q_proj,v_proj | xverse | @@ -206,13 +209,13 @@ huggingface-cli login ### Hardware Requirement -| Method | Bits | 7B | 13B | 30B | 65B | -| ------ | ---- | ----- | ----- | ----- | ------ | -| Full | 16 | 160GB | 320GB | 600GB | 1200GB | -| Freeze | 16 | 20GB | 40GB | 120GB | 240GB | -| LoRA | 16 | 16GB | 32GB | 80GB | 160GB | -| QLoRA | 8 | 10GB | 16GB | 40GB | 80GB | -| QLoRA | 4 | 6GB | 12GB | 24GB | 48GB | +| Method | Bits | 7B | 13B | 30B | 65B | 8x7B | +| ------ | ---- | ----- | ----- | ----- | ------ | ------ | +| Full | 16 | 160GB | 320GB | 600GB | 1200GB | 1000GB | +| Freeze | 16 | 20GB | 40GB | 120GB | 240GB | 200GB | +| LoRA | 16 | 16GB | 32GB | 80GB | 160GB | 120GB | +| QLoRA | 8 | 10GB | 16GB | 40GB | 80GB | 80GB | +| QLoRA | 4 | 6GB | 12GB | 24GB | 48GB | 32GB | ## Getting Started diff --git a/README_zh.md b/README_zh.md index ce8e99ac..5151ab99 100644 --- a/README_zh.md +++ b/README_zh.md @@ -55,9 +55,11 @@ https://github.com/hiyouga/LLaMA-Factory/assets/16256802/6ba60acc-e2e2-4bec-b846 ## 更新日志 +[23/12/12] 我们支持了微调最新的混合专家模型 **[Mixtral 8x7B](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1)**。 + [23/12/01] 我们支持了从 **[魔搭社区](https://modelscope.cn/models)** 下载预训练模型。详细用法请参照 [此教程](#使用魔搭社区可跳过)。 -[23/10/21] 我们支持了 **[NEFTune](https://arxiv.org/abs/2310.05914)** 训练技巧。请使用 `--neft_alpha` 参数启用 NEFTune,例如 `--neft_alpha 5`。 +[23/10/21] 我们支持了 **[NEFTune](https://arxiv.org/abs/2310.05914)** 训练技巧。请使用 `--neftune_noise_alpha` 参数启用 NEFTune,例如 `--neftune_noise_alpha 5`。
展开日志 @@ -101,6 +103,7 @@ https://github.com/hiyouga/LLaMA-Factory/assets/16256802/6ba60acc-e2e2-4bec-b846 | [LLaMA](https://github.com/facebookresearch/llama) | 7B/13B/33B/65B | q_proj,v_proj | - | | [LLaMA-2](https://huggingface.co/meta-llama) | 7B/13B/70B | q_proj,v_proj | llama2 | | [Mistral](https://huggingface.co/mistralai) | 7B | q_proj,v_proj | mistral | +| [Mixtral](https://huggingface.co/mistralai) | 8x7B | q_proj,v_proj | mistral | | [Phi-1.5](https://huggingface.co/microsoft/phi-1_5) | 1.3B | Wqkv | - | | [Qwen](https://github.com/QwenLM/Qwen) | 1.8B/7B/14B/72B | c_attn | qwen | | [XVERSE](https://github.com/xverse-ai) | 7B/13B/65B | q_proj,v_proj | xverse | @@ -206,13 +209,13 @@ huggingface-cli login ### 硬件依赖 -| 训练方法 | 精度 | 7B | 13B | 30B | 65B | -| ------- | ---- | ----- | ----- | ----- | ------ | -| 全参数 | 16 | 160GB | 320GB | 600GB | 1200GB | -| 部分参数 | 16 | 20GB | 40GB | 120GB | 240GB | -| LoRA | 16 | 16GB | 32GB | 80GB | 160GB | -| QLoRA | 8 | 10GB | 16GB | 40GB | 80GB | -| QLoRA | 4 | 6GB | 12GB | 24GB | 48GB | +| 训练方法 | 精度 | 7B | 13B | 30B | 65B | 8x7B | +| ------- | ---- | ----- | ----- | ----- | ------ | ------ | +| 全参数 | 16 | 160GB | 320GB | 600GB | 1200GB | 1000GB | +| 部分参数 | 16 | 20GB | 40GB | 120GB | 240GB | 200GB | +| LoRA | 16 | 16GB | 32GB | 80GB | 160GB | 120GB | +| QLoRA | 8 | 10GB | 16GB | 40GB | 80GB | 80GB | +| QLoRA | 4 | 6GB | 12GB | 24GB | 48GB | 32GB | ## 如何使用 diff --git a/requirements.txt b/requirements.txt index da0b2d88..f56d8cdc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ torch>=1.13.1 -transformers>=4.31.0,<4.35.0 +transformers>=4.36.0 datasets>=2.14.3 accelerate>=0.21.0 peft>=0.7.0 diff --git a/src/llmtuner/data/template.py b/src/llmtuner/data/template.py index 4f8d6a51..2b25a7fd 100644 --- a/src/llmtuner/data/template.py +++ b/src/llmtuner/data/template.py @@ -541,9 +541,7 @@ register_template( "[INST] {{query}} [/INST]" ], system="", - sep=[ - " " - ] + sep=[] ) diff --git a/src/llmtuner/extras/constants.py b/src/llmtuner/extras/constants.py index ee506b65..f73df95d 100644 --- a/src/llmtuner/extras/constants.py +++ b/src/llmtuner/extras/constants.py @@ -382,6 +382,22 @@ register_model_group( "Mistral-7B-Chat": { DownloadSource.DEFAULT: "mistralai/Mistral-7B-Instruct-v0.1", DownloadSource.MODELSCOPE: "AI-ModelScope/Mistral-7B-Instruct-v0.1" + }, + "Mistral-7B-v0.2-Chat": { + DownloadSource.DEFAULT: "mistralai/Mistral-7B-Instruct-v0.2" + } + }, + template="mistral" +) + + +register_model_group( + models={ + "Mixtral-8x7B": { + DownloadSource.DEFAULT: "mistralai/Mixtral-8x7B-v0.1" + }, + "Mixtral-8x7B-Chat": { + DownloadSource.DEFAULT: "mistralai/Mixtral-8x7B-Instruct-v0.1" } }, template="mistral" diff --git a/src/llmtuner/model/loader.py b/src/llmtuner/model/loader.py index 3df33c70..082ee6aa 100644 --- a/src/llmtuner/model/loader.py +++ b/src/llmtuner/model/loader.py @@ -25,7 +25,6 @@ except ImportError: # https://github.com/huggingface/transformers/releases/tag/v from llmtuner.extras.logging import get_logger from llmtuner.extras.misc import count_parameters, get_current_device, infer_optim_dtype, try_download_model_from_ms from llmtuner.extras.packages import is_flash_attn2_available -from llmtuner.extras.patches import llama_patch as LlamaPatches from llmtuner.hparams import FinetuningArguments from llmtuner.model.adapter import init_adapter from llmtuner.model.utils import load_valuehead_params, prepare_model_for_training, resize_embedding_layer @@ -38,7 +37,7 @@ if TYPE_CHECKING: logger = get_logger(__name__) -require_version("transformers>=4.31.0,<4.35.0", "To fix: pip install \"transformers>=4.31.0,<4.35.0\"") +require_version("transformers>=4.36.0", "To fix: pip install transformers>=4.36.0") require_version("datasets>=2.14.3", "To fix: pip install datasets>=2.14.3") require_version("accelerate>=0.21.0", "To fix: pip install accelerate>=0.21.0") require_version("peft>=0.7.0", "To fix: pip install peft>=0.7.0") @@ -124,28 +123,22 @@ def load_model_and_tokenizer( # Set FlashAttention-2 if model_args.flash_attn: - if getattr(config, "model_type", None) == "llama": - if is_flash_attn2_available(): - LlamaModule.LlamaAttention = LlamaPatches.LlamaFlashAttention2 - LlamaModule.LlamaModel._prepare_decoder_attention_mask = LlamaPatches._prepare_decoder_attention_mask - logger.info("Using FlashAttention-2 for faster training and inference.") - else: - logger.warning("FlashAttention-2 is not installed.") - elif getattr(config, "model_type", None) in ["qwen", "Yi"]: + if not is_flash_attn2_available(): + logger.warning("FlashAttention-2 is not installed.") + elif getattr(config, "model_type", None) == "qwen": logger.info("Current model automatically enables FlashAttention if installed.") else: - logger.warning("Current model does not support FlashAttention.") - elif is_trainable and model_args.shift_attn and getattr(config, "model_type", None) == "llama": - LlamaModule.LlamaAttention = LlamaPatches.LlamaShiftShortAttention - logger.warning("Using `--flash_attn` for faster training in large context length.") + setattr(config, "attn_implementation", "flash_attention_2") + logger.info("Using FlashAttention-2 for faster training and inference.") # Set shift short attention (S^2-Attn) if is_trainable and model_args.shift_attn: - if getattr(config, "model_type", None) == "llama": - setattr(config, "group_size_ratio", 0.25) - logger.info("Using shift short attention with group_size_ratio=1/4.") - else: - logger.warning("Current model does not support shift short attention.") + logger.warning("Shift short attention is temporarily invalid due to breaking changes.") + # if getattr(config, "model_type", None) == "llama": + # setattr(config, "group_size_ratio", 0.25) + # logger.info("Using shift short attention with group_size_ratio=1/4.") + # else: + # logger.warning("Current model does not support shift short attention.") # Quantization configurations (using gptq or awq) if getattr(config, "quantization_config", None): From e88b100ce22f85557ad907f054c44d2858ad1b74 Mon Sep 17 00:00:00 2001 From: hiyouga Date: Tue, 12 Dec 2023 11:44:30 +0800 Subject: [PATCH 27/28] update readme Former-commit-id: 42e042a4206aeb5177ddde56386e9655b0c06460 --- README.md | 6 +++--- README_zh.md | 6 +++--- src/llmtuner/hparams/finetuning_args.py | 4 ---- src/llmtuner/model/utils.py | 11 ----------- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 665c1426..a298d9f5 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,14 @@ Compared to ChatGLM's [P-Tuning](https://github.com/THUDM/ChatGLM2-6B/tree/main/ ## Changelog -[23/12/12] We supported fine-tuning the latest MoE model **[Mixtral 8x7B](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1)** in our framework. +[23/12/12] We supported fine-tuning the latest MoE model **[Mixtral 8x7B](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1)** in our framework. See hardware requirement [here](#hardware-requirement). [23/12/01] We supported downloading pre-trained models from the **[ModelScope Hub](https://modelscope.cn/models)** for Chinese mainland users. See [this tutorial](#use-modelscope-models-optional) for usage. -[23/10/21] We supported **[NEFTune](https://arxiv.org/abs/2310.05914)** trick for fine-tuning. Try `--neftune_noise_alpha` argument to activate NEFTune, e.g., `--neftune_noise_alpha 5`. -
Full Changelog +[23/10/21] We supported **[NEFTune](https://arxiv.org/abs/2310.05914)** trick for fine-tuning. Try `--neftune_noise_alpha` argument to activate NEFTune, e.g., `--neftune_noise_alpha 5`. + [23/09/27] We supported **$S^2$-Attn** proposed by [LongLoRA](https://github.com/dvlab-research/LongLoRA) for the LLaMA models. Try `--shift_attn` argument to enable shift short attention. [23/09/23] We integrated MMLU, C-Eval and CMMLU benchmarks in this repo. See [this example](#evaluation) to evaluate your models. diff --git a/README_zh.md b/README_zh.md index 5151ab99..bd221800 100644 --- a/README_zh.md +++ b/README_zh.md @@ -55,14 +55,14 @@ https://github.com/hiyouga/LLaMA-Factory/assets/16256802/6ba60acc-e2e2-4bec-b846 ## 更新日志 -[23/12/12] 我们支持了微调最新的混合专家模型 **[Mixtral 8x7B](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1)**。 +[23/12/12] 我们支持了微调最新的混合专家模型 **[Mixtral 8x7B](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1)**。硬件需求请查阅[此处](#硬件依赖)。 [23/12/01] 我们支持了从 **[魔搭社区](https://modelscope.cn/models)** 下载预训练模型。详细用法请参照 [此教程](#使用魔搭社区可跳过)。 -[23/10/21] 我们支持了 **[NEFTune](https://arxiv.org/abs/2310.05914)** 训练技巧。请使用 `--neftune_noise_alpha` 参数启用 NEFTune,例如 `--neftune_noise_alpha 5`。 -
展开日志 +[23/10/21] 我们支持了 **[NEFTune](https://arxiv.org/abs/2310.05914)** 训练技巧。请使用 `--neftune_noise_alpha` 参数启用 NEFTune,例如 `--neftune_noise_alpha 5`。 + [23/09/27] 我们针对 LLaMA 模型支持了 [LongLoRA](https://github.com/dvlab-research/LongLoRA) 提出的 **$S^2$-Attn**。请使用 `--shift_attn` 参数以启用该功能。 [23/09/23] 我们在项目中集成了 MMLU、C-Eval 和 CMMLU 评估集。使用方法请参阅[此示例](#模型评估)。 diff --git a/src/llmtuner/hparams/finetuning_args.py b/src/llmtuner/hparams/finetuning_args.py index 06e5b2c1..ae3a6f79 100644 --- a/src/llmtuner/hparams/finetuning_args.py +++ b/src/llmtuner/hparams/finetuning_args.py @@ -141,10 +141,6 @@ class FinetuningArguments(FreezeArguments, LoraArguments, RLHFArguments): default=False, metadata={"help": "Whether to upcast the layernorm weights in fp32."} ) - neft_alpha: Optional[float] = field( - default=0, - metadata={"help": "The alpha parameter to control the noise magnitude in NEFTune."} - ) export_dir: Optional[str] = field( default=None, metadata={"help": "Path to the directory to save the exported model."} diff --git a/src/llmtuner/model/utils.py b/src/llmtuner/model/utils.py index d897c849..a8853b1d 100644 --- a/src/llmtuner/model/utils.py +++ b/src/llmtuner/model/utils.py @@ -148,17 +148,6 @@ def prepare_model_for_training( param.data = param.data.to(torch.float32) logger.info("Upcasting weights in layernorm in float32.") - if finetuning_args.neft_alpha > 1e-6: - def neftune_forward_hook(module: torch.nn.Module, args: Tuple[torch.Tensor], output: torch.Tensor): - if module.training: - dims = torch.tensor(output.size(1) * output.size(2)) - mag_norm = finetuning_args.neft_alpha / torch.sqrt(dims) - output = output + torch.zeros_like(output).uniform_(-mag_norm, mag_norm) - return output - - model.get_input_embeddings().register_forward_hook(neftune_forward_hook) - logger.info("Using noisy embedding with alpha={:.2f}".format(finetuning_args.neft_alpha)) - if use_gradient_checkpointing and getattr(model, "supports_gradient_checkpointing", False): if hasattr(model, "enable_input_require_grads"): model.enable_input_require_grads() From a7748f7d70ec840559c328ac7e81730d3590b74b Mon Sep 17 00:00:00 2001 From: hiyouga Date: Tue, 12 Dec 2023 15:27:40 +0800 Subject: [PATCH 28/28] fix webui Former-commit-id: 15ad266206b12181788db5bb112c2299050d6139 --- src/llmtuner/webui/components/train.py | 6 +++--- src/llmtuner/webui/locales.py | 2 +- src/llmtuner/webui/runner.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/llmtuner/webui/components/train.py b/src/llmtuner/webui/components/train.py index 11109c97..310871cc 100644 --- a/src/llmtuner/webui/components/train.py +++ b/src/llmtuner/webui/components/train.py @@ -65,16 +65,16 @@ def create_train_tab(engine: "Engine") -> Dict[str, "Component"]: logging_steps = gr.Slider(value=5, minimum=5, maximum=1000, step=5) save_steps = gr.Slider(value=100, minimum=10, maximum=5000, step=10) warmup_steps = gr.Slider(value=0, minimum=0, maximum=5000, step=1) - neft_alpha = gr.Slider(value=0, minimum=0, maximum=10, step=0.1) + neftune_alpha = gr.Slider(value=0, minimum=0, maximum=10, step=0.1) with gr.Column(): train_on_prompt = gr.Checkbox(value=False) upcast_layernorm = gr.Checkbox(value=False) - input_elems.update({logging_steps, save_steps, warmup_steps, neft_alpha, train_on_prompt, upcast_layernorm}) + input_elems.update({logging_steps, save_steps, warmup_steps, neftune_alpha, train_on_prompt, upcast_layernorm}) elem_dict.update(dict( advanced_tab=advanced_tab, logging_steps=logging_steps, save_steps=save_steps, warmup_steps=warmup_steps, - neft_alpha=neft_alpha, train_on_prompt=train_on_prompt, upcast_layernorm=upcast_layernorm + neftune_alpha=neftune_alpha, train_on_prompt=train_on_prompt, upcast_layernorm=upcast_layernorm )) with gr.Accordion(label="LoRA config", open=False) as lora_tab: diff --git a/src/llmtuner/webui/locales.py b/src/llmtuner/webui/locales.py index 5f5609d8..8d406659 100644 --- a/src/llmtuner/webui/locales.py +++ b/src/llmtuner/webui/locales.py @@ -333,7 +333,7 @@ LOCALES = { "info": "学习率预热采用的步数。" } }, - "neft_alpha": { + "neftune_alpha": { "en": { "label": "NEFTune Alpha", "info": "Magnitude of noise adding to embedding vectors." diff --git a/src/llmtuner/webui/runner.py b/src/llmtuner/webui/runner.py index 664f3354..6b174deb 100644 --- a/src/llmtuner/webui/runner.py +++ b/src/llmtuner/webui/runner.py @@ -119,7 +119,7 @@ class Runner: logging_steps=get("train.logging_steps"), save_steps=get("train.save_steps"), warmup_steps=get("train.warmup_steps"), - neft_alpha=get("train.neft_alpha"), + neftune_noise_alpha=get("train.neftune_alpha"), train_on_prompt=get("train.train_on_prompt"), upcast_layernorm=get("train.upcast_layernorm"), lora_rank=get("train.lora_rank"),