= 회귀라고 이름이 붙은 것 중에서 유일하게 분류 문제


--> 선형 회귀 라인은 0.5 경계 부분이 차이가 얼마 안 나지만 0,1로 분류됨 --> 애매함 --> 확률값을 반환할 수 없을까 ? & 미분가능한 함수였으면 함 --> Sigmoid 함수를 사용해서 0~1 값으로 확률값을 반환해줌.
= 선형 회귀의 결과 값을 기반으로 시그모이드 함수를 사용하면 0~1 사이의 확률값으로 변환이 됨!


1. 로지스틱 회귀는 이진 분류 문제를 해결하는 가장 단순한 분류기
2. 구현이 간단하고 모든 분류 문제의 기초로 널리 사용됨
3. 로지스틱 회귀에서 사용하는 개념은 딥러닝의 분류 문제에도 적용됨
- 딥러닝에서 마지막 층에서 사용하는 활성화 함수로 시그모이드 함수를 쓰는 경우가 많음, 로지스틱 회귀의 기본 아이디어와 같음
4. 독립변수와 종속변수 간의 관계를 찾아내고 평가함- 계산된 선형 결합을 시그모이드 함수로 변환해, 데이터가 특정 클래스에 속할 확률을 평가함.
5.sklearn.linear_model.LogisticRegression(solver='lbfgs')- 사이킷런 라이브러리에서
LogisticRegression클래스를 사용해 모델 구현 가능.solver='lbfgs'는 최적화 알고리즘을 의미 (디폴트값 ,손실 함수를 최소화하는데 사용됨)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.metrics import confusion_matrix, f1_score, roc_curve, roc_auc_score
import seaborn as sns
df = pd.read_csv("datasets/Social_Network_Ads.csv")
df.head()
df.tail(20) # 뒤쪽에서 20개를 볼 수 있음.
df["Purchased"].value_counts()

astype("float32") : 머신러닝의 입력으로 줄 떄 일반적으로 float 타입으로 주는게 좋음 # 두 개의 변수만 인덱싱 (데이터프레임에 인덱스를 가지고 슬라이싱을 해야함)
# iloc 인덱스로 로케이션 하겠다고 해서 iloc
# row 전부다, 그리고 인덱스 2,3번만 추출하겠다, 슬라이싱 하겠다.
X = df.iloc[:,[2,3]].values.astype("float32") # 머신러닝의 입력 float 타입
y = df.iloc[:,4].values.astype("float32")
X.shape, y.shape
- 실행결과 : ((400, 2), (400,))
# 4:1 비율로 나누기!!
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,random_state=0)!
X_train.shape, X_test.shape, y_train.shape, y_test.shape
# 400개의 데이터가 320,80개로 잘 나눠졌음
- 실행결과 : ((320, 2), (80, 2), (320,), (80,))
⚠️train data로 fit하고, test data는 train data의 분포에 맞춰 transform
⚠️ 훈련 데이터 : fit & transform 둘 다 해줌
⚠️ 테스트 데이터 : transform만 해줌!!
sc = StandardScaler()
X_train = sc.fit_transform(X_train) # fit,transform 두 단계를 한번에 합친거임.
X_test = sc.transform(X_test) # X_test는 transform만 해야함.
lr_clf = LogisticRegression() # 그냥 디폴트 값 사용
lr_clf.fit(X_train,y_train)

⚠️ predict() - X_test를 이용해 예측된 class 반환
⚠️ predict_proba() - X_test를 이용해 class당 확률값 반환
y_pred = lr_clf.predict(X_test)
y_pred # 0,1 반환
y_pred_proba = lr_clf.predict_proba(X_test)
y_pred_proba # 확률값 반환
⚠️ threshold를 높여주면 precision은 내려가고 recall은 올라감 --> 목적에 맞게 정하기!
accuracy_score(y_test,y_pred) # 일단 맞춘거는 92.5% 맞춤
# 우리가 맞췄다고 한 것 중에서 우리가 이제 true를 true로 잘 맞추느냐
precision_score(y_test,y_pred) # 물건을 살 사람을 살 사람으로 잘 맞추느냐 --> 94%
recall_score(y_test,y_pred) # 전체 true 중에서 우리가 얼마를 놓치지 않고 다 포함했느냐
# precision score가 94인데 recall은 72임 --> 우리가 놓친 케이스가 많다는 것.
cm = confusion_matrix(y_test, y_pred)
cm
ax = sns.heatmap(cm,annot=True, fmt='d') # annot=True, 박스 안에 숫자 설정
ax.set_title('Confusion Matrix')
ax.set_ylabel('True')
ax.set_xlabel('Predicted')

⚠️ 성능을 비교할 때, 여러 개의 분류기 중에서 커브의 면적이 가장 넓은 모델을 선택 --> 최상의 모델
roc_curve(y_true, y_score)
fpr - false positive rates, tpr - true positive rates
y_proba = lr_clf.predict_proba(X_test)
y_scores = y_proba[:,1]
y_scores # 1일 확률이 몇 %인지 그 값을 알려줌
# 실제로 변수를 3개 반환하는데, 우리는 2개만 쓰니깐 (_) 언더바 써준다.
fpr,tpr,_ = roc_curve(y_test,y_scores)
auc = roc_auc_score(y_test,y_scores)
# 시각화
plt.plot(fpr,tpr,label="auc = {:.2f}".format(auc))
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC_AUC curve")
plt.legend()

앙상블 학습이란 ?
= 다수의 약한 학습기(weak learner)를 조합(Ensemble)하여 더 높은 성능 추출
= 훈련 샘플의 서브셋을 무작위로 추출하여 여러 개의 분류기를 독립적으로 훈련시키는 방법
- Bootstrap : 중복을 허용하는 랜덤 샘플링 방법 (=복원 추출)
ex) b개의 독립적인 훈련 세트에서 나온 결과(=예측값)을 평균하면,
--> 분산 : σ^2 / b 로 감소
--> 평균 유지
- Aggregating(다수결 원칙)
= 여러 분류기의 예측 결과를 구해 다수결의 원칙에 따라 최종 분류 결과를 결정하는 방식
- Random Forest가 대표적인 배깅 방법

📢 복원추출 과정
- 100개의 훈련 데이터에서 10개의 샘플을 뽑는다 --> Sample 1
- 샘플1에서 뽑아낸 10개의 데이터를 다시 원위치 (중복 허용O)
- 다시 100개의 데이터에서 10개를 샘플을 뽑는다 --> Sample 2
- 위의 과정을 반복해 총 6개의 샘플을 뽑아 6개의 결정트리 모델을 만든다 (서로 다른 데이터로 만들어진 모델이므로 6개는 다 다른 모델임)
- 6개의 모델 예측 결과를 다수결 투표로 결합하여 최종 예측을 도출한다.
- Bagging 과정을 통해 만들어진 최종 모델은 Bagging Tree로 이걸 랜덤 포레스트라고도 부른다.
1단계) 훈련 데이터에서 랜덤 샘플링해 X_b, y_b를 고른다
2단계) X_b, y_b를 이용하여 IS3 알고리즘으로 결정트리 구성
3단계) B개의 트리가 만들어질 때까지 1,2단계 반복
4단계) B개의 모든 트리를 이용해 분류를 한 후, 다수결 원칙으로 투표
5단계) 편향을 유지하며 분산을 줄인다
⭐ 배깅은 랜덤 샘플잉에 의해 모델 훈련을 하므로 편향(=모델의 복잡도)가 커지지 않으면서 분산을 줄일 수 있는 특징이 있음!!
배깅트리 --> 랜덤포레스트로 발전
📢 순수 배깅 트리 VS 랜덤포레스트
✔️ 배깅 트리 : 각 트리에서 전체 속성을 사용해 트리를 학습
✔️ 랜덤 포레스트 : 각 트리에서 전체 속성 중 일부만 속성(=피처)를 랜덤하게 선택함 --> 일반화 성능을 높임
ex) 3개의 결정 트리(Classifier 1,2,3)가 각각 서로 다른 결정 경계를 학습하고, 3개 결정 트리의 예측 결과를 다수결 투표로 결합해 최종 랜덤 포레스트 모델이 생성됨

= 잘못 분류된 데이터에 더 높은 가중치를 부여해 다음 번 모델의 샘플링에 포함될 확률을 높이는 것
1단계) 첫 번째 트리에서는 X,y(=정답)를 입력으로 줘서 잔차 r1을 생성함
2단계) 입력을 (데이터, 이전 트리에서 생성한 잔차) --> 즉, (X,r)으로 받는다
3단계) 이전 트리에서 발생한 잔차를 다음 트리에서 보정을 하고, 잔차가 거의 0이 될 때까지 2,3단계 과정 반복
4단계) 학습을 진행할수록 오차가 줄어들고 만약 잔차가 0에 가까워졌다면, 트리들의 결괏값을 더하게 되면 Y값을 알 수 있음.

= 데이터를 부스팅 하는 방법 (이 전에 잘못 분류됐던 데이터의 가중치를 더 높여서 다음엔 반드시 그 데이터가 뽑히도록 함)



import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix, f1_score, accuracy_score
df = pd.read_csv("datasets/Social_Network_Ads.csv")
df.head()

X = df.iloc[:,[2,3]].values.astype("float32")
y = df.iloc[:,4].values
X.shape, y.shape
X_train, X_test, y_train, y_test = train_test_split(X, y , test_size = 0.2, random_state = 0)
X_train.shape, X_test.shape , y_train.shape, y_test.shape
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
⚠️n_estimators : 나무를 몇 개를 만들건지 설정하는 파라미터
#랜덤 포레스트는 랜덤이니깐 random_state을 맞춰준다.
rf = RandomForestClassifier(n_estimators = 10, random_state = 0)
rf.fit(X_train, y_train)

y_pred = rf.predict(X_test)
accuracy_score(y_test, y_pred) # 정확도 92%
앞 과정은 랜덤 포레스트와 동일
⚠️ min_samples_split : node 분리에 필요한 최소 sample 수 => overfitting 방지
⚠️ max_depth : tree 깊이 조절 => overfitting 방지
⚠️ learning_rate : 각 tree 의 기여도 조정, n_estimators 와 trade-off
from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(learning_rate = 0.1, n_estimators = 500, max_depth = 5)
gb.fit(X_train, y_train)

y_pred = gb.predict(X_test)
accuracy_score(y_test, y_pred)
from matplotlib.colors import ListedColormap
# 진한 빨강색, 진한 초록색
cmap_bold = ListedColormap(['#FF0000','#00FF00']) # '#'이라는 건 16진수를 의미. FF00-->그러면 레드가 멕시멈이고
# RGB 그린,블루는 0 이런걸 하나 놓고, 두 번째로는 그린을 최대로 놓고, 빨강,파란을 00으로 설정
# 연한 빨강색, 연한 초록색
cmap_light = ListedColormap(['#FFAAAA','#AAFFAA']) # 빨강을 제외하고 AA를 해주면 연한 붉은색이 나옴.
X_test[:, 0] : X_test의 첫 번째 특성(열)X_test[:, 1] : X_test의 두 번째 특성(열)x1_min, x1_max = X_test[:, 0].min() - 1, X_test[:, 0].max() + 1
x2_min, x2_max = X_test[:, 1].min() - 1, X_test[:, 1].max() + 1
⚠️np.arange(x1_min, x1_max, 0.1) : x1_min ~ x1_max까지 0.1 간격으로 숫자 생성
⚠️np.meshgrid( , ) : 괄호 안의 숫자들을 모두 조합하여 평면에 격자 모양의 좌표 생성
X1, X2 = np.meshgrid(np.arange(x1_min, x1_max, 0.1),
np.arange(x2_min, x2_max, 0.1))
⚠️ .ravel() : 2차원 배열을 1차원으로 펼쳐줌
X1.ravel()
X2.ravel()
⚠️ np.column_stack : 1차원으로 바뀐 X1,X2를 열 단위로 결합해, 각 행이 하나의 죄표쌍을 갖는 2차원 배열 XX를 생성 --> 모델에 입력할 데이터
XX = np.column_stack([X1.ravel(), X2.ravel()])
XX에 대해 예측한 클래스 0,1(=구매 여부)를 반환
Y_rf = np.array(rf.predict(XX))
Y_gb = np.array(gb.predict(XX))
⚠️ 점들은 데이터 포인트로, 모델이 학습한 데이터.
⚠️ 모델 예측을 통해 만들어진 결정 경계(=색깔로 구분)가 실제 데이터 포인트를 잘 구분했는지 시각화
// 하나의 행에 두 개의 열,양옆에 두 개를 그리니깐 OP 형식으로 시각화하기
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)
# Random Forest
# x축, y축, 색깔까지 총 3차원으로 줄거임!
ax1.pcolormesh(X1, X2, Y_rf.reshape(X1.shape), cmap=cmap_light, shading='auto')
# 실제 우리 데이터가 여기 어디에 속하는지 그려보자.
for i in range(2):
ax1.scatter(X_test[y_test == i, 0], X_test[y_test == i,1], color=cmap_bold(i), label=i, s = 30, edgecolor='k')
ax1.set_title("Random Forest")
ax1.legend()
// 생성된 결정경계를 보고 이제 수정을 할 수 있음 --> 나무를 좀 고쳐서 데이터가 잘 분류될 수 있도록
# Gradient Boost
ax2.pcolormesh(X1, X2, Y_gb.reshape(X1.shape), cmap=cmap_light, shading='auto')
for i in range(2):
ax2.scatter(X_test[y_test == i, 0], X_test[y_test == i,1], color=cmap_bold(i), label=i, s = 30, edgecolor='k')
ax2.set_title("Gradient Boost") // 이거 두개로 예측한 걸 시각화해서 비교해보기
ax2.legend()

gb.feature_importances_ # 둘 다 중요도가 비슷하게 나옴 -> 둘다 중요
feature_imp = pd.Series(gb.feature_importances_, ['Age','Estimated Salary']) # 두번째 매개변수는 인덱스를 준다.
feature_imp
feature_imp.plot(kind='bar',title='Feature importance')

결측치 처리
데이터 포맷팅(=형식 통일)

편향된 데이터 처리 --> 데이터가 균일해야 좋은 성능 발휘
데이터 정규화(=피처 스케일링)

Binning(=구간화)

카테고리 변수의 수치화
⭐ 카테고리 피처는 반드시 숫자로 변환해줘야함!
1. ordinal 카테고리 (순서/크기 O)
--> 숫자로 크기(순서) 표시
ex) L > M > S --> 3,2,1
2. nominal 카테고리 (순서/크기가 X --> 보통 이게 더 많음)
--> 원-핫 인코딩 (칼럼을 분리해서 0,1로 표기)
ex) 같은 칼럼에 존재했던 경유,휘발유의 칼럼을 분리한 후, 해당하면 1, 해당하지 않으면 0으로 표시

📢 데이터셋 설명
= Titanic호 data 를 이용한 Feature Engineering 과 Modeling
- Pclass - Passenger Class (1 = 1st; 2 = 2nd; 3 = 3rd) 객실 등급
- survival - 생존 여부 (0 = 사망; 1 = 생존) --> 지도학습의 정답 데이터, 레이블
- name - 이름, sex - 성별 , age - 나이
- sibsp - 함께 탑승한 형제 또는 배우자 수
- parch - 함께 탑승한 부모 또는 자녀 수
- ticket - 티켓 번호 , fare - Passenger Fare (British pound) , cabin - 선실번호
- embarked - 탑승한 항구(얼마나 부유한지와 관련) --> C = Cherbourg; Q = Queenstown; S = Southampton
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv("datasets/titanic.csv")
df.head()
⚠️ 정답과 연관성이 낮은 PassengerId, Name, Ticket, Cabin 칼럼 삭제 --> 노이즈 제거
df.columns # column명 확인
df.drop(['PassengerId','Name', 'Ticket', 'Cabin'],axis=1, inplace=True) # 실제로 pd에서 날려버림
df.head() # 노이즈, 필요없는 데이터 칼럼을 삭제함, 정답과 무관한 칼럼들은 없애주는게 좋은

df.info()
df.isnull() // null인 것은 true, 아닌 것은 false으로 나옴
df.isnull().sum() // 결측치의 갯수
df.describe() // 데이터프레임의 기술 통계 --> 평균, 최대/최소..
df.isnull().sum()

⚠️ Survived(=정답 데이터)와 피처 간의 상관관계 파악하는 단계
df.corr(numeric_only=True)

import seaborn as sns
sns.heatmap(df.corr(numeric_only=True),annot = True, cmap = 'coolwarm')
# 대각선이 가장 높은 이유는 --> 자기 자신이 자기 자신이랑 젤 연관 높으니깐
# Fare하고 Pclass은 아주 강한 역의 관계가 있음
# 1,2,3등석이니깐 Pclass 숫자가 클수록 Fare이랑 역의 관계가 있음
# 그래서 Fare가 비싸질수록 pClass는 숫자가 내려감.

⚠️ 결측치 177개 age는 중앙값으로 대체 --> 다 삭제하기엔 데이터 손실이 큼
⚠️ Embarked 결측치 2개는 그냥 삭제
df.isnull().sum() # 결측치 확인
df['Age'].fillna(df['Age'].median(), inplace = True)
df.dropna(inplace = True)
df.isnull().sum()

⚠️ pandas에는 matplotlib이 내장되어 있음
⚠️ 로그 값을 취하기 전) Fare 데이터가 치우친 분포를 가짐
df.hist(bins = 30, figsize = (8,8))
plt.show()

⚠️ 로그 값을 취한 후) 대소관계가 유지된 상태로 치우침을 완화 --> 가운데로 정렬됨 & 칼럼의 특성은 바뀌지 X
# 로그를 취한 값으로 Fare을 적용해준다.
df['Fare'] = df['Fare'].map(lambda x: np.log(x) if x > 0 else 0)
df.hist(bins = 30, figsize = (8,8))
plt.show()

⚠️pd.get_dummies(df) : 카테고리인 것들만 분리해서 데이터 프레임을 전부 숫자형으로 변형함.
df.head()

df = pd.get_dummies(df)
df.head()
df.shape # (889, 11)
y = df['Survived'].values # 정답 데이터
X = df.drop('Survived', axis = 1).values
X.shape, y.shape # 10개의 피처로 이뤄진 훈련 데이터, 1개의 피처로 이뤄진 정답 데이터 survived
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2, random_state = 0)
X_train.shape, X_test.shape, y_train.shape, y_test.shape
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test) # fit 하면 안됨
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
rf = RandomForestClassifier(n_estimators = 10, random_state= 0, criterion = 'entropy')
rf.fit(X_train, y_train) # 모델 학습 완료, fit

y_pred = rf.predict(X_test)
accuracy_score(y_test,y_pred) # 정확도 76%