From 4a435d7964f8d1096dbdf2b6d777f81eb98b9c1e Mon Sep 17 00:00:00 2001 From: klappstuhl Date: Thu, 4 Jan 2024 14:47:29 +0100 Subject: [PATCH] geiles viel besseres notebook shit --- project-cancer-classification-pca-nn.ipynb | 421 +++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 project-cancer-classification-pca-nn.ipynb diff --git a/project-cancer-classification-pca-nn.ipynb b/project-cancer-classification-pca-nn.ipynb new file mode 100644 index 0000000..c87166c --- /dev/null +++ b/project-cancer-classification-pca-nn.ipynb @@ -0,0 +1,421 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Laden der Rohdaten" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "import pickle\n", + "\n", + "# Laden der 'kirp' Liste aus der Pickle-Datei\n", + "with open('rick.pickle', 'rb') as f:\n", + " data_frame = pickle.load(f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# PCA Klasse zu Reduktion der Dimensionen" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "from torch.utils.data import Dataset\n", + "import torch\n", + "import pandas as pd\n", + "from sklearn.preprocessing import LabelEncoder\n", + "from sklearn.decomposition import PCA\n", + "from sklearn.preprocessing import StandardScaler\n", + "from sklearn.model_selection import train_test_split\n", + "from typing import List, Tuple\n", + "\n", + "\n", + "class GenomeDataset(Dataset):\n", + " \"\"\"\n", + " Eine benutzerdefinierte Dataset-Klasse, die für die Handhabung von Genomdaten konzipiert ist.\n", + " Diese Klasse wendet eine Principal Component Analysis (PCA) auf die Frequenzen der Genome an\n", + " und teilt den Datensatz in Trainings- und Validierungsteile auf.\n", + "\n", + " Attributes:\n", + " dataframe (pd.DataFrame): Ein Pandas DataFrame, der die initialen Daten enthält.\n", + " train_df (pd.DataFrame): Ein DataFrame, der den Trainingsdatensatz nach der Anwendung von PCA und der Aufteilung enthält.\n", + " val_df (pd.DataFrame): Ein DataFrame, der den Validierungsdatensatz nach der Anwendung von PCA und der Aufteilung enthält.\n", + "\n", + " Methods:\n", + " __init__(self, dataframe, n_pca_components=1034, train_size=0.8, split_random_state=42):\n", + " Konstruktor für die GenomeDataset Klasse.\n", + " _do_PCA(self, frequencies, n_components=1034):\n", + " Wendet PCA auf die gegebenen Frequenzen an.\n", + " _split_dataset(self, train_size=0.8, random_state=42):\n", + " Teilt den DataFrame in Trainings- und Validierungsdatensätze auf.\n", + " __getitem__(self, index):\n", + " Gibt ein Tupel aus transformierten Frequenzen und dem zugehörigen Krebstyp für einen gegebenen Index zurück.\n", + " __len__(self):\n", + " Gibt die Gesamtlänge der kombinierten Trainings- und Validierungsdatensätze zurück.\n", + " \"\"\"\n", + "\n", + " def __init__(self, dataframe: pd.DataFrame, n_pca_components: int = 1034, train_size: float = 0.8, split_random_state: int = 42):\n", + " \"\"\"\n", + " Konstruktor für die GenomeDataset Klasse.\n", + "\n", + " Parameters:\n", + " dataframe (pd.DataFrame): Der DataFrame, der die Genome Frequenzen und Krebsarten enthält.\n", + " n_pca_components (int): Die Anzahl der PCA-Komponenten, auf die reduziert werden soll. Standardwert ist 1034.\n", + " train_size (float): Der Anteil der Daten, der als Trainingsdaten verwendet werden soll. Standardwert ist 0.8.\n", + " split_random_state (int): Der Zufalls-Saatwert, der für die Aufteilung des Datensatzes verwendet wird. Standardwert ist 42.\n", + " \"\"\"\n", + " self.dataframe = dataframe\n", + "\n", + " # Umwandlung der Krebsarten in numerische Werte\n", + " self.label_encoder = LabelEncoder()\n", + " self.dataframe['encoded_cancer_type'] = self.label_encoder.fit_transform(dataframe['cancer_type'])\n", + "\n", + " # Anwenden der PCA auf die Frequenzen\n", + " self.dataframe['pca_frequencies'] = self._do_PCA(self.dataframe['genome_frequencies'].tolist(), n_pca_components)\n", + "\n", + " # Teilen des DataFrame in Trainings- und Validierungsdatensatz\n", + " self._split_dataset(train_size=train_size, random_state=split_random_state)\n", + "\n", + " def transform_datapoint(self, datapoint: List[float]) -> List[float]:\n", + " \"\"\"\n", + " Transformiert einen einzelnen Datenpunkt durch Standardisierung und Anwendung der PCA.\n", + "\n", + " Diese Methode nimmt einen rohen Datenpunkt (eine Liste von Frequenzen), standardisiert ihn mit dem \n", + " zuvor angepassten Scaler und wendet dann die PCA-Transformation an, um ihn in den reduzierten \n", + " Feature-Raum zu überführen, der für das Training des Modells verwendet wurde.\n", + "\n", + " Parameters:\n", + " datapoint (List[float]): Ein roher Datenpunkt, bestehend aus einer Liste von Frequenzen.\n", + "\n", + " Returns:\n", + " List[float]: Der transformierte Datenpunkt, nach Anwendung der Standardisierung und der PCA.\n", + " \"\"\"\n", + " # Standardisierung des Datenpunkts\n", + " scaled_data_point = self.scaler.transform([datapoint])\n", + "\n", + " # PCA-Transformation des standardisierten Datenpunkts\n", + " pca_transformed_point = self.pca.transform(scaled_data_point)\n", + "\n", + " return pca_transformed_point.tolist()\n", + "\n", + " def _do_PCA(self, frequencies: List[List[float]], n_components: int = 1034) -> List[List[float]]:\n", + " \"\"\"\n", + " Wendet PCA auf die gegebenen Frequenzen an.\n", + "\n", + " Parameters:\n", + " frequencies (List[List[float]]): Die Liste der Frequenzen, auf die die PCA angewendet werden soll.\n", + " n_components (int): Die Anzahl der Komponenten für die PCA. Standardwert ist 1034.\n", + "\n", + " Returns:\n", + " List[List[float]]: Eine Liste von Listen, die die transformierten Frequenzen nach der PCA darstellt.\n", + " \"\"\"\n", + "\n", + " # Standardisieren der Frequenzen\n", + " self.scaler = StandardScaler()\n", + " scaled_frequencies = self.scaler.fit_transform(frequencies)\n", + "\n", + " # PCA-Instanz erstellen und auf die gewünschte Anzahl von Komponenten reduzieren\n", + " self.pca = PCA(n_components=n_components)\n", + "\n", + " # PCA auf die Frequenzen anwenden\n", + " pca_result = self.pca.fit_transform(scaled_frequencies)\n", + "\n", + " return pca_result.tolist()\n", + "\n", + " def _split_dataset(self, train_size: float = 0.8, random_state: int = 42):\n", + " \"\"\"\n", + " Teilt den DataFrame in Trainings- und Validierungsdatensätze auf.\n", + "\n", + " Parameters:\n", + " train_size (float): Der Anteil der Daten, der als Trainingsdaten verwendet werden soll.\n", + " random_state (int): Der Zufalls-Saatwert, der für die Aufteilung des Datensatzes verwendet wird.\n", + " \"\"\"\n", + "\n", + " class SplittedDataset(Dataset):\n", + " def __init__(self, dataframe):\n", + " self.dataframe = dataframe\n", + "\n", + " # Umwandlung der Genome Frequenzen in Tensoren\n", + " self.genome_frequencies = torch.tensor(dataframe['pca_frequencies'].tolist(), dtype=torch.float32)\n", + "\n", + " # Umwandlung der Krebsarten in numerische Werte\n", + " self.label_encoder = LabelEncoder()\n", + " self.cancer_types = torch.tensor(dataframe['encoded_cancer_type'].tolist(), dtype=torch.long)\n", + "\n", + " def __getitem__(self, index):\n", + " # Rückgabe eines Tupels aus Genome Frequenzen und dem entsprechenden Krebstyp\n", + " return self.genome_frequencies[index], self.cancer_types[index]\n", + "\n", + " def __len__(self):\n", + " return len(self.dataframe)\n", + "\n", + " # Teilen des DataFrame in Trainings- und Validierungsdatensatz\n", + " train_df, val_df = train_test_split(self.dataframe, train_size=train_size, random_state=random_state)\n", + " self.train_df = SplittedDataset(train_df)\n", + " self.val_df = SplittedDataset(val_df)\n", + "\n", + "\n", + " def __getitem__(self, index: int) -> Tuple[torch.Tensor, int]:\n", + " \"\"\"\n", + " Gibt ein Tupel aus transformierten Frequenzen und dem entsprechenden Krebstyp für einen gegebenen Index zurück.\n", + "\n", + " Parameters:\n", + " index (int): Der Index des zu abrufenden Datenelements.\n", + "\n", + " Returns:\n", + " Tuple[torch.Tensor, int]: Ein Tupel, bestehend aus einem Tensor der transformierten Frequenzen und dem zugehörigen Krebstyp.\n", + " \"\"\"\n", + "\n", + " print(self.train_df.shape)\n", + " print(self.val_df.shape)\n", + " \n", + " if index < len(self.train_df):\n", + " row = self.train_df.iloc[index]\n", + " else:\n", + " row = self.val_df.iloc[len(self.train_df) - index]\n", + "\n", + " pca_frequencies_tensor = torch.tensor(row['pca_frequencies'], dtype=torch.float32)\n", + " cancer_type = row['encoded_cancer_type']\n", + "\n", + " return pca_frequencies_tensor, cancer_type\n", + "\n", + " def __len__(self) -> int:\n", + " \"\"\"\n", + " Gibt die Gesamtlänge der kombinierten Trainings- und Validierungsdatensätze zurück.\n", + "\n", + " Returns:\n", + " int: Die Länge der kombinierten Datensätze.\n", + " \"\"\"\n", + " \n", + " return len(self.train_df) + len(self.val_df)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Definition des neuronalen Netzes" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.optim as optim\n", + "import torch.nn.functional as F\n", + "\n", + "class CancerClassifierNN(nn.Module):\n", + " \"\"\"\n", + " Eine benutzerdefinierte neuronale Netzwerkklassifikator-Klasse für die Krebsklassifikation.\n", + "\n", + " Diese Klasse definiert ein mehrschichtiges Perzeptron (MLP), das für die Klassifizierung von Krebsarten\n", + " anhand genetischer Frequenzdaten verwendet wird.\n", + "\n", + " Attributes:\n", + " fc1 (nn.Linear): Die erste lineare Schicht des Netzwerks.\n", + " fc2 (nn.Linear): Die zweite lineare Schicht des Netzwerks.\n", + " fc3 (nn.Linear): Die dritte lineare Schicht des Netzwerks.\n", + " fc4 (nn.Linear): Die Ausgabeschicht des Netzwerks.\n", + " dropout (nn.Dropout): Ein Dropout-Layer zur Vermeidung von Overfitting.\n", + "\n", + " Methods:\n", + " __init__(self, input_size: int, num_classes: int):\n", + " Konstruktor für die CancerClassifierNN Klasse.\n", + " forward(self, x: torch.Tensor) -> torch.Tensor:\n", + " Definiert den Vorwärtsdurchlauf des Netzwerks.\n", + " \"\"\"\n", + "\n", + " def __init__(self, input_size: int, num_classes: int):\n", + " \"\"\"\n", + " Konstruktor für die CancerClassifierNN Klasse.\n", + "\n", + " Parameters:\n", + " input_size (int): Die Größe des Input-Features.\n", + " num_classes (int): Die Anzahl der Zielklassen.\n", + " \"\"\"\n", + " super(CancerClassifierNN, self).__init__()\n", + " # Definieren der Schichten\n", + " self.fc1 = nn.Linear(input_size, 1024) # Eingabeschicht\n", + " self.fc2 = nn.Linear(1024, 512) # Versteckte Schicht\n", + " self.fc3 = nn.Linear(512, 256) # Weitere versteckte Schicht\n", + " self.fc4 = nn.Linear(256, num_classes) # Ausgabeschicht\n", + " self.dropout = nn.Dropout(p=0.5) # Dropout\n", + "\n", + " def forward(self, x: torch.Tensor) -> torch.Tensor:\n", + " \"\"\"\n", + " Definiert den Vorwärtsdurchlauf des Netzwerks.\n", + "\n", + " Parameters:\n", + " x (torch.Tensor): Der Input-Tensor für das Netzwerk.\n", + "\n", + " Returns:\n", + " torch.Tensor: Der Output-Tensor nach dem Durchlauf durch das Netzwerk.\n", + " \"\"\"\n", + " x = F.relu(self.fc1(x))\n", + " x = self.dropout(x)\n", + " x = F.relu(self.fc2(x))\n", + " x = self.dropout(x)\n", + " x = F.relu(self.fc3(x))\n", + " x = torch.sigmoid(self.fc4(x)) # Oder F.log_softmax(x, dim=1) für Mehrklassenklassifikation\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "from torch.utils.data import DataLoader\n", + "\n", + "# Erstellen der Dataframe Klasse\n", + "genome_dataset = GenomeDataset(data_frame)\n", + "train_dataset = genome_dataset.train_df\n", + "valid_dataset = genome_dataset.val_df\n", + "\n", + "# Annahme: input_size ist die Länge Ihrer Genome-Frequenzen und num_classes ist die Anzahl der Krebsarten\n", + "model = CancerClassifierNN(input_size=1034, num_classes=3)\n", + "\n", + "# Daten-Loader\n", + "train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)\n", + "valid_loader = DataLoader(dataset=valid_dataset, batch_size=64, shuffle=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "import torch.optim as optim\n", + "\n", + "# Verlustfunktion\n", + "criterion = nn.CrossEntropyLoss()\n", + "\n", + "# Optimierer\n", + "optimizer = optim.Adam(model.parameters(), lr=0.001)\n", + "\n", + "# Anzahl der Epochen\n", + "num_epochs = 10\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch [1/10], Trainingsverlust: 0.8623, Validierungsverlust: 0.7387\n", + "Epoch [2/10], Trainingsverlust: 0.7593, Validierungsverlust: 0.7306\n", + "Epoch [3/10], Trainingsverlust: 0.7496, Validierungsverlust: 0.7388\n", + "Epoch [4/10], Trainingsverlust: 0.7466, Validierungsverlust: 0.7342\n", + "Epoch [5/10], Trainingsverlust: 0.7456, Validierungsverlust: 0.7340\n", + "Epoch [6/10], Trainingsverlust: 0.7454, Validierungsverlust: 0.7265\n", + "Epoch [7/10], Trainingsverlust: 0.7448, Validierungsverlust: 0.7252\n", + "Epoch [8/10], Trainingsverlust: 0.7469, Validierungsverlust: 0.7239\n", + "Epoch [9/10], Trainingsverlust: 0.7453, Validierungsverlust: 0.7242\n", + "Epoch [10/10], Trainingsverlust: 0.7449, Validierungsverlust: 0.7252\n" + ] + } + ], + "source": [ + "# Listen, um Verluste zu speichern\n", + "train_losses = []\n", + "valid_losses = []\n", + "\n", + "for epoch in range(num_epochs):\n", + " model.train()\n", + " train_loss = 0.0\n", + " for i, (inputs, labels) in enumerate(train_loader):\n", + " optimizer.zero_grad()\n", + " outputs = model(inputs)\n", + " loss = criterion(outputs, labels)\n", + " loss.backward()\n", + " optimizer.step()\n", + " train_loss += loss.item()\n", + "\n", + " # Durchschnittlicher Trainingsverlust\n", + " train_loss /= len(train_loader)\n", + " train_losses.append(train_loss)\n", + "\n", + " # Validierungsverlust\n", + " model.eval()\n", + " valid_loss = 0.0\n", + " with torch.no_grad():\n", + " for inputs, labels in valid_loader:\n", + " outputs = model(inputs)\n", + " loss = criterion(outputs, labels)\n", + " valid_loss += loss.item()\n", + "\n", + " # Durchschnittlicher Validierungsverlust\n", + " valid_loss /= len(valid_loader)\n", + " valid_losses.append(valid_loss)\n", + "\n", + " print(f'Epoch [{epoch+1}/{num_epochs}], Trainingsverlust: {train_loss:.4f}, Validierungsverlust: {valid_loss:.4f}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "rl", + "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.8.18" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +}