빅데이타 & 머신러닝/머신러닝

Wide and deep network 모델 활용하기

Terry Cho 2017. 7. 20. 17:12


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