AI/머신러닝

머신러닝의 이해와 라이브러리 활용 심화 1주차(EDA, 전처리, 인코딩, 스케일링, 데이터분리, 교차검증, GridSearch)

edcrfv458 2025. 2. 13. 15:39

목표

  1. 예측 모델링 프로세스
    1. 데이터 수집
    2. 탐색적 데이터 분석(EDA)
    3. 데이터 전처리
    4. (실습) 탐색적 데이터 분석과 데이터 전처리
    5. 데이터 분리
    6. (실습) 데이터 전체 프로세스 적용
  2. 데이터 분석 프로세스 정리

데이터 수집

  • 데이터 수집 단계는 예제 데이터 혹은 회사에 있는 데이터로 진행되기 때문에, 지나치는 경우가 많다.
  • 실제로 데이터를 수집하려면 개발을 통해 데이터를 적재하고 수집하는 데이터 엔지니어링 역량이 필요한데, 이 부분은 개발자가 직접 설계하고 저장하게 된다.
  • 데이터 분석가는 이미 존재하는 데이터를 SQL 혹은 Python을 통해 추출하고 리포팅 혹은 머신러닝을 통한 예측을 담당한다고 할 수 있다.

 

용어

  • Data Source
    • OLTP Database: OnLine Transaction Processing 온라인 뱅킹, 쇼핑, 주문 입력 등 동시에 발생하는 다수의 트랜잭션(데이터베이스 직업의 단위) 처리 유형
    • Enterprise Applications: 회사 내 데이터 (ex. 고객 관계 데이터, 제품 마케팅 세일즈)
    • Third - Party: Google Analytics와 같은 외부소스에서 수집하는 데이터
    • Web/Log: 사용자의 로그 데이터
  • Data Lake: 원시 형태의 다양한 유형의 데이터 저장
  • Data Warehouse: 보다 구조화된 형태로 정제된 데이터 저장
  • Data Marts: 회사의 금융, 마케팅, 영업 부서와 같이 특정 조직의 목적을 위해 가공된 데이터
  • Bl/Analytics business Intelligence(Bl): 의사결정에 사용될 데이터 수집하고 분석하는 프로세스

 

상황

  • 회사 내 데이터가 존재
    • SQL 혹은 Python 통해 데이터 마트 생성
  • 회사 내 데이터가 존재 x ➡️ 데이터 수집이 필요
    • CSV, EXCEL 파일 다운로드
    • API를 이용
    • Data Crawling

탐색적 데이터 분석(EDA)

  • 데이터의 시각화, 기술통계 등의 방법을 통해 데이터 이해하고 탐구하는 과정이다.
  • 이 과정에서 데이터에 대한 정보를 얻을 수도 있고, 적절한 모델링에 대한 정보도 얻을 수 있다.
  • 예측 모델링이 아니더라도 데이터 분석에서는 반드시 필요한 과정이다.
    • seaborn 라이브러리 활용

 

기술통계를 위한 EDA

  • .describe()
    • include='all' 옵션을 주면 범주형 데이터도 확인 가능

 

시각화 이용한 EDA

  • countplot: 범주형 자료의 빈도 수 시각화
    • 방법: 범주형 데이터의 각 카테고리 빈도수를 나타낼 때
    • x축 범주형 자료
    • y축 자료의 빈도수
    • 예시: 각 날짜의 빈도수
    • sns.countplot(x = 'day', data = tips)
  • barplot: 범주형 데이터의 각 카테고리에 따른 수치 데이터의 평균을 비교
    • x축 범주형 자료
    • y축 연속형 자료
    • 예시: 각 성별의 평균 소득을 비교할 때
    • sns.barplot(x = 'sex', y = 'tip', data = tips)
      • estimator: 어떻게 집계를 내릴지 결정하는 파라미터
  • boxplot: 수치형 & 범주형 자료의 시각화
    • 데이터의 분포, 중앙값, 사분위 수, 이상치 등을 한눈에 표현하고 싶을 때
    • x축 수치형 or 범주형
    • y축 수치형 자료
    • 예시: 식사 시간 별 total_bill의 분포
    • sns.boxplot(x = 'time', y = 'total_bill', data = tips)
  • histogram: 수치형 자료 빈도 시각화 plt.hist or sns.histplot
    • 연속형 분포 나타내고 싶을 때, 데이터가 몰려있는 구간을 파악하기 쉬움
    • x축 수치형 자료
    • y축 자료의 빈도수
    • 예시: total_bill의 분포
    • sns.histplot(x = 'total_bill', data = tips)
      • bins: 막대의 개수 파라미터
  • scatterplot: 수치형끼리 자료의 시각화
    • 두 연속형 변수간의 관계를 시각적으로 파악하고 싶을 때
    • x축 수치형 자료
    • y축 수치형 자료
    • 예시: total_bill과 tip 간 그래프를 산점도로 표현
    • sns.scatterplot(x = 'total_bill', y = 'tip', data = tips)
  • pairplot: 전체 변수에 대한 시각화
    • 한 번에 여러 개의 변수를 동시에 시각화하고 싶을 때
    • x축 수치형 or 범주형 자료
    • y축 수치형 or 범주형 자료
    • 대각선: 히스토그램
    • sns.pairplot(tips)

데이터 전처리

  • 전체 분석 프로세스에서 90%를 차지할 정도로 노동, 시간 집약적인 단계

 

이상치

  • 관측된 데이터 범위에서 많이 벗어난 아주 작은 값 혹은 큰 값
  • ESD 이용한 이상치 발견
    • 데이터가 정규분포 따른다고 가정했을 때, 평균에서 표준편차의 3배 이상 떨어진 값
    • 하지만 모든 데이터가 정규 분포를 따르지 않을 수 있기 때문에 다음 상황에서는 제한됨
      • 데이터가 크게 비대칭일 때 (Log 변환을 이용해 볼 수 있음)
      • 샘플 크기가 작을 경우
  • IQR 이용한 이상치 발견
    • ESD와 동일하게 비대칭이거나 샘플사이즈가 작은 경우 제한됨
    • Box plot: 데이터의 사분위 수 포함하여 분포 보여주는 시각화 그래프
      • 사분위 수: 데이터를 순서에 따라 4등분한 것
  • 조건 필터링을 통한 삭제: df[df['column'] > limit_value]
# ESD 이상치 처리
import numpy as np
mean = np.mean(tips['total_bill'])
std = np.std(tips['total_bill'])
upper_limit = mean + 3*std
lower_limit = mean - 3*std

condition = (tips_df['total_bill'] > upper_limit)
tips[condition]

# IQR 이상치 처리
import seaborn as sns
sns.boxplot(tips['total_bill'])

q1 = df['column'].quantile(0.25)
q3 = df['column'].quantile(0.75)
IQR = q3 - q1
upper_limit = q3 + 1.5*IQR
lower_limit = q1 - 1.5*IQR

condition = (tips['total_bill'] > upper_limit)
tips[condition]
  • 이상치는 사실 주관적인 값이며 데이터 삭제 여부는 분석가가 결정해야 한다
  • 이상치는 도메인과 비즈니스 맥락에 따라 그 기준이 달라지며, 데이터 삭제 시 품질은 좋아질 수 있지만 정보 손실을 동반하기 때문에 이상치 처리에 주의해야 한다
  • 단지, 통계적 기준에 따라 결정 할 수 있다는 점만 기억
  • 또한, Anomlay detection으로 데이터에서 패턴을 다르게 보이는 개체를 찾는 방법으로도 발전 가능하다

 

결측치

  • 존재하지 않는 데이터
  • 수치형 데이터 처리 방법
    • 평균 값 대치: 대표적인 대치 방법
    • 중앙 값 대치: 데이터에 이상치가 많아 평균 값이 대표성이 없다면 중앙 값 이용
  • 범주형 데이터 처리 방법
    • 최빈 값 대치
  • 사용 함수
    • 삭제 & 대치
      • df.dropna(axis = 0) : 행 삭제
      • df.dropna(axis = 1) : 열 삭제
      • df.fillna(value) : 특정 값으로 대치 (평균, 중앙, 최빈값)
    • 알고리즘 이용
      • sklearn.impute.SimpleInputer : 평균, 중앙, 최빈값으로 대치
        • SimpleImputer.statistics_ : 대치한 값 확인 가능
      • sklearn.impute.IterativeImputer : 다변량대치(회귀 대치)
      • sklearn.impute.KNNImputer : KNN 알고리즘을 이용한 대치
  • isna() : 비어있는 값 확인
  • notna() : 비어있지 않은 값 확인
# 행 제거
df.dropna(axis=0)

# 결측치 없는 행만 추출
cond = (df['Age'].notna())
df[cond]

# fillna 이용
age_mean = df['Age'].mean().round(2)
df['Age_mean'] = df['Age'].fillna(age_mean)

# SimpleImputer 이용
from sklearn.impute import SimpleImputer
si = SimpleImputer()
si.fit(df[['Age']])

si.statistics_  # 평균 값

df['Age_si_mean'] = si.transform(df[['Age']])
  • 대표적인 알고리즘 K-Nearest Neighbors(k 최근접 이웃)이라는 방법도 존재

범주형 데이터 전처리 - 인코딩

  • 범주형 데이터에 적용!
  • 어떤 정보를 정해진 규칙에 따라 변환하는 것
  • 머신러닝 모델은 숫자를 기반으로 학습하기 때문에 반드시 인코딩 과정이 필요

 

크게 2가지로 나뉨

  • 레이블 인코딩
    • 정의: 문자열 범주형 값을 고유한 숫자로 할당
      • 1등급 ➡️ 0
      • 2등급 ➡️ 1
      • 3등급 ➡️ 2
    • 장점: 모델이 처리하기 쉬운 수치형으로 데이터 변환
    • 단점: 실제로는 그렇지 않은데 순서 간 크기에 의미가 부여되어 모델이 잘못 해석할 수 있음
    • sklearn.preprocessing.LabelEncoder
      • 메소드
        • fit: 데이터 학습
        • transform: 정수형 데이터 변환
        • fit_transform: fit과 transform 한 번에 실행
        • inverse_transform: 인코딩된 데이터 원래 문자열로 반환
      • 속성
        • classes_: 인코더가 학습한 클래스(범주)
  • 원-핫 인코딩
    • 정의: 각 범주를 이진 형식으로 변환하는 기법
      • 빨강 ➡️ [1, 0, 0]
      • 파랑 ➡️ [0, 1, 0]
      • 초록 ➡️ [0, 0, 1]
    • 명목형 데이터에 권장
    • 장점: 각 범주가 독립적으로 표현되어 순서가 중요도를 잘못 학습하는 것을 방지
    • 단점: 범주 개수가 많을 경우 차원이 크게 증가, 모델의 복잡도가 증가, 과적합 유발
    • 사용 함수
      • 1. pd.get_dummies: 더미화
      • 2. sklearn.preprocessing.OneHotEncoder
        • 메소드는 LabelEncoder와 동일
        • categories_: 인코더가 학습한 클래스(범주)
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
le = LabelEncoder()
oe = OneHotEncoder()

# 성별은 LabelEncoder
le.fit(df[['sex']])
le.classes_

df['sex_le'] = le.transform(df[['sex']])

# 항구는 OneHotEncoder
oe.fit(df[['embarked']])
oe.categories_

embarked_csr = oe.transform(df[['embarked']])   # 차원이 엄청나게 큼
embarked_csr_df = pd.DataFrame(embarked_csr.toarray(), columns=oe.get_feature_names_out())    # toaaray로 쭉 피고, 컬럼 목록 가져와 데이터프레임 화 시킴
pd.concat([df, embarked_csr_df], axis=1)

수치형 데이터 전처리 - 스케일링

  • 수치형 데이터에 적용!
  • 머신러닝 학습에 사용되는 데이터들은 서로 단위 값이 다르므로 보정하는 것

 

이론적으로  3가지가 있지만 2개를 주로 사용

  • 표준화(Standardization)
    • 각 데이터에 평균을 빼고 표준편차를 나누어 평균을 0, 표준편차를 1로 조정하는 방법
    • 함수: sklearn.preprocessing.StandardScaler
      • 메소드 
        • fit: 데이터 학습(평균과 표준편차 계산)
        • transform: 데이터 스케일링 진행
      • 속성
        • mean_: 데이터의 평균값
        • scaler_, var_: 데이터의 표준 편차, 분산값
        • n_features_in_: fit 할때 들어간 변수의 개수
        • feature_names_in_: fit 할 때 들어간 변수 이름
        • n_samples_seen_: fit 할때 들어간 데이터의 개수
    • 장점
      • 이상치가 있거나 분포가 치우쳐져 있을 때 유용
      • 모든 특성의 스케일을 동일하게 맞춤
    • 단점
      • 데이터의 최소/최대 값이 정해져있지 않음
  • 정규화(Normalization)
    • 데이터를 0과 1사이 값으로 조정
    • 함수: sklearn.preprocessing.StandardScaler
      • 속성
        • data_min_: 원 데이터의 최소 값
        • data_max_: 원 데이터의 최대 값
        • data_range_: 원 데이터의 최대-최소 범위
    • 장점
      • 이상치가 없을 때 유용 
      • 모든 특성의 스케일을 동일하게 맞춤
      • 최대-최소 범위가 명확
    • 단점
      • 이상치에 영향을 많이 받을 수 있음
    •  
  • 로버스트 스케일링(Robust Scaling)
    • 중앙값과 IQR을 사용하여 스케일링
    • 함수: sklearn.preprocessing.RobustScaler
      • 속성
        • center_: 훈련데이터의 중앙값
    • 장점
      • 이상치의 영향에 덜 민감
    • 단점
      • 표준화와 정규화에 비해 덜 사용됨

 

실습 (age는 MinMaxScaler, fare는 StandardScaler 적용)

연속 데이터
통계 정보

from sklearn.preprocessing import MinMaxScaler, StandardScaler
mm_sc = MinMaxScaler()
sd_sc = StandardScaler()

titanic['age_mm_sc'] = mm_sc.fit_transform(titanic[['age']])
titanic['fare_sd_sc'] = sd_sc.fit_transform(titanic[['fare']])
titanic.head(3)

데이터 분리

 

과적합

  • 데이터를 너무 과하게 학습해서 해당 문제만 잘 맞추고 새로운 데이터에 대해서는 잘 맞추지 못하는 현상

 

예측 혹은 분류를 하기 위해 모델 복잡도를 설정

  • 모델이 지나치게 복잡할 때: 과대적합 발생 가능성 존재
  • 모델이 지나치게 단순할 때: 과소적합 발생 가능성 존재

 

원인

  1. 모델의 복잡도
  2. 데이터 양이 충분하지 않은 경우
  3. 학습 반복이 많은 경우(딥러닝)
  4. 데이터 불균형의 경우

 

해결법 - 테스트 데이터의 분리

  • 함수: sklearn.model_selection.train_test_split
    • 파라미터:
      • test_size: 테스트 데이터 세트 크기
      • train_size: 학습 데이터 세트 크기
      • shuffle: 데이터 분리 시 섞기
      • random_state: 동일한 데이터로 분리하기 위한 난수 값, 실행시킬 때마다 같은 데이터로 분리됨
      • stratify: 예를 들어 label이 0, 1의 비율이 8:2 일 때 그냥 split을 하게 되면 0이 많이 뽑힐 수도 있다. 하지만 stratify=y를 주게 되면 원본 데이터의 label의 비율을 이용해 split된 데이터의 label의 비율도 맞춰준다.
    • 반환 값:
      • X_train, X_test, y_train, y_test

실습 - x: fare, sex, y:survived

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(titanic[['fare', 'sex']], titanic['survived'],
                                                    test_size=0.3, shuffle=True, random_state=42, stratify=y)

print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)

실습: 데이터 전체 프로세스 적용

  1. 데이터 로드 & 분리 ➡️ train / test
  2. 탐색적 데이터 분석(EDA) ➡️ 분포 & 이상치
  3. 데이터 전처리
    1. 결측치: 수치형 / 범주형 / 삭제
    2. 전처리: 수치형 / 범주형(레이블 인코딩, 원-핫 인코딩)
  4. 모델 수립
  5. 평가

교차 검증과 GridSearch

  • 위에서 모델을 평가하기 위해 별도의 테스트 데이터로 평가하는 과정을 알아봄
  • 하지만, 고정된 테스트 데이터가 존재하기 때문에 과적합에 취약한 단점

 

교차 검증

  • 데이터 셋을 여러 개의 하위 집합으로 나누어 돌아가면서 검증 데이터로 사용하는 방법

 

K-Fold Validation

  • Train Data를 K개의 하위 집합으로 나누어 모델 학습시키고 모델을 최적화하는 방법
    • K는 분할의 개수
  • 특징: 데이터가 부족할 경우에 유용(반복 학습)
  • 함수
    • sklearn.model_selection.KFold
    • sklearn.model_selection.StrifiedKFold: 불균형한 레이블 가지고 있을 때 사용
from sklearn.model_selection import KFold

kfold = KFold(n_splits = 5)
scores = []
X = train_df_2[['Age_mm_sc', 'Fare_sd_sc', 'Family_mm_sc', 'Pclass_le', 'Sex_le', 'Embarked']]
y = train_df_2['Survived']
for i, (train_index, test_index) in enumerate(kfold.split(X)):
	X_train, X_test = X.values[train_index], X.values[test_index]
    y_train, y_test = y.values[train_index], y.values[test_index]
    
    from sklearn.linear_model import LogisticRegression
    from sklearn.metrics import accuracy_score
    model_lor2 = LogisticRegression()
    model_lor2.fit(X_train, y_train)
    y_pred2 = model_lor2.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred2).round(3)
    print(i, '번째 교차검증 정확도는', accuracy)
    scores.append(acuuracy)
   
print("평균 정확도", np.mean(scores))

 

하이퍼 파라미터 자동 적용하기 - GridSearch

  • 하이퍼 파라미터: 사람이 정해줘야 하는 모델의 입력 값
  • GridSearch: 다양한 값을 넣고 실험하는 것을 자동화해주는 것
from sklearn.model_selection import GridSearchCV

params = {'solver': ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga'],
		  'max_iter': [100, 200]}
grid_lor = GridSearchCV(model_lor2, param_grid = params, scoring = 'accuracy', cv = 5)
grid_lor.fit(X_train, y_train)

print("최고의 파라미터", grid_lor.best_params_)
print("최고의 정확도", grid_lor.best_score_.round(3))