Sõnade manustamine ja keele modelleerimine | AI poole

Kuidas saada deterministlikke word2vec / doc2vec / lõik vektorid

OK, tere tulemast meie Wordi manustamissarja. See postitus on sarja esimene lugu. Võite leida, et see lugu sobib vahepealsele või vanemale, kes on Word2veci või doc2vec / lõiguvektorites vähemalt korra treeninud või vähemalt seda proovinud. Kuid ärge muretsege, tutvustan järgmistes postitustes tausta, eeldusi ja teadmisi ning kuidas kood seda rakendab.

Püüan anda endast parima, et teid ei suunataks mõnele muule lingile, mis palub teil lugeda tüütuid õpetusi ja lõpetada loobumisega (usaldage mind, olen tohutute veebijuhendite ohver :)). Ma tahan, et te mõistaksite sõnade vektoreid kodeerimise tasemelt koos minuga, et saaksime teada, kuidas oma sõna kinnistamist ja keelemudelit kujundada ja rakendada.

Kui teil on mõni võimalus sõnavektoreid ise koolitada, võite avastada, et mudel ja vektori esitusviis on igas treeningus erinev, isegi kui sisestate sama treeningu andmed. Selle põhjuseks on koolituse ajal sisse viidud juhuslikkus. Kood suudab ise rääkida, vaatame, kuhu juhuslikkus jõuab ja kuidas seda põhjalikult kõrvaldada. Koodi kuvamiseks kasutan DL4j lõiguvektorite rakendamist. Kui soovite vaadata mõnda muud paketti, minge gensimi doc2vec, millel on sama rakendusmeetod.

Kust tuleb juhus

Mudeliraskuste ja vektori esituse lähtestamine

Me teame, et enne koolitust lähtestatakse mudeli ja vektori esituse kaalud juhuslikult ning juhuslikkust kontrollib seeme. Seega, kui seame seemneks 0, saame lähtestamise iga kord täpselt sama. See on koht, kus seeme jõustub. Siin on syn0 mudeli kaalud ja selle lähtestab Nd4j.rand

// Nd4j võtab siin seemnete konfiguratsiooni
Nd4j.getRandom (). SetSeed (configuration.getSeed ());
// Nd4j lähtestab syn0 juhusliku maatriksi
syn0 = Nd4j.rand (uus int [] {vocab.numWords (), vectorLength}, rng) .subi (0,5) .divi (vectorLength);

PV-DBOW algoritm

Kui lõiguvektorite koolitamiseks kasutame PV-DBOW algoritmi (selgitan selle üksikasju järgmistes postitustes), koolituse iteratsioonide ajal valib see tekstiaknast juhuslikult sõnu, et arvutada ja ajakohastada kaalu. Kuid see juhus pole tegelikult juhuslik. Vaatame seda koodi.

// järgmine juhus on lõime ID-ga lähtestatud AtomicLong
this.nextRandom = uus AtomicLong (this.threadId);

Ja NextRandom on kasutusel

trainSequence (jada, nextRandom, alfa);

Kus rongijärjestuses sees, seda ta ka teeb

nextRandom.set (nextRandom.get () * 25214903917L + 11);

Kui läheme koolituse etappidele sügavamale, siis leiame, et see genereerib nextRandom sama moodi, st tehes sama matemaatilise operatsiooni (minge sellele ja sellele, et teada saada, miks), seega sõltub number ainult lõime ID-st, kus keerme id on 0, 1, 2, 3,…. Seega pole see enam juhuslik.

Paralleelne märgistamine

Seda kasutatakse paralleelselt märgistamiseks, kuna keeruka teksti koostamine võib olla kulukas, paralleelne märgistamine võib toimivust aidata, samal ajal kui koolituse järjepidevus pole tagatud. Tokenizeri poolt töödeldavad jadad võivad treenida lõimedesse sisestamiseks juhuslikus järjekorras. Nagu koodist näete, ootab tokenimist teostav käitatav, kuni see lõpeb, kui me seame lubadaParallelBuilder väärtuseks vale, kus andmete sisestamise järjekord säilib.

if (! lubaParallelBuilder) {
    proovige {
        runnable.awaitDone ();
    } saak (InterruptedException e) {
        Thread.currentThread (). Katkestama ();
        viska uus RuntimeException (e);
    }
}

Järjekord, mis pakub iga treenitava lõime järjestusi

See LinkedBlockingQueue saab koolitusteksti iteraatorist jadad ja pakub need jaod igale lõimele. Kuna iga niit võib tulla juhuslikult, võib igal koolituse ajal treenida iga lõime erinevat järjestust. Vaatame selle andmepakkuja rakendamist.

// lähtestage sekveneerija, et pakkuda andmeid lõimedele
val sequencer = uus AsyncSequencer (this.iterator, this.stopWords);
// kõik lõimed osutavad samale sekveneerijale
// töötaja on lõimede arv, mida tahame kasutada
jaoks (int x = 0; x 
// sekveneerija lähtestab LinkedBlockingQueue puhvri
// ja hoidke suurust vahemikus [limitLower, limitUpper]
privaatne lõplik LinkedBlockingQueue  puhver;
limitLower = töötajad * batchSize;
limitUpper = töötajad * batchSize * 2;
// niidid saavad andmeid järjekorrast läbi
buffer.poll (3L, TimeUnit.SECONDS);

Seega, kui valime töötajate arvuks 1, töötab see ühes keermes ja sellel on igas väljaõppe ajal täpselt samasugune söötmisandmete järjekord. Kuid pange tähele, et üksik niit aeglustab treenimist tohutult.

Tehke kokkuvõte

Kokkuvõtteks tuleb öelda, et juhuslikkuse põhjalikuks välistamiseks peame tegema järgmist:
1. Seadke seemneks 0;
2. Valige lubaParallelTokenization valeks;
3. Seadke töötajate (keermete) arvuks 1.
Siis on meil täpselt samad tulemused sõnavektori ja lõiguvektori korral, kui sisestame samad andmed.

Lõpuks, meie koolituse kood on järgmine:

LõigeVektorid vec = uus lõikVektorid.Builder ()
                .minWordFrequency (1)
                .labels (labelsArray)
                .layerSize (100)
                .stopWords (uus ArrayList  ())
                .windowSize (5)
                .iterate (iter)
                .allowParallelTokenization (vale)
                .töötajad (1)
                .seemned (0)
                .tokenizerFactory (t)
                .build ();

vec.fit ();

Kui teil on tunne nagu

palun jälgige järgmisi lugusid sõna kinnistamise ja keelemudeli kohta, olen teile pidu ette valmistanud.

Viide

[1] Deeplearning4j, ND4J, DataVec ja palju muud - sügav õppimine ja lineaarne algebra Java / Scala jaoks koos GPU-dega + Spark - Skymindilt http://deeplearning4j.org https://github.com/deeplearning4j/deeplearning4j
[2] Java ™ platvorm, standardversiooni 8 API spetsifikatsioon https://docs.oracle.com/javase/8/docs/api/
[3] https://giphy.com/
[4] https://images.google.com/