Ollama 本地部署 Deepseek R1 模型

Ollama项目介绍

Ollama是在Github上的一个开源项目,其项目定位是:一个本地运行大模型的集成框架,目前主要针对主流的LLaMA架构的开源大模型设计,通过将模型权重、配置文件和必要数据封装进由Modelfile定义的包中,从而实现大模型的下载、启动和本地运行的自动化部署及推理流程。此外,Ollama内置了一系列针对大模型运行和推理的优化策略,目前作为一个非常热门的大模型托管平台,基本主流的大模型应用开发框架如LangChainAutoGenMicrosoft GraphRAG及热门项目AnythingLLMOpenWebUI等高度集成。

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启动

如果想加载多张显卡且做到负载均衡,可以去修改 ollamaSystemD配置服务,首先找到当前服务器上GPUID,执行命令如下:

nvidia-smi

如果想加载多张显卡且做到负载均衡,可以去修改 ollamaSystemD配置服务,执行如下代码:

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 CompatibilityOpenAIAPI 接口是大模型应用开发中最常用、且集成度最高的 API 接口规范,其兼容接口主要包括:

  • chat/completions
  • completions
  • models
  • embeddings

我们上两节课程内容中介绍的/api/generate/api/chat 接口,其实就是 Ollama 兼容 OpenAIREST API 接口的底层实现。其中:

  • /api/generate 接口对应 OpenAIcompletions 接口;
  • /api/chat 接口对应 OpenAIchat/completions 接口;

因此我们现在再来看ollama 中的OpenAI compatibilityAPI 接口调用,就非常容易理解了。

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 模型服务的吞吐量和并发量,需要核心关注的是以下几点:

  1. 使用REST API接口进行测试,可以尝试使用/api/generate 或者/api/chat,真实模拟用户在实际使用中的请求模式,帮助评估系统在真实场景下的表现。
  2. Ollama 原生的REST API接口支持多个控制Ollama行为的参数,可以更灵活的控制测试流程,其中:
    1. num_predict 参数来控制生成的token数量
    2. keep_alive 设置为0,使用完模型后立即卸载
    3. temperature 参数来控制生成文本的多样性,很多情况下,希望生成的文本尽可能保持一致,会将其设置为0,
  1. 根据OllamaREST API接口返回响应体中的eval_counteval_duration 来计算每秒生成的Token 数量,即吞吐量,而不是用 resquest 发起和接收到响应的时间差值来计算,将模型服务和网络延迟解耦,更准确的评估模型服务的吞吐量。
&emsp;&emsp;单次调用的伪代码如下:

```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

-

-

从测试结果能够得出的一些关键结论是:

  1. 并发会导致单个请求的处理时间变长;
  2. 并发因为是并行处理,虽然单个请求时间变长,但是系统整体吞吐量会得到提升;
  3. 不一定用更多的卡就可以获得更高的吞吐量,需要根据实际情况去调整。

因此,大家在实际测试的时候,要尝试在不同的硬件配置和并发级别下进行测试,以找到最佳的性能平衡点。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐