拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DeepSeek微调实战:Ollama+Python+Transformers

DeepSeek微调实战:Ollama+Python+Transformers 简介面向零基础AI学习者的“DeepSeek本地模型训练”PDF教程特别适合只掌握JavaScript或略懂Python的初学者帮助其跨越缺乏深度学习背景的障碍尝试大模型微调实践。教程以DeepSeek-R1 1.5B为对象从环境准备讲起包括安装Ollama平台、在命令行拉取模型、确认模型存放在磁盘的具体目录并说明如何准备纯文本文件让每一行文字成为一个独立的训练样本。紧接着搭建Python环境强调安装时要勾选“把Python加入系统路径”并介绍三大依赖库的分工让读者理解它们在深度学习、自然语言处理和数据集中分别起什么作用。代码环节中文档推荐主流代码编辑器逐步演示创建目录、加载本地模型、读取多个文本文件并完成分词、截断、填充等预处理同时给出目录结构、运行命令与成功输出提示。对安装依赖时下载缓慢的现象文档也解释了终端进度信息的含义方便读者判断是正常等待还是出错这在实际操作中非常有用可避免长时间无意义等待。整个资源是一个PDF文档压缩包大小仅1.93MB结构清晰、步骤完整已有269人学习适合个人自学或作为教学参考。1. 本地部署 DeepSeek 后怎么训练先别急着写代码很多人在 Oillama 里把 deepseek-r1:1.5b 跑起来之后都会卡在同一个问题模型能聊天但想让它的回答带有自己的业务风格、术语或者固定话术不知道从哪下手。这篇文章要拆的就是这个场景——在 Ollama 本地部署好 DeepSeek 的基础上用 Python 和 Hugging Face 的工具链对它做微调fine-tuning全程用纯文本 .txt 文件当训练数据代码从加载模型一路写到保存和测试。适合完全没有深度学习背景、甚至只会 JavaScript 的人照着做也适合已经部署过 Ollama 但没碰过微调的开发者快速过一遍边界。先说结论这条路能走通但有几个坑——Ollama 的模型目录格式和 Transformers 期望的格式不一样、训练参数缺失导致 Trainer 直接报错、纯 CPU 跑小模型也需要耐心——文里都会给具体解法。2. 环境准备先确认模型路径再装三个 Python 库2.1 确认 Ollama 模型文件的实际路径Ollama 安装后模型文件默认放在C:\Users\用户名\.ollama\models但很多人会像我一样把模型迁移到别的盘。项目里给出的路径是D:\ollama\models这个路径是你自己指定的未必和我的相同。所以第一步不是写代码而是先打开文件管理器确认你本机deepseek-r1:1.5b的模型文件到底躺在哪个目录。打开 cmd 终端执行ollama list输出会显示已经拉取的模型列表。如果你之前用ollama run deepseek-r1:1.5b跑过这里一定能看到它。然后去D:\ollama\models看一眼目录结构常见的布局是D:\ollama\models\ └── blobs\ └── sha256-xxxxxxxx...注意这个blobs目录Ollama 把模型权重拆成 blob 文件存着不是 Hugging Face 那种pytorch_model.bin加config.json的结构。这意味着 Transformers 直接加载 Ollama 的目录路径大概率会失败。常见做法是去 Hugging Face 拉一份原始权重放到本地或者先用 Ollama 导出 GGUF 再转格式——不过对于 1.5b 这种小模型直接拉 Hugging Face 原版更省事。先把ollama list的输出记下来后面确认模型名字要用。2.2 安装 Python 并勾选 Add Python to PATH微调必须用 Python哪怕你完全不会也没关系照着命令复制粘贴就行。从 Python 官网下载安装包时有一个决定成败的选项安装界面第一步有个Add Python to PATH复选框必须勾上。不勾的话后续在 cmd 里敲python会提示找不到命令而你去改环境变量反而更绕。安装完验证一下python --version能输出版本号就算过。如果出现python 不是内部或外部命令说明 PATH 没配上重装一次勾上复选框比手动改环境变量更快。2.3 安装 torch、transformers、datasets 三个库打开 cmd执行pip install torch transformers datasets这三个库的分工很清晰torch是深度学习框架负责模型的前向计算和反向传播transformers负责加载预训练模型和分词器datasets负责管理和预处理训练数据。装的时候要有心理准备——torch的 wheel 包在 Windows 上有 200 MB 左右下载慢是常态。原项目里截图显示 22.2 KB/s、预计 2 小时 41 分钟这不是网速玄学是 PyPI 源的问题。遇到这种情况换成国内镜像源能快一个数量级pip install torch transformers datasets -i https://pypi.tuna.tsinghua.edu.cn/simple装完之后用pip list核验一下看到torch、transformers、datasets三个名字出现在列表里就算成功。注意版本Transformers 4.x 和 5.x 的 API 有细微差异下面的代码在 4.4x 版本上验证过装到最新版一般也没问题。2.4 创建项目目录结构在D:\ollama\下新建一个fine_tune_deepseek文件夹里面再建data子文件夹。结构如下D:\ollama\ ├── models\ │ └── deepseek-r1:1.5b\ └── fine_tune_deepseek\ ├── data\ └── fine_tune.pydata放你的 .txt 训练数据fine_tune.py放训练代码。建议目录路径里不要有空格和中文后面代码里的路径字符串也能少踩转义坑。3. 加载模型与分词器核心代码与路径参数详解3.1 使用 AutoTokenizer 和 AutoModelForCausalLM 加载模型加载是整个微调流程的地基。打开你常用的编辑器——VSCode、PyCharm、记事本都行——新建fine_tune.py写入以下代码# 导入 Hugging Face Transformers 库的自动加载工具 from transformers import AutoTokenizer, AutoModelForCausalLM # 指定本地模型路径注意使用正斜杠或双反斜杠 model_name D:/ollama/models/deepseek-r1:1.5b # 加载分词器负责把文本切分成 token tokenizer AutoTokenizer.from_pretrained(model_name) # 加载因果语言模型负责生成文本 model AutoModelForCausalLM.from_pretrained(model_name) # 验证加载是否成功 print(Model and tokenizer loaded successfully!)这里有两个关键点。第一路径分隔符要用正斜杠/因为 Python 字符串里反斜杠\会被转义D:\ollama会被解析成D:ollama报错时你根本想不到是路径问题。第二AutoTokenizer和AutoModelForCausalLM是 Transformers 的自动加载器它们会根据模型目录里的config.json自动推断正确的模型类不需要手动指定DeepseekForCausalLM之类的具体类名。如果加载时提示config.json不存在原因就是我前面说的 Ollama 目录格式问题——blobs目录里没有 Hugging Face 标准格式。解法是去 Hugging Face 搜索deepseek-r1-1.5b注意是连字符版本用git clone或下载 zip 拉一份原版权重放到D:/ollama/models/deepseek-r1-1.5b然后把model_name改成这个新路径。这个操作不复杂Hugging Face 每个模型页面都有Use in Transformers按钮复制那段加载代码里的模型名即可。3.2 运行验证python fine_tune.py在 cmd 里进入工作目录并运行cd D:\ollama\fine_tune_deepseek python fine_tune.py如果终端输出Model and tokenizer loaded successfully!说明模型和分词器都加载成功了。这一步是最容易翻车的地方报错类型五花八门缺safetensors库、torch版本和 CUDA 不匹配、模型路径有空格导致读取失败……遇到报错先看最后一行错误信息把ImportError和OSError这两类问题优先解决——前者用pip install补缺的库后者查路径和文件格式。4. 数据预处理把 .txt 训练样本转成模型能读的输入4.1 准备 .txt 训练数据在D:\ollama\fine_tune_deepseek\data目录下放几个 .txt 文件内容格式没有严格要求但建议遵循两个原则一行一个完整样本每个样本语义独立。比如你好我是小明。 今天天气真好。 你喜欢编程吗原项目用f.read()把整个文件读成一个字符串这意味着如果你把多行样本放在同一个文件里它们会被拼成一个长文本。更合理的做法是按行切分成样本列表这也是我在 4.2 的代码里推荐的方式——每个样本独立参与训练不会因为换行符产生不必要的 token 混杂。数据量方面1.5b 参数量的模型做微调最少准备几百行高质量文本如果只有十几个样本训练出来的效果基本等于没有。4.2 读取并 tokenize 训练数据在fine_tune.py中加载模型的代码之后追加以下内容import os # 数据目录路径 data_dir D:/ollama/fine_tune_deepseek/data texts [] # 遍历目录下所有 .txt 文件逐行读取 for filename in os.listdir(data_dir): if filename.endswith(.txt): with open(os.path.join(data_dir, filename), r, encodingutf-8) as f: # 按行切分去掉首尾空白跳过空行 for line in f: line line.strip() if line: texts.append(line) # 用 tokenizer 把文本批量转成模型输入 inputs tokenizer( texts, return_tensorspt, max_length512, truncationTrue, paddingmax_length, ) # 打印张量形状确认预处理成功 print(Data preprocessed successfully! Shape:, inputs[input_ids].shape)这段代码的核心是tokenizer()的四个参数。max_length512限制每个样本的最大长度超过的部分会被截断truncationTrue开启截断防止超长样本撑爆显存paddingmax_length让所有样本统一补到 512 长度这样组成 batch 时张量形状一致return_tensorspt指定返回 PyTorch 张量格式。预处理后文本变成了input_ids和attention_mask两个张量这两个就是模型真正消费的东西。有个细节值得注意如果你的训练样本普遍很短比如只有十几个字max_length设 512 会让大部分位置都是 padding token训练效率会打折扣。常见做法是先统计一下样本长度分布把max_length设在覆盖 90% 样本的阈值附近比如 128 或 256。4.3 把张量包装成 Dataset 对象直接传inputs[input_ids]给 Trainer 会报错因为 Trainer 期望的是datasets.Dataset对象或能迭代__getitem__的对象。这里补一步转换from datasets import Dataset # 把输入张量组装成 Hugging Face Dataset 格式 dataset Dataset.from_dict({ input_ids: inputs[input_ids], attention_mask: inputs[attention_mask], labels: inputs[input_ids], # 因果语言模型的标签就是输入本身 })labels设为input_ids是因果语言模型的标准做法——模型用前 n 个 token 预测第 n1 个 token所以输入和标签同源只是错开一个位置Transformers 内部自动处理这个偏移。这一步不改的话后面 Trainer 会因为train_dataset缺少labels字段而直接抛出ValueError。5. 微调模型TrainingArguments 配置与 Trainer 启动训练5.1 补全训练参数原项目代码里直接用了training_args这个变量但没有任何地方定义它——这属于典型的不完整示例直接跑python fine_tune.py会报NameError: name training_args is not defined。我需要补上这段配置这也是大多数人第一次跑通微调的关键from transformers import Trainer, TrainingArguments # 训练参数配置 training_args TrainingArguments( output_dir./results, # 训练中间结果输出目录 overwrite_output_dirTrue, # 允许覆盖旧结果 num_train_epochs3, # 训练轮数数据量小可以适当增加 per_device_train_batch_size2, # 每块 GPU/CPU 上的 batch 大小显存小就调为 1 gradient_accumulation_steps4, # 梯度累积步数等效增大 batch learning_rate5e-5, # 学习率微调常用区间 1e-5 到 5e-5 warmup_steps100, # 预热步数前 100 步学习率线性上升 logging_steps50, # 每 50 步输出一次日志 save_steps500, # 每 500 步保存一次 checkpoint evaluation_strategyno, # 不跑评估数据量小没有必要 save_total_limit2, # 最多保留 2 个 checkpoint fp16False, # 纯 CPU 训练必须关掉 report_to[], # 不向任何平台上报日志避免 API key 报错 )这些参数里对新手最友好的是per_device_train_batch_size1.5b 模型在 CPU 上跑batch_size 设为 2 是安全的如果你的机器内存小于 16 GB建议直接调到 1。gradient_accumulation_steps是等效增大的 batch 的手段显存不足时靠它弥补 batch 太小的问题。report_to[]这个参数很容易被忽略——如果你不写它Transformers 默认会尝试连接 Weights Biases 或 TensorBoard没装对应库或者没登录时会报 warning甚至卡住。5.2 定义 Trainer 并启动训练在training_args之后继续添加# 定义 Trainer把模型、参数、数据集绑定在一起 trainer Trainer( modelmodel, argstraining_args, train_datasetdataset, # 注意这里是 Dataset 对象不是 input_ids ) # 启动训练 trainer.train() print(Model fine-tuning completed!)Trainer是 Transformers 封装好的高层训练接口背后处理了前向传播、损失计算、梯度回传、学习率调度、checkpoint 保存等所有细节。你不需要手写for epoch in range(...)那种训练循环。train_dataset必须是 4.3 节构造的dataset对象这是新手最容易踩的坑——直接传inputs[input_ids]张量Trainer 内部会尝试调用__getitem__张量虽然支持索引但它没有labels字段最终会死在数据收集环节。训练开始后终端会逐行输出进度日志格式类似{loss: 1.8234, learning_rate: 3.2e-5, epoch: 0.25}关注loss的数值走向正常情况它应该逐步下降从 2.x 降到 1.x 再降到 0.8 以下不同任务量级有差异。如果几轮迭代后 loss 不降反升大概率是学习率太大了把learning_rate从5e-5调到2e-5再试。如果 loss 一上来就是nan检查数据里有没有空行或超长 token 导致的异常。5.3 TensorBoard 可视化训练过程原项目里提到用 TensorBoard 看训练曲线这个功能在训练完成后查看比较好——训练过程中开着浏览器刷新反而分散注意力。安装并启动pip install tensorboard tensorboard --logdir./results然后浏览器打开http://localhost:6006能看到loss和learning_rate随 step 变化的两条曲线。这对判断过拟合很有用如果训练 loss 持续下降但到后期出现明显抖动说明学习率偏高如果 loss 降到很低但你的测试文本效果一般说明数据量不足或数据分布太单一。5.4 纯 CPU 训练的预期管理原项目没有提硬件要求但这一点必须说清楚deepseek-r1:1.5b虽然是 1.5b 参数的小模型但在纯 CPU 环境无 NVIDIA GPU上微调速度大概只有 GPU 的十分之一到二十分之一。以 1000 条训练样本、num_train_epochs3为例CPU 训练可能需要 3 到 6 小时而一台 RTX 3060 大约 20 分钟就能跑完。如果你只有 CPU有两条路可以优化。第一条是降低max_length样本短的话设置 128 或 256能省一半以上的计算量。第二条是减小模型前向传播的开销——torch.set_num_threads(8)放在训练代码开头让 PyTorch 使用多线程推理这在部分 CPU 上能带来可感知的提速。6. 避坑与常见问题排查五条血泪经验6.1 模型路径加载报错Ollama 目录不是 Hugging Face 格式现象AutoTokenizer.from_pretrained(D:/ollama/models/deepseek-r1:1.5b)报OSError: Cant load tokenizer或config.json找不到。原因Ollama 的models\blobs目录存储的是 GGUF 量化格式不是 Transformers 需要的config.jsonpytorch_model.bin结构。两者模型权重格式不同接口也无法直接兼容。解决去 Hugging Face 下载deepseek-r1-1.5b连字符版本或使用huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B --local-dir D:/ollama/models/deepseek-r1-1.5b然后把代码里的model_name指向新目录。注意路径中不要用冒号:做目录名Windows 文件系统不允许。6.2 训练参数不完整TrainingArguments 未定义现象NameError: name training_args is not defined。原因原项目示例代码里Trainer引用了training_args但这个变量从未在示例中定义。新手照抄代码会直接报错。解决按 5.1 节代码补上完整的TrainingArguments定义。最核心的几个参数按顺序检查output_dir、num_train_epochs、per_device_train_batch_size、learning_rate。这四个缺一个 Trainer 都会报错。6.3 训练数据格式错误传了张量而不是 Dataset现象AttributeError: BatchEncoding object has no attribute labels或 Trainer 训练时直接卡死。原因Trainer 的train_dataset参数需要可迭代对象且每个样本必须包含模型前向传播所需的字段。直接传inputs[input_ids]张量虽然可以迭代但每个元素是 token id 向量没有labels和attention_mask的配套结构。解决用 4.3 节的Dataset.from_dict()把input_ids、attention_mask、labels组装成 Dataset 对象再传给 Trainer。如果你用的是datasets库 2.x 版本Dataset.from_dict的用法不变。6.4 一条 .txt 被当成一个训练样本现象你准备了 10 个 .txt 文件每个文件里 100 行文本结果训练集只有 10 条样本。原因f.read()把整个文件读成一个字符串文件内换行不会自动切成样本。解决用 4.2 节的改进版代码遍历文件时按行strip()并跳过空行每行作为一个独立样本。如果某些数据天然按段落组织比如对话记录可以自定义分隔规则比如空行分割。6.5 训练 loss 不降或为 nan现象训练日志中loss持续在 2.0 以上不下降或直接显示nan。原因loss 不降通常是学习率过大、数据噪声太高、部分样本长度远超max_length导致截断后语义断裂。loss 为 nan 常见于学习率过高导致梯度爆炸或数据中混入了非法字符。解决先检查数据中是否有空文本和异常符号。然后把learning_rate调低到2e-5或1e-5试跑 200 步观察 loss 是否开始下降。如果还是 nan把per_device_train_batch_size调到 1并确认fp16FalseCPU 训练下开启混合精度会直接崩。7. 验证与落地用微调后的模型生成文本并回导 Ollama7.1 保存微调后的模型训练完成后模型权重还留在内存里需要落盘。在fine_tune.py末尾追加# 定义输出目录 output_dir D:/ollama/fine_tune_deepseek/fine_tuned_model # 保存模型权重和分词器配置 model.save_pretrained(output_dir) tokenizer.save_pretrained(output_dir) print(fFine-tuned model saved to {output_dir})save_pretrained会写出pytorch_model.bin或safetensors、config.json、tokenizer.json等文件这个目录结构就是 Hugging Face 标准格式可以直接被 Transformers 加载。保存完毕后fine_tuned_model目录下大约有 3 到 4 个文件总大小约 1 GB1.5b 参数 fp32 权重。7.2 编写推理测试加载微调模型并生成文本模型保存是手段验证微调效果才是目标。在代码末尾追加from transformers import AutoTokenizer, AutoModelForCausalLM # 加载微调后的模型 fine_tuned_model_path D:/ollama/fine_tune_deepseek/fine_tuned_model fine_tuned_tokenizer AutoTokenizer.from_pretrained(fine_tuned_model_path) fine_tuned_model AutoModelForCausalLM.from_pretrained(fine_tuned_model_path) # 输入测试文本 input_text 人工智能是 input_ids fine_tuned_tokenizer.encode(input_text, return_tensorspt) # 生成文本控制长度 output fine_tuned_model.generate( input_ids, max_length50, # 生成的最大 token 数 num_return_sequences1, # 只返回一条结果 do_sampleTrue, # 开启采样输出更自然 temperature0.7, # 温度参数越低越保守越高越发散 ) # 解码生成的文本 generated_text fine_tuned_tokenizer.decode(output[0], skip_special_tokensTrue) print(Generated Text:, generated_text)generate()是 Transformers 的解码接口内部包含完整的自回归生成逻辑。max_length50控制输出长度注意是输入加输出的总长度do_sampleTrue让模型每次选词时按概率分布采样而不是贪心取最大值这样输出更多样temperature0.7是常用的折中点低于 0.5 会显得机械高于 1.0 容易胡言乱语。这一步能直观判断微调是否成功如果模型输出的文本明显带有你训练数据中的表达习惯或术语说明微调生效了如果输出还是基座模型的原生风格可能是数据量不够或训练轮数太少。7.3 把微调模型回导 Ollama从 PyTorch 转 GGUF微调完的模型是 PyTorch 格式Ollama 默认不认识。要让它重新跑在ollama run里需要转成 GGUF 格式并重新注册。流程分三步第一步用 Transformers 库把模型导出为 GGUF。OpenAI 的gguf库是常用工具执行pip install gguf第二步把 PyTorch 模型转成 GGUF。这里需要写一个转换脚本核心逻辑是加载fine_tuned_model并用gguf库按GGUFWriter的格式写入权重。1.5b 参数量级转换大约需要 5 到 10 分钟转换完成后得到一个.gguf文件。第三步注册到 Ollamaollama create my-deepseek -f ModelfileModelfile的内容参考FROM ./fine_tuned_model.gguf然后就能用ollama run my-deepseek和微调后的模型对话了。这一步不是必须的——如果你只在 Python 里用模型跳过即可如果想回到 Ollama 的交互界面再花十分钟转一次。7.4 一个小技巧先拿验证集做筛选我在多次微调后养成一个习惯准备 20 条“标准提问”和 3 个不同的输出长度50、80、150 tokens模型保存后逐条跑生成人工看一遍输出质量。特别注意两个崩溃模式一是输出重复同一句话说明训练数据里有大量重复模式二是输出与训练数据无关的幻觉说明数据量不足或模型学习不充分。这个筛选流程每次花费不到 5 分钟但能省下后续反复训练的几个小时。对这步筛选我建议不省——微调模型就像开盲盒偶发一次效果好不代表整体收敛量化验证比重开训练快得多。希望帮到你——把模型保存成 Hugging Face 格式、回导 Ollama 前先转 GGUF这两步做过一次就再也不会忘记目录结构和格式转换的教训了。本文还有配套的精品资源点击获取
返回列表