В настоящее время я работаю над проектом, связанным с Джулией Ланг, алгоритмом geneti c, нейронными сетями и игрой в змеи, но по какой-то причине я не могу преподавать nnet с моим генети c. Я почти уверен, что это проблема с логикой c за генети c, но я не уверен.
AI.jl
#=
4x movement directions
4x check wheter the fruit is in one of the snake head direction OK
4x check wheter the snake body is in one of the snake head direction OK
4x check wheter the snake body is in on of the snake head diagonal quadrants OK
4x check wheter the fruit is in one of the snake head diagonal quadrants OK
4x check wheter the snake is near to the wall OK
=#
mutable struct SnakeCandidate <: Candidate
game::SnakeGame
nn::NeuralNetwork
fitness::Float64
probability::Float64
end
const sensorCount = 20
function aiGetSensorsData(game::SnakeGame)
data = Array{Float64, 1}(undef, 0)
#fruit is on the left side
onLeft = game.snake.head.x > game.fruit.x
#fruit is on the right side
onRight = game.snake.head.x < game.fruit.x
#fruit is on the middle
onMiddle = game.snake.head.x == game.fruit.x
#FRUIT DIRECTION
#fruit is at snake's left
push!(data, onLeft && game.snake.head.y == game.fruit.y)
#fruit is at snake's right
push!(data, onRight && game.snake.head.y == game.fruit.y)
#fruit is at snake's top
push!(data, onMiddle && game.snake.head.y > game.fruit.y)
#fruit is at snake's down
push!(data, onMiddle && game.snake.head.y < game.fruit.y)
#FRUIT QUADRANT
#fruit is at the 1st quadrant
push!(data, onRight && game.snake.head.y > game.fruit.y)
#fruit is at the 2nd quadrant
push!(data, onLeft && game.snake.head.y > game.fruit.y)
#fruit is at the 3rd quadrant
push!(data, onLeft && game.snake.head.y < game.fruit.y)
#fruit is at the 4th quadrant
push!(data, onRight && game.snake.head.y < game.fruit.y)
#SNAKE BODY
#BODY QUADRANT
bodySides = falses(4)
bodyQuadrants = falses(4)
for i in 2:length(game.snake.body)
xEqual::Bool = game.snake.head.x == game.snake.body[i].x
yEqual::Bool = game.snake.head.y == game.snake.body[i].y
#a piece of the snake body is at snake's left
bodySides[1] = bodySides[1] || (yEqual && game.snake.head.x > game.snake.body[i].x)
#a piece of the snake body is at snake's right
bodySides[2] = bodySides[2] || (yEqual && game.snake.head.x < game.snake.body[i].x)
#a piece of the snake body is at snake's top
bodySides[3] = bodySides[3] || (xEqual && game.snake.head.y > game.snake.body[i].y)
#a piece of the snake body is at snake's bottom
bodySides[4] = bodySides[4] || (xEqual && game.snake.head.y < game.snake.body[i].y)
yOver = game.snake.head.y > game.snake.body[i].y
yUnder = game.snake.head.y < game.snake.body[i].y
#a piece of the snake is on the 1st quadrant
bodyQuadrants[1] = bodyQuadrants[1] || (yOver && game.snake.head.x < game.snake.body[i].x)
#a piece of the snake is on the 2nd quadrant
bodyQuadrants[2] = bodyQuadrants[2] || (yOver && game.snake.head.x > game.snake.body[i].x)
#a piece of the snake is on the 3rd quadrant
bodyQuadrants[3] = bodyQuadrants[3] || (yUnder && game.snake.head.x < game.snake.body[i].x)
#a piece of the snake is on the 4th quadrant
bodyQuadrants[4] = bodyQuadrants[4] || (yUnder && game.snake.head.x < game.snake.body[i].x)
end
append!(data, bodySides)
append!(data, bodyQuadrants)
#NEAR TO WALL
#top wall
push!(data, game.snake.head.y == 1)
#left wall
push!(data, game.snake.head.x == 1)
#bottom wall
push!(data, game.snake.head.y == game.mapH)
#right wall
push!(data, game.snake.head.x == game.mapW)
#DIRECTIONS
push!(data, game.snake.direction == UP)
push!(data, game.snake.direction == DOWN)
push!(data, game.snake.direction == LEFT)
push!(data, game.snake.direction == RIGHT)
return data
end
aiCreateCandidate() = SnakeCandidate(SnakeGame(9, 9, 5, 5, UP),
Float64NeuralNetwork(24, [20], 4), 0, 0)
function aiNextMovement(game::SnakeGame, nn::NeuralNetwork)
snakeSetDirection(game, findmax(nnetExecute(nn, aiGetSensorsData(game)))[2])
end
function aiFitnessEvaluate(cand::Candidate)
cand.fitness = 0
maxMovement = 100
movCount = 0
while !cand.game.lost && movCount < maxMovement
aiNextMovement(cand.game, cand.nn)
prevSize = length(cand.game.snake.body)
snakeNextFrame(cand.game)
movCount += 1
movCount = (1 - (length(cand.game.snake.body) - prevSize)) * movCount
end
cand.fitness = 5 * (length(cand.game.snake.body) - 1) + 1
return cand
end
function aiTrain()
tset = genExecute(aiCreateCandidate, aiFitnessEvaluate, 2000, 50, 50, 2, 0.1)
for cand in tset
println(cand.fitness)
end
game = SnakeGame(9,9,5,5,UP)
nn = tset[1].nn
while !game.lost
display(snakeGetDrawMatrix(game))
aiNextMovement(game, nn)
snakeNextFrame(game)
sleep(0.3)
end
en
NeuralNetwork.jl
abstract type Neuron end
abstract type NeuralNetwork end
mutable struct Float64Neuron <: Neuron
weights::Array{Float64, 1}
function Float64Neuron(inputNumber::Int)
neuron = new()
neuron.weights = [2 * rand() - 1 for i in 1:inputNumber]
return neuron
end
end
mutable struct Float64NeuralNetwork <: NeuralNetwork
inputNeuronsNumber::Int
hiddenLayers::Array{Array{Float64Neuron, 1}, 1}
outputLayer::Array{Float64Neuron, 1}
weights::Float64
layers::Array{Array{Float64Neuron, 1}, 1}
function Float64NeuralNetwork(inputNumber::Int,
hiddenLayersArchitecture::Array{Int, 1}, outputNeuronsNumber::Int)
net = new()
net.inputNeuronsNumber = inputNumber
net.hiddenLayers = Array{Array{Float64Neuron, 1}, 1}(undef, 0)
currentInput = inputNumber
for neuronsAmmout in hiddenLayersArchitecture
push!(net.hiddenLayers, [Float64Neuron(currentInput) for i in 1:neuronsAmmout])
currentInput = neuronsAmmout
end
net.outputLayer = [Float64Neuron(currentInput) for i in 1:outputNeuronsNumber]
net.layers = [net.hiddenLayers; [net.outputLayer]]
return net
end
end
neuronSigmoid(number) = 1 / (1 + exp(-number))
neuronExecute(neuron::Float64Neuron, input::Array{Float64, 1}) = neuronSigmoid(dot(neuron.weights, input))
function nnetExecuteLayer(layer::Array{Float64Neuron, 1}, input::Array{Float64, 1})
return [neuronExecute(neuron, input) for neuron in layer]
end
function nnetExecute(net::NeuralNetwork, input)
for layer in net.layers
@inbounds input = nnetExecuteLayer(layer, input)
end
return input
end
Geneti c .jl
#note: the subtypes of candidate must have a NeuralNetwork field named as nn
# and another Float64 field named fitness
abstract type Candidate end
function genSwapArrayIndexes(arr, index1::Integer, index2::Integer)
aux = arr[index1]
arr[index1] = arr[index2]
arr[index2] = aux
end
function genSelectionRoullete(cands, selectedCandidatesCount::Integer)
#reversively sorts the array by each item fitness
sort!(cands, by=v->v.fitness, rev=true)
for i in 1:selectedCandidatesCount
arr = view(cands, i:length(cands))
#calculates fitness sum
fitSum = 0
for cand in arr
fitSum += cand.fitness
end
#calculates each candidate probability
lastProb = 0
for cand in arr
cand.probability = cand.fitness/fitSum + lastProb
lastProb = cand.probability
end
#selects one item from the list by a random value of probability
chosen = rand()
for j in 1:length(arr)
if arr[j].probability > chosen
genSwapArrayIndexes(cands, i, j + i - 1)
break
end
end
end
end
function genCrossoverMixParentsWeights(father::Array{Float64, 1}, mother::Array{Float64, 1},
crossoverPoint::Integer)
return [father[1:crossoverPoint]; mother[(crossoverPoint+1):length(mother)]]
end
function genGetCrossoverPoint(layer, crossoverCuttingFractionDivisor::Integer)
return convert(Int64, floor(length(layer) / crossoverCuttingFractionDivisor))
end
function genCrossover(cands, selectedCandidatesCount::Integer,
crossoverCuttingFractionDivisor::Integer)
for i in (selectedCandidatesCount + 1):length(cands)
father = cands[rand(1:selectedCandidatesCount)].nn
mother = cands[rand(1:selectedCandidatesCount)].nn
for j in eachindex(cands[i].nn.layers)
crossoverPoint = genGetCrossoverPoint(cands[i].nn.layers[j],
crossoverCuttingFractionDivisor)
for k in eachindex(cands[i].nn.layers[j])
cands[i].nn.layers[j][k].weights = genCrossoverMixParentsWeights(
father.layers[j][k].weights, mother.layers[j][k].weights, crossoverPoint)
end
end
end
end
function genMutateWeight(weights::Array{Float64, 1})
weights[rand(1:length(weights))] = 2 * rand() - 1
end
function genMutatation(cands, mutationRate::Float64)
for cand in cands
while mutationRate > rand()
layerindex = rand(1:length(cand.nn.layers))
neuronindex = rand(1:length(cand.nn.layers[layerindex]))
genMutateWeight(cand.nn.layers[layerindex][neuronindex].weights)
mutationRate /= 2
end
end
end
function genExecute(createCandidate::Function, fitnessFunc::Function, populationSize::Integer,
generationCount::Integer, selectedCandidatesCount::Integer, crossoverCutingFractionDivisor::Integer, mutationRate::Float64)
candidates = [fitnessFunc(createCandidate()) for i in 1:populationSize]
for gen in 1:generationCount
genSelectionRoullete(candidates, selectedCandidatesCount)
genCrossover(candidates, selectedCandidatesCount, crossoverCutingFractionDivisor)
genMutatation(candidates, mutationRate)
for candidate in candidates
fitnessFunc(candidate)
end
end
sort!(candidates, by=x->x.fitness, rev=true)
return candidates
end
PS: я пытался тренировать свой nnet с 30 поколениями с населением 2000 субъектов, но у меня не было положительных результатов