26-8-12:反向传播(BP算法),请用两层神经网络解释一下,并通过html展示一下

title:title
date:2026.8月份

图片
图片

下面是HTML代码

点击查看代码


</details><!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>两层神经网络 · 完整复合函数 + BP 数学全解</title>
    <style>
        * {
            box-sizing: border-box;
        }
        body {
            margin: 0;
            min-height: 100vh;
            background: radial-gradient(circle at 20% 30%, #141428, #080812);
            font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
            display: flex;
            justify-content: center;
            padding: 20px;
        }
        .container {
            max-width: 1400px;
            width: 100%;
            background: rgba(16, 16, 32, 0.85);
            backdrop-filter: blur(12px);
            border-radius: 40px;
            padding: 30px 35px;
            border: 1px solid rgba(255, 255, 255, 0.06);
            box-shadow: 0 30px 80px rgba(0, 0, 0, 0.9);
        }
        h1 {
            color: #eee;
            font-weight: 300;
            letter-spacing: 2px;
            margin-top: 0;
            margin-bottom: 4px;
            font-size: 28px;
            display: flex;
            align-items: center;
            gap: 14px;
            flex-wrap: wrap;
        }
        h1 small {
            font-size: 16px;
            color: #88aadd;
            font-weight: 400;
            background: #222244;
            padding: 2px 18px;
            border-radius: 40px;
        }
        .sub {
            color: #99aabb;
            border-bottom: 1px solid #2a2a4a;
            padding-bottom: 12px;
            margin-bottom: 25px;
            display: flex;
            justify-content: space-between;
            flex-wrap: wrap;
            gap: 10px;
        }

        /* 复合函数展示条 */
        .composite-box {
            background: #0a0a18;
            border-radius: 16px;
            padding: 16px 22px;
            margin-bottom: 25px;
            border: 1px solid #ffaa4455;
            border-left: 6px solid #ffaa44;
            font-family: 'Courier New', monospace;
            font-size: 15px;
            color: #d0d8e0;
            line-height: 1.9;
            overflow-x: auto;
            white-space: pre-wrap;
            word-break: break-all;
        }
        .composite-box .fn {
            color: #ffaa44;
            font-weight: 700;
        }
        .composite-box .layer1 {
            color: #66ddff;
        }
        .composite-box .layer2 {
            color: #ff66aa;
        }
        .composite-box .sigma {
            color: #88dd88;
        }

        .dashboard {
            display: grid;
            grid-template-columns: 1.1fr 0.9fr;
            gap: 30px;
        }
        @media (max-width: 900px) {
            .dashboard {
                grid-template-columns: 1fr;
            }
        }

        .card {
            background: rgba(8, 8, 20, 0.6);
            border-radius: 24px;
            border: 1px solid #2a2a4a;
            padding: 20px 22px;
            backdrop-filter: blur(4px);
            box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.02);
        }
        .card h3 {
            color: #ccd;
            font-weight: 400;
            letter-spacing: 1px;
            margin-top: 0;
            margin-bottom: 18px;
            border-bottom: 1px solid #2a2a4a;
            padding-bottom: 10px;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .card h3 span {
            background: #2a2a5a;
            padding: 0 12px;
            border-radius: 20px;
            font-size: 12px;
            color: #88aaff;
        }

        .param-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px 18px;
            font-size: 15px;
        }
        .param-item {
            display: flex;
            justify-content: space-between;
            border-bottom: 1px dashed #1a1a30;
            padding: 4px 0;
        }
        .param-item .label {
            color: #8899bb;
        }
        .param-item .val {
            color: #eee;
            font-weight: 600;
            font-family: 'Courier New', monospace;
        }

        .math-box {
            background: #0a0a18;
            border-radius: 16px;
            padding: 12px 16px;
            margin-top: 12px;
            border-left: 4px solid #ffaa44;
            font-family: 'Courier New', monospace;
            font-size: 14px;
            color: #d0d8e0;
            overflow-x: auto;
            white-space: pre-wrap;
            word-break: break-all;
            line-height: 1.7;
        }
        .math-box .highlight {
            color: #ffaa44;
        }
        .math-box .highlight2 {
            color: #66ddff;
        }
        .math-box .highlight3 {
            color: #ff66aa;
        }

        .slider-group {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
            margin: 15px 0 10px 0;
        }
        .slider-group label {
            color: #bbc;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 12px;
            flex: 1;
            min-width: 140px;
        }
        .slider-group input[type="range"] {
            flex: 1;
            min-width: 80px;
            -webkit-appearance: none;
            background: #2a2a44;
            height: 4px;
            border-radius: 4px;
            outline: none;
        }
        .slider-group input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 16px;
            height: 16px;
            border-radius: 50%;
            background: #ffaa44;
            border: 2px solid #fff;
            cursor: pointer;
        }
        .slider-group .val {
            color: #fff;
            font-weight: 600;
            min-width: 30px;
            text-align: center;
        }

        .btn-group {
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
            margin-top: 16px;
        }
        .btn {
            background: #2a2a5a;
            border: none;
            color: #fff;
            padding: 10px 28px;
            border-radius: 40px;
            font-size: 15px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s;
            border: 1px solid transparent;
            letter-spacing: 0.5px;
            flex: 1;
            min-width: 100px;
        }
        .btn:hover {
            background: #3a3a7a;
            border-color: #88aaff;
            box-shadow: 0 0 30px rgba(80, 120, 255, 0.15);
            transform: translateY(-2px);
        }
        .btn:active {
            transform: scale(0.96);
        }
        .btn.primary {
            background: #cc8833;
            color: #0a0a12;
        }
        .btn.primary:hover {
            background: #ffaa44;
            box-shadow: 0 0 40px rgba(255, 170, 68, 0.3);
        }
        .btn.danger {
            background: #442a2a;
        }
        .btn.danger:hover {
            background: #663a3a;
            border-color: #ff8866;
        }

        .status-badge {
            display: inline-block;
            padding: 4px 16px;
            border-radius: 40px;
            font-weight: 600;
            font-size: 13px;
        }
        .status-badge.running {
            background: #33cc88;
            color: #0a0a12;
        }
        .status-badge.stopped {
            background: #556;
            color: #ccc;
        }

        .loss-display {
            font-size: 24px;
            font-weight: 700;
            color: #ffaa44;
            font-family: 'Courier New', monospace;
        }

        .footnote {
            margin-top: 25px;
            color: #556;
            font-size: 13px;
            text-align: center;
            border-top: 1px solid #1a1a30;
            padding-top: 18px;
        }
        .layer-tag {
            display: inline-block;
            background: #1a2a3a;
            padding: 0 10px;
            border-radius: 12px;
            font-size: 12px;
            color: #88aadd;
        }
    </style>
</head>
<body>
<div class="container">
    <h1>
        🧠 两层神经网络 · 完整复合函数
        <small>2 输入 → 隐藏层(2神经元, σ) → 输出(1神经元, σ)</small>
    </h1>
    <div class="sub">
        <span>🔬 数学公式 + 实时数值代入</span>
        <span><span class="status-badge stopped" id="statusBadge">⏸ 待命</span></span>
    </div>

    <!-- ★★★ 核心:完整复合函数展示 ★★★ -->
    <div class="composite-box" id="compositeDisplay">
        <div style="color:#8899bb; font-size:13px; margin-bottom:6px;">📐 两层网络的完整复合映射 (输入 x₁, x₂ → 输出 ŷ)</div>
        <span class="fn">f(x₁, x₂) = σ(</span>
        <span class="layer2">v₁</span>·<span class="layer1">σ(</span><span class="layer2">w₁₁</span>x₁ + <span class="layer2">w₁₂</span>x₂ + <span class="layer2">b₁</span><span class="layer1">)</span> + 
        <span class="layer2">v₂</span>·<span class="layer1">σ(</span><span class="layer2">w₂₁</span>x₁ + <span class="layer2">w₂₂</span>x₂ + <span class="layer2">b₂</span><span class="layer1">)</span> + 
        <span class="layer2">b_out</span>
        <span class="fn">)</span>
        <div style="margin-top:8px; font-size:13px; color:#667; border-top:1px solid #1a1a30; padding-top:8px;">
            ⚡ 其中 σ(z) = 1/(1+e<sup>-z</sup>) &nbsp; | &nbsp; 
            <span class="layer1">■ 隐藏层 (蓝色)</span> &nbsp; 
            <span class="layer2">■ 输出层 (粉色)</span>
        </div>
    </div>

    <div class="dashboard">
        <!-- 左栏 -->
        <div>
            <div class="card">
                <h3>⚙️ 网络参数 (9个可训练权重)</h3>
                <div class="param-grid" id="paramGrid">
                    <!-- JS 填充 -->
                </div>
                <div style="margin-top:16px; display:flex; gap:10px; flex-wrap:wrap; justify-content:space-between;">
                    <div><span style="color:#8899bb;">学习率 η = </span><span id="lrDisplay" style="color:#ffaa44;font-weight:700;">0.5</span></div>
                    <div><span style="color:#8899bb;">损失 L = </span><span id="lossDisplay" class="loss-display">0.2500</span></div>
                    <div><span style="color:#8899bb;">轮次 </span><span id="epochDisplay" style="color:#eee;font-weight:700;">0</span></div>
                </div>
            </div>

            <div class="card" style="margin-top:16px;">
                <h3>🎮 控制器</h3>
                <div class="slider-group">
                    <label>x₁ <input type="range" id="x1Slider" min="0" max="1" step="0.05" value="0.5"> <span class="val" id="x1Val">0.5</span></label>
                    <label>x₂ <input type="range" id="x2Slider" min="0" max="1" step="0.05" value="0.8"> <span class="val" id="x2Val">0.8</span></label>
                    <label>目标 y <input type="range" id="ySlider" min="0" max="1" step="0.05" value="1.0"> <span class="val" id="yVal">1.0</span></label>
                </div>
                <div class="btn-group">
                    <button class="btn primary" id="trainBtn">▶ 开始训练</button>
                    <button class="btn" id="stepBtn">⏭ 单步 BP</button>
                    <button class="btn danger" id="resetBtn">↺ 重置</button>
                </div>
                <div style="margin-top:12px; color:#667; font-size:13px;">
                    💡 点击“单步BP”观察链式求导的每一步具体数值。
                </div>
            </div>

            <div class="card" style="margin-top:16px;">
                <h3>📐 当前梯度 (反向传播结果)</h3>
                <div id="gradientDisplay" style="font-family:'Courier New',monospace; font-size:13px; color:#ccd; line-height:1.8;">
                    尚未计算...
                </div>
            </div>
        </div>

        <!-- 右栏:逐层数学推导 -->
        <div>
            <div class="card" style="height:100%;">
                <h3>🧮 第1层 (隐藏层) · 子函数 <span class="layer-tag">h₁, h₂</span></h3>
                <div id="layer1Math" class="math-box" style="border-left-color:#66ddff;">
                    <span style="color:#667;">点击“单步BP”查看 h₁、h₂ 的 z 和 a 计算...</span>
                </div>

                <h3 style="margin-top:20px;">🧮 第2层 (输出层) · 子函数 <span class="layer-tag">ŷ</span></h3>
                <div id="layer2Math" class="math-box" style="border-left-color:#ff66aa;">
                    <span style="color:#667;">输出层加权求和 + Sigmoid 激活...</span>
                </div>

                <h3 style="margin-top:20px;">⬇️ 反向传播 · 链式求导 (δ 误差项)</h3>
                <div id="backwardMath" class="math-box" style="border-left-color:#ff8844;">
                    <span style="color:#667;">∂L/∂z 逐层回传...</span>
                </div>

                <h3 style="margin-top:20px;">🔄 权重更新 (梯度下降)</h3>
                <div id="updateMath" class="math-box" style="border-left-color:#33ff88;">
                    <span style="color:#667;">新权重 = 旧权重 - η × 梯度</span>
                </div>
            </div>
        </div>
    </div>
    <div class="footnote">
        ⚡ 网络 = 2层 (隐藏层 + 输出层) · 损失函数 MSE = ½(ŷ - y)² · 激活函数 σ(z) = 1/(1+e<sup>-z</sup>)
    </div>
</div>

<script>
    // ============================================================
    // 1. 参数 (两层: W1(2x2), b1(2), W2(1x2), b2(1))
    // ============================================================
    let w11 = 0.15,
        w12 = 0.20,
        w21 = 0.25,
        w22 = 0.30;
    let b1 = 0.35,
        b2 = 0.35;
    let v1 = 0.40,
        v2 = 0.45;
    let b_out = 0.60;
    let lr = 0.5;
    let epoch = 0;
    let isTraining = false;
    let trainInterval = null;

    let x1 = 0.5,
        x2 = 0.8,
        y_true = 1.0;
    let cache = {};

    // ============================================================
    // 2. 数学函数
    // ============================================================
    function sigmoid(z) { return 1 / (1 + Math.exp(-z)); }
    function sigmoidDeriv(a) { return a * (1 - a); }

    function forward(x1, x2) {
        const z_h1 = w11 * x1 + w12 * x2 + b1;
        const z_h2 = w21 * x1 + w22 * x2 + b2;
        const a_h1 = sigmoid(z_h1);
        const a_h2 = sigmoid(z_h2);
        const z_out = v1 * a_h1 + v2 * a_h2 + b_out;
        const a_out = sigmoid(z_out);
        return { z_h1, z_h2, a_h1, a_h2, z_out, a_out };
    }

    function computeLoss(y_pred, y_true) { return 0.5 * (y_pred - y_true) ** 2; }

    function backward(x1, x2, y_true) {
        const f = forward(x1, x2);
        const y_pred = f.a_out;
        const loss = computeLoss(y_pred, y_true);

        // 输出层
        const dL_da_out = y_pred - y_true;
        const da_out_dz_out = sigmoidDeriv(y_pred);
        const delta_out = dL_da_out * da_out_dz_out;

        const grad_v1 = delta_out * f.a_h1;
        const grad_v2 = delta_out * f.a_h2;
        const grad_b_out = delta_out;

        // 隐藏层
        const da_h1_dz_h1 = sigmoidDeriv(f.a_h1);
        const da_h2_dz_h2 = sigmoidDeriv(f.a_h2);
        const delta_h1 = delta_out * v1 * da_h1_dz_h1;
        const delta_h2 = delta_out * v2 * da_h2_dz_h2;

        const grad_w11 = delta_h1 * x1;
        const grad_w12 = delta_h1 * x2;
        const grad_w21 = delta_h2 * x1;
        const grad_w22 = delta_h2 * x2;
        const grad_b1 = delta_h1;
        const grad_b2 = delta_h2;

        cache = { ...f, y_pred, loss, dL_da_out, da_out_dz_out, delta_out, delta_h1, delta_h2,
            grad_w11, grad_w12, grad_w21, grad_w22, grad_b1, grad_b2, grad_v1, grad_v2, grad_b_out,
            da_h1_dz_h1, da_h2_dz_h2
        };

        return {
            grads: { grad_w11, grad_w12, grad_w21, grad_w22, grad_b1, grad_b2, grad_v1, grad_v2, grad_b_out },
            loss, cache
        };
    }

    function updateWeights(grads) {
        w11 -= lr * grads.grad_w11;
        w12 -= lr * grads.grad_w12;
        w21 -= lr * grads.grad_w21;
        w22 -= lr * grads.grad_w22;
        b1 -= lr * grads.grad_b1;
        b2 -= lr * grads.grad_b2;
        v1 -= lr * grads.grad_v1;
        v2 -= lr * grads.grad_v2;
        b_out -= lr * grads.grad_b_out;
    }

    // ============================================================
    // 3. UI 渲染
    // ============================================================
    function renderParams() {
        const grid = document.getElementById('paramGrid');
        const items = [
            ['w₁₁', w11], ['w₁₂', w12],
            ['w₂₁', w21], ['w₂₂', w22],
            ['b₁', b1], ['b₂', b2],
            ['v₁', v1], ['v₂', v2],
            ['b_out', b_out]
        ];
        grid.innerHTML = items.map(([name, val]) =>
            `<div class="param-item"><span class="label">${name}</span><span class="val">${val.toFixed(4)}</span></div>`
        ).join('');
    }

    function renderGradients(grads) {
        if (!grads) { document.getElementById('gradientDisplay').innerHTML = '<span style="color:#667;">尚未计算...</span>'; return; }
        const items = [
            ['∂L/∂w₁₁', grads.grad_w11], ['∂L/∂w₁₂', grads.grad_w12],
            ['∂L/∂w₂₁', grads.grad_w21], ['∂L/∂w₂₂', grads.grad_w22],
            ['∂L/∂b₁', grads.grad_b1], ['∂L/∂b₂', grads.grad_b2],
            ['∂L/∂v₁', grads.grad_v1], ['∂L/∂v₂', grads.grad_v2],
            ['∂L/∂b_out', grads.grad_b_out]
        ];
        document.getElementById('gradientDisplay').innerHTML = items.map(([name, val]) =>
            `<div><span style="color:#8899bb;">${name}</span> = <span style="color:#ff8844;font-weight:700;">${val.toFixed(6)}</span></div>`
        ).join('');
    }

    function renderMath(cache, grads) {
        if (!cache) {
            document.getElementById('layer1Math').innerHTML = '<span style="color:#667;">等待计算...</span>';
            document.getElementById('layer2Math').innerHTML = '<span style="color:#667;">等待计算...</span>';
            document.getElementById('backwardMath').innerHTML = '<span style="color:#667;">等待计算...</span>';
            document.getElementById('updateMath').innerHTML = '<span style="color:#667;">等待计算...</span>';
            return;
        }
        const f = cache;

        // ---- 第1层 ----
        document.getElementById('layer1Math').innerHTML = `
        <span style="color:#66ddff;">h₁ = σ(z₁)</span> , z₁ = ${w11.toFixed(4)}·${x1.toFixed(2)} + ${w12.toFixed(4)}·${x2.toFixed(2)} + ${b1.toFixed(4)} = ${f.z_h1.toFixed(6)}
        a₁ = σ(${f.z_h1.toFixed(4)}) = <span class="highlight">${f.a_h1.toFixed(6)}</span>
        
        <span style="color:#66ddff;">h₂ = σ(z₂)</span> , z₂ = ${w21.toFixed(4)}·${x1.toFixed(2)} + ${w22.toFixed(4)}·${x2.toFixed(2)} + ${b2.toFixed(4)} = ${f.z_h2.toFixed(6)}
        a₂ = σ(${f.z_h2.toFixed(4)}) = <span class="highlight">${f.a_h2.toFixed(6)}</span>
        `;

        // ---- 第2层 ----
        document.getElementById('layer2Math').innerHTML = `
        <span style="color:#ff66aa;">ŷ = σ(z_out)</span> , z_out = ${v1.toFixed(4)}·${f.a_h1.toFixed(4)} + ${v2.toFixed(4)}·${f.a_h2.toFixed(4)} + ${b_out.toFixed(4)} = ${f.z_out.toFixed(6)}
        ŷ = σ(${f.z_out.toFixed(4)}) = <span class="highlight2">${f.y_pred.toFixed(6)}</span>
        
        🔹 损失 L = ½·(${f.y_pred.toFixed(4)} - ${y_true.toFixed(2)})² = <span class="highlight3">${f.loss.toFixed(6)}</span>
        `;

        // ---- 反向传播 ----
        document.getElementById('backwardMath').innerHTML = `
        🔸 输出层增量 δ_out = (ŷ - y)·σ'(z_out) = (${f.y_pred.toFixed(4)} - ${y_true.toFixed(2)}) · ${f.da_out_dz_out.toFixed(6)} = <span class="highlight3">${f.delta_out.toFixed(6)}</span>
        
        🔸 隐藏层增量 δ₁ = δ_out·v₁·σ'(a₁) = ${f.delta_out.toFixed(6)} · ${v1.toFixed(4)} · ${f.da_h1_dz_h1.toFixed(6)} = <span class="highlight3">${f.delta_h1.toFixed(6)}</span>
        δ₂ = δ_out·v₂·σ'(a₂) = ${f.delta_out.toFixed(6)} · ${v2.toFixed(4)} · ${f.da_h2_dz_h2.toFixed(6)} = <span class="highlight3">${f.delta_h2.toFixed(6)}</span>
        
        🔸 梯度示例: ∂L/∂w₁₁ = δ₁·x₁ = ${f.delta_h1.toFixed(6)} · ${x1.toFixed(2)} = <span class="highlight">${f.grad_w11.toFixed(6)}</span>
        `;

        // ---- 更新 ----
        if (grads) {
            document.getElementById('updateMath').innerHTML = `
            w₁₁(新) = ${w11.toFixed(4)} - ${lr.toFixed(2)}·(${grads.grad_w11.toFixed(6)}) = <span style="color:#33ff88;">${(w11 - lr*grads.grad_w11).toFixed(6)}</span>
            w₁₂(新) = ${w12.toFixed(4)} - ${lr.toFixed(2)}·(${grads.grad_w12.toFixed(6)}) = <span style="color:#33ff88;">${(w12 - lr*grads.grad_w12).toFixed(6)}</span>
            ... (其余权重同理)
            `;
        } else {
            document.getElementById('updateMath').innerHTML = '<span style="color:#667;">执行单步BP后显示更新...</span>';
        }
    }

    function fullUpdateUI() {
        const f = forward(x1, x2);
        const loss = computeLoss(f.a_out, y_true);
        document.getElementById('lossDisplay').textContent = loss.toFixed(6);
        document.getElementById('epochDisplay').textContent = epoch;
        renderParams();
        if (cache && cache.delta_out !== undefined) {
            const grads = {
                grad_w11: cache.grad_w11, grad_w12: cache.grad_w12,
                grad_w21: cache.grad_w21, grad_w22: cache.grad_w22,
                grad_b1: cache.grad_b1, grad_b2: cache.grad_b2,
                grad_v1: cache.grad_v1, grad_v2: cache.grad_v2,
                grad_b_out: cache.grad_b_out
            };
            renderGradients(grads);
            renderMath(cache, grads);
        } else {
            const tempCache = { ...f, y_pred: f.a_out, loss: computeLoss(f.a_out, y_true) };
            renderMath(tempCache, null);
            document.getElementById('gradientDisplay').innerHTML = '<span style="color:#667;">点击“单步BP”计算梯度...</span>';
        }
    }

    // ============================================================
    // 4. 动作
    // ============================================================
    function doBackpropStep() {
        const result = backward(x1, x2, y_true);
        updateWeights(result.grads);
        epoch++;
        renderGradients(result.grads);
        renderMath(cache, result.grads);
        fullUpdateUI();
        return result.loss;
    }

    function resetNetwork() {
        if (trainInterval) { clearInterval(trainInterval);
            trainInterval = null; }
        isTraining = false;
        document.getElementById('trainBtn').textContent = '▶ 开始训练';
        document.getElementById('statusBadge').textContent = '⏸ 待命';
        document.getElementById('statusBadge').className = 'status-badge stopped';
        w11 = 0.15;
        w12 = 0.20;
        w21 = 0.25;
        w22 = 0.30;
        b1 = 0.35;
        b2 = 0.35;
        v1 = 0.40;
        v2 = 0.45;
        b_out = 0.60;
        epoch = 0;
        cache = {};
        document.getElementById('gradientDisplay').innerHTML = '<span style="color:#667;">尚未计算...</span>';
        fullUpdateUI();
        document.getElementById('layer1Math').innerHTML = '<span style="color:#667;">点击“单步BP”查看...</span>';
        document.getElementById('layer2Math').innerHTML = '<span style="color:#667;">点击“单步BP”查看...</span>';
        document.getElementById('backwardMath').innerHTML = '<span style="color:#667;">点击“单步BP”查看...</span>';
        document.getElementById('updateMath').innerHTML = '<span style="color:#667;">点击“单步BP”查看...</span>';
    }

    function toggleTraining() {
        if (isTraining) {
            if (trainInterval) { clearInterval(trainInterval);
                trainInterval = null; }
            isTraining = false;
            document.getElementById('trainBtn').textContent = '▶ 继续训练';
            document.getElementById('statusBadge').textContent = '⏸ 已暂停';
            document.getElementById('statusBadge').className = 'status-badge stopped';
        } else {
            isTraining = true;
            document.getElementById('trainBtn').textContent = '⏹ 停止';
            document.getElementById('statusBadge').textContent = '▶ 训练中...';
            document.getElementById('statusBadge').className = 'status-badge running';
            if (trainInterval) clearInterval(trainInterval);
            trainInterval = setInterval(() => {
                const loss = doBackpropStep();
                if (loss < 0.00001) {
                    clearInterval(trainInterval);
                    trainInterval = null;
                    isTraining = false;
                    document.getElementById('trainBtn').textContent = '✅ 已收敛';
                    document.getElementById('statusBadge').textContent = '✅ 收敛';
                    document.getElementById('statusBadge').className = 'status-badge running';
                }
            }, 80);
        }
    }

    // ============================================================
    // 5. 绑定
    // ============================================================
    document.getElementById('x1Slider').addEventListener('input', (e) => {
        x1 = parseFloat(e.target.value);
        document.getElementById('x1Val').textContent = x1.toFixed(2);
        if (!isTraining) { cache = {};
            fullUpdateUI(); }
    });
    document.getElementById('x2Slider').addEventListener('input', (e) => {
        x2 = parseFloat(e.target.value);
        document.getElementById('x2Val').textContent = x2.toFixed(2);
        if (!isTraining) { cache = {};
            fullUpdateUI(); }
    });
    document.getElementById('ySlider').addEventListener('input', (e) => {
        y_true = parseFloat(e.target.value);
        document.getElementById('yVal').textContent = y_true.toFixed(2);
        if (!isTraining) { cache = {};
            fullUpdateUI(); }
    });
    document.getElementById('stepBtn').addEventListener('click', () => { if (!isTraining) doBackpropStep(); });
    document.getElementById('trainBtn').addEventListener('click', toggleTraining);
    document.getElementById('resetBtn').addEventListener('click', resetNetwork);

    resetNetwork();
</script>
</body>
</html>
posted @ 2026-08-12 16:24  小小冰&碎冰冰  阅读(2)  评论(0)    收藏  举报