1. はじめに:なぜ「揺らす」のか?
突然ですが、みなさんは 「粉粒体(砂や砂利)」を箱いっぱいに詰め込むとき、どうしますか?
おそらく、箱を横に小刻みに揺すったり、トントンと床に打ち付けたりすると思います。
私は大学の卒業研究で、まさにこの「どう揺らせば最も密度高く詰められるか?」という実験をしていました。
研究の日々の中で、ふとある仮説が浮かびました。
「これ、機械学習の損失関数の最小化と同じじゃないか?」
- 準安定状態 $\approx$ 局所解
- 水平振動 $\approx$ 局所探索
- 鉛直振動 $\approx$ 大域探索
-
充填効率の最大化 $\approx$ 損失関数の最小化
こんな感じで。
もし、物理実験で効果的だった「振動のアプローチ」を学習アルゴリズムに組み込んだら、局所解(Local Minima)にハマりやすい複雑な関数でも、効率よく最適解を見つけられるのではないか?
そんな思いつきを検証するために、「物理ベースの振動」を組み込んだオプティマイザを自作して実験してみました。
2. 実験のアイデア:3つの「揺れ」
今回は既存のライブラリ(AdamやSGD)は使わず、挙動を完全制御するためにNumPyでフルスクラッチ実装しました。Numpy使用は見逃してください(;´д`)
実装したのは、物理実験のアナロジーである以下の3つのモードです。
① Horizontal Mode(水平振動)
- 物理イメージ: 箱を横に細かく揺すって、粒子同士の摩擦を減らし、動きやすくする。
- 実装: 勾配(Gradient)に対して、常に一定のガウスノイズを加え続ける。
- 狙い: 平坦な場所でも学習を停滞させず、細かい探索を促進する。
② Vertical Mode(鉛直振動/タッピング)
- 物理イメージ: 箱をドン!と床に打ち付け、強い衝撃で粒子を大きく跳ね上げて配置をリセットする。
- 実装: 数千エポックごとに、重み(Weights)そのものに大きなノイズを加え、強制的に現在の解から飛ばす。
- 工夫: ただ壊すだけでは学習が崩壊するため、「衝撃を加える直前のベストな状態を保存しておき、最終的に一番良かった時代の重みを採用する(いわゆる強くてニューゲーム)」 という機構を入れています。
③ Hybrid Mode(複合振動)
- 物理イメージ: 微細な横揺れを与えつつ、時折強い縦揺れを加える「最強の充填手法」。
- 実装: 上記のHorizontalとVerticalを組み合わせたもの。
3. 実験環境
最適化検証をするため、多数の局所解を持つRastrigin関数(ラストリギン関数) を近似ターゲットとしました。
▼ 今回挑む相手(Rastrigin関数)
${f(x)=10+x^2-10\cos(2 \pi x)
}$
ネットワーク構成
- 構造: 入力層(1) $\to$ 中間層(64) $\to$ 中間層(64) $\to$ 出力層(1)
-
活性化関数:
tanh- ※とりあえず自分が使い慣れている関数だったので採用しました。ここは今後改良予定です。
- パラメータ: ノイズの大きさや衝撃の間隔は、何度か試行錯誤(トライアンドエラー)して良さそうな値を設定しました。
# 最適化クラスのイメージ(抜粋)
class PhysicsOptimizer:
# ...
def apply_horizontal_vibration(self, network):
"""水平振動: 勾配にノイズを乗せる"""
if self.mode not in ["Horizontal", "Hybrid"]: return
for layer in network.layers:
# 勾配にノイズを混入
noise = np.random.normal(0, self.noise_scale, layer.weights_gradient.shape)
layer.weights_gradient += noise
def apply_vertical_vibration(self, network, epoch):
"""鉛直振動: 重みに直接衝撃を与える"""
if self.mode not in ["Vertical", "Hybrid"]: return
if (epoch + 1) % self.shock_interval == 0:
for layer in network.layers:
# 重み自体を大きく揺らす
shock = np.random.normal(0, self.shock_scale, layer.weights.shape)
layer.weights += shock
4. 結果:ハイブリッドが強すぎた
4つの条件(None, Horizontal, Vertical, Hybrid)で10,000エポック学習させた結果がこちらです。
- 左図:Rastrigin関数の近似結果(グレーが正解、各色が学習結果)
- 右図:Loss(損失)の推移
もっときれいにフィッティングさせるために調整する気力がちょっとありませんでしたご容赦を、、、
結果の考察
-
None (黒) / Horizontal (赤)
- 失敗: グラフを見ると、複雑な波形を捉えきれず、適当なところで妥協してしまっています(局所解にハマっている)。水平振動だけでは、深い谷から抜け出すパワーが足りなかったようです。
-
Vertical (青)
- 惜しい: Lossグラフ(右図)の定期的なスパイクは「衝撃」を与えたタイミングです。衝撃で強制的に再探索させることでNoneよりは良い精度ですが、まだフィットしきれていません。
-
Hybrid (緑)
- 他の手法と比べて、フィッティング精度が図抜けて高い結果となりました。
- 水平振動を混ぜたことでただのタッピングにとどまらず、僅かな振動によってより深い谷に落ち込むことができた様子が見て取れます。
5. まとめ
物理実験の知見である**「微細なノイズ(水平)」と「定期的な大きな衝撃(鉛直)」を組み合わせる**というアプローチは、ニューラルネットワークの学習においても有効かもしれないという第一回目の記事にしてはありがたい結果が得られました。
- Horizontal が細かい探索を助け(悪あがき)、
- Vertical が局所解からの脱出を担う(ガチャ)
この役割分担がうまくハマったのだと考えられます。
今回は単純な関数近似でしたが、物理現象とアルゴリズムの類似性はまだまだ奥が深そうです。今後は活性化関数の見直しや、より高次元なタスクでも検証してみたいと思います。
気ままにトライアンドエラー頑張っていきます。
なお、おそらく先行研究があるとは思います。ですがそれらはおそらく熱力学的なアプローチから得られたものが多く、今回は自分の粉粒体工学的なアプローチから行くとどのようなものが得られるのかという試験的な取り組みでもあるため、
それ前からあるよ、があったとしても温かい目で見ていただけると嬉しいです(;_;)
追記:新しく実験を行いPart2にまとめたので、よろしければ見ていってくれると嬉しいです:D
追記(2026/2/28):Part1の直系の世界線となるPart3を公開いたしました!!
見ていただけると嬉しいです!m(_ _)m
