CAFE

과제게시판

머신러닝 교과서 4장 (좋은 훈련 데이터셋 만들기: 데이터 전처리)

작성자정다훈|작성시간23.10.10|조회수12 목록 댓글 0

4.1 누락된 데이터 다루기 

제 애플리케이션에서는 여러 가지 이유로 훈련 샘플에 하나 이상의 값이 누락된 경우가 드물지 않다. 데이터 수집 과정에 오류가 있거나 어떤 측정 방법은 적용이 불가능할 수 있다. 또는 설문에서 특정 필드가 그냥 비워져 있을 수도 있다.

일반적으로 누락된 값은 데이터 테이블에 빈공간이나 예약된 문자열로 채워진다. NaN( not a number)이나 NULL과 같은 값을 사용한다. 이런 누락된 값을 무시했을 때 예상치 못한 결과를 만든다. 분석을 더 진행하기 전에 누락된 값을 처리하는것이 중요하다.

 

# 누락된 값 표시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
from io import StringIO
import sys
 
csv_data = \
'''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
10.0,11.0,12.0,'''
 
 
if (sys.version_info < (30)):
    csv_data = unicode(csv_data)
 
df = pd.read_csv(StringIO(csv_data))
print(df)
cs

# 누락된 값의 개수 구하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
from io import StringIO
import sys
 
csv_data = \
'''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
10.0,11.0,12.0,'''
 
 
if (sys.version_info < (30)):
    csv_data = unicode(csv_data)
 
df = pd.read_csv(StringIO(csv_data))
print(df.isnull().sum())
cs

# 누락된 값의 행이나 열의 특성을 완전히 삭제하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
from io import StringIO
import sys
 
csv_data = \
'''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
10.0,11.0,12.0,'''
 
 
if (sys.version_info < (30)):
    csv_data = unicode(csv_data)
 
df = pd.read_csv(StringIO(csv_data))
print(df.dropna(axis=0))
cs

axis=0 일땐 NaN이 하나라도 있는 행을 삭제한다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
from io import StringIO
import sys
 
csv_data = \
'''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
10.0,11.0,12.0,'''
 
 
if (sys.version_info < (30)):
    csv_data = unicode(csv_data)
 
df = pd.read_csv(StringIO(csv_data))
print(df.dropna(axis=1))
cs

axis=1 일땐 NaN이 하나라도 있는 열을 삭제한다

누락된 데이터를 제거하는 것이 간단해 보이지만 단점도 있다. 예를 들어 너무 많은 데이터를 제거하면 안정된 분석이 불가능할 수 있다. 또는 너무 많은 특성 열을 제거하면 분류기가 클래스를 구분하는 데 필요한 중요한 정보를 잃을 위험이 있다.

 

# 누락된 값 대체

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
from sklearn.impute import SimpleImputer
import numpy as np
from io import StringIO
import sys
 
csv_data = \
'''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
10.0,11.0,12.0,'''
 
 
if (sys.version_info < (30)):
    csv_data = unicode(csv_data)
 
df = pd.read_csv(StringIO(csv_data))
 
imr = SimpleImputer(missing_values=np.nan, strategy='mean')
imr = imr.fit(df.values)
imputed_data = imr.transform(df.values)
print(imputed_data)
cs

사이킷런의 SimpleImputer 클래스를 사용하여 열의 평균으로 NaN값을 대체하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
from sklearn.impute import SimpleImputer
import numpy as np
from io import StringIO
import sys
 
csv_data = \
'''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
10.0,11.0,12.0,'''
 
 
if (sys.version_info < (30)):
    csv_data = unicode(csv_data)
 
df = pd.read_csv(StringIO(csv_data))
print(df.fillna(df.mean()))
cs

사이킷런을 사용하지 않고 판다스 DataFrame 객체에서 바로 평균값으로 누락된 값을 대체하는 방법

>> df.fillna(df.mean())

4.2 범주형 데이터 다루기 

 

# 판다스를 사용한 범주형 데이터 인코딩

1
2
3
4
5
6
7
8
import pandas as pd
 
df = pd.DataFrame([['green''M'10.1'class2'],
                   ['red''L'13.5'class1'],
                   ['blue''XL'15.3'class2']])
 
df.columns = ['color''size''price''classlabel']
print(df)
cs

 

# 순서가 있는 특성 매핑

 

학습 알고리즘이 순서 특성을 올바르게 인식하려면 범주형의 문자열 값을 정수로 바꾸어야 한다. 특성 순서를 올바르게 자동으로 바꾸어주는 함수는 없기 때문에 매핑 함수를 직접 만들어야 한다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd
 
df = pd.DataFrame([['green''M'10.1'class2'],
                   ['red''L'13.5'class1'],
                   ['blue''XL'15.3'class2']])
 
df.columns = ['color''size''price''classlabel']
 
size_mapping= {
                'XL'3,
                'L' : 2,
                'M' : 1}
df['size']=df['size'].map(size_mapping)
print(df)
cs

# 클래스 레이블 인코딩

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
import numpy as np
 
df = pd.DataFrame([['green''M'10.1'class2'],
                   ['red''L'13.5'class1'],
                   ['blue''XL'15.3'class2']])
 
df.columns = ['color''size''price''classlabel']
 
class_mapping = {label:idx for idx,label in enumerate(np.unique(df['classlabel']))}
df['classlabel']=df['classlabel'].map(class_mapping)
print(df)
cs

enumerate를 사용하여 클래스 레이블을 0부터 할당

--> 레이블 인코딩을 사용하게 되면 순서가 없는 값에 관계성이 부여되어 예측 성능의 저하를 일으킨다 

 

# 순서가 없는 특성에 원-핫 인코딩 적용

 

해당 컬럼이 맞으면 1 나머지는 모두 0 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OrdinalEncoder
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
 
 
df = pd.DataFrame([['green''M'10.1'class2'],
                   ['red''L'13.5'class1'],
                   ['blue''XL'15.3'class2']])
 
df.columns = ['color''size''price''classlabel']
 
ord_enc = OrdinalEncoder(dtype=int)
col_trans = ColumnTransformer([('ord_enc', ord_enc, ['color'])])
X_trans = col_trans.fit_transform(df)
col_trans.named_transformers_['ord_enc'].inverse_transform(X_trans)
= df[['color''size''price']].values
color_ohe = OneHotEncoder()
color_ohe.fit_transform(X[:, 0].reshape(-11)).toarray()
c_transf = ColumnTransformer([ ('onehot', OneHotEncoder(dtype=int), [0]),
                               ('nothing''passthrough', [12])])
print(pd.get_dummies(df[['price''color''size']]))
cs

# 데이터셋을 훈련 데이터셋과 테스트 데이터셋으로 나누기

 

사이킷런의 model_selection 모듈에 있는 train_test_split 함수를 사용하면 가장 간편하게 데이터셋을 랜덤한 훈련 데이터셋과  테스트 데이터셋으로 나눌 수 있다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd
from sklearn.model_selection import train_test_split
 
df_wine = pd.read_csv('https://archive.ics.uci.edu/'
                 'ml/machine-learning-databases/'
                 'wine/wine.data', header=None)
 
df_wine.columns = ['Class label''Alcohol''Malic acid''Ash',
                   'Alcalinity of ash''Magnesium''Total phenols',
                   'Flavanoids''Nonflavanoid phenols''Proanthocyanins',
                   'Color intensity''Hue''OD280/OD315 of diluted wines',
                   'Proline']
 
 
X, y = df_wine.head().iloc[:, 1:].values, df_wine.head().iloc[:, 0].values
X_train, X_test, y_train, y_test =\
    train_test_split(X, y, 
                     test_size=0.3
                     random_state=0
                     stratify=y)
print(X_train)
cs

* test_size=0.3 --> X_test 와 Y_test에 30%가 할당된다 (테스트 데이터 30%)

 

* X_train 와 Y_train에는 70%가 할당된다

 

* stratify 매개변수에 클래스 레이블 배열 y를 전달하면 훈련 데이터셋과 테스트 데이터셋에 있는 클래스 비율이 원본 데이터셋과 동일하게 유지된다.

 

#훈련 데이터셋과 테스트 데이터셋의 적절한 분할 비율

 

실전에서 가장 많이 사용하는 비율은 데이터셋의 크기에 따라 60:40, 70:30, 80:20이다. 대용량의 데이터셋일 경우에 90:10 또는 99:1의 비율로 훈련 데이터셋과 테스트 데이터셋을 나누는 것도 보통이고 적절하다.

 

또한, 떼어 놓았던 테스트 데이터셋을 버리지 말고 훈련과 평가 후에 전체 데이터셋으로 모델을 다시 훈련하여 모델의 예측 성능을 향상시키는 방법이 널리 사용된다.

 

4.4 특성 스케일 맞추기

결정 트리와 랜덤 포레스트는 특성 스케일 조정에 대해 걱정할 필요가 없는 몇 안되는 머신 러닝 알고리즘이다.

대부분은 특성 스케일이 같을 때 훨씬 성능이 좋다.

 

# 스케일이 다른 특성을 맞추는 대표적인 방법

 

* 정규화

--> 특성 스케일을 [0,1] 범위에 맞추는것을 의미함

최소 - 최대 스케일 변환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
 
df_wine = pd.read_csv('https://archive.ics.uci.edu/'
                 'ml/machine-learning-databases/'
                 'wine/wine.data', header=None)
 
df_wine.columns = ['Class label''Alcohol''Malic acid''Ash',
                   'Alcalinity of ash''Magnesium''Total phenols',
                   'Flavanoids''Nonflavanoid phenols''Proanthocyanins',
                   'Color intensity''Hue''OD280/OD315 of diluted wines',
                   'Proline']
 
 
 
X, y = df_wine.head().iloc[:, 1:].values, df_wine.head().iloc[:, 0].values
X_train, X_test, y_train, y_test =\
    train_test_split(X, y, 
                     test_size=0.3
                     random_state=0
                     stratify=y)
mms = MinMaxScaler()
X_train_norm = mms.fit_transform(X_train)
X_test_norm = mms.transform(X_test)
print(X_train_norm)
cs
0과1사이의 값으로 스케일 변환

* 표준화

--> 평균을 0에 맞추고 표준편차를 1로 만들어 정규분포와 같은 특징을 가지도록 만든다

1
2
3
import numpy as np
ex = np.array([0,1,2,3,4,5])
print('표준화:', (ex - ex.mean())/ex.std())
cs

4.5 유용한 특성 선택

 

모델이 테스트 데이터셋보다 훈련 데이터셋에서 성능이 훨씬 높다면 과대적합에 대한 강력한 신호이다.

과대적합은 모델 파라미터가 훈련 데이터셋에 있는 특정 샘플들에 대해 너무 가깝게 맞추어져 있다는 의미이다. 새로운 데이터에는 잘 일반화 하지 못하기 때문에 모델 분산이 크다고 말한다.

 

# 과대적합을 줄이는 방법

1. 더 많은 훈련데이터를 모은다.

2. 규제를 통해 복잡도를 제한한다.

3. 파라미터 개수가 적은 간단한 모델을 선택한다.

4. 데이터 차원을 줄인다.

 

# L2 규제

개별 가중치 값을 제한하여 모델 복잡도를 줄이는 방법

# L1 규제

가중치 제곱을 절대값으로 바꿈

# L2 규제의 기하학적 해석

아달린에서 사용했던 제곱 오차합 비용함수가 구 모양이어서 로지스틱 회귀의 비용함수보다 그리기 쉽다.

L1규제 최소비용 찾기

비용함수를 최소화하는 가중치 조합을 찾야아한다. 

 

L2 규제 최소비용 구하기

L2비용은 최소비용을 구하는데 최소 패널티가 부여된다. 최소 패널티영역에 벗어난곳은 최소비용이 될수 없기때문에

비용함수와 최소 패널티 구역이 만나는 끝점이 최소비용 지점이 된다.

 

# 순차 특성 선택 알고리즘

모델 복잡도를 줄이고 과대적합을 피하는 다른 방법은 차원축소이다. 규제가 없는 모델에서 특히 유용하다.

차원 축소 기법에는 두 개의 주요 카테고리인 특성선택과 특성추출이 있다. 특성선택은 원본의 일부에서 특성 추출은 일력의 특성에서 얻은 정보로 새로운 특성을 만든다.

 

순차특성선택은 주어진 문제에 가장 관련이 특성 부분 집합을 자동으로 선택하는것이 목적이다.

 

 

다음검색
현재 게시글 추가 기능 열기
  • 북마크
  • 신고 센터로 신고

댓글

댓글 리스트
맨위로

카페 검색

카페 검색어 입력폼