はじめに
いよいよ5月も終わり、もうすぐ私の嫌いな梅雨がやってきます。
そんな中、ひょんなことから6月からCUDAやPyTorchを使った案件に参画することになりました。
4月頃に「Ai案件に関わるかもしれない」と予感があったため、事前に生成AIを家庭教師にしたり、書籍を購入したりして、Google Colabを中心に予習を進めてきました。
しかし、やはり年齢的なものもあるのか、新しい概念(特にカタカナ用語)を覚える力は衰えを感じざるを得ません。スカラ、ベクトル、マトリックスまでは過去30年の開発経験から理解していたつもりでしたが、蓋を開けてみれば本来の意味を履き違えていたことに気づかされました。
特にダメだったのが「テンソル」。これがどうにも頭に入ってこないのです。
ましてや多次元の話になると、人間が直感的にイメージできるのは3次元(Excelのシートを3枚組み合わせて立方体にするような感覚)までです。4次元以上をどう判断すればいいのか全く分かりませんでした。
業を煮やしてAIに聞いてみたら、「4次元以上は人間には直感的に理解できません」と一蹴され、逆に妙に安心したのを覚えています。
まあ、30年もこの世界(IT・組込み業界)にいると、多次元配列のプログラム自体は扱ったことがあります。要するに「脳内イメージは諦めて、コードとインデックスで覚えればいい」と割り切ることができました。
カタカナ用語との格闘と、実機(RTX 3060)の導入
その後もCNNだ、RNNだ、と新たなカタカナ用語との格闘が始まりました。
やはりここまで来ると、クラウド(Colab)ではなく「実際にGPUを購入して、手元のローカル環境でプログラムを動かしてみたい」というエンジニアの性(さが)が疼きます。
そこで、Windows 11非対応の古いCPUを積んだ手持ちのワークステーション(DELL Precision T3620)に、ASUSの GeForce RTX 3060 12GB を搭載することにしました。
これまで仕事一筋で、PCでゲームをすることなど一度もありませんでした。
思えば40年前、NECのV30(CMOS版8086互換)を販売していた頃、某C社の係長から「8087(浮動小数点数演算コプロセッサ)は購入できないか?」と相談されたのが、私が数値演算プロセッサの存在を知った最初でした。
その後、業務でOpenGLやDirectXに多少触れる機会はあったものの、当時の私は「小数点以下のデータがビットレベルでどう管理されているか」を深く気にすることはありませんでした。まさかその範囲を真に理解しようとするのが30年後(今)になるとは、人生分からないものです。
色(Color)の扱いについては、Windows 95の画面のプロパティで「1677万色(RGB)」を確認したのが原体験です。その後、時代はRGBAになり、ゲーム用PCではGPUがないと処理が追いつかずに画面がカクカク動く環境を見てきました。
GPUの体感
いざグラフィックボードを買おうとすると、種類の多さに驚かされました。供給電源の容量問題、VRAM容量、そして価格。自習用とはいえ、このクラス(RTX 3060 12GB)になると中古でも4万円以上はしますし、物理的にスロットへ設置するためPCIeの空きも必須です。
無事にパーツが届き、ケースを開けて搭載しましたが、最近のGPUはヒートシンクの塊で「重いのなんの」。昨今、世間で騒がれている「AIデータセンターの電力供給・冷却問題」を、身を以てミニチュア体験した気分でした。
しかし、苦労して構築したローカル環境を動かしてみると、Google Colab(無料版)とは比較にならないほど速い。 やはり実機は正義です。
今回の環境は、Ubuntu Desktop 24.04 Lts。実際にIDEは、VsCode、様々な便利なツールもありもうWindow環境には戻れないほどで、リモートデスクトップもありかならいい環境ができた。
コードで割り切るPyTorch
私と同じように「テンソル」や「4次元以上」で躓いているプログラマの方に向け、私なりの「割り切り方」をメモしておきます。高尚な数学として捉えるから破綻するのであって、メモリ空間として見れば一発でした。
4次元テンソル [B, C, H, W] の正体
画像認識(CNN)で頻出する4次元テンソルは、脳内で4次元空間を歪ませる必要はありません。
ただの「構造体(画像データ)がメモリ上にシリアルに並んだ配列」です。
Python
import torch
脳内イメージ:224x224のRGB画像を、メモリ上に16枚連続で確保した巨大な配列
images = torch.randn(16, 3, 224, 224)
print(images.shape) # torch.Size([16, 3, 224, 224])
C言語の感覚で言えば、ただの「4重ループでアクセスする多次元配列」であり、ポインタの指す先にある一本のメモリ領域に過ぎません。
40年前の「コプロセッサ」と同じ構造
PyTorchでのGPU操作の基本も、やってみればかつてのx87コプロセッサ(8087)のデータ転送と本質は同じでした。
Python
import torch
-
データをメインメモリ(CPU側)に確保
cpu_tensor = torch.randn(1000, 1000) -
PCIeバスを通して、GPUのVRAM(RTX 3060)に転送
gpu_tensor = cpu_tensor.to('cuda') -
GPU側で超並列計算(一瞬で終わる)
result_gpu = torch.matmul(gpu_tensor, gpu_tensor) -
結果をメインメモリ(CPU側)に回収
result_cpu = result_gpu.to('cpu')
メインCPUが苦手な重い浮動小数点演算を、専用のハードウェア(GPU)に丸投げしてバス(PCIe)経由で戻す。40年前に追いかけた技術の延長線上に、今の生成AIやディープラーニングがあるのだと思うと、非常に感慨深いものがあります。
おわりに
新しい挑戦への舞台は整いました。記憶力は若い頃に敵いませんが、これまでに培ってきた「構造で理解する力」と「泥臭くコードを読み解く経験」を武器に、6月からの実務に挑もうと思います。
次回以降は、実際の仕事の話も含めて、老兵がどうAI・ディープラーニングを勉強し、現場でハンドリングしていくかの泥臭いプロセスを掲載していきたいと思います。