Compare commits
6 Commits
attention_
...
offload-ac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6100baea0d | ||
|
|
47e0e71bc8 | ||
|
|
0f3587174d | ||
|
|
25e6c5f9bd | ||
|
|
32f51bca35 | ||
|
|
9daa04da90 |
87
.github/workflows/tests-nightly.yml
vendored
87
.github/workflows/tests-nightly.yml
vendored
@@ -18,9 +18,96 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SKIP: no-commit-to-branch
|
SKIP: no-commit-to-branch
|
||||||
|
|
||||||
|
preload-cache:
|
||||||
|
name: Preload HF cache
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
python_version: ["3.11"]
|
||||||
|
pytorch_version: ["2.6.0"]
|
||||||
|
timeout-minutes: 20
|
||||||
|
|
||||||
|
env:
|
||||||
|
AXOLOTL_IS_CI_CACHE_PRELOAD: "1"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Restore HF cache
|
||||||
|
id: hf-cache-restore
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
/home/runner/.cache/huggingface/hub/datasets--*
|
||||||
|
/home/runner/.cache/huggingface/hub/models--*
|
||||||
|
key: ${{ runner.os }}-hf-hub-cache-v2
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python_version }}
|
||||||
|
cache: 'pip' # caching pip dependencies
|
||||||
|
|
||||||
|
- name: upgrade pip
|
||||||
|
run: |
|
||||||
|
pip3 install --upgrade pip
|
||||||
|
pip3 install --upgrade packaging==23.2 setuptools==75.8.0 wheel
|
||||||
|
|
||||||
|
- name: Install PyTorch
|
||||||
|
run: |
|
||||||
|
pip3 install torch==${{ matrix.pytorch_version }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
pip3 show torch
|
||||||
|
pip3 install --no-build-isolation -U -e .
|
||||||
|
python scripts/unsloth_install.py | sh
|
||||||
|
python scripts/cutcrossentropy_install.py | sh
|
||||||
|
pip3 install -r requirements-dev.txt -r requirements-tests.txt
|
||||||
|
|
||||||
|
- name: Make sure PyTorch version wasn't clobbered
|
||||||
|
run: |
|
||||||
|
python -c "import torch; assert '${{ matrix.pytorch_version }}' in torch.__version__"
|
||||||
|
|
||||||
|
- name: Ensure axolotl CLI was installed
|
||||||
|
run: |
|
||||||
|
axolotl --help
|
||||||
|
|
||||||
|
- name: Pre-Download dataset fixture
|
||||||
|
run: |
|
||||||
|
huggingface-cli download --repo-type=dataset axolotl-ai-internal/axolotl-oss-dataset-fixtures
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
pytest -v tests/conftest.py
|
||||||
|
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v5
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
files: ./coverage.xml
|
||||||
|
flags: unittests,pytorch-${{ matrix.pytorch_version }}
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
||||||
|
- name: cleanup pip cache
|
||||||
|
run: |
|
||||||
|
find "$(pip cache dir)/http-v2" -type f -mtime +14 -exec rm {} \;
|
||||||
|
|
||||||
|
- name: Save HF cache
|
||||||
|
id: hf-cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
/home/runner/.cache/huggingface/hub/datasets--*
|
||||||
|
/home/runner/.cache/huggingface/hub/models--*
|
||||||
|
key: ${{ steps.hf-cache-restore.outputs.cache-primary-key }}
|
||||||
|
|
||||||
pytest:
|
pytest:
|
||||||
name: PyTest
|
name: PyTest
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: [preload-cache]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
max-parallel: 2
|
max-parallel: 2
|
||||||
|
|||||||
@@ -612,6 +612,7 @@ lr_div_factor: # Learning rate div factor
|
|||||||
# - optimi_adamw
|
# - optimi_adamw
|
||||||
# - ao_adamw_8bit
|
# - ao_adamw_8bit
|
||||||
# - ao_adamw_fp8
|
# - ao_adamw_fp8
|
||||||
|
# - came_pytorch
|
||||||
optimizer:
|
optimizer:
|
||||||
# Dictionary of arguments to pass to the optimizer
|
# Dictionary of arguments to pass to the optimizer
|
||||||
optim_args:
|
optim_args:
|
||||||
|
|||||||
@@ -34,3 +34,5 @@ We provide a script to delinearize Llama 4 linearized models into regular Huggin
|
|||||||
```bash
|
```bash
|
||||||
axolotl delinearize-llama4 --model path/to/model_dir --output path/to/output_dir
|
axolotl delinearize-llama4 --model path/to/model_dir --output path/to/output_dir
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: This only works with the non-quantized linearized model. If you have an adapter, merge it with the *non-quantized linearized* model before delinearizing.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ liger-kernel==0.5.9
|
|||||||
|
|
||||||
packaging==23.2
|
packaging==23.2
|
||||||
|
|
||||||
|
huggingface_hub==0.31.0
|
||||||
peft==0.15.2
|
peft==0.15.2
|
||||||
transformers==4.51.3
|
transformers==4.51.3
|
||||||
tokenizers>=0.21.1
|
tokenizers>=0.21.1
|
||||||
|
|||||||
1
setup.py
1
setup.py
@@ -142,6 +142,7 @@ extras_require = {
|
|||||||
"apollo-torch",
|
"apollo-torch",
|
||||||
"lomo-optim==0.1.1",
|
"lomo-optim==0.1.1",
|
||||||
"torch-optimi==0.2.1",
|
"torch-optimi==0.2.1",
|
||||||
|
"came_pytorch==0.1.3",
|
||||||
],
|
],
|
||||||
"ray": [
|
"ray": [
|
||||||
"ray[train]",
|
"ray[train]",
|
||||||
|
|||||||
@@ -708,6 +708,20 @@ class HFCausalTrainerBuilder(TrainerBuilderBase):
|
|||||||
optimizer_cls = ADOPT
|
optimizer_cls = ADOPT
|
||||||
adam_kwargs["decouple"] = True
|
adam_kwargs["decouple"] = True
|
||||||
optimizer_kwargs.update(adam_kwargs)
|
optimizer_kwargs.update(adam_kwargs)
|
||||||
|
elif self.cfg.optimizer == "came_pytorch":
|
||||||
|
from came_pytorch import CAME
|
||||||
|
|
||||||
|
optimizer_cls = CAME
|
||||||
|
|
||||||
|
beta1 = training_arguments_kwargs.get("adam_beta1", 0.9)
|
||||||
|
beta2 = training_arguments_kwargs.get("adam_beta2", 0.999)
|
||||||
|
beta3 = training_arguments_kwargs.get("adam_beta2", 0.9999)
|
||||||
|
eps1 = training_arguments_kwargs.get("adam_epsilon", 1e-30)
|
||||||
|
eps2 = training_arguments_kwargs.get("adam_epsilon2", 1e-16)
|
||||||
|
adam_kwargs["betas"] = (beta1, beta2, beta3)
|
||||||
|
adam_kwargs["eps"] = (eps1, eps2)
|
||||||
|
|
||||||
|
optimizer_kwargs.update(adam_kwargs)
|
||||||
|
|
||||||
# Parse any additional optimizer args from config
|
# Parse any additional optimizer args from config
|
||||||
if self.cfg.optim_args:
|
if self.cfg.optim_args:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
@@ -12,7 +13,6 @@ from typing import Any, Dict
|
|||||||
|
|
||||||
import torch
|
import torch
|
||||||
import transformers.modelcard
|
import transformers.modelcard
|
||||||
from accelerate.logging import get_logger
|
|
||||||
from accelerate.utils import save_fsdp_model
|
from accelerate.utils import save_fsdp_model
|
||||||
from datasets import Dataset
|
from datasets import Dataset
|
||||||
from huggingface_hub.errors import OfflineModeIsEnabled
|
from huggingface_hub.errors import OfflineModeIsEnabled
|
||||||
@@ -42,7 +42,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
BetterTransformer = None
|
BetterTransformer = None
|
||||||
|
|
||||||
LOG = get_logger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def setup_model_and_tokenizer(
|
def setup_model_and_tokenizer(
|
||||||
@@ -63,7 +63,6 @@ def setup_model_and_tokenizer(
|
|||||||
# Load tokenizer
|
# Load tokenizer
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
f"loading tokenizer... {cfg.tokenizer_config or cfg.base_model_config}",
|
f"loading tokenizer... {cfg.tokenizer_config or cfg.base_model_config}",
|
||||||
main_process_only=True,
|
|
||||||
)
|
)
|
||||||
tokenizer = load_tokenizer(cfg)
|
tokenizer = load_tokenizer(cfg)
|
||||||
|
|
||||||
|
|||||||
@@ -281,6 +281,10 @@ def load_dataset_w_config(
|
|||||||
**load_ds_kwargs,
|
**load_ds_kwargs,
|
||||||
)
|
)
|
||||||
if not ds:
|
if not ds:
|
||||||
raise ValueError("unhandled dataset load")
|
raise ValueError(
|
||||||
|
"The dataset could not be loaded. This could be due to a misconfigured dataset path "
|
||||||
|
f"({config_dataset.path}). Try double-check your path / name / data_files. "
|
||||||
|
"This is not caused by the dataset type."
|
||||||
|
)
|
||||||
|
|
||||||
return ds
|
return ds
|
||||||
|
|||||||
@@ -1,16 +1,59 @@
|
|||||||
"""custom checkpointing utils"""
|
"""custom checkpointing utils"""
|
||||||
|
|
||||||
|
import importlib
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from axolotl.utils.gradient_checkpointing.unsloth import (
|
from packaging import version
|
||||||
Unsloth_Offloaded_Gradient_Checkpointer,
|
|
||||||
|
from axolotl.utils.gradient_checkpointing.offload_cpu import (
|
||||||
|
CPU_Offloaded_Gradient_Checkpointer,
|
||||||
)
|
)
|
||||||
|
from axolotl.utils.gradient_checkpointing.offload_disk import (
|
||||||
|
DiskOffloadedGradientCheckpointer,
|
||||||
|
)
|
||||||
|
|
||||||
|
transformers_version = version.parse(importlib.metadata.version("transformers"))
|
||||||
|
if transformers_version > version.parse("4.51.3"):
|
||||||
|
from transformers.modeling_layers import GradientCheckpointingLayer
|
||||||
|
|
||||||
|
def uses_gc_layers(decoder_layer):
|
||||||
|
return isinstance(decoder_layer.func.__self__, GradientCheckpointingLayer)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
def uses_gc_layers(_):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def hf_grad_checkpoint_offload_wrapper(
|
def hf_grad_checkpoint_offload_wrapper(
|
||||||
decoder_layer, *args, use_reentrant=None
|
decoder_layer, *args, use_reentrant=None
|
||||||
): # pylint: disable=unused-argument
|
): # pylint: disable=unused-argument
|
||||||
return Unsloth_Offloaded_Gradient_Checkpointer.apply(
|
if uses_gc_layers(decoder_layer):
|
||||||
|
return CPU_Offloaded_Gradient_Checkpointer.apply(
|
||||||
|
decoder_layer,
|
||||||
|
*args,
|
||||||
|
)
|
||||||
|
|
||||||
|
return CPU_Offloaded_Gradient_Checkpointer.apply(
|
||||||
|
(
|
||||||
|
decoder_layer.func.__self__
|
||||||
|
if isinstance(decoder_layer, partial)
|
||||||
|
else decoder_layer.__self__
|
||||||
|
),
|
||||||
|
*args,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def hf_grad_checkpoint_disk_offload_wrapper(
|
||||||
|
decoder_layer, *args, use_reentrant=None
|
||||||
|
): # pylint: disable=unused-argument
|
||||||
|
if uses_gc_layers(decoder_layer):
|
||||||
|
return DiskOffloadedGradientCheckpointer.apply(
|
||||||
|
decoder_layer,
|
||||||
|
*args,
|
||||||
|
)
|
||||||
|
|
||||||
|
return DiskOffloadedGradientCheckpointer.apply(
|
||||||
(
|
(
|
||||||
decoder_layer.func.__self__
|
decoder_layer.func.__self__
|
||||||
if isinstance(decoder_layer, partial)
|
if isinstance(decoder_layer, partial)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Unsloth checkpointing"""
|
"""CPU offloaded checkpointing"""
|
||||||
|
|
||||||
# Copyright 2023-present Daniel Han-Chen & the Unsloth team. All rights reserved.
|
# Copyright 2023-present Daniel Han-Chen & the Unsloth team. All rights reserved.
|
||||||
#
|
#
|
||||||
@@ -26,7 +26,7 @@ else:
|
|||||||
torch_cuda_amp_custom_bwd = torch.amp.custom_bwd(device_type="cuda")
|
torch_cuda_amp_custom_bwd = torch.amp.custom_bwd(device_type="cuda")
|
||||||
|
|
||||||
|
|
||||||
class Unsloth_Offloaded_Gradient_Checkpointer( # pylint: disable=invalid-name
|
class CPU_Offloaded_Gradient_Checkpointer( # pylint: disable=invalid-name
|
||||||
torch.autograd.Function
|
torch.autograd.Function
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
93
src/axolotl/utils/gradient_checkpointing/offload_disk.py
Normal file
93
src/axolotl/utils/gradient_checkpointing/offload_disk.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
"""Disk offloaded checkpointing"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
torch_cuda_amp_custom_fwd = torch.amp.custom_fwd(device_type="cuda")
|
||||||
|
torch_cuda_amp_custom_bwd = torch.amp.custom_bwd(device_type="cuda")
|
||||||
|
|
||||||
|
|
||||||
|
class DiskOffloadedGradientCheckpointer(torch.autograd.Function):
|
||||||
|
"""
|
||||||
|
Saves both VRAM and RAM by offloading activations to disk.
|
||||||
|
Greater hit to performance than RAM offloading, but useful for extremely memory-constrained environments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create a temporary directory for storing tensors
|
||||||
|
_temp_dir = tempfile.mkdtemp(prefix="disk_checkpoint_")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_temp_file_path():
|
||||||
|
"""Generate a unique file path for tensor storage"""
|
||||||
|
return os.path.join(
|
||||||
|
DiskOffloadedGradientCheckpointer._temp_dir, f"{uuid.uuid4()}.pt"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@torch_cuda_amp_custom_fwd
|
||||||
|
def forward(ctx, forward_function, hidden_states, *args):
|
||||||
|
# Generate a unique file path for this tensor
|
||||||
|
file_path = DiskOffloadedGradientCheckpointer._get_temp_file_path()
|
||||||
|
|
||||||
|
# Save tensor to disk in a non-blocking way (detached from compute)
|
||||||
|
# First move to CPU, then save
|
||||||
|
cpu_hidden_states = hidden_states.detach().cpu()
|
||||||
|
torch.save(cpu_hidden_states, file_path)
|
||||||
|
|
||||||
|
# Free CPU memory
|
||||||
|
del cpu_hidden_states
|
||||||
|
|
||||||
|
# Run forward pass
|
||||||
|
with torch.no_grad():
|
||||||
|
output = forward_function(hidden_states, *args)
|
||||||
|
|
||||||
|
# Store the path instead of the tensor
|
||||||
|
ctx.save_for_backward(torch.tensor([0])) # Dummy tensor
|
||||||
|
ctx.file_path = file_path
|
||||||
|
ctx.forward_function = forward_function
|
||||||
|
ctx.args = args
|
||||||
|
return output
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@torch_cuda_amp_custom_bwd
|
||||||
|
def backward(ctx, dY): # pylint: disable=invalid-name
|
||||||
|
# Load the hidden states from disk
|
||||||
|
hidden_states = torch.load(ctx.file_path, weights_only=True)
|
||||||
|
|
||||||
|
# Move to CUDA and prepare for gradient computation
|
||||||
|
hidden_states = hidden_states.to("cuda", non_blocking=True).detach()
|
||||||
|
hidden_states.requires_grad = True
|
||||||
|
|
||||||
|
# Clean up the temporary file
|
||||||
|
try:
|
||||||
|
os.remove(ctx.file_path)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass # Ignore errors in file deletion
|
||||||
|
|
||||||
|
# Compute gradients
|
||||||
|
with torch.enable_grad():
|
||||||
|
output = ctx.forward_function(hidden_states, *ctx.args)
|
||||||
|
# pylint: disable=duplicate-code
|
||||||
|
torch.autograd.backward(output, dY)
|
||||||
|
|
||||||
|
return (
|
||||||
|
None,
|
||||||
|
hidden_states.grad,
|
||||||
|
) + (
|
||||||
|
None,
|
||||||
|
) * len(ctx.args)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def cleanup():
|
||||||
|
"""Clean up the temporary directory when done"""
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.rmtree(
|
||||||
|
DiskOffloadedGradientCheckpointer._temp_dir
|
||||||
|
) # pylint: disable=protected-access
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
@@ -70,7 +70,10 @@ from axolotl.utils.distributed import (
|
|||||||
is_local_main_process,
|
is_local_main_process,
|
||||||
is_main_process,
|
is_main_process,
|
||||||
)
|
)
|
||||||
from axolotl.utils.gradient_checkpointing import hf_grad_checkpoint_offload_wrapper
|
from axolotl.utils.gradient_checkpointing import (
|
||||||
|
hf_grad_checkpoint_disk_offload_wrapper,
|
||||||
|
hf_grad_checkpoint_offload_wrapper,
|
||||||
|
)
|
||||||
from axolotl.utils.lora_embeddings import get_linear_embedding_layers
|
from axolotl.utils.lora_embeddings import get_linear_embedding_layers
|
||||||
from axolotl.utils.model_shard_quant import load_sharded_model, load_sharded_model_quant
|
from axolotl.utils.model_shard_quant import load_sharded_model, load_sharded_model_quant
|
||||||
|
|
||||||
@@ -619,6 +622,10 @@ class ModelLoader:
|
|||||||
|
|
||||||
if self.cfg.gradient_checkpointing in ["unsloth", "offload"]:
|
if self.cfg.gradient_checkpointing in ["unsloth", "offload"]:
|
||||||
transformers.modeling_utils.checkpoint = hf_grad_checkpoint_offload_wrapper
|
transformers.modeling_utils.checkpoint = hf_grad_checkpoint_offload_wrapper
|
||||||
|
if self.cfg.gradient_checkpointing == "offload_disk":
|
||||||
|
transformers.modeling_utils.checkpoint = (
|
||||||
|
hf_grad_checkpoint_disk_offload_wrapper
|
||||||
|
)
|
||||||
|
|
||||||
if self.cfg.flash_attention:
|
if self.cfg.flash_attention:
|
||||||
self.patch_attention()
|
self.patch_attention()
|
||||||
|
|||||||
@@ -78,15 +78,11 @@ def pack_group(
|
|||||||
Returns:
|
Returns:
|
||||||
List of bins, where each bin contains indices of sequences assigned to it
|
List of bins, where each bin contains indices of sequences assigned to it
|
||||||
"""
|
"""
|
||||||
# Get sorting indices and sort lengths in descending order
|
|
||||||
indices = np.argsort(sequence_lengths)[::-1]
|
|
||||||
sorted_lengths = sequence_lengths[indices]
|
|
||||||
|
|
||||||
bins_remaining_space: list = [] # Tracks remaining capacity in each bin
|
bins_remaining_space: list = [] # Tracks remaining capacity in each bin
|
||||||
bins_assigned_sequences: list = [] # Tracks sequence indices assigned to each bin
|
bins_assigned_sequences: list = [] # Tracks sequence indices assigned to each bin
|
||||||
|
|
||||||
for seq_id, size in enumerate(sorted_lengths):
|
for seq_id, size in enumerate(sequence_lengths):
|
||||||
global_idx = indices[seq_id] + group_offset
|
global_idx = seq_id + group_offset
|
||||||
|
|
||||||
# Try to place sequence in existing bins
|
# Try to place sequence in existing bins
|
||||||
add_new_bin = True
|
add_new_bin = True
|
||||||
|
|||||||
@@ -178,9 +178,9 @@ class AxolotlInputConfig(
|
|||||||
|
|
||||||
# torch_dtype: torch.dtype | None
|
# torch_dtype: torch.dtype | None
|
||||||
|
|
||||||
gradient_checkpointing: Literal["unsloth", "offload"] | bool | None = Field(
|
gradient_checkpointing: (
|
||||||
default=False
|
Literal["unsloth", "offload", "offload_disk"] | bool | None
|
||||||
)
|
) = Field(default=False)
|
||||||
gradient_checkpointing_kwargs: dict[str, Any] | None = None
|
gradient_checkpointing_kwargs: dict[str, Any] | None = None
|
||||||
|
|
||||||
unfrozen_parameters: list[str] | None = None
|
unfrozen_parameters: list[str] | None = None
|
||||||
|
|||||||
@@ -53,4 +53,5 @@ class CustomSupportedOptimizers(str, Enum):
|
|||||||
ao_adamw_8bit = "ao_adamw_8bit" # pylint: disable=invalid-name
|
ao_adamw_8bit = "ao_adamw_8bit" # pylint: disable=invalid-name
|
||||||
ao_adamw_fp8 = "ao_adamw_fp8" # pylint: disable=invalid-name
|
ao_adamw_fp8 = "ao_adamw_fp8" # pylint: disable=invalid-name
|
||||||
adopt_adamw = "adopt_adamw" # pylint: disable=invalid-name
|
adopt_adamw = "adopt_adamw" # pylint: disable=invalid-name
|
||||||
|
came_pytorch = "came_pytorch" # pylint: disable=invalid-name
|
||||||
muon = "muon" # pylint: disable=invalid-name
|
muon = "muon" # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -75,8 +75,10 @@ class HyperparametersConfig(BaseModel):
|
|||||||
lr_groups: list[LrGroup] | None = None
|
lr_groups: list[LrGroup] | None = None
|
||||||
|
|
||||||
adam_epsilon: float | None = None
|
adam_epsilon: float | None = None
|
||||||
|
adam_epsilon2: float | None = None
|
||||||
adam_beta1: float | None = None
|
adam_beta1: float | None = None
|
||||||
adam_beta2: float | None = None
|
adam_beta2: float | None = None
|
||||||
|
adam_beta3: float | None = None
|
||||||
max_grad_norm: float | None = None
|
max_grad_norm: float | None = None
|
||||||
num_epochs: float = Field(default=1.0)
|
num_epochs: float = Field(default=1.0)
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class TestKnowledgeDistillation:
|
|||||||
train(cfg=cfg, dataset_meta=dataset_meta)
|
train(cfg=cfg, dataset_meta=dataset_meta)
|
||||||
assert (Path(temp_dir) / "model.safetensors").exists()
|
assert (Path(temp_dir) / "model.safetensors").exists()
|
||||||
check_tensorboard(
|
check_tensorboard(
|
||||||
temp_dir + "/runs", "train/loss", 1.0, "Train Loss is too high"
|
temp_dir + "/runs", "train/loss", 1.2, "Train Loss (%s) is too high"
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -121,5 +121,5 @@ class TestKnowledgeDistillation:
|
|||||||
train(cfg=cfg, dataset_meta=dataset_meta)
|
train(cfg=cfg, dataset_meta=dataset_meta)
|
||||||
assert (Path(temp_dir) / "adapter_model.safetensors").exists()
|
assert (Path(temp_dir) / "adapter_model.safetensors").exists()
|
||||||
check_tensorboard(
|
check_tensorboard(
|
||||||
temp_dir + "/runs", "train/loss", 1.0, "Train Loss is too high"
|
temp_dir + "/runs", "train/loss", 1.2, "Train Loss (%s) is too high"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ class TestMultiGPULlama:
|
|||||||
"sample_packing": True,
|
"sample_packing": True,
|
||||||
"pad_to_sequence_len": True,
|
"pad_to_sequence_len": True,
|
||||||
"sequence_len": 2048,
|
"sequence_len": 2048,
|
||||||
"val_set_size": 0.05,
|
"val_set_size": 0.1,
|
||||||
"special_tokens": {
|
"special_tokens": {
|
||||||
"pad_token": "<|endoftext|>",
|
"pad_token": "<|endoftext|>",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ from axolotl.utils.dict import DictDefault
|
|||||||
|
|
||||||
MODEL_CONFIGS = [
|
MODEL_CONFIGS = [
|
||||||
{
|
{
|
||||||
"name": "openaccess-ai-collective/tiny-mistral",
|
"name": "trl-internal-testing/tiny-MistralForCausalLM-0.2",
|
||||||
"expected_activation": apply_lora_mlp_swiglu,
|
"expected_activation": apply_lora_mlp_swiglu,
|
||||||
"dtype": torch.float16,
|
"dtype": torch.float16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Qwen/Qwen2-7B",
|
"name": "trl-internal-testing/tiny-Qwen2ForCausalLM-2.5",
|
||||||
"expected_activation": apply_lora_mlp_swiglu,
|
"expected_activation": apply_lora_mlp_swiglu,
|
||||||
"dtype": torch.float16,
|
"dtype": torch.float16,
|
||||||
},
|
},
|
||||||
@@ -44,7 +44,7 @@ MODEL_CONFIGS = [
|
|||||||
"dtype": torch.float32,
|
"dtype": torch.float32,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mhenrichsen/gemma-2b",
|
"name": "trl-internal-testing/tiny-Gemma2ForCausalLM",
|
||||||
"expected_activation": apply_lora_mlp_geglu,
|
"expected_activation": apply_lora_mlp_geglu,
|
||||||
"dtype": torch.float16,
|
"dtype": torch.float16,
|
||||||
},
|
},
|
||||||
@@ -156,7 +156,9 @@ def test_swiglu_mlp_integration(small_llama_model):
|
|||||||
def test_geglu_model_integration():
|
def test_geglu_model_integration():
|
||||||
"""Test GeGLU activation with Gemma model."""
|
"""Test GeGLU activation with Gemma model."""
|
||||||
model = AutoModelForCausalLM.from_pretrained(
|
model = AutoModelForCausalLM.from_pretrained(
|
||||||
"mhenrichsen/gemma-2b", torch_dtype=torch.float16, device_map="cuda:0"
|
"trl-internal-testing/tiny-Gemma2ForCausalLM",
|
||||||
|
torch_dtype=torch.float16,
|
||||||
|
device_map="cuda:0",
|
||||||
)
|
)
|
||||||
peft_config = get_peft_config(
|
peft_config = get_peft_config(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from axolotl.cli.args import TrainerCliArgs
|
from axolotl.cli.args import TrainerCliArgs
|
||||||
from axolotl.common.datasets import load_datasets
|
from axolotl.common.datasets import load_datasets
|
||||||
from axolotl.train import train
|
from axolotl.train import train
|
||||||
@@ -23,6 +25,7 @@ class TestFalconPatched(unittest.TestCase):
|
|||||||
Test case for Falcon models
|
Test case for Falcon models
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="no tiny models for testing with safetensors")
|
||||||
@with_temp_dir
|
@with_temp_dir
|
||||||
def test_qlora(self, temp_dir):
|
def test_qlora(self, temp_dir):
|
||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
@@ -71,6 +74,7 @@ class TestFalconPatched(unittest.TestCase):
|
|||||||
train(cfg=cfg, dataset_meta=dataset_meta)
|
train(cfg=cfg, dataset_meta=dataset_meta)
|
||||||
check_model_output_exists(temp_dir, cfg)
|
check_model_output_exists(temp_dir, cfg)
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="no tiny models for testing with safetensors")
|
||||||
@with_temp_dir
|
@with_temp_dir
|
||||||
def test_ft(self, temp_dir):
|
def test_ft(self, temp_dir):
|
||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class TestMistral(unittest.TestCase):
|
|||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
cfg = DictDefault(
|
cfg = DictDefault(
|
||||||
{
|
{
|
||||||
"base_model": "openaccess-ai-collective/tiny-mistral",
|
"base_model": "trl-internal-testing/tiny-MistralForCausalLM-0.2",
|
||||||
"flash_attention": True,
|
"flash_attention": True,
|
||||||
"sample_packing": True,
|
"sample_packing": True,
|
||||||
"sequence_len": 1024,
|
"sequence_len": 1024,
|
||||||
@@ -76,7 +76,7 @@ class TestMistral(unittest.TestCase):
|
|||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
cfg = DictDefault(
|
cfg = DictDefault(
|
||||||
{
|
{
|
||||||
"base_model": "openaccess-ai-collective/tiny-mistral",
|
"base_model": "trl-internal-testing/tiny-MistralForCausalLM-0.2",
|
||||||
"flash_attention": True,
|
"flash_attention": True,
|
||||||
"sample_packing": True,
|
"sample_packing": True,
|
||||||
"sequence_len": 1024,
|
"sequence_len": 1024,
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class TestModelPatches(unittest.TestCase):
|
|||||||
def test_mistral_multipack(self, temp_dir):
|
def test_mistral_multipack(self, temp_dir):
|
||||||
cfg = DictDefault(
|
cfg = DictDefault(
|
||||||
{
|
{
|
||||||
"base_model": "openaccess-ai-collective/tiny-mistral",
|
"base_model": "trl-internal-testing/tiny-MistralForCausalLM-0.2",
|
||||||
"flash_attention": True,
|
"flash_attention": True,
|
||||||
"sample_packing": True,
|
"sample_packing": True,
|
||||||
"sequence_len": 2048,
|
"sequence_len": 2048,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from axolotl.train import train
|
|||||||
from axolotl.utils.config import normalize_config, validate_config
|
from axolotl.utils.config import normalize_config, validate_config
|
||||||
from axolotl.utils.dict import DictDefault
|
from axolotl.utils.dict import DictDefault
|
||||||
|
|
||||||
from ..utils import check_model_output_exists, most_recent_subdir
|
from ..utils import check_model_output_exists, most_recent_subdir, require_torch_2_6_0
|
||||||
|
|
||||||
LOG = logging.getLogger("axolotl.tests.e2e")
|
LOG = logging.getLogger("axolotl.tests.e2e")
|
||||||
os.environ["WANDB_DISABLED"] = "true"
|
os.environ["WANDB_DISABLED"] = "true"
|
||||||
@@ -26,6 +26,7 @@ class TestResumeLlama:
|
|||||||
Test case for resuming training of llama models
|
Test case for resuming training of llama models
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@require_torch_2_6_0
|
||||||
def test_resume_lora_packed(self, temp_dir):
|
def test_resume_lora_packed(self, temp_dir):
|
||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
cfg = DictDefault(
|
cfg = DictDefault(
|
||||||
@@ -62,6 +63,7 @@ class TestResumeLlama:
|
|||||||
"save_total_limit": 5,
|
"save_total_limit": 5,
|
||||||
"max_steps": 15,
|
"max_steps": 15,
|
||||||
"use_tensorboard": True,
|
"use_tensorboard": True,
|
||||||
|
"save_safetensors": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if is_torch_bf16_gpu_available():
|
if is_torch_bf16_gpu_available():
|
||||||
|
|||||||
@@ -19,14 +19,11 @@ class TestE2eEvaluate:
|
|||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
cfg = DictDefault(
|
cfg = DictDefault(
|
||||||
{
|
{
|
||||||
"base_model": "JackFram/llama-68m",
|
"base_model": "HuggingFaceTB/SmolLM2-135M",
|
||||||
"tokenizer_type": "LlamaTokenizer",
|
|
||||||
"sequence_len": 1024,
|
"sequence_len": 1024,
|
||||||
"val_set_size": 0.02,
|
"val_set_size": 0.02,
|
||||||
"special_tokens": {
|
"special_tokens": {
|
||||||
"unk_token": "<unk>",
|
"pad_token": "<|endoftext|>",
|
||||||
"bos_token": "<s>",
|
|
||||||
"eos_token": "</s>",
|
|
||||||
},
|
},
|
||||||
"datasets": [
|
"datasets": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from axolotl.cli.args import TrainerCliArgs
|
from axolotl.cli.args import TrainerCliArgs
|
||||||
from axolotl.common.datasets import load_datasets
|
from axolotl.common.datasets import load_datasets
|
||||||
from axolotl.train import train
|
from axolotl.train import train
|
||||||
@@ -23,6 +25,7 @@ class TestFalcon(unittest.TestCase):
|
|||||||
Test case for falcon
|
Test case for falcon
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="no tiny models for testing with safetensors")
|
||||||
@with_temp_dir
|
@with_temp_dir
|
||||||
def test_lora(self, temp_dir):
|
def test_lora(self, temp_dir):
|
||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
@@ -74,6 +77,7 @@ class TestFalcon(unittest.TestCase):
|
|||||||
train(cfg=cfg, dataset_meta=dataset_meta)
|
train(cfg=cfg, dataset_meta=dataset_meta)
|
||||||
check_model_output_exists(temp_dir, cfg)
|
check_model_output_exists(temp_dir, cfg)
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="no tiny models for testing with safetensors")
|
||||||
@with_temp_dir
|
@with_temp_dir
|
||||||
def test_lora_added_vocab(self, temp_dir):
|
def test_lora_added_vocab(self, temp_dir):
|
||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
@@ -129,6 +133,7 @@ class TestFalcon(unittest.TestCase):
|
|||||||
train(cfg=cfg, dataset_meta=dataset_meta)
|
train(cfg=cfg, dataset_meta=dataset_meta)
|
||||||
check_model_output_exists(temp_dir, cfg)
|
check_model_output_exists(temp_dir, cfg)
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="no tiny models for testing with safetensors")
|
||||||
@with_temp_dir
|
@with_temp_dir
|
||||||
def test_ft(self, temp_dir):
|
def test_ft(self, temp_dir):
|
||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class TestMistral(unittest.TestCase):
|
|||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
cfg = DictDefault(
|
cfg = DictDefault(
|
||||||
{
|
{
|
||||||
"base_model": "openaccess-ai-collective/tiny-mistral",
|
"base_model": "trl-internal-testing/tiny-MistralForCausalLM-0.2",
|
||||||
"flash_attention": True,
|
"flash_attention": True,
|
||||||
"sequence_len": 1024,
|
"sequence_len": 1024,
|
||||||
"load_in_8bit": True,
|
"load_in_8bit": True,
|
||||||
@@ -77,7 +77,7 @@ class TestMistral(unittest.TestCase):
|
|||||||
# pylint: disable=duplicate-code
|
# pylint: disable=duplicate-code
|
||||||
cfg = DictDefault(
|
cfg = DictDefault(
|
||||||
{
|
{
|
||||||
"base_model": "openaccess-ai-collective/tiny-mistral",
|
"base_model": "trl-internal-testing/tiny-MistralForCausalLM-0.2",
|
||||||
"flash_attention": True,
|
"flash_attention": True,
|
||||||
"sequence_len": 1024,
|
"sequence_len": 1024,
|
||||||
"val_set_size": 0.02,
|
"val_set_size": 0.02,
|
||||||
|
|||||||
@@ -199,3 +199,50 @@ class TestCustomOptimizers(unittest.TestCase):
|
|||||||
|
|
||||||
train(cfg=cfg, dataset_meta=dataset_meta)
|
train(cfg=cfg, dataset_meta=dataset_meta)
|
||||||
check_model_output_exists(temp_dir, cfg)
|
check_model_output_exists(temp_dir, cfg)
|
||||||
|
|
||||||
|
@with_temp_dir
|
||||||
|
def test_came_pytorch(self, temp_dir):
|
||||||
|
# pylint: disable=duplicate-code
|
||||||
|
cfg = DictDefault(
|
||||||
|
{
|
||||||
|
"base_model": "JackFram/llama-68m",
|
||||||
|
"tokenizer_type": "LlamaTokenizer",
|
||||||
|
"sequence_len": 1024,
|
||||||
|
"load_in_8bit": True,
|
||||||
|
"adapter": "lora",
|
||||||
|
"lora_r": 8,
|
||||||
|
"lora_alpha": 16,
|
||||||
|
"lora_dropout": 0.05,
|
||||||
|
"lora_target_linear": True,
|
||||||
|
"val_set_size": 0.1,
|
||||||
|
"special_tokens": {
|
||||||
|
"unk_token": "<unk>",
|
||||||
|
"bos_token": "<s>",
|
||||||
|
"eos_token": "</s>",
|
||||||
|
},
|
||||||
|
"datasets": [
|
||||||
|
{
|
||||||
|
"path": "mhenrichsen/alpaca_2k_test",
|
||||||
|
"type": "alpaca",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"num_epochs": 1,
|
||||||
|
"micro_batch_size": 8,
|
||||||
|
"gradient_accumulation_steps": 1,
|
||||||
|
"output_dir": temp_dir,
|
||||||
|
"learning_rate": 0.00001,
|
||||||
|
"optimizer": "came_pytorch",
|
||||||
|
"adam_beta3": 0.9999,
|
||||||
|
"adam_epsilon2": 1e-16,
|
||||||
|
"max_steps": 5,
|
||||||
|
"lr_scheduler": "cosine",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
cfg = validate_config(cfg)
|
||||||
|
normalize_config(cfg)
|
||||||
|
cli_args = TrainerCliArgs()
|
||||||
|
dataset_meta = load_datasets(cfg=cfg, cli_args=cli_args)
|
||||||
|
|
||||||
|
train(cfg=cfg, dataset_meta=dataset_meta)
|
||||||
|
check_model_output_exists(temp_dir, cfg)
|
||||||
|
|||||||
@@ -414,7 +414,6 @@ class TestDatasetPreparation:
|
|||||||
snapshot_path = snapshot_download(
|
snapshot_path = snapshot_download(
|
||||||
repo_id="mhenrichsen/alpaca_2k_test",
|
repo_id="mhenrichsen/alpaca_2k_test",
|
||||||
repo_type="dataset",
|
repo_type="dataset",
|
||||||
local_dir=tmp_ds_path,
|
|
||||||
)
|
)
|
||||||
shutil.copytree(snapshot_path, tmp_ds_path, dirs_exist_ok=True)
|
shutil.copytree(snapshot_path, tmp_ds_path, dirs_exist_ok=True)
|
||||||
|
|
||||||
|
|||||||
@@ -106,3 +106,4 @@ class TestBatchedSamplerPacking:
|
|||||||
|
|
||||||
original_idxs = set(range(len(train_dataset)))
|
original_idxs = set(range(len(train_dataset)))
|
||||||
assert original_idxs == set(batch_idxs)
|
assert original_idxs == set(batch_idxs)
|
||||||
|
assert len(batch_idxs) == len(set(batch_idxs))
|
||||||
|
|||||||
Reference in New Issue
Block a user