fix(cpu): support CPU-only and no-torch import on Windows#5119
fix(cpu): support CPU-only and no-torch import on Windows#5119LeoBorcherding wants to merge 27 commits intounslothai:mainfrom
Conversation
…t installed refs unslothai#5008 Fixes --no-torch mode crashing with ImportError on import. The check is a version compatibility guard, not a hard dependency enforcer.
…efs unslothai#5008 Fixes CPU-only installs (issue unslothai#5008 path 1): when DEVICE_TYPE is "cpu", skip triton import (not available on CPU) and set SUPPORTS_BFLOAT16 = False to avoid NameError.
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Code Review
This pull request introduces a 'no-torch' mode and enhances CPU support, allowing the library to initialize when PyTorch is missing or when a GPU is unavailable. Key changes include environment-based toggling of Torch dependencies and fallback mechanisms for device detection. Review feedback indicates that several import fix functions remain unguarded and may crash in the absence of PyTorch. Additionally, the current guards on model and trainer imports are noted as being too restrictive, potentially preventing GGUF inference on CPU-only systems where PyTorch is present.
| "Unsloth: Pytorch is not installed. Go to https://pytorch.org/.\n" | ||
| "We have some installation instructions on our Github page." | ||
| if _NO_TORCH_MODE: | ||
| torch = None |
There was a problem hiding this comment.
The torch = None assignment correctly handles the missing dependency for the main import flow. However, the subsequent calls to import_fixes functions (lines 193-212 in the full file) are not guarded and will crash if torch is None.
Specifically, functions like check_vllm_torch_sm100_compatibility, fix_vllm_guided_decoding_params, fix_vllm_pdl_blackwell, and patch_enable_input_require_grads attempt to import torch, vllm, or transformers internally, which will fail if PyTorch is not installed. These calls should be wrapped in an if torch is not None: block to ensure the "no-torch" mode works as intended.
| elif DEVICE_TYPE == "xpu": | ||
| elif torch is not None and DEVICE_TYPE == "xpu": | ||
| import bitsandbytes as bnb | ||
|
|
There was a problem hiding this comment.
The guard DEVICE_TYPE != "cpu" is too restrictive here. It prevents unsloth from loading models, tokenizers, and trainers even when PyTorch is installed on a CPU-only machine.
This contradicts the warning issued at line 262, which states that "Only GGUF inference is supported" in CPU mode. To actually support GGUF inference (which requires FastLanguageModel and the saving utilities), the models and tokenizers must be imported. The guard should be simplified to only check if torch is present.
| if torch is not None: |
nidhishgajjar
left a comment
There was a problem hiding this comment.
Review of PR #5119: fix(cpu): support CPU-only and no-torch import on Windows
Summary
This PR fixes import failures on Windows machines without a GPU by supporting CPU-only and no-torch modes. The changes address two failure paths:
- CPU PyTorch with no GPU:
get_device_type()raisesNotImplementedError --no-torchmode:torchvision_compatibility_check()raisesImportError
Changes Analysis
1. unsloth/__init__.py ✅
Added environment detection:
_NO_TORCH_MODE: RespectsUNSLOTH_NO_TORCHenvironment variable_HAS_TORCH: Auto-detects if torch is installed usingfind_spec()- Sets
_NO_TORCH_MODE = Trueif torch is not installed
Conditional imports:
- Torch imports are now gated behind
if not _NO_TORCH_MODE - Device type imports are gated behind
if torch is not None - Provides CPU stubs for device type functions when torch is unavailable
GPU-only imports gated:
tritonimport: gated bytorch is not None and DEVICE_TYPE != "cpu"- Models, trainer, dataprep: all gated by
torch is not None and DEVICE_TYPE != "cpu" - ROCm and XPU-specific imports: properly guarded
User feedback:
- Distinct warnings for each scenario:
- No-torch mode with torch installed
- No-torch mode without torch
- CPU mode with GPU detected
Cleanup:
del _HAS_TORCH, _NO_TORCH_MODEat the end- Good practice to avoid polluting module namespace
2. unsloth/device_type.py ✅
Graceful degradation:
- Changed from raising
NotImplementedErrorto returning"cpu" - Allows CPU-only mode to work without errors
- Maintains backward compatibility for GPU scenarios
Error handling preserved:
- Still raises
RuntimeErrorfor weird accelerator states - Still checks for cuda, xpu, hip availability
3. unsloth/import_fixes.py ✅
Early return:
- Changed from raising
ImportErrorto returning early - Allows no-torch mode to work
- Simple and effective fix
Strengths
- Comprehensive approach: Addresses both failure paths mentioned in the issue
- Graceful degradation: Falls back to CPU mode instead of failing
- Clear user feedback: Distinct warnings for different scenarios
- Proper gating: GPU-only imports are correctly guarded
- Clean code: Well-structured and maintainable
- Draft status: Appropriate for such significant changes
Minor Suggestions
1. Documentation
Consider adding a README section or docstring explaining:
- How to use CPU-only mode
- How to use no-torch mode
- What features are available in each mode
- The
UNSLOTH_NO_TORCHenvironment variable
2. Testing
The PR mentions "Full validation log posted in #5008" - please ensure:
- Tests are added for CPU-only mode
- Tests are added for no-torch mode
- Tests cover both Windows and Linux scenarios
- Tests verify the warnings are displayed correctly
3. Feature Documentation
For the CPU stub functions:
def is_hip():
return False
def get_device_type():
return "cpu"Consider adding docstrings:
def is_hip():
"""Stub function for no-torch mode. Always returns False."""
return False
def get_device_type():
"""Stub function for no-torch mode. Always returns cpu."""
return "cpu"4. Error Messages
The error message for the "weird accelerator" case could be clearer:
raise RuntimeError(
f"Unsloth: Weirdly `torch.cuda.is_available()`, `torch.xpu.is_available()` and `is_hip` all failed.\n"
f"But `torch.accelerator.current_accelerator()` works with it being = `{accelerator}`\n"
f"Please reinstall torch - it"s most likely broken :("
)Consider adding the detected accelerator type to the message for better debugging.
Potential Concerns
1. Breaking Changes
This PR changes behavior from failing to gracefully degrading:
- Before: Import fails on CPU-only machines
- After: Import succeeds but with limited functionality
This is generally a positive change, but ensure:
- Users are aware of the limitations via warnings
- Documentation clearly states what features are unavailable
- The change is mentioned in release notes
2. Companion Branch
The PR mentions a companion branch in unsloth-zoo. Please ensure:
- The companion PR is linked in this PR
- Both PRs are reviewed and merged together
- The changes are compatible
Approval Criteria
I would approve this PR if:
- Changes are well-structured and maintainable ✅
- Error handling is graceful ✅
- User feedback is clear ✅
- Documentation is added (suggestion)
- Tests are added (mentioned in issue, verify they"re in companion PR)
- Companion PR is linked and reviewed
Overall Assessment
✅ Strong approach to fixing the issue
✅ Well-implemented with proper error handling
✅ Good user communication via warnings
This is a solid fix that addresses the core issue while maintaining backward compatibility. The graceful degradation approach is appropriate for this use case.
498f19b to
9952164
Compare
|
Hi @nidhishgajjar, quick status update addressing your review points. Completed
Validated so far (Windows, CPU-only)
Still pending
Not included in this pass
If you want, I can do a small follow-up PR for the optional polish items after this fix lands. |
|
Retested today against the latest commits. Full before/after validation below. Environment
Commits tested
Part 1 — Repro on
|
| Test | Branch | Exit | Result |
|---|---|---|---|
import unsloth (CPU torch, no GPU) |
main |
1 | ❌ FAIL — repro confirmed |
import unsloth (UNSLOTH_NO_TORCH=1) |
main |
1 | ❌ FAIL — repro confirmed |
import unsloth (CPU torch, no GPU) |
fix | 0 | ✅ PASS |
from unsloth import FastLanguageModel (CPU) |
fix | 0 | ✅ PASS |
import unsloth (UNSLOTH_NO_TORCH=1) |
fix | 0 | ✅ PASS |
3/3 fix-branch tests pass. Both original failure paths reproduced and fixed.
Also confirmed from code review (Gemini flagged these): the import_fixes functions are safe in no-torch mode (they guard themselves internally), and FastLanguageModel is importable on CPU as shown by Test 2 — the models/trainer block uses if torch is not None: with no CPU exclusion.
Still pending: Linux validation before final merge.
80b0b52 to
ce35b46
Compare
|
Linux validation done: 3/3 pass on Ubuntu 24.04 (WSL2, Python 3.12, Both platforms confirmed ✅. Also added pytests covering these three paths ( |
|
@LeoBorcherding the issue i see with that , is that if someone wants to use unsloth core (not studio) , they shouldn't be able to do so without GPU and we need to raise those errors for that specific case. Can you please test that scenario. Install unsloth from this branch, take any of the notebooks and try to run it , what happens? |
|
@rolandtannous, I tested fix/cpu-no-torch-import in a simple custom notebook and this comes from inside
here is the test notebook: |
|
@LeoBorcherding show me screenshots of the behavior in a notebook before your fix and after your fix. For the after your fix scenario, install unsloth as usual then re-install both unsloth-zoo and unsloth from the fix branches using --force-reinstall --no-deps git+https://github.com/unsloth-zoo@your-branch (same thing for unsloth) |
|
@rolandtannous here are the screenshots of before and after the cpu guard fix in loader.py in a colab, installed using the commit hash prior vs the latest commit, Before:
After:
as you can see from_pretrained stops and returns an informative error message right away rather than erroring inside gradient_checkpointing.py with an error that users wouldnt understand. |
| "Unsloth: Pytorch is not installed. Go to https://pytorch.org/.\n" | ||
| "We have some installation instructions on our Github page." | ||
| if _NO_TORCH_MODE: | ||
| torch = None |
There was a problem hiding this comment.
Im not sure if this works if torch is imported before unsloth
There was a problem hiding this comment.
torch = None only affects the name inside init.py, sys.modules['torch'] stays untouched so any other module's import torch still works normally. All our code below that line is gated by if torch is not None: so nothing downstream ever sees the None.
|
one thing I want you to check is we have a I'm just concerned about the ripple effects of this change |
it seems from teh screenshots that both tests seem to actually install the fix PRs? while only the after case should. |
|
@rolandtannous the before is using unsloth with the commit hash prior to the CPU guard fix, the after uses the latest commit before: after: |
…rella, clarify README CPU-only note
for more information, see https://pre-commit.ci









Fixes #5008
Companion branch: https://github.com/LeoBorcherding/unsloth-zoo/tree/fix/cpu-no-torch-import
Problem
The Windows installer advertises CPU-only / GGUF mode but
import unslothfails on machines without a GPU via two separate paths:
unsloth_zoo.device_type.get_device_type()raises
NotImplementedErrorat import timetorchvision_compatibility_check()raisesImportError: torch not foundChanges
unsloth:
import_fixes.py: return early intorchvision_compatibility_check()when torch is not installed instead of raising__init__.py: auto-detect no-torch mode, respectUNSLOTH_NO_TORCHflag even when torch is installed, gate GPU-only imports, distinct warnings for each CPU/no-torch scenario, clean up module-level variables after importdevice_type.py: return"cpu"instead of raising when no GPU accelerator foundunsloth-zoo (companion branch: LeoBorcherding/unsloth-zoo@fix/cpu-no-torch-import):
device_type.py: return"cpu"instead of raising when no GPU accelerator found__init__.py: gate all torch-dependent imports and allocator config behind_HAS_TORCH, add CPU stubs for device type functionstemporary_patches/gpt_oss.py: guard CUDA memory query behind device type check to avoid crash on CPUValidation
Full validation log posted in #5008.