본문 바로가기
First step/AI 기초반

[TIL]21.07.19 비지도학습

by Joshua21 2021. 7. 19.

비지도 학습 전에 저번시간에 실습으로 풀었던 연비 학습문제를 복습했다.

#자동차 연비 계산 학습 정답

from tensorflow.keras.models import Sequential

from tensorflow.keras.layers import Dense

from tensorflow.keras.callbacks import ModelCheckpoint , EarlyStopping

from sklearn.model_selection import train_test_split

from sklearn.preprocessing import StandardScaler

from sklearn.preprocessing import LabelEncoder

 

import pathlib

import pandas as pd

import tensorflow as tf

import matplotlib.pyplot as plt

import seaborn as sns

from tensorflow import keras



dataset_path=keras.utils.get_file('auto-mpg.data'

    ,'http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data')

 

column_names=['MPG','Cylinders','Displacement','Horsepower','Weight','Acceleration','Model Year','Origin']

 

raw_dataset=pd.read_csv(dataset_path,names=column_names,na_values='?',comment='\t',sep=' ',skipinitialspace=True)

dataset=raw_dataset.copy()



print(dataset.isnull().sum())

dataset = dataset.dropna()

 

origin= dataset.pop('Origin')

dataset['USA']=(origin==1)*1.0

dataset['Europe']=(origin==2)*1.0

dataset['Japan']=(origin==3)*1.0

dataset.tail()

 

train_dataset=dataset.sample(frac=0.8,random_state=0)

test_dataset=dataset.drop(train_dataset.index)

 

sns.pairplot(train_dataset[['MPG','Cylinders','Displacement','Weight']], diag_kind='kde')

 

train_stats=train_dataset.describe()

train_stats.pop('MPG')

#그룹별 기술 통계량 

train_stats= train_stats.transpose()

print(train_stats)

 

#특성에서 타깃 값 또는 레이블을 분리 이 레이블을 예측하기 위해 모델을 훈련시킴

train_labels=train_dataset.pop('MPG')

test_labels=test_dataset.pop('MPG')

 

def norm(x):

  return (x-train_stats['mean'])/train_stats['std']

 

normed_train_data=norm(train_dataset)

normed_test_data=norm(test_dataset)

 

#모델 만들기

model=tf.keras.Sequential()

model.add(keras.layers.Dense(64,activation='relu',input_shape=[len(train_dataset.keys())]))

model.add(keras.layers.Dense(64,activation='relu'))

model.add(keras.layers.Dense(1))

optimizer=tf.keras.optimizers.RMSprop(0.001)

 

model.compile(loss='mse',optimizer=optimizer,metrics=['mae','mse'])

 

#model을 확인해봄 가중치와 바이어스가 초기값(랜덤값)인 모델

example_batch=normed_train_data[:10]

example_result=model.predict(example_batch)

print(example_result)

 

#에포크가 끝날 때마다 점(.)을 출력해 훈련 진행 과정을 표시합니다

class PrintDot(keras.callbacks.Callback):

  def on_epoch_end(self,epoch,logs):

    if epoch % 100 ==0:

      print('')

    print('.',end='')

EPOCHS=1000

 

#입력 값을 필히 정규화된 값으로 넣어줘야 함

 

history=model.fit(normed_train_data,train_labels,epochs=EPOCHS,validation_split=0.2,verbose=0,callbacks=[PrintDot()])

 

def plot_history(history):

  hist=pd.DataFrame(history.history)

  hist['epoch']=history.epoch

  plt.figure(figsize=(8,12))

 

  plt.subplot(2,1,1)

  plt.xlabel('Epoch')

  plt.ylabel('Mean Abs Error [MPG]')

  plt.plot(hist['epoch'],hist['mae'],label='Train Error')

  plt.plot(hist['epoch'],hist['val_mae'],label='Val Error')

  plt.ylim([0,5])

  plt.legend()

 

  plt.subplot(2,1,2)

  plt.xlabel=('Epoch')

  plt.ylabel('Mean Abs Error [$MPG^2$]')

  plt.plot(hist['epoch'],hist['mse'],label='Train Error')

  plt.plot(hist['epoch'],hist['val_mse'],label='Val Error')

  plt.ylim([0,20])

  plt.legend()

 

  plt.show()

 

plot_history(history)

 

early_stop=keras.callbacks.EarlyStopping(monitor='val_loss',patience=10)

history=model.fit(normed_train_data,train_labels,epochs=EPOCHS,validation_split=0.2,verbose=0,callbacks=[early_stop,PrintDot()])

 

plot_history(history)

 

Y_prediction=model.predict(normed_train_data).flatten()

plt.figure(figsize=(10,8))

plt.plot(Y_prediction,label='predicted')

plt.plot(test_labels.values,label='actual')

plt.legend(prop={'size':20})

 

avr= abs(Y_prediction - test_labels).mean()

print('실제 최소=%f,실제 최대=%f, 오차 평균 = %f'%(min(Y_prediction),max(Y_prediction),avr))

print('실제 최소=%f,실제 최대=%f,'%(min(test_labels),max(test_labels)))

 

비지도학습 예제문제를 단락별로 실행해가면서 배웠다.

#비지도 학습 -1

#skleaen 라이브러리에 포함된 datasets 모듈에서 아이리스 데이터를 로드

from sklearn import datasets

iris= datasets.load_iris()

samples=iris.data

print(samples)

 

#sepal length ,width 두가지 feature 만을 사용하겠음

#데이터로 부터 두 특성만 뽑아내서 산점도를 그려봄 

 

from matplotlib import pyplot as plt

 

x=samples[:,0]

y=samples[:,1]

plt.scatter(x,y,alpha=0.5#색상의 투명도 지정(0완전투명 1 불투명)

plt.xlabel=('sepal length (cm)')

plt.ylabel=('sepal width (cm)')

plt.show()

 

#iris dataset은 원래 라벨이 제공되지만 라벨이 없다고 가정하고 k-mean 알고리즘으로 위 데이터를 그룹화함 K-means 클러스터일 알고리즘 사용



#step1 k개의 centroids(중심값) 임의로 지정하기

import numpy as np

 

#꽃이 3가지 종이 존재하므로 3으로 설정

k =3

 

#랜덤으로 x,y좌표를 3개생성합니다

#np.random.uniform은 주어진 최소,최대값 사이에서 k개 만금 실수 난수를 생성합니다.

centroids_x=np.random.uniform(min(x),max(x),k)

centroids_y=np.random.uniform(min(y),max(y),k)

centroids=list(zip(centroids_x,centroids_y))

 

#centroids 는 임의로 생성한 (x,y) 좌표를 3개 갖게 됨

plt.scatter(x,y,alpha=0.5)

plt.scatter(centroids_x,centroids_y)

plt.show()



#stpe2 : assign datas to nearest centroids

#centroids에 가까운 데이터들을 할당 가깝다 라는 것을 정략적으로 계산하기위해

#각 데이터를 벡터로 간주하여 유클리드 거리를 계산

#두 데이터 포인트 사이의 거리를 계산하는 distance()함수를 작성

def distance(a,b):

  return sum([(el_a -el_b)**2 for el_a, el_b in list(zip(a,b))])**0.5

 

#각 데이터들 별로 3개의 centroids와의 거리를 측정, labels란 배열을 생성하고, 가장 가까운 centroids 의 index를 저장

#각 데이터 포인트를 그룹화할 labels를 생성합니다.

labels= np.zeros(len(samples)) 

sepal_length_width=np.array(list(zip(x,y)))

#각 데이터를 순회하면서 centroids와의; 거리를 측정합니다.

for i in range(len(samples)):

  distances=np.zeros(k) #초기 거리는 모두 0으로 초기화, 3개 요소 1차원 배열(k=3)

  for j in range(k): #3번 반복(각각의 x,y에 대해 세 중심과의 거리를 구해 distance에 집어 넣음)

    distances[j] = distance(sepal_length_width[i],centroids[j])

  cluster= np.argmin(distances) # np.argmin은 가장 작은 값의 index를 반환(세 중심중 어디에 가까운지)

  labels[i]=cluster # label에는 각각 x,y에 대해 세 중심중 어디에 속하는지에 대한 정보가 있음

 

#생성된 label에는 0,1,2가 저장되어 각 데이터(iris data)가 어느 centroids그룹에 속해있는 지를 나타냄 

plt.scatter(x,y,c=labels,alpha=0.5)

plt.scatter(centroids_x,centroids_y,c='red')

plt.show()

 

#데이터들이 가까운 centroid에 잘 할당되어 있지만, 처음 centroid를 선택할 때 랜덤으로 선택했으므로 최적화가 필요



#stpe3 update centroids = centroids를 새롭게 지정함으로써 데이터를 더 잘 그룹화 할수 있도록 만들어야함

#기존에 지정한 centroids를 복사해두어 centroids_old 에 저장

from copy import deepcopy

centroids_old=deepcopy(centroids)

 

#각 그룹별로 데이터의 평균을 계산 즉, 데이터들의 평균 x,y좌표를 계산하여 하나의 좌표를 계산 -> 새로운 centroids 로 지정

for i in range(k):

  # 각 그룹에 속한 데이터들만 골라 points에 저장 라벨에 섞인 값을 sepal_length_width에 구분하여 저장

  #points는 각 그룹의x,y좌표를 가진 list

  points=[sepal_length_width[j] for j in range(len(sepal_length_width)) if labels[j]==i]

  #point의 각 feature,즉 각 좌표의 평균 지점을centroids로 지정

  centroids[i]=np.mean(points,axis=0)

 

# data 및 타입 확인

print(centroids)

print(type(centroids))

print(centroids_old)

print(type(centroids_old))




centroids=np.array(centroids)

centroids_old=np.array(centroids_old)

print(type(centroids))

print(type(centroids_old))

 

#기존 centroids_old와 centroids 위치 비교

plt.scatter(x,y,c=labels, alpha=0.5)

plt.scatter(centroids[:,0], centroids[:,1],c='red')

plt.scatter(centroids_old[:,0],centroids_old[:,1],c='blue')

plt.show()

#centroids가 전체 적으로 중앙으로 이동하여 데이터의 중심 지점에 위치한 것을 확인 할수 있음



#step4: Rpeatstep2~3 until convergence(집중점, 접합점)

#필요한 모든 build blocks를 구현 2~3단계를 반복하여 최적의 centroids를 찾는것이 목표

#error 라는 배열 생성 error의 각 index는 centroids_old 와 새롭게 지정된 centroids의 거리를 저장

#이거리가 모두 0외 되면 최적해에 수혐한것으로 판단하여 반복 종료

 

centroids_old = np.zeros(centroids.shape)

labels=np.zeros(len(samples))

error= np.zeros(k) # error도 초기화

 

for i in range(k):

  error[i]=distance(centroids_old[i],centroids[i])



while (error.all() !=0): #열의 데이터 중 고건과 맞는 데이터가 있으면 True 전혀없으면 False

# step2 가까운 centroids에 데이터를 할당

  for i in range(len(samples)):

    distances=np.zeros(k) #초기 거리는 모두 0으로 초기화 해줍니다.

    for j in range(k):

      distances[j]=distance(sepal_length_width[i],centroids[j])

    cluster=np.argmin(distances) #np.argmin은 가장 작은 값의 index를 반환

    labels[i]=cluster

  #step3 centroids를 업데이트 

  centroids_old= deepcopy(centroids)

  for i in range(k):

    #각 그룹에 속한 데이터들만 골라 points에 저장합니다

    pinrts=[sepal_length_width[j] for j in range(len(sepal_length_width)) if labels[j]==i]

    # points의 각 feature, 즉 각 좌표의 평균지점을 centroid로 저장합니다  

    centroids[i]= np.mean(points, axis=0)

  #새롭게 centroids를 업데이트 했으니 error를 다시 계산합니다

  for i in range(k):

    error[i] = distance(centroids_old[i], centroids[i])

 

#최적의 centroids를 찾았으니 이를 시각화하여 확인

#직관적으로 알아보기 위해 색을 r,g,b로 설정 centroids 는 다이아 본드 형태로 마킹

 

colors=['r','g','b']

for i in range(k):

  points=np.array([sepal_length_width[j] for j in range(len(sepal_length_width)) if labels[j]==i])

  plt.scatter(points[:,0],points[:,1],c=colors[i], alpha=0.5)

 

plt.scatter(centroids[:,0],centroids[:,1],marker='D',s=150)

plt.xlabel=('sepal length (cm)')

plt.ylabel=('sepal width (cm)')

plt.show()

 

 

#연습 문제 2번

import matplotlib.pyplot as plt

from sklearn import datasets

from sklearn.cluster import KMeans

 

iris= datasets.load_iris()

samples=iris.data

#3개의 그룹으로 나누는 k means 모델을 생성

model= KMeans(n_clusters=3)

#fif 매서드를 통해 kmeans 클러스터링을 수행

model.fit(samples)

#k means를 수행한 다음 predict매서드를 통해 unlabeled 데이터를 그룹에 할당

labels=model.predict(samples)

#클러스터링 결과를 시각화 합니다

x=samples[:,0]

y=samples[:,1]

plt.scatter(x, y, c=labels, alpha=0.5)

plt.xlabel('sepal length (cm)')

plt.ylabel('sepal width (cm)')

plt.show()

 

#Evaluearion : iris 데이터를 3가지로 서로 다른 그룹으로 클러스터링 하는 것을 파이썬과 sklearn을 활용하여 구현

#실제로 얼마나 많은 데이터를 올바르게 분류한 것인지 확인

#iris 데이터 셋은 label이 포함되어 있으며 target이라는 내장속성을 통해 접근가능

import numpy as np

target =iris.target

 

#문자열 배열을 생성시킴

species = np.chararray(target.shape,itemsize=150)

for i in range(len(samples)):

  if target[i]==0:

    species[i]='setosa'

  elif target[i]==1:

    species[i]='versicolor'

  elif target[i]==2:

    species[i]='virginica'

 

#다음으로 cross-tabulation을 통해 결과를 분석

#판다스 라이버리를 활용하면 이를 쉽세 구현 가능

#labels : 비지도 학습 데이터, species: 원 데이터

import pandas as pd

 

df=pd.DataFrame({'labels':labels, 'species':species})

ct=pd.crosstab(df['labels'],df['species'])

print(ct)

 

num_clusters=list(range(1,9))

inertias=[]

 

#각 K별로 모델을 생성하여 inertia를 측정합니다.

for i in num_clusters:

  model=KMeans(n_clusters=i)

  model.fit(samples)

  inertias.append(model.inertia_)

 

#k에 따른 inertia의 변화를 시각화 합니다.

plt.plot(num_clusters, inertias,'-o')

plt.xlabel('Number of Clusters (k)')

plt.ylabel('Inertia')

plt.show()

 

 

 

엘보우 메소드 클러스트가 얼마정도가 적당한지 찾기