Neue Wege, gedruckte serielle Quellen zu erschließen

Originally published at: Neue Wege, gedruckte serielle Quellen zu erschließen • Verein für Computergenealogie e.V. (CompGen)

Schon mit den Verlustlisten Österreich-Ungarns haben wir eine neue Herangehensweise gewählt, um umfangreiche, gedruckte, serielle Quellen für die Familien- und Ahnenforschung zu erschließen. Anders als bei den deutschen Verlustlisten des Ersten Weltkriegs haben wir dabei nicht nur auf menschliche Arbeit gesetzt. Viele Schritte kann ein Computerprogramm gut und viel schneller erledigen. In diesem englischen Artikel habe ich vor einiger Zeit das Vorgehen bei den Verlustlisten Österreich-Ungarn bereits beschrieben.

Inhaltsverzeichnis

Grundlegender Ablauf

Ganz grundlegend teilt sich das Vorgehen von der gescannten Quelle bis hin zu den fertigen, strukturierten Daten in folgende Schritte auf:

  1. maschinelle Texterkennung (OCR)
  2. Aufbereiten der OCR-Texte
  3. manuelle Nacharbeit

Bei den Verlustlisten Österreich-Ungarns haben wir die maschinelle Texterkennung mit Hilfe von ABBYY FineReader XIX durchgeführt. Die Qualität war schon in Ordnung; mittlerweile hat sich in dem Bereich aber noch einiges getan, so dass die Erkennungsrate der Wörter nahe an 100 % ist. Die Aufbereitung der Texte erfolgte mit einem eigens entwickelten Programm, das spezielle Regel für die Struktur der österreich-ungarischen Verlustlisten einprogrammiert hat. Die manuelle Nacharbeit erfolgte schließlich im Dateneingabesystem (DES), wo fehlende Teile eines Eintrags ergänzt und falsch erkannte Wörter korrigiert wurden.

Aufbereiten der OCR-Texte

Auf das Aufbereiten der OCR-Texte möchte ich im Folgenden genauer eingehen. Dort habe ich nämlich mit Hilfe von maschinellem Lernen (machine learning) gute Fortschritte erzielt. Die Ergebnisse sind so gut, dass man auch über neue Wege der manuellen Nacharbeit nachdenken sollte.

Zwei Arbeitsschritte sind zu erledigen:

  1. Defragmentieren der Einträge
  2. Erkennung der Struktur eines Eintrags
Defragmentieren

Würde in der Quelle eine Zeile genau einem Eintrag entsprechen, so könnte der Arbeitsschritt Defragmentieren entfallen. Meist gehören jedoch mehrere Zeilen zu einem Eintrag. Noch komplizierter ist es, wenn innerhalb einer Zeile mehrere Einträge enthalten sind, die sich darüber hinaus auch noch über mehrere Zeilen erstrecken können. Mit diesem Problem habe ich mich aber noch nicht beschäftigt. Ziel des Arbeitsschrittes Defragmentieren ist es also, den Text eines Eintrags in eine Zeile zu bekommen. Bei den bisher bearbeiteten Quellen konnte man Einträge gut über die Einrückung erkennen.

Erkennen der Struktur

Als nächstes muss der Text eines Eintrags in seine Bestandteile zerlegt werden. Leider kann man nicht einfach Komma oder ähnliches als Trennzeichen verwenden – nicht alle Bestandteile sind durch Komma getrennt und manchmal macht die OCR einen Punkt daraus. Außerdem sind nicht immer alle Bestandteile für einen Eintrag enthalten und gelegentlich weicht auch die Reihenfolge der Bestandteile ab. Daher kommt an dieser Stelle maschinelles Lernen (chunking) zum Einsatz. Aus der gesamten Menge der Zeilen wird ein Teil (z.B. 100 Zeilen) genommen und von Hand markiert.

Praktisch kann man sogar mit weniger Zeilen starten, dann die Erkennung laufen lassen und das (zunächst schlechte) Ergebnis weiter verbessern. Die 100 Zeilen teilt man in Trainingsdaten und Testdaten auf; als praktikabel hat sich ein Verhältnis von 80:20 bis 90:10 erwiesen.

Mit den Trainingsdaten trainiert man ein Modell. Die Testdaten verwendet man, um zu überprüfen, wie gut die Erkennung mit dem trainierten Modell funktioniert. Ist man mit den Ergebnissen des Trainings zufrieden, kann man die gesamte Menge der Zeilen zerlegen. Natürlich kann man immer noch später weitere richtige bzw. korrigierte Einträge den Trainings- und Testdaten hinzufügen.

Konkret habe ich ein conditional random field für das chunking verwendet. Bei der Erkennung von features habe ich mich auf rudimentäre Informationen beschränkt. Da steckt bestimmt noch viel Potential drin. Dem Modell fehlt außerdem noch das “Wissen” über die innere Struktur eines Eintrags. Wenn z.B. der Dienstrang genannt wurde, dann kann ein späteres Wort nicht nochmal der Dienstrang sein.

Den Java-Code für die gesamte Aufbereitung kann man sich hier ansehen: https://gitlab.genealogy.net/jzedlitz/ocr4des Da die einzelnen Zwischenergebnisse immer wieder als Textdateien gespeichert werden, könnte man auch gut unterschiedliche Programmiersprachen für die einzelnen Schritte verwenden.

Das Vorbereiten der Trainingsdaten erfolgt bisher ganz simpel in Textdateien. Sinnvoll wäre es, die Arbeit auf mehrere Personen aufzuteilen. Fehler beim Markieren lassen sich nie ganz vermeiden. Falsche Markierungen haben aber negativen Einfluss auf das Training des Modells. Daher ist ein Abgleich mit mindestens zwei Eingaben unterschiedlicher Personen sinnvoll. Vielleicht hat jemand eine Idee, wie man das Markieren (auch Annotieren oder Taggen genannt) besser gestalten kann.

Manuelle Nacharbeit

Was macht man nun mit den automatisch strukturierten Einträgen? Ganz perfekt ist die Zerlegung nicht, bisherige Tests sind aber sehr gut. Man könnte die Daten in einer Tabelle nachbearbeiten. Durch geschicktes Sortieren und Suchen kann man bestimmt die problematischen Einträge schnell finden.

Bei den Verlustlisten Österreich-Ungarn haben wir die erkannten Einträge ins DES importiert und dort korrekturlesen bzw. ergänzen lassen. Das DES scheint mir bei der sehr guten Datenqualität nicht gut geeignet zu sein. Es ist nämlich sehr ermüdend, wenn man über 90 % der Einträge einfach nur noch “abnicken” muss, da die maschinelle Erkennung schon so gut war. Denkbar wäre eine neue Webanwendung, die einem unklare Einträge anzeigt und fragt, ob daran eine Korrektur notwendig ist. Das könnte man dann auch bequem auf einem Smartphone oder Tablet machen.

Eine andere Möglichkeit könnte es sein, das DES um einen neuen Modus zu erweitern, bei dem man gezielt zu Seiten mit unklaren Einträgen springen kann, am besten direkt zum Eintrag. Dort könnte man die Korrektur und auch gleich die Verbesserung vornehmen.

Auch hier würde ich mich über Idee freuen, wie wir die maschinell strukturierten Daten am besten nachbearbeiten können.

2 „Gefällt mir“

Moin Jesper! Dies ist eine gute Herangehensweise, man sollte allgemein mehr die Maschine arbeiten lassen. Am Institut machen wir etwas ähnliches (nicht für das Projekt Hochschullisten): Wir taggen verschiedene Teile von DDR-Patenten und lassen die Maschine lernen, was Vorname, Nachname, Firma, Stadt, Produkt etc. sind. Wir arbeiten da ausschließlich mit spacy unter Python, aber das Frontend ist hauptsächlich JS.

VG
Michael

PS: Da ist ein Tippfehler bei „Fehler beim Markieren lassen sie nie ganz vermeiden.“: sie → sich

Kannst du zu dem Vorgehen und der Software etwas mehr erzählen? Die Patente habt ihr schon als digitalen Text vorliegen, richtig? Muss dabei Anfang und Ende eines Patents erkannt werden oder ist das aufgrund des Aufbaus (ein Patent pro Dokument) schon klar?

Ist die Software zum Taggen eine Eigenentwicklung oder gibt es da fertige Opensource-Tools?

Moin!

Ja, der Text muss genau so in als Text vorliegen. Genau genommen handelt es sich nicht um den gesamten Patenttext, sondern um die eingetragenden Erfinder. Das wird in eine kleine SQL-Datenbank eingespeist, die ein Server für den Client (Browser) anzapft.

Das ist eine Eigenentwicklung basierend auf JS, die die Trainingsdaten an spacy übergibt. Ich weiß nicht, ob er da Bibliotheken in JS verwendet. Letztlich werden ja nur Indices und das dazugehörige Label gespeichert, recht simpel. Aber das ganze im Browser zu haben und nutzerfreundlich zu gestalten, das war aufwändig. Kenne mich damit leider nicht aus. Das Lernen übernimmt spacy, ohne das hätten wir das nicht geschafft.

Das ganze schaut so aus:

Die bunten Kacheln sind die Kategorien, die gelernt werden sollen. Darunter der Text, der in diesem Fall schon komplett kategorisiert wurde. Die Katengorien sind flexibel; in einem anderen Projekt gibt’s nur eine Kategorie für Dinge, die wir loswerden wollen (da geht’s um wissenschaftliche Abstracts).

Hier die resultierende json:

[
	{
		"UID": "dbacfd6f-5971-4eb5-9684-750cb95b3ee9",
		"classUID": "7311fd2c-ec76-4968-95ae-cfbc97433866",
		"clientUID": "0b913295-e846-4b4b-b343-c77c01ede816",
		"confirmerUID": "58f7a8b8-5a22-4d53-b9eb-d264a0f680bc",
		"itemUID": "c9cedc6b-236e-4fb5-a5b9-2732130d2bf7",
		"labelSource": "human",
		"projectUID": "3c207f0b-4658-491e-8f2d-f4ea8ad66510",
		"representation": {
			"end": 24,
			"indexEnd": -1,
			"indexStart": -1,
			"start": 17,
			"text": "Dresden"
		},
		"taskUID": "a28076e2-6ee6-41e4-97af-39718ce4f166"
	},
	{
		"UID": "6f8f71b0-9b2c-4fda-a347-f138e80bdcb4",
		"classUID": "105e381a-733d-471c-a0e3-c68b05e8b9bf",
		"clientUID": "0b913295-e846-4b4b-b343-c77c01ede816",
		"confirmerUID": "58f7a8b8-5a22-4d53-b9eb-d264a0f680bc",
		"itemUID": "c9cedc6b-236e-4fb5-a5b9-2732130d2bf7",
		"labelSource": "human",
		"projectUID": "3c207f0b-4658-491e-8f2d-f4ea8ad66510",
		"representation": {
			"end": 15,
			"indexEnd": -1,
			"indexStart": -1,
			"start": 9,
			"text": "Starke"
		},
		"taskUID": "a28076e2-6ee6-41e4-97af-39718ce4f166"
	},
	{
		"UID": "318dfd4f-04a1-4778-94ee-130e08119c45",
		"classUID": "e0510a1c-bd9b-49f4-b5db-bc1a08a21a9e",
		"clientUID": "0b913295-e846-4b4b-b343-c77c01ede816",
		"confirmerUID": "58f7a8b8-5a22-4d53-b9eb-d264a0f680bc",
		"itemUID": "c9cedc6b-236e-4fb5-a5b9-2732130d2bf7",
		"labelSource": "human",
		"projectUID": "3c207f0b-4658-491e-8f2d-f4ea8ad66510",
		"representation": {
			"end": 8,
			"indexEnd": -1,
			"indexStart": -1,
			"start": 4,
			"text": "Hans"
		},
		"taskUID": "a28076e2-6ee6-41e4-97af-39718ce4f166"
	},
	{
		"UID": "41d3de2d-795b-4fa4-a269-6511cdaa3ddf",
		"classUID": "e11fc26c-fabb-420c-93e8-1bf3e337b4cf",
		"clientUID": "0b913295-e846-4b4b-b343-c77c01ede816",
		"confirmerUID": "58f7a8b8-5a22-4d53-b9eb-d264a0f680bc",
		"itemUID": "c9cedc6b-236e-4fb5-a5b9-2732130d2bf7",
		"labelSource": "human",
		"projectUID": "3c207f0b-4658-491e-8f2d-f4ea8ad66510",
		"representation": {
			"end": 3,
			"indexEnd": -1,
			"indexStart": -1,
			"start": 0,
			"text": "Dr."
		},
		"taskUID": "a28076e2-6ee6-41e4-97af-39718ce4f166"
	}
]

Ich kann dir allerdings nicht sagen wo genau da jetzt das Label drin steht :smiley:

Wir erproben das ganze und machen es sukzessive nutzerfreundlicher. Z.B. siehst du hier die Suchansicht: die gab’s bis vor Kurzem nicht. Einige wichtige Dinge fehlen noch, wie z.B. dass Leute die Kategorien selbst einstellen. Das wird momentan hardgecodet. Wir überlegen auch einen zweiten Verifizierierungsschritt zu haben, oder dass derselbe Text von zwei verschiedenen Leuten beackert wird.

Ich ermutige meinen Doktoranden täglich, dies als wiss. Projekt z.B. bei SoftwareX zu publizieren und auf GitHub freizugeben. Das will er auch, aber momentan liegen so viele andere wichtigere Dinge an.

VG
Michael