I was teaching myself how to make a binary genetic algorithm the other day. The goal was to make it so that it would match a randomly generated 35 length binary string. I ran into a problem where methods were editing variables that I did not think were in its scope. This caused my solution to slowly degrade in fitness instead of increase! After I found out where this was happening I fixed it by newP[0].join('').split('') so that newP[0] itself would not be edited. For convenience I've marked where the problem was happening below in comments.
While I have fixed this problem I'd like to hopefully get an understanding as to why this happens and also prevent without doing the join/split silliness.
Here is the code:
var GeneticAlgorithm = function () {};
GeneticAlgorithm.prototype.mutate = function(chromosome, p) {
for(var i = 0; i < chromosome.length; i++) {
if(Math.random() < p) {
chromosome[i] = (chromosome[i] == 0) ? 1 : 0;
}
}
return chromosome;
};
GeneticAlgorithm.prototype.crossover = function(chromosome1, chromosome2) {
var split = Math.round(Math.random() * chromosome1.length);
var c1 = chromosome1;
var c2 = chromosome2;
for(var i = 0; i < split; i++) {
c1[i] = chromosome2[i];
c2[i] = chromosome1[i];
}
return [c1, c2];
};
// fitness = function for finding fitness score of an individual/chromosome (0-1)
// length = length of string (35)
// p_c = percentage chance of crossover (0.6)
// p_m = percentage change of mutation (0.002)
GeneticAlgorithm.prototype.run = function(fitness, length, p_c, p_m, iterations) {
var iterations = 100;
var size = 100;
// p = population, f = fitnesses
var p = [];
var f = [];
for(var i = 0; i < size; i++) {
p.push(this.generate(length));
f.push(fitness(p[i].join('')));
}
while( iterations > 0 && Math.max.apply(Math, f) < 0.999 ) {
var mates = [];
var newP = [];
var newF = [];
mates = this.select(p, f);
newP.push(mates[0], mates[1]);
while(newP.length < size) {
/*-------------------> Problem! <-------------------*/
mates = [newP[0].join('').split(''), newP[1].join('').split('')];
/*
* If I passed newP[0] when mates[0] changed newP[0] would also change
*/
if(Math.random() < p_c) {
mates = this.crossover(mates[0], mates[1]);
}
mates[0] = this.mutate(mates[0], p_m);
mates[1] = this.mutate(mates[1], p_m);
newP.push(mates[0], mates[1]);
}
for(var i = 0; i < size; i++) {
newF.push(fitness(newP[i].join('')));
}
p = newP;
f = newF;
iterations--;
}
return p[f.indexOf(Math.max.apply(Math, f))].join('');
};
Related
I am newbie in machine learning, but decided to make own js library for neural networks, everything went perfect until i tryed to train my NN. In My Mini Library i created some functions...
1) A Function That Creates My Neuron-Object:
this.Node = function (conns) {
var output = {};
output.b = hyth.Random({type: "TanH"});
output.w = [];
for (var a = 0; a < conns; a++){
output.w[a] = hyth.Random({type: "TanH"});
}
output.Value = function (i) {
if (i.length == conns) {
var sum = 0;
for (var a = 0; a < conns; a++){
sum += i[a] * output.w[a];
}
sum += output.b;
return myMath.Activate(sum, {type: "Sigmoid"});
}
}
return output;
}
This function has one argument , which is the amount of wanted weights from neuron, and it returns an object with two properties - "b" the float (bias), and "w" the 1D Array which contains floats, and one method - which calculates the activation of neuron-object.
2) A Function That Creates My Neural Net
this.Network = function () {
var p = arguments;
var arr = [];
for (var a = 0; a < p.length-1; a++){
arr[a] = [];
for (var b = 0; b < p[a+1]; b++){
arr[a][b] = this.Node(p[a]);
}
}
return arr;
}
This Function Returns A 2D Array with Neuron-Object as It's final value, using argument array as settings for layer count and node count for each layer.
3) A Function That Feeds Forward The NN
this.Forward = function (network, input) {
if (network[0][0].w.length == input.length) {
var activations = [];
for (var a = 0; a < network.length; a++){
activations[a] = [];
for (var c = 0; c < network[a].length; c++){
if (a == 0){
activations[0][c] = network[0][c].Value(input);
continue;
}
activations[a][c] = network[a][c].Value(activations[a-1]);
}
}
return activations;
}
}
This Function Returns 2D array with an activation float for every neuron as it's final value. It uses 2 agruments - the output of 2nd function, input array.
4) And Final Function That Backpropagates
this.Backward = function (network, input, target) {
if (network[0][0].w.length == input.length && network[network.length-1].length == target.length) {
var activations = this.Forward(network, input, true);
var predictions = activations[activations.length-1];
var errors = [];
for (var v = 0; v < network.length; v++) {
errors[v] = [];
}
for (var a = network.length-1; a > -1; a--){
for (var x = 0; x < network[a].length; x++) {
var deract = hyth.Deractivate(activations[a][x]);
if (a == network.length-1) {
errors[a][x] = (predictions[x] - target[x]) * deract;
} else {
errors[a][x] = 0;
for (var y = 0; y < network[a+1].length; y++) {
errors[a][x] += network[a+1][y].w[x] * errors[a+1][y];
}
errors[a][x] *= deract;
}
}
}
return errors;
}
}
This Function Returns 2D array with the rror float for every neuron as it's final value. Arguments are 3 - the nnet , input and wanted output.
So I can make a neural network, feed forward and and backpropagate, receive activations and errors, but i always fail to train my net with my errors and activations to work perfect , last time it was outputing same result for every type of input. I want to understand training algorithm from zero , so i need someone's help.
P.S. - i dont want someone say that i need to use famous libraries , i want to understand and make it myself.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I've been trying to evolve a neural network that prints values converging to one, using a genetic algorithm.
I've tried debugging the code but don't know what I've messed up.
I'm using fitness to chose the best "brains" and then cross them over (reproduce).
At the moment it is only trying to evolve "brains" that return the number. the fitness is a function of the difference between returned number and original number.
"use strict";
function sigmoid(x) {
return 1 / (1 + Math.E ** -x);
}
function random(min, max) {
return (max - min) * Math.random() + min
}
function toss() {
return random(-1, 1)
}
function Brain(inputs, hiddens, outputs) {
this.structure = [...arguments];
if (this.structure.length < 3) throw "Invalid layer count";
this.layers = [];
this.layers[this.structure.length - 1] = {
nodes: []
};
for (var i = this.structure.length - 1; i--;) this.layers[i] = {
bias: toss(),
nodes: []
};
for (var i = 1; i < this.structure.length; i++) {
var nodes = this.layers[i].nodes;;
for (var j = this.structure[i]; j--;) {
var node = nodes[j] = {
weights: []
};
for (var k = this.structure[i - 1]; k--;) node.weights[k] = toss();
}
};
}
Brain.prototype.compute = function() {
if (arguments[0] !== this.structure[0]) throw "Invalid input count";
for (var i = arguments.length; i--;) this.layers[0].nodes[i] = {
value: arguments[i]
};
for (var i = 1; i < this.layers.length - 1; i++) {
var layer = this.layers[i];
var feeder = this.layers[i - 1];
for (var j = layer.nodes.length; j--;) {
var node = layer.nodes[j];
var dot = 0;
for (var k = node.weights.length; k--;) dot += node.weights[k] * feeder.nodes[k].value;
node.value = sigmoid(dot + feeder.bias);
}
}
var result = [];
var layer = this.layers[this.layers.length - 1];
var feeder = this.layers[this.layers.length - 2];
for (var j = layer.nodes.length; j--;) {
var node = layer.nodes[j];
var dot = 0;
for (var k = node.weights.length; k--;) dot += node.weights[k] * feeder.nodes[k].value;
result[j] = sigmoid(dot + feeder.bias);
}
return result;
}
Brain.prototype.cross = function() {
var newBrain = new Brain(...this.structure);
var brains = [this, ...arguments];
for (var i = 1; i < newBrain.layers.length; i++) {
var layer = newBrain.layers[i];
for (var j = layer.nodes.length; j--;) {
var node = layer.nodes[j];
for (var k = node.weights.length; k--;) node.weights[k] = mutate() ||
brains[Math.floor(Math.random() * brains.length)]
.layers[i].nodes[j].weights[k];
}
}
for (var i = newBrain.layers.length - 1; i--;) newBrain.layers[i].bias = mutate() ||
brains[Math.floor(Math.random() * brains.length)]
.layers[i].bias;
return newBrain;
}
function mutate(key, nodes) {
if (Math.random() > 0.05) return toss();
}
var brain = new Brain(1, 5, 1);
var newBrain = new Brain(1, 5, 1)
var result = brain.compute(1);
var cross = brain.cross(newBrain);
var brains = [];
for (var node = 45; node--;) brains.push({
brain: new Brain(1, 5, 4, 3, 2, 1)
});
for (var count = 1000000; count--;) {
brains.push({
brain: new Brain(1, 5, 4, 3, 2, 1)
});
for (var node = brains.length; node--;) {
var brain = brains[node];
var number = 1;
var target = number;
brain.fitness = 1 / Math.abs(number - brain.brain.compute(number));
}
brains.sort((a, b) => a.fitness < b.fitness);
if (count % 10000 === 0) console.log(brains.length, brains[0].fitness);
var newBrains = [];
for (var node = 10; node--;)
for (var j = node; j--;) newBrains.push({
brain: brains[node].brain.cross(brains[j].brain)
});
brains = newBrains;
}
console.log(brains);
What will I need to improve/change?
Here is the console log:
46 1.468903884218341
46 1.1881817088540865
46 4.899728181582378
46 1.5494097713447523
46 2.4958253537304644
46 2.4091648830940953
46 1.4000955420478967
46 1.7560836401632383
46 3.3419380735652897
46 2.8290305398668245
46 2.951901023302089
46 2.9400525658126675
46 2.6769575714598948
46 1.55835425177616
As you can see, the fitness seems to be random
Some advice...
Neural networks usually take an input which should somehow be related to the output. I couldn't find any inputs for the network? If you can't come up with any good ideas just use the XOR problem and try to solve it.
When checking if your population is becoming better over time don't look at all brains of each generation. Remember, you are intentionally creating some randomized networks which may or may not be good at your task. Try printing the Top result of each generation and maybe the average score. In a working genetic algorithm both values should become better over time. (altough the top score is way more significant & important)
(Not directly adressing your issue) Don't use javascript. You could probably transscribe your current code to java/c#/c++. These languages execute way faster than JS.
I have written a Javascript file of two algorithms. As shown in the code below, I am using a for loop to generate random values which are used by both algorithms as input.
At present, I am displaying output of the binarySearch and SearchSorted alternatively.
The problem I am facing is I have to pass the same array values generated by randomlyGenerateArray in the main program to both the algorithms for a meaningful comparison. But I don't know how to change the output format.
I have thought of adding them in different loops, but as I have explained above i need to use the same randomArray values for both the algorithms.
i.e., The below code produces output as shown below -
Binary Search Successful 1
Search Sorted Successful 5
Binary Search Successful 3
Search Sorted Successful 10
How do I display the output of Binary Search First and then display output of Search Sorted? it's something like this. Any help will be greatly appreciated.
Binary Search Successful 1
Binary Search Successful 3
Search Sorted Successful 5
Search Sorted Successful 10
// Binary Search Algorithm
function binarySearch(A,K)
{
var l = 0; // min
var r = A.length - 1; //max
var n = A.length;
var operations = 0;
while(l <= r)
{
var m = Math.floor((l + r)/2);
operations++;
if(K == A[m])
{
console.log('Binary Search Successful %d',operations);
return m;
}
else if(K < A[m])
{
r = m - 1;
}
else
{
l = m + 1;
}
}
operations++;
console.log('Binary Search Unsuccessful %d',operations);
return -1;
}
// Search Sorted Algorithm
function searchSorted(A, K)
{
var n = A.length;
var i = 0;
var operations = 0;
while (i < n)
{
operations++;
if (K < A[i])
{
return -1;
}
else if (K == A[i])
{
console.log('Search Sorted Successful %d', operations);
return i;
}
else
{
i = i + 1;
}
}
operations++;
console.log('Search Sorted Unsuccessful %d', operations);
return -1;
}
// Random Array generator
var randomlyGenerateArray = function(size)
{
var array = [];
for (var i = 0; i < size; i++)
{
var temp = Math.floor(Math.random() * maxArrayValue);
var final = array.splice(5, 0, 30);
array.push(final);
}
return array;
}
//Sort the Array
var sortNumber = function(a, b)
{
return a - b;
}
// Main Program
var program = function()
{
var incrementSize = largestArray / numberOfArrays;
for (var i = smallestArray; i <= largestArray; i += incrementSize)
{
var randomArray = randomlyGenerateArray(i);
var sort = randomArray.sort(sortNumber);
var randomKey = 30;
binarySearch(sort, randomKey);
searchSorted(sort, randomKey);
}
}
var smallestArray = 10;
var largestArray = 10000;
var numberOfArrays = 1000;
var minArrayValue = 1;
var maxArrayValue = 1000;
program();
You could store the sorted randomArrays in an array (which I've called sortedRandomArrays), then run a for loop for each search.
The Main Program would then look like:
// Main Program
var program = function()
{
var incrementSize = largestArray / numberOfArrays;
var sortedRandomArrays = [];
for (var i = smallestArray; i <= largestArray; i += incrementSize)
{
var randomArray = randomlyGenerateArray(i));
var sort = randomArray.sort(sortNumber);
sortedRandomArrays.push(sort);
var randomKey = 30;
}
for (var i = 0; i < sortedRandomArrays.length; i++)
{
binarySearch(sortedRandomArrays[i], randomKey);
}
for (var i = 0; i < sortedRandomArrays.length; i++)
{
searchSorted(sortedRandomArrays[i], randomKey);
}
}
Solution is simple: store the results and print with 2 separate loops (take out the printing from within the functions).
var program = function()
{
var binarySearchResults = [];
var sortedSearchResults = [];
var incrementSize = largestArray / numberOfArrays;
for (var i = smallestArray; i <= largestArray; i += incrementSize)
{
var randomArray = randomlyGenerateArray(i);
var sort = randomArray.sort(sortNumber);
var randomKey = 30;
binarySearchResults[i] = binarySearch(sort, randomKey);
sortedSearchResults[i] = searchSorted(sort, randomKey);
}
for (var i = smallestArray; i <= largestArray; i += incrementSize)
{
//print binary results
}
for (var i = smallestArray; i <= largestArray; i += incrementSize)
{
//print sorted results
}
}
I must be doing something stupid. The array newArea needs to add up data from all regions, i.e. be global. Regions are represented by variable p. But when I try to get newArea array to add to itself, e.g. newArea[p] += otherArray, it outputs NaNs. Even newArea[p] += 1 outputs NaNs.
Can anyone spot what I'm doing wrong? It's driving me mad and I'm working to a deadline.
mm=0
var maxVolume = 0;
var tempCAGR = 0;
var maxCAGR = 0;
var newArray = [];
var newRegions = [];
var newConsValues = [];
var newArea = [];
for (var p=0; p<arrayRef[mm].length; p++) {//9 regions
newArray[p] = [];
for (var q=0; q<arrayRef[mm][p].length; q++) {//4 scenarios
newArea[q] = [];
if (q==0) {
newRegions.push(arrayRef[mm][p][q][0]);
newConsValues.push(arrayRef[mm][p][q][1]);
}
for (var r=0; r<dates.length; r++) {//time
//console.log('p: '+p+', q: '+q+', r: '+r);
if (p==0) {
newArea[q][r] = 1;
} else {
newArea[q][r] += 1;
}
}
arrayRef[mm][p][q].shift();
tempCAGR = Math.pow(( arrayRef[mm][p][q][len] / arrayRef[mm][p][q][1] ),(1/len))-1;
//console.log(newRegions[p]+', num: '+arrayRef[mm][p][q][len-1]+', denom: '+arrayRef[mm][p][q][0]+', len: '+len+', cagr: '+tempCAGR);
newArray[p][q] = tempCAGR;
maxCAGR = Math.max(maxCAGR,tempCAGR);
}
}
console.log(newArea);
You are cleaning the array in newArea everytime you loop through it:
...loop q ...
newArea[q] = []; // <-- resets the array at q pos
... loop r ...
if (p==0) {
newArea[q][r] = 1;
} else {
newArea[q][r] += 1;
}
So when p === 0 it will fill an array at q pos of your newArea array. However, next iteration of p will clear them out, so there's nothing there to sum.
You probably want to keep the old array or create a new one if there isn't one.
newArea[q] = newArea[q] || [];
It looks like you do not have the variable initialised. With adding something to undefined, you get NaN.
You can change the art of incrementing with a default value:
if (p == 0) {
newArea[q][r] = 1;
} else {
newArea[q][r] = (newArea[q][r] || 0) + 1;
}
How do I generate objects on a map, without them occupying the same space or overlapping on a HTML5 Canvas?
X coordinate is randomly generated, to an extent. I thought checking inside the array to see if it's there already, and the next 20 values after that (to account for the width), with no luck.
var nrOfPlatforms = 14,
platforms = [],
platformWidth = 20,
platformHeight = 20;
var generatePlatforms = function(){
var positiony = 0, type;
for (var i = 0; i < nrOfPlatforms; i++) {
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
var positionx = (Math.random() * 4000) + 500 - (points/100);
var duplicatetest = 21;
for (var d = 0; d < duplicatetest; d++) {
var duplicate = $(jQuery.inArray((positionx + d), platforms));
if (duplicate > 0) {
var duplicateconfirmed = true;
}
}
if (duplicateconfirmed) {
var positionx = positionx + 20;
}
var duplicateconfirmed = false;
platforms[i] = new Platform(positionx,positiony,type);
}
}();
I originally made a cheat fix by having them generate in an area roughly 4000 big, decreasing the odds, but I want to increase the difficulty as the game progresses, by making them appear more together, to make it harder. But then they overlap.
In crude picture form, I want this
....[]....[].....[]..[]..[][]...
not this
......[]...[[]]...[[]]....[]....
I hope that makes sense.
For reference, here is the code before the array check and difficulty, just the cheap distance hack.
var nrOfPlatforms = 14,
platforms = [],
platformWidth = 20,
platformHeight = 20;
var generatePlatforms = function(){
var position = 0, type;
for (var i = 0; i < nrOfPlatforms; i++) {
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
platforms[i] = new Platform((Math.random() * 4000) + 500,position,type);
}
}();
EDIT 1
after some debugging, duplicate is returning as [object Object] instead of the index number, not sure why though
EDIT 2
the problem is the objects are in the array platforms, and x is in the array object, so how can I search inside again ? , that's why it was failing before.
Thanks to firebug and console.log(platforms);
platforms = [Object { image=img, x=1128, y=260, more...}, Object { image=img, x=1640, y=260, more...} etc
You could implement a while loop that tries to insert an object and silently fails if it collides. Then add a counter and exit the while loop after a desired number of successful objects have been placed. If the objects are close together this loop might run longer so you might also want to give it a maximum life span. Or you could implement a 'is it even possible to place z objects on a map of x and y' to prevent it from running forever.
Here is an example of this (demo):
//Fill an array with 20x20 points at random locations without overlap
var platforms = [],
platformSize = 20,
platformWidth = 200,
platformHeight = 200;
function generatePlatforms(k) {
var placed = 0,
maxAttempts = k*10;
while(placed < k && maxAttempts > 0) {
var x = Math.floor(Math.random()*platformWidth),
y = Math.floor(Math.random()*platformHeight),
available = true;
for(var point in platforms) {
if(Math.abs(point.x-x) < platformSize && Math.abs(point.y-y) < platformSize) {
available = false;
break;
}
}
if(available) {
platforms.push({
x: x,
y: y
});
placed += 1;
}
maxAttempts -= 1;
}
}
generatePlatforms(14);
console.log(platforms);
Here's how you would implement a grid-snapped hash: http://jsfiddle.net/tqFuy/1/
var can = document.getElementById("can"),
ctx = can.getContext('2d'),
wid = can.width,
hei = can.height,
numPlatforms = 14,
platWid = 20,
platHei = 20,
platforms = [],
hash = {};
for(var i = 0; i < numPlatforms; i++){
// get x/y values snapped to platform width/height increments
var posX = Math.floor(Math.random()*(wid-platWid)/platWid)*platWid,
posY = Math.floor(Math.random()*(hei-platHei)/platHei)*platHei;
while (hash[posX + 'x' + posY]){
posX = Math.floor(Math.random()*wid/platWid)*platWid;
posY = Math.floor(Math.random()*hei/platHei)*platHei;
}
hash[posX + 'x' + posY] = 1;
platforms.push(new Platform(/* your arguments */));
}
Note that I'm concatenating the x and y values and using that as the hash key. This is to simplify the check, and is only a feasible solution because we are snapping the x/y coordinates to specific increments. The collision check would be more complicated if we weren't snapping.
For large sets (seems unlikely from your criteria), it'd probably be better to use an exclusion method: Generate an array of all possible positions, then for each "platform", pick an item from the array at random, then remove it from the array. This is similar to how you might go about shuffling a deck of cards.
Edit — One thing to note is that numPlatforms <= (wid*hei)/(platWid*platHei) must evaluate to true, otherwise the while loop will never end.
I found the answer on another question ( Searching for objects in JavaScript arrays ) using this bit of code to search the objects in the array
function search(array, value){
var j, k;
for (j = 0; j < array.length; j++) {
for (k in array[j]) {
if (array[j][k] === value) return j;
}
}
}
I also ended up rewriting a bunch of the code to speed it up elsewhere and recycle platforms better.
it works, but downside is I have fewer platforms, as it really starts to slow down. In the end this is what I wanted, but its no longer feasible to do it this way.
var platforms = new Array();
var nrOfPlatforms = 7;
platformWidth = 20,
platformHeight = 20;
var positionx = 0;
var positiony = 0;
var arrayneedle = 0;
var duplicatetest = 21;
function search(array, value){
var j, k;
for (j = 0; j < array.length; j++) {
for (k in array[j]) {
if (array[j][k] === value) return j;
}
}
}
function generatePlatforms(ind){
roughx = Math.round((Math.random() * 2000) + 500);
type = ~~(Math.random()*5);
if (type == 0) type = 1;
else type = 0;
var duplicate = false;
for (var d = 0; d < duplicatetest; d++) {
arrayneedle = roughx + d;
var result = search(platforms, arrayneedle);
if (result >= 0) {
duplicate = true;
}
}
if (duplicate = true) {
positionx = roughx + 20;
}
if (duplicate = false) {
positionx = roughx;
}
platforms[ind] = new Platform(positionx,positiony,type);
}
var generatedplatforms = function(){
for (var i = 0; i < nrOfPlatforms; i++) {
generatePlatforms(i);
};
}();
you go big data, generate all possibilities, store each in an array, shuffle the array,
trim the first X items, this is your non heuristic algorithm.