Modell für maschinelles Lernen zur Schlaganfallvorhersage | von Gabe Walter | Juni 2023

0
31


Einführung

Jedes Jahr erleiden quick 800.000 Amerikaner einen Schlaganfall. Nach Angaben der World Stroke Group erleidet jeder vierte von uns irgendwann im Leben einen Schlaganfall. Schlaganfälle sind sehr oft das Ergebnis einer Kombination aus gesundheitlichen Faktoren und Lebensstilentscheidungen. Wenn wir vor diesem Hintergrund herausfinden können, welche Verhaltensweisen das größte Risiko darstellen, können wir möglicherweise Schlaganfälle vorhersagen, bevor sie auftreten.

Importe

Das erste, was wir tun müssen, um ein Python-Projekt zu starten, ist, die erforderlichen Bibliotheken zu importieren. (Beachten Sie, dass einige davon später im Projekt aus Bedarf importiert wurden.)

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
from imblearn.over_sampling import RandomOverSampler
from imblearn.over_sampling import SMOTE
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
import xgboost as xgb

Daten

Wie bei jedem Projekt benötigen auch wir unsere Daten. Lassen Sie uns unsere CSV als DataFrame einlesen und ausprobieren.

df = pd.read_csv("stroke information.csv")
df.head()

Wie wir sehen können, gibt es in unseren Daten verschiedene Spalten, darunter persönliche Merkmale (wie Alter, Heiratsstand und Beruf), Gesundheitsprobleme (wie Herzerkrankungen und Bluthochdruck) und Lebensstilentscheidungen (wie Raucherstatus). Wir haben auch 3 unbenannte Metriken. Manche Leute mögen sagen, dass diese Messwerte nutzlos wären, aber wir können sie später tatsächlich verwenden, um zu sehen, ob es einen Zusammenhang zwischen ihnen und einem Schlaganfall gibt.

Schauen wir uns auch die Statistiken zu unseren Daten an:

df.describe()

df.isnull().sum()
id                    0
gender 0
age 0
married 0
hypertension 0
heart_disease 0
occupation 0
residence 0
metric_1 0
metric_2 1462
metric_3 0
metric_4 0
metric_5 0
smoking_status 13292
stroke 0
dtype: int64

Daraus können wir ersehen, dass noch einiges an Arbeit vor uns liegt, was die Bereinigung unserer Daten angeht, bevor wir das Modell erstellen. Uns liegen zum Beispiel einige unverständliche Daten vor, etwa das Mindestalter von -10 Jahren. Wir haben auch einige N/A-Daten in unseren Spalten metric_2 und Smoking_status. Wir können das aufräumen.

# Course of information by dropping uneccessary columns, eradicating incorrect values(age = -10); change columns to numerical information
df.drop("id", axis = 1, inplace = True)
df.drop(df[(df["age"] < 0)].index, inplace = True)

# fillna(0) returns larger accuracy than dropna() for smoking_status
# Some difficulty the place fillna() doesn't work on metric_2, so simply dropna() after fillna(0)
df["smoking_status"].fillna(0)

labeler = LabelEncoder()
c = df.select_dtypes(embrace = "object").columns
df[c] = df[c].apply(labeler.fit_transform)

df.dropna(inplace = True)

Wir können die ID-Spalte löschen, da sie dem Datensatz nichts hinzufügt. Wir können auch alle Zeilen löschen, in denen das Alter kleiner als Null ist, da davon auszugehen ist, dass diese Daten fehlerhaft sind. Wir konnten und N/A-Spalten in Smoking_Status löschen, aber nach Fertigstellung des Modells stellte sich heraus, dass die Verwendung von fillna(0) höhere Genauigkeitswerte lieferte. Als nächstes möchten wir LabelEncoder() verwenden, um alle unsere Objekttypspalten in numerische Spalten umzuwandeln, damit unser Modell sie aufnehmen kann. Jetzt sind alle unsere Daten sauber und es fehlen keine Werte.

df.isnull().sum()
gender            0
age 0
married 0
hypertension 0
heart_disease 0
occupation 0
residence 0
metric_1 0
metric_2 0
metric_3 0
metric_4 0
metric_5 0
smoking_status 0
stroke 0
dtype: int64

Variablen

Wir werden wahrscheinlich nicht jede Spalte unserer Daten in unserem Modell verwenden wollen. Dies liegt daran, dass einige unserer Variablen stärker korrelieren als andere. Wir könnten manuell herausfinden, welche die besten Ergebnisse liefern, indem wir eine Reihe verschiedener Variablen in unserem Modell testen, aber es gibt einen einfacheren Weg. Wir können df.corr() verwenden, um die Korrelationen zwischen jeder Variablen in unserem DataFrame zu finden. Um es optisch ansprechender zu gestalten, habe ich auch Seaborn verwendet.

#Discover finest variables to make use of in mannequin
plt.determine(figsize=(12,12))
sns.heatmap(df.corr()[["stroke"]].sort_values(by = "stroke"), annot = True, cmap="Greens")

Wir haben jetzt ein Diagramm der am stärksten korrelierten Variablen. Wir können sehen, dass offensichtliche Faktoren wie Alter, Herzerkrankungen und Bluthochdruck alle relativ stark korrelieren. Ein überraschendes Ergebnis ist hier ein negativer Zusammenhang zwischen Schlaganfällen und Rauchen, da Rauchen eine bekannte Ursache für Schlaganfälle ist. Dies ist ein gutes Beispiel dafür, warum wir df.corr() verwenden müssen. Hätten wir das nicht getan, wären wir möglicherweise von einer starken positiven Korrelation zwischen Rauchern und Schlaganfallopfern ausgegangen und hätten ein Modell unter falschen Annahmen erstellt.

Modell

Jetzt können wir mit dem Aufbau unseres Modells beginnen. Als Erstes müssen wir unsere Daten für die Einspeisung in das Modell vorbereiten.

# Break up information
best_variables = [k for k, v in df.corr()["stroke"].objects() if v > 0 and okay != "stroke"]
X = df[best_variables]
y = df["stroke"]
Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, test_size=0.2, random_state = 42)

Nach einer Reihe von Experimenten stellte ich fest, dass die besten Genauigkeitswerte erzielt wurden, wenn nur die positiv korrelierten Variablen verwendet wurden, additionally wählten wir diese als beste_Variablen aus. Dann teilen wir unsere Daten in Trainings- und Testdatensätze auf.

Als nächstes müssen wir unsere Daten ausgleichen. Dies liegt daran, dass Schlaganfälle in diesem Datensatz tatsächlich sehr selten sind, da es sich bei unseren Daten um eine Befragung einer Gruppe normaler Menschen jeden Alters handelt. Ohne diesen Schritt wird unser Modell sehr ungenau sein, da es nicht genügend Schlaganfallopfer gibt, um zu lernen, sie genau vorherzusagen. Es gibt viele Möglichkeiten, dies zu tun, aber wir werden eine Methode namens Oversampling verwenden. Konkret werden wir eine Technik namens SMOTE oder Artificial Minority Oversampling Approach verwenden. Ursprünglich hatte ich vor, RandomOverSampler zu verwenden, aber SMOTE lieferte höhere Genauigkeitswerte. SMOTE wird im Wesentlichen eine Reihe synthetischer Schlaganfallopfer mit ähnlichen Kennzahlen wie andere Schlaganfallopfer erstellen und diese an zufälligen Punkten in unseren Datensatz einfügen. Hier ist ein Beispiel für Oversampling, falls Sie nicht damit vertraut sind.

Bild von https://www.mastersindatascience.org/learning/statistics-data-science/undersampling/
# Steadiness Information!! Smote achieved barely larger scores than RandomOverSampler, so went with Smote

# oversample = RandomOverSampler(sampling_strategy="minority")
# Xtrain, ytrain = oversample.fit_resample(Xtrain, ytrain)
# Xtest, ytest = oversample.fit_resample(Xtest, ytest)

smote=SMOTE()
Xtrain, ytrain=smote.fit_resample(Xtrain, ytrain)
Xtest, ytest=smote.fit_resample(Xtest, ytest)

Jetzt können wir unser Modell erstellen. Lassen Sie uns zunächst ein logistisches Regressionsmodell verwenden, da wir versuchen, ein binäres Ergebnis vorherzusagen. Wir werden auch StandardScaler verwenden, da der Umfang unserer Daten sehr unterschiedlich ist.

#Create and prepare mannequin
pipe = Pipeline([("std", StandardScaler()), ("lr", LogisticRegression())])
pipe.match(Xtrain, ytrain)

# Make predictions
ypred = pipe.predict(Xtest)

# Rating mannequin
print("Accuracy:", pipe.rating(Xtest, ytest))
print("nClassification report:n", classification_report(ytest, ypred))
ConfusionMatrixDisplay.from_estimator(pipe, Xtest, ytest)

Daran können wir sehen, dass unser Modell ziemlich intestine abgeschnitten hat. Wir hatten eine Genauigkeitsbewertung von 82,8 %. Wir können auch unsere Verwirrungsmatrix konsultieren. Wir hatten 6.419 richtig unfavorable und 7.280 richtig constructive Ergebnisse, verglichen mit 2.845 falsch positiven oder falsch negativen Ergebnissen. Aber wahrscheinlich können wir unsere Genauigkeit erhöhen, indem wir verschiedene Modelle verwenden. Versuchen wir, unsere Punktzahl mit einem anderen Modell zu steigern.

Ich habe darüber nachgedacht, den RandomForestClassifier zu verwenden, aber er hat ähnliche Ergebnisse wie das LogisticRegression-Modell zurückgegeben. Mithilfe der beliebten XGBoost-Bibliothek können wir die Genauigkeit jedoch erheblich verbessern. Ich habe ein wenig mit den Hyperparametern herumgespielt und konnte eine viel höhere Punktzahl erzielen.

# Second Mannequin, RandomForestClassifier returned low scores so utilizing XGBoost

# Create mannequin
mannequin = xgb.XGBClassifier(n_estimators=1000, max_depth=5, learning_rate=0.1, goal="binary:logistic")

# Match mannequin
mannequin.match(Xtrain, ytrain)

# Make predictions
ypred = mannequin.predict(Xtest)

# Rating mannequin
print("Accuracy:", mannequin.rating(Xtest, ytest))
print("nClassification report:n", classification_report(ytest, ypred))
ConfusionMatrixDisplay.from_estimator(mannequin, Xtest, ytest)

Jetzt verfügen wir über ein zu etwa 95,5 % genaues Modell, um vorherzusagen, ob jemand einen Schlaganfall erleiden wird oder nicht. Zu beachten ist, dass dieses Modell bei der Vorhersage eines Schlaganfalls außerordentlich genau ist. Das erkennen wir an den 7.606 echten Positiven im Vergleich zu nur 84 falschen Positiven. Wenn wir beispielsweise eine Versicherungsgesellschaft wären, könnten Informationen wie diese äußerst wertvoll sein.

Abschluss

Insgesamt konnten wir mit unserem Modell sehr hohe Genauigkeitswerte erzielen. Ich hatte ein paar abschließende Gedanken, die ich in mein Jupyter-Notizbuch geschrieben habe, additionally werde ich sie hier belassen. Wenn ich die Modelle unbedingt maximieren wollte, hätte ich eine weitere Optimierung der Hyperparameter gebrauchen können. Insbesondere die Verwendung von RandomSearchCV oder GridSearchCV hätte wahrscheinlich die Ergebnisse verbessert. Außerdem hätte ich mit verschiedenen Methoden zur Bereinigung der Daten experimentieren können. Beispielsweise fehlten bei „metric_2“ stellenweise Daten. Ich habe darüber nachgedacht, die N/A-Werte durch die Mittelwerte für die Spalte zu ersetzen, um den Effekt zu sehen. Alles in allem würde ich jedoch sagen, dass es sich um ein gelungenes Projekt handelte.



Source link

HINTERLASSEN SIE EINE ANTWORT

Please enter your comment!
Please enter your name here