端の知識の備忘録

技術メモになりきれない、なにものか達の供養先

【続・続】鯖落ちGPUを使った安価な機械学習用マシンの作り方 ~憧れのEPYCを購入して機械学習用マシンを完全体にする編~

前記事:

hashicco.hatenablog.com

後記事:

hashicco.hatenablog.com

前書き

前記事において、P40を買い増して機械学習用マシンをマルチGPUにした。

しかし、普通のRyzenで構成していたがために、PCIeの帯域が足りず片方のGPUがx4接続になってしまっていたのが若干心残りとなっていた。

そこで、EPYCとSupermicroのマザーを中古で買ってみたという話。

まともな帯域とセッティングで2GPUを接続してベンチを取ってみたところ、そこそこマルチGPUの良さが見えてきたので本記事に記しておく。

まとめ

  • 今回購入したのは1ソケット用32Coreの初代Zen 7551Pと対応マザーSupermicro H11SSL-i、そして8x32GBのECC REGメモリのセット。
    • ebayでtugm4470という出品者から$653.60で購入できました。32コアCPU、マザー、メモリ256GBでも10万切るのは素晴らしい。もう少し円高ならなお良かったのだが……。
    • 本当はZen2にしたかったが、コアあたりの単価が倍くらいしたので今回はコスト重視で初代Zenを選択
    • 本題からずれるが、最近異常に中国からの物流が速く驚かされる。前回の2枚目のP40も5日くらいで届いたのだが、今回の商品も注文してから4日程度で届いてびっくりした。

www.ebay.com

  • 本記事の主題である、安価な機械学習用マシンに対する回答としてコストを書いておくと、2xP40, 32core 7551P, 256GB RAMというかなりリッチなマシンではあるものの、電源やSSD代含めてでも18万円程度でこのマシンは組めるはず。

    • 内訳は、CPUマザーメモリとクーラーセット: 10万、GPU: 3万x2、1000W電源: 1.5万、1TB SSD: 5000円くらいを想定。
    • まあ普通に考えればGoogle Colab (Pro)やKaggleのノートブック機能で安価or無料のリソースを有効活用する方が良いと思うが、自宅にEPYCやら256GBのメモリやらマルチなサーバー用GPUがあるのは何事にも代えがたいという同士の方であれば、自己責任で組んでみるのにも現実的な価格と言えよう。
  • H11SSL-iのようなサーバーマザーでメモリが一部認識されないような場合、PCケースのスペーサー(マウントネジ)の位置を確認すること。通常のATXマザーとネジ止めの位置が異なる場合があり、基板裏の端子がスペーサーによってショートしている可能性がある。

    • 原因が分からず小1時間格闘していたとき、Redditでそのものずばりのスレッドがあり解決。まじで助かった。
    • マザーのサイズを変えたときにスペーサーの位置を変え忘れるとかで通常の自作用マザーでも稀にこういうことはあるので注意。
  • 平積みマシンにはサーバー用マシン、というかSupermicro製マザーがとても便利なことが判明。

    • ASPEEDのグラフィック出力がついており余計に画面出力用GPUをセットアップ時に付ける必要がない
    • なによりSupermicroのIPMIという管理機能が使えるためブラウザからマシンの起動停止、温度センサー情報の確認、リモートコンソール経由でブラウザ上でもBIOS設定やUbuntuのデスクトップの表示ができ、半端なく便利。
    • トラブルシューティング時モバイルモニターをいちいち接続する手間がなくなるので最高。
  • 前回記事においては、片方のGPUのみECC機能が有効になっており、利用可能なVRAM量が少なく性能にも影響があったことが判明ECC機能をオフにしてまともなマルチGPUのベンチを取り直した。

  • 前回と同様、GPT2トレーニングとllama.cppでマルチVSシングルのGPU性能比較を実施。十分GPUを2枚にした効果が見られたと思う。

    • 結果的に、GPT2トレーニングにおいては、レーニング時間はシングルの方が短いがtrain_samples_per_secondはマルチのほうが1.9倍ほど高い結果が得られた。
    • llama.cppにおいてはマルチGPUにすることで約1.2倍のトークン生成速度となった。

外観など

こんな箱で届きました。中にはEPYCがセットされたマザーと、個々にブリスターパックされたDDR4メモリが8枚入っていました。

中身というと、緑色の無骨なPCB基板にたくさんのDIMM、PCIeスロットが並び余すことなくチップと配線が敷き詰められたマザー、SP3ソケットに収められたヒートスプレッダ上に踊る輝かしきEPYCの文字、両面いっぱいに2列でDRAMチップが取り付けられた32GBのECC REG DIMMモジュールと、自作趣味の心が踊らずにはいられないセットである。

これにメルカリで中古で入手した適当な240mmのCPUクーラーを取り付け、適当にATX電源を接続することで普通の自作PCと同じく起動可能な状態となります。

一点普通の自作PCと異なるのは、マザーに管理用チップ(BMC)が別付けで取り付けられているため、マシンを起動せずとも様々な操作をLAN経由で実施可能なこと。これの素晴らしさはセットアップの項にて後述します。

元々EPYCをいつか自宅で動かしたいという憧れを持っていたので、大人になってようやくその願望が叶えられた形である。海外通販で中古商品を買うのに10万近い金額を出すのはだいぶ躊躇いがありましたが、学生の時からの夢ですし財布の紐が多少緩むのは仕方ないと度重なる出費に理由をつけて買ってしまいました。

セットアップ

とても便利なIPMI

このマザーではIPMIを利用することで、全くディスプレイを繋ぐことなくBIOSをいじったりOSインストールをすることが可能です!

www.fanatic.co.jp

厳密に言えば最初のIPMIのIPアドレスだけはDHCPで払い出されたものを予想して当てるか、一旦ディスプレイに繋いでPOST画面右下で確認するか、同じくディスプレイに一旦繋いでBIOS設定で静的IPアドレスを与える必要がありますが、一回IPMIにアクセスできてしまえば次のような管理画面で様々な操作が可能です。

ちなみに、IPMIログインのための初期ユーザーはADMINで、パスワードはマザーボード上のシールに印字されたPWDの文字列となります。

本マシンのように基本ディスプレイを繋がずリモートで使うマシンにおいて、初期設定やトラブルシューティングのためにいちいちKVMを物理で繋いで対応するのは非常に面倒であるが、IPMIがあればLANケーブルだけ繋げば後はいつものPCのブラウザからほぼすべての作業が行えるので大変便利です。

今回はこのIPMIのバーチャルKVM機能を使ってUbuntuのインストールや静的IPアドレス設定まで実施して、残りはSSH接続でCUDAやPython関連のセットアップを実施することができました。

ちなみにCUDAのセットアップは特にマルチGPUだから何か違うということはなく、普通にNVIDIAの公式サイト通りに実施して問題なくできました。

https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html

DRAMが1枚認識されないトラブル

また、しばらく何故かメモリが1枚認識されず、計224GBとして認識される問題に小一時間悩まされました。

何度DRAMを入れ替え刺し直しても状況は変わらずマザーの故障を疑い始めた頃、Redditでドンピシャなスレッドを発見。どうやらPCケースのマザーボードマウント用の突起がマザー裏の端子と接触することでこういう症状が起こることがあるという内容。

https://www.reddit.com/r/homelab/comments/zi0xph/epyc_not_detecting_all_ram/

自分の環境でも正しく同じ状況となっており、8つ目のDIMMスロット裏に接触していたマウント用ネジを外したところ問題なく8枚のメモリが認識され、計256GBとしてBIOSからもOSからも見えるようになりました!マジでRedditには感謝です。

GPUECC機能が片方だけオンになっていた件

前回の記事で気づいていなかったのですが、nvidia-smiの結果でみると2枚のP40のVRAM量が異なるように表示されていたようである。

ubuntu@ubuntu-Super-Server:~$ nvidia-smi
Fri Aug 11 15:37:26 2023
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.86.10              Driver Version: 535.86.10    CUDA Version: 12.2     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  Tesla P40                      On  | 00000000:06:00.0 Off |                  Off |
| N/A   28C    P8              10W / 250W |      4MiB / 24576MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+
|   1  Tesla P40                      On  | 00000000:21:00.0 Off |                    0 |
| N/A   28C    P8               9W / 250W |      4MiB / 23040MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

GPU毎のnvidia-smi -aの結果をdiffしてみると、あとから買った方のP40のみECC Modeが有効になっていることが判明。

天下り的に考えてみると、23040MB/24576MB=0.9375=15/16なので、16bitのうち1bitをパリティビットとして利用しているのだと思われる。

また、nvidia-smiでnvidia-smi -g GPU番号 --ecc-config=0/1とすることで、ecc機能の有効無効を切り替えることができると判明(再起動が必要)。

https://thelinuxcluster.com/2013/07/24/turning-off-and-on-ecc-ram-for-nvidia-gp-gpu-cards/

ということで、#1 GPUecc機能をオフにしてみると、ちゃんとVRAM量が同じになりました!

ubuntu@ubuntu-Super-Server:~$ nvidia-smi
Sat Aug 12 13:10:17 2023
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.86.10              Driver Version: 535.86.10    CUDA Version: 12.2     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  Tesla P40                      On  | 00000000:06:00.0 Off |                  Off |
| N/A   24C    P8               9W / 250W |      4MiB / 24576MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+
|   1  Tesla P40                      On  | 00000000:21:00.0 Off |                  Off |
| N/A   23C    P8               9W / 250W |      4MiB / 24576MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

後ほど言及しますが、ECC機能がオンになっていると性能が約1割ほど下がる模様。前回測定したときには片方がECCオン、もう片方がオフになっているアンバランスな状態で、マルチGPUでの性能測定時にも当然遅い方に結果が引っ張られますので、前記事の結果は参考程度にみていただければと思います(その旨すでに記事に反映済みです)。

今回のマルチGPUの性能測定は、ちゃんと両GPUともECCをオフにして実行しました。

Transformers公式のDDPチュートリアル

前回同様、次のページのDDPを利用したGPT2トレーニングのサンプルを利用して、トレーニングにかかる時間とtrain_samples_per_secondの値を比較。詳細なセットアップに関しては前記事参照。

https://huggingface.co/docs/transformers/perf_train_gpu_many

- 2GPU 1GPU(w/o ECC) 1GPU(w ECC)
Training Time(s) 186 175 195
train_samples_per_second 8.6 4.57 4.097

結果、Training TimeはマルチGPUでは改善しないものの、train_samples_per_second1GPUのときより1.9倍近い値となっていた。前回同様予測の域を出ないが、やはりGPU間におけるGradientsの値の共有に余計な時間がかかっているせいでTraining時間が改善しないのであろうか。

マルチGPUでモデルトレーニングを高速化するのは今後の宿題としたい。あまり詳しくないが、ZeROのような別の分散学習方法を採用したり、DeepSpeedのようなライブラリの使用でなんとかなる気もする。

また、先程言及したように、ECCを有効化した状態でのシングルGPUの性能もみてみる。すると、演算性能も1割程度低いことが判明。特にECC機能が必要ない場合はオフにするのが良さそう。

マルチGPU

NCCL_P2P_DISABLE=1 CUDA_VISIBLE_DEVICES=0,1 torchrun --nproc_per_node 2 examples/pytorch/language-modeling/run_clm.py --model_name_or_path gpt2 --dataset_name wikitext --dataset_config_name wikitext-2-raw-v1 --do_train --output_dir ~/tmp/test-clm --per_device_train_batch_size 4 --max_steps 200

***** train metrics *****
  epoch                    =       0.69
  train_loss               =      3.279
  train_runtime            = 0:03:06.03
  train_samples            =       2318
  train_samples_per_second =        8.6
  train_steps_per_second   =      1.075

シングルGPU(w/o ECC)

NCCL_P2P_DISABLE=1 CUDA_VISIBLE_DEVICES=0 torchrun --nproc_per_node 1 examples/pytorch/language-modeling/run_clm.py --model_name_or_path gpt2 --dataset_name wikitext --dataset_config_name wikitext-2-raw-v1 --do_train --output_dir ~/tmp/test-clm --per_device_train_batch_size 4 --max_steps 200
***** train metrics *****
  epoch                    =       0.34
  train_loss               =     3.3276
  train_runtime            = 0:02:54.99
  train_samples            =       2318
  train_samples_per_second =      4.571
  train_steps_per_second   =      1.143

シングルGPU(w/ ECC)

CUDA_VISIBLE_DEVICES=1 torchrun --nproc_per_node 1 examples/pytorch/language-modeling/run_clm.py --model_name_or_path gpt2 --dataset_name wikitext --dataset_config_name wikitext-2-raw-v1 --do_train --output_dir ~/tmp/test-clm --per_device_train_batch_size 4 --max_steps 200
***** train metrics *****
  epoch                    =       0.34
  train_loss               =     3.3276
  train_runtime            = 0:03:15.25
  train_samples            =       2318
  train_samples_per_second =      4.097
  train_steps_per_second   =      1.024

llama.cpp でマルチGPUとシングルGPUを比較する

前回同様、CUBLASを有効化してllama.cppをコンパイルし、8bit量子化されたLLaMa2 13b(llama-2-13b-chat.ggmlv3.q8_0.bin)のモデルをCPUのみ、GPU1枚、GPU2枚で動作させたときのトークン生成速度(eval time)を比較しました。

CPUのみにするときは-nglの値を0とし、GPU枚数のコントロールはコマンド実行時のCUDA_VISIBLE_DEVICESへ渡す値を変えることで行います。

ついでにCPUの性能も比較したいので、前回測定したCPU(5900X)の結果も併記します。

CPU(5900X) CPU(7551P) 2GPU 1GPU(w/o ECC) 1GPU(w ECC)
2.76 1.24 8.51 7.01 6.68

結果、マルチGPUのほうが1.2倍くらい1GPUよりも速く推論できるとの結果に。

また、やはりGPUECCを有効化すると若干性能が下がるというのはllama.cppにおいても同様の結果が得られた。

CPUに関してはコア数が増えた上(12コア→32コア)、メモリチャネルも8チャネルとなり帯域が増したはず(3200MHz * 2 channel * 8B = 51.2GB/s → 2133MHz * 8 channel * 8B = 136.5GB/s)であるが、やはり世代の差は埋められないのか、あまりマルチコアの性能をllama.cppが出し切れないのか、残念ながら7551Pでは5900Xの半分以下のトークン生成速度しか出ない結果となった。

マルチGPU

(llama2) ubuntu@ubuntu-Super-Server:~/Documents/llama.cpp$ ./build/bin/main -m ./models/llama-2-13b-chat.ggmlv3.q8_0.bin --temp 0.1 -p "### Instruction: Create poetry about Mount Fuji in 200 words
### Response:" -ngl 40 -b 512
main: build = 975 (9ca4abe)
main: seed  = 1691814871
ggml_init_cublas: found 2 CUDA devices:
  Device 0: Tesla P40, compute capability 6.1
  Device 1: Tesla P40, compute capability 6.1
llama.cpp: loading model from ./models/llama-2-13b-chat.ggmlv3.q8_0.bin
llama_model_load_internal: format     = ggjt v3 (latest)
llama_model_load_internal: n_vocab    = 32000
llama_model_load_internal: n_ctx      = 512
llama_model_load_internal: n_embd     = 5120
llama_model_load_internal: n_mult     = 256
llama_model_load_internal: n_head     = 40
llama_model_load_internal: n_head_kv  = 40
llama_model_load_internal: n_layer    = 40
llama_model_load_internal: n_rot      = 128
llama_model_load_internal: n_gqa      = 1
llama_model_load_internal: rnorm_eps  = 5.0e-06
llama_model_load_internal: n_ff       = 13824
llama_model_load_internal: freq_base  = 10000.0
llama_model_load_internal: freq_scale = 1
llama_model_load_internal: ftype      = 7 (mostly Q8_0)
llama_model_load_internal: model size = 13B
llama_model_load_internal: ggml ctx size =    0.11 MB
llama_model_load_internal: using CUDA for GPU acceleration
ggml_cuda_set_main_device: using device 0 (Tesla P40) as main device
llama_model_load_internal: mem required  =  698.16 MB (+  400.00 MB per state)
llama_model_load_internal: allocating batch_size x (640 kB + n_ctx x 160 B) = 360 MB VRAM for the scratch buffer
llama_model_load_internal: offloading 40 repeating layers to GPU
llama_model_load_internal: offloaded 40/43 layers to GPU
llama_model_load_internal: total VRAM used: 13218 MB
llama_new_context_with_model: kv self size  =  400.00 MB

system_info: n_threads = 32 / 64 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.100000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = -1, n_keep = 0


 ### Instruction: Create poetry about Mount Fuji in 200 words
### Response:

Mount Fuji, a majestic sight
A symbol of Japan, pure and bright
Its snow-capped peak, a beacon of light
Against the blue sky, a wondrous sight

In spring, cherry blossoms bloom
Around its base, a colorful boom
Summer brings green forests alive
Autumn paints the mountain with gold and red
Winter's snow, a serene delight

Mount Fuji, a constant presence
A source of inspiration, a sacred presence
Its beauty, a reflection of the divine
A reminder of nature's splendor and design [end of text]

llama_print_timings:        load time =  2580.63 ms
llama_print_timings:      sample time =   157.57 ms /   142 runs   (    1.11 ms per token,   901.20 tokens per second)
llama_print_timings: prompt eval time =   355.85 ms /    22 tokens (   16.18 ms per token,    61.82 tokens per second)
llama_print_timings:        eval time = 16562.81 ms /   141 runs   (  117.47 ms per token,     8.51 tokens per second)
llama_print_timings:       total time = 17134.59 ms

シングルGPU(w/o ECC)

(llama2) ubuntu@ubuntu-Super-Server:~/Documents/llama.cpp$ CUDA_VISIBLE_DEVICES=0 ./build/bin/main -m ./models/llama-2-13b-chat.ggmlv3.q8_0.bin --temp 0.1 -p "### Instruction: Create poetry about Mount Fuji in 200 words
### Response:" -ngl 40 -b 512
main: build = 975 (9ca4abe)
main: seed  = 1691809687
ggml_init_cublas: found 1 CUDA devices:
  Device 0: Tesla P40, compute capability 6.1
llama.cpp: loading model from ./models/llama-2-13b-chat.ggmlv3.q8_0.bin
llama_model_load_internal: format     = ggjt v3 (latest)
llama_model_load_internal: n_vocab    = 32000
llama_model_load_internal: n_ctx      = 512
llama_model_load_internal: n_embd     = 5120
llama_model_load_internal: n_mult     = 256
llama_model_load_internal: n_head     = 40
llama_model_load_internal: n_head_kv  = 40
llama_model_load_internal: n_layer    = 40
llama_model_load_internal: n_rot      = 128
llama_model_load_internal: n_gqa      = 1
llama_model_load_internal: rnorm_eps  = 5.0e-06
llama_model_load_internal: n_ff       = 13824
llama_model_load_internal: freq_base  = 10000.0
llama_model_load_internal: freq_scale = 1
llama_model_load_internal: ftype      = 7 (mostly Q8_0)
llama_model_load_internal: model size = 13B
llama_model_load_internal: ggml ctx size =    0.11 MB
llama_model_load_internal: using CUDA for GPU acceleration
llama_model_load_internal: mem required  =  698.16 MB (+  400.00 MB per state)
llama_model_load_internal: allocating batch_size x (640 kB + n_ctx x 160 B) = 360 MB VRAM for the scratch buffer
llama_model_load_internal: offloading 40 repeating layers to GPU
llama_model_load_internal: offloaded 40/43 layers to GPU
llama_model_load_internal: total VRAM used: 13218 MB
llama_new_context_with_model: kv self size  =  400.00 MB

system_info: n_threads = 32 / 64 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.100000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = -1, n_keep = 0


 ### Instruction: Create poetry about Mount Fuji in 200 words
### Response:

Majestic Mount Fuji, a snow-capped wonder,
Rises high above the clouds and thunder.
Its peak soars to the sky, serene and still,
A symbol of strength, beauty, and will.

In springtime, cherry blossoms bloom around,
Painting the mountain with colors profound.
Summer skies are bright and clear,
Reflecting the mountain's grandeur here.

Autumn leaves change to shades of gold,
As nature's canvas, stories untold.
Winter snows blanket the peak,
A peaceful sight, a season to seek.

Through every season, Mount Fuji stands,
A constant presence, a sacred land.
Its beauty inspires and humbles the soul,
A source of wonder, a goal to strive for whole. [end of text]

llama_print_timings:        load time =  2654.84 ms
llama_print_timings:      sample time =   220.07 ms /   195 runs   (    1.13 ms per token,   886.08 tokens per second)
llama_print_timings: prompt eval time =   540.22 ms /    22 tokens (   24.56 ms per token,    40.72 tokens per second)
llama_print_timings:        eval time = 27682.73 ms /   194 runs   (  142.69 ms per token,     7.01 tokens per second)
llama_print_timings:       total time = 28521.56 ms

シングルGPU(w/ ECC)

(llama2) ubuntu@ubuntu-Super-Server:~/Documents/llama.cpp$ CUDA_VISIBLE_DEVICES=1 ./build/bin/main -m ./models/llama-2-13b-chat.ggmlv3.q8_0.bin --temp 0.1 -p "### Instruction: Create poetry about Mount Fuji in 200 words
### Response:" -ngl 40 -b 512
main: build = 975 (9ca4abe)
main: seed  = 1691810359
ggml_init_cublas: found 1 CUDA devices:
  Device 0: Tesla P40, compute capability 6.1
llama.cpp: loading model from ./models/llama-2-13b-chat.ggmlv3.q8_0.bin
llama_model_load_internal: format     = ggjt v3 (latest)
llama_model_load_internal: n_vocab    = 32000
llama_model_load_internal: n_ctx      = 512
llama_model_load_internal: n_embd     = 5120
llama_model_load_internal: n_mult     = 256
llama_model_load_internal: n_head     = 40
llama_model_load_internal: n_head_kv  = 40
llama_model_load_internal: n_layer    = 40
llama_model_load_internal: n_rot      = 128
llama_model_load_internal: n_gqa      = 1
llama_model_load_internal: rnorm_eps  = 5.0e-06
llama_model_load_internal: n_ff       = 13824
llama_model_load_internal: freq_base  = 10000.0
llama_model_load_internal: freq_scale = 1
llama_model_load_internal: ftype      = 7 (mostly Q8_0)
llama_model_load_internal: model size = 13B
llama_model_load_internal: ggml ctx size =    0.11 MB
llama_model_load_internal: using CUDA for GPU acceleration
llama_model_load_internal: mem required  =  698.16 MB (+  400.00 MB per state)
llama_model_load_internal: allocating batch_size x (640 kB + n_ctx x 160 B) = 360 MB VRAM for the scratch buffer
llama_model_load_internal: offloading 40 repeating layers to GPU
llama_model_load_internal: offloaded 40/43 layers to GPU
llama_model_load_internal: total VRAM used: 13218 MB
llama_new_context_with_model: kv self size  =  400.00 MB

system_info: n_threads = 32 / 64 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.100000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = -1, n_keep = 0


 ### Instruction: Create poetry about Mount Fuji in 200 words
### Response:

Oh, majestic Mount Fuji, so serene and divine,
Your snow-capped peak touches the sky, so fine.
In the distance, a haze of blue,
A gentle breeze rustles through.

Your slopes are blanketed in green,
A carpet of life, so serene.
The trees stand tall and proud,
Their leaves rustling, a gentle sound.

In the shadow of your greatness,
I am but a small part,
A tiny speck in your grand design,
Yet, I am filled with awe and wonder at your heart.

Oh, Mount Fuji, you are a symbol of strength and grace,
A beacon of hope, a source of light,
In your presence, I find peace,
And my spirit takes flight. [end of text]

llama_print_timings:        load time =  2480.95 ms
llama_print_timings:      sample time =   204.16 ms /   185 runs   (    1.10 ms per token,   906.13 tokens per second)
llama_print_timings: prompt eval time =   605.07 ms /    22 tokens (   27.50 ms per token,    36.36 tokens per second)
llama_print_timings:        eval time = 27541.31 ms /   184 runs   (  149.68 ms per token,     6.68 tokens per second)
llama_print_timings:       total time = 28424.62 ms

CPU(EPYC 7551P)

(llama2) ubuntu@ubuntu-Super-Server:~/Documents/llama.cpp$ ./build/bin/main -m ./models/llama-2-13b-chat.ggmlv3.q8_0.bin --temp 0.1 -p "### Instruction: Create poetry about Mount Fuji in 200 words
### Response:" -ngl 0 -b 512
main: build = 975 (9ca4abe)
main: seed  = 1691810099
ggml_init_cublas: found 2 CUDA devices:
  Device 0: Tesla P40, compute capability 6.1
  Device 1: Tesla P40, compute capability 6.1
llama.cpp: loading model from ./models/llama-2-13b-chat.ggmlv3.q8_0.bin
llama_model_load_internal: format     = ggjt v3 (latest)
llama_model_load_internal: n_vocab    = 32000
llama_model_load_internal: n_ctx      = 512
llama_model_load_internal: n_embd     = 5120
llama_model_load_internal: n_mult     = 256
llama_model_load_internal: n_head     = 40
llama_model_load_internal: n_head_kv  = 40
llama_model_load_internal: n_layer    = 40
llama_model_load_internal: n_rot      = 128
llama_model_load_internal: n_gqa      = 1
llama_model_load_internal: rnorm_eps  = 5.0e-06
llama_model_load_internal: n_ff       = 13824
llama_model_load_internal: freq_base  = 10000.0
llama_model_load_internal: freq_scale = 1
llama_model_load_internal: ftype      = 7 (mostly Q8_0)
llama_model_load_internal: model size = 13B
llama_model_load_internal: ggml ctx size =    0.11 MB
llama_model_load_internal: using CUDA for GPU acceleration
ggml_cuda_set_main_device: using device 0 (Tesla P40) as main device
llama_model_load_internal: mem required  = 13555.97 MB (+  400.00 MB per state)
llama_model_load_internal: offloading 0 repeating layers to GPU
llama_model_load_internal: offloaded 0/43 layers to GPU
llama_model_load_internal: total VRAM used: 360 MB
llama_new_context_with_model: kv self size  =  400.00 MB

system_info: n_threads = 32 / 64 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.100000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = -1, n_keep = 0


 ### Instruction: Create poetry about Mount Fuji in 200 words
### Response:

Mount Fuji, a majestic sight
A symbol of Japan, pure and bright
Snow-capped peak, serene and still
A challenge to climb, but the view is thrilling

In the distance, the sun sets low
Painting the sky with hues of gold
The mountain's shadow stretches far and wide
As the stars begin to twinkle inside

The mountain's beauty is a work of art
A masterpiece that touches the heart
A source of inspiration, a symbol of strength
A reminder of nature's grand design

In the silence, I find peace
A sense of calm, a world to cease
From the summit to the base
Mount Fuji, a wonder of grace. [end of text]

llama_print_timings:        load time =  1813.56 ms
llama_print_timings:      sample time =   163.44 ms /   169 runs   (    0.97 ms per token,  1034.04 tokens per second)
llama_print_timings: prompt eval time =  1460.09 ms /    22 tokens (   66.37 ms per token,    15.07  tokens per second)
llama_print_timings:        eval time = 135809.05 ms /   168 runs   (  808.39 ms per token,     1.24   tokens per second)
llama_print_timings:       total time = 137504.02 ms

【続】鯖落ちGPUを使った安価な機械学習用マシンの作り方 ~夢のマルチGPU環境にしてみる編~

!!注意!!: この記事で取ったベンチマークは、片方のGPUのみECC有効となっていたことが判明しました。PCIeのレーン数も双方のGPUで異なっており、非常にアンバランスな条件で測定した結果となっておりますので、マルチGPUの効果を見るのにはあまり適しておりません。後記事にてEPYCを購入し、ちゃんとECCを無効化した上で両GPUともPCIe x16で接続した結果を公開しましたので、こちらも合わせて見ていただければと思います。

前記事:

hashicco.hatenablog.com

後記事 :

hashicco.hatenablog.com

前書き

きっかけは、llama2の13BモデルがシングルGPUでは動かなかったこと。

以前記事にした鯖落ちP40を利用して作った機械学習用マシンですが、最近分析界隈でも当たり前のように使われ始めているLLMを動かすことを考えると、GPUはなんぼあってもいい状況です。

また、私自身マルチGPUを扱った経験が薄く、精々SageMaker上でPyTorchのDPでお手軽マルチGPUレーニングをやったことがある程度。今後大きなモデルを扱っていくことを考えると、お家でマルチGPUを試せる環境を準備しとくのは悪くないと思ってしまった。

そこで、P40をもう一台買い増してみることにしたので、色々とベンチやマルチGPUの使い勝手を書いておこうと思います。

まとめ

  • 私の使っているB550マザーでは1本目のPCIeがx16, 2本目がx4で分割もできないようなので、仕方無しに2枚目のP40はx4接続となりました。
    • フルスピードで動かすならThreadripperかEPYCがほしいところ。昔持ってたやつ売らなければ良かった......。
  • テスト項目は次の3つとしました。
    • 1.Transformers公式のDDPチュートリアル(GPT2のトレーニング): 一番実用に近いベンチ。 結果的にシングルGPUでもトレーニングできてしまったのですが、マルチのほうが約25%遅い結果に。 結果をちゃんと見てみたところ、train_samples_per_secondの値は約1.6倍となっていることが判明。Gradientsを共有するのに時間がかかっているのであろうか?
    • 2.Stable Diffusion SDXL base 1.0 画像生成速度(シングルGPU性能): 512x512の画像生成が大体11.6 secでした。StableDiffusion-webuiがマルチGPUに対応しているわけではないですが、別プロセスで並列して生成させることはできるはず。
    • 3.llama.cppでシングルVSマルチGPU性能比較: llama.cppをcuBLAS有効でコンパイルし、llama-2-13b-chat.ggmlv3.q8_0.binGPUなし/1GPU/2GPUで動作させて実行時間を測りました。結果、5%ほど速いスピードで推論できることがわかりました。

PCIeの帯域が問題なのか、あまりうまくスケールしていない感じです。これは中古EPYC買うフラグが立ってしまったかもしれません。

とはいえとりあえずマルチGPUの意味は確認できたので、あとは実用的にKaggleコンペなどで使ってメリットを出していきたいと思います。

直近では次のコンペに出てみようと思ってますので、ここでP40x2を有効活用することができれば嬉しいなあ。

www.kaggle.com

セットアップ

すでに前の記事で構築済みのマシンに、お手製ブラケットでファンを取り付けたP40を増設しました。特にOS上は設定変更などいらずに2枚目のGPUが認識されるようになりました。

1. Transformers公式のDDPチュートリアル

準備

こちらのチュートリアルに、TransformersでマルチGPUを使ってトレーニングを行う方法が紹介されています。ここのコードをベンチマークに利用しました。

huggingface.co

ここのチュートリアルのコードを実行するためには、ソースからTransformersをインストールしなければならないなど少し工夫が必要です。新たなPython仮想環境上で次の手順にて準備を行います(最後のPyTorchインストールは各々のCUDAバージョン等に合わせて変更してください)。

pip install git+https://github.com/huggingface/transformers
pip install evaluate datasets
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

ベンチマークに用いるrun_clm.pyは、transformersの公式レポジトリからクローンしてきます。

git clone https://github.com/huggingface/transformers.git

で、あとは次のコードを実行するのですが、

NCCL_P2P_DISABLE=1 CUDA_VISIBLE_DEVICES=0,1 \
python -m torch.distributed.launch --nproc_per_node 2 examples/pytorch/language-modeling/run_clm.py \
--model_name_or_path gpt2 --dataset_name wikitext --dataset_config_name wikitext-2-raw-v1 \
--do_train --output_dir ~/tmp/test-clm --per_device_train_batch_size 4 --max_steps 200

torch 2.0ではtorch.distributed.launchからtorchrunへの移行が推奨されているようなので、エラーが発生してしまいました。

ValueError: Some specified arguments are not used by the HfArgumentParser: ['--local-rank=0']

これを回避するため、torch.distributed.launchからtorchrunへコマンドを単純に差し替えたところ、問題なく動いてくれました。

NCCL_P2P_DISABLE=1 CUDA_VISIBLE_DEVICES=0,1 \
torchrun --nproc_per_node 2 examples/pytorch/language-modeling/run_clm.py \
--model_name_or_path gpt2 --dataset_name wikitext --dataset_config_name wikitext-2-raw-v1 \
--do_train --output_dir ~/tmp/test-clm --per_device_train_batch_size 4 --max_steps 200

結果

マルチGPUの結果

***** train metrics *****
  epoch                    =       0.69
  train_loss               =      3.279
  train_runtime            = 0:03:37.12
  train_samples            =       2318
  train_samples_per_second =      7.369
  train_steps_per_second   =      0.921
[INFO|modelcard.py:452] 2023-08-05 16:27:29,020 >> Dropping the following result as it does not have all the necessary fields:
{'task': {'name': 'Causal Language Modeling', 'type': 'text-generation'}, 'dataset': {'name': 'wikitext wikitext-2-raw-v1', 'type': 'wikitext', 'config': 'wikitext-2-raw-v1', 'split': 'train', 'args': 'wikitext-2-raw-v1'}}

結果は217sとのこと。Titan RTX V x2のベンチ結果がTransformers公式に載っているのですが、それだと131sらしいのでまあ早くも遅くもない?

シングルGPUで同じトレーニングを実施。むしろ若干早く(173s)でトレーニングできてしまっているので、2GPUの恩恵を感じるほどの結果ではなくなってしまった……。やはり片方のPCIeの帯域がx4なのが悪いのか。

ただ、train_samples_per_secondの値は約1.6倍となっているので、1秒間に処理できているサンプル数は増加している模様。NVLinkがないことから、GPU間でのパラメータ共有に時間がかかっていたりするのだろうか?まあ、train_samples_per_secondの算出方法がわからないのでなんとも言えないが……。

続いて、同じベンチマークコードをシングルGPUで実行します。単にCUDA_VISIBLE_DEVICES=0を頭につけて1枚だけGPUを認識できるようにし、nproc_per_nodeを1にしただけです。

NCCL_P2P_DISABLE=1 CUDA_VISIBLE_DEVICES=0 torchrun --nproc_per_node 1 examples/pytorch/language-modeling/run_clm.py \
--model_name_or_path gpt2 --dataset_name wikitext --dataset_config_name wikitext-2-raw-v1\
 --do_train --output_dir ~/tmp/test-clm --per_device_train_batch_size 4 --max_steps 200

***** train metrics *****
  epoch                    =       0.34
  train_loss               =     3.3276
  train_runtime            = 0:02:53.38
  train_samples            =       2318
  train_samples_per_second =      4.614
  train_steps_per_second   =      1.154

ちなみに、ちゃんと2GPU使用時には、どちらのGPUの利用率も高くなっていることが確認できました。

2. Stable Diffusion SDXL base 1.0でのP40の性能計測

準備

インストールは次のページを参照。唯一、異なるPCのWebUI環境にアクセスする必要があるので、launch.pyの引数に--listenを追加しました。

pc.watch.impress.co.jp

sudo apt install curl gnupg2 git python-is-python3 python3.10-venv python3-pip
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui
cd stable-diffusion-webui
python launch.py --listen

また、モデルファイルとしては次のURLから対象のsafetensorsをダウンロードして、models/Stable-diffusionに配置しました。

https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0/tree/main https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/tree/main

sd_xl_base_1.0.safetensors sd_xl_refiner_1.0.safetensors

結果

txt2imgで512x512のイメージを生成した際の生成時間を確認したところ、 11.6 secとのこと。

当然2枚GPUが刺さっていれば別プロセスとして違うGPUを使うように設定してあげて倍の枚数生成できると思うので、悪くないのではないだろうか。

3. llama.cpp でマルチGPUとシングルGPUを比較する

準備

llama.cppでGPUを使う方法に関しては次のページを参照しました。

note.com

次の手順でcuBLASを有効化したllama.cppをコンパイルします。

git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
mkdir build
cd build
cmake .. -DLLAMA_CUBLAS=ON
cmake --build . --config Release

モデルとしてはllama2のGGML 8bit量子化モデル(llama-2-13b-chat.ggmlv3.q8_0.bin)を次のページからダウンロード

https://huggingface.co/TheBloke/Llama-2-13B-chat-GGML/tree/main

結果

とりあえず200文字程度の詩を生成させてみて、その際のトークン生成速度(eval time)を比較しました。

CPU(5900X) 1GPU 2GPU
2.76 14.83 15.59

結果、5%ほど2GPUのほうが速くなりました。llama.cppの場合多分層ごとに異なるGPUに配置するような形になるため、あんまり効率は良くないのかもしれません。シンプルに1GPUに乗り切らないモデルを動かしたりできるという面で2GPUの価値はあるでしょう。

multi GPU

./build/bin/main -m ./models/llama-2-13b-chat.ggmlv3.q8_0.bin --temp 0.1 -p "### Instruction: Create poetry about Mount Fuji in 200 words
### Response:" -ngl 40 -b 512
main: build = 952 (3323112)
main: seed  = 1691226913
ggml_init_cublas: found 2 CUDA devices:
  Device 0: Tesla P40, compute capability 6.1
  Device 1: Tesla P40, compute capability 6.1
llama.cpp: loading model from ./models/llama-2-13b-chat.ggmlv3.q8_0.bin
llama_model_load_internal: format     = ggjt v3 (latest)
llama_model_load_internal: n_vocab    = 32000
llama_model_load_internal: n_ctx      = 512
llama_model_load_internal: n_embd     = 5120
llama_model_load_internal: n_mult     = 256
llama_model_load_internal: n_head     = 40
llama_model_load_internal: n_head_kv  = 40
llama_model_load_internal: n_layer    = 40
llama_model_load_internal: n_rot      = 128
llama_model_load_internal: n_gqa      = 1
llama_model_load_internal: rnorm_eps  = 5.0e-06
llama_model_load_internal: n_ff       = 13824
llama_model_load_internal: freq_base  = 10000.0
llama_model_load_internal: freq_scale = 1
llama_model_load_internal: ftype      = 7 (mostly Q8_0)
llama_model_load_internal: model size = 13B
llama_model_load_internal: ggml ctx size =    0.11 MB
llama_model_load_internal: using CUDA for GPU acceleration
ggml_cuda_set_main_device: using device 0 (Tesla P40) as main device
llama_model_load_internal: mem required  =  698.16 MB (+  400.00 MB per state)
llama_model_load_internal: allocating batch_size x (640 kB + n_ctx x 160 B) = 360 MB VRAM for the scratch buffer
llama_model_load_internal: offloading 40 repeating layers to GPU
llama_model_load_internal: offloaded 40/43 layers to GPU
llama_model_load_internal: total VRAM used: 13218 MB
llama_new_context_with_model: kv self size  =  400.00 MB

system_info: n_threads = 12 / 24 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.100000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = -1, n_keep = 0


 ### Instruction: Create poetry about Mount Fuji in 200 words
### Response:

Mount Fuji, a majestic sight
Rising high into the morning light
Snow-capped peak, a work of art
A symbol of Japan's heart

Its beauty is beyond compare
A sacred mountain, pure and fair
In spring, cherry blossoms bloom
Summer brings green forests, autumn's hue
Winter's snow, a peaceful sight

The mountain's grace, a source of pride
For Japan, a treasure inside
A place of wonder, awe and dreams
Where nature's beauty, forever gleams

Its majesty, a symbol of strength
A reminder of life's fleeting length
Let us cherish this gift of nature
And honor Mount Fuji's pure creation. [end of text]

llama_print_timings:        load time =  3770.04 ms
llama_print_timings:      sample time =    72.51 ms /   174 runs   (    0.42 ms per token,  2399.74 tokens per second)
llama_print_timings: prompt eval time =   481.74 ms /    22 tokens (   21.90 ms per token,    45.67 tokens per second)
llama_print_timings:        eval time = 11099.33 ms /   173 runs   (   64.16 ms per token,    15.59 tokens per second)
llama_print_timings:       total time = 11679.77 ms

1GPU

CUDA_VISIBLE_DEVICES=0 ./build/bin/main -m ./models/llama-2-13b-chat.ggmlv3.q8_0.bin --temp 0.1 -p "### Instruction: Create poetry about Mount Fuji in 200 words
### Response:" -ngl 40 -b 512
main: build = 952 (3323112)
main: seed  = 1691227096
ggml_init_cublas: found 1 CUDA devices:
  Device 0: Tesla P40, compute capability 6.1
llama.cpp: loading model from ./models/llama-2-13b-chat.ggmlv3.q8_0.bin
llama_model_load_internal: format     = ggjt v3 (latest)
llama_model_load_internal: n_vocab    = 32000
llama_model_load_internal: n_ctx      = 512
llama_model_load_internal: n_embd     = 5120
llama_model_load_internal: n_mult     = 256
llama_model_load_internal: n_head     = 40
llama_model_load_internal: n_head_kv  = 40
llama_model_load_internal: n_layer    = 40
llama_model_load_internal: n_rot      = 128
llama_model_load_internal: n_gqa      = 1
llama_model_load_internal: rnorm_eps  = 5.0e-06
llama_model_load_internal: n_ff       = 13824
llama_model_load_internal: freq_base  = 10000.0
llama_model_load_internal: freq_scale = 1
llama_model_load_internal: ftype      = 7 (mostly Q8_0)
llama_model_load_internal: model size = 13B
llama_model_load_internal: ggml ctx size =    0.11 MB
llama_model_load_internal: using CUDA for GPU acceleration
llama_model_load_internal: mem required  =  698.16 MB (+  400.00 MB per state)
llama_model_load_internal: allocating batch_size x (640 kB + n_ctx x 160 B) = 360 MB VRAM for the scratch buffer
llama_model_load_internal: offloading 40 repeating layers to GPU
llama_model_load_internal: offloaded 40/43 layers to GPU
llama_model_load_internal: total VRAM used: 13218 MB
llama_new_context_with_model: kv self size  =  400.00 MB

system_info: n_threads = 12 / 24 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.100000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = -1, n_keep = 0

 ### Instruction: Create poetry about Mount Fuji in 200 words
### Response:

Mount Fuji, a majestic sight
Rising high into the sky so bright
Snow-capped peak, a work of art
A symbol of Japan, a treasure to the heart

Its base, a lush green forest
Home to wildlife, a peaceful nest
The mountain's beauty, a source of pride
For the Japanese people, it abides

In spring, cherry blossoms bloom
A delicate pink, a sweet perfume
Summer brings warmth and vibrant hues
Autumn leaves, a kaleidoscope of views
Winter's snow, a serene landscape
Year-round, Mount Fuji's grandeur expands

A sacred mountain, a spiritual place
Where nature's beauty, grace and pace
Bring solace to the soul and heart
A journey to Mount Fuji, a work of art. [end of text]

llama_print_timings:        load time =  5271.96 ms
llama_print_timings:      sample time =    80.64 ms /   199 runs   (    0.41 ms per token,  2467.67 tokens per second)
llama_print_timings: prompt eval time =   596.81 ms /    22 tokens (   27.13 ms per token,    36.86 tokens per second)
llama_print_timings:        eval time = 13346.94 ms /   198 runs   (   67.41 ms per token,    14.83 tokens per second)
llama_print_timings:       total time = 14053.59 ms

CPU

./build/bin/main -m ./models/llama-2-13b-chat.ggmlv3.q8_0.bin --temp 0.1 -p "### Instruction: Create poetry about Mount Fuji in 200 words
### Response:" -ngl 0 -b 512
main: build = 952 (3323112)
main: seed  = 1691227261
ggml_init_cublas: found 1 CUDA devices:
  Device 0: Tesla P40, compute capability 6.1
llama.cpp: loading model from ./models/llama-2-13b-chat.ggmlv3.q8_0.bin
llama_model_load_internal: format     = ggjt v3 (latest)
llama_model_load_internal: n_vocab    = 32000
llama_model_load_internal: n_ctx      = 512
llama_model_load_internal: n_embd     = 5120
llama_model_load_internal: n_mult     = 256
llama_model_load_internal: n_head     = 40
llama_model_load_internal: n_head_kv  = 40
llama_model_load_internal: n_layer    = 40
llama_model_load_internal: n_rot      = 128
llama_model_load_internal: n_gqa      = 1
llama_model_load_internal: rnorm_eps  = 5.0e-06
llama_model_load_internal: n_ff       = 13824
llama_model_load_internal: freq_base  = 10000.0
llama_model_load_internal: freq_scale = 1
llama_model_load_internal: ftype      = 7 (mostly Q8_0)
llama_model_load_internal: model size = 13B
llama_model_load_internal: ggml ctx size =    0.11 MB
llama_model_load_internal: using CUDA for GPU acceleration
llama_model_load_internal: mem required  = 13555.97 MB (+  400.00 MB per state)
llama_model_load_internal: offloading 0 repeating layers to GPU
llama_model_load_internal: offloaded 0/43 layers to GPU
llama_model_load_internal: total VRAM used: 360 MB
llama_new_context_with_model: kv self size  =  400.00 MB

system_info: n_threads = 12 / 24 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 |
sampling: repeat_last_n = 64, repeat_penalty = 1.100000, presence_penalty = 0.000000, frequency_penalty = 0.000000, top_k = 40, tfs_z = 1.000000, top_p = 0.950000, typical_p = 1.000000, temp = 0.100000, mirostat = 0, mirostat_lr = 0.100000, mirostat_ent = 5.000000
generate: n_ctx = 512, n_batch = 512, n_predict = -1, n_keep = 0


 ### Instruction: Create poetry about Mount Fuji in 200 words
### Response:

Mount Fuji, a majestic sight
Rising high into the sky so bright
Snow-capped peak, a work of art
A symbol of Japan, a treasure to the heart

Its beauty is beyond compare
A sacred mountain, pure and fair
In spring, cherry blossoms bloom
Summer brings green forests, autumn's hue
Winter's snow, a blanket of white

The mountain's grace, a source of pride
For the Japanese people, it abides
A spiritual journey, a pilgrimage too
Climbing Mount Fuji, a dream come true

The sun sets, the moon glows bright
A night of tranquility, pure delight
The stars twinkle, like diamonds in the sky
Mount Fuji, a wonder why

In the morning light, it stands tall
A beacon of hope, for one and all
A symbol of strength, a source of pride
Mount Fuji, forever by our side. [end of text]

llama_print_timings:        load time =  5799.78 ms
llama_print_timings:      sample time =    91.96 ms /   224 runs   (    0.41 ms per token,  2435.82 tokens per second)
llama_print_timings: prompt eval time =  2474.66 ms /    22 tokens (  112.48 ms per token,     8.89 tokens per second)
llama_print_timings:        eval time = 80750.70 ms /   223 runs   (  362.11 ms per token,     2.76 tokens per second)
llama_print_timings:       total time = 83352.26 ms

最近SSDが安いのでオールフラッシュNASを導入した(ASUSTOR FS6712X)

前書き

我が家には10GbE搭載のQNAP TS-332XというNASを導入済みである。8TBのHDD2枚でRAID1を組んでおり、10Gの速度を活かすためにリード/ライトのSSDがキャッシュを設定している。

https://www.qnap.com/ja-jp/product/ts-332x

hashicco.hatenablog.com

単なるデータ倉庫としての用途だったので特に性能的にも不満はなく、HDDも稼働時間を1000日超えても快調に動いてくれていたのですが、最近あまりにもSSDが安すぎてオールフラッシュにしたい欲が浮き上がっては消えていく。

そんな折、ASUSUTORから興味深い製品の発表が。最大12枚ものM.2 SSDを搭載できるというオールフラッシュ専用の10GbE対応NAS、FS6712Xである。

https://www.asustor.com/product/FS67

NASにしては小型の筐体で置き場に困らなさそうなところとか、(実際にはやらないにしても)8TBのSSDを使えば96TBもの容量を積めるところとか、クアッドコアのN5105搭載で最大16GBまでメモリ積めるとかスペック的にも申し分ないところとか、興味をそそられるポイントが随所に存在する。

ということで、SSDの金額が下落していくのを横目で見ながらボーナスが出るのを待ち、東京に帰省するタイミングでアキバでFS6712Xを調達してきたという話。

まとめ

  • 今メモリ・SSDが激安。今回は店員さんのお勧めでSamsungの980 Pro 2TB 5枚でRaid5を組んだのだが、1枚あたり17,580円で買えてしまった

    • このNASはPCIe 3.0x1接続なので970EVO PLUSでも良かったのだが、あまりにも安かったのでProにしてしまった。ちなみに、970EVOで良ければ13,680円で買えた。
  • NAS本体はオリオスペックで購入。129,000円

  • 更に、アークに寄ってバルク品のDDR4 2x8GBのRAMも購入。こちらもまた衝撃的な安さでなんとたったの3,300円!しかもバルクとはいえMicronの品。
  • トータル金額は、本体、980 Pro 2TB 5枚(RAID5, 8TB), メモリで220,200円となりました。オールフラッシュの10GbE NASと考えれば安いのではないでしょうか。

なんか今めちゃくちゃメモリ・SSDが安いので、買うなら今です。

バルク品メモリが激安なのを見つけたりできたので、久々にアキバで買い物して得した気分になりました。

組み立てなど

箱とSSD。こんなにSSDが並ぶとテンションが上がります。ちなみにSSDが6枚ありますが、一枚は保守用です。

本体は大きいオライリー本を一回り大きくしたくらいのサイズ感。最悪災害時にも抱えて逃げられるかもと思えるサイズ。 上蓋と下蓋の2つの蓋があって、それぞれから6本ずつのM.2スロットにアクセスできます。上蓋側にDRAMスロットがあるので、DRAM増設時にはそこから増やします。

設定は画面通りに進めていけば特に詰まるポイントはありませんでした。Web管理画面はQNAPのものと似ている印象。メモリはバルク品かつデフォルトの2933MHzよりも早い3200MHzでしたが問題なく認識してくれました。

結構本体はアチアチです。4cm角程度の小さなファンが底面と上面にあったと思うのですが、旧NASからのデータ転送中にシステム温度は60度近い温度となっています。本格的に運用するならノートPC用の冷却台などに載せたほうが良いかもしれません。

速度計測

残念ながら今の私のメインPCの10G NICがPCIe2.0 x2接続になってしまっているので、あんまりまともなベンチを取ることができておりません……。RTX4090がでかすぎるのが悪い。

サブのPCのGPUを外して10G NICを刺すか、QNAPのNASの方にLinuxコンテナ立ててdiskspdで計測するか、など色々手段は考えられるのですが面倒くさいのでとりあえずはPCIe2.0 x2(実効8.0Gbps上限)での測定結果でお茶を濁しておきます。

明らかにネットワーク速度がネックになっていますが、Writeの速度が過去のNASよりも速いのがわかります。これがオールフラッシュストレージのちからでしょうか。

いつかもう少しまともな計測環境が揃ったらまた測り直して見ようと思います。

【IMC2023】KaggleでなんやかんやあってExpertになれた話

概要

最近久しぶりにKaggleに復帰して、Image Matching Challenge 2023というコンペに参加しました。結果、92位/464グループ という成績でギリギリ銅メダルを獲得でき、晴れてKaggle Competitions Expert(コンペで銅メダル以上2個獲得)となることができました。

アイコン周りの表示も紫色になってちょっと嬉しい

Kaggleを始めたのは2022年の4月くらいですが、ここ半年くらい何故かTypeScriptの勉強と称してLeetCodeに時間を割り当てていた時期があったので、実質半年くらいのKaggle歴です。

実は1つ目の銅メダルも昨年のImage Matching Challenge 2022にて獲得したのであまり幅広いKaggleの経験があるわけではないのですが、なんかネット上のKaggleに関する記事はKaggle MasterとかGrand Masterによる立派な記事ばかりな気がするので、平凡Kagglerの視点から感想を書いてみます。

ちなみに、タイトルの「なんやかんや」という部分に関しては、期せずKaggle悪徳業者の被害者になりかけたのでそのことを指してます。詳しくは続きをどうぞ。

まとめ

  • コンペをうまく選べばCompetition Expertになるのはそこまで難しくないと思う。コツは次の通り

    • 比較的上位のスコアがばらついているコンペに狙いを定める
    • スコアが最も高い公開ノートブックを追いかける。Disscussionにも一通り目を通して拾えそうな改善案を見つける。
    • 一通りみんなが試してそうな改善案をベースラインノートブックに加える
    • さらなる差別化として、モデルのアンサンブルやTTA(Test Time Augmentation)などを行ってチマチマ精度を上げてみるといいと思います。運が良ければそのまま100位以内に残れます。
  • Kaggleではコードを裏で売買したり、チームを組む機能を悪用してメダルの獲得を斡旋するなど不正行為を働く不届き者がいるため、コンペ終了後の不正チェックの結果順位が上がることがある。メダルギリギリの順位でもワンチャンあるかも。

無事に漠然と目標としていた1年位でKaggle Expertになるという目標を達したので、後はたまに興味のあるコンペに参加してみる感じで続けられればと思います。

また、銅メダルはこんなもんで取れてしまうので、"最低限他人の書いたコードが読めて、単純・定番な改善策を加えることができる"程度の証明にしかならないとも思いました。頑張っていつか銀以上のメダルを取ってみたいと思います。

Image Matching Challenge 2023について

www.kaggle.com

コンペの概要

大した順位ではないしこのコンペで使う技術に専門性があるわけでもなく、提出したのもベースラインのノートブックにちょっとした変更を加えただけのものなので真面目なSolution紹介をするつもりはないですが、一応どんなことをやったか書いておきます。

このコンペでは、同じ建物や物体を色々な場所から撮影された画像(以下同一シーン画像と呼びます)をもとに立体構造の復元を行う、Structure from Motion(SfM)を行います。具体的な提出内容としては、SfMの結果より得られる画像パラメータである3x3rotation_matrixと3次元のtranslation_vectorを各画像ごとに求め、flattenして次のようなCSVファイルとして提出します。

image_path,dataset,scene,rotation_matrix,translation_vector
da1/sc1/images/im1.png,da1,sc1,0.1;0.2;0.3;0.4;0.5;0.6;0.7;0.8;0.9,0.1;0.2;0.3
da1/sc2/images/im2.png,da1,sc1,0.1;0.2;0.3;0.4;0.5;0.6;0.7;0.8;0.9,0.1;0.2;0.3

SfMの原理的なところは他のサイトをご参照ください。ちなみに、私は次の本でさらってなんとなくわかった気になっています。この本は深くはないものの幅広く画像処理の基礎知識が載っているので辞書的な使い方ができて良き。

Amazon.co.jp: ディジタル画像処理[改訂第二版] : ディジタル画像処理編集委員会: 本

rotation_matrixtranslation_vectorをどのように求めるか、というところですが、実際のところはcolmapというパッケージ(と、それをPython用にラップしたpycolmap)が難しいところを全部やってくれます。

つまるところ我々が機械学習でやるべきことは、①同一シーン画像のペアを作り、②そのペア毎に学習済みのFeature DetectorとMatcherを使って一致するポイントをなるべくたくさん正確に見つけ出すことです。次のノートブックの図がわかりやすいかも。

https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F5065877%2F68911db7c4cc430dec05670cd196a960%2Fslide_architecture.png?generation=1687202186098466&alt=media

https://www.kaggle.com/competitions/image-matching-challenge-2023/discussion/416873

このとき、結構複雑なデータ処理パイプラインを構築する必要があるわりに、自前でモデルのトレーニングなどは行わず既存の特徴量抽出機やマッチングモデル(LoFTRやSuperGlue, DISCなど)を使うため、普通の機械学習コンペっぽくないデータエンジニアリングが重要なコンペでした。

自分のやった工夫

ノートブックはここ。

https://www.kaggle.com/code/bobfromjapan/imc-2023-submission-92nd-solution

  • ベースにしたのはこちらのノートブック: imc-2023-submission-example | Kaggle
    • 非常に良くできたノートブックで、本データセットにおけるSfMの処理を一通り行うことができるうえ、3つの特徴点抽出&マッチング方法(LoFTR, KeyAffHardNet, DISC)を実装している。
  • これのパラメータ(画像サイズ、ペア画像数の最低数、画像から取得する特徴点の数、特徴点の選択Threshold)を最適化するため、ローカルマシンで実行環境を整えてTrainデータを利用してグリッドサーチ
    • モデルを自分でトレーニングしないコンペであっても、やはりローカルでそこそこ良いマシンを持っているとこういうところで有利です。
  • 画像の明るさを調整するためにCLAHEを施す
    • これは明確に数%前半の精度向上があったと思う。同じ被写体をいろんな条件で撮った写真のマッチングなので、これはリーズナブルな結果かな?
  • 元の実装では最終的に構築された3次元モデルの中から最も利用された画像枚数が多いもののみを選択し一部の画像のみ結果を出すようになっていたが、これを全モデル利用するように変更。
    • Trainデータにおいての改善は微々たるものだったので、これが本当に意味があったかはわからん。まあサルでも思いつくような変更なので、理由があって最良モデルのみを選択していたのだと思う。
  • LoFTRとKeyAffHardNetのアンサンブル
    • これは今回精度向上には役立たなかった。KeyAffHardNet単体のほうが数%精度が良かった。

言い訳でしかないですが、眼精疲労が酷すぎたり、仕事関連でモチベが下降気味だったり、出張が重なったり、TrainデータとTestデータでかなりスコアが違っており改善効果が読みにくかったり、そもそもSfMに関する理解不足であまり根本的な改善案を入れられなかったりで、途中からまあワンチャン銅メダル取れればいいや的なスタンスでパラメータのチューニングだけしかやらなくなってしまいました。

しかし、今回のコンペに関してはこのパラメータ設定が争点の一つだったため、結果的に大したことはしてないものの銅メダルを取得できました。

反省

画像の対応付けに参考ノートブックの元実装のtorch.cdistから変更してFAISS使いたいとか、明らかに画像の向きが揃っていなかったのでこれを揃える改良をしたいとかアイデアはあったものの、やる気と時間がなく(改めてこれは言い訳で単なる実力不足だが)実装をやめてしまった。

結果的に上位陣の解法でもこの画像の対応付けと回転に対する対応が結構大きなウェイトを占めていたので、ここをもう少し頑張れていたら銀メダルくらいは取れたかもしれないとちょっと後悔……。

Kaggleのちょっとした闇の話

また、今回のコンペに限らずKaggle界隈で蔓延る不正行為の犠牲になりかけたので、その話も少し。実は最初コンペ終了時私の順位は101位/502グループ(!!) と、ギリギリ銅メダルを取れない順位にいました。

あまりにギリギリの順位だった(しかもPublicもPrivateも101位という奇跡)ので、最後頑張らなかったツケが回ってきたかと半笑いでスクショを撮っていたのだが、こんなDisscussionを発見。

Medals sellers are always there

どうやら中国のネットマーケット上でこのIMC2023のメダルの販売が行われているという内容。

今まで参加したコンペでもちょくちょくこういう不正の噂は聞いていたものの、今回はまさに当落線上にいる身としてこの話は無視できない。

今回は諸々の事情(おそらく、ここのディスカッションで議論されてたロシアのウクライナ侵攻に関連した、上位者のCVPR参加可否の件?)でだいぶ順位確定まで時間がかかったのですが、提出締切から1週間ほど経った頃に不正のチェックが終わり、順位が9位くらい上昇し、無事に銅メダル圏内となりました。

まあ、こんな不正業者に負けるようなスコアで喚くのも情けない話ですが、銅であってもメダルを貰えるに越したことはないので、スタッフの方々の頑張りには大変感謝しております。

【2023年最新版】 鯖落ちGPUを使った安価な機械学習用マシンの作り方

続編書きました:

hashicco.hatenablog.com

背景

このブログを始めた2020年頃に、NVIDIA Tesla K40mを使った安価な機械学習GPUマシンを紹介した。

hashicco.hatenablog.com

その後このマシンは勉強用に色々と活用していたのだが、2020年時点でもアーキテクチャが Kepler (Compute Capability 3.5) と古く、何より計算スピードが遅すぎてKaggleなどでの活用が難しい問題があり、 結局Google Colab ProやKaggle Notebookをメインで使っていた。

更に、最近はRTX4090をメインマシンに導入したこともあり、このK40mマシンの出番はほぼなくなってしまった。

で、先日メインマシンのCPUを7950X3Dに変更したことで、AM4のCPUとマザボ、64GBのDDR4メモリなど、ほぼPC一式分のパーツが余剰となった。 売ってお金にすればよいというのに自作趣味の悪いところが出てしまい、サブマシンの更新をしたい欲が沸々と湧き上がる。

とはいえ先ほど言ったようにK40mはもう古いしなぁ……などと思いながら eBayを物色していたところ(これが良くなかった)、 Keplerより2世代新しいサーバー用GPUである Tesla P40が$200程度で売られているのを見つけてしまった。

https://www.ebay.com/itm/374377271140www.ebay.com

ちょっと調べてみると、VRAMはRTX3090/4090と同じく24GB搭載しており、FP32演算性能としてはKaggleやGoogle Colabでも使えるP100と同等と、サブマシンには適当な性能に思える。一方でP100と異なり半精度 (FP16) の演算はHW的に最適化されていないようで、近年当たり前に使われるMixed Precision 利用時の性能には不安が残るものの、このキワモノ感も検証欲をそそられる。

ということで、詳しいことは買ってから考える精神でNVIDIA Tesla P40をポチって令和最新版の格安?機械学習用マシンを組んでみたというお話。

簡単な性能検証や気になっていたMixed Precision利用時の挙動を確認してみたので興味がある方は下へどうぞ。

まとめ

  • P40はお手頃価格の機械学習用24GB VRAM GPUになるポテンシャルあり。3万で買えるGPUとしては破格の性能だと思う。

    • K40m同様、 サーバー用GPUなので普通のGPUとは異なる工夫 (具体的にはPassive冷却や映像出力がないことに対する対応) が必要になる。 試行錯誤や失敗ウェルカムなパワーユーザーにしかお勧めできません。 また、本ブログの内容で損失を被っても私は責任を持ちません。
  • P100と比較すると、 P40はVRAM容量では勝るが、 VRAM帯域やFP16への対応などで劣る。 中古価格では1~2万程度の価格差でP100を買えるようなので、どっちを選ぶかは悩ましい。

    • 5万出せるならRTX3060 12GBを選ぶほうが製品寿命やワッパ、他用途への流用やリセール的に良い気もするのでさらに悩ましい。 その点色々割り切って安いP40を選ぶというのはありかもしれない
    • Pascalアーキテクチャももう4~5世代、7年前の製品なので、 CUDAサポートの打ち切りもいずれ来ることを考慮して購入検討すべき。
  • 懸念であった Mixed Precision 利用時の挙動であるが、確かにT4 GPUのような Mixed Precision利用を想定されているGPU(約80%高速化)に比べてトレーニング時間の削減効果は少なく、 MobileNetトレーニング時に20%程度しか計算時間を改善できなかった

    • ということで、簡易的な計測だがFP32 トレーニングではP40はT4 (Google Colab) よりも2割ほど高速だが、 Mixed Precision トレーニングではT4よりも2割程度低速になるという結果が得られた。
    • しかし、 VRAM使用量の削減効果は大きいので、 P40でもMixed Precision を使う価値はある。
  • 今回はeBayの中国セラーから送料込$207で購入したが、 状態は良好。 注文から10日程度で受け取れた。

    • 電源供給に必要なPCIe 8pin x2→ EPS 8pin ケーブルもNVIDIA純正?らしきケーブルがついてきた
  • MSIのB550マザー+5800X3Dの構成にP40と映像出力用の適当なGPUを刺してUbuntuをセットアップできた。

    • このマザーではグラフィック出力のないGPUの接続があまり考慮されていないようで、デフォルト設定ではUEFI POSTのVGAチェックでコケてしまい大分詰まった。 結局UEFIの設定をいろいろいじったらうまく行ったので、ここは後述する。
  • パーツは流用したから実質タダみたいなもの……という冗談はさておき、 今P40を採用しつつコスパ重視すれば、13万くらいでそこそこの機械学習用マシンを組めると思います。

    • 内訳はGPU: P40(3万)、CPU: 5900X(4.5万)、MB: B550(1.5万)、Mem: DDR4 64GB(2.5万)、 SSD: 1TB(0.7万)、電源: 750W (0.8万)。 ケースなしが許容され冷却用のファンとか細かいパーツはそこらに転がっている逸般の誤家庭 (死語) を想定

なにより、 おうちにサーバー用GPUのTeslaがあるのってロマンだよね! というところが一番大きなモチベだったりするので深く考えずに買っちゃいましたが、思ったよりも使える可能性を感じました。

昨今のLLMやDiffusion系生成モデルのようなVRAM食い機械学習モデルを動かす際にも使えるかもなので、その辺は今後使いながら検証していきたいと思います。とりあえず今回は一通りのセットアップと簡易的なベンチマークまでを記しておくつもり。

P40について

P40の基本スペックは

NVIDIA Tesla P40 Specs | TechPowerUp GPU Database

を参考に次の通りです。

  • Compute Capability: 6.1
  • CUDA cores: 3840
  • VRAM: 24GB GDDR5, 346GB/s
  • FP32演算性能: 12TFLOPS
  • FP16演算性能: 183.7 GFLOPS (1:64)
  • 消費電力: 250W

コンシューマ製品でいうと大体GTX1080 Tiのメモリ多い版と認識しておけば良いかもしれません。

また、ご覧の通りFP16の性能はFP32比で1/64と大きく削減されている(ちなみにP100は 2:1)。P40発売時の記事を見るとトレーニング用GPUとして発表されているものの、発売された2017年頃はまだTensorFlowやPyTorch(Chainer)などのフレームワークでも自動半精度やAuto Mixed Precisionのサポートもなかったであろうから、あまり重視されなかったのであろう。

届いたP40はちゃんとクリーニングされているのか保守在庫だったのかわかりませんが非常にきれい。 バックプレート側に"IASER"というメーカー名っぽいシールが新しく貼られているのですが、軽くググっても何を意味するのか分からず。 もしかするとサーバー用GPUのリファービッシュを販売している中国メーカーなのかもしれません。

P40は電源供給にEPS 8pin(マザボに指すCPU用の12Vコネクタと同じ)を使うのですが、PCIe 8pin x2からの変換ケーブルもNVIDIAのロゴの入ったタグの付いた純正?らしきものがついてきました。

K40mとの比較写真。Pascal世代のカードのデザインはNVIDIAぽさがあって良き。

P40はパッシブ冷却なので、前回作成したダンボールで作ったハウジングを取り付けたファンと一緒に使います。

組み立て

メインのパーツは前世代のPCから流用します。 また、我が家では床に直置きだと取り回しに問題があることが前回の経験からわかっていたので、今回は少しお金はもったいないですが所謂ベンチ台を購入してその上に組立しました。

  • MB: MSI MPG B550 Gaming Plus
  • CPU: Ryzen75800X3D
  • CPU cooler: Deep Cool AK400
  • Mem: DDR4 3200MHz 64GB (Micron, ADATA2枚ずつ混載)
  • SSD: MX500 1TB SATA
  • GPU (Graphics): Quadro K600
  • GPU (Compute): Tesla P40
  • PSU: Corsair RM1000
  • Case: 2WAY ベンチテーブル SMZ-2WBT-ATX

ゲームしないのに5800X3Dはもったいないので、 これだけは売ってしまって5900Xに買い替えたいとは思っています。

ちなみにベンチ台は13000円もしたのですが、正直ただの金属フレームです。安価に作る目的に反してちょっと高すぎる印象は受けますが、工作精度はそれなりに高くねじ穴ずれなどはなかったのでまあ納得して使っています。

Amazon.co.jp: 【長尾製作所×シミラボ】 2WAY ベンチテーブル SMZ-2WBT-ATX : ホーム&キッチン

出来上がりはこんな感じ。

UEFU設定、Ubuntuインストール

UEFI設定はだいぶ苦戦しました。 最初にP40を1つ目のx16に、 K600を2つ目のx16に刺したところ、 "VGA" のPOSTチェックLEDが点灯してしまいUEFI画面が映らず。

K600だけを接続したところちゃんとUEFIが映ったので、色々いじって試したところ次の設定でうまく行きました。 必要十分条件であるとは検証できていませんが、とりあえず私の環境では動いた条件として認識ください。

  • Above 4G memory/Crypto Currency mining: Enabled
  • Re-Size BAR Support: Enabled
  • VGA Detection: False
  • Full Screen Logo Display: Disabled

このUEFI設定でP40を1つ目のx16に、K600を2つ目のx16を刺して起動を確認できました。

Ubuntuは22.04を利用。 適当なUSBメモリにEtcherでISOを焼き、 普通にインストールを実行。

その後、openssh-serverのインストール固定IPアドレス設定CUDAインストールをリンク先を参考に行い、ベンチを取りました。

簡易ベンチマーク

TensorFlow2からの見え方

ベンチマークではないですが、一応。

>>> from tensorflow.python.client import device_lib
>>> device_lib.list_local_devices()
[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 5659398530349240384
xla_global_id: -1
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 23176478720
locality {
  bus_id: 1
  links {
  }
}
incarnation: 13990392878040664411
physical_device_desc: "device: 0, name: Tesla P40, pci bus id: 0000:2b:00.0, compute capability: 6.1"
xla_global_id: 416903419
]

ai-benchmark

この記事でRTX4090の性能を測るときにも紹介した、機械学習性能を簡易的に測れるPythonパッケージ。出力全文は最後に載せます。

>>> from ai_benchmark import AIBenchmark
>>> benchmark = AIBenchmark()

>>   AI-Benchmark-v.0.1.2
>>   Let the AI Games begin..

>>> results = benchmark.run()

*  TF Version: 2.12.0
*  Platform: Linux-5.19.0-38-generic-x86_64-with-glibc2.35
*  CPU: N/A
*  CPU RAM: 63 GB
*  GPU/0: Tesla P40
*  GPU RAM: 21.6 GB
*  CUDA Version: 11.8
*  CUDA Build: V11.8.89

~省略~

Device Inference Score: 11065
Device Training Score: 10797
Device AI Score: 21862

スコア的にはもちろんRTX4090には及ばず。公式のランキングを見ると一番近いスコアはRTX2070ですが、P40はVRAMが24GBもあるので実行できるモデルのサイズは段違い。しかもたった3万円であることを考えれば十二分なスコアではないだろうか。

MobileNetによるcats_and_dogs分類モデル

ここの記事で紹介した、TensorFlow2のチュートリアルで扱われているMobileNet V2でcats_and_dogsデータセットの画像分類モデルをトレーニングする速度を測りました。

比較対象がいないとわかりにくいと思いますので、Google ColabのT4 アクセラレータを有効化したインスタンスでも同様のコードを実行しました。もちろんCPUやディスクの性能は大きく違うので、対等な比較でないことを念頭に参考程度に見てください。

画像サイズを2種類(160x160, 512x512)、Mixed Precision有効/無効の4パターンで計測。2エポック目以降の実行時間(s/epoch)で比較します。

  • 160x160
P40 T4(Colab)
Mixed無効 5.78s 7.61s
有効 5.73s 6.84s
倍率 x1.01 x1.11
  • 512x512
P40 T4(Colab)
Mixed無効 45.71s 54.84s
有効 38.61s 30.47s
倍率 x1.18 x1.79

小さな画像サイズではP40のほうがT4よりも精度に関わらず速い結果に(おそらくColabのCPUかIO性能がリミットになっている?)。一方画像サイズが大きくなると、Mixed Precision無効化ではP40のほうが速いが、有効化ではT4よりも遅くなるという結果が得られた。

P40はあまりMixed Precisionによる演算速度向上の効果が得られにくいということがわかる。とは言え、やはり3万円でT4並の性能でVRAM 24GBのグラボが手に入るのは魅力的だと思う。

Mixed Precisionを利用したときの最大バッチサイズ

最後に、P40におけるMixed Precisionがどのように利用VRAM量に影響を与えるかを確かめるため、先程のMobileNet V2のモデルの入力を512x512にしたときにOOMを吐かずに動く最大バッチサイズを探ってみました。

  • Mixed Precision無効: max_bs=68
  • Mixed Precision無効: max_bs=112

結果、Mixed Precisionを有効にすることで、倍とまではいかないものの1.6倍以上のバッチサイズを設定できることがわかった。大きなモデルを利用するときなどには、P40であってもMixed Precisionを有効にするのが良さそうである。

ai-benchmark全文

>>> from ai_benchmark import AIBenchmark
>>> benchmark = AIBenchmark()

>>   AI-Benchmark-v.0.1.2
>>   Let the AI Games begin..

>>> results = benchmark.run()

*  TF Version: 2.12.0
*  Platform: Linux-5.19.0-38-generic-x86_64-with-glibc2.35
*  CPU: N/A
*  CPU RAM: 63 GB
*  GPU/0: Tesla P40
*  GPU RAM: 21.6 GB
*  CUDA Version: 11.8
*  CUDA Build: V11.8.89

The benchmark is running...
The tests might take up to 20 minutes
Please don't interrupt the script

1/19. MobileNet-V2

1.1 - inference | batch=50, size=224x224: 63.0 ± 0.8 ms
1.2 - training  | batch=50, size=224x224: 191.9 ± 0.5 ms

2/19. Inception-V3

2.1 - inference | batch=20, size=346x346: 62.0 ± 1.1 ms
2.2 - training  | batch=20, size=346x346: 255.4 ± 0.8 ms

3/19. Inception-V4

3.1 - inference | batch=10, size=346x346: 61.9 ± 1.0 ms
3.2 - training  | batch=10, size=346x346: 255.3 ± 0.6 ms

4/19. Inception-ResNet-V2

4.1 - inference | batch=10, size=346x346: 84.6 ± 0.7 ms
4.2 - training  | batch=8, size=346x346: 282.0 ± 0.6 ms

5/19. ResNet-V2-50

5.1 - inference | batch=10, size=346x346: 45.0 ± 0.6 ms
5.2 - training  | batch=10, size=346x346: 166.2 ± 0.4 ms

6/19. ResNet-V2-152

6.1 - inference | batch=10, size=256x256: 60.0 ± 0.7 ms
6.2 - training  | batch=10, size=256x256: 234.7 ± 0.6 ms

7/19. VGG-16

7.1 - inference | batch=20, size=224x224: 91.0 ± 1.0 ms
7.2 - training  | batch=2, size=224x224: 185 ± 1 ms

8/19. SRCNN 9-5-5

8.1 - inference | batch=10, size=512x512: 87.7 ± 3.3 ms
8.2 - inference | batch=1, size=1536x1536: 74.4 ± 2.1 ms
8.3 - training  | batch=10, size=512x512: 247 ± 3 ms

9/19. VGG-19 Super-Res

9.1 - inference | batch=10, size=256x256: 67.7 ± 0.5 ms
9.2 - inference | batch=1, size=1024x1024: 103.0 ± 0.7 ms
9.3 - training  | batch=10, size=224x224: 251.2 ± 0.9 ms

10/19. ResNet-SRGAN

10.1 - inference | batch=10, size=512x512: 116 ± 1 ms
10.2 - inference | batch=1, size=1536x1536: 105.7 ± 0.8 ms
10.3 - training  | batch=5, size=512x512: 198.5 ± 0.7 ms

11/19. ResNet-DPED

11.1 - inference | batch=10, size=256x256: 148.6 ± 0.5 ms
11.2 - inference | batch=1, size=1024x1024: 250.8 ± 0.4 ms
11.3 - training  | batch=15, size=128x128: 265.1 ± 0.5 ms

12/19. U-Net

12.1 - inference | batch=4, size=512x512: 262 ± 2 ms
12.2 - inference | batch=1, size=1024x1024: 262 ± 1 ms
12.3 - training  | batch=4, size=256x256: 304.0 ± 0.3 ms

13/19. Nvidia-SPADE

13.1 - inference | batch=5, size=128x128: 120.4 ± 0.6 ms
13.2 - training  | batch=1, size=128x128: 214.0 ± 0.0 ms

14/19. ICNet

14.1 - inference | batch=5, size=1024x1536: 178 ± 4 ms
14.2 - training  | batch=10, size=1024x1536: 507 ± 12 ms

15/19. PSPNet

15.1 - inference | batch=5, size=720x720: 513 ± 1 ms
15.2 - training  | batch=1, size=512x512: 207 ± 2 ms

16/19. DeepLab

16.1 - inference | batch=2, size=512x512: 142.6 ± 0.5 ms
16.2 - training  | batch=1, size=384x384: 162.8 ± 0.4 ms

17/19. Pixel-RNN

17.1 - inference | batch=50, size=64x64: 339 ± 9 ms
17.2 - training  | batch=10, size=64x64: 1055 ± 33 ms

18/19. LSTM-Sentiment

18.1 - inference | batch=100, size=1024x300: 480 ± 12 ms
18.2 - training  | batch=10, size=1024x300: 846 ± 19 ms

19/19. GNMT-Translation

19.1 - inference | batch=1, size=1x20: 174 ± 3 ms

Device Inference Score: 11065
Device Training Score: 10797
Device AI Score: 21862

Xit WirelessはサウンドチップALC4080から音を出せないっぽい

この間PCを新調したばかりなのだが、ちょっとしたトラブル。

以前も別記事でXit Wirelessをチューナとして使っていることを書いたが、またXit Wirelessでのコンテンツ視聴ができなくなった。

hashicco.hatenablog.com

今回は依存DLLの不足ではなく、起動まではうまくいくものの、コンテンツ保護エラーが出てTV/録画番組の視聴ができないという症状。

音声出力を確認しろ、とのことだったので音声をマザボの光デジタル出力からモニター(HDMI接続)に買えてみると、確かに問題なく再生される。

前のPCでは特に問題なくマザボの光デジタル出力でも視聴ができていたので奇妙に思ったのだが、PCの音声設定で気になる表示を発見。なぜかマザボの音声出力であるはずなのに、表示がRealtek USB Audioとなっているのである。

で、少し調べてみると、今回購入したX670-P WiFiに載っているオーディオチップであるALC4080は、USBを内部接続的に利用する代物であると分かった。

note.com

嫌な予感がしたのでXit WirelessのQ&Aページをみてみると案の定、「USB製品への音声出力には対応しておりません。」などと宣うではないか!

Xit AirBox(XIT-AIR100W) - Q&A - Windows | 株式会社ピクセラ

まあ著作権保護は大事なんだろうが、オーディオチップ大手のRealtekが作っていて普通にマザボに載っている音声出力から音を出せないと言うのはどうなのだろう……。コンシューマPCでもALC4080搭載製品が増えていくと、同様の問題が頻発しそうである。

他にも、AMD製のグラボを使っていたときもXit Wirelessは起動できなかったりしたし、あまりPIXELAの製品に期待するのは無駄な気はするが、対応待ちになるだろう。大してテレビは見ないので良いのだが、せっかくPCを新調したのに少し残念な気持ちである。

7950X3Dを買ったのでCCD毎のベンチとか取ってみた

端書

ブログでもちょこちょこ書いているように、GPU(RX6900XT→RTX4090)、CPU(5600X→5800X3D)などメインマシンのパーツを割りと頻繁に変えながら使ってきたのだが、マザボやCドライブの変更を伴わなかったので1年半近くOSをクリーンインストールしていなかった。

Win11もそろそろ安定してきた頃だろうし、昨年末AMDのZen4世代の7000番代CPUやIntelの13世代CPUが登場しチップセットやメモリの世代も新しくなったし、OS入れ替えのついでというのを言い訳にPCの更新をすることに。

5800X3Dで3D-Vcacheの威力を思い知っていたので、他のパーツをディアゴスティーニ方式で集めながらZen4世代の3D-Vcache搭載CPUが出るのをやきもきしながら待っていたところ、ようやくグローバルでは先月末の2023/2/28に7950X3Dが発売された。

ただ、国内の発売日当日(2023/3/3)は普通に仕事だったため11:00の争奪戦に参加すらできず買えない結果に……。そこから暇があれば商品登録が行われていなかったPCパーツ取り扱いのある通販サイト(Amazon.co.jpNTT-Xなど)を巡回していたのだが、結果的に3/10のちょうど職場の昼休憩の時間中ジョーシンのネットショップで30分近く7950X3Dが販売されていたので、ここで入手することができました。

発送もスムーズに行われ、土日に商品が着荷したので早速PCを組んでベンチを取ってみたというお話。

比較対象は今持っているRyzen7 5800X3Dなので、同コア数の比較でRyzen9 7950X3DのCCD0のみ, CCD1のみのベンチも取ってみたので興味があれば下へどうぞ。

まとめ

  • ベンチの結果から見ると、ゲーム性能は圧巻。5800X3Dから比べてIPC向上・周波数上昇・コア数倍増の効果もあり、文句なしにハイエンドと言えるCPU性能

    • Cinebench R23で5800X3D比マルチが2.5倍、シングルで1.3倍。
    • 特にFF14に関しては3D-Vcacheの効果が顕著であった。4KでもCCD0,CCD1でスコアが2割位変わる
    • 当初CCD1に3D-Vcacheを搭載していないのは欠点と思っていたが、結構周波数による性能差が馬鹿にできず、かつ3D-Vcacheが効く局面が限られていることから、ヘテロなコアであるのは幅広いワークロードに最適化できるという意味でリーズナブルであると感じた。
  • 私の構成ではファンを変更した忍者5による空冷で7950X3Dを運用しているが、割りと無理なく使えていると思う。

    • 流石にCinebenchのマルチコアではリミットの89度に張り付いてしまうので100%性能を発揮できているとは言えないが、クロックの減少は50~100MHz程度と僅かであった。
    • TDPが通常の7950Xよりも低く、CCDも2つに分かれるので5800X3Dよりも冷やしやすい。
  • 国内定価を見るとまだ割高感はあるものの、ゲームをするなら3D-Vcacheの効果はちゃんとあるし、ヘテロな構成のお陰でシングル性能も悪くなく、マルチの性能はトップクラス、と現行世代コンシューマハイエンドとして所有欲の満たされる一品。十分買う価値のあるCPUであると思う。

    • また、CCD0に絞った7800X3D想定のゲーム性能を見るに、7800X3Dにもかなり期待できると思う。FF14用PCを組むなら文句なしにX3Dシリーズをおすすめしたい。

構成

結局数年交換していなかったPCケース、電源も含めてパーツを大幅に更新しました。

  • PCケース: CoolerMaster CM690 Ⅲ→ Fractal Design Torrent ¥30800 @アークオンラインストア
  • 電源: Corsair RM1000x→ CoolerMaster MWE GOLD 1250 V2 ¥30247(5746ポイント還元)@ NTT-X
  • マザーボード: MSI B550 GAMING PLUS→ MSI X670-P WIFI ¥29776(1786ポイント還元) @Amazon アウトレット
  • メモリ: DDR4 3200MHz 4x16GB→ Micron 4800MHz 2x32GB ¥25980(5217ポイント還元) @ジョーシン(ヤフショ)
  • CPU: 5800X3D→ 7950X3D ¥111800 @ジョーシン
  • ついでにHyperVを使えるように、Windows10 HomeからWindows11 Proに買い替え ¥21964@Amazon

以下のパーツは流用しました。

  • GPU: ZOTAC RTX4090 Trinity OC ¥301020
  • SSD1: ADATA XPG GAMMIX S70 ¥27980
  • SSD2: Micron MX500 1TB ¥12000くらい?
  • CPUクーラー: 忍者5、ファンはAINEX Omega Typhoon 高速タイプに変更 合計¥10000くらい?

これらパーツの合計金額は約600000円……。改築を繰り返して結果的に現世代の標準的なハイエンド構成に落ち着いたと思うが、なかなかのお値段。

自分が自作を始めたIvy Bridge時代から考えると、コンシューマのハイエンドの値段はめちゃくちゃ上がってると言わざるを得ない。当然性能は上がっていますが、もう学生じゃ簡単に手が届かない金額ですね。

ベンチマーク

取ったベンチマークは以下の通り

  • CineBench R23
  • 3DMark
    • TimeSpy Extreme, 無印
    • FireStrike Ultra, Extreme, 無印
    • CPU Profile
  • CPU-Z
  • VRMark
  • PCMark10
  • FF14ベンチ(4K, ウルトラワイド3440x1440)
  • ブループロトコルベンチ ウルトラワイド3440x1440

スコアの表中に出てくる"7950X3D CCD0"は3D-Vcacheがある方、"7950X3D CCD1"はない方です。Ryzen Masterを使うことで片方のCCDのみ有効化にすることが可能です。

ちなみに5800X3Dの方はケースが違うので、ちゃんと冷やしきれておらず少しスコアが平均より低めです。

CineBench R23

5800X3D 7950X3D 7950X3D CCD0 7950X3D CCD1
Multi Core 13568 34937 18099 19621
Single Core 1450 1945 1840 2019

3DMark

  • グラフィックス系 - Total -
5800X3D 7950X3D 7950X3D CCD0 7950X3D CCD1
TimeSpy Extreme 13405 17488 14369 14911
TimeSpy 25562 28466 28969 28674
FireStrike Ultra 23078 24392 24130 24207
FireStrike Extreme 35055 40053 38967 38294
FireStrike 46847 53511 55004 53800
  • グラフィックス系 - Physics, CPU -
5800X3D 7950X3D 7950X3D CCD0 7950X3D CCD1
TimeSpy Extreme 4972 11534 5933 6584
TimeSpy 9817 14413 1840 12841
FireStrike Ultra 25363 41934 33127 35575
FireStrike Extreme 25901 42029 33280 35550
FireStrike 25994 42050 33318 35274
  • CPU Profile
5800X3D 7950X3D 7950X3D CCD0 7950X3D CCD1
16 Threads 6682 12910 8111 9126
8 Threads 4939 7548 6603 7370
4 Threads 3169 4225 3748 4184
2 Threads 1653 2185 1887 2136
1 Threads 858 1107 962 1119

FF14

一部レポート出力忘れてFPSの情報がわかりません。

ただ、一番CCD0とCCD1のスコアが異なったのがこのFF14ベンチ。2割以上のスコア差が生じています。

また、興味深いことにCCD0のみに絞ったほうが全コアを使ったときよりもスコアが高いです。コアの振り分けがうまく行っていないのか、9コア以上のスケーリングが良くないのかわかりませんが、これは7800X3Dの性能にも非常に期待が持てますね。

  • 4K
5800X3D 7950X3D 7950X3D CCD0 7950X3D CCD1
Score 25114 28109 29077 23559
Ave. FPS - 187.555 192.6833 -
Min. FPS - 99 106 -
  • Ultra wide 3440x1440
5800X3D 7950X3D 7950X3D CCD0 7950X3D CCD1
Score 31843 34848 37059 29587
Ave. FPS - 240.9709 251.8916 218.5995
Min. FPS - 92 98 71

CPU-Z(v 17.01.64)

ベンチ取りすぎて疲れたので、CPU-Z、VRMark、PCMark10、ブループロトコルベンチはCCD毎の結果を取っていません。あしからず

5800X3D 7950X3D
Multi 5792.5 15096.7
Single 571.4 720.7

ちなみにCPU-Zの表示はこんな感じ。AVX512Fが命令セットに入っているのとか、5nmプロセスなのとか、Zen4らしくてテンション上がりますね。また、L3キャッシュが96MB+32MBになっているのがX3Dならではでしょうか。

VRMark

5800X3D 7950X3D
Orange 15177 16344
Cyan - 12417
Blue 11812 11701

PCMark10

5800X3D 7950X3D
Total 7899 9746
Essential 9945 11543
Productivity 9240 11573
Digital Content Creation 14555 18802

ブループロトコル

こちらは最近出たと聞いたので試しにやってみました。ランチャー画面、設定項目、レポート出力機能など、すごくFF14みのあるベンチです。

こちらは画面の選択機能がなく解像度切替が面倒だったので、ウルトラワイドのみで実行しました。

また、CPUに関係なくスタッターが生じる現象が確認されました。最小FPSはあてにならないと思いますので載せません。

5800X3D 7950X3D
Score 33755 34940

写真

前回の5800X3Dは箱がスカスカでしたが、今回はマグネットで蓋が閉まる少しいい箱に入っています。

久々に色々とパーツを変更。マザボが高いのが今世代のAMDのがっかりポイントですが、Amazon アウトレットにてX670で3万以下のものを見つけて買ってみたところ、問題なく動作しました。自分が使っている10G NICがPCIe x8の接続を持つので、RTX4090に潰されない位置にPCIe x16スロットがあるものを選択。ただ、今回購入したX670-P WiFiは一番下のスロットがPCIe4.0 x2接続らしいので、残念ながらNICのフルスペックは出ません。

とは言えX670でスロット多くて3万以下は珍しいので、買えてよかったと思います。

今回フルタワーのTorrentにケースを変更。自分は空冷主義なのでずっとこのケースが気になっており、思い切って買い替えてみました。

底面、前面から大量の空気を送り込めるので、M.2 SSDチップセットの冷却も安心です。組み立ても裏配線スペースやケーブルを通す穴がたくさんあるので非常にやりやすい。電源も12VHPWRに対応しているものを買ったので、非常に表の見栄えがスッキリしました。

ただ、SSD, HDDを裏配線スペースに配置するので、曲げにくいSATAケーブルをマザボまで持っていくのが非常に大変。今回のマザボに至っては4本もM.2を挿せるので、もう内蔵ストレージはすべてM.2にしてしまいたい。

※ちなみに画像だと一番下のM.2にSSD指してますが、ここはPCIeスロットとレーンを共有するため併用できません。後で一番上に移し替えました。