본문 바로가기

핸즈온 머신러닝

2.5 ~3.7

 

 

2.5 데이터 준비

 

데이터 준비를 함수로 자동화 이유

- 어떤 데이터셋에 대해서도 데이터 변환을 손쉽게 반복할 수 있음

- 향후 프로젝트에 재사용 가능한 변환 라이브러리를 점진적으로 구축할 수 있음

- 실제 시스템에서 알고리즘에 새 데이터를 주입하기 전에 이 함수를 사용해 변환할 수 있음

- 여러 가지 데이터 변환을 쉽게 시도 및 어떤 조합이 가장 좋은지 확인하는데 편리

 

2.5.1 데이터 정제

1. 해당 구역 제거

2. 전체 특성 삭제

3. 결측치를 어떤 값으로 대체함 (imputing) : 주로 0, 1, 평균

 

housing.dropna(subset=["total_bedrooms"], inplace=True)    # 옵션 1

housing.drop("total_bedrooms", axis=1)                     # 옵션 2

median = housing["total_bedrooms"].median()                # 옵션 3
housing["total_bedrooms"].fillna(median, inplace=True)

(아래 코드 삭제가 안 됩니다..... )

housing.dropna(subset=["total_bedrooms"], inplace=True)    # 옵션 1

housing.drop("total_bedrooms", axis=1)                     # 옵션 2

median = housing["total_bedrooms"].median()                # 옵션 3
housing["total_bedrooms"].fillna(median, inplace=True)

 

* SimpleImputer 라이브러리 사용 가능

from sklearn.impute import SimpleImputer

imputer = SimpleImputer(strategy="median")

 

* 사이킷런의 설계 철학 

- 일관성 (추정기 , 변환기 , 예측기)

- 검사 가능

-  클래스 남용 방지

- 합리적인 기본값

 

 

범주형 입력 특성을 인코더 라이브러리를 통해 전처리

 

* 원- 핫 인코딩 : 해당되는 특성을 1, 나머지 특성을 0으로 해 개별 컬럼으로 쪼개는 전처리 방식

* 이의 출력은 넘파이 배열이 아닌 사이파이 희소 행렬임

 

- 특성 스케일링 : 표준정규분포로 스케일링 (라이브러리 사용)

from sklearn.preprocessing import StandardScaler

std_scaler = StandardScaler()
housing_num_std_scaled = std_scaler.fit_transform(housing_num)

 

 

* 멀티모달 분포 > 특정 특성과 특성 모드 사이 유사도를 나타내는 특성을 추가함

* 유사도 측정: 방사 기저 함수 사용

from sklearn.metrics.pairwise import rbf_kernel

age_simil_35 = rbf_kernel(housing[["housing_median_age"]], [[35]], gamma=0.1)
 
# 추가 코드 – 이 셀은 그림 2–18을 생성 합니다

ages = np.linspace(housing["housing_median_age"].min(),
                   housing["housing_median_age"].max(),
                   500).reshape(-1, 1)
gamma1 = 0.1
gamma2 = 0.03
rbf1 = rbf_kernel(ages, [[35]], gamma=gamma1)
rbf2 = rbf_kernel(ages, [[35]], gamma=gamma2)

fig, ax1 = plt.subplots()

ax1.set_xlabel("Housing median age")
ax1.set_ylabel("Number of districts")
ax1.hist(housing["housing_median_age"], bins=50)

ax2 = ax1.twinx()  # x축을 공유 하는 쌍둥이 축을 만듭니다
color = "blue"
ax2.plot(ages, rbf1, color=color, label="gamma = 0.10")
ax2.plot(ages, rbf2, color=color, label="gamma = 0.03", linestyle="--")
ax2.tick_params(axis='y', labelcolor=color)
ax2.set_ylabel("Age similarity", color=color)

plt.legend(loc="upper left")
save_fig("age_similarity_plot")
plt.show()

 

* 모델 피팅 : 선형 회귀 모델 사용 

from sklearn.linear_model import LinearRegression

target_scaler = StandardScaler()
scaled_labels = target_scaler.fit_transform(housing_labels.to_frame())

model = LinearRegression()
model.fit(housing[["median_income"]], scaled_labels)
some_new_data = housing[["median_income"]].iloc[:5]  # 새로운 데이터라고 가정합니다

scaled_predictions = model.predict(some_new_data)
predictions = target_scaler.inverse_transform(scaled_predictions)

 

 

사용자 정의 변환기: 사용자 정의 변환, 정제 연산, 특성 결합과 같은 작업은 자신만의 변환기를 작성할 필요 있음

ex) 어떤 훈련도 필요하지 않는 변환의 경우 넘파이 배열을 입력으로 받고 변환된 배열을 출력하는 함수 작성

 

 - 추가적인 인수를 하이퍼 파라미터로 반을 수 있음

 - 사이킷런의 경우 오버라이딩할 필요는 없음

 

from sklearn.preprocessing import FunctionTransformer

log_transformer = FunctionTransformer(np.log, inverse_func=np.exp)
log_pop = log_transformer.transform(housing[["population"]])
rbf_transformer = FunctionTransformer(rbf_kernel,
                                      kw_args=dict(Y=[[35.]], gamma=0.1))
age_simil_35 = rbf_transformer.transform(housing[["housing_median_age"]])

 

* KMeans 를 사용한 클러스터링 기법에 클러스터 간 유사도를 측정한 코드

from sklearn.cluster import KMeans

class ClusterSimilarity(BaseEstimator, TransformerMixin):
    def __init__(self, n_clusters=10, gamma=1.0, random_state=None):
        self.n_clusters = n_clusters
        self.gamma = gamma
        self.random_state = random_state

    def fit(self, X, y=None, sample_weight=None):
        # 사이킷런 1.2버전에서 최상의 결과를 찾기 위해 반복하는 횟수를 지정하는 `n_init` 매개변수 값에 `'auto'`가 추가되었습니다.
        # `n_init='auto'`로 지정하면 초기화 방법을 지정하는 `init='random'`일 때 10, `init='k-means++'`일 때 1이 됩니다.
        # 사이킷런 1.4버전에서 `n_init`의 기본값이 10에서 `'auto'`로 바뀝니다. 경고를 피하기 위해 `n_init=10`으로 지정합니다.
        self.kmeans_ = KMeans(self.n_clusters, n_init=10, random_state=self.random_state)
        self.kmeans_.fit(X, sample_weight=sample_weight)
        return self  # 항상 self를 반환합니다!

    def transform(self, X):
        return rbf_kernel(X, self.kmeans_.cluster_centers_, gamma=self.gamma)

    def get_feature_names_out(self, names=None):
        return [f"클러스터 {i} 유사도" for i in range(self.n_clusters)]
 
cluster_simil = ClusterSimilarity(n_clusters=10, gamma=1., random_state=42)
similarities = cluster_simil.fit_transform(housing[["latitude", "longitude"]],
                                           sample_weight=housing_labels)

 

* Pilpeline 생성자를 통해 변환 파이프라인 구축 가능

from sklearn.pipeline import Pipeline

num_pipeline = Pipeline([
    ("impute", SimpleImputer(strategy="median")),
    ("standardize", StandardScaler()),
])
from sklearn.pipeline import make_pipeline

num_pipeline = make_pipeline(SimpleImputer(strategy="median"), StandardScaler())

 

* 모델 선택 / 훈련

- 해결하려는 문제에 따라 Loss Function을 다양하게 정의할 수 있음

- K 폴드 교차 검증 사용 가능 (훈련 셋에 오버피팅 막기 위함)

- 랜덤 포레스트 같은 앙상블 메소드 사용 가능

 

* 모델 미세 조정(파인 튜닝)

- GridSearchCV : 제시한 모든 하이퍼파라미터들의 조합 수동으로 다 평가

from sklearn.model_selection import GridSearchCV

full_pipeline = Pipeline([
    ("preprocessing", preprocessing),
    ("random_forest", RandomForestRegressor(random_state=42)),
])
param_grid = [
    {'preprocessing__geo__n_clusters': [5, 8, 10],
     'random_forest__max_features': [4, 6, 8]},
    {'preprocessing__geo__n_clusters': [10, 15],
     'random_forest__max_features': [6, 8, 10]},
]
grid_search = GridSearchCV(full_pipeline, param_grid, cv=3,
                           scoring='neg_root_mean_squared_error')
grid_search.fit(housing, housing_labels)

 

- RandonSearchCV : 랜덤한 수만큼의 조합으로 하이퍼파리미터 평가

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint

param_distribs = {'preprocessing__geo__n_clusters': randint(low=3, high=50),
                  'random_forest__max_features': randint(low=2, high=20)}

rnd_search = RandomizedSearchCV(
    full_pipeline, param_distributions=param_distribs, n_iter=10, cv=3,
    scoring='neg_root_mean_squared_error', random_state=42)

rnd_search.fit(housing, housing_labels)

 

- 앙상블

- 모델의 신뢰 구간 계산

 

- 시스템 배포 및 서비스 모니터링

 

 

3. 분류

데이터: MNIST (숫자 손글씨 이미지 + 실제 숫자 label 데이터 )

 

- 이진 분류기 훈련 : label = 5일 때를 True로 가정

- 확률적 경사 하강법 사용 : 큰 데이터셋 처리 가능, 각 샘플 개별로 취급, 온라인 학습에 좋음

from sklearn.linear_model import SGDClassifier

sgd_clf = SGDClassifier(random_state=42)
sgd_clf.fit(X_train, y_train_5)

 

- 모델 교차 검증 및 평가

from sklearn.model_selection import cross_val_score

cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring="accuracy")
 
array([0.95035, 0.96035, 0.9604 ])
from sklearn.model_selection import StratifiedKFold
from sklearn.base import clone

skfolds = StratifiedKFold(n_splits=3)  # 데이터셋이 미리 섞여 있지 않다면
                                       # shuffle=True를 추가하세요.
for train_index, test_index in skfolds.split(X_train, y_train_5):
    clone_clf = clone(sgd_clf)
    X_train_folds = X_train[train_index]
    y_train_folds = y_train_5[train_index]
    X_test_fold = X_train[test_index]
    y_test_fold = y_train_5[test_index]

    clone_clf.fit(X_train_folds, y_train_folds)
    y_pred = clone_clf.predict(X_test_fold)
    n_correct = sum(y_pred == y_test_fold)
    print(n_correct / len(y_pred))

 

 

오차 행렬: 일반적인 분류기의 성능 평가 방법

- 정확도는 불균형한 데이터셋 등을 다룰 때 지표로 좋지 않음 (예를 들어 이상치 탐지에서 다 맞다고 하면 정확도 90% 이상인 경우 등.....)

- 오차 행렬을 만들기 위해서 cross_val_predict()사용

( 행: 실제 클래스, 열: 예측한 클래스 )

 

*** Confusion Matrix의 용어들은 정확하게 파악하고 있어야 함

- 음성 클래스 : 5 아님  (진짜 음성(TN): 5 아님, 거짓 양성(FP): 5 맞음)

- 양성 클래스: 5 맞음 (거짓 음성(FN): 5 아님, 진짜 양성(TP): 5 맞음)

 

* precision

* recall

 

 

** F1 Score : precision 과 recall의 조화 평균 : 점수가 높으려면 두 값이 모두 높아야 함

 

** 근데 보통 정밀도가 오르면 재현율이 줄어들고 그 반대도 마찬가지인 '정밀도 재현율 트레이드오프' 발생

 

threshold = 3000
y_some_digit_pred = (y_scores > threshold)
y_some_digit_pred
 
array([False])
 
y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3,
                             method="decision_function")
 
from sklearn.metrics import precision_recall_curve

precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)
 
thresholds, precisions
 
(array([-146348.56726174, -142300.00705404, -137588.97581744, ...,
          38871.26391927,   42216.05562787,   49441.43765905]),
 array([0.09035   , 0.09035151, 0.09035301, ..., 1.        , 1.        ,
        1.        ]))
 
plt.figure(figsize=(8, 4))  # 추가 코드
plt.plot(thresholds, precisions[:-1], "b--", label="Precision", linewidth=2)
plt.plot(thresholds, recalls[:-1], "g-", label="Recall", linewidth=2)
plt.vlines(threshold, 0, 1.0, "k", "dotted", label="threshold")

# 추가 코드 – 그림 3–5를 그리고 저장합니다
idx = (thresholds >= threshold).argmax()  # 첫 번째 index ≥ threshold
plt.plot(thresholds[idx], precisions[idx], "bo")
plt.plot(thresholds[idx], recalls[idx], "go")
plt.axis([-50000, 50000, 0, 1])
plt.grid()
plt.xlabel("Threshold")
plt.legend(loc="center right")
save_fig("precision_recall_vs_threshold_plot")

plt.show()

 

* ROC 곡선 : FPR/ TPR

 

from sklearn.ensemble import RandomForestClassifier

forest_clf = RandomForestClassifier(random_state=42)
 
y_probas_forest = cross_val_predict(forest_clf, X_train, y_train_5, cv=3,
                                    method="predict_proba")

 

 

* 다중 분류

- OvR / OvA  전략 : 클래스 N개 중 가장 점수 높은 걸로 선택

from sklearn.multiclass import OneVsRestClassifier

ovr_clf = OneVsRestClassifier(SVC(random_state=42))
ovr_clf.fit(X_train[:2000], y_train[:2000])

 

- OvO : 각각의 이진 분류기를 훈련

from sklearn.svm import SVC

svm_clf = SVC(random_state=42)
svm_clf.fit(X_train[:2000], y_train[:2000])  # y_train_5가 아니고 y_train을 사용합니다.

'핸즈온 머신러닝' 카테고리의 다른 글

9. 비지도 학습  (0) 2025.04.29
4.1~4.3  (0) 2025.03.27
1-4~2-4  (0) 2025.03.16