Tôi đang cố triển khai FFNN bằng Java với tính năng truyền ngược và không biết tôi đang làm gì sai. Nó hoạt động khi tôi chỉ có một tế bào thần kinh duy nhất trong mạng, nhưng tôi đã viết một lớp khác để xử lý các mạng lớn hơn và không có gì hội tụ. Nó có vẻ như là một vấn đề trong toán học - hay đúng hơn là việc thực hiện toán học của tôi - nhưng tôi đã kiểm tra nó nhiều lần và tôi không thể tìm thấy bất cứ điều gì sai trái. Điều này sẽ được làm việc.
Node lớp:
Thực hiện mạng Neural trong java
package arr;
import util.ActivationFunction;
import util.Functions;
public class Node {
public ActivationFunction f;
public double output;
public double error;
private double sumInputs;
private double sumErrors;
public Node(){
sumInputs = 0;
sumErrors = 0;
f = Functions.SIG;
output = 0;
error = 0;
}
public Node(ActivationFunction func){
this();
this.f = func;
}
public void addIW(double iw){
sumInputs += iw;
}
public void addIW(double input, double weight){
sumInputs += (input*weight);
}
public double calculateOut(){
output = f.eval(sumInputs);
return output;
}
public void addEW(double ew){
sumErrors+=ew;
}
public void addEW(double error, double weight){
sumErrors+=(error*weight);
}
public double calculateError(){
error = sumErrors * f.deriv(sumInputs);
return error;
}
public void resetValues(){
sumErrors = 0;
sumInputs = 0;
}
}
LineNetwork lớp:
package arr;
import util.Functions;
public class LineNetwork {
public double[][][] weights; //layer of node to, # of node to, # of node from
public Node[][] nodes; //layer, #
public double lc;
public LineNetwork(){
weights = new double[2][][];
weights[0] = new double[2][1];
weights[1] = new double[1][3];
initializeWeights();
nodes = new Node[2][];
nodes[0] = new Node[2];
nodes[1] = new Node[1];
initializeNodes();
lc = 1;
}
private void initializeWeights(){
for(double[][] layer: weights)
for(double[] curNode: layer)
for(int i=0; i<curNode.length; i++)
curNode[i] = Math.random()/10;
}
private void initializeNodes(){
for(Node[] layer: nodes)
for(int i=0; i<layer.length; i++)
layer[i] = new Node();
nodes[nodes.length-1][0].f = Functions.HSF;
}
public double feedForward(double[] inputs) {
for(int j=0; j<nodes[0].length; j++)
nodes[0][j].addIW(inputs[j], weights[0][j][0]);
double[] outputs = new double[nodes[0].length];
for(int i=0; i<nodes[0].length; i++)
outputs[i] = nodes[0][i].calculateOut();
for(int l=1; l<nodes.length; l++){
for(int i=0; i<nodes[l].length; i++){
for(int j=0; j<nodes[l-1].length; j++)
nodes[l][i].addIW(
outputs[j],
weights[l][i][j]);
nodes[l][i].addIW(weights[l][i][weights[l][i].length-1]);
}
outputs = new double[nodes[l].length];
for(int i=0; i<nodes[l].length; i++)
outputs[i] = nodes[l][i].calculateOut();
}
return outputs[0];
}
public void backpropagate(double[] inputs, double expected) {
nodes[nodes.length-1][0].addEW(expected-nodes[nodes.length-1][0].output);
for(int l=nodes.length-2; l>=0; l--){
for(Node n: nodes[l+1])
n.calculateError();
for(int i=0; i<nodes[l].length; i++)
for(int j=0; j<nodes[l+1].length; j++)
nodes[l][i].addEW(nodes[l+1][j].error, weights[l+1][j][i]);
for(int j=0; j<nodes[l+1].length; j++){
for(int i=0; i<nodes[l].length; i++)
weights[l+1][j][i] += nodes[l][i].output*lc*nodes[l+1][j].error;
weights[l+1][j][nodes[l].length] += lc*nodes[l+1][j].error;
}
}
for(int i=0; i<nodes[0].length; i++){
weights[0][i][0] += inputs[i]*lc*nodes[0][i].calculateError();
}
}
public double train(double[] inputs, double expected) {
double r = feedForward(inputs);
backpropagate(inputs, expected);
return r;
}
public void resetValues() {
for(Node[] layer: nodes)
for(Node n: layer)
n.resetValues();
}
public static void main(String[] args) {
LineNetwork ln = new LineNetwork();
System.out.println(str2d(ln.weights[0]));
for(int i=0; i<10000; i++){
double[] in = {Math.round(Math.random()),Math.round(Math.random())};
int out = 0;
if(in[1]==1^in[0] ==1) out = 1;
ln.resetValues();
System.out.print(i+": {"+in[0]+", "+in[1]+"}: "+out+" ");
System.out.println((int)ln.train(in, out));
}
System.out.println(str2d(ln.weights[0]));
}
private static String str2d(double[][] a){
String str = "[";
for(double[] arr: a)
str = str + str1d(arr) + ",\n";
str = str.substring(0, str.length()-2)+"]";
return str;
}
private static String str1d(double[] a){
String str = "[";
for(double d: a)
str = str+d+", ";
str = str.substring(0, str.length()-2)+"]";
return str;
}
}
nhanh giải thích về cấu trúc: mỗi nút có chức năng kích hoạt f; f.eval
đánh giá hàm và f.deriv
đánh giá đạo hàm của nó. Functions.SIG
là hàm sigmoidal tiêu chuẩn và Functions.HSF
là hàm bước Heaviside. Để thiết lập đầu vào của một hàm, bạn gọi addIW
với giá trị đã bao gồm trọng số của đầu ra trước đó. Một điều tương tự được thực hiện trong backpropagation với addEW
. Các nút được sắp xếp theo mảng 2d và trọng số được tổ chức riêng trong một mảng 3D như được mô tả.
Tôi nhận ra điều này có thể hơi hỏi một chút - và tôi chắc chắn nhận ra có bao nhiêu Java quy ước mã này bị phá vỡ - nhưng tôi đánh giá cao bất kỳ trợ giúp nào có thể cung cấp. EDIT: Vì câu hỏi này và mã của tôi là những bức tường khổng lồ của văn bản, nếu có một dòng liên quan đến nhiều biểu thức phức tạp trong ngoặc mà bạn không muốn tìm ra, hãy thêm nhận xét hoặc điều gì đó hỏi tôi và tôi ' sẽ cố gắng trả lời nó nhanh nhất có thể.
EDIT 2: Vấn đề cụ thể ở đây là mạng này không hội tụ trên XOR. Dưới đây là một số đầu ra để minh họa điều này:
9995: {1,0, 0,0}: 1 1
9996: {0,0, 1,0}: 1 1
9997: {0,0, 0,0}: 0 1
9998 : {0,0, 1,0}: 1 0
9999: {0,0, 1,0}: 1 1
mỗi dòng là định dạngTEST NUMBER: {INPUTS}: EXPECTED ACTUAL
mạng lưới gọitrain
với mỗi bài kiểm tra, do đó, mạng này được backpropagating 10000 lần.
Dưới đây là hai lớp học thêm nếu có ai muốn chạy nó:
package util;
public class Functions {
public static final ActivationFunction LIN = new ActivationFunction(){
public double eval(double x) {
return x;
}
public double deriv(double x) {
return 1;
}
};
public static final ActivationFunction SIG = new ActivationFunction(){
public double eval(double x) {
return 1/(1+Math.exp(-x));
}
public double deriv(double x) {
double ev = eval(x);
return ev * (1-ev);
}
};
public static final ActivationFunction HSF = new ActivationFunction(){
public double eval(double x) {
if(x>0) return 1;
return 0;
}
public double deriv(double x) {
return (1);
}
};
}
package util;
public interface ActivationFunction {
public double eval(double x);
public double deriv(double x);
}
Bây giờ nó thậm chí còn lâu hơn. Darn.
Vấn đề cụ thể là gì? kết quả được mong đợi là gì? Bạn có thể tạo một chương trình ngắn hơn để tái tạo nó không? Vì nó bây giờ tôi bỏ phiếu để đóng này do "Câu hỏi tìm kiếm trợ giúp gỡ lỗi (" tại sao mã này không hoạt động? ") Phải bao gồm hành vi mong muốn, một vấn đề hoặc lỗi cụ thể và mã ngắn nhất cần thiết để tái tạo nó trong câu hỏi Các câu hỏi không có tuyên bố rõ ràng về vấn đề không hữu ích cho những độc giả khác. ” –
Nếu bạn có thể huấn luyện một nơ-ron đơn, vấn đề có thể xảy ra trong phương pháp backpropagate của bạn. Bạn đã thử tính toán "bằng tay" với một mạng nhỏ để so sánh nó với? Nó cũng sẽ giúp ích nếu bạn có thể đăng các lớp bị thiếu để mã của bạn có thể được chạy. – jbkm
@KErlandsson: Tôi đã thêm vấn đề cụ thể, tôi sẽ xem xét một chương trình ngắn hơn nhưng chắc chắn sẽ mất thời gian vì tôi không hoàn toàn chắc chắn điều gì không hiệu quả và tôi sẽ có thể đưa ra điều gì. –