前言

笔者自 22 年底 ChatGPT 推出以来,经常使用大语言模型(LLM)。可以说,LLM 对日常学习生活的助力是不容忽视的。

然而,我时常会想,藏在 OpenAI 的集群中的、售价 20$ 每月的 ChatGPT Plus,什么时候才能「飞入寻常百姓家」?用时髦的话讲,大模型的民主化(democratize)。

早期,我曾为了身边朋友方便体验 LLM,使用 ChuanhuChatGPT 搭建了 chat.naiv.fun。起初使用 OpenAI 的官方 API,后来因为支付的不便,替换为 Copilot 模拟的 OpenAI API

2024 年 5 月,OpenAI 宣布提供免费的 GPT 4o 模型,chat.naiv.fun 的使命于是告终。

托管的 Chat 服务下线通知
托管的 Chat 服务下线通知

但是,使用各类 API Server,都只是在调用别人的接口,究竟有没有办法在自己的设备上运行一个未经审查和数据不外泄的 LLM 呢?

目光投向自己的 MacBook Air。16G 统一内存、Metal GPU 加速,看上去是相当美好的。遂尝试在自己的 MBA 上部署一个完全本地的、可用的 LLM。

当然,本篇文章不止面向 Mac 用户。如果你有 N 卡/A 卡,并且有 4G 以上的显存,那么也可以尝试。(对于更低的配置,使用更小的模型或许也能跑;纯 CPU 也能跑,只是比较慢罢了...)

背景知识

这里需要一些背景知识。

首先,部署使用大模型,有两种情况,分别是推理 (inference) 和训练 (trainning)。

所谓推理,就是将别人预先训练好的权重,在本地进行使用,例如进行对话任务、上下文填充等。而训练则需要准备好数据集,通过 Python 加载权重,进行一系列操作从而得到一个新的、调整过的模型。显然,前者相对简单,对硬件的要求不算苛刻,而后者则很复杂,对显存的要求很高。

在本文中,「部署」指的是使用大模型进行推理。也就是说,我们将会:

  1. 挑选一个大小适中(参数量、量化程度)、用途恰当(中文/英文、Chat/Completion)的大语言模型
  2. 将大模型下载到本地
  3. 使用 LLM 推理框架加载大模型
  4. (可选)部署一个 GUI 方便地使用大模型

模型选择

关于模型的选择,我们首先需要对自己的设备进行评估。

一般情况下,对于 8G 内存的 Mac,只能运行参数量 7B 及以下的模型;对于 16G 内存的 Mac 则可以尝试运行 14B 大小的模型(在开启 q4 量化的情况下)。至于更大内存的 Mac,可以试试较小的量化(例如 q8, q6, q5 等),或者继续提升参数量。

需要注意,一般情况下参数量越大,模型能力越强(i.e., 14B > 7B);同时,量化程度越高,模型的能力越差(i.e., q2 < q4 < q6 < q8 < fp16 < fp32,q2 量化程度最大、能力最差)。参数越大越占显存,量化程度越高越节省显存。

大概了解这些之后就可以到 Hugging Face 上挑选自己中意的模型了。不同模型的用途和特色不尽相同。

如果只是想简单地挑选一款能力强的模型,可以使用 Shanghai AI Lab 推出的 OpenCompass司南 - 评测榜单 进行筛选。

设置简单的筛选条件:模型开源、均分从高到低排序。可以发现,Qwen1.5-14B-ChatQwen1.5-7B-Chat 在同参数数量级上表现较好。

然后就可以到 Hugging Face 上搜索这款模型的详细信息了。

笔者只买得起 16G 内存的 Mac,因此就以 7B 模型,q8 量化进行讲解了:Qwen/Qwen1.5-7B-Chat · Hugging Face

推理框架

为了在我们的设备上尽可能干净、优雅地进行 LLM 推理,我们需要了解 llama.cpp。

llama.cpp 是一个开源项目,它将 Meta 的 LLaMA(Large Language Model Meta AI)模型以 C++ 为主的实现方式提供给用户。这一项目的目的是在不牺牲性能的情况下,提供一个轻量级、高效的方法来运行和部署 LLaMA 模型。这个项目利用 C++ 的高性能特点,使得 LLaMA 模型可以在不同的环境和平台上以较低的延迟运行。

llama.cpp 目前已经成为了主流的推理框架,得益于 C++,这个项目对 Nvidia、Intel、AMD、Apple 的支持都不错。

llama.cpp 采用后缀为 gguf 的权重格式,在挑选模型时需要选择 GGUF 格式下载。有部分模型的仓库中没有上传 GGUF 格式的权重,则可以自行转换(参考:ollama/docs/import.md)。

在 llama.cpp 之上,有很多基于 llama.cpp 的推理应用,可以用于方便地部署大模型。这里着重介绍两款:

推理应用的选择

ollama 使用 Go 语言开发。这个项目借鉴了 Docker 的想法,将 LLM 权重、启动 LLM 需要的 llama.cpp 参数、Prompt 抽象为一个 Modelfile(类似于 Dockerfile)。用户可以通过 ollama 从该项目的服务器拉取(pull)封装好的大模型,也可以自己写 Modelfile,从本地导入大模型。ollama 还使用 Gin 框架实现了一个 API Server,方便其他项目调用。

甚至可以不客观的说,ollama 想要成为大模型时代的 Docker。

至于 llamafile,则是 Mozilla 使用 C/C++ 开发的另一款工具,他和 ollama 都有同一个愿景,即使用单个文件分发和部署一个完整的 LLM。这个项目是完全离线的,所以用户必须自己下载权重,然后使用 llamafile 加载模型。这个项目的好处是,用户不需要进行安装、内置了一个简单的 HTML 聊天界面,非常适合快速上手 LLM。当然这个项目也提供了 API,但生态比较一般。目前这个项目也有一些 BUG,不如 ollama 成熟。

如果只是想简单跑一下 LLM,可以选择 llamafile。如果需要部署,或者形成一个工作流,则最好使用 ollama。

推理框架的使用

ollama 的使用非常简单,直接按照官网教程安装。安装完毕后运行:

ollama run qwen:7b-q8_0

然后等待模型加载完毕,就会看到一个命令行聊天界面。你可以使用 /? 命令查看模型的信息和参数。

如果需要调整对话的 System Prompt 和上下文长度,可以在启动之后使用 /set 设置参数。具体的设置内容可以参考文档了解。

而 llamafile 的使用稍显复杂。

首先需要到 Releases · Mozilla-Ocho/llamafile (github.com) 页面下载 llamafile.zip 压缩包。解压之后,在 bin 文件夹下可以找到 llamafile 可执行文件。

然后到 Hugging Face 或其他地方下载想要使用的 GGUF 格式的模型(例如 Qwen/Qwen1.5-7B-Chat-GGUF at main (huggingface.co)

使用下面的命令:

./path/to/llamafile -m your_model_here.gguf

等待模型加载完毕后,浏览器会自动打开一个简单的聊天 GUI,可以设置 System Prompt,Temperature 等。

llamafile 在加载模型时可以指定一些参数,例如 -m 指定加载的模型路径 ,-c 可以给定上下文的长度。具体可以通过 llamafile --help 查询。

一些调试经验

  • MacBook 硬盘空间捉急怎么办?

    • llamafile 可以直接从外置硬盘启动。
    • ollama 就没这么方便,ollama 最好安装在内置硬盘上(Applications),然后可以在启动时通过设置 OLLAMA_MODELS 环境变量修改模型的存放路径。
  • 上下文大小的设置?

    • 如果上下文长度过大,会导致 OOM,表现为模型输出乱码/卡死。
    • 根据我的经验(16G 内存下),对于 7B 模型(q8)可以使用 4096 上下文长度;对于 14B 模型(q4)可以使用 2048 上下文长度;
  • System Prompt?

    • ollama 可以在 Modelfile 里使用 SYSTEM 关键字,或在对话终端中使用 /set 设置。
    • llamafile 比较简单,直接在浏览器弹出的页面中填写即可。
    • 注意:设置一个好的 System Prompt 对于输出质量的影响比较大。
  • Template?

    • 我们知道,模型的输入和输出是需要遵循一定的格式的,比较知名的格式是 ChatML。需要设置好这个格式,才能获得比较好的效果。
    • 一般模型会提供推理时使用的 Template。如果你在输出中发现了很多形如<|im_start|> <|im_end|> 的文本,那说明要调整 Template 了。
    • 关于 Template 的设置,参考上文 System Prompt。

模型 GUI / 工作流

如果想要更有效率地使用 LLM,可以配置一个好用的 GUI。

前文已经提到过,ollama 可以搭配很多第三方工具使用,因此我们就在这里采用 ollama 作为推理应用。这里,我们将其称为 “推理服务器”。

与之对应地,我们可以部署一些应用,这些应用对外提供 Web 等形式的服务,而内部通过调用推理服务器的 API 来使用 LLM。

Open WebUI 为例。我们可以先在 Mac 上安装 Docker,然后:

docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

即可在本地启动 Open WebUI,通过 WebUI 获得和 ChatGPT 类似的体验。

我不想在 MacBook 上装 Docker。如果你也不想,那就继续往下看吧。

远程/公开访问方案

如果这篇文章到这里就结束了,未免有点太水了(笑死)

问题来了,现在你已经实现了在 MacBook 上运行推理服务器,你也尝试过在本地跑一些 AI 应用。但这能叫「大模型自由」吗?我想躺床上用手机玩怎么办?如果要分享给好朋友众乐乐怎么办?

对于富哥来说,租个 GPU 服务器就解决了,但我只有一台 MacBook 能跑 LLM,所以需要想个办法,灵活、安全地利用 MacBook 上的推理服务器。

我的选择是 Tailscale。Tailscale 说白了就是一个带管理页面和中转节点的 WireGuard 客户端。 WireGuard 在之前的文章已经介绍过,不再赘述。我们使用 Tailscale 实现便捷灵活的组网。

思路是这样的:

  1. 在 VPS 和 MacBook 上安装 Tailscale。
  2. 在 MacBook 上启动 ollama。
  3. 在远程的 VPS 上安装 Docker。
  4. 在 Docker 中启动 Nginx 和 Open WebUI。
  5. Nginx 配置一个反向代理,例如:chat.naiv.fun 反向代理到 open-webui:8080。

最终的效果为:

访问 chat.naiv.fun 就可以使用我的 MacBook 进行 LLM 推理(当然,前提是我的 MacBook 在线)。

OpenWebUI+Ollama 部署效果
OpenWebUI+Ollama 部署效果

关于这个方案的一些细节:

  • 首先获取 MacBook 在 Tailscale 网络中的域名(可以在 Machines - Tailscale 中找到)。
  • 在 MacBook 上,不要使用图标启动 Ollama。在附加环境变量的情况下使用 CLI 启动:

    OLLAMA_HOST=leos-macbook-air.taild0bd4.ts.net ollama serve
  • 在 Open WebUI 容器启动时,添加一个环境变量:

    environment:
      - OLLAMA_BASE_URL=http://leos-macbook-air.taild0bd4.ts.net:11434

总结

近期微软发布的 Phi-3 模型,让我看到了 LLM 在更大范围普及的希望。相信有一天,我们不仅能在消费级设备上看到大模型的推理,还能自己对大模型进行微调和训练。