Klasyfikacja pasażerów Titanica

Projekt ten jest kontynuacją wpisu wprowadzającego do Microsoft Azure Machine Learning. W oparciu o rzeczywisty zbiór danych, zbuduję przykładowy model predykcyjny. Przy poprzednim projekcie założyłem, iż kolejny eksperyment będzie pokazywać inną technologię. Po epizodzie z open source, dziś czas na rozwiązanie Microsoft.

Jako motyw przewodni tego projektu wybrałem jeden z najpopularniejszych konkursów Kaggle. Jako że jest to oficjalny konkurs, to będę mógł „sprawdzić się” w rywalizacji z ponad 6000 zespołów które wzięły w nim udział.


  1. Cel konkursu.
  2. Jak działa Kaggle?
  3. Analiza zbioru danych.
  4. Feature Engineering – iteracja numer 1.
  5. Edycja metadanych.
  6. Uzupełnienie brakujących danych.
  7. Normalizacja danych numerycznych.
  8. Przycięcie odstających wartości.
  9. Wybór algorytmu.
  10. Wybór odpowiednich kolumn.
  11. Wynik + wnioski – iteracja numer 1.
  12. Feature Engineering – iteracja numer 2.
  13. Wynik + wnioski – iteracja numer 2.
  14. Feature Engineering – iteracja numer 3.
  15. Wynik + wnioski – iteracja numer 3.
  16. Feature Engineering – iteracja numer 4.
  17. Wynik + wnioski – iteracja numer 4.
  18. Podsumowanie.

Cel konkursu

Celem całej zabawy jest zbudowanie modelu, który w oparciu o wybrane spośród 10 predyktorów (atrybutów predykcyjnych) pozwoli nam wyznaczyć wartość zmiennej wynikowej. Atrybutem, którego wartość będziemy przewidywać jest „Survival”, który mówi nam czy dany pasażer przeżył katastrofę, czy też nie. Przyjmuje on wartości [0,1], gdzie 0=Nie, 1=Tak.

By wyznaczyć wartość „Survival”, będziemy się posługiwać 10 zmiennymi objaśniającymi:

  • Pclass – klasa | (1 = pierwsza; 2 = druga; 3 = trzecia).
  • Name – imię i nazwisko pasażera.
  • Sex – płeć pasażera.
  • Sibsp – liczba małżonków, lub rodzeństwa na pokładzie.
  • Parch – liczba rodziców, lub dzieci na pokładzie.
  • Ticket – numer biletu.
  • Fare – opłata za bilet.
  • Cabin – kabina.
  • Embarked – port startowy (C = Cherbourg; Q = Queenstown; S = Southampton).

Na początku powinienem przyjąć pewne założenia, np. kiedy kończę zabawę nad udoskonalaniem modelu. Jako punkt odniesienia przyjmę wyniki osiągane przez autorów. Przy konkursie opisane zostały trzy przykładowe podejścia do „ugryzienia” problemu, z których najlepsze uzyskało wynik: Accuracy = 0,77990. Wszystko większe lub równe 0,77990 uznam za sukces i zakończę pracę nad projektem.

Jak działa Kaggle?

Jeśli nie miałeś wcześniej styczności z konkursami publikowanymi na Kaggle, opiszę po krótce ideę przyświecają tej platformie.

Kaggle z założenia ma kojarzyć ze sobą Data Scientist-ów (a w zasadzie całe zespoły) i firmy poszukujące rozwiązania na problemy je trapiące. Problemy te oczywiście powinny być związane z Big Data 🙂 Dla przykładu, można znaleźć tu konkursy dotyczące predykcji zachowania kredytobiorców, segmentacji klientów, czy też przypisywania odpowiednich produktów do klientów banku (niedawny przykład Santandera).

Co ciekawe, prócz konkursów o charakterze czysto komercyjnym można znaleźć tu również takie którym przyświeca szlachetna idea. Problemy wykrywania zagrożonych wyginięciem waleni na zdjęciach robionych z helikopterów, czy zwiększenie współczynnika wykrywania nowotworów, to tylko niektóre z nich.

Niektóre konkursy mają wyznaczoną wysoką nagrodę pieniężna dla najlepszych zespołów. Doskonałym przykładem jest konkurs „Data Science Bowl 2017„, którego celem jest zwiększenie wykrywalności raka płuc. Organizatorzy oferują 1 milion dolarów dla zwycięzcy, więc myślę, że warto „pobrudzić się” danymi 🙂

Schemat wszystkich konkursów jest podobny i w większości przypadków można go opisać w kilku prostych krokach:

  1. Organizatorzy udostępniają zbiory danych i ustalają zasady konkursu.
  2. Osoby zainteresowane (bądź zespoły) zapisują się do konkursu i pobierają udostępnione zbiory danych.
  3. Na podstawie udostępnionych danych, uczestnicy konkursu budują model i wyznaczają poszukiwane wartości.
  4. Dane uzupełnione o zmienne wynikowe (w wymaganej przez organizatora formie) zostają przesłane do organizatora poprzez opcję „Make a submission” na stronie konkursu.
  5. Mechanizm waliduje przesłane dane testowe i wyznacza ocenę modelu.
  6. Wszystkie oceny, wraz z liczbą walidacji modelu i nazwą zespołu są widoczne w rankingu konkursu.
  7. Po upływie wyznaczonego czasu zamykana jest pierwsza część konkursu.
  8. Zwycięskie zespoły przekazują swój model do organizatora.
  9. Model zostaje przetestowany na prywatnych danych organizatora.
  10. Następuje ogłoszenie wyników i zwycięzców konkursu.

Analiza zbioru danych

Ok, więc zaczynamy. Dane pobieram na dysk komputera, a następnie ładuję plik „train.csv” do przestrzeni roboczej nowego projektu Azure ML. Czemu nie ładuję pliku „test.csv”? Ponieważ nie posiada on kolumny Survived, a co za tym idzie nie przyda mi się on przy uczeniu modelu 🙂 Wspomnianą kolumnę będę musiał ręcznie utworzyć i uzupełnić jej wartości z pomocą zbudowanego algorytmu i „wystawionego” Web Service-u.

Wszystko to nie oznacza jednak, że plik „test.csv” jest zupełnie bezużyteczny na etapie budowania modelu. Wykorzystam go do uzupełnienia brakujących wartości w pliku „train.csv” 🙂

By przyjrzeć się nieco bliżej danym, dodałem do projektu moduł Summarize Data. Pozwala on podejrzeć podstawowe metryki dla każdej kolumny, np. wartości minimalne, wartości maksymalne, średnią, medianę, itd.

tit_1

Już na pierwszy rzut oka widać kilka błędów:

  • Istnieje kilkadziesiąt brakujących wartości dla atrybutów: „Age”, „Cabin”, „Embarked”.
  • Kolumny: „Survived” i „Pclass” są automatycznie interpretowane przez Azure ML jako Numeric feature. Są to jednak bez wątpienia zmienne kategoryczne.
  • Kolumny: „Embarked” i „Sex” mechanizm interpretuje jako zmienne tekstowe. Z punktu widzenia badacza danych są to jednak zmienne kategoryczne.

W przypadku brakujących danych, intuicja podpowiada mi, że port, z którego odpływała dana osoba, nie jest tak istotny jak wiek tej osoby. Kabiną nie będę się przejmować, bo brakuje tu niemal 700 wartości. Oczywiście, można się tu doszukiwać ukrytych wzorców, ja jednak wstępnie pominę tę zmienną i nie będę się silić na uzupełnianie brakujących wartości. Pewnego rodzaju substytutem jest tu klasa, w której podróżował pasażer.

Feature Engineering – iteracja numer 1

W tym miejscu muszę przyznać, iż będę musiał się wspomagać skryptem napisanym w R. Pomimo, iż Azure ML pozwala zrobić z danymi wiele bez znajomości żadnego języka programowania, to mi wciąż najwygodniej jest wykonać niektóre operacje za pomocą sprawdzonego R-a 🙂

W kwestii uzupełniania wartości kolumny „Age” posiłkuję się właśnie R-em. Po analizie danych dochodzę do kilku wniosków, które przedstawiają się następująco:

  • Wszystkie kobiety o tytule „Miss”, które nie mają dzieci, to damy o średnim wieku 28 lat.
  • Panowie noszący tytuł „Master”, to kawalerzy, których średnia wieku to ok. 4,5 roku.
  • „Sir”, „Mr”, „Ms” i „Mrs” to z małymi wyjątkami dojrzałe osoby. Wiek dla wszystkich z nich uzupełniam posiłkując się średnią dla danej grupy.
  • Wszyscy panowie z tytułem „Dr”, to dojrzali mężczyźni o średniej wieku 42 lata.

Dla wszystkich innych osób, które nie mają podanego wieku, uzupełniam ich wiek średnią dla danej płci. Cała operacja zajmuje w kodzie R dosłownie kilka linijek, a może w sposób znaczący wpłynąć na wynik.

tit_2

Korzystając z dobrodziejstw jakie niesie ze sobą R, tworzę trzy dodatkowe zmienne, których użyję w dalszej części eksperymentu:

  1. „Family.Size” – tworzę ją przez dodanie do siebie dwóch istniejących wartości: „Parch” i „SibSp”, oraz liczby 1, odpowiadającej za daną osobę. Intuicyjnie uznaję, że wielkość rodziny mogła mieć znaczący wpływ na to czy komuś udało się przeżyć, czy też nie.
  2. „Age.Range” – jest to zmienna kategoryczna, która przypisuje pasażera do jednej z czterech kategorii wiekowych: „Bobas”, „Dzieciak”, „Nastolatek”, „Dorosly”. Mam tu na uwadze zasadę o której mówiono w filmie: „Women and children first” (btw. doczekała się ona osobnego wpisu na Wikipedii).
    plik$Age.Range <- cut(plik$Age,c(0,6,12,18,Inf),labels = c( „Bobas”,”Dzieciak”,”Nastolatek”,”Dorosly”))
    tit_3
  3. „MPC” – zmienna, którą chcę „uwypuklić” szanse na przeżycie dzieci i osób z pierwszej klasy. Jest ona wynikiem mnożenia wieku danej osoby i klasy, w której podróżowała. Dla przykładu, 5-letnie dziecko podróżujące w pierwszej klasie (wynik = 5), miało dużo większe szanse na przetrwanie katastrofy niż 70 letni pan podróżujący klasą trzecią (wynik = 210).

Myślę, że trzy dodatkowe zmienne to dobry punkt wyjścia do walki o jak najwyższy wynik 🙂

Edycja metadanych

Przed przejściem do uzupełnienia brakujących danych należy zmienić typ poszczególnych kolumn. Do eksperymentu dodałem moduł Edit Metadata. Za pomocą kreatora wybrałem kolumny: „Survived”, „Pclass”, „Embarked” i „Sex”. Następnie zmieniłem dla nich wartość typu zmiennej na „Categorical”.

tit_4

W kolejnym kroku wykonałem podobną operację dla zmiennej „Fare”, lecz w tym przypadku zmieniłem typ zmiennej na „Floating point”.

Uzupełnienie brakujących danych

W zasadzie większość pracy wykonałem tu z pomocą R. Pozostało mi uzupełnić brakujące wartości dla „Embarked”. W tym celu dodałem do projektu moduł Clean missing data. Jako metodę uzupełniania brakujących danych wybieram MICE (Multivariate imputation by choinek equations). W większości przypadków, gdy buduję model w Azure ML wybieram właśnie MICE. Jest to najbardziej finezyjna metoda spośród tych dostępnych w Azure ML. Jeśli ktoś chce użyć czegoś lepszego, musi posiłkować się R lub Pythonem.

tit_5

MICE, jak i temat dbania o jakoś danych przybliżę w jednym z najbliższych artykułów. Tymczasem nieco więcej o tym czym jest MICE oraz dlaczego daje ona tak dobre wyniki możesz dowiedzieć się ze tej świetnej publikacji: klik.

Normalizacja danych numerycznych

Wszystkie dane numerycznie muszą zostać znormalizowane. Dlaczego? Otóż podczas uczenia maszynowego zmienne numeryczne o większych wartościach mogą być postrzegane przez algorytm jako ważniejsze. By lepiej to wyjaśnić, posłużę się tu przykładem z danych dotyczących pasażerów Titanica. Dana pasażerka o imieniu „Sandstrom, Miss. Marguerite Rut” szczęśliwie przeżyła katastrofę Titanica. Z danych jasno wynika, że zapłacił za bilet 16.7, a w chwili podróży miała jedynie 4 lata. Algorytm uzna, że zdecydowanie większy wpływ na to, że udało mu się ocaleć miała cena biletu, co oczywiście nie musi być prawdą. Chyba każdy, po obejrzeniu filmu „Titanic” Jamesa Camerona jest w stanie postawić hipotezę, że największe szanse na przeżycie miały kobiety i dzieci… Właśnie dlatego należy dokonać normalizacji 🙂

W Azure ML normalizacja odbywa się poprzez dodanie modułu Normalize Data. W przeciwieństwie do poprzednio dodanych modułów, w tym kolumny wybieram nie po nazwie, lecz po typie zmiennych. W tym przypadku używam filtra „Column type: Numeric, All” oraz „Exclude column names: Survived” (nie chcę by algorytm dokonywał normalizacji danej kategorycznej). Jako metodę użytą do transformacji danych wybieram „MinMax”.

Przycięcie odstających wartości

Przycięcie odstających wartości zaczynam od analizy wartości poszczególnych atrybutów. Najlepszym do tego narzędziem jest wykres punktowy, gdzie nanoszę na obu osiach ten sam atrybut. Dzięki temu czarno na białym widać które wartości odstają. Poniżej przykład wizualizacji wieku poszczególnych pasażerów. Dla kolumny Age usunę wszystkie wartości powyżej 67 i zastąpię je średnim wiekiem.

Przed:

tit_6

Po:

tit_7

UWAGA: Przycinania odstających wartości dokonuję przed normalizacją danych, dlatego moduł Clip Values wstawiam przed Normalize Data.

Podobnie postępuję z ceną biletu, jaką zapłacili poszczególni pasażerowie. Odstające wartości zostają zastąpione średnią.

Wybór algorytmu

Bez wątpienia mamy tu do czynienia z dwuklasowym problemem klasyfikacyjnym. W oparciu o podane atrybuty musimy wyznaczyć jedną z dwóch wartości: [0, 1], [Tak, Nie], [Prawda, Fałsz], etc.

Finalnie będę chciał wybrać maksymalnie 6-8 atrybutów które posłużą mi jako predyktory. Z doświadczenia wiem, że przy takiej liczbie atrybutów, przy problemie klasyfikacyjnym najlepsze rezultaty daje drzewo decyzyjne, dlatego poszukiwania idealnego algorytmu zacznę od: „Boosted Decision Tree” i „Decision Jungle”.

By porównywać wyniki jakie dają poszczególne algorytmy muszę podzielić dane na uczące i walidujące. W tym celu dodaję moduł Split Data. Dane dzielę w sposób losowy, przy czym 70% stanowią dane uczące.

Następnie dodaję dwa moduły Train Model i w obu przypadkach ustawiam „Label Column” na „Survived”. Dodaję do modelu oba algorytmy: „Two-Class Boosted Decision Tree” i „Two-Class Decision Jungle”. Następnie muszę porównać uzyskane wyniki z rzeczywistymi danymi. W tym celu dodaję moduł Score Model. Ostatnim elementem jest dodanie modułu Evaluate Model, który pozwoli mi porównać wyniki.

Na koniec całość powinna wyglądać mniej więcej tak:

tit_8

Wybór odpowiednich kolumn

W Azure ML jest moduł, który pozwala nam badać wpływ zmiennych na osiągany wynik. Permutation Feature Importance, bo tak właśnie się on nazywa bada przydatność zmiennych w kilku podstawowych metrykach, m.in. Accuracy oraz Precision.

Po kilku iteracjach dochodzę do momentu w którym na placu boju pozostają następujące zmienne: „Sex”, „Survived”, „Age”, „Age.Range”, „Pląs”, oraz „Fare”.

Wynik + wnioski – iteracja numer 1

Po zbudowaniu finalnej wersji algorytmu przyszedł czas na weryfikację na danych testowych. Przy kolejnych iteracjach będę powtarzać dokładnie te same kroki, więc w tym miejscu opiszę, jak to robię.

By zweryfikować model na danych testowych utworzyłem Web Service. Nie nanosiłem na niego praktycznie żadnych zmian w stosunku do pierwotnej wersji modelu. Jedynie na samym końcu dodałem moduł „Select Columns in Dataset” (z wybraną kolumną „Scored labels”), który pozwala mi przefiltrować wartości jakie zostaną zwrócone na wyjściu z procesu. Dzięki temu w odpowiedzi na zapytanie otrzymam jedynie estymowaną wartość kolumny „Survived”.

Całość wygląda mniej więcej w ten sposób:

tit_9

Po uruchomieniu, nie pozostaje mi nic innego jak uruchomić WebService. Dzięki niemu uzyskam dostęp m.in. do Excela ze skonfigurowanym API pozwalającym mi odpytywać mój WebService.

tit_10

Mam już pierwsze wartości. Eksportuję je do pliku csv a następnie wysyłam do Kaggle i już po chwili widzę swój wynik. Po drodze jeszcze jeden żart od Kaggle…

tit_11

Iiii jest! Dokładność na poziomie 0.77990 🙂 nie jest źle!

tit_12

W pierwszej próbie z użyciem Azure ML udało mi się wyrównać wynik autorów tutoriali dostępnych na Kaggle. Warto jednak powalczyć o więcej.

Pierwsze wnioski wyglądają następująco:

  • Zmienna „MPC” nie wpłynęła pozytywnie na wynik.
  • Zmienne „Family.Size”, oraz „Age.Range” wpłynęły pozytywnie na wynik osiągany na zbiorze uczącym.
  • Spośród testowanych algorytmów uczenia maszynowego najlepsze wyniki osiągał „Two-Class Decision Jungle”.

Feature Engineering– iteracja numer 2

Przyjrzałem się nieco bliżej danym i doszedłem do wniosku że dużo o sytuacji danej osoby może mówić jej tytuł. Dzięki niemu można powiedzieć nieco więcej o stanie cywilnym danej osoby, wieku, oraz pochodzeniu. Dla przykładu „Master”, to młody kawaler. Tytuł „Mml” nosiły niezamężne Francuzki i jest to odpowiednik dla angielskiego „Miss”. Dodaję zatem z pomocą R nową kolumnę „Title” i przypisuję do niej odpowiednie wartości w oparciu o kolumnę „Name”:

tit_13

By poprawić swój wynik muszę lepiej estymować wartość kolumny „Age” dla wszystkich pasażerów, którzy nie mają podanego wieku. Mogę to zrobić na kilka sposobów.

  1. Wyestymować wartość wieku z pomocą algorytmu regresyjnego – tu pojawia się duże utrudnienie, gdyż Azure ML pozwala korzystać jedynie z jednego algorytmu ML, w jednym eksperymencie. Konieczny byłby więc podział na dwa osobne eksperymentu, z których wynik pierwszego, dawał by wsad do eksperymentu drugiego.
  2. Wyestymować wartość wieku z pomocą algorytmu regresyjnego z poziomu kodu R, lub Python.
  3. Użyć podejścia niekonwencjonalnego i podzielić jeden duży eksperyment na dwa mniejsze: jeden dla pasażerów, którzy mają podany wiek, oraz drugi dla wszystkich których wieku nie znamy.

Spośród powyższych metod w pierwszej kolejności zdecydowałem się na numer 1. Udało mi się osiągnąć dokładność estymowania wieku pasażerów, co do 8 lat. O ile w przypadku dorosłych 8 lat nie robiło dużej różnicy, o tyle w przypadku dzieci różnica niestety była znacząca.

Wynik + wnioski – iteracja numer 2

Po naniesieniu dwóch niewielkich zmian wynik znacząco podskoczył. Już jest lepszy od najlepszego uzyskanego przez autorów tutoriali dostępnych na Kaggle 🙂

tit_14

Wnioski po drugiej iteracji wyglądają następująco:

  • Kolumna „Title” znacząco wpływa na wynik.
  • Kolumna „Age” jest kluczem do sukcesu w konkursie.
  • Dodatkowy model regresyjny poprawia wynik na zbiorze uczącym.

Feature Engineering– iteracja numer 3

W trzeciej iteracji naniosłem dwie niewielkie zmiany w następujących zmiennych:

  1. „Fare” – przeglądając dane zauważyłem w pliku z danymi testowymi kilku pasażerów nie ma podanej wartości ceny biletu. Są to mężczyźni płynący trzecią klasą, wyruszający z portu „Southampton”. Z pomocą R, uzupełniam te braki medianą ceny biletu dla danej grupy (np. mediana opłaty za bilet, pasażera trzeciej klasy płynącego z Southampton, podróżującego samotnie).
  2. „Age” – po ponownym zastosowaniu eksploracyjnej analizy danych dostrzegłem ciekawą prawidłowość. Spośród wszystkich osób, żaden inny tytuł nie wskazuje wieku pasażera z taką dokładnością jak „Master” i „Dr”. Pierwszy z nich jest przypisany w zdecydowanej większości dla dzieci płci męskiej, drugi natomiast dla starszych pasażerów. Decyduję się zatem przypisać wartości dla wszystkich osób posiadających wymienione tytuły, a jednocześnie nie posiadających podanego wieku, „na sztywno”. Posiłkuję się w tym przypadku wartościami średnimi.

Przeglądając fora internetowe, oraz czytając artykuły dotyczące zatonięcia Titanica doszukałem się ciekawej teorii. Mówi ona, iż duże rodziny miały mniejsze szanse na przeżycie niż osoby podróżujące samotnie, ew. będące członkami mniejszych rodzin. Jako założenie przyjąłem, że mała rodzina, to taka która posiada mniej niż 3 osoby. Dodałem zatem zmienną „Family.ID”, która jednoznacznie pozwala mi identyfikować osoby przynależące do danej rodziny. Utworzyłem ją z nazwiska danej osoby, oraz wartości „Family.Size”.

By uniknąć zbyt dużej liczby zmiennych kategorycznych postanowiłem zastąpić wszystkie wartości „Family.ID” dla rodzin mniejszych niż 3, wartością „Small”. Pozwoliło mi to zachować rozsądną liczbę 34 kategorii dla tej kolumny. Dodatkowo, by podkreślić wielkość rodziny i jej wpływ na wartość „Survived”, dodałem kolumnę „Family.Size.P”. Przyjmowała ona jedną z dwóch wartości: „Small”, lub „Big”.
Wszystkie powyższe zmiany są widoczne na poniższym zrzucie ekranu.

tit_15

Wynik + wnioski – iteracja numer 3

Po naniesieniu tych kilku zmian parametry Accuracy i Precision na zbiorze uczącym znacząco się poprawiły. Teoria mówiąca o tym, że duże rodziny miały mniejsze szanse na przeżycie katastrofy Titanica zdaje się być prawdziwa. Pora zweryfikować, jak ma się to odniesieniu do zbioru testowego.

tit_16

Jest poprawa! 🙂 Mam jeszcze jednego asa w rękawie, więc spróbuję wyżyłować ten wynik tak by przekroczyć magiczną granicę 0,8.

Wnioski po trzeciej iteracji wyglądają następująco:

  • Nowa kolumna – „Family.ID” – pozytywnie wpłynęła na wynik.
  • Zmniejszenie liczby zmiennych w kolumnie „Family.ID” podniosło efektywność modelu.
  • Równocześnie z pozytywnym wpływem „Family.ID”, zaobserwowałem odwrotny efekt w przypadku zmiennej „Size.P”. Okazała się ona zupełnie niepotrzebna i jedynie zaniżała osiągany wynik.

Feature Engineering – iteracja numer 4

Czwarta iteracja i ostatnie pomysły które chodziły mi po głowie. Postanowiłem wrócić do elementu, który rozważałem w drugiej iteracji. Pisałem tam o trzech pomysłach związanych z eliminacją szumu spowodowanego dużymi brakami w kolumnie „Age” (która przecież ma największy wpływ na dokładność modelu). Przyjąłem wtedy podejście mówiące o zbudowaniu drugiego, regresyjnego modelu i estymowanie wieku wszędzie tam, gdzie mamy widoczne braki. Chciałbym teraz wypróbować podejście alternatywne i sprawdzić jego wpływ na finalny wynik. Decyduję się na numer 3, a więc:

„Użyć podejścia niekonwencjonalnego i podzielić jeden duży eksperyment na dwa mniejsze: jeden dla pasażerów, którzy mają podany wiek, oraz drugi dla wszystkich których wieku nie znamy.”

Buduję zatem dwa oddzielne modele. W pierwszym uczę model na wszystkich zmiennych predykcyjnych, które sprawdzały się do tej pory, ze zmienną „Age” włącznie. W drugim uczę model na wszystkich zmiennych z wyjątkiem zmiennej „Age” oraz „Age.Range”.

Decyduję się na zastosowanie jeszcze jednej, banalnej zmiany, która ma znaczący wpływ na osiągane wyniki 🙂 Różnicuję liczebność zbiorów użytych w obu eksperymentach. Jak to robię i czemu ma to w ogóle służyć?

Otóż, cały zbiór uczący do którego mam dostęp w konkursie, liczy 891 obserwacji statystycznych (pasażerów i ich zmiennych objaśniających). 719 z nich ma podany wiek. 172 z nich nie mają podanego wieku. Gdybym właśnie tak podzielił cały zbiór, to drugi model byłby bardzo małą próbą w odniesieniu do całej populacji i pierwszego modelu.  Decyduję się zatem użyć w modelu numer dwa cały zbiór testowy, z usuniętą zmienną „Age”. Otrzymuję zatem dwie próby o łącznej liczebności 1610, zamiast pierwotnych 891.

Powyższa zmiana ma znaczący wpływ na wynik. Na zbiorze testowym osiągam bardzo dobre wyniki, co potwierdza następujący zrzut ekranu:

tit_17

Accuracy na poziomie 0.858 i Precision = 0.910. To musi oznaczać jedno z dwóch:

  1. Będzie poprawa wyniku, bo model jest naprawdę dobry.
  2. Będzie drastyczne pogorszenie wyniku, bo model jest przeuczony 🙂

Oba modele są bliźniaczo podobne i wyglądają następująco:

tit_19tit_18Cała „magia” jest zaszyta w jednym z modułów Azure ML i skrypcie R. Sprowadza się ona do dwóch elementów:

  • Trzy dodatkowe linijek kodu, które posiada model „1 – Age”, a więc klasyfikujący pasażerów z podanym wiekiem. Są to kolejno:
    • plik$Age.NA <- FALSE
    • plik$Age.NA[is.na(plik$Age)] <- TRUE
    • plik <- plik[plik$Age.NA==FALSE,]
  • Z pomocą modułu „Select columns in Dataset” usuwam kolumny: „Age”, „Age.Range” z modelu „1 – NA”.

Wynik + wnioski – iteracja numer 4

Podzieliłem zbiór testowym zgodnie z wymaganiami, a więc na tych pasażerów, którzy mają podany wiek i tych którzy go nie mają. Uruchomiłem Web Service-y, złączyłem dane wyjściowe i…

tit_20

Udało się! 🙂 W międzyczasie Kaggle nieco zmienił szatę graficzną i wyniki prezentowane są w nieco inny sposób.

Wnioski:

  • Zmiana podejścia przyniosła oczekiwane rezultaty.
  • Mój model regresyjny jednak nie był tak dobry jak mi się pierwotnie wydawało i wnosił sporo „szumu” do danych. Nauczka na przyszłość dla mnie, by równolegle testować przynajmniej dwa alternatywne podejścia do budowania modelu.
  • Im dalej w las, tym więcej drzew. Każda kolejna próba poprawy modelu była większym wyzwaniem niż poprzednia.
  • Często najlepsze rezultaty dają najprostsze metody.

Podsumowanie

W powyższym „projekcie” pokazałem jedynie cztery iteracje, w których walczyłem o poprawę wyniku. By być w 100% szczerym musze zaznaczyć, że iteracji prowadzących do osiągnięcia finalnego rezultatu było zdecydowanie więcej. Nie umieściłem ich opisu w tym wpisie, bo byłoby to niezwykle czasochłonne. Umieściłem więc jedynie cztery będące swego rodzaju „skrótem” mojej walki z danymi. Niech potwierdzeniem dla Ciebie drogi czytelniku będzie fakt, że wynik 0.80383 osiągnąłem przy 76 zgłoszeniu danych…

Co do samego wyniku, to zdecydowanie uznaję go za satysfakcjonujący. By go osiągnąć nie obeszło się bez wertowania informacji odnośnie katastrofy Titanica na anglojęzycznych forach i stronach internetowych, w poszukiwaniu czynników mających wpływ na to ilu osobom udało się przeżyć. Bardzo dobrym źródłem informacji, które warto polecić jest forum Kaggle. Setki osób dzielą się w nim swoimi przemyśleniami i analizami dotyczącymi danego konkursu.

Czy 0.80383 to wszystko na co mnie stać? Zdecydowanie nie. Nie chciałbym jednak przeznaczać zbyt wiele godzin mojej pracy na zabawę z danymi i udział w konkursach dla przyjemności 🙂 Z opinii ekspertów wynika, iż bazując na tym zbiorze danych można osiągnąć maksymalne rezultaty w granicy 0.85, a więc pozostaje mi jeszcze sporo miejsca na wzrost. Finalnie osiągnięty przeze mnie wynik daje (na dzień pisania tego artykułu) miejsce w okolicy 500 spośród 6000 zespołów, a więc jest to pierwsze 10% spośród wszystkich notowanych zespołów.

Dziękuję Ci za dobrnięcie do samego końca. Jeśli masz jakiekolwiek uwagi, proszę podziel się nimi w komentarzu, lub skontaktuj się ze mną.Udało Ci się osiągnąć w tym samym konkursie lepszy wynik? Również proszę podziel się przyjętym przez Ciebie podejściem.
Wszystkie pliki utworzone przy projekcie można znaleźć na moim GitHub’ie.

źródło zdjęcia

PODOBAŁ CI SIĘ TEN ARTYKUŁ?

Jeśli tak, to zarejestruj się, by otrzymywać informacje o nowych wpisach.
Dodatkowo w prezencie wyślę Ci bezpłatny poradnik :-)

1 Komentarz

Dodaj komentarz

Twój adres email nie zostanie opublikowany.


*