unsloth/unsloth/__init__.py
DoubleMathew a932294627
MLX training support for Studio on Apple Silicon (#5340)
* 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>
2026-05-14 05:24:20 -07:00

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__