youtube 영상에서 오디오만 추출 후, 

영상 제목.mp3으로 저장하기 

from pytube import YouTube

import glob
import os.path

yt = YouTube("https://youtu.be/d22Qye1TbI4")


yt.streams.filter(only_audio=True).first().download()

files = glob.glob("*.mp4")
for x in files:
	if not os.path.isdir(x):
		filename = os.path.splitext(x)
		try:
			os.rename(x,filename[0] + '.mp3')
		except:
			pass

* Accuracy : (True Positives + True Negatives) / (TP + TN + FP + FN) 

올바르게 예측된 데이터의 수 / 전체 데이터 수 . 

from sklearn.metrics import accuracy_score   
train_accuracy = accuracy_score(train_target, train_pred)

만약 평가할 데이터의 target의 비율이 불균형할 경우 Accuracy는 큰 의미가 없다.  (like 0 : 80%, 1: 20%)

전부 0으로 예측하는 모델도 accuracy가 80은 나오기 때문. 

 

 

이럴 경우, balanced accuracy 혹은 classification report를 사용해서 다른 지표들로 모델을 평가할 수 있다. 

 

 

 

 

 

[Classification Report] : Precision, Recall, F1-score, Support 를 출력함. 

 

from sklearn.metrics import classification_report
print(classification_report(test_target, test_pred_18))

 

1. PrecisionTrue Positives / True Positives + False Positives

정밀도. 모델이 True로 예측한 데이터 중 실제로 True 인 데이터의 수 

 

2. Recall : TP / TP +FN

재현율.  실제로 True 인 데이터들 중 True로 예측한 경우의 수

 

3. F1 Score :  2 *Precision*Recall/ (Precision+Recall)

precision 과 recall의 조화평균. 하나의 통계치를 반환. 

산술평균이 아니라 조화평균을 쓰는 이유는  산술 평균을 쓰면 하나가 크고 하나가 작아도 비율에 따라 상쇄될 수 있다.  

따라서, 둘 중 하나가 0에 가까우면 F1 score가 낮은 값을 갖게 하도록 하기 위함이다. 

빨간 점 데이터와 초록 점 데이터를 분류하는 초평면을 f(x) 평면이라고 하자. 

이 초평면은 decision boundary라고도 한다.

초평면과의 가장 가까운 포인트, 즉 최단거리가 되는 지점을 support vector라고 하며, 

평면과 각 점 데이터 사이의 거리를 M (margin)이라고 한다. 

분류 알고리즘에 대한 기본 아이디어는 Margin을 적절하게 설정하여 경계선을 찾는 것이 목적이다. 

Margin이 최대가 될 때의 decision boundary가 가장 적절한 구분선이 된다. 

우선 데이터를 정확히 분류하는 범위를 먼저 찾고, 그 범위 안에서 Margin을 최대화하는 구분선을 택한다.

 

* Support Vector :

 

 

 

* SVMβ와 β0를 찾는 과정. 

 

 

*Robustness : 사전적 의미 ; 견고성, 강건성.    로버스트하다; 아웃라이어의 영향을 덜 받는다. 

 

 

* Kernel Trick : 저차원 공간을 고차원 공간으로 매핑해주는 작업.  기존의 저차원에서는 Linear로 구분선을 그릴 수 없을 때, 

고차원으로 변경함으로써 Linear 혹은 구분선을 더 쉽게 그릴 수 있게 해주는 방법. 

 

linear, polynomial, sigmoid, rbf 등의 kernel 기법을 선택할 수 있다.  (default : rbf)

 

 

 

* C : (cost function의 C 값 ) : 에러의 허용범위를 설정.  C가 크면 decision boundary는 더 굴곡지고, C가 작으면 decision boundary는 직선에 가깝다.  분류할 때 얼마나 더 정교하게 구분선을 만들것인가. 

C가 클수록 error를 적게 허용하나 계산시간이 오래 걸린다. C가 너무 작으면 과소적합, C가 너무 크면 과대적합이 될 수 있다

 

* gamma : 곡률. 

 

 

SVM은 노이즈가 많은 데이터셋에서는 오버피팅이 될 수 있다. 노이즈가 많을 경우에는 나이브 베이즈를 사용하는게 오히려 낫다.

 

 

 

[SVM kernel 방식 별 예시]

 

*linearSVC : 하이퍼 파라미터로 cost값 설정. 

model_1 = svm.SVC(kernel='linear', C=1).fit(X, y)
y_pred_1 = model_1.predict(X)

print(model_1.score(X, y))
confusion_matrix(y, y_pred_1)

 

* rbf kernel : radial basis function.  grid search를 이용해서 최적의 cost, gamma를 찾는다. 

 

model_2 = svm.SVC(kernel='rbf', gamma=0.7, C=1).fit(X, y)
y_pred_2 = model_2.predict(X)

print(model_2.score(X, y))
confusion_matrix(y, y_pred_2)

 

*polynomial kernel : 다항 커널. 

model_3 = svm.SVC(kernel='poly', degree=2, gamma="auto", C=1).fit(X,y)
y_pred_3 = model_3.predict(X)

print(model_3.score(X, y))
confusion_matrix(y, y_pred_3)

 

 

 

 

* 시각화 :

def make_meshgrid(x, y, h=.02):
    x_min, x_max = x.min() - 1, x.max() + 1
    y_min, y_max = y.min() - 1, y.max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    return xx, yy


def plot_contours(ax, clf, xx, yy, **params):
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    out = ax.contourf(xx, yy, Z, **params)
    return out
    
titles = ('1. SVC with linear kernel',
          '2. SVC with RBF(gamma:10, C:1) kernel',
          '3. SVC with RBF(gamma:0.5, C:1) kernel',
          '4. SVC with RBF(gamma:0.5, C:100) kernel',
          '5. SVC with RBF(gamma:0.5, C:100000) kernel',
          '6. SVC with polynomial (degree 3) kernel')
          
          
models = (svm.SVC(kernel='linear', C=1),
          svm.SVC(kernel='rbf', gamma=10, C=1),
          svm.SVC(kernel='rbf', gamma=0.5, C=1),
          svm.SVC(kernel='rbf', gamma=0.5, C=100),
          svm.SVC(kernel='rbf', gamma=0.5, C=100000),
          svm.SVC(kernel='poly', degree=3, gamma='auto', C=1))

models = (model.fit(X, y) for model in models)

fig, sub = plt.subplots(2, 3, figsize=(20, 10))
xx, yy = make_meshgrid(X[:, 0], X[:, 1])

for model, title, ax in zip(models, titles, sub.flatten()):
    plot_contours(ax, model, xx, yy, cmap="rainbow", alpha=0.5)
    ax.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow", s=20, edgecolors='k')
    ax.set_xlabel('Sepal length')
    ax.set_ylabel('Sepal width')
    ax.set_xticks(())
    ax.set_yticks(())
    ax.set_title("{}:{}".format(title, np.round(model.score(X, y), 3)))

plt.show()

우선 데이터를 Linear Regression을 이용해서 분석해본다. 

 

1. train data/ test data 나누기

from sklearn.model_selection import train_test_split

train_feature, test_feature, train_target, test_target = train_test_split( data, target, train_size =0.7, random_state=42)

len(train_feature), len(test_feature)   # train 데이터 갯수, test 데이터 갯수

여기서 data 에  df["target"]을 제외한 feature 들만 넣어준다.  

 

2. 모델 학습

from sklearn.linear_model import LinearRegression
model = LinearRegression()                      #클래스 선언 
model.fit(train_feature, train_target)          #모델 학습
train_pred = model.predict(train_feature)       # train_feature 넣었을 때의 예측값
test_pred = model.predict(test_feature)       # test_feature 넣었을 때의 예측값

 

3. 학습한 모델 시각화

 

fig, axes =  plt.subplots(nrows =1, ncols =2, figsize =(10, 5))

preds =[
		("Train", train_feature, train_pred), 
        ("Test", test_feature, test_pred)
]

for idx, (name, d, pred) in enumerate(preds):
	ax = axes[idx]
    ax.scatter(x=d[:,0], y=pred)
    ax.axhline(0, color = "red", linestype="--")
    ax.axhline(1, color = "red", linestype="--")
    ax.set_xlabel("mean_radius")
    ax.set_ylabel("predict")
    ax.set_title(f"{name}Data")

 

4. 평가하기. 

from sklearn.metrics import auc, roc_curve

fpr, tpr, threshold = roc_curve(train_target, train_pred)  # 실제값과 예측값을 넣으면 fpr, tpr, threshold가 나옴
auroc = auc(fpr, tpr)   # fpr, tpr 그래프의 아래 면적

plt.plot(fpr, tpr)
plt.xlabel("fpr")
plt.ylabel("tpr")

print(f"AUROC : {auroc:.4f}")    # AUROC 계산값 소수점 4째자리까지 출력

 fpr, tpr, threshold 는 각각 같은 크기의 행렬로 나오는데, 

각각 행렬에서의 같은 위치의 값이 해당 threshold 일 때의 fpr, tpr 이라고 생각하면 된다. 

 

 

5. Best Threshold 찾기

요덴 index 를 이용해서 threshold 책정. 

요덴인덱스 :  TPR - FPR 이 Max 가 되는 target의 인덱스값.

J= tpr - fpr
idx = np.argmax(J)     
best_thresh = threshold(idx)

 

plt.plot(fpr, tpr)
plt.plot(np.linspace(0,1,10), np.linspace(0,1,10))
plt.plot((fpr(idx),fpr(idx) ), (fpr(idx),tpr(idx), color= 'red', linestyle = "--")
plt.xlabel("fpr")
plt.ylabel("tpr")

최대가 되는 지점 (빨간 점선)의 threshold 값이 best threshold 이다. 

 

  

6. 정확도 측정

 

from sklearn.metrics import accuracy_score
linear_train_accuracy = accuracy_score(train_target, train_pred_label)
linear_test_accuracy = accuracy_score(test_target, test_pred_label)

 

 

----------------------------------------------------------------------------------------------------------------------------------------------------

 

 

Logistic Regression을 이용한 예측

 

1. 정규화 (Scaling)   :  logistic regression 은 exp 함수를 쓴다. 변수들이 정규화/scaling 되어있지 않으면 exp 함수에 넣었을 때, 값이 너무 크면 overflow/ 수렴이 잘 되지 않을 수 있다. 

 

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(train_feature)

정규화를 할 때는 모든 데이터를 한번에 학습 (정규화)시키지 않고, train/ test 나눠서 정규화한다. 

한번에 학습할 경우 overfitting이 될 수 있다. 

 

학습해서 만들어진 scaler로 train/test 데이터를 변환한다. 

 

scaled_train_feature = scaler.transform(train_feature)
scaled_test_feature = scaler.transform(test_feature)

 

2. Logistic regression 학습

 

from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(scaled_train_feature, train_target)

 

 

3. 모델을 사용해 예측

 

predict :  어떤 클래스(0/1)에 속할 지 바로 알려줌. 

predict_proba : 각 클래스에 속할 확률 알려줌. 

 

train_pred = lr.predict(scaled_train_feature)
test_pred = lr.predict(scaled_test_feature)

train_pred_logit = lr.predict_proba(scaled_train_feature)
test_pred_logit = lr.predict_proba(scaled_test_feature)

 

4. 모델 평가 : AUROC 계산

 

AUROC를 계산하기 위해서는 1로 분류될 확률만 필요. 

train_pred_logit = train_pred_logit[:, 1]
test_pred_logit = test_pred_logit[:, 1]

from sklearn.metrics import auc, roc_curve

fpr, tpr, threshold = roc_curve(train_target, train_pred_logit)
auroc = auc(fpr, tpr)

 

 

5. Best threshold 계산

J = tpr - fpr
idx = np.argmax(J)
best_thresh = threshold[idx]

 

 

6. Best threshold 로 예측값 0, 1로 변환 후 정확도 확인. 

 

train_pred_label = list(map(int, (train_pred_logit > best_thresh))) # threshold 조정 후의 예측값
test_pred_label = list(map(int, (test_pred_logit > best_thresh))

proba_train_accuracy = accuracy_score(train_target, train_pred_label)  # threshold 조정 후의 accuracy
proba_test_accuracy = accuracy_score(test_target, test_pred_label)


train_accuracy = accuracy_score(train_target, train_pred)   # predict_proba로 측정한 accuracy
test_accuracy = accuracy_score(test_target, test_pred)

 

 

7. 불균형한 이진 데이터 -> Balanced Accuracy

 0/ 1을 분류하는 모델에서, 0에 비해 1이 매우 적은 불균형 데이터의 경우, 

1 예측이 틀리더라도 전체 예측 중에 틀린 1 예측의 비중이 굉장히 낮다. 

따라서 Accuracy를 뽑아봐도 Accuracy는 높게 나온다 -> 모델을 평가하는 지표로 부족하다. 

 

이런 경우 Balanced Accuracy를 사용. 

from sklearn.metrics import balanced_accuracy_score
balanced_accuracy_score(test_target, test_pred)

기존에 공부했던 회귀 분석, Linear Regression, Polynomial Regression은 선형변수에 대한 회귀분석이었다. 

 

독립변수와 종속변수 모두 범주형인 경우 많은 분류모델들이 있지만, 

 

독립변수는 선형변수인데 종속변수는 범주형인 경우는 어떻게 할까.

 

 

Logistic Regression : 종속변수가 0/1 형태의 범주형 데이터일 때 사용. 

 

logistic regression은 단순선형회귀로 해결하기 힘든 모델을 시그모이드 함수 형태로 해결한다. 

Linear Regression
Logistic Function
Linear Regression  + Logistic Function

위와 같은 선형회귀방정식과 로지스틱 함수를 결합한 함수가 Logistic Regression의 함수가 된다.

 

 

 

 

logistic regression의 threshold는 기본적으로 0.5 이며, 

어떤 데이터셋에 대한 Accuracy는 threshold에 따라 변할 수 있다. (threshold가 한쪽으로 치우치면 accuracy를 올릴 수 있다.) 따라서, accuracy는 모델에 대한 평가지표로서 살짝 부족하다. 

 

 

 

Confusion Matrix :  TP, FN, FP, TN 등으로 표현한다. 

TP : 실제값이 1일 때, 예측값도 1인 경우                     FP : 실제값이 0일 때, 예측값이 1인 경우

FN : 실제값이 1일 때, 예측값이 0인 경우                    TN : 실제값이 0일 때, 예측값도 0인 경우

TPR : 진짜 1을 제대로 예측한 경우 / 실제로 1인 경우 (1 중에서 정답의 확률)

FPR : 0인데 1로 잘못 예측한 경우 /  실제로 0인 경우 (0 중에서 오류의 확률)

 

from sklearn.metrics import confusion_matrix

confusion_matrix(train_target, train_pred)


from sklearn.metrics import confusion_matrix, plot_confusion_matrix
import matplotlib.pyplot as plt


plot = plot_confusion_matrix(model_18, # 분류 모델
                             scaled_test_data, test_pred, # 예측 데이터와 예측값의 정답(y_true)
                             #display_labels=label, # 표에 표시할 labels
                             #cmap=plt.cm.Blue, # 컬러맵(plt.cm.Reds, plt.cm.rainbow 등이 있음)
                             normalize=None) # 'true', 'pred', 'all' 중에서 지정 가능. default=None
plot.ax_.set_title('Confusion Matrix')

 

 

 

 

AUROC(Area Under ROC) :  Accuracy의 단점을 보완해주는 평가 지표이다. 

 

여러 Threshold 값들에 대해서 각각 TPR과 FPR을 계산한다. 

AUROC는 각 그래프 아래의 면적을 뜻한다. 

 

from sklearn.metrics import auc, roc_curve

fpr, tpr, threshold = roc_curve(test_target, test_pred_logit)
auroc = auc(fpr, tpr)

plt.plot(fpr, tpr)
plt.xlabel("fpr")
plt.ylabel("tpr")

print(f"AUROC : {auroc:.4f}")

 

 

 

 

Best Threshold : Jodan's index  요덴스 인덱스라고 부름. 

J 값이 최대가 되는 Threshold 가 최적의 threshold가 된다. 

 

 

 

One or Nothing : 범주가 세개 이상일 때 사용. ex) 0, 1, 2, 3   A, B, O, AB

 => A or Nothing, B or Nothing, O or Nothing, AB or nothing

각각의 범주에 대해서 0/ 1 로 분류할 수 있다. 

 

(A or Nothing), (B or Nothing), (O or Nothing), (AB or nothing) 각각에 대한 Logistic Regression을 구한다. 

 

[랜덤 포레스트]

다수의 의사결정나무모델에 의한 예측을 종합하는 앙상블 방법
하나의 의사결정나무모델보다 좋은 예측 성능을 보여줌
관측치에 비해 변수의 수가 많은 고차원 데이터에서 중요 변수 선택기법으로 활용
- 의사결정나무의 단점을 보완한다. 
 
 
 
[의사결정나무의 단점] 
  • 계층적 구조로 중간에 에러가 발생하면 다음단계로 에러 전파
  • 학습 데이터의 미세한 변동에도 최종 결과가 크게 영향
  • 최종 노드의 갯수를 늘리면 과적합의 위험 : Low Bias, Large Variance'

 

[앙상블]

  • 여러 모델의 예측을 다수결 법칙이나 평균을 이용해 모델을 통합해서 하나의 모델로 만드는 방법
  • 여러 모델의 장점을 결합하기 때문에 하나의 모델을 사용하는것보다 성능이 좋아짐
  • 아래의 조건을 만족시켜야 하나의 모델을 사용할때 보다 성능이 우수함
    • Base 모델이 서로 독립적일때
    • Base 모델의 무작위 예측을 하는것보다 모델의 성능이 좋은경우

 - 앙상블 모델의 에러율

- Base 모델의 오류율보다 앙상블 모델의 오류율이 작아지는 0.5 이하의 오류율을 가지는 모델들을 사용해야 한다.

 

 

 

 

 

 

Random Forest 의 절차 : 

  • Bagging : Bootstrap 기법을 이용하여 다수의 train data 생성
  • Random subspace : 무작위로 feature 추출, train data로 decision tree 모델 구축 
  • Ensemble  : 여러개의 모델에 대한 결과를 병합하여 예측

 

 

 

 

* Bootstrapping

  • 샘플링 방법
  • 각 모델은 서로 다른 학습 데이터셋을 이용
  • 각 데이터 셋은 복원추출(sampling with replacement)함
    • 복원추출 : 원래 데이터의 수만큼의 크기로 샘플데이터 생성
  • 개별 데이터셋을 부트스트랩셋이라고 부름

 

* Aggregating  : 각 모델들을 앙상블하는 방법. 

1) Training Accuracy 이용:  각 모델들의 Training Accuracy 를 이용한다. 

 결과가 0으로 추출된 모델들의 Training Accuracy를 모두 더하고, 

 결과가 1로 추출된 모델들의 Training Accuracy를 모두 더해서 

가중평균을 구한다.  (둘의 합 = 1)

train_acc_0 = 0.91 + 0.77 + 0.95 + 0.82   # 0으로 예측한 모델들
train_acc_1 = 0.80 + 0.75 + 0.88 + 0.65 + 0.78 + 0.83 # 1로 예측한 모델들 
train_acc_total = 0.80 + 0.75 + 0.88 + 0.91 + 0.77 + 0.65 + 0.95 + 0.82 + 0.78 + 0.83
train_acc_0 / train_acc_total, train_acc_1 / train_acc_total

0으로 예측한 모델의 가중 평균 = 0.4238  <  1로 예측한 모델의 가중 평균 = 0.5761

==> 앙상블한 결과는 1로 예측

 

 

2) 결과값의 확률값 이용 : 모든 모델에서 각 label의 확률값의 평균으로 결정한다. 

test_instance_0 = 0.1 + 0.08 + 0.13 + 0.66 + 0.59 + 0.16 + 0.86 + 0.68 + 0.02 + 0.43
test_instance_1 = 0.9 + 0.92 + 0.87 + 0.34 + 0.41 + 0.84 + 0.14 + 0.32 + 0.98 + 0.57
np.round(test_instance_0 / 10, 3), np.round(test_instance_1 / 10, 3) # 1로 예측

 

 

*Random Subspace 

 

  • 전체 feature에서 특정 갯수(hyper parameter)개의 변수를 선택
  • Information Gain이 가장 높은 변수 하나를 선택해서 노드를 생성
  • 위의 방법을 반복적으로 수행해서 트리를 형성(hyper parameter:tree의 level)
  • 전체 feature에서 선정된 특정갯수의 feature로만 트리를 생성함
  • hypter parameter
    • n_estimators : 트리의 갯수 : 많을수록 과적합을 피할수 있다 : 2000개 이상
    • max_depth : 트리의 레벨
    • max_features : 최대 변수의 갯수

 

from sklearn.datasets import make_moons
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import mglearn
# 샘플 데이터
X, y = make_moons(n_samples=100, noise=0.25, random_state=3)

# 데이터셋 분리 : stratify : y 데이터의 비율을 맞춤
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)

# 모델 생성 및 학습
model = RandomForestClassifier(n_estimators=5, random_state=2).fit(X_train, y_train)
fig, axes = plt.subplots(2, 3, figsize=(20, 10))

for i, (ax, tree) in enumerate(zip(axes.ravel(), model.estimators_)):
    ax.set_title("tree {}".format(i))
    mglearn.plots.plot_tree_partition(X, y, tree, ax=ax)

mglearn.plots.plot_2d_separator(model, X, fill=True, ax=axes[-1, -1], alpha=.4)
axes[-1, -1].set_title("random forest")
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)

plt.show()

 

 

*train_test_split 

Train set 과 Test set 을 분류 -> 모델 overfitting 방지. train set으로 학습 후 test set 으로 validation 해준다. 

 

  • test_size: 테스트 셋 구성의 비율을 나타냅니다. train_size의 옵션과 반대 관계에 있는 옵션 값이며, 주로 test_size를 지정해 줍니다. 0.2는 전체 데이터 셋의 20%를 test (validation) 셋으로 지정하겠다는 의미입니다. default 값은 0.25 입니다.
  • shuffledefault=True 입니다. split을 해주기 이전에 섞을건지 여부입니다. 보통은 default 값으로 놔둡니다.
  • stratifydefault=None 입니다. classification을 다룰 때 매우 중요한 옵션값입니다. stratify 값을 target으로 지정해주면 각각의 class 비율(ratio)을 train / validation에 유지해 줍니다. (한 쪽에 쏠려서 분배되는 것을 방지합니다) 만약 이 옵션을 지정해 주지 않고 classification 문제를 다룬다면, 성능의 차이가 많이 날 수 있습니다.
  • random_state: 세트를 섞을 때 해당 int 값을 보고 섞으며, 하이퍼 파라미터를 튜닝시 이 값을 고정해두고 튜닝해야 매번 데이터셋이 변경되는 것을 방지할 수 있습니다.

Decision Tree (의사결정나무) :  변수들로 기준을 만들어 샘플을 분류하고 분류된 집단의 성질로 추정하는 모형

장점 : 해석력이 높음
단점 : 변동성이 높음(샘플이 조금만 추가되더라도 기준이 잘 바뀜)
 

 종속변수의 종류가 범주형이면 분류, 수치형이면 회귀 분석을 이용한다. 

 

 

 

*Entropy 엔트로피 : 의사결정나무의 수학적인 개념.

엔트로피 함수는 아래와 같다. 

 
클래스가 2개인 데이터를 하나 뽑았을때 클래스 1이 나올 확률이 0.5일때 엔트로피값은 1을 갖는다. 잘 퍼져있다. 
엔트로피가 1이라는 의미는 데이터가 잘 섞여 있다는 의미
 
예를 들어 아래와 같이 모든 결과가 동일한 종속변수 값 'Yes'를 갖는다면, 엔트로피값은 한쪽에만 치우쳐있으므로 0이다. 
'Yes', 'No' 의 결과 수가 동일하다면 엔트로피는 1의 값을 갖는다. 

 

의사결정나무를 통과하기 전에는 엔트로피가 1이지만, 의사결정나무를 통과하게 되면 조건에 따라 종속변수가 종류별로 분리되므로 엔트로피는 0에 가까워진다.  엔트로피가 0에 가까울 수록 잘 분리됐다고 본다.  

 

 

 

 

Information Gain : 의사결정나무의 성능 체크

샘플 데이터가 위와 같다고 할 때, 의사결정나무를 통과하기 전의 Yes의 확률 p = 9/14 이다. 

엔트로피 공식에 넣으면,

before_entropy = -(9/14) * np.log2(9/14) - (1 - (9/14)) * np.log2(1 - (9/14))
before_entropy

분기점을 통과하기 전의 엔트로피 값은 약 0.94가 나온다. 

 

첫번째 분기점을 지나고 난 후의 확률은 각각, 

Yes :4, Yes:2 NO: 3, Yes:3 No:2 으로 , 

1, 0.4, 0.6 이다. 

 

엔트로피는 각각 계산한 후, 가중평균을 이용해서 구한다.

 

 

after_entropy_1 = 0
after_entropy_2 = -0.4 * np.log2(0.4) - (1 - 0.4) * np.log2(1 - 0.4)
after_entropy_3 = -0.6 * np.log2(0.6) - (1 - 0.6) * np.log2(1 - 0.6)
after_entropy = (4 * after_entropy_1 + 5 * after_entropy_2 + 5 * after_entropy_3) / (4 + 5 + 5)

after_entropy_1,2,3은 각각 (0, 0.9709505944546686, 0.9709505944546686)

가중평균의 엔트로피 after_entropy = 0.6935361388961919

 

information_gain = before_entropy - after_entropy

 

[노드를 설정하는 방법]

  • information gain이 가장 높은 변수를 root 변수로 설정
  • 나머지 변수들을 information gain을 구하고 information gain이 가장 높은 변수를 다음 노드로 설정

 

 

 

 

 

 

 

 

[Iris 예제]

 

sklearn datasets에 내장되어있는 iris 데이터를 불러온다. 

from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
iris = datasets.load_iris()

 

 

 

feature와 target 이 이미 나뉘어 있다. 

iris data를 보기 쉽게 데이터 프레임 형태로 변환하면,

import pandas as pd
df = pd.DataFrame(iris.data, columns=iris.feature_names)

 

 

target 값들은 나오지 않는데, target 컬럼을 아래와 같이 추가한다.  

df['label'] = iris.target
df.tail()

 

 

 

[모델의 학습]

model = DecisionTreeClassifier(max_depth=2, random_state=1).fit(iris.data, iris.target)

max_depth :  몇번 분할할 것인지. 너무 많이 분할하면 overfitting 된다. 

 

 

 

 

 

[모델 시각화]

 

from sklearn.tree import export_graphviz
export_graphviz(model, out_file="tree.dot", class_names = iris.target_names, 
                           feature_names = iris.feature_names, impurity=True, filled=True)
import graphviz
# 위에서 생성된 tree.dot 파일을 Graphiviz 가 읽어서 시각화
with open("tree.dot") as f:
    dot_graph = f.read()
graphviz.Source(dot_graph)

※ 지니계수 : 불순도를 나타내는 지표. value = [ ] 로 주어진 데이터 분포에서의 지니계수.  

 

 

 

 

[모델 평가]

Confusion Matrix 

 

True Positives : 1인 레이블을 1이라 하는 경우를 True Positives라고 한다. -> 관심 범주를 정확하게 분류한 값.

False Negatives : 1인 레이블을 0이라 하는 경우를 False Negatives라고 한다. -> 관심 범주가 아닌것으로 잘못 분류함.

False Positives : 0인 레이블을 1이라 하는 경우를 False Positives라고 한다. -> 관심 범주라고 잘못 분류함.

True Negatives : 0인 레이블을 0이라 하는 경우를 True Negatives라고 한다. -> 관심 범주가 아닌것을 정확하게 분류.

 

TP/ TN 이 높고 FN/FP가 낮은게 좋음. 

from sklearn.metrics import confusion_matrix
confusion_matrix(iris.target, model.predict(iris.data))

디시전트리에 의한 TP값은 49, TN은 45이다. 

 

 

model.score(iris.data, iris.target)

 

머신러닝 모델의 분류

 

- 수치형 데이터 예측 : 회귀모델 : 지도학습 : target 이 있음
- 범주형 데이터 예측 : 분류모델 : 지도학습 : target 이 있음
    - 나이브베이즈 : 텍스트 데이터 분류
    - SVM : 이미지, 음향
    - 디시전트리(랜덤포레스트) : 그외의 경우
- 군집분석 : 비지도학습 : target 이 없음

 

--------------------------------------------------------------------------------------------------------------------

 

 

 

앞서 살펴본 회귀분석은 수치형 데이터를 분석하는데 사용했었다. 

범주형 데이터 중 텍스트 데이터 분류에 용이한 나이브 베이지안 모델을 알아본다. 

 

 

나이브 베이즈 : Naive Bayes

  • 특성들 사이의 독립을 가정하는 베이즈 정리를 적용한 확률 분류기

 

 

[나이브 베이즈 방식의 종류 ]

  • GaussianNB : 정규분포
    • 독립변수가 연속적일때 사용
    • 독립변수가 정규분포일때 사용
  • BernoulliNB : 베르누이분포
    • 독립변수가 범주형인경우 사용
    • 종속변수가 0 또는 1의 두가지를 갖는 이산형인 경우 사용
  • MultinomialNB : 다항분포
    • 독립변수가 범주형인경우 사용
    • 종속변수가 여러개의 범주를 갖는 이산형인 경우 사용

 

 

 

 

특성들 사이의 독립이란 아래와 같은 형태를 말한다. 

예를 들어 A가 주소라고 가정하면, 

서울이거나 부산이거나 수원이거나 성남이거나, 

사람마다 무조건 A 가 한개 있지만 여러개가 있거나 겹치지 않는다. 

 

베이지안 정리는 이와 같은 서로간에 독립인 특성들에 대하여,

  • 이전의 경험과 현재의 증거를 토대로 어떤 사건의 확률을 구하는 방법
  • 조건부 확률을 이용하여 사전확률과 사후확률을 계산한다.

 

 

조건부 확률 : 사건 A가 발생했다는 조건 하에 B가 발생할 확률 

 

 

조건부 확률과 베이즈 정리의 관계

 

직접 나누어서 확률을 계산하는 방식은 귀찮으니까 패스하고, 

바로 sklearn의 모듈 naive_bayes를 사용해보도록 한다. 

 

 

[테니스 예제]

사용해볼 데이터는 날씨, 습도에 따른 테니스 play 여부이다. 

 

 

 

 

 

 

모듈 import

from sklearn.naive_bayes import GaussianNB

 

 

데이터 전처리

 

*더미변수화 : pd.get_dummies[ df["column_name"]]

날씨와 습도를 보면, 간편하게 1, 0으로 되어있는 것이 아닌, 문자로 되어있다. 

이를 사용하기 쉽게 'Sunny', 'Rainy', 'Overcast', 'High', 'Normal' 을

각각의 컬럼으로 만들고 1/0 이진수로 표현하도록 해주는 것을 더미변수화라고 한다. 

outlook = pd.get_dummies(tennis["Outlook"])
humidity = pd.get_dummies(tennis["Humidity"])
feature = pd.concat([outlook, humidity], axis=1)
target = tennis["PlayTennis"].map({'Yes': 0, 'No': 1})

get_dummies 함수를 사용하여 정의해준 outlook, humidity는 각각 더미 변수를 컬럼으로 하는 새로운 데이터 프레임이 된다. 

 

* .concat : 컬럼을 합쳐준다. 

* .map( )  : 더미변수화는 각 요소들을 컬럼으로 만들어주는 반면, 

                 map은 컬럼은 기존 모컬럼 그대로 두고 요소에 따른 값만 맵핑해준다. 

 

 

 

 

모델 학습

 

sklearn의 가우시안 나이브 베이지안 함수를 이용한다. 

model_1 = GaussianNB().fit(feature, target)

 

 

 

예측 데이터

 

GaussianNB() 클래스는 .predict 라는 메서드를 갖고 있다. 

.predict는 .fit() 메서드를 통해 만들어진 모델의 예측값을 반환하는 메서드이다.  

pred_1 = model_1.predict(feature)

 

 

모델 평가

 

전체 경우의 수 중에 예측값과 실제값이 같을 확률이 곧 해당 모델의 정확도가 된다. 

 

정확도 [%] = (예측값=타겟값 인 데이터의 갯수)   /  (전체 타겟의 갯수) * 100 

len(pred_1[pred_1 == target.values]) / len(target)

 

확률이 구리다. 

이 케이스는 종속데이터가 0/1로 나오고, 종속/독립 모두 범주형이니, 베르누이나 Multinomial 나이브 베이즈를 쓰는게 맞다. 

 

 

 

 

 

[Multinomial NB] : 가우시안이랑 모듈만 다르고 방법은 똑같다. 

 

from sklearn.naive_bayes import MultinomialNB
# 모델 학습
model_2 = MultinomialNB().fit(feature, target)
# 예측
pred_2 = model_2.predict(feature)
# 평가 : GaussianNB 보다 성능이 좋다. : 독립변수가 정규분포가 아니기 때문에
len(pred_2[pred_2 == target.values]) / len(target)

 

 

새로운 데이터를 주고 모델을 통해 예측을 해보자. 

 

입력데이터는 다음과 같은 형태이구나 파악. 

결과("Play Tennis")를 제외한 평가용 샘플데이터를 만든다. 

sample = np.array([1, 0, 0, 0, 1])

 

 

 

 

MultinomialNB 클래스의 메서드인 .predict_proba 를 이용해 확률을 구한다. 

.predict와의 차이점은 .predict는 예측에 의해서 확률이 가장 높은 값을 반환하는 것이고, 

.predict_proba 는 가능한 결과값들에 대한 확률을 반환한다. ( target의 인덱스 순서대로 확률이 나타남.) 

안의 feature는 벡터값인 [sample]을 넣어준다.

 

(Jupyter notebook은 Shift+Tab 을 하면 설명을 볼 수 있다. )

proba = model_2.predict_proba([sample])[0]
result = model_2.predict([sample])[0]
proba, result

 

 

 

 

다항 회귀할 때 봤던 것 처럼, 

predict_proba([sample]) 는 벡터, 

proba = predict_proba([sample])[0] 은 스칼라 값이다. 

위에서 target = tennis["PlayTennis"].map({'Yes': 0, 'No': 1}) 라고 정의했기 때문에, 

맵핑된 순으로 Yes(0): 93%,  No(1)가 6.9% 이다. 

 

 

피곤하니까 모델 평가는 다음에

다항회귀분석 (polynomial regression) : 2차 이상의 관계식

이런 관계식에 대해서 연습해볼 sample data를 생성해보자. 

샘플데이터는 랜덤함수를 이용해서 feature 값들을 먼저 생성해주고,

위의 수식에 랜덤함수를 이용한 오차를 더해서 y (target) 값을 만들어준다. 

 

이 식을 풀기 위해서 앞서 공부했던 선형회귀분석을 해보고, 그 모델을 평가해볼 것이다. (오차가 당연히 클 것.)

그 다음 다항회귀 분석을 통해서 모델을 만들고 결과를 분석해보자. 

결론부터 말하자면, 다항회귀분석이라고 해서 다른 모듈을 사용하는게 아니라

선형회귀분석과 동일한 모듈을 사용한다.  (OLS, LinearRegression 등)

 

다만, feature를 polynomial로 바꿔주는 절차가 필요하다

 

 

 

1. 샘플 데이터 생성. 

m = 100
np.random.seed(0)
x = 6 * np.random.rand(m, 1) - 3
y = 0.5 * x ** 2 + x + 2 + np.random.randn(m, 1)

x는 랜덤함수로 뽑은 100개의 난수.

y 는 0.5 X^2+X+2+  0~1 사이의 난수 오차 100개.

 

plt.scatter(x, y)
plt.show()

오차를 입력했기 때문에 정확한 2차함수그래프가 아니라 scattered 된 plot을 볼 수 있다. 

 

만들어진 샘플을 분석해보자. 

 

 

 

 

 

 

2. 만들어진 샘플로 모델을 학습시킨다. 

import statsmodels.api as sm
from sklearn.metrics import mean_absolute_error

# 상수항 추가
feature = sm.add_constant(x, has_constant='add')
x[:2], feature[:2]

상수항 추가를 해주면 다음과 같이 맨 앞열에 1이 들어간다.

(statsmodel로 회귀분석할 때 해줘야 함. sklearn은 알아서 1 붙여서 계산함.)

OLS - 최소제곱법을 통해 모델을 만든다. ( 일단 선형회귀분석을 해서 상태를 본다;)

# 모델 학습
model_1 = sm.OLS(y, feature).fit()

# 예측값 구하기
pred_1 = np.dot(feature, model_1.params)

아까 만들어 놓은 샘플데이터의 y값을 학습에 사용한다. (오차를 포함한 raw data)

feature 값과, 모델의 구해진 파라미터 값을 내적해 모델을 통해 예측한 pred_1 값을 구한다.

 

 

 

3. 샘플 데이터(정답)와 모델로 예측한 추세선을 비교해본다.  

 

plt.scatter(x, y) # 실제 데이터
plt.plot(x, pred_1, "r") # 예측 데이터 추세선
plt.show()

많이 안맞아 보인다 ..

 

 

4. 선형회귀분석을 통해 도출된 모델을 평가해보자. 

 

model_1.params, mean_absolute_error(pred_1, y)

 

 

 - sklearn을 통해 학습시켜도 결과는 똑같다.  (참고)

from sklearn.linear_model import LinearRegression

# 모델 학습
model_2 = LinearRegression().fit(x, y)

# 예측값 구하기
pred_2 = model_2.predict(x)

# 그래프 그리기
plt.scatter(x, y) # 실제 데이터
plt.plot(x, pred_2, "r") # 예측 데이터 추세선
plt.show()

# 회귀계수 출력, 모델 평가(MAE)
model_2.intercept_, model_2.coef_, mean_absolute_error(pred_2, y)

 

 

 

 

 

 

---------------------------------------------------------------------------------------------------------------------------------------

5. 다항회귀분석을 이용해보자. 

 

1)  ( x  x^2 ) 형태의 array를 만들어준다.  

b0+b1x + b2x^2 ..  를 행렬로 만들어야하니,    (전에 봤던 행렬 연산식을 떠올려 본다. )

# 2차 방정식으로 만들기
x[:3], x[:3]**2, np.c_[x[:3], x[:3]**2]

* np.c_ : 컬럼을 축으로 행렬을 합친다. 

* np.r_ : row를 축으로 행렬을 합친다. 

 

 

 

2) sklearn - PolynomialFeatures 함수를 이용해 다항식 형태의 feature를 만들어준다. 

# feature값 행렬 데이터로 변경
from sklearn.preprocessing import PolynomialFeatures

# 모델의 학습은 선형대수로 계산이 되기 때문에 제곱한 feature를 추가
poly_features = PolynomialFeatures(degree=2, include_bias=False) # 2차방정식
x_poly = poly_features.fit_transform(x)

np.hstack([x[0], x[0]**2]), x_poly[0]

 

PolynomialFeatures ( degree=n ,  include_bias= True/False)   :

다항식으로 변환시켜주는 변환함수를 생성해서 "poly_features"라는 객체를 정의해준다. 

degree: 몇차식으로 변환시켜줄지, include_bias =True 면 상수항 포함, False면 상수항 미포함. 

include_bias=True 이면 다음과 같이 앞에 상수항이 포함된다. 근데 statsmodels를 쓸때나 필요하지, sklearn에서는 상수항 안만들어줘도 되니 지금은 False로 사용. 

 

poly_features.fit_transform(x)  : 위에서 100개의 난수를 만들어 줬던 feature x 를 이용해 

(x x^2)  형태의 매트릭스를 만들어준다. 

 

 

 

 

 

 

3) sklearn 을 이용해 모델 학습

 

-똑같이 LinearRegression() 함수를 사용한다.  (feature 가 다항형태인 x_poly로 바뀌었을 뿐)

 

*reshape(a,b) : axb 형태로 바꿔준다.  a나 b에 -1이 들어가는 경우가 있는데, 특정 차원을 기준으로 재배열하고 싶은 행이나 열의 개수가 있으면 나머지 차원의 개수는 '-1'로 해두면 알아서 자동으로 재배열을 해준다. 

위의 경우에 x가 (100 x 1) 형태였는데 x.reshape(1, -1) 해주면 1행 ? 열로 알아서 배열을 해준다 .

데이터가 100 개이니 ?는 100으로 자동으로 설정된다. 

 

- 예측된 x, f 값을 나타내는 데이터프레임 df를 만든다.

 

- x.reshape(1,-1) 와 x.reshape(1,-1)[0] 를 돌려보면 사실 출력되는 값은 [[  ]], [ ]   의 차이이다. (벡터, 스칼라)

그러나 우리는 x.reshape(1,-1)[0] 를 사용해야하는데, 이유는  x.reshape(1,-1) 는 행과 열이 있는 벡터 자체를 말하고, x.reshape(1,-1)[0] 그 안에 들은 스칼라 값을 뜻하기 때문이다.

 

plot을 그리기 위해서, x와 pred_4의 스칼라값이 필요한거지, 벡터가 필요한게 아니다. 

 

-plot을 그려본다 (추세선)

- 샘플데이터 y 와 만들어진 모델의  MAE를 구해, 모델의 성능을 확인해본다. 

 

from sklearn.linear_model import LinearRegression

# 모델 학습
model_4 = LinearRegression().fit(x_poly, y)

# 예측값 구하기
pred_4 = model_4.predict(x_poly)

# 그래프 그리기
plt.scatter(x, y) # 실제 데이터

# 예측 데이터 추세선
# plt.scatter(x, pred_4)
df = pd.DataFrame({"x": x.reshape(1, -1)[0], "pred_4": pred_4.reshape(1,-1)[0]}).sort_values("x") # 정렬
plt.plot(df["x"], df["pred_4"], "r")

plt.show()

# 회귀계수 출력, 모델 평가(MAE)
model_4.intercept_, model_4.coef_, mean_absolute_error(pred_4, y)

 

 

 

 

< 다항식의 차수 정하기 >

사실 

2) sklearn - PolynomialFeatures 함수를 이용해 다항식 형태의 feature를 만들어준다. 

를 하기 전에 다항식의 차수 먼저 구했어야 한다. 

 

어쨌든 차수를 구하는 방법을 알아보자면, 

1~9차까지 모델을 다 만들고, 차수에 대한 MAE 그래프를 그려본다. 

 

'degree' 차 다항식이라고 가정했을 때의 모델들을 각각 만들고 MAE를 반환하는 'get_mae' 함수를 만든다. 

def get_mae(degree):
    
    # degree 차원으로 feature를 만듦
    poly_features = PolynomialFeatures(degree=degree, include_bias=False)
    x_poly = poly_features.fit_transform(x)
    
    # 모델 학습
    model = LinearRegression().fit(x_poly, y)
    
    # 데이터 예측
    pred = model.predict(poly_features.fit_transform(x))
    
    # MAE 구함
    return mean_absolute_error(y, pred)

 

x = 차수, y = MAE  로 plot을 그린다. 

 

degrees = range(1, 10)
maes = [get_mae(degree) for degree in degrees]
plt.plot(degrees, maes)
plt.show()

 

 

MAE가 급격하게 낮아지는 차수를 책정하면 된다.

 

 

 

 

-----------------------------------------------------------------------------------------------------------------------------------

* np.random  : 랜덤 패키지

- np. random.rand( ) :  0~1까지의 균일분포 내에서 난수 생성.

  .rand( i )  : i개의 난수 생성. 

  .rand(m, n) : m x n  매트릭스에 생성됨. 

 

- np.random.randint() : 균일 분포의 정수 난수 생성. 

  .randint(i ) :  i개의 난수 생성. 

  .randint(m, n) : m에서 n-1 (half-closed) 까지 중 난수 1 개 생성. 

 

-np.random.randn : 평균 0, 표준편차 1인 가우시안 정규분포 내에서 난수 1개 생성.   

 

 .randn(i) : i개 생성. 

 .randn(m, n) : m x n 매트릭스 어레이 생성. 

 

-np.random.seed() :  다른 랜덤함수들은 실행시마다 다른 값을 나타내지만, seed(i) 를 사용하면, i 값을 같은 값을 넣는 한, 같은 랜덤값이 추출된다. 

추출한 랜덤값을 유지하고 싶을 때 사용. 

----------------------------------------------------------------------------------------------------------------------------------------------

 

(복합 화력발전소) . 07.multiple regression example 2

 

 

df = 복합화력발전소에 대한 데이터 


<sklearn을 이용한 모델링 >

feature = df[['AT', 'V', 'AP', 'RH']]
target = df["PE"]
from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(feature, target)    # 선형 모델 학습 (생성)
from sklearn.metrics import mean_absolute_error
pred = model.predict(feature)
mae = np.round(mean_absolute_error(target, pred), 2)   # 모델 성능 확인.



<statsmodel을 이용한 모델링 >

 

import statsmodels.api as sm
feature = df[['AT', 'V', 'AP', 'RH']]
target = df["PE"]
sm_feature = sm.add_constant(feature)  # add constant 해줘야함. sklearn에서는 필요없음.

model = sm.OLS(target, sm_feature).fit()     # 선형모델 학습(생성)


print(model.summary2())    # 모델 성능 확인.




  <불필요한 컬럼(feature)들 제거>  
- feature 전체 넣고, 하나씩 빼고 각각 모델 만든다음 VIF 측정해본다. 높은데 영향주는 feature는 제거. 

feature = df[['AT', 'RH']]
target = df["PE"]
sm_feature = sm.add_constant(feature)

model = sm.OLS(target, sm_feature).fit()

print(model.summary2())

pred = np.dot(sm_feature, model.params)
mae = np.round(mean_absolute_error(target, pred), 2)

다중선형회귀 : 여러개의 변수(feature)가 target 영향을 주는 경우.

 

선형모델을 만드는 방식은 단순 선형 회귀와 똑같다.  statsmodel이나 sklearn을 쓰면 되는데, 다만 여러개의 변수 중 어떤 변수들을 사용할 지를 결정해줘야 한다. 

 

 

행렬로 나타내 보면 대충 아래와 같다. 

여기서 우리가 구해야하는 것은 베타행렬, 이를 구하려면 x  에 대한 역행렬을 양쪽에 곱해줘야하는데, 

역행렬을 구하는 공식을 보면, 분모 ad-bc가 0 이 되어버리면 값을 구할 수 없다. 

즉, feature 끼리 값이 비슷하면 역행렬이 발산하게 되어 베타 행렬이 의미가 없어진다. 

 

이를 독립변수들 사이의 다중공선성이라고 하고, 다중공선성이 높은 feature들끼리 사용하면

원하는 값을 얻을 수 없다. 

 

다중공선성 : feature끼리의 상관계수가 높으면 같이 feature로 쓸 수 없다. 

따라서, feature 선정 시, feature들 끼리의 다중공선성, 즉 상관계수, 결정계수를 반드시 확인한다. 

 

 

 

 

 

 

 

다중공선성을 확인하기에 앞서서 중요한 개념인 상관계수와 결정계수를 알아보자. 

 

상관 계수 R : -1~1 사이의 수로 변수 사이의 관계를 나타낸다. 주로 feature와 target 사이의 관계를 나타낸다.

1에 가까울 수록 양의 상관도가, -1에 가까울 수록 음의 상관도가 높다. 

아래 두개의 함수 모두 기능은 동일하다. 

 

.corr()   : 일반 함수

np.round(df[["gf", "ga", "points"]].corr(), 2)

np.corrcoef(target, feature)[0,1]   : Numpy 함수

np.corrcoef(point, gf)[0,1]


np.corrcoef 는 [1,  x], [x, 1] 형태의 2x2 행렬로 나온다. 그래서 [0,1] 이나 [1,0] 이 결과 값이다.

 



결정계수 R^2 : 상관계수의 제곱  (0~1)     . . 그냥 상관계수를 제곱하면 된다. 


.corr()**2

 

np.round(df[["gf", "ga", "points"]].corr()**2, 2)


np.corrcoef () [0,1]**2

np.corrcoef(point, gf)[0,1]**2

 

model.rsquared

 

statesmodel로 model을 생성했다면, model.rsquared 로 결정계수를 구할 수 있다. 

 

 

 

 

개념을 알았으니,

다중공선성을 확인하는 방법을 마저 알아보자. 

 

i)  scatter plot 그려서 그림으로 확인. (모델 생성 전)
 - (feature1, target), (feature2, target) .. 간의 plot을 그려 비교.  

 

*scatter(x, y, label="x")

plt.scatter(df["TV"], df["sales"], label="TV")
plt.scatter(df["radio"], df["sales"], label="radio")
plt.scatter(df["newspaper"], df["sales"], label="newspaper")
plt.legend()
plt.show()


스캐터를 보니 radio와 newspaper는 패턴이 거의 같다. 

sales에 대한 radio의 함수,  newspaper의 함수가 거의 똑같이 생겼다고 추측할 수 있다. 

= 다중공선성이 높다. 

따라서, 한개만 feature 로 채택한다. 

 

 


2. heatmap 그려서 확인 : feature들끼리의 결정계수/ 상관계수를 히트맵으로 확인. (모델 생성 전)

 

target을 제외한 나머지 변수들의 계수, 

상관계수의 제곱인 결정계수 

 

sns.heatmap(df.drop(columns=["sales"]).corr(), annot=True)
plt.show()

sns.heatmap(df.corr() ** 2, annot=True)
plt.show()



3. VIF : Variance Inflation Factor. 를 계산해본다. (모델 생성 전 )
분산 팽창 요인. (역행렬 연산 시 1/(ad-bc) 로 연산을 하는데 다중 공선성이 높으면 ad-bc =0 이 되어 분모가 0, 발산하게 된다. ) VIF 지표가 높을 수록 다중공선성이 높다고 판단. 일반적으로 VIF가 10 이상인 경우, 다중공선성으로 판단. (절대적이지는 않음) 

특정 feature를 y값으로 설정하여 모델을 만들었을때 나오는 결정계수(r-squared)로 아래의 수식을 연산한다. 

VIF 구하는 방법 
1) 직접 계산 

feature 끼리 돌아가면서 target으로 설정하고 나머지를 feature로 삼아 모델을 생성했을 때, 
VIF 값을 측정한다. 

 

import statsmodels.api as sm
feature = features_df[["radio", "newspaper"]]
target = features_df["TV"]
model = sm.OLS(target, feature).fit()
1 / (1 - model.rsquared)

 

-> VIF Factor of TV
target으로 삼은 녀석의 VIF 값. 높으면 사용하지 않는게 좋다. 
, radio, newspaper에 대해서도 각각 진행. 

2) 함수로 확인. 

statsmodels에 이미 VIF를 구하는 함수가 있다. 

 

variance_inflation_factor(  feature.values,  feature index  )

ex) ["TV", "Radio","Newspaper"]  

feature index

0 : TV, 

1: Radio

2 : Newspaper

from statsmodels.stats.outliers_influence import variance_inflation_factor

feature = df[ ['', '' ] ]
target = df [ ]
pd.DataFrame({
    "feature": feature.columns,
    "VIF": [variance_inflation_factor(feature.values, idx)
            for idx in range(feature.shape[1])]
})

 

 

 

 

 



4. (모델 생성 후) summary 참조. (05. linear_regression_summary_table)

***summary에서 참고할 만한 수치 : 


Coef : 회귀계수 . 기울기에 해당.  0에 가까울 수록 상관도가 낮다 (target과의). 너무 작은 애는 쓸모없다. 
R-squared : 모델의 결정계수 : 높을수록 좋다. 분산설명력. 
p-value = P> |t| : 우연에 의해 나올 확률 . 보통 0.05 보다 높으면 안좋다고 본다. 5%. 
t = t test : (two sample test) : 회귀계수가 우연인지 확인하는 지표. 0과 가까울 수록 우연일 확률이 높다. 작은게 좋음. 


model.summary()

model.summary2()

feature_1 = sm.add_constant(df[["TV", "radio", "newspaper"]])
model_1 = sm.OLS(df["sales"], feature_1).fit()
print(model_1.summary2())

전체 feature를 넣고 만든 모델, 한개의 메체를 빼고 나머지를 feature로 삼았을 때의 모델들을 모두 생성한다.

 

그리고 각각의 P-Value, MAE 를 확인해본다. 

 

np.round(mean_absolute_error(df["sales"], pred_1), 3)
np.round(mean_absolute_error(df["sales"], pred_2), 3)


.. 모델의 성능이 뭐가 좋은지도 확인해볼 수 있음. 

 

P value 값이 낮으면서 MAE가 높은 모델을 선택한다. 

여기서는 단순 선형회귀란 무엇인지, 

풀기위한 방법들에는 어떤 것들이 있는지 다뤄본다. 

(statsmodel, OLS, OLS 안쓰고 풀기, 경사하강법, 등)

 

 

 단순 선형 회귀 (simple linear regression) :


추세선이 평균으로의 회귀라고 해서 회귀 분석이라고 한다

 

독립변수 x, 종속변수 y 의 관계를 알아본다고 하자. 

 

x, y 의 상관관계가 

 

 y= a+bx  와 같이 일차식으로 나타날 때, 이를 단순 선형 회귀라고 한다. 

 

 

 

이 식의 추세선을 구하는 방법: 


1. 방정식, 선형대수(행렬) ->  손으로 계산하는 경우


2. 최소제곱법 ->  완벽히 직선상에 있지 않을 경우 사용.

주어진 점들과의 오차의 제곱이 최소가 되는 추세선을 구한다. 
 ( python에서는 statsmodel 혹은 Scikit Learn을 이용한다. )

 1) 샘플 데이터(학습시킬 raw data, 주어진 점들)를 데이터프레임으로 만든다 . 
 2) 산점도를 그려 추세를 파악 ( plt.scatter(df['x'], df['y'])  plt.show() )
 3)  y = ax +b +e (모든 점들이 한 직선 상에 있지않을 경우 오차가 있음. )라고 놓고 sklearn이나 statsmodel을 사용해 parameter값들을 구한다. 

 

*statsmodels : 추정 및 검정, 회귀분석, 시계열분석 등의 기능을 제공하는 파이썬 패키지.

여기에 최소제곱법을 이용해 추세선을 구하는 코드(OLS)가 내장되어있다. statsmodels에는 아래와 같은 기능들이 있다. 

예제 데이터셋
검정 및 모수추정
회귀분석
선형회귀
강건회귀
일반화 선형모형
혼합효과모형
이산종속변수
시계열 분석
SARIMAX 모형
상태공간 모형
벡터 AR 모형
생존분석
요인분석 ... 

------------------------------------------------------------------
최소제곱법(OLS)을 이용해서 추세선 구하기 
 OLS(OLS: Ordinary Least Squares), 최소제곱법, 또는 최소자승법, 최소제곱근사법, 최소자승근사법(method of least squares, least squares approximation)이라고도 하며, 어떤 계의 해방정식을 근사적으로 구하는 방법으로서 근사적으로 구하려는 해와 실제 해의 오차의 제곱의 합이 최소가 되는 해를 구하는 방법이다. 


import statsmodels.api as sm
ols('종속변수명 ~ 독립변수명', data=데이터프레임명).fit().summary()



statsmodels를 사용할 때는 feature, target, model을 지정해줘야함. 
feature는 입력시켜 줄 학습값 중 독립변수 (x), 

target은 종속변수 (y)
model 은 statsmodel 내의 여러가지 방법 중에 어떤 함수를 쓸 것인지. (여기서는 ols)


OLS, 최소제곱법 역시 선형대수를 기반으로 하기 때문에, 행렬의 연산 시 상수항 1을 맨 앞열에  독립변수 데이터에 추가해줘야한다.  (상수항 결합). 

 


feature는 input x를 전처리해준거라고 생각하면 된다. 

 

 

[OLS 를 이용해서 추세선 구하기 코드]

 

x,y 정의

datas = np.array([[1, 3], [2, 5], [3, 7]])
df = pd.DataFrame(datas, columns=list("xy"))

 

feature = sm.add_constant(df["x"], has_constant='add')
model = sm.OLS(df["y"], feature).fit()
model.params



 sm.add_constant : 상수항 결합을하는 함수. 

model = sm.OLS( 종속변수,독립변수).fit()    :OLS 모델을 돌린다. 
model.params : 돌린 모델에서 나온 결과값. 파라미터값을 출력한다. 

 

const, x가 나오는데 이게 가중치 벡터이다. (구하는 해)  

>>

const    3.0 (y절편 상수)
x       -1.0

 

y= -x+3 이라는 뜻.  아래의 f를 구한 셈이다. 

 

input   model   output

  x    ->     f       ->  y

 

이제 어떤 x가 들어왔을 때 y 값이 어떻게 나타날지 예측해보자. 

 

x가 f를 거치는 과정은 벡터의 내적으로 나타낼 수 있다. 

백터내적함수 np.dot( x, f) = y  
가중치 벡터를 구했으면 x와 내적을 해야 y 값을 구할 수 있다. 


predicted_data= a+bx (구한 추세선 함수식) 라고 할 때,

 

predicted_data= np.dot( feature, model.params)

 

 

-------------------------------------------------------------------------------------------

 

* 참고 ) OLS의 원리
- 모델을 사용해 쉽게 추세선을 구했지만, 

  이 모델은 어떤 원리인지 알아보자.

 

 

OLS 함수 안쓰고 a,b 찾기 1.   : 오차제곱법 활용

 

1)  a, b의 범위를 지정해준다. 

2) 오차 제곱 합을 구하는 함수를 만든다. 

3) 범위 내의 모든 a, b에 대해서 error_func를 돌려 각각 (a,b)에 대한 e를 저장한다. 

 

 

a, b 를 정해준다. 

batch_size = 0.5 
a_datas = np.arange(0, 3, batch_size)
b_datas = np.arange(0, 3, batch_size)

batch_size :   a,b 값의  resolution.  몇씩 늘려가며 관찰할 것인가. 
0~3까지의 a, b,  (0, 0.5, 1, 1.5. ... )

 

 

오차 제곱 합을 구하는 함수를 만든다. 

def error_func(df, a, b):
    e = 0
    for idx, data in df.iterrows():
        e += (data.y - a - b * data.x) ** 2 # feature 데이터 x, y 값과 비교.
    return e # 오차들 제곱합 값을 반환한다.

df = (x, y) 형태의 데이터 프레임, (a, b) 는 추세선 함수의 가중치 벡터이다. 

 

아래 식에서 

data.y : 실제값 

a+b*data.x : 추정치

 

 

* 참고)  iterrow() : 데이터프레임의 각 행 정보를 담은 객체.  

(행 넘버, 해당 행 데이터) .. (마지막 행 넘버, 해당 행 데이터)

 

 

 


범위 내의 모든 a, b에 대해서 error_func를 돌려 각각 (a,b)에 대한 e를 저장한다. 

error_datas = []

for a_data in a_datas:
    for b_data in b_datas:
        error_datas.append({
            "a_data": a_data,
            "b_data": b_data,
            "error": error_func(df, a_data, b_data),
        })
# error_datas : a, b, 오차 제곱값   딕셔너리 형태로 반환 
error_df = pd.DataFrame(error_datas)
error_df



여기서 가장 작은 오차를 주는 a, b 값이 추세선이 된다. 

 

error_df[error_df.error == np.min(error_df.error)]


--------------------------------------------------------------------------

 

 

OLS 함수 안쓰고 a,b 찾기 2. 
경사하강법으로 추세선 구하기 : a, b의 미분값으로 기울기 구하기. 

위의 오차제곱법은 a, b의 범위를 정해놓고 그 범위 내의 a, b를 전부 입력해서 최소값을 찾는데,   
a, b를 각각 다 넣으면 a x b 만큼의 경우의 수가 나오게 된다. (batch 사이즈 고려하면 더 많아짐. )

 

b 값의 범위만 구해도 구할 수 있다. 

 

1) b 의 범위를 지정해준다. 

2) 오차 제곱의 합을 구하는 함수를 만든다. 

 

b_datas = np.arange(0, 5, 0.5)
def error_func(df, b):
    e = 0
    for idx, data in df.iterrows():
        e += (data.y - b * data.x) ** 2
    return e   # 오차 제곱합값을 반환함. 

error_datas = []

# 0 ~ 4.5 까지 에러를 출력
for b_data in b_datas:
    
    e = error_func(df, b_data)
    
    error_datas.append({
        "b_data":b_data,
        "error": e,
    })
    
# b 값이 2.5일때 가장 오차제곱합이 가장 작다.

error_df = pd.DataFrame(error_datas)
error_df
error_df[error_df.error == np.min(error_df.error)]



---------------------------------------------------
경사하강법2 (일단 b 초기값 아무거나 정하고, 기울기 때려맞히기)

 

- 함수의 최소값은 기울기가 0이 되는 지점이다. 

- 오차의 제곱 = (data.y - (b*data.x+a)) **2 

- 오차의 제곱은 b에 대한 2차 방정식으로도 볼 수 있다.

- 오차의 제곱이 가장 작아지는 부분은 함수 기울기가 0일 때, b 값이 양수이면 값을 낮추고, 음수이면 값을 높여 error 가 0에 가장 가까운 b값을 채택한다. 

 

 

 

1) b 의 초기값을 정해주고 최소의 기울기를 찾아가본다. 

2)  epoch : 몇번의 시도를 통해 기울기값을 찾을 지 정한다. 

 

# b의 초기값
b = 50

# 최적값을 찾기위한 실행 횟수
# epoch가 낮으면 정확한 값을 찾기 어려움
epoch = 50

# 한번에 이동하는 크기제어
batch_size = 0.01

log_datas = []

def slope_func(df, b):
    slope = 0
    for idx, data in df.iterrows():
        slope += -2 * (data.y - b * data.x) * data.x
    return round(slope, 2)

for _ in range(epoch):
    
    # b 값에 대한 기울기 구하기
    slope = slope_func(df, b)
    
    # print(np.round(b, 2), slope)
    
    # 기울기가 0이면 반복문 빠져나가기
    if slope == 0:
        break
    
    # 기울기가 0 미만이면 b값이 증가 (slope가 음수이므로 b 값이 증가)
    # 기울기가 0 초과이면 b값이 증가 (slope가 양수이므로 b 값이 감소)
    b -= batch_size * slope
    b = np.round(b, 5)
    
    # 에러 구하기
    e = error_func(df, b)
    
    # 데이터 저장
    log_datas.append({
        "b": np.round(b, 2),
        "e": e,
        "slope": slope,
    })

log_df = pd.DataFrame(log_datas)
log_df.head(2)

len(log_df), b




---------------------------------------------------------------------
statsmodel 말고 sklearn을 이용해서 선형회귀분석 해보기. 
feature : 독립변수. target : 종속변수 .

feature_gf = df[["gf"]]
feature_ga = df[["ga"]]
target = df["points"]

from sklearn.linear_model import LinearRegression
model_gf = LinearRegression().fit(feature_gf, target)  : 실점을 input으로 했을때의 모델. 
model_ga = LinearRegression().fit(feature_ga, target) :득점을 input으로 했을때의 모델. 

model_gf.intercept_,model_gf.coef_

 

(statsmodel의 const, x 값으로 출력되는 것들, sklearn에서는 용어가 살짝 다르다. )

intercept :  y절편, coef : 기울기. 


두개의 모델을 비교해보자. 
모델의 성능을 평가하는 척도 : MAE, RMSE, MSE, MAPE 등.. 

MAE (Model Absolute Error) :  오차의 절댓값을 척도로 삼음. 
MAE가 낮을 수록 정확한 모델이라고 볼 수 있다. 
 sklearn 에 MAE를 구하는 모듈이 이미 있다. 

from sklearn.metrics import mean_absolute_error
pred = np.round(model_gf.predict(feature_gf))
mean_absolute_error(target, pred)

: 오차의 절댓값을 구할, 결과값 & 추정값
( target 값은 우리가 알고 있는 결과 값. 
pred 값은  target이 나왔던 input 재료값을 모델에 넣었을 때 나온 추정값. )


각 feature를 사용해 모델을 만들었을때 오차값을 구해 모델의 성능을 비교할 수 있다. 

MAPE : Mean Absolute Percentage Error : MAE의 percent 화.
MAPE = MAE/ np.sum(target) * 100
f-value : 클수록 유의미하다. ( 05. multiple linear regression 참조 )

CSV 파일 불러올 때 

엔코딩 오류 발생 -> 보통 'utf-8' 을 넣으면 되지만 

이 경우에는 'cp949' 넣어야 해결 

 

raw_data = pd.read_csv('C:\\Users\\M\\Desktop\\11_.csv', encoding= 'cp949')#
raw_data

조건문을 사용할 때, 

조건이 애매하게 교집합이 되는 경우가 있다. 

 

예를 들어, 숫자를 입력하면 4의 배수인지 5의 배수인지 판별해준다고 하자. 

20의 경우, 4의 배수라고 할 수도 있고, 5의 배수라고 할 수도 있고, 4와 5의 배수라고 할 수도 있다. 

 

일단 4의 배수, 5의 배수 조건만 넣었을 때 어떻게 결과가 나오는지 살펴 보자. (교집합 조건을 넣지 않은 경우)

 

num1=input("숫자 입력:")
num2=int(num1)



if num2%4 == 0:
    x ='4의 배수'
elif num2%5 == 0 :
    x ='5의 배수'

else :
    x='4,5의 배수가 아니다'


print(x)

 결과 :

숫자 입력:20
4의 배수

 

if/ elif 문을 사용할 때,  먼저 나오는 조건이 우선순위가 높다. 

위에서 아래로 필터로 걸러지는 개념이라고 보면 되는데, 

20은 4의 배수이자 5의 배수이지만, 

elif 는 else if 즉, if ~의 조건이 아니면서 elif ~ 조건   이라는 의미이기 때문에 4의 배수로 출력된다. 

 

 

교집합 조건을 넣었을 때는 어떻게 될까. 

 

num1=input("숫자 입력:")
num2=int(num1)


if num2%5 == 0 :
    x ='5의 배수'

elif num2%4 == 0 :
    x ='4의 배수'

elif num2%4 == 0 and num2%5==0:
    x ='4의 배수이자 5의 배수'

else :
    x='4,5의 배수가 아니다'


print(x)

결과 : 

숫자 입력:20
5의 배수

 

역시 위에서부터 걸러지기 때문에 결과는 5의 배수로 출력된다. 

따라서, 

교집합 조건이 있을 때는, 교집합 조건을 맨 위에 넣어주는 것이 누락되지 않는 방법이다. 

 

num1=input("숫자 입력:")
num2=int(num1)

if num2%4 == 0 and num2%5==0:
    x ='4의 배수이자 5의 배수'
elif num2%5 == 0 :
    x ='5의 배수'

elif num2%4 == 0 :
    x ='4의 배수'

else :
    x='4,5의 배수가 아니다'


print(x)

결과: 

숫자 입력:20
4의 배수이자 5의 배수

+ Recent posts