
1. 为什么要在 Jupyter Notebook 里先做环境体检很多人第一次跑 unsloth 微调卡住的地方不是模型本身而是环境。CUDA 版本、PyTorch 编译版本、显卡驱动三者只要有一个对不上torch.cuda.is_available()就会返回False后面FastLanguageModel.from_pretrained直接报错。更麻烦的是Jupyter Notebook 的 kernel 可能和你终端里的 Python 不是同一个终端里nvidia-smi正常Notebook 里却找不到 GPU。我自己的习惯是在 Notebook 里先跑一段环境体检代码把 CUDA、PyTorch、显卡信息全部打印出来确认链路通了再往下走。这一步花两分钟能省掉后面半小时的排错。这篇内容面向的是本地或云主机上装了 Jupyter Notebook想用 unsloth 做模型微调但不确定 CUDA 和 PyTorch 环境是否匹配的人。核心检索词就是 unsloth 模型微调、Jupyter Notebook、CUDA、PyTorch 环境配置。我会给出可复制的检查命令、一次最小微调验证动作以及把统一 Key/API 通道配好的方法让你确认整条微调链路能跑通。先明确一个概念unsloth 本身是一个加速微调库它依赖 PyTorch 和 CUDA。它不负责装驱动也不负责装 CUDA Toolkit。所以环境准备分三层——驱动层nvidia-smi 能看到卡、CUDA 运行时层PyTorch 编译时绑定的 CUDA 版本、Python 库层torch、unsloth、transformers 版本兼容。三层都对齐微调才能启动。在 Notebook 里做这件事的好处是每一步的输出都留在 cell 里出问题能回看。终端里跑完就滚屏了反而不好排查。2. TaoToken 前置把统一 Key 与 API 通道配好unsloth 微调本身是本地计算为什么还要配 API 通道因为实际工作流里微调只是其中一环。你还需要拉取基座模型的元信息、用对话模型生成训练数据、微调完后做效果对比、或者把微调好的模型接到 Agent 里跑。这些环节如果每个都单独配一套 Key管理起来很乱。TaoToken 的作用是提供一个统一的 API 入口把模型对话、Coding Plan、API Keys 管理放在同一个控制台里。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。具体操作路径第一步打开控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 注册或登录。第二步进入 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 创建一个新的 Key。创建后立刻复制页面刷新后就不再完整显示。第三步如果你要做长期编码或 Agent 任务可以看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 它适合需要持续调用模型的场景。第四步验证模型是否可用打开模型对话 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 发一条消息确认通道正常。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 里面有 Base URL、鉴权方式、请求格式的完整说明。这里要强调一个原则Base URL、Key、Model ID 三件套必须同时配齐缺一个都会报错。Base URL 用 https://taotoken.net/api Key 用你刚创建的那串Model ID 按文档里列出的写。不要只配 Base URL 就以为通了。配好之后你可以在 Notebook 里用一个简单的 requests 调用验证import requests API_KEY 你的Key BASE_URL https://taotoken.net/api resp requests.post( f{BASE_URL}/v1/chat/completions, headers{ Authorization: fBearer {API_KEY}, Content-Type: application/json }, json{ model: 按文档填Model ID, messages: [{role: user, content: 回复ok}] }, timeout30 ) print(resp.status_code) print(resp.json())返回 200 且内容里有正常回复说明通道通了。这一步和 unsloth 微调是两条线但都属于「微调链路」的一部分——微调前用 API 生成数据微调后用 API 做对比都需要这个通道稳定。3. 可复制配置CUDA 与 PyTorch 环境检查这一节是核心。打开你的 Jupyter Notebook新建一个 cell把下面的代码贴进去逐段跑。3.1 检查驱动层nvidia-smi在 Notebook 里可以用!前缀执行 shell 命令!nvidia-smi正常输出会显示显卡型号、驱动版本、CUDA Version注意这个 CUDA Version 是驱动支持的最高版本不是当前运行时版本、显存占用。如果这条命令报command not found说明驱动没装或不在 PATH 里先解决驱动问题后面不用看了。3.2 检查 PyTorch 与 CUDA 运行时import torch print(PyTorch 版本:, torch.__version__) print(CUDA 是否可用:, torch.cuda.is_available()) print(PyTorch 编译时的 CUDA 版本:, torch.version.cuda) if torch.cuda.is_available(): print(GPU 数量:, torch.cuda.device_count()) print(当前 GPU 设备:, torch.cuda.current_device()) print(GPU 设备名称:, torch.cuda.get_device_name(torch.cuda.current_device())) print(显存总量(GB):, round(torch.cuda.get_device_properties(0).total_memory / 1024**3, 2))关键看两个值torch.cuda.is_available()必须是Truetorch.version.cuda显示的版本要和驱动支持的版本兼容。比如驱动支持到 12.4PyTorch 编译时用的是 12.1通常没问题但如果 PyTorch 用的是 11.8而驱动只支持 12.x就可能出问题。3.3 检查 unsloth 与依赖版本import unsloth print(unsloth 版本:, unsloth.__version__) import transformers print(transformers 版本:, transformers.__version__) import triton print(triton 版本:, triton.__version__)unsloth 对 transformers 和 triton 的版本有要求。如果版本不匹配from unsloth import FastLanguageModel会直接报 ImportError。建议按 unsloth 官方文档推荐的版本组合来装。3.4 安装或修正 PyTorch如果torch.cuda.is_available()是False大概率是装成了 CPU 版。在 Notebook 里可以这样重装以 CUDA 12.1 为例!pip uninstall -y torch torchvision torchaudio !pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121装完重启 kernel再跑 3.2 的检查代码。注意--index-url后面的 cu121 要和你驱动支持的版本对应不要盲目抄。3.5 统一 Key/API 通道的配置文件如果你希望把 TaoToken 的配置持久化可以在项目根目录建一个config.json{ base_url: https://taotoken.net/api, api_key: 你的Key, model_id: 按文档填Model ID, timeout: 30 }然后在 Notebook 里读取import json with open(config.json, r, encodingutf-8) as f: cfg json.load(f) print(cfg[base_url]) print(cfg[model_id])这样 Base URL、Key、Model ID 三件套就统一管理了换环境时只改这一个文件。4. 验证请求一次最小微调动作环境检查通过后不要直接上完整数据集。先用一个极小的样本跑一次微调确认链路能走通。4.1 加载模型from unsloth import FastLanguageModel import torch max_seq_length 2048 dtype None load_in_4bit True model, tokenizer FastLanguageModel.from_pretrained( model_nameunsloth/Qwen2.5-0.5B-Instruct-bnb-4bit, max_seq_lengthmax_seq_length, dtypedtype, load_in_4bitload_in_4bit, )这里用 0.5B 的小模型做验证显存占用低下载快。确认能加载后再换成你要微调的实际模型。4.2 加 LoRA 适配器model FastLanguageModel.get_peft_model( model, r16, target_modules[q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj], lora_alpha16, lora_dropout0, biasnone, use_gradient_checkpointingunsloth, random_state3407, )4.3 构造一条训练样本from datasets import Dataset train_data [ {instruction: 把下面这句话翻译成英文今天天气很好。, output: The weather is nice today.}, {instruction: 把下面这句话翻译成英文我喜欢编程。, output: I enjoy programming.}, ] dataset Dataset.from_list(train_data) def format_prompt(example): text f### Instruction:\n{example[instruction]}\n\n### Response:\n{example[output]} return {text: text} dataset dataset.map(format_prompt) print(dataset[0][text])4.4 启动最小训练from trl import SFTTrainer from transformers import TrainingArguments trainer SFTTrainer( modelmodel, tokenizertokenizer, train_datasetdataset, dataset_text_fieldtext, max_seq_lengthmax_seq_length, argsTrainingArguments( per_device_train_batch_size1, gradient_accumulation_steps1, max_steps3, learning_rate2e-4, fp16not torch.cuda.is_bf16_supported(), bf16torch.cuda.is_bf16_supported(), logging_steps1, output_diroutputs, optimadamw_8bit, ), ) trainer.train()max_steps3意味着只跑 3 步几十秒就能结束。如果这 3 步能正常打印 loss 并结束说明 CUDA、PyTorch、unsloth、数据格式全部通了。4.5 用 API 通道做一次对比验证微调跑通后可以用 TaoToken 的模型对话做一次效果对比。打开 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 把同样的 instruction 发进去看基座模型的输出和你微调后的输出做对比。这一步不是必须的但能帮你判断微调是否真的有效。5. 本篇常见错排查这一节按真实报错来对照。报错一torch.cuda.is_available()返回 False原因通常是装了 CPU 版 PyTorch或者驱动版本太低。排查顺序先!nvidia-smi确认驱动正常再print(torch.__version__)看是不是带cpu后缀。如果是按 3.4 重装 GPU 版。报错二ImportError: cannot import name FastLanguageModel from unslothunsloth 版本和 transformers 版本不匹配。先pip show unsloth transformers看版本然后按 unsloth 官方文档的版本对照表调整。常见做法是固定 transformers 到某个版本区间。报错三CUDA out of memory显存不够。先降per_device_train_batch_size到 1再降max_seq_length还不行就换更小的模型或开load_in_4bitTrue。用torch.cuda.get_device_properties(0).total_memory看显存总量心里有数。报错四local proxy failed或连接超时如果你在 Notebook 里调 API 通道时遇到这个先检查 Base URL 是不是写成了https://taotoken.net/api不要多加斜杠或路径。再检查 Key 是否完整复制。如果还是不通换模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 测试确认是网络问题还是配置问题。报错五401 UnauthorizedKey 无效或没带。检查请求头里是不是Authorization: Bearer 你的Key注意 Bearer 后面有一个空格。Key 如果是在 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 创建的确认没有多余空格。报错六Error reading choices或返回结构不对通常是 Model ID 填错了。按接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 里列出的 Model ID 原样填不要自己拼。报错七OAuth 相关报错如果你用的是 Claude Code 或类似工具接入遇到 OAuth 报错检查是不是把 API Key 和 OAuth 流程混用了。API 接入用 KeyOAuth 是另一套流程。Claude Code 的接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 里有说明。报错八triton相关编译错误triton 版本和 CUDA 版本不匹配。unsloth 依赖 triton 做内核加速版本错了会编译失败。按 unsloth 官方推荐的 triton 版本装。排查原则先看报错关键词再对照上面几条。如果都不匹配把完整报错贴到搜索里大概率有人遇到过。6. 把微调链路固定下来环境检查、API 通道配置、最小微调验证这三步做完你的 Jupyter Notebook 就具备了一个可复现的微调起点。我的习惯是把 3.1 到 3.3 的检查代码存成一个env_check.ipynb每次换机器或换环境先跑一遍确认全绿再动数据。另外一个小技巧在 Notebook 开头加一个 cell用%env设置环境变量把 API Key 和 Base URL 注入进去避免硬编码在代码里%env TAOTOKEN_BASE_URLhttps://taotoken.net/api %env TAOTOKEN_API_KEY你的Key然后在代码里用os.environ[TAOTOKEN_API_KEY]读取。这样分享 Notebook 时不会泄露 Key。微调本身是个迭代过程环境稳定比什么都重要。先把这条链路跑通后面换模型、换数据集、调参数都只是在这个基础上做替换。