-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNeuralNetwork.java
More file actions
298 lines (275 loc) · 13.2 KB
/
NeuralNetwork.java
File metadata and controls
298 lines (275 loc) · 13.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
import java.io.File;
import java.util.LinkedList;
/*
By Brendan C. Reidy
Created 12/10/2019
Last Modified 12/18/2019
Neural Network Object:
Responsible for handling the Neural Network comprised of various layers and topologies
Performs operations for neural network at a high level
*/
public class NeuralNetwork {
int length;
private Layer[] layers; // All the layers in the network
private Layer inputLayer; // Input layer of network
private Layer outputLayer; // Output layer of network
private CostFunction costFunction; // Cost function for network
private LinkedList<Float> trainCost; // List of train costs
private LinkedList<Float> testCost; // List of test costs
private DatasetAnalysis[] testAnalysisModels;
public float mostRecentCost;
private float learningRate;
// Training Data
private float[][] train_inputs;
private float[][] train_outputs; // AKA Labels
private float[][] test_inputs;
private float[][] test_outputs; // AKA Labels
// For record keeping
private float bestCost;
private int epochNum;
private long lastRecordedTime;
private long avgTime;
public NeuralNetwork()
{
this.inputLayer = null;
this.layers = new Layer[0];
this.length = 0;
this.costFunction = new MeanSquaredError();
this.testAnalysisModels = new DatasetAnalysis[0];
init();
}
private void init()
{
this.trainCost = new LinkedList<Float>();
this.testCost = new LinkedList<Float>();
this.learningRate = 0.1f;
this.bestCost = Float.MAX_VALUE;
this.epochNum = 0;
this.avgTime = 0;
}
private boolean testDataWarning = false;
private boolean didOutput = false;
public float getCost()
{
float avgError = 0;
for(int i=0; i<train_inputs.length; i++) // Iterate through each training sample
{
float[] input = train_inputs[i]; // Get input training data
float[] correctOutput = train_outputs[i]; // Get correct label/output
setInput(input); // Set input of the network to the training input
feedForward(); // Feed forward the network
float[] output = getOutput(); // Get output of the network
float[] cost = costFunction.cost(output, correctOutput); // Get cost for each neuron
for(int j=0; j<cost.length; j++)
avgError+=cost[j]; // Cost is the sum of the error for all neurons
}
avgError/=train_inputs.length; // Get average error over entire training data
return avgError;
}
public void train()
{
if(epochNum!=0)
avgTime += System.currentTimeMillis() - lastRecordedTime;
lastRecordedTime = System.currentTimeMillis();
epochNum++;
if(length<=1) // Either there are no layers in the network or there is only an input layer in the network
{
System.out.println("[FATAL] Unable to train network: no meaningful layers");
return;
}
if(train_inputs == null || train_outputs==null) // No training data in network
{
System.out.println("[FATAL] Unable to train network: training data not set.");
return;
}
if((test_inputs == null || test_inputs==null) && !testDataWarning) // Warn user that there is no test data (Only warns once)
{
testDataWarning = true;
System.out.println("[WARNING] Test data not set for network.");
}
if(!didOutput) // Only need to output once, otherwise floods output
{
didOutput = true;
System.out.println("Training...");
}
float avgError = 0; // Variable to store the average error of the network
for(int i=0; i<train_inputs.length; i++) // Iterate through each training sample
{
float[] input = train_inputs[i]; // Get input training data
float[] correctOutput = train_outputs[i]; // Get correct label/output
setInput(input); // Set input of the network to the training input
feedForward(); // Feed forward the network
float[] output = getOutput(); // Get output of the network
float[] cost = costFunction.cost(output, correctOutput); // Get cost for each neuron
for(int j=0; j<cost.length; j++)
avgError+=cost[j]; // Cost is the sum of the error for all neurons
propagateBack(correctOutput); // Propagate back with the correct output
}
avgError/=train_inputs.length; // Get average error over entire training data
trainCost.add(avgError); // Keep track of for file IO
mostRecentCost = avgError;
System.out.print("Cost: " + avgError); // Print average error to console
if(test_inputs!=null) // Check if there is test data
{
avgError = 0;
for(int i=0; i<testAnalysisModels.length; i++) // Tell each test analysis model a new epoch is starting
testAnalysisModels[i].startEpoch();
for(int i=0; i<test_inputs.length; i++) // Get error for each test sample
{
float[] input = test_inputs[i]; // Test input
float[] correctOutput = test_outputs[i]; // Test output/label
setInput(input); // Set input to test input
feedForward(); // Feed forward
float[] output = getOutput(); // Get output neurons
float[] cost = costFunction.cost(output, correctOutput); // Get cost
for(int j=0; j<testAnalysisModels.length; j++)
testAnalysisModels[j].analyzeLayer(output, correctOutput); // Perform analysis on output
for(int j=0; j<cost.length; j++) // Get sum of error for neurons
avgError+=cost[j];
}
avgError/=test_inputs.length; // Get average error over test subset length
testCost.add(avgError); // Store for file IO
System.out.print("\tTest Cost: " + avgError); // Print test error to console
for(int i=0; i<testAnalysisModels.length; i++)
testAnalysisModels[i].printResults(); // Print result to console
if(avgError<bestCost) {
bestCost = avgError;
for(int i=1; i<layers.length; i++)
layers[i].setBest();
}
}else if(avgError<bestCost)
bestCost = avgError;
System.out.println();
}
public void setTrainingData(float[][] inputs, float[][] outputs) // Sets training data for the network to specified input/output
{
this.train_inputs = inputs;
this.train_outputs = outputs;
}
public void setTestingData(float[][] inputs, float[][] outputs) // Sets test data for the network to specified input/output
{
this.test_inputs = inputs;
this.test_outputs = outputs;
}
public void setLearningRate(float aRate) // Set learning rate for all layers
{
this.learningRate = aRate; // Keep track of in network
for(int i=1; i<layers.length; i++) // Set learning rate for each layer in the network
layers[i].setLearningRate(aRate);
}
public void addDatasetAnaylsisModel(DatasetAnalysis aModel)
{
DatasetAnalysis[] temp = new DatasetAnalysis[this.testAnalysisModels.length+1]; // Temp array; bigger for new model
for(int i=0; i<this.testAnalysisModels.length; i++) // Copy current models
temp[i] = testAnalysisModels[i];
temp[this.testAnalysisModels.length] = aModel; // Add new model at end
this.testAnalysisModels = temp; // Set the networks test models to the newly updated array
}
public void addLayer(Layer aLayer) // Add layer to network
{
if(length!=0) // If not first layer in network
aLayer.setPreviousLayer(layers[length-1]); // Set the previous network for this layer to the previous layer
else
this.inputLayer = aLayer; // This is the input layer since it is the first layer
Layer[] temp = new Layer[length+1]; // Temp array to keep track of all layers
temp[length] = aLayer; // Set last layer to the new layer
for(int i=0; i<length; i++) // Copy layers to temp
temp[i] = layers[i]; // Set layer at given index to the one already designated in the network (copy layers)
this.outputLayer = aLayer; // Output layer is this layer since it is the last
layers = temp; // Set the networks layer to the array containing all layers
length++; // Increase length of network by 1
}
public void setBest()
{
for(int i=1; i<layers.length; i++)
{
layers[i].setBest();
}
}
public Layer[] getLayers()
{
return this.layers;
}
public float[][] getTrainInputs()
{
return this.train_inputs;
}
public float[] getOutput() // Gets output neurons
{
if(outputLayer==null){ // In case user does something very wrong
System.out.println("[FATAL] Attempted to get output but there is no output layer in network.");
return null;
}
return outputLayer.getNeurons(); // Return neurons in last (output) layer
}
public void setInput(float[] aInput) // Sets the input of the network to the given array
{
if(inputLayer==null){ // Network is empty but tried to set input
System.out.println("[FATAL] Attempted 'setInput' but there is no input layer in network.");
return;
}
inputLayer.setNeurons(aInput); // Set the input for the input layer to the array given
}
public void feedForward() // Feed forward each layer
{
float[] previousNeurons = inputLayer.getNeurons(); // Previous neurons (neurons of the layer indexed n-1)
for(int i=1; i<layers.length; i++) // Feed forward each layer
previousNeurons = layers[i].feedForward(previousNeurons); // Call feed forward for each layer object
}
public void propagateBack(float[] correctOutput) // Propagate back each layer
{
float[] currentError = costFunction.error(getOutput(), correctOutput); // Get the error for the output layer
for(int i=length-1; i>0; i--)
currentError = layers[i].propagateBack(currentError); // Propagate back each layer
}
public void saveToFile(String outputDirectory) // Save network to output directory
{
if(outputDirectory!="") // Check if there is an output directory
if(!(new File(outputDirectory).exists())) // Check if that directory already exists
new File(outputDirectory).mkdir(); // Create a new one if not
String topology = "" + layers[0].sizeX()*layers[0].sizeY(); // Stores the topology of the network in a string
String structure = "\t" + layers[0].toString() + "\n"; // Stores the structure of the network in a string
String datasetAnaylsisModelText = "";
for(int i=0; i<testAnalysisModels.length; i++) // Iterate through all test analysis models
{
datasetAnaylsisModelText+=testAnalysisModels[i].appendToReadMe() + "\n"; // Append info to readme
testAnalysisModels[i].saveToFile(outputDirectory); // Save important info to directory
}
for(int i=1; i<layers.length; i++)
{
//layers[i].saveBiasToFile(outputDirectory + "B" + i); // Saves the biases
//layers[i].saveWeightsToFile(outputDirectory + "W" + i); // Saves the weights
topology+= "x" + layers[i].sizeX()*layers[i].sizeY(); // Gets the size at the index and adds to topology
structure+="\t" + layers[i].toString() + "\n"; // Add current layer structure to network structure
}
double averageTime = avgTime*0.001;
averageTime = averageTime / epochNum;
String[] outputs={"*** AUTO GENERATED README ***",
"Topology: ", "\t" + topology,
"\nCost Function:", "\t" + this.costFunction.toString(),
"\nStructure:", structure,
"Statistics: ",
"\tBest Cost: " + Float.toString(bestCost),
"\tNumber of epochs: " + Integer.toString(epochNum),
"\tAverage Epoch Time: " + Double.toString(averageTime) + "s",
"\nTest Dataset Analysis Models:\n" + datasetAnaylsisModelText};
MatrixIO.saveToFile(outputs, outputDirectory + "README.txt"); // Save README file
MatrixIO.saveToFile(trainCost, outputDirectory + "trainCosts.txt"); // Save training costs
MatrixIO.saveToFile(testCost, outputDirectory + "testCosts.txt"); // Save test costs
}
public static float[][] GenerateWeights2D(int dimX, int dimY) // Generates a 2D array of weights
{
float[][] weights = new float[dimY][dimX]; // Rows = X; Columns = Y
for(int x=0; x<dimX; x++)
for(int y=0; y<dimY; y++)
weights[y][x] = (float) (Math.random()*2-1); // Generate a random float between -1 and 1
return weights;
}
public static float[] GenerateBias2D(int size) // Generate bias for a 2D layer
{
float[] bias = new float[size];
for(int i=0; i<size; i++)
bias[i] = (float) (Math.random()*2-1); // Generate a random float between -1 and 1
return bias;
}
}