oversampling - THE DATA SCIENCE LIBRARY https://sigmaquality.pl/tag/oversampling/ Wojciech Moszczyński Fri, 27 Sep 2019 17:57: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 oversampling - THE DATA SCIENCE LIBRARY https://sigmaquality.pl/tag/oversampling/ 32 32 Procedura oversampling dla Random Forest Classifier https://sigmaquality.pl/uncategorized/oversampling-dla-random-forest-classifier/ Fri, 27 Sep 2019 17:57:00 +0000 http://sigmaquality.pl/oversampling-dla-random-forest-classifier/ Przy budowie modelów klasyfikacji 0-1 występuje problem  zbilansowanych zbiorów źródło: http://sigmaquality.pl/machine-learning/model-regresji-logistycznej-czesc-2-oversampling/ In [1]: import numpy as np import pandas as pd #import xgboost as xgb import [...]

Artykuł Procedura oversampling dla Random Forest Classifier pochodzi z serwisu THE DATA SCIENCE LIBRARY.

]]>

Przy budowie modelów klasyfikacji 0-1 występuje problem  zbilansowanych zbiorów

In [1]:
import numpy as np
import pandas as pd
#import xgboost as xgb
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 import cross_validation, metrics
from sklearn.preprocessing import Normalizer
#from sklearn.cross_validation import cross_val_score
from sklearn.preprocessing import Imputer

import matplotlib.pyplot as plote



plt.style.use('ggplot')
In [2]:
df = pd.read_csv('c:/1/bank.csv')
df.head()
Out[2]:
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

MODEL BEZ ZBILANSOWANIA ZBIORÓW ———————————–

Skalowanie standardowe tylko dla wartości dyskretnych

Wybieram kolumny tekstowe, dyskretne, do głębszej analizy. Lepsze było to wybieranie dyskretne i ciągłe.

In [3]:
encoding_list = ['job', 'marital', 'education', 'default', 'housing', 'loan',
       'contact', 'month', 'day_of_week','poutcome']
In [4]:
df[encoding_list] = df[encoding_list].apply(LabelEncoder().fit_transform)
In [5]:
df[encoding_list].head()
Out[5]:
job marital education default housing loan contact month day_of_week poutcome
0 1 1 0 1 2 0 0 1 2 1
1 9 1 7 0 0 0 0 7 0 1
2 4 2 6 0 2 0 0 4 2 2
3 7 1 3 0 0 0 0 0 0 1
4 5 1 0 0 2 0 0 1 0 2

Tworzymy zestaw treningowy i zestaw testowy, budujemy model

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

Złoty podział zioru na testowy i treningowy

In [7]:
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 [8]:
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, 22)
Zbiór X testowy:     (13593, 22)
Zbiór y treningowy:  (27595,)
Zbiór y testowy:     (13593,)

Dane dyskretne są zdygitalizowane

In [9]:
Xtrain.head(4)
Out[9]:
Unnamed: 0 Unnamed: 0.1 age job marital education default housing loan contact duration campaign pdays previous poutcome emp_var_rate cons_price_idx cons_conf_idx euribor3m nr_employed
24697 24697 24697 49 1 1 2 1 0 0 1 222 9 999 0 1 1.4 94.465 -41.8 4.959 5228.1
25855 25855 25855 38 9 0 6 1 0 0 0 125 3 999 0 1 1.4 93.444 -36.1 4.963 5228.1
23236 23236 23236 42 0 0 6 0 0 0 1 26 4 999 0 1 1.4 94.465 -41.8 4.959 5228.1
13812 13812 13812 58 1 1 5 1 0 2 1 25 1 999 0 1 1.4 94.465 -41.8 4.866 5228.1

4 rows × 22 columns

Random Forest Classifier

In [10]:
from sklearn import model_selection
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import make_pipeline
In [11]:
forestVC = RandomForestClassifier (random_state = 1, 
                                  n_estimators = 750, 
                                  max_depth = 15, 
                                  min_samples_split = 5, min_samples_leaf = 1) 
modelF = forestVC.fit(Xtrain, ytrain)
y_predF = modelF.predict(Xtest)

Blok oceny jakości modelu Random Forest Classifier

Podstawienie do wzoru
In [12]:
ypred = modelF.predict(Xtest)
In [13]:
from sklearn.metrics import classification_report, confusion_matrix
from sklearn import metrics

co_matrix = metrics.confusion_matrix(ytest, ypred)
co_matrix
Out[13]:
array([[11692,   370],
       [  797,   734]], dtype=int64)
In [14]:
print(classification_report(ytest, ypred)) 
              precision    recall  f1-score   support

           0       0.94      0.97      0.95     12062
           1       0.66      0.48      0.56      1531

   micro avg       0.91      0.91      0.91     13593
   macro avg       0.80      0.72      0.75     13593
weighted avg       0.91      0.91      0.91     13593

In [15]:
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.91
Precision:   0.66
Recall:      0.48
F1 score:    0.56

Analiza poziomu zbilansowania zmiennej wynikowej

In [16]:
df.y.value_counts(dropna = False, normalize=True)
Out[16]:
0    0.887346
1    0.112654
Name: y, dtype: float64
In [17]:
print("ytrain = 0: ", sum(ytrain == 0))
print("ytrain = 1: ", sum(ytrain == 1))
ytrain = 0:  24486
ytrain = 1:  3109
In [18]:
Proporcja = sum(ytrain == 0) / sum(ytrain == 1) 
Proporcja = np.round(Proporcja, decimals=0)
Proporcja = Proporcja.astype(int)
Proporcja
Out[18]:
8

Na jedną daną sybskrypcje przypada 8 nieprzedłużonych subskrypcji. Powiększamy liczbę próbek niezależnych.

In [19]:
ytrain_pos_OVS = pd.concat([ytrain[ytrain==1]] * Proporcja, axis = 0) 
ytrain_pos_OVS.count()
Out[19]:
24872

Ilość zmiennych wynikowych: (1) zwiększyła się do liczby 24872
Mamy już wektor zmiennych wynikowych y, teraz trzeba zwiększyć liczbę zmiennych niezależnych.

Powiększamy liczbę próbek zmiennych niezależnych X
In [20]:
Xtrain_pos_OVS = pd.concat([Xtrain.loc[ytrain==1, :]] * Proporcja, axis = 0)
In [21]:
Xtrain_pos_OVS.age.count()
Out[21]:
24872
Powiększyliśmy ilość zmiennych gdzie wynik przedłużenia subskrypcji jest równy 1.

Teraz mamy tą samą liczbę wierszy zmiennych wynikowych i zmiennych niezależnych.

Teraz wprowadzamy nowe, dodatkowe zmienne 1 do zbioru treningowego.

In [22]:
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)

Sprawdzamy ilość wierszy w zbiorach przed i po oversampling

In [23]:
print("ilość elementów w zbiorze Xtrain:     ", Xtrain.age.count())
print("ilość elementów w zbiorze Xtrain_OVS: ", Xtrain_OVS.age.count())
print("ilość elementów w zbiorze ytrain:     ", ytrain.count())
print("ilość elementów w zbiorze ytrain_OVS: ", ytrain_OVS.count())
ilość elementów w zbiorze Xtrain:      27595
ilość elementów w zbiorze Xtrain_OVS:  52467
ilość elementów w zbiorze ytrain:      27595
ilość elementów w zbiorze ytrain_OVS:  52467
Teraz podstawiamy nowy zbiór testowy oversampling do siatki grid według tej same formuły, którą użyliśmy wcześniej.
In [24]:
forestVC = RandomForestClassifier (random_state = 1, 
                                  n_estimators = 750, 
                                  max_depth = 15, 
                                  min_samples_split = 5, min_samples_leaf = 1) 
modelF = forestVC.fit(Xtrain_OVS, ytrain_OVS)
Podstawienie do wzoru
In [25]:
ypred = modelF.predict(Xtest)
In [26]:
from sklearn.metrics import classification_report, confusion_matrix
from sklearn import metrics

co_matrix = metrics.confusion_matrix(ytest, ypred)
co_matrix
Out[26]:
array([[10875,  1187],
       [  268,  1263]], dtype=int64)
In [27]:
print(classification_report(ytest, ypred)) 
              precision    recall  f1-score   support

           0       0.98      0.90      0.94     12062
           1       0.52      0.82      0.63      1531

   micro avg       0.89      0.89      0.89     13593
   macro avg       0.75      0.86      0.79     13593
weighted avg       0.92      0.89      0.90     13593

In [28]:
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.89
Precision:    0.52
Recall:       0.82
F1 score:     0.63
Wynik modelu Random Forest Classifier przed Oversampling
  • Accuracy: 0.91
  • Precision: 0.66
  • Recall: 0.48
  • F1 score: 0.56

Wynik kodelu po Oversampling

  • Accuracy: 0.89
  • Precision: 0.52
  • Recall: 0.82
  • F1 score: 0.63

Artykuł Procedura oversampling dla Random Forest Classifier 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.

]]>