「模型部署系列」ubuntu 使用vllm部署MiniCPM-o-2_6多模态模型

一、前置准备工作

1、首先是下载vllm的镜像文件,这不用赘述。

下载完后使用docker image查看镜像的名称备用。

2、下载MiniCPM-o-2_6模型文件。

方法1、SDK下载

#模型下载 from modelscope import snapshot_download model_dir = snapshot_download('OpenBMB/MiniCPM-o-2_6') 

方法2、Git下载

请确保 lfs 已经被正确安装 否则无法下载safetensor

git lfs install
git clone https://www.modelscope.cn/OpenBMB/MiniCPM-o-2_6.git

二、进入模型文件文件夹,创建docker-compose.yaml文件

在tesla t4 4张卡上的docker-compose.yaml文件

services:
  MiniCPM-o-2_6:
    image: 10.75.240.230/vllm/vllm-openai:v0.8.5# 替换为实际的镜像名称
    container_name: MiniCPM-o-2_6
    restart: always
    shm_size: 10.24g
    environment:
      - CUDA_VISIBLE_DEVICES=12,13,14,15
      - TZ=Asia/Shanghai
      - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
    volumes:
      - /data/models/MiniCPM-o-2_6:/models  # 替换为实际的宿主机路径
    command: >
      --model /models
      --served-model-name MiniCPM-o-2_6
      --tensor-parallel-size 4
      --dtype half
      --port 8000
      --gpu_memory_utilization 1
      --max-model-len=4096
      --trust-remote-code
    ports:
      - "8080:8000"# 替换为实际的端口

 附可供参考:在A10卡上测试代码

services:
  MiniCPM-o-2_6:
    image: 10.75.240.230/vllm/vllm-openai:v0.8.5
    container_name: MiniCPM-o-2_6
    restart: always
    environment:
      - CUDA_VISIBLE_DEVICES=0
      - TZ=Asia/Shanghai
      - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
    volumes:
      - /data/MiniCPM-o-2_6:/models  # 替换为实际的宿主机路径
    command: >
      --model /models
      --served-model-name MiniCPM-o-2_6
      --tensor-parallel-size 1
      --dtype auto
      --port 8000
      --gpu_memory_utilization 1
      --max-model-len=4096
      --trust-remote-code
    ports:
      - "8080:8000"

显卡情况如下:

三、运行,查看容器日志,部署成功

docker compose up -d

四、测试代码:

  • 图片测试代码:
import base64
import requests
import os

model_url = "http://10.75.0.51:8080/v1/chat/completions"
script_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(script_dir, 'data', 'image.jpg')

try:
    with open(file_path, 'rb') as file:
        image_base64 = base64.b64encode(file.read()).decode('utf-8')
except FileNotFoundError:
    print(f"文件 {file_path} 未找到,请检查文件路径。")
else:
    # 构建符合规范的请求体
    data = {
        "model": "MiniCPM-o-2_6",
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": "请描述这张图片"
                    },
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/jpeg;base64,{image_base64}"
                        }
                    }
                ]
            }
        ]
    }

    headers = {
        "Content-Type": "application/json"
    }

    try:
        response = requests.post(model_url, json=data, headers=headers)
        response.raise_for_status()
        chat_response = response.json()
        print("Chat response:", chat_response)
        print("Chat response content:", chat_response["choices"][0]["message"]["content"])
    except requests.exceptions.RequestException as e:
        print(f"请求出错: {e}")
        # 打印服务器返回的详细错误信息
        if response.text:
            print("服务器返回的错误信息:", response.text)
    except (KeyError, IndexError):
        print("无法解析响应内容。")
  • 音频测试代码:
import base64
import requests
import os

# 服务器接口地址
model_url = "http://10.75.0.51:8080/v1/chat/completions"
# 获取脚本所在目录
script_dir = os.path.dirname(os.path.abspath(__file__))
# 构建音频文件的完整路径
file_path = os.path.join(script_dir, 'data', 'audio.mp3')

try:
    # 以二进制模式打开音频文件
    with open(file_path, 'rb') as audio_file:
        # 对音频文件内容进行 Base64 编码并转换为字符串
        audio_base64 = base64.b64encode(audio_file.read()).decode('utf-8')
except FileNotFoundError:
    print(f"文件 {file_path} 未找到,请检查文件路径。")
else:
    # 构建符合规范的请求体
    data = {
        "model": "MiniCPM-o-2_6",
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": "请描述这个音频,用中文回答"
                    },
                    {
                        "type": "audio_url",
                        "audio_url": {
                            "url": f"data:audio/mpeg;base64,{audio_base64}"
                        }
                    }
                ]
            }
        ]
    }

    # 设置请求头,指定内容类型为 JSON
    headers = {
        "Content-Type": "application/json"
    }

    try:
        # 发送 POST 请求到服务器
        response = requests.post(model_url, json=data, headers=headers)
        # 检查请求是否成功,如果不成功则抛出异常
        response.raise_for_status()
        # 将响应内容解析为 JSON 格式
        chat_response = response.json()
        print("Chat response:", chat_response)
        print("Chat response content:", chat_response["choices"][0]["message"]["content"])
    except requests.exceptions.RequestException as e:
        print(f"请求出错: {e}")
        # 打印服务器返回的详细错误信息
        if response.text:
            print("服务器返回的错误信息:", response.text)
    except (KeyError, IndexError):
        print("无法解析响应内容。")
  • 视频测试代码:
import base64
import requests
import os

# 服务器接口地址
model_url = "http://10.75.0.51:8080/v1/chat/completions"
# 获取脚本所在目录
script_dir = os.path.dirname(os.path.abspath(__file__))
# 构建视频文件的完整路径
file_path = os.path.join(script_dir, 'data', '视频2.mp4')

try:
    # 以二进制模式打开视频文件
    with open(file_path, 'rb') as video_file:
        # 对视频文件内容进行 Base64 编码并转换为字符串
        video_base64 = base64.b64encode(video_file.read()).decode('utf-8')
except FileNotFoundError:
    print(f"文件 {file_path} 未找到,请检查文件路径。")
else:
    # 构建符合规范的请求体
    data = {
        "model": "MiniCPM-o-2_6",
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": "请描述这个视频"
                    },
                    {
                        "type": "video_url",
                        "video_url": {
                            "url": f"data:video/mp4;base64,{video_base64}"
                        }
                    }
                ]
            }
        ]
    }

    # 设置请求头,指定内容类型为 JSON
    headers = {
        "Content-Type": "application/json"
    }

    try:
        # 发送 POST 请求到服务器
        response = requests.post(model_url, json=data, headers=headers)
        # 检查请求是否成功,如果不成功则抛出异常
        response.raise_for_status()
        # 将响应内容解析为 JSON 格式
        chat_response = response.json()
        print("Chat response:", chat_response)
        print("Chat response content:", chat_response["choices"][0]["message"]["content"])
    except requests.exceptions.RequestException as e:
        print(f"请求出错: {e}")
        # 打印服务器返回的详细错误信息
        if response.text:
            print("服务器返回的错误信息:", response.text)
    except (KeyError, IndexError):
        print("无法解析响应内容。")

五、yaml文件解释

整体结构

services 是 Docker Compose 中用于定义多个服务的顶级关键字。

这里定义了一个名为 MiniCPM-o-2_6 的服务。

服务配置项解释

  • 服务名称
MiniCPM-o-2_6:

这是服务的名称,用于在 Docker Compose 中唯一标识该服务。

  • 镜像配置

image: 10.75.240.230/vllm/vllm-openai:v0.8.5

指定了要使用的 Docker 镜像。这里的镜像存储在私有镜像仓库 10.75.240.230 中,镜像名称为 vllm/vllm-openai,版本标签是 v0.8.5。你需要将其替换为实际的镜像名称。

  • 容器名称
container_name: MiniCPM-o-2_6

为该服务创建的 Docker 容器指定一个名称。

  • 重启策略
restart: always

设置容器的重启策略为 always,意味着无论容器因何原因停止,Docker 都会自动重启它。

  • 共享内存大小
shm_size: 10.24g

为容器分配的共享内存大小为 10.24GB。共享内存用于容器内进程间的通信。

  • 环境变量配置
environment:
  - CUDA_VISIBLE_DEVICES=12,13,14,15
  - TZ=Asia/Shanghai
  - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
  • CUDA_VISIBLE_DEVICES=12,13,14,15:指定容器内可见的 NVIDIA GPU 设备编号,即容器只能使用编号为 12、13、14、15 的 GPU。
  • TZ=Asia/Shanghai:设置容器内的时区为亚洲 / 上海。
  • PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True:配置 PyTorch 的 CUDA 内存分配策略,允许 CUDA 内存段可扩展。

  • 数据卷挂载
volumes:
  - /data/models/MiniCPM-o-2_6:/models

将宿主机的 /data/models/MiniCPM-o-2_6 目录挂载到容器内的 /models 目录。你需要将宿主机路径替换为实际的路径。

  • 容器启动命令
command: >
  --model /models
  --served-model-name MiniCPM-o-2_6
  --tensor-parallel-size 4
  --dtype half
  --port 8000
  --gpu_memory_utilization 1
  --max-model-len=4096
  --trust-remote-code

这是容器启动时执行的命令。具体参数含义如下:

  • --model /models:指定模型的路径为容器内的 /models 目录。
  • --served-model-name MiniCPM-o-2_6:指定服务的模型名称为 MiniCPM-o-2_6
  • --tensor-parallel-size 4:设置张量并行的大小为 4,通常用于分布式训练或推理。
  • --dtype half:指定模型使用半精度浮点数(FP16)进行计算。
  • --port 8000:指定服务监听的端口为 8000。
  • --gpu_memory_utilization 1:设置 GPU 内存利用率为 100%。
  • --max-model-len=4096:指定模型的最大长度为 4096。
  • --trust-remote-code:允许使用远程代码。

  • 端口映射
ports:
  - "8080:8000"

将宿主机的 8080 端口映射到容器内的 8000 端口。这意味着可以通过访问宿主机的 8080 端口来访问容器内服务监听的 8000 端口。你需要将端口替换为实际的端口。

综上所述,这个 Docker Compose 服务配置文件的作用是启动一个基于特定镜像的 Docker 容器,配置容器的环境和资源,挂载数据卷,并运行一个模型服务,同时将服务端口映射到宿主机上。

遇到的坑:

问题1:如果遇到请求出错: 500 Server Error,服务器返回的错误信息: Internal Server Error

去查看容器日志问题:ImportError: Please install vllm[audio] for audio support

解决:在容器内去下载vllm[audio]、vllm[video]

docker exec -it MiniCPM-o-2_6 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple vllm[audio]
docker exec -it MiniCPM-o-2_6 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple vllm[video]

这两个都要安!!安了然后restart docker 容器 注意是restart!!当然如果你想新起一个容器配置好也是可以的。

问题2:Bfloat16 is only supported on GPUs with compute capability of at least 8.0. Your Tesla T4 GPU has compute capability 7.5. You can use float16 instead by explicitly setting the `dtype` flag in CLI, for example: --dtype=half.

解决:这表明你正在使用的 Tesla T4 GPU 的计算能力为 7.5,而 Bfloat16 数据类型仅支持计算能力至少为 8.0 的 GPU。当前的配置中使用了 Bfloat16 数据类型,导致程序无法正常初始化并报错。解决这个问题的方法正如错误信息中所提示的,需要在命令行中显式地设置 dtype 标志为 float16(half)。在你的 docker-compose.yml 文件中,你可以修改 command 字段来实现这一点。如果是其他的卡,例如A10没遇到这个问题可以设置为auto。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值