| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 | 27 |
| 28 | 29 | 30 |
- FlowLayout
- HTML
- FileWriter
- iframe 태그
- 메서드
- html 프로젝트
- inline
- GridLayout
- ObjectOutputStream
- Database
- 푸리에 변환
- 상속
- 반응형 웹 프로젝트
- Position
- 사전학습
- 퍼셉트론
- Codility
- 예제
- FFT
- rnn
- BorderLayout
- 파이썬
- java
- css 기초
- CSS
- g검정
- 미디어쿼리
- 반응형웹
- html 기초
- oracle
- Today
- Total
도라에몽주머니
0. 붓꽃 데이터 분석(Iris Dataset; KNN) 본문
간단하더라도 머신러닝을 활용한 데이터 분석 경험이 필요하다고 생각해 오늘부터 주요 예제들을 조금씩 연습해보려 한다.
첫번째로는 붓꽃 분류 예제를 이용해 연습해보자.

목표
꽃받침과 꽃잎의 크기에 따라 붓꽃을 세 종류(Versicolor, Setosa, Virginica) 로 분류
Code
0. 모듈 불러오기 및 데이터 확인
pandas와 numpy를 이용할 예정이므로 pandas와 numpy 라이브러리를 가져온다.
import pandas as pd
import numpy as np
붓꽃 데이터를 불러와 데이터의 구성을 확인한다.
from sklearn import datasets
iris=datasets.load_iris() # 딕셔너리 형태
# key값 확인
iris.keys()
# datasets에 관한 설명 확인
print(iris['DESCR'])위의 코드를 통해, iris dataset은 아래와 같은 key들로 구성되어 있다는 것을 알 수 있다.

DESCR key를 이용해 확인한 dataset의 설명은 아래와 같다.

설명을 통해, iris dataset은 총 150개의 붓꽃 샘플 데이터와 4개의 피쳐(feature)로 구성되어 있다는 것을 알 수 있다.
피쳐로는 sepal length in cm(꽃받침의 cm 길이), sepal width in cm(꽃받침의 cm 너비), petal length in cm(꽃잎의 cm 길이), petal width in cm(꽃잎의 cm 너비)가 있다.
또한, 아래의 코드를 통해 우리가 붓꽃을 세 종류(Versicolor, Setosa, Virginica)로 분류할 것을 확인할 수 있다.
print(iris['target_names'])
1. Target, Data 속성 확인 및 데이터 프레임 변환
Pandas Dataframe의 형태로 변환하기 전에 목표변수의 데이터셋 크기와 데이터셋 내용을 확인한다.
iris.['target'].shape # Target 크기 확인
iris.['target'] # Target 데이터 확인
setosa(0), versicolor(1), verginica(2) 총 3개의 클래스가 각각 50개씩 들어있다.

실제 데이터의 데이터셋 크기와 데이터셋 내용을 확인한다.
iris['data'].shape
iris['data'][:7, :] # 상위 7개 행 출력
위에서 확인한 데이터의 shape처럼 총 4개의 피쳐와 150개의 데이터로 구성되어 있다.
데이터의 형태를 확인했으니 2차원 배열을 데이터 프레임 형태로 변환시켜 준다.
df=pd.DataFrame(iris['data'], columns=iris['feature_names'])
df.shape # 데이터 프레임 형태 확인
작성한 데이터 프레임 형태를 확인하면 (150, 4)의 배열이 잘 들어온 것을 알 수 있다.
상위 일부 데이터를 열어 확인해본다.
df.head()
데이터 프레임의 column명을 중복값 확인 시 이용해야 하므로 띄어쓰기나 괄호를 없애고 snake 표기법으로 변경해준다.
df.columns=['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
df.head() # column명이 제대로 변경되었는지 확인
마지막으로 target 레이블을 추가해준다.
df['Target']=iris['target']
print(df.shape) # target 열이 추가되어서 (150, 5)의 형태
df.head()
3. 데이터 탐색(EDA)
데이터의 특성을 확인하기 위해 데이터셋의 기본 정보를 확인해본다.
df.info
다음으로는 describe를 이용해 요약된 통계 정보를 확인해보자.
df.describe()데이터가 각각 150개씩 있는 것을 확인할 수 있고, 각 샘플의 평균, 표준편차, 최소값, 최대값 등을 표로 보여준다.
petal_length의 표준편차가 약 1.77으로 가장 크므로, 다양한 값을 가지고 있다는 것을 알 수 있다.

데이터 학습 전 preprocessing을 위해, 위 Dataframe에 결측값이 있는지 확인하고 중복값을 제거하는 과정이 필요하다.
우선, 데이터에 결측값(=null값) 이 있는지 확인해보자.
df.isnull.sum() # 각 column의 null 개수를 count
결측값은 없는걸 확인했으니 중복값 존재 여부를 확인해보자.
df.duplicated().sum() # 중복되는 값의 개수위 코드를 실행하면 1이라는 값이 결과로 출력되므로 1개의 중복되는 데이터가 있음을 확인할 수 있다.
중복되는 데이터가 어떤 데이터인지 확인하고 중복되는 두개의 데이터 중 하나를 지워야 한다.
중복되는 데이터 위치를 우선 찾아보자.
df.loc[df.duplicated(), :]
142행에서 중복이 있는 것을 확인했으니 142행과 중복되는 다른 행은 어떤 것인지 확인해보자.
df.loc[(df.sepal_length==5.8)&(df.petal_width==1.9), :]
df=df.drop_duplicates()
df.loc[(df.sepal_length==5.8)&(df.petal_width==1.9), :]101행과 142행이 중복되므로 중복값을 제거해주고 실제로 잘 제거되었는지 확인해보았다.

142행이 제거되고 101행만 남은 것을 확인할 수 있다.
3-1. 상관 관계 분석
이제 각 속성간의 관계를 파악하기 위해 상관 관계 분석을 해야 한다.
각 변수간의 선형 관계성을 파악하기 위해 상관 계수 행렬을 출력해보자.
df.corr()
상관 계수에 대한 개념이 헷갈려서 아래에 정리해보았다.
상관 계수를 계산하는 방법에는 피어슨 상관계수(Pearson Correlation Coefficient, PCC), 스피어만 상관계수(Spearman Correlation) 등이 있는데 corr() 함수는 기본값으로 피어슨 상관계수(PCC)를 사용한다.
그렇다면 상관계수는 어떤 의미를 가질까?
A, B라는 속성에 대해 상관계수가 주어진다고 가정해보자.
A와 B의 상관계수가 1에 가까워질수록, 하나가 증가할 때 다른 하나도 증가하는 경향이 나타나고,
-1에 가까워질수록 하나가 감소할 때 다른 하나가 감소하는 경향이 있다는 의미를 가진다.
하지만, A와 B의 상관계수가 높은 값을 가진다(=A와 B의 상관관계가 높다) 라는 것은 두 속성이 함께 움직이는 경향이 있다는 의미일 뿐, "A를 증가하게 만들면 반드시 B도 증가할 것이다" 를 나타내는 것은 아니다.
참조 : https://m.blog.naver.com/kiddwannabe/221763497317
숫자만으로 표가 나타나있어 보기 어려우니 heatmap의 형태로 시각화 해보자.
import matplotlib.pyplot as plt
import seaborn as sns
sns.heatmap(data=df.corr(), cbar=True, annot=True) # 수치막대, 상관계수 숫자 출력
Pearson 상관계수는 극단 데이터의 영향을 상당히 많이 받기 때문에 위의 결과만으로 꽃잎과 꽃받침간의 관계에 대한 특성을 정의내릴 수는 없다. 하지만, 상관계수의 값만으로 유추한 데이터의 경향은 아래와 같다.
Target을 제외한 속성들 중, petal_length와 petal_width 사이의 상관계수가 0.96으로 가장 1에 가깝다.
즉, 꽃잎의 길이가 증가할 때, 꽃잎의 너비도 증가하는 경향을 보인다는 것을 알 수 있다.
또한, sepal_width와 petal_length 사이의 상관계수가 -0.43으로 여러 속성들의 상관계수 중 가장 -1에 가깝다.
그렇기 때문에, 위의 데이터에서는 꽃받침의 너비가 감소할 때, 꽃잎의 길이가 감소하는 경향을 보인다는 것을 알 수 있다.
3-2. Scatter Plot
다음으로는 산점도 행렬(Scatter Plot) 을 이용해 붓꽃 품종별 데이터 분포를 확인해보자. 각 품종별 그래프로는 히스토그램 대신 KDE를 사용해 나타내었다.
sns.pairplot(df, hue='Target', diag_kind='kde') # KDE로 표현
plt.show()
산점도 행렬을 통해, 꽃잎과 꽃받침에 따라 각 품종이 비교적 잘 구분되는 것을 알 수 있다. 각 품종끼리 모여있는 경향을 보이므로 KNN을 사용해 모델을 학습시켜 볼 것이다.
4. Train-Test 데이터셋 분할
모델 학습에 활용할 train set과 test set을 분류해보자.
from sklearn.model_selection import train_test_split
# DataFrame에 target과 data가 섞여있으므로 분류
x_data=df.loc[:, 'sepal_length':'petal_width'] # sepal_length, sepal_width, petal_length, petal_width
y_data=df.loc[:, 'Target'] # Target
x_train, x_test, y_train, y_test=train_test_split(x_data,
y_data,
test_size=0.2, # train data 80%, test data 20%
shuffle=True,
random_state=101)
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)
Train이 전체 데이터의 약 80%, Test가 전체 데이터의 약 20%로 잘 나뉘어진 것을 볼 수 있다.
5. 정규화
정규화는 데이터셋의 각 feature의 크기를 통일하기 위해 사용된다.
붓꽃 분류 예제같은 경우에는 꽃잎과 꽃받침의 길이, 너비가 전부 cm단위로 통일되어 있기 때문에 정규화를 진행하지 않아도 모델 학습에 큰 지장이 없을 것이라고 생각했는데, 정규화를 진행하고 학습한 케이스가 있어서 정규화를 해보았다.사실 뭐가 정답인지는 모르겠다...
from sklearn.preprocessing import MinMaxScaler
scaler=MinMaxScaler()
scaler.fit(x_train) # 정규화에 사용되는 평균, 표준편차 등의 값을 계산해 데이터에 저장해두는 명령어
x_train=scaler.transform(x_train) # 실제 fit()을 통해 저장한 값을 이용해 데이터 변환을 수행하는 명령어
pd.DataFrame(x_train, columns=df.columns[:-1]).head()scaler를 fit 할때는 반드시 train 데이터셋만을 가지고 fit을 진행해야 한다. test 데이터를 이용할 경우, test 데이터에 대한 data leak이 발생할 가능성이 있다.

6. KNN 모델 학습
위의 scaler를 이용해 x_test까지 정규화한 후, 임의의 k값을 정해 모델을 작성한다.
from sklearn.neighbors import KNeighborsClassifier
# train 데이터를 이용해 fit한 scaler를 활용해 x_test도 정규화
x_test=scaler.transform(x_test)
knn=KNeighborsClassifier(n_neighbors=5) # k 값을 5로 설정(임의)
knn.fit(x_train, y_train)
k=5일 때, 예측값과 정확도를 구해보자.
from sklearn.metrics import accuracy_score
y_knn_pred=knn.predict(x_test)
print('예측값: ', y_knn_pred) # k=5일때의 예측값
knn_acc=accuracy_score(y_test, y_knn_pred)
print("정확도: %.4f" %knn_acc)
계산된 정확도를 보면, k=5일때도 꽤 높은 정확도를 보이는 것을 알 수 있다.
위에서는 임의로 k=5라고 정했지만 KNN은 k값에 따라 정확도가 달라지기 때문에 임의로 정한 k값 대신 k를 1~80까지 변화시키며 accuracy의 변화를 관찰해보자.
train_list=[]
test_list=[]
# K를 1~80까지만 변화시키며 실험
for k in range(1, 81):
knn=KNeighborsClassifier(n_neighbors=k)
knn.fit(x_train, y_train)
y_knn_pred=knn.predict(x_test)
test_list.append(accuracy_score(y_test, y_knn_pred))
train_score=knn.score(x_train, y_train)
train_list.append(train_score)
# 시각화
plt.figure(figsize = (10, 4))
plt.plot(range(1,81), train_list, label='train_acc', marker="o")
plt.plot(range(1,81), test_list, label='test_acc', marker="d")
plt.legend() # 범례
plt.grid() # 격자 눈금
plt.xticks(range(1,81,2)) # X축 축척 조절
plt.xlabel('K_value') # X축 라벨
plt.ylabel('accuracy_score') # Y축 라벨
plt.show()
train 데이터의 KNN 정확도는 K 값이 증가할수록 점점 떨어지는 경향을 보인다. 하지만 test 데이터로 확인한 결과, k=45까지는 일정하게 높은 정확도를 보이다가 이후부터는 큰 폭으로 변화하는 것을 알 수 있다.
아마 k=45까지 일정한 정확도가 나오는게 정규화 때문일 것이라고 추정하는데 정확한 원인은 확실치 않다...
위 그래프를 통해서는 k가 커질수록 train data에 대한 정확도가 떨어지므로 test data에 overfitting이 일어나게 된다. 그렇기 때문에, train_acc와 test_acc가 같은 k=4일때가 가장 최적의 k값이라고 생각한다.
'Study > ML' 카테고리의 다른 글
| [ML] 하이퍼파라미터 최적화(Random Search, Grid Search) (0) | 2024.12.22 |
|---|---|
| [ML] numpy 모듈으로 푸리에 변환 구현 (4) | 2024.09.27 |
| [ML] 푸리에 변환(Fourier Transform) 이란? (3) | 2024.09.17 |
| [ML] 다층 퍼셉트론(Multi-Layer Perceptron)과 활성화 함수 (1) | 2023.11.13 |
| [ML] 퍼셉트론(Perceptron) 이란? (0) | 2023.11.02 |