Ollama 本地部署 Deepseek R1 模型
Ollama项目介绍
Ollama是在Github上的一个开源项目,其项目定位是:一个本地运行 大模型 的集成框架 ,目前主要针对主流的LLaMA架构的开源大模型设计,通过将模型权重、配置文件和必要数据封装进由Modelfile定义的包中,从而实现大模型的下载、启动和本地运行的自动化部署及推理流程。此外,Ollama内置了一系列针对大模型运行和推理的优化策略,目前作为一个非常热门的大模型托管平台,基本主流的大模型应用开发框架如LangChain、AutoGen、Microsoft GraphRAG及热门项目AnythingLLM、OpenWebUI等高度集成。
Ollama通过将大模型运行的所有必要组件(如权重文件、配置设置和相关数据)封装在一个单一的文件或包中,`Modelfile 允许用户更容易地下载、安装、配置和启动模型。这种方法类似于其他软件或应用程序的安装包,它们将所有必要的文件打包在一起,以便用户可以通过简单的安装过程将软件添加到他们的系统中。
Ollama官方地址:https://ollama.com/
Ollama Github开源地址:https://github.com/ollama/ollama
Ollama项目本地安装
Ollama项目本地安装的方法极为简单,这里我们以Linux系统为例,先进入命令行终端,执行如下一条命令行即可自动化完成:
curl -fsSL https://ollama.com/install.sh | sh
Ollama下载 DeepSeek R1 及启动
需要说明的一点是:Ollama项目虽然提供了本地化大模型的能力,但这并不意味着所有大模型都可以通过它下载和使用,其支持的大模型的详细列表可在Ollama的官方模型库页面查看:https://ollama.com/library。
ollama run deepseek-r1:32b
Ollama 多GPU部署及serve启动
如果想加载多张显卡且做到负载均衡,可以去修改 ollama 的SystemD配置服务,首先找到当前服务器上GPU的 ID,执行命令如下:
nvidia-smi
如果想加载多张显卡且做到负载均衡,可以去修改 ollama 的SystemD配置服务,执行如下代码:
systemctl edit ollama.service
编辑并填写如下内容:
Environment="CUDA_VISIBLE_DEVICES=0,1,2,3" # 这里根据你自己实际的 GPU标号来进行修改
Environment="OLLAMA_SCHED_SPREAD=1" # 这个参数是做负载均衡
保存退出后,重新加载systemd并重新启动Ollama服务使其配置生效,执行如下命令:
systemctl daemon-reload
systemctl restart ollama
Ollama REST API 服务启动及调用
Ollama run xxx命令启动模型后,不仅仅是可以在命令行终端与启动的大模型进行对话,更重要的是它还会同步启动Ollama REST API,这个REST API服务简单理解:我们可以通过某种方式在代码环境中调用到使用Ollama模型启动的大模型,从而和大模型进行对话。默认绑定的 IP + Port 是:http://localhost:11434,所以,如果启动Ollama的服务和当前的代码环境是同一台机器的话,可以使用如下代码进行快速的调用测试:
from openai import OpenAI
client = OpenAI(
base_url='http://localhost:11434/v1/',
api_key='ollama', # 这里随便写,但是api_key字段一定要有
)
chat_completion = client.chat.completions.create(
model='deepseek-r1:32b', # 这里要修改成 你 ollama 启动模型的名称
messages=[
{
'role': 'user',
'content': '你好,请你介绍一下你自己',
}
],
)
print(chat_completion)
Ollama每个命令参数非常容易理解,大家可以自行进行尝试,其参数说明如下所示:
|--------|--------------------|
| 命令 | 描述 |
| serve | 启动 Ollama 服务 |
| create | 从 Modelfile 创建一个模型 |
| show | 显示模型的信息 |
| run | 运行一个模型 |
| stop | 停止正在运行的模型 |
| pull | 从注册表中拉取一个模型 |
| push | 将一个模型推送到注册表 |
| list | 列出所有模型 |
| ps | 列出正在运行的模型 |
| cp | 复制一个模型 |
| rm | 删除一个模型 |
| help | 显示关于任何命令的帮助信息 |
Ollama REST API
Ollama 服务启动后会提供一系列原生 REST API 端点。通过这些Endpoints可以在代码环境下与ollama启动的大模型进行交互、管理模型和获取相关信息。其中两个endpoint 是最重要的,分别是:
- POST /api/generate
- POST /api/chat
其他端点情况:
- POST /api/create
- POST /api/tags
- POST /api/show
- POST /api/copy
- DELETE /api/delete
- POST /api/pull
- POST /api/push
- POST /api/embed
- GET /api/ps
/api/generate 接口参数概览
常规参数
|------------|--------|--------------------------------------------|
| 参数名 | 类型 | 描述 |
| model | (必需) | 模型名称,必须遵循 model:tag 格式,如果不提供,则将默认为 latest。 |
| prompt | (必需) | 用于生成响应的提示。 |
| suffix | (可选) | 模型响应后的文本。 |
| images | (可选) | base64 编码图像的列表(适用于多模态模型,如 llava)。 |
高级参数 (可选)
|----------------|--------|----------------------------------------------|
| 参数名 | 类型 | 描述 |
| format | (可选) | 返回响应的格式。格式可以是 json 或 JSON 模式。最主要的问题是避免产生大量空格 |
| options | (可选) | 文档中列出的其他模型参数,例如 temperature。 |
| system | (可选) | 系统消息,用于覆盖 Modelfile 中定义的内容。 |
| template | (可选) | 要使用的提示模板,覆盖 Modelfile 中定义的内容。 |
| stream | (可选) | 如果为 false,响应将作为单个响应对象返回,而不是对象流。 |
| raw | (可选) | 如果为 true,则不会对提示应用格式。 |
| keep_alive | (可选) | 控制模型在请求后保持加载的时间(默认:5分钟)。 |
| context | (可选) | (已弃用) 从先前请求返回的上下文参数,用于保持简短的对话记忆。 |
import requests # type: ignore
import json
# 设置 API 端点
generate_url = "http://192.168.110.131:11434/api/generate" # 这里需要根据实际情况进行修改
# 示例数据
generate_payload = {
"model": "deepseek-r1:7b", # 这里需要根据实际情况进行修改
"prompt": "请生成一个关于人工智能的简短介绍。", # 这里需要根据实际情况进行修改
"stream": False, # 默认使用的是True,如果设置为False,则返回的是一个完整的响应,而不是一个流式响应
}
# 调用生成接口
response_generate = requests.post(generate_url, json=generate_payload)
if response_generate.status_code == 200:
generate_response = response_generate.json()
print("生成响应:", json.dumps(generate_response, ensure_ascii=False, indent=2))
else:
print("生成请求失败:", response_generate.status_code, response_generate.text)
api/chat 接口详解
该接口使用提供的模型在聊天中生成下一条消息。与 /api/generate 的参数基本一致,但是在请求的参数上会根据聊天场景进行调整。主要调整的是:
- 不再使用
prompt参数,而是使用messages参数。 - 新增了
tools参数,用于支持工具调用。
常规参数
|--------------|--------|--------------------------|
| 参数名 | 类型 | 描述 |
| model | (必需) | 模型名称。 |
| messages | (必需) | 聊天的消息,用于保持聊天记忆。 |
| tools | (可选) | JSON 中的工具列表,供模型使用(如果支持)。 |
消息对象字段
|----------------|-----------------------------------------|
| 字段名 | 描述 |
| role | 消息的角色,可以是 system、user、assistant 或 tool。 |
| content | 消息的内容。 |
| images | (可选) 要在消息中包含的图像列表(适用于多模态模型,如 llava)。 |
| tool_calls | (可选) 模型希望使用的 JSON 中的工具列表。 |
高级参数 (可选)
|----------------|---------------------------------|
| 参数名 | 描述 |
| format | 返回响应的格式。格式可以是 json 或 JSON 模式。 |
| options | 文档中列出的其他模型参数,例如 temperature。 |
| stream | 如果为 false,响应将作为单个响应对象返回,而不是对象流。 |
| keep_alive | 控制模型在请求后保持加载的时间(默认:5分钟)。 |
# 提取 <think> 标签中的内容
think_start = generate_response["response"].find("<think>")
think_end = generate_response["response"].find("</think>")
if think_start != -1 and think_end != -1:
think_content = generate_response["response"][think_start + len("<think>"):think_end].strip()
else:
think_content = "No think content found."
# 提取正常的文本内容
normal_content = generate_response["response"][think_end + len("</think>"):].strip()
# 打印结果
print("思考内容:\n", think_content)
print("\n正常内容:\n", normal_content)
OpenAI Compatibility
本节内容我们来看一下 OpenAI Compatibility。 OpenAI 的 API 接口是大模型应用开发中最常用、且集成度最高的 API 接口规范,其兼容接口主要包括:
chat/completionscompletionsmodelsembeddings
我们上两节课程内容中介绍的/api/generate 和 /api/chat 接口,其实就是 Ollama 兼容 OpenAI 的 REST API 接口的底层实现。其中:
/api/generate接口对应OpenAI的completions接口;/api/chat接口对应OpenAI的chat/completions接口;
因此我们现在再来看ollama 中的OpenAI compatibility 的 API 接口调用,就非常容易理解了。
import requests
import json
# 设置 API 端点
chat_url = "http://192.168.110.131:11434/api/chat" # 这里需要根据实际情况进行修改
# 示例数据
chat_payload = {
"model": "deepseek-r1:32b", # 这里需要根据实际情况进行修改
"messages": [
{
"role": "user", # 消息角色,用户发送的消息
"content": "请生成一个关于人工智能的简短介绍。" # 用户的消息内容
}
],
"tools": [], # 如果有工具可以在这里添加
"stream": False, # 默认使用的是True,如果设置为False,则返回的是一个完整的响应,而不是一个流式响应
}
# 调用聊天接口
response_chat = requests.post(chat_url, json=chat_payload)
if response_chat.status_code == 200:
chat_response = response_chat.json()
print("生成响应:", json.dumps(chat_response, ensure_ascii=False, indent=2))
else:
print("生成请求失败:", response_chat.status_code, response_chat.text)
在OpenAI Compatibility 规范下,目前Ollama 支持的模型参数如下:
支持的功能
|--------|----------------------|
| 功能 | 描述 |
| 聊天完成 | Chat completions |
| 流媒体 | Streaming |
| JSON模式 | JSON mode |
| 可再现的输出 | Reproducible outputs |
| 视觉 | Vision |
| 工具 | Tools |
支持的请求字段
|-------------------|----------|
| 请求字段 | 描述 |
| model | 模型 |
| messages | 消息 |
| frequency_penalty | 频率惩罚 |
| presence_penalty | 存在惩罚 |
| response_format | 响应格式 |
| seed | 种子 |
| stop | 停止 |
| stream | 流式输出 |
| stream_options | 流式选项 |
| include_usage | 包含使用情况 |
| temperature | 温度 |
| top_p | Top-p 采样 |
| max_tokens | 最大令牌数 |
| tools | 工具 |
Ollama 服务接口压力测试
对于企业级应用来说,尤其是后台服务,考虑的因素会非常多。比如大模型问答的响应速度,系统服务的稳定性,业务请求的错误率,资源的利用率等等多个方面。不同应用场景,考虑的因素也会有所不同。像我们正在做的智能客服问答功能,更关注响应速度和稳定性,这就导致高吞吐量和高并发能力比较重要,直接影响服务承载能力和效率,往往是优化的重点。吞吐量通常指系统在单位时间内处理的请求数量,而并发量则是系统同时处理的请求数。
因为企业需要处理大量用户或设备的请求,尤其是在高峰时段。如果服务吞吐量低,可能导致延迟增加,用户体验下降,甚至服务崩溃。如果并发量不足,用户可能会遇到等待或超时;吞吐量低的话,处理速度慢,整体效率低下。
我们基于Ollama 模型服务启动的 REST API接口,每秒生成的 Token 数量可以被视为系统的吞吐量,因此我们需要一些方法,来根据实际的业务需求来评估当前的硬件资源是否满足需求,或者应该如何去采购硬件资源。
我们测试 Ollama 模型服务的吞吐量和并发量,需要核心关注的是以下几点:
- 使用
REST API接口进行测试,可以尝试使用/api/generate或者/api/chat,真实模拟用户在实际使用中的请求模式,帮助评估系统在真实场景下的表现。 Ollama原生的REST API接口支持多个控制Ollama行为的参数,可以更灵活的控制测试流程,其中:
-
num_predict参数来控制生成的token数量keep_alive设置为0,使用完模型后立即卸载temperature参数来控制生成文本的多样性,很多情况下,希望生成的文本尽可能保持一致,会将其设置为0,
-
根据
Ollama的REST API接口返回响应体中的eval_count和eval_duration来计算每秒生成的Token数量,即吞吐量,而不是用resquest发起和接收到响应的时间差值来计算,将模型服务和网络延迟解耦,更准确的评估模型服务的吞吐量。单次调用的伪代码如下:
python# 调用 ollama 的 generate 接口 async with session.post( f"{self.url}/api/generate", json={ "model": self.model, "prompt": prompt, # 使用随机选择的问题 "stream": False, # "keep_alive":0, # 使用完模型后立即卸载 "options": { "temperature": 0.7, "num_predict": 300, # 限制生成token数量,以尽可能保证单个请求的生成时间一致 } } ) as response: result = await response.json() # 从响应中获取性能指标 eval_count = result.get("eval_count", 0) # 生成的token数 eval_duration = result.get("eval_duration", 0) # 生成时间(纳秒) total_duration = result.get("total_duration", 0) # 总时间(纳秒) # 计算 tokens/second tokens_per_second = (eval_count / eval_duration * 1e9) if eval_duration > 0 else 0
本地服务器上测试 DeepSeek-R1:1.5B 模型分别在 双卡负载和四卡负载下的吞吐量和并发量,测试结果如下:
并发测试结果
|-------------|---------|---------|-------------|----------------|---------------|----------------|---------------|----------------------|
| 测试类型 | 并发数 | 成功率 | 总token数 | 平均生成时间 (秒) | 平均总时间 (秒) | 平均每秒token数 | 实际总耗时 (秒) | 系统吞吐量 (tokens/s) |
| 两张卡 | 2 | 100% | 2742 | 3 | 4.03 | 91.96 | 23.84 | 115.01 |
| 两张卡 | 3 | 100% | 2686 | 3.61 | 3.71 | 73.56 | 14.78 | 181.75 |
| 两张卡 | 4 | 100% | 2665 | 4.48 | 4.59 | 59.75 | 14.16 | 188.19 |
| 两张卡 | 5 | 100% | 2556 | 4.8 | 4.91 | 53.34 | 12.39 | 206.27 |
| 单请求性能测试 | - | - | 300 | 2.44 | 2.52 | 123 | - | - |
| 四张卡 | 2 | 100% | 2526 | 2.64 | 4.16 | 96.77 | 25.38 | 99.51 |
| 四张卡 | 3 | 100% | 2456 | 3.34 | 3.44 | 72.44 | 14.51 | 169.28 |
| 四张卡 | 4 | 100% | 2781 | 4.54 | 4.65 | 62.27 | 14.59 | 190.57 |
| 四张卡 | 5 | 100% | 3000 | 6.53 | 6.65 | 45.93 | 14.42 | 208.07 |
| 单请求性能测试 | - | - | 300 | 2.52 | 2.59 | 119.31 | - | - |
从测试结果能够得出的一些关键结论是:
- 并发会导致单个请求的处理时间变长;
- 并发因为是并行处理,虽然单个请求时间变长,但是系统整体吞吐量会得到提升;
- 不一定用更多的卡就可以获得更高的吞吐量,需要根据实际情况去调整。
因此,大家在实际测试的时候,要尝试在不同的硬件配置和并发级别下进行测试,以找到最佳的性能平衡点。