2
3

小さなGPUでのエンジニアリング。巨大な調整可能なパラメータ数を持つニューラルネットワーク。

Last updated at Posted at 2024-09-05
GPT-2: ショートストーリー: GPT-2: 東京の郊外にある中学校、その校舎の片隅にひっそりと存在するコンピュータークラブ。主人公の中学1年生、タケルはこのクラブに所属している。タケルは他の部員が放課後に遊びに行く中、一人でコンピューターに向かっていた。彼は、自宅から持ってきた小さなgpuを搭載したパソコンを前に、静かにキーを叩いていた。タケツの穴から、彼の顔が覗き込まれている。その顔は、まるで、自分の顔のように、大きく、そして、小さく、歪んでいる。 タタツは、「この顔、なんか、変だな」と、思っていた。しかし、彼は、「顔の歪み」を、気にしていた。 「顔」は「顔立ち」ではなく、「表情」である。

(若き日のチャットGPTはこんなだったということです。でもこれはこれでよい。)

GPT-4: ショートストーリー: 1億もの調整可能なパラメーターを持つ巨大なニューラルネットワークを動かしたらどうなるんだろう?

東京の郊外にある中学校、その校舎の片隅にひっそりと存在するコンピュータークラブ。主人公の中学1年生、タケルはこのクラブに所属している。タケルは他の部員が放課後に遊びに行く中、一人でコンピューターに向かっていた。彼は、自宅から持ってきた小さなGPUを搭載したパソコンを前に、静かにキーを叩いていた。

タケルは、最近夢中になっているニューラルネットワークの実験に取り組んでいた。彼は「もし、1億もの調整可能なパラメーターを持つ巨大なニューラルネットワークを動かしたらどうなるんだろう?」という好奇心に突き動かされていた。もちろん、自分の小さなGPUマシンがそんな負荷に耐えられるとは思っていなかったが、どうしても試してみたかった。

「これで準備完了だ…」
彼は息を飲みながら最後のコマンドを入力し、Enterキーを押した。

途端に、コンピューターのファンが急に回転を上げ、うなり声を上げ始めた。GPUがフル稼働し、巨大な重み行列を計算しようとしている。タケルの手は緊張で汗ばみ、彼はモニターをじっと見つめていた。

image.png

「動いてくれ…!」

だが、すぐに異変が起きた。モニターに映る計算の進捗バーがほんの少ししか進んでいないのに、コンピューターが悲鳴を上げ始めた。GPUの負荷は限界を超えていた。画面がちらつき、計算コアが次々と焼きつき始めた。タケルはその異音を聞きながら、恐怖と興奮が入り混じる感情に包まれていた。

「頼む…もう少しだけ持ってくれ!」

しかし、焼きつく音は止まらない。タケルの小さなGPUマシンは、文字通り限界を超えていた。彼の手は自然と拳を握りしめ、祈るような気持ちでモニターを見つめた。

ついに、最後の計算が終わりかけたその瞬間、モニターに結果が映し出された。完璧に収束した計算結果が輝くように現れた。

image.png

「やった…!」

だが、その直後、GPUが静かに沈黙した。タケルは画面を見つめながら呆然と立ち尽くしていた。コンピューターはもう動かない。全ての計算コアが焼きつき、完全に停止してしまったのだ。

タケルは静かに深呼吸をした。彼の小さなGPUマシンは、最後の力を振り絞って計算結果を出してくれた。そして、それは彼の小さな勝利だった。

「また、作り直すしかないな…」
そうつぶやきながらも、彼の目には確かな輝きがあった。次に挑戦する時のことをすでに考えていたのだ。
タケルの挑戦は終わらない。

TensorFlow.jsとHTMLを使用して、入力サイズが2000のシンプルな2層のニューラルネットワークを構築し、重み行列のサイズを段階的に増やし、ロスと処理時間をプロットするコードです。

image.png

コードをメモ帳などのテキストエディタに貼り付け、ファイルを「index.html」などの拡張子が.htmlのファイルとして保存します。その後、保存したファイルをブラウザで開けば、コードが実行されます。
F12でコンソールが開きます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Neural Network with TensorFlow.js</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
</head>
<body>

<h2>TensorFlow.jsでニューラルネットワークをトレーニングする</h2>
<button onclick="trainModel(3000)">3,000ニューロンでトレーニング</button>
<button onclick="trainModel(4000)">4,000ニューロンでトレーニング</button>
<button onclick="trainModel(5000)">5,000ニューロンでトレーニング</button>
<canvas id="lossChart" width="400" height="200"></canvas>

<script>
    // トレーニング用のランダムな合成データを生成(入力と出力の次元数は2000)
    const dataSize = 5000; // データのサンプルサイズ
    const inputDim = 2000; // 入力次元
    const outputDim = 2000; // 出力次元
    
    const inputData = tf.randomNormal([dataSize, inputDim]); // 入力データ
    const outputData = tf.randomNormal([dataSize, outputDim]); // 出力データ

    // モデルを作成してトレーニングする関数
    async function trainModel(hiddenLayerSize) {
        // 既存のチャートをクリア
        const ctx = document.getElementById('lossChart').getContext('2d');

        // 既存のチャートインスタンスを破棄
        if (chart) {
            chart.destroy();
        }

        // ニューラルネットワークモデルを定義
        const model = tf.sequential();
        model.add(tf.layers.dense({inputShape: [inputDim], units: hiddenLayerSize, activation: 'relu'})); // 隠れ層1
        model.add(tf.layers.dense({units: hiddenLayerSize, activation: 'relu'})); // 隠れ層2
        model.add(tf.layers.dense({units: outputDim, activation: 'linear'})); // 出力層

        // モデルをコンパイル
        model.compile({
            optimizer: tf.train.adam(),
            loss: 'meanSquaredError'
        });

        // モデルをトレーニングし、ロス値を収集してプロット
        const losses = [];
        await model.fit(inputData, outputData, {
            epochs: 10,
            batchSize: 32,
            callbacks: {
                onEpochEnd: (epoch, logs) => {
                    console.log(`Epoch ${epoch + 1}: loss = ${logs.loss}`);
                    losses.push(logs.loss);
                    plotLossChart(losses); // ロスをプロット
                }
            }
        });
    }

    // トレーニングロスをCanvasにプロットする関数
    function plotLossChart(losses) {
        const ctx = document.getElementById('lossChart').getContext('2d');
        
        // 既存のチャートインスタンスを破棄
        if (chart) {
            chart.destroy();
        }

        const chartData = {
            labels: Array.from({length: losses.length}, (_, i) => i + 1), // エポック数
            datasets: [{
                label: 'トレーニングロス',
                data: losses, // ロスデータ
                borderColor: 'blue',
                fill: false
            }]
        };

        // 新しいチャートインスタンスを作成
        chart = new Chart(ctx, {
            type: 'line',
            data: chartData,
            options: {
                responsive: true,
                scales: {
                    x: {
                        title: {
                            display: true,
                            text: 'エポック'
                        }
                    },
                    y: {
                        title: {
                            display: true,
                            text: 'ロス'
                        }
                    }
                }
            }
        });
    }

    // グローバル変数としてチャートのインスタンスを保持
    let chart = null;
</script>

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</body>
</html>

モデル作成:

tf.sequential()でシンプルな2層のニューラルネットワークを作成し、入力サイズ2000に対して、異なるサイズの出力層を持つネットワークを構築しています。

トレーニングと評価:

model.fit()を使用して、ランダムに生成したダミーデータを使ってモデルを訓練しています。
それぞれの出力サイズでのロス値とトレーニングにかかった時間を記録します。

結果のプロット:

Chart.jsを使用して、出力サイズごとのロスとトレーニング時間をプロットしています。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3