Machine Learning - THE DATA SCIENCE LIBRARY http://sigmaquality.pl/tag/machine-learning/ Wojciech Moszczyński Fri, 04 Oct 2019 19:38:00 +0000 pl-PL hourly 1 https://wordpress.org/?v=6.8.3 https://sigmaquality.pl/wp-content/uploads/2019/02/cropped-ryba-32x32.png Machine Learning - THE DATA SCIENCE LIBRARY http://sigmaquality.pl/tag/machine-learning/ 32 32 Artificial Intelligence w Marketingu Bankowym – Naive Bayes Classifier part 4 https://sigmaquality.pl/uncategorized/artificial-intelligence-w-marketingu-bankowym-naive-bayes-classifier/ Fri, 04 Oct 2019 19:38:00 +0000 http://sigmaquality.pl/artificial-intelligence-w-marketingu-bankowym-naive-bayes-classifier-part-4/     Exercise 29   Naive Bayes Classifier Naiwny Bayes to prosta technika konstruowania klasyfikatorów: modele, które przypisują etykiety klas do wystąpień problemowych, reprezentowane jako [...]

Artykuł Artificial Intelligence w Marketingu Bankowym – Naive Bayes Classifier part 4 pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
 

 

Exercise 29

 

Naive Bayes Classifier

Naiwny Bayes to prosta technika konstruowania klasyfikatorów: modele, które przypisują etykiety klas do wystąpień problemowych, reprezentowane jako wektory wartości cech , w których etykiety klas są rysowane z pewnego zbioru skończonego. Nie istnieje jeden algorytm do szkolenia takich klasyfikatorów, ale rodzina algorytmów oparta na wspólnej zasadzie: wszystkie naiwne klasyfikatory Bayesa zakładają, że wartość określonej cechy jest niezależna od wartości innej cechy, biorąc pod uwagę zmienną klasy. Na przykład owoc można uznać za jabłko, jeśli jest czerwone, okrągłe i ma około 10 cm średnicy. Naiwny klasyfikator Bayesa uważa, że każda z tych cech przyczynia się niezależnie do prawdopodobieństwa, że ​​ten owoc jest jabłkiem, niezależnie od wszelkich możliwych korelacje między kolorem, okrągłością i średnicą.

W przypadku niektórych rodzajów modeli prawdopodobieństwa naiwnych klasyfikatorów Bayesa można bardzo skutecznie trenować w nadzorowanym środowisku uczenia się . W wielu praktycznych zastosowaniach estymacja parametrów dla naiwnych modeli Bayesa wykorzystuje metodę maksymalnego prawdopodobieństwa ; innymi słowy, można pracować z naiwnym modelem Bayesa bez akceptacji prawdopodobieństwa bayesowskiego lub przy użyciu jakichkolwiek metod bayesowskich.

Pomimo naiwnego projektu i pozornie uproszczonych założeń, naiwne klasyfikatory Bayesa działały całkiem dobrze w wielu złożonych rzeczywistych sytuacjach. W 2004 r. Analiza problemu klasyfikacji bayesowskiej wykazała, że ​​istnieją uzasadnione teoretyczne powody pozornie nieprawdopodobnej skuteczności naiwnych klasyfikatorów Bayesa. [6] Jednak kompleksowe porównanie z innymi algorytmami klasyfikacji w 2006 r. Wykazało, że klasyfikacja Bayesa jest lepsza niż inne podejścia, takie jak wzmocnione drzewa lub losowe lasy . [7]

Zaletą naiwnych Bayesa jest to, że wymaga jedynie niewielkiej liczby danych treningowych do oszacowania parametrów niezbędnych do klasyfikacji.
źródło: https://en.wikipedia.org/wiki/Naive_Bayes_classifier

 
Publikacja jest kolejną częścią analizy machine learning w obszarze marketingu bankowego.
 
http://sigmaquality.pl/machine-learning/artificial-intelligence-w-marketingu-bankowym-threshold-roc-curve/
 
In [1]:
import numpy as np
import pandas as pd
import seaborn as sns

from sklearn.preprocessing import LabelEncoder
import matplotlib.pylab as plt
from pylab import plot, show, subplot, specgram, imshow, savefig
from sklearn import preprocessing
from sklearn.preprocessing import Normalizer
from sklearn.preprocessing import Imputer

import matplotlib.pyplot as plote



plt.style.use('ggplot')

df = pd.read_csv('c:/1/bank.csv')
df.head()
 

Jak w praktyce zastosować Naive Bayes Classifier w marketingu bankowym?

Departament marketingu bankowego niedawno przeprowadził akcję promocyjną. Klienci byli zachęcani do skorzystania z oferty banku – zaciągania kredytów w rachunku bieżącym. Cała operacja została skrupulatnie odnotowana w formie tabeli danych. Tabelę danych można znaleźć pod adresem: https://archive.ics.uci.edu/ml/machine-learning-databases/00222/

 

Dane klienta banku

age: (numerycznie)

job: rodzaj pracy (kategorycznie: „admin.”, „Pracownik fizyczny”, „przedsiębiorca”, „pokojówka”, „zarządzanie”, „emerytowany”, „samozatrudniony”, „ usługi ”,„ student ”,„ technik ”,„ bezrobotny ”,„ nieznany ”)

marital: stan cywilny (kategorycznie:„ rozwiedziony ”,„ żonaty ”,„ samotny ”,„ nieznany ”; uwaga:„ rozwiedziony ”oznacza rozwiedziony lub owdowiały)

education: (kategoryczne: „podstawowy. 4 lata”, „podstawowy. 6 lat”, „podstawowy. 9 lat”, „szkoła średnia”, „analfabeta”, „kurs zawodowy”, „uniwersytet. stopień”, „nieznane”)

default: czy kredyt jest niespłacony? (kategorycznie: „nie”, „tak”, „nieznany”)

housing: ma kredyt mieszkaniowy? (kategorycznie: „nie”, „tak”, „nieznany”)

loan: czy pożyczka osobista? (kategorycznie: „nie”, „tak”, „nieznany”)
Powiązane z ostatnim kontaktem bieżącej kampanii

contact: typ komunikacji kontaktowej (kategorycznie: „komórkowy”, „telefon”)

month:ostatni miesiąc kontaktowy w roku (kategorycznie: „jan”, „lut”, „mar”, …, „lis”, „dec”)

day_of_week: ostatni dzień tygodnia w tygodniu (kategorycznie: „pon”, „wt”, „środ”, „czw”, „pt”)

duration: czas trwania ostatniego kontaktu, w sekundach (numerycznie) . Ważna uwaga: ten atrybut ma duży wpływ na docelowy wynik (np. Jeśli czas trwania = 0, to y = „nie”). Jednak czas trwania nie jest znany przed wykonaniem połączenia. Ponadto po zakończeniu połączenia y jest oczywiście znane. W związku z tym dane te należy uwzględnić wyłącznie do celów porównawczych i należy je odrzucić, jeżeli intencją jest stworzenie realistycznego modelu predykcyjnego.
Inne atrybuty

campaign: liczba kontaktów wykonanych podczas tej kampanii i dla tego klienta (numerycznie, obejmuje ostatni kontakt)

pdays: liczba dni, które upłynęły od ostatniego kontaktu klienta z poprzedniej kampanii (numerycznie; 999 oznacza, że klient nie był wcześniej skontaktowano się)

previous: liczba kontaktów wykonanych przed tą kampanią i dla tego klienta (numerycznie)

poutcome:wynik poprzedniej kampanii marketingowej (kategorycznie: „porażka”, „nieistniejąca”, „sukces”)
Atrybuty kontekstu społecznego i gospodarczego

emp.var.rate: wskaźnik zmienności zatrudnienia – wskaźnik kwartalny (liczbowy)

Cons.price.idx: wskaźnik cen konsumpcyjnych – wskaźnik miesięczny (liczbowy)

cons.conf.idx: wskaźnik zaufania konsumentów – wskaźnik miesięczny (liczbowy )

euribor3 mln: stawka 3-miesięczna euribor – wskaźnik dzienny (liczbowy)

nr_employed: liczba zatrudnionych: liczba pracowników – wskaźnik kwartalny (liczbowy)

In [3]:
df = pd.read_csv('c:/1/bank.csv')
In [3]:
df.shape
Out[3]:
(41188, 23)
 

Sprawdzamy czy baza zawiera komórki puste, Naive Bayes Classifier nie działa gdy baza zawiera komórki puste.

In [4]:
df.isnull().sum()
Out[4]:
Unnamed: 0        0
Unnamed: 0.1      0
age               0
job               0
marital           0
education         0
default           0
housing           0
loan              0
contact           0
month             0
day_of_week       0
duration          0
campaign          0
pdays             0
previous          0
poutcome          0
emp_var_rate      0
cons_price_idx    0
cons_conf_idx     0
euribor3m         0
nr_employed       0
y                 0
dtype: int64
 

Brak komórek pustych w rejestrze bankowym.

In [5]:
df.head()
Out[5]:
  Unnamed: 0 Unnamed: 0.1 age job marital education default housing loan contact campaign pdays previous poutcome emp_var_rate cons_price_idx cons_conf_idx euribor3m nr_employed y
0 0 0 44 blue-collar married basic.4y unknown yes no cellular 1 999 0 nonexistent 1.4 93.444 -36.1 4.963 5228.1 0
1 1 1 53 technician married unknown no no no cellular 1 999 0 nonexistent -0.1 93.200 -42.0 4.021 5195.8 0
2 2 2 28 management single university.degree no yes no cellular 3 6 2 success -1.7 94.055 -39.8 0.729 4991.6 1
3 3 3 39 services married high.school no no no cellular 2 999 0 nonexistent -1.8 93.075 -47.1 1.405 5099.1 0
4 4 4 55 retired married basic.4y no yes no cellular 1 3 1 success -2.9 92.201 -31.4 0.869 5076.2 1

5 rows × 23 columns

 

Kasujemy z rejestru następujące klumny:

Usuwam kolumny: 'day_of_week’,’month’,’previous’,’campaign’,’duration’, 'contact’, 'y’

In [47]:
df.columns
Out[47]:
Index(['age', 'job', 'marital', 'education', 'default', 'housing', 'loan',
       'poutcome', 'emp_var_rate', 'cons_price_idx', 'cons_conf_idx',
       'euribor3m', 'nr_employed', 'y', 'ResultTH09', 'ResultTH08',
       'ResultTH07', 'ResultTH06', 'ResultTH05', 'ResultTH04', 'ResultTH03',
       'ResultTH02', 'Customer_classification'],
      dtype='object')
In [6]:
df = df[['age', 'job', 'marital', 'education','default', 'housing', 'loan', 'poutcome', 'emp_var_rate','cons_price_idx', 'cons_conf_idx', 'euribor3m', 'nr_employed','y']]
In [48]:
df.head(5)
Out[48]:
  age job marital education default housing loan poutcome emp_var_rate cons_price_idx y ResultTH09 ResultTH08 ResultTH07 ResultTH06 ResultTH05 ResultTH04 ResultTH03 ResultTH02 Customer_classification
0 44 1 1 0 1 2 0 1 1.4 93.444 0 0 0 0 0 0 0 0 0 0
1 53 9 1 7 0 0 0 1 -0.1 93.200 0 0 0 0 0 0 0 0 0 0
2 28 4 2 6 0 2 0 2 -1.7 94.055 1 1 1 1 1 1 1 1 1 8
3 39 7 1 3 0 0 0 1 -1.8 93.075 0 0 0 0 0 0 1 1 1 3
4 55 5 1 0 0 2 0 2 -2.9 92.201 1 1 1 1 1 1 1 1 1 8

5 rows × 23 columns

 

Tworzenie Dummy Variables

Naive Bayes Classifier działa na zmiennych numerycznych. Trzeba więc zamienić zmienne tekstowe na format numeryczny.

Wyświetlamy kolumny zmiennnych dyskretnych

In [10]:
df.describe(include=["object"]).columns
Out[10]:
Index(['job', 'marital', 'education', 'default', 'housing', 'loan',
       'poutcome'],
      dtype='object')
In [11]:
list = ['job', 'marital', 'education', 'default', 'housing', 'loan','poutcome']
 

Transforumjemy na dane dummy variables

In [13]:
df[list] = df[list].apply(LabelEncoder().fit_transform)
df[list].head()
Out[13]:
  job marital education default housing loan poutcome
0 1 1 0 1 2 0 1
1 9 1 7 0 0 0 1
2 4 2 6 0 2 0 2
3 7 1 3 0 0 0 1
4 5 1 0 0 2 0 2
 

Dzielimy nowy zbiór df na zmienne opisujące i zmienną wynikową.

In [14]:
y = df['y']
X = df.drop('y', axis=1) 
 

Dzielimy zbiór df na zbiór testowy i zbiór treningowy.

In [16]:
from sklearn.model_selection import train_test_split 
Xtrain, Xtest, ytrain, ytest = train_test_split(X,y, test_size=0.33, stratify = y, random_state = 148)
 

OVERSAMPLING

Dla zbioru df bo zmienna wynikowa nie jest zbilansowana. Oversampling robimy na zmiennych treningowych.

In [49]:
df.y.value_counts(dropna = False, normalize=True)
Out[49]:
0    0.887346
1    0.112654
Name: y, dtype: float64
In [50]:
Proporcja = sum(ytrain == 0) / sum(ytrain == 1) 
Proporcja = np.round(Proporcja, decimals=0)
Proporcja = Proporcja.astype(int)
print('Ile na jedną sybskrypcje przypada nieprzedłużonych subskrypcji:',Proporcja)
Ile na jedną sybskrypcje przypada nieprzedłużonych subskrypcji: 8
 

Replikacja zmiennych wynikowych w zbiorze treningowym

In [51]:
ytrain_pos_OVS = pd.concat([ytrain[ytrain==1]] * Proporcja, axis = 0) 
print('Ilość zmiennych wynikowych y  w zbiorze treningowym df po replikacji:' ,ytrain_pos_OVS.count())
Ilość zmiennych wynikowych y  w zbiorze treningowym df po replikacji: 24872
 

Replikacja zmiennych niezależnych w zbiorze treningowym

In [52]:
Xtrain_pos_OVS = pd.concat([Xtrain.loc[ytrain==1, :]] * Proporcja, axis = 0)
In [53]:
ytrain_pos_OVS = pd.concat([ytrain[ytrain==1]] * Proporcja, axis = 0) 
print('Ilość zmiennych niezależnychX  z zbiorze treningowym df po replikacji:')
Xtrain_pos_OVS.count()
Ilość zmiennych niezależnychX  z zbiorze treningowym df po replikacji:
Out[53]:
age               24872
job               24872
marital           24872
education         24872
default           24872
housing           24872
loan              24872
poutcome          24872
emp_var_rate      24872
cons_price_idx    24872
cons_conf_idx     24872
euribor3m         24872
nr_employed       24872
dtype: int64
 

Wprowadzenie nowych, zreplikowanych zmiennych do zbioru treningowego

In [23]:
ytrain_OVS = pd.concat([ytrain, ytrain_pos_OVS], axis = 0).reset_index(drop = True)
Xtrain_OVS = pd.concat([Xtrain, Xtrain_pos_OVS], axis = 0).reset_index(drop = True)
 

Sprawdzenie ilość wierszy w zbiorach przed i po oversampling

In [24]:
print ('Zbiór X treningowy: ',Xtrain.shape)
print ('Zbiór X testowy:    ', Xtest.shape)
print ('Zbiór y treningowy: ', ytrain.shape)
print ('Zbiór y testowy:    ', ytest.shape)
Zbiór X treningowy:  (27595, 13)
Zbiór X testowy:     (13593, 13)
Zbiór y treningowy:  (27595,)
Zbiór y testowy:     (13593,)
In [25]:
Xtrain_OVS.head(3)
Out[25]:
  age job marital education default housing loan poutcome emp_var_rate cons_price_idx cons_conf_idx euribor3m nr_employed
0 49 1 1 2 1 0 0 1 1.4 94.465 -41.8 4.959 5228.1
1 38 9 0 6 1 0 0 1 1.4 93.444 -36.1 4.963 5228.1
2 42 0 0 6 0 0 0 1 1.4 94.465 -41.8 4.959 5228.1
 

Naive Bayes Classifier na zbiorze df

In [54]:
from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
gnb.fit(Xtrain, ytrain)
Out[54]:
GaussianNB(priors=None, var_smoothing=1e-09)
 

Podstawienie do wzoru

In [55]:
y_pred = gnb.predict(Xtest)
 

Blok diagnostyczny

In [56]:
from sklearn import metrics
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.metrics import confusion_matrix, log_loss, auc, roc_curve, roc_auc_score, recall_score, precision_recall_curve
from sklearn.metrics import make_scorer, precision_score, fbeta_score, f1_score, classification_report

print("Recall Training data:     ", np.round(recall_score(ytrain_OVS, gnb.predict(Xtrain_OVS)), decimals=4))
print("Precision Training data:  ", np.round(precision_score(ytrain_OVS, gnb.predict(Xtrain_OVS)), decimals=4))
print("----------------------------------------------------------------------")
print("Recall Test data:         ", np.round(recall_score(ytest, gnb.predict(Xtest)), decimals=4)) 
print("Precision Test data:      ", np.round(precision_score(ytest, gnb.predict(Xtest)), decimals=4))
print("----------------------------------------------------------------------")
print("Confusion Matrix Test data")
print(confusion_matrix(ytest, gnb.predict(Xtest)))
print("----------------------------------------------------------------------")
print(classification_report(ytest, gnb.predict(Xtest)))
y_pred_proba = gnb.predict_proba(Xtest)[::,1]
fpr, tpr, _ = metrics.roc_curve(ytest,  y_pred_proba)
auc = metrics.roc_auc_score(ytest, y_pred_proba)
plt.plot(fpr, tpr, label='Logistic Regression (auc = 
plt.xlabel('False Positive Rate',color='grey', fontsize = 13)
plt.ylabel('True Positive Rate',color='grey', fontsize = 13)
plt.title('Receiver operating characteristic')
plt.legend(loc="lower right")
plt.legend(loc=4)
plt.plot([0, 1], [0, 1],'r--')
plt.show()
Recall Training data:      0.669
Precision Training data:   0.7853
----------------------------------------------------------------------
Recall Test data:          0.6571
Precision Test data:       0.2769
----------------------------------------------------------------------
Confusion Matrix Test data
[[9435 2627]
 [ 525 1006]]
----------------------------------------------------------------------
              precision    recall  f1-score   support

           0       0.95      0.78      0.86     12062
           1       0.28      0.66      0.39      1531

    accuracy                           0.77     13593
   macro avg       0.61      0.72      0.62     13593
weighted avg       0.87      0.77      0.80     13593

 

Tworzenie listy dla kierownika marketingu

In [37]:
print('Lista dla kierownika marketingu bankowego')
df3[df3['Wynik']==1].head(4)
Lista kierownika marketingu bankowego
Out[37]:
  age job marital education default housing loan poutcome emp_var_rate cons_price_idx cons_conf_idx euribor3m nr_employed Wynik
17516 63 retired married professional.course no no no nonexistent -1.8 93.075 -47.1 1.498 5099.1 1
3037 34 services married high.school no no no nonexistent -1.8 93.075 -47.1 1.445 5099.1 1
37550 40 blue-collar married basic.9y unknown no no nonexistent -1.8 93.075 -47.1 1.405 5099.1 1
32961 46 technician married professional.course no yes no nonexistent -1.8 92.893 -46.2 1.291 5099.1 1
 

Statystyka

 

Threshold ROC curve

 

Klasyfikacja dla progów od 0.2 do 0.9 Threshold ROC curve

In [30]:
gnb_ytest = gnb.predict_proba(X)[:, 1]
In [32]:
X.shape
Out[32]:
(41188, 13)
 

Klasyfikacja dla progów threshold = od 0.2 do 0.9

In [38]:
y_predT09 = (gnb_ytest >= 0.9).astype(int)
df['ResultTH09'] = y_predT09

y_predT08 = (gnb_ytest >= 0.8).astype(int)
df['ResultTH08'] = y_predT08

y_predT07 = (gnb_ytest >= 0.7).astype(int)
df['ResultTH07'] = y_predT07

y_predT06 = (gnb_ytest >= 0.6).astype(int)
df['ResultTH06'] = y_predT06

y_predT05 = (gnb_ytest >= 0.5).astype(int)
df['ResultTH05'] = y_predT05

y_predT04 = (gnb_ytest >= 0.4).astype(int)
df['ResultTH04'] = y_predT04

y_predT03 = (gnb_ytest >= 0.3).astype(int)
df['ResultTH03'] = y_predT03

y_predT02 = (gnb_ytest >= 0.2).astype(int)
df['ResultTH02'] = y_predT02
In [39]:
df.head(3)
Out[39]:
  age job marital education default housing loan poutcome emp_var_rate cons_price_idx nr_employed y ResultTH09 ResultTH08 ResultTH07 ResultTH06 ResultTH05 ResultTH04 ResultTH03 ResultTH02
0 44 1 1 0 1 2 0 1 1.4 93.444 5228.1 0 0 0 0 0 0 0 0 0
1 53 9 1 7 0 0 0 1 -0.1 93.200 5195.8 0 0 0 0 0 0 0 0 0
2 28 4 2 6 0 2 0 2 -1.7 94.055 4991.6 1 1 1 1 1 1 1 1 1

3 rows × 22 columns

 

Punktacja dla każdego klienta

In [57]:
df['Customer_classification']= df['ResultTH02']+df['ResultTH03']+df['ResultTH04']+df['ResultTH05']+df['ResultTH06']+df['ResultTH07']+df['ResultTH08']+df['ResultTH09']
In [60]:
df.sort_values('Customer_classification').head(4)
Out[60]:
  age job marital education default housing loan poutcome emp_var_rate cons_price_idx y ResultTH09 ResultTH08 ResultTH07 ResultTH06 ResultTH05 ResultTH04 ResultTH03 ResultTH02 Customer_classification
0 44 1 1 0 1 2 0 1 1.4 93.444 0 0 0 0 0 0 0 0 0 0
25048 30 9 1 6 0 2 0 1 -0.1 93.200 0 0 0 0 0 0 0 0 0 0
25047 35 1 0 2 1 2 0 1 1.4 93.918 0 0 0 0 0 0 0 0 0 0
25046 42 7 1 3 1 2 2 1 1.1 93.994 0 0 0 0 0 0 0 0 0 0

4 rows × 23 columns

In [43]:
df.pivot_table(index =['Customer_classification'],values='marital', aggfunc='count').plot(kind='bar', legend=False)
Out[43]:
<matplotlib.axes._subplots.AxesSubplot at 0x164606ab780>
In [61]:
df['ResultTH05'].value_counts(normalize=True)
Out[61]:
0    0.737059
1    0.262941
Name: ResultTH05, dtype: float64
 

Podsumowanie

Porównaine Logistic Regression Classifier z Naive Bayes Classifier.

Logistic Regression na zbiorze df

                   precision    recall  f1-score   support

               0       0.95      0.76      0.85     12062
               1       0.27      0.68      0.38      1531

        accuracy                           0.75     13593
       macro avg       0.61      0.72      0.61     13593
    weighted avg       0.87      0.75      0.79     13593


Naive Bayes Classifier na zbiorze df

                    precision    recall  f1-score   support

               0       0.95      0.78      0.86     12062
               1       0.28      0.66      0.39      1531

        accuracy                           0.77     13593
       macro avg       0.61      0.72      0.62     13593
    weighted avg       0.87      0.77      0.80     13593

 

Artykuł Artificial Intelligence w Marketingu Bankowym – Naive Bayes Classifier part 4 pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Regresja Logistyczna Dummy_Variables https://sigmaquality.pl/uncategorized/25-regresja-logistyczna-dummy_variables/ Mon, 30 Sep 2019 19:28:00 +0000 http://sigmaquality.pl/25-regresja-logistyczna-dummy_variables/ Exercise 25 Istnieją dwa sposoby budowania modelu na zmiennych dyskretnych. PYTANIE: czy model zbudowany na różnych metodach kodowania może mieć różne właściwości predykcyjne? 15 kwietnia [...]

Artykuł Regresja Logistyczna Dummy_Variables pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

Exercise 25

Istnieją dwa sposoby budowania modelu na zmiennych dyskretnych.

PYTANIE: czy model zbudowany na różnych metodach kodowania może mieć różne właściwości predykcyjne?

15 kwietnia 1912 r. Podczas swojej dziewiczej podróży Titanic zatonął po zderzeniu z górą lodową, Zginęło 1502 spośród 2224 pasażerów i załogi. Ta sensacyjna tragedia zszokowała społeczność międzynarodową i doprowadziła do lepszych przepisów bezpieczeństwa dla statków.
Jednym z powodów tragedii, był brak wystarczającej liczby łodzi ratunkowych. Zaobserwowano, że niektóre grupy ludzi miały większe szanse na przetrwanie niż inne. Były to kobiety, dzieci oraz klasa pierwsza.
Celem ćwiczenia jest określenia, jakie grupy pasażerów miały większe szanse przeżycia katastrofy.

Zmienne:

Survived: (0 = No, 1 = Yes) Zmienna wynikowa określająca przeżycie
pclass: (1 = 1st, 2 = 2nd, 3 = 3rd) Klasa biletu

Sex: (male, female) płeć pasażera

Age: wiek pasażera w latach
Wiek jest ułamkowy, jeśli jest mniejszy niż 1. Jeżeli wiek jest szacowany, to czy ma on postać xx.5

SibSp: Liczba rodzeństwa / małżonków na pokładzie Titanica
Zestaw danych definiuje relacje rodzinne w ten sposób… Rodzeństwo = brat, siostra, przyrodni brat, przyrodnia siostra Współmałżonek = mąż, żona (kochanki i narzeczone zostały zignorowane)</b>.

Parch: liczba rodziców / dzieci na pokładzie Titanica
Zestaw danych definiuje relacje rodzinne w ten sposób… Rodzic = matka, ojciec Dziecko = córka, syn, pasierbica, pasierb Niektóre dzieci podróżowały tylko z nianią, dlatego dla nich parch = 0.

Ticket: numer biletu

Fare: opłata za bilet

Cabin: Numer kabiny

Embarked: (C = Cherbourg, Q = Queenstown, S = Southampton) Kod portu zaokrętowania.

Zobacz też:

http://sigmaquality.pl/pandas/who-had-a-chance-to-survive-on-the-titanic/

In [1]:
import pandas as pd
import seaborn as sns
import matplotlib as plt
import numpy as np

df = pd.read_csv('c:/1/kaggletrain.csv')
df.head(5)
Out[1]:
Unnamed: 0 PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
1 1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th… female 38.0 1 0 PC 17599 71.2833 C85 C
2 2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
3 3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
4 4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S

Sprawdzamy kompletnoiść danych

In [2]:
df.isnull().sum()
Out[2]:
Unnamed: 0       0
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64
In [3]:
df.shape
Out[3]:
(891, 13)

Naszym zadaniem jest porównanie dwóch metod tworzenia modeli regresji logistycznej. Kasujemy kolumnę 'Cabin’ ponieważ w około 3/4 rekordów jest niekompletna poza tym ma zbyt wiele wartości unikalnych.

In [4]:
df.Cabin.unique()
Out[4]:
array([nan, 'C85', 'C123', 'E46', 'G6', 'C103', 'D56', 'A6',
       'C23 C25 C27', 'B78', 'D33', 'B30', 'C52', 'B28', 'C83', 'F33',
       'F G73', 'E31', 'A5', 'D10 D12', 'D26', 'C110', 'B58 B60', 'E101',
       'F E69', 'D47', 'B86', 'F2', 'C2', 'E33', 'B19', 'A7', 'C49', 'F4',
       'A32', 'B4', 'B80', 'A31', 'D36', 'D15', 'C93', 'C78', 'D35',
       'C87', 'B77', 'E67', 'B94', 'C125', 'C99', 'C118', 'D7', 'A19',
       'B49', 'D', 'C22 C26', 'C106', 'C65', 'E36', 'C54',
       'B57 B59 B63 B66', 'C7', 'E34', 'C32', 'B18', 'C124', 'C91', 'E40',
       'T', 'C128', 'D37', 'B35', 'E50', 'C82', 'B96 B98', 'E10', 'E44',
       'A34', 'C104', 'C111', 'C92', 'E38', 'D21', 'E12', 'E63', 'A14',
       'B37', 'C30', 'D20', 'B79', 'E25', 'D46', 'B73', 'C95', 'B38',
       'B39', 'B22', 'C86', 'C70', 'A16', 'C101', 'C68', 'A10', 'E68',
       'B41', 'A20', 'D19', 'D50', 'D9', 'A23', 'B50', 'A26', 'D48',
       'E58', 'C126', 'B71', 'B51 B53 B55', 'D49', 'B5', 'B20', 'F G63',
       'C62 C64', 'E24', 'C90', 'C45', 'E8', 'B101', 'D45', 'C46', 'D30',
       'E121', 'D11', 'E77', 'F38', 'B3', 'D6', 'B82 B84', 'D17', 'A36',
       'B102', 'B69', 'E49', 'C47', 'D28', 'E17', 'A24', 'C50', 'B42',
       'C148'], dtype=object)
In [5]:
del df['Cabin']
del df['Ticket']
del df['Unnamed: 0']
del df['PassengerId']

Usunąłem kolumnę Cabin, również usunąłem kolumnę 'Ticket’, uważam że numer biletu nie miał wpływu na przeżywalnośc rejsu.
Teraz usunę rekordy gdzie brakuje wartości w kolumnach 'Embarked’ oraz 'Age’.

In [6]:
df = df.dropna(how='any')

Sprawdzenie wyniku

In [7]:
df.shape
Out[7]:
(712, 9)
In [8]:
df.isnull().sum()
Out[8]:
Survived    0
Pclass      0
Name        0
Sex         0
Age         0
SibSp       0
Parch       0
Fare        0
Embarked    0
dtype: int64

Niekompletne rekordy zostały usunięte. Badam pozostałe zmienne niezależne.

In [9]:
df.Embarked.value_counts(normalize=True)
Out[9]:
S    0.778090
C    0.182584
Q    0.039326
Name: Embarked, dtype: float64
In [10]:
df.Sex.value_counts(normalize=True)
Out[10]:
male      0.636236
female    0.363764
Name: Sex, dtype: float64

Dość enigmatyczna jest zmienna SibSp, sprawdźmy ją!

In [11]:
df.SibSp.value_counts()
Out[11]:
0    469
1    183
2     25
4     18
3     12
5      5
Name: SibSp, dtype: int64
In [12]:
df.SibSp.value_counts(normalize=True).plot(kind='bar')
Out[12]:
<matplotlib.axes._subplots.AxesSubplot at 0x233b5eb27b8>

Tak ja widzimy, 469 pasażerów podróżowała samodzielnie a 183 z jednym członkiem rodziny. Przetwarzam liczby na oznaczenia tekstowe, które łatwiej zrozumieć.

In [13]:
fam = {0:'lone', 1:'couple', 2:'three members', 3:'four members', 4:'five members', 5:'six members'}  
df['SibSp'] = df['SibSp'].map(fam)

Parch – Zestaw danych definiuje relacje rodzinne

In [14]:
df.Parch.value_counts()
Out[14]:
0    519
1    110
2     68
5      5
3      5
4      4
6      1
Name: Parch, dtype: int64

Ta zmienna jest zbyt trudna do interpretacji, dlatego przetworzę ją na wartość dyskretną.

In [15]:
df.Parch.dtype
Out[15]:
dtype('int64')
In [16]:
family = {0:'lone', 1:'mother', 2:'father', 3:'daughter', 4:'son', 5:'stepdaughter', 6:'stepson'}  
df['Parch'] = df['Parch'].map(family)

Podmieniliśmy trudne oznaczenie rozin na oznaczenia bardziej przyjazne, sprawdźmy czy dobrze.

In [17]:
df['Parch'].head(5)
Out[17]:
0    lone
1    lone
2    lone
3    lone
4    lone
Name: Parch, dtype: object
In [18]:
df[df.Parch=='daughter'][['Parch','Age', 'Sex', 'Name', 'SibSp']]
Out[18]:
Parch Age Sex Name SibSp
86 daughter 16.0 male Ford, Mr. William Neal couple
437 daughter 24.0 female Richards, Mrs. Sidney (Emily Hocking) three members
736 daughter 48.0 female Ford, Mrs. Edward (Margaret Ann Watson) couple
774 daughter 54.0 female Hocking, Mrs. Elizabeth (Eliza Needs) couple
858 daughter 24.0 female Baclini, Mrs. Solomon (Latifa Qurban) lone

Jedena cureczka jest chłopcem. Pani Baclini jest córką innego pasażera, mimo to jest oznaczona jako podrużyująca samotnie. W bazie widać błędy.

In [19]:
df[df.Parch=='son'][['Parch','Age', 'Sex', 'Name', 'SibSp']]
Out[19]:
Parch Age Sex Name SibSp
167 son 45.0 female Skoog, Mrs. William (Anna Bernhardina Karlsson) couple
360 son 40.0 male Skoog, Mr. Wilhelm couple
438 son 64.0 male Fortune, Mr. Mark couple
567 son 29.0 female Palsson, Mrs. Nils (Alma Cornelia Berglund) lone

Dwa błędy.

In [20]:
df[df.Parch=='mother'][['Parch','Age', 'Sex', 'Name', 'SibSp']].sample(5)
Out[20]:
Parch Age Sex Name SibSp
698 mother 49.0 male Thayer, Mr. John Borland couple
268 mother 58.0 female Graham, Mrs. William Thompson (Edith Junkins) lone
312 mother 26.0 female Lahtinen, Mrs. William (Anna Sylfven) couple
424 mother 18.0 male Rosblom, Mr. Viktor Richard couple
197 mother 42.0 male Olsen, Mr. Karl Siegwart Andreas lone
In [21]:
df[df.Parch=='father'][['Parch','Age', 'Sex', 'Name', 'SibSp']].sample(5)
Out[21]:
Parch Age Sex Name SibSp
165 father 9.0 male Goldsmith, Master. Frank John William „Frankie” lone
88 father 23.0 female Fortune, Miss. Mabel Helen four members
788 father 1.0 male Dean, Master. Bertram Vere couple
419 father 10.0 female Van Impe, Miss. Catharina lone
147 father 9.0 female Ford, Miss. Robina Maggie „Ruby” three members

Niektóre matki mają 9 lat inne 2. Część matek jest mężczyznami. Parametr 'SibSp’ podający ilość towarzyszących członków rodziny dla niektórych rodziców wynosi zero . Wydaje mi się, że to oznaczenie jest błędne. Teraz nie będę tego poprawiał ponieważ nie mieści się to w ramach tego ćwiczenia. Zostawimy dane takimi jakie są.

Analiza zbilansowania zbioru zmiennych wynikowych

Nie będę już badał zmiennych, sprawdzę tylko czy zmienna zależna jest zbilansowana. Czy liczba uratowanych i ofiar jest podobna.

In [22]:
df.Survived.value_counts(normalize=True).plot(kind='bar')
Out[22]:
<matplotlib.axes._subplots.AxesSubplot at 0x233b61e2198>

Między zbiorami uratowanych i ofiar nie ma drastycznych różnic. Nie trzeba stosować oversampling.

Regresja logistyczna bez tworzenia Dummy Variables

Przy budowaniu każdego modelu trzeba brać pod uwagę wszystkie możliwe dane, również (a czasem przede wszystkim) dane tekstowe (dyskretne). Aby możliwe było użycie danych tekstowych, należy je przekształcić na dane cyfrowe.
Dummy Variables to dane kategoryczne 0, 1, Z jednej kolumny tekstowej np. zwierzęta domowe, zawierającej np. trzy stany: pies, kot, mysz tworzy się trzy kolumny: zwierzęta domowe_kot, zwierzęta domowe_pies, zwierzęta domowe_mysz. Jeżeli w danym rekordzie jest kot, wartość w kolumnie zwierzęta domowe_kot = 1, w pozostałych kolumnach wartość ta wynosi 0.

Zamiast tworzyć Dummy Variables można styworzyć cyfrowy kod zmiennych tekstowych. Poniżej zrobimy takie przekształcenie dla zmiennych tekstowych.

Dzielimy zmienne niezależne na tekstowe i numeryczne

In [23]:
df.dtypes
Out[23]:
Survived      int64
Pclass        int64
Name         object
Sex          object
Age         float64
SibSp        object
Parch        object
Fare        float64
Embarked     object
dtype: object
In [24]:
del df['Name']
In [25]:
categorical = df.describe(include=["object"]).columns
continuous = df.describe().columns
In [26]:
categorical
Out[26]:
Index(['Sex', 'SibSp', 'Parch', 'Embarked'], dtype='object')
In [27]:
continuous
Out[27]:
Index(['Survived', 'Pclass', 'Age', 'Fare'], dtype='object')

Przekształacamy zmienne dyskretne na zmienne kodowane cyfrowo

Ktoś może zapytać, po co najpierw tworzymy z danych cyfrowych 'SibSp’ i 'Parch’, zmienne tekstowe aby znowu przekształcić je w dane cyfrowe.
Robimy tak aby poprawić czytelność kolumn w metodzie Dummy Variables.

In [28]:
from sklearn.preprocessing import LabelEncoder

df[categorical] = df[categorical].apply(LabelEncoder().fit_transform)
In [29]:
df[categorical].sample(6)
Out[29]:
Sex SibSp Parch Embarked
819 1 2 1 2
661 1 3 2 0
255 0 3 1 0
513 0 0 2 0
722 1 3 2 2
462 1 3 2 2

Dzielimy zbiory na testowe i treningowe

In [30]:
y = df['Survived']
X = df.drop('Survived' , axis=1)

from sklearn.model_selection import train_test_split 
Xtrain, Xtest, ytrain, ytest = train_test_split(X,y, test_size=0.33, stratify = y, random_state = 148)

Wielkości zbiorów.

In [31]:
print ('Training X set: ',Xtrain.shape)
print ('Test X set      ', Xtest.shape)
print ('Training y set: ', ytrain.shape)
print ('Test y set      ', ytest.shape)
Training X set:  (477, 7)
Test X set       (235, 7)
Training y set:  (477,)
Test y set       (235,)
In [32]:
Xtrain.head(4)
Out[32]:
Pclass Sex Age SibSp Parch Fare Embarked
715 3 1 19.0 3 2 7.65 2
543 2 1 32.0 0 2 26.00 2
748 1 1 19.0 0 2 53.10 2
357 2 0 38.0 3 2 13.00 2

Logistic Regression bez Dummy Variables

In [33]:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV


Parameteres = {'C': np.power(10.0, np.arange(-3, 3))}
LR = LogisticRegression(warm_start = True)
LR_Grid = GridSearchCV(LR, param_grid = Parameteres, scoring = 'roc_auc', n_jobs = 5, cv=2)

LR_Grid.fit(Xtrain, ytrain) 
C:ProgramDataAnaconda3libsite-packagessklearnlinear_modellogistic.py:432: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning.
  FutureWarning)
Out[33]:
GridSearchCV(cv=2, error_score='raise-deprecating',
             estimator=LogisticRegression(C=1.0, class_weight=None, dual=False,
                                          fit_intercept=True,
                                          intercept_scaling=1, l1_ratio=None,
                                          max_iter=100, multi_class='warn',
                                          n_jobs=None, penalty='l2',
                                          random_state=None, solver='warn',
                                          tol=0.0001, verbose=0,
                                          warm_start=True),
             iid='warn', n_jobs=5,
             param_grid={'C': array([1.e-03, 1.e-02, 1.e-01, 1.e+00, 1.e+01, 1.e+02])},
             pre_dispatch='2*n_jobs', refit=True, return_train_score=False,
             scoring='roc_auc', verbose=0)

Ocena modelu regresji logistycznej bez Dummy Variables

In [34]:
ypred = LR_Grid.predict(Xtest)

from sklearn.metrics import classification_report, confusion_matrix
from sklearn import metrics

co_matrix = metrics.confusion_matrix(ytest, ypred)
co_matrix
Out[34]:
array([[117,  23],
       [ 23,  72]], dtype=int64)
In [35]:
print(classification_report(ytest, ypred)) 
              precision    recall  f1-score   support

           0       0.84      0.84      0.84       140
           1       0.76      0.76      0.76        95

    accuracy                           0.80       235
   macro avg       0.80      0.80      0.80       235
weighted avg       0.80      0.80      0.80       235

In [36]:
print("Accuracy:    ",np.round(metrics.accuracy_score(ytest, ypred), decimals=2))
print("Precision:   ",np.round(metrics.precision_score(ytest, ypred), decimals=2))
print("Recall:      ",np.round(metrics.recall_score(ytest, ypred), decimals=2))
print("F1 score:    ",np.round(metrics.f1_score(ytest, ypred), decimals=2))
Accuracy:     0.8
Precision:    0.76
Recall:       0.76
F1 score:     0.76

Usunięcie danych 'Name’, 'Cabin’,’Ticket’,’Unnamed: 0′, 'PassengerId’ poprawiło nieznacznie dobroć modelu.

Logistic Regression z Dummy Variables

Przywracamy kolumny zawierające zmienne tekstowe. Robimy tak żeby mieć identyczne dane jak przy Logistic Regression bez dummy variables.

In [37]:
df.columns
Out[37]:
Index(['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare',
       'Embarked'],
      dtype='object')
In [38]:
df2 = pd.read_csv('c:/1/kaggletrain.csv', usecols=['Survived','Pclass','Sex','Age','SibSp','Parch','Fare','Embarked'])
In [39]:
df2 = df2.dropna(how='any')
df2.isnull().sum()
Out[39]:
Survived    0
Pclass      0
Sex         0
Age         0
SibSp       0
Parch       0
Fare        0
Embarked    0
dtype: int64
In [40]:
fam = {0:'lone', 1:'couple', 2:'three members', 3:'four members', 4:'five members', 5:'six members'}  
df2['SibSp'] = df2['SibSp'].map(fam)
In [41]:
family = {0:'lone', 1:'mother', 2:'father', 3:'daughter', 4:'son', 5:'stepdaughter', 6:'stepson'}  
df2['Parch'] = df2['Parch'].map(family)

Teraz tworzymy zmienne typu Dummy Variables

In [42]:
Dummy_Variables = pd.get_dummies(df2, columns=categorical, drop_first=True)
Dummy_Variables.sample(5)
Out[42]:
Survived Pclass Age Fare Sex_male SibSp_five members SibSp_four members SibSp_lone SibSp_six members SibSp_three members Parch_father Parch_lone Parch_mother Parch_son Parch_stepdaughter Parch_stepson Embarked_Q Embarked_S
810 0 3 26.0 7.8875 1 0 0 1 0 0 0 1 0 0 0 0 0 1
762 1 3 20.0 7.2292 1 0 0 1 0 0 0 1 0 0 0 0 0 0
791 0 2 16.0 26.0000 1 0 0 1 0 0 0 1 0 0 0 0 0 1
38 0 3 18.0 18.0000 0 0 0 0 0 1 0 1 0 0 0 0 0 1
349 0 3 42.0 8.6625 1 0 0 1 0 0 0 1 0 0 0 0 0 1
In [43]:
Dummy_Variables.columns
Out[43]:
Index(['Survived', 'Pclass', 'Age', 'Fare', 'Sex_male', 'SibSp_five members',
       'SibSp_four members', 'SibSp_lone', 'SibSp_six members',
       'SibSp_three members', 'Parch_father', 'Parch_lone', 'Parch_mother',
       'Parch_son', 'Parch_stepdaughter', 'Parch_stepson', 'Embarked_Q',
       'Embarked_S'],
      dtype='object')
In [44]:
y = Dummy_Variables['Survived']
X = Dummy_Variables.drop('Survived', axis=1) 

from sklearn.model_selection import train_test_split 
Xtrain, Xtest, ytrain, ytest = train_test_split(X,y, test_size=0.33, stratify = y, random_state = 148)
In [45]:
Parameteres = {'C': np.power(10.0, np.arange(-3, 3))}
LR = LogisticRegression(warm_start = True)
LR_Dummy_Variables = GridSearchCV(LR, param_grid = Parameteres, scoring = 'roc_auc', n_jobs = 5, cv=2)

LR_Dummy_Variables.fit(Xtrain, ytrain) 
C:ProgramDataAnaconda3libsite-packagessklearnlinear_modellogistic.py:432: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning.
  FutureWarning)
Out[45]:
GridSearchCV(cv=2, error_score='raise-deprecating',
             estimator=LogisticRegression(C=1.0, class_weight=None, dual=False,
                                          fit_intercept=True,
                                          intercept_scaling=1, l1_ratio=None,
                                          max_iter=100, multi_class='warn',
                                          n_jobs=None, penalty='l2',
                                          random_state=None, solver='warn',
                                          tol=0.0001, verbose=0,
                                          warm_start=True),
             iid='warn', n_jobs=5,
             param_grid={'C': array([1.e-03, 1.e-02, 1.e-01, 1.e+00, 1.e+01, 1.e+02])},
             pre_dispatch='2*n_jobs', refit=True, return_train_score=False,
             scoring='roc_auc', verbose=0)

Ocena modelu regresji logistycznej

In [46]:
ypred_DV = LR_Dummy_Variables.predict(Xtest)

co_matrix = metrics.confusion_matrix(ytest, ypred_DV)
co_matrix
Out[46]:
array([[115,  25],
       [ 25,  70]], dtype=int64)
In [47]:
print(classification_report(ytest, ypred_DV)) 
              precision    recall  f1-score   support

           0       0.82      0.82      0.82       140
           1       0.74      0.74      0.74        95

    accuracy                           0.79       235
   macro avg       0.78      0.78      0.78       235
weighted avg       0.79      0.79      0.79       235

In [48]:
print("Accuracy:    ",np.round(metrics.accuracy_score(ytest, ypred_DV), decimals=2))
print("Precision:   ",np.round(metrics.precision_score(ytest, ypred_DV), decimals=2))
print("Recall:      ",np.round(metrics.recall_score(ytest, ypred_DV), decimals=2))
print("F1 score:    ",np.round(metrics.f1_score(ytest, ypred_DV), decimals=2))
Accuracy:     0.79
Precision:    0.74
Recall:       0.74
F1 score:     0.74

Odpowiedź: Model regresji logistycznej wykorzystujący zmienne takstowe zakodowane oraz Model regresji logistycznej wykorzystujący zmienne typu Dummy_Variables mają te same zdolności predykcyjne.

Artykuł Regresja Logistyczna Dummy_Variables pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Model Regresji Logistycznej. Część 3: zmiana progu w modelu regresji logistycznej https://sigmaquality.pl/uncategorized/model-regresji-logistycznej-czesc-3-zmiana-progu-w-modelu-regresji-logistycznej/ Mon, 06 May 2019 19:40:00 +0000 http://sigmaquality.pl/?p=5845 Zmiany progu w modelu regresji logistycznej i poprawa wskaźnika 'recall' kosztem zaśmiecenia wyników, czyli spadkiem poziomu dokładności 'precision'. W poprzednich częściach omówiliśmy jak przygotowywać dane [...]

Artykuł Model Regresji Logistycznej. Część 3: zmiana progu w modelu regresji logistycznej pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

Zmiany progu w modelu regresji logistycznej i poprawa wskaźnika 'recall' kosztem zaśmiecenia wyników, czyli spadkiem poziomu dokładności 'precision'.

W poprzednich częściach omówiliśmy jak przygotowywać dane i jak tworzyć model regresji logistycznej. Mówiliśmy również o tym, w jaki sposób zmienić ilość danych, tak aby przy zmniejszyć wpływ niezbilansowania w zbiorze treningowym podczas tworzenia modelu regresji logistycznej.

Mówiliśmy również o tym, w jaki sposób należy zmienić ilość niezbilansowania w zbiorze treningowym podczas tworzenia modelu regresji logistycznej.

Wcześniejsze części można znaleźć pod tymi adresami:

Przykład Modelu Regresji Logistycznej. Część 1: model podstawowy

Model Regresji Logistycznej. Część 2: Oversampling

Tym razem zajmiemy się kolejną techniką pozwalającą na uzyskanie czułości modelu na zjawiska rzadkie. Przypomnijmy kontekst naszych ćwiczeń. Dla banku bardzo ważne jest to, jaki model zostanie szybko wykryty. Klient zgadza się na ponowny zakup dodatkowych danych ze wskazaniem przez model na anomalię, mimo, że zdarzenia te nie są malwersacjami.

 

Zmiana progu w modelu regresji logistycznej

W modelu regresji logistycznej domyślny próg prawdopodobieństwa wynosi 0,5

LR_Grid_ytest = LR_Grid.predict_proba(Xtest)[:, 1]
new_threshold = 0.1  
ytest_pred = (LR_Grid_ytest >= new_threshold).astype(int)

 

Uruchamiamy moduł diagnostyczny.

print("\n------Training data---------------------------------------------------")
print("RECALL Training data (new_threshold = 0.1):      ", np.round(recall_score(ytrain, LR_Grid.predict(Xtrain)), decimals=3))
print("PRECISION Training data (new_threshold = 0.1):   ", np.round(precision_score(ytrain, LR_Grid.predict(Xtrain)), decimals=3))

print("------Test data-------------------------------------------------------")

print("RECALL Test data (new_threshold = 0.1):        ", np.round(recall_score(ytest, ytest_pred), decimals=3))
print("PRECISION Test data (new_threshold = 0.1):     ", np.round(precision_score(ytest, ytest_pred), decimals=3))
print()
print("The Confusion Matrix Test data (new_threshold = 0.1):-----------------")
print(confusion_matrix(ytest, ytest_pred))
print("----------------------------------------------------------------------")
print(classification_report(ytest, ytest_pred))

# WYKRES-------------------------------------------
y_pred_proba = LR_Grid.predict_proba(Xtest)[::,1]
fpr, tpr, _ = metrics.roc_curve(ytest,  y_pred_proba)
auc = metrics.roc_auc_score(ytest, y_pred_proba)
plt.plot(fpr, tpr, label='Logistic Regression (auc = 
plt.axvline(0.1, color = '#00C251', linestyle = '--', label = 'threshold = 0.1')
plt.axvline(0.5, color = 'grey', linestyle = '--', label = 'threshold = 0.5')
plt.xlabel('False Positive Rate',color='grey', fontsize = 13)
plt.ylabel('True Positive Rate',color='grey', fontsize = 13)
plt.title('Receiver operating characteristic')
plt.legend(loc="lower right")
plt.legend(loc=4)
plt.plot([0, 1], [0, 1],'r--')

plt.show()

 

Zmiana progu w modelu spowodowanej wzrost 'recall' na zbiorze treningowym z poziomu 0.65 do poziomu 0.81. Stało się na kosztem precyzji, który uległby pogorszeniu z pierwotnego poziomu 0.77 do poziomu 0.54.

Next part:

Model regresji Logistyczne. Część 4: Zastosowanie class_weight

Artykuł Model Regresji Logistycznej. Część 3: zmiana progu w modelu regresji logistycznej pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Model regresji Logistyczne. Część 4: Zastosowanie class_weight https://sigmaquality.pl/uncategorized/model-regresji-logistyczne-czesc-4-zastosowanie-class_weight/ Mon, 06 May 2019 19:40:00 +0000 http://sigmaquality.pl/?p=5861 Zastosowanie class_weight jest sposobem na poprawę wskaźnika recall w zbiorach wyników wysoko niezbilansowanych. Podniesienie wskaźnika recall wiąże się ze spadkiem poziomu wskaźnika precision. W poprzednich [...]

Artykuł Model regresji Logistyczne. Część 4: Zastosowanie class_weight pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

Zastosowanie class_weight jest sposobem na poprawę wskaźnika recall w zbiorach wyników wysoko niezbilansowanych. Podniesienie wskaźnika recall wiąże się ze spadkiem poziomu wskaźnika precision.

W poprzednich częściach omówiliśmy jak przygotowywać dane i jak tworzyć model regresji logistycznej. Mówiliśmy również o tym, w jaki sposób zmienić ilość danych, tak aby zmniejszyć wpływ niezbilansowania w zbiorze treningowym podczas tworzenia modelu regresji logistycznej.

Wcześniejsze części można znaleźć pod tymi adresami:

Przykład Modelu Regresji Logistycznej. Część 1: model podstawowy

Model Regresji Logistycznej. Część 2: Oversampling

Model Regresji Logistycznej. Część 3: zmiana progu w modelu regresji logistycznej

Zastosowanie class_weight

Wspominaliśmy we wcześniejszych wpisach, że zbiór zmiennych zależnych jest wysoko niezbilansowany. Na jedną zmienną wynikową klasy 1 w zbiorze treningowym przypada 216 zmiennych klasy 0. Aby ograniczyć znaczenie tej dysproporcji należy podnieść wagę zmiennej zależnej o około 216 razy.

Pw = sum(ytrain == 0) / sum(ytrain == 1)  # size to repeat y == 1
Pw = np.round(Pw, decimals=0)
Pw = Pw.astype(int)
Pw

Parametr eagowy positive weight, Pw = 216.

Występowanie klasy 0 w zbiorze testowym zmiennych zależnych jest 216 razy częstsza niż 1. Należy zatem zwiększyć parametr class_weight  dla klasy 1, w stosunku do klasy 0 według przedziału {0:1,1:216}.

Parameters = {'C': np.power(10.0, np.arange(-3, 3))}
LogReg = LogisticRegression(class_weight = {0 : 1, 1 : Pw}, warm_start = True, solver='lbfgs')

Dostrojenie modelu za pomocą siatki Grid.

LRV_Reg_grid = GridSearchCV(LogReg, param_grid = Parameters, scoring = 'roc_auc', n_jobs = 5, cv = 6)

LRV_Reg_grid.fit(Xtrain, ytrain)

Z ciekawości zbadamy jakie zostały wybrane hiperparametry.

print("The Best parameter:",LRV_Reg_grid.best_params_)
print("The Best estimator:",LRV_Reg_grid.best_estimator_)

Wstawiam moduł diagnostyczny.

print()
print("Recall Training data:     ", np.round(recall_score(ytrain, LRV_Reg_grid.predict(Xtrain)), decimals=4))
print("Precision Training data:  ", np.round(precision_score(ytrain, LRV_Reg_grid.predict(Xtrain)), decimals=4))

print("----------------------------------------------------------------------")
print("Recall Test data:         ", np.round(recall_score(ytest, LRV_Reg_grid.predict(Xtest)), decimals=4)) 
print("Precision Test data:      ", np.round(precision_score(ytest, LRV_Reg_grid.predict(Xtest)), decimals=4))
print("----------------------------------------------------------------------")
print("Confusion Matrix Test data")
print(confusion_matrix(ytest, OVS_grid.predict(Xtest)))


print("----------------------------------------------------------------------")
print(classification_report(ytest, LRV_Reg_grid.predict(Xtest)))

y_pred_proba = LRV_Reg_grid.predict_proba(Xtest)[::,1]
fpr, tpr, _ = metrics.roc_curve(ytest,  y_pred_proba)
auc = metrics.roc_auc_score(ytest, y_pred_proba)
plt.plot(fpr, tpr, label='Logistic Regression (auc = 
plt.xlabel('False Positive Rate',color='grey', fontsize = 13)
plt.ylabel('True Positive Rate',color='grey', fontsize = 13)
plt.title('Receiver operating characteristic')
plt.legend(loc="lower right")
plt.legend(loc=4)
plt.plot([0, 1], [0, 1],'r--')
plt.show()

Zastosowanie class_weight spowodowało podniesienie wagi zmiennych zależnych w klasie 1. Dzięki temu model regresji logistycznej zwiększył wskaźnik recall dla klasy 1 do poziomu 0.92 kosztem precision wynoszącym 0.35 dla tej klasy.

Parameters = {'C': np.power(10.0, np.arange(-3, 3))}
LogReg = LogisticRegression(class_weight = {0 : 1, 1 : Pw}, warm_start = True, solver='lbfgs')
LRV_Reg_grid = GridSearchCV(LogReg, param_grid = Parameters, scoring = 'roc_auc', n_jobs = 5, cv = 6)
LRV_Reg_grid.fit(Xtrain, ytrain)

print()
print("Recall Training data:     ", np.round(recall_score(ytrain, LRV_Reg_grid.predict(Xtrain)), decimals=4))
print("Precision Training data:  ", np.round(precision_score(ytrain, LRV_Reg_grid.predict(Xtrain)), decimals=4))

print("----------------------------------------------------------------------")
print("Recall Test data:         ", np.round(recall_score(ytest, LRV_Reg_grid.predict(Xtest)), decimals=4)) 
print("Precision Test data:      ", np.round(precision_score(ytest, LRV_Reg_grid.predict(Xtest)), decimals=4))
print("----------------------------------------------------------------------")
print("Confusion Matrix Test data")
print(confusion_matrix(ytest, OVS_grid.predict(Xtest)))


print("----------------------------------------------------------------------")
print(classification_report(ytest, LRV_Reg_grid.predict(Xtest)))

y_pred_proba = LRV_Reg_grid.predict_proba(Xtest)[::,1]
fpr, tpr, _ = metrics.roc_curve(ytest,  y_pred_proba)
auc = metrics.roc_auc_score(ytest, y_pred_proba)
plt.plot(fpr, tpr, label='Logistic Regression (auc = 
plt.xlabel('False Positive Rate',color='grey', fontsize = 13)
plt.ylabel('True Positive Rate',color='grey', fontsize = 13)
plt.title('Receiver operating characteristic')
plt.legend(loc="lower right")
plt.legend(loc=4)
plt.plot([0, 1], [0, 1],'r--')
plt.show()

 

Artykuł Model regresji Logistyczne. Część 4: Zastosowanie class_weight pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Przykład tworzenia zamówień automatycznych w piekarni. Model regresji liniowej https://sigmaquality.pl/uncategorized/przyklad-tworzenia-zamowien-automatycznych-w-piekarni-model-regresji-liniowej/ Mon, 06 May 2019 19:40:00 +0000 http://sigmaquality.pl/?p=5876 Dzisiaj omówimy przykład tworzenia zamówień automatycznych w piekarni. Użyjemy do tego modelu regresji logistycznej. Nie da się ukryć, że jest to nieco przestarzały sposób w [...]

Artykuł Przykład tworzenia zamówień automatycznych w piekarni. Model regresji liniowej pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

Dzisiaj omówimy przykład tworzenia zamówień automatycznych w piekarni. Użyjemy do tego modelu regresji logistycznej. Nie da się ukryć, że jest to nieco przestarzały sposób w środowisku machine learning. Niemniej model regresji liniowej jest klasyczną i często skuteczną metodą prognozowania wyników.  

Problem zamówień w sklepach piekarni, przykład tworzenia zamówień automatycznych

Nasza przykładowa sieć sprzedaży składa się z 16 sklepów sprzedających pieczywo. Sieć sklepów działa w małych miastach pod Warszawą.  Sklepy zlokalizowane są blisko dworców kolejowych skąd wielu mieszkańców dojeżdża do pracy do Warszawy. Idąc i wracając z pracy mieszkańcy kupują pieczywo. Na pierwszej zmianie piekarnia dostarcza do swoich sklepów maksymalną ilość produktów gdyż wie, że pieczywo zostanie w większości sprzedane w ciągu dnia . Problem pojawia się gdy trzeba uzupełnić zasoby sklepu przed szczytem powrotów z pracy, pomiędzy godzinami 14 a 18. Jeżeli piekarnia dostarczy zbyt wielu produktów na drugiej zmianie, nie zostaną one sprzedane i zostaną wyrzucone. Jeżeli na drugiej zmianie produktów będzie zbyt mało, z czasem mieszkańcy zniechęcą się do wstępowania do sklepu i spadnie sprzedaż. Teraz omówimy przykład tworzenia zamówień automatycznych opartych na starej, dobrej regresji liniowej.

Model regresji liniowej przewidującej dzienne zapotrzebowanie na produkty

Model zasilany będzie rocznym rejestrem sprzedaży. Zbiór danych zawiera datę sprzedaży, rodzaj asortymentu oraz informacje pogodowe w postaci: temperatury, ciśnienia atmosferycznego oraz opadów. W ocenie prowadzącego badania, dane meteorologiczne mogły mieć bezpośredni wpływ na wielkość zakupów w sklepach piekarni. Ściągamy bazę danych oraz potrzebne biblioteki. W tym ćwiczeniu wyjątkowo baza danych nie będzie dostępna.    

import pandas as pd
import numpy as np
import itertools
from itertools import chain, combinations
import statsmodels.formula.api as smf
import scipy.stats as scipystats
import statsmodels.api as sm
import statsmodels.stats.stattools as stools
import statsmodels.stats as stats 
from statsmodels.graphics.regressionplots import *
import matplotlib.pyplot as plt
import seaborn as sns
import copy
#from sklearn.cross_validation import train_test_split
import math


df = pd.read_csv('c:/1/BazaPiekarni.csv', parse_dates=['Time'])
df.sample(5)

Sprawdzamy jak wielki jest zbiór danych. Zbiór zawiera 1 mln 48 tysięcy transakcji. Duża liczba rekordów dobrze rokuje przy tworzeniu modelu.

df.shape

(1048575, 9)

Sprawdźmy jaki jest format naszych danych. Dzięki zastosowaniu parametru  parse_dates=[’Time’] ) udało się zachować format daty w kolumnie ‘Time’.

df.dtypes

Teraz zmienimy nazwy kolumn oraz kasujemy kolumny ‘index’ i ‘Number’

df.columns

 

df.columns = ['Number', 'index', 'Date', 'Assortment_code', 'Assortment_name', 'Location', 'Temperature', 'Weather', 'Pressure']

del df['index']
del df['Number']
df.sample(5)

Uporządkowane dane mają postać:

Dodajemy nowe kolumny porządkujące datę i czas.

df['Hour'] = df.Date.dt.hour
df['Weekday'] = df.Date.dt.weekday_name
df['Date2'] = df.Date.dt.date
df.sample(4)

Problematyczna druga połowa dnia, druga zmiana, zaczyna się o godzinie 14. Naszym celem jest określenie jaka powinna być wielkość dostaw poszczególnych asortymentów. Powinnyśmy wybrać jedną z lokalizacji następnie wybrać dane sprzedaży tylko po godzinie 14. Należy również wybrać jeden produkt.

Sprawdzamy do jakiej godziny sprzedawane jest pieczywo.

df.Hour.max()

18

Wydzielamy dane tylko dla sklepu nr 7 i tylko po godzinie 14.

df7 = df[(df['Hour']>=14)&(df['Hour']<=18)&(df['Location']==7)&(df['Assortment_code']==1)]
df7.sample(5)

Z danych została wyodrębniona tabela danych zawierająca sprzedaż chleba: ‘Primary Bread’ w sklepie numer 7 i tylko po godzinie 14.

Teraz zliczamy ile sztuk asortymentów pieczywa zostało sprzedanych w tej lokalizacji każdego dnia. Baza zbudowana jest tak, że jeden rekord reprezentuje jedną sprzedaż. Nasz model ma pokazywać ile sztuk pieczywa zostanie  sprzedanych w określonym czasie. Aby zsumować sprzedaż tworzymy tabelę przestawną.

kot = df7.pivot_table(index=['Assortment_name','Assortment_code','Date2','Weather','Temperature','Pressure','Location', 'Weekday'], values='Date',aggfunc='count')

kot.head()

Teraz przerabiam tabelę przestawną na zwykłą tabele danych dataframe, sortujemy dane.

df7 = kot.reset_index()
df7.sort_values('Date2', ascending = True, inplace=True )
df7.head(5)

Kolumnę ze zmiennymi wynikowymi nazwiemy 'Quantity sold’.

df7.rename(columns={'Date':'Quantity_sold'},inplace=True)
df7.sample(3)

Regresja jednoczynnikowa

Pierwszymi modelami stosowanymi do prognozowania były proste jednoczynnikowe modele regresji liniowej.

Aby zbudować przykładowy jednoczynnikowy model regresji liniowej trzeba znaleźć niezależną zmienną, która posiada największy związek liniowy ze zmienną zależną: 'Quantity sold’.

Żeby to jednak zrobić, trzeba najpierw zakodować wszystkie kolumny, które obecnie są wyrażone w formie tekstu. W naszym przykładzie takimi kolumnami są ‘Weather’ i ‘Weekday’

 

Przy okazji okazało się, że straciliśmy format daty w kolumnie ‘date2’. Naprawiamy to.

df7['Date2']= pd.to_datetime(df7.Date2)
df7.dtypes

Teraz możemy utworzyć nowe kolumny z cyfrową wersją tekstu.

df7['Weather_code'] = pd.Categorical(df7['Weather']).codes
df7['Weekday_code'] = df7['Date2'].dt.weekday

Teraz tworzę wektor korelacji między zmiennymi niezależnymi a zmienną zależną.

CORREL = df7.corr().sort_values('Quantity_sold')
CORREL['Quantity_sold']

Podstawowym argumentem do zastosowania modelu regresji liniowej jest istnienie wyraźnego związku liniowego pomiędzy zmienną zależną i zmienną niezależną.

W naszym przypadku zmienna niezależna ‘Weekday_code’ ma wyraźną korelację ze zmienną zależną.

Sprawdzimy teraz korelację wszystkich zmiennych.

sns.heatmap (df7.corr (), cmap="coolwarm")

Jednoczynnikowy model regresji liniowej

Wprowadzamy równanie regresji liniowej oraz kod umożliwiający wizualizację przebiegu linii regresji.

lm = smf.ols(formula = 'Quantity_sold ~ Weekday_code', data = df7).fit()
print(lm.summary())


### wykres--------------------------------------###
plt.figure()
plt.scatter(df7.Weekday_code, df7.Quantity_sold, c = 'grey')
plt.plot(df7.Weekday_code, lm.params[0] + lm.params[1] * df7.Weekday_code, c = 'r')
plt.xlabel('Weekday_code')
plt.ylabel('Quantity_sold')
plt.title("Linear Regression Plot")

Model regresji liniowej okazał się względnie słaby z uwagi na niski poziom wskaźnika R2 (R-squared) wynoszący 0.424

 

Wieloczynnikowy model regresji liniowej

lm = smf.ols(formula = 'Quantity_sold ~ Weekday_code + Pressure + Weather_code + Temperature', data = df7).fit()
print (lm.summary())

Model wielorakiej regresji liniowej nie okazał się lepszy. Jego wskaźnik wyniósł 0,432

Mimo istnienia związku liniowego między zmienną niezależną i zmienną zależną nie udało się stworzyć modelu pozwalającego wiarygodnie szacować przyszły poziom zakupów w sklepach piekarni. Aby model był efektywny należy rozejrzeć się za innym zestawem zmiennych niezależnych, które w leszy sposób opiszą rzeczywistość.

 

Praktyczne użycie modelu regresji wielorakiej jako przykład tworzenia zamówień automatycznych 

Praca nad modelem, którego nie można wykorzystać jest pracą jałową. Nie udało nam się stworzyć dobrego modelu ale nic nie stoi na przeszkodzie aby pokazać jak nasz model jest zły.

Na początek możemy ręcznie przekleić parametry modelu tworząc nową kolumnę.

df7['Model_Reg'] = (87.6337) +(-5.8891*df7['Weekday_code']) +(-0.0478*df7['Pressure']) +(0.6384*df7['Weather_code'])+(0.0236*df7['Temperature'])
df7['Model_Reg']= np.round(df7['Model_Reg'], decimals=0)
df7[['Quantity_sold','Model_Reg']].head()

 Widać, że model nie jest zbyt dokładny.

 

Uruchomienie modelu regresji liniowej

Na przyszłość trudno oczekiwać, że za każdym razem będziemy przeklejali parametry modelu. Dlatego warto tą czynność zautomatyzować.

Tworzymy słownik współczynników.

coef = lm.params.to_dict()

Teraz możemy sobie przywołać dowolny współczynnik przy x.

coef['Weekday_code']

-5.889075106403816

coef['Intercept']

87.6336717107532

Teraz w oddzielnej kolumnie utworzymy kod tworzący równanie regresji.

df7['Model_Reg2'] = (87.6337) +(coef['Weekday_code']*df7['Weekday_code']) +(coef['Pressure']*df7['Pressure']) +(coef['Weather_code']*df7['Weather_code'])+(coef['Temperature']*df7['Temperature'])
df7['Model_Reg2']= np.round(df7['Model_Reg2'], decimals=0)
df7[['Quantity_sold','Model_Reg','Model_Reg2']].head()

Jak widać model nie jest w stanie przewidzieć właściwej ilością produktów.

Teraz sprawdzamy o ile różnią się estymacje modelu.

 

Artykuł Przykład tworzenia zamówień automatycznych w piekarni. Model regresji liniowej pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Model Regresji Logistycznej. Część 2: Oversampling https://sigmaquality.pl/uncategorized/model-regresji-logistycznej-czesc-2-oversampling/ Mon, 06 May 2019 19:31:00 +0000 http://sigmaquality.pl/?p=5834 Oversampling Oversampling jest metodą częściowo usuwającą skutki niezbilansowania zbioru wynikowego. Metodę tą stosuje się dla zbioru treningowego aby tak wytrenował przy jego pomocy model, by [...]

Artykuł Model Regresji Logistycznej. Część 2: Oversampling pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

Oversampling

Oversampling jest metodą częściowo usuwającą skutki niezbilansowania zbioru wynikowego.

Metodę tą stosuje się dla zbioru treningowego aby tak wytrenował przy jego pomocy model, by zniwelować skutki niezbilansowania.

Przy niezbilansowanym zbiorze zmiennych zależnych model regresji logistycznej może nie wskazać wszystkich zdarzeń o charakterze rzadkim. Każdy model ma tendencje do uogólniania rzeczywistości. Dla banku bardzo ważne jest wskazanie wszystkich malwersacji nawet za cenę wskazania niektórych zwykłych transakcji jako malwersacje.

Uwrażliwienie modelu na zdarzenia rzadkie możliwe jest poprzez nadpróbkowanie lub poprzez przesunięcie granicy prawdopodobieństwa.

Przesunięcie wrażliwości modelu na malwersacje poprawi wskaźnik Recall kosztem wskaźnika Precission.

Tworzenie Oversampling

Oversampling i próbkowanie w analizie danych są technikami stosowanymi do dostosowania rozkładu klas zestawu danych (tj. Stosunku między różnymi reprezentowanymi klasami / kategoriami). Terminy te są używane zarówno w statystycznym próbkowaniu, metodologii projektowania badań, jak i w uczeniu maszynowym.

(źródło: https://www.wikiwand.com/en/Oversampling_and_undersampling_in_data_analysis)

 Losowy oversampling  (losowe zwiększenie ilości próbek mniejszościowych)

Losowe nadpróbkowanie polega na uzupełnieniu danych treningowych wieloma kopiami niektórych klas mniejszościowych. Nadpróbkowanie można wykonać więcej niż raz (2x, 3x, 5x, 10x itd.)

 Losowy undersampling  (losowe zmniejszanie ilości próbek większościowych)

Losowo usuwa próbki z klasy większościowych, z lub bez wymiany. Jest to jedna z najwcześniejszych technik stosowanych w celu złagodzenia nierównowagi w zbiorze danych, może to jednak zwiększyć wariancję klasyfikatora i potencjalnie odrzucić użyteczne lub ważne próbki.

(źródło: https://en.wikipedia.org/wiki/Oversampling_and_undersampling_in_data_analysis)

Procedura oversampling

Poniższa analiza jest kontynuacją procesu budowy modelu regresji liniowej prowadzonego tutaj.

Przypomnijmy jak bardzo nasz zbiór zmiennych endogenicznych jest niezbilansowany.

df.Class.value_counts(dropna = False, normalize=True)

Tylko 0,46

Spróbujmy odwzorować tą zależność dla danych treningowych w Pythonie:

print("ytrain = 0: ", sum(ytrain == 0))
print("ytrain = 1: ", sum(ytrain == 1))

OVS_gauge = sum(ytrain == 0) / sum(ytrain == 1) 
OVS_gauge = np.round(OVS_gauge, decimals=0)
OVS_gauge = OVS_gauge.astype(int)
OVS_gauge

216

Na jedną treningową zmienną zależną 1 przypada 216 testowych zmiennych zależnych 0.

Jak widać zbiór treningowy zachował proporcje w stosunku do zbioru pierwotnego dzięki użyciu parametru stratify = y w równaniu definiującym zbiór testowy i zbiór treningowy.

Teraz możemy zwiększyć o 217 razy ilość testowych zmiennych zależnych.

ytrain_pos_OVS = pd.concat([ytrain[ytrain==1]] * OVS_gauge, axis = 0) 
ytrain_pos_OVS

11664.0

Co równa się liczbie pierwotnej ilości zmiennych 1 (w sumie: 54) pomnożonych przez 216.

Teraz to samo musimy zrobić ze wszystkimi rekordami zmiennych niezależnych X, które w wyniku miały wartość: y = 1.

Xtrain.loc[ytrain==1, :]

Zmienne te muszą być również zwielokrotnione 216 razy.

Xtrain_pos_OVS = pd.concat([Xtrain.loc[ytrain==1, :]] * OVS_gauge, axis = 0)

Sprawdzamy czy liczba wierszy dla zmiennych zależnych i niezależnych w zbiorze treningowym jest sobie równa.

Xtrain_pos_OVS.v1.count()

11664.0

Teraz wprowadzamy nowe, dodatkowe zmienne 1 do zbioru testowego.

ytrain_OVS = pd.concat([ytrain, ytrain_pos_OVS], axis = 0).reset_index(drop = True)
Xtrain_OVS = pd.concat([Xtrain, Xtrain_pos_OVS], axis = 0).reset_index(drop = True)

W pierwotnym zbiorze testowym mieliśmy: 11711 rekordów

ytrain.count()

Po przeprowadzeniu oversampling mamy: 23375 rekordów

ytrain_OVS.count()

Znakomicie, teraz podstawiamy nowy zbiór testowy oversampling do siatki grid według tej same formuły, którą użyliśmy wcześniej.

Parametry2 = {'C': np.power(10.0, np.arange(-3, 3))}
OVS_reg = LogisticRegression(warm_start = True)
OVS_grid = GridSearchCV(OVS_reg, param_grid = Parametry2, scoring = 'roc_auc', n_jobs = 5)

OVS_grid.fit(Xtrain_OVS, ytrain_OVS)

Teraz użyjemy bloku diagnostycznego do oceny modelu.

print("Recall Training data:     ", np.round(recall_score(ytrain_OVS, OVS_grid.predict(Xtrain_OVS)), decimals=4))
print("Precision Training data:  ", np.round(precision_score(ytrain_OVS, OVS_grid.predict(Xtrain_OVS)), decimals=4))

print("----------------------------------------------------------------------")
print("Recall Test data:         ", np.round(recall_score(ytest, OVS_grid.predict(Xtest)), decimals=4)) 
print("Precision Test data:      ", np.round(precision_score(ytest, OVS_grid.predict(Xtest)), decimals=4))
print("----------------------------------------------------------------------")
print("Confusion Matrix Test data")
print(confusion_matrix(ytest, OVS_grid.predict(Xtest)))


print("----------------------------------------------------------------------")
print(classification_report(ytest, OVS_grid.predict(Xtest)))

y_pred_proba = OVS_grid.predict_proba(Xtest)[::,1]
fpr, tpr, _ = metrics.roc_curve(ytest,  y_pred_proba)
auc = metrics.roc_auc_score(ytest, y_pred_proba)
plt.plot(fpr, tpr, label='Logistic Regression (auc = 
plt.xlabel('False Positive Rate',color='grey', fontsize = 13)
plt.ylabel('True Positive Rate',color='grey', fontsize = 13)
plt.title('Receiver operating characteristic')
plt.legend(loc="lower right")
plt.legend(loc=4)
plt.plot([0, 1], [0, 1],'r--')
plt.show()

 

Po przeprowadzeniu oversampling wielkość recall dla parametru testowego 1 zwiększyła się z 0.65 do poziomu 0.88. Stało się to kosztem precission które spadło dla zmiennej testowej 1 z poziomu 0.77 do poziomu 0.32.

Next part:

Model Regresji Logistycznej. Część 3: zmiana progu w modelu regresji logistycznej

 

Artykuł Model Regresji Logistycznej. Część 2: Oversampling pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Estimation of the result of the empirical research with machine learning tools (part 2) https://sigmaquality.pl/uncategorized/estimation-of-the-result-of-the-empirical-research-with-machine-learning-tools-part-2/ Sat, 02 Mar 2019 19:38:00 +0000 http://sigmaquality.pl/?p=5740 In first part of this publication described the problem of additional classification of the quality classes. Every charge of Poliaxid have to go through rigoristic, [...]

Artykuł Estimation of the result of the empirical research with machine learning tools (part 2) pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

 Artificial intelligence in process of classification 

In first part of this publication described the problem of additional classification of the quality classes. Every charge of Poliaxid have to go through rigoristic, expensive test to classification to the proper class of quality. [Source of data]

In last study we showed that some quantity factors associated with production have significant impact on the final quality of the substance.

 Existing correlation lead to the conclusion that it is possible effective model of artificial intelligence is applied 

It leads to the two conclusions:

  • Laborious method of classification could be replaced by theoretical model.
  • Persons who monitor production process could be informed by the model about probability of final quality of the substance.

Machine learning procedure allows us make try to build such model.

We open the base in Python.

import pandas as pd
import numpy as np

df = pd.read_csv('c:/2/poliaxid.csv', index_col=0)
df.head(5)

We divide set of data in to the independent variables X and dependent variable y, the result of the process.

X = df.drop('quality class', axis=1) 
y = df['quality class']    

Now we divide database into the training and test underset.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=123,stratify=y)

Pipeline merge standardization and estimation. We took as the estimation method of Random Forest.

from sklearn.pipeline import make_pipeline
from sklearn import preprocessing
from sklearn.ensemble import RandomForestRegressor

pipeline = make_pipeline(preprocessing.StandardScaler(),
    RandomForestRegressor(n_estimators=100))

Hyperparameters of the random forest regression are declared.

hyperparameters = {'randomforestregressor__max_features': ['auto', 'sqrt', 'log2'],
                   'randomforestregressor__max_depth': [None, 5, 3, 1]}

Tune model using cross-validation pipeline.

from sklearn.model_selection import GridSearchCV
clf = GridSearchCV(pipeline, hyperparameters, cv=10)
clf.fit(X_train, y_train)

Random forest is the popular method using regression engine to obtain result.

Many scientists think that this is incorrect. Andrew Ng, in Machine Learning course at Coursers, explains why this is a bad idea - see his Lecture 6.1 - Logistic Regression | Classification at YouTubee. https://www.youtube.com/watch?v=-la3q9d7AKQ&t=0s&list=PLLssT5z_DsK-h9vYZkQkYNWcItqhlRJLN&index=33

I, as an apprentice, will lead this model to the end. Without this rounding Confusion Matrix would be impossible to use because y from the test set has discrete form but predicted y would be in format continuous.

Here we have array with the result of prediction our model. You can see continuous form of result.

y_pred

Empirical result has discrete form.

y.value_counts()

We make rounding continuous data to the discrete form.

y_pred = clf.predict(X_test)
y_pred = np.round(y_pred, decimals=0)

Typical regression equation should not be used to the classification, but logistic regression seems to can make classification.

This is occasion to compare linear regression, logistic regression and typical tool used to classification Support Vector Machine.

Now we make evaluation of the model. We use confusion matrix.

from sklearn.metrics import classification_report, confusion_matrix
from sklearn import metrics

co_matrix = metrics.confusion_matrix(y_test, y_pred)
co_matrix

print(classification_report(y_test, y_pred))

Regression Random Forest with a temporary adaptation to discrete results seems to be good!

According to the f1-score ratio, model of artificial intelligence can good classify for these classes which have many occurrences.

For example 0 class has 13 occurrence and model can't judge this class. In opposite to the class 0 is class 1. There are 136 test values and model can properly judge classes in  78

In next part of this investigation we will test models of artificial intelligence intended to the make classification.

Next part:

Estimation of the result of the empirical research with machine learning tools. Classification with SVM Support Vector Machine (part 3)

Artykuł Estimation of the result of the empirical research with machine learning tools (part 2) pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Zastosowanie estymatora liniowego Support Vector Machine (SVM) do tworzenia prognozy zapadalność na cukrzycę https://sigmaquality.pl/uncategorized/algorytm-support-vector-machine-svm-zaliczany-jest-do-estymatorow-machine-learning-uczenia-z-nadzorem-w-oparciu-o-procesy-klasyfikacji-i-analizy-regresji-klasyfikator-svm-wykorzystuje-algorytm-optyma/ Wed, 05 Sep 2018 19:24:00 +0000 http://sigmaquality.pl/?p=5515 Support Vector Machine zaliczany jest do estymatorów machine learning uczenia z nadzorem w oparciu o procesy klasyfikacji i analizy regresji. Klasyfikator SVM wykorzystuje algorytm optymalizacji [...]

Artykuł Zastosowanie estymatora liniowego Support Vector Machine (SVM) do tworzenia prognozy zapadalność na cukrzycę pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

Support Vector Machine zaliczany jest do estymatorów machine learning uczenia z nadzorem w oparciu o procesy klasyfikacji i analizy regresji.

Klasyfikator SVM wykorzystuje algorytm optymalizacji w oparciu o maksymalizację marginesu hiperplanu. Algorytm SVM jest przeznaczony do prowadzenia możliwie najlepszej klasyfikacji wyników. Wektory oddzielające hiperprzestrzenie mogą mieć charakter liniowy lub (dzięki funkcji SVC) nieliniowy.

W naszym modelu zastosujemy liniowy klasyfikator SVM.

Ćwiczenie przeprowadzimy na próbce badań laboratoryjnych przeprowadzonych na 768 pacjentów. Dane do ćwiczenia z pełnym opisem można znaleźć tutaj: https://www.kaggle.com/kumargh/pimaindiansdiabetescsv

Na początek otworzymy bazę wraz z niezbędnymi bibliotekami.

import pandas as pd 
import numpy as np 
import matplotlib.pyplot as plt
import seaborn as sns
import pprint

from sklearn.pipeline import Pipeline
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC


from sklearn.model_selection import train_test_split
from sklearn import preprocessing
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.externals import joblib


df = pd.read_csv('c:/1/diabetes.csv', usecols=['Pregnancies', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'Age', 'Outcome'])
df.head(3)

Dokonujemy wyboru funkcji celu y jako kolumny 'Outcome’ oraz usuwamy tą kolumnę ze zbioru zmiennych opisujących X.

X=df.drop(['Outcome'],axis=1)
Y=df['Outcome']

Transformacja i klasyfikacja

Tworzymy obiektu pipeline jako połączenie transformatora i klasyfikatora.

Transformator ‘StandardScaler()’ standaryzuje zmienne do populacji opisanej na standaryzowanym rozkładzie normalnym.

Jako estymatora użyliśmy Support Vector Machine (SVM) specjalizującym się w klasyfikacji próbek w kierunku maksymalizacji marginesu pomiędzy grupami danych.

W tym badaniu przyjmiemy klasyfikator liniowy.

ew = [('scaler', StandardScaler()), ('SVM', SVC())]
pipeline = Pipeline(ew)

Istnieją dwa parametry dla jądra SVM, mianowicie C i gamma. Powinniśmy ustawić siatkę parametrów przy użyciu wielokrotności 10. Deklarujemy przestrzeń hiperparametrów. Najlepsza konfiguracja hiperparametrów zostanie wybrana w drodze dostrajania modelu przez GridSearchCV.

parameteres = {'SVM__C':[0.001,0.1,10,100,10e5], 'SVM__gamma':[0.1,0.01]}

Tworzymy zestaw danych do dalszej transformacji i estymacji. Przyjmujemy 20

X_train, X_test, y_train, y_test = train_test_split(X,Y,test_size=0.2, random_state=30, stratify=Y)

Parametr stratify=y powoduje odwzorowanie struktury estymatora do struktury populacji. Dzięki temu parametrowi proporcje określonych wartości w próbce testowej będą taki sam, jak proporcja  w próbce treningowej.

Dostrajanie modelu

grid = GridSearchCV(pipeline, param_grid=parameteres, cv=5)
grid.fit(X_train, y_train)

Powyższy kod odpowiada za dostrajanie modelu.

Istnieją dwa sposoby szukania najlepszych hiperpartametrów do dostrojenia modelu:

  • szukanie przez siatkę (tzw. ‘Grid’)
  • szukanie losowe

 

GridSearchCV – nielosowa metoda dostrajania modelu siatki.

 

 

W Pipeline wskazaliśmy metody transformatora: StandardScaler() i estymator: Support Vector Machine.

Następnie w kodzie Hyperparameters – wskazaliśmy parametry: 'SVM__C'oraz 'SVM__gamma'.

Ponieważ używamy GridSearchCV, ta funkcja decyduje o najlepszej wartości w 'SVM__C' i innych zadanych hiperparametrach w zależności od tego, jak dobrze klasyfikator działa na zbiorze danych.

Zobaczmy jakie parametry są najlepsze według GridSearchCV:

pparam=pprint.PrettyPrinter(indent=2)
print(grid.best_params_)

Czas sprawdzić, na ile nasz model jest dobry, na ile trafnie opisuje rzeczywistość.

Przypomnijmy że celem modelu było wskazanie na podstawie wyników badań czy dany pacjent jest chory na cukrzycę czy nie. Czyli odpowiedź modelu w postaci zmiennej zależnej y powinien wynosić 0 lub 1.

y_pred = grid.predict(X_test)
y_pred = np.round(y_pred, decimals=0)

Ocena modelu przez Confusion Matrix

Do oceny naszego modelu tworzącego odpowiedzi binarne, użyjemy Confusion Matrix.

Macierz wskazuje na ile model trafnie typuje odpowiedzi. Zestawia się tu odpowiedzi ze zbioru testowego z odpowiedziami uzyskanymi w drodze predykcji.

from sklearn.metrics import classification_report, confusion_matrix
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred)) 

Macierz ma dwa wymiary ponieważ odpowiedzi mają charakter binarny.

Jak interpretować Confusion Matrix?

Macierz wskazuje ile razy model trafnie wytypował odpowiedź a ile razy się pomylił.

Liczby w polach białych oznaczają ilość trafnych typowań, zaś liczby w czarnych polach oznaczają typowania błędne. Intuicyjnie będziemy lepiej oceniali modele, które mają istotną przewagę w polach białych nad polami czarnymi.

 Celność modelu  (accuracy (ACC)) jest interpretowana jako dokładność klasyfikacji. Jest liczona jako suma liczb z białych pól do sumy liczb ze wszystkich pól: ACC = A+D / A+B+C+D. Czym wyższa wartość procentowa tym lepiej.

 Precyzja modelu  (precision or positive predictive value (PPV)) czyli poziom sprawdzalności prognoz modelu. Jest liczona jako PPV = A / A+B.

 Odwołanie modelu  (sensitivity, recall, hit rate, or true positive rate (TPR))– w ilu przypadkach obecni pacjenci z cukrzycą  są identyfikowani przez model jako chorzy. Jest liczona jako   TPR = A / A+C.

 

 F-Score 

Trudno jest porównać model z niskim  Precision i wysokim Recall i odwrotnie.

Aby porównać modele należy użyć F-Score, które mierzy  jednocześnie Recall i Precision.

F-Score = (2* Precision* Recall)/( Precision+ Recall) = 2A/(2A+B+C)

 

Porównanie estymatorów

Teraz zestawimy model oparty o estymator Support Vector Machine z modelem z poprzedniego przykładu opartym o estymator Random Forest.

Widzimy, że wyniki estymacji Support Vector Machine versus Random Forest niewiele się różnią.

Praktyczne użycie modelu Machine learning

Model możemy zapisać używając kodu:

joblib.dump(grid, 'c:/1/rf_SVM.pkl')

Możemy odczytać zapisany wcześniej model:

clf2 = joblib.load('c:/1/rf_SVM.pkl')

Wyniki predykcji możemy podstawić do wynikowych danych empirycznych.

lf2.predict(X_test)
X = df.drop('Outcome', axis=1)
WYNIK = clf2.predict(X)
df['MODEL'] = pd.Series(WYNIK)

df['Result'] = df['MODEL'] - df['Outcome']
df[['Outcome','MODEL', 'Result']].sample(10)

cdf['Result'].value_counts()

df['Result'].value_counts(normalize=True)

Artykuł Zastosowanie estymatora liniowego Support Vector Machine (SVM) do tworzenia prognozy zapadalność na cukrzycę pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Zastosowanie estymatora Random Forest do tworzenia prognozy zapadalność na cukrzycę https://sigmaquality.pl/uncategorized/zastosowanie-estymatora-random-forest-do-tworzenia-prognozy-zapadalnosc-na-cukrzyce/ Wed, 05 Sep 2018 19:24:00 +0000 http://sigmaquality.pl/?p=5474 Machine Learning w medycynie W tym badaniu użyto metody Random Forest. Naszym celem jest zbudowanie modelu uczenia maszynowego do predykcji zapadalności na cukrzycę. Próbkę danych, [...]

Artykuł Zastosowanie estymatora Random Forest do tworzenia prognozy zapadalność na cukrzycę pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Machine Learning w medycynie

W tym badaniu użyto metody Random Forest.

Naszym celem jest zbudowanie modelu uczenia maszynowego do predykcji zapadalności na cukrzycę.

Próbkę danych, nad którą będziemy dzisiaj pracowali, wraz z opisem można znaleźć tutaj: https://www.kaggle.com/kumargh/pimaindiansdiabetescsv

Z modelu usunęliśmy zmienną egzogeniczną: ‘Glucose’ ponieważ wykazywała się ona zbyt wysoką korelacją z wartością wynikową. Istnienie doskonałego estymatora oznaczało zmniejszenie w modelu znaczenia pozostałych mniej doskonałych estymatorów.

Przekładając to na język polski, sam poziom glukozy wskazuje czy ktoś jest chory na cukrzycę. Estymator ten jest więc utożsamiany z pojawieniem się choroby a nie z czynnikiem ją wywołującym.

Wczytanie bazy

Otwieram bazę wraz z niezbędnymi bibliotekami. Wyświetlam 5 pierwszych rekordów bazy.

import pandas as pd
import numpy as np

df = pd.read_csv('c:/1/diabetes.csv', usecols=['Pregnancies', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'Age', 'Outcome'])
df.head(5)

W tabeli są ewidentne błędy: ciśnienie krwi równe zero, zerowy poziom insuliny czy grubość skóry.

Usunięcie tych błędów nie mają większego wpływu na dobroć modelu. Można łatwo to sprawdzić wprowadzając do kodu poniższą instrukcje usuwającą błędne rekordy.

df[['Pregnancies', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'Age']] = df[['Pregnancies', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'Age']].replace(0,np.nan)
df = df.dropna(how='any')

Wskazanie zmiennych egzogenicznych i zmiennej endogenicznej

Teraz wskazuje, która z kolumn jest kolumną wynikową y, zawierającą zmienne zależne.

Kolumnę wynikową y usuwamy z DataFrame tworząc zestaw danych opisujących X, czyli kolumny zawierające zmienne niezależne.

y = data.Outcome
X = data.drop('Outcome', axis=1)

Tworzymy zestaw danych do dalszej transformacji i estymacji.

Przyjmujemy 80

Parametr ‘random_state’ określa stabilność, nie ma znaczenia liczba jaką tam wpiszemy. Opuszczenie parametru ‘random_state’ będzie powodowało, że model przy każdym uruchomieniu będzie generował inne wartości losowe, co będzie prowadziło do destabilizacji obliczeń.

Parametr stratify=y powoduje odwzorowanie struktury estymatora do struktury populacji. Dzięki temu parametrowi proporcje określonych wartości w próbce testowej będzie taki sam, jak proporcja określonych wartości w próbce treningowej.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=123,stratify=y)

Obiekt Pipline

Teraz tworzymy zespół transformatora z estymatorem, elementem scalającym jest obiekt pipeline.

Transformator preprocessing.StandardScaler() tworzy standaryzację danych do postaci rozkładu normalnego o średniej 0 i odchyleniu standardowym 1.

Estymator Random Forest tworzy model zachowania zmiennej zależnej w stosunku do przebiegu zmiennych niezależnych. Estymatorem jest w naszym przykładzie RandomForestRegressor(n_estimators=100).

from sklearn.pipeline import make_pipeline
from sklearn import preprocessing
from sklearn.ensemble import RandomForestRegressor

pipeline = make_pipeline(preprocessing.StandardScaler(), RandomForestRegressor(n_estimators=100))

Parametr n_estimators=100 dla Random Forest wskazuje ile drzew decyzyjnych zostanie uruchomionych w trakcie procesu tworzenia modelu. Zwiększenie liczby drzew do np. 1000 może spowodować nieznaczną poprawę trafności modelu, wydłuży jednak czas trwania obliczeń. Sztuką jest znalezienie złotego środka między czasem realizacji obliczeń i trafnością obliczeń.

Deklarowanie hiperparametrów

Teraz tworzymy hiperparametry do dostrojenia modelu. W tej linijce kodu deklarujemy jakimi metodami zamierzamy dostrajać i optymalizować model.

hyperparameters = {'randomforestregressor__max_features': ['auto', 'sqrt', 'log2'],
                   'randomforestregressor__max_depth': [None, 5, 3, 1]}

max_features - określa maksymalną liczbę funkcji do rozważenia podczas szukania podziału.

Mamy tu 3 metody: ['auto', 'sqrt', 'log2'] i program sam wybierze najlepszą metodę dostrojenia.

max_features:"auto" żaden podzbiór cech nie jest wykonywany na drzewach, więc „losowy las” jest właściwie zapakowanym zespołem zwykłych drzew regresji. Przy ustawieniu AUTO, kod weźmie wszystkie funkcje, które mają sens z każdego drzewie. Tutaj nie są nakładane żadnych ograniczenia na pojedyncze drzewa.

max_features: „sqrt”, Ta opcja pobierająca pierwiastek kwadratowy z całkowitej liczby funkcji w pojedynczym przebiegu. Na przykład, jeśli całkowita liczba zmiennych wynosi 100, możemy wziąć tylko 10 z nich w pojedynczym drzewie.

max_depth wskazuje na całkowitą głębokość drzew decyzyjnych.

Dostrajanie modelu

Kolejna linijka kodu prowadzi proces samego dostrajania algorytmu predykcji. Obiekt fit jest to funkcją dopasowującą.

from sklearn.model_selection import GridSearchCV
clf = GridSearchCV(pipeline, hyperparameters, cv=10)
clf.fit(X_train, y_train)

Powyższy kod odpowiada za dostrajanie modelu. Z wielkiej ilości interakcji kod wybiera najlepszy wariant.

Istnieją dwa sposoby szukania najlepszych hiperpartametrów do dostrojenia modelu:

  1. szukanie przez siatkę (tzw. ‘Grid’)
  2. szukanie losowe

GridSearchCV – to metoda siatki, nielosowa metoda dostrajania modelu.

Przypomnijmy:

W Pipeline wskazaliśmy metody transformatora: StandardScaler() i estymator: RandomForestRegressor().

Następnie w kodzie Hyperparameters – wskazaliśmy parametry: max_features oraz max_depth.

Ponieważ używamy GridSearchCV, ta funkcja decyduje o najlepszej wartości w max_features i innych zadanych hiperparametrach w zależności od tego, jak dobrze klasyfikator działa na zbiorze danych.

Teraz sprawdzamy, jakie wartość parametrów dostrajania modelu okazały się optymalne.

import pprint
pparam=pprint.PrettyPrinter(indent=2)
print(clf.best_params_)

Czas sprawdzić, na ile nasz model jest dobry, na ile trafnie opisuje rzeczywistość.

Przypomnijmy, że celem modelu było wskazanie na podstawie wyników badań czy dany pacjent jest chory na cukrzycę czy nie. Czyli odpowiedź modelu w postaci zmiennej zależnej y powinien wynosić 0 lub 1.

Niestety nasz model nie tworzy wartości dyskretnych tylko wartości ciągłe.

Zmieniamy to jedną linijką kodu.

y_pred = np.round(y_pred, decimals=0)

Ocena modelu przez Confusion Matrix

Do oceny naszego modelu tworzącego odpowiedzi binarne, użyjemy Confusion Matrix.

Macierz wskazuje na ile model trafnie typuje odpowiedzi. Zestawia się tu odpowiedzi ze zbioru testowego z odpowiedziami uzyskanymi w drodze predykcji.

from sklearn.metrics import classification_report, confusion_matrix
print(confusion_matrix(y_test, y_pred))

Macierz ma dwa wymiary ponieważ odpowiedzi mają charakter binarny.

Jak interpretować Confusion Matrix?

Macierz wskazuje ile razy model Random Forest trafnie wytypował odpowiedź a ile razy się pomylił.

Wyobraźmy sobie, że to jest macierz Confusion Matrix.

Liczby w polach białych oznaczają ilość trafnych typowań, zaś liczby w czarnych polach oznaczają typowania błędne. Intuicyjnie będziemy lepiej oceniali modele które mają istotną przewagę w polach białych nad polami czarnymi.

Wskaźniki Confusion Matrix

 Celność modelu  (accuracy (ACC)) jest interpretowana jako dokładność klasyfikacji. Jest liczana jako suma liczb z białych pól do sumy liczb ze wszystkich pól: ACC = A+D / A+B+C+D. Czym wyższa wartość procentowa tym lepiej.

(87+20) / (87+20+13+34) = 69

 Precyzja modelu  (precision or positive predictive value (PPV)) czyli poziom sprawdzalności prognoz modelu. Jest liczona jako PPV = A / A+B.

87 / (87+13) = 87

 Odwołanie modelu  (sensitivity, recall, hit rate, or true positive rate (TPR))– w ilu przypadkach obecni pacjenci z cukrzycą  są identyfikowani przez model jako chorzy. Jest liczona jako   TPR = A / A+C.

87 / (87+34) = 72

 F-Score 

Trudno jest porównać model z niskim  Precision i wysokim przypomnieniem Recall i odwrotnie.

Aby porównać modele należy użyć F-Score, które mierzy  jednocześnie Recall i Precision.

F-Score = (2* Precision* Recall)/( Precision+ Recall) = 2A/(2A+B+C)

2*87 / (2*87 + 34 + 13) = 79

 

Wszystkie te obliczenia można uzyskać korzystając z następującego kodu:

print(classification_report(y_test, y_pred))

oraz

print("Accuracy: ",np.round(metrics.accuracy_score(y_test, y_pred), decimals=2))

Na końcu możemy zapamiętać model wpisując kod:

joblib.dump(clf, 'c:/1/rf_regressor.pkl')

Aby otworzyć model wystarczy wpisać:

clf2 = joblib.load('c:/1/rf_regressor.pkl')

 

 

Artykuł Zastosowanie estymatora Random Forest do tworzenia prognozy zapadalność na cukrzycę pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>
Initial graphical analysis of dependency in analyzing of the incidence of diabetes – Seaborn library https://sigmaquality.pl/uncategorized/initial-graphical-analysis-of-dependency-in-analyzing-of-the-incidence-of-diabetes-seaborn-library/ Wed, 05 Sep 2018 19:24:00 +0000 http://sigmaquality.pl/?p=5611 Application of Machine Learning in clinical trials The aim is the initial graphical analysis of the diabetic research. We use Pandas library to prepare the [...]

Artykuł Initial graphical analysis of dependency in analyzing of the incidence of diabetes – Seaborn library pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

Application of Machine Learning in clinical trials

The aim is the initial graphical analysis of the diabetic research.

We use Pandas library to prepare the data. Next we will show preliminary conclusions using Seaborn library.

Our research have to show dependencies between selected features of the patients bodies and the incidence of the diabetes.

Data sample that we use in this research with the description you can find here: https://www.kaggle.com/kumargh/pimaindiansdiabetescsv

 

Let's take a look at our sample

First we are launching all needed Python libraries.

Next we display first ten rows of the data frame.

import pandas as pd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style

df = pd.read_csv('c:/1/diabetes.csv')
df.head(10)

For example value of the blood pressure for some patients shows zero. That's mean the patients were dead during the examination. Similarly, there is impossible lack of the skin thickness or zero value of insulin in the blood.

Contaminated sample is unsuitable to lead further of investigation.

Data table should be cleaned and supplemented by the proper data.

Python has many methods to supplement lack data with mean or dominant.

This is not good for result of our research. Dominant or mean value of sample applied to a particular patient have non sens.

Such solution could lead us to false conclusion. So, we ought to cut out contaminated or cull records.

Let's see something more about sample

df.dtypes

Now we check how many rows and columns are in the table.

df.shape

Now we cut-out contaminated and cull records. Probably we lost many records partly usable for our investigation. At the end we obtain only valuable samples.

Let’s see how many zero value are in columns: ['Glucose','BloodPressure','SkinThickness', 'Insulin','BMI']

(df[['Glucose','BloodPressure','SkinThickness', 'Insulin','BMI']]==0).sum()

To effectively throw out all zero values for selected columns, we have to exchange zero into np.NaN

df[['Glucose','BloodPressure','SkinThickness', 'Insulin','BMI']] = df[['Glucose','BloodPressure','SkinThickness', 'Insulin','BMI']].replace(0,np.nan)

Let's see what is the change in place of zero values we can see NaN.

We check how many is NaN in selected columns.

df.isnull().sum()

Now we can apply formula who cut out incomplete records. We remove records and display what is the result.

df = df.dropna(how='any')
df.isnull().sum()

Great, we have clean samples. Now let's check how many rows we have.

df.shape

The column 'Output' contain result of the trial. This column shows whether the patient is diabetic or not. Now we exchange 0, 1 into 'diabetes' in 'healthy'.

df['Outcome']=df['Outcome'].map({1:'diabetis', 0:'healthy'})

Now our data table looks that.

df.tail(3)

Now with Seaborn library we create big matrix plot. This plot display dependencies between exogenous variables.

sns.pairplot(data=df[['Pregnancies','Glucose' ,'BloodPressure','SkinThickness','Insulin','BMI','DiabetesPedigreeFunction','Age', 'Outcome']], hue='Outcome', dropna=True)

Experienced scientist easily detects anomaly and significant dependencies.

Now we reduce number of variables to better analyze process.

sns.pairplot(data=df[['Glucose' ,'BloodPressure','SkinThickness', 'Outcome']], hue='Outcome', dropna=True, height=3)

We can easily detect most effective variable is level of glucose in the blood. Blue points on the plot show, diabetes appear after exceeding a certain level of glucose.

Great, but just main symptom of diabetes is high level of glucose in the blood. This is obviously, conclusion that lead to nothing. Unfortunately medicine is not my profession.

Let's try again.

Our aim is to detect most significant medical parameters causes diabetes.

Now we check look down other exogenous variables. We change a little colors and form.

Now we check look down other exogenous variables. We a little change colors and markers form on the plot.

sns.pairplot(data=df[['Insulin' ,'Age','SkinThickness', 'Outcome']], hue='Outcome', dropna=True, height=2.5, palette=('husl'), markers='x')

We can display this plot in another form.

g = sns.PairGrid(data=df[['Insulin' ,'Age','SkinThickness', 'Outcome']], hue='Outcome', dropna=True, height=3)
g = g.map_upper(plt.scatter)
g = g.map_lower(sns.kdeplot)

We can observe that the incidence of diabetes is affected by age of patient and level of insulin in the blood. It is visible thanks to change of the dots colors.

g = sns.pairplot(data=df[['Insulin' ,'Age','SkinThickness', 'Outcome']], hue='Outcome', dropna=True, height=3, aspect=2, palette=('cubehelix'), markers='o')
g.axes[2,1].set_xlim((0,140))
g.axes[1,2].set_xlim((0,60))

 

 

 

Artykuł Initial graphical analysis of dependency in analyzing of the incidence of diabetes – Seaborn library pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>