영상 주소는 https://www.youtube.com/watch?v=nBor4jfWetQ&list=PLoROMvodv4rOaMFbaqxPDoLWjDaRAdP9D&index=6 이다.
Gradient Descent
어떤 랜덤지점에서 시작해 그 지점에서의 기울기(gradient)를 구하고 해당 방향으로 조금 내려간(미끄러진) 다음, 그 도착점에서 또 같은 행동을 반복하면 우리가 원하는 가장 낮은 지점을 찾을 수 있다.
아래 그림처럼 1차원(변수 1개)에서는 매우 간단하다.

하지만 차원 수가 커지면 복잡할 것이다.
기본적인 방법은 그 지점에서의 기울기($\bigtriangledown _\theta J(\theta)$)를 구해서 매우 작은 수를 곱해 기존 값에서 빼는 것이다.
$\theta$는 여러개의 차원을 가지고 있을 것이므로 각 차원에 대해 계산을 하려면 아래의 식을 사용한다.

$\alpha$를 작게 설정하는 이유는 너무 크면 한번에 우리가 찾아야 하는 최저점을 넘어가 버릴 수도 있기 때문이다.
Stochastic Gradient Descent
이전 글의 내용에서처럼 $\theta$는 매우 큰 벡터이다. 그래서 위에서 얘기한 방법은 너무 많은 연산이 필요하다.
따라서 일부만 사용하는 방법인 stochastic gradient descent가 사용되게 되었다.
다른 말로 mini batch 또는 mini batch gradient descent라고 한다.
이 방법은 얼핏 보면 빠르지만 안좋은 거 아닌가? 싶은 생각이 드는데 신기하게도 신경망 분야에서는 이렇게 노이즈가 있는 방식이 더 효과가 좋다는 것이다.
따라서 이 방법을 사용하지 않을 이유가 없다.
이전 글에서 word vector는 임의의 벡터에서 시작된다고 했는데 따라서 랜덤한 숫자로 초기화를 해주어야한다.
negative sampling
우리가 이전 글에서 봤던 조건부확률 식에서는 분모에서 모든 단어에 대해 내적과 exp연산을 해 더하게 된다.
단어의 개수는 많기 때문에 이 식을 연산하는데도 오래 걸릴 것이다.
적어도 이 아이디어가 제시되었을 시대에는 그랬지만 요즘에는 슈퍼컴퓨터라면 할만한 계산이다.
어쩃든 당시의 사람들은 계층적 소프트맥스(hierarchical softmax)나 nagative sampling같은 방법을 고안했다.
이 중 nagative sampling에 대해 알아보자.
이전에 봤던 softmax식은 모든 단어를 사용하는 대신, logistic regression을 활용하는 것이다.
로지스틱 회귀는 어떤 자료들을 두 개 중 하나로 분류할 때 보통 사용한다.
여기서는 어떤 단어에 대해, 그 단어가 중심단어와 연관이 있는 단어인지 없는 단어인지를 분류한다.
식은 아래와 같다.

우변의 첫 번째 항 $-\mathbf{log} \sigma (u^T_o v_c)$을 보면 이는 특정한 하나의 context word에 대한 계산 결과에 시그모이드 함수를 씌운 값이라는 것을 알 수 있다. 이 값이 커지는 것을 목표로 하지만, 앞에 (-)부호를 붙여 전체 식의 최소값을 찾는 것으로 바꿔주었다.
시그모이드 함수는 아래처럼 정의되는 함수로 모든범위의 실수를 0~1사이의 값으로 바꿔준다. 이 속성 덕분에 확률로 취급될 수도 있다.

이어서 다음 항을 보면 이 항은 context word가 아닌 $k$개의 단어들에 대해 반대로 연관성을 낮추려고 하는 것이다.
context word의 내적이 클 때 전체 식이 최소화 되어야 하고,
k개의 다른 단어들의 내적이 작을 떄 전체 식이 최소화 되어야 하므로,
위의 식처럼 (-)부호를 배치해준 것이다.
이 k개의 단어는 보통 5~10개 사이인데, 이 단어들을 로또처럼 전체 단어 중 균등하게 랜덤하게 뽑는 것은 아니다.
각 단어들의 빈도를 활용한다.
하지만 빈도방식을 사용하면 'the', 'that'등의 단어들의 빈도가 너무 높기 때문에 단어의 출현 빈도에 3/4 제곱을 하는 방식을 고안했다.
3/4제곱을 하면 빈번한 단어의 빈도가 줄어드는 정도가 빈번하지 않은 단어의 빈도가 줄어드는 정도보다 더 크기 때문이다.
더 간단한 방법(co-occurrence count matrix)
어차피 문맥을 따지고 빈도를 따질거면,
그냥 이웃하는 단어의 종류별 개수만 세서 바로 벡터로 쓰면 되지 않을까? 하는 생각이 들 수 있다.
그러나 이 방법은 한 단어에 대해 전체단어와의 이웃한 횟수를 세야하므로 한 단어당 전체단어 수 만큼의 크기의 벡터를 가지게 된다.
만약 전체 단어가 40만개라면 단어마다 40만의 크기를 가지는 벡터를 가지고 있는 것이다.
따라서 전체 행렬은 40만 x 40만 이 된다.
하지만 여태까지 설명한 방식은 단어 당 수백개 정도의 차원만 가지고 있으므로 40만 x 100 정도의 크기만 있으면 된다.
그래서 이 co-occurrence count matrix 방식을 좋아하는 사람들은 행렬의 크기를 줄여야 했다.
그래서 선형대수학에서 나오는 특이값 분해(SVD, singular value decomposition)을 사용해 행렬의 차원을 축소시켰다.
그러나 이 큰 횟수 행렬에 SVD를 단순히 적용하는 것은 효과가 좋지 못했다.
왜냐하면 'the, he, has'같은 단어들이 너무 자주 등장하기 때문이다.
해결방법으로
로그를 취해 너무 큰 값의 영향을 줄이고,
너무 자주 등장하는 단어 쌍의 카운트 횟수를 제한하고,
중심단어와 가까울 수록 가중치를 더 크게 설정하는
기능어를 무시하는 방법들이 있다.
어쩄든 이 SVD기반의 차원 축소 방식에서 흥미로운 결과가 나왔는데,
바로 단어 사이의 의미 관계가 방향으로 표현된다는 것이었다.
아래 예시에서 어떤 동사들에 대해 특정 방향으로 이동하면 그 동사를 수행하는 사람이 나오는 것을 볼 수 있다.

이 때는 이 점이 크게 유명해지지 않았지만, word2vec이 나오면서 '단어를 벡터로 표현하면 의미관계를 알 수 있다'는 점이 주목받았다.
그래서 'word2vec처럼 신경망으로 어려운 학습을 시키지 않고 위의 횟수 행렬만으로도 비슷한 결과를 낼 수 있지 않을까?'라는 아이디어에서 발전된 알고리즘이 GloVe이다.
GloVe
조금 더 자세히 얘기하면
- Word2Vec은 문맥 단어를 예측하면서 의미를 학습함.
- 우리는 그냥 단어들의 동시 등장 확률(co-occurrence probabilities) 만으로도 그 의미를 잡을 수 있지 않을까?
- 그리고 이 확률의 비율(ratio) 이 실제로 단어 의미의 “선형 구성 요소(linear semantic component)”를 반영할 수 있다면?
이런 아이디어라고 할 수 있다.
수학적으로는 “단어 간의 의미 관계를 동시 등장 확률의 로그(log)로 표현하면,
그 차이가 벡터 공간에서 선형적인 관계(=더하기 빼기 가능한 의미 차이)로 나타난다.” 이다.

위 표를 보면 위의 두 줄(P(x|ice), P(x|steam))의 값만 보면 어떤 의미관계도 없지만,
이 둘의 비율을 살펴보면 의미관계가 생긴다.
예를 들어 8.9의 의미는 ice주변에서 x가 등장할 확률이 steam주변에서 x가 등장할 확률보다 9배 가량 높다는 것을 의미한다.
비율(=나누기)를 로그 취하면 빼기가 된다.
따라서 ice의 단어벡터와 steam의 단어벡터를 뺀 벡터는 고체-기체의 의미 관계를 나타내게 된다.
성능

이 표에서 성능은 숫자가 높을 수록 잘 작동하는 것이다.
SVD는 정말 기본 SVD만 적용한 모델이다. 위에서 얘기한 개선점 4가지를 적용하지 않았다.
이 친구는 성능이 영 별로임을 볼 수 있다.
하지만 로그를 취하는 등 위에서 얘기한 개선점을 적용하면 점수는 크게 오른다.
CBOW와 SG(skip gram)는 우리가 알아본 word2vec의 알고리즘이다.
또 방금 알아본 GloVe도 있다.
보면 GloVe는 복잡한 신경망 학습이 없어도 점수가 좋다는 것을 알 수 있다.
named entity recognition라는 평가 방식도 있다.
사람이름, 장소이름 등을 얼마나 잘 구분하는지 평가하는 방식이다.
discrete라는 평가 시스템은 대문자로 시작하거나 Mr.등의 단어가 있는지 등만 가지고 고전적으로 평가하는 기본적인 방법이다.
이 시스템에 glove의 단어 벡터들을 더했더니 효과가 좋아진 것을 볼 수 있다.

여러 의미를 가진 단어의 표현
예를 들어 단어 bank는 은행, 강둑 처럼 서로 다른 뜻을 가지고 있는데,
의미가 비슷한 것들끼리 cluster를 만든다.
예를 들면 cluster1은 강둑 관련 문맥들
cluster2는 은행 관련 문맥들이다.
cluster1의 문맥에서 나온 bank는 bank1이라는 단어를 학습시키고
cluster2의 문맥에서 나온 bank는 bank2라는 단어를 학습시킨다.
이 방식으로 학습시킨 모델을 시각화한 것이 아래의 사진이다.
jaguar, bank등 다의어가 같은 색으로 표현되어있다.

요즘은 이렇게 하지 않고 여러 벡터를 가중평균 내 사용한다.
결과로 나온 하나의 벡터를 보고 여러 벡터들로 역으로 쪼개는 것은 불가능하다고 생각할 수 있지만, 고급 수학을 사용하면 된다고 한다.
Deep Learning Classification
위에서 봤던 named entity recognition을 다시 생각해보자.
줄여서 NER은 단어가 사람인지, 장소인지, 시간인지 등을 분류하는 것이다.
이 글에서는 예시로 장소인지 아닌지만 분류하는 분류기를 만들어보자.
Paris라는 단어는 보통 장소이지만 누군가의 이름일 수도 있다.
이것은 문맥에 따라 달라지므로 주변의 단어들을 사용한다.

따라서 문맥을 사용하기위해 위의 벡터처럼 주변의 벡터를 연결해 만든 긴 벡터를 생각해보자.
단어의 차원이 100차원이면 이 경우에는 500차원이 된다.
지도학습을 사용해 분류기를 학습시킬 것이므로 training dataset이 필요하다.
이 경우는 1가지(장소인지아닌지)만 판단하므로 label은 [1,0] 같이 단순하게 설정가능하다.
첫 원소는 장소일 때, 두번째 원소는 장소가 아닐 때, 1이 된다.
여기서 사용할 neural network classifier와 기존의 전통적인 classifier의 차이를 잠깐 비교해보자.
전통적인 머신러닝 분류기는 가중치만 학습시키면서 입력은 학습을 하지 않는다. 최종적인 분류 경계는 직선이 된다.
neural network classifier는 입력도 학습이 가능하다.
$x = L e$
L이라는 행렬(단어 수 x 단어당 차원)에 one hot 방식의 벡터를 곱해 L에서 특정 줄을 뽑아 입력단어 벡터로 사용한다.
이 L의 값이 변경되며 학습하기 때문에 입력도 변하게(=학습) 된다.


아래 그림에서 위의 L행렬을 통해 변환된 단어벡터들의 연결 행렬 x를 얻는다.
가중치 행렬 W를 8x500차원이라고 가정하면 Wx의 크기는 8차원이 되고 이를 non-linear한 함수에 통과시켜 h를 얻는다.
그 후 s라는 1개의 숫자를 얻는다.
이를 시그모이드에 넣으면 분류 결과가 확률로 나오게 된다.

이 신경망 분류기가 비선형 분류가 가능한 이유는 중간의 f함수가 비선형함수이기 때문이다.
위의 그림처럼 비선형 분류가 된다면 더 자세한 분류가 가능해진다.