* mlx fixes * Fix studio integration, local dataset files, chat templates without the torch gpu imports * pass grad norm in mlx worker * fix(studio): pass MLX grad clipping settings * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * mlx: update grad value * fix(mlx): address ci and clipping review * fix backward compatibility and CI tests * unsloth local is mlx function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * dont reference runtime * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * studio mlx: hardcode value clipping, drop max_grad_value from frontend Simplifies the MLX grad-clipping plumbing now that we are standardising on elementwise value clipping at [-5, 5] for the compiled MLX path and norm clipping disabled. The MLX worker no longer reads max_grad_norm / max_grad_value from the request; both are pinned in one place. Frontend stops sending the field at all, and the TypeScript request type drops it to match. Non-MLX (CUDA/AMD/Intel) is untouched and continues to pick up HF TrainingArguments' default max_grad_norm = 1.0. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
145 lines
5.3 KiB
Python
145 lines
5.3 KiB
Python
# Copyright 2023-present Daniel Han-Chen & the Unsloth team. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import os, importlib.util, platform
|
|
|
|
os.environ["UNSLOTH_IS_PRESENT"] = "1"
|
|
|
|
|
|
def _is_mlx_available():
|
|
# Transitional import barrier: while the paired unsloth-zoo MLX runtime
|
|
# rollout is in flight, keep non-Apple-Silicon imports from touching
|
|
# unsloth_zoo here. After both PRs are released together and
|
|
# unsloth_zoo.mlx is guaranteed to be import-safe on GPU hosts,
|
|
# this helper can collapse back to the centralized zoo runtime call below.
|
|
if (
|
|
os.environ.get("UNSLOTH_FORCE_GPU_PATH", "0") == "1"
|
|
or platform.system() != "Darwin"
|
|
or platform.machine() != "arm64"
|
|
or importlib.util.find_spec("mlx") is None
|
|
):
|
|
return False
|
|
try:
|
|
from unsloth_zoo.mlx import is_mlx_available
|
|
except ImportError:
|
|
return False
|
|
return is_mlx_available()
|
|
|
|
|
|
# Detect Apple Silicon + MLX before any torch/numpy imports
|
|
_IS_MLX = _is_mlx_available()
|
|
|
|
if _IS_MLX:
|
|
try:
|
|
import unsloth_zoo
|
|
except ImportError as _e:
|
|
raise ImportError(
|
|
"Unsloth: MLX support requires `unsloth-zoo` with MLX modules. "
|
|
"Reinstall with `pip install unsloth-zoo` or rerun install.sh."
|
|
) from _e
|
|
# The mlx.trainer / mlx.loader submodules ship with unsloth-zoo's MLX
|
|
# support. An older installed unsloth-zoo (e.g. from PyPI before the
|
|
# MLX release lands) will satisfy `import unsloth_zoo` but be missing
|
|
# these submodules. Surface the same friendly install hint instead of
|
|
# a raw ImportError on the submodule path.
|
|
try:
|
|
from unsloth_zoo.mlx.trainer import MLXTrainer, MLXTrainingConfig
|
|
from unsloth_zoo.mlx.loader import FastMLXModel
|
|
except ImportError as _e:
|
|
raise ImportError(
|
|
"Unsloth: MLX support requires an unsloth-zoo build that includes "
|
|
"`unsloth_zoo.mlx.trainer` and `unsloth_zoo.mlx.loader`. Upgrade with "
|
|
"`pip install -U unsloth-zoo` or rerun install.sh."
|
|
) from _e
|
|
|
|
# Load raw_text helpers without executing dataprep/__init__.py, which
|
|
# imports synthetic.py -> torch and would defeat the torch-free MLX path.
|
|
from pathlib import Path as _Path
|
|
|
|
_raw_text_path = _Path(__file__).resolve().parent / "dataprep" / "raw_text.py"
|
|
_raw_text_spec = importlib.util.spec_from_file_location(
|
|
"unsloth._mlx_raw_text", _raw_text_path
|
|
)
|
|
if _raw_text_spec is None or _raw_text_spec.loader is None:
|
|
raise ImportError("Unsloth: could not load MLX raw_text dataprep helpers.")
|
|
_raw_text = importlib.util.module_from_spec(_raw_text_spec)
|
|
_raw_text_spec.loader.exec_module(_raw_text)
|
|
RawTextDataLoader = _raw_text.RawTextDataLoader
|
|
TextPreprocessor = _raw_text.TextPreprocessor
|
|
del _raw_text, _raw_text_spec, _raw_text_path, _Path
|
|
|
|
__version__ = unsloth_zoo.__version__
|
|
DEVICE_TYPE = "mlx"
|
|
|
|
class FastLanguageModel:
|
|
@staticmethod
|
|
def from_pretrained(*args, **kwargs):
|
|
return FastMLXModel.from_pretrained(*args, **kwargs)
|
|
|
|
@staticmethod
|
|
def get_peft_model(*args, **kwargs):
|
|
return FastMLXModel.get_peft_model(*args, **kwargs)
|
|
|
|
@staticmethod
|
|
def for_inference(*args, **kwargs):
|
|
return args[0] if args else None
|
|
|
|
class FastVisionModel(FastLanguageModel):
|
|
@staticmethod
|
|
def from_pretrained(*args, **kwargs):
|
|
kwargs.setdefault("text_only", False)
|
|
return FastMLXModel.from_pretrained(*args, **kwargs)
|
|
|
|
@staticmethod
|
|
def for_training(*args, **kwargs):
|
|
return args[0] if args else None
|
|
|
|
FastTextModel = FastLanguageModel
|
|
FastModel = FastLanguageModel
|
|
|
|
class FastSentenceTransformer:
|
|
@staticmethod
|
|
def from_pretrained(*args, **kwargs):
|
|
raise NotImplementedError(
|
|
"Unsloth: FastSentenceTransformer is not yet supported on MLX."
|
|
)
|
|
|
|
@staticmethod
|
|
def get_peft_model(*args, **kwargs):
|
|
raise NotImplementedError(
|
|
"Unsloth: FastSentenceTransformer is not yet supported on MLX."
|
|
)
|
|
|
|
def is_bfloat16_supported():
|
|
try:
|
|
import mlx.core as mx
|
|
|
|
name = mx.device_info().get("device_name", "") or ""
|
|
return not name.startswith(("Apple M1", "Apple M2"))
|
|
except Exception:
|
|
return True
|
|
|
|
is_bf16_supported = is_bfloat16_supported
|
|
|
|
class UnslothVisionDataCollator:
|
|
def __init__(self, *args, **kwargs):
|
|
raise NotImplementedError(
|
|
"Unsloth: UnslothVisionDataCollator is not used on MLX. "
|
|
"Use the MLX trainer/data path instead."
|
|
)
|
|
|
|
else:
|
|
# GPU path: load everything from _gpu_init
|
|
from ._gpu_init import *
|
|
from ._gpu_init import __version__
|