Ollama

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/completions
  • completions
  • models
  • embeddings

我们上两节课程内容中介绍的/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 模型服务的吞吐量和并发量,需要核心关注的是以下几点:

  1. 使用REST API接口进行测试,可以尝试使用/api/generate 或者/api/chat,真实模拟用户在实际使用中的请求模式,帮助评估系统在真实场景下的表现。
  2. Ollama 原生的REST API接口支持多个控制Ollama行为的参数,可以更灵活的控制测试流程,其中:
    1. num_predict 参数来控制生成的token数量
    2. keep_alive 设置为0,使用完模型后立即卸载
    3. temperature 参数来控制生成文本的多样性,很多情况下,希望生成的文本尽可能保持一致,会将其设置为0,
  1. 根据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 | - | - |

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

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

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

相关推荐
2601_9689007710 分钟前
Agent沙盒基础设施拆解:三层镜像与按需加载的工程取舍
人工智能
楚楚25118 分钟前
2026实测:智能体办公平台三周上手真实体验
人工智能
智圣新创0127 分钟前
教育数据要素流通刚需下 智圣新创高校数据中台解决方案的全域建设落地框架
大数据·人工智能·物联网
xianghongtao011631 分钟前
如何选择一台适合你的本地 AI 硬件设备?Lucy AI Studio——从本地 AI 办公到家庭影音的完整指南
人工智能·端脑科技·本地ai硬件设备·kickstarter众筹项目·lucy ai studio·lucy aios
ZhangJun9538 分钟前
Mobike 共享单车分析项目
人工智能·python·算法·kmeans·聚类·knn
互联网资讯41 分钟前
本地创业者入局 AI 短剧,本地化部署怎么选更省钱
大数据·人工智能
唐维康43 分钟前
昆工计算机408考研2027招285人,比去年少6人
人工智能·考研·昆明理工大学
Martina_03211 小时前
AI生成的过山车轨道导入后频繁脱轨?用5步检查样条、轨距与碰撞
图像处理·人工智能·游戏·3d·材质·游戏策划·关卡设计
小猴子爱上树1 小时前
跨马翻译:批量图片翻译+视频字幕+智能抠图,跨境电商在线图片翻译工具
大数据·人工智能·python·音视频
佳瑞Jarrett1 小时前
从 0 到 1 手搓 AI 调解训练系统:双 NPC 实时语音 + AI 自动评分(FastAPI + Qwen-Audio Realtime 实战)
人工智能