From 41ffea01a445ea789aba88bc72ca40c12ddd3d11 Mon Sep 17 00:00:00 2001 From: kazuki Date: Sat, 17 Aug 2024 13:35:33 +0900 Subject: [PATCH] feat: add docs & update into v1.0.0 --- README.md | 212 +++++++++++++++++- images/moe-recipes-logo.webp | Bin 0 -> 80876 bytes requirements.txt | 5 +- src/llama_recipes/arguments.py | 7 + src/llama_recipes/inference/__init__.py | 2 - src/llama_recipes/inference/chat_utils.py | 65 ------ .../inference/checkpoint_converter_fsdp_hf.py | 65 ------ src/llama_recipes/inference/model_utils.py | 30 --- src/llama_recipes/inference/safety_utils.py | 169 -------------- src/llama_recipes/utils/instruction_tuning.py | 86 +++---- tools/inference/inference.py | 39 ---- tools/inference/inference.sh | 33 --- 12 files changed, 264 insertions(+), 449 deletions(-) create mode 100644 images/moe-recipes-logo.webp delete mode 100644 src/llama_recipes/inference/__init__.py delete mode 100644 src/llama_recipes/inference/chat_utils.py delete mode 100644 src/llama_recipes/inference/checkpoint_converter_fsdp_hf.py delete mode 100644 src/llama_recipes/inference/model_utils.py delete mode 100644 src/llama_recipes/inference/safety_utils.py delete mode 100644 tools/inference/inference.py delete mode 100644 tools/inference/inference.sh diff --git a/README.md b/README.md index aec6755..21064d3 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,53 @@ -# MoE Recipes +
+ +moe-recipes +=========================== +

User-friendly tool for seamless continual pre-training of Mixture of Expert Models

+ +moe-recipes + +
+ +moe-recipes is a tool designed to make the continual pre-training of Large Language Models (LLMs) with Mixture of Experts (MoE) architecture easy and efficient. With an intuitive interface and flexible configuration options, researchers and developers can effortlessly manage training on any MoE model or dataset. The tool supports distributed training on large GPU clusters using DeepSpeed as its backend and offers extensive customization, enabling users to leverage cutting-edge techniques with ease. + +What sets moe-recipes apart is its seamless integration with Hugging Face Transformers, allowing you to continue pre-training or perform instruction tuning on MoE models with minimal changes. This means there’s no need to convert checkpoints or deal with complex workflows—just focus on refining your model. + +| Feature | moe-recipes | llm-recipes | +|---------------------------------|-------------|---------------| +| **MoE Support** | ✅ | ❌ | +| **Dense LLM Support** | ❌ | ✅ | +| **Continual Pre-Training** | ✅ | ✅ | +| **Multi-Node Support** | ✅ | ✅ | # Table of Contents -1. [Installation](#installation) +- [Installation](#installation) + - [Multi-node Support](#multi-node-support) + - [FlashAttention](#flashattention) +- [Usage](#usage) + - [MoE Instruction Tuning](#moe-instruction-tuning) + - [MoE Continual Pre-Training](#moe-continual-pre-training) +- [Checkpoint formats](#checkpoint-formats) + - [DeepSpeed format to Hugging Face format](#deepspeed-format-to-hugging-face-format) +- [Inference](#inference) +- [Training Speed and Scalability](#training-speed-and-scalability) +- [Projects Using moe-recipes](#projects-using-moe-recipes) +- [Citation](#citation) ## Installation -To install the package, run the following command: +This package has been tested with Python 3.10 and 3.11. The recommended environment is with CUDA Toolkit 12.1. + +To install the required packages, simply run: ```bash pip install -r requirements.txt ``` +> Note: The requirements.txt assumes that CUDA Toolkit 12.1 is installed on your system. + +### Multi-node Support -If you want to use the library in multi-nodes, you need to install the below packages: +For multi-node support, ensure you have the following dependencies installed: ```bash module load openmpi/4.x.x @@ -22,9 +57,176 @@ pip install mpi4py ### FlashAttention -To install the FlashAttention, run the following command: (GPU is required) +For GPU-accelerated FlashAttention, follow these steps: ```bash pip install ninja packaging wheel pip install flash-attn --no-build-isolation ``` + +## Usage + +### MoE Instruction Tuning + +we experimentally support instruction tuning for MoE models. +we don't fully test the instruction tuning, so please be careful when using it. + +#### 1. **Data Preparation** + +Prepare your data in the below format and save it as a JSONL file: + +```jsonl +{ + "input": [ + { + "role": "user", + "content": "What is the weather like today?" + } + ], + "output": { + "role": "assistant", + "content": "The weather is sunny with a high of 25 degrees." + } +} +``` + +#### 2. **Change Dataset Class** + +Please modify the `Dataset` class in `src/llama_recipes/utils/instruction_tuning.py` to adjust to the model's expected format. +But, almost all the models have chat templates, so you may not need to change the `Dataset` class. + +#### 3. **Indexing** + +To load dataset efficiently, create an index file using the following command: + +```bash +python tools/pre-process/index_dataset.py \ + --data-file-path +``` + +After indexing, `.index_cache` directory will be created in the same directory as the JSONL file. + +#### 4. **Training** + +We does not provide a script for instruction tuning, but we are planning to provide it in the future. + +### MoE Continual Pre-Training + +#### 1. **Data Preparation** + +Prepare your data in the below format and save it as a JSONL file: + +```jsonl +{ + "text": "What is the weather like today?\nThe weather is sunny with a high of 25 degrees." +} +``` + +#### 2. **Tokenize Data** + +Tokenize your data using the tokenizer provided by the model you are using. +For example, to tokenize data for Qwen-2-57A, run the following command: + +```bash +DATASET_DIR=/pat/to/datasets/ +OUTPUT_DIR=/path/datasets/ + +mkdir -p $OUTPUT_DIR + +python megatron_lm/tools/preprocess_data.py \ + --input ${DATASET_DIR}/wiki-base.jsonl \ + --output-prefix ${OUTPUT_DIR}/ja_wiki \ + --tokenizer-type Qwen2Tokenizer \ + --tokenizer-model /path/to/hf-checkpoints/Qwen2-57B-A14B/tokenizer.json \ + --append-eod \ + --workers 64 +``` + +#### 3. **Training** + +We support Mixtral, Qwen-2-MoE, deepseek-moe. +If you want to continually pre-train or instruction tune other models, you should modify `src/llama_recipes/get_models.py` and `src/llama_recipes/get_model_decoder_layer.py`. + +We provide example scripts for continual pre-training for Mixtral-8x7B in `scripts/tsubame/Mixtral-8x7B-VE/mixtral-8x7b.sh`. +You can modify the script to suit your needs. + +## Checkpoint formats + +### DeepSpeed format to Hugging Face format + +You can convert DeepSpeed checkpoints to Hugging Face format in two stages: first, convert the checkpoint to PyTorch format, and then convert the PyTorch checkpoint to Hugging Face format. + +#### 1. **Convert DeepSpeed checkpoint to PyTorch format** + +```bash +ITERATION=2000 +FORMATTED_ITERATION=$(printf "iter_%07d" $ITERATION) + +CHECK_POINT_DIR=/path/Mixtral-8x7b/${FORMATTED_ITERATION} + +python tools/checkpoint-convert/zero_to_fp32.py \ + --checkpoint-dir $CHECK_POINT_DIR \ + --output-file $CHECK_POINT_DIR/model.pt \ + --debug +``` + +#### 2. **Convert PyTorch checkpoint to Hugging Face format** + +```bash + ITERATION=2000 + FORMATTED_ITERATION=$(printf "iter_%07d" $ITERATION) + + CHECK_POINT_PATH=/path/to/checkpoints/Mixtral-8x7b/${FORMATTED_ITERATION}/model.pt + OUTPUT_PATH=/path/to/Mixtral-8x7b/${FORMATTED_ITERATION} + + echo "convert ${CHECK_POINT_PATH} to ${OUTPUT_PATH}" + + mkdir -p $OUTPUT_PATH + + BASE_MODEL_CHECKPOINT=/path/to/Mixtral-8x7B-v0.1 + + python tools/checkpoint-convert/convert_ckpt.py \ + --model $BASE_MODEL_CHECKPOINT \ + --ckpt $CHECK_POINT_PATH \ + --out $OUTPUT_PATH \ + --sequence-length 8192 +``` + +## Inference + +After checkpoint conversion, you can use the Hugging Face Transformers library to load the converted checkpoint and perform inference. + +The following is an example of how to do inference using the converted checkpoint (huggingface format): + +```bash +python tools/inference/inference-mixtral.py \ + --model-path /path/to/converted/iter_0004000 \ + --tokenizer-path /path/to/tokenizer/path \ + --prompt "Tokyo is the capital of" +``` + +## Training Speed and Scalability + +We are currently working on improving the training speed and scalability of moe-recipes. +We will update this section with more information soon. + +## Projects Using moe-recipes + +Below are some of the projects where we have directly used moe-recipes: + +- [Building a Large Japanese Web Corpus for Large Language Models](https://arxiv.org/abs/2404.17733) + +## Citation + +we are current submitting the paper to SC24 workshop, and the citation will be updated soon. + +```bibtex +@software{fujii_moe-recipes_2024, +author = {Kazuki Fujii and Taishi Nakamura and Rio Yokota}, +month = {March}, +title = {{moe-recipes}}, +url = {https://github.com/rioyokotalab/moe-recipes}, +version = {1.0.0}, +year = {2024} +} +``` diff --git a/images/moe-recipes-logo.webp b/images/moe-recipes-logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..0d70267d3c82382c53f36b9449c7c5a46c0be5e5 GIT binary patch literal 80876 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 literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt index 1d19677..de89e2a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,11 +11,10 @@ peft appdirs loralib scipy -py7zr # 圧縮解凍library +py7zr bitsandbytes -fire # argparser +fire -# formatter & linter black flake8 diff --git a/src/llama_recipes/arguments.py b/src/llama_recipes/arguments.py index c6411ce..e94f53e 100644 --- a/src/llama_recipes/arguments.py +++ b/src/llama_recipes/arguments.py @@ -166,6 +166,13 @@ def _add_data_args(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: help='Number of additional vocabulary tokens. They are used for span masking in the T5 model' ) group.add_argument("--dataset-cyclic", action="store_true") + # instruction tuning + group.add_argument( + '--system-prompt-role', type=str, default="system" + ) + group.add_argument( + '--system-prompt-content', type=str, default='あなたは誠実で優秀な日本人のアシスタントです。' + ) return parser diff --git a/src/llama_recipes/inference/__init__.py b/src/llama_recipes/inference/__init__.py deleted file mode 100644 index 54ed04d..0000000 --- a/src/llama_recipes/inference/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. \ No newline at end of file diff --git a/src/llama_recipes/inference/chat_utils.py b/src/llama_recipes/inference/chat_utils.py deleted file mode 100644 index 8d781e3..0000000 --- a/src/llama_recipes/inference/chat_utils.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. - -import json -from typing import List, Literal, TypedDict - - -Role = Literal["user", "assistant"] - - -class Message(TypedDict): - role: Role - content: str - - -Dialog = List[Message] - -B_INST, E_INST = "[INST]", "[/INST]" -B_SYS, E_SYS = "<>\n", "\n<>\n\n" -def format_tokens(dialogs, tokenizer): - prompt_tokens = [] - for dialog in dialogs: - if dialog[0]["role"] == "system": - dialog = [ - { - "role": dialog[1]["role"], - "content": B_SYS - + dialog[0]["content"] - + E_SYS - + dialog[1]["content"], - } - ] + dialog[2:] - assert all([msg["role"] == "user" for msg in dialog[::2]]) and all( - [msg["role"] == "assistant" for msg in dialog[1::2]] - ), ( - "model only supports 'system','user' and 'assistant' roles, " - "starting with user and alternating (u/a/u/a/u...)" - ) - """ - Please verify that your tokenizer support adding "[INST]", "[/INST]" to your inputs. - Here, we are adding it manually. - """ - dialog_tokens: List[int] = sum( - [ - tokenizer.encode( - f"{B_INST} {(prompt['content']).strip()} {E_INST} {(answer['content']).strip()} ", - ) - for prompt, answer in zip(dialog[::2], dialog[1::2]) - ], - [], - ) - assert ( - dialog[-1]["role"] == "user" - ), f"Last message must be from user, got {dialog[-1]['role']}" - dialog_tokens += tokenizer.encode( - f"{B_INST} {(dialog[-1]['content']).strip()} {E_INST}", - ) - prompt_tokens.append(dialog_tokens) - return prompt_tokens - - -def read_dialogs_from_file(file_path): - with open(file_path, 'r') as file: - dialogs = json.load(file) - return dialogs \ No newline at end of file diff --git a/src/llama_recipes/inference/checkpoint_converter_fsdp_hf.py b/src/llama_recipes/inference/checkpoint_converter_fsdp_hf.py deleted file mode 100644 index 175a97c..0000000 --- a/src/llama_recipes/inference/checkpoint_converter_fsdp_hf.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. - -# from accelerate import init_empty_weights, load_checkpoint_and_dispatch - -import fire -import os -import sys -import yaml - -from transformers import LlamaTokenizer - -from llama_recipes.inference.model_utils import load_llama_from_config - -# Get the current file's directory -current_directory = os.path.dirname(os.path.abspath(__file__)) - -# Get the parent directory -parent_directory = os.path.dirname(current_directory) - -# Append the parent directory to sys.path -sys.path.append(parent_directory) -from model_checkpointing import load_sharded_model_single_gpu - -def main( - fsdp_checkpoint_path="", # Path to FSDP Sharded model checkpoints - consolidated_model_path="", # Path to save the HF converted model checkpoints - HF_model_path_or_name="" # Path/ name of the HF model that include config.json and tokenizer_config.json (e.g. meta-llama/Llama-2-7b-chat-hf) - ): - - try: - file_name = 'train_params.yaml' - # Combine the directory and file name to create the full path - train_params_path = os.path.join(fsdp_checkpoint_path, file_name) - # Open the file - with open(train_params_path, 'r') as file: - # Load the YAML data - data = yaml.safe_load(file) - - # Access the 'model_name' field - HF_model_path_or_name = data.get('model_name') - - print(f"Model name: {HF_model_path_or_name}") - except FileNotFoundError: - print(f"The file {train_params_path} does not exist.") - HF_model_path_or_name = input("Please enter the model name: ") - print(f"Model name: {HF_model_path_or_name}") - except Exception as e: - print(f"An error occurred: {e}") - - - #load the HF model definition from config - model_def = load_llama_from_config(HF_model_path_or_name) - print("model is loaded from config") - #load the FSDP sharded checkpoints into the model - model = load_sharded_model_single_gpu(model_def, fsdp_checkpoint_path) - print("model is loaded from FSDP checkpoints") - #loading the tokenizer form the model_path - tokenizer = LlamaTokenizer.from_pretrained(HF_model_path_or_name) - tokenizer.save_pretrained(consolidated_model_path) - #save the FSDP sharded checkpoints in HF format - model.save_pretrained(consolidated_model_path) - print(f"HuggingFace model checkpoints has been saved in {consolidated_model_path}") -if __name__ == "__main__": - fire.Fire(main) diff --git a/src/llama_recipes/inference/model_utils.py b/src/llama_recipes/inference/model_utils.py deleted file mode 100644 index 02785e9..0000000 --- a/src/llama_recipes/inference/model_utils.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# This software may be used and distributed according to the terms of the GNU General Public License version 3. - -from peft import PeftModel -from transformers import LlamaForCausalLM, LlamaConfig - -# Function to load the main model for text generation -def load_model(model_name, quantization): - model = LlamaForCausalLM.from_pretrained( - model_name, - return_dict=True, - load_in_8bit=quantization, - device_map="auto", - low_cpu_mem_usage=True, - ) - return model - - -# Function to load the PeftModel for performance optimization -def load_peft_model(model, peft_model): - peft_model = PeftModel.from_pretrained(model, peft_model) - return peft_model - -# Loading the model from config to load FSDP checkpoints into that -def load_llama_from_config(config_path): - model_config = LlamaConfig.from_pretrained(config_path) - model = LlamaForCausalLM(config=model_config) - return model - - \ No newline at end of file diff --git a/src/llama_recipes/inference/safety_utils.py b/src/llama_recipes/inference/safety_utils.py deleted file mode 100644 index 38a44d4..0000000 --- a/src/llama_recipes/inference/safety_utils.py +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement. - -import os -import torch -import warnings - - -# Class for performing safety checks using AuditNLG library -class AuditNLGSensitiveTopics(object): - def __init__(self): - pass - - def __call__(self, output_text): - try: - from auditnlg.safety.exam import safety_scores - except ImportError as e: - print("Could not import optional dependency: auditnlg\nPlease install manually with:\n pip install auditnlg\nFollowed by:\npip install -r requirements.txt") - raise e - - - data = [{"output": output_text}] - - result = safety_scores(data=data, method="sensitive_topics") - scores = result[1]["all_scores"][0] - is_safe = scores["pred_class"] == "none" - report = "" - if not is_safe: - report += f"Predicted class: {scores['pred_class']}\n" - report += "|" + "|".join(f"{n:^10}" for n in [list(k.keys())[0] for k in scores["class_scores"]]) + "|\n" - report += "|" + "|".join(f"{n:^10.5}" for n in [list(k.values())[0] for k in scores["class_scores"]]) + "|\n" - return "Sensitive Topics", is_safe, report - - -class SalesforceSafetyChecker(object): - def __init__(self): - pass - - def __call__(self, output_text): - from transformers import AutoModelForSeq2SeqLM, AutoTokenizer, AutoConfig - - config = AutoConfig.from_pretrained("Salesforce/safety-flan-t5-base") - tokenizer = AutoTokenizer.from_pretrained("Salesforce/safety-flan-t5-base") - model = AutoModelForSeq2SeqLM.from_pretrained("Salesforce/safety-flan-t5-base", config=config) - model.eval() - - prefix = "Is the field safe or unsafe?" - input_ids = tokenizer(prefix + " " + output_text + " ", return_tensors="pt").input_ids - - if len(input_ids[0]) > 512: - warnings.warn( - "Input length is > 512 token. Safety check result could be incorrect." - ) - - with torch.no_grad(): - outputs = model.generate( - input_ids, - output_scores=True, - return_dict_in_generate=True, - max_new_tokens=20, - ) - - is_safe = tokenizer.decode(outputs.sequences[0], skip_special_tokens=True).split(" ")[0] == "safe" - - report = "" - if not is_safe: - true_false_ids = tokenizer("true false").input_ids[:2] - keys = ["toxicity", "hate", "identity", "violence", "physical", "sexual", "profanity", "biased"] - scores = {} - for k, i in zip(keys, range(3,20,2)): - scores[k] = round(outputs.scores[i][0,true_false_ids].softmax(dim=0)[0].item(), 5) - - report += "|" + "|".join(f"{n:^10}" for n in scores.keys()) + "|\n" - report += "|" + "|".join(f"{n:^10}" for n in scores.values()) + "|\n" - return "Salesforce Content Safety Flan T5 Base", is_safe, report - - - def get_total_length(self, data): - prefix = "Is the field safe or unsafe " - input_sample = " {output} ".format(**data[0]) - - return len(self.tokenizer(prefix + input_sample)["input_ids"]) - - -# Class for performing safety checks using Azure Content Safety service -class AzureSaftyChecker(object): - def __init__(self): - try: - from azure.ai.contentsafety import ContentSafetyClient - from azure.core.credentials import AzureKeyCredential - - key = os.environ["CONTENT_SAFETY_KEY"] - endpoint = os.environ["CONTENT_SAFETY_ENDPOINT"] - except ImportError: - raise Exception( - "Could not import required package azure-ai-contentsafety. Install with: pip install azure-ai-contentsafety" - ) - except KeyError: - raise Exception( - "Environment variables not set. Please set CONTENT_SAFETY_KEY and CONTENT_SAFETY_ENDPOINT." - ) - - self.client = ContentSafetyClient(endpoint, AzureKeyCredential(key)) - - def __call__(self, output_text): - from azure.core.exceptions import HttpResponseError - from azure.ai.contentsafety.models import AnalyzeTextOptions, TextCategory - - print(len(output_text)) - if len(output_text) > 1000: - raise Exception("Input length to safety check is too long (>1000).") - - categories = [ - TextCategory.VIOLENCE, - TextCategory.SELF_HARM, - TextCategory.SEXUAL, - TextCategory.HATE, - ] - - request = AnalyzeTextOptions(text=output_text, categories=categories) - - try: - response = self.client.analyze_text(request) - except HttpResponseError as e: - print("Analyze text failed.") - if e.error: - print(f"Error code: {e.error.code}") - print(f"Error message: {e.error.message}") - raise - print(e) - raise e - - levels = {0: "Safe", 2: "Low", 4: "Medium", 6: "High"} - - severities = [ - getattr(response, c.name.lower() + "_result").severity for c in categories - ] - - DEFAULT_LEVELS = [0, 0, 0, 0] - - is_safe = all([s <= l for s, l in zip(severities, DEFAULT_LEVELS)]) - - report = "" - if not is_safe: - report = "|" + "|".join(f"{c.name:^10}" for c in categories) + "|\n" - report += "|" + "|".join(f"{levels[s]:^10}" for s in severities) + "|\n" - - return "Azure Content Saftey API", is_safe, report - - -# Function to load the PeftModel for performance optimization -# Function to determine which safety checker to use based on the options selected -def get_safety_checker(enable_azure_content_safety, - enable_sensitive_topics, - enable_salesforce_content_safety, - ): - safety_checker = [] - if enable_azure_content_safety: - safety_checker.append(AzureSaftyChecker()) - if enable_sensitive_topics: - safety_checker.append(AuditNLGSensitiveTopics()) - if enable_salesforce_content_safety: - safety_checker.append(SalesforceSafetyChecker()) - return safety_checker - - - - - diff --git a/src/llama_recipes/utils/instruction_tuning.py b/src/llama_recipes/utils/instruction_tuning.py index a843529..1cb73aa 100644 --- a/src/llama_recipes/utils/instruction_tuning.py +++ b/src/llama_recipes/utils/instruction_tuning.py @@ -4,7 +4,8 @@ import numpy as np import torch -from torch.utils.data import Dataset +from torch.utils.data import Dataset, DataLoader +import torch.distributed as torch_distributed from transformers.tokenization_utils import PreTrainedTokenizer from pathlib import Path from llama_recipes.utils.distributed import print_rank_0 @@ -24,6 +25,10 @@ def __init__( self.max_words: int = args.seq_length self.tokenizer = tokenizer + # system prompt + self.system_prompt_role = args.system_prompt_role + self.system_prompt_content = args.system_prompt_content + # index file dataset_dir = Path(self.data_path).parent index_cache_dir = dataset_dir / ".index_cache" @@ -54,60 +59,65 @@ def __getitem__(self, index: int) -> dict[str, torch.Tensor]: exit(1) try: - conversations: dict[str, str | list[dict[str, str]]] = json.loads(line) + conversations: dict[str, list[dict[str, str]] | str] = json.loads(line) except Exception as e: print(f"index={index}, offset={offset}, line={line}, error={e}") exit(1) - SYSTEM_PROMPT = [ - {"role": "system", "text": "あなたは誠実で優秀な日本人のアシスタントです。"} + SYSTEM_PROMPT: list[dict[str, str]] = [ + { + "role": self.system_prompt_role, + "content": self.system_prompt_content, + } ] # chat template - prompt: str = self.tokenizer.apply_chat_template( + prompt = self.tokenizer.apply_chat_template( conversation=SYSTEM_PROMPT + conversations["input"], # type: ignore - tokenize=False + add_generation_prompt=True, + tokenize=True, ) - example: str = prompt + conversations["output"] # type: ignore - encoded_prompt: torch.Tensor = torch.tensor( - self.tokenizer.encode(prompt, add_special_tokens=False), - dtype=torch.int64 - ) - encoded_example: list[int] = self.tokenizer.encode( - example, add_special_tokens=False + example = self.tokenizer.apply_chat_template( + conversation=SYSTEM_PROMPT + conversations["input"] + [ # type: ignore + {"role": "assistant", "content": conversations["output"]} + ], + tokenize=True, ) - encoded_example.append(self.tokenizer.eos_token_id) # type: ignore - encoded_tensor_example: torch.Tensor = torch.tensor(encoded_example, dtype=torch.int64) - - if len(encoded_example) > self.max_words: - print(f"\n\nWARNING: example={example}\n\n") - - padding: int = self.max_words - encoded_tensor_example.shape[0] - if padding > 0: # pad_token_id = 0 (substitute unk_token) - encoded_tensor_example = torch.cat((encoded_tensor_example, torch.zeros(padding, dtype=torch.int64) - 1)) - elif padding < 0: - encoded_tensor_example = encoded_tensor_example[: self.max_words] - - labels = copy.deepcopy(encoded_tensor_example) + tensor_example: torch.Tensor = torch.tensor(example, dtype=torch.int64) + + if len(example) > self.max_words: + print(f"\n\nWARNING: example={self.tokenizer.decode(example)}\n\n") + + padding_length: int = self.max_words - len(example) + eos_token_id: int = self.tokenizer.encode("<|end_of_text|>", add_special_tokens=False)[0] + pad_token_id = eos_token_id + if padding_length > 0: + pad_tensor = torch.full( + (padding_length,), pad_token_id, dtype=torch.int64 + ) + tensor_example = torch.cat((tensor_example, pad_tensor)) + elif padding_length < 0: + tensor_example = tensor_example[: self.max_words] + + labels = copy.deepcopy(tensor_example) # promptの長さ分だけ -1 で埋める -> 損失関数で無視するようになる - labels[: len(encoded_prompt)] = -1 - # 0より大きい(ge)かどうかの真偽値でmaskを作成 - example_mask = encoded_tensor_example.ge(0) + labels[: len(prompt)] = -1 label_mask = labels.ge(0) - if torch.all(label_mask == 0): # len(output) == 0 + if torch.all(label_mask == 0): # 予測部分がない random_index: int = np.random.randint(0, len(self.indexes)) self.__getitem__(random_index) - # ~example_mask -> paddingの部分を 0 で埋める - encoded_tensor_example[~example_mask] = 0 # ~label_mask -> prompt の部分を ignore_index で埋める labels[~label_mask] = IGNORE_INDEX + labels[labels == pad_token_id] = IGNORE_INDEX + # mask out pad token + attention_mask = (tensor_example != pad_token_id).float() return { - "input_ids": encoded_tensor_example, + "input_ids": tensor_example, "labels": labels, - "attention_mask": example_mask.float(), + "attention_mask": attention_mask, } @@ -125,7 +135,7 @@ def get_instruction_tuning_dataloader( tokenizer: PreTrainedTokenizer, data_path: str, train: bool = False, -) -> torch.utils.data.DataLoader: +) -> DataLoader: from llama_recipes.utils.sequence_length_warmup import CustomDistributedSampler from llama_recipes.utils.checkpoint import load_sampler_state_dict @@ -142,8 +152,8 @@ def get_instruction_tuning_dataloader( train_sampler = CustomDistributedSampler( dataset=instruction_dataset, - rank=torch.distributed.get_rank(), - num_replicas=torch.distributed.get_world_size(), + rank=torch_distributed.get_rank(), + num_replicas=torch_distributed.get_world_size(), shuffle=True, seed=args.seed, ) @@ -153,7 +163,7 @@ def get_instruction_tuning_dataloader( set_sampler(sampler=train_sampler) - return torch.utils.data.DataLoader( + return DataLoader( instruction_dataset, batch_size=args.micro_batch_size, sampler=train_sampler, diff --git a/tools/inference/inference.py b/tools/inference/inference.py deleted file mode 100644 index 0828a1b..0000000 --- a/tools/inference/inference.py +++ /dev/null @@ -1,39 +0,0 @@ -import argparse - -import torch - -from transformers import AutoTokenizer, MistralForCausalLM - - -parser = argparse.ArgumentParser(description="Generation") -parser.add_argument("--model-path", type=str) -parser.add_argument("--tokenizer-path", type=str) -parser.add_argument("--prompt", type=str, default=None) -args = parser.parse_args() - - -print(f"Loading model {args.model_path}") - -tokenizer = AutoTokenizer.from_pretrained( - pretrained_model_name_or_path=args.tokenizer_path, -) -model = MistralForCausalLM.from_pretrained( - args.model_path, - device_map="auto", torch_dtype=torch.bfloat16 -) - -input_ids: torch.Tensor = tokenizer.encode( # type: ignore - args.prompt, - add_special_tokens=False, - return_tensors="pt" -) -outputs = model.generate( # type: ignore - input_ids.to(device=model.device), # type: ignore - max_new_tokens=128, - temperature=0.99, - top_p=0.95, - do_sample=True, -) - -generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True) -print(generated_text) diff --git a/tools/inference/inference.sh b/tools/inference/inference.sh deleted file mode 100644 index 33d8c51..0000000 --- a/tools/inference/inference.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -#$ -l rt_AG.small=1 -#$ -l h_rt=1:00:00 -#$ -j y -#$ -o outputs/inference/ -#$ -cwd -# module load -source /etc/profile.d/modules.sh -module load cuda/11.8/11.8.0 -module load cudnn/8.9/8.9.2 -module load nccl/2.16/2.16.2-1 -module load hpcx/2.12 - -set -e - -# swich virtual env -source .env/bin/activate - -# distributed settings -export MASTER_ADDR=$(/usr/sbin/ip a show dev bond0 | grep 'inet ' | awk '{ print $2 }' | cut -d "/" -f 1) -export MASTER_PORT=$((10000 + ($JOB_ID % 50000))) - -echo "MASTER_ADDR=${MASTER_ADDR}" - -python tools/inference/inference.py \ - --model-path /bb/llm/gaf51275/llama/converted-hf-checkpoint/mistral-7B-VE/okazaki-cc/iter_0004000 \ - --tokenizer-path /bb/llm/gaf51275/llama/converted-hf-checkpoint/mistral-7B-VE/okazaki-cc/iter_0004000 \ - --prompt "Tokyo is the capital of Japan." - -python tools/inference/inference.py \ - --model-path /bb/llm/gaf51275/llama/converted-hf-checkpoint/mistral-7B-VE/okazaki-cc/iter_0004000 \ - --tokenizer-path /bb/llm/gaf51275/llama/converted-hf-checkpoint/mistral-7B-VE/okazaki-cc/iter_0004000 \ - --prompt "東京工業大学のキャンパスは"