szymski

Untitled

Apr 19th, 2017
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --@name NeuroEvolution Experiments
  2. --@author Szymekk
  3. --@shared
  4.  
  5. if CLIENT then return end
  6.  
  7. --------------------------------------
  8. -- Neuron class
  9. --------------------------------------
  10.  
  11. local neuron = { }
  12. neuron.__index = neuron
  13.  
  14. function neuron:init()
  15. self.inputs = { }
  16. self.weights = { }
  17.  
  18. self.value = nil
  19. end
  20.  
  21. function neuron:activationFunction(x)
  22. return 1 / (1 + math.exp(-x)) * 2 - 1
  23. --return x
  24. end
  25.  
  26. function neuron:addInput(n)
  27. self.inputs[#self.inputs + 1] = n
  28. self.weights[#self.inputs] = math.random() * 2 - 1
  29. end
  30.  
  31. function neuron:getOutput()
  32. if not self.value and #self.inputs > 0 then
  33. local sum = 0
  34.  
  35. for k, v in pairs(self.inputs) do
  36. sum = sum + v:getOutput() * self.weights[k]
  37. end
  38.  
  39. self.value = neuron:activationFunction(sum)
  40. end
  41.  
  42. return self.value
  43. end
  44.  
  45. function neuron.new()
  46. local tbl = { }
  47. setmetatable(tbl, neuron)
  48. tbl:init()
  49.  
  50. return tbl
  51. end
  52.  
  53. --------------------------------------
  54. -- Neural network class
  55. --------------------------------------
  56.  
  57. local network = { }
  58. network.__index = network
  59.  
  60. function network:init()
  61. self.inputs = { }
  62. self.outputs = { }
  63. self.hiddenLayers = { }
  64. self.allNeurons = { }
  65. end
  66.  
  67. function network:addInputs(count)
  68. for i = 1, count do
  69. local n = neuron.new()
  70. self.inputs[i] = n
  71. self.allNeurons[#self.allNeurons + 1] = n
  72. end
  73. end
  74.  
  75. function network:addHiddenLayer(count)
  76. local layer = { }
  77.  
  78. for i = 1, count do
  79. local n = neuron.new()
  80. layer[i] = n
  81. self.allNeurons[#self.allNeurons + 1] = n
  82. end
  83.  
  84. self.hiddenLayers[#self.hiddenLayers + 1] = layer
  85.  
  86. return layer
  87. end
  88.  
  89. function network:addOutputs(count)
  90. for i = 1, count do
  91. local n = neuron.new()
  92. self.outputs[i] = n
  93. self.allNeurons[#self.allNeurons + 1] = n
  94. end
  95. end
  96.  
  97. function network:setInputs(tbl)
  98. for k, v in pairs(self.inputs) do
  99. v.value = tbl[k]
  100. end
  101. end
  102.  
  103. function network:resetValues()
  104. for k, v in pairs(self.allNeurons) do
  105. v.value = nil
  106. end
  107. end
  108.  
  109. function network:connectNeurons()
  110. local allLayers = { self.inputs }
  111. for k, v in pairs(self.hiddenLayers) do
  112. allLayers[#allLayers + 1] = v
  113. end
  114. allLayers[#allLayers + 1] = self.outputs
  115.  
  116. for i = 2, #allLayers do
  117. for k1, v1 in pairs(allLayers[i - 1]) do
  118. for k2, v2 in pairs(allLayers[i]) do
  119. v2:addInput(v1)
  120. end
  121. end
  122. end
  123. end
  124.  
  125. function network:run(tbl)
  126. self:resetValues()
  127. self:setInputs(tbl)
  128.  
  129. local outputValues = { }
  130.  
  131. for k, v in pairs(self.outputs) do
  132. outputValues[k] = v:getOutput()
  133. end
  134.  
  135. return outputValues
  136. end
  137.  
  138. function network:dumpWeights()
  139. local weights = { }
  140.  
  141. for k, v in pairs(self.allNeurons) do
  142. weights[k] = { }
  143.  
  144. for k2, v2 in pairs(v.weights) do
  145. weights[k][k2] = v2
  146. end
  147. end
  148.  
  149. return weights
  150. end
  151.  
  152. function network:loadWeights(weights)
  153. for k, v in pairs(weights) do
  154. for k2, v2 in pairs(v) do
  155. self.allNeurons[k].weights[k2] = v2
  156. end
  157. end
  158. end
  159.  
  160. function network.new()
  161. local tbl = { }
  162. setmetatable(tbl, network)
  163. tbl:init()
  164.  
  165. return tbl
  166. end
  167.  
  168. --------------------------------------
  169. -- Evolution class
  170. --------------------------------------
  171.  
  172. local evolution = { }
  173. evolution.__index = evolution
  174.  
  175. function evolution:init(net)
  176. self.net = net
  177.  
  178. self.netsPerGeneration = 100
  179. self.maxGenerations = 40
  180.  
  181. self.mutationNeurons = 5
  182. self.mutationRate = 0.01
  183. self.maxWeight = 5
  184. end
  185.  
  186. function evolution:start()
  187. return coroutine.create(function()
  188. self:yieldedEvolve()
  189. end)
  190. end
  191.  
  192. function evolution:yieldedEvolve()
  193. self:onStart()
  194.  
  195. local generation = 1
  196.  
  197. local lastBest
  198. local mutationRateFactor = 1
  199.  
  200. while true do
  201. print("Generation: " .. generation)
  202.  
  203. self:onGenerationStart(generation)
  204.  
  205. local nets = { }
  206. local weights = self.net:dumpWeights()
  207.  
  208. for i = 1, self.netsPerGeneration do
  209. local updatedWeights = self:updateWeights(weights, mutationRateFactor)
  210. self.net:loadWeights(updatedWeights)
  211.  
  212. local fitness = self:onUpdate(self.net)
  213.  
  214. nets[i] = { fitness, updatedWeights }
  215.  
  216. coroutine.yield()
  217. end
  218.  
  219. coroutine.yield()
  220.  
  221. local best = nets[1]
  222. for k, v in pairs(nets) do
  223. if v[1] < best[1] then
  224. best = v
  225. end
  226. end
  227.  
  228. print("Best fitness: ", best[1])
  229.  
  230. if lastBest and lastBest[1] * 1 > best[1] then
  231. print("Not better than before")
  232. self.net:loadWeights(lastBest[2])
  233. mutationRateFactor = mutationRateFactor + 0.5
  234. continue
  235. end
  236.  
  237. self.net:loadWeights(best[2])
  238.  
  239. lastBest = best
  240.  
  241. mutationRateFactor = 1
  242.  
  243. self:onGenerationEnd(generation)
  244.  
  245. if generation >= self.maxGenerations then
  246. break
  247. end
  248.  
  249. generation = generation + 1
  250. end
  251.  
  252. self:onEnd()
  253. end
  254.  
  255. -- Evolution begins, all preparation is done here
  256. function evolution:onStart() end
  257.  
  258. -- Update function of current step, should return current fitness
  259. function evolution:onUpdate(net) end
  260.  
  261. -- Before training individuals
  262. function evolution:onGenerationStart(n) end
  263.  
  264. -- After best individual has been chosen
  265. function evolution:onGenerationEnd(n) end
  266.  
  267. -- The network has evolved
  268. function evolution:onEnd() end
  269.  
  270. function evolution:updateWeights(weights, factor)
  271. local updated = { }
  272.  
  273. for k, v in pairs(weights) do
  274. updated[k] = { }
  275.  
  276. for k2, v2 in pairs(v) do
  277. updated[k][k2] = v2
  278. end
  279. end
  280.  
  281. for i = 1, self.mutationNeurons do
  282. local k = math.random(1, #updated)
  283.  
  284. if #updated[k] == 0 then
  285. i = i - 1
  286. continue
  287. end
  288.  
  289. local l = math.random(1, #updated[k])
  290. updated[k][l] = math.clamp(updated[k][l] + (math.random() * 2 - 1) * self.mutationRate * factor, -self.maxWeight, self.maxWeight)
  291. end
  292.  
  293. return updated
  294. end
  295.  
  296. function evolution.new(net)
  297. local tbl = { }
  298. setmetatable(tbl, evolution)
  299. tbl:init(net)
  300.  
  301. return tbl
  302. end
  303.  
  304. --------------------------------------
  305. -- Action
  306. --------------------------------------
  307.  
  308. chip():setMaterial("models/shiny")
  309.  
  310. local net = network.new()
  311. net:addInputs(3)
  312. net:addHiddenLayer(3)
  313. --net:addHiddenLayer(3)
  314. net:addOutputs(1)
  315. net:connectNeurons()
  316.  
  317. local evo = evolution.new(net)
  318.  
  319. evo.netsPerGeneration = 10
  320. evo.maxGenerations = 5000
  321.  
  322. evo.mutationNeurons = 8
  323. evo.mutationRate = 0.2
  324. evo.maxWeight = 5
  325.  
  326. local evoCoroutine = evo:start()
  327.  
  328. local allPower = true
  329.  
  330. function evo:onStart()
  331. print("Starting evolution")
  332. end
  333.  
  334. function scaleInput(a)
  335. return a * 0.1
  336. end
  337.  
  338. function scaleOutput(a)
  339. return a * 10
  340. end
  341.  
  342.  
  343. function evo:onGenerationStart()
  344. a, b = math.random(1, 10), math.random(1, 10)
  345. end
  346.  
  347. startPos = chip():getPos() + Vector(0, 0, 15)
  348.  
  349. color = Color(math.random(0, 255), math.random(0, 255), math.random(0, 255))
  350.  
  351. baseHolo = holograms.create(startPos, Angle(0, 0, 0), "models/sprops/cuboids/height12/size_2/cube_24x18x12.mdl", Vector(1, 1, 1))
  352. baseHolo:setColor(color)
  353. traceHolo1 = holograms.create(startPos, Angle(0, 0, 0), "models/sprops/geometry/sphere_6.mdl", Vector(1, 1, 1))
  354. traceHolo2 = holograms.create(startPos, Angle(0, 0, 0), "models/sprops/geometry/sphere_6.mdl", Vector(1, 1, 1))
  355. traceHolo3 = holograms.create(startPos, Angle(0, 0, 0), "models/sprops/geometry/sphere_6.mdl", Vector(1, 1, 1))
  356.  
  357. local bestFitness = 0
  358.  
  359. function evo:onUpdate(net)
  360. allPower = false
  361.  
  362. local position = startPos
  363. local angle = math.pi / 2
  364.  
  365. while true do
  366. position = position + Vector(math.cos(angle), math.sin(angle)) * 2
  367.  
  368. baseHolo:setPos(position)
  369. baseHolo:setAngles(Angle(0, angle / math.pi * 180 + 90, 0))
  370.  
  371. local tr1 = trace.trace(position, position + Vector(math.cos(angle), math.sin(angle)) * 100)
  372. local tr2 = trace.trace(position, position + Vector(math.cos(angle + math.pi / 4), math.sin(angle + math.pi / 4)) * 100)
  373. local tr3 = trace.trace(position, position + Vector(math.cos(angle - math.pi / 4), math.sin(angle - math.pi / 4)) * 100)
  374.  
  375. traceHolo1:setPos(tr1.HitPos)
  376. traceHolo2:setPos(tr2.HitPos)
  377. traceHolo3:setPos(tr3.HitPos)
  378.  
  379. if tr1.Fraction < 0.01 or tr2.Fraction < 0.01 or tr3.Fraction < 0.01 then
  380. break
  381. end
  382.  
  383. local rawOutput = net:run({ scaleInput(tr1.Fraction), scaleInput(tr2.Fraction), scaleInput(tr3.Fraction) })[1]
  384. local scaledOutput = scaleOutput(rawOutput)
  385.  
  386. --[[
  387. if scaledOutput < -0.1 then
  388. angle = angle + 0.02
  389. elseif scaledOutput > 0.1 then
  390. angle = angle - 0.02
  391. end
  392. ]]
  393.  
  394. angle = angle + scaledOutput * 0.1
  395.  
  396. coroutine.yield()
  397. end
  398.  
  399. allPower = true
  400.  
  401. local fitness = (position - startPos).y - (position - startPos).x / 2
  402.  
  403. if fitness > bestFitness then
  404. baseHolo:emitSound("resource/warning.wav")
  405. bestFitness = fitness
  406. end
  407.  
  408. return fitness
  409. end
  410.  
  411. function evo:onGenerationEnd()
  412. print("Generation ended")
  413. end
  414.  
  415. function evo:onEnd()
  416.  
  417. end
  418.  
  419. hook.add("think", "", function()
  420. if allPower then
  421. while coroutine.status(evoCoroutine) ~= "dead" and quotaUsed() / quotaMax() < 0.1 do
  422. coroutine.resume(evoCoroutine)
  423. end
  424. else
  425. if coroutine.status(evoCoroutine) ~= "dead" and quotaUsed() / quotaMax() < 0.1 then
  426. coroutine.resume(evoCoroutine)
  427. end
  428. end
  429. end)
  430.  
  431. --[[
  432. chip():setMaterial("models/shiny")
  433.  
  434. local net = network.new()
  435. net:addInputs(6)
  436. net:addHiddenLayer(3)
  437. net:addOutputs(3)
  438. net:connectNeurons()
  439.  
  440. local evo = evolution.new(net)
  441.  
  442. evo.netsPerGeneration = 15
  443. evo.maxGenerations = 5000
  444.  
  445. evo.mutationNeurons = 8
  446. evo.mutationRate = 0.05
  447. evo.maxWeight = 5
  448.  
  449. local evoCoroutine = evo:start()
  450.  
  451. local allPower = true
  452.  
  453. function evo:onStart()
  454. print("Starting evolution")
  455. end
  456.  
  457. function scaleInput(a)
  458. return a * 0.001
  459. end
  460.  
  461. function scaleOutput(a)
  462. return a * 1000
  463. end
  464.  
  465. local a, b
  466.  
  467. function evo:onGenerationStart()
  468. a, b = math.random(1, 10), math.random(1, 10)
  469. end
  470.  
  471. startPos = chip():getPos() + Vector(0, 0, 2)
  472. desiredPos = startPos + Vector(50, 30, 60)
  473.  
  474. holo = holograms.create(desiredPos, Angle(0, 0, 0), "models/sprops/geometry/sphere_9.mdl", Vector(1, 1, 1))
  475.  
  476. function evo:onUpdate(net)
  477. allPower = false
  478.  
  479. local fitness = 0
  480. local nextEndTime = timer.realtime() + 2
  481.  
  482. chip():setPos(startPos)
  483. chip():setFrozen(false)
  484. chip():setVelocity(Vector(0, 0, 0))
  485. chip():enableGravity(false)
  486.  
  487. while timer.realtime() < nextEndTime do
  488. fitness = fitness + ((desiredPos - chip():getPos()):getLength() ^ 2) * timer.frametime()
  489.  
  490. local relPos = chip():getPos() - desiredPos
  491. local vel = chip():getVelocity()
  492. local rawOutput = net:run({ scaleInput(relPos.x), scaleInput(relPos.y), scaleInput(relPos.z), scaleInput(vel.x), scaleInput(vel.y), scaleInput(vel.z) })
  493. local scaledOutput = Vector(scaleOutput(rawOutput[1]), scaleOutput(rawOutput[2]), scaleOutput(rawOutput[3]))
  494.  
  495. chip():applyForceCenter(scaledOutput)
  496.  
  497. coroutine.yield()
  498. end
  499.  
  500. allPower = true
  501.  
  502. return fitness
  503. end
  504.  
  505. function evo:onGenerationEnd()
  506. print("Generation ended")
  507. end
  508.  
  509. function evo:onEnd()
  510.  
  511. end
  512.  
  513. hook.add("think", "", function()
  514. if allPower then
  515. while coroutine.status(evoCoroutine) ~= "dead" and quotaUsed() / quotaMax() < 0.6 do
  516. coroutine.resume(evoCoroutine)
  517. end
  518. else
  519. if coroutine.status(evoCoroutine) ~= "dead" and quotaUsed() / quotaMax() < 0.6 then
  520. coroutine.resume(evoCoroutine)
  521. end
  522. end
  523. end)
  524. ]]
Add Comment
Please, Sign In to add comment