龙芯 3A4000 AI 应用篇 (一):部署本地大语言模型

本贴最后更新于 272 天前,其中的信息可能已经东海扬尘

羊驼

六月份的时候,我用了一块龙芯 3A4000 主板,组装了一台性能尚可的主机,并且移植了一些经典主机的模拟器。虽说折腾的过程很欢乐,不过只用这台机器打游戏有些浪费,做成 NAS 又不能满足我的需求。所以我准备在这台龙芯主机上,尝试做一些 AI 应用,这一篇文章就算是开个头。

首先我能想到的就是部署一个本地的大语言模型了。大模型一般通过 llama.cpp 部署在本地机器上,好处就是纯 C++ 实现,不需要依赖 pytorch 等复杂的框架环境。在 llama.cpp 的基础上有很多应用,比如支持语音输入的 whisper.cpp 等。最近比较流行的是 ollama 框架,ollama 也是在 llama.cpp 的基础上做的,使用起来更加方便。ollama 可以作为后端服务,给多种前端应用提供推理服务,也可以方便的下载、管理模型。我这次的本地大语言模型,就是基于 ollama 部署的,接下来就详细介绍部署过程。

一、安装 Ollama 推理框架

首先我们需要部署一个可以运行本地大语言模型的推理框架,这里我们选择使用 ollama,使用它可以让我们轻松地在本地部署和管理多种大型语言模型(LLMs),比如最新地 Llama 3.1、通义千问,也可以导入和定制自定义地语言模型。ollama 使用 golang 语言编写,底层依赖 llama.cpp 纯 C++ 实现,无需配置 pytorch 环境,可以使用 CPU 推理,也支持使用 NVIDIA CUDAAMD ROCm 显卡加速。

1. 基础编译环境

由于 ollama 目前官方尚没有支持龙芯的 mips64el 或者 loongarch64 架构的安装包,因此需要手动编译源码才能运行。编译需要以下基础环境:

  • cmake 版本 3.24 以上
  • gcc 版本 11.4.0 以上
  • go 版本 1.22 以上

其中 golang 对龙芯的支持目前已经比较完善,可以到官网寻找最新的安装包。比如目前最新的 go 1.22.5 版本的 mips64el 架构对应的安装包为:go1.22.5.linux-mips64le.tar.gzloongarch64 架构的安装包为:https://go.dev/dl/go1.22.5.linux-loong64.tar.gz

# 安装 cmake, gcc $ apt install cmake gcc # 查看 gcc 版本,要求大于 11.4.0 $ gcc --version gcc (Debian 12.2.0-14) 12.2.0 Copyright (C) 2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 查看 cmake 版本,要求大于 3.24 $ cmake --version cmake version 3.30.0 CMake suite maintained and supported by Kitware (kitware.com/cmake). # 安装 golang # golang 目前有编译好的 mips64le 版本,可以直接去官网下载安装 # ollama 要求版本 1.22 以上 # 下载解压 golang 安装包 $ wget https://go.dev/dl/go1.22.5.linux-mips64le.tar.gz $ tar zxvf go1.22.5.linux-mips64le.tar.gz # 添加 golang 环境变量 export PATH=/home1/opt/go-1.22.5/bin:${PATH} # golang 包管理网址 proxy.golang.org 国内无法访问 # 切换国内代理地址 $ go env -w GOPROXY=https://goproxy.cn

这样就完成了准备工作,接下来正式开始编译 ollama 框架。

2. 编译 ollama

首先需要下载 ollama 的源代码镜像,使用递归下载子模块。

# 下载 ollama $ git clone --recurse-submodules https://github.com/ollama/ollama.git # 如果下载子模块失败,可以更新下载 $ git submodule update

下载完成后,就可以开始编译了。ollama 官网提供的教程非常的坑爹简单,只需要两段命令。

$ cd ollama # 编译 $ go generate ./... $ go build .

然而,直接编译在以下代码处报错,应该是不规范的省略写法,导致函数体判断为空。

# 直接编译会在以下代码处报错,主要是由于函数体没写的缘故 /root/go/pkg/mod/github.com/chewxy/math32@v1.10.1/sqrt.go:3:6: missing function body /root/go/pkg/mod/github.com/chewxy/math32@v1.10.1/log.go:76:6: missing function body /root/go/pkg/mod/github.com/chewxy/math32@v1.10.1/exp.go:3:6: missing function body /root/go/pkg/mod/github.com/chewxy/math32@v1.10.1/exp.go:57:6: missing function body /root/go/pkg/mod/github.com/chewxy/math32@v1.10.1/remainder.go:33:6: missing function body

查看了源代码后,发现是函数体的重命名问题。比如 sqrt.go 中,出错的第 3 行的 Sqrt 空函数体,应该是对第 6 行具体的 sqrt 函数的引用。我们依次修改这 4 个文件的 5 处错误,把完整的函数体引用补全即可。

# 可以到文件的对应行,增加函数体的返回写法 # 示例修改 sqrt.go $ vim /root/go/pkg/mod/github.com/chewxy/math32@v1.10.1/sqrt.go 1 package math32 2 3 func Sqrt(x float32) float32 4 5 // TODO: add assembly for !build noasm 6 func sqrt(x float32) float32 { ... # 将第三行改为 3 func Sqrt(x float32) float32 { 4 return sqrt(x) 5 } # 其他位置修改方法类似

继续尝试编译,又会报以下错误。这次则是由于库文件不全导致的。

# 如果没有设置库文件,会报找不到 llama 或 ggml 库文件的错误 # github.com/ollama/ollama /home1/opt/go-1.22.5/pkg/tool/linux_mips64le/link: running gcc failed: exit status 1 /usr/bin/ld: 找不到 -lllama: 没有那个文件或目录 /usr/bin/ld: 找不到 -lggml: 没有那个文件或目录 collect2: error: ld returned 1 exit status

观察缺失的这两个库文件,对应 libllama.so 和 libggml.so 两个文件,是 llama.cpp 中提供的。因此需要首先编译一下 llama.cpp,然后再添加到链接库目录中。

# 另外编译之前,需要首先编译依赖 llama.cpp $ cd ./llm/llama.cpp $ cmake -B build $ cmake --build build --config Release -j4 # 查询编译好的库文件位置 $ find . -type f -name "*.so" ./llm/llama.cpp/build/src/libllama.so ./llm/llama.cpp/build/ggml/src/libggml.so ./llm/llama.cpp/build/examples/llava/libllava_shared.so # 复制库文件到系统目录下 cp ./llm/llama.cpp/build/src/libllama.so /usr/local/lib cp ./llm/llama.cpp/build/ggml/src/libggml.so /usr/local/lib # 使用 LD_LIBRARY_PATH 环境变量设置库路径 $ export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

踩完这些坑之后,再次尝试编译 ollama,顺利通过。

# 生成编译文件 $ cd llm/generate $ bash gen_linux.sh # 编译 $ go generate ./... $ go build .

此时就可以在命令行端使用 ollama 进行简单的 LLM 推理会话了。由于使用 CPU 推理速度有限,结合内存大小(16GB),最好选择参数量为 8B 以下的模型加载。以下是一些常用命令,常见模型的推理速度和效果:

# 开启 ollama 服务 $ ollama serve # 拉取 llama3.1 模型 $ ollama pull llama3.1 # 运行通义千问2 0.5b 参数模型(会自动下载镜像) $ ollama run qwen2:0.5b # 查看已下载模型列表 $ ollama list # 查看模型加载情况 $ ollama ps

80 亿参数规模的 llama 3.1 模型:

模型 llama3.1:8b 的推理速度很慢,约为 0.279 tokens/s。

llama3.1_8b.gif

15 亿参数规模的通义千问 2 模型:

模型 qwen2:1.5b 的推理速度稍微快点,约为 2.626 tokens/s

qwen2_1.5b.gif

5 亿参数规模的通义千问 2 模型:

模型 qwen2:0.5b 的推理速度约为 6 tokens/s

qwen2_0.5b.gif

3. 部署 systemctl 服务

直接使用 ollama 服务需要每次使用 ollama serve 命令服务。为了方便起见,我们为 ollama 制作开机自启动服务,同时允许网络中的其他主机访问。编写 ollama.service 服务文件

# 编写`ollama.service`服务文件 vim ollama.service [Unit] Description=Ollama Service After=network.target [Service] ExecStart=/home/opt/ollama/ollama serve WorkingDirectory=/home/opt/ollama Environment=OLLAMA_MODELS=/home/opt/llm_models Environment=HOME=/root:$HOME Environment=PATH=/usr/bin:/usr/local/bin:$PATH Environment=LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH Environment=OLLAMA_HOST=0.0.0.0:11434 Environment=OLLAMA_ORIGINS=* [Install] WantedBy=multi-user.target

为了方便查看龙芯主机运行 ollama 服务时的工作状态,我们可以利用 tmux 实现分屏监控 htop, dstat, fastfetch 等各项系统指标。以下是分屏监控脚本:

#!/bin/bash # ollama/monitor.sh # 更新包列表并安装所需的监控命令 # sudo apt-get update # sudo apt-get install -y tmux htop dstat # 检查是否存在名为 monitor 的 tmux 会话,如果存在则将其删除 if tmux ls | grep -q "^monitor:"; then tmux kill-session -t monitor fi # 创建新的 tmux 会话 tmux new-session -d -s monitor # 水平分割第1个窗格 tmux split-window -v -t 0 # 垂直分割第1个窗格 tmux split-window -h -t 0 # 垂直分割第3个窗格 tmux split-window -h -t 2 # 在第一个窗格中运行 htop 查看 CPU, 进程, 内存 tmux send-keys -t 0 'htop' C-m # 在第二个窗格中运行 dstat 查看实时网络 tmux send-keys -t 1 'dstat -tdn --tcp --udp --color' C-m # 在第四个窗格中运行 fastfetch 查看内核信息 tmux send-keys -t 3 'sleep 1 && fastfetch' C-m # 第七秒后执行自定义命令: tmux send-keys -t 3 'sleep 7 && watch -n 1 ollama ps' C-m # 选择第三个窗格待命 tmux select-pane -t 2 # tmux send-keys -t 2 'clear' C-m # 设置窗格布局为 tiled # tmux select-layout -t monitor tiled echo "Tmux session 'monitor' created with four panes. Use 'tmux attach -t monitor' to attach. Use ctrl+b d to detach. " tmux attach -t monitor

实际执行的效果如下:

image-20240807160207566.png

二、安装对话前端框架

部署完 ollama 之后,我们就可以在命令行端加载大语言模型了。但是命令行使用还是很不方便,最好能部署到 Web 端,可以通过网页访问。Web 端的框架选择很多,实现方式也多种多样。有的是基于浏览器插件实现,有的则是独立前端服务,大部分都是通过 Docker 部署。

1. 使用 Chrome 插件 Page Assist

基于浏览器插件的 Page Assist 使用非常方便,只需要在 Chrome 扩展商店页面点击安装即可(火狐商店也有对应的插件,安装没有问题,但是奇怪的是无法在聊天框输入中文)。插件页面如下:

image-20240807162105228.png

使用界面非常干净整洁,功能也很强大,支持图片输入(但是大部分 llama 模型不支持解析图片)、语音驶入和网络搜索功能。

image-20240807163506604.png

2. 使用 Lobe-Chat 对话框架

如果追求更丰富的前端功能,可以使用 Lobe Chat 前端框架。Lobe Chat 既支持访问网络主机的 ollama 服务,也可以通过 API 访问各种大语言模型服务,比如 OpenAI ChatGPT。我这里先配置好龙芯主机的服务端口,用另一台主机上部署好的 Lobe Chat 访问龙芯主机的 ollama 服务,效果如下:

外部主机访问 ollama.gif

效果还不错,可以看出中间存在的卡顿,大概率是网络传输造成的。

好的,那么这次的本地大语言模型部署就初步完成了。整体过程还算是顺利,中间的过程我还尝试编译 AMD ROCm,试图利用显卡的 GPU 进行推理加速。不过龙芯 MIPS 架构的工具链缺失的太多,编译过程会非常麻烦。而且之前买的亮机卡 RX460 显存也只有 4GB,即使成功用上了,折腾半天换来的提速实在有限,所以也就作罢了。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...