You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何JavaScript神经网络反向传播函数会使权重与偏置变为NaN?

反向传播导致神经网络权重/偏置变为NaN的问题分析

问题描述

我用JavaScript编写了一个小型神经网络,从C#转用JavaScript让我十分恼火,因为我认为JavaScript更容易出现bug。我已修复最初的错误,但现在无法执行反向传播,因为处理后weights和biases全部变为NaN!请问我的BackPropagation Function为何会导致权重变为NaN?

初始化代码:var neuralNet = new NeuralNet(10, 10, 1);

原代码

class NeuralNet {
// const for learningRate
static LEARNING_RATE = 0.33;
// define float[][] for biases
biases;
// define float[][] for values
values;
// define float[][] for errors
errors;
// define float[][][] for weights
weights;

// constructor
constructor(inputSize, hiddenSize, outputSize) {
    // set biases
    this.biases = [
        // hidden layer
        new Array(hiddenSize).fill(0).map(NeuralNet.random),
        // output layer
        new Array(outputSize).fill(0).map(NeuralNet.random)
    ];
    // set values
    this.values = [
        // input layer
        new Array(inputSize).fill(0),
        // hidden layer
        new Array(hiddenSize).fill(0),
        // output layer
        new Array(outputSize).fill(0)
    ];
    // set errors
    this.errors = [
        // hidden layer
        new Array(hiddenSize).fill(0).map(() => new Array(inputSize).fill(0)),
        // output layer
        new Array(outputSize).fill(0).map(() => new Array(hiddenSize).fill(0))
    ];
    // set weights
    this.weights = [
        // hidden layer
        new Array(hiddenSize).fill(0).map(() => new Array(inputSize).fill(0).map(NeuralNet.random)),
        // output layer
        new Array(outputSize).fill(0).map(() => new Array(hiddenSize).fill(0).map(NeuralNet.random))
    ];
}

// define a random
static random() {
    // return random number between -1 and 1
    return Math.random() * 2 - 1;
}

// tanh function
static tanh(x) {
    // return tanh of x
    return Math.tanh(x);
}

// tanh activation function
feedForward() {
    // for each layer
    for (var i = 1; i < this.values.length; i++) {
        // for each neuron
        for (var j = 0; j < this.values[i].length; j++) {
            // set value to bias
            this.values[i][j] = this.biases[i - 1][j];
            // for each weight
            for (var k = 0; k < this.weights[i - 1].length; k++) {
                // add weight * value
                this.values[i][j] += this.weights[i - 1][k][j] * this.values[i][k];
            }
            // apply tanh
            this.values[i][j] = NeuralNet.tanh(this.values[i][j]);
        }
    }
}

// get output of neural net
getOutput(inpts) {
    // set input
    this.setInput(inpts);
    // feed forward
    this.feedForward();
    // return the output
    return this.values[this.values.length - 1];
}

// backpropagation that doesn't make all values NaN
backpropagation(expected) {
    // for each layer
    for (var i = this.values.length - 1; i > 0; i--) {
        // for each neuron
        for (var j = 0; j < this.values[i].length; j++) {
            // if output layer
            if (i == this.values.length - 1) {
                // set error
                this.errors[i - 1][j] = (expected[j] - this.values[i][j]) * (1 - this.values[i][j] * this.values[i][j]);
            } else {
                // set error
                this.errors[i - 1][j] = 0;
                // for each weight
                for (var k = 0; k < this.weights[i].length; k++) {
                    // add weight * error
                    this.errors[i - 1][j] += this.weights[i][k][j] * this.errors[i][k];
                }
                // multiply error with tanh
                this.errors[i - 1][j] *= (1 - this.values[i][j] * this.values[i][j]);
            }
            // for each weight
            for (var k = 0; k < this.weights[i - 1].length; k++) {
                // add error * value * learning rate
                this.weights[i - 1][k][j] += this.errors[i - 1][j] * this.values[i - 1][k] * NeuralNet.LEARNING_RATE;
            }
            // add error * learning rate
            this.biases[i - 1][j] += this.errors[i - 1][j] * NeuralNet.LEARNING_RATE;
        }
    }
}

问题根源及修复方案

1. Errors数组结构完全错误

你把errors定义成了二维数组,但实际上每个神经元的错误是单个数值,不是数组。当你用数组参与数值计算(比如减法、乘法)时,结果会变成NaN,这是权重/偏置变NaN的核心原因。

修复后的errors定义:

this.errors = [
    // 隐藏层每个神经元对应一个错误值
    new Array(hiddenSize).fill(0),
    // 输出层每个神经元对应一个错误值
    new Array(outputSize).fill(0)
];

2. FeedForward中的权重与输入值索引错误

计算当前层神经元值时,你错误地使用了当前层未计算的values[i][k],而应该用前一层的输出值values[i-1][k];同时权重的索引顺序也搞反了,weights[i-1][k][j]应该改为weights[i-1][j][k](权重矩阵结构是[当前层神经元数][前一层神经元数])。

修复后的feedForward函数:

feedForward() {
    for (var i = 1; i < this.values.length; i++) {
        for (var j = 0; j < this.values[i].length; j++) {
            this.values[i][j] = this.biases[i - 1][j];
            // 遍历前一层的所有神经元,而非当前层
            for (var k = 0; k < this.values[i-1].length; k++) {
                this.values[i][j] += this.weights[i - 1][j][k] * this.values[i - 1][k];
            }
            this.values[i][j] = NeuralNet.tanh(this.values[i][j]);
        }
    }
}

3. 反向传播中的权重更新索引错误

更新权重时,同样搞反了权重的索引顺序,weights[i-1][k][j]应该改为weights[i-1][j][k],同时循环的遍历对象应该是前一层的神经元数量,而非当前层权重数组的长度。

修复后的反向传播权重更新部分:

// 遍历前一层的所有神经元
for (var k = 0; k < this.values[i-1].length; k++) {
    this.weights[i - 1][j][k] += this.errors[i - 1][j] * this.values[i - 1][k] * NeuralNet.LEARNING_RATE;
}

4. 补充缺失的setInput方法

原代码中getOutput调用了setInput但未实现,这会导致输入层值无法正确设置,后续计算也会出错。需添加:

setInput(inpts) {
    this.values[0] = [...inpts];
}

修复后的完整核心代码

class NeuralNet {
static LEARNING_RATE = 0.33;
biases;
values;
errors;
weights;

constructor(inputSize, hiddenSize, outputSize) {
    this.biases = [
        new Array(hiddenSize).fill(0).map(NeuralNet.random),
        new Array(outputSize).fill(0).map(NeuralNet.random)
    ];
    this.values = [
        new Array(inputSize).fill(0),
        new Array(hiddenSize).fill(0),
        new Array(outputSize).fill(0)
    ];
    // 修正errors结构
    this.errors = [
        new Array(hiddenSize).fill(0),
        new Array(outputSize).fill(0)
    ];
    this.weights = [
        new Array(hiddenSize).fill(0).map(() => new Array(inputSize).fill(0).map(NeuralNet.random)),
        new Array(outputSize).fill(0).map(() => new Array(hiddenSize).fill(0).map(NeuralNet.random))
    ];
}

static random() {
    return Math.random() * 2 - 1;
}

static tanh(x) {
    return Math.tanh(x);
}

// 修正feedForward索引
feedForward() {
    for (var i = 1; i < this.values.length; i++) {
        for (var j = 0; j < this.values[i].length; j++) {
            this.values[i][j] = this.biases[i - 1][j];
            for (var k = 0; k < this.values[i-1].length; k++) {
                this.values[i][j] += this.weights[i - 1][j][k] * this.values[i - 1][k];
            }
            this.values[i][j] = NeuralNet.tanh(this.values[i][j]);
        }
    }
}

// 补充setInput方法
setInput(inpts) {
    this.values[0] = [...inpts];
}

getOutput(inpts) {
    this.setInput(inpts);
    this.feedForward();
    return this.values[this.values.length - 1];
}

// 修正反向传播的权重索引和循环逻辑
backpropagation(expected) {
    for (var i = this.values.length - 1; i > 0; i--) {
        for (var j = 0; j < this.values[i].length; j++) {
            if (i == this.values.length - 1) {
                this.errors[i - 1][j] = (expected[j] - this.values[i][j]) * (1 - this.values[i][j] * this.values[i][j]);
            } else {
                this.errors[i - 1][j] = 0;
                for (var k = 0; k < this.weights[i].length; k++) {
                    this.errors[i - 1][j] += this.weights[i][k][j] * this.errors[i][k];
                }
                this.errors[i - 1][j] *= (1 - this.values[i][j] * this.values[i][j]);
            }
            // 遍历前一层神经元数量
            for (var k = 0; k < this.values[i-1].length; k++) {
                this.weights[i - 1][j][k] += this.errors[i - 1][j] * this.values[i - 1][k] * NeuralNet.LEARNING_RATE;
            }
            this.biases[i - 1][j] += this.errors[i - 1][j] * NeuralNet.LEARNING_RATE;
        }
    }
}
}

内容的提问来源于stack exchange,提问作者Mr Facysus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 10:15:30