用普通JS

时间:2019-01-05 21:26:47

标签: javascript node.js typescript neural-network deep-learning

对神经网络的可能性印象深刻,我决定在使用任何库之前,我都想了解它们的工作原理。因此,我编写了一个简单的培训应用,该应用使用了3层网络,每个网络有2个神经元。有一张400x400的画布。给定鼠标在画布<0; 399>上的x,y坐标,结果应为坐标/ 400 <0; 1>(因此对于100,300,应给出0.25,0.75)。

培训看起来很合理。

training

但是当我切换到预测模式时,网络每次训练批次都会一直得到相同的结果。无论输入是什么,它都会给出相同的结果。 training data 然后,经过更多的训练,输出会发生变化,但每个输入仍然相同。

它是用TypeScript编写的。 我没有粘贴整个Web培训页面,而是制作了培训脚本,以便您可以更清楚地了解正在发生的事情。

index.ts

let sigmoid: ActivationFunction = {
    func: (x: number) => (1 / (1 + Math.exp(-x))),
    derivative: (z: number) => {
        return sigmoid.func(z) * (1 - sigmoid.func(z));
    }
};

import Matrix from './matrix';

class NeutralNetwork {
    layers: Array<number>;
    weights: Matrix[];
    biases: Matrix[];
    activation_function: ActivationFunction;
    learning_rate: number;

    constructor(...layers: Array<number>) {
        this.layers = layers;
        this.activation_function = sigmoid;

        //Initialize neural network with random weigths and biases [-1;1]
        this.weights = [];
        for(let i=0; i<this.layers.length - 1; i++){
            this.weights.push(new Matrix(this.layers[i+1], this.layers[i]));
            this.weights[i].randomize();
        }
        this.biases = [];
        for(let i=1; i<this.layers.length; i++){
            this.biases.push(new Matrix(this.layers[i], 1));
            this.biases[i-1].randomize();
        }

        this.setActivationFunction();
        this.setLearningRate();
    }

    feedForward(originalInput: Array<number>): Array<number> {
        if(originalInput.length != this.layers[0]) throw new Error("corrupt input data");

        let input : Matrix = Matrix.createFromArray(originalInput);
        for(let i = 0; i < this.layers.length - 1; i++){
            let output = Matrix.multiply(this.weights[i], input);
            output.add(this.biases[i]);
            output.map(this.activation_function.func);
            input = output;
        }

        return input.toArray();
    }

    train(originalInput: Array<number>, originalTarget: Array<number>) {
        if(originalInput.length != this.layers[0]) throw new Error("corrupt training data");
        if(originalTarget.length != this.layers[this.layers.length - 1]) throw new Error("corrupt training data");

        let outputs : Matrix[] = [];
        let input : Matrix = Matrix.createFromArray(originalInput);
        for(let i = 0; i < this.layers.length - 1; i++){
            let output = Matrix.multiply(this.weights[i], input);
            output.add(this.biases[i]);
            output.map(this.activation_function.func);
            input = output;
            outputs.push(output);
        }

        let target = Matrix.createFromArray(originalTarget);
        let errors = Matrix.subtract(target, outputs[this.layers.length - 2]);


        for(let i = this.layers.length - 2; i>=0; i--){
            let gradients = Matrix.map(outputs[i], this.activation_function.derivative);
            gradients.multiply(errors);
            gradients.multiply(this.learning_rate);

            let outputsOfLayerBeforeTransposed = Matrix.transpose(i > 0 ? outputs[i-1] : Matrix.createFromArray(originalInput));
            let deltas = Matrix.multiply(gradients, outputsOfLayerBeforeTransposed);

            this.weights[i].add(deltas);
            this.biases[i].add(gradients);

            let weightsTransposed = Matrix.transpose(this.weights[i]);
            errors = Matrix.multiply(weightsTransposed, errors);
        }

        return outputs[outputs.length - 1].toArray();

    }



    setActivationFunction(activationFunction = sigmoid) {
        this.activation_function = activationFunction;
    }
    setLearningRate(learning_rate = 0.1) {
        this.learning_rate = learning_rate;
    }
}


interface ActivationFunction {
    func(x: number): number;
    derivative(x: number): number;
}

export = NeutralNetwork;

training.ts

let NN = require('./index');

let n = new NN(2,2,2);

let data = generateTrainingData();
data.forEach(d => n.train(d.i, d.o));

//check how well is it trained
let index = 0
let t = setInterval(()=>{
    let pred = n.feedForward(data[index].i);
    console.log(`PREDICTED - ${pred} EXPECTED = ${data[index].o} COST - ${Math.pow(pred[0]-data[index].o[0],2)+Math.pow(pred[1]-data[index].o[1],2)}`)
    if(index++ == 1000) clearInterval(t);
}, 500);

function generateTrainingData(){
    let data = [];
    for(let i=0;i<1000;i++){
        let x = Math.floor(Math.random() * 400);
        let y = Math.floor(Math.random() * 400);
        data.push({
            i : [x,y],
            o : [x/400, y/400]
        })
    }

    return data;
}

matrix.ts

export default class Matrix {
    rows;
    columns;
    data: Array<Array<number>>;

    constructor(rows, columns) {
        this.rows = rows;
        this.columns = columns;
        this.data = new Array(this.rows).fill().map(() => Array(this.columns).fill(0));
    }

    static map(matrix, f) : Matrix{
        let m = new Matrix(matrix.rows, matrix.columns);
        m.map((v,i,j) => f(matrix.data[i][j], i, j));
        return m;
    }

    map(f) {
        for (let i = 0; i < this.rows; i++) {
            for (let j = 0; j < this.columns; j++) {
                this.data[i][j] = f(this.data[i][j], i, j);
            }
        }
    }

    randomize() {
        this.map(() => Math.random() * 2 - 1);
    }

    add(n) {
        if (n instanceof Matrix) {
            if (this.rows !== n.rows || this.columns !== n.columns) {
                throw new Error('Size of both matrices must match!');
            }
            return this.map((v, i, j) => v + n.data[i][j]);
        } else {
            return this.map(v => v + n);
        }
    }



    static subtract(a, b) : Matrix{
        if (a.rows !== b.rows || a.columns !== b.columns) {
            throw new Error('Size of both matrices must match!');
        }

        let m = new Matrix(a.rows, a.columns);
        m.map((_, i, j) => a.data[i][j] - b.data[i][j]);
        return m;
    }

    static multiply(a, b) {

        if (a.columns !== b.rows) {
            throw new Error('a.columns !== b.rows');
        }

        let m = new Matrix(a.rows, b.columns)
        m.map((_, i, j) => {
            let sum = 0;
            for (let k = 0; k < a.cols; k++) {
                sum += a.data[i][k] * b.data[k][j];
            }
            return sum;
        });

        return m;
    }
    multiply(n) {
        if (n instanceof Matrix) {
            if (this.rows !== n.rows || this.columns !== n.columns) {
                throw new Error('Size of both matrices must match!');
            }
            return this.map((v, i, j) => v * n.data[i][j]);
        } else {
            return this.map(v => v * n);
        }
    }
    toArray() {
        let arr = [];
        for (let i = 0; i < this.rows; i++) {
            for (let j = 0; j < this.columns; j++) {
                arr.push(this.data[i][j]);
            }
        }
        return arr;
    }

    static transpose(matrix) : Matrix {
        let m = new Matrix(matrix.columns, matrix.rows)
        m.map((_, i, j) => matrix.data[j][i]);
        return m;
    }

    static createFromArray(arr): Matrix {
        let m = new Matrix(arr.length, 1);
        m.map((v, i) => arr[i]);
        return m;
    }
}

我不太确定是什么原因造成的。我已经尝试调试了几天,但我认为我经验不足并不能让我在这里看到问题。非常感谢您的所有帮助。

1 个答案:

答案 0 :(得分:1)

Matrix.multiply类方法中有一个错误。它应该是a.columns而不是a.cols。因此,gradientsdeltas无法正确更新。