Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Genetic Algorithm - Sample Code #911

andrewtacon started this conversation in General
Discussion options

Wrote this, not sure if it will be useful to anyone. It:

  1. sets up to create a set of random neural networks
  2. breeds to most successful networks together to create a new generation

/**
 * This creates a random model for a neural network
 * @param {*} config - normal config from a model 
 * @param {*} inputs - list of inputs as text array
 * @param {*} outputs - list of outputs as text array
 * @param {*} qty - how many to produce - default returns 1 model, other values return an array containing requested models
 */
function createRandomModel(config, inputs, outputs, qty = 1) {
 let modelSet = []
 for (let number = 0; number < qty; number++) {
 let model = {}
 model.type = "NeuralNetwork"
 model.options = {
 "inputSize": 0,
 "outputSize": 0,
 "binaryThresh": 0.5
 }
 //options should include config
 for (let [key, value] of Object.entries(config)) {
 model.options[key] = value
 }
 model.trainOpts = {
 "activation": "sigmoid",
 "iterations": 20000,
 "errorThresh": 0.005,
 "log": false,
 "logPeriod": 10,
 "leakyReluAlpha": 0.01,
 "learningRate": 0.3,
 "momentum": 0.1,
 "callbackPeriod": 10,
 "timeout": "Infinity",
 "beta1": 0.9,
 "beta2": 0.999,
 "epsilon": 1e-8
 }
 if (!isNaN(inputs)) {
 model.inputLookupLength = inputs.length
 model.inputLookup = {}
 for (let i = 0; i < inputs.length; i++) {
 model.inputLookup[inputs[i]] = i
 }
 } else {
 model.inputLookupLength = 0
 model.inputLookup = null
 }
 if (isNaN(outputs)) {
 model.outputLookupLength = outputs.length
 model.outputLookup = {}
 for (let i = 0; i < outputs.length; i++) {
 model.outputLookup[outputs[i]] = i
 }
 } else {
 model.outputLookupLength = 0
 model.outputLookup = null
 }
 model.sizes = []
 if (isNaN(inputs)) {
 model.sizes.push(inputs.length)
 } else {
 model.sizes.push(inputs)
 }
 if (!model.options.hiddenLayers) {
 model.sizes.push(3)
 } else {
 model.sizes.push(...model.options.hiddenLayers)
 }
 if (isNaN(outputs)) {
 model.sizes.push(outputs.length)
 } else {
 model.sizes.push(outputs)
 }
 //build the layers models
 model.layers = []
 for (let i = 0; i < model.sizes.length; i++) {
 model.layers.push(
 {
 weights: [],
 biases: []
 }
 )
 }
 //populate the biases and weights
 //biases match the number of elements in the layer
 //weights match the number of elements in the previous layer
 for (let i = 1; i < model.sizes.length; i++) {
 let current = model.sizes[i]
 let previous = model.sizes[i - 1]
 //populate biases
 for (let j = 0; j < current; j++) {
 let bias = Math.random() * 20 - 10 //between -10 and 10
 model.layers[i].biases.push(bias)
 }
 //populate weights
 for (let j = 0; j < current; j++) {
 let weights = []
 for (let k = 0; k < previous; k++) {
 let weight = Math.random() * 20 - 10 //between -10 and 10
 weights.push(weight)
 }
 model.layers[i].weights.push(weights)
 }
 }
 modelSet.push(model)
 }
 if (qty === 1) {
 return modelSet[0]
 } else {
 return modelSet
 }
}
/**
 * this functions takes any two given models and creates a hybrid by 
 * randomly assigning weights and biases to the new individual based on the 
 * parents and then adjusting this value by a small margin (up to 5% above or below current value)
 */
function breedPair(model1, model2, bigVariation) {
 let model3 = JSON.parse(JSON.stringify(model1))
 for (let i = 1; i < model3.layers.length; i++) {
 let layerOn3 = model3.layers[i]
 let layerOn2 = model2.layers[i]
 //combine weights randomly
 for (let w = 0; w < layerOn2.weights.length; w++) {
 let weightsOn3 = layerOn3.weights[w]
 let weightsOn2 = layerOn2.weights[w]
 for (let v = 0; v < weightsOn3.length; v++) {
 if (!bigVariation) {
 weightsOn3[v] = (weightsOn2[v] + weightsOn3[v]) / 2 * (0.95 + Math.random() / 10)
 } else {
 weightsOn3[v] = (weightsOn2[v] + weightsOn3[v]) / 2 * (0.5 + Math.random())
 }
 }
 }
 //combine biases randomly
 for (let b = 0; b < layerOn2.biases.length; b++) {
 if (!bigVariation) {
 layerOn3.biases[b] = (layerOn3.biases[b] + layerOn2.biases[b]) / 2 * (0.95 + Math.random() / 10)
 } else {
 layerOn3.biases[b] = (layerOn3.biases[b] + layerOn2.biases[b]) / 2 * (0.5 + Math.random())
 }
 }
 }
 return model3
}
/**
 * 
 * @param {Array} models - an array of models to breed off of
 * @param {Array} fitnesses - an array of numbers for the fitness of the models - this is used to determine the probability that a model will contribute to the next
 * generation
 */
let generation = 0
function breedNextGeneration(models, fitnesses, keepbest = true) {
 //check - if all fitness the same value the create entire new random generation
 let f = fitnesses[0]
 let bigVariation = true
 for (let i = 0; i < fitnesses.length; i++) {
 if (fitnesses[i] !== f) {
 bigVariation = false
 }
 }
 let initialValue = 0
 let totalFitness = fitnesses.reduce(
 (accumulator, currentValue) => accumulator + currentValue,
 initialValue
 )
 console.log(`Generation ${generation++} average fitness: ${totalFitness / fitnesses.length}`)
 console.log(`Commencing generation ${generation}...`)
 let fit = [] //this is an array that normalises the fitnesses to be proportional to a range of 0-1
 let count = 0
 let max = 0
 let bestModel
 for (let i = 0; i < fitnesses.length; i++) {
 if (fitnesses[i] > max) {
 max = fitnesses[i]
 bestModel = models[i]
 }
 count += fitnesses[i]
 let percent = count / totalFitness
 fit.push(percent * 2) //multiply by two so only top half breed
 }
 let newModels = []
 //keep the previous best model
 if (keepbest) { newModels.push(bestModel) }
 while (newModels.length < models.length) {
 let p = Math.random()
 let pos = 0
 while (p > fit[pos]) {
 pos++
 }
 let model1 = models[pos]
 p = Math.random()
 pos = 0
 while (p > fit[pos]) {
 pos++
 }
 let model2 = models[pos]
 newModels.push(breedPair(model1, model2, bigVariation))
 }
 return newModels
}
You must be logged in to vote

Replies: 0 comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
1 participant

AltStyle によって変換されたページ (->オリジナル) /