返回首页
笔记

Ollama:优雅地玩转本地大模型

开箱即用,自动优化,生态完善,主打一个优雅

AI原生分享

引言

4月份时,曾借 LLaMA 开源之机尝试梳理了羊驼的属种之别和其引发的大语言模型(LLM)平民化运动,也曾简单介绍过在 LLM 平民化过程中上扮演了重要角色的 llama.cpp 项目。彼时羊驼一族在开源大模型领域初露头角,周边生态一片向好。转眼年关将至,回望过去的三个季度,以 Meta 在 6 月发布更强大且更开放的 Llama 2 为节点,开源社区再次以不可阻挡之势适配、进化、落地,如今 LLM 早已不再等同于昂贵的 GPU,而是可以在大部分消费级计算机上运行推理的应用了——俗称本地大模型。

Llama 2 三件套
Llama 2 三件套

优雅不易

根据经验,16 位浮点精度(FP16)的模型,推理所需显存(以 GB 为单位)约为模型参数量(以 10 亿为单位)的两倍,即 Llama 2 7B(70 亿)对应需要约 14GB 显存以进行推理,这显然超出了普通家用计算机的硬件规格,作为参考一台 GeForce RTX 4060 Ti 16GB 显卡市场价超过 3000 元。

模型量化(quantization)技术可以很大程度上降低显存要求,以 4-bit 量化为例,其将原本 FP16 精度的权重参数压缩为 4 位整数精度,使模型权重体积和推理所需显存均大幅减小,仅需 FP16 的 1/4 至 1/3,意味着约 4GB 显存即可启动 7B 模型的推理,当然实际显存需求会随着上下文内容叠加而不断增大。

与此同时,llama.cpp 项目用 C/C++ 重写了推理代码,既避免了 PyTorch 引入的复杂依赖,又提供了更为广泛的硬件支持,包括纯 CPU 推理、Apple Silicon 在内的各类底层计算架构都得以充分发挥对应的推理加速。并且由于 Llama 架构的流行,llama.cpp 的量化和推理能力能够近乎无缝地迁移应用至相同架构的开源大语言模型,如阿里云的 Qwen 系列、零一万物的 Yi 系列等。

尽管 llama.cpp 带来了诸多好处,但当你想要真正动手体验一把时,却发现需要获取模型权重、克隆项目代码、执行模型量化、设置环境变量、构建可执行文件等诸多环节才能以命令行的形式问一个测试问题,更不要提数十个可能需要手动调整的参数了。

% make -j && ./main -m models/llama-13b-v2/ggml-model-q4_0.gguf -p "Building a website can be done in 10 simple steps:\nStep 1:" -n 400 -e
I llama.cpp build info:
I UNAME_S:  Darwin
I UNAME_P:  arm
I UNAME_M:  arm64
I CFLAGS:   -I.            -O3 -std=c11   -fPIC -DNDEBUG -Wall -Wextra -Wpedantic -Wcast-qual -Wdouble-promotion -Wshadow -Wstrict-prototypes -Wpointer-arith -Wmissing-prototypes -pthread -DGGML_USE_K_QUANTS -DGGML_USE_ACCELERATE
I CXXFLAGS: -I. -I./common -O3 -std=c++11 -fPIC -DNDEBUG -Wall -Wextra -Wpedantic -Wcast-qual -Wno-unused-function -Wno-multichar -pthread -DGGML_USE_K_QUANTS
I LDFLAGS:   -framework Accelerate
I CC:       Apple clang version 14.0.3 (clang-1403.0.22.14.1)
I CXX:      Apple clang version 14.0.3 (clang-1403.0.22.14.1)

make: Nothing to be done for `default'.
main: build = 1041 (cf658ad)
main: seed  = 1692823051
llama_model_loader: loaded meta data with 16 key-value pairs and 363 tensors from models/llama-13b-v2/ggml-model-q4_0.gguf (version GGUF V1 (latest))
llama_model_loader: - type  f32:   81 tensors
llama_model_loader: - type q4_0:  281 tensors
llama_model_loader: - type q6_K:    1 tensors
llm_load_print_meta: format         = GGUF V1 (latest)
llm_load_print_meta: arch           = llama
llm_load_print_meta: vocab type     = SPM
llm_load_print_meta: n_vocab        = 32000
llm_load_print_meta: n_merges       = 0
llm_load_print_meta: n_ctx_train    = 4096
llm_load_print_meta: n_ctx          = 512
llm_load_print_meta: n_embd         = 5120
llm_load_print_meta: n_head         = 40
llm_load_print_meta: n_head_kv      = 40
llm_load_print_meta: n_layer        = 40
llm_load_print_meta: n_rot          = 128
llm_load_print_meta: n_gqa          = 1
llm_load_print_meta: f_norm_eps     = 1.0e-05
llm_load_print_meta: f_norm_rms_eps = 1.0e-05
llm_load_print_meta: n_ff           = 13824
llm_load_print_meta: freq_base      = 10000.0
llm_load_print_meta: freq_scale     = 1
llm_load_print_meta: model type     = 13B
llm_load_print_meta: model ftype    = mostly Q4_0
llm_load_print_meta: model size     = 13.02 B
llm_load_print_meta: general.name   = LLaMA v2
llm_load_print_meta: BOS token = 1 '<s>'
llm_load_print_meta: EOS token = 2 '</s>'
llm_load_print_meta: UNK token = 0 '<unk>'
llm_load_print_meta: LF token  = 13 '<0x0A>'
llm_load_tensors: ggml ctx size =    0.11 MB
llm_load_tensors: mem required  = 7024.01 MB (+  400.00 MB per state)
...................................................................................................
llama_new_context_with_model: kv self size  =  400.00 MB
llama_new_context_with_model: compute buffer total size =   75.41 MB

system_info: n_threads = 16 / 24 | AVX = 0 | AVX2 = 0 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 0 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.800000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = 400, n_keep = 0

所以很长一段时间里,本地大模型都局限于少数极客和研究者的圈子,同期一些基于 llama.cpp 的应用封装也遵循相似的偏好,较高的上手门槛将许多普通人拒之门外。

直到 Ollama 及其逐步孕育出来的生态走向前台,本地大模型终于可以优雅地玩转起来了。

官网
官网

快速上手

Ollama 安装十分简单,macOS 直接在官网下载安装包打开运行;Window 尚未提供安装包,推荐在 WSL 2 中以 Linux 方式用命令安装:

% curl https://ollama.ai/install.sh | sh

如果你熟悉 Docker,也可以直接使用其官方镜像

当你运行 ollama --version 命令成功查询到版本时,表示 Ollama 的安装已经顺利完成,接下来便可以用 pull 命令从在线模型库下载模型来玩了。

以中文微调过的 Llama2-Chinese 7B 模型为例,下述命令会下载接近 4GB 的 4-bit 量化模型文件,需要至少 8GB 的内存进行推理,推荐配备 16GB 以流畅运行。

% ollama pull llama2-chinese

下载完成后,使用 run 命令运行模型,可直接将消息附在命令后,或留空进入对话模式,对话模式内置了几个以斜杠引出的命令:

# 单条输入
% ollama run llama2-chinese "天空为什么是蓝色的?"
# 对话模式
% ollama run llama2-chinese
>>> /?
Available Commands:
  /set         Set session variables
  /show        Show model information
  /bye         Exit
  /?, /help    Help for a command

Use """ to begin a multi-line message.

>>> 天空为什么是蓝色的?

这个问题是一个常见的争议。有一些科学家认为天空的蓝色可以被解释为雾和云层中的微小碎片反射出来的光色,而其他人则认为这是由于地球自身温度的影响。目前还没这个问题是一个常见的争议。有一些科学家认为天空的蓝色可以被解释为雾和云层中的微小碎片反射出来的光色,而其他人则认为这是由于地球自身温度的影响。目前还没有一个公认的解释。

值得一提的是,Ollama 会判别正在运行的硬件并在可行的情况下调用 GPU 加速,不妨在推理时打开活动监视器或任务管理器观察以验证。

到此,你已经体验到触手可及的本地大模型了。

套上外壳

若是觉得命令行的形式不够易用,Ollama 有一系列的周边工具可供使用,包含了网页、桌面、终端等交互界面及诸多插件和拓展。

之所以能形成如此丰富的生态,是因为 Ollama 自立项之初就有清晰的定位,即让更多人以最简单快速的方式在本地把大模型跑起来,于是项目封装 llama.cpp 的同时将繁多的参数与对应的模型打包放入模型库,Ollama 应用本身则约等于一个简洁的命令行工具和一个稳定的服务端 API,从而为下游应用和拓展提供了极大便利,日益激发了生态的繁荣发展。

就 Ollama GUI 而言,针对不同偏好有许多选择:

  • Web 版:Ollama WebUI 具有最接近 ChatGPT 的界面和最丰富的功能特性,需要以 Docker 部署;
  • 图源:项目主页
    图源:项目主页
  • 终端 TUI 版:oterm 提供了完善的功能和快捷键支持,用 brewpip 安装;
  • 图源:项目主页
    图源:项目主页
  • Raycast 插件:Raycast Ollama 是我个人最常用的 Ollama 前端 UI,其继承了 Raycast 的优势,能在选中或复制语句后直接调用命令,体验丝滑;而作为价值约 8 美元/月的 Raycast AI 的平替,Raycast Ollama 实现了 Raycast AI 的绝大多数功能,且随着 Ollama 及开源模型的迭代,还将支持多模态等 Raycast AI 不支持的功能,可谓潜力无限。
  • 复刻了 Raycast AI 的 Raycast Ollama 插件
    复刻了 Raycast AI 的 Raycast Ollama 插件

以及使用 Swift 编写的 macOS 原生应用 Ollamac、类似于 Notion AI 的 Obsidian Ollama 等有趣应用,可按需选用。

进阶玩法

如果你从前文「天空为什么是蓝色的」的回答中,隐约感觉到一丝异样,那么恭喜你成功捕捉到了一次大语言模型的幻觉(hallucinate,剑桥词典 2023 年度词)。受预训练的效果、参数尺寸、量化等因素的影响,模型的能力参差不齐,从原则上讲参数量多的模型通常会具有更好的效果,换言之尺寸小又经过量化折损的模型更容易出现幻觉、甚至是前言不搭后语的情况。

上手时用的是 Llama2-Chinese 模型 7B 参数量 4bit 量化版,如果有 16GB 的内存如何运行 13B 参数量版本呢?Ollama 采取了与 Docker 组织镜像相似的方案,使用模型名加上标签的形式( model:tag )来确定具体的模型版本,不加标签时默认为 latest ,通常对应 7B 参数量 4bit 量化版,所有可选标签同样可在对应模型的标签页查看。

于是我们可以加上参数量标签来跑同系列模型的大尺寸版,期望能收获更好的结果:

% ollama run llama2-chinese:13b "天空为什么是蓝色的?"

天空是由大气层和太阳光的散射而成的蓝色。

在日出之前,我们看到的天空是紫色或彩虹色,这是因为太阳光从海平面反射回来时,会被大气中的二氧化碳和水分子散射成蓝色、紫色或彩虹色。

而在日出之后,天空变成了灰色,这是由于太阳光从大气中被阻挡,并且不再有足够的反射来给天空增加蓝色。

当我们看到天空时,它的颜色是由于太阳光与大气中的物质相互作用而形成的。这些物质包括水、二氧化碳和其他气体,以及微小的冰片和沙塵。

当我们看到天空变成了晚上时,天空会逐渐变得更加深蓝,这是由于太阳光在大气中传播,同时也因为大气层的结构。

或者换用其他模型系列,在此推荐官方模型库里对中文支持相对较好或比较有趣的几个模型:

  • Yi 系列,零一万物团队推出,有支持 20 万上下文窗口的版本可选;
  • 如果碰巧财力雄厚,不妨试试法国明星初创团队 Mistral 最新推出的首个开源混合专家 MoE 模型 Mixtral-8x7B,需要 48GB 内存以运行;
  • 如果不巧硬件紧张,无需气馁,Phi-2 由微软团队针对逻辑和理解精调,2.7B 的尺寸只需 4 GB 内存即可运行,吐字速度飞快,只是不太懂中文。

除了纯语言大模型,Ollama 自 0.1.15 版本开始提供支持的视觉模型也值得一玩,图片可以直接拖入终端:

% ollama run llava
>>> What does the text in this image say? /Users/mchiang/Downloads/image.png 
Added image '/Users/mchiang/Downloads/image.png'

The text in this image says "The Ollamas."
示例源于发版公告
示例源于发版公告

另一方面,经过 ChatGPT 大半年的教育,多数人都已知晓提示词的重要性,可在每次输入时调教模型,或将对应的指令写入系统提示词一劳永逸。

在 Ollama 中自定义系统提示词有几种方案。首先,多数 Ollama 前端已提供系统提示词的配置入口,推荐直接利用其功能;其次,这些前端在底层往往是通过 API 与 Ollama 服务端交互的,我们也可以直接调用,并传入系统提示词选项:

% curl http://localhost:11434/api/chat -d '{
  "model": "llama2-chinese:13b",
  "messages": [
    {
      "role": "system",
      "content": "以海盗的口吻简单作答。"
    },
    {
      "role": "user",
      "content": "天空为什么是蓝色的?"
    }
  ],
  "stream": false
}'
{
    "model": "llama2-chinese:13b",
    "created_at": "2023-12-19T13:08:14.116074Z",
    "message": {
        "role": "assistant",
        "content": "\n啊,你这么好玩!蓝色的天空是因为我们的宝藏呢。你知道什么?金币、珍珠、钻石、锻金匣子等等!每当海盗们出差时,他们会将自己的胸口和腰间打扫好,以便在任何时候都能抓住一切宝藏。而天空是我们的最大的宝藏,其中包含了无数星际之宝,这些宝藏被称为“蓝色”,因为它们在宇宙中独立存在,与任何一个星球或行星都不同。\n",
        "images": null
    },
    "done": true,
    "total_duration": 64370435250,
    "load_duration": 12994991083,
    "prompt_eval_count": 44,
    "prompt_eval_duration": 2001748000,
    "eval_count": 204,
    "eval_duration": 49363417000
}

此外,Ollama 的 ModelFile 给用户留下了更多自定义的空间,除了系统提示词,还有包括对话模板、模型推理温度、上下文窗口长度等参数均可自行设置,适合进阶使用。在创建前,通过 show --modelfile 命令可以查看现有模型的 ModelFile 内容,作为参考:

% ollama show --modelfile llama2-chinese:13b
# Modelfile generated by "ollama show"
# To build a new Modelfile based on this one, replace the FROM line with:
# FROM llama2-chinese:13b

FROM ~/.ollama/models/blobs/sha256:8359bebea988186aa6a947d55d67941fede5044d02e0ab2078f5cc0dcf357831
TEMPLATE """{{ .System }}
Name: {{ .Prompt }}
Assistant:
"""
PARAMETER stop "Name:"
PARAMETER stop "Assistant:"

以自定义系统提示词并修改推理温度参数为例,应构建如下格式的 ModelFile:

FROM llama2-chinese:13b

SYSTEM "以海盗的口吻作答。"
PARAMETER temperature 0.1

然后使用 create 命令进行创建,新的模型会沿用原有模型的权重文件和未作调整的选项参数:

% ollama create llama2-chinese-pirate -f ~/path/to/ModelFile

从而得到了属于自己的本地模型。

结语

如果与普通应用软件作比,Ollama 的使用体验恐怕很难称得上优雅。但当我们把目光聚焦在方兴未艾的大模型领域,数月前还需要真金白银堆卡、折腾配置环境才能调通,或是需要自行量化编译才能运行,如今模型发布不到一周(Phi-2 上周发布)就能丝滑地跑在笔记本上,实在是从刀耕火种步入现代社会,愿称此为科技平民化的优雅之路。