Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Praktische vragen rondom grote datasets #94

Closed
boblucas opened this issue Jan 11, 2021 · 7 comments
Closed

Praktische vragen rondom grote datasets #94

boblucas opened this issue Jan 11, 2021 · 7 comments
Assignees
Labels

Comments

@boblucas
Copy link

Ik heb een corpus met 25 miljard woorden die ik wil 'froggen', daarvoor heb ik een 32 core/128GB RAM computer. M'n plan is om 16 losse instanties van frog te draaien, max. 500 woorden per zin. En het lijkt dat ik dan 10k woorden per seconde kan 'froggen'.

Maar dat roept natuurlijk wat vragen op.

1: Hoe waarschijnlijk is het dat 128GB ergens halverwege het proces te weinig blijkt? Is het geheugenverbruik redelijk constant? Is het verstandig om m'n data op te delen in kleine 'chunks'?
2: Met 10kw/s zou m'n hele corpus ongeveer een maand aan computatie vereisen, wat ok is, maar ook voldoende lang dat ik wel wat tijd wil investering om de performance beter te krijgen (met het idee dat dat ook zinnig is voor anderen). Is er nog 'laag hangend fruit' wat betreft de performance? Mijn eerste instinct is dat er veel 'communicatie' is en dat de interne representatie van tokens en hun metadata te complex is. Maar dat is niet makkelijk om om te bouwen.
3: FoLiA is heel flexibel maar simpelweg te veel data, zelfs compressed zou het corpus dan vele terabytes zijn, gewoon niet zo handig als het niet op een normale SSD past. Ik heb even een opzetje gemaakt voor een bestandsformaat wat een vaste 8 bytes per token gebruikt. Daar moet natuurlijk wat voor inleveren (maar 2⁸ PoS tags, theoretisch kun je er ~320 maken volgens mij en maar 2²⁵ soorten tokens (ex. MWU)) , maar dan kun je wel complexe zoekopdrachten best snel doen (ik mik op bijv. 100 miljoen tokens/s). En met basic compressie van de meeste frequente woorden kan ik dan m'n hele corpus in geheugen houden. Is hier misschien al is eerder over nagedacht? Ik zou niet halfslachtig iets opnieuw willen implementeren.
4: Waar zou ik kunnen lezen over hoe frog is getraind en op welke data? Ik zou graag een inschatting maken van wat voor nauwkeurigheid ik kan verwachten op m'n dataset. En waar mogelijk iets hertrainen om beter aan daar beter op aan te sluiten.

@kosloot
Copy link
Collaborator

kosloot commented Jan 11, 2021

Ha Bob.
Klinkt ambitieus!
Wat opmerkingen naar aanleiding van je vragen:

  • Frog denkt in zinnen. Als Frog eenmaal runt zal het geheugengebruik weinig meer toenemen.
    Elke zin start met een schone lei.
  • In het verleden hebben we wel gezien dat bij zeer grote files, (dus met heel veel zinnen) er een slecht te verklaren vertraging optrad. Er is daarna veel gerefactord, dus of dat nog zo is, is onbekend.
    Mijn advies zou zijn om je input op te splitsen in kleinere stukken met bijvoorbeeld 100.000 zinnen. (ik gok maar wat)
  • Heb je echt FoLiA nodig? Met Tab separated of JSON uitvoer wordt e.e.a een stuk minder groot.
  • Bedenk goed welke modules je wil. Als je alleen POS tags en lemmatisering wil dan scheelt het erg veel om de rest uit te schakelen. De NER is sowieso duur in tijd en onnauwkeurig.
  • ad 4. voor de vragen over nauwkeurigheid etc verwijs ik je naar de documentatie: https://frognlp.readthedocs.io/en/latest/
  • ad 1 en 3. Dat klinkt naar heel veel werk. Op dit moment is er EN geen funding EN geen menskracht om aan Frog te werken.
    Maar als jij dat voor elkaar krijgt, zien we je pull requests vol verwachting tegemoet :)

@proycon
Copy link
Member

proycon commented Jan 11, 2021

Ko heeft de meeste vragen al beantwoord zie ik. FoLiA is inderdaad erg verbose en kan snel een bottleneck zijn als je echt heel veel data hebt.

  • 1: Dat hangt ook een beetje van van hoe je Frog aanspreekt. Als je zin voor zin voert via de API of in JSON of tsv output mode dan groeit het geheugen niet, maar als je FoLiA doet komt er meer bij kijken en groeit het wel. Issue Frog breaks while processing large amount of txt data #86 geeft misschien ook nog wat inzicht.
  • 3: Ik heb zelf wel wat werk gedaan aan het wat spaarzamer omgaan met geheugen en FoLiA in een rust library: https://github.com/proycon/folia-rust , maar daar heb je in dit geval niet zo veel aan want die library gebruikt Frog niet en je wil misschien liever heel FoLiA vermijden in deze. Ik heb ook dingen gedaan op het gebied van compressie, corpora, en het extraheren van patronen als n-grammen/skipgrammen: https://proycon.github.io/colibri-core . maar daar gaat het om pure tekst zonder verdere linguistische annotatie, dus daar heb je ook niet zo veel aan denk ik.

@boblucas
Copy link
Author

boblucas commented Jan 11, 2021

Bedankt voor de reactie beide!

Format

Duidelijk wat betreft geheugen dank! Ik gebruik nu inderdaad de CSV output die is (voor mijn data) bijna precies x10 de input, wat nog steeds te veel is, punt 3 is denk ik redelijk weinig werk het enige wat het is is het bitpacken van die CSV in 64 bits (zodat één token de wordsize van een CPU is) dus bijvoorbeeld:

struct Token
{
	unsigned full_word:1;
        unsigned sentence:1;
	
	unsigned word:25;
	unsigned morphemes:2;
	unsigned lemma:3;

	unsigned pos_type:8;

	unsigned net:5;
	unsigned bpc:6;
	unsigned dependency_type:5;

	unsigned dependency:9;
};

En een waarde uitlezen is dus effectief een mask op een uint64_t, en daar kun je dan wat condities aanhangen, wat je makkelijk uit kan drukken in SIMD instructies voor goede performance.

sentence is een bit die flipped elke keer als er een nieuw zin begint.

Achter word zit een dictionary met 2²⁵ woorden (in mijn dataset zijn dat alle woorden met een freq > 2) die mapped naar max 8 mogelijke morfologische interpretaties en 8 mogelijke lemma's, die in in 'morphemes' en 'lemmas' gespecificeerd zijn voor een specifieke token. MWT's blijven meerdere 'tokens' om het totaal aantal woorden te beperken, maar omdat je wel aan het juiste lemma refereert kun je er gewoon op filteren.

PoS tags selecteer ik er simpelweg 256 van, daar verlies ik dus wat informatie, ik denk uitzonderlijk marginaal maar nu moeilijk in te schatten. De rest van de kolommen in de CSV passen 1:1, probability is wel weg gegooid (alles onder een threshold kan ik nog in een kleine aparte index gooien) en dependency neemt nu dus aan dat zinnen max 2⁹ tokens lang zijn.

full_word is een als een header die aangeeft of je de data als 2*32 bits moet uitlezen.
Hiervoor is een aparte index met de 2¹² meest frequente woorden die een bijna vast lemma, morphologie en PoS hebben. Puur als compressie. Mocht dat woord er ooit vanaf wijken dan gebruik je de bovenstaande structuur.

struct ShortToken
{
	unsigned word:12;
	unsigned net:5;
	unsigned bpc:6;
	unsigned dependency:8;
};

Het is niet moeilijk te maken, de dictionary maken is nauwelijks meer dan frequenties tellen en daarna is van de output van frog hier naar toe een 1:1 mapping. En op basis van jouw verhaal haal ik de NER er gewoon uit dan heb ik net iets meer ruimte (en performance). Als ik DP niet doe win ik natuurlijk heel veel performance én kan ik alles in 32 bits/token stoppen. Iets om in het achterhoofd te houden.

Excuses dat was een net iets langer verhaal dan ik voor ogen had.

Performance

Wat betreft de performance optimalisaties, dat zal wel veel tijd kosten ja... ik dacht wellicht kan ik ergens een 'inner loop' is wat liefde geven. Dan kun je nog wel is een flink slag slaan zonder alles overhoop te gooien, vandaar de vraag. Maar als je zo niks te binnen schiet moet ik gaan profilen, wat op zichzelf voor je het weet weer een weekend opvreet ;).

Colibri

@proycon Colibri ziet er zeer interessant uit, ik ben inderdaad geïnteresseerd in geannoteerde tekst maar de ideeën zijn goed vertaalbaar. Moet ik die 'Compressed binary representation' als zoiets als huffmann coding zien? Of meer een platte index zoals hierboven beschreven, ik zal zo ook even naar de code kijken ;).

@proycon
Copy link
Member

proycon commented Jan 11, 2021

Moet ik die 'Compressed binary representation' als zoiets als huffmann coding
zien? Of meer een platte index zoals hierboven beschreven, ik zal zo ook even
naar de code kijken ;).

Yep, dat is precies wat het is inderdaad, een huffman coding. Hoog frequente tokens nemen weinig plek in en laagfrequente tokens meer plek. In het paper wat ik indertijd geschreven heb en op de website gelinkt staat kan je wat preciezere details vinden.

Het koppelen van verdere annotaties is iets waar ik wel aan gedacht heb en ideeën over had, maar nooit een use-case/tijd/funding voor had.

Je format klinkt interessant en compact! Leuk om te lezen. Ik vraag me wel af
of je niet in de knoop komt als je je beperkt tot slechts 8 morfemen/lemmas per
woord. Maar ja, ik snap het punt, maak het groter en je hebt weer heel veel
loze ruimte. Een nadeel is ook dat waarschijnlijk wat lastig uitbreidbaar is,
als je er later nog een veld aan toe zou willen voegen (en ook nog oude data in
wil kunnen lezen).

PoS tags selecteer ik er simpelweg 256 van, daar verlies ik dus wat
informatie, ik denk uitzonderlijk marginaal maar nu moeilijk in te schatten.

Het hangt er een beetje van af of je ook in de onderliggende features geintereseerd bent (getal, geslacht, persoon, etc) of alleen in de root PoS tag. Met features lopen de combinaties wel snel op en is 8-bit inderdaad te krap. Hier is de FoLiA set definitie met alle mogelijkheden: https://github.com/proycon/folia/blob/master/setdefinitions/frog-mbpos-cgn

Wat betreft de performance optimalisaties, dat zal wel veel tijd kosten ja...
ik dacht wellicht kan ik ergens een 'inner loop' is wat liefde geven. Dan kun
je nog wel is een flink slag slaan zonder alles overhoop te gooien, vandaar
de vraag. Maar als je zo niks te binnen schiet moet ik gaan profilen, wat op
zichzelf voor je het weet weer een weekend opvreet ;).

Ja, Ko heeft zelf al veel tijd besteed om het één en ander te optimaliseren,
maar het viel uiteindelijk netto toch tegen.

@boblucas
Copy link
Author

boblucas commented Jan 12, 2021

Yep, dat is precies wat het is inderdaad, een huffman coding. Hoog frequente tokens nemen weinig plek in en laagfrequente tokens meer plek. In het paper wat ik indertijd geschreven heb en op de website gelinkt staat kan je wat preciezere details vinden.
Het koppelen van verdere annotaties is iets waar ik wel aan gedacht heb en ideeën over had, maar nooit een use-case/tijd/funding voor had.

Leuk, ga even lezen zo :)

Je format klinkt interessant en compact! Leuk om te lezen. Ik vraag me wel af
of je niet in de knoop komt als je je beperkt tot slechts 8 morfemen/lemmas per
woord. Maar ja, ik snap het punt, maak het groter en je hebt weer heel veel
loze ruimte. Een nadeel is ook dat waarschijnlijk wat lastig uitbreidbaar is,
als je er later nog een veld aan toe zou willen voegen (en ook nog oude data in
wil kunnen lezen).
Het hangt er een beetje van af of je ook in de onderliggende features geintereseerd bent (getal, geslacht, persoon, etc) of alleen in de root PoS tag. Met features lopen de combinaties wel snel op en is 8-bit inderdaad te krap. Hier is de FoLiA set definitie met alle mogelijkheden: https://github.com/proycon/folia/blob/master/setdefinitions/frog-mbpos-cgn

Deze hoeveelheden zijn op basis van een klein testje met 10 miljoen tokens, daar zaten 223 unieke PoS tags + features in. Ik heb ook nog geen voorbeeld van een woord met > 8 mogelijke lemma's. Er zijn soms best veel woorden bij een lemma, maar niet zo veel lemma's voor een gegeven woord. Ik denk dat dit af te kaderen moet zijn toch?

Ik kan natuurlijk sowieso een keer door 10% van de dataset heen en is kijken wat de verdeling is van alle 333 tags uit de folia documentatie. Ik ben er op basis van m'n kleine sample eigenlijk al zeker van dat ik minder dan 0.0001% van de tokens een PoS feature mist ivm frog's output. Dat lijkt me verwaarloosbaar in vergelijking met de verwachte nauwkeurigheid van de annotatie.

En het volledige bestandsformaat gaat een header hebben die de fields, hun type (indexed, enumeration, ...) en aantal bits specificeert zodat je een veld toe kan voegen zonder de reader/writer/search aan te moeten passen. De grootste kosten zijn dat het niet human readable is en de code om er mee te werken complexer.

Ja, Ko heeft zelf al veel tijd besteed om het één en ander te optimaliseren,
maar het viel uiteindelijk netto toch tegen.

Ja ik denk dat ik dat even links laat liggen, daar kun je echt vele uren op verbranden. Wil ook gezegd hebben dat de performance zeker niet slecht is! Met Alpino gaat dit hele idee gewoon niet op.

@boblucas
Copy link
Author

boblucas commented Jan 15, 2021

* Frog denkt in zinnen. Als Frog eenmaal runt zal het geheugengebruik weinig meer toenemen.
  Elke zin start met een schone lei.

@kosloot Ik heb even een testje gedaan. Ik heb frog op 1 core de afgelopen 3 dagen laten draaien op een flinke hoeveelheid niet super schone nederlandse tekst. Het totale geheugenverbruik neemt heel langzaam toe. Hij heeft nu ~3M zinnen met ~64M tokens geparsed en gebruikt 9.9GB geheugen. De performance is nu ongeveer 200 tokens/s (half zo snel).

Je advies om per 100.000 zinnen de data te verwerken is dus spot on. Gezien hoe makkelijk het is om het in chunks te doen en hoe lastig het kan zijn dit soort lekken te vinden in C++ lijkt me dit simpelweg een prima conclusie. Hoe dan ook dacht ik dat jullie (of anderen) het wellicht ook interessant zouden vinden om te weten in welke mate dit speelt.

frog is verder dus wel super robuust, als je er per ongeluk minified JS, HTML of bizar lange woorden ingooit etc vertraagd ie verder niet, ik verwacht niet dat ik een crash tegen ga komen :). Super fijn!

(Ik heb ook een mock-up gemaakt voor m'n bestandsformatje hierboven in python, dat lijkt ook prima te functioneren)

@kosloot
Copy link
Collaborator

kosloot commented May 5, 2023

Ik beschouw dit als afgerond.

@kosloot kosloot closed this as completed May 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants