はじめに
前回の記事【AIロマン】複数LLMを再学習ゼロで物理合体!Hotswappable LLMの実装と検証では、巨大なLLMをモジュール化し、後から独立学習した「特化シナプス」を再学習なしで物理的に合体(Hot-Swap)させるアプローチについて解説しました。
しかし、知識を「足せる」ようになったのであれば、当然次に湧き上がる疑問があります。
「もし追加した知識が不要になったら?」「ベースモデルの中から『数学の知識』だけを完全に抜き取って容量を空けたい場合はどうするのか?」
既存のLLMにおける「アンラーニング」の絶望的な難しさ
現在の巨大なLLM(モノリスモデル)から「特定の知識だけを忘れさせる(Unlearning)」ことは至難の業です。すべての知識が一つの巨大なパラメータ空間に複雑に絡み合っているため、特定の機能や知識を消そうとして再学習を行うと、全く無関係な汎用的な対話能力まで破壊されてしまう「破滅的忘却」が起きてしまいます。
しかし、私が開発・検証を進めているモジュラーAI構造「SRA(Synaptic Routing Architecture)」であれば、この問題は非常にシンプルかつ物理的に解決できます。 不要になったシナプスを直接切り離すか、空っぽにすればいいのです。
本記事では、Hotswappable LLMの真の完成形とも言える「シナプス削除」と「特定ドメインのパージ(抜き取り)」の実装と、その過程で直面した 「ルーティングの罠」 について解説します。
SRAにおける「削除」の2つのアプローチ
SRAにおいて、不要な知識を削除するには大きく分けて2つのアプローチがあります。
アプローチ①:シナプスの取り外し(物理的カット)
1つ目は、一番シンプルで直感的な方法です。後から追加(Hot-Swap)したシナプスが不要になった場合、モデルの重みテンソルの末尾から直接物理的に切り捨ててしまいます。
# 例:一番最後に追加した2つのシナプスを物理的に取り外す
def pop_synapses(self, num_drop: int):
# 重みテンソルの末尾から num_drop 分だけスライスして削る
self.synapse_emb = nn.Parameter(self.synapse_emb.data[:-num_drop])
Pythonのテンソル操作としてはこれだけです。この手法の最大のメリットは、 VRAM(メモリ)の使用量を物理的に縮小でき、モデルを追加前の状態に完全に復元できる ことです。OSのプラグインをアンインストールするように、AIの脳のパーツを物理的に取り外すことができます。
アプローチ②:特定ドメイン知識のパージ(ゼロクリア)
しかし、アプローチ①には弱点があります。それは「末尾からしか削除できない」ということです。
もし、「ベースモデルの初期学習時から組み込まれていた『数学(Math)』専用のシナプスだけを途中のインデックスから削除したい」場合はどうすればよいでしょうか?
途中のテンソルを物理的に削除してしまうと、 それ以降のシナプスのインデックス(ID番号)がすべて前にズレてしまいます。 SRAでは「特定のシナプスにのみルーティングを許可する」といったメタデータマスクの制御を行っているため、IDがズレると既存の制御システムが完全に崩壊してしまいます。
そこで採用したのが、 シナプスの「ゼロクリア(空きスロット化)」 です。
# 例:インデックス3番のシナプス(数学特化)をパージする
def clear_synapses(self, indices_to_clear: list[int]):
for idx in indices_to_clear:
# 重みとルーターのエンベディングを完全に 0.0 で上書きする
self.synapse_emb.data[idx].zero_()
self.w1.data[idx].zero_()
self.w2.data[idx].zero_()
テンソルのサイズ(形)は一切変えず、特定のスロットの中身だけを強制的に 0.0 で上書きします。これにより、インデックスの整合性を完璧に保ったまま、そのシナプスが持っていた知識だけを完全に無効化できます。
しかも、空きスロットとなったインデックスには、後から新しいシナプスをピッタリと上書き(Hot-Swap)して再利用することが可能です!
技術的裏話:ゼロクリアとコサイン類似度の「恐ろしい罠」
「ゼロクリアすれば無効化できるだろう」と単純に考えて実装したところ、 恐ろしいバグ に直面しました。
特定ドメインをゼロクリアしたモデルを実行してみたところ、 出力が完全に崩壊 してしまったのです。
原因は、SRAのルーターが行っている「コサイン類似度」の計算ロジックにありました。
SRAのルーターは、入力されたデータと各シナプスの「エンベディング(代表ベクトル)」の内積(コサイン類似度)を計算し、最もスコアが高いシナプスに処理をルーティングします。
- シナプスをゼロクリアすると、そのエンベディングも
[0.0, 0.0, ...]になります。 - ゼロベクトルを正規化(Normalize)しても、結果はゼロベクトルのままです。
- 入力ベクトルとゼロベクトルのコサイン類似度を計算すると、結果の Logit は
0.0になります。
ここで問題が起きます。コサイン類似度は「-1.0 〜 1.0」の値をとります。
もし、入力データに対して 正常なシナプスのコサイン類似度がマイナス(例:-0.5)だった場合、なんと「完全に空っぽのシナプス(0.0)」の方が数学的にスコアが高くなってしまい、ルーターが空のシナプスを優先的に選んでしまう という逆転現象が発生したのです!
「存在を消したはずの空きスロットに、データが吸い込まれて消滅する」という、まるでブラックホールのような挙動でした。
解決策:空きスロットの完全封鎖
このバグを防ぐため、ルーターのフォワードパス(順伝播)に、ゼロクリアされたシナプスを検知して完全にルーティングから除外する(マスクする)処理を追加しました。
# 入力とシナプスのコサイン類似度を計算
logits = torch.einsum("btd,nd->btn", h_norm, emb_norm) * self.scale
# 追加:ゼロクリアされた(空きスロットの)シナプスをマスクし、絶対に選ばれないようにする
is_cleared = (full_emb == 0).all(dim=-1)
if is_cleared.any():
# スコアをマイナス無限大(-inf)にして、Top-Kルーティングで除外
logits = logits.masked_fill(is_cleared.view(1, 1, -1), float('-inf'))
スコアを -inf に設定することで、どれだけ他のシナプスのスコアが低くても、空のシナプスが選ばれることは絶対に無くなりました。これにより、ようやく安全な「知識のパージ」が完成しました。
実際にブラウザで試せます
今回実装した「シナプスの取り外し」と「特定ドメインのゼロクリア」の全プロセスは、以下のGoogle Colabノートブックで実際に動かして体験できます。
ノートブックでは、実際にシナプスを追加した後に物理的に削り落としてサイズを戻す処理や、ルーターのノルムが 0.0 にパージされる様子を確認できます。
おわりに
これにて、 「学習 → 合体(Hot-Swap) → 削除(パージ) → スロットの再利用(上書き)」 という、モジュラーAIとしての完全なライフサイクルを実現することができました。
モデルが賢くなるほどブラックボックス化が進む現代において、知能のパーツを物理的に抜き差ししてコントロールできる「Hotswappable LLM」のアプローチは、モデルのメンテナンス性や安全性の観点からも非常に有望な方向性だと確信しています。
この「AIロマン」に共感していただけた方は、ぜひリポジトリを覗いてみてください!
- GitHubリポジトリ: SynapticRouter