머신러닝 활용 품질관리

Chapter.8 머신러닝 활용 품질관리(2)

devjjun 2026. 6. 16. 16:12

TIL(Today I Learned)


1️⃣실무에 바로 쓰는 머신러닝 기초 2

데이터 전처리란?

원시(raw) 데이터에서 불필요하거나 손실(노이즈)이 있는 부분을 처리하고
분석 목적에 맞는 형태로 만드는 과정

 

필요성

  1. 모델 정확도 및 신뢰도 향상
  2. 이상치나 결측치가 존재하면 모델 학습 성능이 크게 떨어짐
  3. 효율적인 데이터 분석과 모델 훈련을 위한 필수적인 단계

결측치 처리

결측치 발생 원인

  1. 삭제(Removal)
    • 결측치가 있는 행(row) 또는 열(column)을 제거
      ->간단하지만 데이터 손실이 발생
      ->결측치가 전체 데이터에서 매우 소수일 때 적합
  2. 대치(Imputation)
    • 평균 또는 중앙값으로 대체 
      ->수치형 데이터에서 많이 사용/분포 왜곡 적음
    • 최빈값으로 대체 
      -> 범주형 데이터에서 사용
    • 예측 모델로 대체
      -> 회귀/분류 모델을 이용해 결측값을 예측 

결측치 처리 방법 코드

결측 데이터

# 1) 결측치 제거 (결측이 하나라도 있으면 해당 행을 제거)
df_drop = df.dropna()
df_drop

 

# 2) 평균값으로 대치
df_mean = df.copy()
df_mean = df_mean.fillna(df_mean.mean(numeric_only=True))
df_mean

 

# 3) 중앙값으로 대치
df_median = df.copy()
df_median = df_median.fillna(df_median.median(numeric_only=True))
df_median

 

# 4) 최빈값으로 대치
#   - DataFrame의 mode()는 각 열별로 최빈값을 반환
#   - mode() 결과가 여러 개(동률)일 경우 첫 번째 행의 값 취함
df_mode = df.copy()
print(df_mode.mode()) # 확인용
mode_values = df_mode.mode().iloc[0]  # 첫 번째 행(가장 상위 mode)만 취함
df_mode = df_mode.fillna(mode_values)
df_mode

이상치 탐지 및 제거 

이상치란?

  • 정상 범주에서 크게 벗어나는 값 
  • 장비 오작동, 환경적 특이 상황 등 원인이 다양함 

탐지 기법

  1. 통계적 기법
    • 데이터가 정규분포를 따른다고 가정하고, 평균에서 ±3σ(표준편차) 범위를 벗어나는 값을 이상치로 간주
    • 직관적이고 간단하나, 정규성 가정이 틀릴 수 있음
  2. 박스플롯 기준
    • 사분위수(IQR = Q3 - Q1)를 이용해 ‘Q1 - 1.5×IQR’, ‘Q3 + 1.5×IQR’를 벗어나는 데이터를 이상치로 간주
    • 분포 특성에 영향을 적게 받는 장점
  3. 머신러닝 기반
    • 이상치 탐지 알고리즘 (Isolation Forest, DBSCAN 등)
    • 복합적 패턴을 고려할 수 있음

처리 기법 

  1. 이상치 단순 제거
  2. 이상치 값을 조정(클리핑, Winsorizing 등) 
  3. 별도로 구분하여 모델에서 제외하거나, 다른 모델(이상치 예측 모델)로 활용 

이상치 제거 방법 코드 

이상치 존재 데이터

# 이상치 제거 (박스플롯 기준 적용 예시)
Q1 = df['sensor_value'].quantile(0.25)
Q3 = df['sensor_value'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df = df[(df['sensor_value'] >= lower_bound) & (df['sensor_value'] <= upper_bound)]
df

 

정규화/표준화

필요 이유 

  1. 모델(거리 기반 알고리즘, 딥러닝 등)에 따라 특정 변수의 스케일이 크게 영향을 미칠 수 있음
  2. 센서 A는 값 범위가 0 ~ 1000, 센서 B는 값 범위가 0 ~ 1이라면 A가 모델에 더 큰 영향을 줌 

정규화 

  • MinMaxScaler: 모든 값은 0과 1 사이로 매핑

  • 특징
    • 값의 스케일이 달라도 공통 범위로 맞춤
    • 딥러닝, 이미지 처리 등에서 입력값을 0~1로 제한할 때 자주 사용 
    • 거리 기반 알고리즘이나 각 특성의 범위를 동일하게 함으로써 계산 안정성 확보
    • 최소,최대값이 극단값에 민감 극단치가 있으면 왜도 발생 
    • 새로운 데이터 추가에 주의 

표준화

  • StandardScaler: 평균을 0, 표준편차를 1로 만듦 

  • 특징
    • 분포가 정규분포에 가깝게 변형됨 
    •  정규분포 가정을 사용하는 알고리즘(선형회귀, 로지스틱회귀, SVM 등)에 자주 쓰임
    • 변환된 값이 -inf ~ +inf 범위 가능 
    • 분포의 왜도가 심할시 충분한 스케일링 불가능(로그변환, RobustScaler 추가 고려)

정규화/표준화 코드 

예시 데이터

 

from sklearn.preprocessing import MinMaxScaler

# 스케일링을 적용할 컬럼만 선정
cols_to_scale = ['impressions', 'clicks', 'conversions', 'cost', 'revenue']

# MinMaxScaler 객체 생성(기본 스케일: [0,1])
minmax_scaler = MinMaxScaler()

# fit_transform을 통해 스케일링된 결과를 데이터프레임으로 변환
df_minmax_scaled = pd.DataFrame(minmax_scaler.fit_transform(df[cols_to_scale]), 
                                columns=cols_to_scale)                            
print(df_minmax_scaled.max())
print(df_minmax_scaled.min())
                     
df_minmax_scaled

 

from sklearn.preprocessing import StandardScaler

# StandardScaler 객체 생성
standard_scaler = StandardScaler()

# fit_transform을 통해 스케일링된 결과를 데이터프레임으로 변환
df_standard_scaled = pd.DataFrame(standard_scaler.fit_transform(df[cols_to_scale]), 
                                  columns=cols_to_scale)

print(df_standard_scaled.mean())
print(df_standard_scaled.std())
df_standard_scaled

 

불균형 데이터 처리 

불균형 데이터란? 

정상 99%, 불량 1%처럼 한 클래스가 극도로 적은 경우 

문제점

  • 모델이 극도로 적은 클래스를 거의 예측하지 못할 가능성이 큼(편향 발생)

해결 기법 

  1. 오버 샘플링: 클래스를 단순히 복제하여 개수를 늘림 
    • SMOTE: 소수 클래스를 무작정 복사하는 것이 아니라
      비슷한 데이터들을 서로 섞어서 새로운 데이터 생성
  2. 언더 샘플링: 다수 클래스의 데이터를 줄이는 방식
  3. 혼합 기법: SMOTE와 언더샘플링을 적절히 섞어서 사용  

불균형 데이터 처리 코드 

예시 데이터

 

데이터 분포 확인

from imblearn.over_sampling import SMOTE
# 불균형 데이터 처리 (SMOTE)
X = df.drop('defect', axis=1)   # 결측치 처리, 이상치 제거, 인코딩 등 사전 처리 후
y = df['defect']
smote = SMOTE(random_state=42)
X_res, y_res = smote.fit_resample(X, y)

범주형 데이터 변환 

원-핫 인코딩(One-Hot Encoding)

  • 범주형 변수를 각각의 범주별로 새로운 열로 표현, 해당 범주에 해당하면 1, 아니면 0
  • 장점: 범주 간 서열 관계가 없을 때 사용
  • 단점: 범주가 매우 많을시 차원이 커짐 

원-핫 인코딩 코드 

예시 데이터

# 범주형 변수 변환 (원-핫 인코딩 예시)
df = pd.get_dummies(df, columns=['label'])

레이블 인코딩 

  • 범주를 숫자로 직접 맵핑
  • 모델이 숫자의 크기를 서열 정보로 잘못 해석할 수 있음 

레이블 인코딩 코드 

예시 데이터

# 범주형 변수 변환 (레이블 인코딩 예시)
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
df["label"] = encoder.fit_transform(df["label"])
df

피처 엔지니어링

모델 성능 향상을 위해 기존 데이터를 변형, 조합하여 새로운 특성(피처)를 만드는 작업 

중요성 

  • 복잡한 데이터 구조 안에 존재하는 패턴을 효과적으로 추출해 모델이 쉽게 학습을 함 

변수 선택 

  • 상관관계
    • 두 변수 간 상관관계가 높은 상황인 경우 다중공선성 의심, 하나만 남기거나 둘 다 제거
  • VIF
    • 회귀분석에서 다중공선성 문제를 파악할 때 사용 
    • VIF는 어떤 변수 하나가 다른 변수들과 얼마나 겹치는지(상관이 큰지) 수치로 보여주는 지표
    • 일정기준(10)이 넘으면 제거하거나 비슷한 변수를 합치는(변환) 방법으로 해결 
  • 모델 기반 중요도(Feature Importance)
    • 트리 기반 모델(랜덤 포레스트, XGBoost 등)을 훈련 후 중요도가 낮은 변수를 제거

2️⃣프로젝트 부트캠프: NASA 엔진 수명 예측 실습 (2)

데이터 EDA 계획 

EDA 항목 대상 사용 방법 / 시각화 기대 결과
데이터 구조 파악 엔진 수, 데이터 타입 .shape, .info(), nunique() 전체 규모 확정 (엔진 100대)
결측치 점검 NaN, 빈 컬럼 유무 .isnull().sum(), .describe() 데이터 클린 여부 확인
엔진별 수명 분포 엔진마다 다른 사이클 수 엔진별 max cycle 히스토그램 수명 범위 파악 
센서 마모 추세 분석  RUL 따라 증가/감소하는 센서 잔존 수명 별 센서값 분포 유효 센서 선별 (핵심 피처)
센서 간 상관관계 같이 움직이는 중복 센서 상관 히트맵 다중공선성 파악 → 변수 정리
타겟(RUL) 분포 train RUL 값 분포 RUL 히스토그램 RUL 추세 확인

 

결측치 점검 

# 결측치 점검 
def check_null(dfs):
  for i in range (len(dfs)):
    print(dfs[i].isnull().sum().sum())

datas = [df_train,df_test,test_last]

check_null(datas)

# 출력값 
0
0
0

엔진별 수명 분포 

더보기
# 엔진별 max_cycle 히스토그램 
import seaborn as sns
import matplotlib.pyplot as plt

life = df_train.groupby('engine_id')['cycle_n'].max()
life.plot(kind='hist', bins=20)
plt.xlabel('Max Cycle)')
plt.ylabel('Engine Count')

잔여 수명 별 센서값 추세 

더보기
# 잔여 수명 별 추세
eng = df_train[df_train['engine_id'] == 1]   # 엔진 1대
sensor_cols = [c for c in df_train.columns if c.startswith('sensor')]

fig, axes = plt.subplots(7, 3, figsize=(15, 18))   # 21개 = 7행 3열
axes = axes.flatten()

for i, sensor in enumerate(sensor_cols):
    axes[i].plot(eng['RUL'], eng[sensor])
    axes[i].set_title(sensor)
    axes[i].invert_xaxis()

plt.tight_layout()
plt.show()

 

상관관계 분석 

더보기
sensor_cols = [c for c in df_train.columns if c.startswith('sensor')]

corr = df_train[sensor_cols].corr()

plt.figure(figsize=(12, 8))
sns.heatmap(corr,
            annot=True,        
            fmt='.2f',          
            center=0,         
            square=True,
            cbar_kws={'shrink': 0.8})
plt.tight_layout()
plt.show()

타겟변수 분포 확인

더보기
# 타겟 분포 
plt.figure(figsize=(10, 5))
plt.hist(df_train['RUL'], bins=50, edgecolor='black')
plt.xlabel('RU')
plt.ylabel('Count')
plt.show()


💡 느낀점

머신러닝을 단계적으로 복습하며, 어떠한 과정에서 어떤 기법을 사용할지 다시 한 번 상기시킬 수 있었다. 이 과정에서 상기시킨 기법을 라이브세션 실습을 진행하며 고민하는 과정을 통해 구체적인 EDA 계획을 세우고 시각화하여 인사이트를 도출하는 과정에서 부족함을 느꼈다. 그리하여 EDA 계획표를 세우고 전체적인 흐름을 정리하며 진행한 결과 어떠한 대상을 어느 방식으로 적용시켜 결과를 도출해갈지 갈피를 잡을 수 있었다. 내일은 전처리 과정을 인사이트를 통해 얻은 결과로 진행해볼 예정이다.