Maskinlæring: Del 2 - Praksis

Harald Vinje

Del:

20. januar 2023

7 min lesning

Kategorier:AIML

I del 2 går vi gjennom et praktisk eksempel på prediksjon ved hjelp av maskinlæring. Vi skal sette opp et utviklingsmiljø, laste inn data, preprossere, tilpasse og evaluere modeller basert på algoritmene vi gikk gjennom i del 1. Hold deg fast!

Jupyter and Python logo

Forrige innlegg fikk vi en introduksjon om maskinlæring, inkludert tre algoritmer. Nå skal vi bruke hver av disse algoritmene for å predikere om en person utvikler diabetes gitt et datasett med en rekke helserelaterte variabler. Kildekode og datasettet ligger på GitHub.

Set up

Jupyterlab/Jupyter Notebooks er gullstandarden for utviklingsmiljø for maskinlæring og datavitenskap. Forhåpentligvis vil en kunne se nytten av såkalte Notebooks underveis og om man leker litt med det. For å installere Jupyter trenger man python og pip.

1pip install jupyterlab

For å holde ting organisert lager vi en mappe hvor vi launcher JupyterLab.

1mkdir diabetes_prediction
2cd diabetes_prediction
3jupyter-lab

Åpner du browseren din på adresse localhost:8888/lab burde det se ut noe som dette:

Jupyter dashboard

Vi skal lage en ny Notebook som inneholder koden og litt dokumentasjon av hva som gjøres på de forskjellige stegene. Noe av det nyttigste med Notebooks er å kunne dokumentere koden med Markdown, og ikke minst å kunne visualisere dataen underveis uten å måtte kjøre hele programmet. Dette gjøres ved å dele inn koden i såkalte celler. I de to første cellene installerer og importerer vi de nødvendige pakkene og bibliotekene vi trenger:

Jupyter notebook code

En titt på dataen

Etter miljø er satt opp er det typiske neste steget å laste inn datasettet man skal bruke for å kunne bli litt kjent med hvordan dataen ser ut. Filen data.csv inneholder all dataen, og kolonnene beskriver følgende fra venstre:

  • Number of pregnancies.
  • Glucose: Konsentrasjon av glukose i blodet.
  • Skin thickness.
  • Insulin: Insulinnivå i blodet.
  • BMI.
  • Inheritance: Score på arvelighet av diabetes.
  • Age.
  • Has diabetes: 1 indikerer at personen har diabetes, 0 indikerer at personen ikke har diabetes.

Mange av radene i datasettet har ukomplett data. For at dette ikke skal ødelegge resultatet gjør vi det enkelt denne gangen og simpelthen sletter (eller dropper) alle rader med manglende data med funksjonen data.dropna(). Etter dette kan vi bruke metodene data.head() og data.describe() (data er her en DataFrame) for å få litt innsikt i dataen:

Diabetes table

Den første tabellen viser de første 5 radene, mens den andre viser et sammendrag og forskjellige statistiske metrikker. Nå som vi er kjent med dataen og kan bekrefte at den ser noenlunde rimelig ut er det bare å begynne å bruke algoritmene.

KNN

Som nevnt i Del 1 av maskinlæringsserien må dataen normaliseres for at KNN skal gi nyttige resultater. Vi ønsker å gjøre om hver kolonne til å ha et verdispenn mellom 0 og 1. Det kan gjøres manuelt med litt matematikk, men biblioteket scikit-learn har en klasse, MinMaxScaler(), som gjør det enklere for oss!

1from sklearn.neighbors import KNeighborsClassifier
2
3X_scaled = MinMaxScaler().fit(X).transform(X)
4X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2)
5
6knn_model = KNeighborsClassifier(n_neighbors=6)
7knn_model.fit(X_train, y_train)
8y_pred_knn = knn_model.predict(X_test)

Kodesnutten over er alt du trenger for å bruke KNN og tilpasse modellen til treningsdataen. Vi har normalisert dataen, splittet den inn i trening og testdata, og lagret prediksjoner i variabelen y_pred_knn. k ble satt til 6 for enkelhets skyld. Etter vi har fått noen prediksjoner med de andre algoritmene skal vi evaluere dette resultatet opp mot fasiten.

Naive Bayes

Neste algoritme på listen er Naive Bayes. For å kunne bruke denne må vi transformere dataen vår inn i kategorier (eller bøtter, eller bins på engelsk). Vi deler alle numeriske verdier inn i 4 kategorier hver basert på persentilen. Altså vil 0%-25% laveste inn i én kategori, 25%-50% i neste, og så videre. For å gjøre dette bruker vi noen hjelpefunksjoner fra numpy, som percentile og digitize.

1from sklearn.naive_bayes import CategoricalNB
2
3def transform_to_bins(col):
4 percentiles = [25, 50, 75]
5 boundaries = np.percentile(col, percentiles)
6 return np.digitize(col, boundaries, right=True)
7
8X_nb = X.copy(deep = True)
9
10for column in X_columns:
11 col = np.array(X_nb[column])
12 X_nb[column] = transform_to_bins(col)
13
14X_nb_train, X_nb_test, _, _ = train_test_split(X_nb, y, test_size=0.2)
15
16nb_model = CategoricalNB(force_alpha=True, min_categories=4)
17nb_model.fit(X_nb_train, y_train)
18y_pred_naive_bayes = nb_model.predict(X_nb_test)

Vi lagrer prediksjonene med Naive Bayes i y_pred_naive_bayes.

Logistisk regresjon

For logistisk regresjon er det passende å bruke det samme normaliserte datasettet som vi bruke med KNN. Vi har da ikke så mye annet å gjøre enn å initialisere modellen og å slenge inn treningsdataen.

1from sklearn.linear_model import LogisticRegression
2
3logreg_model = LogisticRegression()
4logreg_model.fit(X_train, y_train)
5
6y_pred_logreg = logreg_model.predict(X_test)

Det var det for logistisk regresjon! Resultatet er lagret i y_pred_logreg.

Evaluering av resultatene

Vi har så langt ikke gjort noe med resultatet annet enn å lagre det i variabler. Nå skal vi bruke forskjellige evalueringsteknikker for å se hvordan prediksjonene våre av testdataen samsvarer med fasiten. Modellevaluering er et stort tema i seg selv, og det er ikke én riktig måte å gjøre det på som passer alle tilfeller, men vi skal her bruke 3 ofte brukte metrikker:

  • Accuracy score: Hvor stor andel av de predikerte resultatene var riktige?
  • F1 score: En mer presis evaluering, som tar hensyn til ubalanse i et datasett. Accuracy score vil f. eks være over 99% om en modell skulle gi negativt på all input for en sykdom som under 1% av befolkningen har. Les mer om F1 score her.
  • Confusion matrix: En 2x2 matrise som krysser prediksjonene mot det faktiske svaret (se bildene under).

Vi lager en funksjon for å gjøre det lett å evaluere, slik at vi enkelt bare kan putte inn prediksjonene:

1from sklearn.metrics import accuracy_score, confusion_matrix, f1_score, ConfusionMatrixDisplay
2
3def display_model_evaluation(y_true, y_pred):
4 print (f'Accuracy score is {accuracy_score(y_true, y_pred)}')
5 print (f'F1 score is {f1_score(y_true, y_pred)}')
6 print ('Confusion Matrix')
7 cm = confusion_matrix(y_true, y_pred)
8 ConfusionMatrixDisplay(cm).plot(cmap=plt.cm.Blues)

Nå gjenstår det kun å slenge inn variablene vi definerte tidligere i funksjonen og å se resultatet:

KNN:

Confusion matrix

Naive Bayes:

Confusion matrix

Logistisk regresjon:

Confusion matrix

På resultatet ser vi at Logistisk regresjon gjorde det best, mens Naive Bayes var klart svakest i prediksjonene. Alle tre evalueringene har hver sin confusion matrix. Med et godt resultat vil confusion matrixen ha høye tall i diagonalen fra øvre venstre til nedre høyre og lave tall i alle andre celler. Den høyeste F1 scoren var på 0.65 (hvor 0 er konsekvent elendig og 1 er perfekt), noe som er helt brukbart, men ikke veldig bra.

Forbedringspotensiale

Målet her var ikke å få så godt resultat som mulig, men å gi et lite eksempel på hvordan maskinlæring kan jobbes med. I praksis handler maskinlæring stort sett om å være dyktig på å transformere og rense data, velge riktige modeller og å vite hvilke features som er betydningsfulle i forskjellige datasett. For å forbedre resultatet er det en rekke ting som kan gjøres:

  • Interpolere manglende data i radene, slik at datasettet blir større.
  • Skaffe mer data og sørge for at det er representativt.
  • Spotte "outliers" og vurdere fjerne disse. Enkelte datapunkter kan være mer støy enn signal, og vil dermed gjøre modellen dårligere.
  • Kryssvalidering, som betyr å lage flere ulike kombinasjoner av test- og treningsdataen og å trene med hver av disse for å unngå "overfitting".
  • Bruke andre modeller, som Decision Tree, Random Forest eller Nevrale nettverk.

Konklusjon

Det var det om maskinlæring i dag. Håper du har lært noe og fått litt mer greie på hva å jobbe med maskinlæring innebærer.

Kildekode ligger på GitHub.

Del: