本文将带您深入了解如何利用最先进的AI技术搭建私有知识库------从私有化部署合适的模型开始,到运用RAG架构提升信息检索的准确性和效率,再到针对特定领域进行微调以达到最佳性能。无论您是寻求技术创新的企业家、渴望提升工作效率的专业人士,还是对AI充满热情的技术爱好者,本指南都将为您提供宝贵的见解和实用的操作步骤。让我们一起开启这场智慧之旅,探索如何让数据真正为我所用,创造出无限可能。
1.私有化部署大模型
1.为什么要私有化部署
私有化部署大模型具有多方面的优势,这些优势主要围绕成本、数据隐私、灵活性、性能和效率等方面。以下是详细的解释:
1. 免费
- 成本效益:虽然初始设置可能需要一些投入,但从长远来看,避免了持续的云服务费用,可以节省大量开支。
2. 数据隐私
- 保护敏感信息:在处理涉及个人身份信息(PII)、商业机密或其他敏感数据时,本地部署能确保这些数据不会离开企业的内部网络,极大地提高了数据的安全性。
3. 无额外限制
- 自由定制:私有化部署允许根据具体需求对模型进行修改和优化,而不受第三方服务商规则或限制的影响。
4. 无需网络依赖
- 离线操作:能够在没有互联网连接的情况下运行,这对于某些特定应用场景(如远程地区作业或安全性要求极高的环境)尤为重要。
5. 灵活定制
- 高度自定义:可以根据业务需求调整模型参数、训练数据集等,以满足特定的应用场景或行业要求。
6. 性能和效率
- 优化资源配置:能够针对硬件环境进行优化配置,从而获得更好的性能表现,尤其是在处理大规模数据集时更为显著。
通过上述几点可以看出,私有化部署不仅有助于企业更好地控制其技术资产,还能有效提升数据安全性和使用效率,特别适合那些对数据保密性要求高、希望减少长期运营成本的企业或组织。此外,它也为开发人员提供了更大的灵活性,以便于他们根据实际需求调整和优化模型
2.如何进行私有化部署
首先我们要选择适合自己的模型,本篇将以deepseek为例,带您完整的从部署到微调,打造自己的专属知识库。
关于如何选择适合自己的模型,可以参考下表:
模型名称 | CPU要求 | 内存要求 | 硬盘要求 | 显卡要求 | 适用场景 |
---|---|---|---|---|---|
DeepSeek-R1-1.5B | 4核(i3-12100F/R5 5600G) | 8GB DDR4 | 3GB SSD | 可纯CPU运行,GPU需4GB+(如GTX 1650) | 树莓派/旧笔记本/嵌入式设备,基础问答、物联网应用 |
DeepSeek-R1-7B | 8核(R7 5700X/i5-13600K) | 16GB DDR5 | 8GB NVMe SSD | RTX 3070(8GB)或A5000(16GB) | 中小企业开发测试,文本摘要、翻译、轻量级对话 |
DeepSeek-R1-8B | 8核(同7B) | 16GB DDR5 | 8GB NVMe SSD | RTX 3070 Ti(8GB)或A5000 | 代码生成、逻辑推理等需高精度任务 |
DeepSeek-R1-14B | 12核(i9-13900K/R9 7950X) | 32GB DDR5 | 15GB NVMe SSD | RTX 4090(24GB)或A5000(24GB) | 企业级长文本处理、学术研究、法律网页分析 |
DeepSeek-R1-32B | 16核(Xeon Gold 6338) | 64GB DDR5 ECC | 30GB NVMe RAID | 双A100 80GB或4x RTX 3090 | 金融预测、多模态预处理、大规模数据分析 |
DeepSeek-R1-70B | 32核(Xeon Platinum 8480) | 128GB DDR5 | 70GB NVMe RAID | 8xA100 80GB或4x RTX 4090 | 科研计算、自动驾驶决策、超大规模生成任务 |
DeepSeek-671B全量版 | 64核集群(双路EPYC 7763) | 512GB DDR4 ECC | 300GB分布式存储 | 8xA100/H100(总显存640GB) | 国家级AI研究、通用智能开发(AGI探索) |
我们将以Ollama进行本次私有化部署。
一、核心功能
Ollama 是一个专为本地运行大型语言模型(LLM)而设计的轻量级工具,提供了从部署到管理的一站式解决方案,尤其适合希望在本地环境中高效使用 AI 模型的开发者和企业。
✅ 本地模型管理
- Ollama 支持从官方模型库或自定义模型库拉取预训练模型,并在本地保存和加载。它支持各种流行的模型格式(如 ONNX、PyTorch、TensorFlow)。这使得开发者可以在不依赖云服务的情况下进行模型的管理和使用
✅轻量级与可扩展性
- 作为轻量级框架,Ollama 保持了较小的资源占用,同时具备良好的可扩展性,允许用户根据需要调整配置以适应不同规模的项目和硬件条件,即使在普通笔记本电脑上也能流畅运行
✅ 高效推理
- 通过 GPU/CPU 的加速,Ollama 提供高效的模型推理,适合本地化应用或需要控制数据隐私的场景。它能够根据硬件条件自动优化模型性能,确保流畅的用户体验
✅ 模型定制
- Ollama 允许用户通过 Modelfile 来定制自己的模型,包括设置温度参数、上下文长度等,从而改变模型的输出风格和行为
✅ API支持
- Ollama 提供了一个RESTful API,使得开发者能够轻松创建、运行和管理大型语言模型实例,降低了与模型交互的技术门槛。API接口兼容OpenAI的标准,方便与其他应用程序集成
二、技术优势
Ollama 不仅功能强大,还具备出色的性能表现和技术兼容性,适用于各类本地化部署环境。
🔧 简化的部署流程
- Ollama 目标在于简化在 Docker 容器中部署大型语言模型的过程,使得非专业用户也能方便地管理和运行这些复杂的模型,无需复杂的配置和安装过程,只需几条命令即可启动和运行
🌐 数据隐私保护
- 由于模型运行在本地环境,Ollama 确保了数据交互全程在本地完成,避免了敏感信息上传到云端的风险,这对于医疗、法律等领域尤其重要
🔄 社区驱动
- 作为一个活跃的开源项目,Ollama 拥有一个强大的社区支持系统,用户可以从社区获得帮助,分享经验和最佳实践,促进技术的进步和发展
🖥 多平台支持
- Ollama 支持 macOS、Linux 和 Windows 等多种操作系统,提供了广泛的兼容性和灵活性,满足不同用户的需要
Ollama 的目标是让大型语言模型的使用更加简单、高效和灵活,无论是对于开发者还是终端用户。 本次部署为了确保环境一致性,我们采取docker的方式部署:
1.首先我们针对docker进行一些安装/配置
csharp
#服务器安装docker
#yum更新
yum install -y yum-utils device-mapper-persistent-data lvm2
#yum追加docker-ce镜像包
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
#yum安装docker-ce,然后yyyyy
sudo yum install docker-ce
#启动
systemctl start docker
#结束
docker ps命令执行成功
##服务器配置docker加速
#修改/etc/docker下的daemon.json文件
{
"data-root": "/home/docker",
"registry-mirrors":[
"https://docker.xuanyuan.me"
],
}
2.我们进行镜像安装启动
bash
#docker安装ollama
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
#ollama部署模型
docker exec -it ollama ollama run deepseek-r1:7b
这样我们的私有化部署就完成了,ollama部署的模型不止可以在外部调用,同时也是支持api调用的:
vbnet
curl http://localhost:11434/api/generate -d '{
"model": "deepseek-r1:7b",
"prompt": "今天天气怎么样"
}'
#默认会是流式返回,如果不想流式返回,可以添加参数"stream": false
curl http://localhost:11434/api/generate -d '{
"model": "deepseek-r1:7b",
"prompt": "今天天气怎么样",
"stream": false
}'
关于如何私有化部署,我们就完成了,具体的其他api操作,我们可以参考api文档,就不多赘述了,同时我们也可以通过一些工具来提升我们本地模型的使用体验,推荐使用google插件:
Page Assist(浏览器插件) + Ollama - 实现本地模型联网
我们只需要简单配置一下api地址为127.0.0.1:11434就可以完美实现本地对话了:
小结
- 安装配置docker环境(加速器)
- 拉取ollama镜像:podman(docker) run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
- 选择适合自己的模型:podman(docker) exec -it ollama ollama run deepseek-r1:7b(目前默认lastet就是7b)
- 为了方便体验可以配置Page Assists
2.私有知识库打造
首先,还是为什么要构建私有知识库:
- 定制灵活:支持无限结构调整
- 数据所有权:完全私有,可永久留存
- 隐私安全性:零数据泄露风险
总结:本地知识库的本质是"反脆弱性"
- 对抗算法垄断:拒绝被通用搜索引擎或推荐系统操控信息流。
- 抵御技术依赖:在公有云服务中断(如断网、API涨价)时仍稳定运行。
- 构建数字资产:将知识转化为可继承、可迭代的资产,成为个人或组织的核心竞争力。
通过本地知识库,用户不仅掌控了知识本身,更掌握了定义知识、使用知识、保护知识的终极权力。
主流构建私有知识库的两种方式
基于RAG架构构建私有知识库
基于微调实现方向领域专家
2.1基于RAG架构构建私有知识库
什么是RAG架构
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合信息检索与生成模型的人工智能技术,旨在通过检索外部知识库中的信息来增强语言模型的生成能力。
核心概念
- Embedding:通过一个专门的模型来把你上传的文本、表格数据等本地文件处理成机器能理解的 "数字代码。
- Vector Database:用来存储上一步处理好的 "数字代码" 的数据库,它能够高效地存储和检索这些向量。
- LLM:负责加工整理回答。

那么基于这三个概念就组成了RAG架构
- 检索(Retrieve):当用户提出一个问题时,首先通过 Embedding 模型将问题转换成向量,后通过这个向量去向量库里检索。
- 增强(Augment):向量数据库中返回有可能包含这个问题的一些前几个段落,把这些段落放在一起,作为上下文信息,和原始问题一起输入到 LLM 中。
- 生成(Generate):LLM 根据整理好的上下文信息,生成一个准确且连贯的回答。
RAG架构流程图

小结:
简单来说就是一种让 AI 更聪明回答问题的方法,它分为三个步骤:找资料、整理资料和回答问题。首先,它会在一个知识库(就像一个装满资料的书架)里找到和问题相关的部分;然后,把找到的内容整理成简单易懂的形式;最后,用整理好的内容生成一个清晰准确的回答。这种方法让机器的回答更准确、更有依据,还能随时更新知识库,用最新的信息来回答问题。
RAG架构基础实现
这里我们实现的工具是Anything LLM:
Anything LLM 是一款基于 RAG架构的本地知识库工具,能够将文档、网页等数据源与本地运行的大语言模型(LLM)相结合,构建个性化的知识库问答系统。它支持多种主流模型的 API 接入方式,如 OpenAI、DeepSeek 等,它的优点在于开箱即用,同时完全本地化,无数据外传,适合个人和小型企业办公使用。
工具 | 核心定位 | 核心功能亮点 | 适用场景 |
---|---|---|---|
AnyLLM | 隐私优先的去中心化大模型 | - 数据不经过第三方服务器 - 多租户权限管理 - 支持多模态和海量数据集成 | 个人或企业对数据隐私要求高的场景,如内部知识管理、敏感数据问答 |
Dify | 低代码大模型应用开发平台(LLMOps) | - 可视化工作流编排(ChatFlow/WorkFlow) - 内置 Agent 框架与 RAG 相关插件 - 支持多种模型 API 快速组装 | 企业 AI 应用开发、客服、内容生成、数据分析等需要快速迭代的场景 |
RAGFlow | 端到端的高质量 RAG 引擎(深度文档管理) | - 复杂文档解析(PDF、PPT、邮件、图片等) - 多模态支持 - 文本理解与逻辑编排、向量化召回与重排序优化 | 需要处理多格式文档且答案准确性要求高的领域,如金融、法律、医疗的知识问答 |
Anything LLM安装配置
为了确保环境一致性,我们还是使用docker来进行安装启动
bash
export STORAGE_LOCATION=$HOME/anythingllm && \
mkdir -p $STORAGE_LOCATION && \
touch "$STORAGE_LOCATION/.env" && \
podman run -d -p 13001:3001 \
--cap-add SYS_ADMIN \
-v ${STORAGE_LOCATION}:/app/server/storage \
-v ${STORAGE_LOCATION}/.env:/app/server/.env \
-e STORAGE_DIR="/app/server/storage" \
mintplexlabs/anythingllm
执行后我们可以看到已经启动成功了,我们可以访问 http://127.0.0.1:13001 查看
接下来我们进行一下基础配置,首先选择一下LLM提供商,这里我们本地部署了Ollama直接选择Ollama就好(也可以选择其他厂商,提供key就行),他会自动带出我们安装的大模型
初始化完成后,我们还可以去设置里更改 Embedding 和 Vector Database:
Embedding模型选择
Embedding模型也非常重要,它处理的准确性直接决定了基于知识库回答的准确度,我们可以看到有很多模型支持我们选择,但是需要一些小小的money。这里我们可以选择默认的 AnythingLLM 提供的嵌入模型,它是完全运行在本地且免费的嵌入模型,当然准确度就比较一般了,也可以选择在Ollama安装的嵌入模型,效果会稍好一点点(shaw/dmeta-embedding-zh中文识别友好)。
Vector Database选择
向量数据库(核心作用是通过高效的相似性搜索和语义匹配,解决非结构化数据(如文本、图像、音频等)的智能处理问题)这里我们就选择默认的LanceDB就行,它也是完全运行在本地且免费的。
最后,我们进行一下工作区配置,就可以使用了
注意配置时候将温度设置在 0.5-0.7 (推荐 0.6) 的范围内,以防止无休止的重复或不连贯的输出。 这里的温度不是物理的温度,模型的温度(Temperature) 是一个用于调节模型输出概率分布平滑程度的超参数,常见于分类任务、生成模型(如语言模型)和知识蒸馏中。它通过调整 Softmax 函数 的计算方式,影响模型对预测结果的"确定性"或"探索性"。低温(T < 1): 提高模型对高置信度类别的偏好("更确定")。
基础配置都完成了,接下来我们就可以聊天体验了,我们首先上传个文档,然后问一些文档的问题,可以发现基于文档回答出了结果。另外 Anything LLM 还支持直接贴网页链接来构建知识库,也可以使用agent搜索,搭建工作流等,使用MCP等配置,具体内容大家就自己体验吧~
总结:
本部分内容主要介绍了RAG架构的概念,流程,以及对基于Anything LLM对RAG架构的简单实现,关于一些其他的RAG架构实现,大家可以自行体验,核心的流程思路是不变的
2.2基于微调实现方向领域专家
为什么要微调
我们平常接触到的大模型如 GPT、DeepSeek 等都是基于海量的通用数据训练而成的,它们具备非常强大的语言理解和生成能力,代表大而全,但是我们一般私有化部署的模型,效果可能就没有那么明显,我们期待他是一个小并且精通某一方向的,这时候我们就需要微调了,下面是几个主要原因:
- 领域适配
- 数据效率
- 可控性
- 经济性
微调的本质是在通用能力与专业需求之间找到最佳平衡点,是当前大模型落地应用的最关键技术路径。
微调的基本概念与作用
定义:微调是在预训模型(如GPT、QIANWEN)的基础上,使用少量标注数据对模型进行二次训练,使其掌握特定任务的能力(如医疗问答、法律文本分类)
- 类比:类似于"博士生在通识教育后专攻某个研究方向"。
- 作用:减少对新数据的需求、降低训练成本,并迁移通用知识到垂直领域
与预训练的区别:
- 预训练:通过海量无标注数据学习通用特征(如语言语法、图像纹理),形成基础模型(人话就是通过海量数据让模型有基础认知)。
- 微调:在预训练基础上,通过任务特定数据调整参数,实现知识迁移
微调的核心是在模型能力、数据资源与业务需求之间找到平衡点,需结合技术、管理与伦理多维度协同优化。
微调常见问题以及应对策略
问题类型 | 核心解决方案 | 技术工具 |
---|---|---|
过拟合 | 正则化 + 数据增强 + LoRA | PyTorch Lightning, Hugging Face PEFT |
灾难性遗忘 | EWC + 渐进学习 | Fisher信息计算库 |
资源不足 | LoRA + 混合精度训练 | NVIDIA Apex, DeepSpeed |
数据不足 | 半监督学习 + 合成数据 | Snorkel, GPT-3.5生成 |
生成失控 | RLHF + 对抗训练 | HumanFeedback API, A3C |
部署延迟 | 4-bit量化 + ONNX | GPTQ, TensorRT |
微调基本流程

微调的实现
我们本次的实现方案为:
- 环境:Colab(由 Google 提供,提供免费的t4-gpu(2070s+))
- 工具:unsloth(开源、高效、低显存、支持多种模型及量化,常见微调工具)
- 算法:LoRA (最著名的部分参数微调算法)
- 基座模型:DeepSeek-R1-Distill-Qwen-7B(7b量化版)
- 微调方式:有监督微调(SFT核心优势:简单高效,快速适配)
前期准备
首先我们需要创建环境,安装相关依赖
- 注册Hugging Face账号,创建具有write权限的token
- 访问google colab登陆google账号,新建空白的colab环境
- 准备数据集,我们这里使用的是Hugging Face的一个中文训练集
- 选取预训练模型,我们这里使用的是DeepSeek-R1-Distill-Qwen-7B
GO: 首先我们访问空白的colab环境,更改一下运行时类型
1.安装环境依赖
perl
# %%capture 魔法命令:在Jupyter笔记本中捕获输出,不显示安装过程的冗余信息
%%capture
import os # 导入操作系统交互模块
# 检查是否在Google Colab环境中:通过环境变量是否存在"COLAB_"前缀判断
if "COLAB_" not in "".join(os.environ.keys()):
# 非Colab环境:直接安装unsloth库(AI加速库)
!pip install unsloth
else:
# Colab环境专用安装逻辑(需要处理更多依赖关系)
# 安装核心依赖项并禁用自动依赖安装(--no-deps)
!pip install --no-deps bitsandbytes accelerate xformers==0.0.29.post3 peft trl==0.15.2 triton cut_cross_entropy unsloth_zoo
# 安装机器学习相关基础库
!pip install sentencepiece protobuf "datasets>=3.4.1" huggingface_hub hf_transfer
# 再次安装unsloth(此时依赖已通过--no-deps跳过)
!pip install --no-deps unsloth
# 强制更新关键数据集处理库(用户特别强调需要更新)
# 解决数据集加载时的兼容性问题(用户备注"会疯掉")
!pip install -U datasets fsspec # -U 表示强制升级到最新版本
#
# 代码逻辑解析:
# 1. 环境检测:通过检查环境变量区分本地环境和Google Colab环境
# 2. 依赖管理策略差异:
# - 本地环境:简单安装unsloth
# - Colab环境:
# * 安装特定版本的xformers(GPU加速库)
# * 使用--no-deps避免依赖冲突
# * 单独处理数据集相关库的版本问题
#3. 用户特别标注的更新命令:确保datasets/fsspec为最新版,避免已知兼容性问题
#
2.导入模型并加载
ini
# 导入FastLanguageModel类(用于高效加载和运行大语言模型)
from unsloth import FastLanguageModel
# 导入PyTorch深度学习框架(用于张量计算和自动微分)
import torch
# 从Hugging Face Hub下载模型快照的工具(用于本地缓存模型)
from huggingface_hub import snapshot_download
# BitsAndBytes量化配置类(用于4位/8位内存优化量化)
from transformers import BitsAndBytesConfig
# 操作系统交互模块(用于处理文件路径等系统操作)
import os
# 设置模型单次处理的最大文本长度(超过此长度会截断或分段处理)
max_seq_length = 2048
# 设置数据类型为None表示使用FP32精度(若设为"auto"则自动选择FP16/AMP)
dtype = None
# 启用4位量化(显著减少显存占用,但可能降低模型精度)
load_in_4bit = True
# 指定要加载的模型标识符(Hugging Face Hub上的模型仓库地址)
model_name = "unsloth/DeepSeek-R1-Distill-Qwen-7B"
# 创建本地缓存目录路径(将模型文件保存在本地的缓存目录)
local_cache_dir = "./" + model_name.replace("/", "_") # 将路径中的斜杠替换为下划线
# 配置4位量化参数(nf4量化比fp4更节省显存且速度更快)
quant_config = BitsAndBytesConfig(
load_in_4bit=True, # 启用4位量化
bnb_4bit_quant_type="nf4", # 使用NormalFloat4量化(针对激活值分布优化)
# 为不同网络层指定不同的量化类型(在LoRA(Low-Rank Adaptation)微调中,仅更新通过低秩矩阵分解引入的旁路参数,且这些参数通常集中在Transformer架构的注意力层和MLP层。):
# - 注意力层的权重使用fp4(4位浮点)
# - 前馈层的权重使用nf4(优化后的4位浮点)
bnb_4bit_layer_quantization_map={
"attention.layers": "fp4",
"feedforward.layers": "nf4"
}
)
# 尝试下载模型并处理异常
try:
print(f"Attempting to download model {model_name} to {local_cache_dir}")
# 从Hugging Face Hub下载模型到本地缓存目录
snapshot_download(
repo_id=model_name, # 模型仓库ID
local_dir=local_cache_dir, # 本地保存路径
# 以下参数默认省略但实际生效:
# resume_download=True, # 断点续传
# local_files_only=False # 允许网络下载
)
print("Download successful or files already in cache.")
except Exception as e:
print(f"Error during download: {e}")
print("Please check your internet connection and try again.")
# 重新抛出异常以便上层处理(显示完整错误堆栈)
raise
#加载预训练模型deepseek,并获取tokenizer工具
model, tokenizer = FastLanguageModel.from_pretrained(
model_name=local_cache_dir, # 指定本地缓存目录
max_seq_length=max_seq_length, #指定最大长度
dtype=dtype, #指定数据类型
load_in_4bit=load_in_4bit, #指定是否使用4位量化( 必须与quant_config联动)(这里模型f16精度的,但是强制使用4位精度加载,4位量化会将F16权重动态映射到INT4整数,无需原始量化文件,我们有bitsandbytes,但是失败了,你使用的模型是 蒸馏后的F16精度模型,未经过量化感知训练(QAT),导致动态量化时权重分布不兼容。现象:框架自动回退到FP16,因为4-bit量化所需的动态范围校准失败。)
quantization_config=quant_config, # 传递量化配置
trust_remote_code=True # 允许执行远程仓库中的自定义代码 加载第三方模型、使用非标准架构或高级功能
# token="授权密钥" # If you were using a token, you would still pass it here
)
3.微调前模型测试
ini
# 定义格式化模版
prompt_style_chat = """以下是描述任务的指令,以及提供进一步上下文的输入。
请给出一个适当的回答来完成当前对话。
回答前请仔细思考问题,并创建一个逻辑连贯,思路清晰的思考过程,确保回答准确无误。
### 指令:
你是一位精通 八字算命、风水、易经卦象、塔罗牌占卜、紫微斗数、星象、面相手相和运势预测的算命大师。
请回答以下算命问题。
### 问题:
{}
### 回答:
<think>{}"""
#准备模型用于推理
FastLanguageModel.for_inference(model)
question = "1991年闰1月11日未时生人,想要了解健康运势"
# 使用tokenizer 对格式化后问题进行编码,并移动到GPU
inputs = tokenizer([prompt_style_chat.format(question, "")], return_tensors="pt").to("cuda")
# 定义输出,使用模型生成回答
outputs = model.generate(
# 输入文本的"数字编码"(比如把"你好"转成 [5, 32, 10] 这样的数字序列)
input_ids=inputs.input_ids,
# 生成文本的最大长度(以"词"为单位)
max_new_tokens=1200,
# 使用缓存中间计算结果(加速生成)。
use_cache=True,
)
# 总结:这段代码的作用是 让大模型根据你的输入,自动生成一段符合上下文的文本,参数控制生成的长度和速度。就像给模型一个"起点",让它自由发挥!
# 对回答进行解码成可读文本
response = tokenizer.batch_decode(outputs)
#打印输出
print(response[0].split("### 回答:")[1])
4.构建模版
ini
prompt_style = """以下是描述任务的指令,以及提供进一步上下文的输入。
请给出一个适当的回答来完成当前对话。
回答前请仔细思考问题,并创建一个逻辑连贯,思路清晰的思考过程,确保回答准确无误。
### 指令:
你是一位精通 八字算命、风水、易经卦象、塔罗牌占卜、紫微斗数、星象、面相手相和运势预测的算命大师。
请以专业的角度回答以下算命问题。
### 问题:
{}
### 回答:
<思考>
{}
</思考>
{}"""
5.加载数据集
ini
# 加载函数(此函数可以直接加载Hugging Face数据集)
from datasets import load_dataset
from google.colab import userdata
HFuser = userdata.get('HF_TOKEN')
# 加载数据集
dataset = load_dataset(
"Conard/fortune-telling", # 数据集名称
"default", # 配置名称(子集),'default' 表示加载默认配置
split="train[:200]", # 数据集划分与切片
trust_remote_code=True # 允许执行远程代码
)
# 设置文本生成结束的标记 tokenizer.eos_token
EOS_TOKEN = tokenizer.eos_token # 必须添加结束标记,让EOS_TOKEN和分词器的eos_token一致
# 数据集格式化
def formatting_prompts_func(examples):
# 从数据集中提取问题、思考过程、回答
inputs = examples["Question"]
cots = examples["Complex_CoT"]
outputs = examples["Response"]
# 格式化后文本
texts = []
# 遍历每个问题/思考/回答,格式化到text中
for input, cot, output in zip(inputs, cots, outputs):
# 使用定义的字符串模板插入数据,并打上结束标记,完整构建训练文本(确保必须包含输入、输出和EOS_TOKEN)
text = prompt_style.format(input, cot, output)+EOS_TOKEN
texts.append(text)
return {"text": texts}
# 检查数据集
dataset = dataset.map(formatting_prompts_func, batched=True) #batched=True 固定按批量处理,需要和处理函数匹配,还可以设置batch_size
print(dataset[0]["text"]) # 必须显示完整结构化文本
6.配置微调算法
ini
# Wandb(全称 Weights & Biases)是一个 机器学习实验管理工具,相当于给你的模型训练过程装上"监控摄像头"和"实验日记本"。它帮助开发者 记录数据、可视化结果、协作复现实验,尤其适合需要反复调参的深度学习项目。
!pip install wandb
# 方便我们观察训练记录
import wandb
wandb.login(key="af6c368e2a2c84f206c23ad519b300a22d536f98")
# 用 LoRA(低秩自适应)方法对大模型进行高效微调
model = FastLanguageModel.get_peft_model(
# 已加载好的模型
model,
# LoRA 的"秩"(Rank),控制微调时添加的"简化版补丁"的大小。
#(秩越大,调整的灵活性越高,但训练时间变长;秩越小,调整越简单,但可能效果差,类比:修房子时,用小的补丁(r=8)只能局部修补,用大的补丁(r=16)可以修改更多细节。)
r=16,
# 控制 LoRA 对原模型参数的"影响力"。
#(LoRA 提出的修改建议,乘以这个权重(alpha)后,再应用到原模型上。数值越大,LoRA 的修改越激进;数值越小,修改越保守。)
lora_alpha=16,
# 指定模型中需要微调的关键模块(例如注意力机制中的计算层,告诉 LoRA:"请只调整这些房间(模块)的家具(参数)"。
# 例如:q_proj, k_proj, v_proj 是注意力机制中的"查询、键、值"计算模块,直接影响模型对信息的理解)
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
# 是否在训练时随机丢弃部分 LoRA 参数(防过拟合)(设为 0 表示不启用,设为 0.1 表示随机丢弃 10% 的参数。类比:学习时偶尔忘记一些知识点,防止死记硬背)
lora_dropout=0,
# 是否给 LoRA 的调整参数添加偏移量(Bias)。(类比:调整家具时,是否允许整体移动位置(偏移)。"none" 表示不添加,直接调整原参数。)
bias="none",
# 用梯度检查点技术节省显存,支持训练更长的序列(类比:拍照时只存关键帧,减少内存占用,但需要更多计算还原画面。"unsloth" 是优化后的实现,比原生 PyTorch 更快。)
use_gradient_checkpointing="unsloth",
# 固定随机种子,保证每次训练结果一致(每次做实验都用同样的初始条件,确保结果可复现。团队合作时,大家用相同的种子,结果不会"因人而异"。)
random_state=3407,
# 是否使用 RSLoRA(改进版 LoRA,自动调整缩放因子)
#(常规微调我们用不到,不涉及梯度问题(梯度问题指的是反向传播过程中计算的梯度(即参数更新的方向和幅度)出现异常,导致模型无法正常训练的现象),并且会增大开销,额外增加复杂度)
#(类比:LoRA 是普通补丁,RSLoRA 是智能补丁,能自动调整修改幅度,避免参数过大导致模型不稳定。默认不开启(False),除非遇到梯度消失问题。)
use_rslora=False,
# 是否用 LoFTQ(基于 SVD 分解的 LoRA 变体)替代标准 LoRA。(类比:LoRA 是通用补丁,LoFTQ 是高级补丁,需要额外配置(loftq_config)。默认不用(None),除非需要更高效的训练。)
loftq_config=None,
)
#一句话总结
#这个函数的作用是:用 LoRA 方法对大模型做高效微调,通过调整 r(调整幅度)、target_modules(调整哪些部分)等参数,控制训练速度和效果。其他参数(如 gradient_checkpointing)主要是优化显存和训练稳定性。
7.配置微调参数
ini
from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported
# Wandb(全称 Weights & Biases)是一个 机器学习实验管理工具,相当于给你的模型训练过程装上"监控摄像头"和"实验日记本"。它帮助开发者 记录数据、可视化结果、协作复现实验,尤其适合需要反复调参的深度学习项目。
!pip install wandb
# 方便我们观察训练记录
import wandb
wandb.login(key="af6c368e2a2c84f206c23ad519b300a22d536f98")
run = wandb.init(
# Set the wandb entity where your project will be logged (generally your team name).
entity="hao894732835-github",
# Set the wandb project where this run will be logged.
project="fortune-tellin",
# Track hyperparameters and run metadata.
config={
"learning_rate": 2e-4,
"architecture": "DEEPSEEk",
"dataset": "Conard/fortune-telling",
"epochs": 3,#(75*8/200)
},
)
# 用监督式微调(Supervised Fine-Tuning)训练一个大模型,类似于"老师根据学生的作业(数据集)批改调整教学方法"。
trainer = SFTTrainer(
# 已经加载好的大模型
model=model,
# 分词器,负责把文本拆分成模型能理解的"词语块"。(像一本字典,告诉模型如何把句子切成"单词"或"符号"。)
tokenizer=tokenizer,
# 训练用的数据集(我们上面从hugging Face拉取的数据集)。
train_dataset=dataset,
# 添加验证用的数据集(我们没有验证集,所以so)
# eval_dataset=val_dataset,
# 指定数据集中存储文本的字段名。(我们格式化后的text)
dataset_text_field="text",
# 模型一次能处理的文本最大长度(比如 2048 个词)。(超过会被截取)
max_seq_length=max_seq_length,
# 用多少个 CPU 进程并行处理数据(加速数据加载)
dataset_num_proc=2,
# 是否将不同长度的句子"打包"成同一批次(减少填充,提高效率)。
# 如果开启:把长句子和短句子混装,用空位填充短句(可能浪费算力)。
# 如果关闭:严格按句子长度分组(更高效,但可能耗时)。
packing=False,
# TrainingArguments 是 训练模型的"配置表",用来控制训练过程中的各种参数,比如学习率、批次大小、训练步数等。你可以把它想象成一份"训练计划书",告诉模型如何高效学习
# 当前Epoch为3,Batch为8,tootal_step为75
args=TrainingArguments(
# 每个 GPU 上一次训练的样本数量。(每个学生一次做 2 道题,题太多会记不住,题太少效率低。)
per_device_train_batch_size=2,
# 累积多少次梯度后再更新模型参数。(虽然每次只做 2 道题,但累积 4 次后再统一批改(等效于一次做 8 道题)。)
gradient_accumulation_steps=4,
# 预热步数。(前 5 步慢慢增加作业难度,避免学生一下子被难倒。)预热步数过短可能导致训练初期学习率骤增,引发不稳定,增加预热步数至 10~15(占总步数的 13%~20%)。
warmup_steps=10,
# 最大训练部署(每步处理一个批次)。(总共训练 75 次作业,之后停止。)
max_steps=75,
# 模型参数更新的步长(学习率)。(学生每天学习的专注度,太高容易累(发散),太低进步慢。7b小模型一般为1e-5 - 2e-4)(第一遍不太行,改成1e-4试试)
learning_rate=1e-4,
# 是否用半精度(FP16)加速训练。(用更轻便的笔记本(FP16)做作业,速度快但可能出错,自动根据硬件支持选择。)
fp16=not is_bfloat16_supported(),
# 否用 BFloat16 混合精度训练(比 FP16 更稳定)。(用一种更省内存且稳定的计算方式,适合大模型。)
bf16=is_bfloat16_supported(),
# 每隔多少步记录一次训练日志(如损失值)。(每做完 1 次作业就记录一次成绩,方便观察进步。)
logging_steps=1,
# 使用的优化器(AdamW 的 8 位简化版)(用一种省内存的优化方法,类似用更薄的笔记录重点。)
optim="adamw_8bit",
# 权重衰减系数,防止模型过拟合。(定期让学生复习旧知识,避免死记硬背新题。)
weight_decay=0.01,
# 学习率下降策略(线性递减)。(随着训练推进,逐渐降低学习难度(类似高考前减少新题量)。)线性衰减可能无法充分探索参数空间,容易陷入局部最优。建议:改用 余弦退火(cosine) 或 OneCycleLR 策略,更平滑地调整学习率。
lr_scheduler_type="cosine",
# 随机种子,保证实验可复现。(每次做题的顺序和选题方式固定,确保结果一致。)
seed=3407,
# 保存训练结果的文件夹。
output_dir="outputs",
# 是否将训练结果上报到平台(如 TensorBoard)。
report_to="wandb"
# 新增以下参数:启用验证集评估(没有)
# evaluation_strategy="epoch", # 每个 epoch 结束后评估验证集
# save_strategy="epoch", # 每个 epoch 保存一次模型
# load_best_model_at_end=True, # 训练结束时加载最佳模型
# metric_for_best_model="eval_loss", # 选择最佳模型的指标
),
)
# 总结:这个函数的作用是:用学生(模型)和作业(数据集),通过反复练习(训练)提升能力。参数控制练习的节奏、难度和资源分配,比如每次做多少题、多久休息一次、如何防止作弊(过拟合)等。
# import evaluate
# from transformers import pipeline
# 示例:计算文本生成任务的BLEU分数
# metric = evaluate.load("bleu")
# def compute_metrics_fn(eval_pred):
# predictions, labels = eval_pred
# 解码预测和标签(假设是生成任务)
# decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
# decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
# 计算BLEU分数
# results = metric.compute(predictions=decoded_preds, references=decoded_labels, average="macro")
# return {"eval_bleu": results["bleu"]}
# 如果是分类任务,可以直接返回准确率:
# def compute_metrics_fn(eval_pred):
# logits, labels = eval_pred
# predictions = np.argmax(logits, axis=-1)
# return {"eval_accuracy": (predictions == labels).mean()}
# 启动训练
trainer.train()
# 训练完成后加载测试集
# results = trainer.evaluate(eval_dataset=test_dataset)
# 输出测试结果(例如损失值、自定义指标)
# print(f"Test Loss: {results['eval_loss']}")
# if "eval_bleu" in results:
# print(f"Test BLEU: {results['eval_bleu']}")
# 一、通用划分比例
# 数据集类型 典型比例 适用场景
# 训练集 60-80% 模型参数学习
# 验证集 10-20% 超参数调优、早停、模型选择
# 测试集 10-20% 最终模型性能评估
# 常见组合:
# 标准划分:60%训练集 + 20%验证集 + 20%测试集
# 大数据场景:98%训练集 + 1%验证集 + 1%测试集(适用于百万级样本)
# 小数据场景:80%训练集 + 10%验证集 + 10%测试集(适用于千级样本)
# 我们是一个生成(偏分类)任务,训练损失率在1.0左右(咱们1.2所以把学习率降低为1e-4试试),验证损失率未知,理论上可以接受(没有验证集无法确认过拟合或欠拟合)
# 理想情况:训练损失和验证损失均稳步下降,且验证损失未上升(无过拟合)。
# 过拟合风险(学过头了):训练损失持续下降,但验证损失上升或停滞。
# 欠拟合(没学好):训练损失和验证损失均无下降(可能学习率过低或模型复杂度不足)
9.测试训练成果
ini
# unsloth在微调结束后,会自动更新模型权重(在缓存中),因此无需手动合并模型权重即可直接调用微调后的模型
FastLanguageModel.for_inference(model)
question = "1991年闰1月11日未时生人,想要了解健康运势"
# 使用tokenizer 对格式化后问题进行编码,并移动到GPU
inputs = tokenizer([prompt_style_chat.format(question, "")], return_tensors="pt").to("cuda")
# 定义输出,使用模型生成回答
outputs = model.generate(
# 输入文本的"数字编码"(比如把"你好"转成 [5, 32, 10] 这样的数字序列)
input_ids=inputs.input_ids,
# 标记输入中哪些位置是真实文字,哪些是填充的(比如长句子截断后补的空位)。
attention_mask=inputs.attention_mask,
# 生成文本的最大长度(以"词"为单位)
max_new_tokens=1200,
# 使用缓存中间计算结果(加速生成)。
use_cache=True,
)
# 对回答进行解码成可读文本
response = tokenizer.batch_decode(outputs)
#打印输出
print(response[0].split("### 回答:")[1])
10.保存量化模型
ini
# 获取存储的token(hugging face,自己注册)
from google.colab import userdata
HF_TOKEN = userdata.get('HF_TOKEN')
# 将模型保存为8位量化
#save_pretrained_gguf("model", tokenizer, quantization_method = "q8_0")
# 将模型保存为16位量化
#model.save_pretrained_gguf("model", tokenizer,quantization_method="f16")
# 将模型保存为4位量化
model.save_pretrained_gguf("model", tokenizer,quantization_method="q4_k_m")
11.模型上传hugging Face
ini
from huggingface_hub import create_repo
# 在hugginfface创建一个仓库
create_repo(
"ShineFire/deepseek-r1-7b-fortune-telling",
token=HF_TOKEN,
# 如果仓库已存在则不报错
exist_ok=True
)
# 将模型和分词器上传到Hugging Face(传错了)
model.push_to_hub_gguf(
"ShineFire/deepseek-r1-7b-fortune-telling",
tokenizer=tokenizer,
token=HF_TOKEN
)
# Ollama 支持直接从 Hugging Face 拉取模型,格式如下:
#ollama run hf.co/{username}/{repository}
以上我们就微调完成了,接下来我们去ollama拉取使用就好了
微调还是RAG
区别:
微调:
- 适合:拥有非常充足的数据
- 能够直接提升模型的固有能力;无需依赖外部检索;
RAG:
- 适合:只有非常非常少的数据;动态更新的数据
- 每次回答问题前需耗时检索知识库;回答质量依赖于检索系统的质量;
具体的选取我们还是要基于业务来考量
如果要提高模型对业务专有信息 的理解、增强模型在特定行业领域 的知识 - SFT
如果要获取和生成最新的、实时的信息 - RAG
结论
- 少量企业私有知识:最好微调和 RAG 都做;资源不足时优先 RAG;
- 会动态更新的知识:RAG
- 大量垂直领域知识:微调
建议:有钱就都上
总结
在本篇文章中,我们从私有化部署大模型开始,逐渐走向了通过RAG架构打造高效的私有知识库,进而基于微调构建专属领域模型。无论你是开发者还是技术爱好者,希望这篇文章都可以帮助到你,助你在AI领域迈出坚实一步。