LEX kimenete:
NEWLINE:\n ID:print ASSIGN:= PARAM(: )PARAM: ->
INDENT:4 ID:request { ID:table : STRNUM:"task" } ,
PARAM(:( ID:respond )PARAM:) ->
INDENT:4 IF ID:respond DOT:. ID:data ?
INDENT:4 ID:_out DOT:. ID:innerHTML ASSIGN:= STRNUM:""
NEWLINE:\n FOR: ID:x IN: ID:respond DOT:. ID:data DOT:. ID:text
INDENT:4 ID:_out DOT:. ID:innerHTML ASSIGN:+= ID:x +-:+ STRNUM:"<br>"
TOKENS kimenete:
NEWLINE:\n ID:print ASSIGN:= PARAM(: )PARAM: ->
INDENT:4 ID:request CALL(: { ID:table : STRNUM:"task" } ,
PARAM(:( ID:respond )PARAM:) ->
INDENT:4 IF ID:respond DOT:. ID:data ?
INDENT:4 ID:_out DOT:. ID:innerHTML ASSIGN:= STRNUM:""
NEWLINE:\n FOR: ID:x IN: ID:respond DOT:. ID:data DOT:. ID:text
INDENT:4 ID:_out DOT:. ID:innerHTML ASSIGN:+= ID:x +-:+ STRNUM:"<br>"
AST kimenete:
Assign =
Var print
Fun
Block
Chain
Var request
Call
Obj
Prop
Key table
Literal "task"
AST tűnik használhatóbbnak. Szerintem meg tudom írni általánosra, hogy más compile output-ot is meg lehessen etetni az elemző megoldással más nyelvek esetén.
Írtam egy Ruby kódot, mellyel kikapom a fenti AST kimenetből az első szót (mely nem tartalmaz fejlesztő által megadott saját elnevezést) és tömbbe rendezem. Majd a tömb egyforma elemeit átszámozom 0-tól felfelé addig növelve az értéket, amíg van új kulcsszó. Kód alább:
a = `node_modules/livescript/bin/lsc --ast file`.split("\n")
b = a.map{|x| x.strip[/^[a-zA-Z]+/] }.compact
c = {}
i = 0
b.each {|x|
if not c[x]
c[x] = i
i += 1
end
}
d = b.map{|x| x = c[x] }
p b, d, i
A kimenete így néz ki:
["Block", "Assign", "Var", "Fun", "Var", "Block", "Return", "Var", "Assign", "Var", "Fun", "Block", "Chain", "Var", "Call", "Obj", "Prop", "Key", "Literal", "Fun", "Var", "Block", "If", "Existence", "Chain", "Var", "Index", "Key", "Block", "Assign", "Chain", "Var", "Index", "Key", "Literal", "For", "Var", "Chain", "Var", "Index", "Key", "Index", "Key", "Block", "Assign", "Chain", "Var", "Index", "Key", "Binary", "Var", "Literal", "Assign", "Var", "Fun", "Var", "Block", "If", "Binary", "Chain", "Var", "Index", "Key", "Literal", "Block", "Assign", "Var", "Chain", "Var", "Index", "Key", "If", "Binary", "Var", "Literal", "Block", "Chain", "Var", "Call", "Obj", "Prop", "Key", "Literal", "Prop", "Key", "Literal", "Fun", "Var", "Block", "Chain", "Var", "Call", "Block", "Chain", "Var", "Call", "Obj", "Prop", "Key", "Literal", "Prop", "Key", "Obj", "Prop", "Literal", "Var", "Fun", "Var", "Block", "Chain", "Var", "Call", "Assign", "Chain", "Var", "Index", "Key", "Literal", "Chain", "Var", "Call"]
[0, 1, 2, 3, 2, 0, 4, 2, 1, 2, 3, 0, 5, 2, 6, 7, 8, 9, 10, 3, 2, 0, 11, 12, 5, 2, 13, 9, 0, 1, 5, 2, 13, 9, 10, 14, 2, 5, 2, 13, 9, 13, 9, 0, 1, 5, 2, 13, 9, 15, 2, 10, 1, 2, 3, 2, 0, 11, 15, 5, 2, 13, 9, 10, 0, 1, 2, 5, 2, 13, 9, 11, 15, 2, 10, 0, 5, 2, 6, 7, 8, 9, 10, 8, 9, 10, 3, 2, 0, 5, 2, 6, 0, 5, 2, 6, 7, 8, 9, 10, 8, 9, 7, 8, 10, 2, 3, 2, 0, 5, 2, 6, 1, 5, 2, 13, 9, 10, 5, 2, 6]
16
Ez a legalsó szám azt mutatja, hogy hányféle paranccsal találkoztunk.
Egy RNN (Recursive Neural Network) idegrendszert próbálok majd betanítani LSTM-et használva (Long Short Term Memory). Kutatásaim alapján az az elsődleges sejtésem, hogy megfelelő tanítási halmaz után ki tudhat mutatni a fejlesztőnek anomáliákat a kódjával kapcsolatban, vagy egyéb, magasabb szinten képzett minta illesztési eltéréseket. Ez az a rész amit nem tudunk felfogni, mivel rendkívül nagy a kombinációs halmaz és ebből kell kiválasztani az eljárással lehetséges eseteket.
Persze kérdés a bemeneti, rejtett és kimeneti réteg nagysága. A kísérleteket egyforma értékkel fogom kezdeni (x = h = y = N), akár egy genetikai algoritmust alkalmazva, próbálgatva hogy milyen struktúra hozza a jobb eredményt. Viszont ehhez létre kell hoznom egy tanítási adatbázist. Nem lesz olyan kis meló megtalálni a működő kombinációt egy teljesen teszteletlen és ismeretlen paraméterekkel rendelkező adhoc saját készítésű adathalmazzal (még jó, mert ha ismerném milyen paraméter kell, akkor magát az adatbázist is tudnám tesztelni hogy megfelelő mennyiségű és minőségű-e a tanításhoz, 22-es csapdája). Itt rendkívül sok a lehetséges kutatási irány mind a machine learning algoritmusok tekintetében, mind az LSTM típust illetően (hogyan adagolom az adatot szekvenciálisan a fix méretű vektoroknak, vagyis mekkora szeletekben - illetve a "forget-gate"-et mikor aktiválom - például minden "block" parancs után, hogy csak kód blokkokat értelmezzen és ne legyen átfedés - de nem biztos hogy kell, mert ha elég intelligens, akkor úgyis látja).
A jelzést a hibáról a fejlesztő felé úgy tervezem megoldani, hogy az ACE editor (amit használok szerkesztő felülethez, remek munka C9-től) által tartalmazott program kódot kijelölöm vízszintesen a fejlesztőnek, így mentés után látja, hogy az AI szerint hol érdemes nézelődni anomáliák után. Valszeg egy pici gombot (link) teszek be balra a "diff" gombom mellé a szerkesztő felületen (valami marketinges "ai" névvel, még meglátom).
Egyelőre folytatom a munkát..
- log69 blogja
- A hozzászóláshoz be kell jelentkezni
Hozzászólások
Közben azon gondolkodok, hogy lehet JS kód elemzéssel jobban járnék, mert egyrészt mindegyik dialektust erre fordítom a platformomon, másrészt github-ról tudnék lehúzni sok kódot, például NodeJS-re írtat.
- A hozzászóláshoz be kell jelentkezni
Nem tudom pontosan mi a celod ezzel az elemzessel, de en szemely szerint kicsit mas iranyba mennek. Tapasztalatom szerint az AST-t nem celszeru kilapitani, ha mar egyszer van egy szep fa strukturad, a lapitas rovid tavon problemakhoz fog vezetni. Ennek a ket kodnak az AST-je kulonbozik ugyan, de ugyan arra a szamsorra fog fordulni:
if (condition) {
a = 1;
}
b = 2;
// AST(?):
If
Var
Block
Assign
Var
Literal
Assign
Var
Literal
if (condition) {
a = 1;
b = 2;
}
// AST(?):
If
Var
Block
Assign
Var
Literal
Assign
Var
Literal
Mindketto kodja valami ilyesmi lesz az algoritmusoddal: 0 1 2 3 1 4 3 1 4
Ugye az AST nyelvtan fuggo, ugyhogy nem biztos hogy pont ezt kapod, de a lenyeg az, hogy fontos tudni a block vege hol van. Mivel ezt figyelmen kivul hagyod, lenyegesen kulonbozo kodokat tekinthet ugyan annak a neuralis halod.
Tapasztalatom szerint jobb megkozelites, ha az AST-t egy grafnak tekinted es azt reprezentalod valahogy. Peldaul a melyseget hozzacsapod prefixkent, vagy ilyesmi. Pl a fenti esetben ez ilyen kodsorokat fog eredmenyezni:
1. eset: 0-0 1-1 1-2 2-3 3-1 3-4 0-3 1-1 1-4
2. eset: 0-0 1-1 1-2 2-3 3-1 3-4 2-3 3-1 3-4
Tovabba (megint csak attol fuggoen, hogy mi a celod) fontos lehet, hogy milyen valtozokon tortenik a muvelet. Peldaul ha a neuralis haloddal fel akarod ismertetni a buborek rendezes implementaciojat vagy ilyesmi, akkor ugyan a valtozok neve nem szamit, de az igen, hogy ugyan az a valtozo van hivatkozva a masodik, nyolcadik meg tizedik utasitasban is. Ilyen esetben erdemes az AST-ben levo valtozo/fuggvenynev referenciakat feloldani, es egysegesiteni, igy ket kod osszehasonlitasakor az elnevezesek nem fognak szamitani. Itt figyelni kell arra is, hogy pl az "i" valtozo gyakori ciklusvaltozo ugyhogy a nevfeloldasnal szamit a scope.
Ha a nevfeloldas is megvan, akkor mar eleg kozel vagy ahhoz hogy legyen egy teljes control flowd a programrol (az exceptionok es egyeb implicit ugrasok hianyoznak).
Szerintem ez az, amivel csodakat lehetne tenni ha van egy jol betanitott neuralis halod :)
(automatizalt forraskod elemzessel foglalkozok; neuralis halokat csak tavolrol lattam)
- A hozzászóláshoz be kell jelentkezni
Ez jó amit mondasz (még csak ma kezdtem el vizsgálni a lehetőségeket), a mélység hozzácsapása jó lehet. Viszont ezeken kívül sok egyéb szempontot is figyelembe kell vennem. Például azt, hogy belátható erőforrás elég legyen a NN tanításához. Ugyanis ha túl nagy részletességű adatot teszünk bele, akkor nagyobb struktúra kell és exponenciálisan nő a szükséges számítási kapacitás.
Kérdés még hogy mekkora tanító db-t tudok összeállítani. Nyilván minél nagyobb annál jobb. De ha túl részletgazdag, akkor sokkal több kell belőle, különben a tanítás nem lesz jó és under fitting vagy over fitting lesz. Nem egyszerű.
Képeknél szoktak 16 x 16 pixelt beadni fekete fehérként. Azért mert NN elég jól tanul kicsi infóból is. Mondhatjuk hogy a tanítás minőségéhez képesti eredmény nagy tud lenni. Ha jól van összeállítva, akkor meglepően jól következtet és extrapolál nem tanult dolgokra a kevés megtanultból. Persze itt a kevés nagyon relatív, sok mindentől függ.
Tehát a célom az, hogy minél lejjebb vigyen az input részletgazdagságát de maradjon legalább akkora, amely még elég nagy kombinációs teret jelent ahhoz, hogy eléggé tudjon tanulni az AI.
- A hozzászóláshoz be kell jelentkezni
Az NN részéhez nem értek, de az AST egyszerűsíthető egy kis utófeldolgozással. Ha a mélységeket is számon tartod, akkor pl a Block elhagyható mert nem ad új információt, csak a parseolásnál segít egységbe rendezni az utasításokat. A while ciklus for ciklussá alakítható, az obj.property obj['property']-vé, stb.
Így egy "csomó", amúgy valid esettel nem kell foglalkozni.
A parseExpression csak kifejezéseket fog tudni parseolni a neve alapján, általános utasításokat nem. Pl. while(cond) {...} - ban csak a cond-ot.
- A hozzászóláshoz be kell jelentkezni
Csak sokszor amire nem gondoljuk hogy információt hordoz, továbbra is hordozhat fontos összefüggéseket az AI számára. Ilyet lehet hogy nem veszek ki mert ha magas szinten kombinál és észrevesz kódolási szokásokat amely hibás, akkor jól jöhet.
Persze ez még teória. Gyakrolattá kell fordítani.
Kérdés az is hogy hogyan tanítom a hibás kódolásra. Ha nincsenek negatív mintáim, akkor azzal fogok próbálkozni, hogy a nagy átlag kódolási szokásához igazítom. Persze nem biztos hogy a nagy átlag jelenti a best practice-t, de a kezdőnek a semminél jobb indulás lehet.
- A hozzászóláshoz be kell jelentkezni
Szerintem a melyseg helyett a blokkveget (meg az else kulcsszot) kellene belepakolni a tobbi kulcsszohoz hasonloan. A te modszereddel ugyanaz az algoritmus teljesen mas, ha beteszed egy elagazasba/ciklusba, vagy kiszervezed egy fuggvenybe. Igy viszont ugyanaz a szerkezete.
--
Any A.I. smart enough to pass a Turing test is smart enough to know to fail it. -Ian McDonald
- A hozzászóláshoz be kell jelentkezni
Közben keresek parsert és úgy látom BabelJS Babylon parserját fogom használni JS-ből AST generáláshoz:
sudo npm install -g babylon
var b = require("babylon");
console.log(b.parse("var a = 5*6"));
Github repo:
https://github.com/babel/babel/tree/master/packages/babylon
Ahogy nézem, .parse() helyett jobban járok .parseExpression() függvénnyel. Kevesebb infó és gyorsabb parsolás:
...tries to parse a single Expression with performance in mind.
Egy "| grep type" meg is adja amit keresek.
- A hozzászóláshoz be kell jelentkezni
Babylon AST spec-ből úgy látom hogy max 109 fajta típus van:
https://github.com/babel/babylon/blob/master/ast/spec.md
Kódjaim max 32 típust dobnak. Még meglátom hogy mekkora input réteget válasszak. Valszeg 1 inputot fogok 1 értéknek választani 0 vagy 1 értékkel, viszont az sem jó ha túl nagy az input réteg és a felső értékek nincsenek kihasználva. Részben mert több input nagyobb számítási kapacitást igényel, részben pedig azért mert eltolja a rejtett réteg súlyát.
Lehet hogy forgatni fogom körbe az inputot úgy, hogy maradékot képzek (N mod 32). Tehát a 32. értéket 0-nak veszem, 33-at 1-nek, 64-et megint nullának.
- A hozzászóláshoz be kell jelentkezni
Megoldandó probléma még a hiba visszaképezése a forráskódra ami nem lesz egyszerű a sok absztrakció miatt. Azt tudni fogom hogy hányadik művelet típusról van szó és mi a neve (pl.: BreakStatement), de ennek a helyét meg kell mutatnom a kódban.
- A hozzászóláshoz be kell jelentkezni
Közben látom hogy Babylon vissza ad "start" és "end" értékeket melyek megmondják hogy az adott objektum hol található az eredeti fájlban, úgyhogy így már egyszerűbb lesz. A forrás dialektusra kell majd visszaképeznem még.
- A hozzászóláshoz be kell jelentkezni