Part of Speech-tagger
En af de store klumper, som jeg gerne vil udvikle er en Part of Speech-tagger, der fungerer på dansk.
Hvad er Part of Speech-tagging?
Helt grundlæggende handler POS-tagging om at opmarkere alle ordene i en tekst med deres Part of Speech - altså deres rolle i sætningskonstruktionen.
Lad mig give et eksempel. Her er vores input:
const text = Jeg vil bygge et værktøj, der hjælper dig med at skrive bedre tekster.
Hvis vi kører den gennem en POS-tagger, så får vi noget ala det her ud i den anden ende:
Jeg[Personlig pronomien] vil[verbum, nutid] bygge[verbum, infinitiv] et[ubestemt pronomien] værktøj[substantiv, intentkøn] ,[Tegn] der[adverbium] hjælper[verbum, nutid] dig[personlig pronomien] med[?] at[?] skrive[verbum, infinitiv] bedre[adjektiv] tekster[substantiv, ubestemt, pluralis].[Tegn]
Det ser måske lidt skørt ud. Og i koden vil det blive struktureret helt anderledes. Men det giver et billede af, hvordan hvert enkelt ord bliver tagget op i sætningen.
Og det giver et lille billede af, at der også er nogle ord, som jeg simpelthen er i tvivl om, hvordan vi definerer. Så der er sikkert også en god slat grammatik at lære undervejs.
De overordnede trin:
- Opsætte unittest med ekstreme eksempler
- Indsamle ordlister
- Ensrette ordlister
- Optimér
tekstr.sentencen()
til at kunne lexe på højt niveau (skal kunne styre udenom forkortelser). - Optimér
tekstr.words()
til at kunne tokenize med stor præcision (skal sætte alle tegn for sig selv). - Skriv funktion til hurtig match / lookup af det aktuelle ord i de forskellige lister (brug eventuelt => https://github.com/spencermountain/efrt)
- ...
Hvordan ser processen ud?
Når POS-taggeren får en tekst, så sker der følgende:
- Teksten deles op i sætninger [perioder]. Det kalder vi at tokenize den.
- Derefter gennemgår vi hver sætning for sig. Den enkelte sætning tokenizer vi også, så vi har de enkelte ord.
- Så gennemgår vi de enkelte ord og tagger dem op med ordklasse og tid.
- Vi spytter det hele ud i et ´object´ eller lignende.
Hvad kan vi bruge det til?
Selve det object, der bliver spyttet ud er jo ikke super nemt at læse. Vi så det i eksemplet herover. Det er svært at grave sig gennem de enkelte ord og deres tags.
Det kan vi eksempelvis bruge til:
- markere grundled og udsagnsled i sætninger, så du lettere kan sætte dine kommaer.
- tælle antallet af negativt og positivt ladede ord og sætninger, så vi kan lave sentimentanalyser.
- fortælle dig, at du har brugt en hel del verbal substantiver i din tekst, og det gør den svær at læse.
- fange mismatch mellem brug af nutid og datid.
Der er mange muligheder. POS-taggeren er lidt som en dåseåbner for de muligheder.
Hvad består POS-taggeren af?
Vi skal bygge en POS-tagger, der kan:
- Klargøre tekst: & skal eksempelvis erstattes med &, hvis den optræder.
- Lexing: Opdele sætninger og ord (er tekstr.sentences og tekstr.words præcise nok?).
- POS-tagging: Markere ord i en tekst
- Afhængighedstræ: Hvordan står de enkelte ord i relation til hinanden i sætningen (er det Gitte eller Søren, der er sød?)
- Skal der ske noget med resultatet bagefter?
De enkelte dele af opgaven
Klargøre tekstr
Det er sandsynligt, at en streng kan indeholde HTML-entities eller lignende, når den bliver givet til POS-taggeren. Derfor skal vi kunne matche de mest almindelige og så erstatte dem med det korrekte symbol.
& --> & > --> > < --> <
Lexing
POS-tagging
Vi skal finde en ekstremt hurtig måde at matche de her ord på
Helt grundlæggende, så er en stor del af vores sprog sammensat af den samme lille gruppe af ord. Se eksempelvis Zips Law
https://en.wikipedia.org/wiki/Zipf%27s_law
Det kræver en del gode ordlister, som vi nemt kan matche med. Og så kommer det måske til at kræve en del hastighedstest med forskellige teksteksempler for at finde den optimale rækkefølge til at tjekke de her ting.
Hvilke ordlister skal vi hente ind?
- Det grundlæggende leksikon af ord (HunSpell ordbogen)
- Liste med personnavne
- List med bynavne
- Liste med forkortelser
- Liste med sammentrækninger (bli'r --> bliver)
- Liste med uformelle ord, slang ord
- Kan vi med RegEx matche egennavne som på engelsk? https://stackoverflow.com/questions/38054067/regex-to-match-proper-nouns-numbers
- Kan vi bruge almindelige tidsangivelser som 'i dag', 'i morgen' til noget?
Hvilke mønstre kan vi nemt matche med?
- URL
- time
- ID
- tal
Afhængighedstræ
Afhængighedstræet er en struktur i vores analyse af de enkelte ord, hvor vi placerer dem i forhold til de andre ord i teksten. Det vil give os større præcision, fordi mange fuldstændig ens ord kan være flere forskellige ordklasser, hvis du ikke kender den præcise kontekst.
Se mere under punktet: Kendte problemer - ord der kan optræde i forskellige klasser.
Kendte problemer
Jeg nørkler med små demoer af det her setup allerede. Og jeg er stødt på en del problemer. Det er ikke så meget det tekniske i koden, der gør det svært. Det er det danske sprog, der er svært.
Ord der kan optræde i flere forskellige ordklasser
Modellen er lige nu, at jeg matcher ord med kendte ordlister ud fra en række regler. Men det giver mig en del problemer, fordi et match på det enkelte ord i virkeligheden ikke er præcist nok.
Her er et par hurtige eksempler på problemet:
Du er både et personligt pronomien ('Du er ikke rigtig klog') og et verbum ('det vil ikke du').
Husker er både et substantiv ('en husker er et billede på hjernen som i huskeren') og et verbum ('jeg husker dig ikke').
Der er bunkevis af den slags eksempler. En del kan måske løses ved at få styr på de enkelte ords forhold til hinanden gennem et afhængigstræ for de enkelte ord. Det kan være en måde at skabe noget kendskab til konteksten. Men det er stadig et temmelig kompleks sandsynlighedsscenarie for hvert ord.