「諸行無常の響きあり」は、平安時代末期の軍記物語『平家物語』の語り出しのフレーズで、「祇園精舎の鐘の音には、この世のすべての現象は絶えず変化していくものだという響きがある」という意味です。
「諸行無常」は仏教の根本的な思想である三法印の一つで、世のすべてのものは移り変わり、生まれては消滅する運命を繰り返し、永遠に変わらないものはないという意味です。
ショートストーリー「世の中は最適解を求めて移ろうものである。ニューラルネットワークもまたしかりである。」
東京に住むプログラマーの真田和也は、日々AIモデルの最適化に取り組んでいた。彼の最新の挑戦は、ニューラルネットワークのトレーニングを通じて、データに隠されたパターンを解き明かし、できるだけ最良のモデルパラメータを導き出すことだった。しかし、ニューラルネットワークのトレーニングは容易ではなく、最適解を探る過程はときに迷路のようだった。
和也は改めて思った。「ニューラルネットワークとは、所詮、所望の出力と入力をマッピングさせるための重み行列や重み関数の最適化問題に過ぎない。」勾配降下法を使ってこの重み関数を調整し、誤差を小さくして収束を目指すが、その方法では時折、局所的な最低点に陥り、真の最適解には到達できないことも多かった。彼は、自身のスキルと時間をかけて作り上げたモデルが、結局は「近似解」に過ぎない現実に少しもどかしさを感じていた。
和也はふと考えた。「もし、この重み関数を最適化するための革新的な手法が存在すれば…」彼の脳裏には、現在の勾配降下法に代わる、より柔軟で創造的な最適化プロセスが必要だという思いが浮かんだ。
もし、誤差が下がり止まったところで勾配の方向を逆にしたらどうだろうか?勾配降下法の計算符号を反転し、誤差を一時的に拡散させる方向に進めることで、今まで見えなかった新しい経路を発見できるかもしれない。そして、再度勾配の符号を元に戻し、また収束を目指す。拡散させるフェーズで一度高みへ引き戻し、再度収束させる。この交互のプロセスは、局所解から抜け出し、より広い空間での最適解を探るための新たな試みだった。
誤差が下がり止まった時、勾配の方向を逆にして一時的に拡散させ、また再度収束の方向へ進める交互のアプローチ。
和也の中にはさらなる方法を模索する意欲が湧き上がった。
こうして彼は、重み関数の最適化を進化させるための新たな手法を模索し始めた。それは、単なる数値の調整に留まらない、ネットワークが自らの「答え」を探し求めているような、動的かつ探求的な最適化の旅だった。和也はその先にある、未知の最適解を見つけ出すために、限りない挑戦に乗り出すことを決意した。
彼はコンピュータのスクリーンを見つめ、静かに呟いた。「ニューラルネットワークの最適化は、人生のようなものかもしれない。絶えず答えを探し、進化し続けること。それが本当の意味での最適解なのかもしれない。」
コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「index.html」として保存します。その後、保存したファイルをブラウザで開けば、コードが実行されます。
説明
行列の生成:
A, B_normal, B_reversal, C_targetをTensorFlow.jsのtf.randomUniformで生成。
トレーニングループ:
各イテレーションでtf.matMulを用いて現在の出力を計算。
誤差行列、損失の計算、重みの更新、符号の反転を行います。
メモリリークの防止:
disposeで不要なテンソルを解放。
グラフの描画:
Chart.jsを使って、lossHistoryNormalとlossHistoryReversalを表示。
このコードを実行すると、ブラウザでグラフとしてロスの推移が見られ、勾配降下法の通常バージョンと符号反転バージョンの比較ができます。
『平家物語』のコード。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Gradient Descent: Normal vs Reversal</title>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
</head>
<body>
<h1>勾配降下法のシミュレーション: 通常 vs 符号反転</h1>
<canvas id="chart" width="600" height="400"></canvas>
<script>
// 行列の次元
const n = 10;
const learningRate = 0.001;
const iterations = 3000;
const switchInterval = 300;
let sign = 1;
// 行列の初期化
const A = tf.randomUniform([n, n]);
let B_normal = tf.randomUniform([n, n]);
let B_reversal = tf.randomUniform([n, n]);
const C_target = tf.randomUniform([n, n]);
// ロスの保存リスト
let lossHistoryNormal = [];
let lossHistoryReversal = [];
async function train() {
for (let i = 0; i < iterations; i++) {
// 通常のCと符号反転Cを計算
const C_current_normal = tf.matMul(A, B_normal);
const C_current_reversal = tf.matMul(A, B_reversal);
// 誤差行列を計算
const error_normal = C_current_normal.sub(C_target);
const error_reversal = C_current_reversal.sub(C_target);
// 損失(MSE)を計算して記録
const lossNormal = error_normal.square().mean().dataSync()[0];
const lossReversal = error_reversal.square().mean().dataSync()[0];
lossHistoryNormal.push(lossNormal);
lossHistoryReversal.push(lossReversal);
// 勾配を計算してBを更新
const grad_B_normal = tf.matMul(A.transpose(), error_normal);
const grad_B_reversal = tf.matMul(A.transpose(), error_reversal);
B_normal = B_normal.sub(grad_B_normal.mul(learningRate));
B_reversal = B_reversal.sub(grad_B_reversal.mul(sign * learningRate));
// インターバルごとに符号を反転
if ((i + 1) % switchInterval === 0) {
sign *= -1;
console.log(`Iteration ${i + 1}: Loss (reversal) = ${lossReversal} (sign reversed)`);
} else if (i % 100 === 0) {
console.log(`Iteration ${i}: Loss (normal) = ${lossNormal}, Loss (reversal) = ${lossReversal}`);
}
// メモリリークを防ぐためにテンソルを削除
C_current_normal.dispose();
C_current_reversal.dispose();
error_normal.dispose();
error_reversal.dispose();
grad_B_normal.dispose();
grad_B_reversal.dispose();
await tf.nextFrame(); // 画面を更新
}
plotLoss(); // 学習が完了したらロスのプロットを実行
}
function plotLoss() {
const ctx = document.getElementById('chart').getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: Array.from({ length: iterations }, (_, i) => i + 1),
datasets: [
{
label: 'Normal Gradient Descent',
data: lossHistoryNormal,
borderColor: 'blue',
fill: false
},
{
label: 'Reversal Gradient Descent',
data: lossHistoryReversal,
borderColor: 'red',
fill: false
}
]
},
options: {
responsive: true,
scales: {
x: { type: 'linear', title: { display: true, text: 'Iteration' } },
y: { title: { display: true, text: 'Loss' } }
}
}
});
}
// 学習を実行
train();
</script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</body>
</html>