JavaScript 實現簡單的神經網路演算法

taogang發表於2016-06-14

神經網路簡介

神經網路試圖模擬大腦的神經元之間的關係來處理資訊。它的計算模型通常需要大量彼此連線的節點。每個神經元通過某種特殊的輸出函式來處理來自其它相鄰神經元的加權輸入值。

神經元之間的資訊傳遞的強度,用所謂的加權值來定義,演算法會不斷的調整加權值來實現自我的學習過程。

1

神經網路分為多層,如上圖,有輸入層,隱藏層和輸出層。

JS線性代數包

神經網路的計算涉及到大量的矩陣計算,有許多的線性代數的開源的軟體,Python下有著名的numpy,非常有名。 Javascript也有幾個:

我使用了numericjs,效果還不錯。推薦大家可以試試。

兩層神經網路

我們有一些簡單的輸入輸出的資料用來訓練神經網路。這裡每一行代表一條資料。輸入有三個引數,輸出是一個。

Inputs 0 Inputs 1 Inputs 2 Output
0 0 1 0
1 1 1 1
1 0 1 1
0 1 1 0

首先我們實現一個最簡單的神經網路,沒有隱藏層,輸入直連輸出。

2

因為輸入是三個引數,輸出是一個,所以我們的神經網路輸入層是三個節點,輸出是1個。

// Sigmod function
function nonlin(x, deriv) {
  if (deriv) {
    return numeric.mul(x, numeric.sub(1, x));
  }

  return numeric.div(1, numeric.add(1, numeric.exp(numeric.neg(x))));
}

function train_neural(X, y, iteration) {
  // initialize weights
  var syn0 = numeric.sub(numeric.mul(2, numeric.random([3, 1])), 1);
  //Training loop
  var i = 0;
  for (; i < iteration; i++) {
    var l0 = X;
    var l1 = nonlin(numeric.dot(l0, syn0));
    var l1_error = numeric.sub(y, l1);
    var l1_delta = numeric.mul(l1_error, nonlin(l1, true));
    syn0 = numeric.add(syn0, numeric.dot(numeric.transpose(l0), l1_delta));
    } 
  }
}

//Initial input/ouput values
var X = [
  [0, 0, 1],
  [0, 1, 1],
  [1, 0, 1],
  [1, 1, 1]
];

var y = [
  [0],
  [0],
  [1],
  [1]
];

train_neural(X, y, 1000);

簡單介紹一下訓練的程式碼和過程

  • X 輸入資料
  • y 輸出資料
  • nonlin, S函式
  • l0,網路第一層,這是等於輸入資料
  • l1,網路第二層,這裡就是輸出層
  • syn0,第一層網路的權重

訓練的迭代過程就是先給出一個初始的權重,利用這個權重算出一個輸出值,用目標結果減去這個值,得到一個差異值,再利用這個差異值對權重進行修正。

1000次迭代後的網路輸出: [0.03,0.02, 0.979, 0.974]

1000次迭代後的syn0權重值: [7.266,-0.221,-3.415]

這裡我們發現第一個節點的權重較大,這個和我們的資料是一致的,通過觀察資料我們也可以發現,輸出值和第一列的輸入值是強相關。如果增加迭代的次數,這個值會更大。

三層神經網路

Inputs 0 Inputs 1 Inputs 2 Output
0 0 1 0
0 1 1 1
1 0 1 1
1 1 1 0

現在我們有了一組新的資料,通過觀察發現,第三列和結果完全無關,第一列和第二列相同時結果為0,否則為1。這是一種非線性的關係,為了有效學習我們增加一層,網路變成了這個樣子。

3

// Sigmod function
function nonlin(x, deriv) {
  if (deriv) {
    return numeric.mul(x, numeric.sub(1, x));
  }

  return numeric.div(1, numeric.add(1, numeric.exp(numeric.neg(x))));
}

function train_neural(X, y, iteration) {
  // initialize weights
  var syn0 = [
    [-0.1653904, 0.11737966, -0.71922612, -0.60379702],
    [0.60148914, 0.93652315, -0.37315164, 0.38464523],
    [0.7527783, 0.78921333, -0.82991158, -0.92189043]
  ];

  var syn1 = [
    [-0.66033916],
    [0.75628501],
    [-0.80330633],
    [-0.15778475]
  ];

  //Training loop
  var i = 0;
  for (; i < 1000; i++) {
    var l0 = X;
    var l1 = nonlin(numeric.dot(l0, syn0));
    var l2 = nonlin(numeric.dot(l1, syn1));
    var l2_error = numeric.sub(y, l2);
    var l2_delta = numeric.mul(l2_error, nonlin(l2, true));
    var l1_error = numeric.dot(l2_delta, numeric.transpose(syn1));
    var l1_delta = numeric.mul(l1_error, nonlin(l1, true));
    syn1 = numeric.add(syn1, numeric.dot(numeric.transpose(l1), l2_delta));
    syn0 = numeric.add(syn0, numeric.dot(numeric.transpose(l0), l1_delta));
  }
}

//Initial input/output values
var X = [
  [0, 0, 1],
  [0, 1, 1],
  [1, 0, 1],
  [1, 1, 1]
];

var y = [
  [0],
  [1],
  [1],
  [0]
];

train_neural(X, y, 1000);

訓練的過程和之前的兩層差別不大,只是多了一層。通過增加的這一層,可以有效的學習資料中的複雜非線性的關聯關係。

經過1000次迭代, 輸出值為:[0.02,0.95,0.94,0.05]

syn0 :

4

如果大家對訓練的過程有興趣,可以執行我的code

相關文章