古めの環境ですが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)あたりを入手すれば高速化も狙えそうです。