Paprastas neuroninių aritmetinių loginių vienetų (NALU) vadovas: Paaiškinimas, intuicija ir kodas

Andrew Traskas, „DeepMind“ mokslo inžinierius, įskaitant žinomą AI tyrėją ir knygos „Grokking Deep Learning“ autorių, paskelbė įspūdingą straipsnį apie neuroninio tinklo modelį, kuriame galima išmokti paprastų ir sudėtingų skaitinių funkcijų, pasižyminčių dideliu ekstrapoliacijos (apibendrinimo) gebėjimu.

Šiame įraše paaiškinsiu NALU, jo architektūrą, komponentus ir reikšmę tradiciniams neuroniniams tinklams. Pagrindinis šio įrašo tikslas yra pateikti paprastą ir intuityvų NALU paaiškinimą (tiek su sąvokomis, tiek su kodu), kurį galėtų suprasti tyrėjai, inžinieriai ir studentai, turintys ribotas žinias apie neuroninius tinklus ir gilų mokymąsi.

Pastaba: skaitytojams primygtinai rekomenduoju perskaityti originalų darbą, kad suprastumėt dalyką. Popierių galite atsisiųsti iš čia.

Kur sugenda neuroniniai tinklai?

Šis vaizdas paimtas iš šio vidutinio įrašo

Neuroniniai tinklai, teoriškai, yra labai geri funkcijų aproksimatoriai. Jie beveik visada gali sužinoti bet kokį prasmingą ryšį tarp duomenų (duomenų ar funkcijų) ir rezultatų (etikečių ar tikslų). Taigi, jie naudojami daugelyje programų, pradedant objektų aptikimu ir klasifikavimu, baigiant kalbomis ir baigiant teksto pavertimu intelektualiaisiais žaidimų agentais, kurie gali įveikti pasaulio pasaulio čempionus. Yra daugybė efektyvių neuroninių tinklų modelių, tenkinančių įvairius tokių programų poreikius, tokius kaip konvoliuciniai neuroniniai tinklai (CNN), pasikartojantys neuroniniai tinklai (RNN), autoencoderiai ir kt. Kita savaime savaime yra giluminio mokymosi ir neuroninių tinklų modelių pasiekimai.

Tačiau, pasak straipsnio autorių, jiems trūksta labai paprastų sugebėjimų, kurie žmogui ar net bitėms atrodo nereikšmingi! Tai yra galimybė suskaičiuoti / manipuliuoti skaičiais ir taip pat ekstrapoliuoti skaitinį ryšį iš stebimo skaitinio modelio. Straipsnyje parodyta, kad standartiniai neuroniniai tinklai net stengiasi išmokti tapatybės funkcijos (funkcijos, kurios įvestis ir išvestis yra identiška; f (x) = x), kuri yra tiesiškiausias skaitinis ryšys. Žemiau esančiame paveikslėlyje parodyta įvairių NN, apmokytų išmokti tokią tapatybės funkciją, vidutinė kvadratinė paklaida (MSE).

Paveikslėlyje parodytas standartinio neuroninio tinklo MSE, turinčio tiksliai tą pačią architektūrą, mokoma paslėptuose sluoksniuose naudojant skirtingas aktyvinimo funkcijas (netiesiškumą).

Kodėl jie žlunga?

Pagrindinė priežastis, kodėl NN nesugeba išmokti tokio skaitinio atvaizdo, yra netiesinių aktyvavimo funkcijų naudojimas paslėptuose tinklo sluoksniuose. Tokios aktyvavimo funkcijos yra labai svarbios norint išmokti abstrakčiojo netiesinio įėjimo ir etikečių santykio, tačiau jos nesugeba apgauti, kai reikia išmokti skaitinį vaizdą už treniruočių duomenyse matomų skaičių diapazono ribų. Taigi tokie tinklai yra labai geri įsimenant treniruočių rinkinyje matomą skaitinį modelį, tačiau jie nesugeba tinkamai ekstrapoliuoti šio vaizdavimo.

Tai yra tarsi atsakymo ar temos įsiminimas, nesuvokiant pagrindinės egzamino sampratos. Tai padarius, žmogus gali labai gerai pasirodyti, jei panašūs klausimai bus užduodami egzamine, tačiau nepavyks, jei susukti klausimai bus užduodami kandidato žinioms, o ne įsiminimo galimybėms patikrinti.

Šios nesėkmės sunkumas tiesiogiai atitinka pasirinktos įjungimo funkcijos netiesiškumo laipsnį. Iš aukščiau pateikto paveikslo galima aiškiai pastebėti, kad sunkiai apribotos netiesinės funkcijos, tokios kaip Tanhas ir Sigmoidas, turi labai mažai galimybių gerai apibendrinti nei minkštosios suvaržytos netiesinės funkcijos, tokios kaip PReLU ir ELU.

Sprendimas: neuroninis akumuliatorius (NAC)

Neuroninis akumuliatorius (NAC) sudaro NALU modelio pagrindą. NAC yra paprastas, bet efektyvus neuroninio tinklo modelis (vienetas), palaikantis gebėjimą išmokti sudėti ir atimti - tai yra pageidautina savybė efektyviai išmokti linijines funkcijas.

NAC yra specialus tiesiškumo sluoksnis, kurio svorio parametrų apribojimai turi tik 1, 0 arba -1. Tokiu būdu ribojant svorio reikšmes, sluoksnis neleidžia keisti įvesties duomenų mastelio ir jie išlieka vienodi visame tinkle, nesvarbu, kiek sluoksnių sukraunama kartu. Taigi išėjimas bus linijinis įvesties vektoriaus derinys, kuris lengvai parodo sudėjimo ir atėmimo operacijas.

Intuicija: Norėdami suprasti šį faktą, apsvarstykime šiuos NN sluoksnių, atliekančių įvesties tiesinę aritmetinę operaciją, pavyzdžius.

Iliustracija paaiškinti, kad neuroninio tinklo sluoksniai, neturintys šališkumo ir turintys -1, 0 arba 1 svorio reikšmes, gali išmokti tiesinę ekstrapoliaciją.

Kaip parodyta aukščiau esančiuose NN sluoksniuose, tinklas gali išmokti ekstrapoliuoti paprastas aritmetines funkcijas, tokias kaip sudėjimas ir atimtis (y = x1 + x2 ir y = x1 – x2), apribojant svorio parametrus iki –1, 0 ir 1.

Pastaba: kaip parodyta aukščiau pateiktose tinklo schemose, NAC nėra jokio šališkumo parametro (b) ir jokio netiesiškumo, taikomo paslėptų sluoksnių vienetams.

Kadangi standartiniam nervų tinklui sunku išmokti svorio parametrų apribojimus NAC, autoriai aprašė labai naudingą formulę, kaip išmokti tokias ribotas parametrų reikšmes naudojant standartinius (nevaržomus) parametrus W_hat ir M_hat. Šie parametrai yra kaip ir bet kurie standartiniai NN svorio parametrai, kurie gali būti inicijuojami atsitiktine tvarka ir kuriuos galima išmokti mokymo proceso metu. Žemiau pateikiama formulė W gavimui pagal W_hat ir M_hat:

Formulė žymi sandarą tarp dviejų matricų

Aukščiau pateiktos lygties naudojimas svorio parametrų tinkle apskaičiavimui garantuoja, kad tokių parametrų vertė bus [-1,1] intervale, labiau linkusi link -1, 0 ir 1. Taip pat ši lygtis yra diferencijuojama lygtis. (kurių darinius galima apskaičiuoti atsižvelgiant į svorio parametrus). Taigi NAC bus lengviau išmokti W, naudojant nuolydžio nusileidimą ir pasklidimą atgal. Žemiau yra NAC įrenginio architektūrinė schema.

NAC architektūra norint išmokti paprastų (linijinių) skaitinių funkcijų

NAC įgyvendinimas python'e naudojant Tensorflow

Kaip galima įsivaizduoti, NAC yra paprastas NN modelis, turintis keletą mažų patarimų. Žemiau aš parodžiau paprastą vieno NAC sluoksnio įgyvendinimą python'e, naudojant Tensorflow ir Numpy biblioteką.

importuoti numpy kaip np
importuoti tensorflow kaip tf
# Apibrėžkite nervinį akumuliatorių (NAC), kurį reikia sudėti / atimti -> Naudinga išmokti sudėjimo / atėmimo operaciją.

def nac_simple_single_layer (x_in, out_units):
    '' '
    Atributai:
        x_in -> Įvesties tenzorius
        out_units -> išvesties vienetų skaičius

    Grįžti:
       y_out -> Minėtos formos išvesties tenzorius
       W -> Sluoksnio svorio matrica
    '' '
# Gaukite įvesties funkcijų skaičių (skaičius)
    in_features = x_in.shape [1]

    # apibrėžti W_hat ir M_hat

    W_hat = tf.get_variable (figūra = [ypatybės, išeiti vienetai],
    iniciatorius = tf.inicializatoriai.dažniausia_vienalytė (minval = -2, maxval = 2),
    trainable = True, name = "W_hat")

    „M_hat = tf.get_variable“ (forma = [formos, formos vienetai],
    iniciatorius = tf.inicializatoriai.dažniausia_vienalytė (minval = -2, maxval = 2),
    trainable = True, name = "M_hat")

    # Gaukite W naudodami formulę
    W = tf.nn.tanh (W_hat) * tf.nn.sigmoid (M_hat)

    y_out = tf.matmul (x_in, W)

    grįžti y_out, W

Aukščiau pateiktame kode aš naudojau atsitiktinę vienodą inicializaciją traukiamiems parametrams W_hat ir M_hat, tačiau šiems parametrams galima naudoti bet kurią rekomenduojamą svorio inicijavimo metodiką. Norėdami gauti visą darbo kodą, maloniai patikrinkite mano „GitHub“ repą, paminėtą šio įrašo pabaigoje.

Perėjimas prie sudėjimo ir atimties: NAC sudėtinėms skaitinėms funkcijoms

Nors aukščiau paminėtas paprastas nervų tinklo modelis gali išmokti pagrindines aritmetines funkcijas, tokias kaip sudėjimas ir atimtis, pageidautina turėti galimybę išmokti sudėtingesnių aritmetinių operacijų, tokių kaip daugybos, dalijimo ir galios funkcijos.

Žemiau yra modifikuota NAC architektūra, galinti išmokti sudėtingesnių skaitinių funkcijų, naudodama rąstų erdvę (logaritmines vertes ir eksponentus) savo svorio parametrams. Atkreipkite dėmesį, kuo šis NAC skiriasi nuo pirmojo įraše paminėto.

NAC architektūra norint išmokti sudėtingesnių skaitinių funkcijų

Kaip parodyta aukščiau pateiktoje diagramoje, prieš pradedant matricos dauginimą su svorio matrica W, ši ląstelė taiko žurnalo funkciją įvestiems duomenims, o tada gautai matricai taiko eksponentinę funkciją. Išvesties formulė pateikiama žemiau pateiktoje lygtyje.

Aukščiau parodyta komplekso NAC išvesties lygtis. „Epsilon“ čia yra labai maža vertė, norint išvengti log (0) situacijos treniruotėse

Taigi viskas yra ta pati, kalbant apie pagrindinį paprasto NAC ir sudėtingo NAC mechanizmą, įskaitant riboto svorio W formulę W_hat ir M_hat. Vienintelis skirtumas yra tas, kad sudėtingas NAC taškų žurnalas naudoja sluoksnio įvestį ir išvestį.

Python komplekso NAC įgyvendinimas:

Kaip ir abiejų NAC architektūrų atveju, sudėtingo NAC python įgyvendinimas yra beveik tas pats, išskyrus paminėtą išvesties tenzoro formulės pakeitimą. Žemiau yra tokio NAC kodas.

# apibrėžkite sudėtingą NAC rąstų erdvėje -> sudėtingesnėms aritmetinėms funkcijoms, tokioms kaip daugyba, dalijimas ir galia, nustatyti

def nac_complex_single_layer (x_in, out_units, epsilon = 0,000001):

    '' '
    : param x_in: įvesties ypatybių vektorius
    : param out_units: langelio išvesties vienetų skaičius
    : param epsilon: maža reikšmė, kad išvesties rezultatas nebūtų log (0)
    : grąžinimo m: išvesties tenzorius
    : grįžtamasis W: susijusi svorio matrica
    
    '' '

    in_features = x_in.shape [1]

    W_hat = tf.get_variable (shape = [in_shape, out_units],
    iniciatorius = tf.inicializatoriai.dažniausia_vienalytė (minval = -2, maxval = 2),
    trainable = True, name = "W_hat")

    M_hat = tf.get_variable (forma = [ypatybės, išeiti vienetai],
    iniciatorius = tf.inicializatoriai.dažniausia_vienalytė (minval = -2, maxval = 2),
    trainable = True, name = "M_hat")
    # Gaukite neribotą parametrų matricą W
    W = tf.nn.tanh (W_hat) * tf.nn.sigmoid (M_hat)

    # „Express Input“ funkcija žurnale, kad išmoktumėte sudėtingas funkcijas
    x_modified = tf.log („tf.abs“ (x_in) + „epsilon“)

    m = tf.exp (tf.matmul (x_modified, W))

    grįžti m, W

Dar kartą norėdami sužinoti visą darbo kodą, patikrinkite mano „GitHub“ repą, paminėtą šio įrašo pabaigoje.

Viską sudėjus: Neuroniniai aritmetiniai loginiai vienetai (NALU)

Iki šiol galima įsivaizduoti, kad kartu su dviem NAC modeliais galima išmokti beveik visas aritmetines operacijas. Tai yra pagrindinė NALU idėja, kurią sudaro svertinis paprasto NAC ir aukščiau minėto NAC derinys, valdomas išmokto vartų signalo. Taigi NAC sudaro pagrindinį NALU elementą. Taigi, jei jūs gerai supratote NAC, NALU yra labai lengva suvokti. Jei to nepadarėte, praleiskite laiką ir dar kartą peržvelkite abu NAC paaiškinimus. Žemiau pateiktoje nuotraukoje aprašoma NALU architektūra.

NALU anotuota schema

Kaip parodyta aukščiau esančiame paveikslėlyje, NALU abu NAC (purpurinės spalvos ląstelės) yra interpoliuojami (derinami) išmoktu sigmoidiniu vartų valdymu (oranžinė ląstelė) taip, kad bet kurio NAC išėjimą galima įjungti arba išjungti vartuose, remiantis skaitine funkcija, kurią mes bandome apmokyti tinklą.

Kaip minėta aukščiau, paprastas NAC apskaičiuoja kaupimo funkciją, todėl jis yra atsakingas už NALU tiesinių (sudėjimo ir atėmimo) operacijų saugojimą. Nors sudėtingas NAC yra atsakingas už savo sudėtingesnių skaitinių funkcijų, tokių kaip daugybos, padalijimo ir galios funkcijų vykdymą. Pagrindinių ląstelių išvestis NALU gali būti matematiškai pavaizduota taip:

Paprastas NAC: a = W X
Sudėtingas NAC: m = exp (W log (| X | + e))
Kur W = tanh (W_hat) * sigmoid (M_hat)
Vartų langelis: g = sigmoidas (GX) # Kur G yra standartinė traukiamų parametrų matrica
# Ir galiausiai NALU išėjimas
NALU: y = g * a + (1-g) * m # Kur * yra elementas, išmintingas produktas

Aukščiau pateiktoje NALU formulėje galime pasakyti, kad jei vartų išvestis g = 0, tinklas išmoks tik sudėtingas, bet ne paprastas funkcijas. Jei g = 1, tinklas išmoks tik papildomas, o ne sudėtingas funkcijas. Taigi iš viso NALU gali išmokti bet kokias skaitines funkcijas, susidedančias iš daugybos, sudėjimo, atėmimo, dalijimo ir galios funkcijų, tokiu būdu, kad būtų galima gerai ekstrapoliuoti skaičius, esančius už skaičių diapazono ribų, kurie buvo matomi treniruotės metu.

Python NALU įgyvendinimas:

Įgyvendindami NALU, mes naudosime ir paprastą, ir sudėtingą NAC, apibrėžtą ankstesniuose kodo fragmentuose.

def nalu (x_in, out_units, epsilon = 0,000001, get_weights = False):
    '' '
    : param x_in: įvesties ypatybių vektorius
    : param out_units: langelio išvesties vienetų skaičius
    : param epsilon: maža reikšmė, kad išvesties rezultatas nebūtų log (0)
    : param get_weights: Tiesa, jei norite gauti modelio svorius
                        atsilyginimui
    : grąža: išvesties tensorius
    : grįžimas: vartų svorio matrica
    : grąža: NAC1 (paprasta NAC) svorio matrica
    : grąža: NAC2 (kompleksinė NAC) svorio matrica
    '' '

    in_features = x_in.shape [1]

    # Gaukite išvesties tenzorą iš paprasto NAC
    a, W_simple = nac_simple_single_layer (x_in, out_units)

    # Gaukite išvesties tenzorą iš sudėtingo NAC
    m, W_complex = nac_complex_single_layer (x_in, out_units, epsilon = epsilon)

    # Vartų signalo sluoksnis
    G = tf.get_variable (forma = [ypatybės, išeiti vienetai],
    iniciatorius = tf.random_normal_initializer (stddev = 1.0),
    trainable = True, name = "Gate_weights")

    g = tf.nn.sigmoid (tf.matmul (x_in, G))

    y_out = g * a + (1 - g) * m

    if (gauti_svoriai):
        grįžti y_out, G, W_simple, W_complex
    Kitas:
        grąžinti y_out

Vėlgi, aukščiau pateiktame kode, vartų parametrams G aš naudojau atsitiktinę normaliąją inicializaciją, tačiau galima naudoti bet kurią rekomenduojamą svorio inicijavimo metodiką.

Ibelieve NALU yra modernus proveržis PG ir ypač neuroniniuose tinkluose, kuris atrodo labai perspektyvus. Jie gali atverti duris daugeliui programų, su kuriomis, atrodo, sunku susidurti standartiniams NN.

Autoriai pademonstravo įvairius eksperimentus ir rezultatus, įgyvendinančius NALU skirtingose ​​neuroninio tinklo taikymo srityse, pradedant nuo paprastų aritmetinių funkcijų mokymosi užduočių ir suskaičiuojant ranka rašytų skaitmenų skaičių pateiktose MNIST vaizdų serijose, kad tinklas išmoktų įvertinti kompiuterio programas!

Rezultatai yra nuostabūs ir įrodo, kad NALU puikiai tinka apibendrinti beveik visas užduotis, susijusias su skaitmeniniu vaizdavimu, nei standartiniai NN modeliai. Skaitytojams rekomenduoju pažvelgti į šiuos eksperimentus ir jų rezultatus, kad būtų galima giliau suprasti, kaip NALU gali būti naudingas atliekant keletą įdomių skaitinių užduočių.

Tačiau mažai tikėtina, kad NAC ar NALU bus puikus sprendimas kiekvienai užduočiai. Jie greičiau parodo bendrą modelio, skirto tikslinei skaitmeninių funkcijų klasei, kūrimo strategiją.

Žemiau yra nuoroda į mano „GitHub“ saugyklą, kurioje parodytas visas šio įrašo kodo fragmentų įgyvendinimas.

https://github.com/faizan2786/nalu_implementation

Kviečiame išbandyti įvairias funkcijas, kad išbandytumėte mano modelį, naudodamiesi skirtingais hiperprametrais tinklui sureguliuoti.

Praneškite man, jei turite klausimų ar minčių dėl šio įrašo komentaruose žemiau ir stengsiuosi į juos atsakyti.

PS: Tai yra mano pirmasis tinklaraščio įrašas bet kuria tema. Taigi, visos mano rašytos rekomendacijos, pasiūlymai ir patarimai ateityje - tiek techniniai, tiek netechniniai - yra laukiami.