以前の企画でRaspberry Pi 5の上で大規模言語モデル(LLM)を動かし、AIと会話をする実験を行いました。

その時に残された課題として生成の遅さがありました。これは推論に並列処理が苦手なCPUを使っているためで、またRaspberry Pi 5のSoCは一般的なPCに比べてCPU自体の演算能力も低いことも要因の一つです。
改善策の一つとして、前回の企画の最後にも紹介したとおりRaspberry Pi 5のPCIeレーンにGPUを外付けするという方法があります。VRAM上にニューラルネットワークを展開し、GPUを使って推論することで動作を高速化できる可能性があります。
本記事では、Raspberry Pi 5に外付けGPUを接続して、ソフトウェア上から使えるようになるまでの過程の記録を紹介します。
本記事と合わせて動画も公開しておりますので、そちらの方も併せてご覧ください。
使用機材
Raspberry Pi 5 16GBモデル
今後LLMをGPUで動かす場合はVRAM上に展開するため、8GBモデルでも問題ないかもしれません。
しかし今回はせっかくなので16GBモデルを使ってみます。


NVMeシールド

GPU
今回はRadeon RX6600XTを中古で調達しました。

NVMe-OCuLinkアダプタ
NVMeからPCIeへ、OCuLinkケーブルを経由して変換するアダプタです。

OCuLinkケーブル

ATX電源
GPU一枚が動かせればよいので、そこまで大容量である必要はありません。

PCIe用補助電源8pinが必要です。
SSDとケース


準備
GPUのセットアップをする前に、Raspberry Pi 5のSoC内蔵GPUでのベンチマークをしてみます。
$ sudo apt install glmark2
$ glmark2
832という結果が出ました。
この結果を後ほどGPUでのベンチマーク結果と比べてみます。
USBブート化
Raspberry Pi 5は背面にNVMeシールドを取り付けることでM.2 SSDからのブートが可能になりますが、外付けGPUを接続する場合、PCIeレーンがGPUに占有されるため、SSDを直接接続して起動することができなくなります。
そのためOSはMicroSDカードかUSB接続したストレージにインストールすることになりますが、速度や容量の面から考えると、SSDをUSB接続できるケースに入れて、USB接続することがよさそうです。

Raspberry Pi Imagerで通常の64bit版Raspberry Pi OSをSSDに焼き、起動させます。すでにセットアップ済みのRaspberry Pi 5から、プリインストールされているSD Card Copierを使ってシステムを丸ごとSSDにコピーすることもできます。
既にNVMeシールドからの起動環境が出来上がっている場合には、そのSSDをそのままケースに入れてUSB接続することで、USBブートを実現することができます。
私が試した限りでは、SSDによって起動できるものとできないものがありました。もしかすると、相性などの問題があるのかもしれません。前項にて紹介したSSDは、私の環境で動作を確認しています。
まず最初に/boot/firmware/config.txt
を編集します。USBブート時の電流制限の警告をスキップする設定と、PCIe3.0有効化の設定を書き込みます。
usb_max_current_enable=1
dtparam=pciex1_gen=3
EEPROMの設定を書き換えます。
rpi-eeprom-config --edit
テキストエディタnanoの画面が開きますので、以下のように設定を書き換えます。
[all]
BOOT_UART=1
POWER_OFF_ON_HALT=1
WAKE_ON_GPIO=1
BOOT_ORDER=0xf14
PCIE_PROBE=1
BOOT_ORDER
はブート優先順位の設定です。NVMe(0x6)を除外して、USB(0x1)を優先にしています。
なお、この設定はRaspberry Pi 5のボード本体上のEEPROMに書き込まれるので、OSを焼き直してもリセットされません。本体を他の用途に再利用する場合は、設定を元に戻す必要があります。
Raspberry Pi OSの設定
ウィンドウサーバの切り替え
raspi-config
でウィンドウサーバをX11に切り替えておきます。
ウィンドウサーバは、LinuxのGUIの描画を制御するシステムです。Raspberry Piにおいては、これまで古くから使われていたX11からWaylandへの移行が行われましたが、AMDのGPUを動かした場合に不安定になる部分が多かったので、X11に切り替えておきます。
Raspberry Piには標準で両方インストールされているので、簡単に切り替えることができます。
$ sudo raspi-config

[6 Advanced Options]>[A6 Wayland]>[W1 X11]を選択します。
Waylandでの成功例もインターネット上で無数に確認できているのですが、私が試した限りではうまくいきませんでした。もしWaylandのままGPUからの映像出力に成功した方がおられましたら、ぜひコメントをください。
必要なパッケージ類のインストール
セットアップの過程で必要なパッケージ類や、Vulkanのライブラリなどをインストールします。
以下のコマンドを実行します。
$ sudo apt-get update
#ビルドに必要なパッケージ
$ sudo apt-get install -y ca-certificates build-essential ccache cmake wget git curl rsync xz-utils libcap-dev bc bison flex libssl-dev make libncurses5-dev
#Vulkanライブラリ類(今回はおそらく不要)
$ sudo apt-get install -y libvulkan1 mesa-vulkan-drivers vulkan-tools libvulkan-dev glslc
#AMDのGPUのファームウェア
$ sudo apt-get install -y firmware-amd-graphics
私が試行錯誤しながら洗い出したパッケージなので、もしかすると不必要なパッケージも含まれているかもしれません。
また、もし不足があればコメントを頂ければ幸いです。
カーネルのコンパイルとインストール
Raspberry Pi OSそのままでは、外付けのGPUを使うことができません。
これは、Raspberry Pi OS標準のLinuxカーネルがGPUに対応していないためです。
そのため、サードパーティによってフォークされたLinuxカーネルのソースコードをコンパイルし、自前でインストールする必要があります。
各種GPUの対応状況はこちらをご確認ください。
また、こちらのリポジトリのIssue欄には、各種GPUへの対応可否や、それぞれのコンパイル手順、有志作成のパッチなどの情報が蓄積されています。併せてご確認ください。
リポジトリの準備
カスタムされたLinuxカーネルのリポジトリのcloneしてrpi-6.6.y-gpu
ブランチを参照します。
リポジトリが大きいので--depth=1
のオプションを付けるとよいでしょう。
$ git clone --depth=1 --branch rpi-6.6.y-gpu https://github.com/Coreforge/linux.git
設定の構成
以下のコマンドでRaspberry Pi 5のSoCであるBCM2712用のデフォルト設定を書き出してから、設定メニューを開きます。
$ cd linux/
$ KERNEL=kernel_2712
$ make bcm2712_defconfig
$ make menuconfig
メニュー内で以下の項目を設定します。必要なのは、”Fix up …”と”AMD GPU”の項目の2点で、AMD GPU以下の項目は任意です。
- Kernel Features
- [*] Fix up misaligned loads and stores from userspace for 64bit code
- Device Drivers
- Graphics support
- [M] AMD GPU
- [*] Enable amdgpu support for SI parts(任意)
- [*] Enable amdgpu support for CIK parts(任意)
- [M] AMD GPU
- Graphics support
設定が完了したら.config
という名前で保存してメニューを閉じます。
memcopy
Cのmemcpyを独自に実装したライブラリをgccでコンパイルし、インストールします。
$ wget https://gist.githubusercontent.com/Coreforge/91da3d410ec7eb0ef5bc8dee24b91359/raw/b4848d1da9fff0cfcf7b601713efac1909e408e8/memcpy_unaligned.c
$ gcc -shared -fPIC -o memcpy.so memcpy_unaligned.c
$ sudo mv memcpy.so /usr/local/lib/memcpy.so
$ sudo vi /etc/ld.so.preload
以下の内容を書き込み
/usr/local/lib/memcpy.so
カーネルのコンパイルとインストール
ソースコードのコンパイルを実行し、続けてインストールします。コンパイルには1時間程度かかります。
$ make -j6 Image.gz modules dtbs
$ sudo make modules_install
コンパイルが完了したら、必要なファイルをブート領域へコピーします。
このとき、先ほど設定した変数$KERNEL
を参照しているので、この間に再起動をしてしまった場合は変数を再設定しておきましょう。
$ sudo cp arch/arm64/boot/dts/broadcom/*.dtb /boot/firmware/
$ sudo cp arch/arm64/boot/dts/overlays/*.dtb* /boot/firmware/overlays/
$ sudo cp arch/arm64/boot/dts/overlays/README /boot/firmware/overlays/
$ sudo cp arch/arm64/boot/Image.gz /boot/firmware/$KERNEL.img
ここまでの作業が完了したら、一旦電源を落とします。
$ sudo poweroff
インストール後の設定
GPUの準備
Raspberry Pi 5のNVMeシールドにOCuLinkアダプタを取り付け、PCIeライザーと接続し、GPUを取り付けます。

念のためRaspberry Pi 5本体のMicro HDMIコネクタにモニタを接続しておきます。
ATX電源を配線して、電源とRaspberry Pi 5本体に通電します。
カーネルのコンパイルとインストールがうまくいっていれば、正常に起動するはずです。このときのデスクトップは内蔵GPUによって描画されています。
ここで正常に起動しない場合はカーネルのコンパイルとインストールに失敗している可能性があります。
Xorgの設定
ウィンドウマネージャが使うGPUを設定します。
xorg.conf.d以下の.confファイルは、ファイル名先頭の数値を読み込み順位としてグラフィックドライバの設定などを行います。
デフォルトで存在しているファイル99-v3d.confは内蔵GPUのものなので、それと衝突しない適当な番号でファイルを作成します。
$ sudo vi /etc/X11/xorg.conf.d/20-amdgpu.conf
以下のように書き込みます。
Section "Device"
Identifier "AMD GPU"
Driver "amdgpu"
EndSection
ここまで完了したら、映像出力をGPU側に差し替えて、Raspberry Pi 5を再起動します。
起動後の確認
正常にインストールと設定ができていれば、ここでGPUから映像が出力されます。

もし映像が出力されていなくても、SSHの接続が可能であればリカバリできる可能性がありますので、この後のトラブルシューティングの章を確認してください。
カーネルのモジュールが正常にロードされているか確認します。
”amdgpu”を含むカーネルモジュールがロードされていれば正常です。
$ lsmod | grep amdgpu
amdgpu 8175616 5
amdxcp 49152 1 amdgpu
drm_exec 49152 1 amdgpu
gpu_sched 98304 2 v3d,amdgpu
i2c_algo_bit 49152 1 amdgpu
drm_buddy 49152 1 amdgpu
drm_suballoc_helper 49152 1 amdgpu
drm_display_helper 196608 2 vc4,amdgpu
drm_ttm_helper 49152 1 amdgpu
ttm 114688 2 amdgpu,drm_ttm_helper
drm_kms_helper 245760 6 drm_dma_helper,vc4,drm_display_helper,amdgpu
drm 688128 19 gpu_sched,drm_kms_helper,drm_exec,drm_dma_helper,v3d,vc4,drm_suballoc_helper,drm_shmem_helper,drm_display_helper,drm_buddy,amdgpu,drm_ttm_helper,ttm,amdxcp
backlight 49152 4 drm_kms_helper,drm_display_helper,amdgpu,drm
次に、dmesgで起動時のログも確認します。
$ dmesg | grep amdgpu
[ 4.818367] [drm] amdgpu kernel modesetting enabled.
[ 4.818537] amdgpu 0000:03:00.0: enabling device (0000 -> 0002)
[ 4.865716] amdgpu 0000:03:00.0: amdgpu: Fetched VBIOS from ROM BAR
[ 4.865723] amdgpu: ATOM BIOS: 113-3E448MU-S6S
[ 4.921733] amdgpu 0000:03:00.0: amdgpu: Trusted Memory Zone (TMZ) feature disabled as experimental (default)
[ 4.921742] amdgpu 0000:03:00.0: amdgpu: PCIE atomic ops is not supported
[ 4.921812] amdgpu 0000:03:00.0: BAR 2: releasing [mem 0x1810000000-0x18101fffff 64bit pref]
[ 4.921817] amdgpu 0000:03:00.0: BAR 0: releasing [mem 0x1800000000-0x180fffffff 64bit pref]
[ 4.921865] amdgpu 0000:03:00.0: BAR 0: assigned [mem 0x1800000000-0x19ffffffff 64bit pref]
[ 4.921874] amdgpu 0000:03:00.0: BAR 2: assigned [mem 0x1a00000000-0x1a001fffff 64bit pref]
[ 4.921920] amdgpu 0000:03:00.0: amdgpu: VRAM: 8176M 0x0000008000000000 - 0x00000081FEFFFFFF (8176M used)
[ 4.921922] amdgpu 0000:03:00.0: amdgpu: GART: 512M 0x0000000000000000 - 0x000000001FFFFFFF
[ 4.921925] amdgpu 0000:03:00.0: amdgpu: AGP: 267894784M 0x0000008400000000 - 0x0000FFFFFFFFFFFF
...
起動時にカーネルが吐き出したログに、amdgpuという文字列が含まれてるエラーらしい文が一緒に出ていなければ正常です。
grepを付けずにdmesg全文をざっと見て、その他に明らかなエラーが出ていないかも確認しておきましょう。
$ sudo apt install neofetch
$ neofetch

GPU使用率などの情報を表示するツール、nvtopをインストールしておきます。
$ sudo apt install nvtop
ベンチマーク
glmark2で再びベンチマークを実行してみます。
VideoCore VII (Broadcom BCM2712内蔵) | Radeon RX6600XT | |
---|---|---|
スコア | 832 | 1821 |
2倍近くスコアが向上しています。
思ったよりも伸び幅は小さかったのですが、グラフィックの演算にしっかりGPUが使われているようです。
nvtopを実行しながら別ウィンドウでベンチマークを実行すると、GPUに負荷がかかる状況を見ることができます。
GPUを使ってみる
WebGLの実験
以下のページにアクセスして、適当なWebGLのサンプルページを開いてみます。
別ウィンドウでnvtopを開いてGPUにかかる負荷を観察します。描画するオブジェクト数が増えると負荷が増えていることが確認できるかと思います。

Super Tux Kart
オープンソースのレースゲーム、Super Tux Kartをインストールしてみました。
$ sudo apt install supertuxkart
描画もスムーズで、ストレスなく動いています。

消費電力の計測
アイドリング状態と負荷がかかった状態の消費電力を計測してみます。
オンボードGPU | RX6600XT | |
---|---|---|
アイドリング | 3.3 | 17.6 |
高負荷時 | 7.6 | 25.0 |
「高負荷時」の計測は、先述のWebGLSamplesのサイトのAquariumにて表示数を最大の30,000にした状態で行いました。
WebGLではそこまで高い負荷をかけることはできませんでしたが、本来のグラフィックボードの定格上は最大160W程度消費するはずです。
注目すべきはアイドリング時の消費電力です。一般的なグラフィックボードを搭載したデスクトップPCのアイドル状態での消費電力がおよそ100W程度ですので、それよりもかなり低い値になりました。
まとめ
今回はRaspberry Pi 5に外付けのGPUを接続してみました。
Raspberry PiのOpenGLのレンダリング性能などを向上させられることが確認できましたが、性能の伸び幅については思ったよりも小さかったという印象です。
一方で消費電力について、アイドリング時の消費電力が一般的なデスクトップPCに比べてかなり低く抑えられることがわかりました。
エッジAIデバイスとしてLLMを組み込まれる製品を想定すると、スマートホームアシスタントなど、稼働時間より待機時間のほうが長いシステムが挙げられます。そういった用途において、高い演算能力と低い待機電力を両立させられるシステムは有意義であると思います。
一方で、LLMを動かすにあたって課題もあります。
描画・演算のためのAPIとして、NVIDIAのCUDAに相当する仕組みがRadeonのROCmにあたります。しかしROCmはARM CPU向けのビルドができないので、今回の構成では実質的に使うことができません。
そのため、ROCmなどを使った描画や、LLMなどの推論は使うことができません。
一方でVulkanというAPIはARM上でも動作するため、Vulkanを使ったLLMの推論などは動作させることができるはずです。
なので次の企画では、Raspberry Pi 5と外付けGPUを使ったLLMの推論に挑戦してみたいと思います。
お楽しみに。
トラブルシューティング
セットアップ後に、うまく動作しない場合の対処法を紹介します。
GPUは認識されているか?
次に以下のコマンドで、GPUがOSに認識されているか確認します
$ lspci -nn|grep VGA
GPUが認識されていなければ、接続などを確認してください。カーネルのモジュールのロードが正常にできていなくても認識だけはするはずです。
GPUから映像が出ない
ディスプレイを検出して自動で設定する。
$ export DISPLAY=:0
$ xrandr --auto
dmesgでモジュールがロードできていない場合
エラーがある、あるいはモジュールがロードされていないために映像出力が出ていない場合は、手動でのロードを試みます。
$ sudo modprobe amdgpu
これで正常にロードできた場合、起動直後のロードが不安定になっている可能性があります。
modprobeのブラックリストにamdgpuを登録して、起動時に自動でロードされないようにすることで対応することができます。/etc/modprobe.d/blacklist-amdgpu.conf
を作成し、以下の内容を書き込みます。
blacklist amdgpu
この方法では、起動時に毎回手動でamdgpuをロードする必要があります。
映像は出力されるが動作が重い、glmark2などアプリケーションがGPUを掴めていない
ウィンドウサーバがWaylandのままになっている可能性があります。
raspi-configから設定を見直しましょう。
参考にした記事など
GIGAZINE『ついにRaspberry Pi 5にグラボを接続して画面出力に成功したので手順をまとめてみた』

かなり古い世代のGPUを使用している点に注意。Githubのraspberry-pi-pcie-devicesのリポジトリのIssueにて最新の情報を確認してください。
Githubリポジトリ『raspberry-pi-pcie-devices』Issue欄
Jeff Geerling『Use an External GPU on Raspberry Pi 5 for 4K Gaming』
Raspberry Pi Documentation『Build the kernel』
Linuxカーネルのコンパイルについてはこちらを参照。Raspberry Pi公式のドキュメント。
コメント