페이지

2022년 8월 3일 수요일

신경망 기반 추천시스템은 어떻게 동작할까? -2편

 1편에 이어 2편에서는, 전 처리, 협업필터 모델 설계, 심층망 기반 모델 설계 그리고 상품 및 사용자 특성을 입력으로 하여 모델을 훈련 및 테스트하고 평가하는 부분을 살펴보겠다.

전 처리 (정규화 및 분할)

신경망에 입력하기위해 데이터를 아래 그림11과 같이 sklearn 의 MinMaxScaler 를 사용하여 df_users 의 feature 들 값의 범위를 0.5~1 사이의 값으로 정규화 한다.  


<그림 11. df_users features 값 정규화>

아래 그림 12의 ①에서 users x products 로 구성된 df_users의 컬럼의 80%를 split으로 정하고 ②에서 split 만큼을 train에 ③에서 나머지를 test 로 할당. 전체 9,741 column 의 80% 인 7,792 컬럼을 보유한다.


<그림 12. train, test 분할>

신경망 추천시스템에 결합되는 정보들 

협업필터 모델 설계 목적은 user와 product 행렬의 생성이다. user-product쌍이 입력이고 출력은 평점이다. 우리가 활용하려고 하는 데이터를 한번 살펴보자.

목표 변수: 고객 평점이다. 직접 주어질 수도 있고 시청 시간 등으로 추정할 수도 있다.
상품 특성들(features): 우리의 경우는 영화의 장르다.
사용자 프로파일: 사용자에 대한 인구통계학적, 시청시간 같은 행위적 정보들이다.
맥락적 정보(context): 우리의 경우 평가와 관련된 주중 혹은 주말 여부 정보다.

1편의 그림2와 같이 우선 추가적으로 신경망에 결합될 상품 특성들(features)과 맥락적 정보(context)들을 아래 그림 13과 같이 생성한다. ①과 같이 genres 와 name을 제거하여 old 컬럼과 19개 장르를 포함한 상품 특성(features) 정보, ②에서 context중에 user와 product를 제거하여 사용자가 언제 평점을 주는 지에 대한 정보인 daytime 과 weekend 컬럼을 맥락 정보로 사용한다. context는 1편의 그림 7 참고.


< 그림 13. features 와 context 생성>

Train 과 Test에 feature 와 context 부가정보 추가

 아래 그림14의 ①과 같이 판다스 데이터프레임의 stack() 함수를 사용하여 컬럼 별 내용을 인덱스별로 쌓는다. 그림12의 df_train 이 사용자별 영화의 sparse 한 행렬 구조였다면 stack()함수를 통해 영화 컬럼 별 내용을 사용자 인덱스별로 쌓는다. 영화 평점이 없는 것은 제외(dropna=True)하고 인덱스를 리셋하고 컬럼의 첫번째 이름인 평점을 “y”로 변경한다. ②에서 여기에 features들을 앞의 ①의 내용의 고유 값들을 왼쪽(how=’left’)에 두고, 추가할 features 데이터프레임을 왼쪽 ‘product’ 열을 기준으로(left_on=”product”) 추가할 때 오른쪽 데이터프레임의 인덱스를 join key 로 사용(right_index=True). 여기에 더해서 ③에 context 데이터프레임을 왼쪽에 이어서 추가.


<그림 14. train 에 features 와 context 추가>

아래 그림 15와 같이 test 데이터프레임도 ①과 같이 products 데이터프레임에서 genres와 name 을 제거한 features 데이터프레임을 위 train과 같이 병합한다. 


<그림 15. test 에 features 와 context 추가>

상품 및 사용자 특성이 반영된 심층망 기반 모델 설계

  심층망 기반 협업필터는 행렬 인수분해 와 신경망을 결합한다. 그리고 여기에 더해 심층망 기반의 장점인 확장성을 활용하여 feature 와 context 가 결합된 모델을 작성한다. 우선 신경망과 결합될 행렬 인수분해(matrix factorization)모델을 구축한다. 아래 그림16의 ①은 usr와 prd의 임베딩 출력차원 크기로 50을 정했다. ②users 데이터프레임의 행과 열을 usr, prd 로 할당 ③feature 와 ④context 입력 층 구성을 위해 크기가 필요 ⑤users 입력 층은 1자리 입력 ⑥users 임베딩은 (None, 1, 50)의 출력 모양을 갖는다. None 은 batch 크기. ⑦mf_users 와 mf_products 를 dot product 실행하기위해 출력 모양을 각각 (None, 50)으로 reshape한다. ⑧mf_users 와 mf_products 를 ⑧은 mf_users와 mf_products 를 행 중심(axes=1)으로 dot product.

<그림 16. 행렬 인수분해 구축>

 이어서 아래 그림 17은 다중 퍼셉트론 신경망(multi layer perceptron)기반 구축이다. ①은 mlp_users와 mlp_products를 Keras의 layers.ConcaTenate로 결합(concateneate). ②는 Dense층을 units=25, relu 활성화함수로 생성.


<그림 17. 신경망 기반 구축>

 다음으로 상품 및 사용자 특성을 추가한 층을 생성하고 이 모든 4가지 모델을 결합한 모델을 작성하고 컴파일한다. ①은 feat차원의 입력을 받는 입력층 ②는 feat만큼의 unit을 보유하는 Dense 층으로 relu 활성화함수를 통과시킨다. 아래 contents도 마찬가지. ③은 지금까지 생성한 4가지 협업필터, 다중퍼셉트론, feature 및 context를 결합(concat)한다. ④결합된 4개 입력을 unit 하나의 dense 층에 linear 활성화함수를 통과시킨다. Keras의 activation.linear는 입력 값이 변화되지 않고 그대로 출력된다.

<그림 18. 상품, 사용자 특성 부가 및 모델 컴파일>

 아래 그림 19의 ①과 같이 입력으로 train의 user, product, feature, context를 정의하고 출력으로 평점 y 를 설정하여 ②에서 100회, epoch batch 크기는 128, train의 30%를 validation으로 설정하여 model.fit 을 통해 훈련을 시행한다. 100회 훈련 후 validated loss 가 0.1890으로 표시된다.

<그림 19. model 훈련>

 훈련된 모델에 4가지 입력을 넣어 아래 그림 20의 ①과 같이 모델이 사용자의 영화에 대한 평점을 예측한 값 “yhat”을 구한다.

<그림 20. test 의 평점 예측>

 아래 그림 21은 특정사용자를 지정하고, 해당 사용자의 test 의 영화 top 5와 모델이 예측하는 영화 top 5를 비교하여 평가하는 부분이다. 우선 처음 사용자로 (user=1) 정하고 ①에서 사용자1이 관람한 영화의 평점 “y” 값의 내림차순으로 test 데이터세트의 영화(product)를 상위 5개를 표시한다. ②에서 마찬가지로 사용자1에 대한 모델이 예측한 평점 “yhat” 값의 내림차순으로 영화(product) 상위 5개를 출력. ③은 평점순서대로 대응되는 test 데이터세트의 영화(상품)번호, ④는 모델이 예측한 영화(상품)번호. ⑤true positive는 5개중에 4개를 맞추어 80%. ⑥과 ⑦ accuracy와 mrr은 순서가 중요하여 점수가 낮다.

<그림 21. 특정 사용자의 top5 영화 예측 평가>

 mrr 평가지표는 아래 그림 22와 같이 제안된 결과의 순위에서 상호간(reciprocal)의 순서 위치의 평균을 산출한다. 아래 예에서는 (1/3 + 1/2 + 1)/3 = 11/18 이다. 1에 가까울수록 좋은 점수이다.

<그림 22. mean-reciprocal-rank 의 예>

결언

  지금까지 추천시스템에서 협업필터 방식의 모델이 비선형 함수를 학습하도록 하여 모델의 표현력을 향상시키도록 협업필터, 심층신경망, 상품 및 사용자특성을 모두 결합하여 반영하는 신경망기반 추천시스템의 작동 방법을 살펴보았다. 


신경망 기반 추천시스템은 어떻게 동작할까? -1편

영화 추천 시스템

MovieLens 데이터세트가 제공하는 9700 여개의 영화와 수백만의 사용자의 평점이 담긴 데이터세트를 사용하여 영화 추천 시스템을 제작해보기로 한다. 추천시스템에 대한 일반적인 개념은 이전 뉴스레터10호에서 설명한 협업필터링과 행렬인수분해(Matrix Factorization) 부분을 참고하기 바란다. 본 추천시스템 개발은 NVIDIA A100 GPU X 1, Tensorflow 2.6, Keras 2.6, Python 3.8 환경에서 개발되었고 data_movies.xlsx 데이터는 약 3MB 의 크기다. 아래 그림1은 추천시스템 처리 절차에 대한 흐름도다.



<그림 1. 추천시스템 처리 절차>

 

상품 및 사용자 특성이 반영된 신경망 협업필터링 추천 시스템

 
 본 시스템은 아래 그림2와 같이 전통적인 행렬인수분해(Matrix Factorization)를 통한 협업필터링에 더해서 최근의 신경망기반 협업필터링 논문에서 제시하는 신경망의 비선형성과 행렬인수분해의 결합을 통한 구성을 시도한다.  심층신경망의 완전연결층을 통한 정보를 부가적으로 넣어 줌으로써 행렬인수분해에서 혹시라도 빠뜨릴 수 있는 패턴과 특성들을 획득한다. 이러한 신경망 기반 협업필터링 시스템은 입력되는 데이터가 제공하는 가용한 모든 데이터를 아래 그림2와 같이 완전 연결층을 통하여 예측하는데 있어 모두 결합하여 반영할 수 있는 확장성을 제공한다. 협업필터링에 사용되는 사용자-상품 임베딩에 더해서 예를 들면, 영화 장르와 같은 상품 특성과 주중 시청 혹은 주말 시청과 같은 사용자 특성이 추가 정보로 신경망에 결합될 수 있다.


<그림 2. 신경망 기반 협업필터링 추천 시스템>


왜 심층신경망 기반인가?

 일반적인 행렬 인수분해(matrix factorization) 접근방식은 그 선형성(linearity)으로 인해 모델의 표현력을 제한한다. 사용자와 상품(영화)이 동일한 잠재 공간에서 dot product의 선형 연산을 통해 평점을 산출한다. 이경우, 규정된 사용자끼리 만 연산이 수행되어 어떤 새로운 사용자와 나머지 모든 사용자 간의 관계가 묘사될 수 없다는 점이다. 심층신경망을 사용하는 이유는 보다 복잡한 관계를 잠재 공간(latent space)에서 허용하도록 심층신경망을 통하여 비선형(non-linear)함수를 학습하도록 하여 모델의 표현력을 향상시키는 것이다.


확률적 경사 하강법을 통한 행렬 분해

 자 그럼, 우리가 살펴보려고 하는 문제를 생각해보자. Matrix factorization(MF, 행렬인수분해)는 추천 시스템에서 사용되는 협업 필터링(CF, Collaborative Filtering)중 대표적인 알고리즘이다. Matrix factorization 알고리즘은 아래 그림3과 같이 사용자-상품 간 상호작용 행렬을 두개의 저차원의 정사각형 행렬로 분해해 작동하게 표시된다. 우선 데이터로부터 ④와 같은 사용자-상품(영화)의 행렬을 만든다. 실제로는 ④의 노란색 셀과 같은 곳에만 값을 알 수 있고 나머지 값들을 모른다. 신경망을 통해 입력되면 ①의 사용자-특성 과 ②의 상품별-특성의 정사각형 행렬로 분해된다. 
  머신러닝이 ①과 ②에 임의의 행렬 초기 값을 가지고 시작한다고 가정해보자. 좌측의 Amy 사용자의 영화1 값이 ③과 같이 행렬 내적(dot product)를 통하여 1.44 라는 값을 가지게 된다. 머신러닝은 우측의 Amy 사용자의 영화1에 대한 평점 3과 방금 계산된 1.44를 비교하여 손실(loss)차가 있음을 파악하고 이 손실을 최소화되도록 확률적 경사 하강법(stochastic gradient descent)를 통한 훈련을 시행한다. 이를 통하여 F1과 F2, 즉 예를 들면, 로맨틱 과 액션이 영화 장르의 특성 값을 ①과 ②의 저 차원 행렬이 보유하게 된다. 따라서 만약 ④의 Bill 의 영화 3의 평점을 모른다면, ③과 같은 방식으로 행렬 내적 곱을 통하여 값을 예측할 수 있게 된다.


<그림 3. 행렬 인수분해 값을 찾기 위한 훈련>

데이터 로드 및 products feature engineering

 우선 아래 그림4의 ①과 같이 data_movies.xlsx 에서 products sheet 만 로드 한다. ②와 같이 dtf_products 판다스 데이터프레임중에 ‘genres’가 없는 데이터를 제외한다. ③에서 ‘title’에 있는 특수문자를 제거한다. 


<그림 4. products feature engineering>

  ④에서 ‘title’중 마지막 연도표시의 괄호를 벗기고 정수화 한다. ⑤에서 년도가 2000년 이상인 경우 1로 표시한다. 

Users feature engineering

아래 그림 5의 ①과 같이 users sheet 만 로드 하되 10,000 record 만 로드 한다. ②에서 timestamp 형식을 바꾸고 ③에서 6에서 20시 사이를 1로 표시하는 daytime 컬럼 생성, ④는 date.weekday() 에서 정수로 요일을 반환. 즉 5,6 은 토,일요일로 weekend 에 1로 표시.


<그림 5. users feature engineering>

데이터 정제

아래 그림 6과 같이 사용할 컬럼만 정제한다. ①은 4개의 컬럼을 product를 인덱스로 하여 구성, ②의 users 데이터프레임은 3개 항목으로, “y”는 평점.

<그림 6. 데이터 정제>

 이전 그림 5에서 추출한 daytime 과 weekend 변수를 나중에 신경망에 추가적으로 입력되는 맥락적(context) 정보로 사용하기위해 아래 그림 7과 같은 context dataframe을 생성한다.


< 그림 7. context dataframe 작성>

Products-Features matrix

 Products 를 가지고 우선 products-features 행렬을 구성한다. 아래 그림8과 같이 genres로부터 장르 값들을 뽑아낸다. ①은 “genres”컬럼에서 “|”를 제거 후 tags로 담아, ②에서 columns에 대입하고 ③에서 장르가 없는 데이터를 제거. ④를 통하여 ‘genres’에 컬럼이 있는 위치에 해당되면 값 1을 표시하는 products 데이터프레임 생성.

                       

          <그림 8. products-features 행렬 구성>

             

Users-Products matrix

 users 데이터프레임을 가지고 users-products 행렬을 구성한다. 아래 그림 9의 ①에서 피봇하기 전에 tmp에 복사. ②users df 를 “user”를 index로, 컬럼은 “product”고 값은 “y”인 평점인 매우 sparse 한 df_users 생성. ③을 통하여 missing_cols엔 평점이 없는 컬럼 인덱스가 담김. ④에서 해당 컬럼에 NaN을 삽입. ⑤에서 컬럼 sorting.



<그림 9. users-products 행렬 생성>

 생성된 users-products 행렬을 아래 그림10과 같이 seaborn 라이브러리의 heatmap을 사용하여 표시해본다. ①에서 df_users.isnull() 로 null 값이 있는 곳이 true로 표시되고, vmin 과 vmax 는 colormap 의 단계를 0에서 1의 두 단계로, cbar=False 로 옆에 나타나는 color bar 는 숨기도록 했다. 무척 희박한(sparse)행렬이다.

                           

<그림 10. Users-Products 행렬 가시화>

지금까지 추천시스템 처리 절차 중에 데이터 로드 및 전 처리, products-features 행렬과 users-products 행렬을 생성하고 users-products 행렬의 feature 값들의 범위를 0.5~1.0 사이로 정규화 하였다. Users-products 행렬을 생성하고 가시화하는 것까지 살펴보았다. 2편에서는 전 처리, 협업필터 모델 설계, 심층망 기반 모델 설계 그리고 상품 및 사용자 특성을 입력으로 하여 모델을 훈련 및 테스트하고 평가하는 부분을 살펴보겠다.