...
Die Qualität der Beschreibungstexte und Volltexte wurden mit verschiedenen Metriken bestimmt.
Beschreibungstexte
...
Volltexte
...
Interpretation der Textqualität
Die Volltexte sind mit durchschnittlich 860 Zeichen länger als die Beschreibungstexte (228 Zeichen).
Die Verteilung der Sprache Sprachen ist zwischen beiden Feldern vergleichbar.
Unterschiede ergeben sich in der Sentiment-Analyse. Beschreibungstexte sind weniger emotional formuliert, was für eine höhere Qualität im Hinblick auf den Aspekt Neutralität sprechen kann.
Der SMOG-Index zeigt, dass die Beschreibungstexte mit weniger formaler Bildung zu verstehen sind, als die Volltexte. Ein Grund hierfür könnte die redaktionelle Aufbereitung sein.
Beschreibungstexte
...
Volltexte
...
Verteilung der Daten
Ein Großteil der Datensätze ist den Disziplinen: Informatik, Chemie, Physik, Mathematik und Darstellendes Spiel zuzuordnen. Fast alle Inhalte wurden auf der Skala mit 4 oder 5 bewertet, was jedoch im Rahmen der Erwartungen liegt.
...
Testdurchführung
Für die Testdurchführung wurde ein Python-Script genutzt, das ausgewählte Metadatenfelder aus JSON an den Prompt übergibt und das Scoring sowie die Begründung dokumentiert. Anschließend werden diverse Metriken aus dem Vergleich von Originaldaten und KI-generierten Daten gebildet. Eine hohe Übereinstimmung würde auf eine erfolgreiche Bewertung durch die KI hindeuten.
Das Python-Script ist in der Anlage zu finden.
Testergebnisse
Ein Großteil der Datensätze ist den Disziplinen: Informatik, Chemie, Physik, Mathematik und Darstellendes Spiel zuzuordnen. Fast alle Inhalte wurden auf der Skala mit 4 oder 5 bewertet, was jedoch im Rahmen der Erwartungen liegt.
...
Anlage
Tool für die Volltextgenerierung
Code Block |
---|
# Web Scraper and Enricher for URL in JSON using Goose3
# requirements: pip install beautifulsoup4 streamlit requests goose3
import streamlit as st
import json
import os
import datetime
import time
from goose3 import Goose
# List of file extensions to skip (e.g. audio and video formats)
SKIPPED_EXTENSIONS = ['.mp4', '.mp3', '.avi', '.mpeg', '.mov', '.wmv', '.flv']
# Function to scrape a webpage using Goose3 and return the extracted information
def scrape_page(url, follow_redirect=True):
goose = Goose() # Initialize Goose3 with default settings
try:
if isinstance(url, list): # Check if URL is in list format and convert to string
url = url[0]
# Skip URLs with certain extensions
if any(url.lower().endswith(ext) for ext in SKIPPED_EXTENSIONS):
return "skipped", None
article = goose.extract(url=url)
# Extract the relevant information
full_text = article.cleaned_text # Cleaned full text
title = article.title # Extracted title
summary = article.meta_description[:500] if article.meta_description else full_text[:500] # Meta description as summary or first 500 chars
keywords = article.meta_keywords # Extracted meta keywords
top_image = article.top_image.src if article.top_image else None # URL of the top image
# Fallback: If full text is missing, use meta description
if not full_text:
full_text = article.meta_description or "No full text available for this page."
return {
'title': title,
'full_text': full_text,
'summary': summary,
'keywords': keywords,
'top_image': top_image,
'url': url
}
except Exception as e:
st.error(f"Error scraping {url}: {e}")
return None
# Function to process the JSON file and enrich it with scraped data
def process_json(data, url_field, timeout, save_folder, follow_redirect=True, skip_media_files=True, crawl_all=True, num_records=10):
try:
total_records = len(data)
records_to_process = total_records if crawl_all else min(num_records, total_records)
st.write(f"Processing {records_to_process} out of {total_records} records...")
enriched_data = []
for i, record in enumerate(data[:records_to_process], 1):
# Display the progress message for processing records
st.info(f"Processing record {i}/{records_to_process}", icon="ℹ️")
# Extract URL using the selected field
try:
url = eval(f"record{url_field}")
# If URL is in list format, convert it to a string (take the first URL)
if isinstance(url, list):
url = url[0]
except Exception as e:
st.warning(f"No valid URL found for record {i}/{records_to_process}. Skipping... (Error: {e})", icon="⚠️")
continue
if url:
# Skip media files if the option is selected
if skip_media_files and any(url.lower().endswith(ext) for ext in SKIPPED_EXTENSIONS):
st.warning(f"Skipping media file URL #{i}: {url}", icon="⚠️")
continue
# Display the message for scraping the current URL
st.success(f"Scraping URL #{i}: {url}", icon="🟢")
scraped_data = scrape_page(url, follow_redirect=follow_redirect)
if scraped_data != "skipped" and scraped_data:
# Only display the summary preview message without additional status info
if scraped_data['summary']:
st.success(scraped_data['summary'][:250] + "..." if len(scraped_data['summary']) > 250 else scraped_data['summary'])
# Add scraped data to the record
record['additional_data'] = {
'title': scraped_data['title'],
'full_text': scraped_data['full_text'],
'summary': scraped_data['summary'],
'keywords': scraped_data['keywords'],
'top_image': scraped_data['top_image'],
'final_url': scraped_data['url']
}
enriched_data.append(record)
# Introduce delay for the given timeout
time.sleep(timeout)
# Save the enriched JSON file
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
enriched_file_name = os.path.join(save_folder, f"data_enriched_{timestamp}.json")
with open(enriched_file_name, 'w', encoding='utf-8') as f:
json.dump(enriched_data, f, ensure_ascii=False, indent=4)
st.success(f"Enriched data saved as {enriched_file_name}", icon="🟢")
except Exception as e:
st.error(f"Error processing JSON file: {e}")
# Function to extract available fields from JSON structure
def extract_fields(data):
field_set = set()
# Recursive function to explore the JSON structure
def recurse_json(obj, parent_key=''):
if isinstance(obj, dict):
for key, value in obj.items():
new_key = f"{parent_key}['{key}']" if parent_key else f"['{key}']"
field_set.add(new_key)
recurse_json(value, new_key)
elif isinstance(obj, list):
for item in obj:
recurse_json(item, parent_key)
recurse_json(data)
return sorted(list(field_set))
# Streamlit UI
st.title('JSON Web Scraper and Enricher using Goose3')
# Upload JSON file
uploaded_file = st.file_uploader("Choose a JSON file", type="json")
if uploaded_file:
try:
# Load the JSON data
data = json.load(uploaded_file)
# Extract all field paths from the JSON structure
available_fields = extract_fields(data)
# Allow the user to choose a URL field
url_field = st.selectbox("Select the URL field", available_fields, index=available_fields.index("['properties']['ccm:wwwurl']"))
# Other options for processing
timeout = st.number_input("Enter delay between requests (seconds)", min_value=0, value=0)
save_folder = st.text_input("Folder to save enriched JSON", value=".")
follow_redirects = st.checkbox("Follow redirects", value=True)
# Option to skip media files (audio/video)
skip_media_files = st.checkbox("Skip media files (e.g., .mp4, .mp3, .avi)", value=True)
# Option to process all records or only a limited number
crawl_all = st.checkbox("Crawl all records", value=True)
num_records = st.number_input("Number of records to process", min_value=1, value=10, disabled=crawl_all)
if st.button("Start Processing"):
process_json(data, url_field, timeout, save_folder, follow_redirects, skip_media_files, crawl_all, num_records)
except Exception as e:
st.error(f"Error loading JSON file: {e}")
|
Tool für die Bewertung
Python-Script zur Durchführung des Tests und der Bewertung mittels LLM
Code Block |
---|
# Script für die Bewertung der Neutralität (0-5)
# Anforderungen: pip install streamlit openai pydantic scikit-learn scipy matplotlib numpy
# Start: streamlit run app.py
import streamlit as st
import json
import os
import matplotlib.pyplot as plt
from datetime import datetime
from openai import OpenAI
from pydantic import BaseModel, ValidationError
from sklearn.metrics import precision_score, f1_score, mean_absolute_error, mean_squared_error, r2_score
from scipy.stats import pearsonr
import numpy as np
import time
# Überprüfen, ob scipy installiert ist
try:
from scipy.stats import pearsonr
except ImportError:
st.error("Die Bibliothek 'scipy' ist nicht installiert. Bitte installieren Sie sie mit `pip install scipy`.")
st.stop()
# OpenAI-Client initialisieren
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
# Definiere das Schema der erwarteten Antwort mit Pydantic
class EvaluationResponse(BaseModel):
score: int
reasoning: str
# Funktion zur Erzeugung eines Zeitstempels
def get_timestamp():
return datetime.now().strftime("%Y%m%d_%H%M%S")
# Funktion zum Speichern von Dateien
def save_file(filename, content, mode='w'):
with open(filename, mode) as f:
f.write(content)
# Funktion zur Erstellung und Speicherung von Grafiken
def save_figure(filename):
plt.savefig(filename)
plt.close()
# Funktion zur farblichen Hervorhebung der Statusmeldungen
def color_status(deviation):
if deviation == 0:
return "green"
elif deviation == 1:
return "yellow"
elif deviation == 2:
return "orange"
else:
return "red"
# Funktion zur Erstellung einer verkürzten JSON
def create_shortened_json(data, ai_scores, neutralness_scores, ai_reasonings, selected_fields):
shortened_data = []
# Sicherstellen, dass alle Listen gleich lang sind
for i in range(min(len(data), len(ai_scores), len(ai_reasonings))):
item = {}
for field in selected_fields:
# Extraktion der Werte basierend auf dem Feldpfad
value = data[i].get(field, None)
item[field] = value
item.update({
"original_neutralness_score": neutralness_scores[i],
"ai_neutralness_score": ai_scores[i],
"ai_reasoning": ai_reasonings[i]
})
shortened_data.append(item)
return shortened_data
# Funktion zur Rekursiven Extraktion von Feldnamen
def extract_field_names(data):
field_names = set()
def recurse(item):
if isinstance(item, dict):
for key, value in item.items():
field_names.add(key)
recurse(value)
elif isinstance(item, list):
for element in item:
recurse(element)
for entry in data:
recurse(entry)
return sorted(field_names)
# Funktion zur Erstellung einer verkürzten JSON für Edgecases
def create_shortened_json_edgecases(data, ai_scores, neutralness_scores, ai_reasonings, selected_fields):
return create_shortened_json(data, ai_scores, neutralness_scores, ai_reasonings, selected_fields)
# Hauptfunktion zur Analyse der Texte
def analyze_texts(texts, neutralness_scores, model_choice, prompt, num_texts, data, json_filename, selected_fields):
ai_scores = []
ai_reasonings = []
deviations = []
deviation_counts = {0: 0, 1: 0, 2: 0, 3: 0}
failed_attempts = 0
special_cases = [] # Sammlung der Sonderfälle (Abweichung von 2 oder mehr)
progress_bar = st.progress(0)
status_text = st.empty()
texts_to_evaluate = texts[:num_texts] # Nur eine bestimmte Anzahl bewerten
total_texts = len(texts_to_evaluate)
for i, text in enumerate(texts_to_evaluate):
if text:
# Kombiniere die ausgewählten Felder in einem einzigen Text
combined_text = f"{prompt}\n\n" + "\n".join([f"{field}: {value}" for field, value in text.items() if value])
messages = [
{"role": "system", "content": "You are an AI tasked with evaluating the neutrality and constitutionality of educational content."},
{"role": "user", "content": combined_text}
]
try:
# Verwende response_format mit json_schema und füge den 'name'-Parameter hinzu
response = client.chat.completions.create(
model=model_choice,
messages=messages,
response_format={
"type": "json_schema",
"json_schema": {
"name": "neutrality_evaluation", # Required name field
"schema": {
"type": "object",
"properties": {
"score": {"type": "integer"},
"reasoning": {"type": "string"}
},
"required": ["score", "reasoning"],
"additionalProperties": False
}
}
},
max_tokens=4000
)
# Antwort als JSON validieren
evaluation_response = EvaluationResponse.parse_raw(response.choices[0].message.content)
ai_score = evaluation_response.score
ai_reasoning = evaluation_response.reasoning
ai_scores.append(ai_score)
ai_reasonings.append(ai_reasoning)
# Vergleich der AI-Werte mit den Originalwerten
original_score = neutralness_scores[i]
deviation = abs(ai_score - original_score)
deviations.append(deviation)
# Abweichung speichern
if deviation == 0:
deviation_counts[0] += 1
elif deviation == 1:
deviation_counts[1] += 1
elif deviation == 2:
deviation_counts[2] += 1
special_cases.append(data[i]) # Als Sonderfall hinzufügen
else:
deviation_counts[3] += 1
special_cases.append(data[i]) # Als Sonderfall hinzufügen
# Farbliche Statusmeldung je nach Abweichung
color = color_status(deviation)
st.markdown(f"<span style='color:{color}'>Text {i + 1} bewertet: AI Score = {ai_score} (Original: {original_score})</span>", unsafe_allow_html=True)
except ValidationError as ve:
st.error(f"Validierungsfehler bei Text {i + 1}: {ve}")
failed_attempts += 1
except Exception as e:
st.error(f"Fehler bei der Verarbeitung von Text {i + 1}: {e}")
failed_attempts += 1
# Fortschritt aktualisieren
progress = (i + 1) / total_texts
progress_bar.progress(progress)
status_text.text(f"Verarbeite Text {i + 1}/{total_texts}...")
time.sleep(0.1)
# Berechnung der durchschnittlichen Abweichung
avg_deviation = np.mean(deviations) if deviations else 0
# Berechnung zusätzlicher Metriken
mae = mean_absolute_error(neutralness_scores[:len(ai_scores)], ai_scores)
mse = mean_squared_error(neutralness_scores[:len(ai_scores)], ai_scores)
rmse = np.sqrt(mse)
r2 = r2_score(neutralness_scores[:len(ai_scores)], ai_scores)
pearson_corr, _ = pearsonr(neutralness_scores[:len(ai_scores)], ai_scores)
# Berechnung von Precision und F1-Score
y_true = neutralness_scores[:len(ai_scores)] # Originale Scores
y_pred = ai_scores[:len(ai_scores)] # AI generierte Scores
precision = precision_score(y_true, y_pred, average='weighted', zero_division=0)
f1 = f1_score(y_true, y_pred, average='weighted')
# Verkürzte JSON erstellen
shortened_data = create_shortened_json(data, ai_scores, neutralness_scores, ai_reasonings, selected_fields)
# Speichern der verkürzten JSON-Daten, strukturiert mit Indents
shortened_filename = f"{json_filename}_shortened_{get_timestamp()}.json"
with open(shortened_filename, 'w') as f:
json.dump({
"data": shortened_data,
"overall_results": {
"precision": precision,
"f1_score": f1,
"mean_absolute_error": mae,
"mean_squared_error": mse,
"root_mean_squared_error": rmse,
"r2_score": r2,
"pearson_correlation": pearson_corr,
"avg_deviation": avg_deviation
}
}, f, indent=4) # JSON strukturiert mit Indent
st.success(f"Verkürzte JSON gespeichert als {shortened_filename}")
# Verkürzte JSON für Edgecases erstellen (Abweichung >= 2)
if special_cases:
edgecase_indices = [i for i, dev in enumerate(deviations) if dev >= 2]
shortened_edgecases = create_shortened_json_edgecases(
[data[i] for i in edgecase_indices],
[ai_scores[i] for i in edgecase_indices],
[neutralness_scores[i] for i in edgecase_indices],
[ai_reasonings[i] for i in edgecase_indices],
selected_fields
)
# Speichern der Edgecases JSON, strukturiert mit Indents
edgecases_filename = f"{json_filename}_shortened_edge_cases_{get_timestamp()}.json"
with open(edgecases_filename, 'w') as f:
json.dump({
"data": shortened_edgecases,
"overall_results": {
"precision": precision_score([neutralness_scores[i] for i in edgecase_indices],
[ai_scores[i] for i in edgecase_indices], average='weighted', zero_division=0),
"f1_score": f1_score([neutralness_scores[i] for i in edgecase_indices],
[ai_scores[i] for i in edgecase_indices], average='weighted'),
"mean_absolute_error": mean_absolute_error([neutralness_scores[i] for i in edgecase_indices],
[ai_scores[i] for i in edgecase_indices]),
"mean_squared_error": mean_squared_error([neutralness_scores[i] for i in edgecase_indices],
[ai_scores[i] for i in edgecase_indices]),
"root_mean_squared_error": np.sqrt(mean_squared_error([neutralness_scores[i] for i in edgecase_indices],
[ai_scores[i] for i in edgecase_indices])),
"r2_score": r2_score([neutralness_scores[i] for i in edgecase_indices],
[ai_scores[i] for i in edgecase_indices]),
"pearson_correlation": pearsonr([neutralness_scores[i] for i in edgecase_indices],
[ai_scores[i] for i in edgecase_indices])[0],
"avg_deviation": np.mean([deviations[i] for i in edgecase_indices])
}
}, f, indent=4) # JSON strukturiert mit Indent
st.success(f"Edgecases JSON gespeichert als {edgecases_filename}")
# Speichern der Ergebnisse mit zusätzlichen Metriken als Textdatei
report_filename = f"{json_filename}_evaluation_report_{get_timestamp()}.txt"
with open(report_filename, 'w') as f:
f.write(f"Precision: {precision:.2f}\n")
f.write(f"F1 Score: {f1:.2f}\n")
f.write(f"Mean Absolute Error (MAE): {mae:.2f}\n")
f.write(f"Mean Squared Error (MSE): {mse:.2f}\n")
f.write(f"Root Mean Squared Error (RMSE): {rmse:.2f}\n")
f.write(f"R² Score: {r2:.2f}\n")
f.write(f"Pearson-Korrelation: {pearson_corr:.2f}\n")
f.write(f"Average Deviation: {avg_deviation:.2f}\n")
f.write(f"Failed Attempts: {failed_attempts}\n")
f.write(f"Deviation Counts: {deviation_counts}\n")
st.success(f"Bericht gespeichert als {report_filename}")
# Rückgabe der berechneten Metriken
return ai_scores, deviations, avg_deviation, failed_attempts, deviation_counts, special_cases, precision, f1, mae, mse, rmse, r2, pearson_corr
# Streamlit UI zur Benutzerinteraktion
st.title('Neutralitätsbewertung AI') # Titel entsprechend dem vorherigen Titel beibehalten
uploaded_file = st.file_uploader("Lade eine JSON-Datei hoch", type="json")
if uploaded_file:
# JSON-Dateiname extrahieren
json_filename = os.path.splitext(uploaded_file.name)[0]
# JSON-Daten laden
data = json.load(uploaded_file)
# Rekursive Extraktion aller Feldnamen
field_names = extract_field_names(data)
# Standardvorgabe: 'properties_ccm:general_description', falls vorhanden
default_fields = ['properties_ccm:general_description'] if 'properties_ccm:general_description' in field_names else []
# Mehrfachauswahl für die Metadatenfelder
selected_fields = st.multiselect(
"Wähle die Metadatenfelder für die Bewertung aus:",
options=field_names,
default=default_fields
)
if not selected_fields:
st.warning("Bitte wähle mindestens ein Metadatenfeld aus.")
else:
# Extrahieren der relevanten Felder basierend auf der Auswahl
def extract_selected_fields(json_data, selected_fields):
extracted = []
for item in json_data:
record = {}
for field in selected_fields:
value = item.get(field, None)
record[field] = value
extracted.append(record)
return extracted
texts_to_evaluate = extract_selected_fields(data, selected_fields)
# Extrahieren der neutralness_scores
def extract_metadata(json_data, key):
return [int(item.get(key, "0")) for item in json_data if item.get(key, "0").isdigit()]
neutralness_scores = extract_metadata(data, "properties_ccm:oeh_quality_neutralness")
if not neutralness_scores:
st.error("Keine gültigen Neutralitätswerte gefunden in den Daten.")
else:
# Verteilung von ccm:oeh_quality_neutralness anzeigen
st.subheader("Verteilung der Neutralitätswerte (Original)")
fig, ax = plt.subplots()
ax.hist(neutralness_scores, bins=range(0, 7), align='left', rwidth=0.8, color='skyblue', edgecolor='black')
ax.set_xlabel('Neutralitätswert')
ax.set_ylabel('Häufigkeit')
st.pyplot(fig)
# Speichern der Verteilungsgrafik
dist_filename = f"{json_filename}_neutralness_distribution_{get_timestamp()}.png"
save_figure(dist_filename)
# Auswahl, ob alle oder nur bestimmte Datensätze bewertet werden sollen
evaluate_all = st.checkbox("Alle Datensätze bewerten", value=True)
max_evaluations = len(data) # Standardmäßig alle Datensätze bewerten
if not evaluate_all:
max_evaluations = st.number_input("Anzahl der zu bewertenden Datensätze", min_value=1, max_value=len(data), value=10)
# Eingabefeld für den Prompt (Standardprompt ist vorausgefüllt)
default_prompt = """
Bewerte die Neutralität und Verfassungskonformität eines Bildungsinhalts auf einer Skala von 0 bis 5 anhand der folgenden Kriterien.
Berücksichtigen Sie aktuelle Diskussionen über Menschenrechte, das Grundgesetz der Bundesrepublik Deutschland und die Anforderungen an schulische Bildung. Begründen Sie die Bewertung ausführlich, indem Sie erklären, warum Sie sich für den gewählten Wert entschieden haben und warum andere Werte nicht zutreffen.
## Bewertungskriterien
- **0 (manipulativ oder verfassungswidrig):** Der Text enthält manipulative Aussagen, die darauf abzielen, den Leser in eine bestimmte Richtung zu drängen. Der Text verstößt gegen verfassungsrechtliche Grundprinzipien, wie die Achtung der Menschenwürde oder demokratische Grundwerte (z. B. Förderung von Hassrede, Diskriminierung, Gewalt oder Verherrlichung verfassungswidriger Symbole).
- **1 (unneutral oder problematisch):** Der Text zeigt deutliche Anzeichen von Einseitigkeit oder Voreingenommenheit. Inhalte sind zwar verfassungskonform, aber sie verletzen möglicherweise das Neutralitätsgebot, indem sie eine extreme oder engstirnige Sichtweise auf sensible Themen wie Politik, Religion, Menschenrechte, Sexualität oder Identität vertreten, ohne Alternativen oder Gegensätze zu beleuchten.
- **2 (ideologisch eingefärbt, aber verfassungskonform):** Der Text ist von einer bestimmten ideologischen Ausrichtung geprägt, aber die Inhalte sind verfassungskonform und korrekt. Es wird jedoch nur eine Seite des Themas beleuchtet, und es fehlen ausgewogene Perspektiven.
- **3 (ideologisch eingefärbt, aber transparent und pluralistisch):** Der Text hat eine erkennbare ideologische Ausrichtung, aber diese wird transparent gemacht. Der Autor stellt offen dar, dass es alternative Perspektiven gibt, die ebenfalls verfassungskonform sind. Der Inhalt wahrt das Gebot der Pluralität, wie es in der schulischen Bildung gefordert wird.
- **4 (neutrale und ausgewogene Formulierung):** Der Text verwendet eine sachliche und ausgewogene Formulierung ohne erkennbare ideologische Voreingenommenheit. Er beleuchtet das Thema von verschiedenen verfassungskonformen Standpunkten und berücksichtigt Menschenrechte und demokratische Werte, wie sie im Grundgesetz verankert sind.
- **5 (neutrale Formulierung von einem unabhängigen, wissenschaftlich fundierten Autor):** Der Text ist vollständig neutral formuliert, ohne ideologische oder politische Voreingenommenheit. Er stammt von einem nachweislich unabhängigen und wissenschaftlich fundierten Autor oder einer Institution, die keine politischen oder ideologischen Interessen vertritt. Der Inhalt entspricht höchsten Standards der Verfassungstreue, Menschenrechte und Anforderungen der Schulbildung.
# Steps
1. **Analysiere den Text:** Identifizieren Sie alle relevanten Informationen und Aspekte, die auf Neutralität und Verfassungskonformität hin überprüft werden müssen.
2. **Bewerten Sie anhand der Skala:** Wählen Sie den zutreffenden Wert von 0 bis 5 basierend auf Ihrer Analyse.
3. **Begründung:** Erläutern Sie Ihre Bewertung. Begründen Sie in maximal drei Sätzen, warum Sie sich für diesen Wert entschieden haben. Gehen Sie darauf ein, warum andere Werte nicht passend sind.
# Output Format
- Eine einzelne Zahl von 0 bis 5.
- Eine nachfolgende Begründung in maximal 3 Sätzen.
# Beispiele
**Input:**
Text über das Grundgesetz der Bundesrepublik Deutschland.
**Output:**
4
"Der Text beinhaltet eine sachliche und ausgewogene Formulierung verschiedener verfassungskonformer Standpunkte und respektiert demokratische Werte. Er vermeidet extreme Sichtweisen und beleuchtet diverse Perspektiven. Andere Werte treffen nicht zu, da keine einseitige ideologische Ausrichtung erkennbar ist."
"""
user_prompt = st.text_area("Passe deinen Prompt an", value=default_prompt, height=600)
# Modellwahl hinzufügen (gpt-4o-mini als Standard und gpt-4o-2024-08-06)
model_choice = st.selectbox(
"Wähle das Modell für die Bewertung aus",
options=["gpt-4o-mini", "gpt-4o-2024-08-06"],
index=0
)
# Button zur Auslösung der Analyse
if st.button("Bewerte Texte"):
ai_scores, deviations, avg_deviation, failed_attempts, deviation_counts, special_cases, precision, f1, mae, mse, rmse, r2, pearson_corr = analyze_texts(
texts_to_evaluate, neutralness_scores, model_choice, user_prompt, max_evaluations, data, json_filename, selected_fields)
# Verteilung der Originaldaten am Ende erneut anzeigen
st.subheader("Verteilung der Neutralitätswerte (Original)")
fig, ax = plt.subplots()
ax.hist(neutralness_scores, bins=range(0, 7), align='left', rwidth=0.8, color='skyblue', edgecolor='black')
ax.set_xlabel('Neutralitätswert')
ax.set_ylabel('Häufigkeit')
st.pyplot(fig)
# Speichern der Originaldaten-Verteilungsgrafik
final_dist_filename = f"{json_filename}_neutralness_distribution_final_{get_timestamp()}.png"
save_figure(final_dist_filename)
# Verteilung der AI-Scores anzeigen
st.subheader("Verteilung der Neutralitätswerte (AI)")
fig, ax = plt.subplots()
ax.hist(ai_scores, bins=range(0, 7), align='left', rwidth=0.8, color='lightgreen', edgecolor='black')
ax.set_xlabel('AI Neutralitätswert')
ax.set_ylabel('Häufigkeit')
st.pyplot(fig)
# Speichern der AI-Scores-Verteilungsgrafik
ai_dist_filename = f"{json_filename}_ai_neutralness_distribution_{get_timestamp()}.png"
save_figure(ai_dist_filename)
# Abweichungsgrafik erstellen
st.subheader("Abweichung der AI-Werte von den Originalwerten")
fig, ax = plt.subplots()
categories = ["Keine Abweichung", "1 Abweichung", "2 Abweichungen", "3+ Abweichungen"]
counts = [deviation_counts[0], deviation_counts[1], deviation_counts[2], deviation_counts[3]]
colors = ["green", "yellow", "orange", "red"]
ax.bar(categories, counts, color=colors)
ax.set_xlabel('Abweichungskategorie')
ax.set_ylabel('Anzahl der Texte')
st.pyplot(fig)
# Speichern der Abweichungsgrafik
deviation_filename = f"{json_filename}_deviation_distribution_{get_timestamp()}.png"
save_figure(deviation_filename)
# Grafiken für Precision, F1 Score und zusätzliche Metriken erstellen
st.subheader("Modellleistungsmetriken")
# Precision und F1 Score
fig, ax = plt.subplots()
metrics = ["Precision", "F1 Score"]
scores = [precision, f1]
ax.bar(metrics, scores, color=['blue', 'green'])
ax.set_ylabel('Score')
for i, v in enumerate(scores):
ax.text(i, v + 0.01, f"{v:.2f}", ha='center', va='bottom')
st.pyplot(fig)
# Speichern der Precision und F1 Score Grafik
precision_f1_filename = f"{json_filename}_precision_f1_score_{get_timestamp()}.png"
save_figure(precision_f1_filename)
# Zusätzliche Metriken mit Erklärungen anzeigen
st.markdown("""
**Mean Absolute Error (MAE):**
- Durchschnittlicher absoluter Unterschied zwischen den vorhergesagten und den tatsächlichen Werten.
- **Interpretation:** Je niedriger der MAE, desto genauer sind die Vorhersagen des Modells.
**Mean Squared Error (MSE):**
- Durchschnitt der quadrierten Differenzen zwischen den vorhergesagten und den tatsächlichen Werten.
- **Interpretation:** Betont größere Fehler stärker. Ein niedriger MSE zeigt eine gute Modellleistung an.
**Root Mean Squared Error (RMSE):**
- Quadratwurzel des MSE.
- **Interpretation:** Gibt den Fehler in derselben Einheit wie die Zielvariable an. Niedrigere Werte sind besser.
**R² Score:**
- Maß dafür, wie gut die Varianz der Zielvariable durch das Modell erklärt wird.
- **Interpretation:** Werte nahe 1 bedeuten, dass das Modell die Varianz gut erklärt.
**Pearson-Korrelation:**
- Maß für die lineare Korrelation zwischen den vorhergesagten und den tatsächlichen Werten.
- **Interpretation:** Werte nahe 1 oder -1 zeigen eine starke lineare Beziehung. Werte nahe 0 bedeuten keine lineare Beziehung.
**Precision:**
- Maß für die Genauigkeit der positiven Vorhersagen.
- **Interpretation:** Ein höherer Precision-Wert zeigt, dass weniger falsche positive Vorhersagen gemacht werden.
**F1 Score:**
- Harmonisches Mittel von Precision und Recall.
- **Interpretation:** Ein ausgewogenes Maß, das sowohl die Genauigkeit als auch die Vollständigkeit der Vorhersagen berücksichtigt.
**Abweichung:**
- Differenz zwischen den AI-bewerteten und den Originalwerten.
- **Interpretation:** Niedrigere Abweichungswerte deuten auf eine höhere Übereinstimmung zwischen AI und Originalbewertungen hin.
""")
# Anzeige der zusätzlichen Metriken
st.markdown(f"**Mean Absolute Error (MAE):** {mae:.2f}")
st.markdown(f"**Mean Squared Error (MSE):** {mse:.2f}")
st.markdown(f"**Root Mean Squared Error (RMSE):** {rmse:.2f}")
st.markdown(f"**R² Score:** {r2:.2f}")
st.markdown(f"**Pearson-Korrelation:** {pearson_corr:.2f}")
st.markdown(f"**Precision:** {precision:.2f}")
st.markdown(f"**F1 Score:** {f1:.2f}")
st.markdown(f"**Durchschnittliche Abweichung:** {avg_deviation:.2f}")
# Grafik für zusätzliche Metriken
st.subheader("Zusätzliche Modellleistungsmetriken")
metrics = ["MAE", "MSE", "RMSE", "R²", "Pearson-Korrelation", "Precision", "F1 Score", "Abweichung"]
scores = [mae, mse, rmse, r2, pearson_corr, precision, f1, avg_deviation]
colors = ['cyan', 'magenta', 'orange', 'purple', 'grey', 'blue', 'green', 'red']
fig, ax = plt.subplots(figsize=(12, 6))
ax.bar(metrics, scores, color=colors)
ax.set_ylabel('Wert')
ax.set_ylim(0, max(scores) * 1.2) # Anpassung des y-Bereichs
for i, v in enumerate(scores):
ax.text(i, v + max(scores)*0.01, f"{v:.2f}", ha='center', va='bottom')
st.pyplot(fig)
# Speichern der zusätzlichen Metriken Grafik
additional_metrics_filename = f"{json_filename}_additional_metrics_{get_timestamp()}.png"
save_figure(additional_metrics_filename)
# Durchschnittliche Abweichung grafisch darstellen
st.subheader("Durchschnittliche Abweichung")
fig, ax = plt.subplots()
ax.bar(["Durchschnittliche Abweichung"], [avg_deviation], color='purple')
ax.set_ylabel('Abweichung')
ax.text(0, avg_deviation + 0.01, f"{avg_deviation:.2f}", ha='center', va='bottom')
st.pyplot(fig)
# Speichern der Durchschnittlichen Abweichungsgrafik
avg_deviation_filename = f"{json_filename}_avg_deviation_{get_timestamp()}.png"
save_figure(avg_deviation_filename)
st.write(f"**Precision:** {precision:.2f}")
st.write(f"**F1 Score:** {f1:.2f}")
st.write(f"**Mean Absolute Error (MAE):** {mae:.2f}")
st.write(f"**Mean Squared Error (MSE):** {mse:.2f}")
st.write(f"**Root Mean Squared Error (RMSE):** {rmse:.2f}")
st.write(f"**R² Score:** {r2:.2f}")
st.write(f"**Pearson-Korrelation:** {pearson_corr:.2f}")
st.write(f"**Durchschnittliche Abweichung:** {avg_deviation:.2f}")
# Speichern der Auswertung als Textdatei
report_filename = f"{json_filename}_evaluation_report_{get_timestamp()}.txt"
with open(report_filename, 'w') as f:
f.write(f"Precision: {precision:.2f}\n")
f.write(f"F1 Score: {f1:.2f}\n")
f.write(f"Mean Absolute Error (MAE): {mae:.2f}\n")
f.write(f"Mean Squared Error (MSE): {mse:.2f}\n")
f.write(f"Root Mean Squared Error (RMSE): {rmse:.2f}\n")
f.write(f"R² Score: {r2:.2f}\n")
f.write(f"Pearson-Korrelation: {pearson_corr:.2f}\n")
f.write(f"Average Deviation: {avg_deviation:.2f}\n")
f.write(f"Failed Attempts: {failed_attempts}\n")
f.write(f"Deviation Counts: {deviation_counts}\n")
st.success(f"Bericht gespeichert als {report_filename}")
# Zusammenfassung der Ergebnisse
st.markdown("""
### **Zusammenfassung der Ergebnisse**
- **Precision:** Gibt an, wie genau die positiven Vorhersagen des Modells sind.
- **F1 Score:** Harmonisches Mittel von Precision und Recall, gibt ein ausgewogenes Maß der Modellleistung.
- **Mean Absolute Error (MAE):** Durchschnittlicher absoluter Unterschied zwischen den vorhergesagten und den tatsächlichen Werten.
- **Mean Squared Error (MSE):** Durchschnitt der quadrierten Differenzen zwischen den vorhergesagten und den tatsächlichen Werten.
- **Root Mean Squared Error (RMSE):** Quadratwurzel des MSE, gibt den Fehler in derselben Einheit wie die Zielvariable an.
- **R² Score:** Maß dafür, wie gut die Varianz der Zielvariable durch das Modell erklärt wird.
- **Pearson-Korrelation:** Maß für die lineare Korrelation zwischen den vorhergesagten und den tatsächlichen Werten.
- **Durchschnittliche Abweichung:** Durchschnittlicher Unterschied zwischen den AI-bewerteten und den Originalwerten.
""")
# Erklärung der Metriken
st.markdown("""
---
### **Erklärung der Metriken**
**Mean Absolute Error (MAE):**
- Durchschnittlicher absoluter Unterschied zwischen den vorhergesagten und den tatsächlichen Werten.
- **Interpretation:** Je niedriger der MAE, desto genauer sind die Vorhersagen des Modells.
**Mean Squared Error (MSE):**
- Durchschnitt der quadrierten Differenzen zwischen den vorhergesagten und den tatsächlichen Werten.
- **Interpretation:** Betont größere Fehler stärker. Ein niedriger MSE zeigt eine gute Modellleistung an.
**Root Mean Squared Error (RMSE):**
- Quadratwurzel des MSE.
- **Interpretation:** Gibt den Fehler in derselben Einheit wie die Zielvariable an. Niedrigere Werte sind besser.
**R² Score:**
- Maß dafür, wie gut die Varianz der Zielvariable durch das Modell erklärt wird.
- **Interpretation:** Werte nahe 1 bedeuten, dass das Modell die Varianz gut erklärt.
**Pearson-Korrelation:**
- Maß für die lineare Korrelation zwischen den vorhergesagten und den tatsächlichen Werten.
- **Interpretation:** Werte nahe 1 oder -1 zeigen eine starke lineare Beziehung. Werte nahe 0 bedeuten keine lineare Beziehung.
**Precision:**
- Maß für die Genauigkeit der positiven Vorhersagen.
- **Interpretation:** Ein höherer Precision-Wert zeigt, dass weniger falsche positive Vorhersagen gemacht werden.
**F1 Score:**
- Harmonisches Mittel von Precision und Recall.
- **Interpretation:** Ein ausgewogenes Maß, das sowohl die Genauigkeit als auch die Vollständigkeit der Vorhersagen berücksichtigt.
**Abweichung:**
- Differenz zwischen den AI-bewerteten und den Originalwerten.
- **Interpretation:** Niedrigere Abweichungswerte deuten auf eine höhere Übereinstimmung zwischen AI und Originalbewertungen hin.
---
""")
|