動いた。でも学習していなかった。」— 自作LLMの静かな罠
注釈
※本記事は個人による学習・研究過程の記録です。
商用モデルの再現や特定企業技術の再実装を目的としたものではありません。
数学的理解と実装検証を主目的としたスクラッチ実装の過程を共有しています。
記述内容には試行錯誤段階のコード・設計も含まれます。
動いた。
C++で書いた自作 mini_llm。
ビルドも通った。
train_llm.exe も infer_llm.exe も動いた。
ログはこうだ。
step 10 loss=0
step 20 loss=0
...
step 100 loss=0
train ok一見、完璧。
でも──
これは成功ではなかった。
学習していないのに、loss=0
普通、学習初期のクロスエントロピーは
loss ≈ log(vocab_size)例えば語彙16なら 2.77 付近になる。
それがゼロ。
つまりこれは、
何も学習していない。
forward は通っている。
backward も呼ばれている。
optimizer.step() も動いている。
でも中身は空。
「動く」と「学習する」は別物
今回分かったこと。
LLM開発で一番怖いのは、
コンパイルが通ること
ではなく
正しく勾配が流れているか
だった。
TransformerBlock4Dは動いている。
Attentionも計算している。
でも
重みが使われていない
CEが本物ではない
backwardが実質ダミー
だから loss=0。
これは失敗か?
いや、違う。
これは
構造が完成した瞬間
だ。
心臓はできた。
血管もつながった。
でも血が流れていない。
今いる場所は、
「動く模型」から「学習するモデル」への境界線。
地獄は抜けたか?
前はコンパイル地獄だった。
API不整合地獄だった。
ヘッダとcppが食い違う地獄だった。
今日は違う。
今日の問題は
数学。
これは前進。
次にやること
本物のSoftmax
本物のCrossEntropy
本物の勾配
ここまで来たら、
もう「フレームワークごっこ」ではない。
DBAなのに
OracleのHWMやindex fragmentationを追っていた人間が、
いま attention の勾配を追っている。
タグは違うかもしれない。
でも、
理解したいからやっている。
それだけだ。
Attention backward を本気で実装した日
今日は、
Multi-Head Attention の backward をフルスクラッチで実装した。
逃げずに、ちゃんと。
■ 何をやったのか?
・QKV の線形変換
・Scaled Dot-Product Attention
・Softmax
・Wo の最終射影
・そしてそれらすべての逆伝播
数式だけ理解しているつもりではダメだった。
実装すると、
「どこに何を保存しておかないといけないか」
が一気に可視化される。
■ 本当に難しかったのはどこか
Wo の勾配が壊れる。
softmax backward が直感と違う。
Q と K の対称性に混乱する。
4Dテンソルの reshape で頭がバグる。
forward では簡単に見えたものが、
backward では一気に複雑になる。
でも、それが“本物”だった。
■ 今日わかったこと
Attention は魔法ではない。
やっていることは:
内積
スケーリング
正規化
重み付き和
線形変換
それだけ。
でも、それを
全部つなげて逆向きに流すと地獄になる。
■ でも嬉しかったこと
loss がちゃんと下がった。
NaN が出なかった。
勾配爆発もしなかった。
それはつまり、
「理論と実装が一致した」ということ。
■ ここまで来て思うこと
Python で1行で書ける世界の裏に、
これだけの計算がある。
フレームワークを使うのは悪くない。
でも一度くらい、
全部バラしてみるのも悪くない。
■ 明日は?
・Causal Mask
・RoPE
・FlashAttention 構造化
まだやる。
今日の一言
LLMは難しい。
でも、
分解すると“ただの線形代数”になる。
それがわかってきた。
