블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

Wide and deep network 모델 활용하기

빅데이타/머신러닝 | 2017.07.20 17:12 | Posted by 조대협


Wide & deep model 알아보기

조대협 (http://bcho.tistory.com)

Wide & deep model

이글에 설명된 예제는 https://www.tensorflow.org/tutorials/wide_and_deep  문서에 있는 코드를 활용하였습니다. 음식 검색 키워드와 검색 결과를 학습 시킨 후에 이 결과를 기반으로 사용자에게 음식을 추천해주는 서비스가 있다고 하자.

Monetization and Wide model (기억과 와이드 모델)

로지스틱 회귀 모델을 이용하여 추천 알고리즘을 작성하여 학습을 시킨 경우, 학습 데이타를 기반으로 상세화된 예측 결과를 리턴해준다. 예를 들어 검색 키워드 (프라이드 치킨)으로 검색한 사용자가 (치킨과 와플)을 주문한 기록이 많았다면, 이 모델은 (프라이드 치킨)으로 검색한 사용자는 항상 (치킨과 와플)을 추천해주게 된다.  즉 예전에 기억된 값 (Memorization된 값)을 통해서 예측을 하는데, 이러한 모델을 와이드 모델이라고 한다.



<그림 와이드 모델 >

그러나 (프라이드 치킨)으로 검색한 사용자에게 같은 패스트 푸드 종류인 햄버거나 프렌치프라이등을 추천해도 잘 구매가 되지만 와이드 모델은 기존에 기억된 결과로만 추천을 하기 때문에 이러한 결과를 얻기가 어렵다.


Generalization and Deep model (일반화와 딥모델)

뉴럴네트워크 모델의 경우 프라이드 치킨을 햄버거, 프랜치 프라이등을 일반화 시켜서 패스트 푸드로 분류하여 프라이드 치킨으로 검색을 해도 이와 같은 종류의 햄버거를 추천해도 사용자가 택할 가능성이 높다.


<그림 딥 모델>


이러한 모델을 딥모델이라고 하는데, 딥 모델의 경우 문제점이, 너무 일반화가(under fitting)  되서 엉뚱한 결과가 나올 수 있다는 것인데, 예를 들어서 따뜻한 아메리카노를 검색했는데, 커피라는 일반화 범주에서 아이스 라떼를 추천해줄 수 있다는 것이다. 즉 커피라는 일반화 범주에서 라떼는 맞는 추천일 수 있지만, 따뜻한 음료를 원하는 사람에게 차가운 음료를 추천하는 지나친 일반화가 발생할 수 있다.


그래서 이런 문제를 해결하기 위해서 와이드 모델과 딥모델을 합친 “Wide & deep model”이라는 것을 구글이 개발하였고 이를 구글 플레이 스토어에 적용한 결과, 큰 효과를 얻었다고 한다. (https://arxiv.org/abs/1606.07792)


<그림 와이드 앤 딥모델 >


모델 사용 방법

이 모델이 텐서플로우에서 tf.contrib.learn 패키지에 라이브러리 형태로 공개가 되었다.

Classification 용은 tf.contrib.learn.DNNLinearCombinedClassifier

Regression 용은 tf.contrib.learn.DNNLinearCombinedRegressor

를 사용하면 된다.


이 라이브러리들은 텐서플로우의 Esimator API (https://www.tensorflow.org/extend/estimators)인데, 복잡한 알고리즘을 구현할 필요 없이 불러다 쓸 수 있는 하이레벨 API 이면서 학습에서 중요한 다음 두가지를 도와준다.

  • 분산러닝
    멀티 GPU나 멀티 머신에서 분산학습을 하려면 직접 텐서플로우 코드를 써서 작업 분산 및 취합 작업을 해줘야 하는데, Estimator API를 사용할 경우 Experiment API 를 통해서 Google CloudML 인프라 상에서 이런 작업을 자동으로 해준다.

  • 모델 EXPORT
    그리고 학습된 모델은 운영환경에서 예측용으로 사용할때, 모델을 Export 하여 Tensorflow Serving 과 같은 예측 엔진에 배포해야 하는데, 모델을 Export 하려면, 예측에 사용할 텐서플로우 그래프를 다시 그려주고 변수 값을 채워넣는 것에 대한 코드를 작성해야 하는데 (자세한 설명은 http://bcho.tistory.com/1183 문서 참조), 이 역시도 자동화를 해준다.


자 이제 머신러닝 모델은  있으니 여기에 데이타 즉 적절한 피쳐만 제대로 넣어서 학습을 시키면 되는데, 와이드 모델과 딥모델 각각 학습 하기 좋은 피쳐가 따로 있다.

와이드 모델 학습용 피쳐

와이드 모델에는 카테고리(분류)와 같은 비연속성을 가지는 데이타가 학습에 적절하다. 카테고리성 컬럼의 경우에는 다음과 같이 크게 두 가지가 있다.

Sparse based column

성별, 눈동자의 색깔과 같이 비연속성을 지니는 값으로 학습에 사용하려면 이를 벡터화를 해야 한다.

예를 들어 남자 = [1,0] 여자는 = [0,1] 식으로 또는 검정눈 = [1,0,0], 갈색눈 = [0,1,0], 푸른눈 = [0,0,1] 식으로 벡터화할 수 있다.

이때는 다음과 같이 sparse_column_with_keys라는 메서드를 써주면 위와 같은 방식으로 인코딩을 해준다.

gender = tf.contrib.layers.sparse_column_with_keys(column_name="gender", keys=["Female", "Male"])

만약에 나이와 같이 연속형 데이타라도 이를 10대,20대,30대와 같이 구간으로 나눠서 비연속성 분류 데이타로 바꾸고자 할 경우에는 다음과 같이 bucketized_column을 사용하면 된다.

age_buckets = tf.contrib.layers.bucketized_column(age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])

Crossed column

다음은 crossed column 이라는 피쳐인데, 예를 들어 교육 수준과, 직업이라는 피쳐가 있다고 하자. 이를 각각의 독립된 변수로 취급할 수 도 있지만, 교육수준과 직업에 상관 관계가 있다고 할때 이를 관계를 묶어서 피쳐로 사용할 수 있다. 예를 들어 대졸 사원의 연봉, 컴퓨터 프로그래머의 연봉과 같이 독립된 특징으로 보는것이 아니라 대졸 컴퓨터 프로그래머, 대학원졸 컴퓨터 프로그래머와 같은 상관 관계를 기반으로 피쳐를 사용할 수 있는데 이를 Crossed column이라고 한다. Cross column은 다음과 같이 crossed_colmn이라는 메서드를 이용해서 정의할 수 있다.

tf.contrib.layers.crossed_column([education, occupation], hash_bucket_size=int(1e4))

딥 모델 학습용 피쳐

딥 모델용 학습데이타는 연속성을 가지는 데이타가 적절하다.

Continuous column

Continuous column은 일반적인 연속형 데이타 변수이고 간단하게 real_valued_column 메서드를 정해서 다음과 같이 정의가 가능하다.

age = tf.contrib.layers.real_valued_column("age")

Embedding column

문장의 단어들을 학습 시키기 위해서 각 단어를 벡터로 표현하고자 할때 , 예를 들어 boy = [1,0,0,0..], girl=[0,1,0,...] 으로 단어 하나를 하나의 숫자로 1:1 맵핑을 시킬 수 있다. 그러나 이 경우 이 단어가 다른 단어와 어떤 상관 관계를 갖는지 표현이 불가능하다. 예를 들어 남자:소년=여자:?? 라는 관계식을 줬을때, 위의 방식으로는 단어간의 관계를 유추할 수 없기 때문에, ?? 를 찾아낼 수 없다. 즉 컴퓨터가 “단어가 다른 단어와 어떤 차이점과 공통점”을 가지는지 이해할 수가 없다는 단점이 존재한다.

이런 문제를 해결하기 위해서 단어를 다차원 공간에서 벡터로 표현하여 각 단어간의 관계를 표현할 수 있는 방법을 만들었다.

이와 같은 원리로 어떤 비연속된 카테고리 피쳐들을 숫자로 맵핑할때, 위의 boy,girl 과 같은 방식 (on_hot_encoding) 으로 의미없이 1:1 맵핑을 하는 것이 아니라, 각 카테고리들이 어떠한 연관 관계를 가질때 이 연관성을 표현하여 벡터값으로 변환하는 방법을 임베딩 (embedding)이라고 한다.


그래서 카테고리내의 값들이 서로 연관성을 가질때는 임베딩을 이용하여 벡터 값으로 변경을 한 후, 이 값을 딥모델에 넣어서 학습하면 좋은 결과를 얻을 수 있다. 카테고리화된 값을 임베딩하기 위해서는 아래와 같이 embedding_column이라는 메서드를 사용하면 된다.


tf.contrib.layers.embedding_column(education, dimension=8)

피쳐를 모델에 넣는 방법

위와 같은 방법으로 분리되고 변경된 피쳐는, Wide & deep model에서 각각 와이드 모델과, 딥모델로 주입되서 학습되게 된다.

아래와 같이 피쳐를 와이드 컬럼과 딥 컬럼으로 구별한 후에, 리스트에 넣는다.

wide_columns = [
 gender, native_country, education, occupation, workclass, relationship, age_buckets,
 tf.contrib.layers.crossed_column([education, occupation], hash_bucket_size=int(1e4)),
 tf.contrib.layers.crossed_column([native_country, occupation], hash_bucket_size=int(1e4)),
 tf.contrib.layers.crossed_column([age_buckets, education, occupation], hash_bucket_size=int(1e6))

deep_columns = [
 tf.contrib.layers.embedding_column(workclass, dimension=8),
 tf.contrib.layers.embedding_column(education, dimension=8),
 tf.contrib.layers.embedding_column(gender, dimension=8),
 tf.contrib.layers.embedding_column(relationship, dimension=8),
 tf.contrib.layers.embedding_column(native_country, dimension=8),
 tf.contrib.layers.embedding_column(occupation, dimension=8),
 age, education_num, capital_gain, capital_loss, hours_per_week]

다음 딥모델용 피쳐 리스트와 와이드 모델용 피쳐 리스트를 DNNLinearCombinedClassifier 에 각각 변수로 넣으면 된다. 이때 딥 모델은 뉴럴네트워크이기 때문에, 네트워크의 크기를 정해줘야 하는데 아래 코드에서는 각각 크기가 100인 히든 레이어와 50인 레이어 두개를 넣어서 구성하도록 하였다.

m = tf.contrib.learn.DNNLinearCombinedClassifier(
   model_dir=model_dir,
   linear_feature_columns=wide_columns,
   dnn_feature_columns=deep_columns,
   dnn_hidden_units=[100, 50])



지금 까지 아주 간단하게 나마 Wide & deep model에 대한 이론 적인 설명과 이에 대한 구현체인 DNNLinearCombinedRegressortf.contrib.learn.DNNLinearCombinedClassifier 에 대해서 알아보았다.  이 정도 개념만 있으면 실제 Wide & deep model 튜토리얼을 이해할 수 있으니, 다음은 직접 튜토리얼을 참고하기 바란다. https://www.tensorflow.org/tutorials/wide_and_deep


Reference


수포자를 위한 딥러닝


#3 - 텐서플로우로 선형회귀 학습을 구현해보자


조대협 (http://bcho.tistory.com)


앞에서 살펴본 선형 회귀(Linear regression) 머신 러닝 모델을 실제 프로그래밍 코드를 만들어서 학습을 시켜보자. 여러가지 언어를 사용할 수 있지만, 이 글에서는 텐서플로우를 기반으로 설명한다.

텐서플로우 개발 환경 셋업

텐서 플로우 개발 환경을 설정하는 방법은 여러가지가 있지만, 구글 클라우드의 데이타랩 (datalab)환경을 사용하기로 한다. 텐서플로우 환경을 설정하려면 파이썬 설치 및 연관된 수학 라이브러리를 설치해야 하는 등 설치가 까다롭기 때문에, 구글 클라우드에서 제공하는 파이썬 노트북 (Jupyter 노트북 : http://jupyter.org/ ) 이 패키징 된 도커 이미지를 사용하기로 한다.

파이썬 노트북은 일종의 위키나 연습장 같은 개념으로 연산등에 필요한 메모를 해가면서 텐서 플로우나 파이썬 코드도 적어넣고 실행도 할 수 있기 때문에 데이타 관련 작업을 하기 매우 편리하다.

또한 도커로 패키징된 데이타랩 환경은 로컬에서나 클라우드 등 아무곳에서나 실행할 수 있기 때문에 편리하고 별도의 과금이 되지 않기 때문에 편리하게 사용할 수 있다.

구글 클라우드 계정 및 프로젝트 생성

GCP 클라우드를 사용하기 위해서는 구글 계정에 가입한다. 기존에 gmail 계정이 있으면 gmail 계정을 사용하면 된다. http://www.google.com/cloud 로 가서, 좌측 상당에 Try it Free 버튼을 눌러서 구글 클라우드에 가입한다.





다음 콘솔에서 상단의 Google Cloud Platform 을 누르면 좌측에 메뉴가 나타나는데, 메뉴 중에서 “결제" 메뉴를 선택한후 결제 계정 추가를 통해서 개인 신용 카드 정보를 등록한다.



개인 신용 카드 정보를 등록해야 모든 서비스를 제한 없이 사용할 수 있다.  단 Trial의 경우 자동으로 한달간 300$의 비용을 사용할 수 있는 크레딧이 자동으로 등록되니, 이 범위를 넘지 않으면 자동으로 결제가 되는 일이 없으니 크게 걱정할 필요는 없다.

프로젝트 생성

계정 생성 및 결제 계정 세팅이 끝났으면 프로젝트를 생성한다.

프로젝트는 VM이나 네트워크 자원, SQL등 클라우드 내의 자원을 묶어서 관리하는 하나의 집합이다. 여러 사람이 하나의 클라우드를 사용할때 이렇게 프로젝트를 별도로 만들어서 별도로 과금을 하거나 각 시스템이나 팀별로 프로젝트를 나눠서 정의하면 관리하기가 용이하다.


화면 우측 상단에서 프로젝트 생성 메뉴를  선택하여 프로젝트를 생성한다.




프로젝트 생성 버튼을 누르면 아래와 같이 프로젝트 명을 입력 받는 창이 나온다. 여기에 프로젝트명을 넣으면 된다.

도커 설치

이 글에서는 로컬 맥북 환경에 데이타랩을 설치하는 방법을 설명한다.

데이타 랩은 앞에서 언급한것과 같이 구글 클라우드 플랫폼 상의 VM에 설치할 수 도 있고, 맥,윈도우 기반의 로컬 데스크탑에도 설치할 수 있다. 각 플랫폼별 설치 가이드는  https://cloud.google.com/datalab/docs/quickstarts/quickstart-local 를 참고하기 바란다. 이 문서에서는 맥 OS를 기반으로 설치하는 방법을 설명한다.


데이타 랩은 컨테이너 솔루션인 도커로 패키징이 되어 있다. 그래서 도커 런타임을 설치해야 한다.

https://www.docker.com/products/docker 에서 도커 런타임을 다운 받아서 설치한다.

도커 런타임을 설치하면 애플리케이션 목록에 다음과 같이 고래 모양의 도커 런타임 아이콘이 나오는 것을 확인할 수 있다.

하나 주의할점이라면 맥에서 예전의 도커 런타임은 오라클의 버추얼 박스를 이용했었으나, 제반 설정등이 복잡하기 때문에, 이미 오라클 버추얼 박스 기반의 도커 런타임을 설치했다면 이 기회에, 도커 런타임을 새로 설치하기를 권장한다.

다음으로 도커 사용을 도와주는 툴로 Kitematic 이라는 툴을 설치한다. (https://kitematic.com/) 이 툴은 도커 컨테이너에 관련한 명령을 내리거나 이미지를 손쉽게 관리할 수 있는 GUI 환경을 제공한다.


구글 클라우드 데이타 랩 설치

Kitematic의 설치가 끝났으면 데이타랩 컨테이너 이미지를 받아서 실행해보자, Kitematic 좌측 하단의 “Dokcer CLI” 버튼을 누르면, 도커 호스트 VM의 쉘 스크립트를 수행할 수 있는 터미널이 구동된다.



터미널에서 다음 명령어를 실행하자


docker run -it -p 8081:8080 -v "${HOME}:/content" \

 -e "PROJECT_ID=terrycho-firebase" \

 gcr.io/cloud-datalab/datalab:local


데이타랩은 8080 포트로 실행이 되고 있는데, 위에서 8081:8080은  도커 컨테이너안에서 8080으로 실행되고 있는 데이타 랩을 외부에서 8081로 접속을 하겠다고 정의하였고, PROJECT_ID는 데이타랩이 접속할 구글 클라우드 프로젝트의 ID를 적어주면 된다. 여기서는 terrycho-firebase를 사용하였다.

명령을 실행하면, 데이타랩 이미지가 다운로드 되고 실행이 될것이다.

실행이 된 다음에는 브라우져에서 http://localhost:8081로 접속하면 다음과 같이 데이타랩이 수행된 것을 볼 수 있다.


학습하기

이제 텐서 플로우 기반의 머신러닝을 위한 개발 환경 설정이 끝났다. 이제 선형 회귀 모델을 학습 시켜보자

테스트 데이타 만들기

학습을 하려면 데이타가 있어야 하는데, 여기서는 랜덤으로 데이타를 생성해내도록 하겠다. 다음은 데이타를 생성하는 텐서 플로우코드이다.

텐서 플로우 자체에 대한 설명과 문법은 나중에 기회가 되면 별도로 설명하도록 하겠다.


import numpy as np

num_points = 200

vectors_set = []

for i in xrange(num_points):

 x = np.random.normal(5,5)+15

 y =  x*1000+ (np.random.normal(0,3))*1000

 vectors_set.append([x,y])

 

x_data = [v[0] for v in vectors_set ]

y_data = [v[1] for v in vectors_set ]


for 루프에서 xrange로 200개의 샘플 데이타를 생성하도록 하였다.

x는 택시 주행거리로,  정규 분포를 따르는 난수를 생성하되 5를 중심으로 표준편차가 5인 데이타를 생성하도록 하였다. 그래프를 양수로 만들기 위해서 +15를 해주었다.

다음으로 y값은 택시비인데, 주행거리(x) * 1000 + 정규 분포를 따르는 난수로 중심값은 0, 그리고 표준편차를 3으로 따르는 난수를 생성한후, 이 값에 1000을 곱하였다.

x_data에는 x 값들을, 그리고 y_data에는 y값들을 배열형태로 저장하였다.


값들이 제대로 나왔는지 그래프를 그려서 확인해보자. 아래는 그래프를 그리는 코드이다.

Pyplot이라는 모듈을 이용하여 plot이라는 함수를 이용하여 그래프를 그렸다. Y축은 0~40000, X축은 0~35까지의 범위를 갖도록 하였다.

import matplotlib.pyplot as plt

plt.plot(x_data,y_data,'ro')

plt.ylim([0,40000])

plt.xlim([0,35])

plt.xlabel('x')

plt.ylabel('y')

plt.legend()

plt.show()

그려진 그래프의 모양은 다음과 같다.


학습 로직 구현 

이제 앞에서 생성한 데이타를 기반으로해서 선형 회귀 학습을 시작해보자. 코드는 다음과 같다.


import tensorflow as tf


W = tf.Variable(tf.random_uniform([1],-1.0,1.0))

b = tf.Variable(tf.zeros([1]))

y = W * x_data + b


loss = tf.reduce_mean(tf.square(y-y_data))

optimizer = tf.train.GradientDescentOptimizer(0.0015)

train = optimizer.minimize(loss)


init = tf.initialize_all_variables()

sess = tf.Session()

sess.run(init)


for step in xrange(10):

 sess.run(train)

 print(step,sess.run(W),sess.run(b))

 print(step,sess.run(loss))

 

 plt.plot(x_data,y_data,'ro')

 plt.plot(x_data,sess.run(W)*x_data + sess.run(b))

 plt.xlabel('x')

 plt.ylabel('y')

 plt.legend()

 plt.show()


W의 초기값은 random_uniform으로 생성을 한다. 초기값은 -1.0~1.0 사이의 값으로 생성하도록 하였다.

( radom_uniform 에서 첫번째 인자 [1]은 텐서의 차원을 설명하는데, 1은 1차원으로 배열과 같은 형태가 2는 2차원으로 행렬과 같은 형태, 3은 3차원 행렬 행태가 된다.)

다음 b는 tf.zeros([1])으로 정의했는데, 1차원 텐서로 값이 0이 된다. (zeros)

학습을 하고자 하는 공식 (가설은) y = W * x_data + b 이 된다.


다음으로 코스트 함수와 옵티마이져를 지정하는데, 코스트 함수는 앞 글에서 설명한것과 같이 

가설에 의해 계산된 값 y에서 측정값 y_data를 뺀후에, 이를 제곱하여 평균한 값이다. 코드로 옮기면 다음과 같다.

loss = tf.reduce_mean(tf.square(y-y_data))


코스트 함수에서 최소 값을 구하기 위해서 옵티마이저로 경사하강법 (Gradient descent) 알고리즘을 사용하기 때문에, 옵티마이저로 tf.train.GradientDescentOptimizer(0.0015) 과 같이 지정하였다. 인자로 들어가는 0.0015는 경사 하강법에서 학습 단계별로 움직이는 학습 속도를 정의하는 것으로 러닝 레이트 (Learning rate라고 한다)) 이 내용은 뒤에서 다시 자세하게 설명하겠다.

코스트 함수와 옵티마이져(Gradient descent)가 정의되었으면 트레이닝 모델에 적용한다.

train = optimizer.minimize(loss)

는 경사 하강법(Gradient descent) 을 이용하여 코스트 함수 (loss)가 최소가 되는 값을 찾으라는 이야기이다.


다음 코드에서는 for loop로 학습을 10번을 반복해가면서 학습을 하라는 이야기로,

for step in xrange(10):

 sess.run(train)

 print(step,sess.run(W),sess.run(b))

 print(step,sess.run(loss))


학습 단계별로, W,b값 그리고 loss의 값을 화면으로 출력하도록 하였다.

그리고 학습이 어떻게 되는지 그래프로 표현하기 위해서

 plt.plot(x_data,sess.run(W)*x_data + sess.run(b))

X_data를 가로축으로 하고, W*x_data + b의 값을 그래프로 출력하도록 하였다.


이렇게 해서 학습을 진행하면 다음과 같은 그래프가 순차적으로 출력되는 것을 확인할 수 있다.


그래프가 점점 데이타의 중앙에 수렴하면서 조정되는 것을 확인할 수 있다.

이렇게 해서 맨 마직막에 다음과 같은 결과가 출력된다.



W는 1018, b는 51 그리고 코스트의 값은 10272684.0이 됨을 확인할 수 있다.

이렇게 학습이 끝났고, 이제 거리에 따른 택시비는

(택시비) = 1018 * (거리) + 51로 

이 공식을 가지고 거리에 따른 택시비를 예측할 수 있다.


테스트에 사용한 모든 데이타는 링크를 참고하면 얻을 수 있다.

https://github.com/bwcho75/tensorflowML/blob/master/1.%20Linear%20Regression.ipynb

학습 속도(러닝 레이트 / Learning Rate) 조정하기 

앞의 예제에서 optimizer를  tf.train.GradientDescentOptimizer(0.0015) 에서 0.0015로 학습 속도를 지정하였다. 그렇다면 학습 속도란 무엇인가?


선형 회귀 분석의 알고리즘을 되 짚어보면, 가설에 의한 값과 원래값의 차이를 최소화 하는 값을 구하는 것이 이 알고리즘의 내용이고, 이를 코스트 함수를의 최소값을 구하는 것을 통해서 해결한다.

W의 값을 조정해 가면서 코스트의 값이 최소가 되는 값을 찾는데, 이때 경사 하강법 (Gradient descent)방법을 사용하고 경사의 방향에 따라서 W의 값을 조정하는데, 다음 W의 값이 되는 부분으로 이동하는 폭이 학습 속도 즉 러닝 레이트이다. (아래 그림)


이 예제에서는 학습 속도를 0.0015로 설정하고, 매번 학습 마다 W를 경사 방향으로 0.0015씩 움직이도록 하였다.  그러면 적정 학습 속도를 어떻게 구할까?

오퍼 슈팅 (Over shooting)

먼저 학습 속도가 크면 어떤일이 벌어지는지를 보자

학습 속도를 0.1로 주고 학습을 시키면 어떤 결과가 생길까?

W,b 그리고 cost 함수를 찍어보면 다음과 같은 결과가 나온다.

(0, array([ 86515.3671875], dtype=float32), array([ 4038.51806641], dtype=float32))
(0, 3.1747764e+12) ← cost
(1, array([-7322238.], dtype=float32), array([-341854.6875], dtype=float32))
(1, 2.3281766e+16)
(2, array([  6.27127488e+08], dtype=float32), array([ 29278710.], dtype=float32))
(2, 1.7073398e+20)
(3, array([ -5.37040691e+10], dtype=float32), array([ -2.50728218e+09], dtype=float32))
(3, 1.252057e+24)
(4, array([  4.59895629e+12], dtype=float32), array([  2.14711517e+11], dtype=float32))
(4, 9.1818105e+27)
(5, array([ -3.93832261e+14], dtype=float32), array([ -1.83868557e+13], dtype=float32))
(5, 6.7333667e+31)
(6, array([  3.37258807e+16], dtype=float32), array([  1.57456078e+15], dtype=float32))
(6, 4.9378326e+35)
(7, array([ -2.88812128e+18], dtype=float32), array([ -1.34837741e+17], dtype=float32))
(7, inf)
(8, array([  2.47324691e+20], dtype=float32), array([  1.15468523e+19], dtype=float32))
(8, inf)
(9, array([ -2.11796860e+22], dtype=float32), array([ -9.88816316e+20], dtype=float32))
(9, inf)

Cost 값이 3.1e+12,2.3e+16,1.7e+20 ... 오히려 커지다가 7,8,9에서는 inf(무한대)로 가버리는 것을 볼 수 있다.


그래프를 보면 다음과 같은 형태의 그래프가 나온다.


학습이 진행될 수 록, 코스트 함수의 결과 값이 작아지면서 수렴이 되어야 하는데,  그래프의 각이 서로 반대로 왔다갔다 하면서 발산을 하는 모습을 볼 수 있다.

코스트 함수의 그래프를 보고 생각해보면 그 원인을 알 수 있다.


학습 속도의 값이 크다 보니, 값이 아래 골짜기로 수렴하지 않고 오히려 반대편으로 넘어가면서 점점 오히려 그래프 바깥 방향으로 발산하면서, W값이 발산을 해서 결국은 무한대로 간다. 이를 오버 슈팅 문제라고 한다.

그래서, 학습 과정에서 코스트 값이 수렴하지 않고 점점 커지면서 inf(무한대)로 발산하게 되면, 학습 속도가 지나치게 큰것으로 판단할 수 있다.

스몰 러닝 레이트(Small Learning Rate)

반대로 학습 속도가 매우 작을때는 어떤일이 발생할까?

학습속도를 0.0001로 작게 설정을 해보자.


(0, array([ 86.40672302], dtype=float32), array([ 4.03895712], dtype=float32))
(0, 3.6995174e+08)
(1, array([ 165.43540955], dtype=float32), array([ 7.72794485], dtype=float32))
(1, 3.1007162e+08)
(2, array([ 237.61743164], dtype=float32), array([ 11.09728241], dtype=float32))
(2, 2.6011749e+08)
(3, array([ 303.54595947], dtype=float32), array([ 14.17466259], dtype=float32))
(3, 2.18444e+08)
(4, array([ 363.76275635], dtype=float32), array([ 16.98538017], dtype=float32))
(4, 1.8367851e+08)
(5, array([ 418.76269531], dtype=float32), array([ 19.55253601], dtype=float32))
(5, 1.5467589e+08)
(6, array([ 468.99768066], dtype=float32), array([ 21.89723206], dtype=float32))
(6, 1.304809e+08)
(7, array([ 514.8805542], dtype=float32), array([ 24.03874016], dtype=float32))
(7, 1.1029658e+08)
(8, array([ 556.78839111], dtype=float32), array([ 25.99466515], dtype=float32))
(8, 93458072.0)
(9, array([ 595.06555176], dtype=float32), array([ 27.78108406], dtype=float32))
(9, 79410816.0)

 

코스트값이 점점 작은 값으로 작아지는 것을 볼 수 있지만 계속 감소할 뿐 어떤 값에서 정체 되거나 수렴이 되는 형태가 아니다.

그래프로 표현해보면 아래 그래프와 같이 점점 입력 데이타에 그래프가 가까워 지는 것을 볼 수 있지만, 입력 데이타에 그래프가 겹쳐지기 전에 학습이 중지 됨을 알 수 있다.


이런 문제는 학습속도가 너무 작을 경우 아래 그림 처럼, 코스트 값의 최소 값에 도달하기전에, 학습이 끝나버리는 문제로 Small learning rate 라고 한다.




이 경우에는 학습 횟수를 느리거나 또는 학습 속도를 조절함으로써 해결이 가능하다.


다음글에서는 분류 문제의 대표적인 알고리즘인 로지스틱 회귀 (Logistic Regression)에 대해서 알아보도록 한다.


수포자를 위한 딥러닝


#2 - 선형회귀분석을 통한 머신러닝의 기본 개념 이해


조대협 (http://bcho.tistory.com)


Linear Regression을 통한 머신 러닝의 개념 이해

거리에 따른 택시 요금 문제

머신러닝이란 무엇일까? 개념 이해를 돕기 위해서 선형 회귀 (Linear Regression)이라는 머신러닝 모델을 보자. 먼저 선형 회귀 (Linear regression)이 무엇인지 부터 이해를 해야 하는데, 쉽게 설명하자면 결과값 (output value)이 있고 그 결과값을 결정할 것이라고 추정되는 입력값 (input value)과 결과 값의 연관관계를 찾는 것이고 이를 선형 관계를 통해 찾는 방법이 선형 회귀 (Linear regression)이다.

예를 들어서 설명해보자, 택시 요금을 예로 들어보자,택시 요금은 물론 막히냐 마냐에 따라 편차가 있지만, 대부분 거리에 비례해서 요금이 부과된다. 그래서 결과값 (요금)과 입력값 (거리)의 관계를 찾아야 한다.


거리별 요금을 그래프로 나타내보면 대략 다음과 같은 분포를 띄게 된다

원본 데이타의 거리를 x_data 그리고, 그 거리에서 측정된 택시 요금을 y_origin 이라고 하자.



가설 (Hypothesis) 정의

거리와 요금이 서로 비례하기 때문에, 거리(x_data)와 요금(y_data)간의 상관 관계는 다음과 같이 일차 방정식과 형태의 그래프를 그리게 된다고 가정하자. W (Weight)는 그래프의 각도, b는 bias를 뜻한다

y_data = Wx_data + b

이 일차 방정식 형태로 대충 1차원 그래프를 그려보자 같은 형태로 아래와 같이 그래프를 그려봤다.


그래프를 그려보니 그래프의 각이 안맞는것 같다. 그래프의 각도와 높이를 보정해보자


그래프를 보정했지만 또 안 맞는 것 같다. 그렇다면 최적의 그래프의 각도 W와, 높이 B는 어떻게 찾아야 하는 것일까?

코스트(비용) 함수

우리가 구하고자 하는 그래프는 실제 값에서 그래프의 값까지 차이가 가장 작은 값을 구하고자 하는 것이다. 아래 그림을 보자, 아래와 같이 y_data=Wx_data +b와 같은 그래프를 그렸다고 하자.


원래 값에서 우리가 예측한 값의 차이는 

(원래값과 계산된 값의 차이) = 측정값 - 그래프의 값

인데, 차이를 d라고 하자. 그리고 그래프에 의해서 계산된 값은 y_data라고 하면 택시 거리 x_data 에서 원래 측정된 값을 y_orgin라고 해서 수식으로 나타내면,

d = y_data - y_origin

이 된다. 이때 측정값은 여러개가 있기 때문에 n이라고 하면  n번째 측정한 택시비와 산식에 의해서 예측된 값의 차이는 dn이 된다.


dn = y_data_n - y_origin_n


즉 우리가 구하고자 하는 값은 dn의 합이 최소가 되는 W와 b의 값을 구하고자 하는 것이다.

다르게 설명하면 실제 측정한값과, 예측한 값의 차이가 최소가 되는 W와 b를 구하고자 하는 것이다.

dn은 위의 그래프에서 처럼 그래프 위에도 있을 수 있지만 (이경우 dn은 양수), 그래프 아래에도 있을 수 있기 때문에, (이경우 dn은 음수). 합을 구하면, 예측 선에서의 실측값 까지의 거리의 합이 되지 않기 때문에, dn에 대한 절대값을 사용한다고 하자.

그리고 n이 측정에 따라 여러개가 될 수 있기 때문에, 평균을 사용하자.


( ABS(d1)+ABS(d2)+ABS(d3)+.....+ABS(dn)) ) / n


즉 우리가 구하고자 하는 W와 b는 위의 함수의 값이 최소가 되는 값을 구하면 된다.

이렇게 측정된 값에서 연산된 값간의 차이를 연산하는 함수를 비용 함수 또는 영어로 코스트 함수 (Cost function이라고 한다.


사람이 일일이 계산할 수 없이니 컴퓨터를 이용해서 W=0.1,0.2,0.3,.... b=0.1,0.2,0.3,..... 식으로 넣어보고 이 코스트 함수가 가장 최소화되는 W와 b의 값을 찾을 수 있다.

옵티마이져 (Optimizer)

코스트 함수의 최소값을 찾는 알고리즘을 옵티마이져(Optimizer)라고 하는데, 상황에 따라 여러 종류의 옵티마이져를 사용할 수 있다. 여기서는 경사 하강법 (Gradient Descent) 라는 옵티마이져에 대해서 소개하도록 하겠다.


경사 하강법

그러면 W와 b를 구할때 W와 b를 어떤식으로 증가 또는 감소 시켜서 코스트 함수의 최소값을 가장 효율적으로 찾아낼 수 있을까? 위에서 언급한것 처럼 W를 0.0에서 부터 ). 0.1씩 증가시켜나가고 b도 같이 0.0에서 부터 1씩 증가 시켜 나갈까? 무한한 컴퓨팅 자원을 이용하면 되기는 하겠지만, 이렇게 무식하게 계산하지는 않는다.

코스트 함수를 최적화 시킬 수 있는 여러가지 방법이 있지만, Linear regression의 경우에는 경사 하강법 (그레이언트 디센트 : Gradient descent)라는 방식을 사용한다. 경사하강법에 대해서는 자세하게 알필요는 없고 ”대략 이런 개념을 사용하는 구나” 하는 정도만 알면 된다.


경사 하강법을 사용하기 위해서는 위의 코스트 함수를,측정값과 예측값의 절대값의 평균이 아니라 평균 제곱 오차라는 함수를 사용한다.

이 함수는 형식으로 정의되는데, 평균 제곱 오차 함수 (Mean square error function)이라고 한다.


Cost =  Sum( (y_data_n - y_origin_n) ^ 2) / n


풀어서 설명하면, n 번째의 원래데이타(y_origin_n)와 예측 데이타(y_data_n)의 차이를 제곱(^2)해서, 이 값을 n으로 나눈 평균 값이다.

즉 이 Cost가 최소가 되는 W와 b값을 구하면 된다.

편의상 W하나만을 가지고 설명해보자. 위의 그래프를 W와 b에 대한 상관 그래프로 그려보면 다음과 같은 함수 형태가 된다.


이 그래프에서 W에 대한 적정값에 대한 예측을 시작하는 점을 위의 그림에서 파란 점이라고 하면, 경사 하강법은 현재 W의 위치에 대해서, 경사가 아래로 되어 있는 부분으로 점을 움직이는 방법이다. 어느 방향으로 W를 움직이면 Cost 값이 작아지는지는 현재 W위치에서 비용 함수를 미분하면 된다. (고등학교 수학이 기억이 나지 않을 수 있겠지만 미분의 개념은 그래프에서 그 점에 대한 기울기를 구하는 것이다. )


이렇게, 경사를 따라서 아래로 내려가다 보면 Cost 함수가 최소화가 되는 W 값을 찾을 수 있다. 이렇게 경사를 따라서 하강 (내려가면서) 최소값을 찾는다고 하여 경사 하강법이라고 한다.  


학습

코스트 함수가 정의 되었으면 실제 데이타 x_data_n과 y_data_n을 넣어서 경사하강법에 의해서 코스트 함수가 최소가 되는 W와 b를 구한다. 이 작업은 W값을 변화시키면서 반복적으로 x_data_n로 계산을 하여, 실제 측정 데이타와 가설에 의해서 예측된 결과값에 대한 차이를 찾아내고 최적의 W와 b값을 찾아낸다.

예측

학습 과정에 의해서 최적의 W와 b를 찾았으면 이제, 이 값들을 이용해서 예측 해보자

학습에 의해서 찾아낸 W가 1600, b가 2000이라고 하면, 앞의 가설에서 정의한 함수는 Wx*b였기 때문에, 예측 함수는 


y = Wx +b

거리에 따른 택시비 = W*(거리) + b

거리에 따른 택시비 = 1600 * (거리) + 2000

이 되고, 이를 학습된 모델 이라고 한다.


이제 예측을 수행해보자, 거리가 10km일 때 택시비는 얼마일까? 공식에 따라

택시비 = 1600 * 10km + 2000

으로, 18000원이 된다.

머신 러닝의 순서

지금까지 택시 거리와 택시비에 대한 문제를 가지고 머신 러닝에 대한 기본 원리를 살펴보았다.

이를 요약해서 머신 러닝이란 것이 어떤 개념을 가지고 있는지 다시 정리해보자.


기본 개념은 데이타를 기반으로해서 어떤 가설 (공식)을 만들어 낸 다음, 그 가설에서 나온 값이 실제 측정값과의 차이(코스트 함수)가 최소한의 값을 가지도록 변수에 대한 값을 컴퓨터를 이용해서 찾은 후, 이 찾아진 값을 가지고 학습된 모델을 정의해서 예측을 수행 하는 것이다.  


학습 단계

즉 모델을 만들기 위해서, 실제 데이타를 수집하고, 이 수집된 데이타에서 어떤 특징(피쳐)를 가지고 예측을 할것인지 피쳐들을 정의한 다음에, 이 피쳐를 기반으로 예측을 한 가설을 정의하고, 이 가설을 기반으로 학습을 시킨다.


예측 단계

학습이 끝나면 모델 (함수)가 주어지고, 예측은 단순하게, 모델에 값을 넣으면, 학습된 모델에 의해서 결과값을 리턴해준다.


지금까지 Linear regression 분석을 통한 머신러닝의 원리에 대해서 간략하게 알아보았다. 다음 다음장에서는 이 모델을 어떻게 프로그래밍 언어를 이용하여 학습을 시키고 운영을 하는지에 대해서 알아보도록 하겠다.



Thanx to 

이글은 딥러닝 전문가 김홍회 박사님(Ayden Kim - https://www.facebook.com/Ayden.Kim )이 검수해주셨습니다. 감사합니다.