"# Skalierbare Methoden der Künstlichen Intelligenz\n",
"Dr. Charlotte Debus (<charlotte.debus@kit.edu>) \n",
"Dr. Markus Götz (<markus.goetz@kit.edu>) \n",
"Dr. Marie Weiel (<marie.weiel@kit.edu>) \n",
"Dr. Kaleb Phipps (<kaleb.phipps@kit.edu>) \n",
"\n",
"## Übung 3 am 17.12.24: Paralleles Ensemble Learning mit verteilten Random Forests\n",
"In der dritten Übung beschäftigen wir uns mit parallelem Ensemble Learning am Beispiel von Random Forests (siehe Vorlesung vom 28.11.2024). \n",
"Dazu verwenden wir [Scikit-learn](https://scikit-learn.org/stable/), die meist genutzte Software-Bibliothek für klassisches maschinelles Lernen in Python. Scikit-learn bietet verschiedene Klassifikations-, Regressions- und Clustering-Algorithmen, darunter Support-Vektor-Maschinen, Random Forests, Gradient Boosting (wie XGBoost), k-Means und DBSCAN. Das Package basiert auf den bekannten Python-Bibliotheken [NumPy](https://numpy.org) und [SciPy](https://scipy.org). \n",
"\n",
"Unser Ziel ist es, den [SUSY-Datensatz](https://archive.ics.uci.edu/ml/datasets/SUSY) mithilfe eines verteilten Random Forests zu klassifizieren. Der SUSY-Datensatz besteht aus 5M Monte-Carlo-Samples supersymmetrischer und nicht-supersymmetrischer Teilchenkollisionen. Der Signalprozess ist die Produktion elektrisch geladener, supersymmetrischer Teilchen, die in $W$-Bosonen und ein elektrisch neutrales, supersymmetrisches Teilchen zerfallen, welches für den Detektor unsichtbar ist. Bei diesem Klassifikationsproblem möchten wir anhand von insgesamt 18 Features zwischen diesem Signalprozess, der supersymmetrische Teilchen erzeugt, und einem Hintergrundprozess, der dies nicht tut, unterscheiden.\n",
"Bei den ersten acht Features handelt es sich um kinematische Eigenschaften, die von Detektoren in Teilchenbeschleunigern direkt gemessen werden können. Die verbleibenden zehn Features sind Funktionen der ersten acht und wurden von Physiker*innen abgeleitet, um eine Unterscheidung zwischen den beiden Klassen zu erleichtern. \n",
"Sie finden den kompletten SUSY-Datensatz im CSV-Format im Workspace auf dem Cluster: `/pfs/work7/workspace/scratch/ku4408-VL-ScalableAI/data/SUSY.csv`. Er enthält insgesamt $n_\\mathrm{samples}$ = 5M Samples. Die erste Spalte enthält die Klassenbezeichnungen (\"Labels\" bzw. \"Targets\"; 1 für Signal, 0 für Hintergrund) gefolgt von den $n_\\text{features}$ = 18 Features.\n",
"\n",
"Zur binären Klassifizierung dieses Datensatzes in die oben genannten Klassen nutzen wir einen verteilten [Random Forest](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html#sklearn.ensemble.RandomForestClassifier.predict) mit insgesamt $n_\\text{trees}$ Bäumen. Wir betrachten ein Setting mit $p$ Prozessoren. Jeder Prozessor $i=1,\\cdots,p$ hält \n",
"- einen Sub Random Forest mit $n_\\text{trees}^{i}=n_\\text{trees}/p$ Bäumen (vorbehaltlich Load Balancing), wobei $\\sum_{i=1}^p n_\\text{trees}^i = n_\\text{trees}$,\n",
"- eine gewisse Anzahl $n_\\text{train}^i = n_\\text{train}/p$ aller Trainings-Samples (vorbehaltlich Load Balancing) als lokalen Trainingsdatensatz sowie \n",
"- den kompletten, global einheitlichen Testdatensatz mit $n_\\text{test}$ Samples. \n",
"\n",
"Das bedeutet, dass es einen global einheitlichen Train-Test-Split $f_\\text{train}$ des gesamten Datensatzes gibt, wobei die Testdaten auf allen Prozessoren gleichermaßen vollständig vorliegen, wohingegen jeder Prozessor eine gewisse Anzahl an Samples aus dem globalen Trainingsdatensatz als lokal unterschiedliche Trainingsdaten erhält. Es gilt also:\n",
"Jeder Prozessor trainiert seinen Sub Random Forest auf seinem lokalen Trainingsdatensatz. Zum Schluss werden die Teil-Ergebnisse über einen Majority Vote zu einem finalen Ergebnis kombiniert."
]
},
{
"cell_type": "markdown",
"id": "de426257",
"metadata": {},
"source": [
"### Aufgabe 1: Daten laden\n",
"Um unser Modell zu trainieren und zu testen, müssen wir zunächst die verfügbaren Daten laden. Nach dem Train-Test-Split soll jeder Prozessor eine gewisse Anzahl an Datenpunkten aus dem globalen Trainingsdatensatz *mit Zurücklegen* ziehen. Diese Samples bilden den jeweiligen lokalen Trainingsdatensatz. Dazu gibt es verschiedene Ansätze: \n",
"1. Alle Prozessoren laden einen entsprechenden Teil der Daten aus dem globalen Trainingsdatensatz echt-parallel: Dataloader `load_data_csv_parallel()`\n",
"2. Der Root-Prozess lädt den kompletten globalen Trainingsdatensatz und verteilt diese Samples entsprechend an alle Prozessoren mit `Scatterv`: Dataloader `load_data_csv_root()`\n",
"\n",
"Implementieren und testen Sie die zwei Varianten der Dataloader. Untenstehend finden Sie dazu Code-Gerüste der Funktionen `load_data_csv_parallel()` und `load_data_csv_root()` inklusive einiger Hilfsfunktionen sowie ein Test- und ein beispielhaftes Submitskript. Vollziehen Sie diese nach und vervollständigen Sie den Code an den markierten Stellen. **Normale Kommentare mit '#' beschreiben wie üblich den Code, in Zeilen mit '##' müssen Sie Code ergänzen.** Erstellen Sie entsprechende Python-Skripte mit allen Funktionsdefinitionen bzw. dem Main-Teil. Führen Sie das Test-Skript auf 2 und 4 Knoten des bwUniClusters aus. "
"source ${VENVDIR}/bin/activate # Activate your virtual environment.\n",
"\n",
"mpirun python ${PYDIR}/test_dataloaders.py"
]
},
{
"cell_type": "markdown",
"id": "7c0910e3",
"metadata": {},
"source": [
"### Aufgabe 2\n",
"Laden Sie die Daten mit den verschiedenen Dataloadern und trainieren Sie einen verteilen Random Forest auf 1, 2, 4, 8, 16, 32 und 64 rein CPU-basierten Knoten des bwUniClusters. Untenstehend finden Sie ein entsprechendes Code-Gerüst sowie ein beispielhaftes Submit-Bash-Skript für 2 Knoten. Vervollständigen Sie den Code an den markierten Stellen. \n",
"\n",
"**Normale Kommentare mit '#' beschreiben wie üblich den Code, in Zeilen mit '##' müssen Sie Code ergänzen.**\n",
"Erstellen Sie ein vollständiges Python-Skript mit allen Funktionsdefinitionen sowie dem Main-Teil. Messen und vergleichen Sie:\n",
"- die Datenladedauer\n",
"- die Trainingsdauer\n",
"- die lokale Accuracy der Sub Random Forests auf den Testdaten (auf jedem Rank)\n",
"- die globale Accuracy des finalen globalen Random Forest auf den Testdaten\n",
"\n",
"Plotten Sie die oben genannten Größen über die Anzahl der verwendeten Knoten. Was fällt Ihnen auf? Welche Trends können Sie beobachten und wie lassen sich diese erklären?"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "deed7fc1",
"metadata": {},
"outputs": [],
"source": [
"# IMPORTS\n",
"# Put together with helper functions and main (see below) into `distributed_forest.py`.\n",
"source ${VENVDIR}/bin/activate # Activate your virtual environment.\n",
"\n",
"mpirun python ${PYDIR}/distributed_forest.py --dataloader parallel # Use truly parallel dataloader.\n",
"mpirun python ${PYDIR}/distributed_forest.py --dataloader root # Use root-based dataloader.\n"
]
}
],
"metadata": {
"jupytext": {
"main_language": "python"
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
%% Cell type:markdown id:2432be9b tags:
# Skalierbare Methoden der Künstlichen Intelligenz
Dr. Charlotte Debus (<charlotte.debus@kit.edu>)
Dr. Markus Götz (<markus.goetz@kit.edu>)
Dr. Marie Weiel (<marie.weiel@kit.edu>)
Dr. Kaleb Phipps (<kaleb.phipps@kit.edu>)
## Übung 3 am 17.12.24: Paralleles Ensemble Learning mit verteilten Random Forests
In der dritten Übung beschäftigen wir uns mit parallelem Ensemble Learning am Beispiel von Random Forests (siehe Vorlesung vom 28.11.2024).
Dazu verwenden wir [Scikit-learn](https://scikit-learn.org/stable/), die meist genutzte Software-Bibliothek für klassisches maschinelles Lernen in Python. Scikit-learn bietet verschiedene Klassifikations-, Regressions- und Clustering-Algorithmen, darunter Support-Vektor-Maschinen, Random Forests, Gradient Boosting (wie XGBoost), k-Means und DBSCAN. Das Package basiert auf den bekannten Python-Bibliotheken [NumPy](https://numpy.org) und [SciPy](https://scipy.org).
Unser Ziel ist es, den [SUSY-Datensatz](https://archive.ics.uci.edu/ml/datasets/SUSY) mithilfe eines verteilten Random Forests zu klassifizieren. Der SUSY-Datensatz besteht aus 5M Monte-Carlo-Samples supersymmetrischer und nicht-supersymmetrischer Teilchenkollisionen. Der Signalprozess ist die Produktion elektrisch geladener, supersymmetrischer Teilchen, die in $W$-Bosonen und ein elektrisch neutrales, supersymmetrisches Teilchen zerfallen, welches für den Detektor unsichtbar ist. Bei diesem Klassifikationsproblem möchten wir anhand von insgesamt 18 Features zwischen diesem Signalprozess, der supersymmetrische Teilchen erzeugt, und einem Hintergrundprozess, der dies nicht tut, unterscheiden.
Bei den ersten acht Features handelt es sich um kinematische Eigenschaften, die von Detektoren in Teilchenbeschleunigern direkt gemessen werden können. Die verbleibenden zehn Features sind Funktionen der ersten acht und wurden von Physiker*innen abgeleitet, um eine Unterscheidung zwischen den beiden Klassen zu erleichtern.
Sie finden den kompletten SUSY-Datensatz im CSV-Format im Workspace auf dem Cluster: `/pfs/work7/workspace/scratch/ku4408-VL-ScalableAI/data/SUSY.csv`. Er enthält insgesamt $n_\mathrm{samples}$ = 5M Samples. Die erste Spalte enthält die Klassenbezeichnungen ("Labels" bzw. "Targets"; 1 für Signal, 0 für Hintergrund) gefolgt von den $n_\text{features}$ = 18 Features.
Zur binären Klassifizierung dieses Datensatzes in die oben genannten Klassen nutzen wir einen verteilten [Random Forest](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html#sklearn.ensemble.RandomForestClassifier.predict) mit insgesamt $n_\text{trees}$ Bäumen. Wir betrachten ein Setting mit $p$ Prozessoren. Jeder Prozessor $i=1,\cdots,p$ hält
- einen Sub Random Forest mit $n_\text{trees}^{i}=n_\text{trees}/p$ Bäumen (vorbehaltlich Load Balancing), wobei $\sum_{i=1}^p n_\text{trees}^i = n_\text{trees}$,
- eine gewisse Anzahl $n_\text{train}^i = n_\text{train}/p$ aller Trainings-Samples (vorbehaltlich Load Balancing) als lokalen Trainingsdatensatz sowie
- den kompletten, global einheitlichen Testdatensatz mit $n_\text{test}$ Samples.
Das bedeutet, dass es einen global einheitlichen Train-Test-Split $f_\text{train}$ des gesamten Datensatzes gibt, wobei die Testdaten auf allen Prozessoren gleichermaßen vollständig vorliegen, wohingegen jeder Prozessor eine gewisse Anzahl an Samples aus dem globalen Trainingsdatensatz als lokal unterschiedliche Trainingsdaten erhält. Es gilt also:
Jeder Prozessor trainiert seinen Sub Random Forest auf seinem lokalen Trainingsdatensatz. Zum Schluss werden die Teil-Ergebnisse über einen Majority Vote zu einem finalen Ergebnis kombiniert.
%% Cell type:markdown id:de426257 tags:
### Aufgabe 1: Daten laden
Um unser Modell zu trainieren und zu testen, müssen wir zunächst die verfügbaren Daten laden. Nach dem Train-Test-Split soll jeder Prozessor eine gewisse Anzahl an Datenpunkten aus dem globalen Trainingsdatensatz *mit Zurücklegen* ziehen. Diese Samples bilden den jeweiligen lokalen Trainingsdatensatz. Dazu gibt es verschiedene Ansätze:
1. Alle Prozessoren laden einen entsprechenden Teil der Daten aus dem globalen Trainingsdatensatz echt-parallel: Dataloader `load_data_csv_parallel()`
2. Der Root-Prozess lädt den kompletten globalen Trainingsdatensatz und verteilt diese Samples entsprechend an alle Prozessoren mit `Scatterv`: Dataloader `load_data_csv_root()`
Implementieren und testen Sie die zwei Varianten der Dataloader. Untenstehend finden Sie dazu Code-Gerüste der Funktionen `load_data_csv_parallel()` und `load_data_csv_root()` inklusive einiger Hilfsfunktionen sowie ein Test- und ein beispielhaftes Submitskript. Vollziehen Sie diese nach und vervollständigen Sie den Code an den markierten Stellen. **Normale Kommentare mit '#' beschreiben wie üblich den Code, in Zeilen mit '##' müssen Sie Code ergänzen.** Erstellen Sie entsprechende Python-Skripte mit allen Funktionsdefinitionen bzw. dem Main-Teil. Führen Sie das Test-Skript auf 2 und 4 Knoten des bwUniClusters aus.
%% Cell type:code id:563f8376 tags:
``` python
# IMPORTS
importpathlib
fromtypingimportBinaryIO,List,Tuple,Union
frommpi4pyimportMPI
frommpi4py.util.dtlibimportfrom_numpy_dtype
importnumpyasnp
fromsklearn.model_selectionimporttrain_test_split
```
%% Cell type:code id:eabeeef7 tags:
``` python
# HELPER FUNCTIONS FOR TRULY PARALLEL DATALOADER
# Put together with imports and other dataloaders into file `dataloaders.py`.
source${VENVDIR}/bin/activate# Activate your virtual environment.
mpirunpython${PYDIR}/test_dataloaders.py
```
%% Cell type:markdown id:7c0910e3 tags:
### Aufgabe 2
Laden Sie die Daten mit den verschiedenen Dataloadern und trainieren Sie einen verteilen Random Forest auf 1, 2, 4, 8, 16, 32 und 64 rein CPU-basierten Knoten des bwUniClusters. Untenstehend finden Sie ein entsprechendes Code-Gerüst sowie ein beispielhaftes Submit-Bash-Skript für 2 Knoten. Vervollständigen Sie den Code an den markierten Stellen.
**Normale Kommentare mit '#' beschreiben wie üblich den Code, in Zeilen mit '##' müssen Sie Code ergänzen.**
Erstellen Sie ein vollständiges Python-Skript mit allen Funktionsdefinitionen sowie dem Main-Teil. Messen und vergleichen Sie:
- die Datenladedauer
- die Trainingsdauer
- die lokale Accuracy der Sub Random Forests auf den Testdaten (auf jedem Rank)
- die globale Accuracy des finalen globalen Random Forest auf den Testdaten
Plotten Sie die oben genannten Größen über die Anzahl der verwendeten Knoten. Was fällt Ihnen auf? Welche Trends können Sie beobachten und wie lassen sich diese erklären?
%% Cell type:code id:deed7fc1 tags:
``` python
# IMPORTS
# Put together with helper functions and main (see below) into `distributed_forest.py`.