LIBCAS / DL4DH

DL4DH – development of tools for effective utilization and mining of data from digital libraries to reinforce digital humanities research
GNU General Public License v3.0
8 stars 2 forks source link

postup OCR → TEI: kdy pouštět jazykovou analýzu? #9

Closed stranak closed 2 years ago

stranak commented 3 years ago

Jazykovou analýzu lze pustit buď na plaintextu a nebo na text převedený už do TEI: vyextrahuje se z něj plaintext, pustí analýza a vloží se zpět do TEI.

Viz také LIBCAS/DL4DH#1: toto rozhodnutí silně ovlivní, jak bude výsledné TEI vypadat.

Takto, až v TEI, pouštíme analýzu, když děláme parlamentní TEI dokumenty:

  1. Převod do tei
  2. metadata pro neanotovaná data(vyplnění hlavičky)
  3. anotace
  4. metadata pro anotovaná data
  5. konverze do teitoku

volání udpipe:

https://github.com/ufal/ParCzech/blob/master/src/run_parczech.sh#L402 volá tento skript: https://github.com/ufal/ParCzech/blob/master/src/udpipe2/udpipe2.pl

volání nametag:

https://github.com/ufal/ParCzech/blob/master/src/run_parczech.sh#L421 volá skript: https://github.com/ufal/ParCzech/blob/master/src/nametag2/nametag2.pl

Má to smysl řešit, pokud na začátku jako výsledek OCR máme ALTO, které obsahuje dost informací vč. pozic na stránce. Pokud chceme tyto informace zachovat i na výstupu do TEI, tak je asi nejsnazší TEI generovat přímo z ALTO. Zde je ukázka TEI z ALTO: https://cunicz-my.sharepoint.com/:f:/g/personal/51291532_cuni_cz/EjADq5FS0URHkvlkWlGpVmkBNLGfIisuJ6S55wfB10xMaw?e=LG8pgL

Druhá možnost je v současném schématu: ALTO >> plain text >> NLP analýza (+ přidat k tokenům koordináty z ALTO) >> TEI

daliboris commented 3 years ago

Některé výstupy OCR jsou momentálně ve fromátu prostého textu, některé jsou ve formátu ALTO. V případě jednotného postupu by bylo možné i prostý text nejprve převésto do TEI (byť bez vazby na obrazovou předlohu) a následně TEI předat na jazykovou analýzu. Problém s ALTO bývá, že za jeden token považuje slovo + interpunkci (viz např. <w xml:id="e108" facs="#f108">zapsati,</w>), přičemž pro jazykovou analýzu jde o tokeny 2. Vznikne tak nesoulad v počtu "tokenů", který se bude muset vyřešit (nejspíš restrukturalizací TEI při zanášení výsledků jazykové analýzy).

stranak commented 3 years ago

Dobrá poznámka a docela problém, pokud bychom chtěli jak udělat správnou tokenizaci (tedy oddělit interpunkci od slov), tak zachovat odkazy na koordináty tokenu na stránce. Možná by ale stačilo jednoduché řešení, jako třeba ALTO "token" zapsati, rozdělit na dva v TEI a oba mohou odkazovat na stejné koordináty.

Pokud by se tedy použilo TEI s elementy facs jako v tomto převodu z ALTO

...
<tei:zone type="String" xml:id="f446" ulx="1803" uly="1213" lrx="1982" lry="1255"/>
...
<w xml:id="e446" facs="#f446">pokladně,</w>
...

tak by se <w xml:id="e446" facs="#f446">pokladně,</w> mohl rozdělit na 2 elementy <w>, které by ale oba odkazovaly na facs="#f446".

bodnarIQ commented 3 years ago

Na poslednej schôdzke 15.1 sme sa teda zhodli na tom, že vstupom bude stále ALTO formát, takže na toto sa teda budeme spoliehať. Je otázne, ako by mal vyzerať vstup do TEI Converteru, aby z neho bol converter schopný vyskladať TEI formát. Akým spôsobom mu budú predávané dáta + metadáta, aby vedel, ako ich poskladať do TEI?

Mne príde ako veľmi príjemné riešenie posielanie práve ukladanej stromovej štruktúry do vstupu TEI Converteru. Stromová štruktúra je veľmi prirodzená pre prevod do XML formátu a bolo by jednoznačné, ako má výsledné XML (TEI) vyzerať. Preto by som navrhoval nasledovný flow: ALTO -> vybudovanie základného stromu z ALTO metadát -> obohatenie stromu UdPipeom -> obohatenie stromu NameTagom -> prevod stromu do TEI

Keďže by sme metadáta ukladali ako strom, mohli by sme tento strom v hociktorom kroku poslať do TEI converteru a pracoval by s ním stále rovnakým spôsobom, a zároveň by takmer ani nezáležalo, ako ten strom vyzerá, mohli by sme ho ľubovoľne obohatiť a TEI Converter by s ním stále pracoval rovnako, ideálne by nebolo nutné robiť žiadne prográmatorské zásahy do TEI Converteru pri novom type obohatenia.

Problém s tokenizáciou v ALTO by sa mohol riešiť v prvom kroku - pri budovaní stromu. Ak by sa detekovalo, že ide o slovo ktoré je potrebné rozdeliť na dve tokeny, vytvorili by sa dva listy pre daný token s rovnakými atribútmi prevzanými z ALTO(pozície). Takisto by sme tu mohli riešiť rozdelenie slova - ak sa detekuje, že ide o rozdelené slovo na dva riadky, ako Content pre daný list by sa mohlo uložiť celé slovo. Tu je otázka, či nebude problém pri prevode do TEI, že by sa tieto slový vyskytovali duplikovane(celé slovo z predchádzajúcej strany + celé slovo z nasledujúcej strany), no myslím si, že aj toto by sme mohli nejakým spôsobom pomerne jednoducho vyriešiť.

Zaujímalo by ma ešte, či toto je jediný prípad kedy sa tokenizácia z ALTO nezhoduje so správnou tokenizáciou pri jazykovej analýze. Momentálne ma napadá napríklad tokenizácia pri slovách spojených spojovníkom (čierno-biely), alebo bodka pri skracovaní slov (napr. napr.).

stranak commented 3 years ago

Zaujímalo by ma ešte, či toto je jediný prípad kedy sa tokenizácia z ALTO nezhoduje so správnou tokenizáciou pri jazykovej analýze. Momentálne ma napadá napríklad tokenizácia pri slovách spojených spojovníkom (čierno-biely), alebo bodka pri skracovaní slov (napr. napr.).

Jsou i další případy a jsou různé v různých jazycích. Za robustnější řešení považuji nezkoušet tokenizaci vylepšit ručně, tokeny pospojovat do řetězce a nechat tokenizaci udělat UDPipe. Čili bych z ALTO vzal jen informaci o spojení rozděleného tokenu, pak nechal UDPipe udělat tokenizaci a tu použil pro TEI a namapoval na původní tokeny ALTO. To mapování bude trošku těžší, ale bude to robustnější workflow. Hlavní je, že výsledná tokenizace bude správně pro jazykovou analýzu (která by se měla pustit asi rovnou zároveň).

zabak commented 3 years ago

Prosím ještě pamatujte na rozdělená slova na konci řádku. Pokud je to v rámci jedné stránky, tak nověji dělané OCR už obsahuje korektně otagované dělení slov. U těch starších to může vypadat i takto (slovo "povídá"):

            <String CONTENT="poví" HEIGHT="63" WIDTH="116" VPOS="1031" HPOS="1834"/>
            <SP WIDTH="20" VPOS="1048" HPOS="533"/>
            <SP WIDTH="24" VPOS="1031" HPOS="728"/>
            <SP WIDTH="24" VPOS="1068" HPOS="949"/>
            <SP WIDTH="23" VPOS="1039" HPOS="1136"/>
            <SP WIDTH="19" VPOS="1029" HPOS="1211"/>
            <SP WIDTH="19" VPOS="1046" HPOS="1366"/>
            <SP WIDTH="20" VPOS="1030" HPOS="1510"/>
            <SP WIDTH="22" VPOS="1037" HPOS="1811"/>
            <HYP CONTENT="172"/>
          </TextLine>
          <TextLine HEIGHT="72" WIDTH="1487" VPOS="1106" HPOS="483">
            <String CONTENT="dá" HEIGHT="48" WIDTH="65" VPOS="1109" HPOS="483"/>

u novějších

            <String CONTENT="poví" HEIGHT="64" HPOS="1774" SUBS_CONTENT="povídá" SUBS_TYPE="HypPart1" VPOS="958" WIDTH="115"/>
            <SP HPOS="496" VPOS="974" WIDTH="19"/>
            <SP HPOS="690" VPOS="958" WIDTH="18"/>
            <SP HPOS="903" VPOS="996" WIDTH="24"/>
            <SP HPOS="1087" VPOS="967" WIDTH="17"/>
            <SP HPOS="1155" VPOS="957" WIDTH="22"/>
            <SP HPOS="1311" VPOS="975" WIDTH="22"/>
            <SP HPOS="1457" VPOS="957" WIDTH="23"/>
            <SP HPOS="1752" VPOS="964" WIDTH="21"/>
            <HYP CONTENT="­"/>
          </TextLine>
          <TextLine BASELINE="1081" HEIGHT="67" HPOS="446" VPOS="1034" WIDTH="1463">
            <String CONTENT="dá" HEIGHT="46" HPOS="446" SUBS_CONTENT="povídá" SUBS_TYPE="HypPart2" VPOS="1034" WIDTH="63"/>

Na konci stránky pak bývá jen něco jako

           <String CONTENT="do-" HEIGHT="46" WIDTH="83" VPOS="2374" HPOS="1857"/>

Protože ocr se dělá bez kontextu souvisejících stránek. Ale tam je aspoň rozdělovník.

bodnarIQ commented 3 years ago

Pôvodný návrh so stromovou štruktúrou je momentálne neaktuálny, pretože by priniesol zbytočnú zložitosť pri práci so stromom. Vraciame sa teda k ukladaniu obsahu stránky publikácie ako pole tokenov, kde každý token obsahuje metadáta ktoré sa k nemu viažu.

Ideálne by sme chceli, aby prevod do formátu TEI a obohacovanie metadátami boli dva oddelené, navzájom nezávislé procesy. Nerád by som to miešal a snažil sa implementovať obohacovanie metadátami, kde vstupom do obohacovacieho procesu môže byť aj TEI formát, aj plain textový formát. Metadáta získané k jednotlivým tokenom z externých nástrojov(udPipe, nameTag), prípadne iných formátov zápisu(ALTO) budeme musieť nejakým spôsobom(iným než TEI formát) ukladať do primárnej databázy a následne predávať do TEI Convertoru, takže výstup z obohacovacieho procesu vo formáte TEI a následné spracovanie TEI do nejakého univerzálneho formátu, v ktorom budeme ukladať do DB, by bolo dosť nepraktické. Navyše pokiaľ správnu tokenizáciu(ako to bolo spomenuté vyššie) ktorá bude základom obohacovania, má zaisťovať nástroj UdPipe, bude musieť toto obohatenie prebehnúť v prvom kroku. Následne by sa tým pádom aj v TEI formáte označovali jednotlivé slová nie podľa ALTO formátu, ale podľa tokenizácie z UdPipe.

Preto by som teda preferoval druhú možnosť, ktorú som pre naše interné účely rozpísal trochu detailnejšie a tento popis prikladám nižšie.

Popis postupu Prvým krokom je získanie plainTextu pre spracovanie nástrojom UdPipe. V ideálnom prípade sa dostaneme priamo k plainTextu z OCR, v prípade že neexistuje, musíme extrahovať plainText z formátu ALTO(tento bude stále dostupný). Tento plaintext sa následne pošle na spracovanie nástrojom UdPipe, ktorý primárne vykoná tokenizáciu textového obsahu. Týmto sa plaintext prevedie na pole objektov - tokenov. Každý token bude obsahovať `startOffset` a `endOffset` atribúty, a atribút `udPipeMetadata`, ktorý bude obsahovať metadáta získané z nástroja udPipe. V tomto momente môžeme uložiť/vytvoriť dokument v DB. Následne sa opäť spracuje ALTO formát - v tomto kroku sa naviažu metadáta z jednotlivých slov z ALTO na korešpondujúce tokeny získané z UdPipe. Budú sa postupne prechádzať jednotlivé slová z ALTO, pričom sa bude držať počítadlo pre `startOffset` a `endOffset`. Pre každý token z UdPipe sa podľa počítadla `startOffset` a `endOffset` nájde korešpondujúci tag v ALTO, z ktorého sa extrahujú príslušné metadáta a uložia sa do atribútu `altoMetadata`. Keďže ALTO rozdeľuje slová inak, než UdPipe, môže dôjsť k niekoľkým situáciám: - Offsety z UdPipe tokenu budú totožné s offsetmi z ALTO: toto je ideálny prípad, kedy sa nemusí robiť žiadna práca navyše, metadáta z ALTO sa jednoducho zoberú a uložia pre daný token - Offsety z UdPipe sú podmnožinou offsetov z ALTO: k tejto situácii môže dôjsť v prípade, že narazíme na slovo obsahujúce interpunkciu (`,`, `.`, `(`, `)`, ...). Tieto znaky sa v ALTO vyskytujú spolu so slovom v jednom tagu, zatiaľčo UdPipe ich rozdelí do dvoch tokenov. V tomto prípade potrebujeme uložiť metadáta z ALTO tagu pre všetky tokeny, ktoré patria do podmnožiny ALTO offsetov. - `startOffset` z UdPipe je v jednom tagu a `endOffset` je v nasledujúcom tagu: k tomuto môže dôjsť pri rozdeľovaní slov. Ak sa plainText extrahuje správne (rozdelené slová sa spoja do jedného), tak UdPipe takéto slovo vyhodnotí do jedného tokenu, no v ALTO sa bude nachádzať prvá polka rozdelného slova v jednom tagu (na konci riadka) a druhá polka slova v nasledujúcom tagu (prvé slovo na ďalšom riadku/ďalšej stránke). Ak ide o rozdelenie slova cez viaceré stránky, tag pre dané slovo obsahuje aj znak pomlčky(``). Pri rozdelení slova v rámci jednej stránky: - V prípade novších OCR skenov môžeme toto detekovať atribútom `SUBS_CONTENT`(obsahuje plný tvar slova) a `SUBS_TYPE`(`HypPart1` pri prvej časti rozdeleného slova, `HypPart2` pri druhej časti) v oboch tagoch rozdeleného slova v ALTO a tagom `` na konci riadka, - u starších skenov iba prítomnosťou tagu `` na konci riadka Pre správnu transformáciu do TEI musíme uložiť aj informáciu o rozdelení slova. **TODO: Ako reprezentovať rozdelené slová v TEI formáte?** Po spracovaní metadát z ALTO formátu môžeme dáta spracovať ďalšou službou - momentálne ide najmä o službu NameTag. Táto služba vracia typy rozpoznaných entít k jednotlivým tokenom. Pre namapovanie na naše tokeny(objekty) budeme musieť postupovať po jednotlivých tokenoch v pôvodnom poradí a v prípade, že sa na danej pozícií nachádza informácia z NameTagu, priložíme tieto metadata do atribútu `NameTagMetadata`.

Za robustnější řešení považuji nezkoušet tokenizaci vylepšit ručně, tokeny pospojovat do řetězce a nechat tokenizaci udělat UDPipe. Čili bych z ALTO vzal jen informaci o spojení rozděleného tokenu, pak nechal UDPipe udělat tokenizaci a tu použil pro TEI a namapoval na původní tokeny ALTO.

Držal by som sa teda práve tohto. Dôležité v popise vyššie sú hlavne situácie, ktoré môžu nastať, ak sa tokenizácia pomocou UdPipe nezhoduje s ALTO rozdelením na slová. Tu som definoval tri možnosti, ktoré môžu nastať(ak som na nejaký zabudol, prosím doplňte ma) a ako ich budeme riešiť. Prosím tiež o doplnenie, ako by sa rozdelené slová mali reprezentovať v TEI - viaceré <w> tagy, jeden tag s viacerými odkazmi na polohy z ALTO, ...?

Zdá sa Vám tento postup vhodný?

bukovskyIQ commented 3 years ago

Prosíme o vyjádření. @stranak @zabak

daliboris commented 3 years ago

Pokud budeme chtít zachovat vazbu na pozice v ALTO, lze v TEI požít konstrukci typu

<w xml:id="w-2" next="#w-3">poví</w><w xml:id="w-3" prev="#w-2">dá</w>.

daliboris commented 3 years ago

Konverze ALTO na TEI https://github.com/cneud/ocr-conversion

stranak commented 3 years ago

Zdá sa Vám tento postup vhodný? Ano, mně to připadá dobré. Jen pár poznámek:

  • počítal bych s tokenizací a obohacením plaintextu – tedy i lematizace – jak 2 procesy. Nejprve tokenizace, potom metadata k tokenu, např. to lemma. I pokud to teď bude dělat stejná služba, počítal bych se 2 nezávislými voláními.
  • jednak i v UDPipe se může hodit použít různé modely (různá volání) pro tokenizaci a lematizaci, jednak to dovolí případně vyměnit tokenizer a lemmatizer nezávisle.
  • plaintext se z ALTO musí extrahovat správně. Existují na to nějaké nástroje, ale netuším, jestli třeba to spojení rozdělených slov umění vyřešit správně (navíc když jsou na to 2 způsoby, jak popsal @zabak).
  • @daliboris Jak se to rozdělené slovo reprezentuje v TEI? Konvertor ALTO>>TEI, co jsem našel dříve, tu informaci zahazuje.
  • existují taky hotové projekty pro práci s ALTO. Třeba se něco z toho může hodit. https://github.com/altoxml/documentation/wiki/Software#tools-for-transforming-alto-or-other-formats-into-alto
  • např. extrakce dat a metadat: https://github.com/cneud/alto-tools
  • ruční opravy OCR přímo v ALTO XML: https://github.com/renevanderark/altoedit-2.0 (http://renevanderark.github.io/altoedit-2.0/) aj.

Pro srovnání, zajímavě vypadá taky toto obohacování o metadata: https://github.com/altomator/EN-data_mining

bodnarIQ commented 3 years ago

Dobrý deň,

z dôvodu prác na spracovávaní ALTO formátu by som rád znovuotvoril toto issue, najmä z dôvodu spomenutom v druhom bode tejto odpovede.


počítal bych s tokenizací a obohacením plaintextu – tedy i lematizace – jak 2 procesy

Tento postup by bol možný, ale treba brať do úvahy, že to v praxi znamená o jeden request na externý server viac. Momentálne každá stránka potrebuje 3 requesty (jeden pre získanie obsahu, jeden pre tokenizáciu a lematizáciu UDPipom a jeden pre obohatenie NameTagom), a keďže úzke hrdlo rýchlosti obohacovania je práve dotazovanie sa na externé služby, dĺžka obohacovacieho procesu by sa zvýšila približne o tretinu.

plaintext se z ALTO musí extrahovat správně. Existují na to nějaké nástroje, ale netuším, jestli třeba to spojení rozdělených slov umění vyřešit správně (navíc když jsou na to 2 způsoby, jak popsal @zabak).

Pôvodne pracoval systém Kramerius+ s textami stránok získaných z OCR streamu, t.j. plaintextom. Plaintext získaný z API krameria obsahoval aj znaky "-" a znaky nového riadku, čiže text presne odpovedal svojej fyzickej podobe. Pre spájanie slov na konci riadkov som používal veľmi jednoduchý regex pre nahradzovanie znakov

Tento postup bol síce veľmi triviálny, ale zdal sa byť pomerne efektívny.

S príchodom ALTO formátu sme plánovali plaintext vyťahovať z ALTO. Už pri prvotnej diskusii sme ale narazili na 2 rôzne spôsoby, akým môže byť rozdelené slovo na konci riadka zapísané(staršie vs novšie OCR). Teraz som narazil ešte na tretí spôsob, kedy rozdelené slovo nie je vôbec nijako označené za rozdelené.

Ukážka Slovo `arcipastýře` ``` ```

Nabízí se tedy otázka, či má vôbec zmysel snažiť sa vymyslieť nejaký sofistikovanejší spôsob spájania slov než pôvodný "algoritmus", ktorý pracoval s plaintextom a jednoducho iba nahradzoval dvojice znakov na kocni riadka za prázdny znak.

Tým pádom by sme si mohli uľahčiť proces obohacovania o časť extrakcie plaintextu z ALTO(bolo by to menej pracné, proces obohacovania by bol rýchlejší a menej náchylný na programátorske chyby) a namiesto toho využiť už existujúci plaintext uložený v Krameriovi. Pre získanie metadát z ALTO formátu (poloha tokenu na stránke, šírka, výška) by sme museli namapovať existujúce tokeny na slová v ALTO, čo by sme ale museli aj pri extrakcii plaintextu z ALTO, takže tento krok je nevyhnutný v oboch prípadoch.

daliboris commented 3 years ago

úzke hrdlo rýchlosti obohacovania je práve dotazovanie sa na externé služby

Podle repozitáře UDPipe is available as a binary for Linux/Windows/OS X, as a library for C++, Python, Perl, Java, C# (binárky jsou zde). Pokud by měla být tokenizace úzké hrdlo, možná by binární knihovna pomohla. Bylo by potřeba mít k dispozici aktuální verze modelů.

plaintext se z ALTO musí extrahovat správně. Existují na to nějaké nástroje

Zveřejnil jsem na svém forku DL4DH program v XProc, který data/texty Krameria extrahuje a obohacuje.

Pro získání textu nejprve transformuju ALTO na TEI pomocí šablony alto2tei.xsl; převzato odsud. Následně používám vlastní šablonu tei2text.xsl, která převede TEI na prostý text (včetně dvou prázdných odstavců pro oddělení původních odstavců).

jednoduchý regex pre nahradzovanie znakov

Nejspíš to opravdu pokryje většinu případů. Ještě mě napadá, že by se v některých textech mohl rozpoznat delší spojovník jako pomlčka (–, —).

Pomocí Alta možná budeme moci odlišit živé záhlaví, popř. zápatí, které by se mělo zpracovávat nezávisle na ostatním textu stránky.

Je třeba počítat s tím, že v některých případech se spojovník při OCR nerozpozná správně, případně se z pomlčky na konci řádku stane spojovník, což je časté v lingvistických textech, kde se pomlčka používá k označení zbytku slova (např. předpony od-, do-, při-).

V obou zmiňovaných případech bych to považoval za chybu externích nástrojů, kterou nebudeme opravovat. Pokud bychom ale objevili nějaký jednoduchý a 100% platný princip, který by část chyb eliminoval, tak bych nějakou opravu zavedl.

hlageek commented 3 years ago

Já v tom nejsem žádný expert, ale intuitivně bych se přikláněl k tezi, že v průměrném textu bude většinou spojovník na konci řádku značit rozdělení slova na dva řádky a zmiňovaných regex tak, aspoň u většiny textů, víc chyb odfiltruje než jich vyrobí. Vzhledem k tomu, že zdaleka největší vliv na kvalitu bude mít OCR, bylo by kontraproduktivní vymýšlet nějaké složité pravidlové systémy. Za sebe bych šel po lince ALTO->regex->UDPipe a NameTag z plaintextu -> lemmatizovaný a obohacený text konvertovat do Kramerius+ v tabulkové nebo JSON formě a tentýž text konvertovat do TEI. TEI a tabulky by na sebe měly lícovat, což půjde zajistit jen tak, že TEI konverze bude posledním krokem.

stranak commented 3 years ago

Omlouvám se, ale k dalším bodům se mohu vyjádřit až v září.

zabak commented 3 years ago

Já v tom nejsem žádný expert, ale intuitivně bych se přikláněl k tezi, že v průměrném textu bude většinou spojovník na konci řádku značit rozdělení slova na dva řádky a zmiňovaných regex tak, aspoň u většiny textů, víc chyb odfiltruje než jich vyrobí. Vzhledem k tomu, že zdaleka největší vliv na kvalitu bude mít OCR, bylo by kontraproduktivní vymýšlet nějaké složité pravidlové systémy. Za sebe bych šel po lince ALTO->regex->UDPipe a NameTag z plaintextu -> lemmatizovaný a obohacený text konvertovat do Kramerius+ v tabulkové nebo JSON formě a tentýž text konvertovat do TEI. TEI a tabulky by na sebe měly lícovat, což půjde zajistit jen tak, že TEI konverze bude posledním krokem.

Přidal bych ještě to, že spojovník na konci řádku, před kterým není mezera ale písmeno znamená rozdělení slova.

bodnarIQ commented 3 years ago

Za sebe bych šel po lince ALTO->regex->UDPipe a NameTag z plaintextu -> lemmatizovaný a obohacený text konvertovat do Kramerius+ v tabulkové nebo JSON formě a tentýž text konvertovat do TEI. TEI a tabulky by na sebe měly lícovat, což půjde zajistit jen tak, že TEI konverze bude posledním krokem.

Z Krameria mame ale k dispozicii aj priamo plaintext, takze linka ALTO->regex pre získanie plaintextu je zbytočne komplikovaná. Preto navrhujem skôr nasledovny postup: PlainText + regex -> UDPipe a NameTag z plaintextu -> lemmatizovane a obohacene tokeny obohatit este o metadata z ALTO -> ulozit do DB (JSON) -> konvertovat do TEI

nepsal bych nic složitého, co bude třeba udržovat. Spojení slov bych klidně bral jen to, kde je indikované v ALTO, tedy kde to rozpoznal již ten OCR server, jak jsme řešili výše. Pokud to konvertor nezachová správně, tak bych udělal opravu do toho konvertoru (a pull request do původního projektu)

Ako bolo spomenute vyssie, zdroj plaintextu nemusi byt ALTO, usetrilo by nam to cas, ak by sme vychadzali uz z existujuceho plaintextu z Krameria. Z ALTO by sme nasledne iba vyextrahovali potrebne metadata pre jednotlive tokeny (pozicia na strane, pripadne rozdelenie strany na bloky textu a pod.)

Odstavce taky detekuje již OCR v ALTO. Nejsem na to žádný expert, ale když do ALTO souboru z knihovny kouknu, vidím tam elementy ParagraphStyle a o kousek dál elementy TextBlock, které hodnotou atributu STYLEREFS odkazují na ty odstavcové styly. Tak bych řekl, že to jsou odstavce. Zase to nemusí být ideální, ale než bych psal vlastní pravidla, která nejspíš rozbije upgrade OCRka, tak bych asi spíš doufal – aspoň prozatím – že to to OCR bude dělat slušně a nebo se zlepší časem a pustíme ho znovu. Koukám na ALTO z ABBYY Recognition Serveru 4, zpracováno 2020-02-13.

Elementy TextBlock označujú blok textu, nie však priamo odstavce. Napríklad tato strana je v ALTO rozdelena na bloky nasledovne:

Obrazok ![default](https://user-images.githubusercontent.com/76157153/129184038-419ccc9b-66f9-4e26-bc97-83d08722c520.jpg)

a táto stránka s viacerými odstavcami je v alto ako jeden TextBlock. Ide o ABBYY Recognition Server 4.0 z 19.01.2021

daliboris commented 3 years ago

Jenom upozorňuju, že zpracování formátu ALTO na TEI se nelze vyhnout, protože jenom díky tomuto převodu bude zachována informace o tom, kde na stránce/obrázku se daný token nachází. Ale je pravda, že by takovou transformaci šlo provádět paralelně, pokud by pro NameTag a UDPipe sloužil jako základ výstup OCR v prostém textu.

K identifikaci odstavců v ALTO viz též můj komentář k LIBCAS/DL4DH-TEI-Converter#5.

hlageek commented 3 years ago

Pokud není problém obohacené tokeny spárovat s metadaty z ALTO a navíc je to efektivnější, tak jsem za všemi deseti. Více ve LIBCAS/DL4DH-TEI-Converter#5.

motyc commented 3 years ago

Mám trochu obavu, zda ale bude PlainText přímo z Krameria a PlainText generovaný přes ALTO z Krameria totéž. Není to produkt jiného OCR mechanismu a výsledky tedy budou jiné = nemůže to jít přesně napárovat? (@MLhotak, @zabak)

Aby nám to nepřineslo víc komplikací, než užitku...

daliboris commented 3 years ago

zda ale bude PlainText přímo z Krameria a PlainText generovaný přes ALTO z Krameria totéž

Prostý text z Krameria (tj. dostupný přes rozhraní .../streams/TEXT_OCR) a text z ALTO (dostupný přes rozhraní .../strams/ALTO) převedený na TEI a následně na prostý text může být identický. V rámci konverze na TEI dochází totiž ke slučování slov rozdělených spojovníkem do jednoho tokenu, pokud je v ALTO naznačeno, že jde o rozdělené slovo. U výstupů typu TEXT_OCR to teď kolegové řeší pomocí regulárních výrazů.

Problém je v tom, že v ALTO není rozdělení slov pomocí spojovníku zachyceno důsledně: v ukázce, kterou jsme zpracovávali, je např. problém u slova Schwarzenbergů, které se při převodu z ALTO > TEI realizuje jako 3 tokeny: Schwarzen, - a bergů, zatímco při použití regulárních výrazů z toho vznikne jedno slovo.

Pokud při manipulaci s TEI zavedeme další podobné úpravy jako u prostého textu z OCR, mohli bychom se dostat ke shodnému výstupu z obou formátů.

Úpravy prostého textu pomocí regulárních výrazů mohou dát lepší výsledky (lepší podklady pro jazykovou analýzu); jak jsem ale upozorňoval v komentáři, u některých (hlavně asi lingvistických) textů to může zase vést k horším výsledkům.

motyc commented 3 years ago

@daliboris Já jsem to spíš myslel tak, zda nebyl pro generování PlainText OCR použit jiný nástroj, než pro ALTO. To by se pak mohlo lišit i více (kvalitou rozpoznání atd.). Možná to tak není a problém zde nebude, raději jsem to ale chtěl ověřit.

zabak commented 3 years ago

Většinou se obojí generuje stejným nástrojem. Raději ale prověřím reálnou praxi při některých specifických dodatečných úpravách existujících dat.

daliboris commented 3 years ago

Identifikace jazyků

Výstupy morfologické analýzy z UDPipe se liší na základě použitého jazykového modelu. Německá slova rozpoznaná s použitím modelu pro češtinu se sice považují za cizí/nečeský text (Foreign=Yes), ale rozpoznání morfologických kategorií a lemmatu selhává (např. u němekého zuführen: lemma zuführený, Gender=Masc|Number=Sing|Polarity=Pos|Variant=Short|VerbForm=Part|Voice=Pass. Při použití odpovídajícího jazykového modelu jsou výsledky adekvátní: lemma zuführen, VerbForm=Inf.

Ve formátu ALTO lze ve verzi 2.1, která je základem výstupů v Krameriovi, identifikovat jazyk rozpoznaného textu pomocí atributu @LANG na úrovni elementů <String>, <TextLine> a <TextBlock>. Kódy pro jazyky jsou stejné jako u @xml:lang, takže podle Tags for Identifying Languages (BCP47): cs pro češtinu, de pro němčinu atp.

ALTO v Krameriovi atribut pro jazyk neobsahují (otestováno na 4 publikacíh, českých a německých). Odtud tedy nelze údaj o jazyce dokumentu přebírat. Otázka na digitalizační pracoviště: proč v ALTO není jazyk uveden? Je to velká škoda, protože v případě vícejazyných publikací se můžou jazyky jednotlivých úseků (oddílů, stran, odstavců) lišit.

Identifikace jazyka celého dokumentu se nachází v metadatech typu MODS, konkrétně v elementu /mods:modsCollection/mods:mods/mods:language/mods:languageTerm. Pro označení jazyka se používají trojpísmené zkratky podle normy ISO 639-2, popř. asi jiné normy identifikované v atributu @authority.

Ukázky

Kniha v němčině

<mods:language> 
  <mods:languageTerm authority="iso639-2b" type="code">ger</mods:languageTerm> 
</mods:language>

Německo-český slovník

<mods:language>
    <mods:languageTerm authority="iso639-2b" type="code">cze</mods:languageTerm>
</mods:language>
<mods:language>
    <mods:languageTerm authority="iso639-2b" type="code">ger</mods:languageTerm>
</mods:language>

Kniha v němčině; vznikla překladem z češtiny, český text ale neobsahuje

<mods:language>
  <mods:languageTerm authority="iso639-2b" type="code">ger</mods:languageTerm>
</mods:language>
<mods:language objectPart="translation">
  <mods:languageTerm authority="iso639-2b" type="code">cze</mods:languageTerm>
</mods:language>

Požadavek na Kramerius+

Při jazykovém zpracování využít údaj o jazyce, který se nachází v metadatech /mods:modsCollection/mods:mods/mods:language/mods:languageTerm; přebírat údaje pouze z elementů, jejichž rodičkovský prvek nemá atribut @objectPart.

Volat službu UDPipe, popř. NameTag s parametrem, který aplikuje správný jazykový model. Co dělat v případě, že publikace obsahuje jazyk, který UDPipe nebo NameTag nedokáže zpracovat? Existují 3 možnosti:

zabak commented 3 years ago

Vzhledem k tomu, jaké je zastoupení jazyků podle metadat má asi smysl použít variantu 2, snad s výjimkou nejčastějších jazyků, kde by se mohla hodit varianta 3. Které z níže uvedených jazyků nejsou těmi nástroji podporovány (a museli bychom hledat pro ně speciální nástroj)?

Čeština 197647 Němčina 26964 Angličtina 14701 Latina 6170 Francouzština 5284 Slovenština 3942 Ruština 3593 Italština 1890 Polština 1388 Maďarština 1123 Nizozemština 1079 Ukrajinština 1005 Španělština 423 Slovinština 387 Rumunština 356

stranak commented 3 years ago



23. 9. 2021 v 22:52, Petr Žabička @.***>:  Vzhledem k tomu, jaké je zastoupení jazyků podle metadat má asi smysl použít variantu 2, snad s výjimkou nejčastějších jazyků, kde by se mohla hodit varianta 3. Které z níže uvedených jazyků nejsou těmi nástroji podporovány (a museli bychom hledat pro ně speciální nástroj)?

Já jsem Borisův příspěvek pochopil tak, že by chtěl správně zpracovat hlavně smíšený text, kde se třeba v českém textu objeví německé slovo. To je ale poměrně těžké. My zatím službu identifikace jazyka nemáme, ale i když ji plánujeme, pochybuji, že toto půjde. Obecně identifikace jazyka potřebuje delší kus textu. Pokud se jazyky střídají třeba po odstavci, to už by šlo (identifikace převažujícího jazyka odstavce). Teoreticky by to OCR služba dělat mohla, souhlasím, ale zjevně to nedělá.

Pokud by daná funkce byla důležitá – takové texty se často objevují – je možno zapojit existující službu na identifikaci jazyka jako Apache Tikka apod., nebo počkat, až bude dostupná od nás jako součást API UDPipe, NameTagu aj. Ale jak říkám, nečekejte, že to bude správně identifikovat jazyk jednoho cizího slova ve větě.

Čeština 197647 Němčina 26964 Angličtina 14701 Latina 6170 Francouzština 5284 Slovenština 3942 Ruština 3593 Italština 1890 Polština 1388 Maďarština 1123 Nizozemština 1079 Ukrajinština 1005 Španělština 423 Slovinština 387 Rumunština 356

Pokud jde jen o to říci, že celý titul (celá strana ALTO) je v daném jazyce, tak UDPipe má modely pro všechny tyto jazyky a mnoho dalších. Ne všechny jsou ale ze stejně velkých trénovacích dat, takže některé mají o dost menší úspěšnost. Pouštět to ale jde i nyní a nic tomu nebrání. Jen ve volání parametrem zvolíte buď jazyk, nebo přímo konkrétní model.

NameTag má těch modelů pak podstatně méně: z výše uvedených první tři, nizozemštinu a španělštinu.

daliboris commented 3 years ago

Já jsem Borisův příspěvek pochopil tak, že by chtěl správně zpracovat hlavně smíšený text, kde se třeba v českém textu objeví německé slovo....

Nemyslel jsem to tak, neboť jsem pochopil, že v aktuálních datech Krameria v ALTO jazyk označen není, takže se musíme orientovat podle jazyka celé publikace.

Samozřejmě by bylo výhodnější získat informaci o jazyce pro jednotlivé odstavce/strany a podle toho usměrňovat obohacování o jazykovou analýzu.

Pokud by daná funkce byla důležitá – takové texty se často objevují – je možno zapojit existující službu na identifikaci jazyka jako Apache Tikka apod., nebo počkat, až bude dostupná od nás jako součást API UDPipe, NameTagu aj.

Je fakt, že jsem se zatím díval na volně dostupná díla, ale např. v novějších a zejména v odborných pracích nebude prolínání výjimkou. Např. sborníky nebo časopisy s různojazyčnými příspěvky. To už může mít na analýzu těchto typů dokumentů velký dopad, nemyslíte?

U sborníků (a předpokládám i časopisů) jsem narazil na to, že informace o jazyce jsou uvedený pro celé periodikum, nikoli pro jednotlivá vydání (ročníky, čísla), viz např. Laser 2018. Předpokládám ale, že složení jazyků v různých ročnících může být proměnlivé.