Attempts to fix genetic training
This commit is contained in:
parent
6698148d2c
commit
fbed641470
60
src/main.nim
60
src/main.nim
|
@ -7,6 +7,7 @@ import std/tables
|
||||||
import std/math
|
import std/math
|
||||||
import std/random
|
import std/random
|
||||||
import std/algorithm
|
import std/algorithm
|
||||||
|
import std/strformat
|
||||||
|
|
||||||
|
|
||||||
## A bunch of activation functions
|
## A bunch of activation functions
|
||||||
|
@ -27,28 +28,29 @@ func ind2sub(n: int, shape: tuple[rows, cols: int]): tuple[row, col: int] =
|
||||||
|
|
||||||
proc loss(params: TableRef[string, float]): float =
|
proc loss(params: TableRef[string, float]): float =
|
||||||
## Our loss function for tris
|
## Our loss function for tris
|
||||||
result = params["moves"]
|
if params.hasKey("sameMove"):
|
||||||
if int(params["result"]) == GameStatus.Draw.int:
|
result = 24 - params["moves"]
|
||||||
result += 6.0
|
else:
|
||||||
elif int(params["result"]) == GameStatus.Lose.int:
|
result = params["moves"]
|
||||||
result += 12.0
|
if int(params["result"]) == GameStatus.Draw.int:
|
||||||
result = sigmoid(result)
|
result += 6
|
||||||
|
elif int(params["result"]) == GameStatus.Lose.int:
|
||||||
|
result += 12
|
||||||
|
echo result
|
||||||
|
|
||||||
|
|
||||||
proc compareNetworks(a, b: NeuralNetwork): int =
|
proc compareNetworks(a, b: NeuralNetwork): int =
|
||||||
|
if a.params.len() == 0:
|
||||||
|
return -1
|
||||||
|
elif b.params.len() == 0:
|
||||||
|
return 1
|
||||||
return cmp(loss(a.params), loss(b.params))
|
return cmp(loss(a.params), loss(b.params))
|
||||||
|
|
||||||
|
|
||||||
proc crossover(a, b: NeuralNetwork): NeuralNetwork =
|
proc crossover(a, b: NeuralNetwork): NeuralNetwork =
|
||||||
result = deepCopy(a)
|
result = deepCopy(a)
|
||||||
for i, layer in a.layers:
|
|
||||||
result.layers[i].weights = layer.weights.copy()
|
|
||||||
result.layers[i].biases = layer.biases.copy()
|
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < a.layers.len():
|
while i < a.layers.len():
|
||||||
result.layers[i] = new(Layer)
|
|
||||||
result.layers[i].inputSize = a.layers[i].inputSize
|
|
||||||
result.layers[i].outputSize = a.layers[i].outputSize
|
|
||||||
# We inherit 50% of our weights and biases from our first
|
# We inherit 50% of our weights and biases from our first
|
||||||
# parent and the other 50% from the other parent
|
# parent and the other 50% from the other parent
|
||||||
result.layers[i].weights = where(rand[float](a.layers[i].weights.shape) >= 0.5, a.layers[i].weights, b.layers[i].weights)
|
result.layers[i].weights = where(rand[float](a.layers[i].weights.shape) >= 0.5, a.layers[i].weights, b.layers[i].weights)
|
||||||
|
@ -65,23 +67,25 @@ proc crossover(a, b: NeuralNetwork): NeuralNetwork =
|
||||||
|
|
||||||
|
|
||||||
## Our training program
|
## Our training program
|
||||||
const Population = 2
|
const Population = 100
|
||||||
const Iterations = 100
|
const Iterations = 300
|
||||||
const Epochs = 10
|
const Epochs = 10
|
||||||
const Take = 2
|
const Take = 15
|
||||||
|
|
||||||
|
|
||||||
var networks: seq[NeuralNetwork] = @[]
|
var networks: seq[NeuralNetwork] = @[]
|
||||||
|
var best: seq[NeuralNetwork] = @[]
|
||||||
for _ in 0..<Population:
|
for _ in 0..<Population:
|
||||||
networks.add(newNeuralNetwork(@[9, 8, 10, 9], activationFunc=newActivation(sigmoid, func (x, y: float): float = 0.0),
|
networks.add(newNeuralNetwork(@[9, 16, 12, 9], activationFunc=newActivation(sigmoid, func (x, y: float): float = 0.0),
|
||||||
lossFunc=newLoss(loss, func (x, y: float): float = 0.0), weightRange=(-1.0, +1.0), learnRate=0.02))
|
lossFunc=newLoss(loss, func (x, y: float): float = 0.0), weightRange=(-1.0, +1.0), biasRange=(-0.5, 0.5),
|
||||||
|
learnRate=0.02))
|
||||||
|
|
||||||
var gameOne: TrisGame
|
var gameOne: TrisGame
|
||||||
var gameTwo: TrisGame
|
var gameTwo: TrisGame
|
||||||
var one: NeuralNetwork
|
var one: NeuralNetwork
|
||||||
var two: NeuralNetwork
|
var two: NeuralNetwork
|
||||||
var pos: tuple[row, col: int]
|
var pos: tuple[row, col: int]
|
||||||
var lost: bool = false
|
|
||||||
for epoch in 0..<Epochs:
|
for epoch in 0..<Epochs:
|
||||||
for iteration in 0..<Iterations:
|
for iteration in 0..<Iterations:
|
||||||
gameOne = newTrisGame()
|
gameOne = newTrisGame()
|
||||||
|
@ -99,21 +103,21 @@ for epoch in 0..<Epochs:
|
||||||
# We consider this a loss
|
# We consider this a loss
|
||||||
one.params["result"] = float(Lose)
|
one.params["result"] = float(Lose)
|
||||||
two.params["result"] = float(Lose)
|
two.params["result"] = float(Lose)
|
||||||
lost = true
|
one.params["sameMove"] = 1.0
|
||||||
|
two.params["sameMove"] = 1.0
|
||||||
break
|
break
|
||||||
gameTwo.place(TileKind.Self, pos.row, pos.col)
|
gameTwo.place(TileKind.Self, pos.row, pos.col)
|
||||||
gameOne.place(TileKind.Enemy, pos.row, pos.col)
|
gameOne.place(TileKind.Enemy, pos.row, pos.col)
|
||||||
if not lost:
|
if not one.params.hasKey("sameMove"):
|
||||||
one.params["result"] = gameOne.get().float()
|
one.params["result"] = gameOne.get().float()
|
||||||
two.params["result"] = gameTwo.get().float()
|
two.params["result"] = gameTwo.get().float()
|
||||||
else:
|
|
||||||
lost = false
|
|
||||||
one.params["moves"] = gameOne.moves.float()
|
one.params["moves"] = gameOne.moves.float()
|
||||||
two.params["moves"] = gameTwo.moves.float()
|
two.params["moves"] = gameTwo.moves.float()
|
||||||
networks.sort(cmp=compareNetworks)
|
networks.sort(cmp=compareNetworks)
|
||||||
networks = networks[0..<Take]
|
best = networks[0..<Take]
|
||||||
one = sample(networks)
|
while networks.len() < Population:
|
||||||
two = sample(networks)
|
one = sample(best)
|
||||||
while one == two:
|
two = sample(best)
|
||||||
two = sample(networks)
|
while one == two:
|
||||||
networks.add(one.crossover(two))
|
two = sample(best)
|
||||||
|
networks.add(one.crossover(two))
|
|
@ -85,7 +85,7 @@ proc newActivation*(function: proc (input: float): float {.noSideEffect.}, deriv
|
||||||
result.derivative = derivative
|
result.derivative = derivative
|
||||||
|
|
||||||
|
|
||||||
proc newLayer*(inputSize: int, outputSize: int, weightRange: tuple[start, stop: float]): Layer =
|
proc newLayer*(inputSize: int, outputSize: int, weightRange, biasRange: tuple[start, stop: float]): Layer =
|
||||||
## Creates a new layer with inputSize input
|
## Creates a new layer with inputSize input
|
||||||
## parameters and outputSize outgoing outputs.
|
## parameters and outputSize outgoing outputs.
|
||||||
## Weights are initialized with random values
|
## Weights are initialized with random values
|
||||||
|
@ -96,7 +96,7 @@ proc newLayer*(inputSize: int, outputSize: int, weightRange: tuple[start, stop:
|
||||||
var biases = newSeqOfCap[float](outputSize)
|
var biases = newSeqOfCap[float](outputSize)
|
||||||
var biasGradients = newSeqOfCap[float](outputSize)
|
var biasGradients = newSeqOfCap[float](outputSize)
|
||||||
for _ in 0..<outputSize:
|
for _ in 0..<outputSize:
|
||||||
biases.add(0.0)
|
biases.add(rand(biasRange.start..biasRange.stop))
|
||||||
biasGradients.add(0.0)
|
biasGradients.add(0.0)
|
||||||
var weights = newSeqOfCap[seq[float]](inputSize * outputSize)
|
var weights = newSeqOfCap[seq[float]](inputSize * outputSize)
|
||||||
var weightGradients = newSeqOfCap[seq[float]](inputSize * outputSize)
|
var weightGradients = newSeqOfCap[seq[float]](inputSize * outputSize)
|
||||||
|
@ -113,13 +113,13 @@ proc newLayer*(inputSize: int, outputSize: int, weightRange: tuple[start, stop:
|
||||||
|
|
||||||
|
|
||||||
proc newNeuralNetwork*(layers: seq[int], activationFunc: Activation, lossFunc: Loss,
|
proc newNeuralNetwork*(layers: seq[int], activationFunc: Activation, lossFunc: Loss,
|
||||||
learnRate: float, weightRange: tuple[start, stop: float]): NeuralNetwork =
|
learnRate: float, weightRange, biasRange: tuple[start, stop: float]): NeuralNetwork =
|
||||||
## Initializes a new neural network
|
## Initializes a new neural network
|
||||||
## with the given layer layout
|
## with the given layer layout
|
||||||
new(result)
|
new(result)
|
||||||
result.layers = newSeqOfCap[Layer](len(layers))
|
result.layers = newSeqOfCap[Layer](len(layers))
|
||||||
for i in 0..<layers.high():
|
for i in 0..<layers.high():
|
||||||
result.layers.add(newLayer(layers[i], layers[i + 1], weightRange))
|
result.layers.add(newLayer(layers[i], layers[i + 1], weightRange, biasRange))
|
||||||
result.activation = activationFunc
|
result.activation = activationFunc
|
||||||
result.loss = lossFunc
|
result.loss = lossFunc
|
||||||
result.learnRate = learnRate
|
result.learnRate = learnRate
|
||||||
|
|
Loading…
Reference in New Issue