데이터 전처리
데이터를 분석 및 처리에 적합한 형태로 만드는 과정을 총칭한다.
우수한 예측 분석 결과는 잘 정돈된 데이터에서 출발한다. 정교한 예측 분석 모델을 얻기 위해서는 수집된 데이터에 누락된 부분이나, 오차, 또는 데이터 처리에 있어서 가공할 부분은 없는지 확인해야 한다.
One-hot Encoding(원-핫 인코딩)
단 하나의 값만 True 이고 나머지는 모두 False 인 인코딩을 의미한다. 모든 범주형 변수를 정수인 0,1 의 이진형 벡터로 표시하면서 변수를 열거하고 해당하지 않는 모든 항목은 0으로 표시, 해당하는 항목은 1로 표시한다.
데이터 형태는 0,1 로 이루어져 있기 때문에 컴퓨터가 인식하고 학습하기에 용이하다.
입력값으로 2차원 데이터가 필요하다.
인코딩 결과가 밀집 행렬(Dense Matrix)이기 때문에 다시 희소 행렬(Parse Matrix)로 변환해야한다.
from sklearn.preprocessing import OneHotEncoder
import numpy as np
maker = ['Samsung', 'LG', 'Apple', 'SK']
maker = np.array(maker).reshape(-1,1)
#print(maker)
encoder = OneHotEncoder()
# 인코딩 단계
encoder.fit(maker) # 해당 데이터를 가지고 학습한다.
one_hot = encoder.transform(maker) # transform : 정보를 가지고 실제로 변환하는 단계
print('원-핫 인코딩 결과(밀집행렬)')
print(one_hot) # 위치 정보를 튜플로 반환
# 네 개의 데이터를 가지고 있기 때문에 4개의 열이 생김
print()
print('원-핫 인코딩 결과(희소행렬)')
print(one_hot.toarray()) # 배열로 바뀌는 것
# 유니크 값을 알파벳 순으로 정렬해서 위치를 잡음
원-핫 인코딩 결과(밀집행렬)
(0, 3) 1.0
(1, 1) 1.0
(2, 0) 1.0
(3, 2) 1.0
원-핫 인코딩 결과(희소행렬)
[[0. 0. 0. 1.]
[0. 1. 0. 0.]
[1. 0. 0. 0.]
[0. 0. 1. 0.]]
One-hot Encoding(원-핫 인코딩) 은 1. Scikit-Learn , 2. Pandas 총 두가지 방법이 있다.
1. Scikit-Learn
import pandas as pd
df = pd.DataFrame( { 'maker' : ['Samsung', 'LG', 'Apple', 'SK'] })
encoder = OneHotEncoder()
encoded = encoder.fit_transform(df['maker'].values.reshape(-1,1)).toarray().astype(int)
# 변환을 하기 위한 준비와 실제 변환을 동시에 진행
# encoded : 밀집행렬 형태 -> 희소행렬 형태
# print(encoded)
# 데이터 프레임은 새로운 컬럼을 생성해야함
print(np.sort(df['maker']))
df[['maker-apple','maker-lg','maker-sk','maker-samsung']] = encoded
df
판다스를 이용하여 데이터 프레임을 만든다.
결과는 밀집행렬 -> 희소행렬로 나타난다.
2. Pandas
import pandas as pd
df = pd.DataFrame( { 'maker' : ['Samsung', 'LG', 'Apple', 'SK'] })
pd.get_dummies(df, columns=['maker']) # 컬럼명 _ 갖고 있는 유니크 값
행의 개수 만큼 자동적으로 만들어준다.
column 명을 지정해주면 '컬럼명 _ 갖고 있는 유니크 값' 으로 자동적으로 이름을 생성해준다.
Label Encoding(라벨 인코딩)
명목형 변수의 값을 알파벳 정렬 순서로 할당해 주는 것을 말한다.
명목형 변수는 가급적으로 원-핫 인코딩으로 처리해주지만, 명목형 변수의 범주가 너무 많아 컬럼의 수가 과해지면 라벨인코딩을 한다.
Label Encoding(라벨 인코딩) 은 1. Scikit-Learn , 2. Pandas 총 두가지 방법이 있다.
1. Scikit-Learn
from sklearn.preprocessing import LabelEncoder
language = ['Java','Python','C#','Pascal']
df = pd.DataFrame({'language': language})
print('라벨 인코딩 전')
display(df)
encoded= LabelEncoder().fit_transform(language)
# print(encoded) # 알파벳 순서대로 정렬한 후 순서를 매김
df['language'] = encoded
print('라벨 인코딩 후')
df
2. Pandas
import pandas as pd
language = ['Java','Python','C#','Pascal']
df = pd.DataFrame({'language': language})
# mapping 정보 -> 딕셔너리 컴플리헌트 사용
map_data = { v:i for i,v in enumerate(df['language'].sort_values())}
df['language'] = df['language'].map(map_data)
df
Pandas 는 df.컬럼명.map(딕셔너리 객체) 의 방법을 사용한다.
데이터 스케일링 (Data Scaling)
데이터 분포가 달라 학습이 원활히 되지 않는 경우 지정한 값의 범위로 변경하여 전반적인 데이터 분포를 맞춰주는 기법
정규화 Normalization
속성값의 범위를 0 ~ 1 사이의 값으로 맞추는 작업
x = x - min / max - min
표준화 StanDardization
속성값의 범위를 평균은 0에 표준편차는 1에 가깝게 만드는 작업(표준정규분포의 값)
이상치 정보가 그대로 유지되는 특징이 있다.
x = x - mean(x) / st.dev (= 표준편차) ( ※ z - score )
=> 이상치가 많은 경우에는 이상치 제거를 위해 표준화를 하는 것이 유리하고 상대적 크기를 줄이는 것은 정규화가 유리하다.
iris 데이터는 분류 분석의 가장 대표적인 데이터 예시로 쓰인다.
from sklearn.datasets import load_iris # 분류분석의 대표적 데이터
import pandas as pd
iris = load_iris()
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names) # 독립변수
# iris_df['target'] = iris.target
# print(iris_df['target'].unique()) # 라벨링을 통해 꽃의 종류를 0,1,2 로 둠
# display(iris_df.head())
print('컬럼 평균')
print(iris_df.mean())
print('컬럼 분산')
print(iris_df.var())
컬럼 평균
sepal length (cm) 5.843333
sepal width (cm) 3.057333
petal length (cm) 3.758000
petal width (cm) 1.199333
dtype: float64
컬럼 분산
sepal length (cm) 0.685694
sepal width (cm) 0.189979
petal length (cm) 3.116278
petal width (cm) 0.581006
dtype: float64
원 데이터의 평균과 분산값이다.
1. 표준화 StanDardization
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler() # 객체 생성
scaler.fit(iris_df) # 표준화 위한 기본정보 수집
iris_scaled = scaler.transform(iris_df)
# 4,5 번째 줄을 한 번에 정리한 문장
# iris_scaled = scaler.fit_transform(iris_df)
iris_scaled_df = pd.DataFrame(iris_scaled, columns= iris.feature_names)
display(iris_scaled_df.head()) # 평균 0이고 표준편차 =1 인 데이터
print('표준화된 컬럼 평균')
print(iris_scaled_df.mean())
print('표준화된 컬럼 분산')
print(iris_scaled_df.var())
# 특정 범위 안에 데이터들을 얻을 수 있다.
# -1.69 * 10^-15 0에 근사한 값
표준화된 컬럼 평균
sepal length (cm) -1.690315e-15
sepal width (cm) -1.842970e-15
petal length (cm) -1.698641e-15
petal width (cm) -1.409243e-15
dtype: float64
표준화된 컬럼 분산
sepal length (cm) 1.006711
sepal width (cm) 1.006711
petal length (cm) 1.006711
petal width (cm) 1.006711
dtype: float64
-1.69 * 10^-15 = 0에 근사한 값으로 평균은 0에 표준편차는 1에 가깝게 속성값이 변한 것을 볼 수 있다.
2. 정규화 Normalization
from sklearn.preprocessing import MinMaxScaler
iris_scaled = MinMaxScaler().fit_transform(iris_df) # 변환까지 => 모든 데이터가 0-1 사이의 값
iris_scaled_df = pd.DataFrame(iris_scaled, columns= iris.feature_names)
print('정규화된 컬럼 최소값')
print(iris_scaled_df.min())
print('정규화된 컬럼 최대값')
print(iris_scaled_df.max())
정규화된 컬럼 최소값
sepal length (cm) 0.0
sepal width (cm) 0.0
petal length (cm) 0.0
petal width (cm) 0.0
dtype: float64
정규화된 컬럼 최대값
sepal length (cm) 1.0
sepal width (cm) 1.0
petal length (cm) 1.0
petal width (cm) 1.0
dtype: float64
속성의 값을 0 ~1 사이의 값으로 맞추게 되었으므로 가장 작은 값이 0 , 가장 큰 값이 1로 나오게 된다.
>> fit() : 데이터 스케일링을 처리한다(정규화: 최소, 최대값 인지/ 표준화: 평균, 표준편차 계산)
>> transform() : 실제 변환작업을 수행한다.
>> fit_transform() : fit() 과 transform() 을 동시에 처리한다.
학습데이터에 한해 사용 -> 테스트 데이터 기반으로 다른 기준을 적용하게 되기 때문에 평가가 적절하게 되지 않기 때문이다.
불균형 데이터 처리
데이터의 분포가 각 범주별로 골고루 분포되어있는 것이 아니라 어느 한쪽으로 심하게 치우쳐져 있는 데이터를 말한다.
(정상 범주의 관측치 수와 이상 범주의 관측치 수가 크게 차이 나는 것)
불균형 데이터는 높은 예측 정확도를 보일 수는 있어도 정밀도와 재현도가 낮아질 수 있어 분류 모형의 성능이 저하되는 문제가 생길 수 있다.
샘플링 기법
- 언더 샘플링 Under Sampling : 다수의 데이터를 줄이는 방법
: 다수의 유의미한 데이터가 손실될 수 있으므로 소수의 데이터가 확보되어 있는 상황에서 사용하는 것이 적합하다.
- 오버 샘플링 Over Sampling : 소수의 데이터를 부풀리는 방법
:과적합이 생길수도 있다는 단점이 존재한다.
- 모델 조정 기법
- 비용 민감 학습 Cost sensitive learning : 클래스의 오분류에 대한 cost의 가중치를 조절하여 학습하는 방법
불균형 데이터 처리 예시
불균형 데이터 처리를 위한 ' imbalanced-learn 모듈' 설치
!pip install scikit-learn==1.2.2 --user
!pip install imbalanced-learn
!pip install imblearn
필요한 패키지들
import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from collections import Counter
import matplotlib.pyplot as plt
import seaborn as sns
from imblearn.under_sampling import RandomUnderSampler, TomekLinks
from imblearn.over_sampling import RandomOverSampler, SMOTE
0. 샘플 불균형 데이터 생성
x,y = make_classification(n_samples= 10000 , n_features= 5, weights= [0.99], flip_y= 0, random_state= 10) # 분류모형 샘플 데이터수
# flip_y : y값이 임의의 변경되는 비율 (default = 0.01)
# 불순데이터를 만들지 않고 정확하게 만개 중에 9900 개는 0 , 100개는 1
# n_features : 독립변수의 개수 , weights: 종속변수의 비율,
# n_class : 종속변수의 개수 (지정하지 않으면 두 개가 디폴트)
print(x[:2,:])
print(x.shape, y.shape)
print(np.unique(y))
print(Counter(y))
sns.scatterplot(x = x[:,3], y = x[:,4], hue= y)
plt.show()
[[ 0.38201186 -0.10502417 -1.65203303 -0.0260045 0.80085356]
[ 0.03174755 0.65503326 0.94612307 0.32881832 -0.80422025]]
(10000, 5) (10000,)
[0 1]
Counter({0: 9900, 1: 100})
** sampling_strategy
'majority' : 다수 집단 데이터만 샘플링
'not minority': 소수 집단을 제외한 나머지 범주 데이터를 리샘플링
'not majority' : 다수 집단을 제외한 나머지 범주 데이터를 리샘플링
'all' : 모든 범주 데이터를 리샘플링
'auto' : 'not minarity' 와 동일하다.
1. 언더 샘플링 Under Sampling
- Random Undersampling
다수 범주의 샘플들을 무작위로 선택 (무작위 선택으로 인해 샘플링 결과에 따라 분류경계면의 변동이 심하다.)
# sampling_strategy = 'majority': 다수집단에서 언더샘플링 하여 소수 집단의 수와 동일하게 맞추는 방식이다.
under_sample = RandomUnderSampler(sampling_strategy = 'majority')
x_under, y_under = under_sample.fit_resample(x,y)
print(Counter(y_under)) # 소수 집단인 1에 맞춰 0이 100으로 줄었다.
sns.scatterplot(x = x_under[:,3], y = x_under[:,4], hue= y_under)
plt.show() # 데이터가 축소된 것을 알 수 있다.
Counter({0: 100, 1: 100})
다수집단인 0이 소수집단인 1의 개수만큼 줄었다.(유효한 다수의 데이터가 날아갔다.)
비율 지정 : 비율값(소수집단 데이터수 / 다수집단데이터 수)으로 다수집단의 데이터를 샘플링
# sampling_strategy 의 값을 0 에서 1사이의 값으로 지정하면 비율값(소수집단 데이터수 / 다수집단데이터 수)으로 다수집단의 데이터를 샘플링한다.
# 0.4 = 100/250(다수집단의 수)
under_sample = RandomUnderSampler(sampling_strategy = 0.4)
x_under, y_under = under_sample.fit_resample(x,y)
print(Counter(y_under)) # 소수 집단인 1에 맞춰 0이 250으로 줄었다.
sns.scatterplot(x = x_under[:,3], y = x_under[:,4], hue= y_under)
plt.show() # 데이터가 축소된 것을 알 수 있다.`
Counter({0: 250, 1: 100})
- Tomet Link
다수 범주랑 소수 범주 데이터를 연결했을때 중간의 어떠한 점이 없는 연결을 만들어 Tomet link 를 형성한 후 다수 범주에 속한 관측치를 제거한다. 분류경계면 자체가 크게 손상되지 않는다.
tometlink = TomekLinks(sampling_strategy = 'auto')
x_under,y_under = tometlink.fit_resample(x,y)
print(Counter(y_under))
sns.scatterplot(x = x_under[:,3], y = x_under[:,4], hue= y_under)
plt.show() # 처음 산점도와 크게 다른 점은 없다.
Counter({0: 9854, 1: 100})
2. 오버샘플링
- Random Oversampling
소수 범주의 데이터를 무작위로 단순히 복제하여 데이터의 수를 늘린다.
동일한 데이터가 반복적으로 학습에 포함되면서 학습정확도는 높지만, 검증 정확도는 낮은 과적합이 발생할 수 있다.
over_sample = RandomOverSampler(sampling_strategy = 'minority')
x_over, y_over = over_sample.fit_resample(x,y)
print(Counter(y_over)) # 소수집단이었던 1의 개수가 100 -> 9900 으로 증가되었다.
# 동일 데이터에 대해 반복적으로 복제해서 만들었기 때문에 산점도가 원본 데이터와 같아보인다.
# 학습데이터는 오차가 적지만 새로운 데이터에 대해서는 정확도가 떨어지는 과적합이 발생할 수 있다.
sns.scatterplot(x = x_over[:,3], y = x_over[:,4], hue= y_over)
plt.show() # 변화가 없는 이유? 동일데이터를 반복적으로 찍음
Counter({0: 9900, 1: 9900})
- SMOTE
소수 범주의 데이터를 활용하여 가상의 데이터를 생성한다.
소수범주에서 각 샘플들의 K-NN(유사한 특성을 가지는 가장 근접한 데이터)을 찾아 무작위 점을 생성한다.
smote_sample = SMOTE(sampling_strategy= 0.4) # 비율은 앞의 비율과 만들어지는 과정이 같다.
x_sm, y_sm = smote_sample.fit_resample(x,y)
print(Counter(y_sm))
sns.scatterplot(x = x_sm[:,3], y = x_sm[:,4], hue= y_sm)
plt.show() #
Counter({0: 9900, 1: 3960})
3. Cost Sensitive Learning
데이터 자체를 생성하지 않음.(데이터 수의 변경이 없음) 모델의 정확도를 최적화 하는 대신 전체 오분류 비용을 최소화 하는 것이 목적이다.
실제 세계의 불균형 분류 문제는 일반적으로 각 분류 오류에 대해 발생되는 비용이 적다. (예시: 은행 대출 문제, 암 진단 문제)
라벨의 가중치 옵션 사용
from sklearn.model_selection import RepeatedStratifiedKFold, cross_val_score # 교차검증마다 점수를 얻어오는
from sklearn.ensemble import RandomForestClassifier # 앙상블모형 (다수의 의사결정나무 사용)
def evaluate_model(x,y,model):
cv = RepeatedStratifiedKFold(n_splits=5, n_repeats= 3, random_state= 1) # 5등분으로 교차검증 실행, 3번 반복
scores = cross_val_score(model, x, y, scoring='precision', cv = cv, n_jobs= -1) # scoring : 평가지표 n_jobs: 최대한 모든 코어를 이용하여 교차검증을 수행
return scores
model = RandomForestClassifier(n_estimators= 100) # n_estimators: 의사결정 나무 개수
scores = evaluate_model(x, y, model)
print(f'Mean Precision No Weight: {np.mean(scores):.3f}') # 정밀도를 5개를 반환 -> 평균값을 취하는 것
# class_weight: 종속 변수 label 별 가중치
# 'balanced' : 각 클래스에 대해 [전체 샘플수/(클래스 수 * 클래스별 빈도)]로 계산된 가중치를 부여
# 0: 10000/(2*9900) , 1: 10000/(2*100)
weights = {0:1.0 , 1:10} # 가중치를 명시적으로 줌
model = RandomForestClassifier(n_estimators= 100, class_weight= weights)#'balanced' ) # n_estimators: 의사결정 나무 개수
scores = evaluate_model(x, y, model)
print(f'Mean Precision Weight: {np.mean(scores):.3f}')
Mean Precision No Weight: 0.360
Mean Precision Weight: 0.393
실행시마다 값이 변경되는 것을 확인 할 수 있다.
계층화된 K-fold (StratifiedKFold: 분류된 교차검증을 할때에)
: 종속변수 y 의 비율이 중요함(데이터를 각각 고르게 분할 시켜준다.)
'국비 교육 > 머신러닝, 딥러닝' 카테고리의 다른 글
[머신러닝] 회귀분석 - L1, L2규제 ((문제) 보스톤 집값 예측, 대한민국 육군 몸무게 예측) (0) | 2023.11.20 |
---|---|
[머신러닝] 회귀분석 - 다중공선성, L1 규제 ,L2규제 (0) | 2023.11.20 |
[머신러닝] 지도학습 알고리즘 - 회귀 분석 Regression Analystic (0) | 2023.11.16 |
[머신러닝] 머신러닝 개요 (0) | 2023.11.16 |