Skip to content

Instantly share code, notes, and snippets.

@boxbeam
Created January 23, 2020 22:56
Show Gist options
  • Save boxbeam/c28b3d5cabcd474ef460945021fea25d to your computer and use it in GitHub Desktop.
Save boxbeam/c28b3d5cabcd474ef460945021fea25d to your computer and use it in GitHub Desktop.
package redempt.numberrecognition.ai;
import java.util.ArrayList;
import java.util.List;
public class Layer {
private List<Neuron> neurons = new ArrayList<>();
public void setNextLayerSize(int size) {
for (Neuron neuron : neurons) {
neuron.generateWeights(size);
}
}
public Layer(int count) {
for (int i = 0; i < count; i++) {
neurons.add(new Neuron());
}
}
public List<Neuron> getNeurons() {
return neurons;
}
public void addNeuron(Neuron neuron) {
neurons.add(neuron);
}
public Layer clone() {
List<Neuron> cloned = new ArrayList<>();
neurons.forEach(n -> cloned.add(n.clone()));
Layer layer = new Layer(0);
layer.neurons = cloned;
return layer;
}
}
package redempt.numberrecognition.ai;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class NeuralNetwork {
private List<Layer> layers = new ArrayList<>();
public int score = -1;
private int[] layerStructure;
public NeuralNetwork(int... layerStructure) {
if (layerStructure.length <= 1) {
throw new IllegalArgumentException("Neural network must have at least 2 layers!");
}
this.layerStructure = layerStructure;
for (int num : layerStructure) {
layers.add(new Layer(num));
}
for (int i = 0; i < layers.size() - 1; i++) {
layers.get(i).setNextLayerSize(layers.get(i + 1).getNeurons().size());
}
for (Neuron neuron : layers.get(layers.size() - 1).getNeurons()) {
neuron.weights = new double[] {1};
neuron.bias = 0;
}
layers.get(layers.size() - 1).setNextLayerSize(1);
}
public double[] feed(double[]... inputs) {
int pos = 0;
for (double[] array : inputs) {
for (double num : array) {
layers.get(0).getNeurons().get(pos).feed(num);
pos++;
}
}
for (Neuron neuron : layers.get(0).getNeurons()) {
double[] output = neuron.output();
neuron.reset();
for (int i = 0; i < output.length; i++) {
layers.get(1).getNeurons().get(i).feed(output[i]);
}
}
for (int i = 1; i < layers.size() - 1; i++) {
for (Neuron neuron : layers.get(i).getNeurons()) {
double[] output = neuron.output();
neuron.reset();
for (int x = 0; x < output.length; x++) {
layers.get(i + 1).getNeurons().get(x).feed(output[x]);
}
}
}
double[] outputs = new double[layers.get(layers.size() - 1).getNeurons().size()];
for (int i = 0; i < outputs.length; i++) {
Neuron neuron = layers.get(layers.size() - 1).getNeurons().get(i);
outputs[i] = neuron.output()[0];
neuron.reset();
}
return outputs;
}
public double[] dryFeed(double[]... inputs) {
int pos = 0;
for (double[] array : inputs) {
for (double num : array) {
layers.get(0).getNeurons().get(pos).feed(num);
pos++;
}
}
for (Neuron neuron : layers.get(0).getNeurons()) {
double[] output = neuron.output();
for (int i = 0; i < output.length; i++) {
layers.get(1).getNeurons().get(i).feed(output[i]);
}
}
for (int i = 1; i < layers.size() - 1; i++) {
for (Neuron neuron : layers.get(i).getNeurons()) {
double[] output = neuron.output();
for (int x = 0; x < output.length; x++) {
layers.get(i + 1).getNeurons().get(x).feed(output[x]);
}
}
}
double[] outputs = new double[layers.get(layers.size() - 1).getNeurons().size()];
for (int i = 0; i < outputs.length; i++) {
Neuron neuron = layers.get(layers.size() - 1).getNeurons().get(i);
outputs[i] = neuron.output()[0];
}
return outputs;
}
public double getError(double[][] samples, double[][] expected) {
double error = 0;
for (int x = 0; x < samples.length; x++) {
double[] input = samples[x];
double[] output = feed(input);
for (int i = 0; i < output.length; i++) {
error += Math.abs(output[i] - expected[x][i]);
}
}
return error;
}
public void train(double[][] samples, double[][] expected, int iterations) {
double error = getError(samples, expected);
double[][] nudges = getAverageNudges(samples, expected);
List<NeuronLocation> locations = new ArrayList<>();
int tx = -1;
int ty = -1;
double size = -1;
for (int x = 0; x < nudges.length; x++) {
for (int y = 0; y < nudges[x].length; y++) {
locations.add(new NeuronLocation(x, y));
if (Math.abs(nudges[x][y]) > size) {
size = Math.abs(nudges[x][y]);
ty = y;
tx = x;
}
}
}
locations.sort((a, b) -> {
return (int) Math.signum(nudges[a.getLayer()][a.getNumber()] - nudges[b.getLayer()][b.getNumber()]);
});
}
private void trainLayer(double[][] sample, double[][] expected, int l) {
Layer layer = layers.get(l);
dryFeed(sample);
List<Neuron> neurons = new ArrayList<>(layer.getNeurons());
double maxVal = -1;
Neuron best = null;
for (Neuron neuron : neurons) {
double value = Math.abs(neuron.value);
for (double weight : neuron.weights) {
value += (1 - Math.abs(weight)) / neuron.weights.length;
}
if (value > maxVal || best == null) {
maxVal = value;
best = neuron;
}
}
resetNeurons();
}
private double[][] getNudges(double[] expected) {
double[][] nudges = new double[layers.size()][];
for (int i = 0; i < layers.size(); i++) {
nudges[i] = new double[layers.get(i).getNeurons().size()];
}
for (int n = 0; n < layers.get(layers.size() - 1).getNeurons().size(); n++) {
Neuron neuron = layers.get(layers.size() - 1).getNeurons().get(n);
double diff = expected[n] - neuron.value;
nudges[layers.size() - 1][n] = diff;
}
for (int l = layers.size() - 2; l > 0; l--) {
Layer layer = layers.get(l);
for (int n = 0; n < layer.getNeurons().size(); n++) {
Neuron neuron = layer.getNeurons().get(n);
double nudge = 0;
for (int d = 0; d < nudges[l + 1].length; d++) {
double diff = nudges[l + 1][d];
nudge += diff * neuron.weights[d];
}
nudge /= nudges[l + 1].length;
nudges[l][n] = nudge;
}
}
return nudges;
}
private double[][] getAverageNudges(double[][] samples, double[][] expected) {
double[][] nudges = new double[layers.size()][];
for (int i = 0; i < layers.size(); i++) {
nudges[i] = new double[layers.get(i).getNeurons().size()];
}
for (int i = 0; i < samples.length; i++) {
resetNeurons();
dryFeed(samples[i]);
nudges = add(nudges, getNudges(expected[i]));
}
nudges = divide(nudges, samples.length);
return nudges;
}
private double[][] add(double[][] first, double[][] second) {
double[][] result = new double[first.length][];
for (int i = 0; i < result.length; i++) {
result[i] = new double[first[i].length];
}
for (int x = 0; x < result.length; x++) {
for (int y = 0; y < result[x].length; y++) {
result[x][y] = first[x][y] + second[x][y];
}
}
return result;
}
private double[][] divide(double[][] array, double divide) {
double[][] result = new double[array.length][];
for (int i = 0; i < result.length; i++) {
result[i] = new double[array[i].length];
}
for (int x = 0; x < result.length; x++) {
for (int y = 0; y < result[x].length; y++) {
result[x][y] = array[x][y] / divide;
}
}
return result;
}
private Set<NeuronLocation> getSignificantNeurons(NeuronLocation loc) {
Set<NeuronLocation> significant = new HashSet<>();
for (int x = 1; x < loc.getLayer() - 1; x++) {
for (int y = 0; y < layers.get(x).getNeurons().size(); y++) {
significant.add(new NeuronLocation(x, y));
}
}
return significant;
}
private double[] tweakAndCheck(NeuronLocation location, int weight) {
if (weight > -1) {
NeuralNetwork clone = clone();
Neuron neuron = clone.getNeuron(location);
neuron.weights[weight] += 0.01;
return clone.dryFeed(getValues(0));
} else {
NeuralNetwork clone = clone();
Neuron neuron = clone.getNeuron(location);
neuron.bias += 0.01;
return clone.dryFeed(getValues(0));
}
}
private double[] getValues(int layer) {
double[] values = new double[layers.get(layer).getNeurons().size()];
for (int i = 0; i < values.length; i++) {
values[i] = layers.get(layer).getNeurons().get(i).value;
}
return values;
}
private double[] getOutputs() {
double[] values = new double[layers.get(layers.size() - 1).getNeurons().size()];
for (int i = 0; i < values.length; i++) {
values[i] = layers.get(layers.size() - 1).getNeurons().get(i).output()[0];
}
return values;
}
public Neuron getNeuron(NeuronLocation location) {
return layers.get(location.getLayer()).getNeurons().get(location.getNumber());
}
public void resetNeurons() {
layers.stream().forEach(c -> c.getNeurons().stream().forEach(Neuron::reset));
}
public void mutate(int iterations) {
for (int i = 0; i < iterations; i++) {
Layer layer = layers.get((int) Math.round(Math.random() * (layers.size() - 2)));
Neuron neuron = layer.getNeurons().get((int) Math.round(Math.random() * (layer.getNeurons().size() - 1)));
neuron.mutate();
}
}
public List<Layer> getLayers() {
return layers;
}
public NeuralNetwork clone() {
List<Layer> cloned = new ArrayList<>();
layers.stream().forEach(l -> cloned.add(l.clone()));
NeuralNetwork network = new NeuralNetwork(layerStructure);
network.layers = cloned;
return network;
}
}
package redempt.numberrecognition.ai;
import java.util.Random;
public class Neuron {
protected double[] weights = null;
protected double bias = 0;
protected double value = 0;
private Neuron(double[] weights, double bias) {
this.weights = weights;
this.bias = bias;
}
public Neuron() {
bias = Math.random() - 0.5;
}
public void generateWeights(int size) {
weights = new double[size];
for (int i = 0; i < weights.length; i++) {
weights[i] = (Math.random() * 3) - 1.5;
}
}
public void reset() {
value = 0;
}
public void feed(double value) {
this.value += value;
}
public double[] output() {
// if (weights == null) {
// return new double[] {process(value + bias)};
// }
double[] outputs = new double[weights.length];
for (int i = 0; i < outputs.length; i++) {
outputs[i] = process((weights[i] * value) + bias);
}
return outputs;
}
public void mutate() {
if (Math.random() > 0.9) {
bias = Math.random() - 0.5;
} else {
Random random = new Random();
weights[random.nextInt(weights.length - 1)] = (Math.random() * 3) - 1.5;
}
}
public Neuron clone() {
return new Neuron(weights.clone(), bias);
}
protected static double process(double num) {
// return 1d / (1d + Math.exp(-num));
// return num;
return Math.tanh(num);
}
}
package redempt.numberrecognition.ai;
import java.util.Objects;
public class NeuronLocation {
private int layer;
private int number;
private int focus;
public NeuronLocation(int layer, int number) {
this.layer = layer;
this.number = number;
}
public NeuronLocation(int layer, int number, int focus) {
this.layer = layer;
this.number = number;
this.focus = focus;
}
public int getFocus() {
return focus;
}
public void setFocus(int focus) {
this.focus = focus;
}
public int getLayer() {
return layer;
}
public int getNumber() {
return number;
}
@Override
public boolean equals(Object o) {
if (o instanceof NeuronLocation) {
NeuronLocation other = (NeuronLocation) o;
return other.layer == this.layer && other.number == this.number;
}
return super.equals(o);
}
@Override
public int hashCode() {
return Objects.hash(number, layer);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment