cs

 

 

회사에서는 ML을 이제 직접 도입해서 프로덕트 레벨에서 태워보자 이런 단계이지만, 

 

말이 ML이고 머신러닝이지 쉬운게 아니다.

 

책에서야 뭐 간단하게 훑는 정도지만, 현업에서는 그냥 회사 두개가 협동하든, 대학원이랑 산하협력을 하든..  쉬운게 아니당..

 

지금 회사에서는 더 복잡하고 힘든걸 작업하고 리서치하고있지만,

 

혼공머신 이 책이든 다른 책들이든 다 하나하나 집고 가려고한다.

 

이유는? 내가 기본이 맨날 부족하다고 느끼니까.


책 볼 시간도 없었나,, 한참 밀렸으니 속도 내보자.

 

생선놈들의 길이와 무게 리스트를 먼저 다시 확인해보자.

fish_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0, 
                31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0, 
                35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0, 9.8, 
                10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0]
fish_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0, 
                500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0, 
                700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0, 6.7, 
                7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]

위의 해당 리스트를 보면 

 

길이와 무게로 나뉘어져 있는데, 이를 행렬형태로 변경이 필요하다.

 

한마리를 조회했을때 길이와 무게가 같이 나오게 하려는 목적으로. 

 

그럴때 이 책에서 사용하는 것이 column_stack() 함수.

 

fish_data = np.column_stack((fish_length, fish_weight))
print(fish_data[:5])

깔끔하게 두개의 열로 절리가 되었다.

 

 

다음으로 이 49의 물고기 데이터를 통해 타깃 데이터를 만들어 줘야한다는데, 

 

1, 0을 통해 구분을 할때 

 

손쉽게 concatenate() 함수를 사용해서 타깃 데이터를 만들 수 있다. 

 

fish_target = np.concatenate((np.ones(35), np.zeros(14)))
print(fish_target)

 

두개의 함수를 통해서 손쉽게? 넘파이 배열의 인덱스를 직접 섞어서 훈련과 테스트 셋으로 나눈건데,,, 

 

이것보다 더 좋은 방식이 sklearn에 있다. 

 

train_test_split()함수를 통해서 리스트나 배열을 비율에 맞게 바로 훈련,테스트 세트로 나누어준다. 

 

나누고싶은 리스트나 배열을 함수에 바로 넣어주고 random_state 매개변수를 통해 랜덤시드를 자체적으로도 지정이 가능하다. 

 

또한 샘플림 편향을 조절해주기 위해서 stratify 매개변수를 통해서 클래스 비율도 조정이 가능하다.

 

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    fish_data, fish_target, stratify = fish_target, random_state= 42
)
#임의로 랜덤시드 42를 지정해줌

print(test_target)

원래부터 도미가 더 많았기 때문에, 빙어의 갯수가 정확히 1:1이 될 수는 없다. 그래도 이정도면 괜찮은 비율이니?

 

책에서 말하는 모든 데이터는 준비 완료가 되었다. 

 

 

다시 테스트 하기 

지난 포스트처럼 k-최근접 이웃을 통해서 스코어를 봐보면..

from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier()
kn.fit(train_input, train_target)
kn.score(test_input, test_target)
1.0
이라는 당연한 스코어가 나오고, 도미 데이터를 넣고 돌린다고 해도 
print(kn.predict([[25,150]]))

0.

이라는 스코어가 나온다.. 

 

그림을 통해 다시 확인해보면..!

%matplotlib inline #plt.show()를 했는데 이미지가 나오지 않는다면, import절 앞에 해당 구문을 써주면 해결이됨
import matplotlib.pyplot as plt
plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25,150, marker ='^') #도미데이터에 표시를 해주기
plt.xlabel('lenght') 
plt.ylabel('weight')
plt.show()

오른쪽에 조금 더 가까울 수 있는데, 빙어에 가깝다고 판단을 한것이다. 

 

k-최근접 이웃의 개념을 다시 보면, 주변의 샘플중에서 다수인 클래스를 예측으로 활용하기 때문에 이런 결과가 나온 것으로 보인다. 

 

그럼 저 도미데이터 (세모)에 근첩한 친구들이 뭔지 확인해보는 코드를 작성해보자.

 

# 거리를 측정하는 indexes를 지정하고 
distance, indexes = kn.kneighbors([[25,150]])

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, marker = '^')
plt.scatter(train_input[indexes,0], train_input[indexes,1], marker='D')
plt.xlabel('lenght') 
plt.ylabel('weight')
plt.show()

n_neighbors의 기본값이 5개이기 때문에 가까웠던 빙어쪽의 4개 샘플를 따라 간것이다..??

 

이상하지 이상하다.. 

 

print(distances)

[[ 92.00086956 130.48375378 130.73859415 138.32150953 138.39320793]]

 

이런식으로 길이가 나오는데, 실제 측정값의 범위보다 더 적게 나와보인다..

 

x축의 범위를 y축과 동일하게 나타낸다면 어떻게 나올까?

plt.scatter(train_input[:,0], train_input[:,1])
plt.scatter(25, 150, marker = '^')
plt.scatter(train_input[indexes,0], train_input[indexes,1], marker='D')
plt.xlim((0, 1000)) #x축의 범위를 설정해버림
plt.xlabel('lenght') 
plt.ylabel('weight')
plt.show()

자 다시 보면, 데이터가 모두 수직형태로 나타난다. 

 

즉 생선의 길이는 가까운 이웃(k-최근접)을 사용하는데 크게 영향을 미치지 못하고, 오직 무게(y축)만을 고려해서 예측하게 된것이다..

 

쉽게말해 두 특성의 스케일이 달랐기 때문이다. 

 

이런 스케일 조정을 해주고 하는 작업을 데이터 전처리 라고 생각하면 된다. 

 

가장 쉬운 방식중에는 표준점수(standard score)를 사용 할 수있는데, 손쉽게 평균을 뺀 후 표준편차로 나누어주면 나온다.

 

mean = np.mean(train_input, axis = 0)
std = np.std(train_input, axis = 0)
#axis=0인 이유는 특성마다 값의 스케일이 다르기 때문에 평균과 표준 편차는 각 특성별로 계산해야함 -> 행을 따라 각 열의 통계값을 계산하게됨

print(mean, std)

각 train_input에 표준점수를 적용해주고 ,

 

새롭게 데이터를 넣을 친구에게도 적용한 뒤 다시 그림을 그려보면

 

#그리고  손쉽게 모든 행에서 계산이 가능함 -> 이런걸 브로드캐스팅이라고도 함
train_scaled = (train_input - mean)/std

new =([25, 150]-mean)/std 


plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(new[0], new[1], marker='D')
plt.xlabel('lenght') 
plt.ylabel('weight')
plt.show()

x축, y축을 보면 조정이 된것을 알 수 있다. 

 

다시한번 평가?를 해보면..!

 

#훈련을  다시 시키고
kn.fit(train_scaled, train_target)

# 마찬가지로 테스트 세트도 훈련 세트의 평균과 표준편차로 변환해야함 
test_scaled = (test_input-mean)/std

print(kn.score(test_scaled, test_target))
print(kn.predict([new]))

1.0

[1.]

 

 

스캐일 조정을 하고 나니까 new를 넣어도 맞추는 것을 볼 수 있다. 

 

마지막으로 다시 new데이터와 가까운 5개의 산점도를 표현해보면,

 

distances, indexes = kn.kneighbors([new])

plt.scatter(train_scaled[:,0], train_scaled[:,1])
plt.scatter(new[0], new[1], marker='^')
plt.scatter(train_scaled[indexes,0], train_scaled[indexes,1], marker='D')
plt.xlabel('lenght') 
plt.ylabel('weight')
plt.show()

가까운 데이터들을 잘 따라가는 걸 볼 수 있댱..! 

 

스케일 조정하는거 명심하쟈. 

ㅠㅠ

 

 

+ Recent posts