From 8420115b5e7b8015ea3d7765b5b96dab1983e050 Mon Sep 17 00:00:00 2001 From: zhjunqin Date: Sun, 10 Sep 2023 23:46:45 +0800 Subject: [PATCH 1/6] fix(chat): fix stream_chat to return generator (#123) --- tools/transformers/modeling_internlm.py | 70 +++++++++++++++++++------ 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/tools/transformers/modeling_internlm.py b/tools/transformers/modeling_internlm.py index df1e19f..da7aaa0 100644 --- a/tools/transformers/modeling_internlm.py +++ b/tools/transformers/modeling_internlm.py @@ -20,6 +20,7 @@ """ PyTorch InternLM model.""" import math from typing import List, Optional, Tuple, Union +import threading, queue import torch import torch.utils.checkpoint @@ -810,35 +811,70 @@ class InternLMForCausalLM(InternLMPreTrainedModel): temperature: float = 0.8, top_p: float = 0.8, **kwargs): + """ + Return a generator in format: (response, history) + Eg. + ('你好,有什么可以帮助您的吗', [('你好', '你好,有什么可以帮助您的吗')]) + ('你好,有什么可以帮助您的吗?', [('你好', '你好,有什么可以帮助您的吗?')]) + """ + + response_queue = queue.Queue(maxsize=20) + class ChatStreamer(BaseStreamer): def __init__(self, tokenizer) -> None: super().__init__() self.tokenizer = tokenizer - + self.queue = response_queue + self.query = query + self.history = history + self.response = "" + self.received_inputs = False + self.queue.put((self.response, history + [(self.query, self.response)])) + def put(self, value): if len(value.shape) > 1 and value.shape[0] > 1: raise ValueError("ChatStreamer only supports batch size 1") elif len(value.shape) > 1: value = value[0] + + if not self.received_inputs: + # The first received value is input_ids, ignore here + self.received_inputs = True + return + token = self.tokenizer.decode([value[-1]], skip_special_tokens=True) if token.strip() != "": - print(token, end="") - + self.response = self.response + token + history = self.history + [(self.query, self.response)] + self.queue.put((self.response, history)) + def end(self): - print("") - - return self.chat( - tokenizer=tokenizer, - query=query, - streamer=ChatStreamer(tokenizer=tokenizer), - history=history, - max_new_tokens=max_new_tokens, - do_sample=do_sample, - temperature=temperature, - top_p=top_p, - **kwargs - ) - + self.queue.put(None) + + def stream_producer(): + return self.chat( + tokenizer=tokenizer, + query=query, + streamer=ChatStreamer(tokenizer=tokenizer), + history=history, + max_new_tokens=max_new_tokens, + do_sample=do_sample, + temperature=temperature, + top_p=top_p, + **kwargs + ) + + def consumer(): + producer = threading.Thread(target=stream_producer) + producer.start() + while True: + res = response_queue.get() + if res is not None: + return + yield res + + return consumer() + @add_start_docstrings( """ From e354410bd22f9b2d789d3fd5161edbab59ae2f48 Mon Sep 17 00:00:00 2001 From: huangting4201 <1538303371@qq.com> Date: Mon, 11 Sep 2023 20:06:22 +0800 Subject: [PATCH 2/6] fix(configs/7B_sft.py): model dtype float16 to bfloat16 (#302) --- configs/7B_sft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/7B_sft.py b/configs/7B_sft.py index cbcd0e5..a430e8a 100644 --- a/configs/7B_sft.py +++ b/configs/7B_sft.py @@ -127,7 +127,7 @@ model = dict( num_layers=NUM_LAYER, mlp_ratio=MLP_RATIO, apply_post_layer_norm=False, - dtype="torch.float16", # Support: "torch.float16", "torch.half", "torch.bfloat16", "torch.float32", "torch.tf32" + dtype="torch.bfloat16", # Support: "torch.float16", "torch.half", "torch.bfloat16", "torch.float32", "torch.tf32" norm_type="rmsnorm", layer_norm_epsilon=1e-5, use_flash_attn=True, From 09e71cebf37317b54a8c39b99f75ad4bcc227961 Mon Sep 17 00:00:00 2001 From: jiangtann <39088437+jiangtann@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:17:11 +0800 Subject: [PATCH 3/6] fix(convert2hf.py): fix the rotary_emb.inv_freq KeyError (#299) --- tools/transformers/convert2hf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/transformers/convert2hf.py b/tools/transformers/convert2hf.py index 7b44090..f8604df 100644 --- a/tools/transformers/convert2hf.py +++ b/tools/transformers/convert2hf.py @@ -38,7 +38,7 @@ def convert2hf(model_config, states_tp_pps): current_states["lm_head.weight"] = states.pop("head.weight") for i in range(model_config["num_layers"]): - states.pop(f"blocks.{i}.mixer.rotary_emb.inv_freq") + states.pop(f"blocks.{i}.mixer.rotary_emb.inv_freq", None) wqkv = states.pop(f"blocks.{i}.mixer.Wqkv.weight").reshape( 3, model_config["num_attention_heads"], -1, model_config["hidden_size"] From 42802a2b319d9c5a7dc57e27c897880973f2a94f Mon Sep 17 00:00:00 2001 From: huangting4201 <1538303371@qq.com> Date: Fri, 15 Sep 2023 15:29:58 +0800 Subject: [PATCH 4/6] docs(doc/code-docs): update quickstart usage (#301) * docs(usage.md): update usage.md * docs(doc/code-docs): update en usage --------- Co-authored-by: huangting4201 --- .../locales/en/LC_MESSAGES/checkpoint.po | 76 +++----- doc/code-docs/locales/en/LC_MESSAGES/usage.po | 142 ++++++++------- doc/en/usage.md | 168 ++++++++++++++++- doc/usage.md | 169 +++++++++++++++++- internlm/utils/model_checkpoint.py | 5 +- 5 files changed, 438 insertions(+), 122 deletions(-) diff --git a/doc/code-docs/locales/en/LC_MESSAGES/checkpoint.po b/doc/code-docs/locales/en/LC_MESSAGES/checkpoint.po index f1d7a41..6ec16d1 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/checkpoint.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/checkpoint.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: InternLM \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-07 10:56+0800\n" +"POT-Creation-Date: 2023-09-11 14:25+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -19,30 +19,30 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.12.1\n" -#: ../../source/checkpoint.rst:2 09c8645fba264cdf9a80c4b62c2bb4d1 +#: ../../source/checkpoint.rst:2 msgid "模型保存" msgstr "Model Checkpointing" -#: ../../source/checkpoint.rst:4 8b158d34631045b1afdb4fb0169b3c71 +#: ../../source/checkpoint.rst:4 msgid "" "InternLM 使用 ``internlm.utils.model_checkpoint.CheckpointManager`` " "来管理模型保存。 其中,可以 使用 ``CheckpointManager.try_save_checkpoint(train_state)`` " "来保存指定 step 的模型状态。InternLM支持启动时自动加载最新的模型备份,并在接收信号退出训练时自动进行模型备份。" msgstr "" -"InternLM uses ``internlm.utils.model_checkpoint.CheckpointManager`` to manage model checkpointing. In the implementation, " -"we use ``CheckpointManager.try_save_checkpoint(train_state)`` to checkpoint training states at specific steps. InternLM supports " -"automatic loading of latest ckpt at startup and automatic model checkpointing at signal quit." +"InternLM uses ``internlm.utils.model_checkpoint.CheckpointManager`` to " +"manage model checkpointing. In the implementation, we use " +"``CheckpointManager.try_save_checkpoint(train_state)`` to checkpoint " +"training states at specific steps. InternLM supports automatic loading of" +" latest ckpt at startup and automatic model checkpointing at signal quit." -#: ../../source/checkpoint.rst:8 a023b5a6d15749bfaa51cf2da194bda1 +#: ../../source/checkpoint.rst:8 msgid "Checkpointing" msgstr "" -#: 938575c699d1426c87e0b3f589a85d50 #: internlm.utils.model_checkpoint.CheckpointManager:1 of msgid "StorageManagerContext" msgstr "" -#: 754d6881cd034c5ebaab0f3362dd14c2 #: internlm.utils.model_checkpoint.CheckpointManager.quit_signal_handler:1 of msgid "" "Exit signal detection function, if we write the exit step in the " @@ -51,34 +51,27 @@ msgid "" "quit." msgstr "" -#: 2169f9fb4a8b40bc9bf6093894fc7a5e 6a55d2b2b24a44c8b78b40f19f4d950b -#: internlm.utils.model_checkpoint.CheckpointManager.quit_signal_handler -#: internlm.utils.model_checkpoint.CheckpointManager.try_resume_training of +#: internlm.utils.model_checkpoint.CheckpointManager.quit_signal_handler of msgid "参数" msgstr "" -#: 360a89b1591e4627ac432f4d75050354 #: internlm.utils.model_checkpoint.CheckpointManager.quit_signal_handler of msgid "返回" msgstr "" -#: 2426832f4a8a4c5481be1c940e0e7b50 #: internlm.utils.model_checkpoint.CheckpointManager.quit_signal_handler:9 of msgid "whether to quit." msgstr "" -#: 5f6842c261544a3c89f32d981b3ad755 #: internlm.utils.model_checkpoint.CheckpointManager.quit_signal_handler of msgid "返回类型" msgstr "" -#: 1392da84b6e645bcb8dab605e1231fdc #: internlm.utils.model_checkpoint.CheckpointManager.wait_async_upload_finish:1 #: of msgid "wait for all checkpoint uploads to be completed" msgstr "" -#: d1774593e9c94608b49b10504bfbc38b #: internlm.utils.model_checkpoint.CheckpointManager.query_latest_snapshot_step_boto3:1 #: of msgid "" @@ -86,38 +79,25 @@ msgid "" "found, None will return." msgstr "" -#: a3abbbd2bd574872892d908ab248e804 -#: internlm.utils.model_checkpoint.CheckpointManager.try_resume_training:1 of -msgid "Attempt to restore the training state of the last ckpt." -msgstr "" - -#: de021d1eb6d54955a2850c11c0191710 -#: internlm.utils.model_checkpoint.CheckpointManager.try_resume_training:3 of -msgid "lr_scheduler object." -msgstr "" - -#: 20be15854f2e420a9d96c86b5869bfa6 -#: internlm.utils.model_checkpoint.CheckpointManager.try_resume_training:5 of -msgid "optimizer object." -msgstr "" - -#: 68f69086c5054acc8aca15c8a764acc5 -#: internlm.utils.model_checkpoint.CheckpointManager.try_resume_training:7 of -msgid "learning rate." -msgstr "" - -#: 5d34d34a972d4abeab4bda3e49ee157b -#: internlm.utils.model_checkpoint.CheckpointManager.try_resume_training:9 of -msgid "traing states." -msgstr "" - -#: 82ebb67afaa748ecabc4cef598d7fc30 -#: internlm.utils.model_checkpoint.CheckpointManager.try_resume_training:11 of -msgid "traning dataloader object" -msgstr "" - -#: 0c95dfcd712749279daca78166bb4326 #: internlm.utils.model_checkpoint.CheckpointManager.save_checkpoint:1 of msgid "Save checkpoint to the given folder path." msgstr "" +#~ msgid "Attempt to restore the training state of the last ckpt." +#~ msgstr "" + +#~ msgid "lr_scheduler object." +#~ msgstr "" + +#~ msgid "optimizer object." +#~ msgstr "" + +#~ msgid "learning rate." +#~ msgstr "" + +#~ msgid "traing states." +#~ msgstr "" + +#~ msgid "traning dataloader object" +#~ msgstr "" + diff --git a/doc/code-docs/locales/en/LC_MESSAGES/usage.po b/doc/code-docs/locales/en/LC_MESSAGES/usage.po index 2297f8f..868a25f 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/usage.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/usage.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: InternLM \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-07 14:15+0800\n" +"POT-Creation-Date: 2023-09-11 14:25+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -19,11 +19,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.12.1\n" -#: ../../../usage.md:2 a64aaaa1525e4e01b0ddcebc42c24bbd +#: ../../../usage.md:2 msgid "使用教程" msgstr "Quickstart Guide" -#: ../../../usage.md:4 f1b40737fb584d889b82c7f55b652977 +#: ../../../usage.md:4 msgid "" "启动一个 Demo " "模型训练,需要进行三项准备,**安装**,**数据集准备**和**模型训练配置**。接下来,首先会介绍数据准备相关的操作,再简要描述模型训练配置相关的内容。" @@ -33,21 +33,21 @@ msgstr "" "configuration**. In this guide, we will first cover the steps for dataset" " preparation and then briefly describe the model training configuration." -#: ../../../usage.md:6 b35abe307c2f4d23866fff828308ebf2 +#: ../../../usage.md:6 msgid "安装" msgstr "Installation" -#: ../../../usage.md:7 64a8c1f5f71c45519e636aa7edba10bc +#: ../../../usage.md:7 msgid "请参考[安装文档](./install.md)进行安装。" msgstr "" "Please refer to the [installation guide](./install.md) for instructions " "on how to install the necessary dependencies." -#: ../../../usage.md:9 bd96714d12ee415794dea5a4578bd8cd +#: ../../../usage.md:9 msgid "数据准备 (预训练)" msgstr "Dataset Preparation (Pre-training)" -#: ../../../usage.md:11 5a0b39fb9da94e96b87db40d1f231a0c +#: ../../../usage.md:11 msgid "InternLM训练任务的数据集包括一系列的`bin`和`meta`文件。使用`tokenizer`从原始文本文件生成训练用数据集。通过在`tools/tokenizer.py`中指定模型参数路径的方式来导入tokenizer模型。目前提供`V7_sft.model`来生成tokens。若想使用不同的模型,可直接修改`tokernizer.py`中的模型参数路径。" msgstr "" "The dataset for the InternLM training task includes a series of `bin` and" @@ -58,7 +58,7 @@ msgstr "" "different model, you can directly modify the model parameter path in " "`tokenizer.py`." -#: ../../../usage.md:13 3cef8126b8784af48d81cc140322909e +#: ../../../usage.md:13 msgid "可以运行以下命令生成原始数据对应的`bin`和`meta`文件,其中参数`text_input_path`表示原始文本数据路径,目前支持`txt`、`json`和`jsonl`三种输入格式,`bin_output_path`表示生成的`bin`文件的保存路径。" msgstr "" "You can run the following command to generate `bin` and `meta` files " @@ -67,30 +67,30 @@ msgstr "" "`txt`, `json`, and `jsonl` formats, while `bin_output_path` represents " "the save path of the generated `bin` files." -#: ../../../usage.md:18 107ff2280da14cb6a27f4e9857186333 +#: ../../../usage.md:18 msgid "下面是一个数据处理的例子:" msgstr "Here is an example of data processing:" -#: ../../../usage.md:20 c11a9860263c4e2288a561f3435fa706 +#: ../../../usage.md:20 msgid "给定一个包含原始数据集的文件`raw_data.txt`,原始数据集如下所示:" msgstr "" "Given a file `raw_data.txt` containing the raw dataset, the raw dataset " "is shown below:" -#: ../../../usage.md:27 4012599b42ab47bd979d2a0b79ca1147 +#: ../../../usage.md:27 msgid "可以通过运行以下命令来生成`bin`和`meta`文件:" msgstr "" "You can generate the `bin` and `meta` files by running the following " "command:" -#: ../../../usage.md:32 cca91b6cf53a4082932dd34ea4b7f954 +#: ../../../usage.md:32 msgid "需要注意的是,生成的`bin`文件需要保存在`cn`或者`en`或者`code`或者`ja`或者`ar`或者`kaoshi`这六个目录下,以区分数据集的类型。" msgstr "" "It should be noted that the generated `bin` files need to be saved in one" " of the following directories: `cn`, `en`, `code`, `ja`, `ar`, or " "`kaoshi`, depending on the type of dataset." -#: ../../../usage.md:34 417312ca1e35479e811953f777e3565a +#: ../../../usage.md:34 msgid "其中,`cn`表示中文数据集;`en`表示英文数据集;`code`表示代码数据集;`ja`表示日语数据集;`ar`表示阿拉伯语数据集;`kaoshi`表示考试数据集。" msgstr "" "Here, `cn` represents the Chinese dataset, `en` represents the English " @@ -98,22 +98,22 @@ msgstr "" " dataset, `ar` represents the Arabic dataset, and `kaoshi` represents the" " exam dataset." -#: ../../../usage.md:36 79c21f8e89b34499ba4e25e20593ec28 +#: ../../../usage.md:36 msgid "生成的bin文件的格式如下:" msgstr "The format of the generated `bin` files is as follows:" -#: ../../../usage.md:42 26388d996c4e4116bc216be9bc007f62 +#: ../../../usage.md:42 msgid "`bin`文件中的每一行均对应原始数据集中的每一个句子,表示每个句子的`token`(下文将用sequence指定)。" msgstr "" "Each line in the `bin` file corresponds to each sentence in the original " "dataset, representing the tokens of each sentence (referred to as " "sequence below)." -#: ../../../usage.md:44 b39148a85ee64a349975d26282fbe59b +#: ../../../usage.md:44 msgid "生成的`meta`文件的格式如下:" msgstr "The format of the generated `meta` file is as follows:" -#: ../../../usage.md:48 175a6007197a40568535f945672e5df2 +#: ../../../usage.md:48 msgid "" "在`meta`文件中,每个元组对应着`bin`文件中每一个`sequence`的元信息。其中,元组的第一个元素表示每个`sequence`在所有`sequence`中的`starting" " index`,第二个元素表示每个`sequence`中有多少个`tokens`。" @@ -123,7 +123,7 @@ msgstr "" "index` of each `sequence` among all `sequences`, and the second element " "indicates the number of `tokens` for each `sequence`." -#: ../../../usage.md:50 46874a3de3924837979f9949f1237e39 +#: ../../../usage.md:50 msgid "" "例如,对于第一个`sequence`,`starting index`为 0,有 11 " "个`tokens`;对于第二个`sequence`,由于第一个`sequence`转换为`string`后的长度为`89`,因此它的`starting" @@ -132,17 +132,17 @@ msgstr "" "For example, the first `sequence` starts at index 0 and has 16 `tokens`. " "The second `sequence` starts at index 110 and has 24 `tokens`." -#: ../../../usage.md:52 25ea049fa411408b8856e7aa657835ab +#: ../../../usage.md:52 msgid "`json`和`jsonl`类型的文件的`bin`和`meta`文件格式和`txt`一致,此处不再赘叙。" msgstr "" "The `bin` and `meta` file formats for `json` and `jsonl` type files are " "the same as for `txt`, so we won't go over them here." -#: ../../../usage.md:54 bc52f959cb57494483a181e843014ed1 +#: ../../../usage.md:54 msgid "数据准备 (微调)" msgstr "Data Preparation (Fine-tuning)" -#: ../../../usage.md:56 73c74620c2994486acc747ba0c7f0b46 +#: ../../../usage.md:56 msgid "" "微调任务的数据集格式与预训练任务保持一致,生成的数据格式为一系列的`bin`和`meta`文件。以下以 Alpaca " "数据集为例,介绍微调的数据准备流程。" @@ -152,7 +152,7 @@ msgstr "" "the Alpaca dataset as an example to explain the data preparation process " "for fine-tuning." -#: ../../../usage.md:58 75f0e22d10ca413389ec8b947ae6141f +#: ../../../usage.md:58 msgid "" "下载 [Alpaca 数据集](https://github.com/tatsu-" "lab/stanford_alpaca/blob/main/alpaca_data.json)" @@ -160,87 +160,86 @@ msgstr "" "Download the [Alpaca dataset](https://github.com/tatsu-" "lab/stanford_alpaca/blob/main/alpaca_data.json)." -#: ../../../usage.md:60 667606fcea454af48353a5b40f82fc46 +#: ../../../usage.md:60 msgid "对 Alpaca 数据进行 tokenize,使用以下命令" msgstr "Tokenize the Alpaca dataset using the following command:" -#: ../../../usage.md:66 60283b9237c8462ea37288b8ece79081 +#: ../../../usage.md:66 msgid "建议用户参考 alpaca_tokenizer.py 编写新的脚本对自己的数据集进行 tokenize" msgstr "" "It is recommended that users refer to alpaca_tokenizer.py to write new " "scripts to tokenize their own datasets" -#: ../../../usage.md:68 cdf45a4de9874e9fb65f7104dcee3c61 +#: ../../../usage.md:68 msgid "训练配置" msgstr "Training Configuration" -#: ../../../usage.md:70 7c42ebc23246450cbc1270e1461b16f6 -msgid "以 7B Demo 的配置文件`configs/7B_sft.py`为例,介绍启动一个模型训练所需要进行的数据、模型和并行等相关的配置。" +#: ../../../usage.md:70 +#, fuzzy +msgid "以 7B Demo 的配置文件`configs/7B_sft.py`为例:" msgstr "" "Taking the configuration file `configs/7B_sft.py` for the 7B demo as an " -"example, let's discuss the data, model, and parallel configurations " +"example," + +#: ../../../usage.md:237 +msgid "接下来将详细介绍启动一个模型训练所需要进行的数据、模型、并行和监控等相关的配置。" +msgstr "let's discuss the data, model, parallel and monitoring configurations " "required to start a model training." -#: ../../../usage.md:72 247cfe98a7f44c2293aa2e2351f1ea69 +#: ../../../usage.md:239 msgid "数据配置" msgstr "Data Configuration" -#: ../../../usage.md:73 31327e7dce5848778db5361b3fbded1c +#: ../../../usage.md:240 msgid "数据相关的关键参数配置及释义如下所示:" msgstr "Here are the key parameters and their explanations for data configuration:" -#: ../../../usage.md:88 4d2608136fef4141bd6e47f78b8591b2 +#: ../../../usage.md:255 msgid "![pack_into_one](./imgs/pack_into_one.png)" msgstr "" -#: ../../../usage.md:88 c5acb028f2694712b2af788a864d5927 +#: ../../../usage.md:255 msgid "pack_into_one" msgstr "" -#: ../../../usage.md:91 db6b9ce8e8294952845893dd7aad098f +#: ../../../usage.md:258 msgid "目前支持传入数据集文件路径`train_folder`,且要求文件格式如下:" msgstr "" "Currently, it supports passing the dataset file path `train_folder`, and " "the file format is required to be as follows:" -#: ../../../usage.md:98 f22536fc3dfa4552a103a7cb57a20f92 +#: ../../../usage.md:265 msgid "数据集的详细内容可参考``数据准备``模块相关的介绍。" msgstr "" "For detailed information about the dataset, please refer to the \"Data " "Preparation\" section." -#: ../../../usage.md:100 bc4f0b06e9c24730a7a831b7aca417e2 +#: ../../../usage.md:267 msgid "模型配置" msgstr "Model Configuration" -#: ../../../usage.md:102 ecf278a0a851496fae2e49c436e59368 +#: ../../../usage.md:269 msgid "如果在启动训练时要加载模型 `checkpoint`,可进行如下相关配置:" msgstr "" "If you want to load a model checkpoint when starting the training, you " "can configure it as follows:" -#: ../../../usage.md:115 38244aba74294067a4019d0777621746 +#: ../../../usage.md:282 msgid "注意:" msgstr "Note:" -#: ../../../usage.md:116 19d1eb0a797f4bd9a702a00e525d7753 -msgid "`load_model_only_folder`与`load_ckpt_folder`不能同时设置" -msgstr "" -"`load_model_only_folder` and `load_ckpt_folder` cannot be set at the same" -" time." - -#: ../../../usage.md:117 3ea27a1f6be044a3959890be69311b24 +#: ../../../usage.md:283 msgid "路径若以 `local:` 为前缀,则存储在本地文件系统;若以 `boto3:` 为前缀,则存储在远程 oss 上" msgstr "" "If the path starts with `local:`, it means the file is stored in the " "local file system. If it starts with `boto3:`, it means the file is " "stored in the remote OSS." -#: ../../../usage.md:119 1d6381b4cfff41d8bdd5347e8a135869 +#: ../../../usage.md:285 msgid "模型相关关键参数配置如下所示:" msgstr "The configuration for the model is as follows:" -#: ../../../usage.md:143 1026791c9f054576857ef1930db6b167 +#: ../../../usage.md:309 msgid "注意:用户可自定义模型类型名和模型结构,并配置相对应的模型参数。通过`utils/registry.py`下的`MODEL_INITIALIZER`对象进行模型初始化函数接口注册,在训练主函数`train.py`中初始化模型时,可通过`model_type`配置获取指定的模型初始化接口函数。" msgstr "" "Note: Users can customize the model type name and model structure, and " @@ -251,7 +250,7 @@ msgstr "" "interface function can be obtained through the `model_type` " "configuration." -#: ../../../usage.md:145 34823bcbe7754190bc9747758c1aad0c +#: ../../../usage.md:311 msgid "" "*如果基于 InternLM 7B继续训练,可以参考 " "[ModelZoo](https://github.com/InternLM/InternLM/tree/main#model-zoo) 中 " @@ -261,21 +260,21 @@ msgstr "" "OpenXLab [ModelZoo](https://github.com/InternLM/InternLM/tree/main#model-" "zoo) to download weights*." -#: ../../../usage.md:147 4cabc928f8884cd38a6bb683b3bfade3 +#: ../../../usage.md:313 msgid "并行配置" msgstr "Parallel Configuration" -#: ../../../usage.md:149 f97ade07340340959345e73567bae793 +#: ../../../usage.md:315 msgid "训练并行配置样例如下:" msgstr "Training parallel configuration example:" -#: ../../../usage.md:158 87fb5a4e4a4047ee8a9b8bb43915636d +#: ../../../usage.md:324 msgid "zero1:zero 并行策略,分如下三种情况,默认值为 -1" msgstr "" "zero1: zero parallel strategy, divided into the following three cases, " "default value is -1" -#: ../../../usage.md:159 58dc08e2c52e4aaba99b4fbb6cf2e8b4 +#: ../../../usage.md:325 #, fuzzy msgid "当`zero1 <= 0`,则 zero1 进程组的大小等于数据并行进程组的大小,因此优化器状态参数将在数据并行范围内分配" msgstr "" @@ -283,57 +282,57 @@ msgstr "" "size of the data parallel process group, so the optimizer state " "parameters will be split within the data parallel range." -#: ../../../usage.md:160 67e2ebd795d840b29fd1d684a068e90d +#: ../../../usage.md:326 #, fuzzy msgid "当`zero1 == 1`,则不使用 zero1 ,所有数据并行组保留完整的优化器状态参数" msgstr "" -"When `zero1 == 1`, zero1 is not used, and all data parallel groups retain " -"the complete optimizer state parameters." +"When `zero1 == 1`, zero1 is not used, and all data parallel groups retain" +" the complete optimizer state parameters." -#: ../../../usage.md:161 7caedfc943514b9b83090b858ef6d163 +#: ../../../usage.md:327 #, fuzzy msgid "当`zero1 > 1`且`zero1 <= data_parallel_world_size`,则 zero1 进程组是数据并行进程组的子集" msgstr "" -"When `zero1 > 1` and `zero1 <= data_parallel_world_size`, the zero1 process" -" group is a subset of the data parallel process group." +"When `zero1 > 1` and `zero1 <= data_parallel_world_size`, the zero1 " +"process group is a subset of the data parallel process group." -#: ../../../usage.md:162 b38d3a1f72d543c6a44728fb6babea6b +#: ../../../usage.md:328 msgid "tensor:张量并行大小,通常是每个节点的 GPU 数量,默认值为 1" msgstr "" "tensor: tensor parallel size, usually the number of GPUs per node, " "default is 1" -#: ../../../usage.md:163 237ac76df68f4a999396dad37c5495c3 +#: ../../../usage.md:329 msgid "pipeline:流水线并行策略" msgstr "pipeline: pipeline parallel strategy" -#: ../../../usage.md:164 c8c38f6ab2ea432eb9ebbb62618ca33e +#: ../../../usage.md:330 msgid "size:流水线并行大小,默认值为 1" msgstr "size: pipeline parallel size, the default value is 1" -#: ../../../usage.md:165 b9158818e72e49acbdd52ad317cb80df +#: ../../../usage.md:331 msgid "interleaved_overlap:bool 类型,交错式调度时,开启或关闭通信优化,默认值为关闭" msgstr "" "interleaved_overlap: bool type, when interleaved scheduling, enable or " "disable communication optimization, the default value is False" -#: ../../../usage.md:166 28e4d48661ff4f80aff788fdda604433 +#: ../../../usage.md:332 msgid "sequence_parallel:是否开启序列化并行,默认值为 False" msgstr "" "sequence_parallel: Whether to enable sequence parallelism, the default " "value is False" -#: ../../../usage.md:168 27528ab826824d2280506460e1f2f7bd +#: ../../../usage.md:334 msgid "注意:`数据并行大小 = 总的 GPU 数目 / 流水线并行大小 / 张量并行大小`" msgstr "" "Note: `Data parallel size = Total number of GPUs / Pipeline parallel size" " / Tensor parallel size`" -#: ../../../usage.md:170 5a7af23cec604f1d9096a5ab81993c87 +#: ../../../usage.md:336 msgid "启动训练" msgstr "Start Training" -#: ../../../usage.md:172 795e51542ed84cea83b63c5233bb88bc +#: ../../../usage.md:338 msgid "完成了以上数据集准备和相关训练配置后,可启动 Demo 训练。接下来分别以 slurm 和 torch 环境为例,介绍训练启动方式。" msgstr "" "After completing the data preparation and relevant training " @@ -341,25 +340,30 @@ msgstr "" "following examples demonstrate how to start the training in both slurm " "and torch environments." -#: ../../../usage.md:174 96402cbe443044c0a0a1695c9847140b +#: ../../../usage.md:340 msgid "若在 slurm 上启动分布式运行环境,多节点 16 卡的运行命令如下所示:" msgstr "" "If you want to start distributed training on slurm with 16 GPUs across " "multiple nodes, use the following command:" -#: ../../../usage.md:179 c569e60401a6471eb9af2473acc4d5a6 +#: ../../../usage.md:345 msgid "若在 torch 上启动分布式运行环境,单节点 8 卡的运行命令如下所示:" msgstr "" "If you want to start distributed training on torch with 8 GPUs on a " "single node, use the following command:" -#: ../../../usage.md:184 a045a060d0734aab9d894aed553cef34 +#: ../../../usage.md:350 msgid "运行结果" msgstr "Training Results" -#: ../../../usage.md:186 c68e8dfa259647c7a6e6e0c0446b0b18 +#: ../../../usage.md:352 msgid "以 slurm 上单机 8 卡的 Demo 训练配置为例,训练结果日志展示如下:" msgstr "" "Taking the configuration of the demo training on a single machine with 8 " "GPUs on slurm as an example, the training result log is shown below:" +#~ msgid "`load_model_only_folder`与`load_ckpt_folder`不能同时设置" +#~ msgstr "" +#~ "`load_model_only_folder` and `load_ckpt_folder` " +#~ "cannot be set at the same time." + diff --git a/doc/en/usage.md b/doc/en/usage.md index d115fb1..864ead6 100644 --- a/doc/en/usage.md +++ b/doc/en/usage.md @@ -74,7 +74,173 @@ It is recommended that users refer to alpaca_tokenizer.py to write new scripts t ### Training Configuration -Taking the configuration file `configs/7B_sft.py` for the 7B demo as an example, let's discuss the data, model, and parallel configurations required to start a model training. +Taking the configuration file `configs/7B_sft.py` for the 7B demo as an example, let's discuss the data, model, parallel and monitoring configurations required to start a model training. +```python +JOB_NAME = "7b_train" +DO_ALERT = False + +SEQ_LEN = 2048 +HIDDEN_SIZE = 4096 +NUM_ATTENTION_HEAD = 32 +MLP_RATIO = 8 / 3 +NUM_LAYER = 32 +VOCAB_SIZE = 103168 + +MODEL_ONLY_FOLDER = "local:llm_ckpts/xxxx" +# Ckpt folder format: +# fs: 'local:/mnt/nfs/XXX' +SAVE_CKPT_FOLDER = "local:llm_ckpts" +LOAD_CKPT_FOLDER = "local:llm_ckpts/49" + +# boto3 Ckpt folder format: +# import os +# BOTO3_IP = os.environ["BOTO3_IP"] # boto3 bucket endpoint +# SAVE_CKPT_FOLDER = f"boto3:s3://model_weights.{BOTO3_IP}/internlm" +# LOAD_CKPT_FOLDER = f"boto3:s3://model_weights.{BOTO3_IP}/internlm/snapshot/1/" +CHECKPOINT_EVERY = 50 +ckpt = dict( + enable_save_ckpt=False, # enable ckpt save. + save_ckpt_folder=SAVE_CKPT_FOLDER, # Path to save training ckpt. + # load_ckpt_folder= dict(path=MODEL_ONLY_FOLDER, content=["model"], ckpt_type="normal"), + load_ckpt_folder="local:llm_ckpts/", + # 'load_ckpt_info' setting guide: + # 1. the 'path' indicate ckpt path, + # 2. the 'content‘ means what states will be loaded, support: "model", "sampler", "optimizer", "scheduler", "all" + # 3. the ’ckpt_type‘ means the type of checkpoint to be loaded, now only 'normal' type is supported. + load_ckpt_info=dict(path=MODEL_ONLY_FOLDER, content=("model",), ckpt_type="internlm"), + checkpoint_every=CHECKPOINT_EVERY, + async_upload=True, # async ckpt upload. (only work for boto3 ckpt) + async_upload_tmp_folder="/dev/shm/internlm_tmp_ckpt/", # path for temporarily files during asynchronous upload. + oss_snapshot_freq=int(CHECKPOINT_EVERY / 2), # snapshot ckpt save frequency. +) + +TRAIN_FOLDER = "/path/to/dataset" +VALID_FOLDER = "/path/to/dataset" +data = dict( + seq_len=SEQ_LEN, + # micro_num means the number of micro_batch contained in one gradient update + micro_num=4, + # packed_length = micro_bsz * SEQ_LEN + micro_bsz=2, + # defaults to the value of micro_num + valid_micro_num=4, + # defaults to 0, means disable evaluate + valid_every=50, + pack_sample_into_one=False, + total_steps=50000, + skip_batches="", + rampup_batch_size="", + # Datasets with less than 50 rows will be discarded + min_length=50, + # train_folder=TRAIN_FOLDER, + # valid_folder=VALID_FOLDER, + empty_cache_and_diag_interval=10, + diag_outlier_ratio=1.1, +) + +grad_scaler = dict( + fp16=dict( + # the initial loss scale, defaults to 2**16 + initial_scale=2**16, + # the minimum loss scale, defaults to None + min_scale=1, + # the number of steps to increase loss scale when no overflow occurs + growth_interval=1000, + ), + # the multiplication factor for increasing loss scale, defaults to 2 + growth_factor=2, + # the multiplication factor for decreasing loss scale, defaults to 0.5 + backoff_factor=0.5, + # the maximum loss scale, defaults to None + max_scale=2**24, + # the number of overflows before decreasing loss scale, defaults to 2 + hysteresis=2, +) + +hybrid_zero_optimizer = dict( + # Enable low_level_optimzer overlap_communication + overlap_sync_grad=True, + overlap_sync_param=True, + # bucket size for nccl communication params + reduce_bucket_size=512 * 1024 * 1024, + # grad clipping + clip_grad_norm=1.0, +) + +loss = dict( + label_smoothing=0, +) + +adam = dict( + lr=1e-4, + adam_beta1=0.9, + adam_beta2=0.95, + adam_beta2_c=0, + adam_eps=1e-8, + weight_decay=0.01, +) + +lr_scheduler = dict( + total_steps=data["total_steps"], + init_steps=0, # optimizer_warmup_step + warmup_ratio=0.01, + eta_min=1e-5, + last_epoch=-1, +) + +beta2_scheduler = dict( + init_beta2=adam["adam_beta2"], + c=adam["adam_beta2_c"], + cur_iter=-1, +) + +model = dict( + checkpoint=False, # The proportion of layers for activation aheckpointing, the optional value are True/False/[0-1] + num_attention_heads=NUM_ATTENTION_HEAD, + embed_split_hidden=True, + vocab_size=VOCAB_SIZE, + embed_grad_scale=1, + parallel_output=True, + hidden_size=HIDDEN_SIZE, + num_layers=NUM_LAYER, + mlp_ratio=MLP_RATIO, + apply_post_layer_norm=False, + dtype="torch.float16", # Support: "torch.float16", "torch.half", "torch.bfloat16", "torch.float32", "torch.tf32" + norm_type="rmsnorm", + layer_norm_epsilon=1e-5, + use_flash_attn=True, + num_chunks=1, # if num_chunks > 1, interleaved pipeline scheduler is used. +) +""" +zero1 parallel: + 1. if zero1 <= 0, The size of the zero process group is equal to the size of the dp process group, + so parameters will be divided within the range of dp. + 2. if zero1 == 1, zero is not used, and all dp groups retain the full amount of model parameters. + 3. zero1 > 1 and zero1 <= dp world size, the world size of zero is a subset of dp world size. + For smaller models, it is usually a better choice to split the parameters within nodes with a setting <= 8. +pipeline parallel (dict): + 1. size: int, the size of pipeline parallel. + 2. interleaved_overlap: bool, enable/disable communication overlap when using interleaved pipeline scheduler. +tensor parallel: tensor parallel size, usually the number of GPUs per node. +""" +parallel = dict( + zero1=8, + pipeline=dict(size=1, interleaved_overlap=True), + sequence_parallel=False, +) + +cudnn_deterministic = False +cudnn_benchmark = False + +monitor = dict( + # feishu alert configs + alert=dict( + enable_feishu_alert=DO_ALERT, + feishu_alert_address=None, # feishu webhook to send alert message + light_monitor_address=None, # light_monitor address to send heartbeat + ), +) +``` #### Data Configuration Here are the key parameters and their explanations for data configuration: diff --git a/doc/usage.md b/doc/usage.md index 1b98c10..82c20e0 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -66,7 +66,174 @@ python tools/alpaca_tokenizer.py /path/to/alpaca_dataset /path/to/output_dataset ### 训练配置 -以 7B Demo 的配置文件`configs/7B_sft.py`为例,介绍启动一个模型训练所需要进行的数据、模型和并行等相关的配置。 +以 7B Demo 的配置文件`configs/7B_sft.py`为例: +```python +JOB_NAME = "7b_train" +DO_ALERT = False + +SEQ_LEN = 2048 +HIDDEN_SIZE = 4096 +NUM_ATTENTION_HEAD = 32 +MLP_RATIO = 8 / 3 +NUM_LAYER = 32 +VOCAB_SIZE = 103168 + +MODEL_ONLY_FOLDER = "local:llm_ckpts/xxxx" +# Ckpt folder format: +# fs: 'local:/mnt/nfs/XXX' +SAVE_CKPT_FOLDER = "local:llm_ckpts" +LOAD_CKPT_FOLDER = "local:llm_ckpts/49" + +# boto3 Ckpt folder format: +# import os +# BOTO3_IP = os.environ["BOTO3_IP"] # boto3 bucket endpoint +# SAVE_CKPT_FOLDER = f"boto3:s3://model_weights.{BOTO3_IP}/internlm" +# LOAD_CKPT_FOLDER = f"boto3:s3://model_weights.{BOTO3_IP}/internlm/snapshot/1/" +CHECKPOINT_EVERY = 50 +ckpt = dict( + enable_save_ckpt=False, # enable ckpt save. + save_ckpt_folder=SAVE_CKPT_FOLDER, # Path to save training ckpt. + # load_ckpt_folder= dict(path=MODEL_ONLY_FOLDER, content=["model"], ckpt_type="normal"), + load_ckpt_folder="local:llm_ckpts/", + # 'load_ckpt_info' setting guide: + # 1. the 'path' indicate ckpt path, + # 2. the 'content‘ means what states will be loaded, support: "model", "sampler", "optimizer", "scheduler", "all" + # 3. the ’ckpt_type‘ means the type of checkpoint to be loaded, now only 'normal' type is supported. + load_ckpt_info=dict(path=MODEL_ONLY_FOLDER, content=("model",), ckpt_type="internlm"), + checkpoint_every=CHECKPOINT_EVERY, + async_upload=True, # async ckpt upload. (only work for boto3 ckpt) + async_upload_tmp_folder="/dev/shm/internlm_tmp_ckpt/", # path for temporarily files during asynchronous upload. + oss_snapshot_freq=int(CHECKPOINT_EVERY / 2), # snapshot ckpt save frequency. +) + +TRAIN_FOLDER = "/path/to/dataset" +VALID_FOLDER = "/path/to/dataset" +data = dict( + seq_len=SEQ_LEN, + # micro_num means the number of micro_batch contained in one gradient update + micro_num=4, + # packed_length = micro_bsz * SEQ_LEN + micro_bsz=2, + # defaults to the value of micro_num + valid_micro_num=4, + # defaults to 0, means disable evaluate + valid_every=50, + pack_sample_into_one=False, + total_steps=50000, + skip_batches="", + rampup_batch_size="", + # Datasets with less than 50 rows will be discarded + min_length=50, + # train_folder=TRAIN_FOLDER, + # valid_folder=VALID_FOLDER, + empty_cache_and_diag_interval=10, + diag_outlier_ratio=1.1, +) + +grad_scaler = dict( + fp16=dict( + # the initial loss scale, defaults to 2**16 + initial_scale=2**16, + # the minimum loss scale, defaults to None + min_scale=1, + # the number of steps to increase loss scale when no overflow occurs + growth_interval=1000, + ), + # the multiplication factor for increasing loss scale, defaults to 2 + growth_factor=2, + # the multiplication factor for decreasing loss scale, defaults to 0.5 + backoff_factor=0.5, + # the maximum loss scale, defaults to None + max_scale=2**24, + # the number of overflows before decreasing loss scale, defaults to 2 + hysteresis=2, +) + +hybrid_zero_optimizer = dict( + # Enable low_level_optimzer overlap_communication + overlap_sync_grad=True, + overlap_sync_param=True, + # bucket size for nccl communication params + reduce_bucket_size=512 * 1024 * 1024, + # grad clipping + clip_grad_norm=1.0, +) + +loss = dict( + label_smoothing=0, +) + +adam = dict( + lr=1e-4, + adam_beta1=0.9, + adam_beta2=0.95, + adam_beta2_c=0, + adam_eps=1e-8, + weight_decay=0.01, +) + +lr_scheduler = dict( + total_steps=data["total_steps"], + init_steps=0, # optimizer_warmup_step + warmup_ratio=0.01, + eta_min=1e-5, + last_epoch=-1, +) + +beta2_scheduler = dict( + init_beta2=adam["adam_beta2"], + c=adam["adam_beta2_c"], + cur_iter=-1, +) + +model = dict( + checkpoint=False, # The proportion of layers for activation aheckpointing, the optional value are True/False/[0-1] + num_attention_heads=NUM_ATTENTION_HEAD, + embed_split_hidden=True, + vocab_size=VOCAB_SIZE, + embed_grad_scale=1, + parallel_output=True, + hidden_size=HIDDEN_SIZE, + num_layers=NUM_LAYER, + mlp_ratio=MLP_RATIO, + apply_post_layer_norm=False, + dtype="torch.float16", # Support: "torch.float16", "torch.half", "torch.bfloat16", "torch.float32", "torch.tf32" + norm_type="rmsnorm", + layer_norm_epsilon=1e-5, + use_flash_attn=True, + num_chunks=1, # if num_chunks > 1, interleaved pipeline scheduler is used. +) +""" +zero1 parallel: + 1. if zero1 <= 0, The size of the zero process group is equal to the size of the dp process group, + so parameters will be divided within the range of dp. + 2. if zero1 == 1, zero is not used, and all dp groups retain the full amount of model parameters. + 3. zero1 > 1 and zero1 <= dp world size, the world size of zero is a subset of dp world size. + For smaller models, it is usually a better choice to split the parameters within nodes with a setting <= 8. +pipeline parallel (dict): + 1. size: int, the size of pipeline parallel. + 2. interleaved_overlap: bool, enable/disable communication overlap when using interleaved pipeline scheduler. +tensor parallel: tensor parallel size, usually the number of GPUs per node. +""" +parallel = dict( + zero1=8, + pipeline=dict(size=1, interleaved_overlap=True), + sequence_parallel=False, +) + +cudnn_deterministic = False +cudnn_benchmark = False + +monitor = dict( + # feishu alert configs + alert=dict( + enable_feishu_alert=DO_ALERT, + feishu_alert_address=None, # feishu webhook to send alert message + light_monitor_address=None, # light_monitor address to send heartbeat + ), +) +``` +接下来将详细介绍启动一个模型训练所需要进行的数据、模型、并行和监控等相关的配置。 #### 数据配置 数据相关的关键参数配置及释义如下所示: diff --git a/internlm/utils/model_checkpoint.py b/internlm/utils/model_checkpoint.py index b6aab02..dad2fc6 100644 --- a/internlm/utils/model_checkpoint.py +++ b/internlm/utils/model_checkpoint.py @@ -447,8 +447,8 @@ class CheckpointManager: Args: ckpt_config (dict): model checkpoint config. - model (nn.module): model obj - optimizer (object): optimzier obj. + model (nn.module): model obj. + optimizer (object): optimizer obj. lr_scheduler (object): lr_scheduler obj. model_config (dict): model config. """ @@ -712,7 +712,6 @@ now step_count is {train_state.step_count}", return dict(path=latest_ckpt, content=("all",), ckpt_type="internlm") def try_resume_training(self, train_state: TrainState, current_time=""): - if self.load_ckpt_info is None or self.load_ckpt_info["path"] is None: if gpc.is_rank_for_log(): logger.info( From 2710fa7343d3ef3a29c5633fb8ddcb2f337e3fdb Mon Sep 17 00:00:00 2001 From: huangting4201 <1538303371@qq.com> Date: Fri, 15 Sep 2023 19:12:38 +0800 Subject: [PATCH 5/6] Merge develop to main (#314) * feat: add unitest for model (#300) * feat: add unitest for model * feat:add model test * Merge main to develop (#309) * fix(chat): fix stream_chat to return generator (#123) * fix(configs/7B_sft.py): model dtype float16 to bfloat16 (#302) * fix(convert2hf.py): fix the rotary_emb.inv_freq KeyError (#299) --------- Co-authored-by: yingtongxiong <974106207@qq.com> Co-authored-by: zhjunqin Co-authored-by: jiangtann <39088437+jiangtann@users.noreply.github.com> * docs(doc/code-docs): add figure for training docs (#307) * add training image for docs * docs(doc/code-docs): add training img for en doc * docs(doc/code-docs): fix en docs for initialize * docs(doc/code-docs): update conf file for readthedocs * docs(doc/code-docs): fix typos * docs(doc/code-docs): fix typos for reathedocs * docs(doc/code-docs): minor typo fix for reathedocs * docs(doc/code-docs): fix readthedocs conf file * docs(doc/code-docs): update training image * docs(doc/code-docs): fix typos * docs(doc/code-docs): update training image * docs(doc/code-docs): move training image to section initialize * docs(doc/code-docs): fix lint * add badge about reathedocs status * Merge main to develop (#312) * fix(chat): fix stream_chat to return generator (#123) * fix(configs/7B_sft.py): model dtype float16 to bfloat16 (#302) * fix(convert2hf.py): fix the rotary_emb.inv_freq KeyError (#299) * docs(doc/code-docs): update quickstart usage (#301) * docs(usage.md): update usage.md * docs(doc/code-docs): update en usage --------- Co-authored-by: huangting4201 * docs(doc/code-docs): update en usage --------- Co-authored-by: yingtongxiong <974106207@qq.com> Co-authored-by: zhjunqin Co-authored-by: jiangtann <39088437+jiangtann@users.noreply.github.com> Co-authored-by: huangting4201 * feat: more tgs (#310) * feat:more tgs * feat:add more tgs * feat:more tgs * feat: add optimizer_unitest (#303) * feat: add optimizer_unitest * feat: add optimizer test * feat: add optimizer test * feat:add optimizer test * fianl change * feat:add optimizer test * feat:add optimizer test * feat:add optimizer test --------- Co-authored-by: jiaxingli <43110891+li126com@users.noreply.github.com> Co-authored-by: yingtongxiong <974106207@qq.com> Co-authored-by: zhjunqin Co-authored-by: jiangtann <39088437+jiangtann@users.noreply.github.com> Co-authored-by: Season Co-authored-by: huangting4201 --- README-ja-JP.md | 1 + README-zh-Hans.md | 1 + README.md | 1 + .../locales/en/LC_MESSAGES/checkpoint.po | 15 +- .../en/LC_MESSAGES/example/30B_demo.po | 4 +- .../locales/en/LC_MESSAGES/example/7B_demo.po | 4 +- .../locales/en/LC_MESSAGES/initialize.po | 82 +++- .../locales/en/LC_MESSAGES/profiler.po | 21 +- .../locales/en/LC_MESSAGES/training.po | 89 ++-- doc/code-docs/locales/en/LC_MESSAGES/usage.po | 6 +- doc/code-docs/source/checkpoint.rst | 5 +- doc/code-docs/source/conf.py | 4 +- doc/code-docs/source/initialize.rst | 22 +- doc/code-docs/source/profiler.rst | 2 +- doc/code-docs/source/qa.rst | 2 +- doc/imgs/hybrid_parallel_training.png | Bin 0 -> 213327 bytes internlm/core/trainer.py | 19 + internlm/train/training_internlm.py | 59 ++- tests/test_model/test_embedding.py | 65 +++ tests/test_model/test_model_internlm.py | 379 ++++++++++++++++++ tests/test_model/test_norm.py | 84 ++++ tests/test_solver/test_optimizer.py | 364 +++++++++++++++++ 22 files changed, 1150 insertions(+), 79 deletions(-) create mode 100644 doc/imgs/hybrid_parallel_training.png create mode 100644 tests/test_model/test_embedding.py create mode 100644 tests/test_model/test_model_internlm.py create mode 100644 tests/test_model/test_norm.py create mode 100644 tests/test_solver/test_optimizer.py diff --git a/README-ja-JP.md b/README-ja-JP.md index 1cfce1f..395e248 100644 --- a/README-ja-JP.md +++ b/README-ja-JP.md @@ -16,6 +16,7 @@ [![license](./doc/imgs/license.svg)](./LICENSE) [![evaluation](./doc/imgs/compass_support.svg)](https://github.com/internLM/OpenCompass/) +[![Documentation Status](https://readthedocs.org/projects/internlm/badge/?version=latest)](https://internlm.readthedocs.io/zh_CN/latest/?badge=latest) [📘使用法](./doc/en/usage.md) | [🛠️インストール](./doc/en/install.md) | diff --git a/README-zh-Hans.md b/README-zh-Hans.md index 5979c7f..6679939 100644 --- a/README-zh-Hans.md +++ b/README-zh-Hans.md @@ -16,6 +16,7 @@ [![license](./doc/imgs/license.svg)](https://github.com/open-mmlab/mmdetection/blob/main/LICENSE) [![evaluation](./doc/imgs/compass_support.svg)](https://github.com/internLM/OpenCompass/) +[![Documentation Status](https://readthedocs.org/projects/internlm/badge/?version=latest)](https://internlm.readthedocs.io/zh_CN/latest/?badge=latest) [📘使用文档](./doc/usage.md) | [🛠️安装教程](./doc/install.md) | diff --git a/README.md b/README.md index d8711f2..0097aa8 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ [![license](./doc/imgs/license.svg)](./LICENSE) [![evaluation](./doc/imgs/compass_support.svg)](https://github.com/internLM/OpenCompass/) +[![Documentation Status](https://readthedocs.org/projects/internlm/badge/?version=latest)](https://internlm.readthedocs.io/zh_CN/latest/?badge=latest) [📘Usage](./doc/en/usage.md) | [🛠️Installation](./doc/en/install.md) | diff --git a/doc/code-docs/locales/en/LC_MESSAGES/checkpoint.po b/doc/code-docs/locales/en/LC_MESSAGES/checkpoint.po index 6ec16d1..bd81fa5 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/checkpoint.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/checkpoint.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: InternLM \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-11 14:25+0800\n" +"POT-Creation-Date: 2023-09-13 17:07+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -26,16 +26,19 @@ msgstr "Model Checkpointing" #: ../../source/checkpoint.rst:4 msgid "" "InternLM 使用 ``internlm.utils.model_checkpoint.CheckpointManager`` " -"来管理模型保存。 其中,可以 使用 ``CheckpointManager.try_save_checkpoint(train_state)`` " -"来保存指定 step 的模型状态。InternLM支持启动时自动加载最新的模型备份,并在接收信号退出训练时自动进行模型备份。" +"来管理模型保存。其中,可以使用 ``CheckpointManager.try_save_checkpoint(train_state)`` " +"来保存指定 step 的模型状态。" msgstr "" "InternLM uses ``internlm.utils.model_checkpoint.CheckpointManager`` to " "manage model checkpointing. In the implementation, we use " "``CheckpointManager.try_save_checkpoint(train_state)`` to checkpoint " -"training states at specific steps. InternLM supports automatic loading of" -" latest ckpt at startup and automatic model checkpointing at signal quit." +"training states at specific steps. " -#: ../../source/checkpoint.rst:8 +#: ../../source/checkpoint.rst:6 +msgid "InternLM支持启动时自动加载最新的模型备份,并在接收信号退出训练时自动进行模型备份。" +msgstr "InternLM supports automatic loading of latest ckpt at startup and automatic model checkpointing at signal quit. " + +#: ../../source/checkpoint.rst:9 msgid "Checkpointing" msgstr "" diff --git a/doc/code-docs/locales/en/LC_MESSAGES/example/30B_demo.po b/doc/code-docs/locales/en/LC_MESSAGES/example/30B_demo.po index 345e06b..67f2451 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/example/30B_demo.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/example/30B_demo.po @@ -37,8 +37,8 @@ msgstr "Start Training" #: ../../source/example/30B_demo.rst:166 24974384d5ab42e68266aeb67ae222ce msgid "完成以上训练配置后,可启动模型训练,以在 ``slurm`` 平台上为例,启动两节点 16GPU 的训练命令如下所示:" -msgstr "After completing the data preparation and relevant training configurations, you can start the demo training. -The following example shows how to start distributed training in ``slurm`` environments with 16 GPUs." +msgstr "After completing the data preparation and relevant training configurations, you can start the demo training. " +"The following example shows how to start distributed training in ``slurm`` environments with 16 GPUs." #: ../../source/example/30B_demo.rst:173 948ac71ed53848f9bad07f69d956c4bb msgid "训练结果" diff --git a/doc/code-docs/locales/en/LC_MESSAGES/example/7B_demo.po b/doc/code-docs/locales/en/LC_MESSAGES/example/7B_demo.po index 904cd71..ccc6bca 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/example/7B_demo.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/example/7B_demo.po @@ -37,8 +37,8 @@ msgstr "Start Training" #: ../../source/example/7B_demo.rst:164 9e7a864ae2e14d05b0681f16792e5278 msgid "完成以上训练配置后,可启动模型训练,以在 ``slurm`` 平台上为例,启动单节点 8GPU 的训练命令如下所示:" -msgstr "After completing the data preparation and relevant training configurations, you can start the demo training. -The following example shows how to start distributed training in ``slurm`` environments with 8 GPUs." +msgstr "After completing the data preparation and relevant training configurations, you can start the demo training. " +"The following example shows how to start distributed training in ``slurm`` environments with 8 GPUs." #: ../../source/example/7B_demo.rst:171 fdd053efb1854d46aabf6c0f279fe7fc msgid "训练结果" diff --git a/doc/code-docs/locales/en/LC_MESSAGES/initialize.po b/doc/code-docs/locales/en/LC_MESSAGES/initialize.po index c3ea055..3303581 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/initialize.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/initialize.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: InternLM \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-08 15:32+0800\n" +"POT-Creation-Date: 2023-09-14 12:23+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: zh_CN\n" @@ -23,24 +23,68 @@ msgstr "" msgid "训练构建" msgstr "Training Setup" -#: ../../source/initialize.rst:7 +#: ../../source/initialize.rst:4 +msgid "InternLM 的训练流程可以归纳为两个步骤:" +msgstr "The training process of InternLM can be summarized into two steps: " + +#: ../../source/initialize.rst:6 +msgid "初始化" +msgstr "Initialization" + +#: ../../source/initialize.rst:8 +msgid "初始化模型、优化器、数据加载器、Trainer,生成不同种类的进程组,为混合并行的迭代训练做准备。" +msgstr "" +"Initialize model, optimizer, dataloader, trainer, and create different " +"types of process groups to prepare for iterative steps of hybrid parallel training. " + +#: ../../source/initialize.rst:9 +msgid "初始化Logger、Checkpoint管理器、Monitor管理器、Profiler,对迭代训练的过程观察、预警、记录。" +msgstr "" +"Initialize logger, checkpoint manager, monitor manager, and profiler to " +"watch, alert, and record the iterative training steps. " + +#: ../../source/initialize.rst:11 +msgid "迭代训练" +msgstr "Iterative training steps" + +#: ../../source/initialize.rst:13 +msgid "根据配置文件定义的张量并行、流水线并行、数据并行的大小,加载训练引擎和调度器进行混合并行训练。" +msgstr "" +"Load the training engine and scheduler for hybrid parallel training " +"according to the configuration such as tensor parallel size, pipeline " +"parallel size, and data parallel size. " + +#: ../../source/initialize.rst:14 +msgid "在迭代训练中,调用 Trainer API 进行梯度置零,前向传播计算损失并反向传播,参数更新。" +msgstr "" +"In iterative training steps, the Trainer API is called to perform zero " +"gradients, forward-loss-backward, and parameter update." + +#: ../../source/initialize.rst:20 +msgid "InternLM训练流程图" +msgstr "InternLM training process" + +#: ../../source/initialize.rst:25 msgid "命令行参数解析" msgstr "Argument Parsing" -#: ../../source/initialize.rst:9 -#, fuzzy +#: ../../source/initialize.rst:27 msgid "" "InternLM 使用 `argparse `_" -" 库来向InternLM运行时提供命令行参数配置。用户可使用 " -"``internlm.initialize.get_default_parser()`` 来获取 InternLM " -"的默认解析器,其中包含一些内置参数,用户可以向此解析器添加自定义参数。" +" 库来向InternLM运行时提供命令行参数配置。" msgstr "" "InternLM uses the `argparse " "`_ library to supply " -"commandline configuration to the InternLM runtime. Use " -"``internlm.initialize.get_default_parser()`` to get InternLM's default " -"parser with some builtin arguments, users can add custom parameters to " -"this parser." +"commandline configuration to the InternLM runtime. " + +#: ../../source/initialize.rst:29 +msgid "" +"用户可使用 ``internlm.initialize.get_default_parser()`` 来获取 InternLM " +"的默认解析器,其中包含一些内置参数,用户可以向此解析器添加自定义参数。" +msgstr "" +"Use ``internlm.initialize.get_default_parser()`` to get InternLM's " +"default parser with some builtin arguments, users can add custom " +"parameters to this parser." #: internlm.initialize.launch.get_default_parser:1 of msgid "" @@ -69,7 +113,7 @@ msgstr "" msgid "返回类型" msgstr "" -#: ../../source/initialize.rst:25 +#: ../../source/initialize.rst:45 msgid "模型初始化" msgstr "Model Initialization" @@ -81,26 +125,26 @@ msgstr "" msgid "The neural network model to be trained or evaluated." msgstr "" -#: ../../source/initialize.rst:29 +#: ../../source/initialize.rst:49 msgid "InternLM 在配置文件中使用字段 ``model_type`` 和 ``model`` 来控制模型初始化过程。示例模型初始化配置定义如下:" msgstr "" "InternLM uses the field ``model_type`` and ``model`` in the config file " "to control model initialization process. An example model initialization " "configuratio" -#: ../../source/initialize.rst:57 +#: ../../source/initialize.rst:77 msgid "字段 ``model_type`` 指明了要初始化的模型类型" msgstr "" "The field ``model_type`` specifics the model type has been registered and" " to be initialized." -#: ../../source/initialize.rst:58 +#: ../../source/initialize.rst:78 msgid "字段 ``model`` 中的参数指定了在模型初始化过程中的参数设置" msgstr "" "The parameters in field ``model`` specific the configuration settings " "during model initialization." -#: ../../source/initialize.rst:60 +#: ../../source/initialize.rst:80 msgid "" "值得注意的是,用户可以定义新的模型类型,并使用装饰器 ``@MODEL_INITIALIZER.register_module`` " "注册模型的初始化函数,其中 ``MODEL_INITIALIZER`` 是类 " @@ -112,7 +156,7 @@ msgstr "" " instantiated object of class ``internlm.util.registry.Registry``, the " "example is shown as follows." -#: ../../source/initialize.rst:72 +#: ../../source/initialize.rst:92 msgid "优化器初始化" msgstr "Optimizer Initialization" @@ -134,7 +178,7 @@ msgstr "" msgid "A tuple of (optimizer, beta2_scheduler, lr_scheduler)." msgstr "" -#: ../../source/initialize.rst:79 +#: ../../source/initialize.rst:99 msgid "数据加载器初始化" msgstr "Dataloader Initialization" @@ -162,7 +206,7 @@ msgstr "" msgid "A tuple of (train_dl, dataset_types)." msgstr "" -#: ../../source/initialize.rst:86 +#: ../../source/initialize.rst:106 msgid "Trainer 初始化" msgstr "Trainer Initialization" diff --git a/doc/code-docs/locales/en/LC_MESSAGES/profiler.po b/doc/code-docs/locales/en/LC_MESSAGES/profiler.po index 71adf14..38858cd 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/profiler.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/profiler.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: InternLM \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-08 15:32+0800\n" +"POT-Creation-Date: 2023-09-14 11:05+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -32,13 +32,13 @@ msgid "" "InternLM 使用 ``internlm.train.initialize_llm_profile()`` " "来收集和分析模型训练或推理期间的性能数据,如 CPU/CUDA/memory 等性能数据。这个实现基于 `torch.profiler " "`_ ,输出的性能分析 trace 文件可以使用 " -"`tensorboard `_ 进行可视化。" +"`tensorboard `_ 进行可视化。" msgstr "" "InternLM uses ``internlm.train.initialize_llm_profile()`` to profile " "performance data, execution time duration and breakdown analysis of step " "time. The implementation is based on `torch.profiler " "`_ and output tracing " -"files can be visualized with `tensorboard `_." +"files can be visualized with `tensorboard `_." #: ../../source/profiler.rst:11 msgid "" @@ -53,11 +53,15 @@ msgstr "" #: ../../source/profiler.rst:13 msgid "实际运行生成的 ``Torch Profiler`` 目录结构如下:" -msgstr "The directory structure of ``Torch Profiler`` generated files is as follows:" +msgstr "" +"The directory structure of ``Torch Profiler`` generated files is as " +"follows:" #: ../../source/profiler.rst:22 msgid "其中, ``traces`` 可以通过 ``TensorBoard`` 可视化,运行命令" -msgstr "Among them, ``traces`` can be visualized through ``TensorBoard`` and run with the command" +msgstr "" +"Among them, ``traces`` can be visualized through ``TensorBoard`` and run " +"with the command" #: ../../source/profiler.rst:29 msgid "" @@ -66,7 +70,12 @@ msgid "" "tensorboard " "`_" -msgstr "In the opened ``TensorBoard -> PyTorch Profiler -> Views -> Trace`` page, you can see the timeline of profiled operators and GPU kernels. For more usage, please refer to `torch profiler with tensorboard `_" +msgstr "" +"In the opened ``TensorBoard -> PyTorch Profiler -> Views -> Trace`` page," +" you can see the timeline of profiled operators and GPU kernels. For more" +" usage, please refer to `torch profiler with tensorboard " +"`_" #: internlm.train.training_internlm.initialize_llm_profile:1 of msgid "Initialize and return the profiler context manager instance." diff --git a/doc/code-docs/locales/en/LC_MESSAGES/training.po b/doc/code-docs/locales/en/LC_MESSAGES/training.po index c9d9521..05c834f 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/training.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/training.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: InternLM \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-07 10:56+0800\n" +"POT-Creation-Date: 2023-09-14 12:23+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: en\n" @@ -19,109 +19,144 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.12.1\n" -#: ../../source/training.rst:2 6eafa5eb08e040039309a39cdb0f1bfe +#: ../../source/training.rst:2 msgid "训练 API" msgstr "Training API" -#: ../../source/training.rst:4 74d81f3d0ca54c839d4e80bd589aedb2 +#: ../../source/training.rst:4 msgid "" "InternLM 的训练 API 由 ``internlm.core.trainer.Trainer`` " "管理。在定义了训练引擎和调度器之后,我们可以调用 Trainer API 来执行模型训练、评估、梯度清零和参数更新等。" msgstr "" -"InternLM training API is managed in ``internlm.core.trainer.Trainer``. After defining the " -"training engine and runtime scheduler, we can call training API to perform training, evaluation, " -"zero gradients and parameter update steps." +"InternLM training API is managed in ``internlm.core.trainer.Trainer``. " +"After defining the training engine and runtime scheduler, we can call " +"training API to perform training, evaluation, zero gradients and " +"parameter update steps." -#: ../../source/training.rst:6 0e0cfddbb2334d3da99d3289edf4161d +#: ../../source/training.rst:6 msgid "有关详细用法,请参阅 Trainer API 文档和示例。" -msgstr "For detailed usage, please refer to Trainer API documentation and examples." +msgstr "" +"For detailed usage, please refer to Trainer API documentation and " +"examples." -#: 7ea10280a8f1489984cb9994aa08976b internlm.core.trainer.Trainer:1 of +#: internlm.core.trainer.Trainer:1 of msgid "" "This is a class tending for easy deployments of users' training and " "evaluation instead of writing their own scripts." msgstr "" -#: 7969dca55840451193bffd3b071ab3b3 aff576168b59460491bb5da0ce41ea74 #: internlm.core.trainer.Trainer internlm.core.trainer.Trainer.execute_schedule #: of msgid "参数" msgstr "" -#: 59754d3e9ee8452a872bf397c01e0d8c internlm.core.trainer.Trainer:4 of +#: internlm.core.trainer.Trainer:4 of msgid "Engine responsible for the process function." msgstr "" -#: 2d18ff15256e48f98901c7a7e0cbbe35 internlm.core.trainer.Trainer:6 of +#: internlm.core.trainer.Trainer:6 of msgid "Runtime schedule. Defaults to None." msgstr "" -#: 76f4b3c7feba40eca3ee2b32559c53f5 internlm.core.trainer.Trainer.engine:1 of +#: internlm.core.trainer.Trainer.engine:1 of msgid "" "Returns the engine that responsible for managing the training and " "evaluation process." msgstr "" -#: c7eae2d4d06c4ef891e314902d80b7f3 internlm.core.trainer.Trainer.schedule:1 of +#: internlm.core.trainer.Trainer.schedule:1 of msgid "Returns the runtime scheduler." msgstr "" -#: cb495b21b3444881aec83803e92386d9 #: internlm.core.trainer.Trainer.uses_pipeline:1 of msgid "Returns whether the pipeline parallel is used or not." msgstr "" -#: 86b0b631189e46468281a397c5e97350 internlm.core.trainer.Trainer.train:1 of +#: internlm.core.trainer.Trainer.train:1 of msgid "Sets the model to training mode." msgstr "" -#: f997e13120ee4d8b9e45ea6698b3e2a6 internlm.core.trainer.Trainer.eval:1 of +#: internlm.core.trainer.Trainer.eval:1 of msgid "Sets the model to evaluation mode." msgstr "" -#: a8179e50312d47dcbe9de0433a65c2f7 internlm.core.trainer.Trainer.zero_grad:1 -#: of +#: internlm.core.trainer.Trainer.zero_grad:1 of msgid "Sets the gradient of all parameters in the model to zero." msgstr "" -#: f936136ef9e0452ca439b7c66dc8884b internlm.core.trainer.Trainer.step:1 of +#: internlm.core.trainer.Trainer.step:1 of msgid "Executes the parameter update step." msgstr "" -#: 250e2af89cfd432c84d228f9e03c174c #: internlm.core.trainer.Trainer.execute_schedule:1 of msgid "" "Runs the forward, loss computation, and backward for the model. Returns a" " tuple of (output, label, loss)." msgstr "" -#: 6ca7de83033b432792eb0d7935ea04da #: internlm.core.trainer.Trainer.execute_schedule:4 of msgid "The data iterator." msgstr "" -#: 6d3044e75b3149beba3c659e15607b79 #: internlm.core.trainer.Trainer.execute_schedule:6 of msgid "Additional keyword arguments." msgstr "" -#: 99d5a297d6414c30b432acf2566f0d3c #: internlm.core.trainer.Trainer.execute_schedule of msgid "返回" msgstr "" -#: b625ebf0cf874edba384456d33e740b4 #: internlm.core.trainer.Trainer.execute_schedule:8 of msgid "A tuple of (output, label, loss)." msgstr "" -#: 391cde57d2e2478d8f83a7ad270c2a65 #: internlm.core.trainer.Trainer.execute_schedule of msgid "返回类型" msgstr "" -#: d4c4fb0fbddb499786970509cf0c9e13 #: internlm.core.trainer.Trainer.execute_schedule:9 of msgid "Tuple[:class:`torch.Tensor`]" msgstr "" +#~ msgid "InternLM 的训练流程可以归纳为两个步骤:" +#~ msgstr "The training process of InternLM can be summarized into two steps: " + +#~ msgid "初始化" +#~ msgstr "Initialization" + +#~ msgid "初始化模型、优化器、数据加载器、Trainer,生成不同种类的进程组,为混合并行的迭代训练做准备。" +#~ msgstr "" +#~ "Initialize model, optimizer, dataloader, " +#~ "trainer, and create different types of" +#~ " process groups to prepare for " +#~ "iterative steps of hybrid parallel " +#~ "training. " + +#~ msgid "初始化Logger、Checkpoint管理器、Monitor管理器、Profiler,对迭代训练的过程观察、预警、记录。" +#~ msgstr "" +#~ "Initialize logger, checkpoint manager, monitor" +#~ " manager, and profiler to watch, " +#~ "alert, and record the iterative training" +#~ " steps. " + +#~ msgid "迭代训练" +#~ msgstr "Iterative training steps" + +#~ msgid "根据配置文件定义的张量并行、流水线并行、数据并行的大小,加载训练引擎和调度器进行混合并行训练。" +#~ msgstr "" +#~ "Load the training engine and scheduler" +#~ " for hybrid parallel training according " +#~ "to the configuration such as tensor " +#~ "parallel size, pipeline parallel size, " +#~ "and data parallel size. " + +#~ msgid "在迭代训练中,调用 Trainer API 进行梯度置零,前向传播计算损失并反向传播,参数更新。" +#~ msgstr "" +#~ "In iterative training steps, the Trainer" +#~ " API is called to perform zero " +#~ "gradients, forward-loss-backward, and " +#~ "parameter update." + +#~ msgid "InternLM训练流程图" +#~ msgstr "InternLM training process" + diff --git a/doc/code-docs/locales/en/LC_MESSAGES/usage.po b/doc/code-docs/locales/en/LC_MESSAGES/usage.po index 868a25f..37e7cba 100644 --- a/doc/code-docs/locales/en/LC_MESSAGES/usage.po +++ b/doc/code-docs/locales/en/LC_MESSAGES/usage.po @@ -183,7 +183,8 @@ msgstr "" #: ../../../usage.md:237 msgid "接下来将详细介绍启动一个模型训练所需要进行的数据、模型、并行和监控等相关的配置。" -msgstr "let's discuss the data, model, parallel and monitoring configurations " +msgstr "" +"let's discuss the data, model, parallel and monitoring configurations " "required to start a model training." #: ../../../usage.md:239 @@ -275,7 +276,6 @@ msgstr "" "default value is -1" #: ../../../usage.md:325 -#, fuzzy msgid "当`zero1 <= 0`,则 zero1 进程组的大小等于数据并行进程组的大小,因此优化器状态参数将在数据并行范围内分配" msgstr "" "When `zero1 <= 0`, the size of the zero1 process group is equal to the " @@ -283,14 +283,12 @@ msgstr "" "parameters will be split within the data parallel range." #: ../../../usage.md:326 -#, fuzzy msgid "当`zero1 == 1`,则不使用 zero1 ,所有数据并行组保留完整的优化器状态参数" msgstr "" "When `zero1 == 1`, zero1 is not used, and all data parallel groups retain" " the complete optimizer state parameters." #: ../../../usage.md:327 -#, fuzzy msgid "当`zero1 > 1`且`zero1 <= data_parallel_world_size`,则 zero1 进程组是数据并行进程组的子集" msgstr "" "When `zero1 > 1` and `zero1 <= data_parallel_world_size`, the zero1 " diff --git a/doc/code-docs/source/checkpoint.rst b/doc/code-docs/source/checkpoint.rst index a192469..ee4f037 100644 --- a/doc/code-docs/source/checkpoint.rst +++ b/doc/code-docs/source/checkpoint.rst @@ -1,8 +1,9 @@ 模型保存 =================== -InternLM 使用 ``internlm.utils.model_checkpoint.CheckpointManager`` 来管理模型保存。 其中,可以 -使用 ``CheckpointManager.try_save_checkpoint(train_state)`` 来保存指定 step 的模型状态。InternLM支持启动时自动加载最新的模型备份,并在接收信号退出训练时自动进行模型备份。 +InternLM 使用 ``internlm.utils.model_checkpoint.CheckpointManager`` 来管理模型保存。其中,可以使用 ``CheckpointManager.try_save_checkpoint(train_state)`` 来保存指定 step 的模型状态。 + +InternLM支持启动时自动加载最新的模型备份,并在接收信号退出训练时自动进行模型备份。 Checkpointing ------------- diff --git a/doc/code-docs/source/conf.py b/doc/code-docs/source/conf.py index a41f850..c752047 100644 --- a/doc/code-docs/source/conf.py +++ b/doc/code-docs/source/conf.py @@ -72,14 +72,14 @@ exclude_patterns = [] # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "sphinx_rtd_theme" -html_static_path = ["_static"] +html_static_path = [] # GitHub integration html_context = { "display_github": True, "github_user": "InternLM", "github_repo": "InternLM", - "github_version": "master", + "github_version": "main", "conf_py_path": "/doc/code-docs/source/", } diff --git a/doc/code-docs/source/initialize.rst b/doc/code-docs/source/initialize.rst index 37ee66e..e94f4f6 100644 --- a/doc/code-docs/source/initialize.rst +++ b/doc/code-docs/source/initialize.rst @@ -1,12 +1,32 @@ 训练构建 ============== +InternLM 的训练流程可以归纳为两个步骤: + +1. 初始化 + + * 初始化模型、优化器、数据加载器、Trainer,生成不同种类的进程组,为混合并行的迭代训练做准备。 + * 初始化Logger、Checkpoint管理器、Monitor管理器、Profiler,对迭代训练的过程观察、预警、记录。 + +2. 迭代训练 + + * 根据配置文件定义的张量并行、流水线并行、数据并行的大小,加载训练引擎和调度器进行混合并行训练。 + * 在迭代训练中,调用 Trainer API 进行梯度置零,前向传播计算损失并反向传播,参数更新。 + +.. figure:: ../../imgs/hybrid_parallel_training.png + :scale: 45% + :class: with-border + + InternLM训练流程图 + .. _InternLM-args: 命令行参数解析 ---------------- -InternLM 使用 `argparse `_ 库来向InternLM运行时提供命令行参数配置。用户可使用 ``internlm.initialize.get_default_parser()`` 来获取 InternLM 的默认解析器,其中包含一些内置参数,用户可以向此解析器添加自定义参数。 +InternLM 使用 `argparse `_ 库来向InternLM运行时提供命令行参数配置。 + +用户可使用 ``internlm.initialize.get_default_parser()`` 来获取 InternLM 的默认解析器,其中包含一些内置参数,用户可以向此解析器添加自定义参数。 .. code-block:: python diff --git a/doc/code-docs/source/profiler.rst b/doc/code-docs/source/profiler.rst index 7ff42cb..0163ebe 100644 --- a/doc/code-docs/source/profiler.rst +++ b/doc/code-docs/source/profiler.rst @@ -6,7 +6,7 @@ Torch Profiler ----------------- -InternLM 使用 ``internlm.train.initialize_llm_profile()`` 来收集和分析模型训练或推理期间的性能数据,如 CPU/CUDA/memory 等性能数据。这个实现基于 `torch.profiler `_ ,输出的性能分析 trace 文件可以使用 `tensorboard `_ 进行可视化。 +InternLM 使用 ``internlm.train.initialize_llm_profile()`` 来收集和分析模型训练或推理期间的性能数据,如 CPU/CUDA/memory 等性能数据。这个实现基于 `torch.profiler `_ ,输出的性能分析 trace 文件可以使用 `tensorboard `_ 进行可视化。 用户如果想使用这个 torch 性能分析工具,需要在启动训练时传递 ``--profiling`` 参数以启用性能分析。完成 torch 性能分析后,用户可以在 ``{JOB_NAME}/{start_time}/traces/rank{}_dp{}_tp{}_pp{}`` 文件夹中看到性能分析结果。 diff --git a/doc/code-docs/source/qa.rst b/doc/code-docs/source/qa.rst index e1b990a..3912bb3 100644 --- a/doc/code-docs/source/qa.rst +++ b/doc/code-docs/source/qa.rst @@ -1,2 +1,2 @@ 问&答 -==== \ No newline at end of file +===== \ No newline at end of file diff --git a/doc/imgs/hybrid_parallel_training.png b/doc/imgs/hybrid_parallel_training.png new file mode 100644 index 0000000000000000000000000000000000000000..33e4ff9e34099e61c6ac7818aa07979937b7414b GIT binary patch literal 213327 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

k@Jj7vo4y8079{R4q;RYgr4lNL$Fa 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 50: + tgs_statistic["SMA_tg_50"] -= tgs_statistic["SMA_tg_50_list"][0] + tgs_statistic["SMA_time_50"] -= tgs_statistic["SMA_time_50_list"][0] + tgs_statistic["SMA_tg_50_list"].popleft() + tgs_statistic["SMA_time_50_list"].popleft() + + last_tgs_1 = round(tk_per_gpu / time_cost, 2) + tgs_statistic["sum_tgs"] += last_tgs_1 + + if tgs_statistic["sum_step"] % 10 == 0: + tgs_statistic["last_tgs_10"] = round(tgs_statistic["sum_last_tg_10"] / tgs_statistic["sum_last_time_10"], 2) + tgs_statistic["sum_last_tg_10"] = 0 + tgs_statistic["sum_last_time_10"] = 0 + + if tgs_statistic["sum_step"] % 50 == 0: + tgs_statistic["last_tgs_50"] = round(tgs_statistic["sum_last_tg_50"] / tgs_statistic["sum_last_time_50"], 2) + tgs_statistic["sum_last_tg_50"] = 0 + tgs_statistic["sum_last_time_50"] = 0 + + last_tgs_10 = tgs_statistic["last_tgs_10"] + last_tgs_50 = tgs_statistic["last_tgs_50"] + + tgs_all = round(tgs_statistic["sum_tg"] / tgs_statistic["sum_time"], 2) + tgs_avg = round(tgs_statistic["sum_tgs"] / tgs_statistic["sum_step"], 2) + tgs_SMA = round(tgs_statistic["SMA_tg_50"] / tgs_statistic["SMA_time_50"], 2) + + tflops = get_tflops_func((time.time() - start_time)) + + tgs_origin = round( num_tokens_in_batch * gpc.get_world_size(ParallelMode.DATA) / gpc.get_world_size(ParallelMode.GLOBAL) @@ -382,13 +425,17 @@ def record_current_batch_training_metrics( 2, ) - tflops = get_tflops_func((time.time() - start_time)) - infos = { "tflops": tflops, "step": batch_count, "loss": loss.item(), - "tgs (tokens/gpu/second)": tk_per_gpu, + "tgs (tokens/gpu/second)": tgs_origin, + "tgs/last_tgs_1": last_tgs_1, + "tgs/tgs_all": tgs_all, + "tgs/tgs_avg": tgs_avg, + "tgs/tgs_SMA": tgs_SMA, + "tgs/last_tgs_10": last_tgs_10, + "tgs/last_tgs_50": last_tgs_50, "lr": lr, "loss_scale": scaler, "grad_norm": grad_norm, @@ -428,7 +475,7 @@ def record_current_batch_training_metrics( "num_consumed_tokens": train_state.num_consumed_tokens, "loss": loss.item(), "flops": tflops, - "tgs": tk_per_gpu, + "tgs": last_tgs_1, "acc": acc_perplex["acc"], "perplexity": acc_perplex["perplexity"], "fwd_bwd_time": fwd_bwd_time, diff --git a/tests/test_model/test_embedding.py b/tests/test_model/test_embedding.py new file mode 100644 index 0000000..324ca2b --- /dev/null +++ b/tests/test_model/test_embedding.py @@ -0,0 +1,65 @@ +import multiprocessing as mp + +import pytest +import torch + +from internlm.model.embedding import Embedding1D +from tests.test_model.test_model_internlm import build_environment, seed_all + + +def check_embedding(args): + # init + rank, world_size = args + device = torch.device("cuda") + build_environment(rank, world_size) + rtol, atol = (1e-3, 5e-3) + vocab_size = 4 + hidden_size = 2 + + # fix seed + seed_all(1024) + + # define embedding + embedding = Embedding1D( + num_embeddings=vocab_size, + embedding_dim=hidden_size, + padding_idx=None, + ) + + embedding.weight.data.copy_(torch.randn(vocab_size, hidden_size)) + embedding = embedding.to(device) + + # create input + input_ids = torch.tensor([[0, 2], [1, 3]]).to(device) + result = embedding(input_ids) + + standard_list = [[[-1.4837, 0.2671], [0.6002, -0.5496]], [[-1.8337, -0.1047], [1.0391, 0.2261]]] + standard_result = torch.tensor(standard_list).to(device) + + # check output + assert torch.allclose(result, standard_result, rtol=rtol, atol=atol, equal_nan=True) + + loss = torch.randn_like(result) + + # backward + result.backward(loss) + + grad = embedding.weight.grad + standard_glist = [[-0.4461, 0.5602], [0.4353, 1.2988], [-0.0625, -1.3609], [0.9595, -0.1144]] + standard_grad = torch.tensor(standard_glist).to(device) + + # check grad + assert torch.allclose(grad, standard_grad, rtol=rtol, atol=atol, equal_nan=True) + + +@pytest.mark.embedding +def test_embedding(): + ctx = mp.get_context("spawn") + with ctx.Pool(processes=8) as pool: + pool.map(check_embedding, [[rank, 8] for rank in range(8)]) + pool.close() + pool.join() + + +if __name__ == "__main__": + pytest.main(["-s", "-q", "test_embedding.py"]) diff --git a/tests/test_model/test_model_internlm.py b/tests/test_model/test_model_internlm.py new file mode 100644 index 0000000..fb9c678 --- /dev/null +++ b/tests/test_model/test_model_internlm.py @@ -0,0 +1,379 @@ +import multiprocessing as mp +import random + +import numpy as np +import pytest +import torch +from torch import nn + +import internlm +from internlm.core.context import ParallelMode +from internlm.core.context.parallel_context import Config +from internlm.core.context.parallel_context import global_context as gpc +from internlm.model.linear import RewardModelLinear, ScaleColumnParallelLinear +from internlm.model.modeling_internlm import PackedFlashBaseLayer1D +from internlm.model.utils import gather_forward_split_backward + +config = Config( + dict( + parallel=dict(zero1=1, pipeline=dict(size=1, interleaved_overlap=False), sequence_parallel=False, tensor=1), + model_type="INTERNLM", + data=dict(seq_len=2048, micro_num=1, micro_bsz=1, pack_sample_into_one=False, min_length=0, total_steps=9999), + model=dict( + checkpoint=False, + num_attention_heads=2, + embed_split_hidden=True, + vocab_size=103168, + embed_grad_scale=1, + parallel_output=True, + hidden_size=1024, + num_layers=2, + mlp_ratio=1, + apply_post_layer_norm=False, + dtype=torch.bfloat16, + norm_type="rmsnorm", + layer_norm_epsilon=1e-5, + use_flash_attn=True, + num_chunks=1, + ), + resume_tb_folder="", + tensorboard_folder="", + alert_address=None, + monitor=dict(alert=dict(enable_feishu_alert=False, feishu_alert_address=None, light_monitor_address=None)), + ) +) + + +def build_environment(rank, world_size): + import os + + os.environ["RANK"] = str(rank) + os.environ["LOCAL_RANK"] = str(rank) + os.environ["WORLD_SIZE"] = str(world_size) + os.environ["MASTER_ADDR"] = "127.0.0.1" + os.environ["MASTER_PORT"] = "12345" + torch.cuda.empty_cache() + # launcher="torch" + internlm.launch_from_torch(config=config, seed=1024) + + +def seed_all(seed, cuda_deterministic=False): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + if torch.cuda.is_available(): + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + if cuda_deterministic: # slower, more reproducible + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True + + +def check_block(args): + # init + rank, world_size = args + build_environment(rank, world_size) + device = torch.device("cuda") + rtol, atol = (1e-3, 5e-3) + + # fix seed + seed_all(1024) + + # define block + blocks = nn.ModuleList( + [ + PackedFlashBaseLayer1D( + hidden_size=4, # 768 + num_attention_heads=2, # 12 + mlp_ratio=2, + attn_drop_rate=0.0, + drop_rate=0.0, + dtype=torch.bfloat16, + layer_norm_epsilon=1e-5, + checkpoint=lid < 0, + layer_idx=lid + 0, # This parameter is used for caching during generation + residual_in_fp32=False, + device=device, + norm_type="rmsnorm", + dropout_selective_checkpoint=True, + use_scaled_init=True, + use_swiglu=True, + ) + for lid in range(4) # 32 + ] + ) + + # create input + cu_seqlens = torch.tensor([0, 2, 4], dtype=torch.int32).to(device) # [0, 8, 16] + indexes = torch.tensor([0, 1, 0, 1]).to(device) # [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7] + hidden_states = torch.tensor([[0, 3, 2, 1]]).to(device) # [[4, 118, 0, 1, 2, 3, 0, 1, 1, 97, 0, 0, 0, 0, 0, 0]] + max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item() + + hidden_states = torch.tensor( + [ + [ + [-1.1620, 1.3113, 0.1507, 2.2698], + [-1.2610, 1.0990, 0.3787, -0.3478], + [1.4001, 1.1982, -0.6696, 0.3269], + [1.3304, 1.2262, 1.0735, -1.1169], + ] + ] + ) + + hidden_states = hidden_states.squeeze(0).to(device).requires_grad_() + + # forward + for _, block in enumerate(blocks): + block = block.to(torch.bfloat16) + block = block.to(device) + hidden_states = block( + hidden_states, + cu_seqlens=cu_seqlens, + indexes=indexes, + inference_params=None, + max_seqlen=max_seqlen, + ) + + result = hidden_states + standard_result = torch.tensor( + [ + [-1.1621, 1.3111, 0.1509, 2.2697], + [-1.2611, 1.0988, 0.3787, -0.3478], + [1.4000, 1.1982, -0.6694, 0.3268], + [1.3303, 1.2262, 1.0736, -1.1169], + ] + ).to(device) + + # check output + assert torch.allclose(result, standard_result, rtol=rtol, atol=atol) + + hidden_states.retain_grad() + loss = torch.randn_like(result) + + # backward + result.backward(loss) + + grad = hidden_states.grad + standard_grad = torch.tensor( + [ + [0.7999, -0.2595, 0.2649, -1.3256], + [0.7064, 0.0283, -0.5508, 0.6494], + [-1.4657, -2.0316, 1.3776, 0.7211], + [-0.6046, 0.4329, -0.1884, 1.1170], + ] + ).to(device) + + # check grad + assert torch.allclose(grad, standard_grad, rtol=rtol, atol=atol) + + +def check_head(args): + # init + rank, world_size, is_reward = args + device = torch.device("cuda") + build_environment(rank, world_size) + rtol, atol = (1e-3, 5e-3) + hidden_size = 4 + vocab_size = 4 + embed_grad_scale = 1 + + # fix seed + seed_all(1024) + + # load standard + if is_reward: + head_cls = RewardModelLinear + standard_result = torch.tensor([[3.5938], [1.0703], [3.6250], [3.6250]], dtype=torch.bfloat16).to(device) + standard_grad = torch.tensor( + [ + [-0.2246, 0.0164, -0.0591, 0.1660], + [-0.5625, 0.0408, -0.1484, 0.4160], + [-0.1758, 0.0128, -0.0464, 0.1299], + [-0.4785, 0.0347, -0.1260, 0.3516], + ], + dtype=torch.bfloat16, + ).to(device) + else: + head_cls = ScaleColumnParallelLinear + standard_result = torch.tensor( + [ + [3.5938, -2.2188, 2.0312, 3.5625], + [1.0703, -1.1797, 1.1406, 1.6641], + [3.6250, -2.0156, 1.7656, 3.4531], + [3.6250, -2.0156, 1.7656, 3.4531], + ], + dtype=torch.bfloat16, + ).to(device) + standard_grad = torch.tensor( + [ + [-0.2354, 0.0981, -0.2930, -0.6328], + [0.2344, -0.2334, -0.0918, 0.1396], + [-0.5898, -1.0156, -0.7070, 1.3750], + [0.0242, -0.1494, 0.1206, -0.0427], + ], + dtype=torch.bfloat16, + ).to(device) + + # define head + head = head_cls( + in_features=hidden_size, + out_features=gpc.get_world_size(ParallelMode.TENSOR) if is_reward else vocab_size, + process_group=gpc.get_group(ParallelMode.TENSOR), + bias=False, + device=device, + dtype=torch.bfloat16, + weight_scale=embed_grad_scale, + ) + + head = head.to(torch.bfloat16) + head = head.to(device) + + # create input + hidden_states = torch.tensor( + [ + [8.3726, 1.9245, 5.5101, 1.0000], + [3.3474, 2.9582, 1.0000, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000], + ], + dtype=torch.bfloat16, + requires_grad=True, + ).to(device) + + # forward + result = head(hidden_states) + + # check output + assert torch.allclose(result, standard_result, rtol=rtol, atol=atol) + + hidden_states.retain_grad() + loss = torch.randn_like(result) + + # backward + result.backward(loss) + grad = hidden_states.grad + + # check grad + assert torch.allclose(grad, standard_grad, rtol=rtol, atol=atol) + + +def check_gather_forward(args): + # init + rank, world_size, parallel_tensor = args + assert parallel_tensor in [1, 2] + config.parallel.tensor = parallel_tensor + device = torch.device("cuda") + build_environment(rank, world_size) + rtol, atol = (1e-3, 5e-3) + + # fix seed + seed_all(1024) + + # load standard + if parallel_tensor == 1: + standard_result = torch.tensor( + [ + [8.3726, 1.9245, 5.5101, 1.0000], + [3.3474, 2.9582, 1.0000, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000], + ] + ).to(device) + standard_grad = torch.tensor( + [ + [-0.4461, 0.5602, -0.0625, -1.3609], + [0.4353, 1.2988, 0.9595, -0.1144], + [-0.7593, -0.4031, 0.2041, 1.4955], + [0.5706, 0.9047, -0.6965, -0.3757], + ] + ).to(device) + else: + standard_result = torch.tensor( + [ + [8.3726, 1.9245, 5.5101, 1.0000, 8.3726, 1.9245, 5.5101, 1.0000], + [3.3474, 2.9582, 1.0000, 1.0000, 3.3474, 2.9582, 1.0000, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000, 8.3726, 1.2875, 5.5101, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000, 8.3726, 1.2875, 5.5101, 1.0000], + ] + ).to(device) + if rank % 2 == 0: + standard_grad = torch.tensor( + [ + [-0.4461, 0.5602, -0.0625, -1.3609], + [-0.7593, -0.4031, 0.2041, 1.4955], + [0.8093, 1.7580, 1.2996, -0.7545], + [1.0474, -0.5767, -1.0401, 0.8233], + ] + ).to(device) + else: + standard_grad = torch.tensor( + [ + [0.4353, 1.2988, 0.9595, -0.1144], + [0.5706, 0.9047, -0.6965, -0.3757], + [-1.3589, -0.7202, 0.6094, -0.8208], + [-1.0042, 0.3695, 0.2511, -0.2718], + ] + ).to(device) + + # create input + hidden_states = torch.tensor( + [ + [8.3726, 1.9245, 5.5101, 1.0000], + [3.3474, 2.9582, 1.0000, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000], + ], + requires_grad=True, + ).to(device) + + # forward + result = gather_forward_split_backward(hidden_states, ParallelMode.TENSOR, dim=-1) + + # check output + assert torch.allclose(result, standard_result, rtol=rtol, atol=atol) + + loss = torch.randn_like(result) + hidden_states.retain_grad() + + # backward + result.backward(loss) + grad = hidden_states.grad + + # check grad + assert torch.allclose(grad, standard_grad, rtol=rtol, atol=atol) + + +@pytest.mark.block +def test_block(): + ctx = mp.get_context("spawn") + with ctx.Pool(processes=8) as pool: + pool.map(check_block, [[rank, 8] for rank in range(8)]) + pool.close() + pool.join() + + +@pytest.mark.head +@pytest.mark.parametrize("is_reward", [True, False]) +def test_head(is_reward): + ctx = mp.get_context("spawn") + with ctx.Pool(processes=8) as pool: + pool.map(check_head, [[rank, 8, is_reward] for rank in range(8)]) + pool.close() + pool.join() + + +@pytest.mark.gather_forward +@pytest.mark.parametrize("parallel_tensor", [1, 2]) +def test_gather_forward(parallel_tensor): + ctx = mp.get_context("spawn") + with ctx.Pool(processes=8) as pool: + pool.map(check_gather_forward, [[rank, 8, parallel_tensor] for rank in range(8)]) + pool.close() + pool.join() + + +if __name__ == "__main__": + pytest.main(["-s", "-q", "test_model_internlm.py"]) diff --git a/tests/test_model/test_norm.py b/tests/test_model/test_norm.py new file mode 100644 index 0000000..4078ef5 --- /dev/null +++ b/tests/test_model/test_norm.py @@ -0,0 +1,84 @@ +import multiprocessing as mp + +import pytest +import torch + +from internlm.model.utils import try_import_RMSNorm +from tests.test_model.test_model_internlm import build_environment, seed_all + +RMSNorm = try_import_RMSNorm() + + +def check_norm(args): + # init + rank, world_size = args + device = torch.device("cuda") + build_environment(rank, world_size) + rtol, atol = (1e-3, 5e-3) + hidden_size = 4 + layer_norm_epsilon = 1e-05 + + # fix seed + seed_all(1024) + + # define norm + norm = RMSNorm(hidden_size, eps=layer_norm_epsilon) + norm = norm.to(device) + + # create input + hidden_states = torch.tensor( + [ + [8.3726, 1.9245, 5.5101, 1.0000], + [3.3474, 2.9582, 1.0000, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000], + [8.3726, 1.2875, 5.5101, 1.0000], + ], + requires_grad=True, + ).to(device) + + # forward + result = norm(hidden_states.float()) + + standard = torch.tensor( + [ + [1.6329, 0.3753, 1.0746, 0.1950], + [1.4288, 1.2626, 0.4268, 0.4268], + [1.6490, 0.2536, 1.0852, 0.1970], + [1.6490, 0.2536, 1.0852, 0.1970], + ] + ).to(device) + + # check output + assert torch.allclose(result, standard, rtol=rtol, atol=atol, equal_nan=True) + + hidden_states.retain_grad() + loss = torch.randn_like(result) + + # backward + result.backward(loss) + grad = hidden_states.grad + + standard_grad = torch.tensor( + [ + [-0.0193, 0.1248, 0.0324, -0.2573], + [-0.2140, 0.2010, 0.2901, -0.1683], + [-0.0815, -0.0689, 0.0850, 0.3027], + [0.0847, 0.1739, -0.1554, -0.0773], + ] + ).to(device) + + # check grad + assert torch.allclose(grad, standard_grad, rtol=rtol, atol=atol, equal_nan=True) + + +@pytest.mark.norm +def test_norm(): + ctx = mp.get_context("spawn") + with ctx.Pool(processes=8) as pool: + pool.map(check_norm, [[rank, 8] for rank in range(8)]) + pool.close() + pool.join() + + +if __name__ == "__main__": + pytest.main(["-s", "-q", "test_norm.py"]) diff --git a/tests/test_solver/test_optimizer.py b/tests/test_solver/test_optimizer.py new file mode 100644 index 0000000..6a22797 --- /dev/null +++ b/tests/test_solver/test_optimizer.py @@ -0,0 +1,364 @@ +import copy +import multiprocessing as mp +import random + +import numpy as np +import pytest +import torch +from torch import nn +from torch.nn.parallel import DistributedDataParallel as DDP +from torch.testing import assert_close + +import internlm +from internlm.core.context.parallel_context import Config +from internlm.solver.optimizer import HybridZeroOptimizer +from internlm.solver.optimizer.utils import ParamBcastSyncHandler + + +class MlpModel(nn.Module): + def __init__(self): + super().__init__() + self.linear1 = nn.Linear(128, 256) + self.linear2 = nn.Linear(256, 512) + + def forward(self, x): + x = self.linear1(x) + x = self.linear2(x) + return x + + +config = Config( + dict( + parallel=dict(zero1=1, pipeline=dict(size=1, interleaved_overlap=False), sequence_parallel=False, tensor=1), + model_type="INTERNLM", + data=dict(seq_len=2048, micro_num=1, micro_bsz=1, pack_sample_into_one=False, min_length=0, total_steps=9999), + model=dict( + dtype=torch.bfloat16, + ), + resume_tb_folder="", + tensorboard_folder="", + alert_address=None, + monitor=dict(alert=dict(enable_feishu_alert=False, feishu_alert_address=None, light_monitor_address=None)), + grad_scaler=dict( + fp16=dict( + initial_scale=1, + min_scale=1, + growth_interval=1, + ), + growth_factor=1.1, + backoff_factor=0.9, + max_scale=1, + hysteresis=1, + ), + adam=dict( + lr=1e-4, + adam_beta1=0.9, + adam_beta2=0.95, + adam_beta2_c=0, + adam_eps=1e-8, + weight_decay=0.01, + ), + hybrid_zero_optimizer=dict( + overlap_sync_grad=False, + overlap_sync_param=False, + reduce_bucket_size=512 * 1024 * 1024, + clip_grad_norm=1.0, + ), + ) +) + + +def build_environment(rank, world_size): + import os + + os.environ["RANK"] = str(rank) + os.environ["LOCAL_RANK"] = str(rank) + os.environ["WORLD_SIZE"] = str(world_size) + os.environ["MASTER_ADDR"] = "127.0.0.1" + os.environ["MASTER_PORT"] = "12345" + torch.cuda.empty_cache() + # launcher="torch" + internlm.launch_from_torch(config=config, seed=1024) + + +def loose_close(a, b, dtype: torch.dtype = torch.float32): + + if dtype is torch.float32: + rtol = 1.3e-6 + atol = 1e-5 + elif dtype is torch.bfloat16: + rtol = 2e-2 + atol = 2e-2 + + if isinstance(a, torch.Tensor): + a = a.detach().to(dtype) + b = b.detach().to(dtype) + + assert_close(a, b, rtol=rtol, atol=atol) + + +def init_optimizer_grouped_parameters(check_group, model): + if check_group: + optimizer_grouped_parameters = [ + { + "params": list(model.parameters())[:2], + "weight_decay": config.adam.weight_decay, + }, + { + "params": list(model.parameters())[2:], + "weight_decay": config.adam.weight_decay, + }, + ] + else: + optimizer_grouped_parameters = [{"params": model.parameters(), "weight_decay": config.adam.weight_decay}] + + return optimizer_grouped_parameters + + +def seed_all(seed, cuda_deterministic=False): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + if torch.cuda.is_available(): + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + if cuda_deterministic: # slower, more reproducible + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True + + +def exam_hybrid_zero_optim_with_ddp(args): + # init + rank, world_size, zero_parallel, overlap_sync_param, overlap_sync_grad, micro_num, check_group, dtype = args + # TODO: Need to test the combine of overlap param and group_params when ready + # ParamBcastSyncHandler does not consider paramters in different optimizer group currently + if overlap_sync_param and check_group: + return + config.parallel.zero1 = zero_parallel + config.hybrid_zero_optimizer.overlap_sync_param = overlap_sync_param + config.hybrid_zero_optimizer.overlap_sync_grad = overlap_sync_grad + config.data.micro_num = micro_num + config.model.dtype = dtype + totel_step = 5 + if not overlap_sync_param: + totel_step = 1 + + build_environment(rank, world_size) + seed_all(1024) + + # create models + torch_model = MlpModel().cuda() + zero_model = copy.deepcopy(torch_model).to(dtype) + torch_model = DDP(torch_model.cuda(), static_graph=True).cuda() + + # create optimizer + if config.hybrid_zero_optimizer.overlap_sync_param: + param_bcast_sync_handler = ParamBcastSyncHandler(zero_model) + else: + param_bcast_sync_handler = None + + optimizer_grouped_parameters_zero = init_optimizer_grouped_parameters(check_group, zero_model) + optimizer_grouped_parameters_torch = init_optimizer_grouped_parameters(check_group, torch_model) + + naive_optimizer = torch.optim.AdamW( + params=optimizer_grouped_parameters_zero, + lr=config.adam.lr, + betas=(config.adam.adam_beta1, config.adam.adam_beta2), + eps=config.adam.adam_eps, + ) + + zero_optimizer = HybridZeroOptimizer( + naive_optimizer, + grad_scal_cfg=config.grad_scaler, + zero_cfg=config.hybrid_zero_optimizer, + param_bcast_sync_handler=param_bcast_sync_handler, + ) + + torch_optimizer = torch.optim.AdamW( + params=optimizer_grouped_parameters_torch, + lr=config.adam.lr, + betas=(config.adam.adam_beta1, config.adam.adam_beta2), + eps=config.adam.adam_eps, + ) + + for _ in range(totel_step): + zero_optimizer.zero_grad() + torch_optimizer.zero_grad() + zero_optimizer.skip_grad_reduce = True + for num in range(micro_num): + if num == micro_num - 1: + zero_optimizer.skip_grad_reduce = False + + seed_all(1024 + rank) + # create input + input_data = torch.rand(16, 128).cuda() + + # zero-dp forward + zero_output = zero_model(input_data.to(dtype)) + + # torch-ddp forward + torch_output = torch_model(input_data) + + # check output + loose_close(zero_output, torch_output, dtype=dtype) + + # zero-dp backward + zero_optimizer.backward(zero_output.mean()) + + # torch-ddp backward + if num == micro_num - 1: + torch_output.mean().backward() + else: + with torch_model.no_sync(): + torch_output.mean().backward() + + # zero-dp step + zero_optimizer.step() + + # torch-ddp step + torch_optimizer.step() + + # check grad + if check_group: + group1 = zip(list(torch_model.parameters())[:2], list(zero_model.parameters())[:2]) + group2 = zip(list(torch_model.parameters())[2:], list(zero_model.parameters())[2:]) + for torch_parm, zero_parm in group1: + if zero_parm.grad is not None: + loose_close(torch_parm.grad, zero_parm.grad, dtype=dtype) + for torch_parm, zero_parm in group2: + if zero_parm.grad is not None: + loose_close(torch_parm.grad, zero_parm.grad, dtype=dtype) + else: + for torch_parm, zero_parm in zip(torch_model.parameters(), zero_model.parameters()): + if zero_parm.grad is not None: + loose_close(torch_parm.grad, zero_parm.grad, dtype=dtype) + + torch.cuda.synchronize() + # check updated param + if check_group: + group1 = zip(list(torch_model.parameters())[:2], list(zero_model.parameters())[:2]) + group2 = zip(list(torch_model.parameters())[2:], list(zero_model.parameters())[2:]) + for torch_parm, zero_parm in group1: + loose_close(torch_parm, zero_parm, dtype=dtype) + for torch_parm, zero_parm in group2: + loose_close(torch_parm, zero_parm, dtype=dtype) + else: + for torch_parm, zero_parm in zip(torch_model.parameters(), zero_model.parameters()): + loose_close(torch_parm, zero_parm, dtype=dtype) + + +def exam_hybrid_zero_optim_with_ckpt_load_save(args): + # init + rank, world_size, zero_parallel, check_group, dtype = args + config.parallel.zero1 = zero_parallel + config.parallel.dtype = dtype + + build_environment(rank, world_size) + + # create models + zero_model = MlpModel().cuda().to(dtype) + + # create optimizer + if config.hybrid_zero_optimizer.overlap_sync_param: + param_bcast_sync_handler = ParamBcastSyncHandler(zero_model) + else: + param_bcast_sync_handler = None + + optimizer_grouped_parameters1 = init_optimizer_grouped_parameters(check_group, zero_model) + optimizer_grouped_parameters2 = init_optimizer_grouped_parameters(check_group, zero_model) + + naive_optimizer = torch.optim.AdamW( + params=optimizer_grouped_parameters1, + lr=config.adam.lr, + betas=(config.adam.adam_beta1, config.adam.adam_beta2), + eps=config.adam.adam_eps, + ) + + zero_optimizer = HybridZeroOptimizer( + naive_optimizer, + grad_scal_cfg=config.grad_scaler, + zero_cfg=config.hybrid_zero_optimizer, + param_bcast_sync_handler=param_bcast_sync_handler, + ) + + naive_optimizer2 = torch.optim.AdamW( + params=optimizer_grouped_parameters2, + lr=config.adam.lr, + betas=(config.adam.adam_beta1, config.adam.adam_beta2), + eps=config.adam.adam_eps, + ) + + zero_optimizer2 = HybridZeroOptimizer( + naive_optimizer2, + grad_scal_cfg=config.grad_scaler, + zero_cfg=config.hybrid_zero_optimizer, + param_bcast_sync_handler=param_bcast_sync_handler, + ) + + # save and load states + states = zero_optimizer.state_dict() + zero_optimizer2.load_state_dict(states) + + # check fp32 model weights + for zero1_param, zero2_param in zip( + zero_optimizer._fp32_flat_param_groups_of_current_rank.values(), + zero_optimizer2._fp32_flat_param_groups_of_current_rank.values(), + ): + assert torch.equal(zero1_param, zero2_param) + + # check fp16 model weights + for zero1_param, zero2_param in zip( + zero_optimizer._fp16_param_groups.values(), zero_optimizer2._fp16_param_groups.values() + ): + assert zero1_param == zero2_param + + +zero_parallel_check_list = [-1, 1, 4] +overlap_sync_param_check_list = [True, False] +overlap_sync_grad_check_list = [True, False] +miro_num_check_list = [1, 2, 4] +check_group_list = [True, False] +dtype_list = [torch.float32, torch.bfloat16] + + +@pytest.mark.parametrize("zero_parallel", zero_parallel_check_list) +@pytest.mark.parametrize("overlap_sync_param", overlap_sync_param_check_list) +@pytest.mark.parametrize("overlap_sync_grad", overlap_sync_grad_check_list) +@pytest.mark.parametrize("micro_num", miro_num_check_list) +@pytest.mark.parametrize("check_group", check_group_list) +@pytest.mark.parametrize("dtype", dtype_list) +def test_hybrid_zero_optim_with_ddp( + zero_parallel, overlap_sync_param, overlap_sync_grad, micro_num, check_group, dtype +): + ctx = mp.get_context("spawn") + with ctx.Pool(processes=8) as pool: + pool.map( + exam_hybrid_zero_optim_with_ddp, + [ + [rank, 8, zero_parallel, overlap_sync_param, overlap_sync_grad, micro_num, check_group, dtype] + for rank in range(8) + ], + ) + pool.close() + pool.join() + + +@pytest.mark.parametrize("zero_parallel", zero_parallel_check_list) +@pytest.mark.parametrize("check_group", check_group_list) +@pytest.mark.parametrize("dtype", dtype_list) +def test_hybrid_zero_optim_with_ckpt_load_save(zero_parallel, check_group, dtype): + ctx = mp.get_context("spawn") + with ctx.Pool(processes=8) as pool: + pool.map( + exam_hybrid_zero_optim_with_ckpt_load_save, + [[rank, 8, zero_parallel, check_group, dtype] for rank in range(8)], + ) + pool.close() + pool.join() + + +if __name__ == "__main__": + pytest.main(["-s", "-q", "test_optimizer.py"]) From bfefc4ea3c321afa25d6c4c4591f16f62c3d772c Mon Sep 17 00:00:00 2001 From: kkscilife <126147887+kkscilife@users.noreply.github.com> Date: Tue, 19 Sep 2023 14:52:32 +0800 Subject: [PATCH 6/6] test(ci_scripts): move ci env (#317) * change partition and runner label * change rm action to mv * use spot * use rsync to move test files * remove * * remove * * change into llm_s partition --------- Co-authored-by: wangmengke --- .github/workflows/demo_in_readme.yaml | 24 +++++++++++++----------- ci_scripts/common/variables.sh | 1 + ci_scripts/data/tokenizer_alpaca.sh | 7 ++++--- ci_scripts/data/tokenizer_chinese.sh | 11 ++++++----- ci_scripts/model/convert_to_hf.sh | 3 ++- ci_scripts/train/load_ckpt.sh | 15 ++++++++++----- ci_scripts/train/slurm_train.sh | 7 +++++-- ci_scripts/train/torchrun.sh | 11 +++++++---- 8 files changed, 48 insertions(+), 31 deletions(-) diff --git a/.github/workflows/demo_in_readme.yaml b/.github/workflows/demo_in_readme.yaml index 7a330ed..a3d4cd9 100644 --- a/.github/workflows/demo_in_readme.yaml +++ b/.github/workflows/demo_in_readme.yaml @@ -9,11 +9,11 @@ on: - "**.md" env: WORKSPACE_PREFIX: $(echo $GITHUB_WORKSPACE |cut -d '/' -f 1-4) - SLURM_PARTITION: llm + SLURM_PARTITION: llm_s jobs: check-requirements: - runs-on: [lmtest] + runs-on: [t_cluster] steps: - name: mask env run: | @@ -37,7 +37,7 @@ jobs: dataset-preparation: if: ${{ always() }} needs: check-requirements - runs-on: [lmtest] + runs-on: [t_cluster] steps: - name: mask env run: | @@ -57,7 +57,7 @@ jobs: train: if: ${{ always() }} needs: check-requirements - runs-on: [lmtest] + runs-on: [t_cluster] timeout-minutes: 30 steps: - name: mask env @@ -83,18 +83,19 @@ jobs: source activate internlm-env-test export PYTHONPATH=$PWD:$PYTHONPATH sh ./ci_scripts/train/load_ckpt.sh 7B_load_new_ckpt ${GITHUB_RUN_ID}-${GITHUB_JOB} - rm -rf $GITHUB_WORKSPACE/llm_ckpts + rsync -av --remove-source-files $GITHUB_WORKSPACE/llm_ckpts ${{env.WORKSPACE_PREFIX}}/ci_clean_bak - name: torchrun-train run: | source activate internlm-env-test sh ./ci_scripts/train/torchrun.sh ${GITHUB_RUN_ID}-${GITHUB_JOB} - rm -rf $GITHUB_WORKSPACE/llm_ckpts + rsync -av --remove-source-files $GITHUB_WORKSPACE/llm_ckpts ${{env.WORKSPACE_PREFIX}}/ci_clean_bak convert-model-then-load: if: ${{ always() }} needs: check-requirements - runs-on: [lmtest] + runs-on: [t_cluster] + timeout-minutes: 15 steps: - name: mask env run: | @@ -107,13 +108,14 @@ jobs: export PYTHONPATH=$PWD:$PYTHONPATH sh ./ci_scripts/model/convert_to_hf.sh cd ./hf_ckpt - srun -p ${SLURM_PARTITION} --job-name=${GITHUB_RUN_ID}-${GITHUB_JOB} --gpus-per-task=2 python ../ci_scripts/model/loaded_as_transformer.py + srun -p ${SLURM_PARTITION} --quotatype=spot --job-name=${GITHUB_RUN_ID}-${GITHUB_JOB} --gpus-per-task=2 python ../ci_scripts/model/loaded_as_transformer.py cd .. - rm -rf $GITHUB_WORKSPACE/hf_ckpt + rsync -av --remove-source-files $GITHUB_WORKSPACE/hf_ckpt ${{env.WORKSPACE_PREFIX}}/ci_clean_bak load-chat-model-in-hf: if: ${{ always() }} needs: check-requirements - runs-on: [lmtest] + runs-on: [t_cluster] + timeout-minutes: 15 steps: - name: mask env run: | @@ -123,4 +125,4 @@ jobs: - name: chat-model-in-hf run: | source activate internlm-env-test - srun -p ${SLURM_PARTITION} --job-name=${GITHUB_RUN_ID}-${GITHUB_JOB} --gpus-per-task=2 python ./ci_scripts/model/demo_load_7B_chat_model.py + srun -p ${SLURM_PARTITION} --quotatype=spot --job-name=${GITHUB_RUN_ID}-${GITHUB_JOB} --gpus-per-task=2 python ./ci_scripts/model/demo_load_7B_chat_model.py diff --git a/ci_scripts/common/variables.sh b/ci_scripts/common/variables.sh index cc1b0e0..077fee4 100644 --- a/ci_scripts/common/variables.sh +++ b/ci_scripts/common/variables.sh @@ -1,3 +1,4 @@ #!/bin/bash readonly DATA_VOLUME=$(echo $GITHUB_WORKSPACE | cut -d '/' -f 1-4)/data +readonly CLEAN_PATH=$(echo $GITHUB_WORKSPACE | cut -d '/' -f 1-4)/ci_clean_bak diff --git a/ci_scripts/data/tokenizer_alpaca.sh b/ci_scripts/data/tokenizer_alpaca.sh index 456921c..db43d80 100644 --- a/ci_scripts/data/tokenizer_alpaca.sh +++ b/ci_scripts/data/tokenizer_alpaca.sh @@ -3,6 +3,7 @@ set -x source ./ci_scripts/common/variables.sh [[ -n ${DATA_VOLUME} ]] || { echo "should set DATA_VOLUME first before ci, exit."; exit 1; } +[[ -n ${CLEAN_PATH} ]] || { echo "should set CLEAN_PATH first before ci, exit."; exit 1; } readonly SRC_DATASET_META=${DATA_VOLUME}/lm_data/alpaca_data/alpaca_data.json readonly RESULTS=${DATA_VOLUME}/lm_data/alpaca_data/result @@ -19,7 +20,7 @@ source ./ci_scripts/common/basic_func.sh echo "start to test alpaca_tokenizer.py." if [[ -d ${RESULTS} ]]; then - if ! rm -rf ${RESULTS}/*; then + if ! rsync -av --remove-source-files ${RESULTS} ${CLEAN_PATH}; then echo "cleaning test data in ${RESULTS} failed, exit." exit 1 fi @@ -41,8 +42,8 @@ for file in ${file_list[@]}; do fi done -# clean the test files. -if ! rm -rf ${RESULTS}/*; then +# move the test files. +if ! rsync -av --remove-source-files ${RESULTS} ${CLEAN_PATH}; then echo "cleaning test data in ${RESULTS} failed." exit_code=$(($exit_code + 1)) fi diff --git a/ci_scripts/data/tokenizer_chinese.sh b/ci_scripts/data/tokenizer_chinese.sh index 6b68df8..81a5198 100644 --- a/ci_scripts/data/tokenizer_chinese.sh +++ b/ci_scripts/data/tokenizer_chinese.sh @@ -2,7 +2,8 @@ set -x source ./ci_scripts/common/variables.sh -[[ -n ${DATA_VOLUME} ]] || { echo "should set DATA_VOLUME first before ci."; exit 1; } +[[ -n ${DATA_VOLUME} ]] || { echo "should set DATA_VOLUME first before ci, exit."; exit 1; } +[[ -n ${CLEAN_PATH} ]] || { echo "should set CLEAN_PATH first before ci, exit."; exit 1; } readonly DATA=${DATA_VOLUME}/lm_data/cn_data/raw_data.txt readonly RESULT=${DATA_VOLUME}/lm_data/cn_data/result.bin @@ -16,13 +17,13 @@ echo "start to test tokenizer.py." num=$(num_files "${RESULTS}") if [[ ${num} -gt 0 ]]; then - if ! rm -rf ${RESULTS}; then + if ! rsync -av --remove-source-files ${RESULTS} ${CLEAN_PATH}; then echo "cleaning test data ${RESULTS} failed, exit." exit 1 fi fi -srun -p ${SLURM_PARTITION} --job-name=$1 --gpus-per-task=1 python tools/tokenizer.py --text_input_path ${DATA} --bin_output_path ${RESULT} +srun -p ${SLURM_PARTITION} --quotatype=spot --job-name=$1 --gpus-per-task=1 python tools/tokenizer.py --text_input_path ${DATA} --bin_output_path ${RESULT} [[ $? -ne 0 ]] && { echo "test tokenizer.py failed."; exit_code=$(($exit_code + 1)); } file_list=($RESULT $RESULT_META) @@ -33,8 +34,8 @@ for file in ${file_list[@]}; do fi done -# clean the test files. -if ! rm -rf ${RESULTS}/*; then +# move the test files. +if ! rsync -av --remove-source-files ${RESULTS} ${CLEAN_PATH}; then echo "cleaning cached file in ${RESULTS} failed." exit_code=$(($exit_code + 1)) fi diff --git a/ci_scripts/model/convert_to_hf.sh b/ci_scripts/model/convert_to_hf.sh index 7d6536b..d1af389 100644 --- a/ci_scripts/model/convert_to_hf.sh +++ b/ci_scripts/model/convert_to_hf.sh @@ -4,6 +4,7 @@ set -x source ./ci_scripts/common/variables.sh [[ -n ${DATA_VOLUME} ]] || { echo "should set DATA_VOLUME first before ci, exit."; exit 1; } [[ -n ${GITHUB_WORKSPACE} ]] || { echo "should set GITHUB_WORKSPACE first before ci, exit."; exit 1; } +[[ -n ${CLEAN_PATH} ]] || { echo "should set CLEAN_PATH first before ci, exit."; exit 1; } readonly CKPTS_INPUT="${DATA_VOLUME}/lm_data/alpaca_data/llm_ckpts/20" readonly CKPTS_OUTPUT="${GITHUB_WORKSPACE}/hf_ckpt" @@ -18,7 +19,7 @@ source ./ci_scripts/common/basic_func.sh echo "start to test convert2hf.py." if [[ -d ${CKPTS_OUTPUT} ]]; then - if ! rm -rf ${CKPTS_OUTPUT}/*; then + if ! rsync -av --remove-source-files ${CKPTS_OUTPUT}/* ${CLEAN_PATH}; then echo "cleaning cached file in ${CKPTS_OUTPUT} failed, exit." exit 1 fi diff --git a/ci_scripts/train/load_ckpt.sh b/ci_scripts/train/load_ckpt.sh index 413dba4..06c6c1e 100644 --- a/ci_scripts/train/load_ckpt.sh +++ b/ci_scripts/train/load_ckpt.sh @@ -1,7 +1,10 @@ #!/bin/bash set -x +source ./ci_scripts/common/variables.sh [[ -n ${GITHUB_WORKSPACE} ]] || { echo "should set GITHUB_WORKSPACE first before ci, exit."; exit 1; } +[[ -n ${CLEAN_PATH} ]] || { echo "should set CLEAN_PATH first before ci, exit."; exit 1; } + readonly CKPTS_PATH="$GITHUB_WORKSPACE/llm_ckpts" readonly CKPTS40_PATH="$GITHUB_WORKSPACE/llm_ckpts/40" readonly CKPTS40_OUTPUT="${CKPTS40_PATH}/*.pt" @@ -19,7 +22,7 @@ if [[ ! -f ${file} ]]; then exit_code=$(($exit_code + 1)) fi -srun -p ${SLURM_PARTITION} --exclusive --job-name=$2 -n 8 --ntasks-per-node=8 --gpus-per-task=1 python train.py --config ${file} +srun -p ${SLURM_PARTITION} --exclusive --quotatype=spot --job-name=$2 -n 8 --ntasks-per-node=8 --gpus-per-task=1 python train.py --config ${file} [[ $? -ne 0 ]] && { echo "test slurm training failed."; exit_code=$(($exit_code + 1)); } @@ -29,10 +32,12 @@ if [[ ${num} -ne ${expected_num} ]]; then exit_code=$(($exit_code + 1)) fi -# clean the test files. -if ! rm -rf ${CKPTS_PATH}/*; then - echo "cleaning cached file in ${CKPTS_PATH} failed." - exit_code=$(($exit_code + 1)) +# move the test files. +if [[ -d ${CKPTS_PATH} ]]; then + if ! rsync -av --remove-source-files ${CKPTS_PATH} ${CLEAN_PATH}; then + echo "cleaning cached file in ${CKPTS_PATH} failed." + exit_code=$(($exit_code + 1)) + fi fi exit $exit_code diff --git a/ci_scripts/train/slurm_train.sh b/ci_scripts/train/slurm_train.sh index 19d7c9b..3871fc4 100644 --- a/ci_scripts/train/slurm_train.sh +++ b/ci_scripts/train/slurm_train.sh @@ -1,7 +1,10 @@ #!/bin/bash set -x +source ./ci_scripts/common/variables.sh [[ -n ${GITHUB_WORKSPACE} ]] || { echo "should set GITHUB_WORKSPACE first before ci, exit."; exit 1; } +[[ -n ${CLEAN_PATH} ]] || { echo "should set CLEAN_PATH first before ci, exit."; exit 1; } + readonly CKPTS_PATH="$GITHUB_WORKSPACE/llm_ckpts" readonly CKPTS20_PATH="$GITHUB_WORKSPACE/llm_ckpts/20" readonly CKPTS20_OUTPUT="${CKPTS20_PATH}/*.pt" @@ -13,13 +16,13 @@ source ./ci_scripts/common/basic_func.sh echo "start to test slurm training." if [[ -d ${CKPTS20_PATH} ]]; then - if ! rm -rf ${CKPTS20_PATH}/*; then + if ! rsync -av --remove-source-files ${CKPTS20_PATH} ${CLEAN_PATH}; then echo "cleaning cached file in ${CKPTS20_PATH} failed, exit." exit 1 fi fi -srun -p ${SLURM_PARTITION} --exclusive --job-name=$1 -n 8 --ntasks-per-node=8 --gpus-per-task=1 python train.py --config ./ci_scripts/train/ci_7B_sft.py +srun -p ${SLURM_PARTITION} --exclusive --quotatype=spot --job-name=$1 -n 8 --ntasks-per-node=8 --gpus-per-task=1 python train.py --config ./ci_scripts/train/ci_7B_sft.py [[ $? -ne 0 ]] && { echo "test slurm training failed."; exit_code=$(($exit_code + 1)); } num=$(num_files "${CKPTS20_OUTPUT}") diff --git a/ci_scripts/train/torchrun.sh b/ci_scripts/train/torchrun.sh index 8870761..29ed54f 100644 --- a/ci_scripts/train/torchrun.sh +++ b/ci_scripts/train/torchrun.sh @@ -1,7 +1,10 @@ #!/bin/bash set -x +source ./ci_scripts/common/variables.sh [[ -n ${GITHUB_WORKSPACE} ]] || { echo "should set GITHUB_WORKSPACE first before ci, exit."; exit 1; } +[[ -n ${CLEAN_PATH} ]] || { echo "should set CLEAN_PATH first before ci, exit."; exit 1; } + readonly CKPTS_PATH="$GITHUB_WORKSPACE/llm_ckpts" readonly CKPTS20_PATH="$GITHUB_WORKSPACE/llm_ckpts/20" readonly CKPTS_OUTPUT="${CKPTS20_PATH}/*.pt" @@ -13,13 +16,13 @@ source ./ci_scripts/common/basic_func.sh echo "start to test torch training." if [[ -d ${CKPTS20_PATH} ]]; then - if ! rm -rf ${CKPTS20_PATH}/*; then + if ! rsync -av --remove-source-files ${CKPTS20_PATH} ${CLEAN_PATH}; then echo "cleaning cached file in ${CKPTS20_PATH} failed, exit." exit 1 fi fi -srun -p ${SLURM_PARTITION} --exclusive --job-name=$1 -N 1 torchrun --nnodes=1 --nproc_per_node=8 --master_port=29501 train.py --config ./ci_scripts/train/ci_7B_sft.py --launcher torch +srun -p ${SLURM_PARTITION} --exclusive --quotatype=spot --job-name=$1 -N 1 torchrun --nnodes=1 --nproc_per_node=8 --master_port=29501 train.py --config ./ci_scripts/train/ci_7B_sft.py --launcher torch [[ $? -ne 0 ]] && { echo "test torch training failed."; exit_code=$(($exit_code + 1)); } num=$(num_files "${CKPTS_OUTPUT}") @@ -28,8 +31,8 @@ if [[ ${num} -ne ${expected_num} ]]; then exit_code=$(($exit_code + 1)) fi -# clean the test files. -if ! rm -rf ${CKPTS_PATH}/*; then +# move the test files. +if ! rsync -av --remove-source-files ${CKPTS_PATH}/* ${CLEAN_PATH}; then echo "cleaning cached file in ${CKPTS_PATH} failed." exit_code=$(($exit_code + 1)) fi