Repository: yuhuixu1993/qa-lora Branch: main Commit: 91604c71e981 Files: 7 Total size: 58.3 KB Directory structure: gitextract_jckgmwf6/ ├── LICENSE ├── README.md ├── environment.yaml ├── merge.py ├── peft_utils.py ├── qalora.py └── requirements.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 YuhuiXu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # QA-LoRA QA-LoRA has been accepted by ICLR 2024! This repository provides the official PyTorch implementation of [QA-LoRA: Quantization-Aware Low-Rank Adaptation of Large Language Models](https://arxiv.org/pdf/2309.14717.pdf).
QA-LoRA is easily implemented with a few lines of code, and it equips the original LoRA with two-fold abilities: (i) during fine-tuning, the LLM's weights are quantized (e.g., into INT4) to reduce time and memory usage; (ii) after fine-tuning, the LLM and auxiliary weights are naturally integrated into a quantized model without loss of accuracy. ## Todo list Fix the conflict with the newest Auto-gptq version. ## Installation ```bash conda create -n qalora python=3.8 conda activate qalora conda install pytorch==2.2.0 torchvision==0.17.0 torchaudio==2.2.0 pytorch-cuda=12.1 -c pytorch -c nvidia git clone -b v0.3.0 https://github.com/PanQiWei/AutoGPTQ.git && cd AutoGPTQ pip install . cd .. pip install bitsandbytes pip install -r requirements.txt pip install protobuf==3.20.* ``` Change the `peft_utils.py` in your own auto-gptq path(python path/auto_gptq/utils/peft_utils.py) with the new one. For the users of [GPTQLORA](https://github.com/qwopqwop200/gptqlora), you only need to change the `peft_utils.py` file. ## Quantization We use [GPTQ](https://github.com/qwopqwop200/GPTQ-for-LLaMa) for quantization. bits=4, group-size=32, act-order=False If you change the group-size, you need to change the group_size in `peft_utils.py` and `merge.py` accordingly. ## Training ```bash python qalora.py --model_path ``` The file structure of the model checkpoint is as follows: ``` config.json llama7b-4bit-32g.bin special_tokens_map.json tokenizer_config.json generation_config.json quantize_config.json tokenizer.model ``` ## Merge Note that our trained LoRA modules can be perfectly merged into the quantized model. We offer a simple merged script in this repo. ## Notice ### About the implementations There are two kinds of implementations of the dimention reduction(x from D_in to D_in//L). Both are mathematical equivalent. #### The first one(this repo) Adopt avgpooling operation. But the weights of adapters will be divided by D_in//L during merge(refer to `merge.py`). ```bash adapter_result = (lora_B(lora_A(lora_dropout(self.qa_pool(x)))) * scale).type_as(result) model[tmp_key+'.qzeros'] -= (lora['base_model.model.'+tmp_key+'.lora_B.weight'] @ lora['base_model.model.'+tmp_key+'.lora_A.weight']).t() * scale / group_size / model[tmp_key+'.scales'] ``` #### The second one Utilize sum operation. The adapters do not need to be divided during merge) ```bash adapter_result = (lora_B(lora_A(lora_dropout(self.qa_pool(x) * group_size))) * scale).type_as(result) model[tmp_key+'.qzeros'] -= (lora['base_model.model.'+tmp_key+'.lora_B.weight'] @ lora['base_model.model.'+tmp_key+'.lora_A.weight']).t() * scale / model[tmp_key+'.scales'] ``` ### About the quantization Some GPTQ implementation such as [GPTQ-for-llama](https://github.com/qwopqwop200/GPTQ-for-LLaMa) further compress the zeros into qzeros. You need to decode the qzeros first and restore fp16 format zeros. ## Acknowledgements Our code is based on [QLoRA](https://github.com/artidoro/qlora), [GPTQLORA](https://github.com/qwopqwop200/gptqlora), [Auto-GPTQ](https://github.com/PanQiWei/AutoGPTQ/tree/main) ================================================ FILE: environment.yaml ================================================ name: alpaca channels: - defaults dependencies: - _libgcc_mutex=0.1=main - _openmp_mutex=5.1=1_gnu - ca-certificates=2023.05.30=h06a4308_0 - ld_impl_linux-64=2.38=h1181459_1 - libffi=3.4.4=h6a678d5_0 - libgcc-ng=11.2.0=h1234567_1 - libgomp=11.2.0=h1234567_1 - libstdcxx-ng=11.2.0=h1234567_1 - ncurses=6.4=h6a678d5_0 - openssl=3.0.9=h7f8727e_0 - pip=23.1.2=py38h06a4308_0 - python=3.8.16=h955ad1f_4 - readline=8.2=h5eee18b_0 - setuptools=67.8.0=py38h06a4308_0 - sqlite=3.41.2=h5eee18b_0 - tk=8.6.12=h1ccaba5_0 - wheel=0.38.4=py38h06a4308_0 - xz=5.4.2=h5eee18b_0 - zlib=1.2.13=h5eee18b_0 - pip: - absl-py==1.2.0 - accelerate==0.21.0.dev0 - addict==2.4.0 - aiohttp==3.8.4 - aiosignal==1.3.1 - appdirs==1.4.4 - async-timeout==4.0.2 - attrs==22.2.0 - auto-gptq==0.3.0.dev0 - bert-score==0.3.13 - bitsandbytes==0.39.0 - certifi==2022.9.24 - charset-normalizer==3.0.1 - click==8.1.3 - cmake==3.26.3 - colorama==0.4.5 - contourpy==1.0.6 - cycler==0.11.0 - datasets==2.12.0 - dill==0.3.5.1 - evaluate==0.4.0 - fonttools==4.39.4 - frozenlist==1.3.3 - fsspec==2022.11.0 - gitdb==4.0.10 - gitpython==3.1.31 - huggingface-hub==0.14.1 - importlib-resources==5.12.0 - jinja2==3.1.2 - joblib==1.2.0 - lazy-import==0.2.2 - lit==16.0.5 - lxml==4.8.0 - markupsafe==2.1.2 - matplotlib==3.7.1 - mpmath==1.2.1 - multidict==6.0.2 - multiprocess==0.70.12.2 - networkx==2.8.8 - ninja==1.11.1 - nltk==3.8.1 - numpy==1.24.2 - packaging==23.1 - pandas==2.0.0 - pathlib2==2.3.7.post1 - pathtools==0.1.2 - peft==0.4.0.dev0 - pillow==9.3.0 - protobuf==3.20.2 - psutil==5.9.4 - pyarrow==10.0.1 - pyparsing==3.0.9 - python-dateutil==2.8.2 - pytz==2022.6 - pyyaml==6.0 - requests==2.31.0 - responses==0.18.0 - rouge==1.0.0 - rouge-score==0.1.2 - safetensors==0.3.1 - scikit-learn==1.2.2 - scipy==1.10.1 - sentencepiece==0.1.99 - sentry-sdk==1.24.0 - setproctitle==1.3.2 - six==1.16.0 - smmap==5.0.0 - sympy==1.11.1 - terminaltables==3.1.10 - threadpoolctl==3.1.0 - tokenizers==0.13.3 - torch==2.0.0+cu117 - tqdm==4.63.1 - transformers==4.31.0.dev0 - triton==2.0.0 - typing-extensions==4.6.2 - tzdata==2022.7 - urllib3==1.26.16 - wandb==0.15.2 - xformers==0.0.20 - xxhash==3.0.0 - yapf==0.32.0 - yarl==1.8.1 - zipp==3.15.0 ================================================ FILE: merge.py ================================================ import torch model_path = 'path of the quantized model' lora_path = 'path of the saved LoRA adapters' merged_path = 'target path of the merged model' scale = 16 /64 group_size = 32 model = torch.load(model_path, map_location='cpu') lora = torch.load(lora_path, map_location='cpu') tmp_keys = [key[17:-14] for key in lora.keys() if 'lora_A' in key] for tmp_key in tmp_keys: model[tmp_key+'.qzeros'] -= (lora['base_model.model.'+tmp_key+'.lora_B.weight'] @ lora['base_model.model.'+tmp_key+'.lora_A.weight']).t() * scale / group_size /model[tmp_key+'.scales'] torch.save(model, merged_path) ================================================ FILE: peft_utils.py ================================================ import warnings import re from contextlib import contextmanager from dataclasses import asdict from enum import Enum from typing import List, Optional import torch from peft import get_peft_model, PeftConfig, PeftModel, PeftType from peft.peft_model import PEFT_TYPE_TO_MODEL_MAPPING from peft.tuners.lora import LoraConfig, LoraLayer, LoraModel, Embedding from peft.tuners.adalora import AdaLoraConfig, AdaLoraLayer, AdaLoraModel from peft.mapping import PEFT_TYPE_TO_CONFIG_MAPPING from peft.utils.other import _get_submodules from ..modeling._base import BaseGPTQForCausalLM group_size = 32 # quantization group_size class GPTQLoraConfig(LoraConfig): injected_fused_attention: bool = False injected_fused_mlp: bool = False class GPTQLoraLinear(torch.nn.Linear, LoraLayer): def __init__( self, adapter_name: str, linear_module: torch.nn.Linear, r: int = 0, lora_alpha: int = 1, lora_dropout: float = 0.0, fan_in_fan_out: bool = False, # Set this to True if the layer to replace stores weight like (fan_in, fan_out) **kwargs, ): init_lora_weights = kwargs.pop("init_lora_weights", True) torch.nn.Linear.__init__(self, linear_module.in_features, linear_module.out_features) LoraLayer.__init__(self, linear_module.in_features//group_size, linear_module.out_features) self.linear_module = linear_module self.weight.requires_grad = False self.weight = self.linear_module.weight self.bias = self.linear_module.bias self.fan_in_fan_out = fan_in_fan_out if fan_in_fan_out: self.weight.data = self.weight.data.T self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights) self.active_adapter = adapter_name self.qa_pool = torch.nn.AvgPool1d(group_size) # using pooling layer to conduct sum operation def reset_lora_parameters(self, adapter_name): if adapter_name in self.lora_A.keys(): torch.nn.init.xavier_uniform_(self.lora_A[adapter_name].weight) torch.nn.init.zeros_(self.lora_B[adapter_name].weight) def merge(self): raise NotImplementedError("gptq model not support merge lora adapter") def unmerge(self): raise NotImplementedError("gptq model not support unmerge lora adapter") def forward(self, x: torch.Tensor): previous_dtype = x.dtype if self.active_adapter not in self.lora_A.keys(): return self.linear_module(x) if self.disable_adapters: if self.r[self.active_adapter] > 0 and self.merged: self.unmerge() result = self.linear_module(x) elif self.r[self.active_adapter] > 0 and not self.merged: result = self.linear_module(x) lora_B = self.lora_B[self.active_adapter] lora_A = self.lora_A[self.active_adapter] lora_dropout = self.lora_dropout[self.active_adapter] scale = self.scaling[self.active_adapter] x = x.type_as(lora_A.weight.data) adapter_result = (lora_B(lora_A(lora_dropout(self.qa_pool(x)))) * scale).type_as(result) result += adapter_result else: result = self.linear_module(x) result = result.to(previous_dtype) return result class GPTQLoraModel(LoraModel): def _find_and_replace(self, adapter_name): lora_config = self.peft_config[adapter_name] is_target_modules_in_base_model = False kwargs = { "r": lora_config.r, "lora_alpha": lora_config.lora_alpha, "lora_dropout": lora_config.lora_dropout, "fan_in_fan_out": lora_config.fan_in_fan_out, "init_lora_weights": lora_config.init_lora_weights, } key_list = [key for key, _ in self.model.named_modules()] for key in key_list: if isinstance(lora_config.target_modules, str): target_module_found = re.fullmatch(lora_config.target_modules, key) else: target_module_found = any(key.endswith(target_key) for target_key in lora_config.target_modules) if target_module_found: if not is_target_modules_in_base_model: is_target_modules_in_base_model = True parent, target, target_name = _get_submodules(self.model, key) bias = False if hasattr(target, "bias"): bias = target.bias is not None if isinstance(target, LoraLayer): target.update_layer( adapter_name, lora_config.r, lora_config.lora_alpha, lora_config.lora_dropout, lora_config.init_lora_weights, ) else: if isinstance(target, torch.nn.Embedding): embedding_kwargs = kwargs.copy() embedding_kwargs.pop("fan_in_fan_out", None) in_features, out_features = target.num_embeddings, target.embedding_dim new_module = Embedding(adapter_name, in_features, out_features, **embedding_kwargs) else: if isinstance(target, torch.nn.Linear): if kwargs["fan_in_fan_out"]: warnings.warn( "fan_in_fan_out is set to True but the target module is `torch.nn.Linear`. " "Setting fan_in_fan_out to False." ) kwargs["fan_in_fan_out"] = lora_config.fan_in_fan_out = False else: raise ValueError( f"Target module {target} is not supported. " f"Currently, only `torch.nn.Linear` and its subclasses are supported." ) new_module = GPTQLoraLinear(adapter_name, target, **kwargs) self._replace_module(parent, target_name, new_module, target) if not is_target_modules_in_base_model: raise ValueError( f"Target modules {lora_config.target_modules} not found in the base model. " f"Please check the target modules and try again." ) def _replace_module(self, parent_module, child_name, new_module, old_module): setattr(parent_module, child_name, new_module) if not isinstance(new_module, GPTQLoraLinear): new_module.weight = old_module.weight if hasattr(old_module, "bias"): if old_module.bias is not None: new_module.bias = old_module.bias if getattr(old_module, "state", None) is not None: new_module.state = old_module.state new_module.to(old_module.weight.device) # dispatch to correct device for name, module in new_module.named_modules(): if "lora_" in name: module.to(old_module.weight.device) def merge_adapter(self): raise NotImplementedError("gptq model not support merge ada lora adapter") def unmerge_adapter(self): raise NotImplementedError("gptq model not support unmerge ada lora adapter") def merge_and_unload(self): raise NotImplementedError("gptq model not support merge and unload") class GPTQAdaLoraConfig(AdaLoraConfig): injected_fused_attention: bool = False injected_fused_mlp: bool = False class GPTQSVDLinear(torch.nn.Linear, AdaLoraLayer): def __init__( self, adapter_name: str, linear_module: torch.nn.Linear, r: int = 0, lora_alpha: int = 1, lora_dropout: float = 0.0, fan_in_fan_out: bool = False, # Set this to True if the layer to replace stores weight like (fan_in, fan_out) **kwargs, ): init_lora_weights = kwargs.pop("init_lora_weights", True) torch.nn.Linear.__init__(self, linear_module.in_features, linear_module.out_features) AdaLoraLayer.__init__(self, linear_module.in_features, linear_module.out_features) self.linear_module = linear_module self.weight.requires_grad = False self.weight = self.linear_module.weight self.bias = self.linear_module.bias self.fan_in_fan_out = fan_in_fan_out if fan_in_fan_out: self.weight.data = self.weight.data.T self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights) self.active_adapter = adapter_name def merge(self): raise NotImplementedError("gptq model not support merge lora adapter") def unmerge(self): raise NotImplementedError("gptq model not support unmerge lora adapter") def forward(self, x: torch.Tensor): if self.active_adapter not in self.lora_A.keys(): return self.linear_module(x) if self.disable_adapters: if self.r[self.active_adapter] > 0 and self.merged: self.unmerge() result = self.linear_module(x) elif self.r[self.active_adapter] > 0 and not self.merged: result = self.linear_module(x) result += ( ( self.lora_dropout[self.active_adapter](x) @ (self.lora_A[self.active_adapter] * self.lora_E[self.active_adapter]).T @ self.lora_B[self.active_adapter].T ) * self.scaling[self.active_adapter] / (self.ranknum[self.active_adapter] + 1e-5) ) else: result = self.linear_module(x) return result class GPTQAdaLoraModel(AdaLoraModel): def _find_and_replace(self, adapter_name): lora_config = self.peft_config[adapter_name] is_target_modules_in_base_model = False kwargs = { "r": lora_config.init_r, "lora_alpha": lora_config.lora_alpha, "lora_dropout": lora_config.lora_dropout, "fan_in_fan_out": lora_config.fan_in_fan_out, "init_lora_weights": lora_config.init_lora_weights, } key_list = [key for key, _ in self.model.named_modules()] for key in key_list: if isinstance(lora_config.target_modules, str): target_module_found = re.fullmatch(lora_config.target_modules, key) else: target_module_found = any(key.endswith(target_key) for target_key in lora_config.target_modules) if target_module_found: if not is_target_modules_in_base_model: is_target_modules_in_base_model = True parent, target, target_name = _get_submodules(self.model, key) bias = target.bias is not None if isinstance(target, LoraLayer): target.update_layer( adapter_name, lora_config.init_r, lora_config.lora_alpha, lora_config.lora_dropout, lora_config.init_lora_weights, ) else: if isinstance(target, torch.nn.Linear): in_features, out_features = target.in_features, target.out_features if kwargs["fan_in_fan_out"]: warnings.warn( "fan_in_fan_out is set to True but the target module is `torch.nn.Linear`. " "Setting fan_in_fan_out to False." ) kwargs["fan_in_fan_out"] = lora_config.fan_in_fan_out = False else: raise ValueError( f"Target module {target} is not supported. " f"Currently, only `torch.nn.Linear` and its subclasses are supported." ) new_module = GPTQSVDLinear(adapter_name, target, **kwargs) self._replace_module(parent, target_name, new_module, target) if not is_target_modules_in_base_model: raise ValueError( f"Target modules {lora_config.target_modules} not found in the base model. " f"Please check the target modules and try again." ) def _replace_module(self, parent_module, child_name, new_module, old_module): setattr(parent_module, child_name, new_module) # dispatch to correct device for name, module in new_module.named_modules(): if "lora_" in name: module.to(old_module.weight.device) def merge_adapter(self): raise NotImplementedError("gptq model not support merge ada lora adapter") def unmerge_adapter(self): raise NotImplementedError("gptq model not support unmerge ada lora adapter") def merge_and_unload(self): raise NotImplementedError("gptq model not support merge and unload") def find_all_linear_names(model: BaseGPTQForCausalLM, ignore: Optional[List[str]] = None, ignore_lm_head: bool = True): if not ignore: ignore = [] lm_head_name = model.lm_head_name if ignore_lm_head and lm_head_name not in ignore: ignore.append(lm_head_name) results = set() for n, m in model.named_modules(): if isinstance(m, torch.nn.Linear): res = n.split('.')[-1] if res not in ignore: results.add(res) return list(results) @contextmanager def hijack_peft_mappings(): PEFT_TYPE_TO_CONFIG_MAPPING[PeftType.LORA] = GPTQLoraConfig PEFT_TYPE_TO_MODEL_MAPPING[PeftType.LORA] = GPTQLoraModel PEFT_TYPE_TO_CONFIG_MAPPING[PeftType.ADALORA] = GPTQAdaLoraConfig PEFT_TYPE_TO_MODEL_MAPPING[PeftType.ADALORA] = GPTQAdaLoraModel try: yield except: PEFT_TYPE_TO_CONFIG_MAPPING[PeftType.LORA] = GPTQLoraConfig PEFT_TYPE_TO_MODEL_MAPPING[PeftType.LORA] = GPTQLoraModel PEFT_TYPE_TO_CONFIG_MAPPING[PeftType.ADALORA] = GPTQAdaLoraConfig PEFT_TYPE_TO_MODEL_MAPPING[PeftType.ADALORA] = GPTQAdaLoraModel raise finally: PEFT_TYPE_TO_CONFIG_MAPPING[PeftType.LORA] = GPTQLoraConfig PEFT_TYPE_TO_MODEL_MAPPING[PeftType.LORA] = GPTQLoraModel PEFT_TYPE_TO_CONFIG_MAPPING[PeftType.ADALORA] = GPTQAdaLoraConfig PEFT_TYPE_TO_MODEL_MAPPING[PeftType.ADALORA] = GPTQAdaLoraModel def get_gptq_peft_model( model: BaseGPTQForCausalLM, peft_config: PeftConfig = None, model_id: str = None, adapter_name: str = "default", auto_find_all_linears: bool = True, train_mode: bool = False ): if train_mode and not model.trainable: model.enable_trainable_mode() if train_mode and not peft_config: raise ValueError("peft_config not specified when in train mode.") if not train_mode and not model_id: raise ValueError("model_id(where to load adapters) not specified when in inference mode.") if model.fused_attn_module_type is not None and not model.injected_fused_attention: peft_types = [PeftType.LORA.value, PeftType.ADALORA.value] warnings.warn( f"You can just ignore this warning if the peft type you use isn't in {peft_types}.\n" f"{model.__class__.__name__} supports injecting fused attention but not enables this time. " "If you are training adapters, you must also disable fused attention injection when loading quantized " "base model at inference time, otherwise adapters may not be added to base model properly. " "If you are loading adapters to do inference, you can reference to adapter's config file to check " "whether the adapters are trained using base model that not enable fused attention injection." ) if model.injected_fused_mlp: raise NotImplementedError("GPTQ model that enables fused mlp injection is not supported to integrate with peft.") if train_mode: peft_type = peft_config.peft_type if not isinstance(peft_type, str): peft_type = peft_type.value if peft_type in [PeftType.LORA.value, PeftType.ADALORA.value]: if auto_find_all_linears: peft_config.target_modules = find_all_linear_names(model, ignore_lm_head=True) if peft_type == PeftType.LORA.value and not isinstance(peft_config, GPTQLoraConfig): peft_config = GPTQLoraConfig(**peft_config.to_dict()) if peft_type == PeftType.ADALORA.value and not isinstance(peft_config, GPTQAdaLoraConfig): peft_config = GPTQAdaLoraConfig(**peft_config.to_dict()) peft_config.injected_fused_attention = model.injected_fused_attention peft_config.injected_fused_mlp = model.injected_fused_mlp if peft_type == PeftType.ADAPTION_PROMPT.value: if peft_config.adapter_layers > model.config.num_hidden_layers: warnings.warn( f"model has only {model.config.num_hidden_layers} layers " f"but adapter_layers is set to {peft_config.adapter_layers}, " f"will reset value to {model.config.num_hidden_layers}." ) peft_config.adapter_layers = model.config.num_hidden_layers if model.injected_fused_attention: raise NotImplementedError( "model with fused attention injected isn't supported to use ADAPTION_PROMPT peft type yet." ) with hijack_peft_mappings(): try: if train_mode: peft_model = get_peft_model(model.model, peft_config, adapter_name=adapter_name) else: peft_model = PeftModel.from_pretrained(model.model, model_id, adapter_name) except: raise NotImplementedError( f"{model.__class__.__name__} not support {peft_config.peft_type.value} peft type yet." ) return peft_model __all__ = [ "GPTQLoraConfig", "GPTQLoraModel", "GPTQAdaLoraConfig", "GPTQAdaLoraModel", "find_all_linear_names", "get_gptq_peft_model" ] ================================================ FILE: qalora.py ================================================ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. from collections import defaultdict import copy import json import os from os.path import exists, join, isdir from dataclasses import dataclass, field import sys from typing import Optional, Dict, Sequence import numpy as np from tqdm import tqdm import logging import torch import transformers from torch.nn.utils.rnn import pad_sequence import argparse from transformers import ( AutoTokenizer, AutoModelForCausalLM, set_seed, Seq2SeqTrainer, LlamaTokenizerFast ) from datasets import load_dataset import evaluate from peft import ( LoraConfig, get_peft_model_state_dict, set_peft_model_state_dict, PeftModel ) from peft.tuners.lora import LoraLayer from transformers.trainer_utils import PREFIX_CHECKPOINT_DIR from auto_gptq.utils.peft_utils import get_gptq_peft_model, GPTQLoraConfig from auto_gptq import AutoGPTQForCausalLM from auto_gptq.nn_modules.qlinear import GeneralQuantLinear torch.backends.cuda.matmul.allow_tf32 = True logger = logging.getLogger(__name__) IGNORE_INDEX = -100 DEFAULT_PAD_TOKEN = "[PAD]" import os os.environ["TOKENIZERS_PARALLELISM"] = "false" def prepare_model_for_int8_training(model, use_gradient_checkpointing=True): r""" This method wraps the entire protocol for preparing a model before running a training. This includes: 1- Cast the layernorm in fp32 2- making output embedding layer require grads 3- Add the upcasting of the lm head to fp32 Args: model, (`transformers.PreTrainedModel`): The loaded model from `transformers` """ for name, param in model.named_parameters(): # freeze base model's layers param.requires_grad = False if use_gradient_checkpointing: # For backward compatibility if hasattr(model, "enable_input_require_grads"): model.enable_input_require_grads() else: def make_inputs_require_grad(module, input, output): output.requires_grad_(True) model.get_input_embeddings().register_forward_hook(make_inputs_require_grad) # enable gradient checkpointing for memory efficiency model.gradient_checkpointing_enable() model.lm_head = model.lm_head.float() for _, param in model.named_parameters(): if param.dtype == torch.float16: param = param.float() return model @dataclass class ModelArguments: model_path: Optional[str] = field( default="./llama-7b/" ) trust_remote_code: Optional[bool] = field( default=False, metadata={"help": "Enable unpickling of arbitrary code in AutoModelForCausalLM#from_pretrained."} ) @dataclass class DataArguments: eval_dataset_size: int = field( default=1024, metadata={"help": "Size of validation dataset."} ) max_train_samples: Optional[int] = field( default=None, metadata={ "help": "For debugging purposes or quicker training, truncate the number of training examples to this " "value if set." }, ) max_eval_samples: Optional[int] = field( default=None, metadata={ "help": "For debugging purposes or quicker training, truncate the number of evaluation examples to this " "value if set." }, ) source_max_len: int = field( default=1024, metadata={"help": "Maximum source sequence length. Sequences will be right padded (and possibly truncated)."}, ) target_max_len: int = field( default=256, metadata={"help": "Maximum target sequence length. Sequences will be right padded (and possibly truncated)."}, ) dataset: str = field( default='alpaca', metadata={"help": "Which dataset to finetune on. See datamodule for options."} ) @dataclass class TrainingArguments(transformers.Seq2SeqTrainingArguments): cache_dir: Optional[str] = field( default=None ) train_on_source: Optional[bool] = field( default=False, metadata={"help": "Whether to train on the input in addition to the target text."} ) mmlu_split: Optional[str] = field( default='eval', metadata={"help": "The MMLU split to run on"} ) mmlu_dataset: Optional[str] = field( default='mmlu-fs', metadata={"help": "MMLU dataset to use: options are `mmlu-zs` for zero-shot or `mmlu-fs` for few shot."} ) do_mmlu_eval: Optional[bool] = field( default=False, metadata={"help": "Whether to run the MMLU evaluation."} ) max_mmlu_samples: Optional[int] = field( default=None, metadata={"help": "If set, only evaluates on `max_mmlu_samples` of the MMMLU dataset."} ) mmlu_source_max_len: int = field( default=2048, metadata={"help": "Maximum source sequence length for mmlu."} ) full_finetune: bool = field( default=False, metadata={"help": "Finetune the entire model without adapters."} ) adam8bit: bool = field( default=False, metadata={"help": "Use 8-bit adam."} ) lora_r: int = field( default=64, metadata={"help": "Lora R dimension."} ) lora_alpha: float = field( default=16, metadata={"help": " Lora alpha."} ) lora_dropout: float = field( default=0.0, metadata={"help":"Lora dropout."} ) max_memory_MB: int = field( default=24000, metadata={"help": "Free memory per gpu."} ) report_to: str = field( default='none', metadata={"help": "To use wandb or something else for reporting."} ) output_dir: str = field(default='./output', metadata={"help": 'The output dir for logs and checkpoints'}) optim: str = field(default='paged_adamw_32bit', metadata={"help": 'The optimizer to be used'}) per_device_train_batch_size: int = field(default=1, metadata={"help": 'The training batch size per GPU. Increase for better speed.'}) gradient_accumulation_steps: int = field(default=16, metadata={"help": 'How many gradients to accumulate before to perform an optimizer step'}) max_steps: int = field(default=10000, metadata={"help": 'How many optimizer update steps to take'}) weight_decay: float = field(default=0.0, metadata={"help": 'The L2 weight decay rate of AdamW'}) # use lora dropout instead for regularization if needed learning_rate: float = field(default=0.0002, metadata={"help": 'The learnign rate'}) remove_unused_columns: bool = field(default=False, metadata={"help": 'Removed unused columns. Needed to make this codebase work.'}) max_grad_norm: float = field(default=0.3, metadata={"help": 'Gradient clipping max norm. This is tuned and works well for all models tested.'}) gradient_checkpointing: bool = field(default=True, metadata={"help": 'Use gradient checkpointing. You want to use this.'}) do_train: bool = field(default=True, metadata={"help": 'To train or not to train, that is the question?'}) lr_scheduler_type: str = field(default='constant', metadata={"help": 'Learning rate schedule. Constant a bit better than cosine, and has advantage for analysis'}) warmup_ratio: float = field(default=0.03, metadata={"help": 'Fraction of steps to do a warmup for'}) logging_steps: int = field(default=10, metadata={"help": 'The frequency of update steps after which to log the loss'}) group_by_length: bool = field(default=True, metadata={"help": 'Group sequences into batches with same length. Saves memory and speeds up training considerably.'}) save_strategy: str = field(default='steps', metadata={"help": 'When to save checkpoints'}) save_steps: int = field(default=250, metadata={"help": 'How often to save a model'}) save_total_limit: int = field(default=40, metadata={"help": 'How many checkpoints to save before the oldest is overwritten'}) @dataclass class GenerationArguments: # For more hyperparameters check: # https://huggingface.co/docs/transformers/main_classes/text_generation#transformers.GenerationConfig # Length arguments max_new_tokens: Optional[int] = field( default=256, metadata={"help": "Maximum number of new tokens to be generated in evaluation or prediction loops" "if predict_with_generate is set."} ) min_new_tokens : Optional[int] = field( default=None, metadata={"help": "Minimum number of new tokens to generate."} ) # Generation strategy do_sample: Optional[bool] = field(default=False) num_beams: Optional[int] = field(default=1) num_beam_groups: Optional[int] = field(default=1) penalty_alpha: Optional[float] = field(default=None) use_cache: Optional[bool] = field(default=True) # Hyperparameters for logit manipulation temperature: Optional[float] = field(default=1.0) top_k: Optional[int] = field(default=50) top_p: Optional[float] = field(default=1.0) typical_p: Optional[float] = field(default=1.0) diversity_penalty: Optional[float] = field(default=0.0) repetition_penalty: Optional[float] = field(default=1.0) length_penalty: Optional[float] = field(default=1.0) no_repeat_ngram_size: Optional[int] = field(default=0) def find_all_linear_names(args, model): cls = GeneralQuantLinear if not(args.full_finetune) else torch.nn.Linear lora_module_names = set() for name, module in model.named_modules(): if isinstance(module, cls): names = name.split('.') lora_module_names.add(names[0] if len(names) == 1 else names[-1]) if 'lm_head' in lora_module_names: # needed for 16-bit lora_module_names.remove('lm_head') return list(lora_module_names) class SavePeftModelCallback(transformers.TrainerCallback): def save_model(self, args, state, kwargs): print('Saving PEFT checkpoint...') if state.best_model_checkpoint is not None: checkpoint_folder = os.path.join(state.best_model_checkpoint, "adapter_model") else: checkpoint_folder = os.path.join(args.output_dir, f"{PREFIX_CHECKPOINT_DIR}-{state.global_step}") peft_model_path = os.path.join(checkpoint_folder, "adapter_model") kwargs["model"].save_pretrained(peft_model_path) pytorch_model_path = os.path.join(checkpoint_folder, "pytorch_model.bin") if os.path.exists(pytorch_model_path): os.remove(pytorch_model_path) def on_save(self, args, state, control, **kwargs): self.save_model(args, state, kwargs) return control def on_train_end(self, args, state, control, **kwargs): def touch(fname, times=None): with open(fname, 'a'): os.utime(fname, times) touch(join(args.output_dir, 'completed')) self.save_model(args, state, kwargs) def get_accelerate_model(args, checkpoint_dir): n_gpus = torch.cuda.device_count() max_memory = f'{args.max_memory_MB}MB' max_memory = {i: max_memory for i in range(n_gpus)} if args.full_finetune: assert args.bits in [16, 32] print(f'loading base model {args.model_path}...') model = AutoGPTQForCausalLM.from_quantized( args.model_path, device_map='auto', max_memory=max_memory, trust_remote_code=args.trust_remote_code, inject_fused_attention = False, inject_fused_mlp = False, use_triton=True, warmup_triton=False, trainable=True ) model.model.quantize_config = model.quantize_config model.train() setattr(model, 'model_parallel', True) setattr(model, 'is_parallelizable', True) #modules = find_all_linear_names(args, model) model.config.torch_dtype=(torch.float32 if args.fp16 else (torch.bfloat16 if args.bf16 else torch.float32)) if not args.full_finetune: model = prepare_model_for_int8_training(model, use_gradient_checkpointing=args.gradient_checkpointing) if args.gradient_checkpointing: model.gradient_checkpointing_enable() config = GPTQLoraConfig( r=args.lora_r, lora_alpha=args.lora_alpha, #target_modules=modules, lora_dropout=args.lora_dropout, bias="none", task_type="CAUSAL_LM", ) if not args.full_finetune: if checkpoint_dir is not None: print("Loading adapters from checkpoint.") model = PeftModel.from_pretrained(model, join(checkpoint_dir, 'adapter_model')) for name, p in model.named_parameters(): if 'lora' in name: print(name, p.sum()) else: print(f'adding LoRA modules...') model = get_gptq_peft_model(model, config, auto_find_all_linears=True, train_mode=True) if args.gradient_checkpointing: if hasattr(model, "enable_input_require_grads"): model.enable_input_require_grads() else: def make_inputs_require_grad(module, input, output): output.requires_grad_(True) model.get_input_embeddings().register_forward_hook(make_inputs_require_grad) for name, module in model.named_modules(): if isinstance(module, LoraLayer): if args.bf16: module = module.to(torch.bfloat16) if 'norm' in name: module = module.to(torch.float32) if 'lm_head' in name or 'embed_tokens' in name: if hasattr(module, 'weight'): if args.bf16 and module.weight.dtype == torch.float32: module = module.to(torch.bfloat16) return model def print_trainable_parameters(args, model): """ Prints the number of trainable parameters in the model. """ trainable_params = 0 all_param = 0 for _, param in model.named_parameters(): all_param += param.numel() if param.requires_grad: trainable_params += param.numel() try: trainable_params /= (32//model.quantize_config.bits) except: pass print(f"trainable params: {trainable_params} || all params: {all_param} || trainable: {100 * trainable_params / all_param}") def smart_tokenizer_and_embedding_resize( special_tokens_dict: Dict, tokenizer: transformers.PreTrainedTokenizer, model: transformers.PreTrainedModel, ): """Resize tokenizer and embedding. Note: This is the unoptimized version that may make your embedding size not be divisible by 64. """ num_new_tokens = tokenizer.add_special_tokens(special_tokens_dict) model.resize_token_embeddings(len(tokenizer)) if num_new_tokens > 0: input_embeddings = model.get_input_embeddings().weight.data output_embeddings = model.get_output_embeddings().weight.data input_embeddings_avg = input_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) output_embeddings_avg = output_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) input_embeddings[-num_new_tokens:] = input_embeddings_avg output_embeddings[-num_new_tokens:] = output_embeddings_avg @dataclass class DataCollatorForCausalLM(object): tokenizer: transformers.PreTrainedTokenizer source_max_len: int target_max_len: int train_on_source: bool predict_with_generate: bool def __call__(self, instances: Sequence[Dict]) -> Dict[str, torch.Tensor]: # Extract elements sources = [example['input'] for example in instances] targets = [f"{example['output']}{self.tokenizer.eos_token}" for example in instances] # Tokenize tokenized_sources_with_prompt = self.tokenizer( sources, max_length=self.source_max_len, truncation=True, ) tokenized_targets = self.tokenizer( targets, max_length=self.target_max_len, truncation=True, add_special_tokens=False, ) # Build the input and labels for causal LM input_ids = [] labels = [] for tokenized_source, tokenized_target in zip( tokenized_sources_with_prompt['input_ids'], tokenized_targets['input_ids'] ): if not self.predict_with_generate: input_ids.append(torch.tensor(tokenized_source + tokenized_target)) if not self.train_on_source: labels.append( torch.tensor([IGNORE_INDEX for _ in range(len(tokenized_source))] + copy.deepcopy(tokenized_target)) ) else: labels.append(torch.tensor(copy.deepcopy(tokenized_source + tokenized_target))) else: input_ids.append(torch.tensor(tokenized_source)) # Apply padding input_ids = pad_sequence(input_ids, batch_first=True, padding_value=self.tokenizer.pad_token_id) labels = pad_sequence(labels, batch_first=True, padding_value=IGNORE_INDEX) if not self.predict_with_generate else None data_dict = { 'input_ids': input_ids, 'attention_mask':input_ids.ne(self.tokenizer.pad_token_id), } if labels is not None: data_dict['labels'] = labels return data_dict def extract_unnatural_instructions_data(examples, extract_reformulations=False): out = { 'input': [], 'output': [], } for example_instances in examples['instances']: for instance in example_instances: out['input'].append(instance['instruction_with_input']) out['output'].append(instance['output']) if extract_reformulations: for example_reformulations in examples['reformulations']: if example_reformulations is not None: for instance in example_reformulations: out['input'].append(instance['instruction_with_input']) out['output'].append(instance['output']) return out PROMPT_DICT = { "prompt_input": ( "Below is an instruction that describes a task, paired with an input that provides further context. " "Write a response that appropriately completes the request.\n\n" "### Instruction:\n{instruction}\n\n### Input:\n{input}\n\n### Response: " ), "prompt_no_input": ( "Below is an instruction that describes a task. " "Write a response that appropriately completes the request.\n\n" "### Instruction:\n{instruction}\n\n### Response: " ), } def extract_alpaca_dataset(example): if example.get("input", "") != "": prompt_format = PROMPT_DICT["prompt_input"] else: prompt_format = PROMPT_DICT["prompt_no_input"] return {'input': prompt_format.format(**example)} def make_data_module(tokenizer: transformers.PreTrainedTokenizer, args) -> Dict: """ Make dataset and collator for supervised fine-tuning. Datasets are expected to have the following columns: { `input`, `output` } Available datasets to be selected with `dataset` argument: - alpaca, 52002 examples - alpaca cleaned, 51942 examples - chip2 (OIG), 210289 examples - self-instruct, 82612 examples - hh-rlhf (Anthropic), 160800 examples - longform, 23.7k examples Coming soon: - unnatural instructions core, 66010 examples - unnatural instructions full, 240670 examples - alpaca-gpt4, 52002 examples - unnatural-instructions-gpt4, 9000 examples - oa-rlhf (OpenAssistant) primary message tree only, 9209 examples - oa-rlhf-assistant (OpenAssistant) all assistant replies with ranking - supernatural-instructions, 69624 examples (same as paper with 100 ex/task more can be used) - flan (FLAN v2), up to 20M examples available Not Available: - vicuna, not released at the moment. """ # Load dataset. # Alpaca if args.dataset == 'alpaca': dataset = load_dataset("tatsu-lab/alpaca") dataset = dataset.map(extract_alpaca_dataset, remove_columns=['instruction']) # Alpaca clean elif args.dataset == 'alpaca-clean': dataset = load_dataset("yahma/alpaca-cleaned") dataset = dataset.map(extract_alpaca_dataset, remove_columns=['instruction']) # Chip2 elif args.dataset == 'chip2': dataset = load_dataset("laion/OIG", data_files='unified_chip2.jsonl') dataset = dataset.map(lambda x: { 'input': x['text'].split('\n: ')[0].replace(': ', ''), 'output': x['text'].split('\n: ')[1], }, remove_columns=['text', 'metadata']) # Self Instruct elif args.dataset == 'self-instruct': dataset = load_dataset("yizhongw/self_instruct", name='self_instruct') for old, new in [["prompt", "input"], ["completion", "output"]]: dataset = dataset.rename_column(old, new) # Anthropic rlhf elif args.dataset == 'hh-rlhf': dataset = load_dataset("Anthropic/hh-rlhf") dataset = dataset.map(lambda x: { 'input': '', 'output': x['chosen'] }, remove_columns=['chosen', 'rejected']) # LongForm elif args.dataset == 'longform': dataset = load_dataset("akoksal/LongForm") elif args.dataset == 'vicuna': raise NotImplementedError("Vicuna data was not released.") else: raise NotImplementedError(f"Dataset {args.dataset} not implemented yet.") # Split train/eval, reduce size if args.do_eval or args.do_predict: if 'eval' in dataset: eval_dataset = dataset['eval'] else: print('Splitting train dataset in train and validation according to `eval_dataset_size`') dataset = dataset["train"].train_test_split( test_size=args.eval_dataset_size, shuffle=True, seed=42 ) eval_dataset = dataset['test'] if args.max_eval_samples is not None and len(eval_dataset) > args.max_eval_samples: eval_dataset = eval_dataset.select(range(args.max_eval_samples)) if args.group_by_length: eval_dataset = eval_dataset.map(lambda x: {'length': len(x['input']) + len(x['output'])}) if args.do_train: train_dataset = dataset['train'] if args.max_train_samples is not None and len(train_dataset) > args.max_train_samples: train_dataset = train_dataset.select(range(args.max_train_samples)) if args.group_by_length: train_dataset = train_dataset.map(lambda x: {'length': len(x['input']) + len(x['output'])}) data_collator = DataCollatorForCausalLM( tokenizer=tokenizer, source_max_len=args.source_max_len, target_max_len=args.target_max_len, train_on_source=args.train_on_source, predict_with_generate=args.predict_with_generate, ) return dict( train_dataset=train_dataset if args.do_train else None, eval_dataset=eval_dataset if args.do_eval else None, predict_dataset=eval_dataset if args.do_predict else None, data_collator=data_collator ) def get_last_checkpoint(checkpoint_dir): if isdir(checkpoint_dir): is_completed = exists(join(checkpoint_dir, 'completed')) if is_completed: return None, True # already finished max_step = 0 for filename in os.listdir(checkpoint_dir): if isdir(join(checkpoint_dir, filename)) and filename.startswith('checkpoint'): max_step = max(max_step, int(filename.replace('checkpoint-', ''))) if max_step == 0: return None, is_completed # training started, but no checkpoint checkpoint_dir = join(checkpoint_dir, f'checkpoint-{max_step}') print(f"Found a previous checkpoint at: {checkpoint_dir}") return checkpoint_dir, is_completed # checkpoint found! return None, False # first training def train(): hfparser = transformers.HfArgumentParser(( ModelArguments, DataArguments, TrainingArguments, GenerationArguments )) model_args, data_args, training_args, generation_args, extra_args = \ hfparser.parse_args_into_dataclasses(return_remaining_strings=True) training_args.generation_config = transformers.GenerationConfig(**vars(generation_args)) args = argparse.Namespace( **vars(model_args), **vars(data_args), **vars(training_args) ) checkpoint_dir, completed_training = get_last_checkpoint(args.output_dir) if completed_training: print('Detected that training was already completed!') model = get_accelerate_model(args, checkpoint_dir) training_args.skip_loading_checkpoint_weights=True resume_from_checkpoint = checkpoint_dir if resume_from_checkpoint: # Check the available weights and load them checkpoint_name = os.path.join( checkpoint_dir, "pytorch_model.bin" ) # Full checkpoint if not os.path.exists(checkpoint_name): checkpoint_path = os.path.join( checkpoint_dir, "adapter_model" ) checkpoint_name = os.path.join( checkpoint_path, "adapter_model.bin" ) # only LoRA model - LoRA config above has to fit resume_from_checkpoint = ( False # So the trainer won't try loading its state ) # The two files above have a different name depending on how they were saved, but are actually the same. if os.path.exists(checkpoint_name): print(f"Restarting from {checkpoint_name}") adapters_weights = torch.load(checkpoint_name) set_peft_model_state_dict(model, adapters_weights) else: print(f"Checkpoint {checkpoint_name} not found") model.config.use_cache = False print_trainable_parameters(args, model) print('loaded model') set_seed(args.seed) # Tokenizer tokenizer = AutoTokenizer.from_pretrained( args.model_path, cache_dir=args.cache_dir, padding_side="right", use_fast=True, ) if tokenizer.pad_token is None: smart_tokenizer_and_embedding_resize( special_tokens_dict=dict(pad_token=DEFAULT_PAD_TOKEN), tokenizer=tokenizer, model=model, ) if isinstance(tokenizer, LlamaTokenizerFast): # LLaMA tokenizer may not have correct special tokens set. # Check and add them if missing to prevent them from being parsed into different tokens. # Note that these are present in the vocabulary. # Note also that `model.config.pad_token_id` is 0 which corresponds to `` token. tokenizer.add_special_tokens( { "eos_token": tokenizer.convert_ids_to_tokens(model.config.eos_token_id), "bos_token": tokenizer.convert_ids_to_tokens(model.config.bos_token_id), "unk_token": tokenizer.convert_ids_to_tokens(model.config.pad_token_id), } ) data_module = make_data_module(tokenizer=tokenizer, args=args) trainer = Seq2SeqTrainer( model=model, tokenizer=tokenizer, args=training_args, **{k:v for k,v in data_module.items() if k != 'predict_dataset'}, ) # Callbacks if not args.full_finetune: trainer.add_callback(SavePeftModelCallback) if args.do_mmlu_eval: if args.mmlu_dataset == 'mmlu-zs': mmlu_dataset = load_dataset("json", data_files={ 'eval': 'data/mmlu/zero_shot_mmlu_val.json', 'test': 'data/mmlu/zero_shot_mmlu_test.json', }) mmlu_dataset = mmlu_dataset.remove_columns('subject') # MMLU Five-shot (Eval/Test only) elif args.mmlu_dataset == 'mmlu' or args.mmlu_dataset == 'mmlu-fs': mmlu_dataset = load_dataset("json", data_files={ 'eval': 'data/mmlu/five_shot_mmlu_val.json', 'test': 'data/mmlu/five_shot_mmlu_test.json', }) # mmlu_dataset = mmlu_dataset.remove_columns('subject') mmlu_dataset = mmlu_dataset[args.mmlu_split] if args.max_mmlu_samples is not None: mmlu_dataset = mmlu_dataset.select(range(args.max_mmlu_samples)) abcd_idx = [ tokenizer("A", add_special_tokens=False).input_ids[0], tokenizer("B", add_special_tokens=False).input_ids[0], tokenizer("C", add_special_tokens=False).input_ids[0], tokenizer("D", add_special_tokens=False).input_ids[0], ] accuracy = evaluate.load("accuracy") class MMLUEvalCallback(transformers.TrainerCallback): def on_evaluate(self, args, state, control, model, **kwargs): data_loader = trainer.get_eval_dataloader(mmlu_dataset) source_max_len = trainer.data_collator.source_max_len trainer.data_collator.source_max_len = args.mmlu_source_max_len trainer.model.eval() preds, refs = [], [] loss_mmlu = 0 for batch in tqdm(data_loader, total=len(data_loader)): (loss, logits, labels) = trainer.prediction_step(trainer.model,batch,prediction_loss_only=False,) # There are two tokens, the output, and eos token. for i, logit in enumerate(logits): label_non_zero_id = (batch['labels'][i] != -100).nonzero()[0][0] logit_abcd = logit[label_non_zero_id-1][abcd_idx] preds.append(torch.argmax(logit_abcd).item()) labels = labels[labels != IGNORE_INDEX].view(-1, 2)[:,0] for label in labels.tolist(): if label in abcd_idx: refs += [abcd_idx.index(label)] loss_mmlu += loss.item() # Extract results by subject. results = {'mmlu_loss':loss_mmlu/len(data_loader)} subject = mmlu_dataset['subject'] subjects = {s:{'refs':[], 'preds':[]} for s in set(subject)} for s,p,r in zip(subject, preds, refs): subjects[s]['preds'].append(p) subjects[s]['refs'].append(r) subject_scores = [] for subject in subjects: subject_score = accuracy.compute( references=subjects[subject]['refs'], predictions=subjects[subject]['preds'] )['accuracy'] results[f'mmlu_{args.mmlu_split}_accuracy_{subject}'] = subject_score subject_scores.append(subject_score) results[f'mmlu_{args.mmlu_split}_accuracy'] = np.mean(subject_scores) trainer.log(results) trainer.data_collator.source_max_len = source_max_len trainer.add_callback(MMLUEvalCallback) # Verifying the datatypes. dtypes = {} for _, p in model.named_parameters(): dtype = p.dtype if dtype not in dtypes: dtypes[dtype] = 0 dtypes[dtype] += p.numel() total = 0 for k, v in dtypes.items(): total+= v for k, v in dtypes.items(): print(k, v, v/total) all_metrics = {"run_name": args.run_name} # Training if args.do_train: train_result = trainer.train(resume_from_checkpoint=resume_from_checkpoint) metrics = train_result.metrics trainer.log_metrics("train", metrics) trainer.save_metrics("train", metrics) trainer.save_state() all_metrics.update(metrics) # Evaluation if args.do_eval: logger.info("*** Evaluate ***") metrics = trainer.evaluate(metric_key_prefix="eval") trainer.log_metrics("eval", metrics) trainer.save_metrics("eval", metrics) all_metrics.update(metrics) # Prediction if args.do_predict: logger.info("*** Predict ***") prediction_output = trainer.predict(test_dataset=data_module['predict_dataset'],metric_key_prefix="predict") prediction_metrics = prediction_output.metrics predictions = prediction_output.predictions predictions = np.where(predictions != -100, predictions, tokenizer.pad_token_id) predictions = tokenizer.batch_decode( predictions, skip_special_tokens=True, clean_up_tokenization_spaces=True ) with open(os.path.join(args.output_dir, 'predictions.jsonl'), 'w') as fout: for i, example in enumerate(data_module['predict_dataset']): example['prediction_with_input'] = predictions[i].strip() example['prediction'] = predictions[i].replace(example['input'], '').strip() fout.write(json.dumps(example) + '\n') print(prediction_metrics) trainer.log_metrics("predict", prediction_metrics) trainer.save_metrics("predict", prediction_metrics) all_metrics.update(prediction_metrics) if (args.do_train or args.do_eval or args.do_predict): with open(os.path.join(args.output_dir, "metrics.json"), "w") as fout: fout.write(json.dumps(all_metrics)) if __name__ == "__main__": train() ================================================ FILE: requirements.txt ================================================ bert-score==0.3.13 evaluate==0.4.0 rouge-score==0.1.2 scikit-learn==1.2.2 sentencepiece==0.1.99 wandb==0.15.2 transformers==4.31.0 peft==0.4.0 accelerate==0.21.0