Tôi đã xây dựng một thiết lập ANN-BP thông thường với một đơn vị trên lớp đầu vào và đầu ra và 4 nút ẩn với sigmoid. Cung cấp cho nó một nhiệm vụ đơn giản để ước tính tuyến tính f(n) = n
với n trong phạm vi 0-100.Hồi quy ANN, xấp xỉ hàm tuyến tính
VẤN ĐỀ: Bất kể số lượng các lớp, các đơn vị trong lớp ẩn hay hay không Tôi đang sử dụng thiên vị trong nút đánh giá cao nó học để xấp xỉ f (n) = Average (dữ liệu) như sau:
Mã được viết bằng JavaScript làm bằng chứng về khái niệm. Tôi đã định nghĩa ba lớp: Net, Layer và Connection, trong đó Layer là một mảng các giá trị đầu vào, thiên vị và đầu ra, kết nối là một mảng 2D có trọng số và trọng số delta. Đây là mã lớp nơi mà tất cả các tính toán quan trọng xảy ra:
Ann.Layer = function(nId, oNet, oConfig, bUseBias, aInitBiases) {
var _oThis = this;
var _initialize = function() {
_oThis.id = nId;
_oThis.length = oConfig.nodes;
_oThis.outputs = new Array(oConfig.nodes);
_oThis.inputs = new Array(oConfig.nodes);
_oThis.gradients = new Array(oConfig.nodes);
_oThis.biases = new Array(oConfig.nodes);
_oThis.outputs.fill(0);
_oThis.inputs.fill(0);
_oThis.biases.fill(0);
if (bUseBias) {
for (var n=0; n<oConfig.nodes; n++) {
_oThis.biases[n] = Ann.random(aInitBiases[0], aInitBiases[1]);
}
}
};
/****************** PUBLIC ******************/
this.id;
this.length;
this.inputs;
this.outputs;
this.gradients;
this.biases;
this.next;
this.previous;
this.inConnection;
this.outConnection;
this.isInput = function() { return !this.previous; }
this.isOutput = function() { return !this.next; }
this.calculateGradients = function(aTarget) {
var n, n1, nOutputError,
fDerivative = Ann.Activation.Derivative[oConfig.activation];
if (this.isOutput()) {
for (n=0; n<oConfig.nodes; n++) {
nOutputError = this.outputs[n] - aTarget[n];
this.gradients[n] = nOutputError * fDerivative(this.outputs[n]);
}
} else {
for (n=0; n<oConfig.nodes; n++) {
nOutputError = 0.0;
for (n1=0; n1<this.outConnection.weights[n].length; n1++) {
nOutputError += this.outConnection.weights[n][n1] * this.next.gradients[n1];
}
// console.log(this.id, nOutputError, this.outputs[n], fDerivative(this.outputs[n]));
this.gradients[n] = nOutputError * fDerivative(this.outputs[n]);
}
}
}
this.updateInputWeights = function() {
if (!this.isInput()) {
var nY,
nX,
nOldDeltaWeight,
nNewDeltaWeight;
for (nX=0; nX<this.previous.length; nX++) {
for (nY=0; nY<this.length; nY++) {
nOldDeltaWeight = this.inConnection.deltaWeights[nX][nY];
nNewDeltaWeight =
- oNet.learningRate
* this.previous.outputs[nX]
* this.gradients[nY]
// Add momentum, a fraction of old delta weight
+ oNet.learningMomentum
* nOldDeltaWeight;
if (nNewDeltaWeight == 0 && nOldDeltaWeight != 0) {
console.log('Double overflow');
}
this.inConnection.deltaWeights[nX][nY] = nNewDeltaWeight;
this.inConnection.weights[nX][nY] += nNewDeltaWeight;
}
}
}
}
this.updateInputBiases = function() {
if (bUseBias && !this.isInput()) {
var n,
nNewDeltaBias;
for (n=0; n<this.length; n++) {
nNewDeltaBias =
- oNet.learningRate
* this.gradients[n];
this.biases[n] += nNewDeltaBias;
}
}
}
this.feedForward = function(a) {
var fActivation = Ann.Activation[oConfig.activation];
this.inputs = a;
if (this.isInput()) {
this.outputs = this.inputs;
} else {
for (var n=0; n<a.length; n++) {
this.outputs[n] = fActivation(a[n] + this.biases[n]);
}
}
if (!this.isOutput()) {
this.outConnection.feedForward(this.outputs);
}
}
_initialize();
}
feedforward chính và chức năng backProp được định nghĩa như sau:
this.feedForward = function(a) {
this.layers[0].feedForward(a);
this.netError = 0;
}
this.backPropagate = function(aExample, aTarget) {
this.target = aTarget;
if (aExample.length != this.getInputCount()) { throw "Wrong input count in training data"; }
if (aTarget.length != this.getOutputCount()) { throw "Wrong output count in training data"; }
this.feedForward(aExample);
_calculateNetError(aTarget);
var oLayer = null,
nLast = this.layers.length-1,
n;
for (n=nLast; n>0; n--) {
if (n === nLast) {
this.layers[n].calculateGradients(aTarget);
} else {
this.layers[n].calculateGradients();
}
}
for (n=nLast; n>0; n--) {
this.layers[n].updateInputWeights();
this.layers[n].updateInputBiases();
}
}
Mã kết nối khá đơn giản:
Ann.Connection = function(oNet, oConfig, aInitWeights) {
var _oThis = this;
var _initialize = function() {
var nX, nY, nIn, nOut;
_oThis.from = oNet.layers[oConfig.from];
_oThis.to = oNet.layers[oConfig.to];
nIn = _oThis.from.length;
nOut = _oThis.to.length;
_oThis.weights = new Array(nIn);
_oThis.deltaWeights = new Array(nIn);
for (nX=0; nX<nIn; nX++) {
_oThis.weights[nX] = new Array(nOut);
_oThis.deltaWeights[nX] = new Array(nOut);
_oThis.deltaWeights[nX].fill(0);
for (nY=0; nY<nOut; nY++) {
_oThis.weights[nX][nY] = Ann.random(aInitWeights[0], aInitWeights[1]);
}
}
};
/****************** PUBLIC ******************/
this.weights;
this.deltaWeights;
this.from;
this.to;
this.feedForward = function(a) {
var n, nX, nY, aOut = new Array(this.to.length);
for (nY=0; nY<this.to.length; nY++) {
n = 0;
for (nX=0; nX<this.from.length; nX++) {
n += a[nX] * this.weights[nX][nY];
}
aOut[nY] = n;
}
this.to.feedForward(aOut);
}
_initialize();
}
Và chức năng kích hoạt và các dẫn xuất của tôi được xác định như sau:
Ann.Activation = {
linear : function(n) { return n; },
sigma : function(n) { return 1.0/(1.0 + Math.exp(-n)); },
tanh : function(n) { return Math.tanh(n); }
}
Ann.Activation.Derivative = {
linear : function(n) { return 1.0; },
sigma : function(n) { return n * (1.0 - n); },
tanh : function(n) { return 1.0 - n * n; }
}
Và cấu hình JSON cho mạng như sau:
var Config = {
id : "Config1",
learning_rate : 0.01,
learning_momentum : 0,
init_weight : [-1, 1],
init_bias : [-1, 1],
use_bias : false,
layers: [
{nodes : 1},
{nodes : 4, activation : "sigma"},
{nodes : 1, activation : "linear"}
],
connections: [
{from : 0, to : 1},
{from : 1, to : 2}
]
}
Có lẽ, mắt kinh nghiệm của bạn có thể phát hiện các vấn đề với tính toán của tôi?
Cảm ơn sự quan tâm của bạn, nhưng tôi không hiểu: 1) Tại sao chúng tôi đang tích lũy delta_weights? 2) Tại sao chúng ta cần 4 lớp ẩn cho một xấp xỉ đơn giản? –
Tôi đã sai khi tích lũy, cảm ơn vì đã chỉ ra điều đó. Đối với chiều sâu, nó làm việc tốt hơn theo cách đó sau khi các mã nhẹ khác thay đổi. Tôi đã giảm nó không 1,2,2,1 ... học tỷ lệ 0,06 và động lượng 0,04. Nhìn chung, mã có vẻ hoạt động tốt hơn nó. Nếu bạn không đồng ý, đó là tốt. Tôi chỉ cố gắng giúp đỡ trong khi tôi học. –
Cảm ơn. Một điều khác mà tôi vừa mới nhận thấy, bạn đã loại bỏ bình phương lỗi, điều này cho phép một dấu hiệu lỗi để lẻn vào tính toán. Điều này có nghĩa là lỗi âm sẽ hủy bỏ lỗi tích cực trong vòng lặp hoặc lỗi âm có thể tích lũy và ngăn chúng tôi đo khi dừng đào tạo. –