GRPO, vLLM, Bug Fixes, Reinforcement Learning (#1620)

* use exact model name

* Update save.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* print

* Update _utils.py

* Update _utils.py

* Update llama.py

* Update _utils.py

* Update vision.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update loader.py

* accurate_accumulation

* Update loader.py

* Update loader.py

* Update _utils.py

* Update loader.py

* Update loader.py

* Update loader.py

* Update loader.py

* Update pyproject.toml

* Update __init__.py

* Update pyproject.toml

* Update __init__.py

* Update __init__.py

* Fix Triton heuristics

https://github.com/triton-lang/triton/issues/5224

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Xformers

* Update loader.py

* Update loader.py

* Rewind

* Update _utils.py

* Update _utils.py

* requires grad

* Update loader.py

* Update _utils.py

* Update loader.py

* changing model to base_model if peft model is already used

* Improve debugging experience (#1512)

* Create CONTRIBUTING.md (#1472)

Creating contributing guidelines

* Update CONTRIBUTING.md

improved sentence

* Improve logging control in `unsloth_compile_transformers` by conditionally redirecting stdout based on UNSLOTH_DISABLE_LOGGER environment variable

---------

Co-authored-by: Michael Han <107991372+shimmyshimmer@users.noreply.github.com>
Co-authored-by: Nino Risteski <95188570+NinoRisteski@users.noreply.github.com>

* Update loader.py

* Update llama.py

* Update llama.py

* Revert "Update llama.py"

This reverts commit a8edd0931a.

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Auto change is_bfloat16_supported

* Update llama.py

* Force data-type

* Update llama.py

* All attention refactor fix (#1491)

* change initilization of n_heads, n_kv_heads, hidden_size in llama.py

* do the same for cohere, mistral, gemma2, granite

* do the same for flexattention,cohere, mistral, granite

* Update llama.py

* Update llama.py

* Update granite to work with latest post_patch methods (#1502)

* Update granite to work with latest post_patch methods

* Pass position_embeddings for granite even if transformers<4.47

* Update llama.py

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>

* Minor fixes for granite models (#1503)

* Update granite.py

Grab residual multiplier directly from layer

* Update llama.py

Version should read >= 4.47.1 as that is the version requiring the changes

* Update granite.py

* Update llama.py

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>

* support modelscope models and datasets (#1481)

* support modelscope

* change modelscope args

* remove useless import

* remove useless import

* fix

* wip

* fix

* remove useless code

* add readme

* add some comments

* change print to raise error

* update comment

* Update loader.py

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>

* Merge branch 'main' into nightly

* Phi 4

* Update llama.py

* Torch.Cuda Is Available Condition and Warning (#1545)

* check for torch.cuda and triton if available
on my machine(mac m3) the cuda were not available

* Update pyproject.toml

* Update __init__.py

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>

* Update mistral.py

* Update mistral.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Update _utils.py

* Fix

* Bug fixes

* Update mapper.py

* Add dropout to granite to match HF's implementation (#1557)

Signed-off-by: datta0 <venkatadattasainimmaturi@gmail.com>

* Update llama.py

* Update llama.py

* Bug fixes

* fix: flash_attn_detection_error (#1556)

* fix: flash_attn_detection_error

* Update _utils.py

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>

* Update mapper.py

* Update gemma.py

* Update gemma.py

* Update gemma.py

* Update gemma.py

* dim fix

* Update _utils.py

* Torch 2.6 support

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Update llama.py

* Faster inference?

* Update llama.py

* Update llama.py

* Update utils.py

* Update llama.py

* Update llama.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update utils.py

* Update mapper.py

* Fast Inference via vLLM

* Update llama.py

* Update llama.py

* Update utils.py

* Create rl.py

* PatchRL

* Update rl.py

* Update rl.py

* Update rl.py

* PatchRLStatistics

* Update rl.py

* Update rl.py

* Update rl.py

* Update utils.py

* Update utils.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* RL metrics

* Update rl.py

* RL metrics

* Update __init__.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update chat_templates.py

* Update mapper.py

* Fp8 cache

* Update llama.py

* Update llama.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update rl.py

* Update __init__.py

* Update loader.py

---------

Signed-off-by: datta0 <venkatadattasainimmaturi@gmail.com>
Co-authored-by: Itsuro Tajima <tajima@georepublic.de>
Co-authored-by: Muhammad Osama <muhammadosama1994@gmail.com>
Co-authored-by: Edd <68678137+Erland366@users.noreply.github.com>
Co-authored-by: Michael Han <107991372+shimmyshimmer@users.noreply.github.com>
Co-authored-by: Nino Risteski <95188570+NinoRisteski@users.noreply.github.com>
Co-authored-by: Kareem <81531392+KareemMusleh@users.noreply.github.com>
Co-authored-by: Datta Nimmaturi <datta.nimmaturi@nutanix.com>
Co-authored-by: Z <coffeevampirebusiness@gmail.com>
Co-authored-by: tastelikefeet <58414341+tastelikefeet@users.noreply.github.com>
Co-authored-by: AminWhat <88392440+aminwhat@users.noreply.github.com>
Co-authored-by: Zhe Zhang <2631992879@qq.com>
This commit is contained in:
Daniel Han 2025-02-06 02:41:12 -08:00 committed by GitHub
commit 144190bd06
13 changed files with 918 additions and 283 deletions

View file

@ -131,6 +131,12 @@ cu124onlytorch240 = [
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.28.post1-cp311-cp311-win_amd64.whl ; python_version=='3.11' and platform_system == 'Windows'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.28.post1-cp312-cp312-win_amd64.whl ; python_version=='3.12' and platform_system == 'Windows'",
]
cu118onlytorch250 = [
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.28.post2-cp39-cp39-manylinux_2_28_x86_64.whl ; python_version=='3.9' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.28.post2-cp310-cp310-manylinux_2_28_x86_64.whl ; python_version=='3.10' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.28.post2-cp311-cp311-manylinux_2_28_x86_64.whl ; python_version=='3.11' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.28.post2-cp312-cp312-manylinux_2_28_x86_64.whl ; python_version=='3.12' and platform_system == 'Linux'",
]
cu121onlytorch250 = [
"xformers @ https://download.pytorch.org/whl/cu121/xformers-0.0.28.post2-cp39-cp39-manylinux_2_28_x86_64.whl ; python_version=='3.9' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu121/xformers-0.0.28.post2-cp310-cp310-manylinux_2_28_x86_64.whl ; python_version=='3.10' and platform_system == 'Linux'",
@ -147,6 +153,12 @@ cu124onlytorch250 = [
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.28.post2-cp311-cp311-win_amd64.whl ; python_version=='3.11' and platform_system == 'Windows'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.28.post2-cp312-cp312-win_amd64.whl ; python_version=='3.12' and platform_system == 'Windows'",
]
cu118onlytorch251 = [
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.29.post1-cp39-cp39-manylinux_2_28_x86_64.whl ; python_version=='3.9' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.29.post1-cp310-cp310-manylinux_2_28_x86_64.whl ; python_version=='3.10' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.29.post1-cp311-cp311-manylinux_2_28_x86_64.whl ; python_version=='3.11' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.29.post1-cp312-cp312-manylinux_2_28_x86_64.whl ; python_version=='3.12' and platform_system == 'Linux'",
]
cu121onlytorch251 = [
"xformers @ https://download.pytorch.org/whl/cu121/xformers-0.0.29.post1-cp39-cp39-manylinux_2_28_x86_64.whl ; python_version=='3.9' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu121/xformers-0.0.29.post1-cp310-cp310-manylinux_2_28_x86_64.whl ; python_version=='3.10' and platform_system == 'Linux'",
@ -163,6 +175,28 @@ cu124onlytorch251 = [
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post1-cp311-cp311-win_amd64.whl ; python_version=='3.11' and platform_system == 'Windows'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post1-cp312-cp312-win_amd64.whl ; python_version=='3.12' and platform_system == 'Windows'",
]
cu118onlytorch260 = [
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.29.post2-cp39-cp39-manylinux_2_28_x86_64.whl ; python_version=='3.9' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.29.post2-cp310-cp310-manylinux_2_28_x86_64.whl ; python_version=='3.10' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.29.post2-cp311-cp311-manylinux_2_28_x86_64.whl ; python_version=='3.11' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu118/xformers-0.0.29.post2-cp312-cp312-manylinux_2_28_x86_64.whl ; python_version=='3.12' and platform_system == 'Linux'",
]
cu124onlytorch260 = [
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post2-cp39-cp39-manylinux_2_28_x86_64.whl ; python_version=='3.9' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post2-cp310-cp310-manylinux_2_28_x86_64.whl ; python_version=='3.10' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post2-cp311-cp311-manylinux_2_28_x86_64.whl ; python_version=='3.11' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post2-cp312-cp312-manylinux_2_28_x86_64.whl ; python_version=='3.12' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post2-cp39-cp39-win_amd64.whl ; python_version=='3.9' and platform_system == 'Windows'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post2-cp310-cp310-win_amd64.whl ; python_version=='3.10' and platform_system == 'Windows'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post2-cp311-cp311-win_amd64.whl ; python_version=='3.11' and platform_system == 'Windows'",
"xformers @ https://download.pytorch.org/whl/cu124/xformers-0.0.29.post2-cp312-cp312-win_amd64.whl ; python_version=='3.12' and platform_system == 'Windows'",
]
cu126onlytorch260 = [
"xformers @ https://download.pytorch.org/whl/cu126/xformers-0.0.29.post2-cp39-cp39-manylinux_2_28_x86_64.whl ; python_version=='3.9' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu126/xformers-0.0.29.post2-cp310-cp310-manylinux_2_28_x86_64.whl ; python_version=='3.10' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu126/xformers-0.0.29.post2-cp311-cp311-manylinux_2_28_x86_64.whl ; python_version=='3.11' and platform_system == 'Linux'",
"xformers @ https://download.pytorch.org/whl/cu126/xformers-0.0.29.post2-cp312-cp312-manylinux_2_28_x86_64.whl ; python_version=='3.12' and platform_system == 'Linux'",
]
cu118 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
@ -223,21 +257,31 @@ cu121-torch240 = [
"bitsandbytes>=0.43.3",
"unsloth[cu121onlytorch240]",
]
cu121-torch250 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu121onlytorch250]",
]
cu124-torch240 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu124onlytorch240]",
]
cu118-torch250 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu118onlytorch250]",
]
cu121-torch250 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu121onlytorch250]",
]
cu124-torch250 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu124onlytorch250]",
]
cu118-torch251 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu118onlytorch251]",
]
cu121-torch251 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
@ -248,6 +292,21 @@ cu124-torch251 = [
"bitsandbytes>=0.43.3",
"unsloth[cu124onlytorch251]",
]
cu118-torch260 = [
"unsloth[huggingface]",
"bitsandbytes>=0.45.1",
"unsloth[cu118onlytorch260]",
]
cu124-torch260 = [
"unsloth[huggingface]",
"bitsandbytes>=0.45.1",
"unsloth[cu124onlytorch260]",
]
cu126-torch260 = [
"unsloth[huggingface]",
"bitsandbytes>=0.45.1",
"unsloth[cu126onlytorch260]",
]
kaggle = [
"unsloth[huggingface]",
]
@ -381,24 +440,36 @@ cu121-ampere-torch240 = [
"unsloth[cu121onlytorch240]",
"unsloth[flashattention]",
]
cu121-ampere-torch250 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu121onlytorch250]",
"unsloth[flashattention]",
]
cu124-ampere-torch240 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu124onlytorch240]",
"unsloth[flashattention]",
]
cu118-ampere-torch250 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu118onlytorch250]",
"unsloth[flashattention]",
]
cu121-ampere-torch250 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu121onlytorch250]",
"unsloth[flashattention]",
]
cu124-ampere-torch250 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu124onlytorch250]",
"unsloth[flashattention]",
]
cu118-ampere-torch251 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
"unsloth[cu118onlytorch251]",
"unsloth[flashattention]",
]
cu121-ampere-torch251 = [
"unsloth[huggingface]",
"bitsandbytes>=0.43.3",
@ -411,6 +482,24 @@ cu124-ampere-torch251 = [
"unsloth[cu124onlytorch251]",
"unsloth[flashattention]",
]
cu118-ampere-torch260 = [
"unsloth[huggingface]",
"bitsandbytes>=0.45.1",
"unsloth[cu118onlytorch260]",
"unsloth[flashattention]",
]
cu124-ampere-torch260 = [
"unsloth[huggingface]",
"bitsandbytes>=0.45.1",
"unsloth[cu124onlytorch260]",
"unsloth[flashattention]",
]
cu126-ampere-torch260 = [
"unsloth[huggingface]",
"bitsandbytes>=0.45.1",
"unsloth[cu126onlytorch260]",
"unsloth[flashattention]",
]
[project.urls]
homepage = "http://www.unsloth.ai"

View file

@ -196,7 +196,7 @@ pass
# Check for unsloth_zoo
try:
unsloth_zoo_version = importlib_version("unsloth_zoo")
if Version(unsloth_zoo_version) < Version("2025.1.4"):
if Version(unsloth_zoo_version) < Version("2025.2.1"):
try:
os.system("pip install --upgrade --no-cache-dir --no-deps unsloth_zoo")
except:

View file

@ -18,14 +18,16 @@ from packaging.version import Version as V
v = V(torch.__version__)
cuda = str(torch.version.cuda)
is_ampere = torch.cuda.get_device_capability()[0] >= 8
if cuda != "12.1" and cuda != "11.8" and cuda != "12.4": raise RuntimeError(f"CUDA = {cuda} not supported!")
if cuda != "12.1" and cuda != "11.8" and cuda != "12.4" and cuda != "12.6": raise RuntimeError(f"CUDA = {cuda} not supported!")
if v <= V('2.1.0'): raise RuntimeError(f"Torch = {v} too old!")
elif v <= V('2.1.1'): x = 'cu{}{}-torch211'
elif v <= V('2.1.2'): x = 'cu{}{}-torch212'
elif v < V('2.3.0'): x = 'cu{}{}-torch220'
elif v < V('2.4.0'): x = 'cu{}{}-torch230'
elif v < V('2.5.0'): x = 'cu{}{}-torch240'
elif v < V('2.6.0'): x = 'cu{}{}-torch250'
elif v < V('2.5.1'): x = 'cu{}{}-torch250'
elif v <= V('2.5.1'): x = 'cu{}{}-torch251'
elif v < V('2.7.0'): x = 'cu{}{}-torch260'
else: raise RuntimeError(f"Torch = {v} too new!")
x = x.format(cuda.replace(".", ""), "-ampere" if is_ampere else "")
print(f'pip install --upgrade pip && pip install "unsloth[{x}] @ git+https://github.com/unslothai/unsloth.git"')

View file

@ -759,6 +759,10 @@ DEFAULT_SYSTEM_MESSAGE["llama-3.1"] = "" # Llama3.1 default system message is em
CHAT_TEMPLATES["llama-31"] = (llama31_template, llama31_template_eos_token, False, llama31_ollama,)
DEFAULT_SYSTEM_MESSAGE["llama-31"] = "" # Llama3.1 default system message is empty + the dates
for version in ("llama-3.2", "llama-3.3", "llama-32", "llama-33"):
CHAT_TEMPLATES[version] = CHAT_TEMPLATES["llama-3.1"]
DEFAULT_SYSTEM_MESSAGE[version] = ""
pass

View file

@ -15,6 +15,7 @@
import triton
MAX_FUSED_SIZE : int = 65536
next_power_of_2 = triton.next_power_of_2
import functools
# torch.cuda.amp.custom_fwd is deprecated >= 2.4
import torch
@ -66,6 +67,8 @@ global CUDA_STREAM
CUDA_STREAM = None
get_ptr = bnb.functional.get_ptr
import ctypes
ctypes_c_int = ctypes.c_int
ctypes_c_int32 = ctypes.c_int32
cdequantize_blockwise_fp32 = bnb.functional.lib.cdequantize_blockwise_fp32
cdequantize_blockwise_fp16_nf4 = bnb.functional.lib.cdequantize_blockwise_fp16_nf4
cdequantize_blockwise_bf16_nf4 = bnb.functional.lib.cdequantize_blockwise_bf16_nf4
@ -98,25 +101,31 @@ pass
def get_lora_parameters_bias(proj):
# For DPO or disabled adapters
base_layer = (proj.base_layer if hasattr(proj, "base_layer") else proj)
base_layer = getattr(proj, "base_layer", proj) # (proj.base_layer if hasattr(proj, "base_layer") else proj)
W = base_layer.weight
bias = base_layer.bias
if not hasattr(proj, "disable_adapters") or proj.disable_adapters or proj.merged:
# if not hasattr(proj, "disable_adapters") or proj.disable_adapters or proj.merged:
if getattr(proj, "disable_adapters", True) or proj.merged:
return W, QUANT_STATE(W), None, None, None, bias
pass
active_adapter = proj.active_adapters[0] if \
hasattr(proj, "active_adapters") else proj.active_adapter
getattr(proj, "active_adapters", ) else proj.active_adapter
A = proj.lora_A [active_adapter].weight
B = proj.lora_B [active_adapter].weight
s = proj.scaling[active_adapter]
return W, QUANT_STATE(W), A, B, s, bias
pass
global WEIGHT_BUFFER
WEIGHT_BUFFER = None
global ABSMAX_BUFFER
ABSMAX_BUFFER = None
if HAS_CUDA_STREAM:
def fast_dequantize(W, quant_state = None, out = None):
@torch.inference_mode
def fast_dequantize(W, quant_state = None, out = None, use_global_buffer = False):
if quant_state is None: return W
if type(quant_state) is not list:
# New quant_state as a class
@ -139,36 +148,54 @@ if HAS_CUDA_STREAM:
global CUDA_STREAM
if CUDA_STREAM is None: CUDA_STREAM = torch.cuda.current_stream("cuda:0")
n_elements_absmax = absmax.numel()
# Create weight matrix
if out is None:
out = torch.empty(shape, dtype = dtype, device = "cuda:0")
if use_global_buffer:
# Use same buffers for faster inference
size = shape[0]*shape[1]
global WEIGHT_BUFFER
global ABSMAX_BUFFER
if WEIGHT_BUFFER is None:
WEIGHT_BUFFER = torch.empty(size, dtype = dtype, device = "cuda:0", requires_grad = False)
ABSMAX_BUFFER = torch.empty(n_elements_absmax, dtype = torch.float32, device = "cuda:0", requires_grad = False)
if size > WEIGHT_BUFFER.numel(): WEIGHT_BUFFER.resize_(size)
if n_elements_absmax > ABSMAX_BUFFER.numel(): ABSMAX_BUFFER.resize_(n_elements_absmax)
out = WEIGHT_BUFFER[:size].view(shape)
out_absmax = ABSMAX_BUFFER[:n_elements_absmax]
else:
assert(out.shape == shape)
assert(out.dtype == dtype)
if out is None:
out = torch.empty(shape, dtype = dtype, device = "cuda:0", requires_grad = False)
else:
assert(out.shape == shape)
assert(out.dtype == dtype)
out_absmax = torch.empty(n_elements_absmax, dtype = torch.float32, device = "cuda:0", requires_grad = False)
pass
# NF4 dequantization of statistics
n_elements_absmax = absmax.numel()
out_absmax = torch.empty(n_elements_absmax, dtype = torch.float32, device = "cuda:0")
# Do dequantization
ptr_out_absmax = get_ptr(out_absmax)
cdequantize_blockwise_fp32(
get_ptr(code2), get_ptr(absmax), get_ptr(absmax2), ptr_out_absmax,
ctypes.c_int(blocksize2), ctypes.c_int(n_elements_absmax), CUDA_STREAM,
ctypes_c_int(blocksize2), ctypes_c_int(n_elements_absmax), CUDA_STREAM,
)
out_absmax += offset
# Dequantize W
fx = cdequantize_blockwise_fp16_nf4 if dtype == torch.float16 else \
cdequantize_blockwise_bf16_nf4
fx(get_ptr(None), get_ptr(W), ptr_out_absmax, get_ptr(out),
ctypes.c_int(blocksize), ctypes.c_int(out.numel()), CUDA_STREAM,)
ctypes_c_int(blocksize), ctypes_c_int(out.numel()), CUDA_STREAM,)
# Careful returning transposed data
is_transposed = (True if W.shape[0] == 1 else False)
return out.t() if is_transposed else out
pass
else:
def fast_dequantize(W, quant_state = None, out = None):
@torch.inference_mode
def fast_dequantize(W, quant_state = None, out = None, use_global_buffer = False):
if quant_state is None: return W
if type(quant_state) is not list:
# New quant_state as a class
@ -189,29 +216,45 @@ else:
absmax2, code2, blocksize2, _, _, _, _ = state2
pass
# Create weight matrix
if out is None:
out = torch.empty(shape, dtype = dtype, device = "cuda:0")
else:
assert(out.shape == shape)
assert(out.dtype == dtype)
# NF4 dequantization of statistics
n_elements_absmax = absmax.numel()
out_absmax = torch.empty(n_elements_absmax, dtype = torch.float32, device = "cuda:0")
# Create weight matrix
if use_global_buffer:
# Use same buffers for faster inference
size = shape[0]*shape[1]
global WEIGHT_BUFFER
global ABSMAX_BUFFER
if WEIGHT_BUFFER is None:
WEIGHT_BUFFER = torch.empty(size, dtype = dtype, device = "cuda:0", requires_grad = False)
ABSMAX_BUFFER = torch.empty(n_elements_absmax, dtype = dtype, device = "cuda:0", requires_grad = False)
if size > WEIGHT_BUFFER.numel(): WEIGHT_BUFFER.resize_(size)
if n_elements_absmax > ABSMAX_BUFFER.numel(): ABSMAX_BUFFER.resize_(n_elements_absmax)
out = WEIGHT_BUFFER[:size].view(shape)
out_absmax = ABSMAX_BUFFER[:n_elements_absmax]
else:
if out is None:
out = torch.empty(shape, dtype = dtype, device = "cuda:0", requires_grad = False)
else:
assert(out.shape == shape)
assert(out.dtype == dtype)
out_absmax = torch.empty(n_elements_absmax, dtype = torch.float32, device = "cuda:0", requires_grad = False)
pass
# Do dequantization
ptr_out_absmax = get_ptr(out_absmax)
cdequantize_blockwise_fp32(
get_ptr(code2), get_ptr(absmax), get_ptr(absmax2), ptr_out_absmax,
ctypes.c_int(blocksize2), ctypes.c_int(n_elements_absmax),
ctypes_c_int(blocksize2), ctypes_c_int(n_elements_absmax),
)
out_absmax += offset
fx = cdequantize_blockwise_fp16_nf4 if dtype == torch.float16 else \
cdequantize_blockwise_bf16_nf4
fx(get_ptr(None), get_ptr(W), ptr_out_absmax, get_ptr(out),
ctypes.c_int(blocksize), ctypes.c_int(out.numel()),)
ctypes_c_int(blocksize), ctypes_c_int(out.numel()),)
# Careful returning transposed data
is_transposed = (True if W.shape[0] == 1 else False)
@ -263,17 +306,17 @@ if HAS_CUDA_STREAM:
lda = shape[0]
ldc = shape[0]
ldb = (hd+1)//2
m = ctypes.c_int32(m)
n = ctypes.c_int32(n)
k = ctypes.c_int32(k)
lda = ctypes.c_int32(lda)
ldb = ctypes.c_int32(ldb)
ldc = ctypes.c_int32(ldc)
m = ctypes_c_int32(m)
n = ctypes_c_int32(n)
k = ctypes_c_int32(k)
lda = ctypes_c_int32(lda)
ldb = ctypes_c_int32(ldb)
ldc = ctypes_c_int32(ldc)
df = torch.empty(absmax.shape, dtype = torch.float32, device = "cuda:0")
cdequantize_blockwise_fp32(
get_ptr(code2), get_ptr(absmax), get_ptr(absmax2), get_ptr(df),
ctypes.c_int(blocksize2), ctypes.c_int(df.numel()), CUDA_STREAM,
ctypes_c_int(blocksize2), ctypes_c_int(df.numel()), CUDA_STREAM,
)
df += offset
absmax = df
@ -281,7 +324,7 @@ if HAS_CUDA_STREAM:
fx = cgemm_4bit_inference_naive_fp16 if dtype == torch.float16 else \
cgemm_4bit_inference_naive_bf16
blocksize = ctypes.c_int32(blocksize)
blocksize = ctypes_c_int32(blocksize)
fx(m, n, k, get_ptr(X), get_ptr(W), get_ptr(absmax), get_ptr(stats), get_ptr(out),
lda, ldb, ldc, blocksize, CUDA_STREAM,)
@ -327,17 +370,17 @@ else:
lda = shape[0]
ldc = shape[0]
ldb = (hd+1)//2
m = ctypes.c_int32(m)
n = ctypes.c_int32(n)
k = ctypes.c_int32(k)
lda = ctypes.c_int32(lda)
ldb = ctypes.c_int32(ldb)
ldc = ctypes.c_int32(ldc)
m = ctypes_c_int32(m)
n = ctypes_c_int32(n)
k = ctypes_c_int32(k)
lda = ctypes_c_int32(lda)
ldb = ctypes_c_int32(ldb)
ldc = ctypes_c_int32(ldc)
df = torch.empty(absmax.shape, dtype = torch.float32, device = "cuda:0")
cdequantize_blockwise_fp32(
get_ptr(code2), get_ptr(absmax), get_ptr(absmax2), get_ptr(df),
ctypes.c_int(blocksize2), ctypes.c_int(df.numel()),
ctypes_c_int(blocksize2), ctypes_c_int(df.numel()),
)
df += offset
absmax = df
@ -345,7 +388,7 @@ else:
fx = cgemm_4bit_inference_naive_fp16 if dtype == torch.float16 else \
cgemm_4bit_inference_naive_bf16
blocksize = ctypes.c_int32(blocksize)
blocksize = ctypes_c_int32(blocksize)
fx(m, n, k, get_ptr(X), get_ptr(W), get_ptr(absmax), get_ptr(stats), get_ptr(out),
lda, ldb, ldc, blocksize,)
@ -354,6 +397,9 @@ else:
pass
torch_mm = torch.mm
torch_mv = torch.mv
torch_matmul = torch.matmul
def fast_linear_forward(proj, X, temp_lora = None, out = None):
W, W_quant, lora_A, lora_B, lora_S, bias = get_lora_parameters_bias(proj)
@ -361,12 +407,12 @@ def fast_linear_forward(proj, X, temp_lora = None, out = None):
if q_len != 1: return matmul_lora(X, W, W_quant, lora_A, lora_B, lora_S)
if W_quant is None:
out = torch.matmul(X, W.t(), out = out)
out = torch_matmul(X, W.t(), out = out)
elif bsz == 1 and q_len == 1:
out = fast_gemv(X, W, W_quant, out = out)
else:
W = fast_dequantize(W.t(), W_quant)
out = torch.matmul(X, W, out = out)
W = fast_dequantize(W.t(), W_quant, use_global_buffer = True)
out = torch_matmul(X, W, out = out)
pass
# Add in LoRA weights
@ -381,11 +427,11 @@ def fast_linear_forward(proj, X, temp_lora = None, out = None):
if bsz == 1:
out = out.view(out_dim)
temp_lora = torch.mv(lora_A._fast_lora, X.ravel(), out = temp_lora)
temp_lora = torch_mv(lora_A._fast_lora, X.ravel(), out = temp_lora)
out.addmv_(lora_B._fast_lora, temp_lora, alpha = lora_S)
else:
out = out.view(bsz, out_dim)
temp_lora = torch.mm(X.view(bsz, in_dim), lora_A._fast_lora.t(), out = temp_lora)
temp_lora = torch_mm(X.view(bsz, in_dim), lora_A._fast_lora.t(), out = temp_lora)
out.addmm_(temp_lora, lora_B._fast_lora.t(), alpha = lora_S)
pass
out = out.view(bsz, 1, out_dim)
@ -399,7 +445,7 @@ pass
def matmul_lora(X, W, W_quant, A, B, s, out = None):
dtype = X.dtype
W = fast_dequantize(W.t(), W_quant)
W = fast_dequantize(W.t(), W_quant, use_global_buffer = True)
if X.dim() == 3:
batch, seq_len, d = X.shape
@ -409,7 +455,7 @@ def matmul_lora(X, W, W_quant, A, B, s, out = None):
reshape = False
pass
out = torch.matmul(X, W, out = out)
out = torch_matmul(X, W, out = out)
if W_quant is not None: del W
if A is not None:

View file

@ -20,3 +20,4 @@ from .mistral import FastMistralModel
from .qwen2 import FastQwen2Model
from .dpo import PatchDPOTrainer, PatchKTOTrainer
from ._utils import is_bfloat16_supported
from .rl import PatchFastRL

View file

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
__version__ = "2025.1.8"
__version__ = "2025.2.1"
__all__ = [
"SUPPORTS_BFLOAT16",

View file

@ -17,115 +17,8 @@ __all__ = [
"PatchKTOTrainer",
]
try:
from transformers.utils.notebook import (
IntervalStrategy,
NotebookTrainingTracker,
NotebookProgressCallback,
)
HAS_NOTEBOOK = True
except:
HAS_NOTEBOOK = False
pass
import torch
from ._utils import torch_compile_options
import inspect
import torch.nn as nn
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union
from .rl import PatchFastRL
def PatchDPOTrainer(): PatchFastRL("DPO")
DPOTrainer_metrics = [
"rewards/chosen",
"rewards/rejected",
"rewards/accuracies",
"rewards/margins",
"logps/rejected",
"logps/chosen",
"logits/rejected",
"logits/chosen",
]
set_DPOTrainer_metrics = frozenset(DPOTrainer_metrics)
def NotebookProgressCallback_on_train_begin(self, args, state, control, **kwargs):
self.first_column = "Epoch" if args.eval_strategy == IntervalStrategy.EPOCH else "Step"
self.training_loss = 0
self.last_log = 0
column_names = [self.first_column] + ["Training Loss"]
if args.eval_strategy != IntervalStrategy.NO:
column_names.append("Validation Loss")
column_names += [x.replace("/", " / ") for x in DPOTrainer_metrics]
self.training_tracker = NotebookTrainingTracker(state.max_steps, column_names)
pass
def NotebookProgressCallback_on_log(self, args, state, control, logs=None, **kwargs):
# Only for when there is no evaluation
if args.eval_strategy == IntervalStrategy.NO and "loss" in logs:
values = {"Training Loss": logs["loss"]}
for metric in DPOTrainer_metrics:
values[metric.replace("/", " / ")] = logs[metric]
pass
# First column is necessarily Step since we're not in epoch eval strategy
values["Step"] = state.global_step
self.training_tracker.write_line(values)
pass
pass
def NotebookTrainingTracker_write_line(self, values):
"""
Write the values in the inner table.
Args:
values (`Dict[str, float]`): The values to display.
"""
if self.inner_table is None:
self.inner_table = [list(values.keys()), list(values.values())]
else:
columns = self.inner_table[0]
new_values = {}
for key, value in values.items():
lowered = key.lower()
if lowered in set_DPOTrainer_metrics:
new_values[lowered.replace("/", " / ")] = value
else:
new_values[key] = value
pass
values = new_values
self.inner_table[0] = columns
if len(self.inner_table) > 1:
last_values = self.inner_table[-1]
first_column = self.inner_table[0][0]
if last_values[0] != values[first_column]:
# write new line
self.inner_table.append([values[c] if c in values else "No Log" for c in columns])
else:
# update last line
new_values = values
for c in columns:
if c not in new_values.keys():
new_values[c] = last_values[columns.index(c)]
self.inner_table[-1] = [new_values[c] for c in columns]
else:
# Edit for evaluation purposes
self.inner_table.append([values[c] if c in values else 0 for c in columns])
pass
pass
pass
def PatchDPOTrainer():
if HAS_NOTEBOOK:
from transformers.trainer import is_in_notebook
if is_in_notebook():
# Patch DPO notebook printing
NotebookTrainingTracker.write_line = NotebookTrainingTracker_write_line
from transformers.trainer import DEFAULT_PROGRESS_CALLBACK
DEFAULT_PROGRESS_CALLBACK.on_train_begin = NotebookProgressCallback_on_train_begin
DEFAULT_PROGRESS_CALLBACK.on_log = NotebookProgressCallback_on_log
pass
pass
pass
PatchKTOTrainer = PatchDPOTrainer
def PatchKTOTrainer(): PatchFastRL("KTO")

View file

@ -210,7 +210,15 @@ class GemmaFixedRotaryEmbedding(torch.nn.Module):
config = None, # [TODO] Hack to pass in config - need to remove later
):
super().__init__()
if config is not None: return # [TODO] Hack to pass in config - need to remove later
if config is not None:
# [TODO] Hack to pass in config - need to remove later
base = config.rope_theta
partial_rotary_factor = config.partial_rotary_factor if hasattr(config, "partial_rotary_factor") else 1.0
dim = getattr(config, "head_dim", None)
if dim is None: dim = int((config.hidden_size // config.num_attention_heads))
device = "cuda"
max_position_embeddings = config.max_position_embeddings
pass
self.dim = dim
self.max_position_embeddings = max_position_embeddings
self.base = base

View file

@ -20,7 +20,7 @@ from ._utils import *
from ._utils import __version__
from torch.nn.functional import scaled_dot_product_attention
from transformers import __version__ as transformers_version
from unsloth_zoo.utils import Version
from unsloth_zoo.utils import Version, _get_dtype
transformers_version = Version(transformers_version)
# Transformers moved rotary embeddings out of all attention layers
IS_ATTENTION_REFACTOR = transformers_version > Version("4.47.1")
@ -70,7 +70,8 @@ except:
from huggingface_hub.utils._token import get_token
pass
from triton import __version__ as triton_version
BlockDiagonalCausalMask = xformers.attn_bias.BlockDiagonalCausalMask if xformers is not None else None
HAS_XFORMERS = xformers is not None
BlockDiagonalCausalMask = xformers.attn_bias.BlockDiagonalCausalMask if HAS_XFORMERS else None
def original_apply_qkv(self, X):
@ -89,6 +90,8 @@ pass
from math import sqrt as math_sqrt
KV_CACHE_INCREMENT = 256 # KV Cache update size
torch_nn_functional_softmax = torch.nn.functional.softmax
# SDPA has GQA internally
SDPA_HAS_GQA = "enable_gqa" in scaled_dot_product_attention.__doc__
# Fix new HF's inference code
def _fast_prepare_inputs_for_generation(self, input_ids, **kwargs,):
@ -243,7 +246,7 @@ def LlamaAttention_fast_forward_inference(
# Grouped query attention
_, _, cached_len, _ = Knn.shape
if n_groups != 1:
if bsz == 1 or not SDPA_HAS_GQA and n_groups != 1:
Knn = Knn[:, :, None, :, :].expand(bsz, n_kv_heads, n_groups, cached_len, head_dim)
Vnn = Vnn[:, :, None, :, :].expand(bsz, n_kv_heads, n_groups, cached_len, head_dim)
Knn = Knn.reshape(bsz, n_heads, cached_len, head_dim)
@ -262,7 +265,10 @@ def LlamaAttention_fast_forward_inference(
A[:] = torch_nn_functional_softmax(A, dim = -1, dtype = torch.float32)#.to(A.dtype)
A = torch_matmul(A, Vnn, out = Qn)
else:
A = scaled_dot_product_attention(Qn, Knn, Vnn, attn_mask = attention_mask, is_causal = False)
if SDPA_HAS_GQA:
A = scaled_dot_product_attention(Qn, Knn, Vnn, attn_mask = attention_mask, is_causal = False, enable_gqa = True)
else:
A = scaled_dot_product_attention(Qn, Knn, Vnn, attn_mask = attention_mask, is_causal = False)
pass
A = A.transpose(1, 2)
A = A.reshape(bsz, 1, attention_size)
@ -272,15 +278,15 @@ pass
torch_nn_functional_silu = torch.nn.functional.silu
def fast_swiglu_inference(self, X):
def fast_swiglu_inference(self, X, temp_gate = None, temp_up = None):
# gate = self.gate_proj(X)
# up = self.up_proj(X)
bsz, _, hd = X.shape
# mlp_size = self.config.intermediate_size
# temp = torch.empty((2, bsz, 1, mlp_size), dtype = X.dtype, device = "cuda:0")
gate = fast_linear_forward(self.gate_proj, X)#, out = temp[0])
up = fast_linear_forward(self. up_proj, X)#, out = temp[1])
gate = fast_linear_forward(self.gate_proj, X, out = temp_gate)
up = fast_linear_forward(self. up_proj, X, out = temp_up)
gate = torch_nn_functional_silu(gate, inplace = True)
gate *= up
@ -289,14 +295,23 @@ def fast_swiglu_inference(self, X):
return down
pass
def fast_rms_layernorm_inference(self, X):
torch_square = torch.square
torch_mean = torch.mean
def fast_rms_layernorm_inference(self, X, XX = None, XX2 = None, variance = None):
old_dtype = X.dtype
XX = X.to(torch.float32)
variance = XX.square().mean(-1, keepdim = True)
if XX is None:
XX = X.to(torch.float32)
variance = XX.square().mean(-1, keepdim = True)
else:
XX.copy_(X)
torch_mean(torch_square(XX, out = XX2), -1, keepdim = True, out = variance)
pass
variance += self.variance_epsilon
XX *= variance.rsqrt_()
X = XX.to(old_dtype) # Must preserve due to residual
if XX is None: X = XX.to(old_dtype)
else: X.copy_(XX)
X *= self.weight
return X
pass
@ -403,7 +418,7 @@ def LlamaAttention_fast_forward(
past_key_value = (K, V) if use_cache else None
# Attention module
if (not HAS_FLASH_ATTENTION and attention_mask is None):
if (not HAS_FLASH_ATTENTION and HAS_XFORMERS and attention_mask is None):
# Xformers memory efficient attention
# Also has Flash Attention v2 dispatching
Q = Q.transpose(1, 2)
@ -902,15 +917,29 @@ def LlamaModel_fast_forward_inference(
attention_mask = None,
):
input_ids = input_ids[:,:self.max_seq_length]
hidden_states = self.model.embed_tokens(input_ids)
hidden_states = hidden_states.to(self.config.torch_dtype)
bsz, q_len, hd = hidden_states.shape
bsz, q_len = input_ids.shape
hd = self.config.hidden_size
mlp_size = self.config.intermediate_size
X = self.model.embed_tokens(input_ids)
X = X.to(self.config.torch_dtype)
bsz, q_len, hd = X.shape
assert(q_len == 1)
# Get saved buffers to reduce memory movement
residual = torch.empty((bsz, q_len, hd), dtype = torch.float32, device = "cuda:0")
_XX = torch.empty((2, bsz, q_len, hd), dtype = torch.float32, device = "cuda:0")
XX, XX2 = _XX[0], _XX[1]
variance = torch.empty((bsz, q_len, 1), dtype = torch.float32, device = "cuda:0")
temp_mlp = torch.empty((2, bsz, 1, mlp_size), dtype = X.dtype, device = "cuda:0")
temp_gate, temp_up = temp_mlp[0], temp_mlp[1]
seq_len = past_key_values[0][0].shape[-2]
if bsz != 1:
attention_mask = _prepare_4d_causal_attention_mask_for_sdpa(
attention_mask,
(bsz, q_len),
hidden_states,
X,
seq_len,
sliding_window = getattr(self.config, "sliding_window", None),
)
@ -919,30 +948,54 @@ def LlamaModel_fast_forward_inference(
pass
next_decoder_cache = []
for idx, decoder_layer in enumerate(self.model.layers):
residual = hidden_states
hidden_states = fast_rms_layernorm_inference(decoder_layer.input_layernorm, hidden_states)
hidden_states, present_key_value = LlamaAttention_fast_forward_inference(
residual.copy_(X) # residual = X
X = fast_rms_layernorm_inference(
decoder_layer.input_layernorm,
X,
XX = XX,
XX2 = XX2,
variance = variance,
)
X, present_key_value = LlamaAttention_fast_forward_inference(
decoder_layer.self_attn,
hidden_states = hidden_states,
hidden_states = X,
past_key_value = past_key_values[idx],
position_ids = position_ids,
attention_mask = attention_mask,
do_prefill = not hasattr(decoder_layer.self_attn, "paged_attention"),
)
hidden_states += residual
X += residual
residual = hidden_states
hidden_states = fast_rms_layernorm_inference(decoder_layer.post_attention_layernorm, hidden_states)
hidden_states = fast_swiglu_inference(decoder_layer.mlp, hidden_states)
hidden_states += residual
residual.copy_(X) # residual = X
X = fast_rms_layernorm_inference(
decoder_layer.post_attention_layernorm,
X,
XX = XX,
XX2 = XX2,
variance = variance,
)
X = fast_swiglu_inference(
decoder_layer.mlp,
X,
temp_gate = temp_gate,
temp_up = temp_up,
)
X += residual
next_decoder_cache.append(present_key_value)
pass
hidden_states = fast_rms_layernorm_inference(self.model.norm, hidden_states)
X = fast_rms_layernorm_inference(
self.model.norm,
X,
XX = XX,
XX2 = XX2,
variance = variance,
)
return BaseModelOutputWithPast(
last_hidden_state = hidden_states,
last_hidden_state = X,
past_key_values = next_decoder_cache,
hidden_states = [],
attentions = [],
@ -977,7 +1030,7 @@ def CausalLM_fast_forward(fast_forward_inference):
attention_mask = attention_mask,
)
else:
causal_mask = xformers.attn_bias.LowerTriangularMask()
causal_mask = xformers.attn_bias.LowerTriangularMask() if HAS_XFORMERS else None
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
output_hidden_states = (
@ -1159,7 +1212,8 @@ class LlamaRotaryEmbedding(torch.nn.Module):
# [TODO] Hack to pass in config - need to remove later
base = config.rope_theta
partial_rotary_factor = config.partial_rotary_factor if hasattr(config, "partial_rotary_factor") else 1.0
dim = int((config.hidden_size // config.num_attention_heads))
dim = getattr(config, "head_dim", None)
if dim is None: dim = int((config.hidden_size // config.num_attention_heads))
device = "cuda"
max_position_embeddings = config.max_position_embeddings
pass
@ -1580,9 +1634,18 @@ class FastLlamaModel:
model_patcher = None,
tokenizer_name = None,
trust_remote_code = False,
fast_inference = False, # uses vLLM
gpu_memory_utilization = 0.5,
float8_kv_cache = False,
random_state = 3407,
max_lora_rank = 16,
disable_log_stats = False,
**kwargs,
):
if trust_remote_code:
if fast_inference:
raise NotImplementedError("Unsloth: Fast inference does not support `trust_remote_code` yet.")
print(
"Unsloth: WARNING `trust_remote_code` is True.\n"\
"Are you certain you want to do remote code execution?"
@ -1596,9 +1659,9 @@ class FastLlamaModel:
statistics = \
f"==((====))== Unsloth {__version__}: Fast {model_patcher.__name__[4:-5]} patching. Transformers: {transformers_version}.\n"\
f" \\\ /| GPU: {gpu_stats.name}. Max memory: {max_memory} GB. Platform: {platform_system}.\n"\
f"O^O/ \_/ \\ Torch: {torch.__version__}. CUDA: {gpu_stats.major}.{gpu_stats.minor}. CUDA Toolkit: {torch.version.cuda}. Triton: {triton_version}\n"\
f"\ / Bfloat16 = {str(SUPPORTS_BFLOAT16).upper()}. FA [Xformers = {xformers_version}. FA2 = {HAS_FLASH_ATTENTION}]\n"\
f" {chr(92)}{chr(92)} /| GPU: {gpu_stats.name}. Max memory: {max_memory} GB. Platform: {platform_system}.\n"\
f"O^O/ {chr(92)}_/ {chr(92)} Torch: {torch.__version__}. CUDA: {gpu_stats.major}.{gpu_stats.minor}. CUDA Toolkit: {torch.version.cuda}. Triton: {triton_version}\n"\
f"{chr(92)} / Bfloat16 = {str(SUPPORTS_BFLOAT16).upper()}. FA [Xformers = {xformers_version}. FA2 = {HAS_FLASH_ATTENTION}]\n"\
f' "-____-" Free Apache license: http://github.com/unslothai/unsloth'
print(statistics)
@ -1626,7 +1689,11 @@ class FastLlamaModel:
assert(dtype == torch.float16 or dtype == torch.bfloat16 or dtype == torch.float32)
# RoPE Scaling
model_config = AutoConfig.from_pretrained(model_name, token = token)
model_config = AutoConfig.from_pretrained(
model_name,
token = token,
attn_implementation = "sdpa",
)
model_max_seq_length = model_config.max_position_embeddings
# Check if RoPE Scaling is even allowed
@ -1647,6 +1714,9 @@ class FastLlamaModel:
rope_scaling = max_seq_length / model_max_seq_length
if fast_inference:
raise NotImplementedError("Unsloth: Fast inference does not yet work with RoPE Scaling.")
logger.warning_once(
f"Unsloth: {model_name} can only handle sequence lengths of at most "\
f"{model_max_seq_length}.\nBut with kaiokendev's RoPE scaling of "\
@ -1688,17 +1758,54 @@ class FastLlamaModel:
# Cannot be None, since HF now checks for the config
if load_in_4bit: kwargs["quantization_config"] = bnb_config
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map = device_map,
torch_dtype = dtype,
# quantization_config = bnb_config,
token = token,
max_position_embeddings = max_position_embeddings,
trust_remote_code = trust_remote_code,
attn_implementation = "eager",
**kwargs,
)
if not fast_inference:
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map = device_map,
torch_dtype = dtype,
# quantization_config = bnb_config,
token = token,
max_position_embeddings = max_position_embeddings,
trust_remote_code = trust_remote_code,
attn_implementation = "eager",
**kwargs,
)
else:
from unsloth_zoo.vllm_utils import (
load_vllm,
get_vllm_state_dict,
convert_vllm_to_huggingface,
generate_batches,
)
allowed_args = inspect.getfullargspec(load_vllm).args
load_vllm_kwargs = dict(
model_name = model_name,
config = model_config,
gpu_memory_utilization = gpu_memory_utilization,
max_seq_length = max_seq_length,
dtype = dtype,
float8_kv_cache = float8_kv_cache,
enable_lora = True,
max_lora_rank = max_lora_rank,
disable_log_stats = disable_log_stats,
)
for allowed_arg in allowed_args:
if allowed_arg not in load_vllm_kwargs and allowed_arg in kwargs:
load_vllm_kwargs[allowed_arg] = kwargs[allowed_arg]
pass
# Load vLLM first
llm = load_vllm(**load_vllm_kwargs)
# Convert to HF format
_, quant_state_dict = get_vllm_state_dict(llm, config = model_config)
model = convert_vllm_to_huggingface(quant_state_dict, model_config, dtype)
model.vllm_engine = llm
model.fast_generate = model.vllm_engine.generate
from functools import partial
model.fast_generate_batches = partial(generate_batches, model.vllm_engine)
pass
# Return old flag
os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = old_hf_transfer
# We currently only support NVIDIA GPUs - AMD / Intel is a work in progress!
@ -2194,6 +2301,20 @@ class FastLlamaModel:
modules_to_save = list(set(modules_to_save))
pass
vllm_engine = None
if hasattr(model, "vllm_engine"):
# Fast inference!
vllm_engine = model.vllm_engine
vllm_fast_generate = model.fast_generate
vllm_fast_generate_batches = model.fast_generate_batches
if modules_to_save is not None:
raise NotImplementedError("Unsloth: Currently fast inference does not work with training embeddings or lm_head.")
if bias != "none":
raise NotImplementedError("Unsloth: Currently fast inference does not work with using biases for LoRA.")
pass
# Get LoRA
arguments = dict(
r = r,
@ -2300,6 +2421,19 @@ class FastLlamaModel:
torch.cuda.empty_cache()
pass
# Patch for fast inference
if vllm_engine is not None:
model.vllm_engine = vllm_engine
model.fast_generate = vllm_fast_generate
model.fast_generate_batches = vllm_fast_generate_batches
# Also saving and loading LoRA
from functools import partial
from unsloth_zoo.vllm_utils import save_lora, load_lora
model.save_lora = partial(save_lora, model)
model.load_lora = partial(load_lora, model)
pass
return model
pass
@ -2509,18 +2643,24 @@ class FastLlamaModel:
# return
# pass
internal_model = model
internal_model.gradient_checkpointing = False
internal_model.training = False
while hasattr(internal_model, "model"):
internal_model = internal_model.model
internal_model.gradient_checkpointing = False
internal_model.training = False
pass
if hasattr(internal_model, "training"):
internal_model.training = False
m = model
while hasattr(m, "model"):
if hasattr(m, "gradient_checkpointing"):
m.gradient_checkpointing = False
if hasattr(m, "training"):
m.training = False
# Pad tokenizer to the left
if hasattr(m, "_saved_temp_tokenizer"):
m._saved_temp_tokenizer.padding_side = "left"
m = m.model
pass
if hasattr(m, "gradient_checkpointing"):
m.gradient_checkpointing = False
if hasattr(m, "training"):
m.training = False
# Pad tokenizer to the left
if hasattr(m, "_saved_temp_tokenizer"):
m._saved_temp_tokenizer.padding_side = "left"
# Also check if lm_head / embeddings are trained
internal_model = model
@ -2529,30 +2669,13 @@ class FastLlamaModel:
pass
lm_head = internal_model.lm_head.weight
device_type = lm_head.device.type
dtype = model.config.torch_dtype
if type(dtype) is str:
if dtype == "float16": dtype = torch.float16
elif dtype == "bfloat16": dtype = torch.bfloat16
pass
dtype = _get_dtype(model.config.torch_dtype)
# Wrap model.generate
if model.generate.__name__ != "_fast_generate":
model._unwrapped_old_generate = model.generate
model.generate = _wrap_fast_inference(model.generate, device_type, dtype, model)
pass
# Patch tokenizer to pad to the left
internal_model = model
while hasattr(internal_model, "model"):
if hasattr(internal_model, "_saved_temp_tokenizer"):
internal_model._saved_temp_tokenizer.padding_side = "left"
pass
internal_model = internal_model.model
pass
if hasattr(internal_model, "_saved_temp_tokenizer"):
internal_model._saved_temp_tokenizer.padding_side = "left"
pass
# Also disable training for embeddings for NEFTune
if hasattr(model, "get_input_embeddings"):
@ -2570,9 +2693,6 @@ class FastLlamaModel:
@staticmethod
def for_training(model, use_gradient_checkpointing = True):
internal_model = model
internal_model.gradient_checkpointing = use_gradient_checkpointing
internal_model.training = True
# Delete all fast inference loras
for param in model.parameters():
@ -2580,14 +2700,24 @@ class FastLlamaModel:
del param._fast_lora
pass
while hasattr(internal_model, "model"):
internal_model = internal_model.model
internal_model.gradient_checkpointing = use_gradient_checkpointing
internal_model.training = True
pass
if hasattr(internal_model, "training"):
internal_model.training = True
m = model
while hasattr(m, "model"):
if hasattr(m, "gradient_checkpointing"):
m.gradient_checkpointing = use_gradient_checkpointing
if hasattr(m, "training"):
m.training = True
# Pad tokenizer to the right
if hasattr(m, "_saved_temp_tokenizer"):
m._saved_temp_tokenizer.padding_side = "right"
m = m.model
pass
if hasattr(m, "gradient_checkpointing"):
m.gradient_checkpointing = use_gradient_checkpointing
if hasattr(m, "training"):
m.training = True
# Pad tokenizer to the right
if hasattr(m, "_saved_temp_tokenizer"):
m._saved_temp_tokenizer.padding_side = "right"
# Also revert model.generate
if hasattr(model, "_unwrapped_old_generate"):
@ -2595,18 +2725,6 @@ class FastLlamaModel:
del model._unwrapped_old_generate
pass
# Patch tokenizer to pad to the right
internal_model = model
while hasattr(internal_model, "model"):
if hasattr(internal_model, "_saved_temp_tokenizer"):
internal_model._saved_temp_tokenizer.padding_side = "right"
pass
internal_model = internal_model.model
pass
if hasattr(internal_model, "_saved_temp_tokenizer"):
internal_model._saved_temp_tokenizer.padding_side = "right"
pass
# Also re-enable training for embeddings for NEFTune
if hasattr(model, "get_input_embeddings"):
embeddings = model.get_input_embeddings()

View file

@ -30,11 +30,11 @@ except:
from huggingface_hub.utils._token import get_token
pass
from huggingface_hub import HfFileSystem
import importlib.util
# [TODO] Move USE_MODELSCOPE to utils
USE_MODELSCOPE = os.environ.get("UNSLOTH_USE_MODELSCOPE", "0") == "1"
if USE_MODELSCOPE:
import importlib
if importlib.util.find_spec("modelscope") is None:
raise ImportError(f'You are using the modelscope hub, please install modelscope by `pip install modelscope -U`')
pass
@ -73,9 +73,25 @@ class FastLanguageModel(FastLlamaModel):
resize_model_vocab = None,
revision = None,
use_exact_model_name = False,
fast_inference = False, # uses vLLM
gpu_memory_utilization = 0.5,
float8_kv_cache = False,
random_state = 3407,
max_lora_rank = 64,
disable_log_stats = True,
*args, **kwargs,
):
if token is None: token = get_token()
if fast_inference:
if importlib.util.find_spec("vllm") is None:
raise ImportError(
"Unsloth: Please install vLLM before enabling `fast_inference`!\n"\
"You can do this in a terminal via `pip install vllm`"
)
pass
pass
old_model_name = model_name
if not use_exact_model_name:
@ -255,6 +271,24 @@ class FastLanguageModel(FastLlamaModel):
tokenizer_name = None
pass
if fast_inference:
from unsloth_zoo.vllm_utils import (
patch_vllm,
vllm_dynamic_quant_supported,
)
patch_vllm()
if model_name.endswith("unsloth-bnb-4bit"):
if not vllm_dynamic_quant_supported(model_name, model_config):
# Instead use -bnb-4bit variant
print(
f"Unsloth: Switching from Unsloth dynamic quant to normal quant since\n"\
f"we do not yet support fast inference for {model_name}"
)
model_name = model_name[:-len("unsloth-bnb-4bit")] + "bnb-4bit"
pass
pass
pass
model, tokenizer = dispatch_model.from_pretrained(
model_name = model_name,
max_seq_length = max_seq_length,
@ -268,6 +302,13 @@ class FastLanguageModel(FastLlamaModel):
tokenizer_name = tokenizer_name,
trust_remote_code = trust_remote_code,
revision = revision if not is_peft else None,
fast_inference = fast_inference,
gpu_memory_utilization = gpu_memory_utilization,
float8_kv_cache = float8_kv_cache,
random_state = random_state,
max_lora_rank = max_lora_rank,
disable_log_stats = disable_log_stats,
*args, **kwargs,
)

View file

@ -304,25 +304,30 @@ __INT_TO_FLOAT_MAPPER = \
"unsloth/Mistral-Small-Instruct-2409",
"mistralai/Mistral-Small-Instruct-2409",
),
"unsloth/Qwen2.5-0.5B-Instruct-bnb-4bit" : (
"unsloth/Qwen2.5-0.5B-Instruct-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-0.5B-Instruct",
"Qwen/Qwen2.5-0.5B-Instruct",
"unsloth/Qwen2.5-0.5B-Instruct-bnb-4bit",
),
"unsloth/Qwen2.5-1.5B-Instruct-bnb-4bit" : (
"unsloth/Qwen2.5-1.5B-Instruct-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-1.5B-Instruct",
"Qwen/Qwen2.5-1.5B-Instruct",
"unsloth/Qwen2.5-1.5B-Instruct-bnb-4bit",
),
"unsloth/Qwen2.5-3B-Instruct-bnb-4bit" : (
"unsloth/Qwen2.5-3B-Instruct-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-3B-Instruct",
"Qwen/Qwen2.5-3B-Instruct",
"unsloth/Qwen2.5-3B-Instruct-bnb-4bit",
),
"unsloth/Qwen2.5-7B-Instruct-bnb-4bit" : (
"unsloth/Qwen2.5-7B-Instruct-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-7B-Instruct",
"Qwen/Qwen2.5-7B-Instruct",
"unsloth/Qwen2.5-7B-Instruct-bnb-4bit",
),
"unsloth/Qwen2.5-14B-Instruct-bnb-4bit" : (
"unsloth/Qwen2.5-14B-Instruct-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-14B-Instruct",
"Qwen/Qwen2.5-14B-Instruct",
"unsloth/Qwen2.5-14B-Instruct-bnb-4bit",
),
"unsloth/Qwen2.5-32B-Instruct-bnb-4bit" : (
"unsloth/Qwen2.5-32B-Instruct",
@ -332,25 +337,30 @@ __INT_TO_FLOAT_MAPPER = \
"unsloth/Qwen2.5-72B-Instruct",
"Qwen/Qwen2.5-72B-Instruct",
),
"unsloth/Qwen2.5-0.5B-bnb-4bit" : (
"unsloth/Qwen2.5-0.5B-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-0.5B",
"Qwen/Qwen2.5-0.5B",
"unsloth/Qwen2.5-0.5B-bnb-4bit",
),
"unsloth/Qwen2.5-1.5B-bnb-4bit" : (
"unsloth/Qwen2.5-1.5B-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-1.5B",
"Qwen/Qwen2.5-1.5B",
"unsloth/Qwen2.5-1.5B-bnb-4bit",
),
"unsloth/Qwen2.5-3B-bnb-4bit" : (
"unsloth/Qwen2.5-3B-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-3B",
"Qwen/Qwen2.5-3B",
"unsloth/Qwen2.5-3B-bnb-4bit",
),
"unsloth/Qwen2.5-7B-bnb-4bit" : (
"unsloth/Qwen2.5-7B-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-7B",
"Qwen/Qwen2.5-7B",
"unsloth/Qwen2.5-7B-bnb-4bit",
),
"unsloth/Qwen2.5-14B-bnb-4bit" : (
"unsloth/Qwen2.5-14B-unsloth-bnb-4bit" : (
"unsloth/Qwen2.5-14B",
"Qwen/Qwen2.5-14B",
"unsloth/Qwen2.5-14B-bnb-4bit",
),
"unsloth/Qwen2.5-32B-bnb-4bit" : (
"unsloth/Qwen2.5-32B",
@ -555,12 +565,12 @@ __INT_TO_FLOAT_MAPPER = \
"deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
),
"unsloth/Mistral-Small-24B-Base-2501-unsloth-bnb-4bit" : (
"unsloth/Mistral-Small-24B-Base",
"unsloth/Mistral-Small-24B-Base-2501",
"mistralai/Mistral-Small-24B-Base-2501",
"unsloth/Mistral-Small-24B-Base-2501-bnb-4bit",
),
"unsloth/Mistral-Small-24B-Instruct-2501-unsloth-bnb-4bit" : (
"unsloth/Mistral-Small-24B-Instruct",
"unsloth/Mistral-Small-24B-Instruct-2501",
"mistralai/Mistral-Small-24B-Instruct-2501",
"unsloth/Mistral-Small-24B-Instruct-2501-bnb-4bit",
),

423
unsloth/models/rl.py Normal file
View file

@ -0,0 +1,423 @@
# 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.
__all__ = [
"PatchFastRL",
]
METRICS_MOVE_TO_END = [
"nll",
"aux",
"beta",
"alpha",
]
import torch
try:
from transformers.utils.notebook import (
IntervalStrategy,
NotebookTrainingTracker,
NotebookProgressCallback,
)
HAS_NOTEBOOK = True
except:
HAS_NOTEBOOK = False
pass
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union
import inspect
import os
import re
import functools
from unsloth_zoo.compiler import create_new_function
def PatchRL(FastLanguageModel):
from trl.models.utils import unwrap_model_for_generation
from contextlib import contextmanager
@contextmanager
def unsloth_unwrap_model_for_generation(model, accelerator):
with unwrap_model_for_generation(model, accelerator) as unwrapped_model:
# Put the model in inference mode.
FastLanguageModel.for_inference(unwrapped_model)
# We must use .clone for Unsloth since we force inference_mode
# Rather we should have used no_grad
original_generate = unwrapped_model.generate
def generate_with_clone(*args, **kwargs):
out = original_generate(*args, **kwargs)
if isinstance(out, torch.Tensor):
return out.clone()
return out
pass
unwrapped_model.generate = generate_with_clone
try:
yield unwrapped_model
finally:
# Restore generate and return
unwrapped_model.generate = original_generate
FastLanguageModel.for_training(model)
pass
pass
pass
import trl.trainer
trainers = dir(trl.trainer)
trainers = [x for x in trainers if x.endswith("_trainer")]
unwrap = "unwrap_model_for_generation"
for trainer in trainers:
if hasattr(eval(f"trl.trainer.{trainer}"), unwrap):
exec(f"trl.trainer.{trainer}.{unwrap} = unsloth_{unwrap}")
pass
pass
def NotebookProgressCallback_on_train_begin(Trainer_metrics):
def _NotebookProgressCallback_on_train_begin(self, args, state, control, **kwargs):
self.first_column = "Epoch" if args.eval_strategy == IntervalStrategy.EPOCH else "Step"
self.training_loss = 0
self.last_log = 0
column_names = [self.first_column] + ["Training Loss"]
if args.eval_strategy != IntervalStrategy.NO:
column_names.append("Validation Loss")
column_names += [x.replace("/", " / ") for x in Trainer_metrics]
self.training_tracker = NotebookTrainingTracker(state.max_steps, column_names)
pass
return _NotebookProgressCallback_on_train_begin
pass
def NotebookProgressCallback_on_log(Trainer_metrics):
def _NotebookProgressCallback_on_log(self, args, state, control, logs=None, **kwargs):
# Only for when there is no evaluation
if args.eval_strategy == IntervalStrategy.NO and "loss" in logs:
values = {"Training Loss": logs["loss"]}
for metric in Trainer_metrics:
# Sometimes metric is not inside logs
try: values[metric.replace("/", " / ")] = logs[metric]
except: pass
pass
# First column is necessarily Step since we're not in epoch eval strategy
values["Step"] = state.global_step
self.training_tracker.write_line(values)
pass
pass
return _NotebookProgressCallback_on_log
pass
def NotebookTrainingTracker_write_line(Trainer_metrics):
set_Trainer_metrics = set(Trainer_metrics)
def _NotebookTrainingTracker_write_line(self, values):
"""
Write the values in the inner table.
Args:
values (`Dict[str, float]`): The values to display.
"""
if self.inner_table is None:
self.inner_table = [list(values.keys()), list(values.values())]
else:
columns = self.inner_table[0]
new_values = {}
for key, value in values.items():
lowered = key.lower()
if lowered in set_Trainer_metrics:
new_values[lowered.replace("/", " / ")] = value
else:
new_values[key] = value
pass
values = new_values
self.inner_table[0] = columns
if len(self.inner_table) > 1:
last_values = self.inner_table[-1]
first_column = self.inner_table[0][0]
if last_values[0] != values[first_column]:
# write new line
self.inner_table.append([values[c] if c in values else "No Log" for c in columns])
else:
# update last line
new_values = values
for c in columns:
if c not in new_values.keys():
new_values[c] = last_values[columns.index(c)]
self.inner_table[-1] = [new_values[c] for c in columns]
else:
# Edit for evaluation purposes
self.inner_table.append([values[c] if c in values else 0 for c in columns])
pass
pass
pass
return _NotebookTrainingTracker_write_line
pass
def _PatchRLStatistics(metrics, algorithm):
if HAS_NOTEBOOK:
if len(metrics) == 0:
raise RuntimeError(f"Unsloth: RL statistics for {algorithm} failed with no metrics seen?")
from transformers.trainer import is_in_notebook
if is_in_notebook():
# Patch DPO notebook printing
NotebookTrainingTracker.write_line = NotebookTrainingTracker_write_line(metrics)
from transformers.trainer import DEFAULT_PROGRESS_CALLBACK
DEFAULT_PROGRESS_CALLBACK.on_train_begin = NotebookProgressCallback_on_train_begin(metrics)
DEFAULT_PROGRESS_CALLBACK.on_log = NotebookProgressCallback_on_log(metrics)
pass
pass
pass
@functools.cache
def get_trl_metrics():
# Gets metrics so we can output them in notebooks
import trl.trainer
trainers = dir(trl.trainer)
trainers = [x for x in trainers if x.endswith("_trainer")]
filepath = inspect.getfile(trl.trainer)
filepath = os.path.split(filepath)[0]
all_metrics = dict()
for trainer in trainers:
filename = os.path.join(filepath, f"{trainer}.py")
if not os.path.exists(filename): continue
with open(filename, "r") as file: file = file.read()
# Get metrics['kl'] or stats['kl']
metrics = re.findall(r"metrics\[[\"\']([^\"\']{1,})[\"\']\]", file)
stats = re.findall(r"stats\[[\"\']([^\"\']{1,})[\"\']\]", file)
metrics = metrics + stats
# Get optional f-strings
metrics_f = re.findall(r"metrics\[f[\"\']\{[^\}]{1,}\}([^\"\']{1,})[\"\']\]", file)
stats_f = re.findall(r"stats\[f[\"\']\{[^\}]{1,}\}([^\"\']{1,})[\"\']\]", file)
metrics_f = metrics_f + stats_f
# Filter out prefixes if seen
# metrics[f"{prefix}rewards/chosen"]
left_prefix = 'prefix = "eval_" if train_eval == "eval" else ""' in file
if left_prefix: metrics += metrics_f
# Move all eval_ things to the end and reward to the front
beginning = []
middle = []
end = []
for x in metrics:
lowered = x.lower()
if "reward" in lowered:
beginning.append(x)
elif x.lower().startswith("eval"):
end.append(x)
else:
# Check if we want to move to the end
moved = False
for move_end in METRICS_MOVE_TO_END:
if move_end in lowered:
end.append(x)
moved = True
break
if not moved:
middle.append(x)
pass
pass
metrics = beginning + middle + end
all_metrics[trainer[:trainer.find("_")].upper()] = metrics
pass
return all_metrics
pass
def PatchRLStatistics(algorithm = "GRPO"):
# Get notebook statistics columns to show up
algorithm = algorithm.upper()
all_metrics = get_trl_metrics()
if algorithm not in all_metrics:
print(
f"Unsloth for {algorithm.upper()} is not yet implemented! Just ignore this function.\n"\
f"We support: `{list(all_metrics.keys())}`"
)
pass
_PatchRLStatistics(all_metrics[algorithm], algorithm)
pass
def _patch_trl_rl_trainers(trainer_file = "grpo_trainer"):
# Patch for vLLM and Unsloth PEFT
import trl
import trl.trainer
trainer = eval(f"trl.trainer.{trainer_file}")
name = [x for x in dir(trainer) if x.endswith("Trainer") and x != "Trainer" and trainer_file.split("_")[0] in x.lower()]
assert(len(name) == 1)
RLTrainer_name = name[0]
RLTrainer = eval(f"trl.trainer.{trainer_file}.{RLTrainer_name}")
try:
__init__ = inspect.getsource(RLTrainer.__init__)
except:
# Already patched most likely!
return
old__init__ = __init__
all_imports = dir(trainer)
assert("Union" in all_imports)
imports = [x for x in all_imports if not x.startswith("_")]
imports += ["Trainer"]
spaces = __init__.find("def")
__init__ = __init__.split("\n")
__init__ = "\n".join(x[spaces:] for x in __init__)
# Replace vLLM sections since we already have it done!
vllm_part = re.findall(
r"(\n[\s]{4}"\
r"if (self|args)\.use_vllm\:.+?"\
r"\n[\s]{4,}"\
"else:\n)",
__init__,
flags = re.MULTILINE | re.DOTALL,
)
if (len(vllm_part) != 1): return
vllm_part, args = vllm_part[0][0], vllm_part[0][1]
# Strip all comments
new_vllm_part = re.sub(r"\#[^\n]{1,}\n", "", vllm_part)
# Get SamplingParams
sampling_params = re.findall(
r"\n[\s]{4,}(self\.[^\s]{1,}[\s]{0,}\=[\s]{0,}"\
r"SamplingParams\(.+?\))",
new_vllm_part,
flags = re.MULTILINE | re.DOTALL,
)
if len(sampling_params) != 1: return
sampling_params = sampling_params[0]
# Replace with our vLLM engine
sampling_params = \
" "*8 + "self.llm = model.vllm_engine; self._last_loaded_step = 0; " + \
sampling_params # Add spaces
new_vllm_part = f"\n if {args}.use_vllm:\n{sampling_params}\n else:\n"
__init__ = __init__.replace(vllm_part, new_vllm_part)
# Remove peft_config
__init__ = __init__.replace("elif peft_config is None:", "elif False:")
__init__ = __init__.replace("elif peft_config is not None:", "elif False:")
__init__ = __init__.replace("if peft_config is None:", "if False:")
__init__ = __init__.replace("if peft_config is not None:", "if False:")
__init__ = __init__.replace("get_peft_model(model, peft_config)", "model")
# Add spaces back into __init__
__init__ = __init__.split("\n")
__init__ = "\n".join(' '*spaces + x for x in __init__)
# Search for vLLM calling in all child functions
functions = dir(RLTrainer)
RLTrainer_source = inspect.getsource(RLTrainer)
functions = [x for x in functions if f"def {x}" in RLTrainer_source]
changed = {"__init__" : (old__init__, __init__,)}
for function in functions:
if not hasattr(RLTrainer, function): continue
fx = getattr(RLTrainer, function)
try:
source = inspect.getsource(fx)
except:
continue
original_source = source
# llm_model = self.llm.llm_engine.model_executor.driver_worker.model_runner.model
source = re.sub(
r"(\n[\s]{4,}).+?model_executor\.driver_worker.+?\n",
r"\n\1pass\n",
source,
)
# llm_model.load_weights(model.state_dict().items())
source = re.sub(
r"(\n[\s]{4,}).+?load_weights\(.+?\n",
r"\n\1pass\n",
source,
)
# .state_dict()
source = re.sub(
r"\.state_dict\(\)",
r"",
source,
)
# Replace self.llm.generate and self.llm.chat
lora_name = trainer_file + "_lora_model"
source = re.sub(
r"(self\.llm\.(?:generate|chat)\([^\)]{1,})\)",
r"\1, lora_request = model.load_lora('" + lora_name + r"', load_tensors = True))",
source
)
# Skip if no changes done
if source == original_source: continue
# Find all imports
imports += [x for x in all_imports if not x.startswith("_") and x in source]
changed[function] = (original_source, source,)
pass
# Import all functions
imports = list(set(imports))
# Patch all functions
for function in changed:
old, new = changed[function]
RLTrainer_source = RLTrainer_source.replace(old, new)
pass
RLTrainer_source = RLTrainer_source.replace(
f"class {RLTrainer_name}", f"class Unsloth{RLTrainer_name}", 1
)
# Create new class in compiled cache and import it
module = create_new_function(
RLTrainer_name,
RLTrainer_source,
f"trl.trainer.{trainer_file}",
imports,
)
# Patch over modules
exec(f"trl.{RLTrainer_name} = module.Unsloth{RLTrainer_name}", locals(), globals())
exec(f"trl.trainer.{RLTrainer_name} = module.Unsloth{RLTrainer_name}", locals(), globals())
exec(f"trl.trainer.{trainer_file}.{RLTrainer_name} = module.Unsloth{RLTrainer_name}", locals(), globals())
return module
pass
def patch_trl_rl_trainers():
# Patch all TRL modules if they have vLLM or PEFT
import trl.trainer
all_trainers = dir(trl.trainer)
all_trainers = [x for x in all_trainers if x.islower() and x.endswith("_trainer")]
for trainer in all_trainers:
_patch_trl_rl_trainers(trainer)
return
pass
def PatchFastRL(algorithm = "GRPO", FastLanguageModel = None):
if FastLanguageModel is not None: PatchRL(FastLanguageModel)
patch_trl_rl_trainers()
PatchRLStatistics(algorithm)
pass