[머신러닝 무작정 따라하기] 1.머신러닝 시작하기 ~ 이진 분류기 만들기

     


최근 AI나 블록체인과 같은 신기술이 많이 등장해서 공부할 게 너무 많아졌습니다 @_@


저도 공부를 좋아해서 이것저것 많이 팠는데요.

석사 때 NLP전공이여서 word2vec같은 라이브러리를 활용해 챗봇도 만들어보고, 번역도 해보고 여러 가지 접해봤습니다. 하지만 이런 생각이 들더군요. 


'나는 AI/ML에 대해서 잘 알고 있는가?'


현재 하는 업무는 Linux에서 C로 서버 어플리케이션 개발입니다. AI나 ML은 따로 공부하고 있는 것이기는 한데, 제 생각에는 그냥 존재하는 라이브러리나 이미 잘 만들어진 모델로 흉내만 내고 있지 않나 생각이 들었습니다. 그러다가 '만약 내가 AI 분야로 이직을 할 수 있을까?'라는 질문을 저에게 해보고, 제대로 공부해보자는 생각이 들었죠.


가장 최근에 LINE에서 AI 개발자를 뽑기 위한 job description을 봤습니다.


[이런 분을 찾습니다!]

머신러닝 논문들을 읽고 적용할 수 있으신 분

추천 알고리즘을 구현해 본 적이 있거나이해하고 있으신 분

다음 개념들을 이해하고 적용할 수 있으신 분

- Logistic Regression, Gradient Boosting

- Collaborative Filtering, Factorization Machine, Multi-armed bandit problem

- Word2Vec, Doc2Vec, Latent Dirichlet Allocation

- CNN, RNN, Autoencoder, Wide & Deep learning

 

다음 도구를 사용할 수 있으신 분

- Python, Pandas, TensorFlow, Spark 또는Pyspark

 

[이런 부분도 잘하시면 더욱 좋습니다!]

  다음 중 하나에 대해 깊은 이해가 있으신 분

- Bayesian Statistics, GLM(Generalized Linear Model)

- Deep Learning(Vanishing Gradient problem, RBM, Sequence-to-sequence, Attention, GAN)​

 

 다음 중 하나의 논문을 읽고 설명할 수 있으신 분

- Covington, Paul, Jay Adams, and Emre Sargin. "Deep neural networks for youtube recommendations."    

  Proceedings of the 10th ACM Conference on Recommender Systems. ACM, 2016.

- Liu, David C., et al. "Related pins at pinterest: The evolution of a real-world recommender system."

  Proceedings of the 26th International Conference on World Wide Web Companion. International World Wide 

  Web Conferences Steering Committee, 2017.

- McMahan, H. Brendan, et al. "Ad click prediction: a view from the trenches."

  Proceedings of the 19th ACM SIGKDD international conference on Knowledge discovery and data mining. 

  ACM, 2013.

- Chapelle, Olivier, Eren Manavoglu, and Romer Rosales. "Simple and scalable response prediction for display advertising."

  ACM Transactions on Intelligent Systems and Technology (TIST) 5.4 (2015): 61.

- Juan, Yuchin, et al. "Field-aware factorization machines for CTR prediction."

  Proceedings of the 10th ACM Conference on Recommender Systems. ACM, 2016.

- He, Xinran, et al. "Practical lessons from predicting clicks on ads at facebook."

  Proceedings of the Eighth International Workshop on Data Mining for Online Advertising. ACM, 2014.


Job Description을 한번 읽고 다시 한번 생각해보니, 저는 뭔가 AI나 머신러닝을 할 수 있다고 말하기에는 부족하다고 뼈저리게 느꼈습니다. 그래서 이번 기회에 위에 Job Description을 기준으로 본격적인 공부를 해보려고 합니다. 공부하면서 이렇게 블로그에 정리하는 이유는, 어떤 것에 대해서 잘 알고 있다면, 다른 사람들에게 설명할 수 있어야 한다는 게 제 생각이기 때문입니다. 


본격적으로 공부를 시작하기 전에, 제가 선택한 교재는 핸즈 온 머신러닝입니다. 다른 책에 비해 단어나 용어의 정의가 잘 나와 있어서 선택했습니다.






Chapter.3 분류 p.123

책을 처음부터 읽어보니 챕터1과 2는 이론적인 내용이라서 딱히 정리할 것이 없었습니다. 그렇다고 필요 없는 내용이 있는 것은 아닙니다. 다만 챕터2를 단독으로 읽어서 이해하는 것보다 챕터3을 진행하면서 필요할 때마다 챕터 2의 내용을 보는 것이 전체적으로 더 이해하기 쉬웠던 것 같습니다. 핸즈온 머신러닝 챕터3에서는 "MNIST"를 이용해 머신러닝을 진행하는 내용이 나와 있습니다. 여기서 MNIST란 머신러닝을 접해본 사람들이라면 한 번쯤은 들어봤을 텐데요, 마치 프로그래밍의 Hello World!와 비슷한 존재입니다. 머신러닝을 처음 시작할 때 접하는 튜토리얼 같은 존재죠.


MNIST (Modified National Institute of Standards and Technology database)

 미국 고등학생과 인구조사국 직원들이 손으로 직접 쓴 숫자데이터. 손으로 쓴 숫자데이터와 실제 그 숫자가 매핑된 데이터세트이다. 머신러닝 테스트용으로 상당히 자주 쓰인다. 



일단 파일을 다운받아야 되는데, 책에 나와 있긴 하지만, 책대로 하면 다운로드가 진행되지 않더군요. 그래서 구글링 좀 해서 좀 수정했습니다. 


# 라이브러리 불러오기
from sklearn.datasets import fetch_mldata

#''' p.124
mnist = fetch_mldata('MNIST original',data_home="./")
print(mnist) # mnist 데이터 셋 출력

X, y = mnist["data"], mnist["target"] # X와 y에 mnist의 값을 넣습니다. X가 대문자인 이유는 행렬이기 때문입니다. p.73참조
print(X.shape) # X의 형태 출력 (70000, 784)
print(y.shape) # Y의 형태 출력 (70000, )

print(X)


위 코드를 실행하면 MNIST 데이터를 다운받습니다. (참고로 툴은 파이참을 사용했습니다.)

sklearn에 있는 fetch_mldata를 사용하면 쉽게 다운받을수 있도록 되어있습니다. 그만큼 많이 사용되기 때문이겠죠?


print(mnist)를 하면 데이터가 어떻게 생겨먹었는지 보여주게 됩니다.


{'DESCR': 'mldata.org dataset: mnist-original', 'COL_NAMES': ['label', 'data'], 'target': array([0., 0., 0., ..., 9., 9., 9.]), 'data': array([[0, 0, 0, ..., 0, 0, 0],

       [0, 0, 0, ..., 0, 0, 0],

       [0, 0, 0, ..., 0, 0, 0],

       ...,

       [0, 0, 0, ..., 0, 0, 0],

       [0, 0, 0, ..., 0, 0, 0],

       [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)}


보통 print는 대부분의 언어에서 기본출력함수입니다. print(mnist)를 했을 때 mnist에 들어있는 데이터를 보여줘야 할 것 같지만, 여기서는 데이터가 아니라 "구조"를 보여주고 있습니다. 즉 mnist에 어떤 데이터가 들어있는지를 출력하는 것이 아니라, mnist에 어떤 데이터가 들어있는지 그 구조를 출력합니다. 출력내용을 보면 DESCR - 설명, COL_NAMES - 데이터 라벨, 그리고 데이터 부분으로 구성되어있습니다.


좀 더 자세히 들어다보기 위해 mnist에 데이터부분을 읽어보겠습니다. X에 mnist에 데이터 중 'data'를 넣고, y에는 'target'을 넣습니다. (컬럼이름이 label, data이지만 실제 데이터는 target, data로 정의되있습니다. label = target입니다.)


target과 data가 어떻게 생겨먹었는지 이미 print(mnist)를 통해 확인했었습니다. 하지만 생략으로 나와 있기 때문에 데이터의 크기가 어떻게 정의되어있는지 확인하기 어렵습니다. shape을 사용하면 데이터의 크기를 쉽게 파악할 수 있습니다. 머신러닝에서는 대부분 행렬을 사용하고, 행렬연산을 많이 이용하기 때문에 데이터의 크기를 파악하는 것이 매우 중요합니다. print(X.shape)과 print(y.shape)을 통해 각각의 크기를 파악할 수 있습니다.


(70000, 784) -- print(X.shape)

(70000,) -- print(y.shape)


X는 (70000, 784)의 크기를 갖는 행렬(2차원 배열)이고, y는 (70000, )의 크기를 갖는 1차원 배열입니다. 왜 X는 2차원이고 y는 1차원일까요? 이것은 MNIST 데이터의 특징을 알면 쉽게 이해할 수 있습니다. 머신러닝 데이터는 보통 data-label 쌍으로 이루어져 있습니다. data는 말 그대로 데이터이고, label은 data에 대한 정의입니다. 즉 정답이라고 생각하면 됩니다. MNIST는 사람이 손으로 쓴 숫자 데이터입니다. data는 28x28 크기를 가진 캔버스에 숫자가 그려져 있고, label은 이 data가 어떤 숫자인지를 가지고 있습니다. 즉 X와 y는 각각 70000개의 data와 label을 나타내고 있습니다. 단 data는 하나가 28x28[784]로 이루어져있기 때문에 2차원 배열로 표현되고, label은 0~9사이에 하나의 숫자만 들어가기 때문에 1차원 배열로 표현됩니다.


실제로 X가 어떻게 생긴 데이터인지 보고 싶다면 파이썬에 있는 matplot 라이브러리를 사용해 확인할 수 있습니다. 


# 라이브러리 불러오기
from sklearn.datasets import fetch_mldata
import matplotlib
import matplotlib.pyplot as plt

#''' p.124
mnist = fetch_mldata('MNIST original',data_home="./")
print(mnist) # mnist 데이터 셋 출력

X, y = mnist["data"], mnist["target"] # X와 y에 mnist의 값을 넣습니다. X가 대문자인 이유는 행렬이기 때문입니다. p.73참조
print(X.shape) # X의 형태 출력 (70000, 784)
print(y.shape) # Y의 형태 출력 (70000, )

print(X)

#''' p.125

some_digit = X[36000] # 70000개중 임의로 하나를 some_digit(784, ) 으로 정의.
some_digit_image = some_digit.reshape(28,28) # (784, 1)을 (28, 28) 로 변경

plt.imshow(some_digit_image, cmap = matplotlib.cm.binary, interpolation="nearest") #cmap : 그래프 색 옵션 interpolation, interpolation : 픽셀의 경계옵션
plt.axis("off") #기준선 표시 X
plt.show() #화면에 이미지 띄우기


위 코드는 some_digit이라는 변수에 X 안에 있는 70000개 중에 36000번째에 있는 값을 넣어서 화면에 출력하는 예제입니다. 코드를 실행하면 아래와 같은 화면을 볼 수 있습니다.



some_digit을 28x28로 변경하고 화면에 그린 것이 바로 이 그림입니다. 무슨 모양인지 애매하긴 하지만 숫자라고 생각하면 5에 가깝지 않나요? 실제로 이 그림이 무엇을 뜻하는지는 y 값을 보면 알 수 있습니다. 우리가 some_digit에 넣은 것이 X[36000]이기 때문에, 이 값이 나타내는 값은 y[36000]를 찍으면 알 수 있습니다.


print([y[36000]]) # X[36000]의 숫자값 보기


출력해보면 '5.0'이 출력되는 것을 볼 수 있습니다. 예상대로 5가 맞는군요. 이제 36000이라는 숫자를 바꾸어가면서 테스트해 봅시다. 0~69999까지 아무 숫자나 넣어볼 수 있습니다.



이제 X에 저장된 데이터가 어떤 방식으로 저장되어 있고 어떤 것을 의미하는지를 확인할 수 있게 되었습니다. 머신러닝을 하기위한 필요한 요소인 [데이터, 레이블] 세트에 개념을 여기서 확실히 알고 넘어가야 합니다. 


컴퓨터가 학습하기 위해서는 데이터가 필요하고 그 데이터가 무엇인지 정의하는 레이블이 필요하다.


MNIST에서 데이터는 X이고 레이블은 y입니다.


이제 실제로 학습을 시켜보고, 제대로 학습하는지 검증해보도록 합시다. 여기서 머신러닝의 목적을 다시 한번 생각해보고 넘어갑시다. 우리가 머신러닝을 하는 이유는 임의의 데이터가 들어왔을 때 그 데이터가 무엇인지 '예측'하기 위한 것입니다. 즉 우리가 MNIST데이터를 가지고 머신러닝을 하는 목적은 어떤 글씨가 주어질 때 그 글씨가 어떤 숫자를 나타내는지 판단하기 위함입니다.



이진 분류기

숫자는 0~9까지 총 10가지가 있습니다. 처음부터 10가지를 전부 분류하기보다는 한 가지를 분류할 수 있는 분류기를 만들어 봅시다. 분류를 '예/아니요'로 간단하게 줄여보는 것입니다. 이렇게 두 가지로 분류할 수 있는 분류기를 이진 분류기(binary classifier)라고 말합니다. 


분류기를 만들기에 앞서, 데이터 세트를 수정합니다. 머신러닝을 할 때 데이터는 항상 '학습 데이터'와 '테스트 데이터'가 필요합니다. '학습 데이터'는 말 그대로 학습에 필요한 데이터입니다. '테스트 데이터'는 학습이 끝나고 분류기의 성능을 검증하기 위해 필요한 데이터입니다. 둘 다 [데이터, 레이블]로 구성되어야 합니다.


MNIST데이터를 학습 용도와 테스트 용도로 나눕니다. 사실 MNIST는 디폴트로 학습과 테스트가 나뉘어있습니다. 바로 0~59999까지가 학습이고, 60000~69999까지가 테스트용도입니다. 7만 개씩 통으로 저장해놨던 데이터(X)와 레이블(y)를 아래와 같이 따로따로 저장합니다.


X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:] #0~59999는 학습 60000~69999는 테스트로 분류


학습은 0~59999번까지 _train, 테스트는 60000~69999까지 _test로 나누어 저장합니다. X[:60000]이 0부터 59999까지를 뜻하는 파이썬 표현법입니다. X[60000:]은 60000부터 마지막까지를 나타냅니다.


위에서 some_digit에 값을 이것저것 넣어보신 분이라면 눈치채셨겠지만, MNIST데이터는 0부터 9까지 차례대로 데이터가 들어가 있습니다. 학습데이터는 0에 가까울수록 숫자 0에 가깝고, 59999에 가까울수록 숫자 9에 가깝습니다. 이 상태로 학습을 진행하면 분류기는 0부터 차례로 학습하게 됩니다. 물론 이게 안되는 것은 아니지만, 학습에 성능을 높이기 위해서는 무작위순서로 학습하는게 도움이 됩니다. 정렬된 데이터에 무작위성을 넣기 위해 파이썬 라이브러리인 numpy의 도움을 받도록 합시다.


shuffled_index = np.random.permutation(60000) # 0~59999까지 무작위 배열 생성
X_train, y_train = X_train[shuffled_index], y_train[shuffled_index] #X와 y를 위에서 정의한 무작위 배열순서로 다시 정렬


여기서 중요한 건 무작위로 바꿀 때 데이터와 레이블 둘 다 같이 바꿔줘야 한다는 겁니다. 만약 둘 중 하나만 바꾼다면 X[1000]이 나타내는 값이 y[1000]이 아니게 될 수가 있기 때문입니다.



데이터도 잘 만들어놨겠다, 다시 우리가 하려던 이진 분류기를 만들어보도록 합시다. 이 이진 분류기는 0~9중에 5만 분류할 수 있는 분류기입니다. 즉 '이 숫자는 5다' 거나 '이 숫자는 5가 아니다' 입니다.


그러기 위해선 레이블(y)을 조금 바꿔줘야 합니다. 지금 레이블은 숫자 0부터 9까지를 전부 나타내기 때문에 이진 분류기를 만들기에는 적합하지 않습니다. 여기서는 숫자 5를 분류 할 거기 때문에 5면 1이고 아니면 0으로 바꿔줘야 합니다.  예를들어 { 2, 3, 4, 8, 7, 0, 5, 5 } 이런 배열을 { 0, 0, 0, 0, 0, 0, 1, 1 } 로 바꿔야합니다.


y_train_5 = (y_train == 5)
y_test_5 = (y_test == 5)


위 코드로 y_train_5와 y_test_5를 만들어줍니다. 이 _5가 붙은 레이블은 기존 레이블이 5일 때만 1 값을 갖는 레이블 배열입니다. 데이터 세트는 바꿀 필요가 없습니다. 데이터 역시 바꿔줘야 하는 것 아니냐고 생각하실 수 있겠지만, 사실 우리가 우리는 데이터가 0~9까지의 숫자를 표현하고 있다는 것을 알고 있지만 분류기 입장에서는 그저 데이터입니다. 분류기는 데이터가 5인지가 중요하기 때문에 5가 아닌 데이터는 분류기 입장에서 같은 데이터(5가 아닌 데이터)입니다.


이진 분류기를 학습하기 위해 분류 모델 중에 하나인 SGD(확률적 경사 하강법)를 써보도록 합니다. 분류 모델들의 목적은 손실을 줄이는 것입니다. 여기서 손실이란 잘못된 예측을 하게 될 확률이라고 봐도 됩니다. 즉 손실을 줄일수록 예측정확도가 높아지게 됩니다. SGD는 손실을 줄이기 위한 최적값을 찾기 위해 무작위로 아무 값이나 넣어보면서 최적값을 구하는 분류 모델입니다.


SGD는 아주 많이 쓰이는 개념이기 때문에 여기서 확실히 알고 넘어가는 것을 추천합니다.

(구글링 꼬~)


복잡한 이론에 비해 사용법은 매우 간단합니다. sklearn라이브러리에 있는 SGDClassifier를 사용합니다.


from sklearn.linear_model import SGDClassifier  #sklearn 라이브러리 불러오기

sgd_clf = SGDClassifier(max_iter=5, random_state=43) #SGD분류모델 사용 max_iter는 반복수 random_state는 상태저장값
sgd_clf.fit(X_train,y_train_5) #SGD 분류모델에 데이터와 레이블값을 집어넣음


SGDClassifier 옵션중에 random_state가 있는데, 이것은 SGD의 상태를 저장하기 위한 옵션입니다. SGD 모델은 위에서 말한 대로 무작위 값을 넣어보면서 최적값을 찾기 때문에 실행할 때마다 결과가 달라집니다. 하지만 random_state를 지정하면 실행했던 상태를 다시 불러올 수 있기 때문에 여러 번 돌려도 똑같은 값을 출력하게 됩니다. 이렇게 만든 SGD 모델에 fit 함수를 이용해 데이터와 레이블을 집어넣어 학습하면 됩니다.


학습이 제대로 됐는지 맨 처음 some_digit을 이용해 테스트해봅시다. some_digit은 숫자 5를 저장하고 있었기 때문에 이 분류기에서는 5가 맞는다는 값이 나오면 학습이 제대로 된 것입니다.


print(sgd_clf.predict([some_digit])) #some_digit이 5인지 예측해보자(predict)


이 결과는 TRUE가 나올 때도 있고 FALSE가 나올 수도 있습니다. 모델의 학습상태에 따라서 말이죠... @_@


좀 더 그럴듯하게 성능을 평가해봅시다. 일단 '교차 검증'을 통해 성능을 평가합니다. 교차 검증은 데이터 세트를 좀더 작은 데이터 세트로 나누고 각 데이터 세트를 테스트하기 위해 다른 데이터세트들을 학습데이터로 사용하는 방법입니다. 예를 들어 기존 데이터 세트를 A, B, C로 나누고 A에 대해 테스트 하기위해 B, C 데이터를 이용하고, B를 테스트하기위해 A, C를 이용하는 방법입니다. 따라서 데이터 세트를 분할하는 갯수만큼의 성능이 나오게됩니다. sklearn라이브러리에서 cross_val_socre를 사용하면 됩니다.


from sklearn.model_selection import cross_val_score #sklearn 라이브러리 불러오기
print(cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring="accuracy")) #교차검증 cv : 데이터셋 분할 수

cv = 3이 데이터 세트를 몇개로 나눌지 정하는 옵션입니다. 여기서는 3개로 나누어 각각에 대해 정확도를 구합니다. 총 3개의 정확도 값이 나오게 됩니다. cv값을 늘리거나 줄여서 테스트해봅시다.



이번 포스팅에서는 머신러닝 데이터 세트를 설정하는 방법과 이진 분류기에 대해 알아보고 교차 검증을 통해 성능을 측정해보았습니다. 다음 포스팅에서는 오차 행렬을 이용하여 성능측정을 해보고, 이진 분류기가 아닌 다중 분류기를 만들어 보겠습니다.








반응형

댓글

Designed by JB FACTORY