Python/Scikit-learn

[Python] logistic regression

언킴 2022. 1. 17. 20:03
반응형

로지스틱 회귀는 이진분류에서 사용되는 모델 중 흔히 쓰이는 모델이라고 보면 된다. 이론적인 부분을 공부하고 싶다면 여기를 눌러서 들어가면 볼 수 있다. 나는 임의로 데이터를 생성하여 분석을 진행했기 때문에 모델의 성능이 좋게 나오지는 않았다. 공식 데이터를 가지고 분석을 수행하면 보다 좋은 결과를 도출할 수 있을 것이다.

 

EDA

 

 

def sigmoid(z):
    return 1/(1+np.exp(-z))

def f(x, w, b):
    return np.dot(x, w)  + b 

def df(x, w, b):
    return x, 1

def binary_cross_entropy(z, t):
    return -(t*np.log(z) + (1-t)*np.log(1-z)).mean()

분석을 진행하기 앞서 로지스틱의 수식(sigmoid)을 만들고, 각 미분값을 구하는 함수를 설정했다. 그리고 loss function은 cross entropy를 사용했다. 

 

fitW, fitB = np.random.rand(5, 1), np.random.rand(1)
bestW, bestB = fitW, fitB
eta = 5e-5
bestLoss = 350
epochs = 250
bestepoch = 0
# target_col = ['Lepton_pt', 'Jet_pt']
train_loss_total, eval_loss_total = [], []

for epoch in range(epochs):
    train_loss, eval_loss = 0.0, 0.0 
    
    for i in range(len(x_train)):
        x = x_train_ary[i] 
        label = y_train_ary[i]
        
        z = f(x, fitW, fitB)
        z = sigmoid(z)
        loss = binary_cross_entropy(z, label)
    
        gradW, gradB = df(x, fitW, fitB)
        
        gradW = ((label-z) * gradW).mean()
        gradB = ((label-z) * gradB).mean()
        
        fitW += gradW*eta 
        fitB += gradB*eta
    
        train_loss += loss 
    train_loss_total.append(train_loss)
    
    
    for i in range(len(x_eval)):
        x = x_eval_ary[i]
        label = y_eval_ary[i]
        
        z = f(x, fitW, fitB)
        z = sigmoid(z) 
        
        loss = binary_cross_entropy(z, label)
        
        eval_loss += loss 
    eval_loss_total.append(eval_loss)
    if eval_loss_total[-1] < bestLoss : 
        bestLoss = eval_loss_total[-1]
        bestW, bestB = fitW, fitB
        bestepoch = epoch
        
    if (epoch + 1) % 5 == 0 :
        print(f'epoch { epoch + 1 }, train loss : {train_loss:.4f}, eval loss : {eval_loss:.4f}, best Loss {bestLoss:.4f}, best epoch {bestepoch}')

코드는 한 번 값을 계산한 후 기울기에 학습률 만큼 이동하면서 오차를 갱신해가는 방식으로 진행하였고, 가장 좋았던 값을 저장하여 그 값을 최종 값으로 설정했다. 

logit = f(x_test_ary, bestW, bestB)
y_pred =  [1 if x > 0.5 else 0 for x  in sigmoid(logit)]

accuracy = (y_pred == y_test_ary).sum() / len(y_test_ary)
print(f'accuracy(%) : {accuracy*100:.4f}')
# accuracy(%) : 68.0217

임의로 데이터를 만들어서 했더니.. 68% 정도 나온다. 찍은 것 보다는 잘 나오긴 했지만 딱히 마음에 드는 정확도는 아니다..ㅠ

 

 

 

from sklearn.linear_model import LogisticRegression

model = LogisticRegression(max_iter=1000)
model.fit(x_train_ary, y_train_ary)


y_pred = model.predict(x_test_ary)
accuracy = (y_pred == y_test_ary).sum() / len(y_test_ary)
print(f'accuracy : {accuracy*100:.4f}%')

from sklearn.metrics import roc_curve
positive = model.predict_proba(x_test_ary)[:, 1]

fprs, tprs, thresholds = roc_curve(y_test_ary, positive)

plt.plot([0, 1], [0, 1])
plt.plot(fprs, tprs, label = 'ROC')
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.grid()
plt.legend();

이렇게 위에서 장황하게 미분하고 계산하고, 학습률만큼 이동하고... 뻘짓을 해도 사이킷-런에서는 단순히 몇 줄 코딩으로 값을 산출해준다. 이렇게 편리한 패키지이다. 

from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, AdaBoostClassifier, RandomForestRegressor
from sklearn.naive_bayes import BernoulliNB
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier

logit = LogisticRegression()
rf_cf = RandomForestClassifier()
naive = BernoulliNB()
gb = GradientBoostingClassifier()
ada = AdaBoostClassifier()
svc = SVC(probability=True)
mlp = MLPClassifier()

model_accuracy = []
plt.figure(figsize = (16, 9))
for model in [logit, rf_cf, naive, gb, ada,svc,  mlp]:
    model.fit(x_train_ary, y_train_ary)
    y_eval_pred = model.predict(x_eval_ary)
    y_eval_score = model.score(x_eval_ary, y_eval_ary)
    y_test_score = model.score(x_test_ary, y_test_ary)
    model_name = str(model).split('(')[0]
    model_accuracy.append([model_name, y_eval_score, y_test_score])
    
    positive = model.predict_proba(x_test_ary)[:, -1]
    
    fprs, tprs, thresholds = roc_curve(y_test_ary, positive)
    plt.plot(fprs, tprs, label = model_name + ' ROC_curve')
plt.legend(fontsize = 14);

 

사이킷-런에 있는 각 모델들을 한 번 전체적으로 그려보았다. 

 

 

 

결론: 사이킷-런 쓰자.

'Python > Scikit-learn' 카테고리의 다른 글

[Python] Scikit-learn에서 cross validation 사용하기  (0) 2022.01.14