Från kataloger till strukturerad data – exemplet CODICUM

Har ni inskannade katalogkort? Eller inskannade tabeller? Att extrahera texten med OCR eller HTR har länge varit ett mål för många. Extraherar man bara texten ur katalogkort och tabeller för att göra sådan information sökbar förlorar vi ju fortsatt information. Katalogkort och tabeller har ju namngivna fält som man vill kunna använda för att filtrera, validera och få statistik om innehållet. Hur gör man då?

Projektet CODICUM utforskar betydelsen av de cirka 50 000 medeltida manuskriptfragment som finns i Norden och de böcker de en gång utgjorde. Med naturvetenskapliga metoder såväl som med data driven humaniora hoppas forskare hitta nya kopplingar mellan verk och författare. Datan om fragmenten är i Norden är lika fragmenterad som fragmenten själva och i några fall måste man utgå ifrån kataloger skrivna på maskin. I denna artikel tar vi upp hur vi i ett fall gick ifrån inskannade kataloger till strukturerade data med hjälp av multimodala språkmodeller.

Utmaningen

Inom CODICUM-projektet har vi forskare som vill forska på de medeltida handskriftsfragment som finns i Danmark, men en stor del av fragmenten finns bara beskrivna i inskannade kataloger. Vi behövde inte bara få ut texten ur dessa dokument utan vi behövde göra dessa bildfiler till riktiga kalkylark som forskarna kunde använda för att bygga vidare på.

Utsnitt ur en inskannad katalogtabell.

Lösningen

Det är här multimodala AI modeller kommer in. Multimodala AI modeller är inte tränade att bara förstå text eller bild utan kan förstå flera typer av media, samtidigt. Idag kan majoriteten av flaggskeppsmodellerna ifrån teknikjättarna förstå flera typer av media. I vårt fall landade vi på Googles modell Nano Banana (Gemini 2.5 Flash Image), trots att modellen främst är avsedd att användas för bildgenerering och -redigering.

För att få ut strukturerade data, klara att importeras till Excel, gav vi Nano Banana-modellen en av bilderna som vi ville ha processade tillsammans med vår instruktion/prompt:

”Följande bild innehåller en tabell, ignorera allt utanför tabellen. Tabellen har fyra kolumner: fragment_nummer, datering, innehåll(kodex), och proveniens.

Konvertera allt i tabellen till CSV, använd , för att separera värden och “ för att avgränsa värden oavsett om det behövs eller ej.

Tabelltexten är på danska, ignorera eventuella helradsrubriker.”

Låt mig bryta ner instruktionen i dess beståndsdelar.

”Följande bild innehåller en tabell, ignorera allt utanför tabellen.”

Vi gav modellen skannade sidor som kunde innehålla kommentarer, fotnoter med mera. För att slippa beskära bilderna manuellt så fick vi skriva ut detta explicit.

”Tabellen har fyra kolumner: fragment_nummer, datering, innehåll(kodex), och proveniens.”

Endast den första sidan innehöll tabellens rubriker. För att modellen alltid skulle tolka kolumnerna likadant även när den inte sett rubrikerna fick vi skriva ut även detta explicit.

”Konvertera allt i tabellen till CSV, använd , för att separera värden och för att avgränsa värden oavsett om det behövs eller ej.”

Formatet CSV som vi valde kan skrivas på lite olika vis och detta ser till att det görs konsekvent så att det blev enkelt för oss att slå ihop resultatet till ett dokument.

”Tabelltexten är på danska, ignorera eventuella helradsrubriker”

Ibland noterade modellen att texten var på danska. Genom att skriva ut det explicit så hade varje körning samma information. Ibland förekom helradsrubriker i tabellen; dessa ville vi exkludera.

Har man inte så mycket innehåll hade man kunnat komma undan med en kortare instruktion, men vi hade hundratals bilder och kunde inte låta modellen hålla alla bilder samt in- och ut-data i minnet/kontexten. Hade vi kunnat låta den ha allt i minnet hade vi till exempel inte behövt ange kolumnerna explicit, den hade helt enkelt sett rubrikerna en gång och haft de i åtanke gång på gång.

Resultatet

Ifrån avsnittet ovan så ser CSV resultatet ut på följande vis:

fragment_nummer,datering,innehåll,proveniens
"127 (E127)","14. -16.","AN:19","\"1614\""
"128 (E128)","14. -16.","AN:19","Flensborg AR 1613-1614"
"129 (E129)","14. -16.","AN:19","Flensborg AR 1610"
"130 (E130)","14. -16.","AN:19","Flensborg AR 1612-1613"
"131 (E131)",15. -16.","AN:20","Flensborg AR, Geldhebung 1649-50"
"132 (E132)",15. -16.","AN:21","Flensborg AR 1639-40"
"133 (E133)",15. -16.","AN:21","Flensborg AR, Geldhebung 1639-40"
"134 (E134)",15. -16.","AN:21","Flensborg AR 1638-39"
"135 (E135)",14. -16.","GR:6","Tryggevælde LR, jordebog 1662"
"136 (E136)",14. -15.","GR:7","Rigens Dombog 1540-45"
"137 (E137)",14. -15.","GR:7","Sorø LR, ugekostreg. 1614-15"
"138 (E138)",14. -15.","AN:22","Sorø LR, 1614-15?"

Trots det goda resultatet finns det fallgropar att se upp för.

Tips

  • Testa er fram med olika modeller. Marknaden ändras hela tiden och de presterar olika i olika sammanhang. Den modell som vi använde för bara några månader sedan finns inte längre tillgänglig.
  • Kom ihåg att denna typ av modeller kan hallucinera och regelbundet kommer att rätta stavfel och gammalt språk. Om syftet är bevarande ska man kanske använda annan teknik. 
  • Inte alla hallucinationer är “positiva”, i vårt fall hade modellen ibland svårt att upptäcka brott i löpnummerserier och dylikt där innehållet gick på tvärs med ett regelbundet mönster.
  • Dela inte känsliga eller skyddade data med externa tjänster. 
  • Tjänster såsom Google AI Studio, Claude.ai, etc är bra för att testa men de erbjuder också lösningar som gör att utvecklare kan automatisera sådana här arbetsflöden.
  • Vi konverterade till CSV för att vi ville ha kalkylark, men inget hindrar en att välja andra format. Kanske ett format som gör det möjligt att importera till ett specifikt system eller en tjänst är mer lämpligt för er?

Kommentarer

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *

Denna webbplats använder Akismet för att minska skräppost. Lär dig om hur din kommentarsdata bearbetas.