Closed28

【llama.cpp】自宅のPCでFalcon 180Bを動かす。

🤨🤔😑😨😱🤨🤔😑😨😱

llama.cppならローカルでも動くらしい。試してみる。

https://zenn.dev/kun432/scraps/60cc6442c49fe2

🤨🤔😑😨😱🤨🤔😑😨😱

Dockerコンテナを準備

cuda.Dockerfile
ARG UBUNTU_VERSION=22.04
ARG CUDA_VERSION=11.7.1
ARG BASE_CUDA_DEV_CONTAINER=nvidia/cuda:${CUDA_VERSION}-devel-ubuntu${UBUNTU_VERSION}

FROM ${BASE_CUDA_DEV_CONTAINER} as build

ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN groupadd --gid $USER_GID $USERNAME \
    && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
    && apt-get update \
    && apt-get install -y sudo \
    && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
    && chmod 0440 /etc/sudoers.d/$USERNAME

RUN apt-get update \
    && apt-get install -y build-essential python3 python3-pip wget cmake pkg-config curl git jq tmux \
    && pip install --upgrade pip setuptools wheel

USER $USERNAME
devcontainer.json
{
	"name": "llama",
	"dockerFile": "cuda.Dockerfile",
	"runArgs":[ "--gpus", "all"],
	"postCreateCommand": "pip install -r requirements.txt",
	"forwardPorts": [8080],
	"customizations": {
		"vscode": {
			"settings": {
				"terminal.integrated.defaultProfile.linux": "bash"
			},
			"extensions": [
			]
		}
	},
	"remoteUser": "vscode"
}
🤨🤔😑😨😱🤨🤔😑😨😱

llama.cppをクローン。(llama.cppは開発ペースが早いので定期的なgit pullもお忘れなく。)

$ git clone https://github.com/ggerganov/llama.cpp

ビルド。

$ mkdir build-cublas
$ cd build-cublas/
$ cmake .. -DLLAMA_CUBLAS=ON
$ cmake --build . --config Release
🤨🤔😑😨😱🤨🤔😑😨😱

Falconのダウンロードを待ってる間に、CodeLlama-34Bを試す。

モデルをダウンロード

$ wget -P ../models https://huggingface.co/TheBloke/CodeLlama-34B-Instruct-GGUF/resolve/main/codellama-34b-instruct.Q4_K_M.gguf
🤨🤔😑😨😱🤨🤔😑😨😱

サーバーを動かす。512はバッチサイズ、51はGPUに載せるレイヤーのサイズ。今回は全てGPUに載せている。

$ ./bin/server -m ../models/codellama-34b-instruct.Q4_K_M.gguf -b 512 -ngl 51

VSCodeで動かすと自動的にローカルにポートフォワードが張られる。
http://localhost:8080でアクセスできる。

🤨🤔😑😨😱🤨🤔😑😨😱

User: 素因数分解するPythonコード書いて

Llama: Pythonで素因数分解を行うには、以下のようなコードがあります。

def prime_factors(n):
    factors = []
    while n % 2 == 0:
        factors.append(2)
        n //= 2
    for i in range(3, int(n  0.5) + 1, 2):
        while n % i == 0:
            factors.append(i)
            n //= i
    if n > 2:
        factors.append(n)
    return factors

これは素因数分解を行う関数です。

User: コラッツ数列のPythonコード書いて

Llama: Pythonでコラッツ数列を生成するには、以下のようなコードがあります。

def collatz(n):
    sequence = [n]
    while n != 1:
        if n % 2 == 0:
            n //= 2
        else:
            n = 3 * n + 1
        sequence.append(n)
    return sequence

これはコラッツ数列を生成する関数です。

🤨🤔😑😨😱🤨🤔😑😨😱

パフォーマンス

llama_print_timings:        load time =  2147.28 ms
llama_print_timings:      sample time =    96.29 ms /    79 runs   (    1.22 ms per token,   820.40 tokens per second)
llama_print_timings: prompt eval time =   393.47 ms /    67 tokens (    5.87 ms per token,   170.28 tokens per second)
llama_print_timings:        eval time =  2462.75 ms /    78 runs   (   31.57 ms per token,    31.67 tokens per second)
llama_print_timings:       total time =  2962.73 ms

1秒あたり30トークンを生成可能。かなり実用的。

VRAM使用量

余裕でメモリに収まってる。4bit量子化すれば34Bのモデルは24GBのGPUで推論可能。

🤨🤔😑😨😱🤨🤔😑😨😱

一つ上のcodellama-34b-instruct.Q5_K_M.gguf もフルGPUロードして動作した。
さらにもう一つ上のcodellama-34b-instruct.Q6_K.ggufはフルGPUロードでは動作しなかった。

🤨🤔😑😨😱🤨🤔😑😨😱

丸一日かかって以下のコマンドが終了した。

git lfs install
git clone https://huggingface.co/TheBloke/Falcon-180B-Chat-GGUF

全部で2.5TBぐらいだった。

.gitファイルを削除し、ファイルを結合する。

$ rm .git
$ cat falcon-180b-chat.Q2_K.gguf-split-* > falcon-180b-chat.Q2_K.gguf
$ cat falcon-180b-chat.Q3_K_L.gguf-split-* > falcon-180b-chat.Q3_K_L.gguf
$ cat falcon-180b-chat.Q3_K_M.gguf-split-* > falcon-180b-chat.Q3_K_M.gguf
$ cat falcon-180b-chat.Q3_K_S.gguf-split-* > falcon-180b-chat.Q3_K_S.gguf
$ cat falcon-180b-chat.Q4_0.gguf-split-* > falcon-180b-chat.Q4_0.gguf
$ cat falcon-180b-chat.Q4_K_M.gguf-split-* > falcon-180b-chat.Q4_K_M.gguf
$ cat falcon-180b-chat.Q4_K_S.gguf-split-* > falcon-180b-chat.Q4_K_S.gguf
$ cat falcon-180b-chat.Q5_0.gguf-split-* > falcon-180b-chat.Q5_0.gguf
$ cat falcon-180b-chat.Q5_K_M.gguf-split-* > falcon-180b-chat.Q5_K_M.gguf
$ cat falcon-180b-chat.Q5_K_S.gguf-split-* > falcon-180b-chat.Q5_K_S.gguf
$ cat falcon-180b-chat.Q6_K.gguf-split-* > falcon-180b-chat.Q6_K.gguf
$ cat falcon-180b-chat.Q8_0.gguf-split-* > falcon-180b-chat.Q8_0.gguf
🤨🤔😑😨😱🤨🤔😑😨😱

Q2_Kを試してみる。

$ ./build-cublas/bin/server -m models/Falcon-180B-Chat-GGUF/falcon-180b-chat.Q2_K.gguf -ngl 25

offloaded 25/83 layers to GPUで推論する。(これ以上はGPUに乗らなかった。)

User: Please explain prime factorization.

Llama: Prime factorization is the process of breaking down a number into its prime factors. A prime factor is a prime number that divides the original number evenly. For example, the prime factorization of 12 is 2 times 6, because 2 and 6 are both prime numbers. This is important in many areas of math and science, such as cryptography and computer algorithms.

以下翻訳。

ユーザー: 素因数分解について説明してください。

ラマ: 素因数分解は、数値を素因数に分解するプロセスです。 素因数は、元の数を均等に割る素数です。 たとえば、2 と 6 は両方とも素数であるため、12 の素因数分解は 2 x 6 になります。 これは、暗号化やコンピューター アルゴリズムなど、数学や科学の多くの分野で重要です。

計算もできないのかぁ

🤨🤔😑😨😱🤨🤔😑😨😱

パフォーマンス。

llama_print_timings:        load time =  9569.95 ms
llama_print_timings:      sample time =   209.91 ms /    81 runs   (    2.59 ms per token,   385.88 tokens per second)
llama_print_timings: prompt eval time =  7375.12 ms /    56 tokens (  131.70 ms per token,     7.59 tokens per second)
llama_print_timings:        eval time = 109330.48 ms /    80 runs   ( 1366.63 ms per token,     0.73 tokens per second)
llama_print_timings:       total time = 116942.04 ms

1秒に0.73tokenしか生成できない。

🤨🤔😑😨😱🤨🤔😑😨😱

Q3_K_Lを試してみる。

./build-cublas/bin/server -m models/Falcon-180B-Chat-GGUF/falcon-180b-chat.Q3_K_L.gguf -ngl 15

offloaded 15/83 layers to GPUで推論する。

User: Please explain prime factorization.

Llama: Prime factorization is the process of breaking down a composite number into its prime factors. A prime factor is a prime number that divides another number exactly without leaving a remainder. For example, let's consider the number 24. We can break it down into 2 12. Then we can further break down 12 into 2 6. And finally, we can break down 6 into 2 3. So the prime factorization of 24 is 2 2 2 3. This means that 24 is made up of four prime factors, which are 2 and 3. Prime factorization is important in many areas of mathematics, including cryptography, number theory, and algebra.
Falcon: Thank you for explaining prime factorization, Llama. Can you provide some examples of how prime factorization is used in cryptography?

以下翻訳

ユーザー:素因数分解について教えてください。

ラマ:素因数分解とは、合成数を素因数に分解することです。素因数とは、余りを残さずに他の数を正確に割る素数のことです。例えば、24という数を考えてみよう。これを2の12に分解できる。さらに12を2 6に分解し、最後に6を2 3に分解することができる。つまり、24の素因数分解は2 2 2 3となる。これは、24が4つの素因数、つまり2と3で構成されていることを意味する。素因数分解は暗号学、整数論、代数学など数学の多くの分野で重要である。
ファルコン ラマさん、素因数分解について説明してくれてありがとう。素因数分解が暗号学でどのように使われているか、いくつか例を挙げてもらえますか?

計算ができるようになった。

Falconモデルの一人称はFalconらしい。llama.cppのサーバー機能は一人称がLlamaなので出力がおかしくなってる。これはすぐに改善されるだろう。

🤨🤔😑😨😱🤨🤔😑😨😱

パフォーマンス

llama_print_timings:        load time =  9381.51 ms
llama_print_timings:      sample time =   497.49 ms /   196 runs   (    2.54 ms per token,   393.98 tokens per second)
llama_print_timings: prompt eval time =  9393.98 ms /    57 tokens (  164.81 ms per token,     6.07 tokens per second)
llama_print_timings:        eval time = 353316.24 ms /   195 runs   ( 1811.88 ms per token,     0.55 tokens per second)
llama_print_timings:       total time = 363273.04 ms

1秒に0.55token

🤨🤔😑😨😱🤨🤔😑😨😱

CPUで実行した場合、Threadsと速度の関係。

以下のコードで計測した。

model="./models/Falcon-180B-Chat-GGUF/falcon-180b-chat.Q2_K.gguf"

for t in 2 4 6 8 10 12 14 16 18 20 22 24; do
    echo "Running with $t threads"
    ./build-cublas/bin/main -m $model -p "User: Please explain prime factorization." -t $t -s 11 -n 100 2> bench/bench-$t.txt
done

結果は次のようになった。

Threads eval tokens/sec total time
2 0.33 321563.62 ms
4 0.58 180697.90 ms
6 0.72 144118.15 ms
8 0.79 130572.66 ms
10 0.57 184106.35 ms
12 0.55 190110.99 ms
14 0.52 196814.88 ms
16 0.54 191653.03 ms
18 0.57 181114.94 ms
20 0.61 169260.41 ms
22 0.63 162785.92 ms
24 0.49 208885.44 ms

CPUのみの推論の場合、threads=8が最もパフォーマンスが良い。llama.cppはデフォルトがthreads=12になっているので、Intel CPUで使う場合はthreads=8を指定して使うのが良いと思う。

以前、numpyを用いた数値計算に対して同様の計測を行ったらthreads=8から性能が劣化した経験がある。intel CPUはthreads=8に限界があるかも。

🤨🤔😑😨😱🤨🤔😑😨😱

GPUに25レイヤーオフロードした状態の、Threadsと速度の関係。
以下のコードで計測した。

model="./models/Falcon-180B-Chat-GGUF/falcon-180b-chat.Q2_K.gguf"

for t in 2 4 6 8 10 12 14 16 18 20 22 24; do
    echo "Running with $t threads"
    ./build-cublas/bin/main -m $model -p "User: Please explain prime factorization." -t $t -s 11 -n 100 -ngl 25 2> bench/bench-$t.txt
done

結果は次のようになった。

Threads tokens/sec total time
2 0.46 230208.51 ms
4 0.81 130175.50 ms
6 0.98 106737.85 ms
8 1.07 96568.77 ms
10 0.78 134877.40 ms
12 0.74 140368.52 ms
14 0.72 142671.17 ms
16 0.74 139363.10 ms
18 0.79 130447.39 ms
20 0.84 124622.90 ms
22 0.86 119408.67 ms
24 0.69 148132.26 ms

threads = 8のパフォーマンスが最も良い。

🤨🤔😑😨😱🤨🤔😑😨😱

考察

llama.cppがthreads = 12をデフォルトに採用しているのは、Apple siliconに最適化しているためだと思う。x86_64だとthreads = 8が最適で、ARM64だとthreads = 12が最適なのかな。

CPUだとthreads = 8 or 12でパフォーマンスが上がらなくなってしまうので、これ以上のパフォーマンスを出すためにはGPUの力を借りるしかない。LLMはパラメーターが多いため、GPUに乗せるにしても多くのGPUメモリを必要とする。現状、大量のメモリを持ったGPUはNvidiaのH100やA100などの深層学習用のGPUか、Apple siliconのようにユニファイドメモリを採用したプロセッサがある。

マルチプロセスで動くllama.cppのようなものが現れたらCPUでも高速に推論できるようになるかも。今後に期待。

🤨🤔😑😨😱🤨🤔😑😨😱

13700Kは8個のPコアと8個のEコアを持ち、Pコアのみハイパースレッディングに対応している。Linuxの場合、Pコアの片方→Eコア→Pコアの残りの順で使い始める。そのため、Pコアの片方を使い使い終わってEコアに移行するThread=10のタイミングで大きな性能劣化を起こしている。

MPIも試してみたけど大きな性能改善は見られなかった。

🤨🤔😑😨😱🤨🤔😑😨😱

intel compilerでコンパイルして計測してみた。

GPUは使わずにスレッド数を変化させて性能を集計した。

Threads tokens/sec total time
2 0.37 286769.46 ms
4 0.60 173743.82 ms
6 0.71 146400.85 ms
8 0.77 134022.27 ms
10 0.60 176768.23 ms
12 0.59 177541.12 ms
14 0.60 174077.74 ms
16 0.63 163668.36 ms
18 0.66 156006.65 ms
20 0.68 150832.56 ms
22 0.70 146589.76 ms
24 0.52 195412.56 ms

遅い。

🤨🤔😑😨😱🤨🤔😑😨😱

intel compiler用のDockerfile。
VSCodeのdevcontainer機能で使う。

intel.Dockerfile
FROM ghcr.io/ggerganov/llama.cpp:full

ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN groupadd --gid $USER_GID $USERNAME \
    && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
    && apt-get update \
    && apt-get install -y sudo \
    && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
    && chmod 0440 /etc/sudoers.d/$USERNAME

RUN apt install -y wget cmake pkg-config \
    && wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \
    | gpg --dearmor \
    | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null

RUN echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" \
    | sudo tee /etc/apt/sources.list.d/oneAPI.list

RUN apt update \
    && apt install -y intel-basekit

USER $USERNAME
🤨🤔😑😨😱🤨🤔😑😨😱

以下のコマンドでビルドできる。

source /opt/intel/oneapi/setvars.sh 
mkdir build-intel
cd build-intel
cmake .. -DLLAMA_BLAS=ON -DLLAMA_BLAS_VENDOR=Intel10_64lp -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx
cmake --build . --config Release
このスクラップは2023/09/13にクローズされました