<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>OCR | FDMLab@LABW</title><link>https://fdmlab.landesarchiv-bw.de/tag/ocr/</link><atom:link href="https://fdmlab.landesarchiv-bw.de/tag/ocr/index.xml" rel="self" type="application/rss+xml"/><description>OCR</description><generator>Wowchemy (https://wowchemy.com)</generator><language>de-de</language><lastBuildDate>Tue, 09 Aug 2022 00:00:00 +0000</lastBuildDate><image><url>https://fdmlab.landesarchiv-bw.de/media/sharing.jpg</url><title>OCR</title><link>https://fdmlab.landesarchiv-bw.de/tag/ocr/</link></image><item><title>Das FDMLab@LABW: Data-Science-Methoden und -Techniken für den Einsatz im Archiv</title><link>https://fdmlab.landesarchiv-bw.de/publication/2022-abi-technik-42/</link><pubDate>Tue, 09 Aug 2022 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/publication/2022-abi-technik-42/</guid><description/></item><item><title>Verborgene Datenschätze heben: Das FDMLab experimentiert mit KI im Archiv</title><link>https://fdmlab.landesarchiv-bw.de/publication/2022-archivnachrichten-64/</link><pubDate>Thu, 28 Apr 2022 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/publication/2022-archivnachrichten-64/</guid><description/></item><item><title>Ist NER robust gegenüber OCR Fehlern?</title><link>https://fdmlab.landesarchiv-bw.de/post/2021-08-ner-robust-bei-ocr-fehlern/</link><pubDate>Tue, 03 Aug 2021 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/post/2021-08-ner-robust-bei-ocr-fehlern/</guid><description>&lt;p>Wir haben schon mehrfach die Behauptung gehört, dass &lt;em>Named Entity Recognition&lt;/em> (NER) robust gegenüber OCR Fehlern ist.
Daher werden wir in diesem Beitrag die &lt;em>Named Entity Recognition&lt;/em> anhand einiger Beispiele genauer betrachten.&lt;/p>
&lt;p>NER ist ein intuitiv einfach wirkendes Verfahren, um Schlagwörter aus Texten zu extrahieren und zu kategorisieren.
Dabei sind die Fähigkeiten von NER abhängig von den Trainingsdaten und der Art, wie das Modell trainiert wurde.&lt;/p>
&lt;p>Wir verwenden für unsere Beispiele &lt;a href="https://spacy.io/" target="_blank" rel="noopener">spaCy&lt;/a> und &lt;a href="https://github.com/flairNLP/flair/" target="_blank" rel="noopener">flairNLP&lt;/a> in Kombination mit einem deutschsprachigen Sprachmodell.
Diese Modelle können die vier Klassen &lt;em>Personen&lt;/em> (PER), &lt;em>Orte&lt;/em> (LOC), &lt;em>Organisationen&lt;/em> (ORG) und &lt;em>Sonstiges&lt;/em> (MISC) unterscheiden.
Das ist recht wenig im Vergleich zu Modellen für die englische Sprache, wo zum Beispiel mit den auf &lt;a href="https://catalog.ldc.upenn.edu/LDC2013T19" target="_blank" rel="noopener">OntoNotes&lt;/a> basierten Modellen 18 Klassen unterschieden werden können. Das liegt an der fehlenden Verfügbarkeit von öffentlichen Trainingsdaten für die deutsche Sprache.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
Die Visualisierungen in diesem Artikel wurden mit &lt;em>displaCy&lt;/em> erstellt.
Mit der &lt;a href="https://explosion.ai/demos/displacy-ent" target="_blank" rel="noopener">Online Demo von displaCy&lt;/a> können die Beispiele selbst nachvollzogen werden.
&lt;/div>
&lt;/div>
&lt;h2 id="beispiele-mit-spacy">Beispiele mit spaCy&lt;/h2>
&lt;p>Zum Trainieren der Sprachmodelle liegen menschlich und/oder heuristisch annotierte Beispiele/Sätze vor.
&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
arbeitet bei der
&lt;mark class="entity" style="background: #f4cae4; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
britischen
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">MISC&lt;/span>
&lt;/mark>
&lt;mark class="entity" style="background: #cbd5e8; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Example Inc
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">ORG&lt;/span>
&lt;/mark>
in
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Paris
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/p>
&lt;p>Anhand solcher Beispiele kann ein Sprachmodell lernen, was ein &lt;em>Named Entity&lt;/em> ist und wie es kategorisiert werden soll.
Mit genügend Beispielen lernt das Sprachmodell, dass es sich bei &lt;em>Adam&lt;/em> und eine &lt;em>Person&lt;/em> handelt und bei &lt;em>Paris&lt;/em> um einen &lt;em>Ort&lt;/em>. &lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>&lt;/p>
&lt;p>Aber das ist noch nicht alles. Nehmen wir einen einfachen Beispielsatz und &lt;em>mutieren&lt;/em> einzelne Buchstaben, wie es bei OCR Fehlern vorkommt.
Anschließend wenden wir die NER Komponenten von spaCy auf die Beispiele an.&lt;/p>
&lt;ol>
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
lebt in
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Paris
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
lebt i&lt;strong>m&lt;/strong>
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Paris
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
lebt in
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Par&lt;strong>e&lt;/strong>s
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
l&lt;strong>i&lt;/strong>bt in
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Par&lt;strong>e&lt;/strong>s
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Es scheint so, als ob NER &amp;ldquo;robust&amp;rdquo; gegenüber diesen Mutationen ist.
Aber bevor wir uns über dieses stabile Verfahren freuen, schauen wir uns ein weiteres Beispiel an.&lt;/p>
&lt;ol start="5">
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
lebt i&lt;strong>m&lt;/strong> Par&lt;strong>e&lt;/strong>s.&lt;/div>&lt;/li>
&lt;/ol>
&lt;h3 id="was-ist-hier-passiert">Was ist hier passiert?&lt;/h3>
&lt;p>Das Sprachmodell von spaCy hat vermutlich gelernt, dass es sich bei &lt;em>Paris&lt;/em> um ein &lt;em>Named Entity&lt;/em> der Kategorie &lt;em>Ort&lt;/em> handelt und markiert es entsprechend in den Beispielen 1 und 2.&lt;/p>
&lt;p>Es hat möglicherweise auch gelernt, dass zum Beispiel die Konstruktion &lt;strong>&amp;ldquo;in ABC&amp;rdquo;&lt;/strong> einen &lt;em>Ort&lt;/em> kennzeichnet und markiert entsprechend &lt;em>Par&lt;strong>e&lt;/strong>s&lt;/em> in den Beispielen 3 und 4.&lt;/p>
&lt;p>Im Beispiel 5 haben wir zwei Fehler kombiniert, wodurch das Sprachmodell weder &lt;em>Paris&lt;/em>, noch die Konstruktion &lt;strong>&amp;ldquo;in ABC&amp;rdquo;&lt;/strong> erkennen konnte. &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>&lt;/p>
&lt;p>Interessant dabei ist, dass das Sprachmodell basierend auf der Satzkonstruktion auch die Beispiele 6 und 7 erkennt.&lt;/p>
&lt;ol start="6">
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
lebt in
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Bxyueqty
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Xoyietzqgf
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
lebt in
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Bxyueqty
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Hier können wir davon ausgehen, dass die Begriffe &lt;em>Xoyietzqgf&lt;/em> und &lt;em>Bxyueqty&lt;/em> nicht in den Trainingsdaten vorkommen.
Die Bestimmung der &lt;em>Entities&lt;/em> erfolgt aus der Satzkonstruktion und zeigt die Übertragbarkeit der gelernten Inhalte auf unbekannte Beispiele.&lt;/p>
&lt;h2 id="vergleich-mit-flairnlp">Vergleich mit flairNLP&lt;/h2>
&lt;p>Nicht alle Sprachmodelle lernen die gleichen Regeln aus den Trainingsdaten.
Beispielsweise hat flairNLP ein Problem mit unserem Beispiel 4 (8), erkennt Beispiel 5 (9) aber korrekt.&lt;/p>
&lt;ol start="8">
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
l&lt;strong>i&lt;/strong>bt in Par&lt;strong>e&lt;/strong>s.&lt;/div>&lt;/li>
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
lebt i&lt;strong>m&lt;/strong>
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Par&lt;strong>e&lt;/strong>s
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Vielleicht hat das Sprachmodell von flairNLP gelernt, dass die Konstruktion &lt;strong>&amp;ldquo;lebt &amp;hellip; ABC&amp;rdquo;&lt;/strong> einen &lt;em>Ort&lt;/em> kennzeichnet. &lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup>&lt;/p>
&lt;h2 id="ner-und-mehrdeutigkeit">NER und Mehrdeutigkeit&lt;/h2>
&lt;p>Ein interessantes Feld ist auch das Auflösung von Mehrdeutigkeiten.
So ist für den menschlichen Leser klar, dass es sich bei &lt;em>Paris&lt;/em> in Beispiel 10 um eine &lt;em>Person&lt;/em> handelt.
SpaCy kennzeichnet &lt;em>Paris&lt;/em> in Beispiel 10 als &lt;em>Ort&lt;/em>.&lt;/p>
&lt;ol start="10">
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
liebt
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Paris
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
. Sie ist wirklich nett.&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Da die meisten Sprachmodelle auf einzelne Sätze trainiert werden, fehlt ihnen die Möglichkeit den umgebenden Kontext mit in Betracht zu ziehen.&lt;/p>
&lt;p>Für flairNLP gibt es die &lt;a href="https://arxiv.org/abs/2011.06993" target="_blank" rel="noopener">FLERT Modelle&lt;/a>, die mehrere Sätze gleichzeitig betrachten und daher den Kontext mit in Betracht ziehen können.
Beispiel 11 wurde mit einem FLERT Model markiert.&lt;/p>
&lt;ol start="11">
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
liebt
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Paris
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
. Sie ist wirklich nett.&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Überraschenderweise wurde hier auch &lt;em>Paris&lt;/em> als &lt;em>Ort&lt;/em> markiert.
Laut &lt;a href="https://github.com/flairNLP/flair/issues/2193" target="_blank" rel="noopener">flairNLP/flair#2193&lt;/a> wurde für Version 0.8.0 das Sprachmodell neu trainiert und erkennt &lt;em>Paris&lt;/em> als &lt;em>Ort&lt;/em>, wo es bei früheren Versionen &lt;em>Paris&lt;/em> noch korrekt als &lt;em>Person&lt;/em> erkannte.&lt;/p>
&lt;p>Versuchen wir es mit einem anderen Beispiel (12 und 13), was zwar nicht zu unserem &lt;em>Paris&lt;/em> Beispiel passt, jedoch die Fähigkeiten der FLERT Modelle zeigt:&lt;/p>
&lt;ol start="12">
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
liebt
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Buxtehude
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">LOC&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
liebt
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Buxtehude
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
.
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
und
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Buxtehude
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
sind verheiratet.&lt;/div>&lt;/li>
&lt;/ol>
&lt;h2 id="fazit">Fazit&lt;/h2>
&lt;p>Es ist nicht transparent, welche exakten Regeln ein Sprachmodell aus den Trainingsdaten ableitet.
Es können einzelne Begriffe sein, die als &lt;em>Named Entity&lt;/em> erkannt werden, oder eine bestimmte Wortkonstruktion (&lt;strong>&amp;ldquo;in ABC&amp;rdquo;&lt;/strong>).
Manchmal wird auch die Struktur und die Grammatik des Satzes beim Training und der Anwendung mit berücksichtigt (Beispiel 14).
Oder anstatt auf einzelnen Sätzen wird auf größeren Textabschnitten trainiert, wie es bei den FLERT Modellen der Fall ist.&lt;/p>
&lt;ol start="14">
&lt;li>&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="de" id="431aaf8db8fa4f83bbf5bd08f7c179aa-0" class="displacy" width="750" height="224.5" direction="ltr" style="max-width: none; height: 224.5px; color: #000000; background: #ffffff; font-family: Arial; direction: ltr">
&lt;text class="displacy-token" fill="currentColor" text-anchor="middle" y="134.5">
&lt;tspan class="displacy-word" fill="currentColor" x="50">Adam&lt;/tspan>
&lt;tspan class="displacy-tag" dy="2em" fill="currentColor" x="50">PROPN&lt;/tspan>
&lt;/text>
&lt;text class="displacy-token" fill="currentColor" text-anchor="middle" y="134.5">
&lt;tspan class="displacy-word" fill="currentColor" x="225">lebt&lt;/tspan>
&lt;tspan class="displacy-tag" dy="2em" fill="currentColor" x="225">VERB&lt;/tspan>
&lt;/text>
&lt;text class="displacy-token" fill="currentColor" text-anchor="middle" y="134.5">
&lt;tspan class="displacy-word" fill="currentColor" x="400">in&lt;/tspan>
&lt;tspan class="displacy-tag" dy="2em" fill="currentColor" x="400">ADP&lt;/tspan>
&lt;/text>
&lt;text class="displacy-token" fill="currentColor" text-anchor="middle" y="134.5">
&lt;tspan class="displacy-word" fill="currentColor" x="575">Paris.&lt;/tspan>
&lt;tspan class="displacy-tag" dy="2em" fill="currentColor" x="575">PROPN&lt;/tspan>
&lt;/text>
&lt;g class="displacy-arrow">
&lt;path class="displacy-arc" id="arrow-431aaf8db8fa4f83bbf5bd08f7c179aa-0-0" stroke-width="2px" d="M70,89.5 C70,2.0 225.0,2.0 225.0,89.5" fill="none" stroke="currentColor"/>
&lt;text dy="1.25em" style="font-size: 0.8em; letter-spacing: 1px">
&lt;textPath xlink:href="#arrow-431aaf8db8fa4f83bbf5bd08f7c179aa-0-0" class="displacy-label" startOffset="50%" side="left" fill="currentColor" text-anchor="middle">sb&lt;/textPath>
&lt;/text>
&lt;path class="displacy-arrowhead" d="M70,91.5 L62,79.5 78,79.5" fill="currentColor"/>
&lt;/g>
&lt;g class="displacy-arrow">
&lt;path class="displacy-arc" id="arrow-431aaf8db8fa4f83bbf5bd08f7c179aa-0-1" stroke-width="2px" d="M245,89.5 C245,2.0 400.0,2.0 400.0,89.5" fill="none" stroke="currentColor"/>
&lt;text dy="1.25em" style="font-size: 0.8em; letter-spacing: 1px">
&lt;textPath xlink:href="#arrow-431aaf8db8fa4f83bbf5bd08f7c179aa-0-1" class="displacy-label" startOffset="50%" side="left" fill="currentColor" text-anchor="middle">mo&lt;/textPath>
&lt;/text>
&lt;path class="displacy-arrowhead" d="M400.0,91.5 L408.0,79.5 392.0,79.5" fill="currentColor"/>
&lt;/g>
&lt;g class="displacy-arrow">
&lt;path class="displacy-arc" id="arrow-431aaf8db8fa4f83bbf5bd08f7c179aa-0-2" stroke-width="2px" d="M420,89.5 C420,2.0 575.0,2.0 575.0,89.5" fill="none" stroke="currentColor"/>
&lt;text dy="1.25em" style="font-size: 0.8em; letter-spacing: 1px">
&lt;textPath xlink:href="#arrow-431aaf8db8fa4f83bbf5bd08f7c179aa-0-2" class="displacy-label" startOffset="50%" side="left" fill="currentColor" text-anchor="middle">nk&lt;/textPath>
&lt;/text>
&lt;path class="displacy-arrowhead" d="M575.0,91.5 L583.0,79.5 567.0,79.5" fill="currentColor"/>
&lt;/g>
&lt;/svg>&lt;/li>
&lt;/ol>
&lt;p>Die vermeintliche Robustheit von NER gegenüber OCR Fehlern kommt von der Fähigkeit der Sprachmodelle auf Basis der Satzstruktur auch unbekannte oder mit OCR Fehlern versehene &lt;em>Entities&lt;/em> zu erkennen.
OCR Fehler im &lt;em>Entity&lt;/em> sind also nicht problematisch.
Ein simpler OCR Fehler &lt;strong>außerhalb&lt;/strong> des &lt;em>Entity&lt;/em>, wie ein fehlendes Leerzeichen, kann abhängig vom Sprachmodell die Erkennung massiv stören (Beispiel 15).&lt;/p>
&lt;ol start="15">
&lt;li>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
Adam lebtin Paris
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">PER&lt;/span>
&lt;/mark>
.&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Von daher ist für uns die Aussage &amp;ldquo;NER ist robust gegenüber OCR Fehlern&amp;rdquo; zu unspezifisch.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Auf die Mehrdeutigkeit des Begriffs &lt;em>Paris&lt;/em>, welches auch als Vorname verwendet wird, gehen wir am Ende des Artikels ein.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Für diese Beispiele haben wir das Sprachmodell &lt;a href="https://spacy.io/models/de#de_core_news_sm" target="_blank" rel="noopener">de-core-news-sm&lt;/a> in Version 3.0.0 verwendet. In Version 2.3.0 wurde Beispiel 5 noch richtig erkannt.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>Für diese Beispiele haben wir das Sprachmodell &lt;a href="https://huggingface.co/flair/ner-german-large" target="_blank" rel="noopener">ner-german-large&lt;/a> in Version 0.8.0 verwendet.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Analyse tabellarischer Layoutstrukturen mit Transkribus</title><link>https://fdmlab.landesarchiv-bw.de/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/</link><pubDate>Tue, 13 Jul 2021 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/</guid><description>&lt;p>Eine gute Layouterkennung ist die Voraussetzung für eine gute OCR- oder HTR-Erfassung von Dokumenten.
Anhand von Stammrollen aus dem LABW testen wir, welche Möglichkeiten Transkribus bei der Layouterkennung komplexer Tabellenstrukturen bietet.&lt;/p>
&lt;h2 id="unser-ausgangsmaterial-stammrollen-mit-tabellen-layout">Unser Ausgangsmaterial: Stammrollen mit Tabellen-Layout&lt;/h2>
&lt;p>Die militärischen Bestände des LABW aus dem Zeitraum zwischen 1871 bis ca. 1920 beinhalten u.a. umfangreiche militärische Personalunterlagen. Dazu gehören die sogenannten Kriegs- und Friedensstammrollen von verschiedenen Regimentern. Diese listen alle Angehörigen einer Einheit mit Daten zur Person, den Angehörigen, mitgemachten Gefechten, Auszeichnungen etc. auf. Die Stammrollen sind für viele unterschiedliche Forschungsfragen interessant und insbesondere auch für Genealogen eine gerne genutzte historische Quelle.&lt;/p>
&lt;p>Die Stammrollen sind bereits vollständig digitalisiert und über das Online Findmittelsystem (OLF) des LABW einsehbar.&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> Durch eine zusätzliche Volltexterfassung der Namen und Geburtsdaten aus den Stammrollen könnte man schnell und bequem nach einer bestimmten Person suchen. Das wäre ein echter Mehrwert für genealogische Forschungen! Wir testen, wie wir dies mit Transkribus umsetzen können.&lt;/p>
&lt;p>Hier zuerst ein Blick auf das Layout unserer Stammrollen:&lt;/p>
&lt;figure id="figure-tabellarisches-layout-der-stammrollen-quelle-landesarchiv-baden-württemberg-hauptstaatsarchiv-stuttgart-m-439-band-270">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Tabellarisches Layout der Stammrollen (Quelle: Landesarchiv Baden-Württemberg, Hauptstaatsarchiv Stuttgart M 439 Band 270)." srcset="
/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/HStA_M439_Bd270_0002_hu3eb8821a4bffdb5c79db1d340978c84b_1441863_9834594ad07ec8e3364b9788615f812e.webp 400w,
/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/HStA_M439_Bd270_0002_hu3eb8821a4bffdb5c79db1d340978c84b_1441863_4d3b465e60c1d84f0682c0c46280b672.webp 760w,
/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/HStA_M439_Bd270_0002_hu3eb8821a4bffdb5c79db1d340978c84b_1441863_1200x1200_fit_q75_h2_lanczos.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/HStA_M439_Bd270_0002_hu3eb8821a4bffdb5c79db1d340978c84b_1441863_9834594ad07ec8e3364b9788615f812e.webp"
width="760"
height="606"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Tabellarisches Layout der Stammrollen (Quelle: Landesarchiv Baden-Württemberg, Hauptstaatsarchiv Stuttgart M 439 Band 270).
&lt;/figcaption>&lt;/figure>
&lt;h2 id="layouterkennung-als-basis-für-die-transkription">Layouterkennung als Basis für die Transkription&lt;/h2>
&lt;p>Die Layoutanalyse ist in Transkribus der erste Schritt, bevor man ein Dokument manuell oder automatisiert transkribieren kann. Bei der Layoutanalyse wird ein Dokument in Textregionen, Zeilen und Basislinien unterteilt. Die Texterkennung arbeitet auf der Basis dieser Segmentierung. Ausführliche Informationen, wie man die automatische Layoutanalyse durchführen kann, gibt es auf der Transkribus-Website.&lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>&lt;/p>
&lt;p>Wir testen, wie gut die automatische Layoutanalyse bei unseren Stammrollen funktioniert.&lt;/p>
&lt;h2 id="ergebnisse-der-automatischen-layoutanalyse">Ergebnisse der automatischen Layoutanalyse&lt;/h2>
&lt;p>Schon ein kurzer Blick auf eine Seite aus unseren Stammrollen zeigt, dass wir es mit einem komplexen Layout zu tun haben: Unsere Tabelle besitzt 15 Spalten mit Spaltenüberschriften. Auf einer (Doppel-)Seite gibt es in der Regel zwei Einträge, sprich Zeilen. Die Zellen innerhalb einer Zeile sind teilweise noch einmal in zwei kleinere Zellen unterteilt. Was wir als Menschen sofort verstehen, stellt an Transkribus hohe Anforderungen bei der richtigen Erkennung von Textabschnitten und Zeilen und der Festlegung der Lesereihenfolge der einzelnen Elemente. Wir haben Transkribus eine harte Nuss zu knacken gegeben!&lt;/p>
&lt;p>Die Ergebnisse der automatischen Layoutanalyse sind dementsprechend fehlerhaft. Der Großteil des Textes wurde als Textzeilen erkannt. Vor allem in den Spaltenüberschriften, vereinzelt auch in den Tabelleneinträgen, gibt es aber auch Textzeilen, die nicht als solche erkannt wurden.&lt;/p>
&lt;p>Bei der Erkennung der Textregionen scheint Transkribus sich insgesamt an den Tabellenspalten zu orientieren. Dies hat jedoch nicht zufriedenstellend funktioniert. Während einerseits mehrere Tabellenspalten in einer Textregion zusammengefasst wurden, sind andere Spalten in mehrere Regionen aufgeteilt worden, die sich teilweise auch überlappen.&lt;/p>
&lt;p>Auch, wenn das Layout der Tabelle auf jeder Seite dasselbe ist, werden bei jeder Seite unterschiedliche Textregionen erkannt, da die Textverteilung sich auf den Seiten unterscheidet. Das Ergebnis der automatischen Layoutanalyse ist daher bei gleichen Voreinstellung auf jeder Seite etwas anders.&lt;/p>
&lt;p>Die fehlerhaften Textregionen stellen deshalb ein Problem dar, weil dadurch auch die Lesereihenfolge der Zeilen durcheinandergerät.&lt;/p>
&lt;figure id="figure-ergebnis-der-automatischen-layoutanalyse-von-transkribus">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="HStA_M439_Bd270_0002_auto_analyse.jpg" alt="Ergebnis der automatischen Layoutanalyse von Transkribus." loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Ergebnis der automatischen Layoutanalyse von Transkribus.
&lt;/figcaption>&lt;/figure>
&lt;h2 id="manuelle-bearbeitungsmöglichkeiten">Manuelle Bearbeitungsmöglichkeiten&lt;/h2>
&lt;p>Um zu einem brauchbaren Ergebnis zu kommen, sind manuelle Nacharbeiten erforderlich. Transkribus stellt uns dafür umfangreiche Möglichkeiten zur Verfügung. Eine detaillierte Anleitung findet sich ebenfalls auf der Transkribus-Website.&lt;sup id="fnref:3">&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref">3&lt;/a>&lt;/sup> Zuerst müssen die Textregionen korrigiert werden. Sinnvoll ist es hier, jeweils eine Tabellenzelle als eine Textregion zu kennzeichnen.&lt;sup id="fnref:4">&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref">4&lt;/a>&lt;/sup> Nachdem die Lesereihenfolge der Textregionen berichtigt wurde, werden auch die Basislinien innerhalb der Textregionen manuell korrigiert, wo dies nötig ist. Abschließend wird die Lesereihenfolge der Zeilen angegeben.&lt;/p>
&lt;figure id="figure-manuelle-nachbearbeitung-der-layouterkennung">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Manuelle Nachbearbeitung der Layouterkennung." srcset="
/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/HStA_M439_Bd270_0002_manuell_hucfcc503a372b8c67b8c4d03d635ce251_1110370_fe7e1ac24797277b36727d86eca1deea.webp 400w,
/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/HStA_M439_Bd270_0002_manuell_hucfcc503a372b8c67b8c4d03d635ce251_1110370_bbfbafe7d61d64180e4f2a5f9002ea37.webp 760w,
/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/HStA_M439_Bd270_0002_manuell_hucfcc503a372b8c67b8c4d03d635ce251_1110370_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/post/2021-07-analyse-tabellarischer-layoutstrukturen-mit-transkribus/HStA_M439_Bd270_0002_manuell_hucfcc503a372b8c67b8c4d03d635ce251_1110370_fe7e1ac24797277b36727d86eca1deea.webp"
width="760"
height="596"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption data-pre="Abbildung&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered">
Manuelle Nachbearbeitung der Layouterkennung.
&lt;/figcaption>&lt;/figure>
&lt;div class="alert alert-note">
&lt;div>
&lt;p>&lt;strong>Tipps für manuelle Layoutkorrekturen&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Um fehlerhafte Textzeilen zu korrigieren, müssen Sie die Basislinien korrigieren, an denen sich die Texterkennung orientiert. Die Zeilen werden dann automatisch ebenfalls angepasst.&lt;/li>
&lt;li>Wenn Sie Text aus einer Textregion auf zwei unterschiedliche Textregionen aufteilen möchten, nutzen Sie das Werkzeug zum Zerschneiden von Elementen (gekennzeichnet durch das Scherensymbol). Nur so werden die Zeilen korrekt zu zwei Regionen zugeordnet.&lt;/li>
&lt;li>Achten Sie darauf, dass sich Textregionen nicht überlappen.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;h2 id="fazit">Fazit&lt;/h2>
&lt;p>Die automatische Layoutanalyse von Transkribus, die bei einfachen Layouts durchaus überzeugend funktionieren mag, kann ein komplexes Tabellenlayout wie das unserer Stammrollen nicht mit ausreichender Qualität verarbeiten. Transkribus bietet uns umfangreiche Möglichkeiten, die Layouterkennung manuell nachzubearbeiten. So haben wir am Ende unseres Tests die Seite aus der Stammrolle zufriedenstellend segmentiert. Die Nacharbeiten nehmen jedoch viel Zeit in Anspruch, sodass dieses Vorgehen nicht geeignet ist, um unseren gesamten Bestand an Kriegs- und Friedensstammrollen im LABW zu bearbeiten.&lt;/p>
&lt;p>Um einen effizienteren Weg zu finden, unser Tabellenlayout zu analysieren, werden wir als nächstes das Tool &lt;em>P2PaLA&lt;/em> testen, welches in Transkribus integriert und ab Version 1.15.1 für alle Transkribus User freigeschaltet ist. Mit &lt;em>P2PaLA&lt;/em> lässt sich ein spezifisches Strukturmodell für die Layouterkennung einer Dokumentensammlung trainieren. Auf diese Weise möchten wir einen höheren Automatisierungsgrad bei der Erkennung unseres Stammrollen-Tabellenlayouts erreichen. Wir werden in einem Folgebeitrag davon berichten.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Die Bestände des Hauptstaatsarchivs Stuttgart &lt;a href="https://www2.landesarchiv-bw.de/ofs21/olb/struktur.php?archiv=1&amp;amp;klassi=1.12.002.002&amp;amp;anzeigeKlassi=1.12.002.002&amp;amp;zeigehauptframe=1" target="_blank" rel="noopener">HStA M 430/1 - M631&lt;/a> in OLF.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Anleitung zur &lt;a href="https://readcoop.eu/de/transkribus/wiki/layout-analysis/" target="_blank" rel="noopener">Layoutanalyse mit Transkribus&lt;/a> im Transkribus-Ressourcenzentreum der READ COOP.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:3">
&lt;p>Anleitung &lt;a href="https://readcoop.eu/de/transkribus/anleitungen/dokumente-transkribieren/" target="_blank" rel="noopener">„Wie man Dokumente mit Transkribus transkribiert“&lt;/a> im Transkribus-Ressourcenzentreum der READ COOP.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:4">
&lt;p>Grundsätzlich kennt das von Transkribus verwendete &lt;a href="https://github.com/PRImA-Research-Lab/PAGE-XML" target="_blank" rel="noopener">PAGE XML&lt;/a> Format auch Tabellen. Jedoch werden rekursive Strukturen (Tabelle -&amp;gt; Zelle -&amp;gt; Text) unserer Erfahrung nach nicht von allen Werkzeugen zuverlässig unterstützt.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>Einträge analoger Findbüchern automatisiert in Datenbanken übernehmen - Reguläre Ausdrücke</title><link>https://fdmlab.landesarchiv-bw.de/post/2021-07-analoge-findbuecher-in-db-uebernehmen/</link><pubDate>Tue, 06 Jul 2021 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/post/2021-07-analoge-findbuecher-in-db-uebernehmen/</guid><description>&lt;p>Im FDMLab haben wir einige analoge Findbücher digitalisiert und die Einträge automatisiert in unsere Datenbanksysteme übernommen.
Dieser Blogbeitrag konzentriert sich auf die Extraktion der Informationen mit regulären Ausdrücken.&lt;/p>
&lt;p>Dabei gehen wir recht technisch vor und zeigen Codebeispiele in Python und mit &lt;a href="https://spacy.io/" target="_blank" rel="noopener">spaCy 3.0&lt;/a>.&lt;/p>
&lt;p>Bei spaCy handelt es sich um ein Python basiertes Framework für Natural Language Processing (NLP).
Wir konzentrieren uns hier auf einen kleinen Teil des Frameworks, nämlich das &lt;a href="https://spacy.io/usage/rule-based-matching" target="_blank" rel="noopener">regelbasierte Matching&lt;/a>.&lt;/p>
&lt;details class="toc-inpage d-print-none " open>
&lt;summary class="font-weight-bold">Inhaltsverzeichnis&lt;/summary>
&lt;nav id="TableOfContents">
&lt;ul>
&lt;li>&lt;a href="#einfache-reguläre-ausdrücke">Einfache reguläre Ausdrücke&lt;/a>&lt;/li>
&lt;li>&lt;a href="#mehrere-reguläre-ausdrücke">Mehrere reguläre Ausdrücke&lt;/a>&lt;/li>
&lt;li>&lt;a href="#regelbasiertes-matching-mit-spacy">Regelbasiertes Matching mit spaCy&lt;/a>&lt;/li>
&lt;li>&lt;a href="#erweitertes-regelbasiertes-matching-mit-spacy">Erweitertes regelbasiertes Matching mit spaCy&lt;/a>&lt;/li>
&lt;li>&lt;a href="#kombiniere-reguläre-ausdrücke-mit-spacy">Kombiniere reguläre Ausdrücke mit spaCy&lt;/a>&lt;/li>
&lt;li>&lt;a href="#fazit">Fazit&lt;/a>&lt;/li>
&lt;/ul>
&lt;/nav>
&lt;/details>
&lt;p>Folgende &lt;code>imports&lt;/code> sind notwendig um die Code-Beispiele auszuführen.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">re&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">spacy&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">displacy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">spacy.lang.de&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">German&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">spacy.matcher&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Matcher&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">spacy.tokens&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Span&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="alert alert-note">
&lt;div>
Wir versuchen die (Code-)Beispiele so knapp und verständlich wie möglich zu halten.
Daher fehlen einige Details (Fehlerbehandlung, Spezialfälle, &amp;hellip;).
&lt;/div>
&lt;/div>
&lt;h2 id="einfache-reguläre-ausdrücke">Einfache reguläre Ausdrücke&lt;/h2>
&lt;p>Bei unseren Projekten zur Findbuchextraktion haben wir häufig die Situation, dass wir die Inhalte eines Textblocks auf mehrere Felder in der Zieldatenbank mappen.&lt;/p>
&lt;p>Hier haben wir ein markiertes Beispiel für den Beginn eines Findbucheintrages.&lt;/p>
&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">NR&lt;/span>
&lt;/mark>
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
HSTA L13 42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SIG&lt;/span>
&lt;/mark>
(
&lt;mark class="entity" style="background: #cbd5e8; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
A9594
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">ASIG&lt;/span>
&lt;/mark>
)
&lt;mark class="entity" style="background: #e6f5c9; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
1798
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">DATUM&lt;/span>
&lt;/mark>
&lt;/div>
&lt;p>Hier sind die Findbuchnummer (NR), die Bestellsignatur (SIG), die Altsignatur (ASIG), die Vorsignatur (VSIG) und ein zugehöriges Datum (DATUM) der Archivalie in einer Zeile und sollen als einzelne Datenfelder extrahiert werden.&lt;/p>
&lt;p>Mit einem regulären Ausdruck und so genannten &amp;ldquo;named capturing groups&amp;rdquo; lassen sich die benötigten Datenschnippsel (Entities) extrahieren und direkt für die Datenbank weiterverarbeiten.&lt;/p>
&lt;p>&lt;strong>Code-Beispiel:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">text1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;42 HSTA L13 42 (A9594) 1798&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">pattern&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;^(?P&amp;lt;NR&amp;gt;\d+)\s+(?P&amp;lt;SIG&amp;gt;[^\(]+)\s+\((?P&amp;lt;ASIG&amp;gt;.+)\)\s+(?P&amp;lt;DATUM&amp;gt;\d+)$&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">groupdict&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Ausgabe:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;NR&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;42&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;SIG&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;HSTA L13 42&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;ASIG&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;A9594&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;DATUM&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;1798&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Aber:&lt;/strong> zumindest in den von uns bearbeiteten Findbüchern blieb das Schema nie so konsistent, als dass die Daten mit nur einem verständlichen regulären Ausdruck extrahiert werden konnten.
Hier eine weitere Zeile aus einem Findbucheintrag aus dem gleichen Band.&lt;/p>
&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
113a
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">NR&lt;/span>
&lt;/mark>
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
HSTA L13 50-55c
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SIG&lt;/span>
&lt;/mark>
(Vorsignatur:
&lt;mark class="entity" style="background: #f4cae4; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
GLAK B33 F 7 13b
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">VSIG&lt;/span>
&lt;/mark>
,
&lt;mark class="entity" style="background: #cbd5e8; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
T1234c
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">ASIG&lt;/span>
&lt;/mark>
)
&lt;mark class="entity" style="background: #e6f5c9; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
ca. 18. Jhd.
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">DATUM&lt;/span>
&lt;/mark>
&lt;/div>
&lt;p>Hier funktioniert der oben gezeigte reguläre Ausdruck nicht mehr.&lt;/p>
&lt;p>&lt;strong>Code-Beispiel:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">text2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;113a HSTA L13 50-55c (Vorsignatur: GLAK B33 F 7 13b, T1234c) ca. 18. Jhd.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">match&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pattern&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Ausgabe:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Der Einsatz von einzelnen regulären Ausdrücken führte bei uns zu unübersichtlichen und nicht zu pflegenden Konstrukten.
Daher sind wir dazu übergegangen, die unterschiedlichen Bestandteile einzeln und mit mehreren regulären Ausdrücken zu erfassen.&lt;/p>
&lt;h2 id="mehrere-reguläre-ausdrücke">Mehrere reguläre Ausdrücke&lt;/h2>
&lt;p>Anstatt alle Bestandteile in einem regulären Ausdruck zu erfassen,
kann man einen Text auch mit mehreren getrennten regulären Ausdrücken bearbeiten.&lt;/p>
&lt;p>&lt;strong>Code-Beispiel:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">asig_regex&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;[A-Z]\d+[a-z]?&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">entity_definitions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;NR&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;^\d+[a-z]?&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;SIG&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;HSTA.+(?=\()&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;ASIG&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="sa">fr&lt;/span>&lt;span class="s2">&amp;#34;(?&amp;lt;=\()&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">asig_regex&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">(?=\))&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sa">fr&lt;/span>&lt;span class="s2">&amp;#34;(?&amp;lt;=, )&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">asig_regex&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">(?=\))&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;VSIG&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;(?&amp;lt;=Vorsignatur: ).+(?=,)&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;DATUM&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;(?&amp;lt;=\) ).+$&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">text1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text2&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">found&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">regex_list&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">entity_definitions&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">regex&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">regex_list&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">match&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">regex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">match&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">found&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">group&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">found&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Ausgabe:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;NR&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;42&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;SIG&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;HSTA L13 42 &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;ASIG&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;A9594&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;DATUM&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;1798&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;NR&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;113a&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;SIG&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;HSTA L13 50-55c &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;ASIG&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;T1234c&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;VSIG&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;GLAK B33 F 7 13b&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;DATUM&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;ca. 18. Jhd.&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Diese Methode ist aufwendiger in der Implementierung, jedoch deutlich flexibler als die Verwendung eines einzelnen regulären Ausdrucks.&lt;/p>
&lt;p>Aber auch hier zeigen sich schnell mehrere Probleme:&lt;/p>
&lt;ol>
&lt;li>Keine Warnung/Fehler, wenn sich reguläre Ausdrücke überlappen.&lt;/li>
&lt;li>Keine automatische Anpassung, wenn es mehrere Ergebnisse für ein Entity gibt.&lt;/li>
&lt;li>Keine Markierung von schon ausgewählten/extrahierten Informationen.&lt;/li>
&lt;/ol>
&lt;p>Dies lässt sich nachrüsten, sorgt jedoch für weitere Komplexität bei der Umsetzung.
Eine Lösung hierfür bieten Frameworks zur Extraktion von Informationen aus Text,
wobei wir spaCy für diesen Zweck einsetzen.&lt;/p>
&lt;h2 id="regelbasiertes-matching-mit-spacy">Regelbasiertes Matching mit spaCy&lt;/h2>
&lt;p>Das NLP Framework spaCy hat ein eigenes System für das &lt;a href="https://spacy.io/usage/rule-based-matching" target="_blank" rel="noopener">regelbasierte Matching&lt;/a>.&lt;/p>
&lt;p>Daher hier ein Beispiel mit einer &lt;a href="https://spacy.io/usage/processing-pipelines" target="_blank" rel="noopener">spaCy Pipeline&lt;/a>.&lt;/p>
&lt;p>&lt;strong>Code-Beispiel:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">nlp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">German&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ruler&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;entity_ruler&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">patterns&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;NR&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;nr&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;IS_SENT_START&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;IS_DIGIT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">}]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;NR&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;nr&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;IS_SENT_START&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;REGEX&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;^\d+[a-z]?$&amp;#34;&lt;/span>&lt;span class="p">}}]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;SIG&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;sig&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;HSTA&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;SHAPE&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Xdd&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;REGEX&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;^\d+[a-z]?$&amp;#34;&lt;/span>&lt;span class="p">}},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;-&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;OP&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;?&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;REGEX&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;^\d+[a-z]?$&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="s2">&amp;#34;OP&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;?&amp;#34;&lt;/span>&lt;span class="p">}]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ASIG&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;asig&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;SHAPE&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Xdddd&amp;#34;&lt;/span>&lt;span class="p">}]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ASIG&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;asig&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;SHAPE&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Xddddx&amp;#34;&lt;/span>&lt;span class="p">}]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;DATUM&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;date&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;SHAPE&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;dddd&amp;#34;&lt;/span>&lt;span class="p">}]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;DATUM&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;date&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ca.&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;SHAPE&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;dd.&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Jhd.&amp;#34;&lt;/span>&lt;span class="p">}]},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ruler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_patterns&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">patterns&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">doc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">displacy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">style&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;ent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">NR&lt;/span>
&lt;/mark>
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
HSTA L13 42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SIG&lt;/span>
&lt;/mark>
(
&lt;mark class="entity" style="background: #cbd5e8; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
A9594
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">ASIG&lt;/span>
&lt;/mark>
)
&lt;mark class="entity" style="background: #e6f5c9; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
1798
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">DATUM&lt;/span>
&lt;/mark>
&lt;/div>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">doc2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text2&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">displacy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">style&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;ent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
113a
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">NR&lt;/span>
&lt;/mark>
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
HSTA L13 50-55c
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SIG&lt;/span>
&lt;/mark>
(Vorsignatur: GLAK B33 F 7 13b,
&lt;mark class="entity" style="background: #cbd5e8; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
T1234c
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">ASIG&lt;/span>
&lt;/mark>
)
&lt;mark class="entity" style="background: #e6f5c9; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
ca. 18. Jhd.
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">DATUM&lt;/span>
&lt;/mark>
&lt;/div>
&lt;p>Bei diesem Code-Beispiel sehen wir einige Vorteile bei der Verwendung von spaCy.
Wir können die mitgelieferten &lt;a href="https://spacy.io/usage/visualizers#ent" target="_blank" rel="noopener">Visualisierungstools&lt;/a> verwenden, mehrere Regeln für ein Entity hinterlegen und auf weniger komplexe Regeln zurückgreifen (Shape-Pattern, &lt;code>IS_PUNCT&lt;/code>, &amp;hellip;).&lt;/p>
&lt;p>Wir können uns auch direkt die übersprungenen Teile ausgeben lassen (oder sie markieren).&lt;/p>
&lt;p>&lt;strong>Code-Beispiel:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">token&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">token&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">doc2&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">token&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ent_type_&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">token&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">is_punct&lt;/span>&lt;span class="p">)])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Ausgabe:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="n">Vorsignatur&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">GLAK&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">B33&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">F&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">7&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">13&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Hier zeigen sich auch gleich mehrere Probleme bei der Verwendung von regelbasiertem Matching mit spaCy:&lt;/p>
&lt;ol>
&lt;li>Jedes Muster passt immer nur auf einen Token.&lt;/li>
&lt;li>Ein &amp;ldquo;lookahead&amp;rdquo; oder &amp;ldquo;lookbehind&amp;rdquo; wie bei regulären Ausdrücken ist (noch) nicht möglich.
Es gibt also keine Entsprechung für &lt;code>(?&amp;lt;=Vorsignatur: )[^,]+(?=,)&lt;/code>.&lt;/li>
&lt;/ol>
&lt;p>Um trotzdem nur die Vorsignatur zu extrahieren, ohne das Prefix &amp;ldquo;Vorsignatur: &amp;quot; mitzuspeichern, gibt es mehrere Lösungen:&lt;/p>
&lt;ol>
&lt;li>Nachbearbeitung der Entities, also das Prefix in einem separaten Durchlauf löschen.&lt;/li>
&lt;li>Die komplexen Muster in einem separaten Durchlauf verarbeiten (nächster Abschnitt).&lt;/li>
&lt;li>Reguläre Ausdrücke (Gesamttext) mit spaCy kombinieren (übernächster Abschnitt).&lt;/li>
&lt;/ol>
&lt;h2 id="erweitertes-regelbasiertes-matching-mit-spacy">Erweitertes regelbasiertes Matching mit spaCy&lt;/h2>
&lt;p>Für das folgende Beispiel wollen wir ein Entity basierend auf &amp;ldquo;Umgebungsinformationen&amp;rdquo; bestimmen.
Nehmen wir zum Beispiel an, dass es ziemlich komplex ist die Signatur zu bestimmen, wir aber wissen, dass die Signatur immer nach der Findbuch-Nr zu finden ist.&lt;/p>
&lt;p>Also würden wir gerne ein Muster der Form &lt;code>(?&amp;lt;=NR).+(?=\()&lt;/code>definieren.
Dafür verarbeiten wir den ersten Beispielsatz zweimal mit spaCy.&lt;/p>
&lt;p>Zuerst erstellen wir wieder eine Pipeline, die uns die &amp;ldquo;einfachen&amp;rdquo; Entities markiert.&lt;/p>
&lt;p>&lt;strong>Code-Beispiel:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">nlp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">German&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ruler&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;entity_ruler&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">patterns&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;NR&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;IS_SENT_START&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;IS_DIGIT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">}]}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ruler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_patterns&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">patterns&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">doc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">displacy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">style&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;ent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">NR&lt;/span>
&lt;/mark>
HSTA L13 42 (A9594) 1798&lt;/div>
&lt;p>Anschließend verarbeiten wir den mit Markierungen versehenen Beispielsatz mit einem &lt;code>Matcher&lt;/code> und unserem komplexen Muster.&lt;/p>
&lt;p>&lt;strong>Code-Beispiel:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">matcher&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Matcher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">nlp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">vocab&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">signature_pattern&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;ENT_TYPE&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;NR&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;NOT_IN&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;(&amp;#34;&lt;/span>&lt;span class="p">]},&lt;/span> &lt;span class="s2">&amp;#34;OP&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;+&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;TEXT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;(&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">matcher&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Signature&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">signature_pattern&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">matches&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">matcher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">start&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">matches&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entity&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Span&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">start&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">label&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;SIG&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ents&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">entity&lt;/span>&lt;span class="p">,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">displacy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">style&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;ent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">NR&lt;/span>
&lt;/mark>
&lt;mark class="entity" style="background: #fdcdac; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
HSTA L13 42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">SIG&lt;/span>
&lt;/mark>
(A9594) 1798&lt;/div>
&lt;p>Hier erstellen wir das Entity selbst und fügen es den von spaCy verwalteten Informationen hinzu.
Der gefundene Treffer beinhaltet das Entity &lt;code>NR&lt;/code> und die öffnende Klammer &lt;code>(&lt;/code>,
daher schneiden wir noch vorne und hinten jeweils einen Token ab.&lt;/p>
&lt;p>Im ersten Durchlauf für die &lt;code>Nr&lt;/code> hat die Pipeline Komponente diese Aufgaben übernommen.&lt;/p>
&lt;div class="alert alert-note">
&lt;div>
&lt;strong>Hinweis:&lt;/strong> leider beinhalten die Treffer keinen Hinweis auf die Regel, die den Treffer erzeugt haben.
Hat man viele Regeln, die mit unterschiedlichen Labels oder Beschnitt versehen werden sollen, empfehlen sich
entweder der &lt;a href="https://spacy.io/usage/rule-based-matching#on_match" target="_blank" rel="noopener">on_match Callback&lt;/a> für die Regeln,
oder eine &lt;a href="https://spacy.io/usage/processing-pipelines#custom-components" target="_blank" rel="noopener">Custom Component&lt;/a> für die Pipeline.
&lt;/div>
&lt;/div>
&lt;h2 id="kombiniere-reguläre-ausdrücke-mit-spacy">Kombiniere reguläre Ausdrücke mit spaCy&lt;/h2>
&lt;p>Die Ansätze mit regulären Ausdrücken auf dem Gesamttext und dem regelbasierten Matching mit spaCy lassen sich auch kombinieren.
Zuerst lassen wir das Dokument von spaCy analysieren.
Anschließend wenden wir die regulären Ausdrücke auf den Gesamttext an und ermitteln die Position der Treffer.
Mit dieser Information erzeugen wir dann Entities mit spaCy.&lt;/p>
&lt;p>&lt;strong>Code-Beispiel:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">nlp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">German&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ruler&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_pipe&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;entity_ruler&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">patterns&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;label&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;NR&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;nr&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;pattern&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;IS_SENT_START&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;IS_DIGIT&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">}]}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ruler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">add_patterns&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">patterns&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">doc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">nlp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">text1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">displacy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">style&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;ent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">NR&lt;/span>
&lt;/mark>
HSTA L13 42 (A9594) 1798&lt;/div>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">entity_definitions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;DATUM&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">r&lt;/span>&lt;span class="s2">&amp;#34;(?&amp;lt;=\) ).+$&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">regex&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">entity_definitions&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">match&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">re&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">regex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">match&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">match&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">span&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">entity&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">char_span&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">start&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">end&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">label&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ents&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">entity&lt;/span>&lt;span class="p">,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">displacy&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">style&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;ent&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="entities" style="line-height: 2.5; direction: ltr">
&lt;mark class="entity" style="background: #b3e2cd; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
42
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">NR&lt;/span>
&lt;/mark>
HSTA L13 42 (A9594)
&lt;mark class="entity" style="background: #e6f5c9; padding: 0.45em 0.6em; margin: 0 0.25em; line-height: 1; border-radius: 0.35em;">
1798
&lt;span style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">DATUM&lt;/span>
&lt;/mark>
&lt;/div>
&lt;p>Ein ähnliches Beispiel dazu findet sich auch in der &lt;a href="https://spacy.io/usage/rule-based-matching#regex-text" target="_blank" rel="noopener">spaCy Dokumentation&lt;/a>.&lt;/p>
&lt;p>Hier sind noch die folgenden Dinge zu beachten:&lt;/p>
&lt;ol>
&lt;li>Es werden keine Entities überschrieben.&lt;/li>
&lt;li>Es können keine sich überschneidenden Entities angelegt werden.&lt;/li>
&lt;li>Manchmal &amp;ldquo;rutschen&amp;rdquo; Trennzeichen (z.B. Leerzeichen) mit in den Beginn oder das Ende der Treffer. Diese führen beim Setzen einer Entity in spaCy zu einem Fehler.&lt;/li>
&lt;/ol>
&lt;p>Die entsprechenden Fehlerbehandlungen haben wir im Code-Beispiel ausgelassen.&lt;/p>
&lt;h2 id="fazit">Fazit&lt;/h2>
&lt;p>Bei der Aufbereitung von Findbüchern für die Datenbank sind wir sowohl mit regulären Ausdrücken, als auch mit dem Regelsystem von spaCy an die jeweiligen Grenzen der Technologie gestoßen.
Dabei haben wir die Toolunterstützung von spaCy zu schätzen gelernt, die uns einiges an Entwicklungsaufwand abnimmt und bei der Visualisierung der Ergebnisse unterstützt.&lt;/p>
&lt;p>Mit einer Kombination aus regulären Ausdrücken und Regeln in spaCy, die auf schon identifizierten Entities basieren,
konnten wir mit einer übersichtlichen Codebasis selbst komplexe Datenaufbereitungen durchführen.&lt;/p></description></item><item><title>80. Südwestdeutscher Archivtag 2021</title><link>https://fdmlab.landesarchiv-bw.de/event/2021-werkzeuge-zur-texterkennung/</link><pubDate>Fri, 18 Jun 2021 11:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/event/2021-werkzeuge-zur-texterkennung/</guid><description>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube.com/embed/PaEjLgqzmo8" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video">&lt;/iframe>
&lt;/div></description></item><item><title>Werkzeuge zur Texterkennung: ein Blick in die digitale Werkstatt des FDMLab am Landesarchiv Baden-Württemberg</title><link>https://fdmlab.landesarchiv-bw.de/publication/2021-suedwestdeutscher-archivtag/</link><pubDate>Fri, 18 Jun 2021 11:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/publication/2021-suedwestdeutscher-archivtag/</guid><description/></item><item><title>Wie wir gedruckte Findbücher in unsere Datenbank bekommen, ohne zu viel Zeit in manuelle Arbeiten zu investieren</title><link>https://fdmlab.landesarchiv-bw.de/post/2021-06-wie-wir-gedruckte-findbuecher-in-unsere-datenbank-bekommen/</link><pubDate>Tue, 15 Jun 2021 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/post/2021-06-wie-wir-gedruckte-findbuecher-in-unsere-datenbank-bekommen/</guid><description>&lt;p>Viele Archive kennen diese Situation: Es gibt ein archivisches Fachinformationssystem (AFIS), über das Archivgut erschlossen und für eine Online-Recherche bereitgestellt wird. Doch gleichzeitig existiert zu einigen Beständen auch noch eine Reihe älterer gedruckter Findbücher im Lesesaal. Diese Bestände können nicht in der Datenbank recherchiert werden.&lt;/p>
&lt;p>Um für diese Bestände zeitgemäße Recherchemöglichkeiten bieten zu können, müssen die Erschließungsinformationen aus den analogen Findbüchern in das digitale Fachinformationssystem übertragen werden. Wie geht das, ohne dass Archivar*innen zu viel wertvolle Arbeitszeit in manuelle Arbeitsschritte investieren? In diesem Beitrag zeigen wir an einem Beispiel, wie wir dieses Problem im FDMLab angegangen sind und was wir dabei gelernt haben.&lt;/p>
&lt;h2 id="der-prozess">Der Prozess&lt;/h2>
&lt;div class="mermaid">---
title: Findbuch Extraktionsprozess
config:
look: handDrawn
theme: neutral
---
flowchart LR
A[Findbuch]
--> |OCR| B[Word]
--> |Textexport| C[Text]
--> |Daten Transformation| D[CSV]
--> |Import| E[(AFIS)]
&lt;/div>
&lt;p>Überblick über unseren Prozess, es folgen die einzelnen Bearbeitungsschritte im Detail.&lt;/p>
&lt;h3 id="findbuch-digitalisieren-und-volltexterkennung-durchführen">Findbuch digitalisieren und Volltexterkennung durchführen&lt;/h3>
&lt;div class="mermaid">---
title: Prozess: OCR
config:
look: handDrawn
theme: neutral
---
flowchart LR
A[Findbuch]
--> |OCR| B[Word]
&lt;/div>
&lt;p>Im FDMLab wollen wir das Findbuch „Fürstlich Thurn und Taxissches Archiv Obermarchtal Grafschaft Friedberg-Scheer“&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> bearbeiten, das 1.517 Urkunden des 14. bis 19. Jahrhunderts enthält. Das Findbuch wurde im Vorfeld digitalisiert und es wurde eine Volltexterkennung mittels OCR durchgeführt. Das Ergebnis hat das FDMLab in Form einer docx-Datei erhalten.&lt;/p>
&lt;h3 id="extraktion-der-erschließungsdaten">Extraktion der Erschließungsdaten&lt;/h3>
&lt;div class="mermaid">---
title: Prozess: Textexport
config:
look: handDrawn
theme: neutral
---
flowchart LR
B[Word]
--> |Textexport| C[Text]
&lt;/div>
&lt;p>Ziel ist es nun, die Daten in ein maschinenlesbares Format umzuwandeln, das wir später in unser AFIS importieren können. Hierfür wandeln wir die Datei in eine txt-Datei um.&lt;/p>
&lt;p>Die docx-Datei ist aus zwei Gründen nicht für die weitere automatische Verarbeitung der Daten geeignet:&lt;/p>
&lt;ol>
&lt;li>Word &lt;em>verwendet&lt;/em> schwebende Felder, um manchen Text so zu platzieren, wie er vermeintlich im Original vorlag. Diese Felder müssen in der Struktur des XML-Dokuments erstmal gefunden und dann deren Position im Text separat berechnet werden. Daher ist es einfacher, mit dem Reintext zu arbeiten, in dem der Text schon &amp;ldquo;richtig&amp;rdquo; formatiert ist.&lt;/li>
&lt;li>Word hat einige Findbuchnummern und Datumsangaben als Aufzählfelder deklariert. Eine manuelle Korrektur oder Veränderung eines solchen Feldes (z.B. wegen OCR oder Layoutproblemen) kann unerwartete Seiteneffekte haben. Beispielsweise wird in so einem Fall beim Einfügen einer Leerzeile von Word automatisch eine Findbuchnummer mehrere Seiten später hochgezählt.&lt;/li>
&lt;/ol>
&lt;div class="alert alert-note">
&lt;div>
&lt;p>Es hat sich bewährt, eine Ausgabe des analogen Findbuchs stets zur Hand zu haben, um&lt;/p>
&lt;ul>
&lt;li>fehlende/fehlerhafte Abschnitte korrigieren zu können,&lt;/li>
&lt;li>Kürzel verstehen und auflösen zu können,&lt;/li>
&lt;li>Validitätschecks (Anzahl Einträge, &amp;hellip;) durchführen zu können.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;div class="mermaid">---
title: Prozess: Daten Transformation
config:
look: handDrawn
theme: neutral
---
flowchart LR
C[Text]
--> |Daten Transformation| D[CSV]
&lt;/div>
&lt;p>Aus der txt-Datei können mit Verfahren der automatischen Textextraktion die verschiedenen Findbucheinträge aus dem Gesamtdokument separiert und die einzelnen Erschließungsdaten wie Titel, Datierung, Signatur etc. extrahiert werden. Die extrahierten Daten werden in eine csv-Datei geschrieben, da sich das csv-Format für den Import in das AFIS eignet.&lt;/p>
&lt;p>In diesem Projekt haben wir ausschließlich sogenannte reguläre Ausdrücke verwendet, um Muster zur Datenextraktion zu definieren.
Mit dem regulären Ausdruck &lt;code>\d{4}(-\d{2}){0,2}&lt;/code> lässt sich zum Beispiel ein Datum in der Form &lt;code>2020&lt;/code>, &lt;code>2020-03&lt;/code> oder &lt;code>2020-03-28&lt;/code> erkennen.
Auf Details werden wir in separaten Artikeln nochmal eingehen.&lt;/p>
&lt;h3 id="import-der-erschließungsdaten-ins-afis">Import der Erschließungsdaten ins AFIS&lt;/h3>
&lt;div class="mermaid">---
title: Prozess: Import
config:
look: handDrawn
theme: neutral
---
flowchart LR
D[CSV]
--> |Import| E[(AFIS)]
&lt;/div>
&lt;p>Die csv-Datei enthält die Erschließungsdaten in einem strukturierten, maschinenlesbaren Format. Sie kann mit einem Übernahmeassistenten verarbeitet werden, der einen automatischen Import der Daten in das AFIS ermöglicht.&lt;/p>
&lt;p>War bis hierher technisches Wissen gefragt, braucht es nun archivarische Expertise, denn im nächsten Schritt geht es um das Mapping der extrahierten Daten mit den Datenfeldern im Erschließungsformular für Urkunden. Nachdem die csv-Datei ausgelesen und das Mapping im Übernahmeassistenten vorgenommen wurden, können die Daten in einem Arbeitsgang in das AFIS importiert und unter einem zuvor definierten Punkt in der Tektonik eingehängt werden.&lt;/p>
&lt;h3 id="freischalten-der-erschließungsdaten-für-die-online-recherche">Freischalten der Erschließungsdaten für die Online-Recherche&lt;/h3>
&lt;p>Die Erschließungsdaten können jetzt an das &lt;a href="https://www2.landesarchiv-bw.de/ofs21/home.php" target="_blank" rel="noopener">Online-Findmittelsystem&lt;/a> (OLF) des LABW übertragen und für die Internetrecherche freigeschaltet werden. Nutzer*innen können nun online nach Urkunden der Grafschaft Friedberg-Scheer recherchieren und müssen nicht mehr auf die Printversion des Findbuchs zurückgreifen.&lt;/p>
&lt;h2 id="lessons-learned">Lessons Learned&lt;/h2>
&lt;p>Nach der Bearbeitung des Findbuchs können wir die folgenden Punkte als Lessons Learned mitnehmen:&lt;/p>
&lt;ol>
&lt;li>Die automatische Textextraktion stellt ein geeignetes Verfahren für die Automatisierung der Übertragung von Findbüchern in die archivische Datenbank dar. Trotzdem gab es nach wie vor noch manuelle Nacharbeiten. Dies resultiert u.a. daraus, dass das gedruckte Findbuch zwar auf den ersten Blick eine normierte Form besitzt, bei näherem Hinsehen aber verschiedene Unregelmäßigkeiten bei Struktur, Layout und auch sprachlicher Gestaltung der Findbucheinträge sichtbar werden, die die Formulierung von Extraktionsregeln erschweren.&lt;/li>
&lt;li>Word ist als Dateiformat für die Datenextraktion ungeeignet. Da das Layout bei der Datenextraktion relevant ist, sollte, wenn möglich, direkt mit Bilddateien und reinen Textdateien (txt) gearbeitet werden &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>.&lt;/li>
&lt;li>Bei der Findbuchextraktion ist eine enge Zusammenarbeit von technischem und archivfachlichem Personal empfehlenswert. Durch die Zusammenarbeit kann die Granularität der Datenextraktion schneller bestimmt und die anschließende Abbildung der extrahierten Daten auf das Erfassungsformular frustfrei durchgeführt werden.&lt;/li>
&lt;/ol>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Dep. 30/1 T1 Fürstlich Thurn und Taxissches Archiv Obermarchtal Grafschaft Friedberg-Scheer. Urkundenregesten 1304-1802. Bearb. Von Robert Kretzschmar. Stuttgart 1993 (Inventare der nichtstaatlichen Archive in Baden-Württemberg ; Bd. 18), &lt;a href="https://www2.landesarchiv-bw.de/ofs21/olf/struktur.php?bestand=2240&amp;amp;klassi=001" target="_blank" rel="noopener">Online-Version&lt;/a> des gedruckten Inventars.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Die &lt;strong>digitale&lt;/strong> Druckvorlage ist natürlich noch besser geeignet.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item><item><title>2. Workshop des OCR-BW-Projektes 2021</title><link>https://fdmlab.landesarchiv-bw.de/event/2021-ocr-im-archiv/</link><pubDate>Wed, 09 Jun 2021 11:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/event/2021-ocr-im-archiv/</guid><description/></item><item><title>Wie wir Docker einsetzen</title><link>https://fdmlab.landesarchiv-bw.de/post/2021-06-wie-wir-docker-einsetzen/</link><pubDate>Tue, 08 Jun 2021 00:00:00 +0000</pubDate><guid>https://fdmlab.landesarchiv-bw.de/post/2021-06-wie-wir-docker-einsetzen/</guid><description>&lt;p>Programme haben Anforderungen an ihre Umgebung.
Dies kann ein bestimmtes Betriebssystem sein (Windows, Linux, macOS),
eine Laufzeitumgebung für eine Programmiersprache (Python, Java, C#),
oder die Verfügbarkeit bestimmter Bibliotheken (GTK, .NET, Qt).
Wir wollen uns beim Testen und Evaluieren aber nicht einschränken!&lt;/p>
&lt;p>Im FDMLab evaluieren wir Programme und Bibliotheken, die sowohl für den Gebrauch am Rechner, als auch als Serveranwendung entwickelt wurden.
Würden wir diese alle auf unserem Testrechner installieren, hätten wir schnell die von Randall Munroe dargestellte Situation erzeugt.
Also unser System mit Programmen, Laufzeitumgebungen und Konfigurationsdateien vollgemüllt.&lt;/p>
&lt;figure id="figure-python-environmenthttpsxkcdcom1987-von-randall-munroe-unter-cc-by-nc-lizenzhttpcreativecommonsorglicensesby-nc25">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="[Python Environment](https://xkcd.com/1987/) von Randall Munroe unter [CC BY-NC Lizenz](http://creativecommons.org/licenses/by-nc/2.5/)." srcset="
/post/2021-06-wie-wir-docker-einsetzen/python_environment_huf01e0eb7d16bfdafe1594868b0e0e25e_54078_eaf385cb765fbdd143396c6e7cb3e5a8.webp 400w,
/post/2021-06-wie-wir-docker-einsetzen/python_environment_huf01e0eb7d16bfdafe1594868b0e0e25e_54078_cd12d25540c3a8bdf9c0e7a4206b9819.webp 760w,
/post/2021-06-wie-wir-docker-einsetzen/python_environment_huf01e0eb7d16bfdafe1594868b0e0e25e_54078_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://fdmlab.landesarchiv-bw.de/post/2021-06-wie-wir-docker-einsetzen/python_environment_huf01e0eb7d16bfdafe1594868b0e0e25e_54078_eaf385cb765fbdd143396c6e7cb3e5a8.webp"
width="492"
height="487"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
&lt;a href="https://xkcd.com/1987/" target="_blank" rel="noopener">Python Environment&lt;/a> von Randall Munroe unter &lt;a href="http://creativecommons.org/licenses/by-nc/2.5/" target="_blank" rel="noopener">CC BY-NC Lizenz&lt;/a>.
&lt;/figcaption>&lt;/figure>
&lt;p>Stattdessen versuchen wir - sofern möglich - die zu evaluierenden Programme als Docker Container zu testen.
Wie im Eingangsbild symbolhaft dargestellt&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>, handelt es sich bei Docker Containern (vereinfacht)
um einen Standard um Anwendungen mitsamt ihrer Umgebung zu verpacken und somit auf unterschiedlichen Docker Hosts laufen zu lassen.&lt;/p>
&lt;p>Wir verwenden dafür &lt;a href="https://docs.docker.com/docker-for-windows/install/" target="_blank" rel="noopener">Docker Desktop für Windows&lt;/a>.&lt;/p>
&lt;h2 id="docker-container-via-browser">Docker Container via Browser&lt;/h2>
&lt;div class="mermaid">---
title: Docker Container via Browser
config:
look: handDrawn
theme: neutral
---
flowchart TD
subgraph Container[Container]
FC[Dateien]
Server[[Server]]
DB[(Datenbank)]
Lib{{Bibliotheken}}
end
Browser[[Browser]]
FU[Dateien]
FU &lt;--> FC --> Server
Server --> Browser
DB --> Server
Lib --> Server
&lt;/div>
&lt;p>Eine Variante Docker zu verwenden, ist mit der Anwendung in dem Container via einer Web GUI im Browser zu interagieren.
Ein Beispiel hierfür ist das Projekt &lt;a href="http://ocr4all.org/" target="_blank" rel="noopener">OCR4All&lt;/a>.
Mit dem folgenden PowerShell Befehl wird die Vorlage für einen &lt;em>OCR4All Container&lt;/em> (&lt;code>image&lt;/code>)
aus der &lt;em>Docker Hub Registry&lt;/em> heruntergeladen und lokal ein &lt;em>OCR4All Container&lt;/em> erstellt.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">docker&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-rm&lt;/span> &lt;span class="n">-it&lt;/span> &lt;span class="n">-p&lt;/span> &lt;span class="mf">8080&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="mf">8080&lt;/span> &lt;span class="n">ls6uniwue&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">ocr4all&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Über die Adresse &lt;code>http://localhost:8080&lt;/code> lässt sich die Web GUI des &lt;em>OCR4All Containers&lt;/em> aufrufen und bedienen.&lt;/p>
&lt;p>Es ist möglich Daten von der eigenen Festplatte mit &lt;a href="https://docs.docker.com/storage/volumes/" target="_blank" rel="noopener">in den Container einzuhängen&lt;/a> (&lt;em>mounten&lt;/em>).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">docker&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-rm&lt;/span> &lt;span class="n">-it&lt;/span> &lt;span class="n">-p&lt;/span> &lt;span class="mf">8080&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="mf">8080&lt;/span> &lt;span class="n">-v&lt;/span> &lt;span class="p">${&lt;/span>&lt;span class="n">PWD&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">var&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">ocr4all&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="n">ls6uniwue&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">ocr4all&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="docker-container-als-skript">Docker Container als Skript&lt;/h2>
&lt;p>Docker Container können ähnliche wie Skripte verwendet werden.&lt;/p>
&lt;p>Mit dem folgenden Beispiel wird ein &lt;a href="https://ocr-d.de/en/workflows" target="_blank" rel="noopener">OCR-D Workflow&lt;/a> ausgeführt,
ohne dass die mehr als 60 unterschiedlichen Prozessoren separat installiert werden müssen.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">docker&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-rm&lt;/span> &lt;span class="n">-v&lt;/span> &lt;span class="p">${&lt;/span>&lt;span class="n">PWD&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="n">-w&lt;/span> &lt;span class="p">/&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="p">--&lt;/span> &lt;span class="n">ocrd&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">all&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="n">maximum&lt;/span> &lt;span class="n">bash&lt;/span> &lt;span class="n">workflow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="py">sh&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="alert alert-warning">
&lt;div>
Das Image &lt;code>ocrd/all:maximum&lt;/code> ist mehrere Gigabyte groß!
&lt;/div>
&lt;/div>
&lt;h2 id="wo-docker-nicht-funktioniert">Wo Docker nicht funktioniert&lt;/h2>
&lt;p>Es gibt Software, die nicht als Docker Image vorliegt, oder als solche verwendet werden kann.
Dazu gehört Software, die eine systemspezifische GUI Bibliothek verwendet.&lt;/p>
&lt;p>Beispiele hierfür sind der &lt;a href="https://www.primaresearch.org/tools/PAGEViewer" target="_blank" rel="noopener">Page Viewer&lt;/a>
oder der &lt;a href="https://readcoop.eu/transkribus/download/" target="_blank" rel="noopener">Transkribus Expert Client&lt;/a>.
Diese benötigen beide Java als Laufzeitumgebung und stellen ihre GUI mit nativen Betriebssystemkomponenten dar.
Docker Container haben keine nativen GUI Komponenten, weshalb diese Programme in einem Docker Container nicht genutzt werden können &lt;sup id="fnref:2">&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref">2&lt;/a>&lt;/sup>.&lt;/p>
&lt;h2 id="unser-fazit">Unser Fazit&lt;/h2>
&lt;p>Wir verwenden Docker um schnell und unkompliziert unterschiedlichste Anwendungen evaluieren zu können.
Inzwischen kommen Docker Container bei uns auch praktisch zum Einsatz.
Zum Beispiel um gemeinsame Projekte reproduzierbar bearbeiten zu können.&lt;/p>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Die Standardisierung von Seefracht-Containern zu &lt;a href="https://de.wikipedia.org/wiki/ISO-Container" target="_blank" rel="noopener">ISO-Containern&lt;/a> optimierte die Transportkette im Güterverkehr.
Daher werden standardisierte virtuelle Container häufig mit Fracht-Containern verglichen.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;li id="fn:2">
&lt;p>Es gibt Bibliotheken, die die nativen GUI Komponenten für Webbrowser darstellen können.
Ein Beispiel hierfür ist &lt;a href="https://developer.gnome.org/gtk3/stable/gtk-broadway.html" target="_blank" rel="noopener">GTK Broadway&lt;/a>.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item></channel></rss>