Obligatorisk oppgave om Titanic (frist 28. mars)
Oppgaveteksten er nå fullstendig. Dersom det er noe som er uklart, er det lurt å spørre/sende en mail. Enkelte ting som dukker opp i obligen har ikke vært gjennomgått i undervisningen. Da finnes det forhåpentligvis noe informasjon i statistikkboka, blant medstudenter eller blant underviserne i HON2200!
Etter å ha fullført denne obligen skal studenten kunne - Lese inn og behandle kategoriske data i pandas - Implementere logistisk klassifikasjon - Benytte og beskrive beslutningstrær - Benytte og beskrive random forests
Denne obligen skal fungere som en øvelse på maskinlæring anvendt på kategoriske data. Dere skal gjennom forbereding av data med pandas og scikit-learn, og så tilpasning av dataene med logistisk klassifikasjon og beslutningstrær:
- Logistisk klassifikasjon for å få erfaring med å gjøre maskinlæring fra grunnen av,
- Beslutningstrær for å få erfaring med et sentralt verktøy i maskinlæring som brukes i f.eks Random forests.
- Random forests fordi det fungerer veldig bra i praksis.
Dataene dere skal se på inneholder informasjon om passasjerene på Titanic, inkludert om de overlevde katestrofen eller ikke. Målet er å lage algoritmer som kan predikere om en gitt person på titanic overlevde eller ikke, og å se hvilke personlige egenskaper som var “avgjørende”.
Datasettet dere skal bruke heter train.csv og kan lastes ned her
Gjennom hele obligen har vi gjort det slik at vi først gir oppgavene, og under gir vi en del hint og forklaringer på hvordan de kan løses. Det betyr at om du står fast, kan det være lurt å lese litt lenger nedover på siden. I blant forklarer vi ting i selve oppgaven som ikke har blitt gjennomgått tidligere i undervisningen.
I tillegg til å gjøre de statistiske analysene vi ber om krever vi også at besvarelsen er godt kommentert. Det holder altså ikke bare å levere et dokument som inneholder riktig kode, tall og figurer, du må også beskrive og diskutere resultatene.
Merk at Oppgave 2 har to versjoner
- Oppgave-2—Bruk-av-scikitlearn for de som går på humanioraprogrammet
- Oppgave-2—Fra-grunnen for de som går på realfag
da sistnevnte krever forkunnskaper tilsvarende MAT1110.
Lesing og behandling av kategoriske data i pandas
Oppgave 1:
a) Les datasettet (“train.csv”) med pandas. Se på dataene! Finn ut hva kolonnene betyr.
b) Håndter de manglende dataene for alder.
c) Gjør de kategoriske dataene du vil benytte brukbare ved å gjøre en one hot encoding.
d) Samle kolonnene du vil bruke til å predikere overlevelse i en dataframe/array X
. Samle overlevelses-kolonnen i en annen dataframe/array Y
. Under er det vist hvordan dere kan hente ut kolonner til en egene variable. - X = df[["Alder", "Kjønn"]]
- Y = df["Lønn"]
e) Del dataenne videre inn i trening og test data. Se dokumentasjonen for scikit-learn sin train_test_split
: https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html.
Få oversikt over dataene
Man bruker som regel pd.read_csv() når man leser data med pandas. Da får man et objekt av typen dataframe. For å få denne printet pent i en notebook kan man enten avslutte en celle med dataframe objektet, eller bruke display()
funksjonen (ikke print()
, det blir stygt).
Manglende data er et stort problem i dataanalyse, og har mange løsninger med egne ulemper og fordeler. I dette datasettet er det mye manglende data for alder og kabin. Du kan få en rask oversikt over manglende data med kodesnutten
sum()) display(df.isnull().
Manglende data (her NaN verdier) gjør at maskinlæringsalgoritmene feiler. Den enkleste løsningen er å fjerne alle rader med manglende data. Dette kan man f.eks gjør med koden
= df.dropna(subset=["Age"]) df
Men, når vi allerede jobber med så lite data er det en stor kostnad å bare kaste bort så mye. En annen løsning er å fylle inn manglende verdier med gjennomsnittlig alder for kombinasjonen av kjønnet og klassen personen har. Om denne dataen vi fyller inn gjør mer godt enn skade vil variere fra dataset til dataset. Dere velger selv hvordan dere vil håndtere manglende data.
"Age"] = df.groupby(["Pclass", "Sex"],group_keys=False)["Age"].apply(lambda x: x.fillna(x.median())) df[
Kategorisk data og train-test split
Kategorisk data er data som ikke skal bli tolket som en numerisk verdi. Maskinlæringsalgoritmer skjønner seg kun på numeriske verdier, så dette fører til et problem. Eksempler på kategorisk data kan være nasjonalitet og telefonnummer. Når en maskinlæringsalgoritme ser telefonnummerene 12345678, 23456789 og 34567891 vil den prøve å finne en sammenheng basert på størrelsen til tallene, selv om det ikke gir mening. Og når den ser ordet “Norge”, får du en feilmelding.
Løsningen er å gi hver enkelt mulige verdi av de kategoriske dataene sin egen kolonne. Kolonnen vil da inneholde nuller og enere som forteller om man hører hjemme i den kategorien eller ikke. En kolonne Norge
kan da ha en null hvis personen ikke kommer fra Norge, eller et ett-tall hvis personen kommer fra Norge. Dette kalles one hot encoding. Dette gjør man i pandas med metoden .get_dummies(). Hvis kolonnene Nasjonalitet
og Telefonnummer
skal bli gjort om til en one-hot kolonner gjøres det slik:
= ["Nasjonalitet", "Telefonnummer"]
categorical_cols = pd.get_dummies(df, columns=categorical_cols, prefix=categorical_cols, prefix_sep='_') df
Merk at kategoriske data som navn og telefonnummer ikke er særlig nyttige, spesielt når du ender opp med en kolonne for hver person.
Når manglende og kategorisk data er tatt hånd om, er neste steg å samle de dataene man skal bruke til å gjøre prediksjoner, og de dataene som man prøver å predikere. Man tar ut kolonner av en dataframe ved å bruke hakeparanteser og navnet på kolonnen, eller en liste med navnene på kolonnene man vil ta ut.
= df[["Nasjonalitet_Norge", "Nasjonalitet_Sverige", "Alder"]]
X = df["Lønn"] Y
Til slutt må man dele opp dataene i trenings-, validerings- og testdata. Algoritmene blir trent på treningsdata, og så ser vi hvor godt de funker ved å teste de på test data. Merk at i denne obligen holder det at dere deler opp i treningsdata og valideringsdata. Vi har låst ned testdataene i kjelleren på forhånd.
Logistisk Klassifisering
Denne oppgaven kommer i to versjoner: Én for dem som går på realgasprogrammet og en for dem som går på humanioraprogrammet. Det kommer av at realfagsversjonen krever forkunnskaper tilsvarende MAT1110.
Oppgave 2 - Bruk av scikitlearn (SV/HF):
a) Plott sigmoidfunksjonen (den logistiske funksjonen). Hva gjør denne funksjonen godt egnet til å “svare på” ja/nei spørsmål?
b) Bruk scikit-learn til å trene en logistisk klassifikasjonsmodell.
c) Test logistisk klassifikasjon modellen. Dere kan bruke clf.score()
til å se hvor stor andel riktige modellen hadde.
d) Bruk funksjonen confusion_matrix
fra sklearn.metrics
til å finne ut i hvilken grad modellen produserer sanne positive, sanne negative, falske positive og falske negative prediksjoner.
Oppgave 2 - Fra grunnen (MN-programmet og eventuelt interesserte):
a) Plott sigmoid-funksjonen. Hva gjør denne funksjonen godt egnet til å “svare” på ja/nei spørsmål?
b) Implementer logistisk klassifisering og tren modellen. - Sett opp vekter (array med like mange tall som input) og bias (ett tall) - Regn ut output. Multipliser trenings-input (X) med vektene, legg til bias, og mat gjennom sigmoid funksjonen. (Deler av kode under) - Regn ut gradienter. (Kode under) - Oppdater vekter. (Formler under)
Viktig i alle program med vektorer og vektorprodukter: Få oversikt over dimensjonene til alle variablene i programmet ditt. Er noe et tall, vektor eller matrise(har flere rader og kolonner)? print()
alt du er usikker på, og print()
.shape
egenskapen for å se størrelsen på ting. Gjør dette underveis mens du skriver programmet, men unngå alt unødvendig i det fullførte programmet.
c) Test logistisk klassifikasjon modellen på test-dataen ved å ta testing-input (X) gjennom input-prosedyren over og sammenlign med test output (Y).
Her kommer det en del formeler og teori, men det viktigste er å forstå de overordnede ideene, som skal være overkommelig. Programmet dere skal skrive vil ha en slik struktur:
Lag vekter og bias
for i in range(iterasjoner):
Regn ut output (ved hjelp av vekter og bias)
Regn ut gradienter (ved hjelp av output)
Oppdater vekter og bias (ved hjelp av gradienter)
En logistisk modell, som alle andre modeller, tar inn en input og gir en output. Vi kaller input til modellen
Output til modellen
Den logistiske modellen inneholder vekter
De ulike vektene ganges komponentvis med de ulike egenskapene, og blir så lagt sammen med bias:
Dette kan vi gjøre som et vektorprodukt i Python. Merk at vi kan gange vektene med alle egenskapene til ALLE passasjerene på en gang! Dette gjør alt mye mer kompakt: (Vi bruker @
for matrise og vektorprodukt i Python)
= X_train @ W + b z
Alt dette (
Hvorfor får jeg overflow feil av og til? Det finnes to likegyldige definisjoner av sigmoid funksjonen:
Begge to gir akkurat samme svar, men datamaskiner behandler de to ulikt for veldig store positive og negative tall (fordi utregningen vil inkludere tall som er for store til å behandles av datamaskinen). Den første definisjonen fører til utregningen av np.where()
er en slags effektiv if
for utregninger. Å bruke en vanlig if
til å bruke riktig sigmoid definisjon vil gjøre at man ikke kan bruke funksjonen til vektoriserte utregninger (som å bruke sigmoid funksjonen på en hel vektor på en gang).
def sigmoid(z): #hvis? True False
return np.where(z >= 0, 1 / (1 + np.exp(-z)), np.exp(z) / (1 + np.exp(z)))
Merk at når man gjør disse operasjonene på X_train
som inneholder egenskapene til alle passasjerene, blir
Siden hele modellen kun består av ett sett vekter, en bias og en sigmoid, kan en logistisk modell bli sett på som et nevralt nettverk med kun én node. Hvis modellen tar fem inputs kan man tegne den slik:
Cost funksjonen og gradient
Modellen gjør sannsynligvis en veldig dårlig jobb i å predikere noe som helst, siden vektene og bias kun er tilfeldige tall så langt. Vi samler vektene og bias i en variabel
Hva er motivasjonen bak cost funksjonen?
Modellen vår vil predikere enten 0 eller 1 (i praksis vil den under trening gi et tall mellom 0 og 1 som er sannsynligheten for at den vil predikere 1). Sannsynligheten for at den predikerer 1 gitt en input
Vi ønsker å maksimere sannsynligheten for at modellen predikerer alle datapunktene riktig, så vi prøver å finne
Vi vil velge parametre
I python kan man bruke likhetene i uttrykkene til å spare litt beregninger, vi lager et mellomledd delta
som er gjennomsnittlig forskjell mellom
= -(Y_train - y_hat) / n
delta = X_train.T @ delta
dCdW = np.sum(delta, axis = 0) dCdb
De deriverte peker i retningen hvor
Testing av modellen
Når du skal bruke modellen til å gjøre faktiske prediksjoner må du finne
Beslutningstrær
Oppgave 3:
a) Lag et tre med maksdybde 3 som tilpasser treningsdataene.
b) Test treet på testdataene.
c) Plot treet og beskriv med ord hva alle forgreningene sier, og hvilke personer treet sier vil overleve.
d) Se hvordan maksdybden til treet påvirker hvor godt treet klassifiserer testdataene.
Beslutningstrær fungerer på en helt annen måte enn logistiske modeller eller nevrale nettverk. Istedenfor å justere mange vekter litt og litt med gradienter, finner beslutningstrær ja/nei spørsmål som best skiller dataene inn i kategoriene man er ute etter. I Titanic eksempelet finner treet de spørsmålene som best skiller de som overlever fra de som dør. Et slikt spørsmål kan f.eks være om en passasjer er en kvinne, eller om de er yngre enn 50 år. For hvert slikt spørsmål får treet to nye grener, en for ja og en for nei. På hver av disse grenene kan man igjen stille nye spørsmål og få enda flere grener som bedre grupperer dataene.
Når man trener et beslutningstre på data kan man alltid la treet stille så mange spørsmål det bare vil, slik at det kan gruppere alle dataene riktig. Da kan man ende opp med et tre hvor omtrent hvert eneste datapunkt har sin egen forgrening, og treet ikke har noen “overordnede mønstre” det grupperer dataene etter. Treet vil da ha et overtilpasnings(“overfitting”) problem som vil føre til at det presterer dårlig på å klassifisere data det aldri har sett før. Dette kan unngås ved å begrense antall forgreninger treet kan ha, eller maks-dybden til treet.
Dere skal bruke DecisionTreeClassifier modellen til scikit-learn. Følg eksempelet i dokumentasjonen(linken til venstre), og lag et tre med maks dybde 3. Bruk scikit learn sin accuracy_score() for å teste modellen. For å plotte treet anbefaler vi
=(16,10))
plt.figure(figsize=3, feature_names=X_train.columns, class_names=["Dead", "Alive"], filled=True)
tree.plot_tree(model, max_depth plt.show()
Random forest
Oppgave 4:
a) Lag en random forest-modell som tilpasser treningsdataene.
b) Test modellen på testdataene.
c) Undersøk hvordan antallet weak learners i modellen påvirker nøyaktigheten.
d) For din beste modell: Plot “feature importance” og “partial dependance”. Hva sier plottene om hva som var avgjørende for om noen overlevde på Titanic?
e) Kan man ut ifra plottene vite hva Random forest-modellen vil predikere for en spesifikk person? Hvor godt kan man forstå hvordan en slik modell fungerer? Hva med et beslutningstre, eller en logistisk klassifiseringsmodell?