普通视图

发现新文章,点击刷新页面。
昨天以前Lucien's Blog

Webhook 配置备忘

作者 Lucien
2025年6月6日 23:05
本文地址:blog.lucien.ink/archives/552

将下列代码保存为 install.sh,然后 bash install.sh

#!/usr/bin/env bash
set -e
wget 'https://github.mirrors.lucien.ink/https://github.com/adnanh/webhook/releases/download/2.8.2/webhook-linux-amd64.tar.gz'
tar -xzvf webhook-linux-amd64.tar.gz
mv webhook-linux-amd64 /usr/local/
cat << EOF > /etc/systemd/system/webhook.service
[Unit]
Description=Webhook server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/webhook-linux-amd64/webhook \
  -nopanic \
  -hooks /etc/webhook/hooks.yaml \
  -hotreload \
  -logfile /var/log/webhook/webhook.log \
  -port 9000
Restart=on-failure
User=root
Group=root

[Install]
WantedBy=multi-user.target
EOF
systemctl enable webhook
mkdir -p /etc/webhook/scripts
cat << EOF > /etc/webhook/hooks.yaml
- id: test
  execute-command: "/etc/webhook/scripts/test.sh"
  command-working-directory: "/root/"
  trigger-rule:
    match:
      type: value
      value: Bearer change-this
      parameter:
        source: header
        name: Authorization
EOF
cat << EOF > /etc/webhook/scripts/test.sh
#!/usr/bin/env sh
echo foo > bar
EOF
chmod +x /etc/webhook/scripts/test.sh
mkdir -p /var/log/webhook
systemctl start webhook
curl 'http://localhost:9000/hooks/test' -H 'Authorization: Bearer change-this'

zsh 配置备忘

作者 Lucien
2025年1月4日 17:31
本文地址:blog.lucien.ink/archives/551

在这里记录一下我自己的 zsh 配置。

mkdir -p "${HOME}/.local"
cd "${HOME}/.local"

git clone https://github.com/zsh-users/zsh-syntax-highlighting.git --depth=1 -b master
git clone https://github.com/zsh-users/zsh-autosuggestions.git --depth=1 -b master
git clone https://github.com/romkatv/powerlevel10k.git  --depth=1 -b master

cat << EOF >> ~/.zshrc
source "${HOME}/.local/zsh-autosuggestions/zsh-autosuggestions.zsh"
source "${HOME}/.local/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
source "${HOME}/.local/powerlevel10k/powerlevel10k.zsh-theme"
EOF

从零开始实践大模型 - 模型推理

作者 Lucien
2024年12月6日 01:08
本文地址:blog.lucien.ink/archives/550

以 Qwen2 为例,本章节将介绍在配置好环境后,如何快速启动一个模型服务,并将简单介绍面向生产的模型服务应该怎样部署。

下载模型

Qwen2.5-0.5B-Instruct 为例,因为它的尺寸很小,架构也和 Qwen2.5-72B-Instruct 一样。

在这里,我们首先将 Qwen2.5-0.5B-Instruct 模型下载至本地,而在此之前,还需要再做一些准备。

在大部分情况下我都推荐使用 git clone 的方式,而不是使用官方提供的 Toolkit,因为 git clone 出来的文件夹可以通过 git pull 来追踪模型的每一次更改,而不用重新下载。

而 git 擅长管理的是可以用纯文本来表示的文件,而对于模型权重、可执行程序、Word、Excel 等这类不太文本友好的文件来说,一般会用 git-lfs 来进行管理,在这里我们不深入了解 git-lfs 是什么,仅仅简单描述如何正确使用 git pull 来下载一个模型。

安装 git-lfs

我们前往 Releases git-lfs 页面获取 git-lfs 的下载地址:

wget 'https://github.com/git-lfs/git-lfs/releases/download/v3.5.1/git-lfs-linux-amd64-v3.5.1.tar.gz'
tar -xzvf 'git-lfs-linux-amd64-v3.5.1.tar.gz' -C /usr/local/
bash /usr/local/git-lfs-3.5.1/install.sh
git lfs install

使用 git clone 下载模型

Qwen2 目前在两个模型平台上可以下载到,一个是 HuggingFace,另一个是 ModelScope,对于大陆来说 ModelScope 是更快的。

git clone https://modelscope.cn/models/qwen/Qwen2.5-0.5B-Instruct

第一段推理代码

在这里我们使用 从零开始实践大模型 - 配置环境 中创建的 python3 环境
pip install transformers
from transformers import AutoModelForCausalLM, AutoTokenizer

device = "cuda"
model_path = "Qwen2.5-0.5B-Instruct"  # 本地模型的路径

model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype="auto").to(device)
tokenizer = AutoTokenizer.from_pretrained(model_path)

prompt = "介绍一下大模型"
messages = [{"role": "user", "content": prompt}]
text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)

generated_ids = model.generate(
    **model_inputs,
    max_new_tokens=512
)
generated_ids = [
    output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]

response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response)

如果环境配置正确,能得到类似的输出:

$ python main.py 
大模型是人工智能领域的一个重要概念。大模型是指那些能够处理和理解大量复杂数据、具有高度抽象能力和大规模特征表示的大规模预训练模型。这些模型通常使用大量的标注数据进行训练,并且可以学习到更复杂的模式和关系。

在实际应用中,大模型被广泛应用于自然语言处理、计算机视觉、语音识别、推荐系统等多个领域。例如,在自然语言处理中,大模型可以用于提高机器翻译的准确性和效率;在计算机视觉中,它们可以增强图像识别和目标检测的能力;在语音识别中,大模型可以帮助实现更加精准的人工智能对话体验。

此外,大模型还被用于生成内容的创作,如文字生成、音乐合成等。随着计算能力的提升和算法的进步,大模型在未来可能会成为推动技术进步的重要力量之一

推理加速

上述代码实现了最基本的 Hello World!,但是存在效率问题,主要体现在两点:

  1. TTFT,Time to first token,也就是首字响应时长
  2. 吞吐量,每秒的 Token 数量,单位:tokens/s

有许多优化方法能缓解这些问题,开源社区有很多推理加速的方案,比如:

还有一些面向易用性的方案,比如:

个人在这里最推荐的是 vLLM,稳定、易用、性能这几个方面的表现较为均衡,没有明显的短板。

在这里也小小的安利一下 SGLang,它的吞吐相比 vLLM 有优势,Benchmark 见:Achieving Faster Open-Source Llama3 Serving with SGLang Runtime (vs. TensorRT-LLM, vLLM)

接下来我将基于上一篇博客配置的环境来讲一讲较为便捷的部署方式,以及一些面向生产部署的经验。

Docker 部署

Deploying with docker - vLLM

基于 vLLM 的官方文档,不难写出 compose.yaml

services:
  vllm:
    image: vllm/vllm-openai:v0.6.4.post1
    volumes:
      - ${PWD}/Qwen2.5-0.5B-Instruct:/model:ro
    ports:
      - 8000:8000
    restart: always
    entrypoint: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
    command: ["--host", "0.0.0.0", "--port", "8000", "--model", "/model", "--served-model-name", "qwen2.5-0.5b-instruct"]
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["0"]
              capabilities: [gpu]

然后执行 docker compose up -d,就能快速启动一个 vLLM 的服务,通过 curl 验证:

$ curl localhost:8000/v1/chat/completions \
    -d '{"model":"qwen2.5-0.5b-instruct","messages":[{"role":"user","content":"介绍一下大模型"}]}'
    -H 'Content-Type: application/json'

可以得到类似的输出:

{"id":"chatcmpl-f1114b6c0184443da2efb51758c77bfe","object":"chat.completion","created":1733416711,"model":"qwen2.5-0.5b-instruct","choices":[{"index":0,"message":{"role":"assistant","content":"大模型通常指的是在自然语言处理(NLP)、计算机视觉等领域中,具有大量参数的深度学习模型。这些模型通过在大规模数据集上进行训练,能够学习到丰富的表示能力,从而在各种任务上表现出色。大模型的概念逐渐成为人工智能研究和应用的一个重要方向。下面是一些关于大模型的关键点:\n\n1. **参数量大**:大模型通常包含数亿甚至上百亿的参数,这使得模型能够捕捉到数据中的复杂模式和关系。\n\n2. **预训练与微调**:大模型通常首先在大规模语料库上进行预训练,然后在特定任务的数据集上进行微调,以适应具体的应用场景。\n\n3. **零样本学习与少样本学习能力**:得益于其强大的泛化能力,大模型在没有或仅有少量标注数据的情况下,也能表现得很出色。\n\n4. **提升任务性能**:在多项自然语言处理和计算机视觉任务中,大模型往往能够提供比传统模型更好的性能。\n\n5. **挑战与限制**:虽然大模型在性能上有显著提升,但它们也带来了计算资源消耗大、训练时间长、模型解释性差等问题。\n\n6. **发展趋势**:随着技术的进步,研究者们正在探索更高效、更节能的训练方法,以及如何减少模型的环境影响。\n\n大模型的发展正在推动人工智能技术的进步,并在各个领域展现出巨大的潜力,但同时,如何平衡性能提升与资源消耗、如何提高模型的可解释性等问题也是当前研究的热点。","tool_calls":[]},"logprobs":null,"finish_reason":"stop","stop_reason":null}],"usage":{"prompt_tokens":32,"total_tokens":355,"completion_tokens":323,"prompt_tokens_details":null},"prompt_logprobs":null}

一些经验

  1. 监控和灾备很重要,尤其是对于一个实时业务系统来说,相比 CPU、硬盘、内存、电源,GPU 的故障率尤其高。
  2. 降低 TTFT 和提升吞吐可以同时通过增加并行度来实现(多卡推理),但超过一个甜点值提升就很微弱了,对于 72B 的模型来说,4 卡 SXM 是一个比较有显著收益的选择。
  3. 对于显卡较多或比较分散的小伙伴来说,比较建议的是通过 Kubernetes 来管理所有的显卡,会省去很多的工作量。
  4. 需要根据业务场景和模型大小在 PCIe 与 SXM 之间做出选择,如果对推理效率没有过高的要求,3090 或 A100 能满足大部分的场景。如果需要推理超长上下文或很大的模型,建议 SXM,PCIe 的卡间通信是个灾难。

从零开始实践大模型 - 配置环境

作者 Lucien
2024年6月17日 14:45
本文地址:blog.lucien.ink/archives/549

本文将介绍在面向深度学习时,推荐的环境配置以及一些使用 Linux 的习惯。

本文的部分内容与 Debian 下 CUDA 生产环境配置笔记 有所重叠,但也有些许的不一样,在正文中不额外注明。

前言

本文将主要分 4 部分:

  1. 配置 SSH 登陆
  2. 安装显卡驱动
  3. 安装 Docker 并配置“Docker 显卡驱动”
  4. 切换至普通用户并安装 miniconda

配置 SSH 登陆

在安装完系统并重启之后,首先看到的是一个登陆界面,在这里输入我们在安装阶段设定好的 root 用户及密码即可。请注意,在输入密码的时候,是看不见自己输了什么、输了几个字符的。

配置用户登陆

登陆进 root 之后,在这里我们先什么都不做,先配置 root 用户的 ssh 登陆权限。在大部分的教程中都会直接在 /etc/ssh/sshd_config 中添加一行 PermitRootLogin yes,在这里笔者是及其不推荐的。

对于 root 用户来说,推荐的方式是密钥登陆,在本地用 ssh-keygen 生成一个公私钥对,将本地生成的 ~/.ssh/id_rsa.pub 拷贝至服务器的 ~/.ssh/authorized_keys 中(如果服务器中提示 ~/.ssh 不存在则执行 mkdir ~/.ssh 创建一个就好)。

在这里给出简单的命令:

mkdir -p ~/.ssh
echo 'content of your id_rsa.pub' >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

有些小伙伴会遇到如何把本地的 ~/.ssh/id_rsa.pub 弄到服务器中的问题,在这里提供 3 个解决方案:

  1. 先临时打开 PermitRootLogin yes,用 ssh 拷过去后再关掉
  2. 本地在 ~/.ssh 目录下用 python3 -m http.server 3000 起一个 HTTP 文件服务,然后去服务器上执行 wget
  3. 使用 PasteMe 来传输,在这里不赘述

基础软件

在这里使用 TUNA 的 Debian 软件源 作为 APT mirror:

cat << EOF > /etc/apt/sources.list
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware
EOF
apt update  # 更新索引
apt install curl wget screen git -y  # 常用软件

安装显卡驱动

软件依赖

apt update
apt install linux-headers-`uname -r` build-essential  # CUDA 驱动的依赖

禁用 Nouveau

这一步是必要的,因为 Nouveau 也是 NVIDIA GPU 的驱动程序,参考 nouveau - 维基百科

cat << EOF > /etc/modprobe.d/blacklist-nouveau.conf
blacklist nouveau
options nouveau modeset=0
EOF
update-initramfs -u
reboot

下载驱动

前往 Official Drivers | NVIDIA 下载显卡驱动,请注意,CUDA Toolkit 不要选 Any,否则会获得一个十分旧的驱动,会影响 nvidia docker (CUDA >= 11.6) 的安装。

对于大部分服务器来说,操作系统选 Linux 64-bit,语言推荐选 English (US)。CUDA Toolkit 笔者在这里选择 12.4 版本,得到的下载链接为:NVIDIA-Linux-x86_64-550.90.07.run,下载到服务器上即可。

在这里我额外测试了一下,对于 Linux 64-bit 来说,不论是消费卡(RTX 4090、RTX 3090),还是面向数据中心的卡(H100、A100、V100、P4),驱动是一模一样的。
wget 'https://us.download.nvidia.com/tesla/550.90.07/NVIDIA-Linux-x86_64-550.90.07.run'

安装驱动

chmod +x NVIDIA-Linux-x86_64-550.90.07.run
./NVIDIA-Linux-x86_64-550.90.07.run -s --no-questions --accept-license --disable-nouveau --no-drm

在这之后,执行 nvidia-smi -L 应该能看到如下内容:

$ nvidia-smi -L
GPU 0: Tesla P4 (UUID: GPU-***)
GPU 1: Tesla P4 (UUID: GPU-***)

显卡常驻

nvidia-persistenced 常驻

默认情况下,nvidia-smi 执行起来会很慢,它的等待时长会随着显卡数量的增加而增加。这是因为常驻模式(Persistence Mode)没有打开,对于服务器来说,强烈建议打开这一选项。

可以通过添加一个 启动项来保持常驻模式打开:

cat <<EOF >> /etc/systemd/system/nvidia-persistenced.service
[Unit]
Description=NVIDIA Persistence Daemon
Before=docker.service
Wants=syslog.target

[Service]
Type=forking
ExecStart=/usr/bin/nvidia-persistenced
ExecStopPost=/bin/rm -rf /var/run/nvidia-persistenced

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl start nvidia-persistenced
systemctl enable nvidia-persistenced

可以通过 nvidia-smi -q -i 0 | grep Persistence 来检查某张显卡该模式的状态。

安装 NVSwtich 驱动

如果读者使用的不是 SXM 的卡,请跳过这一步,如果不明白这里是在说什么,也可以先跳过

对于 H100 SXMA100 SXM 等拥有 NVSwitch 的整机来说,需要额外安装 nvidia-fabricmanager 来启用对 NVSwitch 的支持。

前往 Index of cuda 搜索关键词 nvidia-fabricmanager 找到对应版本进行下载。

wget https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64/nvidia-fabricmanager-550_550.90.07-1_amd64.deb
dpkg -i nvidia-fabricmanager-550_550.90.07-1_amd64.deb
systemctl start nvidia-fabricmanager.service
systemctl enable nvidia-fabricmanager.service

请注意,这里的 nvidia-fabricmanager 需要与 CUDA Driver 版本匹配。

通过执行 nvidia-smi -q -i 0 | grep -i -A 2 Fabric 来验证 nvidia-fabricmanager 是否安装成功,看到 Success 代表成功。(参考资料:fabric-manager-user-guide.pdf,第 11 页)

$ nvidia-smi -q -i 0 | grep -i -A 2 Fabric
    Fabric
        State                             : Completed
        Status                            : Success

特殊情况处理

笔者曾经遇到过下载的 CUDA 驱动版本并未被 APT 中的 nvidia-fabricmanager 支持的情况,比如通过执行 apt-cache madison nvidia-fabricmanager-550 可以发现,nvidia-fabricmanager-550 只支持 550.90.07-1550.54.15-1550.54.14-1 三个版本,这种时候可通过执行 ./NVIDIA-Linux-x86_64-550.90.07.run --uninstall 来卸载 CUDA 驱动,然后重新下载支持的驱动版本。

$ apt-cache madison nvidia-fabricmanager-550
nvidia-fabricmanager-550 | 550.90.07-1 | https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64  Packages
nvidia-fabricmanager-550 | 550.54.15-1 | https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64  Packages
nvidia-fabricmanager-550 | 550.54.14-1 | https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64  Packages

安装 Docker

Docker CE 软件仓库
export DOWNLOAD_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
wget -O- https://get.docker.com/ | sh

令 Docker 能够调用显卡

Installing the NVIDIA Container Toolkit
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
apt update
apt install -y nvidia-container-toolkit
nvidia-ctk runtime configure --runtime=docker  # 这一步会修改 /etc/docker/daemon.json
systemctl restart docker

测试:

如果网络不通的话,在镜像名前面添加 hub.uuuadc.top 以使用代理:hub.uuuadc.top/nvidia/cuda:11.6.2-base-ubuntu20.04
docker run --rm --gpus all nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi

如果能看到 nvidia-smi 的内容,则代表安装成功了。

赋予普通用户权限

让普通用户使用 docker 有两种方案:

  1. rootless: 新建一个隔离的 docker 环境(类似 conda 的多环境)
  2. root: 赋予直接操作 root docker 的权限

Docker rootless mode

Rootless mode | Docker Docs

在这里假设我们要用的用户名为 foo。对于 rootless 而言,需要先用 root 做一些配置:

apt-get install uidmap -y  # 依赖
loginctl enable-linger foo  # 允许 docker 在 foo 用户退出登录后继续运行
nvidia-ctk config --set nvidia-container-cli.no-cgroups --in-place  # 避免潜在的权限问题,经过实测这条不是必要的

随后切换到 foo 用户,执行以下命令:

dockerd-rootless-setuptool.sh install  # 配置
echo "export DOCKER_HOST=unix:///run/user/${UID}/docker.sock" >> ~/.bashrc  # 添加环境变量
nvidia-ctk runtime configure --runtime=docker --config=$HOME/.config/docker/daemon.json  # 给予 CUDA 权限
systemctl --user restart docker

到此,foo 这个用户就能开始使用 docker 了。值得注意的是,rootless 模式下的镜像、容器都是独立的,即便是 root 用户也看不到 foo 究竟有哪些镜像与容器

Docker root mode

直接用 root 用户执行:

usermod -aG docker foo

然后重新登陆 foo 用户,就能开始使用 docker 了。值得注意的是,此时在 docker 内部创建的所有文件都归属于 root 用户。除非显式指定 docker 中的文件权限归属。

普通用户安装 conda 环境

首先登陆 foo 用户,随后我们从 Miniconda 下载 Miniconda: Miniconda3 Linux 64-bit

wget 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh'
bash Miniconda3-latest-Linux-x86_64.sh -b -p ${HOME}/.local/miniconda3
${HOME}/.local/miniconda3/bin/conda init

配置 conda 镜像

Anaconda 镜像使用帮助
mv "${HOME}/.local/miniconda3/.condarc" "${HOME}/.local/miniconda3/.condarc.bak"
cat << EOF > ~/.condarc
channels:
  - defaults
show_channel_urls: true
default_channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
  conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
EOF

重新登陆以后可以看到,已经有 conda 环境了。

配置 pypi 镜像

PyPI 镜像使用帮助
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip3 config set global.trusted-host pypi.tuna.tsinghua.edu.cn  # 当使用 http 或自签证书时需要这个配置

验证 Python 对 CUDA 的调用

在这里笔者也不推荐直接使用 base 环境,我们新建一个环境:

conda create -n python3 python=3.12
conda config --set auto_activate_base false  # 默认不激活 base 环境
echo 'conda activate python3' >> ~/.bashrc  # 默认激活 python3 环境

重新登录后可看到 python3 已经作为默认环境了。

我们简单下载一个 torch 来验证环境安装的正确性:

pip3 install torch numpy
python3 -c 'import torch; print(torch.tensor(0).cuda())'

尾声

建议

在这里再多啰嗦几句,希望能让后辈们少走些弯路:

  1. 任何行为,不论是安装软件、配环境、写代码还是一些系统操作,都应该将影响降低至 最小范围。比如将 nvcc、gcc 装至用户、环境级别而不是直接用 root 安装。
  2. 除了本章节的内容,在任何情况下,都不建议直接使用 root 账户进行操作,除非读者是一位对 Linux 非常熟悉的专家并且明白自己在做什么,否则会面临各种潜在的权限问题、崩溃、挖矿病毒、数据丢失等风险。
  3. 在任何情况下,都不应该操作 Linux 本身的 Python 环境,请使用 venv 或 conda
  4. 在任何情况下,都不应该随意变更宿主机的 CUDA 版本,请使用 docker
  5. 不建议在宿主机中安装 nvcc、TensoRT 等内容,据笔者观察,至少 90% 的用户他们并不明白自己在做什么,所以 请使用 conda 或 docker

备忘

  1. 安装 cudnn

    conda install conda-forge::cudnn
  2. 安装 nvcc

    conda install nvidia::cuda-nvcc
  3. 安装 gcc

    conda install conda-forge::gcc

Reference

从零开始实践大模型 - 安装系统

作者 Lucien
2024年6月16日 23:29
本文地址:blog.lucien.ink/archives/548

本章节将介绍在面向深度学习时,推荐安装的系统以及对应的安装选项。

系统选择

目前主流操作系统有 Linux、macOS、Winodws,如果不考虑日常当作个人电脑来使用的话,强烈建议使用 无图形化界面 的 Linux,因为图形化界面会占用一定的显存(虽然也有不占用显存且同样拥有图形化的方法,这不在本文的讨论范围)。

接下来就是 Linux 发行版的选择,大部分企业(包括 NVIDIA 自己)会选择 Ubuntu,因为内置的东西多,笔者在这里不选择 Ubuntu,也是因为它内置的东西太多了,比如 snap 和 systemd-resolved。

基于笔者的实践经验,推荐使用 Debian 作为操作系统,因为它足够精简,而基于这般精简,也不会在后续使用上产生任何额外的复杂,且行为都足够可控,故在本文包括后续的一系列文章中,都会使用 Debian 作为演示操作系统。

下载安装包

在这里特意注明下 Debian 的下载地址,以免大家被百度的广告误导:Installing Debian via the Internet

安装系统

启动页面

Installer menu

语言

在这里 强烈不推荐 选择中文,除非你准备好应对各种因中文字符而产生的问题。

Select a language

地区

美国或中国都可以,这会影响到安装完成后的时区:

  • 可以在这里先选择 United States 然后进入系统后再更改。
  • 也可以直接去 other 里找 Asia 然后 China

Select location other
Select Region
Select territory or area

编码

一律选 en_US.UTF-8,可以规避很多潜在的问题。

Configure locales

键盘布局

Configure the keyboard

主机名 & 域名

如果只是单台服务器的话,这里随便填就好。
如果打算组建集群,这里就直接起个 node-0 之类的遍于自己区分的名字就好。

The hostname for this system
Domain name

设定用户 & 密码

Set up root password
Set up normal user

硬盘分区

推荐直接用一整块硬盘,不启用 LVM 和加密。

Partition disks
Select disk
Partition Scheme
Disk_part Checkout
Verify disk part

配置 apt

在这里选择国内的镜像,否则会很慢。

Extra media
APT China
APT Tuna mirror
APT HTTP Proxy

是否参与数据采集

Survey

选择预装软件

在这里只选择 SSH 和基础工具就好,没有特殊需求不建议勾选图形化界面(Debian desktop environment)。

Software Selection

安装引导

GRUB
Choose Grub Device

重启进入系统

Complete
Bootloader

自建 Docker 镜像源

作者 Lucien
2024年6月9日 22:12
本文地址:blog.lucien.ink/archives/547
本文主要参考自:自建Docker 镜像/源加速的方法

1. 简介

最近 Docker Hub 被禁一事引起了不小的波动,在这里简单讲下在这之后应该如何访问公开的 Docker Hub。

2. Cloudflare

2.1 搭建

搭建的前提是有一个在 Cloudflare 中被管理的域名,此处不展开介绍,在这里假设这个域名是 your-domain.com

2.1.1 创建 Worker

点击页面左侧的 Workers & Pages,创建一个 Worker,填入以下内容。请注意将 your-domain.com 替换为你自己的域名。

'use strict'

const hub_host = 'registry-1.docker.io'
const auth_url = 'https://auth.docker.io'
const workers_url = 'https://your-domain.com'
/**
 * static files (404.html, sw.js, conf.js)
 */

/** @type {RequestInit} */
const PREFLIGHT_INIT = {
    status: 204,
    headers: new Headers({
        'access-control-allow-origin': '*',
        'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS',
        'access-control-max-age': '1728000',
    }),
}

/**
 * @param {any} body
 * @param {number} status
 * @param {Object<string, string>} headers
 */
function makeRes(body, status = 200, headers = {}) {
    headers['access-control-allow-origin'] = '*'
    return new Response(body, {status, headers})
}


/**
 * @param {string} urlStr
 */
function newUrl(urlStr) {
    try {
        return new URL(urlStr)
    } catch (err) {
        return null
    }
}


addEventListener('fetch', e => {
    const ret = fetchHandler(e)
        .catch(err => makeRes('cfworker error:\n' + err.stack, 502))
    e.respondWith(ret)
})


/**
 * @param {FetchEvent} e
 */
async function fetchHandler(e) {
    const getReqHeader = (key) => e.request.headers.get(key);
    let url = new URL(e.request.url);
    if (url.pathname === '/token') {
        let token_parameter = {
            headers: {
                'Host': 'auth.docker.io',
                'User-Agent': getReqHeader("User-Agent"),
                'Accept': getReqHeader("Accept"),
                'Accept-Language': getReqHeader("Accept-Language"),
                'Accept-Encoding': getReqHeader("Accept-Encoding"),
                'Connection': 'keep-alive',
                'Cache-Control': 'max-age=0'
            }
        };
        let token_url = auth_url + url.pathname + url.search
        return fetch(new Request(token_url, e.request), token_parameter)
    }

    url.hostname = hub_host;

    let parameter = {
        headers: {
            'Host': hub_host,
            'User-Agent': getReqHeader("User-Agent"),
            'Accept': getReqHeader("Accept"),
            'Accept-Language': getReqHeader("Accept-Language"),
            'Accept-Encoding': getReqHeader("Accept-Encoding"),
            'Connection': 'keep-alive',
            'Cache-Control': 'max-age=0'
        },
        cacheTtl: 3600
    };

    if (e.request.headers.has("Authorization")) {
        parameter.headers.Authorization = getReqHeader("Authorization");
    }

    let original_response = await fetch(new Request(url, e.request), parameter)
    let original_response_clone = original_response.clone();
    let original_text = original_response_clone.body;
    let response_headers = original_response.headers;
    let new_response_headers = new Headers(response_headers);
    let status = original_response.status;

    if (new_response_headers.get("Www-Authenticate")) {
        let auth = new_response_headers.get("Www-Authenticate");
        let re = new RegExp(auth_url, 'g');
        new_response_headers.set("Www-Authenticate", response_headers.get("Www-Authenticate").replace(re, workers_url));
    }

    if (new_response_headers.get("Location")) {
        return httpHandler(e.request, new_response_headers.get("Location"))
    }

    return new Response(original_text, {
        status,
        headers: new_response_headers
    })
}

/**
 * @param {Request} req
 * @param {string} pathname
 */
function httpHandler(req, pathname) {
    const reqHdrRaw = req.headers
    // preflight
    if (req.method === 'OPTIONS' &&
        reqHdrRaw.has('access-control-request-headers')
    ) {
        return new Response(null, PREFLIGHT_INIT)
    }
    let rawLen = ''
    const reqHdrNew = new Headers(reqHdrRaw)
    const refer = reqHdrNew.get('referer')
    let urlStr = pathname
    const urlObj = newUrl(urlStr)
    /** @type {RequestInit} */
    const reqInit = {
        method: req.method,
        headers: reqHdrNew,
        redirect: 'follow',
        body: req.body
    }
    return proxy(urlObj, reqInit, rawLen, 0)
}


/**
 *
 * @param {URL} urlObj
 * @param {RequestInit} reqInit
 */
async function proxy(urlObj, reqInit, rawLen) {
    const res = await fetch(urlObj.href, reqInit)
    const resHdrOld = res.headers
    const resHdrNew = new Headers(resHdrOld)

    // verify
    if (rawLen) {
        const newLen = resHdrOld.get('content-length') || ''
        const badLen = (rawLen !== newLen)

        if (badLen) {
            return makeRes(res.body, 400, {
                '--error': `bad len: ${newLen}, except: ${rawLen}`,
                'access-control-expose-headers': '--error',
            })
        }
    }
    const status = res.status
    resHdrNew.set('access-control-expose-headers', '*')
    resHdrNew.set('access-control-allow-origin', '*')
    resHdrNew.set('Cache-Control', 'max-age=1500')

    resHdrNew.delete('content-security-policy')
    resHdrNew.delete('content-security-policy-report-only')
    resHdrNew.delete('clear-site-data')

    return new Response(res.body, {
        status,
        headers: resHdrNew
    })
}

2.1.2 添加域名

进入创建好的 Worker 的配置页面,在 Settings Tab 中选择 Triggers,点击 Add Custom Domain,添加 your-domain.com

2.2 使用

2.2.1 配置为镜像

/etc/docker/daemon.json 加入以下内容:

{
  "registry-mirrors": [
    "https://your-domain.com"
  ]
}

然后重启 docker:systemctl restart docker

随后就能像往常一样直接 pull 了:

docker pull busybox:latest
docker pull mysql/mysql-server:latest

2.2.2 直接使用

docker pull your-domain.com/library/busybox:latest
docker pull your-domain.com/mysql/mysql-server:latest

3. 使用 registry

首先你需要一个能正常访问 Docker Hub 的机器,并在那台机器上正常安装 Docker。

3.1 搭建

找一个文件夹,编辑 compose.yml 文件,填入以下内容:

services:
  registry:
    image: registry:2
    ports:
      - "5000:5000"
    environment:
      REGISTRY_PROXY_REMOTEURL: https://registry-1.docker.io  # 上游源
      REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR: inmemory # 内存缓存,去掉本行以直接使用硬盘
    volumes:
      - ./data:/var/lib/registry

然后执行 docker compose up -d 即可。

3.2 使用

使用方法同上。

使用 300 元的显卡推理 Qwen1.5-14B

作者 Lucien
2024年3月17日 23:26
本文地址:blog.lucien.ink/archives/546

一直以来模型推理成本对于想要使用大模型却又注重隐私的用户来说都是个难题,今天在这里探讨一下如何用尽可能低的成本去获得尽可能高的模型性能。

曾经尝试过用 Tesla P4(目前市场价 300 元,7.5G 显存)使用 vllm 去跑 GPTQ 量化版本的 Qwen 7B,提示显卡太旧无法支持 GPTQ,后来这张卡就被我拉去跑 Stable Diffusion 了。

现如今 Qwen1.5 问世,同时 Qwen 团队也放出了 gguf 格式的模型,从 0.5B 至 72B 都有,甚至从 q2_k 到 q8_0 都有,还很贴心的放了 模型大小量化方法 的困惑度矩阵,这令我狠狠地心动了,果断掏出我的 P4 再战一回。

在这篇文章中,我将借助 llama.cpp 采用 GPU + CPU 混合计算的形式,使用 Tesla P4 来推理 Qwen1.5-14B-Chat,支持 2048 上下文并且达到 11 tokens/s 左右的速度。

Sizefp16q8_0q6_kq5_k_mq5_0q4_k_mq4_0q3_k_mq2_k
0.5B34.2034.2234.3133.8034.0234.2736.7438.2562.14
1.8B15.9915.9915.9916.0916.0116.2216.5417.0319.99
4B13.2013.2113.2813.2413.2713.6113.4413.6715.65
7B14.2114.2414.3514.3214.1214.3514.4715.1116.57
14B10.9110.9110.9310.9810.8810.9210.9211.2412.27
72B7.977.997.997.998.018.008.018.068.63

部署

运行环境参考之前写的 Debian 下 CUDA 生产环境配置笔记,不赘述。在这里简述下电脑配置:CPU i3-12100、8G RAM DDR4、Tesla P4(7.5GB 显存)。

再次强调下,最便宜的就是这张显卡了,市场价 300 块。

下载模型文件

前往 Qwen1.5-14B-Chat-GGUF/files 选择心仪的量化模型。

在这里选择的是 qwen1_5-14b-chat-q2_k.gguf,虽然是 q2_k 量化,但其在维基百科数据集上的困惑度仍旧是小于 fp16 的 7B,所以其实际表现 可能 是要更好。

使用 docker compose 部署

LLaMA.cpp HTTP Server

这 3 个参数会直接决定模型能不能跑起来,以及跑起来的效果,在这里我直接放出适用于 Tesla P4 的参数,可以直接抄作业:

  • --n-gpu-layers 40 代表有 40 层模型将被加载进 VRAM(14B-Chat 总共有 41 层)。
  • -c 2048 代表模型总上下文长度(模型本身支持 32K,但在这里限定 2K)。
  • --batch-size 256 代表在处理 Prompt 时的 batch size,比如你的 prompt 拥有 768 个 token,那么就会被分为 3 个 batch 去 inferece。这个数值默认是 512,在这里为了节省显存,调整为 256(会拖慢服务处理 prompt 的速度)。

其它的参数官方文档写的较为清晰,有啥不明白的直接 --help 一下就行,在这里直接贴出 compose.yml 文件,不赘述。

version: "3"

services:
  qwen2-llama-cpp:
    container_name: qwen2-llama-cpp
    image: ghcr.io/ggerganov/llama.cpp:server-cuda
    volumes:
      - /mnt/nas/huggingface/model/nlp/Qwen/Qwen1.5-14B-Chat-GGUF/qwen1_5-14b-chat-q2_k.gguf:/model.gguf:ro
    command: "-m /model.gguf --host 0.0.0.0 --port 8000 --n-gpu-layers 40 -c 2048 --batch-size 256 --threads 4 --alias this_is_a_model_name --api-key this_is_a_api_key"
    shm_size: 8GB
    restart: always
    ports:
      - 8000:8000
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["0"]
              capabilities: [gpu]

调用

llama.cpp 官方支持了非常多样化的 API 调用形式,为了缩短篇幅,在这里我只列举 OpenAI API 格式的调用,因为围绕 OpenAI API 的社区产品会更加多样化一些。

from openai import OpenAI

client = OpenAI(base_url="http://192.168.1.2:8000/v1", api_key="this_is_a_api_key")

stream = client.chat.completions.create(
    model="this_is_a_model_name",
    messages=[{"role": "user", "content": "给我讲一个故事。"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)

推理速度

在这里简单测试一下推理速度:

Token 数量耗时速率
prompt167213.43 s124.46 tokens/s
predict51145.18 s11.31 tokens/s

视频:用 300 元的显卡推理 Qwen1.5-14B 效果展示

总结

本文探索了如何使用一张 300 元的显卡借助 llama.cpp 来推理 Qwen1.5-14B-Chat 的 q2_k 量化模型,获得不慢的推理速度与不俗的性能表现。

值得注意的是,可以观察到视频中在 predict 期间,GPU 计算的利用率是没有达到 100% 的,原因是因为我们将一部分计算交给了 CPU,数据交换以及 CPU 运算增大了 GPU 的数据等待时间。

解决这个问题也有几个改进点:

  1. 换用性能更强的 CPU
  2. 换用带宽更高的 RAM
  3. 将所有模型都进 VRAM
    3.1 换用更小的上下文长度,比如 1024
    3.2 换用更大的显存

从工业的角度讲,Tesla P4 可能是目前市面上能买到的 价格/显存 性价比较高的一款显卡了,8 张 P4(2400 元,60G 显存,600W 功耗)能推理精度较高的 72B 的模型,如果不在意功耗的话,配合退役的服务器能将整机成本控制在 3000 元左右,对于工业上自建推理服务还算一个不错的选择。

但其缺点也较为明显,主要来自于其年代久远,sm 版本较低、不支持 GPTQ 等依赖新 op 的 方法、不支持 bf16 等等。得益于 llama.cpp 这样的项目存在,这张卡才能在现如今的深度学习环境下焕发新春。

相关资料

  1. llama.cpp
  2. Debian 下 CUDA 生产环境配置笔记
  3. modelscope.cn/qwen/Qwen1.5-14B-Chat-GGUF

WireGuard 配置备忘

作者 Lucien
2024年3月5日 00:23
本文地址:blog.lucien.ink/archives/545

最近学会了用 WireGuard 来打洞,在此记录一下以备忘。

概述

cd /etc/wireguard
wg genkey > private && chmod 600 private
wg pubkey < private > public && chmod 600 public

将配置文件写在 /etc/wireguard/${name}.conf 中,可全局通过 wg-quick [up/down] ${name} 来进行启停,配置文档详见:Quick Start - WireGuard

如果需要作为常驻服务,更推荐使用 systemctl [enable/disable/start/stop] wg-quick@${name}.service 来进行管理。

懒人脚本

在服务端,我们执行:

#!/usr/bin/env sh
CONFIG_NAME="wg"

SERVER_PRIVATE_KEY="$(cat private)"
SERVER_INTERNAL_IP="192.168.1.2"
SERVER_PORT="10086"

CLIENT_PUBLIC_KEY="this_is_a_public_key_copy_from_your_client"
CLIENT_INTERNAL_IP="192.168.1.3"

cat << EOF > "/etc/wireguard/${CONFIG_NAME}.conf"
[Interface]
PrivateKey = ${SERVER_PRIVATE_KEY}
Address = ${SERVER_INTERNAL_IP}/24
ListenPort = ${SERVER_PORT}

[Peer]
PublicKey = ${CLIENT_PUBLIC_KEY}
AllowedIPs = ${CLIENT_INTERNAL_IP}/24
PersistentKeepalive = 25
EOF

systemctl enable "wg-quick@${CONFIG_NAME}.service"
systemctl start "wg-quick@${CONFIG_NAME}.service"

在客户端,我们执行:

#!/usr/bin/env sh
CONFIG_NAME="wg"

SERVER_PUBLIC_IP="server_public_ip_or_domain"
SERVER_PORT="10086"
SERVER_PUBLIC_KEY="this_is_a_public_key_copy_from_your_server"
SERVER_INTERNAL_IP="192.168.1.2"

CLIENT_PRIVATE_KEY="$(cat private)"
CLIENT_INTERNAL_IP="192.168.1.3"

cat << EOF > "/etc/wireguard/${CONFIG_NAME}.conf"
[Interface]
PrivateKey = ${CLIENT_PRIVATE_KEY}
Address = ${CLIENT_INTERNAL_IP}/24

# aliyun
[Peer]
Endpoint = ${SERVER_PUBLIC_IP}:${SERVER_PORT}
PublicKey = ${SERVER_PUBLIC_KEY}
AllowedIPs = ${SERVER_INTERNAL_IP}/24
PersistentKeepalive = 25
EOF

systemctl enable "wg-quick@${CONFIG_NAME}.service"
systemctl start "wg-quick@${CONFIG_NAME}.service"

Reference

各种 mirror 备忘

作者 Lucien
2023年10月21日 17:54
本文地址:blog.lucien.ink/archives/544

Pip

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

Conda

channels:
  - defaults
show_channel_urls: true
default_channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
  conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  pytorch-lts: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
  deepmodeling: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/

brew

过于复杂,参考 Homebrew / Linuxbrew 镜像使用帮助Homebrew-bottles 镜像使用帮助

APT

Debian

Debian 软件仓库镜像使用帮助

Ubuntu

Ubuntu 软件仓库镜像使用帮助

docker

export DOWNLOAD_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
wget -O- https://get.docker.com/ | bash

Proxmox

Proxmox 软件仓库镜像使用帮助

ubuntu 扩容逻辑卷

作者 Lucien
2023年10月14日 00:53
本文地址:blog.lucien.ink/archives/543

Ubuntu 22.04 LTS 在安装过程中默认只使用 100G 的硬盘,执行以下命令即可扩容:

$ df -h
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv  100G   11G  89G   11% /

$ lvdisplay
  --- Logical volume ---
  LV Path                /dev/ubuntu-vg/ubuntu-lv
  LV Name                ubuntu-lv
  VG Name                ubuntu-vg
  LV Write Access        read/write
  LV Creation host, time ubuntu-server, 2023-10-11 10:53:11 +0000
  LV Status              available
  # open                 1
  LV Size                100 GB
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     512
  Block device           253:0

$ lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv

$ resize2fs /dev/ubuntu-vg/ubuntu-lv

$ df -h
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv  3.5T   11G  3.3T   1% /

nvidia-persistenced 常驻

作者 Lucien
2023年9月13日 01:30
本文地址:blog.lucien.ink/archives/542

发现每次执行 nvidia-smi 都特别慢,发现是需要 nvidia-persistenced 常驻才可以,这个并不会在安装完驱动之后自动配置,需要手动设置一个自启。

cat <<EOF >> /etc/systemd/system/nvidia-persistenced.service
[Unit]
Description=NVIDIA Persistence Daemon
Wants=syslog.target

[Service]
Type=forking
ExecStart=/usr/bin/nvidia-persistenced
ExecStopPost=/bin/rm -rf /var/run/nvidia-persistenced

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl start nvidia-persistenced
systemctl enable nvidia-persistenced

Debian 初始化命令备忘

作者 Lucien
2023年9月13日 01:21
本文地址:blog.lucien.ink/archives/541

以 Debian 11 为例,主要用于备忘。

deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free
apt update && apt install vim curl wget git cifs-utils -y

export DOWNLOAD_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
curl -fsSL https://get.docker.com/ | sh

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

hostnamectl set-hostname your_host
hostname your_host
/etc/hosts

timedatectl set-timezone Asia/Shanghai

groupmod -g 3000 foo  # 添加用户组
adduser  # 添加用户
usermod -aG group_name user_name  # 将用户添加至组

Debian 使用 systemd 自动挂载 Samba

作者 Lucien
2023年8月29日 01:11
本文地址:blog.lucien.ink/archives/540

写成了一键脚本,直接执行即可。

需要注意的是,挂载的路径和 systemd service 的名字要对应上,比如挂载的路径为 /mnt/nas 那 service 的文件名应为 mnt-nas.service

#!/usr/bin/env bash

SERVER="nas.local"
USERNAME="foo"
PASSWORD="bar"

apt install cifs-utils -y

groupadd -g 17510 nas

cat <<EOF > /etc/systemd/system/dns-ready.service
[Unit]
Description=Wait for DNS to come up using 'host'
After=nss-lookup.target

[Service]
Type=oneshot
ExecStart=/bin/bash -c "until host ${SERVER}; do sleep 1; done"

[Install]
WantedBy=multi-user.target
EOF

cat <<EOF > /etc/systemd/system/mnt-nas.mount
[Unit]
Description=Mount NAS
Requires=network-online.target
After=network-online.target dns-ready.service

[Mount]
What=//${SERVER}/Data
Where=/mnt/nas
Type=cifs
Options=username=${USERNAME},password=${PASSWORD},dir_mode=0775,gid=nas,rw,file_mode=0664

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload

systemctl start dns-ready.service
systemctl enable dns-ready.service

systemctl start mnt-nas.mount
systemctl enable mnt-nas.mount

ChatGPT 相关资料收集

作者 Lucien
2023年4月2日 23:01
本文地址:blog.lucien.ink/archives/538

本文用来收集各种和生成式模型相关的内容,由于 ChatGPT 是其代表,也是会被写入人类历史进程的一个名字,所以便用 ChatGPT 作为标题的一部分,以表示我对 OpenAI 团队由衷的敬佩。

2023-04-02 更新

暂时先收集这些,总结下来就一句话:OpenAI 不够体面,开源社区帮他体面。以及作为马后炮,我认为在对 GPT 现代化改进的加持下,于多数日常任务来说,10B 左右的规模应该是足够的。

2023-04-04 更新

2023-04-06 更新

  • Koala: A Dialogue Model for Academic Research

    • 主要是使用 EasyLM 提升了训练速度,使用 8 张 A100 完成两轮 epoch 只需要 6 个小时,大大降低了训练成本。评测效果优于 Alpaca,达到 ChatGPT 50% 的性能。

2023-04-09 更新

2023-04-22 更新

当前的开源社区大致有 3 个方向:

  1. 复现 ChatGPT 的效果
  2. 加速(模型轻量化、更底层的训练/推理加速)
  3. 应用(插件、Auto-GPT、ViedoChat、提示魔法)

2023-04-23 更新

  • Awesome ChatGPT Prompts

    • ChatGPT 中文指南 的英文版本
  • ChatGPT 学术优化

    • 科研工作专用ChatGPT/GLM拓展,特别优化学术Paper润色体验,模块化设计支持自定义快捷按钮&函数插件,支持代码块表格显示,Tex公式双显示,新增Python和C++项目剖析&自译解功能,PDF/LaTex论文翻译&总结功能,支持并行问询多种LLM模型,支持gpt-3.5/gpt-4/chatglm
  • Chat with any PDF

    • 可以将 PDF GPT 作为上下文,然后可以问他任何问题,比如摘要、理解、建议等。

OpenWRT 安装 PassWall

作者 Lucien
2023年3月16日 20:12
本文地址:blog.lucien.ink/archives/537

访问 OpenWRT Download Server,找到自己的架构,以 x86_64 为例:

  1. /etc/opkg/customfeeds.conf 中添加 src/gz openwrt_kiddin9 https://op.supes.top/packages/x86_64
  2. 注释或删掉 /etc/opkg.conf 中的 option check_signature
  3. 执行 opkg update
  4. 执行 opkg install luci-app-passwall

PVE 下解决 iKuai 断流、重启问题

作者 Lucien
2023年2月1日 01:24
本文地址:blog.lucien.ink/archives/536

0. 前言

懒得看过程可直接移步第 2 部分

之前入手了 N5105 + i225-V,收到后装了 PVE 7.2 作为底层系统,虚拟化 iKuai + OpenWRT 来做软路由。

随着 iKuai 系统的升级,逐渐发现一些问题,比如断流、频繁重启等。OpenWRT 也时不时会毫无征兆的宕机,只是不频繁。

上网搜索了很多资料,很多都是基于经验的尝试,比如说换用 32 位的 iKuai,关掉 ASPM 、ROM-Bar、NUMA,更改 MTU 等等。也有的说换用 ESXi 之后就不重启了等等。在这里就不一一列举了。

下面提供两种解决方案,一种是治标,另一种是治本。

1. 更换旧版本的 iKuai

最开始我的策略是换用低版本的 iKuai,经过实测,3.4.9 版本是很稳定的,可以前往 iKuai 历史版本下载 进行下载。

但这样的问题是,新的一些特性都没有办法享受,比如静态 DHCP 指定网关、DNS。

再加上,即便 iKuai 不崩了,OpenWRT 也还处于摇摇欲坠的状态,于是我便开始尝试治本。

2. 打补丁

我注意到当连接数增多,譬如进行 PT 下载时,iKuai 崩溃、重启的概率会大大增加,加之我在 iKuai 的更新日志里看到了关于内核的升级,于是大胆猜测本质上应该是更底层的原因,与设置无关。

费尽九牛二虎之力,终于是搜到了一位大佬的文章:PVE详细安装op、ikuai教程,含修改国内源、直通、更新内核,里面讲了很多内容,也包含了关于虚拟机重启的解决方案,在这里作个提炼。

  1. 更新内核、安装微代码更新套件
apt update
apt install pve-kernel-6.1 iucode-tool
  1. 重启
  2. 安装微代码补丁
wget http://http.us.debian.org/debian/pool/non-free/i/intel-microcode/intel-microcode_3.20221108.1_amd64.deb
dpkg -i intel-microcode/intel-microcode_3.20221108.1_amd64.deb
update-initramfs -u -k all
  1. 再次重启

执行完上述 4 个步骤,经历 2 次重启之后,补丁就算是打完了。经过实测,连接数到达 3200 时 iKuai 3.6.13 也能稳定运行,OpenWRT 也暂时没有出现重启的情况。

3. 参考资料

  1. 关于 N5105/N6005+i225/i226 系列软路由搭配 OpenWrt 在高连接数条件下崩溃/软重启的问题和解决思路汇总
  2. 进阶 - 解决 PVE 下虚拟机自动重启
  3. 爱快在PVE下不定时反复重启死机的解决方法
  4. PVE详细安装op、ikuai教程,含修改国内源、直通、更新内核
  5. iKuai 历史版本下载

OpenWRT 扩容

作者 Lucien
2022年12月29日 16:15
本文地址:blog.lucien.ink/archives/535

官网原生的 overlay 只有 100M,不够用。本文只讨论新安装的情形,已安装扩容的场景在本文不涉及。

步骤

假设从官网下载的文件名为 openwrt-22.03.2-x86-64-generic-squashfs-combined.img.gz

# 解压
gzip -kd openwrt-22.03.2-x86-64-generic-squashfs-combined.img.gz
# 重命名,方便操作
mv openwrt-22.03.2-x86-64-generic-squashfs-combined.img op.img
# 在尾部增加 4096M
dd if=/dev/zero bs=1M count=4096 >> op.img
# 进入分区工具
parted op.img

parted 的 console 里执行:

print # 打印当前分区
resizepart 2 100% # 将剩余空间分配给分区 2
print # 再次打印分区
qiut # 退出

参考文章

  1. 软路由探索之旅 篇三:给openwrt扩容overlay 3月30日更新

Debian 下 CUDA 生产环境配置笔记

作者 Lucien
2022年12月29日 16:11

最近整了张 Tesla P4,由于是半高卡,索性就直接将其塞进了我的 NAS 里,试图将原来用 onnx 跑在 CPU 上的模型迁移至 GPU 上,遇到了些许问题,在此记录下。

本文地址:blog.lucien.ink/archives/534

0. 前言

由于是用于生产环境,我的所有服务(包含本次要迁移的模型)都是容器化运行的,所以我这次也是考虑用 NVIDIA Docker 2 来进行生产部署。

我的操作系统是 Debian 11 x64,运行在 i3-12100 上。

对本篇文章的读者,我在这里假定你已装好 Docker,若没有,可以参考 Docker 入门手记

1. 环境部署

1.1 下载驱动

前往 Official Drivers | NVIDIA 下载 Tesla P4 的驱动,CUDA Toolkit 记得不要选 Any,否则会给你一个十分旧的驱动,会影响 nvidia docker (CUDA >= 11.6) 的安装。

由于实际运行是在 docker 内,所以在稳定的前提下,宿主机的 CUDA 版本是越新越好,在这里我选择 CUDA 12.0 版本。

1.2 系统准备

1.2.1 禁用 Nouveau

这一步是必要的,因为 Nouveau 也是 NVIDIA GPU 的驱动程序,参考 nouveau - 維基百科

  1. 创建文件
touch /etc/modprobe.d/blacklist-nouveau.conf
  1. 在文件中写入以下内容:
blacklist nouveau
options nouveau modeset=0
  1. 重新生成 kernel initramfs
update-initramfs -u
  1. 重启
reboot

1.2.2 安装编译环境

参考 Nvidia unable to find kernel source tree
apt install linux-headers-`uname -r` build-essential

1.3 安装驱动

直接 chmod +x 然后运行驱动安装程序即可,不赘述。

1.4 安装 NVIDIA Docker

据我观察,NVIDIA Docker 2 类似是 Docker 的一个插件,让原生的 Docker 可以调用 NVIDIA Driver。

1.4.1 添加源

distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
      && curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
      && curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
            sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
            tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

1.4.2 安装

apt update
apt install -y nvidia-docker2
systemctl restart docker

1.4.3 测试

docker run --rm --gpus all nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi

如果能看到 nvidia-smi 的内容,则代表安装成功了。

3. 参考文档

Windows 删除恢复分区

作者 Lucien
2022年12月2日 00:57

Windows 删除恢复分区

本文地址:blog.lucien.ink/archives/533

目前不论是 Windows 10 还是 Windows 11,在安装完成后在 C 盘的后面都会有一个恢复分区,在虚拟机场景下,需要对硬盘进行扩容时,这个恢复分区会使得分区不连续,导致无法直接将容量扩展至 C 盘中,所以需要在扩容前删掉这个分区。

什么是恢复分区

在查看了一些资料后,用一句话来讲,就是个 Windows PE,可被 Windows 引导镜像所代替。

以及在查询了 微软社区 的后,我得出的结论是:“删除这个分区不会有任何影响”。

怎么删除

很多小伙伴进入到系统自带的 磁盘管理 中会发现这个恢复分区是受保护的,没有办法直接用 GUI 的形式删除。

在查阅了资料后,按照 这篇文章 可以成功地删掉这个分区,步骤如下:

  1. 右键点击 开始 菜单,并选择 Windows Powershell(管理员)
  2. 输入 diskpart,然后输入 list disk
  3. 输入 select disk ## 是恢复分区所在硬盘的编号)
  4. 输入 list partition
  5. 输入 select partition ## 是恢复分区所在分区的编号)
  6. 输入 delete partition override

执行完以上步骤之后,回到 磁盘管理 中,你会发现恢复分区已经被成功删除。

❌
❌