## Data Transformation

Das Kapitel Data Transformation spielt eine wichtige Rolle im Gesamtdatenverarbeitungsprozess des Transparenzregister-Projekts. Es koordiniert die Datenverarbeitung zwischen MongoDB und bereitet die Daten für die Verwendung in der SQL-Datenbank auf.

Die Implementierung dieses Moduls ermöglicht eine nahtlose Übertragung und Transformation von Daten zwischen verschiedenen Datenbanken sowie die Anwendung fortschrittlicher KI-Modelle zur Anreicherung von Informationen. \
Die folgende Einleitung bietet einen Überblick über die Funktionalitäten und Ziele dieses Datenverarbeitungsmoduls.

![Scheme_Data_Transformation](Abbildungen/Data_Transformation.PNG)

Die Module wurden entwickelt, um die automatisierte Extraktion von Organisationen (Entitäten) aus unstrukturierten Textdaten sowie die Analyse von Sentiments in Nachrichtenartikeln zu ermöglichen. Diese Funktionalitäten sind entscheidend, um umfassende Einblicke in die in der Staging-Datenbank enthaltenen Informationen zu gewinnen. \
Die Module sind konfigurierbar, um diese an verschiedene Anforderungen anzupassen, wie z.B. Datenbankkonfiguration, Auswahl der Entitäten, Auswahl von KI-Modellen.

**Containerhierarchie**

Um die Funktion des Containers zu verstehen, wird die Hierarchie erklärt.\
Zum Orchestrieren der verschiedenen Module und als Einstiegspunkt für die Datenverarbeitung wurde das Hauptskript **data_processing.py** angelegt. \
Dieses Skript ruft verschiedene Funktionen aus Submodulen auf, welche im weiteren detaillierter beschreiben werden. \
Es dient als zentrale Steuereinheit, für die Datenverarbeitung und gewährleistet die koordinierte Ausführung der erforderlichen Module. \
Die Abbildung stellt schematisch die Module in blau dar und die verwendeten Funktionen in grün, welche aufgerufen werden und soll ein erstes Verständnis für die Struktur schaffen.

![Structure of Docker Container](Abbildungen/Container_structure.PNG)

### KI Module

Die KI Module werden benötigt, um aus den vorhandenen Nachrichtenartikeln die benötigten Informationen zu erhalten. \
Mit Hilfe dieser Module werden die Daten aus der StagingDB analysiert, Informationen extrahiert und übertragen.\
Um die Module automatisiert verwenden zu können, werden diese in einer Pipeline als Werkzeuge verwendet. \
Jede Pipeline ist eigenständig lauffähig, um diese unabhängig voneinander ausführen zu können. Bei auftretendem Fehlern kann eine einzelne Pipeline isoliert ausgeführt werden.\
Ausserdem wurden logger-Funktionen implementiert, um den Status oder Fehlermeldungen zu erhalten.

#### NER Pipeline

Die NER (Named Entity Recognition) Pipeline verwendet verschiedene Services für Named Entity Recognition, um Entitäten wie Firmennamen in den Textdaten zu identifizieren. \
Die NER-Analyseklasse unterstützt drei verschiedene Methoden zur Erkennung von Entitäten, welche innerhalb des Projektverlaufs entstanden sind und an Komplexität zugenommen haben.

Ausserdem kann bei den Nachrichtenartikeln zwischen den Attributen *title* oder *text* gewählt werden.
Für hohe Performance, aber ungenauere Ergebnisse bietet sich die Verwendung des Nachrichtentitels an, für niedrige Performance aber akkurate Ergebnisse bietet sich die Analyse des gesamten Textes an.

``` python
#from ner_pipeline.py
ner_methods = Literal["spacy", "company_list", "transformer"]
doc_attribs = Literal["text", "title"]
```

Beim Ausführen der Pipeline werden die Umgebungsvariablen ausgelesen, um den zu verwendenen Service und das zu analysierende Attribut zu wählen. \
Sollten die Parameter nicht definiert sein wird als Standardeinstellung der *NER Transformer* auf das Attribut *text* angewendet.

```python
#from ner_pipeline.py
def execute_ner(config_provider: ConfigProvider) -> None:
    .
    .
    .
   if (
        ner_method := os.getenv("PYTHON_NER_METHOD", "transformer").lower()
    ) not in get_args(ner_methods):
        raise ValueError(
            f"Please use either {', '.join(get_args(ner_methods))} as an ENV variable defining your ner method."
            f"Currently used is {ner_method}"
        )
    if (doc_attrib := os.getenv("PYTHON_NER_DOC", "text").lower()) not in get_args(
        doc_attribs
    ):
        raise ValueError(
            f"Please use either {', '.join(get_args(doc_attribs))} as an ENV variable defining your ner document."
            f"Currently used is {doc_attrib}"
        )
    .
    .
    .
```
##### NER Company List

Die *Company List* ist der naive Ansatz mittels String-Matching Unternehmen zu finden. Dafür wurde aus der Collection *companies* der StagingDB eine Unternehmensliste erstellt. \
Der zu untersuchende Text wird mit der Unternehmensliste abgeglichen und bei einem String Matching die Entität markiert.

##### NER Spacy
Bei diesem Service wird die spaCy Bibliothek für NLP (Natural Language Processing) verwendet. Diese beruht auf einer RNN-Architektur (Recurrent Neural Network), um Entitäten zu identifizieren.

Ablauf NER mit spaCy:
1. Tokenisierung: Die Textsequenzen werden tokenisiert, d.h. jedes Wort wird durch einen Token repräsentiert.
2. Part-of-Speech Tagging (POS): Jedem Token wird ein POS-Tag zugewiesen, um die grammatikalische Klasse zuzuordnen (wie z.B. Substantiv, Verb, Adjektiv).
3. Depency Parsing (DP): Beim DP wird der syntaktische Zusammenhang zwischen den Token als Baumstruktur dargestellt, um die grammatikalische Beziehung darzustellen.
4. NER: Anhand der vorhab generierten Merkmale (Token, POS, DP) identifiziert ein Recurrent Neural Network (RNN) die Entitäten, durch Wortmuster oder Kontextinformationen.

Für den implementierten Service wird das spaCy-Modell "de_core_news_lg" verwendet, welches auf deutschen Nachrichtenartikeln trainiert wurde.\
Bei der Initialisierung des Service kann ein anderes beliebiges Modell verwendet werden.

##### NER Transformer
Der Transformer ist eine neuronale Netzarchitektur, welche die Aufgabe hat Abhängigkeiten in Daten zu modellieren. Die Schichten des Modells sind vorab trainiert, um die Entitäten in Wort- bzw. Tokensequenzen zu erkennen. Dabei definieren die Trainingsdaten das Einsatzgebiet des Transformers, d.h. ein großes Modell welches auf vielen Trainingdaten basiert, hat eine hohe Genauigkeit, aber schlechte Performance. \
Auf [Hugging Face](www.huggingface.co) gibt es eine Vielzahl vortrainierter Modelle für verschiedene Aufgaben.
Für das Transparenzregister wurde die Aufgabe auf *Token Classification* und *deutsche Sprache* eingegrenzt, wie die Abbildung zeigt.

![NER Transformer](Abbildungen/NER_Transformer.PNG)

Im implementierten Service wird ein feingetuntes BERT-Modell der FH-SWF verwendet ([fhswf/bert_de_ner](https://huggingface.co/fhswf/bert_de_ner)), welches bei der Initialisierung des Service geändert werden kann.

**Aufbau und Funktion der Pipeline**\
Folgend wird der Aufbau und die Funktion der NER-Pipeline erklärt. \
Die Abbildung zeigt die schematische Struktur, bestehend aus vier Modulen, welche sequentiell abgearbeitet werden.

![NER Pipeline](Abbildungen/NER_Pipeline.PNG)

*Mongo Connect:* Verbindet sich mit der StagingDB unter Verwendung der Credentials in der `secrets.json` und den Verbindungseinstellungen in der `.env`-Datei.

*Mongo Read:* Diese Funktion liest alle Dokumente der StagingDB und überprüft, ob das Attribut *companies* vorhanden ist. Die IDs der Dokumente ohne dieses Attribut werden für den NER Service gespeichert.

*NER Service:* Der NER-Service verarbeitet sequentiell alle Dokumente, bei welchem das Attribut *companies* fehlt. Dabei übergibt er das zu analysierenden Dokument, die zu identifizierende Entität und das gewünschte Dokumentattribut. Der aufgerufene Service analysiert den Text und gibt ein Dictionary mit dem Entitätenname und der gefundenen Anzahl zurück.
```python
#from ner_pipeline.py
# NER Service Execution
for document in tqdm(documents):
    ents = ner_service_func(document, "ORG", doc_attrib)
    self.news_obj.collection.update_one(
        {"_id": document["_id"]},
        {"$set": {"companies": ents}},
    )
```

*Mongo Update Documents:* Abschließend wird das aktuelle Dokument aktualisiert, d.h. es wird ein Attribut *companies* mit dem Ergebnis-Dictionary angelegt und geschrieben. Dieses Attribut signalisiert bei einem erneutem Durchlauf der Pipeline, dass dieses Dokument bereits verarbeitet wurde.

#### Sentiment Pipeline
Die Sentiment-Pipeline analysiert und bestimmt die Stimmung in den Nachrichtenartikeln. Sie spielt eine wichtige Rolle bei der Anreicherung der Datenbank mit zusätzlichen Informationen darüber, wie der Inhalt der Nachrichtenartikel wahrgenommen wird. Dies kann in späteren Analysen oder Anwendungen nützlich sein, um beispielsweise Trends oder Stimmungen in Bezug auf bestimmte Themen zu identifizieren. \
Die Sentiment-Klasse beinhaltet zwei verschiedene Services zur Stimmungsbestimmung.
```python
doc_attribs = Literal["text", "title"]
sentiment_methods = Literal["spacy", "transformer"]
```

Beim Ausführen der Pipeline werden die Umgebungsvariablen ausgelesen, um den zu verwendenen Service und das zu analysierende Attribut zu wählen. \
Sollten die Parameter nicht definiert sein wird als Standardeinstellung der *Sentiment Transformer* auf das Attribut *text* angewendet.

```python
#from sentiment_pipeline.py
def execute_sentiment(config_provider: ConfigProvider) -> None:
    """Reads entries with missing data from the db and fills them with found sentiments.

    Args:
        config_provider: A config prover to define the MongoDB to read and write.
    """
    if (
        sentiment_method := os.getenv("PYTHON_SENTIMENT_METHOD", "transformer").lower()
    ) not in get_args(sentiment_methods):
        raise ValueError(
            f"Please use either {', '.join(get_args(sentiment_methods))} as an ENV variable defining your sentiment method."
            f"Currently used is {sentiment_method}"
        )
    if (
        doc_attrib := os.getenv("PYTHON_SENTIMENT_DOC", "text").lower()
    ) not in get_args(doc_attribs):
        raise ValueError(
            f"Please use either {', '.join(get_args(doc_attribs))} as an ENV variable defining your sentiment document."
            f"Currently used is {doc_attrib}"
        )
```

##### Sentiment spaCy
Die spaCy-Bibliothek bietet keine spezialisierte Sentimentanalyse, kann aber durch Verwendung externer Ressourcen implementiert werden. \
Der hier implementierte Service beruht auf einem deutschen Wortschatz, welcher [SentiWS](https://wortschatz.uni-leipzig.de/de/download) heißt. \
Dieses Wörterbuch enthält 1650 positive und 1800 negative Grundformen, wodurch sich durch verschiedene Flexionsformen ein Umfang von 16000 positiven und 18000 negativen Wörtern ergibt.

Ablauf Sentimentanalyse mit spaCy:
1. Tokenisierung: Die Wörter der Textsequenz werden tokenisiert.
2. Analyse mit Wortschatz: Die Token der Textsequenz werden nacheinander durchlaufen und mit dem Sentiment-Wörterbuch bestimmt. Die Werte der Token werden aufsummiert, wodurch sich ein Gesamtergebnis für den Text ergibt.

Diese Art der Sentimentanalyse ist eine sehr einfache Form und funktioniert gut mit kurzen Textsequenzen mit eindeutiger Aussage. Die Analyse ist nicht kontextabhängig und stark abhängig von der Qualität und Umfang des Wortschatzes, dafür aber einfach zu implementieren und benötigt wenig Ressourcen.

**Sentiment Transformer:**\
Die Sentimentanalyse mit einem Transformer basiert auf der Verwendung eines vortrainierten Transformer-Modells, das auf großen Mengen an Textdaten trainiert wurde.
Auch hier hängt die Qualität der Sentimentanalyse von den spezifischen Anforderungen und dem verfügbaren Datensatz ab. Das passende Modell ist dann in der Lage komplexe Kontexte zu erfassen und nuanciertere Sentimentanalysen durchzuführen im Vergleich zu einfachen regelbasierten Ansätzen.\
Für das Transparenzregister wurde ebenfalls ein Transformer von [Hugging Face](www.huggingface.co) ausgewählt, welcher auf *Text Classification* und *deutsche Sprache* trainiert ist.

![Sentiment Model](Abbildungen/Sentiment_Transformer.PNG)

Die Auswahl eines Modells erfolgt experimentell, die erste Wahl viel auf[oliverguhr/german-sentiment-bert](https://huggingface.co/oliverguhr/german-sentiment-bert).\
Dieses Modell wurde auf 1312 Nachrichtenartikeln angewendet, die Ergebnisse lauteten:
|positiv|negativ|neutral|
|--------|--------|--------|
|22|192|1098|

Die Ergebnisse spiegeln nicht die Stimmung der Nachrichtenartikel wider, deshalb wurde mit einem weiteren Modell experimentiert.\
Die zweite Wahl war [bardsai/finance-sentiment-de-base](https://huggingface.co/bardsai/finance-sentiment-de-base) und führte zu folgenden Ergebissen bei Anwendung auf 3655 Nachrichtenartikel:

|positiv|negativ|neutral|
|--------|--------|--------|
|1637|1854|164|

Dieses Ergebnis hat subjektiv die Stimmung besser repräsentiert, weshalb dieses Modell ausgewählt wurde.


**Aufbau und Funktion der Pipeline**\
Die folgende Abbildung zeigt die schematische Struktur der Sentiment-Pipeline, zur Verarbeitung von Textdaten und Speicherung in einer MongoDB. Die Pipeline lässt sich in vier Modulen unterteilen, welche sequentiell abgearbeitet werden.

![Sentiment Pipeline](Abbildungen/Sentiment_Pipeline.PNG)

*Mongo Connect:* Verbindet sich mit der StagingDB unter Verwendung der Credentials in der secrets.json und den Verbindungseinstellungen in der .env-Datei.

*Mongo Read:* Diese Funktion ließt alle Dokumente der StagingDB und überprüft, ob das Attribut *sentiment* vorhanden ist. Die IDs der Dokumente ohne dieses Attribut werden für den Sentiment Service gespeichert.

*Sentiment Service:* Der Sentiment-Service verarbeitet sequentiell alle Dokumente, bei welchem das Attribut *sentiment* fehlt. Dabei übergibt er den zu analysierenden Text und die gewünschte Sentiment Methode. Der aufgerufene Service analysiert den Text und gibt ein Dictionary mit dem Sentiment und dem Score zurück.

*Mongo Update Documents:* Abschließend wird das aktuelle Dokument aktualisiert, d.h. es wird ein Attribut *sentiment* mit dem Ergebnis-Dictionary angelegt und geschrieben. Dieses Attribut signalisiert bei einem erneutem Durchlauf der Pipeline, dass dieses Dokument bereits verarbeitet wurde.
