部署本地deepseek/Qwen模型并在局域网中多人交互
文章目录
目的: 在局域网中部署离线deepseek(Qwen模型通用,可直接替换代码中的模型路径),用自己专业领域的数据进行训练,形成某个行业的业务专家AI,可以供多人在局域网环境中在网页端进行交互对话。
硬件设施: 3070ti (8g显存),32G内存,win11系统,Anaconda虚拟环境,python编译
使用组件(全部开源免费):cuda、cudnn、nvidia显卡驱动、Anaconda虚拟环境、pycharm编译器、open-webui网页交互前端、Ollama并发交互管理、deepseek开源模型,以及其他所需的依赖库。
这些组件都有linux版本,windows上能部署,linux系统也能部署。
1.需要的环境包和模型
1.1 deepseek模型下载
本人硬件条件有限,选择的是DeepSeek-R1-7B-instruct原生模型,在加载时进行4-bit量化也能跑起来。下载模型的时候要注意是数学类模型还是通用类模型。
deepseek的官方Github地址:https://github.com/deepseek-ai
模型下载页面:https://huggingface.co/Qwen/Qwen2.5-7B-Instruct/tree/main

1.2 并发交互和web前端
(1)并发交互使用Ollama,下载地址:https://ollama.com/
双击运行下载的安装包,按照提示完成安装。安装完成后,Ollama 会自动在后台运行。你可以在系统托盘中看到一个羊驼图标。
一般会自动将ollama文件夹添加进系统变量,没有的话手动添加,还需要重开命令行窗口验证
ollama --version
Ollama 默认安装在你的用户目录下。打开资源管理器,在地址栏输入 %LOCALAPPDATA%\Programs\Ollama 并回车,里面应该有一个 ollama.exe 文件。 如果没找到,也可以在 C:\Program Files\Ollama 目录下找找看。
将路径添加到 PATH 环境变量:
①右键点击“此电脑”(或“我的电脑”),选择“属性”。
②点击“高级系统设置”。
③在弹出的窗口中,点击“环境变量”。
④ 在“系统变量”列表中,找到并选中 Path 变量,然后点击“编辑”。
⑤点击“新建”,然后粘贴你刚刚找到的 Ollama 文件夹路径(例如 C:\Users\你的用户名\AppData\Local\Programs\Ollama)。
⑥点击“确定”保存所有窗口。
(2)web前端使用open-webui
open-webui 仅支持python3.11及以上,否则安装会报错,因此conda创建虚拟环境时需要强制指定python版本,尤其版本号要加上小版本指定。
conda create -n open-webui-env python=3.11.9
虚拟环境中安装open-webui
pip install open-webui -i https://pypi.tuna.tsinghua.edu.cn/simple
1.3 依赖环境
cuda、cudnn、pytorch、显卡驱动、pycharm、Anaconda的安装参考以下文章:
(1)总流程
https://blog.csdn.net/m0_55127902/article/details/135654771
https://blog.csdn.net/qq_48176053/article/details/147335005
(2)cuda
https://developer.nvidia.com/cuda-90-download-archive?target_os=Windows&target_arch=x86_64&target_version=10&target_type=exenetwork
(3)cudnn
https://developer.nvidia.com/rdp/cudnn-archive
(4)pytorch
https://pytorch.org/get-started/previous-versions/
(5)conda常用命令
https://blog.csdn.net/2303_78957394/article/details/146009614
https://comate.baidu.com/zh/page/lre5hkldtwh
推荐将完全配置好的conda虚拟环境完全打包,以后再部署直接解压然后激活就行。
2.deepseek模型的本地部署和运行
编译器里运行模型主要是为了测试模型文件是否完整,或者重新训练的模型文件能否正常运行。实际在局域网中交互时由Ollama自动加载指定的本地模型文件或文件夹。
2.1 运行前准备工作确认
(1)依赖库确认
pip install transformers accelerate bitsandbytes
(2)确保 model_path 指向正确的本地模型文件夹(里面应有 config.json、tokenizer.json 以及权重文件等)。
(3)如果你的显卡显存比较紧张(8GB 运行 7B 模型已经比较极限),可以适当降低 max_new_tokens 的值,比如改为 256,以节省显存。
对话历史管理:使用 messages 列表存储所有对话轮次,每个元素是一个字典,包含 role(user 或 assistant)和 content。这样模型每次都能看到完整的上下文。
2.2 运行代码简介
(1)自动构建 Prompt:apply_chat_template 是 Hugging Face 分词器提供的标准方法,它会根据模型要求的格式(比如 Qwen 的 ChatML 格式)自动拼接历史消息,并添加必要的控制标记(如 <|im_start|>、<|im_end|>)。强烈推荐使用这种方式,比自己手动拼接更可靠。
(2)生成参数调整:
max_new_tokens=512:控制每次回复的最大长度,可根据需要调整。
temperature=0.7、top_p=0.9:控制回答的随机性和多样性。
repetition_penalty=1.05:轻微惩罚重复内容,让回答更自然。
eos_token_id 和 pad_token_id:确保生成在正确的结束标记处停止。
(3)循环退出条件:输入 exit(不区分大小写)即可结束程序。
2.3连续对话完整代码(Transformers + 4-bit 量化)
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch
# 模型路径(替换为你本地的模型文件夹路径)
model_path = "./Qwen2.5-7B-Instruct" # 例如下载到当前目录下的这个文件夹
# 1. 配置 4-bit 量化(如果你之前已量化保存,可以去掉这一部分,直接加载)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16
)
# 2. 加载分词器和模型
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_path,
quantization_config=bnb_config,
device_map="auto",
trust_remote_code=True
)
# 3. 初始化对话历史(使用 OpenAI 格式的 messages 列表)
messages = []
print("模型加载成功!开始连续对话(输入 'exit' 结束)\n")
# 4. 开始对话循环
while True:
# 获取用户输入
user_input = input("用户: ")
if user_input.lower() == "exit":
print("对话结束。")
break
# 将用户输入添加到历史消息
messages.append({"role": "user", "content": user_input})
# 使用分词器的 apply_chat_template 方法构建带历史的 prompt
# 这个方法会自动处理角色标签和结束符,确保格式正确
prompt = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True # 添加助手的生成提示符
)
# 编码并移至 GPU
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
# 生成回复
outputs = model.generate(
**inputs,
max_new_tokens=512,
do_sample=True,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.05,
eos_token_id=tokenizer.eos_token_id,
pad_token_id=tokenizer.pad_token_id or tokenizer.eos_token_id
)
# 解码回复(去掉输入部分)
response = tokenizer.decode(
outputs[0][inputs.input_ids.shape[1]:],
skip_special_tokens=True
)
# 将助手回复添加到历史消息
messages.append({"role": "assistant", "content": response})
# 打印助手回复
print(f"助手: {response}\n")
3.deepseek模型的训练
3.1 训练数据结构示例
由于需要多轮对话,所以采用ShareGPT格式,它是一个JSON文件,列表中的每个元素代表一段完整的对话历史。
核心字段:conversations 是一个列表,包含多轮对话。
角色标识:“from”: “human” 表示用户,“from”: “gpt” 表示模型。
系统提示:“system” 字段是可选的,用于设定本次对话的系统角色
在 ShareGPT 格式中,from 字段的值并没有硬性规定必须为 “human” 或 “gpt”。它的作用只是标识说话的角色,方便你在预处理时将其映射为模型所识别的角色名称。只要在训练代码的数据预处理函数中,将这些自定义的角色名称统一转换为模型期望的 role 字段,即 “user” 或 “assistant”:
if turn['from'] == '顾客':
role = "user"
else:
role = "assistant"
[
{
"instruction": "你是谁?",
"input": "",
"output": "我是一个由CSDN迪菲赫尔曼开发和维护的大语言模型。"
},
{
"instruction": "请解释一下什么是机器学习",
"input": "",
"output": "机器学习是一门人工智能科学,它让计算机能够通过数据学习,而不是被明确编程..."[
{
"conversations": [
{
"from": "human",
"value": "你好,请介绍一下你自己。"
},
{
"from": "gpt",
"value": "你好!我是一个由CSDN迪菲赫尔曼开发和维护的AI助手,很高兴为你服务!"
},
{
"from": "human",
"value": "你能做什么?"
},
{
"from": "gpt",
"value": "我可以帮你解答问题、提供建议、辅助学习等。"
}
],
"system": "你是一个乐于助人的助手。"
},
{
"conversations": [
{
"from": "human",
"value": "如何学习Python?"
},
{
"from": "gpt",
"value": "学习Python可以从基础语法开始..."
}
]
}
]
},
{
"instruction": "翻译下面的话成英文",
"input": "今天天气真好。",
"output": "The weather is nice today."
}
]
3.2 训练代码
(1)运行前确认依赖库已安装
pip install torch transformers accelerate peft bitsandbytes datasets trl scipy
transformers若报错建议检查版本,在 transformers 4.x 到 5.x 的大版本升级中,API 发生了重大变化,可降级至4.46.3
pip uninstall transformers -y
pip install transformers==4.46.3
(2)训练注意事项
(3)完整的微调训练代码如下
①针对8GB显存的优化:代码中已经设置了 per_device_train_batch_size=1、gradient_accumulation_steps=8 和 max_length=1024。如果你的显存还是不够,可以进一步降低 max_length 或 gradient_accumulation_steps。
②数据量:不要指望用十几条数据就让模型产生质变。针对特定任务,准备几百到上千条高质量样本是基础。如果是改变模型的“自我认知”,几十条精心设计的样本也能生效。
③过拟合:如果你的数据集很小(如几百条),可以适当降低学习率(如 1e-4)和训练轮次(如 epochs=3),并观察loss值。
④保存的产物:训练完成后,output_dir 里保存的是 LoRA适配器权重,它非常小(几十MB)。要使用微调后的模型,你需要:
*推理时加载:使用 PeftModel.from_pretrained(base_model, adapter_path) 来合并并推理。
*永久合并:使用脚本将LoRA权重合并回原模型,生成一个新的完整模型。
⑤训练参数
num_train_epochs,训练轮数。如果数据集有 1000 条样本,per_device_train_batch_size=1,gradient_accumulation_steps=8,那么每个 epoch 的实际更新步数为:
步数 = ceil(样本数 / (per_device_train_batch_size * gradient_accumulation_steps))
即 1000 / (1×8) = 125 步。训练3个 epoch 总共 125*3 = 375 步
小数据集(几百条):通常 3-5 个 epoch 足够
中等数据集(几千条):1-3 个 epoch 常见
大数据集(几万条以上):可能只需 1 个 epoch
max_steps,最大更新步数(优先级更高)。如果同时设置了 max_steps,它会覆盖 num_train_epochs,训练将在达到指定步数后停止,无论是否完成所有 epoch。常用于调试、控制总训练时间
training_args = TrainingArguments(
num_train_epochs=3,
max_steps=500, # 最多训练500步
...
)
此时,即使 3 个 epoch 需要 375 步,但 max_steps=500 更大,所以实际会跑完 500 步(可能超过 3 个 epoch);反之,如果 max_steps=200,则训练 200 步后停止,不足 3 个 epoch。
per_device_train_batch_size:单卡批大小
gradient_accumulation_steps:梯度累积步数
数据集大小:样本总数越多,每个 epoch 的步数越多
完整的训练代码如下:
import torch
from transformers import (
AutoTokenizer,
AutoModelForCausalLM,
BitsAndBytesConfig,
TrainingArguments,
Trainer,
DataCollatorForLanguageModeling
)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from datasets import load_dataset
import os
# ==================== 配置区域 ====================
model_path = "./Qwen2.5-7B-Instruct" # 你的模型下载路径
data_file = "./my_chat_data.json" # 你的训练数据文件路径
output_dir = "./qwen_lora_output" # 训练输出目录
# 量化配置 (4-bit QLoRA - 显存优化核心)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
# LoRA配置
lora_config = LoraConfig(
r=8, # LoRA秩,8-16之间,8更省显存
lora_alpha=16, # 缩放参数,通常是r的两倍
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # 常见模块
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
# 训练参数配置
training_args = TrainingArguments(
output_dir=output_dir,
per_device_train_batch_size=1, # 8GB显存,batch_size必须为1
gradient_accumulation_steps=8, # 梯度累积,模拟batch_size=8
num_train_epochs=3, # 训练轮次,数据量少可以增加
learning_rate=2e-4, # LoRA通常用稍高学习率
fp16=True, # 使用混合精度
logging_steps=10,
save_strategy="epoch",
save_total_limit=2,
remove_unused_columns=False,
report_to="none"
)
# ==================== 加载模型和分词器 ====================
print("正在加载分词器...")
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
# 设置padding token,因为Qwen的tokenizer默认没有
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
print("正在加载模型(4-bit量化)...")
model = AutoModelForCausalLM.from_pretrained(
model_path,
quantization_config=bnb_config,
device_map="auto",
trust_remote_code=True
)
# 为k-bit训练准备模型
model = prepare_model_for_kbit_training(model)
# 应用LoRA
model = get_peft_model(model, lora_config)
model.print_trainable_parameters() # 打印可训练参数量
# ==================== 加载和预处理数据 ====================
print("正在加载数据集...")
# 以JSON格式加载数据,这里假设使用我们上面提到的ShareGPT格式
dataset = load_dataset('json', data_files=data_file, split='train')
def format_sharegp_toconversations(example):
"""将ShareGPT格式的数据处理成模型能理解的文本格式"""
# 使用apply_chat_template是标准做法
messages = example['conversations']
# 将列表中的每条消息转换为 {"role": "user"/"assistant", "content": "xxx"} 格式
formatted_messages = []
for turn in messages:
role = "user" if turn['from'] == 'human' else "assistant"
formatted_messages.append({"role": role, "content": turn['value']})
# 使用tokenizer的聊天模板将消息列表转换成模型输入的文本
text = tokenizer.apply_chat_template(
formatted_messages,
tokenize=False,
add_generation_prompt=False # 训练时不加生成提示
)
return {"text": text}
# 如果你的数据是Alpaca格式,可以用这个函数
def format_alpaca_to_text(example):
if example.get("input", ""):
text = f"Instruction: {example['instruction']}\nInput: {example['input']}\nResponse: {example['output']}"
else:
text = f"Instruction: {example['instruction']}\nResponse: {example['output']}"
return {"text": text}
# 根据你的数据格式,取消注释下面一行
dataset = dataset.map(format_sharegp_toconversations)
# dataset = dataset.map(format_alpaca_to_text)
def tokenize_function(examples):
return tokenizer(
examples["text"],
truncation=True,
max_length=1024, # 根据显存调整,显存小就调低一点
padding=False,
return_tensors=None
)
print("正在Tokenize数据集...")
tokenized_dataset = dataset.map(tokenize_function, batched=True, remove_columns=dataset.column_names)
# 数据整理器,用于动态padding
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
# ==================== 开始训练 ====================
print("开始训练...")
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
data_collator=data_collator,
tokenizer=tokenizer,
)
trainer.train()
# ==================== 保存模型 ====================
print("训练完成,保存模型...")
# 保存LoRA权重(只有几十MB)
model.save_pretrained(output_dir)
tokenizer.save_pretrained(output_dir)
print(f"模型已保存至 {output_dir}")
3.3 微调训练后模型的使用
(1)训练输出并不是模型
输出目录(例如 ./qwen_lora_output)中,应该有以下文件:
①adapter_config.json:LoRA 的配置文件。
②adapter_model.bin(或 .safetensors):训练好的 LoRA 权重。
③(可能还有 tokenizer 相关文件,如果保存了的话)
需要将 LoRA 适配器加载到原始的基础模型上才能使用。
(2)方法一:直接使用训练后的模型
这是最灵活的方式,推理时动态合并 LoRA 权重,不改变原始模型文件。
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import PeftModel
import torch
# -------------------- 配置路径 --------------------
base_model_path = "./Qwen2.5-7B-Instruct" # 基础模型路径
adapter_path = "./qwen_lora_output" # 训练输出的 LoRA 适配器路径
# -------------------- 加载基础模型(4-bit量化,适配8GB显存) --------------------
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16
)
print("正在加载基础模型...")
base_model = AutoModelForCausalLM.from_pretrained(
base_model_path,
quantization_config=bnb_config, # 如果训练时未用量化,可以去掉此行
device_map="auto",
trust_remote_code=True
)
# -------------------- 加载分词器 --------------------
tokenizer = AutoTokenizer.from_pretrained(base_model_path, trust_remote_code=True)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# -------------------- 加载 LoRA 适配器 --------------------
print("正在加载 LoRA 适配器...")
model = PeftModel.from_pretrained(base_model, adapter_path)
# -------------------- 多轮对话 --------------------
messages = [] # 存储完整对话历史
print("\n模型加载成功!开始连续对话(输入 'exit' 结束)\n")
while True:
# 获取用户输入
user_input = input("用户: ")
if user_input.lower() == "exit":
print("对话结束。")
break
# 将用户输入加入历史
messages.append({"role": "user", "content": user_input})
# 使用聊天模板构建 prompt
prompt = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
# 编码并移至 GPU
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
# 生成回复
outputs = model.generate(
**inputs,
max_new_tokens=512,
do_sample=True,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.05,
eos_token_id=tokenizer.eos_token_id,
pad_token_id=tokenizer.pad_token_id
)
# 解码(去掉输入部分)
response = tokenizer.decode(
outputs[0][inputs.input_ids.shape[1]:],
skip_special_tokens=True
)
# 将助手回复加入历史
messages.append({"role": "assistant", "content": response})
# 打印回复
print(f"助手: {response}\n")
(3)方法二:合并原始模型和训练后的模型
合并模型会出现显存不足的错误,因此在加载基础模型时指定 device_map={“”: “cpu”},确保所有参数都在 CPU 上,然后执行合并。CPU 内存通常足够容纳 15GB 的模型(如果内存不足,可以尝试加载为 torch_dtype=torch.float16 减少一半内存占用)。
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
import torch
base_model_path = "./Qwen2.5-7B-Instruct"
adapter_path = "./qwen_lora_output"
merged_save_path = "./qwen_lora_merged"
# 将基础模型加载到 CPU(避免 GPU 显存不足导致分散)
print("正在将基础模型加载到 CPU...")
base_model = AutoModelForCausalLM.from_pretrained(
base_model_path,
torch_dtype=torch.float16, # 可选:半精度减少内存占用
device_map={"": "cpu"}, # 关键:强制所有参数在 CPU
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained(base_model_path, trust_remote_code=True)
# 加载 LoRA 适配器(适配器会自动加载到与基础模型相同的设备,即 CPU)
print("正在加载 LoRA 适配器...")
model = PeftModel.from_pretrained(base_model, adapter_path)
# 合并权重并卸载 LoRA 结构(在 CPU 上执行)
print("正在合并 LoRA 权重...")
merged_model = model.merge_and_unload()
# 保存合并后的完整模型
print(f"保存合并后的模型至 {merged_save_path}")
merged_model.save_pretrained(merged_save_path)
tokenizer.save_pretrained(merged_save_path)
print("完成!")
3.4 自动将连续文段转换成训练数据
import json
def segments_to_sharegpt(segments, start_with="human", max_turns=None):
"""
将文本段列表转换为 ShareGPT 格式的对话列表。
Args:
segments (list of str): 文本段列表。
start_with (str): 第一个消息的角色,"human" 或 "gpt"。
max_turns (int, optional): 每个对话的最大轮次(消息条数)。超过时切分为新对话。
Returns:
list: ShareGPT 格式的对话列表。
"""
conversations = []
current_conv = []
roles = ["human", "gpt"]
# 确定角色循环顺序
if start_with == "gpt":
roles = ["gpt", "human"]
for i, text in enumerate(segments):
role = roles[i % 2] # 交替分配角色
current_conv.append({"from": role, "value": text.strip()})
# 如果指定了 max_turns 且当前对话已达到最大轮次,或者这是最后一段,则保存当前对话
if (max_turns and len(current_conv) >= max_turns) or i == len(segments) - 1:
# 只有至少包含一轮完整对话(human+gpt)才保存,但也可以根据需要调整
if len(current_conv) >= 1:
conversations.append({"conversations": current_conv})
current_conv = []
# 重置角色循环(让新对话从头开始)
# 如果不重置,下一段的角色会接着上一段的最后一段,但通常新对话应该重新开始交替
# 所以这里重置角色索引
i_offset = i + 1
# 但为了简单,我们让每个新对话都从 start_with 开始,重新计算 i % 2 会依赖全局索引,不好
# 所以更好的做法是每次新对话重置角色计数器,但为了简单,这里重新赋值角色列表
# 由于我们不知道下一段是新对话的开始,所以这里直接重置计数器,新对话从 start_with 开始
# 但为了代码简洁,我们可以在外层循环中用局部变量控制
return conversations
# 读取文段(这里假设文件每行一个段,你可以根据实际格式调整)
def read_segments(file_path, delimiter=None):
with open(file_path, 'r', encoding='utf-8') as f:
if delimiter:
content = f.read()
segments = content.split(delimiter)
else:
segments = [line.strip() for line in f if line.strip()]
return segments
# 示例用法
if __name__ == "__main__":
# 1. 从文件读取文段
segments = read_segments("segments.txt") # 每行一个段
# 或者手动定义文段列表(用于演示)
# segments = [
# "你好,请问今天天气怎么样?",
# "今天天气晴朗,气温15-25℃。",
# "明天呢?",
# "明天多云,可能有小雨。",
# "谢谢!",
# "不客气,祝您愉快!"
# ]
# 2. 转换为 ShareGPT 格式(每个对话最多包含4轮消息,即2个回合)
conversations = segments_to_sharegpt(segments, start_with="human", max_turns=4)
# 3. 保存为 JSON 文件
with open("sharegpt_data.json", "w", encoding="utf-8") as f:
json.dump(conversations, f, ensure_ascii=False, indent=2)
print(f"成功生成 {len(conversations)} 个对话,已保存到 sharegpt_data.json")
上述代码输出结果示例:
[
{
"conversations": [
{"from": "human", "value": "你好,请问今天天气怎么样?"},
{"from": "gpt", "value": "今天天气晴朗,气温15-25℃。"},
{"from": "human", "value": "明天呢?"},
{"from": "gpt", "value": "明天多云,可能有小雨。"}
]
},
{
"conversations": [
{"from": "human", "value": "谢谢!"},
{"from": "gpt", "value": "不客气,祝您愉快!"}
]
}
]
(1)角色必须正确交替:大多数模型期望对话以 “human” 开头,然后是 “gpt”,再 “human”… 不要连续出现两个相同角色。
(2)每条消息内容建议去除多余空白,保留原始文本即可。
(3)JSON 文件格式:确保最终文件是合法的 JSON(用 json.dump 会自动处理)。
(4)数据量少时:即使只有十几段,生成几个对话也是可以的,微调时依然有效。
4.Ollama 并发交互工具部署
可以直接在命令行输入例如“ollama run qwen2.5”,第一次运行时会自动下载对应的模型。关键是要让Ollama运行本地已经存在的模型文件。
4.1 链接模型
不管是原生模型(文件夹形式)还是gguf模型,ollama都可以提取。需要在模型或模型文件夹同级目录下创建没有后缀名的Modelfile文件,用文本编辑:
FROM 你的模型绝对路径/Qwen2.5-Math-7B-Instruct-Q6_K.gguf
或者
FROM 你的模型绝对路径/qwen2.5-7b-instruct.gguf
4.2 提取并转换模型
(2)打开终端 (CMD 或 PowerShell),输入以下命令
ollama create XXX-model -f ./Modelfile
需要等待很久
用ollama list命令确认导入成功:
4.3 运行模型测试(可选)
其实不需要在命令行里运行模型,对话时自动开启模型占用显存。
命令行输入:
ollama run XXX-model
4.4 配置ollama环境变量(重要)
配置并发量和上下文长度
命令行输入:
set OLLAMA_NUM_PARALLEL=3
set OLLAMA_CONTEXT_LENGTH=8192
需要重启 Ollama 服务才能生效,这一点很重要,否则对话时会报错。
重启要先退出ollama程序,然后在在命令行输入:
ollama serve
如果报错,进程被占用,用命令关闭
netstat -ano | findstr :11434

继续输入命令终止查询到的占用线程
taskkill /PID 20152 /F
5.open-webui 网页交互前端部署
5.1 启动服务
安装完成后在conda虚拟环境中启动open-webui服务,启动服务前需要将电脑改成固定IP,否则会报错。
open-webui serve
出现下图结果说明启动成功
5.2 注册管理员账号
(1)在浏览器中打开 http://localhost:8080,你会看到一个注册页面。在这里创建的第一个账户将自动成为管理员,拥有管理用户、连接模型等所有权限。
(2)填写用户名、邮箱和密码(邮箱可以随便填,只要格式正确即可,比如 admin@local.com)。
(3)点击注册,登录后就会进入主界面。
5.3 连接 Ollama 模型
(1)登录后,点击左下角的用户头像,选择 “管理员面板” (Admin Panel)。
(2)点击最上面的“设置”,跳出来的窗口中找到 “外部连接” (Connections) -> “Ollama”。一般会自动配置好。

(3)若没有配置好,则在 “Ollama 基础 URL” 一栏中,填入你的 Ollama 服务地址。由于 Open WebUI 和 Ollama 在同一台电脑上,填入 http://localhost:11434 即可。
(4)点击 “保存” (Save)。如果连接成功,点击窗口的“模型”会出现你自己的模型。
6 开始使用
6.1 允许普通用户创建账号
在管理员面板中开启:允许新用户注册,以及开启默认新用户为“用户”。
6.2 管理员将某个模型公开或对某个用户组公开
在管理员面板中选择“设置”→“模型”→某个具体模型后面“…”→选择“Make Public”。
6.3 局域网中普通用户浏览器中输入主机地址
(1)我的主机地址是:http://192.168.1.3:8080,普通用户进入注册页面,邮箱随便输,等待管理员激活账号。
(2)在管理员面板中激活普通用户的账号
(3)用户在对话框中选择模型进行对话
在对话框右下角的模型选择器中,选择你想要对话的模型,开始对话。
(管理员需要点击左上角“Open WebUI”图标回到主界面)

若报错如下,记得回去设置ollama的长下文长度环境变量。
成功对话如下图所示:
更多推荐



所有评论(0)