페이지

2022년 9월 28일 수요일

트랜스포머는 어떻게 동작할까? – 2편

 1편에 이어서 2편에서는 Linear layer, Query, Key, Value 개념 그리고 multi-head self-attention 동작에 대해 살펴보고자 한다.

Linear Layer

 아래 그림 10의 ① 의 단일 위치를 인식하는 word embedding 이 Linear 층에 입력되기위해서는 transpose(전치행렬)하여 ② 와 같은 형식으로 입력된다. 5차원의 벡터 입력이 ③의 3개 노드와 연결된 완전연결층을 통과한다.  5개의 임베딩 벡터 입력을 3개 노드로 통과시켜 ④의 3개의 임베딩 벡터로 축소시켜 연산 비용을 절감하는 효과를 갖는다. ③의  3개 노드는 5개 입력 벡터와 각각의 weight 값들을 사용하여 연결 되어있다. 이  weight 들은 scaler 숫자들로 ⑤는 이 값들을 행렬로 표시한 것이다. 이 값들은 모델이 back propagation 을 통하여 최적화하면서 결정된다. 이 weight 값들은 모델에 ⑤와 같은 행렬로 주입된다.


<그림 10. Linear Layer>

  지금까지 Linear 층이 하는 일을 살펴보았다. 그런데 이전 1편의 그림 9의 ①을 보면 Linear layer 가 3개가 있다. 왜 일까?  각 Linear 층은 특별한 기능을 담당한다. 이 3개의 Linear 를 각각 Query, Key, Value Linear 층이라고 부른다.

Query, Key, Value 

 query, key, value 이 3가지 기능들은 전통적 자연어처리의 순차적 네트워크와 다르게transformers 가 attention 만을 가지고 자연어처리를 어떻게 하는가를 이해하는, 즉 self-attention 에 대한 중요한 부분이어서 비유적으로 좀 더 자세히 살펴보도록 하겠다. 
먼저 아래 그림 11과 같이 유튜브를 통해 기아 신차 ‘EV7’ 을 검색한다고 가정해보자. 검색창에 EV7 이라고 입력하는 것이 ①Query(Q) 라고 한다면, 이 질의에 해당하는 리스트로 나타난 붉은색 사각형이 Key인데 이 중 첫번째가 가장 유사한 것이니까 ②Key(K1), 이 Key1 에 해당하는 value 즉 동영상 콘텐츠는 ③Value(V1) 이라고 할 수 있다.


<그림 11. Query, Key, Value>

코사인 유사도(Cosine Similarity)

  위의 Query, 와 Key 는 질의 문장(‘EV7’)의 임베딩 벡터와 유사한 벡터를 유튜브 내부문장에서 검색하는데 이때 벡터 간의 유사도를 측정하는 것이 코사인 유사도이다. 아래 그림 12의 ①이 코사인 유사도 식이다. 코사인법칙은 고등학교 2학년 수학시간에 배운다. 이를 파이선 함수로 표현한 부분이 ④로 분자는 A, B 간의 벡터 곱이고 ⑤의 분모는 np.linalg.norm 즉 행렬 norm 들의 곱이다. 이것을 위의 그림 11의 Q(질의) 와 K(키) 간의 유사도 측정이라고 생각해본다면, 분자는 ②와 같이 Q 와 K 의 행렬 곱이 되고, 분모는 ③과 같이 크기의 정도로 대치될 수 있다. 이 식이 이후 설명할 3개의 Linear 와 self-attention 관계에 사용되니 잘 기억해두자.


<그림 12. 코사인 유사도>

self-attention

자 그럼 위에 살펴본 코사인 유사도는 attention 과 어떠한 연관이 있을까? 아래 그림 을 보면 Query, Key, Value 가 모두 다른 내용인데 왜 같은 Linear 층을 3개나 가지고 있을까?  
 아래 그림 13의 ①에 when you play the game of throne 이라는 문장이 positional embedding 벡터의 형태로 ②의 3개의 Linear 층에 각각 입력된다.


<그림 13. Linear 층>

이때 그림 14와 같이 transpose(행렬전치)된 ①의 위치 임베딩 벡터가 앞의 그림 10의



< 그림 14. Query, Key, Value 행렬 생성>

Linear 층의 ② 5 X 3 행렬과 행렬 곱의 연산이 일어나고 그 결과 ③과 같이 각각 7 X 3의 Query, Key, Value 행렬이 만들어진다. 

Query 와 Key Linear 행렬의 MatMul

이제 아래 그림 15의 ①의 Multi-head self-attention 층 내부의 MatMul(Matrix Multiplication)층을 통과하는 단계로 왔다. 이전 그림 11의 유튜브 비디오 검색 예에서 Query 와 Key 가 ‘EV7’ 과 유사한 비디오 컨텐츠를 검색하는 것에 착안하면서, 우선 Query 와 Key 에만 집중해보자.
Query 와 Key 행렬이 행렬 곱(MatMul)이 실행되기위해  ②의 7 X 3 Query 행렬이 ③의 Key 행렬의 Transpose 와 행렬 곱을 수행한다. 그 결과, ④의 7 X 7 크기의 Attention Filter 를 얻는다. 즉 아래 그림 왼쪽의 ①번 즉, Query 와 Key 의 MatMul  이 수행된 것이다.


<그림 15. Attention Filter 생성>

Scaled Attention Filter

  위 그림 15 ①의 MatMul 수행 결과로 탄생한 Attention Filter 는 아주 중요한 출력으로 좀 더 자세히 살펴보자. 아래 그림 16의 ①은 Q와 K 행렬 곱의 출력 attention filter 다. 이 7 X 7 행렬의 값들은 초기에는 임의의 수로 채워지지만, 이전 그림 10의 ⑤와 같은 Linear 층의 weight 값들이 훈련을 통해 변경되어 문맥을 이해하는데 최적화되도록 훈련이 끝나고 나면 아래 그림 16과 같이 단어 간의 attention score 를 표시하게 된다.  ②에서 가장 attention 점수가 높을 관계는 당연하게도 자기 자신들끼리 의 관계로 game 열과 game 행이 98로 가장 높다. 다음으론 ③의 play 열과 game 행인 90 이다. game 열과 play 행도 마찬가지로 90 으로 이 두 단어의 유사도는 가장 높다고 할 수 있다. 이렇게 단어들 간의 유사도를 유추하는  Filter 다.
 이제 ④의 Scale 층의 연산을 통과하게 된다. 


<그림 16. Scale 로 나눈 Attention Filter>

Q 와 K 간 유사도를 측정하는 것은, 결국 아래 그림 17과 같은 연산을 수행한 것이다. ③의 scaling 을 Key 행렬 차원의 크기의 제곱근 즉 우리의 경우는 attention filter 가 7 X 7 행렬 차원이므로,  으로 나눈 값이 된다. 


< 그림 17. Q 와 K 간 유사도 함수>

  논문에서는 아래 그림 18의 ①과 같은 layer 들의 구조를 Scaled Dot-Product Attention 이라는 이름으로 설명하고 있다. 즉, 이 부분을 식으로 표현한다면 ②와 같은 Q, K, V 행렬을 통한 attention 함수로써, 빨간색 박스 안의 수식으로 표시할 수 있다. 이 중에 ③의 파란색 네모안에 부분이 지금 Scale layer 를 통과한 시점까지의 연산을 표시한다. 코사인 유사도함수의 분모인 scaling 부분을  로 나누고 있는데 이 부분을 논문은 이렇게 설명하고 있다. 만약 dk 즉 Key 의 차원이 커지면 분자 즉, Q * K T 의 크기가 커지고, 여기에 softmax 함수가 적용되면 각 차원의 값이 매우 작은 경사도(gradient) 영역으로 이루어진 값을 갖게 한다. 이를 방지하기위해  로 나눈다.


<그림 18. Scaled Dot-Product Attention 구조>

결국 위 그림 18의 ③의 파란색 박스는 앞의 유튜브 검색에서 보았던 Query 와 Key 행렬 간에 유사도를 산출하는 식이다. 여기에 softmax layer 를 통과하면 아래 그림 19와 같이 0과 1사이의 값으로 변환된 Attention Filter 가 출력된다.


<그림 19. Softmax 함수 적용 후의 attention filter>

  아래 그림 20을 보면, Scaled Dot-Product Attention 의 마지막 단계로 ①과 같이 Attention Filter 와 처음 그대로의 Original Value 행렬의 MatMul(행렬 곱)이 수행되어 최종적으로 Filtered Value 를 출력한다. 이는 위 그림 18의 ②의 Q, K, V 행렬 간의 attention 산출의 최종 출력이다. 다시 앞의 유튜브 검색에서의 Q, K, V 를 예로 들어 설명하면, Key1에 해당하는 동영상 컨텐츠Value1, 즉 문장 안에 주목해야할 단어만을 걸러낼 필터 값을 가진 것이다. 여기 까지가 Scaled Dot-Product Attention 이고 아직 하나가 더 있다.


<그림 20. 최종 filtered value 산출>

Multi-Head Attention

 아래 그림 21의 ①과 같이 Scaled Dot-Product Attention 을 여러 개 덧붙임으로써(multi-head) 각각 다른 위치의 부분공간들의 묘사(representation)들을 연합하여 정보를 취득하게 하는 것이 단일 Scaled Dot-Product Attention 배치보다 유효하였다고 설명하고 있다. 이렇게 아래 ①을 여러 개 Concatenate(결합)하여 Linear 층을 통과시켜 Multi-Head Self-Attention 이 완성된다.


<그림 21. Multi-Head Attention>

결언

1편에 이어 Transformers 를 이해하는데 중요한 Linear layer, Query, Key, Value 개념 그리고 multi-head self-attention 동작에 대해 살펴보았다. 

트랜스포머는 어떻게 동작할까? – 1편


 이번 호에서는  지난 10 년간 머신러닝 분야에서의 가장 큰 진보 중 하나로 꼽을 수 있는, 그 중에서도 특히 자연어처리 분야로부터 시작되어 비전을 포함하여 다른 분야에까지 커다란 파장을 미친  트랜스포머(Transformers)에 대해 다룰까 한다.  2017년 “Attention is all you need”라는 논문이 구글 브레인에서 발표되면서 소개된 Transformers 모델은 다수의 자연어처리 성능 최고 기록을 갈아치우며 당대의 SOTA(State of the Art)로 당당히 이름을 올렸다. 전통적으로 순차적 문제들을 해결해왔던 RNN, GRU, LSTM 등을 훨씬 뛰어넘어 독자들도 한번쯤 들어봤을BERT, GPT-3 같은 거대 언어모델의 근간이 된 기술. 트랜스포머는 현재 머신 러닝 분야에서 맹위를 떨치고 있는 기술임에는 틀림없다. 트랜스포머는 어떻게 동작할까?


전통적 sequence 모델의 한계

  그간 자연어처리에서 사용된 순차적모델 중 하나인 Recurrent Neural Network(RNN) 기반 인코더와 디코더로 구성된 기계번역의 예를 보자. 아래 그림 1의 ①과 같이 RNN 특성 상, Encorder 의 이전 시퀀스들을 context-vector 의 한 개 벡터에 담으려다 보니 이전 시퀀스들이 늘어나면, 입력 시퀀스의 정보가 일부 소실된다. 따라서 Encoder 의 시퀀스가 늘어날수록 여러 시간 스텝이 떨어진 gradient 가 전달되지 못하는 경사소멸(gradient vanishing)을 겪게 되어 멀리 떨어져 있는 이전 단어들을 참조할 수 없게 된다.

<그림 1. RNN 의 한계>

Self Attention

 트랜스포머를 이해하려면 논문 제목처럼 우선 attention mechanism 을 이해해야 한다. attention 은 인간 두뇌가 문장을 이해하는 방식을 차용한 것이다. 아래 그림 2와 같은 문장이 있다고 하자. 인간의 방식은 주목(attention)해야 할 첫번째  bank(붉은색) 를 account 와 함께 주목하고, 두번째 bank(파란색)는 옆의 river 와 함께 주목하여 문장을 이해한다. RNN 처럼 모든 문장을 처음부터 하나하나 분석하지 않는다. 이것이 어텐션 매카니즘이라고 할 수 있다. 

<그림 2. 문장 이해에서의 어텐션 메카니즘>

  특히 위 그림 2와 같이, transformers 가 문장내의 하나의 단어를 다른 단어들 간에 관계를 가지고 attention 점수를 산출하여 주목 할 단어를 이해하는 방법을 self-attention 이라고 한다.  아래 그림 3과 같이 “the train left the station on time” 이라는 입력 시퀀스가 있다고 할 때, ① 과 같이 토큰 벡터로 나누고 ②임베딩과 벡터간 연산을 통해 어텐션 점수가 있는 행렬을 얻게 된다. ‘station’ 경우에는 ③ 과 같은 벡터를 가지고 ④ 와 같이 토큰 벡터와 연산을 통해 가중치가 부여된 토큰 벡터가 되고, 이 벡터들이 모두 더해져서 ⑤ context-aware vector 가 된다. 이 점이 RNN 의 제한된 context vector 와 의 큰 차이이며 이 개념은 트랜스포머를 이해하는데 주요 요소가 된다. 지금 이해가 되지 않더라도 multi-head self-attention부분에서 내부 동작에 대해 보다 자세히 다룰 예정이다.




<그림 3. Self-attention 개념>

Transformers 모델 구조

 논문에서 선보인 Transformers 모델 구조는 아래 그림 4와 같다. 구조는 attention 에 기반한 인코더-디코더 형태의 구조다. 이 모델의 핵심은, multi-head self-attention을 이용해 RNN 과 같은 순차적 연산을 줄여 더 많은 부분을 병렬처리가 가능하게 만들면서 동시에 더 많은 단어들 간 의존도(dependency)를 모델링 한다는 것이다. 즉 문장이 RNN 과 달리 한꺼번에 입력되고 문장내의 단어들 간의 관계에서 attention  이 결정된다. 핵심인 multi-head self-attention 을 좀 더 자세히 살펴보기 위하여 왼쪽의 Encoder 부분에 집중해서 주로 ① inputs, ② input embedding, ③positional embedding, ④multi-head self-attention 에 초점을 두고 살펴보자.


<그림 4. Transformers 모델 구조>

Inputs

 위 모델과 같은 인코더-디코더 모델은 transformers 를 사용한GPT-3 와 같이 사람이 질문을 Encoder 에 입력하면, Decoder 가 응답하는 문장을 만들어내는 경우와 같이 사용된다고 생각하자. 이 경우 Encoder 는 입력되는 문장의 문맥을 이해하는 역할을 하며 Decoder 는 이러한 훈련을 통해 이해한 문맥의 법칙을 기반으로 문장을 생성한다고 각각의 역할을 생각하면 될 것 같다. 이전 뉴스레터 16호 에 Bert(Bidirectional Encoder Representation from Transformers) 언어모델에 대해 다루었는데, Bert 는 구글이 2020년 공개한 AI 언어모델로 Transformers 의 Encoder 부분만을 사용하여 한글 질의응답, 요약, 감성분석등의 여러 NLP 작업에서 이전 성능을 압도적으로 능가한 모델이다. 학습 방법이 첫째, 두개의 문장을 문장 순서대로 학습하여 두번째 이어서 오는 문장이 순서에 맞는 문장인지를 학습하고, 둘째 양방향으로 학습하여 가려진(mask 된)단어를 맞추는 방식으로 학습한다. 
 자 그럼 아래 그림 5와 같이 ①과 같은 문장이 입력된다고 가정해보자. ②의 vocabulary 는 어휘집으로 자주 등장하는 단어는 메모리에 가져다 놓고 사용하게 된다. Vocabulary indices 란 결국 ① 과 같은 문장을 전체 단어가 저장된 인덱스에 해당하는 코드로 변환한 것이다. 이것이 inputs으로 transformers 에 입력된다.


<그림 5. inputs 의 코드 변환>

Input Embedding

 그 다음 이전 그림 4의 ②에 표시된 Input Embedding layer를 통과하게 된다. 아래 그림 6의 ①과 같이 해당 단어의 vocabulary index에 해당하는 벡터가 첨가된다. 초기에는 임의의 숫자로 초기화되나 훈련이 진행될 수록 특성을 구별할 수 있는 값으로 변화한다.  아래 그림 ②에서는 임베딩 크기를 5로 예시를 들었으나 논문에서는 512를 사용했다. 

<그림 6. input embedding>

 play 라는 단어에 해당하는 임베딩은 무엇일까? 그림 6의 ①과 같이 5개 차원을 가진 벡터 묘사(representation)다. 훈련이 진행되면서 각 차원은 언어적 특성을 획득하게 되는데 각 차원이 내포하는 특성이 play 라는 단어의 특성을 묘사한다. 아래 그림 7과같이 play, game, orange 라는 3개의 임베딩 벡터를 5차원상의 공간에 위치시켜본다면 훈련이 진행될 수록 play와 game 은 언어학적 특성이 비슷해서 점차 가까운 거리에 모이고 orange는 관련성이 적어서 멀리 떨어지게 된다. 일반적으로 두 벡터 간의 유사도는 코사인 각도로 산출한다. 두 벡터의 방향이 완전히 동일한 경우는 1 이며 반대의 방향을 가지면 -1의 값을 갖는다. 즉, 값이 1에 가까울수록 유사도가 높다고 판단할 수 있다.


<그림 7.  5차원 공간 상의 임베딩 벡터>

Positional Embedding

 임베딩 벡터들은 이제 그림 4의 ③ 의 positional embedding 층을 거친다. 아래 그림 8의 ①은 임베딩 벡터 에 추가될 positional embedding 이다. 이전 그림 6의 input embedding 을 각각 e0, e1, e2, e3, e4, e5, e6, e7 이라고 하면 이전의 RNN 과 같은 순차적 구조에서는 하나씩 순서대로 입력되어 각각의 위치가 명확하였다. 반면 트랜스포머에서는 이 7개의 e0 ~ e7 임베딩이 한꺼번에 병렬로 입력된다. 이것이 장점이기도 하지만 각 임베딩의 위치가 파악되지 않는다. 이를 위하여 논문 에서는 아래 그림 8과 같은 방식의 positional embedding 을 부과하는 방법을 사용한다. 아래 그림 ①이 position=0 벡터라고 할 때, 위치가 홀수 일 경우에는 ②의 sine wave 를, 짝수 일 경우에는 ⑤ 의 cosine wave 값을 사용한다. ③ 의 하늘색 wave 는 i=4 일 경우의 sine wave 이다. 이 wave 를 이용하여 p0 부터 p5 를 순서대로 구별할 수 있지만 문제는 p0와 p6 가 같은 값을 가진다. 이때 다행히도 i=0 일 경우에 ④ 와 같이 p0와 p6 가 다른 값을 갖는다. 이렇게 wave 를 사용하는 이유는 임베딩의 갯수가 늘어나도 일정하게 위치를 보장하는 방법으로 논문에서 사용하고 있다.


<그림 8. Positional Embedding>

Multi-head self-attention

 이제 그림 4의 ④ multi-head self-attention 층에 대해 설명할 차례다.  그 전에 먼저 일반적인 attention 과 self-attention 의 차이를 짚고 넘어가자. 처음 소개된 attention 개념은 외부 질의에 따라 중요한 단어는 질의에 따른 답의 관계에서 선택적으로 집중할 단어가 결정되는 반면, self-attention 은 이와는 다르게 같은 문장 안에서 단어와 단어들 사이의 관계에서 집중할 단어가 결정되는 점이 다르다고 볼 수 있다. 우선 먼저 그림 4의 ④ multi-head self-attention 층을 확대해 보면 아래 그림 9와 같다. 
 첫번째 층은 아래 그림 9의 ①과 같이3개의 Linear 층으로 구성되었다. 이 Linear 층의 내부는 활성화(activation)함수가 없는 다수의 완전연결(fully connected, FC)층으로 이루어져 있다.  앞에서 살펴본 positional embedding 이 통과할Linear 층의 역할은 2가지이다. 첫째, 입력을 출력과 매핑하고, 둘째, 행렬/벡터의 차원을 변화하는 역할이다.


<그림 9. Multi-head self_attention 층의 구조>

 지금까지 전통적 sequence 모델의 한계, self-attention, Transformers 모델 구조 중 inputs, input embedding, positional embedding 을 살펴보고 multi-head self_attention 층 중에 Linear 층에 대해 다루어 보았다. 
2편에서는 Linear Layer 에 대하여 좀 더 살펴보고 Query, Key, Value 개념과 self_attention 층을 거치는 연산을 통하여 attention filter를 획득하는 방법과 multi-head self-attention 층의 출력을 얻는 방법을 살펴본다

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


2022년 4월 27일 수요일

라벨 없는 영상 데이터 들로부터 딥러닝을 통해 유사 이미지 탐색해오기 – 2편

 1편에 이어서 3중 손실을 통한 훈련 시행, 이미지 검색을 위한 특징 추출, 유사 이미지 검색 모듈에 대해서 살펴보겠다


3중 손실함수(triplet loss)

실제 프로젝트에서 딥러닝을 통한 이미지 분류를 처리하고자 할 때, 라벨이 부여된 수천 건의 데이터를 준비하는 문제 혹은 분류되는 이미지 종류(class)가 너무 많은 (안면인식과 같은) 경우에 봉착하는 경우가 많다. 2015년 구글에서 발표한 안면인식 FaceNet  논문 에서 컨볼루션 네트워크에서 안면 이미지와 유사 이미지, 그리고 다른 이미지의 3중 손실로 훈련하는 방법이 발표되면서 훈련에 zero shot learning 과 같은 비지도학습 가능성이 소개되었다.


 아래 그림1과 같이 입력이 되는 이미지인 anchor 이미지 A 가 있고, 유사한 positive 이미지 P, 그리고 다른 negative 이미지 N 이 있다. 결국 목적하고자 하는 학습 목표는 그 아래 식 에 표시된 바와 같이, A P 간의 거리 d(A, P) A N 간의 거리 d(A, N) 보다 작아야 하는 것이다. 여기서 +α는 margin 이다. 결국 훈련을 통해 아래 그림의 왼쪽의 거리가 오른쪽의 거리로, 3중 손실 구조는 이미지 간의 유사도를 예측하도록 학습된다.




<그림 1. Triplet loss 함수>


Loss는 아래 그림2의 ①과 같이 Siamese 네트워크의 출력인 ap_distance an_distance 의 차이로 표시된다. ②와 같이 margin 을 두고 0.0 중에 최대치를 취하게 함으로써 음수 값을 갖지 않도록 한다.



<그림 2. 손실 산출>


3중 손실을 통한 훈련 시행

 3중 손실을 통한 훈련 시행은 아래 그림 3과 같이 옵티마이저는 Adam을 채용하고 learning_rate0.0001, 그리고 epoch40으로 하였을 때, validated loss 가 ①과 같이0.0374로 나타났다.



<그림 3. 훈련 시행>


이미지 검색을 위한 특징 추출

컨볼루션 심층망을 통과하여 feature vector 가 생성된 layer를 임베딩(embedding)이라고 칭한다면 비교하고자 하는 모든 이미지들이 이러한 같은(동일한 파라미터 조건하의) 컨볼루션 망을 통과하면 결국 같은 길이의 임베딩으로 이루어질 것이다. 만약 이러한 임베딩들을 고차원의 벡터 공간에 위치시킨다면 같은 구조를 가진 임베딩끼리는 같은 위치에 모일 것이고 다른 구조를 가진 임베딩끼리는 멀리 떨어지게 된다. 우리는 자연어처리에서 word2vec 묘사들을 PCA를 통해 2차원에 투영했을 때 이러한 묘사를 볼 수 있다.

 이전  1편 그림 7에서 정의한 embedding layer를 참고로 다시 보자.  아래 그림 4의 ①과 같이 layer.trainable 상태를 출력해본다. ② 이전 그림에서 설정한데로 154번째 layer 부터 trainable True 로 변경 되어있음을 확인할 수 있다.



<그림 4. layer.trainable 확인>


아래 그림 5와 같이 훈련된 siamese 모델로부터 특징 추출 모델을 생성한다. 3중 손실을 통한 훈련을 통해 생성된 Siamese 모델의 입력으로 embedding inputoutput embeddig,out 즉 그림 4에서 살펴본 256차원의 output 이다.


<그림 5. feature_model 생성>


Feature vector 추출

 아래 그림 6의 ①과 같이 생성된 모델의 model.predict 150 X 150 크기의 입력 값을 통하여 feature_vector 를 얻고 ②로 그 값을 return 한다.



<그림 6. Feature vector 추출>


   feature vector 추출 함수를 이용하여 비교할 대상인 전체 anchor 이미지들을 대상으로 한 데이터프레임 df 를 작성한다. 데이터프레임은 ‘file’‘features’를 컬럼으로 하며 각 row df[‘features’]feature vector가 담기도록 만든다.


유사도 함수

feature vector 를 가지고 유사도를 측정할 경우, cosine_similarity를 사용하며 공식은 아래 그림 7의 ①과 같다. 즉 두 feature vector (그 중 하나는 transpose)의 벡터 곱을 두 feature vector 정규화 값의 곱으로 나눈 것이다.



<그림 7. 코사인 유사도>


함수로 정의된 코드는 아래 그림 8과 같다.



<그림 8. 유사도 함수>


유사 이미지 검색 모듈

유사 이미지 검색 모듈은 아래 그림 9에서 찾고자 하는 입력 이미지 파일인 file, 유사한 이미지를 전체 anchor 이미지들의 feature vector 가 담긴 데이터프레임 feature_df, 그리고 feature_model model_name 을 매개 변수로 전달하여 ①에서 feature vectorimg_features 로 추출하고, ②에서 getCosineSimilarity함수에 img_features와 비교할 numpy array row[‘features’]를 행방향 원소들로 더해서(axis=1) featu_df[‘similarity’] 컬럼에 저장한다. ③에서 ‘similarity’ 컬럼을 내림차순으로 정렬. ④에서 유사도 순서로 5개의 파일을 출력하는 plotSimilarImages를 호출한다.



<그림 9. 유사 이미지 검색 및 출력>


이때 비교할 test 이미지도 순서대로 정렬하여 test_images_path 밑에 위치시킨 test_images 파일로 준비한다.  이러한 test_imagesanchor 이미지 중에 62.jpg 파일을 선택해서 test_images_path 밑에 위치시키고, 아래 그림 10과 같이 실행시키면 이미지 유사도 순서로 5개의 이미지가 출력된다.

 아래 그림 10의 ①에서 test_images에 여러 파일이 있다면 아래 그림과 같은 6개 이미지 블록이 그 수 만큼 표시된다. ②유사이미지 검색 모듈 호출, ③은 test_image 파일, ④는 기존 anchor 이미지들에도 62.jpg 가 있기 때문에 유사도 1.000 로 동일 파일이 나타난다.



<그림 10. 유사 이미지 출력>


결언

지금까지 1편에 이어서 3중 손실을 통한 훈련 시행, 이미지 검색을 위한 특징 추출, 유사 이미지 검색 모듈에 대해서 살펴보았다. 3중 손실을 통한 훈련에서 validated loss 0.037로 낮은 손실을 보여주었고 유사도 검색의 정확도가 높은 feature vector 간의 cosine 유사도 검색으로 라벨 없는 비지도방식으로 높은 유사도 정확도를 보여주었다.