[수업 5째주 15일차] 머신러닝-6

김유민·2022년 4월 11일
0

대구 A.I. 스쿨

목록 보기
13/90

1. 학습내용

-오늘은 데이터 전처리에 대해 우선적으로 배워보았다.
총 5가지 방법이 있다.

첫번째 데이터 전처리(Data preprocessing)이였다.
간단하게 이야기하면 data를 분석하기 용이하게 고치는 모든 작업이다.
이걸 하는 이유는 알고리즘이나 파라메터가 잘못된 경우에는 지속적인 실험으로 문제를 찾아서 개선해 나갈 수 있지만, 데이터 자체가 잘못된 경우에는 실험결과가 개선되지 않기 때문이다.

데이터의 문제점들이 있는 경우들은 다음과 같다:

  • 결측치: 중요한 데이터가 빠진 경우.
  • 데이터 오류: 잘못된 데이터가 입력된 경우.
  • 이상치: 값의 범위가 일반적인 범위에서 벗어난 경우.
  • 데이터 형식: 데이터 형식이 분석하기 적합하지 않은 경우. (ex. 날짜가 22-02-01이런식으로표현되어야 될 것을 22년 2월1일 이런식으로 적힌경우 등)
  • 범주형 데이터: 범주형(categorical)으로 표현되어야 하는데, 데이터가 다른형태로 표현된 경우. (ex. 참 거짓같은 문자로 된 결과값을 1과 0등 숫자로 표현하는것)

그래서 위의 같은 상황을 해결하기 위해 사용되는 대표적인 데이터 전처리 기법이 있다.

  • Scaling
  • Sampling
  • Dimensionality Reduction
  • Categorical Variable to Numeric Variable

차례차례로 코딩을 하면서 배워보았다.
먼저
Scaling은 다 변수의 크기가 너무 작거나 큰경우 결과에 미치는 영향력이 일정하지 않을 수 있다.
(ex 어떤건 시간, 어떤건 분단위로 결과값이 있다면 0.1 시간 30분 이런식이면 숫자가 큰걸 영향력이 크다라고 결론내릴수 있다.)
-> 변수의 크기를 일정하게 맞추는 작업을 Scaling이라고 한다.
오늘 배운 대표적인 스케일링 함수는(scikit-learn안에 있는 함수)는 아래와 같다.

  • Min-Max 스케일링
  • z-정규화를 이용한 Standard 스케일링

Min-Max 스케일링은 0~1사이의 값으로 전부 변경해 주는 함수다.
이유는 특정 값들이 전체에 미치는 영향을 줄여서 모든 값들이 단위 크기와 상관없이 중요한 영향력을 가질수 있게 하기 위함이다.
즉, 전체적인 수치를 '1'기준으로 비율이 조정되기 때문에 모든 Feature들이 같은 조건에서 학습될 수 있게 하는 기법이다.

sklearn.preprocessing 패키지 안에 전처리와 관련된 기능들이 포함되어 있다.

먼저 패키지들을 가져온다.

# 먼저 필요한 패키지를 가져옵니다.

import numpy as np
import pandas as pd
import sklearn
import matplotlib.pyplot as plt

그런다음 강사님께서 주신 전복관련 조사 데이터인 txt파일을 불러온다.

abalone_columns = list()
for l in open('data/abalone_attributes.txt'):
    abalone_columns.append(l.strip()) #.strip은 선행 및 후행문자가 제거된 문자열의 복사본을 돌려준다.
    
data = pd.read_csv('data/abalone.txt', header=None, names=abalone_columns)   
abalone_columns

데이터의 이름을 두번째 파일인 'abalone_attributes.txt'로 라벨링하기위해
파일을 리스트화해야 한다. list()라고 하면 리스트타입에 저장공간이 생긴것이다.
반복문인 for 문을 사용해 파일을 열어준다.
한줄씩 읽어서 'l'에 저장되고 이걸 list에 저정하는것이다. for문은 뒤에 계속 추가가되어야 되니까 적는것이다.
.append는 참고로 선택된 요소의 마지막에 새로운 요소나 콘텐츠를 추가하는 명령어이다.

그런다음 data중에서 'Sex'라 되어 있는 라벨(target) 하나만 출력해보려고 하면

label = data['Sex']

그리고 앞부분 조금만 보려면

data.head()

하면 앞의 5줄까지만 출력된다.
전체 데이터가 총 몇개의 칼럼을 가지고 있는지 보려면

data.shape

하면 된다.

그럼 총 4117개의 데이터와 9개의 컬럼이 있는걸 알수 있다.
이제 결과값을 일정하게 Scaling할려면 먼저 숫자가 아닌 열을 먼저 삭제하고 시작한다.

del data['Sex']

그럼 아래와 같이 삭제가 된것을 확인할 수 있다.

데이터의 설명을 간단하게 보려면

data.describe()

그럼 아래와 같이 데이터의 갯수, 평균값, 표준편차 등등 기초 통계량이 나온다.

그리고 정확한 데이터의 정보를 볼려면 .info를 치면 나온다.

데이터 형식이 DataFrame으로 되어 있고, 나머지 정보가 죽 나온다.

이제 데이터를 Min-Max 스케일링 형식으로 데이터를 일정하게 만들어볼까 한다. 먼저 그냥 식을 써서 만들면 다음과 같이 나온다.

data = (data -np.min(data)) / (np.max(data) - np.min(data))

numpy에 있는 최소값을 낸다음 원 데이터 값에 빼기를 하면 된다.
그럼 아래와 같이 0~1사이의 값으로 대체되어 나온다.

하지만 sklearn에 공식이 다 있으므로 다른 방식으로 해볼까 한다.

from sklearn.preprocessing import MinMaxScaler

MinMaxScaler에 대한 변수를 선언하고,

mMscaler = MinMaxScaler()

데이터를 주고 조정을 하려면 .fit을 쓰면된다.

mMscaler.fit(data) #<-데이터적용만


.fit과 세트로 움직이는게 있다. fit은 데이터를 변수에 넣는 역활이고, 데이터를 변환시키는것은 transform을 붙여야 한다.

mMscaled_data = mMscaler.fit_transform(data) 

면 위의 사진과 똑같은 명령이 나온다.
위에는 fit과 transform을 붙인 것.
mMscaled_data가 그런데 type으로 확인해보면 numpy배열이고, 위의 data는 DataFrame 즉 pandas 객체다.
학습을 시킬때는 numpy배열로 만들어 사용하는 게 일반적이다.
fit이 numpy배열로 돌려주는것이다.

두번째는 Standard Scaling 방법이다.
z-score라고 부르는 데이터를 통계적으로 표준정규분포화 시켜 스케일링 하는 방식으로,
데이터의 평균이 0, 표준편차가 1이 되도록 스케일링 하는것을 말한다.
식은 아래 사진과 같다.

데이터를 점으로 해서 찍어봤을 때, 어떤 데이터는 튀고, 어떤건 서로 가까이 있으면 이 분포 자체를 가까이 맞추는 것을 말한다.
또 코드를 실습해 보기로 했다.

from sklearn.preprocessing import StandardScaler
sdscaler = StandardScaler()

아까처럼 fit_transform해서 데이터를 변환해 준다.

sdscaled_data = sdscaler.fit_transform(data)

+와 -값이 있는건 표준분포로 바꿨기 때문에 그를 중심으로 +와 -두개 값을 사이로 출력되기 때문이다.
(표준분포를 사용하는거랑 크기를 맞추는건 결과가 차이가 있고, 데이터를 이해하는데 어려움이 있어 대체적으로 min-max를 많이 사용한다고 하셨다.)

이번엔 Sampling이다.
샘플링은 클래스 불균형 문제를 해결하기 위함이다.
클래스 불균형 문제란,
분류를 목적으로 하는 데이터 셋에 클래스 라벨의 비율이 균형적으로 맞지않고, 한쪽으로 치우치게 되어 각 클래스의 데이터를 학습하기 어려워지는 경우를 의미한다.
이 '샘플링'에는 두가지 방식이 있다.

  • 적은 클래스의 수를 증가시키는 Oversampling (데이터가 너무 작아 늘여서 확인하는것.)
  • 많은 클래스의 수를 감소시키는 Undersampling이 있다. (불필요한 데이터를 솎아내어 추려놓은 것을 말한다.)

주로 Oversampling을 많이 쓴다. (주로 이미지 학습등에 많이 쓰인다.)

가장 쉽게 샘플링하는 방법은 임의(Random)로 데이터를 선택, 복제 혹은 제거하는 방식을 사용할 수 있지만, 이런 경우 아래와 같은 문제점이 있다.

  • 복제하는 경우, 선택된 데이터가 많아지게 되면서 데이터 자체가 과적합 될수 있다.
  • 제거하는 경우, 데이터 셋이 가지고 있는 정보자체의 손실이 생길 수 있다.
    샘플링 알고리즘은 imblearn에서 제공한다.
    예를 들어 서울시 각 구의 데이터를 복제를 했는데 하필 랜덤으로 선택한게 겹치는 경우가 있고, 이런 데이터가 많아지면 그것만 컴퓨터가 색출할 가능성이 많아진다.
    그래서 랜덤값을 몇가지 줘보고 테스트 해본다음 가장 좋은 결과값이 나올때 까지 돌려보는게 낫다.

아래는 실습한 내용이다.

from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler

만약 이렇게 출력했는데 에러가 나는 경우는 imblearn이 설치가 안된경우기 때문에 설치를 해주면된다.

pip install imblearn

다음 변수를 지정해주고

ros = RandomOverSampler()
rus = RandomUnderSampler()

위에서 쓴 데이터를 그대로 쓸것이기 때문에 data와 label를 여기 패키지에 적용하기 위해 변수를 또 선언해준다.

#데이터에서 특징을 학습함과 동시에 데이터 샘플링
oversampled_data, oversamples_label = ros.fit_resample(data, label) #label은 전복데이터의 성별로 변수를 선언했었음.
#data와 label에 각각 따로 줌.

보기 쉬우라고 DataFrame으로 바꾼다.

oversampled_data = pd.DataFrame(oversampled_data, columns=data.columns)

그럼 아래와 같이 출력된다.

위에서는 oversampled에서는 기존보다 데이터수가 늘어났다.
그럼 이번엔 undersample_data는 어떨까?

undersampled_data, undersampled_label = rus.fit_resample(data, label)

그럼 아래와 같이 데이터 수가 기존(4177,8)보다 줄게 입력되어 있는것을 알 수 있다.

그런데 여기서 어떤게 늘어나고 줄어드는지 알수 없다. (랜덤이기 때문에)
이걸 제어하는 방법이 있는데 바로 SMOTE 알고리즘이 있다.
이 알고리즘은 수가 적은 클래스의 점을 하나 선택해 k개의 가까운 데이터 샘플을 찾고 그사이에 새로운 점을 생성하는 방식이다.
SMOTE의 장점은 데이터 손실이 없고 과적합을 완화 시킬 수 있다.
(멀리 튀어있는 데이터와 주류가 되는 데이터 사이를 채우는것. 무작위로 점을 찍음)
여기가 좀 어려운 부분이였다.

2. 어려웠던 점 및 해결 방안

왜 data와 label을 make_classification을 하는지 이해가 안됬다.
그리고 ()안에 수많은 조건들을 적어주는데 생소한게 많이 나왔다.
데이터를 임의로 만들어주는 클래스라고 한다. 한쪽에 몰린 데이터를 사용하기 불편해서 이런 알고리즘을 쓰셨다고 한다.
그러니까 2차원 데이터로 바꾸기 위해 쓰는 알고리즘이였다.

from sklearn.datasets import make_classification
data, label = make_classification(
    n_samples=1000, #샘플의 갯수
    n_classes=3, #3개의 그룹 생성. 이걸 생성해야 컴퓨터가 추출할수 있다.
    n_repeated=0, #반복하지 않는다는 뜻.
    n_informative=2,  # 겹치는 게 얼마나 관계가 있는지 최소값 지정
    n_redundant=0,   #여기의 값이 중복적으로 나와서 독자적으로 갈수 있는 계수 보통은 '0'을 씀
    n_clusters_per_class=1, #클러스터 안에 클래스가 1개씩만 들어간다는 뜻.
    weights=[0.05,0.15,0.8], #3개 그룹의 구성비. 백분율로 해서 1을 기준으로 한다.
    class_sep=1, #각각의 클래스가 얼마나 떨어질것인지.
    random_state=2022 #랜덤값을 지정해줌.
)


그런데 2차원은 x,y두개의 값만 나와야 한다. 그래서 데이터의 수를 줄여야 해서
n_features=2, 을 넣는다
그런데 저렇게 하면 에러가 난다. 이유는 random때문이다. 횟수 지정을 해주지 않아서 에러가 나는것.
그래서 n_redundant=0 을 추가해주었더니

이런 식으로 묶어서 출력되었다.

이걸 그래프로 그릴려면 plt.scatter()을 쓰면 된다.
전체 데이터중 0번째를 x축으로, 전제데이터중 1번째를 y축으로 하려면 아래와 같이 하면된다.

plt.scatter(data[:,0], data[:,1],c=label, linewidth=1, edgecolor='black')
#그래프에 찍히는 점의 외각선 'linewidht' 그의 색깔 'edgecolor'

그런데 저기에 빈공간이 생긴다. 저 빈공간을 매꿔주는게 SMOT다.

from imblearn.over_sampling import SMOTE
smote = SMOTE(k_neighbors=5) #k_neighbors는 점이 가장 많이 떨어져 있는 걸 구하는것즉. 점을 몇개를 기준으로 삼아서 구할건지 정하는 것이다.

이걸 데이터 특징을 찾기위해 리샘플링을 한다.

smoted_data, smoted_label = smote.fit_resample(data, label)

그럼 원본데이터와 비교해 보았을 때 늘어난 것을 볼수 있다.

처음에 만든 데이터는 불균형이 있다. 새로 알고리즘을 입힌 데이터와 서로 비교를 하기 위해 값을 내면 다음과 같다.

print('원본 데이터의 클래스 비율 \n{}'.format(pd.get_dummies(label).sum()))
print('\nSMOTE 결과 \n{}'.format(pd.get_dummies(smoted_label).sum()))


안에 있는 데이터를 털어내어 합계를 구해서 출력을 했다.
비율이 서로 달랐는데, SMOTE결과 큰 비율에 맞춰서 다 갯수가 같아진 것을 알 수 있다.
임의로 찾은게 아닌 5개를 기준으로 제일 멀리 있고 외따로 떨어진 애를 기준으로 빈공간에 점을 찍어 맞춰준 것이다.

plt.scatter(smoted_data[:,0], smoted_data[:,1],
           c=smoted_label, linewidth=1, edgecolor='black')

그래프로 출력하면 다음과 같다.

다음은 Dimensionality Reduction이다.
하나의 점은 스칼라, 그다음 뻗어나가는 것은 x축, y축, z축 해서 3차원으로 나간다
차원의 저주라는 게 있는데 저차원에서 일어나지 않았던 현상들이 고차원에서 데이터를 분석하거나 다룰때 발생하는 현상이다.
이게 발생하는 이유는

  • 고차원으로 올라갈수록 공간의 크기가 증가
  • 공간의 크기가 증가할 경우, 데이터가 존재하지 않는 빈공간이 생김
  • 이런 빈공간들이 데이터를 해석할 때 문제를 일으키기 때문이다.

데이터의 차원이 불필요하게 큰 경우에는 필요없는 변수를 제거하고 과적합을 방지하기 위해서 데이터 차원을 축소한다.
또, 과적합의 이유 말고도 높은 차원의 데이터는 사람이 해석하기에도 어려움이 있어서 차원을 축소하는 작업을 하기도 한다.

from sklearn.datasets import load_digits
digits = load_digits()

해서 이 자료는 숫자를 그림으로 찍은걸 컴퓨터가 구별하기 위해 만든 자료이다.

print(digits.DESCR) #어떤 자료인지 살펴보는것.


진한 점을 찍은 것에 숫자를 더 크게 매겨서 손글씨를 인식시키기 위한 자료이다.

digits.data.shape #총 1797개의 64개의 항목이 있다고 출력된다.

데이터를 변수를 선언해 카피한다.

data = digits.data
label = digits.target 

데이터가 쭉 이어져 있는 것을 특정 개수만큼 잘라서 밑으로 쌓아(1797,8,8) 이미지로 만들어야 할때는 아래와 같이 쓴다.

plt.imshow(data[0].reshape(8,8))

data[0].reshape(8,8) 까지 하면 아래와 같이 출력된다.

수치가 큰것대로 색을 넣으면 숫자 '0'이 나온다.

3. 학습소감

이번 개념은 설명을 들어도 이해가 힘들었다. 다시 동영상을 처음부터 다시 봐야 할 정도 였다.
뭔가 차원도 나오고 데이터를 처음부터 잘 이해하지 못하면 알고리즘조차 제대로 적용하지 못하겠다는 생각도 들었다.
그래도 오늘도 오랜시간 복습해서 뿌듯하긴했다. 이제 힘들어지는 구간에 다가왔다고 생각하니 정신을 바싹 차리고 해야겠다는 생각이 간절해지는 하루였다.

profile
친숙한 개발자가 되고픈 사람

0개의 댓글