日曜大工のニューラルネットワークを最初から作成します。パート2。実装

私はへの導入で述べたように最初の部分、私はフロントエンドの開発者です、そして私の母国語ではJavaScriptで、我々はそれを使用して、この記事では私たちのニューラルネットワークを実装します。まず、構造について少しお話します。さまざまな計算されたプロパティとメソッドを除いて、ニューラルネットワークオブジェクトにはレイヤーの配列が含まれ、各レイヤーにはニューロンの配列が含まれ、各ニューロンには「入力」の配列が含まれます。前の入力レイヤー。また、活性化関数、その導関数、学習率など、ネットワーク全体に共通するものがあり、各ニューロンからそれらにアクセスする必要があるため、ニューロンには、その層への_layerリンクがあることに同意しましょう。それは属し、レイヤーには_network(ネットワーク自体へのリンク)があります。





プライベートから一般に移り、最初にニューロンの入力クラスについて説明しましょう。





class Input {
 constructor(neuron, weight) {
   this.neuron = neuron;
   this.weight = weight;
 }
}
      
      



ここではすべてが非常に簡単です。各入力には、数値の重みと前の層のニューロンへの参照があります。さらに進んでみましょう。ニューロン自体のクラスについて説明しましょう。





class Neuron {
 constructor(layer, previousLayer) {
   this._layer = layer;
   this.inputs = previousLayer
     ? previousLayer.neurons.map((neuron) => new Input(neuron, Math.random() - 0.5))
     : [0];
 }

 get $isFirstLayerNeuron() {
   return !(this.inputs[0] instanceof Input)
 }

 get inputSum() {
   return this.inputs.reduce((sum, input) => {
     return sum + input.neuron.value * input.weight;
   }, 0);
 }

 get value() {
   return this.$isFirstLayerNeuron
     ? this.inputs[0]
     : this._layer._network.activationFunction(this.inputSum);
 }

 set input(val) {
   if (!this.$isFirstLayerNeuron) {
     return;
   }

   this.inputs[0] = val;
 }

 set error(error) {
   if (this.$isFirstLayerNeuron) {
     return;
   }

   const wDelta = error * this._layer._network.derivativeFunction(this.inputSum);

   this.inputs.forEach((input) => {
     input.weight -= input.neuron.value * wDelta * this._layer._network.learningRate;
     input.neuron.error = input.weight * wDelta;
   });
 }
}
      
      



ここで何が起こっているのか見てみましょう。ニューロンのコンストラクターに2つのパラメーターを渡すことができます。このニューロンが配置されているレイヤーと、これがニューラルネットワークの入力レイヤーでない場合は、前のレイヤーへのリンクです。





コンストラクターでは、前のレイヤーのニューロンごとに、ニューロンを接続してランダムな重みを持つ入力を作成し、すべての入力を入力配列に書き込みます。これがネットワークの入力レイヤーである場合、inputs配列は、入力に渡す単一の数値で構成されます。





$isFirstLayerNeuron - , , . , , .





inputSum - readonly , (, ) .





value - . , , inputSum.





:





input - , .





- error. , , error . , , .





. .





class Layer {
 constructor(neuronsCount, previousLayer, network) {
   this._network = network;
   this.neurons = [];
   for (let i = 0; i < neuronsCount; i++) {
     this.neurons.push(new Neuron(this, previousLayer));
   }
 }

 get $isFirstLayer() {
   return this.neurons[0].$isFirstLayerNeuron;
 }

 set input(val) {
   if (!this.$isFirstLayer) {
     return;
   }

   if (!Array.isArray(val)) {
     return;
   }

   if (val.length !== this.neurons.length) {
     return;
   }

   val.forEach((v, i) => this.neurons[i].input = v);
 }
}
      
      



- , neurons , , , .





$isFirstLayer - , , , input, , , . , .





, ,





class Network {
 static  sigmoid(x) {
   return 1 / (1 + Math.exp(-x));
 }

 static sigmoidDerivative(x) {
   return Network.sigmoid(x) * (1 - Network.sigmoid(x));
 }

 constructor(inputSize, outputSize, hiddenLayersCount = 1, learningRate = 0.5) {
   this.activationFunction = Network.sigmoid;
   this.derivativeFunction = Network.sigmoidDerivative;
   this.learningRate = learningRate;

   this.layers = [new Layer(inputSize, null, this)];

   for (let i = 0; i < hiddenLayersCount; i++) {
     const layerSize = Math.min(inputSize * 2 - 1, Math.ceil((inputSize * 2 / 3) + outputSize));
     this.layers.push(new Layer(layerSize, this.layers[this.layers.length - 1], this));
   }

   this.layers.push(new Layer(outputSize, this.layers[this.layers.length - 1], this));
 }

 set input(val) {
   this.layers[0].input = val;
 }

 get prediction() {
   return this.layers[this.layers.length - 1].neurons.map((neuron) => neuron.value);
 }

 trainOnce(dataSet) {
   if (!Array.isArray(dataSet)) {
     return;
   }

   dataSet.forEach((dataCase) => {
     const [input, expected] = dataCase;

     this.input = input;
     this.prediction.forEach((r, i) => {
       this.layers[this.layers.length - 1].neurons[i].error = r - expected[i];
     });
   });
 }

 train(dataSet, epochs = 100000) {
   return new Promise(resolve => {
     for (let i = 0; i < epochs; i++) {
       this.trainOnce(dataSet);
     }
     resolve();
   });
 }
}
      
      



, learning rate.





input - , .





prediction - , . .





trainOnce dataset - , , , - . , , . , , , .





train - , . . , .then, main thread.





, . - XOR.





.





const network = new Network(2, 1);
      
      



:





const data = [
 [[0, 0], [0]],
 [[0, 1], [1]],
 [[1, 0], [1]],
 [[1, 1], [0]],
];

      
      



, .





network.train(data).then(() => {
 const testData = [
   [0, 0],
   [0, 1],
   [1, 0],
   [1, 1],
 ];

 testData.forEach((input, index) => {
   network.input = input;
   console.log(`${input[0]} XOR ${input[1]} = ${network.prediction}`)
 });
});
      
      



, . .








All Articles