Zum Inhalt

Data Science & Machine Learning Engineering auf Linux

Erstellt: 05.11.2026  ·  Zuletzt aktualisiert: 06.11.2026  ·  Nejat Hakan

Offizielles Python-Logo

Modernes Data Science und Machine Learning Engineering finden nicht in isolierten Tabellenkalkulationen statt – sie laufen auf Linux. Von der Vorverarbeitung gigantischer Log-Streams auf der CLI und GPU-beschleunigtem Training tiefer neuronaler Netze bis hin zu verteilten Apache Spark-Clustern und containerisierten Inference-APIs: Linux ist das native Betriebssystem des weltweiten KI-Ökosystems.

Dieser Leitfaden schlägt die Brücke zwischen klassischem Linux-Systems-Engineering und angewandter Data Science: von reproduzierbaren Python-Umgebungen (uv & venv) über POSIX-Kommandozeilen-Datenanalysen und explorative Visualisierung bis hin zu leckfreien Scikit-learn-Pipelines, Deep Learning, PySpark, MLflow und Microservice-Deployments mit FastAPI.


1. Warum Linux Data Science & MLOps dominiert

Die Modellentwicklung direkt in derselben Betriebssystemumgebung durchzuführen, in der Modelle später produktiv laufen, verhindert Reibungsverluste zwischen Experiment und Live-Betrieb:

%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
    subgraph Ingest ["1️⃣ Ingestion & CLI-Wrangling"]
        Raw[("🗄️ Roh-Logs, APIs & SQL")] --> CLI["🐧 POSIX-Stream-Tools<br/>(curl, awk, grep, sed, jq)"]
    end

    subgraph Explore ["2️⃣ Exploration & Feature-Eng."]
        CLI --> DF["🐼 Pandas 2 / Polars<br/>Vektorisierte DataFrames"]
        DF --> Viz["📊 Matplotlib & Seaborn<br/>Statistische EDA"]
    end

    subgraph Train ["3️⃣ Modellierung & Tracking"]
        Viz --> Pipe["⚙️ Scikit-learn / PyTorch<br/>Leckfreie ML-Pipelines"]
        Pipe --> MLF["📈 MLflow Registry<br/>Metriken, Params & Artefakte"]
    end

    subgraph Serve ["4️⃣ Produktions-Serving"]
        MLF ==> API["🚀 FastAPI + Uvicorn<br/>Containerisierte Inference-API"]
    end

Vier zentrale Gründe für Linux in Data Science

  • Stream-Verarbeitung in Hochgeschwindigkeit


    Bevor eine 20 GB CSV-Datei in den RAM geladen wird, filtern, zerlegen und aggregieren POSIX-Tools (awk, sed, grep, cut, sort, jq) Millionen von Zeilen in Sekunden mit minimalem Speicherverbrauch.

  • Native GPU- & CUDA-Beschleunigung


    Erstklassige Kernel-Treiber für NVIDIA CUDA / cuDNN und AMD ROCm für maximalen Tensor-Durchsatz in PyTorch, TensorFlow und lokalen LLMs ohne Virtualisierungs-Overhead.

  • Isolierte, reproduzierbare Toolchains


    Nahtlose Integration schneller Umgebungsmanager (uv, venv, conda) mit System-Compilern (gcc, gfortran, openblas) und rootless Podman / Docker-Containern.

  • 100 % Produktionsparität


    Über 95 % aller Cloud-ML-Instanzen, Kubernetes-Cluster und Apache-Spark-Knoten laufen unter Linux. Lokale Entwicklung unter Linux garantiert absolute Kompatibilität mit dem Produktivsystem.


2. Einrichtung einer High-Performance Data-Science-Umgebung

Schritt 1: Build-Tools & Systembibliotheken installieren

Zahlreiche rechenintensive Python-Bibliotheken (numpy, scipy, scikit-learn, psycopg2) binden kompilierte C-, C++- und Fortran-Bibliotheken ein (OpenBLAS, LAPACK). Installiere vorab die Basispakete:

Terminal: Build-Toolchain unter Debian/Ubuntu installieren
sudo apt update && sudo apt upgrade -y
sudo apt install -y build-essential git curl wget jq \
  python3 python3-pip python3-venv python3-dev \
  libopenblas-dev liblapack-dev gfortran \
  libssl-dev libffi-dev libsqlite3-dev
Terminal: Build-Toolchain unter Fedora/RHEL installieren
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y git curl wget jq python3 python3-pip python3-devel \
  openblas-devel lapack-devel gcc-gfortran openssl-devel libffi-devel sqlite-devel
Terminal: Build-Toolchain unter Arch Linux installieren
sudo pacman -Syu --needed base-devel git curl wget jq python python-pip openblas lapack

Schritt 2: Isolierte Python-Umgebung erstellen (uv vs. venv)

Goldene Regel: Niemals sudo pip install ausführen!

Moderne Linux-Distributionen erzwingen PEP 668 (externally-managed-environment), um Systemtools (apt, unattended-upgrades) vor inkompatiblen Abhängigkeiten zu schützen. Arbeite immer in einer virtuellen Umgebung!

uv (von Astral) ist die moderne, extrem schnelle Alternative zu pip und venv. Es löst Abhängigkeiten blitzschnell auf:

Terminal: Workspace mit uv aufsetzen
# 1. uv installieren (falls noch nicht vorhanden)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. Projektverzeichnis und virtuelle Umgebung anlegen
mkdir -p ~/ds-workspace && cd ~/ds-workspace
uv venv .venv
source .venv/bin/activate

# 3. Den Data-Science-Stack in Sekundenschnelle installieren
uv pip install numpy pandas matplotlib seaborn scikit-learn jupyterlab mlflow fastapi uvicorn joblib

Nativ in jeder Standard-Python-3-Installation ohne Zusatzwerkzeuge enthalten:

Terminal: Workspace mit Standard-venv aufsetzen
mkdir -p ~/ds-workspace && cd ~/ds-workspace
python3 -m venv .venv
source .venv/bin/activate

python -m pip install --upgrade pip
pip install numpy pandas matplotlib seaborn scikit-learn jupyterlab mlflow fastapi uvicorn joblib

Das Python Data-Science-Ökosystem im Überblick

Bibliothek Primäre Aufgabe Zentrale Datenstruktur
NumPy Vektorisierte Numerik & Lineare Algebra in C ndarray (Mehrdimensionales zusammenhängendes Array)
Pandas 2.x Tabellarische Datenanalyse, Zeitreihen & Joins DataFrame & Series (NumPy / PyArrow Backend)
📊 Matplotlib & Seaborn Publikationsreife 2D-Plots & statistische Visualisierung Figure, Axes & deklarative Diagramme
Scikit-learn Klassisches Machine Learning, Preprocessing & Evaluation Estimator (.fit()), Transformer (.transform()), Pipeline
PyTorch / Keras Deep Learning, neuronale Netze & GPU-Autograd torch.Tensor, nn.Module, keras.Sequential
MLflow Experiment-Tracking, Hyperparameter-Logging & Model Registry Runs, Metrics, Parameters & Model Artifacts

JupyterLab auf einem Headless-Linux-Server via SSH-Tunnel betreiben

Wenn JupyterLab auf einem Remote-Server läuft, exponiere Port 8888 niemals direkt ins Internet! Binde JupyterLab an 127.0.0.1 und tunnel den Port sicher via SSH:

Terminal auf dem Laptop: SSH-Port-Forwarding
# 1. Auf dem Remote-Server (in aktivierter .venv):
jupyter lab --no-browser --ip=127.0.0.1 --port=8888

# 2. Auf dem lokalen Rechner: Port 8888 via SSH weiterleiten
ssh -N -L 8888:127.0.0.1:8888 nejathakan
Danach öffnest du einfach http://localhost:8888 im Browser deines Laptops!


3. Daten-Inspektion direkt auf der Kommandozeile

Erfahrene Data Scientists untersuchen und bereinigen Rohdaten vorab direkt in der Linux-Shell, bevor sie Python starten:

%%{init: {'flowchart': {'wrappingWidth': 600}}}%%
flowchart TB
    Download["🌐 curl / wget<br/>CSV oder JSON-API laden"] ==> Inspect["🔍 file / head / wc -l<br/>Encoding & Zeilenanzahl prüfen"]
    Inspect ==> Filter["✂️ grep / cut / sed<br/>Filtern & Delimiter bereinigen"]
    Filter ==> Aggregate["⚡ awk / sort / uniq -c<br/>Gruppieren & Aggregieren"]

Der POSIX-CLI-Spickzettel für Data Scientists

Aufgabe Befehl Vorteil gegenüber Python
Zeilen sofort zählen wc -l dataset.csv Zählt 50 Mio. Zeilen in ~1 Sekunde ohne RAM-Bedarf.
Header & Stichprobe ansehen head -n 5 dataset.csv && tail -n 5 dataset.csv Zeigt Trennzeichen, fehlende Kopfzeilen und Leerzeilen sofort.
Encoding & Nullwerte prüfen file -i dataset.csv und grep -c ",," dataset.csv Erkennt UTF-8/ISO-8859-Konflikte und leere Felder sofort.
Spalten extrahieren cut -d',' -f1,5 dataset.csv \| head Schneidet Spalten direkt als Bytestream aus.
Kategorien zählen cut -d',' -f5 iris.csv \| grep -v '^$' \| sort \| uniq -c Schnelles Äquivalent zu value_counts() in reiner Bash.
Schnelle Mittelwerte berechnen awk -F',' 'NR>1 {sum+=$1; n++} END {print sum/n}' iris.csv Berechnet den Durchschnitt von Spalte 1 in einem Durchlauf (O(1) RAM).
JSON-APIs zerlegen (jq) curl -s https://api.example.com/items \| jq '.[] \| {id, price}' Formatiert und filtert verschachtelte JSON-Daten direkt im Terminal.

Hands-On Lab 1: Datenanalyse ohne Python am UCI Iris-Datensatz

Lade den bekannten UCI Iris-Datensatz und analysiere Zeilenanzahl, Klassenverteilung und Mittelwerte rein über die Shell:

Terminal: Stream-Analyse mit wget, cut, sort, uniq und awk
mkdir -p ~/ds-workspace/labs && cd ~/ds-workspace/labs
wget -q "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data" -O iris.csv

# Leerzeilen am Dateiende direkt mit sed entfernen
sed -i '/^$/d' iris.csv

# Zeilenanzahl und erste 5 Zeilen prüfen
wc -l iris.csv
head -n 5 iris.csv

# Klassenverteilung ermitteln (wie value_counts)
echo "--- Klassenverteilung ---"
cut -d',' -f5 iris.csv | sort | uniq -c

# Durchschnittliche Kelchblattlänge (Spalte 1) pro Art (Spalte 5) via awk berechnen
echo "--- Durchschnittliche Kelchblattlänge pro Art ---"
awk -F',' '{sum[$5] += $1; count[$5]++} END {for (s in sum) printf "%-18s : %.2f cm (n=%d)\n", s, sum[s]/count[s], count[s]}' iris.csv

4. Explorative Datenanalyse (EDA) & Statistische Visualisierung

Nach der ersten Vorprüfung laden wir den Datensatz in Pandas für mehrdimensionale EDA und erzeugen Visualisierungen mit Seaborn und Matplotlib.

Die richtige Visualisierung wählen

  • Verteilung: sns.histplot & sns.boxplot


    Zeigt Schiefe, Modalität, Median, Interquartilsabstand (IQR) und Extremwerte numerischer Features über verschiedene Gruppen hinweg.

  • Beziehungen: sns.scatterplot & sns.pairplot


    Visualisiert paarweise Wechselwirkungen zwischen Merkmalen und Trennbarkeiten über Zielklassen hinweg (hue='Species').

  • Korrelationen: sns.heatmap(df.corr())


    Quantifiziert Pearson- (r) oder Spearman-Rangkorrelationen zwischen allen Variablen, um Kollinearität und prädiktive Merkmale aufzudecken.

  • Aggregationen: sns.barplot & sns.countplot


    Vergleicht Klassenverteilungen und Gruppenmittelwerte inklusive Konfidenzintervallen für diskrete kategoriale Variablen.

Hands-On Lab 2: Automatisierte EDA & Plot-Generierung auf Headless Linux

Auf einem Linux-Server benötigt man keine grafische Oberfläche – plt.savefig(..., dpi=300) exportiert hochauflösende Grafiken direkt auf die Festplatte:

~/ds-workspace/labs/eda_iris.py
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

cols = ["SepalLengthCm", "SepalWidthCm", "PetalLengthCm", "PetalWidthCm", "Species"]
df = pd.read_csv("iris.csv", header=None, names=cols)

print("=== 1. Dimensionen & Fehlwerte ===")
print(f"Shape: {df.shape}")
print(df.isna().sum())

print("\n=== 2. Statistische Zusammenfassung ===")
print(df.groupby("Species").describe().T)

# Korrelationsmatrix & Pairplot als 300-DPI-Grafiken speichern
sns.set_theme(style="whitegrid")

plt.figure(figsize=(7, 5))
corr = df.drop(columns=["Species"]).corr()
sns.heatmap(corr, annot=True, cmap="crest", fmt=".2f", linewidths=0.5)
plt.title("Iris Feature Correlation Matrix")
plt.tight_layout()
plt.savefig("iris_correlation_heatmap.png", dpi=300)
plt.close()

pair_grid = sns.pairplot(df, hue="Species", markers=["o", "s", "D"])
pair_grid.fig.suptitle("Pairwise Feature Separability", y=1.02)
pair_grid.savefig("iris_pairplot.png", dpi=300)
print("\n[OK] 'iris_correlation_heatmap.png' und 'iris_pairplot.png' gespeichert!")

5. Feature Engineering & Leckfreie ML-Pipelines

Echte Datensätze enthalten fehlende Werte (NaN), unausgewogene Verteilungen und unkodierte Textspalten.

Der häufigste Fehler im Machine Learning: Data Leakage!

Wende niemals scaler.fit_transform(df) oder Median-Imputationen auf den gesamten Datensatz vor dem Train/Test-Split an! Wenn Mittelwerte oder Standardabweichungen über den gesamten Datensatz berechnet werden, fließen Informationen des Testdatensatzes in die Trainingsphase ein – das führt zu scheinbar exzellenten, in der Praxis jedoch instabilen Modellen.

Die saubere Lösung: Scikit-learn ColumnTransformer + Pipeline

Statt manueller pd.get_dummies()-Aufrufe, die beim Fehlen einer Kategorie im Testset scheitern, kapseln wir alle Vorverarbeitungsschritte in einer Scikit-learn Pipeline:

%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
    Raw["📋 Raw Input DataFrame<br/>Age, Fare, Pclass, Sex, Title"]
    CT["🔀 ColumnTransformer<br/>Feature-Typen aufteilen"]

    subgraph Preprocessing ["Scikit-learn Vorverarbeitungs-Pipelines"]
        direction TB
        N_Branch["🔢 Numerischer Zweig<br/>(Age, Fare, FamilySize)"]
        N1["SimpleImputer (strategy='median')"]
        N2["StandardScaler (Zero Mean, Unit Variance)"]
        N_Branch --> N1 --> N2

        C_Branch["🏷️ Kategorialer Zweig<br/>(Pclass, Sex, Embarked, Title)"]
        C1["SimpleImputer (strategy='most_frequent')"]
        C2["OneHotEncoder (handle_unknown='ignore')"]
        C_Branch --> C1 --> C2
    end

    Concat["🧱 Einheitliche Feature-Matrix (Concat)"]
    Model["🌲 Estimator-Modell (RandomForest / LogReg)"]

    Raw ==> CT
    CT --> N_Branch
    CT --> C_Branch
    N2 ==> Concat
    C2 ==> Concat
    Concat ==> Model

Warum diese Architektur überlegen ist:

  1. Null Data Leakage: Bei pipeline.fit(X_train, y_train) werden Mediane und Skalierungswerte ausschließlich aus X_train gelernt und bei pipeline.predict(X_test) exakt auf X_test angewendet.
  2. Robustheit im Live-Betrieb: Erhält die Produktions-API eine bisher ungesehene Kategorie, ignoriert OneHotEncoder(handle_unknown='ignore') diese sicher ohne Fehlermeldung.
  3. Einfache Serialisierung: Die gesamte Pipeline (Imputer + Encoder + Skalierer + Modell) wird in einer einzigen .joblib-Datei abgelegt.

Hands-On Lab 3: Leckfreie Pipeline am Titanic-Datensatz trainieren

~/ds-workspace/labs/titanic_pipeline.py
import pandas as pd
import numpy as np
import joblib
from sklearn.model_selection import train_test_split, StratifiedKFold, GridSearchCV
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, roc_auc_score

# 1. Titanic-Datensatz laden
url = "https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv"
df = pd.read_csv(url)

# 2. Feature-Engineering auf Zeilenebene (Sicher vor dem Split, da nur eigene Werte genutzt werden)
df["FamilySize"] = df["SibSp"] + df["Parch"] + 1
df["IsAlone"] = (df["FamilySize"] == 1).astype(int)
df["Title"] = df["Name"].str.extract(r" ([A-Za-z]+)\.", expand=False)
df["Title"] = df["Title"].where(df["Title"].isin(["Mr", "Miss", "Mrs", "Master"]), "Rare")

features = ["Pclass", "Sex", "Age", "Fare", "Embarked", "FamilySize", "IsAlone", "Title"]
X = df[features]
y = df["Survived"]

# 3. Stratified Train/Test Split VOR jeglicher Imputation oder Skalierung!
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)

# 4. Leckfreie Vorverarbeitungspipelines aufbauen
num_cols = ["Age", "Fare", "FamilySize"]
cat_cols = ["Pclass", "Sex", "Embarked", "Title", "IsAlone"]

num_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler())
])

cat_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore", sparse_output=False))
])

preprocessor = ColumnTransformer([
    ("num", num_transformer, num_cols),
    ("cat", cat_transformer, cat_cols)
])

full_pipeline = Pipeline([
    ("prep", preprocessor),
    ("clf", RandomForestClassifier(random_state=42, n_jobs=-1))
])

# 5. Hyperparameter-Tuning via 5-Fold Stratified Cross-Validation
param_grid = {
    "clf__n_estimators": [100, 200],
    "clf__max_depth": [5, 10, None],
    "clf__min_samples_leaf": [1, 2, 4]
}

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
grid = GridSearchCV(full_pipeline, param_grid, cv=cv, scoring="roc_auc", n_jobs=-1)
grid.fit(X_train, y_train)

print(f"Bester CV ROC-AUC: {grid.best_score_:.4f}")
print(f"Beste Parameter  : {grid.best_params_}")

# 6. Finale Auswertung auf dem unberührten Test-Set
best_pipeline = grid.best_estimator_
y_pred = best_pipeline.predict(X_test)
y_prob = best_pipeline.predict_proba(X_test)[:, 1]

print(f"\nTest-Set ROC-AUC: {roc_auc_score(y_test, y_prob):.4f}")
print("\nKlassifikationsbericht:\n", classification_report(y_test, y_pred))

# 7. Gesamte Pipeline in einem einzigen Artefakt speichern
joblib.dump(best_pipeline, "titanic_production_pipeline.joblib")
print("[OK] Pipeline gespeichert unter 'titanic_production_pipeline.joblib'")

6. Modell-Evaluationsmetriken im Überblick

Problemtyp Metrik Bedeutung Wann anwenden?
Klassifikation Precision (Präzision) TP / (TP + FP) Wenn False Positives teuer sind (z. B. legitime E-Mail als Spam blockiert).
Klassifikation Recall (Sensitivität) TP / (TP + FN) Wenn False Negatives gefährlich sind (z. B. medizinische Diagnose, Einbruchserkennung).
Klassifikation F1-Score 2 × (Precision × Recall) / (Precision + Recall) Harmonisches Mittel aus Precision & Recall; ideal bei unbalancierten Klassen.
Klassifikation ROC-AUC Fläche unter der TPR/FPR-Kurve Schwellenwertunabhängige Modellgüte (1.0 = perfekt, 0.5 = Zufall).
Regression MAE (1/n) × Σ \|y_true - y_pred\| Durchschnittlicher Fehler in Originaleinheiten; robust gegen Ausreißer.
Regression RMSE √((1/n) × Σ (y_true - y_pred)²) Bestraft große Abweichungen durch Quadrierung besonders hart.
Regression R² (Bestimmtheitsmaß) 1 - (SS_res / SS_tot) Anteil der erklärten Varianz der Zielvariablen (1.0 = optimal).

7. Deep Learning auf Linux (PyTorch & Keras/TensorFlow)

Für unstrukturierte Daten (Bilder, Audio, Texte, Zeitreihen) lernen tiefe neuronale Netze Repräsentationen hierarchisch:

Bietet explizite Kontrolle über GPU-Allokation (cuda/cpu), dynamische Berechnungsgraphen (autograd) und Trainingsschleifen:

PyTorch CNN & CUDA Device-Check
import torch
import torch.nn as nn

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"PyTorch Version {torch.__version__} läuft auf: {device}")

class MnistCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Conv2d(32, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Dropout(0.5),
            nn.Linear(64 * 7 * 7, 10)
        )

    def forward(self, x):
        return self.classifier(self.features(x))

model = MnistCNN().to(device)
print(model)

Ermöglicht schnelles Prototyping mit integrierten Trainingsschleifen (model.fit):

Keras / TensorFlow MNIST Classifier
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = (x_train.astype("float32") / 255.0)[..., None]
x_test  = (x_test.astype("float32") / 255.0)[..., None]

model = keras.Sequential([
    keras.Input(shape=(28, 28, 1)),
    layers.Conv2D(32, kernel_size=(3, 3), activation="relu"),
    layers.MaxPooling2D(pool_size=(2, 2)),
    layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
    layers.MaxPooling2D(pool_size=(2, 2)),
    layers.Flatten(),
    layers.Dropout(0.5),
    layers.Dense(10, activation="softmax"),
])

model.compile(loss="sparse_categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
model.fit(x_train, y_train, batch_size=128, epochs=5, validation_split=0.1)
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print(f"Test Accuracy: {test_acc:.4f}")

8. Verteilte Big-Data-Verarbeitung mit PySpark

Übersteigen Datensätze den RAM eines Einzelsystems, verteilt Apache Spark (PySpark) Berechnungen über alle CPU-Kerne oder Cluster-Knoten via Lazy Evaluation:

~/ds-workspace/labs/pyspark_log_analytics.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import regexp_extract, col, desc

spark = SparkSession.builder \
    .appName("LinuxLogAnalytics") \
    .master("local[*]") \
    .getOrCreate()

spark.sparkContext.setLogLevel("WARN")

log_regex = r'^(\S+) \S+ \S+ \[([\w:/]+\s[+\-]\d{4})\] "(\S+) (\S+)\s*\S*" (\d{3}) (\d+)'

raw_logs = spark.read.text("/var/log/nginx/access.log")
parsed_df = raw_logs.select(
    regexp_extract("value", log_regex, 1).alias("client_ip"),
    regexp_extract("value", log_regex, 3).alias("http_method"),
    regexp_extract("value", log_regex, 4).alias("endpoint"),
    regexp_extract("value", log_regex, 5).cast("integer").alias("status_code"),
    regexp_extract("value", log_regex, 6).cast("long").alias("bytes")
).filter(col("client_ip") != "")

parsed_df.groupBy("status_code").count().orderBy(desc("count")).show()
parsed_df.groupBy("client_ip").count().orderBy(desc("count")).limit(10).show()

spark.stop()

9. MLOps: Experiment-Tracking (MLflow) & FastAPI-Deployment

Ein Modell im Jupyter-Notebook erzeugt noch keinen Produktionswert. In einer professionellen MLOps-Pipeline: 1. MLflow protokolliert Hyperparameter, Metriken und Modellversionen beim Training. 2. FastAPI + Pydantic stellt die gespeicherte Pipeline als typsicheren, schnellen ASGI-Dienst mit automatischer Dokumentation (/docs) bereit.

%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
    Client(("🌐 Client / Backend App")) ==>|"POST /predict (JSON)"| FastAPI

    subgraph Container ["📦 Rootless-Container (FastAPI + Uvicorn)"]
        FastAPI["⚡ FastAPI Endpoint"] ==> Pydantic["🛡️ Pydantic v2 Schema<br/>(Strikte Typvalidierung)"]
        Pydantic ==> Joblib["⚙️ Scikit-learn Pipeline<br/>(Imputer → OneHot → RF)"]
    end

    Joblib ==>|"JSON: prediction + probability"| Client

Hands-On Lab 4: Modellbereitstellung via FastAPI

Da wir die gesamte Pipeline inklusive Vorverarbeitung in titanic_production_pipeline.joblib gespeichert haben, benötigt die API keinerlei manuelle Transformationen:

~/ds-workspace/labs/app.py
from fastapi import FastAPI
from pydantic import BaseModel, Field
import joblib
import pandas as pd

app = FastAPI(title="Titanic Survival Inference API", version="1.0.0")
pipeline = joblib.load("titanic_production_pipeline.joblib")

class PassengerInput(BaseModel):
    Pclass: int = Field(..., ge=1, le=3, examples=[1])
    Sex: str = Field(..., examples=["female"])
    Age: float = Field(..., ge=0, le=120, examples=[38.0])
    Fare: float = Field(..., ge=0, examples=[71.28])
    Embarked: str = Field(..., examples=["C"])
    FamilySize: int = Field(..., ge=1, examples=[2])
    IsAlone: int = Field(..., ge=0, le=1, examples=[0])
    Title: str = Field(..., examples=["Mrs"])

@app.post("/predict")
def predict_survival(passenger: PassengerInput):
    df_input = pd.DataFrame([passenger.model_dump()])
    pred = int(pipeline.predict(df_input)[0])
    prob = float(pipeline.predict_proba(df_input)[0][1])
    return {
        "survived": pred,
        "label": "Survived" if pred == 1 else "Did Not Survive",
        "survival_probability": round(prob, 4)
    }

API testen:

Terminal: Server starten & via curl abfragen
uvicorn app:app --host 127.0.0.1 --port 8000 &

curl -s -X POST "http://127.0.0.1:8000/predict" \
     -H "Content-Type: application/json" \
     -d '{"Pclass": 1, "Sex": "female", "Age": 38.0, "Fare": 71.28, "Embarked": "C", "FamilySize": 2, "IsAlone": 0, "Title": "Mrs"}' | jq .


10. Fehlerbehebung & Häufige Fragen

1. Warum bricht mein Python-Prozess mit Killed (Exit-Code 137) beim Laden großer Dateien ab?

Ursache: Der Out-Of-Memory (OOM) Killer des Linux-Kernels (dmesg -T | grep -i oom) hat den Prozess beendet, da pd.read_csv() oft das 3- bis 5-fache der Dateigröße im RAM belegt.

Lösung: 1. Nutze die PyArrow-Engine: pd.read_csv("big.csv", engine="pyarrow"). 2. Filtere Spalten vorab mit awk oder nutze speicherschonendes Streaming mit DuckDB oder Polars (pl.scan_csv()).

2. Wie verifiziere ich die NVIDIA-GPU-Nutzung in PyTorch unter Linux?

Lösung: Prüfe zunächst den Treiber über nvidia-smi und teste danach in Python:

nvidia-smi
python3 -c "import torch; print('CUDA verfügbar:', torch.cuda.is_available(), '| Device:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'None')"

3. Fehler error: externally-managed-environment bei pip install

Ursache: Debian 12 und Ubuntu 24.04 erzwingen PEP 668, um Konflikte mit dem Paketmanager apt zu vermeiden.

Lösung: Erstelle immer eine virtuelle Umgebung (uv venv .venv && source .venv/bin/activate oder python3 -m venv .venv).