2
3

小さなGPUでのエンジニアリング。数値シミュレーションからAIへ。

Last updated at Posted at 2024-09-03

GPT-2: ショートストーリー: 「数値からaiへ」数値のaiは、aiが人間に与える影響を数値で表し、数値がaiに与えている影響の量と、人間のaiの数値との差をaiによって表します。 数値はaiが行う数値計算のアルゴリズムによって、人間が計算した数値を、人間が行う計算方法によって計算しますが、この数値を計算するアルゴリズムは「数値」と「ai」の2種類があります。 「aiによる数値処理」は、「数値による計算」に比べて、精度が格段に向上しています。 例えば、ある数値にaiが行った数値の結果を「計算結果」として表示しますと、「計算結果は、計算式によって数値として表されます。」というように

GPT-4: ショートストーリー: 「数値からAIへ」

東京の夜が深まるころ、都会の喧騒から少し離れた小さなアパートメントの一室で、田中健一はパソコンの前に集中していた。彼は熱心なプログラマーで、物理シミュレーションの数値計算を行う仕事をしていた。画面には複雑な数式やパーティクルの動きを描いたグラフが映し出されており、彼の仕事は非常に精密であった。

スクリーンショット 2024-09-04 041659.png

「これが一番効率的なシミュレーション結果だな。」健一はつぶやきながら、最新の計算結果を確認していた。彼の目標は、物理シミュレーションを行い、そのデータからリアルなシミュレーションを生成することだった。しかし、彼は一つのアイデアに思い至った。もし、このシミュレーションデータをAIモデルのトレーニングデータとして利用することができれば、物理的な計算なしでシミュレーションを模倣できるのではないかと考えたのだ。

健一の頭の中では、計算の精度とAIの能力を組み合わせるという新しいビジョンが広がっていた。彼は数値シミュレーションを模倣するAIモデルの作成に向けて、Latent Space Dynamics Model(LSDM)を使用する計画を立てた。このモデルは、物理的な動きを学習し、それを基に新たな動きを予測する能力を持っている。

スクリーンショット 2024-09-04 043623.png

まず、健一は数値シミュレーションから得られるデータを収集した。これはパーティクルの位置や速度など、シミュレーションに関する詳細な情報だった。そのデータを元に、LSDMモデルのトレーニングを開始した。彼は、このトレーニングが成功すれば、数値計算を行わずにリアルなパーティクルシミュレーションを再現することができると確信していた。
スクリーンショット 2024-09-04 050947.png

数週間後、モデルのトレーニングが完了した。健一は新しいAIモデルを使って、パーティクルシミュレーションの模倣を実行する準備が整った。パソコンの画面には、実際のシミュレーションと同じように動くパーティクルが表示された。AIモデルが生成したシミュレーションは、物理的な動きとほぼ一致しており、そのリアルさに健一は驚嘆した。

スクリーンショット 2024-09-04 051035.png

「これだ、これが私が求めていたものだ。」健一は満足げに微笑んだ。AIモデルによるシミュレーションは、実際の計算を行わなくても、リアルな動きを再現することができた。これにより、シミュレーションの計算コストが大幅に削減され、より効率的にシミュレーションを実行できるようになった。

健一の研究は、物理シミュレーションの新しいアプローチを示すものであり、AIと数値計算の融合によって、計算の未来が変わる可能性を秘めていた。彼はこの成功を、今後の研究と開発に生かし、さらに多くの領域でAIを活用することを決意した。

東京の夜が明け、健一は新たな希望とともに、次のプロジェクトに向けて動き出した。彼の目標は、AIが数値シミュレーションを超えて、もっと多くの可能性を引き出すことだった。数値からAIへ、彼の挑戦はこれからも続く。

入力・出力の次元数ごとに、隠れ層のサイズに応じた最終的なロス値の変化が視覚的に確認できます。これにより、異なる次元数に対する最適な隠れ層サイズを見つけるのに役立ちます。

(F12でコンソール開きます。)
スクリーンショット 2024-09-04 041659.png

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TensorFlow.js Neural Network Training</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <!-- Chart.jsのインクルード -->
</head>
<body>
    <h1>Neural Network Training with TensorFlow.js</h1>
    <canvas id="lossGraph" width="800" height="600"></canvas>
    <script>
        async function trainModel(inputDim, outputDim, hiddenSize, epochs = 20) {
            // モデルの定義
            const model = tf.sequential();
            model.add(tf.layers.dense({ units: hiddenSize, inputShape: [inputDim], activation: 'relu' }));
            model.add(tf.layers.dense({ units: Math.floor(hiddenSize / 2), activation: 'relu' }));
            model.add(tf.layers.dense({ units: outputDim }));

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

            // 訓練データの生成
            const numSamples = 100;
            const X_train = tf.randomNormal([numSamples, inputDim]);
            const y_train = tf.randomNormal([numSamples, outputDim]);

            // トレーニングの開始時間を記録
            const startTime = performance.now();

            // モデルの訓練
            await model.fit(X_train, y_train, {
                epochs: epochs,
                verbose: 0
            });

            // トレーニングの終了時間を記録
            const endTime = performance.now();

            // 最終的なロスを取得
            const finalLoss = model.evaluate(X_train, y_train).dataSync()[0];

            // 処理時間をコンソールにプリント
            console.log(`Input/Output Dim: ${inputDim}, Hidden Size: ${hiddenSize}, Final Loss: ${finalLoss}, Time: ${(endTime - startTime).toFixed(2)} ms`);

            return finalLoss;
        }

        async function runTraining() {
            const hiddenSizes = [16, 32, 64, 128, 256];
            const dimensions = [5, 10, 20, 50, 100];

            // グラフの設定
            const ctx = document.getElementById('lossGraph').getContext('2d');
            const colors = ['red', 'blue', 'green', 'orange', 'purple'];
            const datasets = [];

            for (let i = 0; i < dimensions.length; i++) {
                const dim = dimensions[i];
                const finalLosses = [];

                for (let j = 0; j < hiddenSizes.length; j++) {
                    const hiddenSize = hiddenSizes[j];
                    const finalLoss = await trainModel(dim, dim, hiddenSize);
                    finalLosses.push(finalLoss);
                }

                // データセットに追加
                datasets.push({
                    label: `Dim ${dim}`,
                    data: finalLosses,
                    borderColor: colors[i],
                    fill: false
                });
            }

            // グラフの描画
            new Chart(ctx, {
                type: 'line',
                data: {
                    labels: hiddenSizes,
                    datasets: datasets
                },
                options: {
                    title: {
                        display: true,
                        text: 'Final Loss vs. Hidden Layer Size for Different Input/Output Dimensions'
                    },
                    scales: {
                        x: {
                            title: {
                                display: true,
                                text: 'Hidden Layer Size'
                            }
                        },
                        y: {
                            title: {
                                display: true,
                                text: 'Final Loss'
                            }
                        }
                    }
                }
            });
        }

        runTraining();
    </script>
</body>
</html>

このコードを実行すると、2変数サイン関数のフレームデータを生成しそれを使用して次のタイムステップを予測するAIモデルを訓練します。その後モデルの推論結果をアニメーションで表示します。

(F12でコンソール開きます。)
スクリーンショット 2024-09-04 043623.png

スクリーンショット 2024-09-04 052017.png
スクリーンショット 2024-09-04 052105.png

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sine Function Frame Prediction</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
    <div id="plot"></div>
    <script>
        // 2変数サイン関数のデータを生成
        function generateSineFrameData(gridSize = 5) {
            const x = tf.linspace(-5, 5, gridSize);
            const y = tf.linspace(-5, 5, gridSize);
            const xyGrid = tf.meshgrid(x, y);
            const X = xyGrid[0].reshape([gridSize, gridSize]);
            const Y = xyGrid[1].reshape([gridSize, gridSize]);

            const timeSteps = 50;
            const frames = [];

            for (let t = 0; t < timeSteps; t++) {
                const Z = tf.sin(tf.sqrt(X.square().add(Y.square())).add(t * 0.1));
                frames.push(Z);
            }

            return frames;
        }

        // モデルの構築
        function createModel(gridSize = 5) {
            const model = tf.sequential();
            model.add(tf.layers.flatten({ inputShape: [gridSize, gridSize] })); // フラット化
            model.add(tf.layers.dense({ units: 128, activation: 'relu' }));
            model.add(tf.layers.dense({ units: gridSize * gridSize })); // 5x5の次元で出力
            model.add(tf.layers.reshape({ targetShape: [gridSize, gridSize] })); // 元の形に戻す
            model.compile({ optimizer: 'adam', loss: 'meanSquaredError' });
            return model;
        }

        // トレーニングと予測
        async function trainModelAndPredict(model, frames, gridSize) {
            const currentFrames = tf.stack(frames.slice(0, -1)); // 現在のフレーム
            const nextFrames = tf.stack(frames.slice(1)); // 次のタイムステップのフレーム

            await model.fit(currentFrames, nextFrames, {
                epochs: 500,
                verbose: 1, // 進捗表示
                callbacks: {
                    onEpochEnd: (epoch, logs) => {
                        console.log(`Epoch: ${epoch + 1} - Loss: ${logs.loss.toFixed(4)}`);
                    }
                }
            });

            const predictions = [];
            let currentFrame = frames[0];

            for (let t = 0; t < 50; t++) {
                const predictedFrame = model.predict(currentFrame.reshape([1, gridSize, gridSize])).reshape([gridSize, gridSize]);
                predictions.push(predictedFrame);
                currentFrame = predictedFrame;
            }

            return predictions;
        }

        // 3Dプロットの更新
        function plotSurface(Z, gridSize = 5) {
            const x = [...Array(gridSize).keys()].map(i => -5 + i * 10 / (gridSize - 1));
            const y = x;
            const data = [{
                z: Z.arraySync(),
                x: x,
                y: y,
                type: 'surface',
                colorscale: 'Viridis'
            }];
            const layout = {
                title: 'Sine Wave Prediction',
                autosize: true,
                margin: { l: 0, r: 0, b: 0, t: 0 },
                scene: { zaxis: { range: [-1, 1] } }
            };
            Plotly.newPlot('plot', data, layout);
        }

        // アニメーション
        async function animateSurface(gridSize = 5) {
            const frames = generateSineFrameData(gridSize);
            const model = createModel(gridSize);
            const predictions = await trainModelAndPredict(model, frames, gridSize);

            let frame = 0;
            const intervalId = setInterval(() => {
                if (frame < predictions.length) {
                    plotSurface(predictions[frame], gridSize);
                    frame++;
                } else {
                    frame = 0; // 最初のフレームに戻る
                }
            }, 100);
        }

        // アニメーションを開始
        animateSurface(5);
    </script>
</body>
</html>

Three.jsを使用してパーティクルシミュレーションを行い、TensorFlow.jsと内臓GPUを用いてそのシミュレーションデータを学習し、AIモデルを用いてパーティクルの動きを模倣します。

このコードでは、リアルタイムのシミュレーション中に、各パーティクルの動きを個別に推論しています。

スクリーンショット 2024-09-04 050947.png

スクリーンショット 2024-09-04 051035.png

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Particle Simulation with TensorFlow.js</title>
    <style>
        body { margin: 0; overflow: hidden; background-color: black; }
        #info { position: absolute; top: 0; width: 100%; color: white; padding: 10px; font-family: Arial, sans-serif; font-size: 16px; text-align: center; }
    </style>
</head>
<body>
    <div id="info">Training the Simple Model, please wait...</div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script>
    <script>
        // Three.jsによるシーン設定
        let scene, camera, renderer, particleSystem, positions, velocities, numParticles = 20;

        function initThreeJS() {
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
            camera.position.z = 1000;

            positions = new Float32Array(numParticles * 3);
            velocities = new Float32Array(numParticles * 3);

            // パーティクルの初期位置と速度を設定
            for (let i = 0; i < numParticles; i++) {
                positions[i * 3] = (Math.random() * 2 - 1) * 500;
                positions[i * 3 + 1] = (Math.random() * 2 - 1) * 500;
                positions[i * 3 + 2] = (Math.random() * 2 - 1) * 500;

                velocities[i * 3] = (Math.random() - 0.5) * 2;
                velocities[i * 3 + 1] = (Math.random() - 0.5) * 2;
                velocities[i * 3 + 2] = (Math.random() - 0.5) * 2;
            }

            const geometry = new THREE.BufferGeometry();
            geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));

            // 大きくカラフルなパーティクルの設定
            const material = new THREE.PointsMaterial({ size: 10, vertexColors: true });
            const colors = [];
            for (let i = 0; i < numParticles; i++) {
                colors.push(Math.random(), Math.random(), Math.random()); // ランダムな色を設定
            }
            geometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));

            particleSystem = new THREE.Points(geometry, material);
            scene.add(particleSystem);
        }

        // シンプルモデルの作成とトレーニング
        async function trainSimpleModel() {
            const model = tf.sequential();
            model.add(tf.layers.dense({ units: 64, inputShape: [6], activation: 'relu' }));
            model.add(tf.layers.dense({ units: 64, activation: 'relu' }));
            model.add(tf.layers.dense({ units: 6 }));

            model.compile({ optimizer: 'adam', loss: 'meanSquaredError' });

            const inputs = [];
            const targets = [];

            // パーティクルシミュレーションからデータを生成
            for (let step = 0; step < 100; step++) {
                for (let i = 0; i < numParticles; i++) {
                    const pos = positions.subarray(i * 3, i * 3 + 3);
                    const vel = velocities.subarray(i * 3, i * 3 + 3);

                    inputs.push([...pos, ...vel]);

                    // 実際のパーティクル挙動を使用してターゲットデータを生成
                    const newPos = pos.map((p, idx) => p + vel[idx] * 0.01 + (Math.random() - 0.5) * 0.01);
                    const newVel = vel.map(v => v + (Math.random() - 0.5) * 0.01);

                    targets.push([...newPos, ...newVel]);
                }
            }

            const inputTensor = tf.tensor2d(inputs);
            const targetTensor = tf.tensor2d(targets);

            await model.fit(inputTensor, targetTensor, { epochs: 20 });

            return model;
        }

        async function startSimulation() {
            initThreeJS();

            const model = await trainSimpleModel();
            document.getElementById('info').textContent = 'Simulation running...';

            function animate() {
                for (let i = 0; i < numParticles; i++) {
                    const input = [
                        positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2],
                        velocities[i * 3], velocities[i * 3 + 1], velocities[i * 3 + 2]
                    ];

                    const prediction = model.predict(tf.tensor2d([input])).dataSync();

                    positions[i * 3] = prediction[0];
                    positions[i * 3 + 1] = prediction[1];
                    positions[i * 3 + 2] = prediction[2];

                    velocities[i * 3] = prediction[3];
                    velocities[i * 3 + 1] = prediction[4];
                    velocities[i * 3 + 2] = prediction[5];
                }

                particleSystem.geometry.attributes.position.needsUpdate = true;
                renderer.render(scene, camera);
                requestAnimationFrame(animate);
            }

            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            animate();
        }

        window.addEventListener('resize', () => {
            if (renderer) {
                renderer.setSize(window.innerWidth, window.innerHeight);
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
            }
        });

        startSimulation();
    </script>
</body>
</html>

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