Rocky LinuxとRadeon RX 480でROCmを使ってLLMを動かしてみる

古めの環境ですがRyzen 7 1700とRX 480の環境があったのでROCmで機械学習ができないかと思ったので実験してみます。

環境は以下の自作PCにRocky Linux9.4をMinimumインストールしました。
CPU:Ryzen 7 1700
MB:TUF B450M-PLUS GAMING
RAM:16GB×2枚 2666 MHz
SSD:Intel 670p 1TB

Radeonのドライバをインストールするツールをインストールします。現時点(2024/6/1)の最新版はROCm6系ですが、RX480を動かす都合で5系の最新版だった5.7.3をインストールします。

# dnf install https://repo.radeon.com/amdgpu-install/5.7.3/rhel/9.2/amdgpu-install-5.7.50703-1.el9.noarch.rpm -y

EPELリポジトリとCodeReady Linux Builderリポジトリが必要なので有効にしてから、amdgpu-installを実行します。5系の対応バージョンが9.2までみたいなので、amdgpudistroをセットしておきます。
(最初は9.2をインストールしたんですが、dnf updateしたらバージョンが上がってしまいました)

# echo "9.2" > /etc/yum/vars/amdgpudistro
# dnf install epel-release -y
# crb enable
# amdgpu-install --usecase=rocm

ダウンロードに2.4G程度、ディスクも17G程度要求されたので、しばらく待ちます。

dkmsでモジュールを確認するとaddedとなっており、rocminfoで確認してもROCk module is loadedとなっているので、インストールはできたようです。

# dkms status
amdgpu/6.2.4-1697730.el9: added
# rocminfo
ROCk module is loaded
=====================
HSA System Attributes
=====================
Runtime Version:         1.1
System Timestamp Freq.:  1000.000000MHz
Sig. Max Wait Duration:  18446744073709551615 (0xFFFFFFFFFFFFFFFF) (timestamp count)
Machine Model:           LARGE
System Endianness:       LITTLE
Mwaitx:                  DISABLED
DMAbuf Support:          YES

==========
HSA Agents
==========
*******
Agent 1
*******
  Name:                    AMD Ryzen 7 1700 Eight-Core Processor
  Uuid:                    CPU-XX
  Marketing Name:          AMD Ryzen 7 1700 Eight-Core Processor
  Vendor Name:             CPU
  Feature:                 None specified
  Profile:                 FULL_PROFILE
  Float Round Mode:        NEAR
  Max Queue Number:        0(0x0)
  Queue Min Size:          0(0x0)
  Queue Max Size:          0(0x0)
  Queue Type:              MULTI
  Node:                    0
  Device Type:             CPU
  Cache Info:
    L1:                      32768(0x8000) KB
  Chip ID:                 0(0x0)
  ASIC Revision:           0(0x0)
  Cacheline Size:          64(0x40)
  Max Clock Freq. (MHz):   3000
  BDFID:                   0
  Internal Node ID:        0
  Compute Unit:            16
  SIMDs per CU:            0
  Shader Engines:          0
  Shader Arrs. per Eng.:   0
  WatchPts on Addr. Ranges:1
  Features:                None
  Pool Info:
    Pool 1
      Segment:                 GLOBAL; FLAGS: FINE GRAINED
      Size:                    32508848(0x1f00bb0) KB
      Allocatable:             TRUE
      Alloc Granule:           4KB
      Alloc Alignment:         4KB
      Accessible by all:       TRUE
    Pool 2
      Segment:                 GLOBAL; FLAGS: KERNARG, FINE GRAINED
      Size:                    32508848(0x1f00bb0) KB
      Allocatable:             TRUE
      Alloc Granule:           4KB
      Alloc Alignment:         4KB
      Accessible by all:       TRUE
    Pool 3
      Segment:                 GLOBAL; FLAGS: COARSE GRAINED
      Size:                    32508848(0x1f00bb0) KB
      Allocatable:             TRUE
      Alloc Granule:           4KB
      Alloc Alignment:         4KB
      Accessible by all:       TRUE
  ISA Info:
*******
Agent 2
*******
  Name:                    gfx803
  Uuid:                    GPU-XX
  Marketing Name:          AMD Radeon (TM) RX 480 Graphics
  Vendor Name:             AMD
  Feature:                 KERNEL_DISPATCH
  Profile:                 BASE_PROFILE
  Float Round Mode:        NEAR
  Max Queue Number:        128(0x80)
  Queue Min Size:          64(0x40)
  Queue Max Size:          131072(0x20000)
  Queue Type:              MULTI
  Node:                    1
  Device Type:             GPU
  Cache Info:
    L1:                      16(0x10) KB
  Chip ID:                 26591(0x67df)
  ASIC Revision:           1(0x1)
  Cacheline Size:          64(0x40)
  Max Clock Freq. (MHz):   1266
  BDFID:                   1792
  Internal Node ID:        1
  Compute Unit:            36
  SIMDs per CU:            4
  Shader Engines:          4
  Shader Arrs. per Eng.:   1
  WatchPts on Addr. Ranges:4
  Features:                KERNEL_DISPATCH
  Fast F16 Operation:      TRUE
  Wavefront Size:          64(0x40)
  Workgroup Max Size:      1024(0x400)
  Workgroup Max Size per Dimension:
    x                        1024(0x400)
    y                        1024(0x400)
    z                        1024(0x400)
  Max Waves Per CU:        40(0x28)
  Max Work-item Per CU:    2560(0xa00)
  Grid Max Size:           4294967295(0xffffffff)
  Grid Max Size per Dimension:
    x                        4294967295(0xffffffff)
    y                        4294967295(0xffffffff)
    z                        4294967295(0xffffffff)
  Max fbarriers/Workgrp:   32
  Packet Processor uCode:: 730
  SDMA engine uCode::      58
  IOMMU Support::          None
  Pool Info:
    Pool 1
      Segment:                 GLOBAL; FLAGS: COARSE GRAINED
      Size:                    8388608(0x800000) KB
      Allocatable:             TRUE
      Alloc Granule:           4KB
      Alloc Alignment:         4KB
      Accessible by all:       FALSE
    Pool 2
      Segment:                 GLOBAL; FLAGS:
      Size:                    8388608(0x800000) KB
      Allocatable:             TRUE
      Alloc Granule:           4KB
      Alloc Alignment:         4KB
      Accessible by all:       FALSE
    Pool 3
      Segment:                 GROUP
      Size:                    64(0x40) KB
      Allocatable:             FALSE
      Alloc Granule:           0KB
      Alloc Alignment:         0KB
      Accessible by all:       FALSE
  ISA Info:
    ISA 1
      Name:                    amdgcn-amd-amdhsa--gfx803
      Machine Models:          HSA_MACHINE_MODEL_LARGE
      Profiles:                HSA_PROFILE_BASE
      Default Rounding Mode:   NEAR
      Default Rounding Mode:   NEAR
      Fast f16:                TRUE
      Workgroup Max Size:      1024(0x400)
      Workgroup Max Size per Dimension:
        x                        1024(0x400)
        y                        1024(0x400)
        z                        1024(0x400)
      Grid Max Size:           4294967295(0xffffffff)
      Grid Max Size per Dimension:
        x                        4294967295(0xffffffff)
        y                        4294967295(0xffffffff)
        z                        4294967295(0xffffffff)
      FBarrier Max Size:       32
*** Done ***

上記の出力のAgent 2がRX480(gfx803)になります。GPUも認識できているようですね。

次にLLMを動かすために、llama.cppをコンパイルします。LLAMA_HIPBLAS=1を指定してROCmを有効にします。

# dnf install git make wget -y
# cd /work
# git clone https://github.com/ggerganov/llama.cpp
# cd /work/llama.cpp
# export CC=/opt/rocm/llvm/bin/clang
# export CXX=/opt/rocm/llvm/bin/clang++
# make LLAMA_HIPBLAS=1 -j

GGUF形式のモデルをダウンロードします。今回は以下を使わせていただきました。
https://huggingface.co/mmnga/ELYZA-japanese-Llama-2-13b-fast-instruct-gguf/
llama2の13bを量子化したモデルです。8GB弱のモデルなのでほぼGPUに乗せることができるのではないかと思いました。

# mkdir /work/model
# cd /work/model
# wget https://huggingface.co/mmnga/ELYZA-japanese-Llama-2-13b-fast-instruct-gguf/resolve/main/ELYZA-japanese-Llama-2-13b-fast-instruct-q4_K_M.gguf

41layerをGPUオフロードしようとするとOut of Memoryになって落ちたので39指定にしました。

# /work/llama.cpp/main -ngl 39 -m /work/model/ELYZA-japanese-Llama-2-13b-fast-instruct-q4_K_M.gguf -p '[INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>Pythonでmysqlからselectするサンプルを実装して[/INST]'
~~~~~~~~~~~~省略~~~~~~~~~~~~
ggml_cuda_init: GGML_CUDA_FORCE_MMQ:   no
ggml_cuda_init: CUDA_USE_TENSOR_CORES: yes
ggml_cuda_init: found 1 ROCm devices:
  Device 0: AMD Radeon (TM) RX 480 Graphics, compute capability 8.0, VMM: no
llm_load_tensors: ggml ctx size =    0.37 MiB
llm_load_tensors: offloading 39 repeating layers to GPU
llm_load_tensors: offloaded 39/41 layers to GPU
llm_load_tensors:      ROCm0 buffer size =  7090.72 MiB
llm_load_tensors:        CPU buffer size =  7585.80 MiB
~~~~~~~~~~~~省略~~~~~~~~~~~~
 [INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>Pythonでmysqlからselectするサンプルを実装して[/INST] mysql-connector-pythonをpipでインスト ールしたら、次のように実装することができます。
```
import mysql.connector

cnx = mysql.connector.connect(host='localhost', user='username', password='password')
cursor = cnx.cursor()

query = "SELECT * FROM table_name"
cursor.execute(query)

results = cursor.fetchall()

for row in results:
    print(row)
``` [end of text]

llama_print_timings:        load time =    5032.17 ms
llama_print_timings:      sample time =       4.97 ms /   102 runs   (    0.05 ms per token, 20539.67 tokens per second)
llama_print_timings: prompt eval time =   31577.39 ms /    37 tokens (  853.44 ms per token,     1.17 tokens per second)
llama_print_timings:        eval time =    8256.41 ms /   101 runs   (   81.75 ms per token,    12.23 tokens per second)
llama_print_timings:       total time =   39857.12 ms /   138 tokens
Log end

プロンプトの表示は12.23 tokens per secondなので、実用的な速度だと感じました。

Ryzen 2400Gで同様のモデルでCPUで実行した場合、3.86 tokens per secondだったので、RX480はは3倍速いです。ただ、load timeはGPUを使うケースでは長くなるようです。

せっかくなので、全部VRAMに乗せられるのが期待できる7Bのモデルも試してみます。

# cd /work/model
# wget https://huggingface.co/mmnga/ELYZA-japanese-Llama-2-7b-instruct-gguf/resolve/main/ELYZA-japanese-Llama-2-7b-instruct-q4_K_M.gguf
# /work/llama.cpp/main -ngl 33 -m /work/model/ELYZA-japanese-Llama-2-7b-instruct-q4_K_M.gguf -p '[INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>Pythonでmysqlからselectするサンプルを実装して[/INST]'
~~~~~~~~~~~~省略~~~~~~~~~~~~
ggml_cuda_init: GGML_CUDA_FORCE_MMQ:   no
ggml_cuda_init: CUDA_USE_TENSOR_CORES: yes
ggml_cuda_init: found 1 ROCm devices:
  Device 0: AMD Radeon (TM) RX 480 Graphics, compute capability 8.0, VMM: no
llm_load_tensors: ggml ctx size =    0.30 MiB
llm_load_tensors: offloading 32 repeating layers to GPU
llm_load_tensors: offloading non-repeating layers to GPU
llm_load_tensors: offloaded 33/33 layers to GPU
llm_load_tensors:      ROCm0 buffer size =  3820.94 MiB
llm_load_tensors:        CPU buffer size =    70.31 MiB
~~~~~~~~~~~~省略~~~~~~~~~~~~

 [INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>Pythonでmysqlからselectするサンプルを実装して[/INST]  ```python
import mysql.connector

# クライアントライブラリのインストール
mysql.connector/connectors/pandas-mysql.pyx

# 接続情報の設定
host = 'localhost'
username = 'root'
password = ''
dbname = 'test'

# 接続
cnx = mysql.connector.connect(host=host, user=username, passwd=password,
                                         database=dbname)

# データの取得
cursor = cnx.cursor()

# ステートメント
cursor.execute("SELECT * FROM table1")

# データを読み込む
df = pd.io.sql(cursor, index_col=None)

# クローズ
cnx.close()

# データを表示
print(df)
``` [end of text]

llama_print_timings:        load time =    3086.38 ms
llama_print_timings:      sample time =       7.60 ms /   228 runs   (    0.03 ms per token, 30000.00 tokens per second)
llama_print_timings: prompt eval time =   15435.83 ms /    61 tokens (  253.05 ms per token,     3.95 tokens per second)
llama_print_timings:        eval time =   10469.52 ms /   227 runs   (   46.12 ms per token,    21.68 tokens per second)
llama_print_timings:       total time =   25941.17 ms /   288 tokens
Log end

RX480で21.68 tokens per secondでした。WebのAI系のサービスでの速度と遜色ないです。

Ryzen 2400Gでは7.01 tokens per secondだったのでこちらも3倍ですね。
残り1layerならすべてVRAMに載せきってもそこまで実行速度には影響でないようです。

第一世代のRyzenはWindows11にアップグレードできないので、どうするかと思っていましたが、これならローカルのAIChat環境として余生が送れそうです。

RX480がFP16で5.834 TFLOPS メモリ帯域256GB/sとそこまでハイスペックではない環境でこれなので、中古で安くに入手できるVEGA56(FP16 21.09 TFLOPS メモリ帯域409.6GB/s)あたりを入手すれば高速化も狙えそうです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)