この記事は、AI時代にNVIDIAがどれだけすごいかということを
勉強し始めて1か月の初心者の独り言です。
目次
1.AIとは行列計算と三角関数のかたまり
この項で言いたいことは、「AIは多次元のデータを一気に処理する行列計算と、
データの変換に関わる三角関数を大量に計算した結果である。」です。
AIは、Excelで例えると「究極の予測式」を自動で作る仕組みです。
| 工程 | エクセルの例 |
| 入力と正解を並べる | A列に「画像データ」、Z列に「正解ラベル(猫・犬)」を数億行並べます |
| 間の列を埋める | B列からY列までに、最初は適当な数字が入った計算式をギッシリ埋めます |
| 誤差を埋める特訓 | A列のデータを計算して、Z列の正解とズレていたら、B~Y列の計算式の「数字」を少しずつ書き換えます(これが重みの更新) |
| 関数が完成 | 何兆回も繰り返すと、どんな画像を入れても正しい答えを導く「黄金の計算式(B~Y列)」が完成します |
| 本番 | 完成した後は、正解(Z列)を消しても、A列に新しい画像を入れるだけで、B~Y列を通り抜けた結果が「猫」と出てくるようになります |
実際にはこの計算(行列計算)が膨大すぎて、普通のパソコン(CPU)ではフリーズしてしまうため、計算専用のモンスターマシン(GPU)を使って力技で終わらせているのが今のAIです。
もう少しAIっぽい表現を使います。
AIが処理の中で一般的に行う工程です。
| 工程 | 作業内容 | 一般的な呼び方 | 保存先 |
| 準備1 (田舎DC) | モデルに特徴(重み)を覚えさせる | 学習 (Training) | モデルファイルに保存 |
| 準備2 (田舎DC) | 未学習のデータ等をベクトル化してDBに入れる | インデキシング (Indexing) | ベクトルDBに保存 |
| 実行1 (都会DC) | ユーザーの質問をベクトル化 | 埋め込み (Embedding) | メモリ |
| 実行2 (都会DC) | ベクトルDBから似たデータを探す | 検索 (Retrieval) | メモリ |
| 実行3 (都会DC) | ベクトル化結果、検索結果からモデルを通し回答を作る | 推論・生成 (RAG) | 結果返却 |
※DC=データセンター
※ベクトル化=あらゆるデータを3次元空間の(x軸, y軸, z軸)座標のイメージで保存する(実際にはn次元)
意味の近さが、距離の近さになる
※検索(Retrieval)では角度が0に近いことで似たデータを探すことが多いです
例「猫が好き」
「猫が好きで好きで毎日吸ってます」
は意味(角度)は同じですが、文字数(距離)が違いすぎて意味が違うと判定されてしまうことがあります
土地や電力コストが低い田舎では、データ量が多く処理負荷が大きい学習・インデキシングを行います。
ユーザーに近い都会では、田舎で整理されたデータをコピーし、それをもとに埋め込み・検索・推論・生成を行います。
Geminiさんに最も典型的なAIプログラム例を作ってもらいました。
このプログラム例から言えることは(この項で言いたいこと)、
・行列計算をたくさんしている
・三角関数の概念を活用している
ということなのです。
プログラムの解説を少しだけすると、
multiply関数でforが3重にネストしているところがn次元×n次元×n次元の繰り返し処理で、
「1件の学習データ」や「1つの質問」に対する計算量が数十億~数百億回になる部分です
それをネット上のありとあらゆるデータで学習したり、ユーザーからの質問の答えを推論するのです。
Javaプログラム例
public class AiProcessSimulation {
public static void main(String[] args) {
// --- 1. 準備:学習 (Training) & インデキシング ---
// モデルの「重み」:特徴を [ペット適性, 野生度] に変換する2x2の行列
double[][] weightMatrix = {
{0.8, 0.2}, // 「動物らしさ」の重み(左:ペット、右:野生)
{0.1, 0.9} // 「可愛さ」の重み
};
// ベクトルDBに保存された「正解データ」の座標(インデキシング済み)
// ここでは「理想的なペット」という概念の座標を [1.0, 0.0] とします
double[] vectorDB = {1.0, 0.0};
// --- 2. 実行:推論 (Inference) / 行列の掛け算 ---
// 入力:[猫のデータ(動物度, 可愛さ), 犬のデータ(動物度, 可愛さ)]
double[][] inputData = {
{0.9, 0.8}, // 猫
{0.7, 0.4} // 犬
};
System.out.println("【工程:推論】行列計算により特徴を変換します...");
// 入力(2x2) × 重み(2x2) = 変換後の特徴(2x2)
double[][] transformed = multiply(inputData, weightMatrix);
// --- 3. 実行:検索 (Retrieval) / 三角関数の応用 ---
System.out.println("\n【工程:検索】ベクトルDBとの角度(類似度)を計算します...");
String[] labels = {"猫", "犬"};
for (int i = 0; i < transformed.length; i++) {
// ここでコサイン類似度(三角関数)を使用
double score = calculateCosineSimilarity(transformed[i], vectorDB);
System.out.printf("%s のペット親和性スコア: %.4f\n", labels[i], score);
}
}
/**
* 行列の掛け算:AIが入力データを「意味の空間」へ飛ばす作業
*/
public static double[][] multiply(double[][] A, double[][] B) {
double[][] C = new double[2][2];
for (int i = 0; i < 2; i++) { // 入力データの行(猫、犬)
for (int j = 0; j < 2; j++) { // 重みの列(ペット性、野生度)
for (int k = 0; k < 2; k++) { // 掛けて足す「内積」の計算
// ここがAIの計算の9割を占める「積和演算」
C[i][j] += A[i][k] * B[k][j];
}
}
}
return C;
}
/**
* コサイン類似度:三角関数を用いて「原点からの角度」を求める
* cos(θ) = (A・B) / (|A| * |B|)
*/
public static double calculateCosineSimilarity(double[] vecA, double[] vecB) {
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (int i = 0; i < vecA.length; i++) {
dotProduct += vecA[i] * vecB[i]; // 分子:内積
normA += Math.pow(vecA[i], 2); // 分母:大きさの計算(2乗)
normB += Math.pow(vecB[i], 2);
}
// √(2乗の和)で割ることで、純粋な「角度(cos)」を取り出す
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
}
****実行結果****
【工程:推論】行列計算により特徴を変換します...
【工程:検索】ベクトルDBとの角度(類似度)を計算します...
猫 のペット親和性スコア: 0.6644
犬 のペット親和性スコア: 0.7682
********
ベクトルDBについて独断と偏見で補足します。
| データベースの種類 | 特徴 | 流行した時代 |
| RDB | ほぼエクセル。金融取引で振込元と振込先のデータ更新を同時に行わなければならないような場面が得意 | 2000年代 IBM,ORACLEの時代 |
| NoSQL | テキストファイルのイメージ。例えばtwitterのシステムを作成するならユーザーごとにテキストファイルを用意してその中につぶやきの履歴を格納する。テキストファイルが分かれていれば日本のユーザーのデータは日本サーバーに、米国のデータは米国サーバーに保管を分散させられる。 | 2010年代 SNS,MongoDBの時代 |
| ベクトルDB | NoSQLにベクトルデータ(文章の意味)をもたせたイメージ。例えばtwitterで「猫」を検索するときNoSQLだと「猫」を含む文章を検索していたが、ベクトルDBだと猫っぽい文章「タマのエサ」も検索にヒットさせられる。 | 2020年代 AIの時代 |
2.3Dグラフィックは三角関数と行列計算のかたまり
この項で言いたいことは、「3Dグラフィックは三角関数と行列計算のかたまりである。
GPUは3Dグラフィックスを専門的に行うものであるので、 GPUは三角関数と行列計算をCPUよりはるかに高速に処理できる。」です。
NVIDIA等のGPUはもともと3Dグラフィックス処理を専門とする機械でした。
3Dグラフィックスを描くとき、画面上では座標の「回転」や「拡大縮小」が行われています。
これらはすべて、sinやcosを用いた回転行列によって計算されています。
具体例として立方体が回転する3Dグラフィックスのプログラム例を描いてみました。
Javascriptプログラム例
このプログラム例から言えることは(この項で言いたいこと)、
・行列計算をたくさんしている
・三角関数の概念を活用している
ということなのです。(AIの項と同じ)
3D 回転デモ
辺の長さ:
x' = x ・ cos(θ) - z ・ sin(θ)
z' = x ・ sin(θ) + z ・ cos(θ)
z' = x ・ sin(θ) + z ・ cos(θ)
ソースコードの解説
このデモで使用している行列計算の核心部分関数は以下の通りです。
// 1. 立方体のローカル座標 (頂点データ)
const vertices = [
[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1],
[-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]
];
const edges = [
[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6],
[6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]
];
// --- 【行列計算部分】 ---
// ラジアン角に変換
const rad = angle * Math.PI / 180;
/* * [ Y軸周りの回転行列 ]
* | x' | | cosθ 0 sinθ | | x |
* | y' | = | 0 1 0 | | y |
* | z' | | -sinθ 0 cosθ | | z |
*/
let nx = x * Math.cos(rad) - z * Math.sin(rad);
let nz = x * Math.sin(rad) + z * Math.cos(rad);
/* * [ X軸周りの回転行列 ]
* | x'' | | 1 0 0 | | x' |
* | y'' | = | 0 cosθ -sinθ | | y' |
* | z'' | | 0 sinθ cosθ | | z' |
*/
let ny = y * Math.cos(rad * 0.5) - nz * Math.sin(rad * 0.5);
nz = y * Math.sin(rad * 0.5) + nz * Math.cos(rad * 0.5);
// --- 【スケーリングと投影】 ---
nx *= size; ny *= size; nz *= size;
// 透視投影 (遠近法): 奥(z)に行くほど座標を中央に寄せる
const distance = 400;
const factor = distance / (distance + nz);
// 全頂点を行列計算で変換
const projectedPoints = vertices.map(v => project(v[0], v[1], v[2], size));
// 描画処理
ctx.strokeStyle = '#00ffcc';
ctx.lineWidth = 2;
edges.forEach(edge => {
const p1 = projectedPoints[edge[0]];
const p2 = projectedPoints[edge[1]];
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
});
angle += 1; // 角度を更新
requestAnimationFrame(draw);
3.NVIDIAのCUDAは計算を高速化する
NVIDIAが覇権を握った理由は、この膨大な行列計算を「並列処理」で爆速にするCUDAというプラットフォームを構築したからです。↑こういう説明を散々みました。で、CUDAって何?って話です
Geminiさんに最も典型的なCUDAプログラム例を作ってもらいました。
C++プログラム例(C++からCUDAを利用するのが王道みたいです)
#include <iostream>
#include <cuda_runtime.h>
// 1. GPUで実行されるカーネル関数
__global__ void matrixAddKernel(float *A, float *B, float *C, int N) {
// スレッド番号の計算
int i = blockIdx.x * blockDim.x + threadIdx.x;
// 配列の範囲内であれば計算を実行
if (i < N) {
C[i] = A[i] + B[i];
}
}
int main() {
int N = 4; // 2x2 行列なので要素数は 4
size_t size = N * sizeof(float);
// 2. ホスト(CPU)メモリの準備
float h_A[] = {1.0f, 2.0f, 3.0f, 4.0f};
float h_B[] = {5.0f, 6.0f, 7.0f, 8.0f};
float h_C[4];
// 3. デバイス(GPU)メモリのポインタと確保
float *d_A, *d_B, *d_C;
cudaMalloc(&d_A, size);
cudaMalloc(&d_B, size);
cudaMalloc(&d_C, size);
// 4. データをCPUからGPUへ転送
cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);
// 5. カーネルの起動 GPUに処理を命令
// 第1引数: ブロック数, 第2引数: ブロックあたりのスレッド数
matrixAddKernel<<<1 n="">>>(d_A, d_B, d_C, N);
// 6. 結果をGPUからCPUへ書き戻す
cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);
// 結果の表示
std::cout << "Result Matrix (2x2):" << std::endl;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
std::cout << h_C[i * 2 + j] << " ";
}
std::cout << std::endl;
}
// 7. メモリの解放
cudaFree(d_A);
cudaFree(d_B);
cudaFree(d_C);
return 0;
}
1>
AIの処理をするプログラムの中にNVIDIAのGPU(CUDAライブラリ)を利用して処理するコードが直接書かれていて
世界中のデータセンターにCUDAプログラムが星の数ほど埋め込まれているのです。
NVIDIAのGPUから他のメーカーのGPUに乗り換えるなら、
プログラムを修正しないといけないのが深い堀になっているのです。
話題が少しそれますが、
NVIDIAのGPU処理が高速な理由がGPUの並行処理が得意ということです。
1つのサーバーにCPUが1個、GPUが2個挿さっていて並行処理させるプログラム例
個人のPCでGPUを2枚挿している人もいますが、
AIデータセンターのサーバーでは8枚挿して並行に計算させるのが一般的なようです。
1つのサーバーにCPUが1個、GPUが2個挿さっていて並行に処理させる例
#include <iostream>
#include <cuda_runtime.h>
// 1つの列(2要素)を加算するカーネル
__global__ void addColumnKernel(float *d_A, float *d_B, float *d_C) {
int i = threadIdx.x; // 0行目と1行目をスレッドで並列化
if (i < 2) {
d_C[i] = d_A[i] + d_B[i];
}
}
int main() {
// 2x2行列のデータ (列優先で考えると扱いやすい)
// GPU 0用 (0列目): {A[0,0], A[1,0]} = {1, 3}
float h_A0[] = {1.0f, 3.0f};
float h_B0[] = {5.0f, 7.0f};
// GPU 1用 (1列目): {A[0,1], A[1,1]} = {2, 4}
float h_A1[] = {2.0f, 4.0f};
float h_B1[] = {6.0f, 8.0f};
float h_C0[2], h_C1[2]; // 結果格納用
float *d_A0, *d_B0, *d_C0; // GPU 0用ポインタ
float *d_A1, *d_B1, *d_C1; // GPU 1用ポインタ
size_t col_size = 2 * sizeof(float);
// --- GPU 0 の処理 ---
cudaSetDevice(0);
cudaMalloc(&d_A0, col_size);
cudaMalloc(&d_B0, col_size);
cudaMalloc(&d_C0, col_size);
cudaMemcpy(d_A0, h_A0, col_size, cudaMemcpyHostToDevice);
cudaMemcpy(d_B0, h_B0, col_size, cudaMemcpyHostToDevice);
addColumnKernel<<<1 2="">>>(d_A0, d_B0, d_C0); // 0列目の計算開始
// --- GPU 1 の処理 ---
cudaSetDevice(1);
cudaMalloc(&d_A1, col_size);
cudaMalloc(&d_B1, col_size);
cudaMalloc(&d_C1, col_size);
cudaMemcpy(d_A1, h_A1, col_size, cudaMemcpyHostToDevice);
cudaMemcpy(d_B1, h_B1, col_size, cudaMemcpyHostToDevice);
addColumnKernel<<<1 2="">>>(d_A1, d_B1, d_C1); // 1列目の計算開始
// --- 結果の回収 ---
cudaSetDevice(0);
cudaMemcpy(h_C0, d_C0, col_size, cudaMemcpyDeviceToHost);
cudaSetDevice(1);
cudaMemcpy(h_C1, d_C1, col_size, cudaMemcpyDeviceToHost);
// 結果の統合表示
std::cout << "Result Matrix (Combined from 2 GPUs):" << std::endl;
std::cout << h_C0[0] << " " << h_C1[0] << std::endl; // 0行目
std::cout << h_C0[1] << " " << h_C1[1] << std::endl; // 1行目
// 解放 (各デバイスをセットしてから解放)
cudaSetDevice(0); cudaFree(d_A0); cudaFree(d_B0); cudaFree(d_C0);
cudaSetDevice(1); cudaFree(d_A1); cudaFree(d_B1); cudaFree(d_C1);
return 0;
}
1>1>
並行処理をさせるとGPU1個あたりの処理量が減るので処理時間の大幅な短縮になります。
GPUを並行処理させるというのは一般的なやり方で
↓図はLINEヤフーの引用ですが、
(1サーバーにCPU=1、GPU=4)×2サーバーの図で、
NIC(LANケーブルを挿しこむ穴の機械)8つを介したネットワークで接続されることで
プログラムは8つのGPUを認識できるようになります。
※NICはGPUに対して必ずしも1対1である必要はないが、
NICが少ないと、GPUからデータが殺到したときに送信待ちが発生してしまい、
高価なGPUが送信待ちで遊ばせるのはもったいないから1対1が最も効率的な可能性が高いと思われます。
ネットワーク接続のケーブルは短くて太い銅線であったり、長くて細い光ファイバーであったりしますが、
チップ間のデータ送受信は「チップ(電気)→ 基板(電気)→ NIC(電気)→ 【光モジュールで変換】 → 光ファイバー」
というように「電気+銅線」「光+光ファイバー」で混在していたものをチップで光に変換することで
データ送受信速度をさらに向上させようという技術が次世代の光電融合といわれています。
データセンターでAIサーバーを構築するためにはCPUはそれほど必要ではないが、
GPUやケーブルが大量に必要になってくるのです。
また、この図にはありませんがメモリDRAMも大量に必要になります。
たとえば動画の学習をするさい、動画1ファイルあたり容量が1GBを超えることはよくあります。
GPUのなかのメモリVRAMにおさまらなければメモリDRAMに一時保存をしながら処理しますので、
メモリDRAMもあればあるだけ嬉しいという性格のものです。
4.NVIDIAの牙城を崩すもの
NVIDIAのCUDAエコシステムがAI計算の標準となっています。ライバルのAMDの「ROCm」やGoogleAIクラウド専用の「TPU」に加え、
クラウド企業の「独自ASIC(Trainiumなど)」が依存を減らす動きとして台頭しています。
依存を減らす動きというのは、ソフトウェアの観点では
サンプルプログラムにあったCUDAエコシステム「cuda_runtime.h」を前提としたプログラムを
AMDで動く命令に置き換えてGPUもプログラムも置き換えてしまう動き、
どんなGPUやTPUでも動く共通言語「Triton」や「PyTorch」が普及する動きがあります。
NVIDIA自身もCUDA縛りからマルチプラットフォームに舵を切っています。
ハードウェアの観点では競合がいろいろ出てきてて、
ブログ主の主観で評価するとこんな感じのようです(異論は認める)
| 種類 | 企業 | チップ | 主な機能 | 性能 | 柔軟性 | 価格 |
| GPU | NVIDIA | Rubin | 行列計算・並列処理・画像描画 | 高 | 高 | 高 |
| GPU | AMD | MI350 | 行列計算・並列処理・画像描画 | 高 | 高 | 中~高 |
| カスタムASIC | TPU(Tensor Processing Unit) | 行列計算(AI学習・推論特化) | 極高 | 低 | 低~中 | |
| カスタムASIC | Meta | MTIA | 行列計算(AI推論・レコメンド特化) | 高 | 低 | 低 |
| カスタムASIC | Microsoft | Maia | 行列計算(LLM推論特化) | 高 | 低 | 中 |
| カスタムASIC | Amazon | Trainium/Inferentia | 行列計算(学習・推論) | 高 | 低 | 低~中 |
ちなみにMicrosoftのコンピューター機器の減価償却期間は2年~6年とされています。
個人向けNVIDIAのGPUは2年ごとにバージョンアップされますが、
AIサーバー向けGPUはそんなに頻繁に買い替えるのか?
最初は処理が重い学習用のAIサーバーで利用して、
2年で性能が陳腐化したら推論用のAIサーバーに転用するのかもしれません。(わかりません)
チップのニュースで「TSMCの3nmプロセス」という言葉をよく見ます。
原子の大きさは0.1~0.3nm程度なので、半導体は原子数十個ほどのスケールで作られる非常に精密な技術です。
マスク(型枠)を使って回路をチップに焼き付ける機械(露光装置)を作っているのがASMLで、 最先端のEUV露光装置はほぼASMLだけが作れます。
また、その回路を写すためのマスク(型枠)の欠陥を検査する装置で強いのがレーザーテックだそうです。
5.AIの学習が終わったその後
内閣の成長戦略会議の引用です。・これまでインターネット上の大量のテキストデータを学習し、性能を向上させてきた生成これまではGAFAを中心にネット上のきれいなデータを学習するステップでした。
AIも、昨今では目前に迫っている「学習データの枯渇」が大きな問題に。
・今後は、全世界で流通するデータの6割を占める企業内データ(≒エンタープライズデータ)
の利活用が産業戦略上の焦点に。
これから各企業がAIを利用するなら企業内の秘密データを覚えさせる(インデキシング)必要があります。
ただ、企業内のデータの問題として
・紙しかない
・会社合併の名残で合併前2社のデータフォーマットがばらばら
・蓄積されたデータがそもそも少ない、偏っている
という問題がでてくるような気がして
そういうデータをクレンジングするサービス(SIer?)の需要が徐々に生まれてくるんじゃないかと思うんです。
ブログの先パイに勧められてツイッターはじめてみました。



0 コメント:
コメントを投稿