Loading [MathJax]/jax/output/CommonHTML/jax.js

 

 

오늘은 lecture 2에 이은, lecture 3에 대해서 정리해보겠습니다.

 

 

이전 시간에 다룬 내용을 정리하는 내용들은 생략하고, 새로운 내용들부터 다뤄보겠습니다.

 

 

 

 

 

저번 시간에는 Linear Classifier에 대해서 알아보았는데요, input data에 곱해지는 weight matrix인 W를 어떤 값으로 정하는지에 대해서는 다루지 않았습니다. 따라서 이번 강의에서 그 내용들을 다루게 됩니다.

 

weight matrix의 값을 어떻게 결정하는지에 대한 기준이 바로 loss function인데요. 이는 학습 데이터를 통해서 얻은 weight matrix를 이용해 각 이미지에 대해서 label을 예측하였을 때, 얼마나 못 예측하였는지 정량적으로 알려주는 역할을 합니다. 즉, loss function이 높을수록 예측을 못한 것입니다.

 

우리의 목표는 잘 예측하는 model을 얻는 것이니까... loss function이 낮은 weight matrix를 얻는 것이 목표가 되겠죠? 따라서 loss function을 최소화하는 weight를 효율적으로 찾기 위한 방법인 optimization에 대해서도 다루게 됩니다.

 

 

 

 

 

3개의 학습 예시가 있고, 3개의 class가 있다고 가정해봅시다. 

 

loss function은 현재 classifier가 얼마나 잘하는지를 알려주는 정량적인 척도가 될 것이고요.

 

보통 데이터셋은 (xi,yi)로 표현되고(i가 하나의 row로 보시면 됩니다.) i는 1부터 데이터셋의 크기인 N까지 구성됩니다.

 

이때, xi는 image data이고, yi는 integer로 표현된 label입니다. 위 슬라이드 예시의 경우는, label이 0이면 cat, label이 1이면 car, label이 2이면 frog로 정할 수 있겠네요.

 

dataset에 대한 전체 loss는 모든 데이터에 대한 loss의 합을 구한 뒤, dataset의 크기로 나누어 평균을 내서 구하게 됩니다. 여기서 Li가 바로 loss function이 됩니다.

 

 

 

 

 

 

다음으로는 loss function의 구체적인 예시를 다뤄보겠습니다.

 

여기서는 첫 번째로 Multiclass SVM loss를 loss function으로 정했습니다.

 

s는 데이터 xi가 입력으로 들어갔을 때, weight matrix인 W를 곱해서 얻게 되는 class score를 나타낸 것입니다.

 

SVM loss는 슬라이드에서 나타난 것처럼, 다음과 같이 정의할 수 있는데요.

 

수식을 해석해보자면, Li : i번째 sample에 대한 loss function 값을 의미하고요.

 

if syi >= sj+1일 때는 loss function의 값이 0입니다. 여기서 syi는 정답에 해당하는 class의 class score를 나타내는 것이고, sj는 정답이 아닌 class의 class score를 나타냅니다. 시그마의 아래 부분에 나온 걸 보시면, j yi라고 표기된 것을 볼 수 있는데요, 이는 j가 정답이 아닌 class를 나타낸다는 것을 의미합니다.

 

if syi < sj+1일 때는 loss function의 값은 sjsyi+1로 구할 수 있습니다. 

 

이러한 loss function이 어떠한 의미를 가지고 있는지 한번 생각해볼 필요가 있는데요, if syi >= sj+1일 때 loss function이 0이 되는 것을 주목해볼 필요가 있습니다. 

 

만약 정답에 해당하는 class score가 정답이 아닌 class의 class score에다가 1을 더한 값 보다 크거나 같으면 loss function이 0이 됩니다. 즉 정답인 class의 class score는 정답이 아닌 class의 class score보다 1 이상이면 잘 맞춘 것으로 생각한다는 의미입니다. 따라서 Multiclass SVM loss의 특징은 정답인 class의 class score가 정답이 아닌 class의 class score보다 어떤 특정값 이상이기만 하면 잘 맞춘 것으로 생각한다는 것입니다. 여기서 1은 margin이라고 불러주고, 이를 사용하는 사람이 다른 값으로 지정할 수 있는 hyperparameter입니다.

 

 

 

 

Multiclass SVM loss처럼, 0과 어떤 값 중에서 max를 취하는 loss를 Hinge loss라고 부른다고 합니다. Hinge가 한국어로는 경첩인데요, 아마 loss graph가 경첩과 비슷하게 생겨서 붙여진 이름이 아닐까 싶습니다.

 

Hinge Loss를 나타내는 그래프를 살펴보면, x축은 true class의 class score를 나타내고, y축은 loss function의 값을 나타냅니다. true class의 class score가 정답이 아닌 class의 class score보다 1 이상이면 loss가 0을 나타내고 있고, 1 미만이면 선형적으로 loss 값이 증가하고 있는 모습을 나타내고 있습니다. 

 

지금까지 대략 Multiclass SVM loss에 대해서 설명을 드렸는데, 이게 예시가 없으면 사실 헷갈릴 수 있는 부분이라 다음 슬라이드에서 나오게 되는 예시들을 함께 보면서 다시 한번 정리해보겠습니다.

 

 

 

 

 

 

아까 다루었던 SVM loss를 실제 예시를 통해 다시 계산해보겠습니다. 

 

왼쪽의 고양이 사진에 대해서 계산을 해본다고 가정하면, 이 사진의 실제 정답 class는 당연히 cat입니다. 따라서, 정답이 아닌 class(car, frog)의 class score와 정답인 class(cat)의 class score를 비교해서 loss function을 계산해주면 됩니다.

 

먼저 car의 경우, class score가 5.1이고, cat의 class score는 3.2입니다. loss 값의 계산은 sjsyi+1로 계산할 수 있으며 계산 값은 2.9가 됩니다. 

 

frog의 경우, cat의 class score가 3.2이고 frog의 class score가 -1.7이므로 cat의 class score가 frog의 class score와 비교했을 때 margin인 1보다 더 큰 상황입니다. 따라서 이때는 loss가 0이 됩니다.

 

 

 

 

 

같은 방식으로, 나머지 두 사진에 대해서도 loss 값을 계산해주게 되면 다음과 같이 구할 수 있습니다.

 

전체 loss는 각 sample들의 loss 값의 합을 sample의 수로 나눠주기 때문에, (2.9 + 0 + 12.9) / 3 = 5.27로 구할 수 있습니다.

 

 

 

 

 

다음 슬라이드는 퀴즈가 포함되어 있습니다. 만약 car score가 약간 바뀌었을 때 loss는 어떻게 변할지 물어보고 있네요.

 

이 문제가 약간 불명확하다고 생각하는데, 왜냐면 car score가 슬라이드에는 3개 있기 때문이죠.

 

강의자에 답변을 토대로 생각해보면, car가 정답인 경우의 class score를 바꾸는 경우를 물어보는 것 같습니다.

 

두 번째 사진의 경우, car의 class score가 4.9로, cat의 class score인 1.3, frog의 class score인 2.0보다 1 이상으로 더 크기 때문에 현재 두 번째 사진은 loss가 0인 상황입니다. 이런 경우, car의 class score를 바꾸더라도 이미 1 이상이므로 loss에는 변화가 없다는 것이 정답이 되겠습니다.

 

 

 

 

다음 질문은 loss의 min과 max가 몇인지를 물어보는 질문입니다.

 

loss의 min은 당연히 0이 되겠고, max는 +입니다. 이는 아까 보여준 hinge loss 그래프를 생각해보시면 이해가 될 것 같아요. sj+1보다 클 때는 loss가 0이고, 이보다 작을 때는 선형적으로 올라가는 그래프를 봤었습니다.

 

 

 

 

세 번째 질문은, W의 초기값이 작아서 class score가 0에 가깝다면, loss는 어떻게 계산되는지 물어보는 질문입니다.

 

SVM loss의 식을 다시 한번 살펴보도록 할게요. SVM loss는 max(0, sjsyi+1) 로 계산할 수 있었는데요.

 

정답인 class의 class score와 정답이 아닌 class의 class score가 0이라면, loss는 1로 계산됩니다.

 

따라서, Li는 class의 수보다 1만큼 작은 값이 될 것입니다. 예를 들어, 위의 예시에서는 class가 3종류 있기 때문에, loss의 값이 2가 되겠죠?(정답인 class에 대해서는 계산을 하지 않기 때문에 1을 빼줍니다.)

 

 

 

 

 

네 번째 질문입니다. 만약 loss 계산을 할 때, 정답인 class의 class score에 대해서도 계산하면 어떻게 되는지 묻고 있습니다.

 

정답인 class에 대해서 계산을 한다면, sjsyi가 같다는 것을 의미합니다. 따라서 loss가 1 올라갑니다.

 

 

 

 

다섯 번째 질문입니다. 만약 sum 대신에 mean을 사용하면 어떻게 되는지 묻고 있습니다.

 

사실상 기존 loss 값을 어떤 특정 상수로 나눠주는 것이기 때문에, loss값을 rescale 해주는 효과밖에 되지 않습니다.

 

 

 

 

 

여섯 번째 질문입니다. 만약 기존의 loss function을 제곱으로 바꾸면 어떻게 되는지 묻고 있습니다.

 

기존의 loss function과는 확실히 다른 trick이라고 볼 수 있는데요. 

 

기존의 loss function은 loss 값이 선형적으로 올라갔다면, square term을 이용하면 제곱 형태로 올라가기 때문에 훨씬 더 빠른 속도로 loss값이 올라갑니다.

 

이는 기존 loss function에 비해서 틀렸을 때 훨씬 더 안 좋게 평가하도록 만드는 것입니다.

 

예를 들어, 첫 번째 고양이 사진에 대한 예시를 보면 현재 loss 값이 2.9인데, square term을 이용하면 loss 값이 8.41로 올라갑니다.

 

하지만 세 번째 개구리 사진에 대한 예시를 보면 현재 loss 값이 12.9인데, square term을 이용하면 loss 값이 166.41로 엄청나게 증가함을 알 수 있습니다.

 

따라서 오답에 대해 더 강하게 처벌하는 효과를 가져온다고 볼 수 있습니다.

 

기존의 loss function처럼 선형적으로 증가하는 loss function을 사용할 것인지, 아니면 square term을 사용할 것인지는 설계하는 사람의 선택입니다.

 

 

 

 

 

다음으로는 Multiclass SVM Loss에 대한 예시 코드를 보여주고 있는데요.

 

네 번째 줄에서 margins [y] = 0으로 처리해주는 게 nice trick이라고 소개하고 있습니다.

 

정답인 class에 대해서 loss값을 0으로 지정해버리면 정답인 class만 제외하고 더할 필요 없이 모든 class에 대해서 더하도록 코드를 짤 수 있기 때문입니다.

 

 

 

 

 

만약 우리가 loss 값이 0이 되는 weight matrix인 W를 찾았다고 합시다. 그렇다면 W는 딱 하나만 존재하는 것일까요?

 

 

 

 

 

 

아닙니다. SVM loss에서는 정답인 class의 class score가 정답이 아닌 class의 class score + 1보다 크거나 같기만 하면 loss를 0으로 계산해주기 때문에, weight matrix에 어떠한 상수를 곱해서 class score를 상수 배 해주어도 동일하게 loss값이 0으로 계산됩니다. 따라서 loss를 0으로 만드는 weight matrix는 여러 개 존재할 수 있습니다.

 

 

 

 

 

 

우리가 이전에 본 예시에서, W2W로 바꾸면 어떻게 될까요?

 

SVM loss에서는 정답인 class의 class score가 정답이 아닌 class의 class score + margin(여기에서는 1) 보다 크거나 같은지 아닌지만 판단하기 때문에 여전히 loss 값은 0으로 유지됩니다.

 

강의를 들을 때, 강의자가 "만약 W를 2배로 올리면, correct score와 incorrect score 사이의 margin 또한 두배가 된다"라고 설명했는데, SVM loss에서의 margin과 단어가 헷갈려서 한참 고민했었습니다. 두 class score 간의 차이 또한 margin이라는 단어로 표현한 것 같은데, 아무래도 SVM loss에서 사용되는 margin과 헷갈릴 수 있기 때문에 difference이나 gap이라고 하는 단어로 설명했다면 더 명확하지 않았을까 하는 아쉬움이 남습니다. (제가 영어권 사람이 아니다 보니 margin, difference, gap 간의 의미상 차이점을 잘 몰라서 이렇게 생각하는 것일 수도 있습니다.)

 

자 그렇다면, 하나의 의문점이 듭니다. W도 loss 값이 0이고, 2W도 loss 값이 0이라면 우리는 어떤 weight를 우리 모델의 parameter로 사용해야 할까요? loss가 0이기만 하면 아무거나 선택해도 되는 것일까요?

 

이러한 고민은 우리가 training data에만 잘 작동하는 weight을 찾으려고 했기 때문에 발생합니다. 저번 시간에 얘기했듯이, machine learning의 목표는 우리가 가지고 있는 training data를 통해 데이터에 있는 정보를 parameter로 요약하고 이를 통해 새로운(본 적 없는) 데이터에 대해서도 잘 예측하는 model을 만드는 것입니다. 즉, training data에 대한 loss가 0인 것은 사실 중요한 것이 아닙니다. 우리가 목표로 하는 것은 test data에 대해서 좋은 성능을 내는 weight를 찾는 것입니다. 

 

test data에 대해서 좋은 성능을 내는 weight를 찾을 수 있도록 loss function에 부가적인 term을 추가해주는데요, 이를 Regularization이라고 합니다.

 

 

 

 

 

우리가 지금까지 얘기한 대로 loss function을 설정하게 되면, training data에 대해서 최대한 잘 분류하는 모델이 만들어지게 됩니다. 슬라이드에 나타난 것처럼, 5개의 점을 분류하는데도 굉장히 복잡한 모델이 만들어진 것을 볼 수 있습니다.

 

 

 

 

 

근데 이렇게 복잡한 모델을 가지고 있게 되면, 녹색 네모처럼 새로운 데이터가 들어왔을 때 적절하게 분류하기가 어렵습니다. 여기서는 따로 용어를 언급하진 않았지만, 이러한 현상을 overfitting(과적합)이라고 부르죠.

 

모델이 training data에 너무 과하게 학습되어 새로운 데이터에 대해서는 성능을 잘 내지 못하는 것을 의미합니다.

 

 

 

 

 

 

 

오히려 파란색의 복잡한 곡선보다는, 녹색처럼 단순한 직선이 데이터들을 더 잘 분류할 수 있습니다. 

 

따라서, model이 너무 복잡해져서 새로운 데이터에 대해 좋은 성능을 내지 못하는 현상을 방지하기 위해 기존의 loss에 Regularization term을 추가하여 model이 더 단순해지도록 만들어줍니다.

 

이는 "가설끼리 경쟁할 때, 가장 단순한 것이 미래의 발견에 대해 일반화하기 쉽다."는 주장인 Occam's Razor에서 영감을 받아 만들어지게 되었습니다.

 

 

 

 

Regularization term은 λR(W)로 구성되어 있는데요, λ는 regularization을 얼마나 줄 것인지를 나타내는 hyperparameter입니다. 즉, 이 값이 클수록 모델이 더 단순해지도록 강력하게 규제하고, 이 값이 작을수록 모델이 더 단순해지도록 규제하는 것이 적어집니다. 

 

λ값이 너무 크면, 모델이 너무 단순해져서 새로운 데이터에 대해서 좋은 성능을 내기가 어렵고, 너무 작으면 모델이 너무 복잡해져서 새로운 데이터에 대해서 내기가 어려워집니다. 따라서 적절한 값으로 지정하는 것이 매우 중요하죠.

 

다음으로는 R(W)에 대해서 얘기해보려고 합니다. 여기에는 다양한 방법들이 존재할 수 있는데요.

 

먼저 L2 regularization이 있습니다. 이는 모든 weight값들의 제곱 값을 모두 더해서 R(W)값으로 지정하게 됩니다. 때로는 미분하는 것을 감안해서 1/2를 곱해주기도 합니다.

 

다음으로는 L1 regularization이 있는데요. 이는 모든 weight값들의 절댓값을 모두 더해서 R(W)값으로 지정하게 됩니다. 

 

L1와 L2 regularization을 합해서 만들어지는 Elastic net이라는 것도 있는데요. 여기서는 β라는 hyperparameter가 추가적으로 들어가게 됩니다. 이 β는 L2 regularization과 L1 regularization 중에서 어떤 쪽을 더 많이 사용할지 결정하는 hyperparameter가 됩니다. 

 

그 외에도 Max norm regularization, dropout, batch normalization, stochastic depth와 같은 방법들이 존재합니다. 

 

지금까지 공부해오면서 본 바로는 dropout과 batch normalization을 많이 사용되는 것 같네요.

 

Regularization term에 사용되는 방법들은 여러 가지 존재하지만, 결국 기본 아이디어는 model의 복잡성에 규제를 가해서 더 단순한 모델이 될 수 있도록 만드는 것이라는 점을 기억해둬야 합니다.

 

 

 

 

 

 

다음으로는, regularization 방법에 따라서 어떤 weight가 선호되는지를 보여주고 있습니다. 

 

 

w1w2 모두 다 x와 내적을 했을 때의 값은 1로 동일합니다. (즉, class score가 같다는 의미겠죠?)

 

L1 regularization을 사용하게 되면, w1의 형태를 더 선호하게 됩니다.

 

보통 영어로는 sparse matrix라고 표현하는데, w1처럼 몇 개만 non-zero 값이고 나머지가 0으로 구성된 경우를 희소 행렬이라고 부르죠. L1 regularization은 이러한 형태를 더 선호합니다.

 

L1 regularization은 weight vector에서 모델의 복잡도를 non-zero의 개수로 측정하게 되고, 이는 0이 아닌 게 많을수록 복잡하다고 판단합니다. 따라서 w1와 같은 형태의 weight vector를 갖도록 강제하죠.

 

 

이와는 다르게, L2 regularization을 사용하게 되면, w2의 형태를 더 선호하게 됩니다.

 

L2 regularization은 weight를 분산시켜 x의 다른 값들에 대해서 영향력을 분산시키는 역할을 합니다.

 

이는 모델이 특정 x에만 치중되어 class score를 계산하는 것이 아니라, x의 여러 요소들을 반영하여 class score를 계산하게 되기 때문에 훨씬 더 강건한(robust) 모델이 되도록 만들어주는 효과를 가져옵니다.

 

 

 

저는 이 부분을 공부하면서 하나의 의문점이 들었는데요. L1과 L2 regularization의 특징을 들어보니 L2가 당연히 좋아 보이는데 L1은 왜 존재하는 것일까? 하는 점이었습니다. 강의자가 분명 이 부분을 몰라서 얘기를 안 한 것은 아니겠지만.... 설명을 해줬어야 하는 부분이라고 생각합니다.

 

 

출처: https://www.quora.com/When-would-you-chose-L1-norm-over-L2-norm

 

 

 

이와 관련되어 다른 분께서 설명해주신 내용을 참고해보자면, L1 regularization은 sparse 한 solution을 만들어내기 때문에, 우리가 input으로 둔 데이터 중에 일부만 가지고 class score를 예측하게 됩니다. 즉, 필요 없는 feature는 제외하는 효과를 가져오는 것이죠. 따라서 feature extraction 혹은 feature selection 효과를 가져오는 것입니다.

 

 

실시간으로 detection을 해야 한다거나 하는 상황처럼, computation을 줄이는 것이 요구되는 상황에서 사용된다고 보시면 될 것 같습니다. 어떻게 보면 L2 regularization과 L1 regularization은 accuracy와 computation 사이의 trade-off라고 생각할 수도 있겠죠?

 

 

 

 

다음으로는 새로운 Classifier와 새로운 loss에 대해서 알아볼 건데요.

 

Multinomial Logistic Regression이라고도 불리는 softmax classifier입니다.

 

우리가 지금까지 구했던 class score를 그대로 쓰지 않고, 새로운 방법을 이용해서 처리해줌으로써 class score에 의미를 부여하려는 시도입니다. 

 

class score를 s라고 했을 때, 먼저 각 class score에 exp를 취한 다음, 전체 합으로 나눠줍니다.

 

이를 softmax function이라고 부릅니다. 

 

원하는 class score / 전체 class score의 형태가 되기 때문에, 해당 class가 선택될 확률로 볼 수 있습니다.

 

그렇다면... 이 softmax function을 거친 후의 값이 어떻게 되어야 가장 이상적인 상황이 될까요?

 

정답 class에 해당하는 softmax 값이 1이 되어서 해당 class를 선택될 확률이 1이 되는 것이 가장 좋겠죠?

 

 

 

 

 

 

정답 클래스인 yi가 선택될 확률을 P(Y=yi|X=xi)이라고 표현한다면, 이것이 최대한 높을수록 좋다고 볼 수 있습니다.

 

따라서 이를 최대화하는 방향으로 weight를 구하면 되는데요, 단순히 이 확률을 높여주는 것보다, log를 취한 값을 최대화하는 것이 수학적으로 더 쉽다고 합니다.

 

그래서 log를 붙여서  logP(Y=yi|X=xi)로 만들어주고, 이를 최대화하는 방향으로 weight를 조정해줍니다.

 

하지만 loss function은 반대로 나쁨을 측정하는 것이기 때문에, -를 붙여서 계산해줍니다.

 

 

 

loss function 함수의 그래프를 한번 볼까요?

 

y=log(x) 는 다음과 같은 모양이 되는데요. x값이 1에 가까워질수록 loss가 0에 가까워지고, x값이 1로부터 멀어질수록 loss가 높아지는 모습을 나타냅니다.

 

따라서 이러한 loss function을 사용하게 되면, 정답 class의 class score에 softmax를 취한 값이 1에 가까워질 때 loss 값이 0에 가까워지고 정답 class의 class score에 softmax를 취한 값이 1에서 멀어질수록 loss값이 점점 커집니다. 

 

이러한 형태의 loss function은 자주 사용되니까, 어떤 모양을 가지는 그래프인지는 기억해둘 필요가 있어 보입니다.

 

 

 

 

 

위에서 우리가 했던 예시에 대해서 softmax를 적용한 예시입니다.

 

원래 우리가 구한 class score인 s에 exp를 취한 뒤, 이를 전체 합으로 나눠주어 각 class가 선택될 확률을 구하는 모습입니다.

 

 

 

 

 

다음으로는, softmax classifier에 대한 여러 가지 질문들에 대해 고민해보겠습니다.

 

첫 번째, loss Li의 최솟값과 최댓값은 어떻게 될까요?

 

먼저 최솟값을 생각해보겠습니다. 아까 y=log(x) 그래프를 기억해보시면, 현재 우리가 x 자리에 들어가는 것이 바로 위 슬라이드에서 녹색에 해당하는 probability입니다. 즉 각 class를 선택할 확률이 되죠. 확률은 0보다 크거나 같고 1보다 작거나 같기 때문에, y=log(x)에서 x 값이 0보다 크거나 같고 1보다 작거나 같다는 것입니다.

 

따라서, 해당하는 x의 범위에서 가장 작은 값은 0이 됩니다. 물론, 실제로 loss가 0이 되려면 score 값이 무한대에 가까울 정도로 매우 커야 하므로, 실제로는 0이 될 수는 없고 이론적으로만 가능하다고 합니다.

 

다음으로는 최댓값을 생각해보겠습니다. y=log(x) 그래프에서, 최댓값은 바로 x가 0일 때에 해당할 것입니다. 

 

근데 softmax를 취한 값이 0이 되려면, exp를 취한 값이 0이 된다는 의미입니다.

 

즉, e = 0이기 때문에 class score 값이 가 되어야 합니다. 이런 경우도 실제에서는 있기 힘들고, 이론적으로 가능한 값이라고 합니다.

 

 

 

 

다음으로는, s = 0에 가까우면 loss가 어떻게 되는지를 묻고 있습니다.

 

s = 0이면 여기에 exp를 취한 빨간색 네모에 해당하는 값이 전부 1이 될 것입니다. 

 

이 값들에 normalize를 취하면 각 class에 대한 probability가 1 / c가 되겠죠?

 

따라서, loss는 log(1/c) = logc가 됩니다.

 

 

 

 

지금까지 다룬 두 loss function에 대해서 정리해보겠습니다.

 

hinge loss는 정답 class의 class score가 정답이 아닌 class의 class score보다 정해진 margin만큼 크기만 하면 loss 값을 0으로 계산하는 특징이 있습니다.

 

실제 계산하는 식은 max(0,sjsyi+1)이였죠.

 

cross-entropy loss 혹은 softmax loss라고 불리는 아래의 loss function은 정답 class가 선택될 확률인 probability가 1로부터 얼마나 멀리 있냐에 따라 loss를 계산해주게 됩니다. 정답 class가 선택될 확률이 1에 가까우면 loss가 0에 가까워지고, 1로부터 멀어지면 loss가 점점 커지는 모습을 보여주게 되죠. 

 

cross-entropy loss를 사용하게 되면 정답 class가 선택될 확률이 1에 가까워지도록, 정답이 아닌 class가 선택될 확률이 0에 가까워지도록 weight를 조절하는 특징을 보입니다.

 

 

 

 

지금까지 다룬 내용들을 정리해봅니다. 

 

우리가 dataset을 가지고 있다면, 각 data point들에 대해서 score function을 통과시켜 class score를 계산하고 이를 이용해서 loss function을 계산해주게 됩니다.

 

우리가 다뤄본 loss function은 Softmax와 SVM loss가 있고요. 앞에서 언급된 Regularization을 이용한다고 하면, 전체 loss 함수는 Li+λR(W) 형태가 될 것입니다. (위 슬라이드에서는 λ가 빠져있네요.)

 

다음으로 우리가 생각해볼 것은, 어떻게 최적의 Weight를 찾을 것인가? 하는 문제입니다.

 

이것이 바로 Optimization이라고 하는 영역에 해당합니다.

 

 

 

 

 

강의자가 Optimization을 설명하기 위해서 다음과 같은 예시를 제시하였습니다.

 

우리가 큰 협곡에서 걷고 있다고 가정합니다. 모든 지점에서의 풍경은 parameter W setting에 대응되고요. 

 

즉 위 슬라이드에서 캐릭터가 서있는 저 지점 또한 어떤 특정 parameter 값을 나타낸 것으로 볼 수 있습니다.

 

협곡의 높이는 해당 parameter에 의해서 계산되는 loss와 동일하다고 생각해줍니다.

 

우리의 목표는, 이 협곡의 바닥을 찾기를 바라는 것입니다. (즉 loss가 0인 경우를 찾고 싶다는 얘기죠.)

 

사실 저 그림에서는 단순히 중간 쪽으로 이동해서 협곡의 바닥으로 가면 되는 게 아닌가?라고 생각할 수 있지만, 현실에서는 Weight와 loss function, regularizer들은 큰 차원을 가지고 있고 복잡하기 때문에, 협곡의 바닥에 해당하는 최저점에 곧바로 도달할 수 있는 명시적 해답을 바로 구할 수 없다고 합니다.

 

따라서 현실에서는 다양한 반복적 방법을 사용해서 점점 더 협곡의 낮은 지점으로 나아가는 방식을 통해 협곡의 바닥을 찾아나갑니다.

 

 

 

 

첫 번째로 우리가 생각해볼 수 있는 방법은 바로 random search입니다. 수많은 Weight 중에서 랜덤으로 하나를 샘플링하고, 이를 loss function에 집어넣어서 해당 weight가 얼마나 잘 예측하는지를 보는 것이죠.

 

이런 방식을 이용하면, 정확도가 매우 낮게 나오기 때문에 실제로는 사용할 수 없다고 합니다.

 

 

 

 

 

 

다음으로 생각해볼 수 있는 방법은, slope(기울기)를 따라서 가보는 것입니다.

 

실제로 우리가 계곡에서 걷는다고 생각해보면, 아마 계곡의 바닥으로 가는 경로를 바로 알 수는 없을 겁니다. 하지만 현재 있는 위치에서 어떤 방향으로 가야만 내리막길로 가게 되는지는 바닥의 기울기를 느껴서 찾아낼 수 있습니다. 

 

따라서, 현재 위치에서 어느 방향으로 가야만 내리막인지를 판단한 뒤, 해당 방향으로 약간 이동하고, 또 그 자리에서 다시 방향을 판단한 뒤, 해당 방향으로 약간 이동하고 하는 과정을 계속해서 거쳐서 협곡의 바닥을 찾아나갈 수 있을 것입니다.

 

 

 

 

우리가 고등학교 때 배운 수학에서는 1차원이었기 때문에, 함수의 미분을 이용해서 해당 점의 기울기를 바로 구할 수 있었습니다.

 

하지만 우리가 다루게 되는 문제들은 대부분 고차원의 데이터이기 때문에, 이러한 방식으로 기울기를 구할 수는 없습니다.

 

고차원에서는 gradient라고 하는 개념을 이용하게 되는데요, 이는 다변량 미적분에서 사용되는 개념입니다.

 

gradient는 입력값인 x와 같은 차원을 가지고 있으면서, 각 차원에 대한 편미분 값으로 구성되어 있어서 해당하는 좌표 방향으로 움직였을 때 함수의 기울기가 어떤지 알려줍니다.

 

gradient가 사용되는 이유 중 하나는, gradient의 기하학적인 의미 때문입니다. gradient는 함수가 가장 크게 상승하는 방향을 알려주기 때문에, gradient가 작아지는 방향을 구하게 되면 해당 함수가 가장 빠르게 작아지는 방향을 알 수 있게 됩니다. 

 

gradient에 대해서 궁금하시다면, 구글에서 조금 더 검색해보시면 다양한 자료들을 찾아보실 수 있을 겁니다.

 

한 번쯤은 gradient가 무엇인지에 대해서 공부해보시면 더더욱 좋을 것 같습니다.

 

 

 

 

 

 

Gradient를 실제로 계산하는 방법은 두 가지가 존재하는데, 먼저 첫 번째 방법인 Numeric Gradient를 보도록 하겠습니다.

 

현재 기울기 W가 왼쪽 첫 번째 column처럼 구성되어 있다고 한다면, 첫 번째 row에 해당하는 0.34에 매우 작은 양수인 0.0001을 더해줍니다.

 

다음으로 f(x+h)f(x)/h를 계산해서, 해당 weight 값의 dW를 계산해주게 됩니다.

 

동일한 방식을 이용해서 weight의 각 값들에 대해서 dW를 계산해볼 수 있겠죠?

 

근데 보통 weight의 차원이 매우 크다는 점을 감안하면, 이렇게 계산하는 것은 너무나 느립니다. 왜냐하면 모든 차원의 값들을 전부 계산해야 하기 때문이죠.

 

 

 

 

 

따라서, 사람들은 Loss function을 W에 대해서 미분을 하는 방법을 이용해서 Gradient를 계산했습니다.

 

미분을 이용하면, 고차원의 데이터도 한 번에 계산을 할 수 있기 때문이죠.

 

 

 

 

 

요약하자면, Gradient를 계산하는 방법은 Numerical gradient와 Analytic gradient로 총 두 가지 방법이 존재하는데 실제로는 미분을 이용하는 Analytic gradient를 사용합니다.

 

물론 numerical gradient가 필요가 없는 것은 아닙니다. 우리가 짠 코드가 잘 맞는지 확인해보기 위해서 numerical gradient를 이용해 일종의 unit test를 진행하는 데 사용됩니다. 이를 gradient check라고 부른다고 합니다.

 

 

 

 

 

 

실제로 우리가 최적의 W를 구하기 위해서는 다음과 같은 방법을 이용해줍니다.

 

먼저 W를 랜덤 값으로 초기화해줍니다.

 

그러고 나서, 해당 W값을 이용해 loss를 계산해주고, loss function을 W로 미분하여 gradient를 계산합니다.

 

gradient가 loss를 가장 빠르게 높여주는 방향을 알려주기 때문에, 이에 -를 취해서 loss를 가장 빠르게 작아지는 방향을 찾습니다. 그리고 해당 방향으로 아주 작은 step 만큼만 이동합니다.

 

이러한 과정을 계속 반복하여, loss가 가장 작은 지점에 수렴하는 방법을 이용합니다.

 

이를 Gradient Descent라고 부릅니다.

 

위 슬라이드에서 step_size라고 하는 변수가 등장하는데, 이는 hyperparameter로 loss가 가장 빠르게 작아지는 방향으로 얼마큼 이동할 것인지 결정합니다. Learning rate라고 하는 용어로도 불립니다. 

 

아마 추후 강의에서 다룰 것이겠지만, step size를 너무 작게 주면, 훨씬 많이 반복해야 loss가 가장 작아지는 지점에 도달하게 되기 때문에 연산하는데 시간이 오래 걸리게 됩니다. 하지만 step size를 너무 크게 주게 되면, 아예 loss function이 발산해버릴 수도 있습니다. 따라서 step size(learning rate)를 얼마로 줄 것인지도 학습에 있어서 중요한 요소가 됩니다.

 

 

 

 

 

그림에서 빨간 부분이 loss가 작은 곳을 나타내고, 파란 부분이 loss가 큰 부분을 나타냅니다.

 

현재 있는 하얀 동그라미의 위치가 original W이며, 여기서 negative gradient 방향으로 이동하는 모습을 그림으로 표현한 것입니다.

 

 

 

 

 

하지만 Gradient Descent 방법을 전체 데이터에 대해서 계산하기에는 연산량이 매우 부담스럽습니다.

 

보통 데이터가 작으면 수천, 수만 개에서 크면 수백만, 수천만 개가 될 수 있습니다.

 

따라서, Gradient를 계산할 때 minibatch라고 하는 일부 데이터만 이용해서 계산해주고 이를 반복해서 계산해 전체 데이터에 대한 Gradient값의 추정치를 계산하는 방법이 바로 SGD라고 볼 수 있습니다.

 

minibatch는 관례적으로 2의 제곱 값을 이용하며 32개, 64개, 128개 등을 주로 사용합니다. 

 

강의자가 얘기하기로는, 이를 stochastic 하다고 이름 붙여진 이유가 바로 해당 방법이 전체 데이터에 대한 gradient값을 Monte Carlo 방식을 이용해서 구하기 때문이라고 합니다.

 

Monte Carlo가 무엇인지에 대해서는 다양한 자료들이 있으니, 찾아보시면 좋을 것 같습니다.

 

Monte Carlo의 대략적인 아이디어는, 수많은 샘플링을 통해서 어떠한 값의 근삿값을 찾아내는 방법론인데요. 현재 SGD가 하고 있는 작업과 사실상 동일합니다. 데이터 중 일부인 minibatch를 이용해서 gradient를 계산하는 작업을 여러 번 반복하여 전체 데이터의 gradient를 근사하는 것이지요.

 

 

 

 

 

Optimization에 이어, 추가적으로 Image Feature와 관련된 내용들을 다루고 있습니다.

 

Deep learning을 사용하기 전에는 Image Feature를 어떻게 추출했는지를 설명하고 있는데요.

 

이미지의 raw pixel을 linear classifier에 넣어주게 되면 multi-modality 등의 문제에 대응할 수 없다는 한계점이 존재했기 때문에, 다음과 같은 two-stage 접근법이 요구되었습니다. 첫 번째로, 이미지의 모습과 관련된 다른 종류의 quantity를 계산하고, 두 번째로 이미지의 feature representation을 얻기 위해 첫 번째에서 구한 것들을 concat 시켰습니다.

 

마지막으로 구해진 feature representation을 linear classifier에 넣어서 이미지들을 분류하곤 했습니다.

 

그렇다면 사람들은 어떻게 이러한 Image Feature를 뽑아야 한다는 아이디어를 생각해냈을까요?

 

 

 

 

왼쪽의 그림을 보시게 되면, 빨간색 data point과 파란색 data point를 linear classifier를 이용해서는 분류할 수 없습니다.

 

하지만, feature transform을 이용해서 data들이 분포된 형태를 바꿔주게 되면, data point들을 linear classifier를 이용해서 분류할 수 있게 됩니다.

 

따라서 사람들은 이미지에 대해서 어떤 feature representation을 뽑아내게 되면, 오른쪽의 모습처럼 data point를 linear classifier를 이용해서 분류할 때 더 좋은 성능을 낼 수 있을 것이라고 생각하게 되었습니다.

 

그렇다면 deep learning이 사용되기 전에 사람들은 어떤 feature representation을 사용했을까요?

 

 

 

 

 

 

첫 번째 예시는 바로 Color Histogram입니다. 

 

색에 대한 스펙트럼을 bucket으로 나누고, 이미지에 존재하는 각 pixel에 대해서 해당 pixel이 어떤 bucket에 해당하는지를 count 합니다.

 

따라서 이를 이용하면, 이미지를 global 하게 봤을 때 어떤 컬러들로 분포되어 있는지를 확인할 수 있습니다.

 

 

 

 

두 번째 예시는 Histogram of Oriented Gradients (HoG)입니다. 

 

사람의 visual system에서 oriented edge가 중요한 역할을 하는데, HoG는 이러한 직관을 포착하려고 시도한 feature representation입니다.

 

강의자가 자세한 방법론을 설명해주진 않았지만, 대략 이미지를 여러 부분으로 localize 해서 각 부분이 어떤 종류의 edge로 구성되어 있는지를 판단해보는 방법이라고 이해해보면 좋을 것 같습니다.

 

슬라이드의 오른쪽 그림을 보면, 왼쪽 원본 그림의 edge를 잘 포착하고 있는 것을 확인할 수 있습니다.

 

자세하게 HoG를 어떻게 구하는지 궁금하시다면, 추가적으로 검색해보시는 것을 추천합니다.

 

 

 

 

 

 

세 번째 예시로는 Bag of Words를 사용하는 방법이 있습니다. 이는 Natural Language Processing에서 영감을 받은 아이디어인데요.

 

예를 들어서, 만약 문단을 표현하고 싶다면 해당 문단에 나타나는 단어들의 빈도를 count 해서 보여줄 수 있겠죠?

 

이러한 아이디어를 이미지에 적용해보는 시도입니다.

 

이미지의 각 patch들을 추출해서, 이를 visual word로 생각하고 k-means clustering 등의 방법을 이용해 군집을 형성해서 "codebook"을 만들어줍니다. 

 

codebook의 각각을 잘 보면, edge와 color를 추출하고 있는 것을 확인할 수 있습니다.

 

다음으로는, 이미지에 대해서 이러한 visual word가 얼마나 자주 나오는지를 count 하여 이미지의 feature representation을 얻을 수 있습니다.

 

 

 

 

 

Deep learning 이전에 사용했던, 5~10년 전에 사용한 Image feature와 현재 ConvNet과의 차이를 비교해보겠습니다.

 

기존에는 이미지에서 BoW나 HoG와 같은 Feature를 추출한 뒤, 이를 f로 표기된 linear classifier에 투입해 각 class에 대한 score를 얻는 방식을 이용하였습니다. Feature는 한 번 추출되면, training 하는 동안 바뀌지 않습니다. 학습이 진행되면 linear classifier만 업데이트되는 방식이었죠.

 

하지만 Deep learning의 경우는 ConvNet을 이용해서 Feature를 추출하게 되고, 학습이 진행될 때 feature를 추출하는 ConvNet도 학습되고, 추출된 feature를 이용해서 각 class의 class score를 예측하는 linear classifier도 학습되게 됩니다.

 

 

 

지금까지 cs231n의 lecture 3에 대해서 정리해보았고, 이번에도 내용을 정리해볼 수 있는 간단한 Quiz를 만들어보았습니다.

 

 

 

cs231n lecture 3 Quiz

 

 

Q1. loss function이 무엇이고, 어떤 의미를 가지고 있나요?

 

Q2. SVM loss와 softmax loss의 차이점은 무엇인가요?

 

Q3. L1 Regularization과 L2 Regularization의 식을 설명하고, 각 Regularization은 어떤 형태의 weight를 선호하는지 이야기해보세요.

 

Q4.

해당 케이스에 대해서 softmax loss를 직접 계산해보세요.

 

Q5. Gradient Descent에 대해서 설명해보고, Gradient Descent과 Stochastic Gradient Descent는 어떤 차이가 있는지 설명해보세요.

 

Q6. Deep learning을 이용해서 이미지 분류를 수행한 경우와, Deep learning을 이용하지 않고 직접 Feature를 추출해서 이미지 분류를 수행한 경우에 어떤 차이가 있는지 설명해보세요.

 

cs231n을 3월 ~ 5월 정도에 걸쳐 한번 정리를 쭉 했으나, 중간중간에 이해를 완벽하게 하지 못하고 넘긴 부분들이 많은 것 같아 학기가 시작하기 전에 다시 한번 싹 정리하는 것을 목표로 하려고 합니다.

 

 

슬라이드를 과도하게 많이 집어넣으면 글 하나당 내용이 너무 많아지는 것을 방지하기 위해서 필요 없다고 생각되는 일부 슬라이드는 제외하면서 작성하려고 합니다.

 

 

또한, lecture를 하나씩 정리할 때마다, 글의 마지막에 해당 lecture를 정리하였다면 바로 대답할 수 있어야 하는 내용들을 Quiz 형식으로 정리해서 만들어보고자 합니다. 이를 통해서 해당 강의의 내용을 내가 잘 이해하고 습득하였는지 확인해보실 수 있을 것이라고 생각합니다. 

 

 

저도 이 분야의 전문가는 아니라서, 어떤 게 가장 중요한지는 명확하지 않아 덜 중요한 내용이 Quiz로 출제될 수 있습니다.

 

 

 

오늘 다룰 내용은 두 번째 강의에 해당하는 Image Classification pipeline에 대해서 정리해보려고 합니다.

 

 

Computer Vision에서, 중요한 task인 Image Classification은 특정한 input image data가 주어졌을 때, 이미 정해져 있는 label 중에서 어떤 label에 해당하는지 구별하는 task입니다. 위에 나온 슬라이드의 경우는, 고양이 사진이 주어졌기 때문에, 고양이라는 label로 구분하는 것이 요구됩니다.

 

 

 

 

인간의 입장에서, 고양이 사진을 보고 고양이로 판단하는 것은 전혀 어렵지 않은 문제입니다. 하지만 컴퓨터가 Image를 분류하는 것은 매우 어려운 일인데, 이는 컴퓨터 입장에서 보는 것이 고양이 사진이 아니라, 거대한 size를 가진 숫자들의 matrix 이기 때문입니다. 슬라이드의 오른쪽에 보이는 것처럼, 실제로 컴퓨터는 0부터 255로 구성되어있는 matrix를 보게 됩니다.

 

이렇게, 컴퓨터가 실제로 보고 있는 pixel 값과, 고양이라고 하는 의미상 아이디어 간에는 큰 gap이 존재하게 되는데, 이를 'Semantic Gap'이라고 설명합니다. 

 

 

 

 

 

 

그렇다면, 컴퓨터가 Image classification을 하면서 어떠한 어려움을 겪게 되는지를 강의에서 설명하고 있습니다.

 

 

첫 번째 어려움은 바로 viewpoint variation입니다. 고양이가 얌전히 있다고 생각하였을 때, 만약 카메라가 움직이게 되면 image data matrix 안에 있는 숫자들은 바뀌지만 고양이라고 하는 label은 그대로 유지됩니다. 따라서 우리가 사용하게 될 알고리즘은 이러한 상황에도 강건하게 작동할 수 있어야 합니다.

 

 

 

 

 

 

 

두 번째로는 밝기와 관련된 어려움으로, 밝은 곳에서 찍었을 때와 어두운 곳에서 찍었을 때 모두 고양이로 분류할 수 있어야 합니다.

 

세 번째로는 자세와 관련된 어려움으로, 가만히 앉아있는 자세뿐 아니라, 누워있거나 다른 자세로 앉아있는 경우에도 고양이로 분류할 수 있어야 합니다.

 

이외에도, 고양이의 신체 중 일부가 가려져 고양이의 일부만 보고도 고양이로 분류할 수 있어야 한다거나, 혹은 고양이의 주변 배경이 고양이와 유사할 때도 고양이로 분류할 수 있어야 합니다.

 

그리고 Intraclass variation이라고 하는 어려움도 존재하는데, 이는 같은 class 여도 그 안에서 차이가 있다는 의미입니다. 즉 고양이들도 모두 똑같이 생긴 것이 아니라, 생김새나 모양, 크기가 다를 수 있는데 이런 경우에도 모두 고양이로 분류할 수 있어야만 합니다.

 

 

 

 

 

숫자로 구성된 list를 분류하는 것과는 다르게, 물체들을 어떻게 인식할지에 대한 명백하고, 직관적인 알고리즘이 존재하지 않습니다. 따라서 image classification은 어려운 문제라고 설명합니다.

 

 

 

 

 

 

그렇다면, 과거 사람들은 이 문제를 그냥 포기했을까요? 그렇지 않습니다. 이 문제를 어떻게든 풀기 위해 다양한 연구들이 진행되어왔습니다. 과거에 사용했던 방법 중 하나는 다음과 같은 방식으로 물체를 분류했습니다. 먼저 사진에서 edge를 찾습니다. (edge는 가장자리라는 뜻으로, 오른쪽의 사진을 보시면 대략 이해할 수 있을 것입니다.) 그러고 나서 모든 코너와 boundary들을 분류하고, 3개의 선이 만나면 코너이고, 귀에는 코너가 몇 개이고... 등등 이런 식으로 어떤 사물을 인식할 수 있는 규칙을 만든 것입니다. 아마 물체마다 가지고 있는 특징들이 있으니, 이런 방법을 통해 해결하려고 한 것 같습니다. 

 

하지만 이러한 방식은 잘 작동하지 않았습니다. 일단 매우 불안정합니다. 방금 설명했지만, 같은 고양이일지라도 포즈가 다르거나, 조명이 다르거나, 혹은 고양이의 일부만 가지고도 고양이를 구별해야 하는데, 이렇게 규칙 기반으로 하게 되면 해당 규칙에서 벗어난 경우는 고양이로 분류할 수 없을 것입니다. 그리고 우리가 구분해야 하는 모든 사물에 대해서 각각 이러한 규칙을 작업해야 한다는 문제점이 있었습니다. 구분해야 하는 사물의 종류가 많다면, 이는 매우 어려운 일이겠죠?

 

이처럼 기존 방법은 세상에 있는 모든 사물들을 분류하기에는 한계점이 명확했기 때문에, 사람들은 세상에 있는 모든 사물들을 분류할 수 있는 어떤 새로운 방법을 찾고 싶어 했습니다.

 

 

 

 

 

그렇게 해서 나오게 된 방법이 바로 Data-Driven Approach입니다. 각 사물에 대해서 규칙을 만드는 것이 아니라, 데이터를 기반으로 학습을 하는 방법을 생각해낸 것입니다.

 

먼저, 이미지와 label로 구성된 dataset을 모은 후, 이를 이용해서 사물을 분류할 수 있는 classifier를 학습시켰습니다. 그러고 나서, 새로운 이미지에 대해서 이 classifier가 사물을 분류하게 됩니다.

 

여기서 classifier가 가지고 있는 의미에 대해서 조금 더 생각해볼 필요가 있는데요, classifier는 다양한 데이터들을 받아들여서 이를 어떤 방법으로 요약하고, 여러 가지 사물들을 어떻게 인식할 것인지에 대한 지식을 요약하는 모델이라고 생각하시면 됩니다. 기존의 방법론은 사람이 직접 손수 각 사물을 어떻게 인식할 것인지에 대한 지식을 규칙을 기반으로 만들어준 것이지만, Data-driven approach에서는 데이터를 기반으로 모델이 이러한 지식을 직접 요약한다는 점에서 차이가 있다고 생각할 수 있습니다.

 

Data-Driven approach에서는 train과 predict 총 두 가지로 모델이 나눠지게 되는데요. train의 경우는 input으로 이미지와 label을 받게 되고, 이 데이터들을 기반으로 여러 가지 사물들을 어떻게 인식할 것인지에 대한 지식을 요약하는 모델이 output으로 나오게 됩니다. classifier라고 하는 용어로 위에서는 소개되었죠.

 

predict는 사물을 분류할 수 있는 model과 분류할 이미지인 test_image를 input으로 받게 되고, model을 이용하여 분류할 이미지인 test_image들의 label을 예측한 결괏값인 test_labels를 output으로 내보내게 됩니다. 즉, Data-Driven approach에서는 '학습'과 '예측'이 분리되어 있음을 나타내고 있습니다.

 

 

 

 

 

첫 번째로 소개할 classifier는 Nearest Neighbor라고 하는 model입니다. 말 그대로, 자신과 가장 가까이에 있는 이웃(데이터)과 같은 label을 가지게 만들어주는 model입니다.

 

train(학습)에서는, 단순히 모든 data와 label들을 기억합니다. 

 

predict(예측)에서는, 예측하려고 하는 이미지의 label을 training image 중에서 가장 유사하다고 생각되는 image의 label로 예측해줍니다.

 

근데 여기서 한 가지 의문이 듭니다. 그렇다면, '가장 유사하다'라는 것을 어떻게 알 수 있을까요? 그것은 곧 다루게 됩니다.

 

 

 

 

 

우리가 사용하는 예시 dataset은 바로 CIFAR10이라고 하는 dataset입니다. 10개의 class로 구성되어있고, 5만 개의 training image로 구성되어 있습니다. 각 class 별로 균등하게 분포되어있어, class 당 5천 개의 image가 있습니다. 그리고 10000개의 testing image도 포함되어 있습니다.

 

 

화면의 오른쪽에는 Test image와 nearest neighbor를 나타내고 있는데요, test image는 label을 예측해야 하는 이미지를 의미하고, nearest neighbor는 해당 이미지와 가장 가깝다고 생각되는 training image들을 나타낸 것입니다. 

 

 

 

 

 

 

 

아까 궁금했던 내용이 드디어 나오게 됩니다. 즉 두 이미지 간의 유사성은 어떻게 판단할 수 있을까요?

 

 

방금 CIFAR10의 예시에서 test image와 training image 간의 유사성은 다음과 같은 척도를 이용해 판단하게 됩니다.

 

 

L1 distance는 두 이미지 간의 차이에 절댓값을 이용해서 계산하게 됩니다. 각 pixel 별로 계산한 뒤에, 모두 합쳐서 하나의 값으로 나타내 줍니다. 즉, 슬라이드에 나온 456이라는 값이 바로 test image와 training image가 얼마나 비슷한지를 나타내는 척도가 되는 것이죠. 이 값이 작으면 작을수록 두 이미지가 비슷하다는 의미가 됩니다.

 

 

 

 

 

다음은 Nearest Neighbor classifier를 코드로 나타낸 것입니다. 먼저 학습하는 코드를 살펴보면, 별도의 연산 과정 없이 image 데이터와 label을 변수로 저장하는 코드를 나타내고 있습니다.

 

 

 

 

 

 

 

다음으로는 예측하는 코드입니다. L1 distance를 이용해서 training dataset에 있는 데이터들과 test image 각각에 대해서 거리를 측정하고, 이것이 가장 작은 것을 label로 지정해주는 코드입니다.

 

 

 

 

 

 

 

강의자료에서는 N개의 data가 있다고 할 때, 학습과 예측이 얼마나 빠른지를 물어보고 있습니다.

 

 

Training은 단순히 자료를 저장하면 되기 때문에, 메모리에 올리는 작업만 하면 되지만, 예측의 경우 N개의 image에 대해서 모두 계산해봐야 하기 때문에, 연산량은 O(N)로 계산됩니다. 이는 문제가 되고, 우리는 예측은 빠르게 하면서 학습은 느려도 되는 상황을 만들고자 합니다. 예측이 빨라야 하는 이유는, 우리가 이러한 예측을 사용하는 것은 핸드폰이나 브라우저, 저전력 device 등에서 사용되기 때문에 연산량이 많이 요구되면 활용하기가 어렵기 때문입니다. 예를 들어, 사진을 찍어서 해당 물체가 어떤 종류의 물체인지 알려주는 앱이 있는데 사진을 찍고 나서 1시간 뒤에 답을 알려준다면 앱으로써의 가치가 없겠죠? 이렇게 생각해보면 예측이 빠른 것이 얼마나 중요한지 알 수 있습니다.

 

 

 

 

 

위의 사진은, 데이터에 대해서 Nearest Neighbor classifier를 적용했을 때 나타나는 모습을 나타낸 것입니다. 빨간색, 파란색, 녹색, 노란색, 보라색은 각각 해당 영역의 label을 나타냅니다. 즉, 이 데이터는 5개의 label을 가진 데이터들로 구성되어 있습니다. 

 

 

이 그림에서 문제가 되는 점은 두 가지 있습니다. 첫 번째로, 사진의 중간쯤에 보이는 yellow island가 형성된다는 점입니다. 이 점은 label을 기준으로는 노란색으로 구별되는 것이 맞지만, 가장 가까이에 있는 점들이 녹색이라는 점에서 녹색으로 분류되었어야 더 자연스러울 것입니다. 두 번째로, 손가락처럼 삐죽삐죽 튀어나오는 부분들이 존재합니다. 그림의 왼쪽에 빨간색과 파란색이 마주하고 있는 영역들을 보면 이러한 현상이 나타나는 것을 알 수 있습니다. 또한, 중심에서 조금 위로 올라가 보면 점은 아무것도 없지만 녹색 영역이 위로 쭉 뻗어있는 영역을 확인할 수 있는데, 이 부분 또한 녹색이 파란색 영역을 침범한 것으로 문제가 될 수 있습니다. 이는 잡음 때문이거나, 결과가 잘못된 것입니다.

 

 

 

 

 

단순히 가장 가까운 이웃에 의해서 label을 결정하는 것이 아니라, 가장 가까운 K개의 데이터들로부터 majority vote를 하는 방식인 K-Nearest Neighbor classifier을 나타내는 슬라이드입니다. 이는 Nearest Neighbor classifier의 일반화된 버전이라고 볼 수 있습니다.

 

 

데이터의 label을 판단하는 데 사용되는 데이터의 개수인 K가 1에서 3, 5로 증가할수록 앞에서 언급된 Nearest Neighbor classifier의 문제점들이 조금 완화되는 모습을 확인할 수 있습니다. K=5 일 때는 손가락처럼 튀어나오는 부분이나, yellow island가 발생하지 않는 모습을 확인할 수 있습니다. 이를 통해, K의 값이 커질수록 잡음에 대해서 더 강건해지면서 decision region이 훨씬 부드러워지는 것을 볼 수 있습니다.

 

 

여기서 하얀색으로 나타난 부분은, KNN에 의해서 label을 결정할 수 없는 부분을 표현한 것입니다.

 

 

 

 

 

KNN을 사용하면서 추가적으로 생각해볼 수 있는 점은, 바로 Distance Metric을 결정하는 것입니다. 이전에는 L1 distance만 소개했으나, 자주 사용되는 distance metric에는 L2 distance도 존재합니다.

 

L2 distance는 두 거리의 차이에 제곱을 취해서 모두 더한 뒤, 다시 제곱근을 취해서 구할 수 있습니다. 보통 중학교, 고등학교 수학에서 두 점 사이의 거리를 구할 때 사용되는 공식과 동일합니다.

 

L1 distance의 경우, 선택하는 좌표계에 의존합니다. 따라서, 좌표계를 바꾸면 점과 점 사이의 L1 distance가 바뀌게 됩니다. 또한, input의 각각이 중요한 의미를 가지고 있다면 L1을 사용하는 것이 좋습니다. 예를 들어서, input data가 4차원이고, 각각 키, 몸무게, 나이, 발 사이즈를 의미한다면, L1을 사용하는 게 좋다는 것입니다.

 

L2 distance의 경우, 좌표계에 의존하지 않습니다. 따라서, 좌표계를 바꾸더라도 점과 점 사이의 L2 distance가 바뀌지 않게 됩니다. L1 distance와 반대로, input이 어떤 것을 의미하는지 모를 때는 L2를 사용하는 것이 좋습니다.

 

이처럼 distance metric을 고르는 것은 공간의 기저에 깔린 기하학에 대한 가정이 달라지기 때문에, 어떤 것을 선택하는지가 중요합니다.

 

 

 

 

위 슬라이드는, distance metric을 어떤 것으로 선택하느냐에 따라 decision region이 달라지는 것을 보여주고 있습니다.

 

L1 distance의 경우, decision region이 좌표계 형태로 나타납니다. 잘 보면 부드러운 느낌보다는, 사각형처럼 나타나는 것을 확인할 수 있습니다.

 

L2 distance의 경우, decision region이 자연스럽게 나타나는 것을 확인할 수 있습니다.

 

 

 

vision.stanford.edu/teaching/cs231n-demos/knn/

 

http://vision.stanford.edu/teaching/cs231n-demos/knn/

 

vision.stanford.edu

이 사이트에서 KNN의 demo을 제공하고 있는데, distance Metric과 K, class 수, 데이터 수를 다르게 지정해보면서 어떤 식으로 영역이 나타나는지를 확인해볼 수 있습니다. 

 

 

 

 

 

K-Nearest neighbor에서 K를 어떻게 결정할지, 혹은 distance metric을 어떤 것을 사용할지는 매우 중요한 부분입니다.

 

하지만, 이는 데이터로부터 학습되는 요소가 아닙니다. 알고리즘을 설계하는 사람이 선택해줘야 하는 값들이죠. 이를 hyperparameter라고 부릅니다. 머신러닝이나 딥러닝에서 매우 매우 자주 사용되는 용어이니, 꼭 기억해두어야 합니다.

 

hyperparameter는 어떤 게 가장 좋은지 바로 알 수 있는 방법이 없기 때문에, 문제에 따라서 다양한 값들을 시도해보고 가장 잘 작동한다고 보이는 값을 이용합니다. 

 

그렇다면, hyperparameter는 단순히 아무 값이나 다 해보면서 선택할까요? 가능한 모든 값을 직접 해보긴 현실적으로 힘들지 않을까요? 따라서 다음으로는 hyperparameter를 어떻게 결정해야 할지에 대해서 얘기해봅니다.

 

 

 

 

첫 번째로 생각해볼 수 있는 방법은 우리의 Dataset에 대해서 가장 잘 작동하는 hyperparameter를 사용해보는 것입니다. 하지만, 이러한 방법은 좋은 방법이 아닙니다. Machine Learning에서는 현재 우리가 가지고 있는 데이터에 대해서 최고 성능을 내는 것이 목표가 아니라, 본 적이 없는 데이터에 대해서 좋은 성능을 내는 것이 목표이기 때문입니다. 따라서, 우리가 가지고 있는 데이터에서만 가장 좋은 성능을 내는 hyperparameter를 사용하는 것은 좋은 선택이라고 보기 어렵습니다. 

 

 

 

 

두 번째로 생각해볼 수 있는 방법은 dataset을 학습용과 테스트용으로 나누고, 테스트 데이터에서 가장 좋은 성능을 내는 hyperparameter를 선택하는 것입니다. 하지만 이 방법도 문제가 있습니다. Test data는 우리가 만든 machine learning model이 본 적 없는 데이터에 대해서 어느 정도 성능을 낼지에 대한 추정치를 주는 역할을 하는데, 이 방법을 이용하면 test data에 대해서 나온 성능이 본 적 없는 데이터를 대표할 수 없게 됩니다. 

 

 

 

 

 

 

세 번째로 생각해볼 수 있는 방법은 데이터를 학습용, 검증용, 테스트용으로 분할시키는 것입니다. 학습용을 이용해서 모델을 학습하고, 검증용에서 가장 좋은 성능을 내는 hyperparameter를 선택한 뒤, 테스트용에서 성능을 점검해보는 것입니다. 이러한 방식을 이용하면 앞에서 나타났던 문제들 모두 발생하지 않게 됩니다. 실제로 대부분 이러한 3 분할 방식을 이용해 모델을 학습시키고, 테스트합니다.

 

 

 

 

 

 

네 번째로 생각해볼 수 있는 방법은 바로 Cross-Validation이라는 방법입니다. 데이터를 크게 학습용과 테스트용으로 나누고, 학습용 데이터를 K개의 fold로 나눈 다음, 한 번씩 돌아가면서 각 fold를 검증용으로 사용하는 것입니다. 이렇게 하면 모델이 훨씬 더 다양한 상황에서 검증을 진행해 더 좋은 hyperparameter를 선택할 수 있게 됩니다. 

 

 

 

 

 

이전에 설명했던 방법인 KNN은 이미지에서 사용하지 않는다고 합니다. 아까 설명했듯이, test time에서는 N개의 데이터 각각에 대해서 거리를 계산하여 가장 가까운 K개의 이미지 중 다수결에 해당하는 label을 지정하기 때문에 연산량이 높아 매우 느립니다. 그리고 픽셀에 대해서 distance metrics을 사용하였을 때, 유용한 정보를 주지 않는다고 합니다.

 

아래 나타난 사진 중 가장 왼쪽은 원본, 그리고 나머지 오른쪽 3개는 원본 이미지에 특수한 처리를 해준 이미지입니다. 하지만, 특수한 처리를 해준 이미지 3개 모두 원본 이미지와의 L2 distance를 계산하였을 때, 같은 값을 가진다고 합니다. 즉, 이러한 거리 척도는 지각적인 유사성에 대응되지 않습니다. (만약 지각적인 유사성에 대응되려면, 거리가 달라야만 합니다.)

 

따라서, 이미지에 대해서는 지각적인 유사성에 대응될 수 있는 다른 거리 척도가 필요합니다.

 

 

 

 

이외에도 차원의 저주라고 하는 문제점이 존재합니다. 

 

KNN이 좋은 성능을 내려면 우리의 training dataset이 공간을 빽빽이 채워야 합니다. 그렇지 않으면 모델이 판단하기에 가장 가까운 이웃이라고 생각하는 데이터가 꽤 멀리 존재할 수 있게 됩니다. (큰 공간에 점들이 듬성듬성 분포되어 있다고 상상해보시면 대략 이해가 가실 것입니다.)

 

문제는, 실제로 training dataset이 공간을 빽빽이 채우는 것은 충분한 학습 데이터가 필요하다는 것을 의미하며 문제의 차원에 지수적입니다. 그림에서 나타난 것처럼, 차원이 하나 늘어날수록 필요한 데이터의 수가 지수적으로 증가함을 알 수 있습니다. 현실적으로는 공간을 빽빽하게 메울 만큼의 데이터를 구하는 것은 불가능합니다. 오히려 데이터를 많이 구할 수 없는 경우들이 많기 때문에 더 적은 데이터로도 학습할 수 있는 모델이나 방법론들이 많이 연구되고 있죠. 

 

 

 

 

 

KNN에 대한 내용을 요약한 것입니다. 앞의 내용을 모두 숙지하셨다면, 읽으면서 아 이런 걸 얘기했었지? 하며 넘어가실 수 있을 겁니다.

 

 

 

다음으로는, Linear Classification에 대해서 다룹니다. 물론 매우 단순한 방법이긴 하지만, 아무리 복잡한 딥러닝 모델이어도 결국에는 가장 기초가 되는 개념은 linear classification이기 때문에 이에 대해서 확실하게 이해하는 것이 중요합니다.

 

 

 

Neural Network(신경망)이라고 하는 것은 결국에 linear classifier들을 여러 개 쌓아서 만든 것입니다. 즉 레고 블록과 같은 것이죠. 

 

 

 

 

 

 

이것이 바로 신경망을 활용한 예시입니다. Input으로는 사진을 넣어주고, output으로는 사진을 설명하는 글을 만들어서 알려줍니다. 사진을 이해하는데 활용되는 것은 CNN이며, 이를 입력으로 받아 글을 만들어주는 것은 RNN 계열의 신경망입니다.

 

 

 

 

 

지금부터 다뤄볼 linear classification은 parameter를 사용하는 모델 중 가장 간단한 모델인데요. 이를 통해 parametric approach에 대해서 알아봅니다.

 

이전에 우리가 사용했던 Nearest Neighbor나 K-Nearest Neighbor의 경우에는 별개의 parameter가 존재할 필요가 없었습니다. Test data를 기준으로 가장 가까운 데이터를 알아내서, 이를 통해 label을 결정하면 되기 때문이었죠. 하지만, Nearest Neighbor의 방식에는 치명적인 문제점이 있었죠. 바로 Test time이 매우 오래 걸린다는 점입니다. 

 

따라서, training time은 오래 걸려도 되지만, test time은 매우 짧을 수 있도록 하는 접근법이 필요한데요. 이것이 바로 parametric approach입니다. Parameter는 training data에 대한 우리의 지식을 요약한 결과라고 보시면 됩니다. 이를 이용하면 test 할 때는 별도의 training data를 이용하지 않고도, parameter만 가지고 본 적 없는 새로운 데이터에 대해서 label을 예측할 수 있게 됩니다. 

 

슬라이드에 나온 구체적인 예시를 한번 볼까요? Input data로는 32x32x3의 이미지 데이터를 받습니다. 총 3072개로 구성된 데이터죠. 이를 f(x, W)라고 하는 함수에 집어넣으면 함수는 주어진 10개의 class에 대해서 class score를 알려줍니다. 여기서 class score란, 해당 class에 속할 가능성을 나타내는 것으로 score가 높을수록 해당 class에 속할 가능성이 높아지게 됩니다.  

 

 

 

 

 

 

앞에서 f(x, W)가 어떤 식으로 구성되어있는지는 얘기해주지 않았는데요. 보통은 다음과 같은 형태로 구성됩니다.

 

W는 가중치를 나타내는 matrix이고, x는 input data, b는 bias를 나타냅니다. Wx + b 연산을 이용하면, 10개의 class score를 얻을 수 있습니다. 

 

우리가 training data를 이용해서 학습을 한다는 것은, 결국 여기서 W를 알아내는 것을 말합니다. 즉, 어떤 이미지 데이터가 입력되었을 때, 10개의 class 중에 어디에 속할지 틀리지 않고 잘 맞출 수 있도록 만들어주는 matrix를 학습하는 것이죠. 

 

여기서 b는 bias를 나타내는데, 이는 데이터를 이용해 학습되지 않으며 보정 효과를 나타냅니다. 예를 들어서, 데이터에 고양이 사진이 개 사진보다 많다면, 고양이에 해당하는 bias가 다른 것보다 더 높은 값을 가지게 될 것입니다. 

 

 

 

 

조금 더 수치적으로 구체적인 예시를 볼까요? 만약 input image가 2x2 짜리 데이터이고, 3개의 class가 존재하는 상황이라고 가정합니다.

 

앞에서 설명한 대로, Wx + b의 연산을 진행하면, 각각의 클래스에 대한 점수를 얻을 수 있게 됩니다. 

 

Cat score를 얻는 과정만 한번 따라가보자면, W의 첫 번째 row와 input image를 stretch 한 것과 곱해줍니다.

 

즉 0.2*56 + -0.5*231 + 0.1*24 + 2.0*2 로 계산할 수 있죠. 여기에 bias인 1.1을 더해주면 cat score에 해당하는 -96.8을 얻을 수 있습니다.

 

 

이 슬라이드를 통해 기억해둬야 하는 점은, linear classification을 'template matching'이라고 하는 접근법으로 생각할 수 있다는 점입니다. 즉, 특정한 class를 나타낼 수 있는 template를 학습하는 것이라고 볼 수 있는데요.

 

예를 들어, 위의 슬라이드에서 첫 번째 줄인 [0.2, -0.5, 0.1, 2.0]는 고양이라고 하는 class를 분류할 수 있는 '틀(template)'이라고 보시면 됩니다. 두 번째 줄은 개를 분류하는 틀이 될 것이고, 세 번째 줄은 배를 분류하는 틀이 될 것입니다.

 

여기서 Wx는 dot product(내적)을 의미하는데요, 이는 각 클래스에 대한 template과 이미지 pixel 사이의 유사성을 계산한다고 생각할 수 있습니다. 

 

예를 들어서, template이 이미지 pixel과 유사하게 학습되었다면, 내적 값은 높아지게 될 것이고, 결국 해당 class로 분류될 가능성이 높아져 정답을 잘 맞힐 수 있겠죠?

 

 

 

 

 

 

위 슬라이드는, linear classification을 template matching의 관점에서 봤을 때 이를 시각화한 것입니다.

 

아까 weight matrix인 W에서 각 행이 각각의 class에 대응되는 template이라는 얘기를 했었는데요, 이를 input image 사이즈와 동일하게 재배열시켜서 만든 것이 바로 아래에 있는 사진들입니다.

 

자세히는 안 보이시겠지만, car의 경우는 우리가 차를 앞쪽에서 바라본 모습이 대략 보이시죠? cat의 경우는 고양이의 두 귀가 사진의 위쪽에 희미하게 보이는 것을 알 수 있고요. horse의 경우도 말의 모습이 어렴풋이 보이는 걸 확인할 수 있습니다. 

 

하지만 이러한 linear classifier에는 한계점이 존재합니다. 각각의 class에 대해서 딱 하나의 template만 학습한다는 것이죠. 따라서 같은 class 내에서도 variation이 존재한다면, 모든 variation들을 평균을 내버립니다. 그렇기 때문에 다양한 케이스에 대해서 모두 학습할 수는 없다는 한계점이 존재합니다.

 

이러한 linear classifier를 레고 블록처럼 여러 개 쌓은 neural network는, 각 class 별로 여러 개의 template을 학습한다고 생각할 수 있으며, 이를 통해 class 내에 존재하는 다양한 variation에 대해서도 학습할 수 있는 힘이 생깁니다. 예를 들어서, 우리가 맨 앞에서 언급했던 대로 고양이의 일부 모습만 보고도 고양이를 구별해야 한다거나, 혹은 고양이의 크기나 생김새가 다를 때도 모두 고양이로 분류하려면 다양한 template을 학습하는 것이 필요할 것입니다.

 

 

 

 

linear classifier를 다른 시각으로도 볼 수 있습니다. 이는 Geometric viewpoint(기하학적 관점)에 해당합니다.

 

이런 관점에서는, image를 고차원 공간에 있는 점으로 인식하게 되며, linear classifier는 한 범주와 다른 범주를 구분 짓는 선형 결정 경계를 만들어냅니다.

 

 

 

 

 

 

다음으로는, linear classifier를 이용해서 구분하기 어려운 케이스들을 보여주고 있습니다.

 

가장 왼쪽의 첫 번째 케이스의 경우는, 1 사분면과 3 사분면이 class 2, 2 사분면과 4 사분면이 class 1에 해당하는 경우인데, 두 class를 완벽하게 구분할 수 있는 linear classifier는 만들 수 없습니다.

 

두 번째 케이스와 세 번째 케이스도 마찬가지입니다. 

 

이렇게, linear classifier로 구분할 수 없는 경우들이 발생하기 때문에, neural network에서는 다양한 non-linearity를 추가해주어 이러한 경우도 분류할 수 있게 만들어줍니다. 

 

 

 

 

지금까지, 각 class의 score를 알려주는 linear score function을 이용했습니다.

 

하지만, 단순히 이 score만 가지고는, 해당 model이 얼마나 이미지를 잘 분류했는지 알 수 있는 방법이 없습니다.

 

잘 분류했는지를 파악해야 이 모델을 당장 쓸 수 있는지, 혹은 더 학습을 진행해서 더 잘 맞출 수 있는 weight을 얻어야 할지 판단할 수 있겠죠?

 

 

 

 

다음 강의에서는, 그렇다면 우리가 구한 weight가 얼마나 좋은지를 알려주는 loss function에 대해서 다루어볼 예정입니다.

 

또한, 임의의 W 값으로부터 시작해서, 최적의 W를 찾기 위해 어떻게 해야 하는지에 대해서도 알아볼 것이고요.

 

ConvNet에 대해서도 알아볼 예정입니다.

 

 

 

 

 

 

지금까지 cs231n의 두 번째 강의인 lecture 2에 대해서 모두 정리해 보았습니다.

 

역시나 혼자 공부할 때와 비교했을 때, 정리하는 글을 작성해보면 훨씬 더 정리가 잘 되는 것 같고 또한 공부한 내용들이 더 잘 정리되는 것 같습니다.

 

이번 Lecture 2를 잘 이해하셨는지 확인해볼 수 있는 문제를 한번 내보도록 하겠습니다.

 

 

 

 

cs231n Lecture 2 Quiz

 

 

 

 

Q1. Semantic Gap이란 무엇인가요?

 

 

Q2. 컴퓨터가 이미지의 class를 분류하는 데 있어서 다양한 어려움들이 존재하는데, 이러한 어려움들의 예시를 제시하세요.

 

 

Q3. Data-Driven approach는 왜 나오게 되었으며, 어떤 process를 가지고 있나요?

 

 

Q4. K-Nearest Neighbor classifier는 어떤 방식으로 test data의 label을 결정하나요? 또 K의 값이 커질수록 어떠한 특징을 나타내나요?

 

 

Q5. KNN에서 사용된 distance metric이 두 가지 있습니다. 각각의 식과, 특징은 무엇인가요?

 

 

Q6. Hyperparameter란 무엇인지 정의를 설명하고, 어떤 방법을 통해 hyperparameter를 결정하나요?

 

 

Q7. linear classifier는 template matching의 아이디어로도 설명할 수 있는데요. template matching이란 무엇인가요?

 

 

Q8. 데이터가 총 8개의 차원으로 구성되어있고, 우리가 분류해야 하는 class가 10개라고 했을 때, Wx+b에서 W, x, b 각각의 차원은 어떻게 되어야 하나요? 그리고 W를 template의 관점에서 어떻게 해석할 수 있는지 설명하세요.

 

 

Q9. linear classifier의 한계점이 두 가지 있었습니다. 두 가지 한계점에 대해서 설명하세요. 

 

 

Face Recognition(얼굴 인식)분야에서 사용되는 Loss인 ArcFace loss에 대한 논문이다.

 

 

얼굴 인식 분야를 공부하는 것은 아니나, 다른 논문을 읽다가 loss function으로 ArcFace loss를 활용하는 논문이 있어 해당 논문을 읽게 되었다.

 

 

원 논문 자료: https://arxiv.org/abs/1801.07698

 

ArcFace: Additive Angular Margin Loss for Deep Face Recognition

One of the main challenges in feature learning using Deep Convolutional Neural Networks (DCNNs) for large-scale face recognition is the design of appropriate loss functions that enhance discriminative power. Centre loss penalises the distance between the d

arxiv.org

논문에 들어가기 앞서, 이 논문을 읽으면서 필요한 개념들을 정리해보고 논문 리뷰를 시작하려고 한다.

 

 

 

1) open-set face recognition vs closed-set face recognition

 

 

 

ArcFace 논문에서는, 기존 방법론의 한계점을 지적하는 과정에서 closed-set에 대한 언급을 하게 되는데, 이는 선행 연구 중 하나인 SphereFace : Deep Hypersphere Embedding for Face Recognition에서 언급되었다.

 

 

open-set face recognition vs closed-set face recognition

 

Closed-set face recognition이란, 내가 맞추어야 하는 Testing set에 있는 얼굴 사진에 대한 identity(label 혹은 정답이라고 생각하면 될듯하다.)가 training set에 있는 경우를 의미한다. 따라서 이는 우리가 머신러닝이나 딥러닝에서 풀고자 하는 classification problem과 같다는 것을 의미한다. 

 

 

하지만, 현실 문제에서는 우리가 한번도 본 적 없는 얼굴에 대해서 인식을 해야 하는 일이 발생한다. 이에 해당하는 것이 바로 open-set face recognition이다. open-set face recognition이란, 내가 맞추어야 하는 Testing set에 있는 얼굴 사진에 대한 identity가 training set에 없는 경우를 의미한다. 

 

 

open-set의 경우, 얼굴 이미지를 학습 데이터에 알려진 identity로 분류하는 것이 불가능하기 때문에 얼굴 이미지를 discriminative 한 feature space로 mapping 시키는 것이 요구된다.

 

 

2) Logit

 

 

 

neural network에서 classification task를 위해 사용되는 마지막 layer를 의미하고, 실수 형태로 raw prediction value 값을 뽑아낸 결과를 의미한다.

 

 

예를 들어서, 이미지를 주고 이것이 개, 고양이, 사자, 고슴도치를 구별하는 classification을 해야 한다면, CNN을 이용해서 특징을 추출한 후, 마지막에 Fully connected layer를 이용해서 최종적으로 4개의 클래스 중에 어떤 클래스에 가까운지에 대한 예측값을 뽑게 될 것이다. 보통 분류 문제의 경우는, 마지막에 softmax를 취해서 각 클래스에 해당할 확률의 합이 1이 되게끔 하는 처리를 해주지만, softmax를 취해주기 전에 나온 raw prediction value 값을 logit이라고 부르는 것으로 보인다.

 

 

 

3) Geodesic distance

 

geodesic distance란, 두 점을 잇는 최단 거리를 의미한다. 수학시간에는 두 점을 잇는 최단 거리를 직선이라고 배운 것 같은데, 이를 영어로 표기하면 geodesic distance인 듯....

 

 

 

 

논문을 읽는데 필요한 3가지 개념들을 모두 정리하였고, 본격적으로 논문에 대한 내용들을 다루어보려고 한다.

 

 

 

 

 

 

Abstract

 

 

대규모 얼굴 인식을 위해 Deep Convolutional Neural Networks(DCNNs)를 사용해 feature learning을 하는 데 있어서 주요한 challenge 중 하나는 discriminative power를 강화하는 적절한 loss function의 설계이다.

Centre loss는 intra-class compactness를 달성하기 위해 Euclidean space에서 deep feature와 그에 대응되는 class 중심 사이의 거리에 처벌을 가한다. 

SphereFace는 마지막 fully connected layer에서의 linear transformation matrix가 angular space에서 class의 중심을 나타내는 표현으로 사용될 수 있다는 것을 가정하며, deep feature와 그것에 대응되는 weight 사이의 각도에 multiplicative 한 방법을 이용해 처벌을 가한다.

최근, 인기 있는 연구분야는 face class separability를 최대화하기 위해서 잘 설계된 손실 함수에 margin을 포함시키는 것이다. 이번 연구에서, 우리는 얼굴 인식에 있어서 매우 discriminative 한 feature를 얻기 위해서 additive angular Margin Loss (ArcFace)를 제안한다. 제안된 ArcFace는 hypersphere에서의 geodesic distance와 정확히 일치하기 때문에, 명확한 기하학적 해석을 가지고 있다. 

 

 

1. Introduction

 

 

Deep Convolutional Neural Network (DCNN) embedding을 사용하는 face representation은 얼굴 인식을 위한 최고의 방법이다. DCNNs은 일반적으로 포즈 정규화 단계를 거친 후, 얼굴 이미지를 클래스 내에서는 작은 거리를 가지고 클래스 간에는 큰 거리를 갖는 특징으로 mapping 한다. 얼굴 인식을 위해서 DCNNs을 학습시키는 것에는 두 개의 주요한 연구분야가 있다. 그중 하나는 softmax classifier를 사용하여 학습 데이터에서의 다른 identity를 구별할 수 있는 multi-class classifier를 학습하는 것이고, 다른 하나는 triplet loss와 같이 embedding을 직접적으로 학습하는 것이다.

 

 

대규모의 학습 데이터와 정교한 DCNN architecture에 기반해서, softmax loss 기반의 방법과 triplet loss 기반의 방법은 얼굴 인식에서 훌륭한 성능을 얻을 수 있었다. 하지만, softmax loss와 triplet loss 둘 다 결점을 가지고 있다. softmax loss에 대해서는 첫 번째로, 선형 변환 행렬 WRd×n 의 크기가 identity의 개수 n에 의해 선형적으로 증가한다는 것이고, 두 번째로, closed-set 분류 문제에 대해서는 학습된 특징들이 분리될 수 있지만, open-set 얼굴 인식 문제에 대해서는 충분히 discriminative 하지 않다는 것이다. 

 

 

Triplet loss에 대해서는 첫 번째로, 특히 대규모 데이터셋에서 face triplet의 수에서 combinatorial explosion이 존재할 수 있으며, 이는 iteration step의 수에서의 상당한 증가를 가져올 수 있다. 두 번째로, 효율적인 모델 학습을 위해서는 semi-hard sample mining이 상당히 어려운 문제라는 것이다.(triplet loss를 계산하기 위해서는, 기준점인 anchor, anchor와 같은 identity를 가지고 있는 점인 positive, anchoer와 다른 identity를 가지고 있는 점인 negative의 triplet 쌍을 뽑아야 한다. 이를 face triplet이라고 표현한 것으로 보이고, 데이터가 많을 경우 이러한 triplet 쌍을 뽑을 수 있는 경우의 수가 폭발적으로 많아지기 때문에 combinatorial explosion이 발생한다고 표현한 것으로 보인다. 또한, triplet을 어떻게 sampling 하는지에 따라서 성능의 차이가 많이 나기 때문에 sampling을 어떻게 하는지가 매우 중요한 문제인데, 그렇기 때문에 sample mining이 상당히 어렵다고 표현한 것으로 보인다.)

 

 

Figure 1. four kinds of Geodesic Distance (Gdis) constraint.

 

Figure 1 - centre normalization과 feature normalization에 기반해서, 모든 identity는 hypersphere에 분포되어있다. 클래스 내 compactness와 클래스 간 discrepancy를 향상하기 위해서, 4종류의 Geodesic Distance constraint를 고려한다.

 

(A) Margin-loss: sample과 중심 간의 geodesic distance margin을 추가한다. (위 그림을 보면 알겠지만, sample과 중심 간의 거리 + m 이 sample과 다른 identity의 중심 간의 거리보다 작게끔 만드려고 한다. 따라서, 여기서의 margin이라는 의미는 sample과 다른 identity의 중심 간의 거리가 m이라는 값보다 크게끔 한다는 것이다. 만약 m이라는 값보다 작으면 penalty를 부과한다.)

(B) Intra-loss: sample과 중심 간의 geodesic distance를 감소시킨다.

(C) Inter-loss: 다른 중심 간의 geodesic distance를 증가시킨다.

(D) Triplet-loss: triplet sample 간에 geodesic distance margin을 추가함. (위 그림을 보면, 기준이 되는 anchor인 x11과 같은 identity를 가지는 샘플인 x12와의 거리 + m 이 anchor와 다른 identity를 가지는 샘플인 x21 보다 작게끔 만드려고 한다. 따라서, 여기서의 margin이라는 의미는 anchor와 다른 identity를 가지는 샘플 간의 거리가 m이라는 값보다 크게끔 한다는 것이다. 만약 m이라는 값보다 작으면 penalty를 부과한다.)

 

이번 연구에서, 우리는 Additive Angular Margin Loss (ArcFace)를 제안하며, 이는 (A)에서의 geodesic distance (Arc) margin penalty와 정확히 대응되며, 얼굴 인식 모델의 discriminative power를 강화시킨다.

 

 

 

혼자 이 논문을 읽으면서, centre normalization과 feature normalization에 기반해서, 모든 identity가 hypersphere에 분포되어있다는 의미가 무엇인지 몰라서 꽤 애를 먹었다. 따라서 논문의 그다음으로 넘어가기 전에, 이 부분에 대해서 보충 설명을 진행하고 넘어가도록 하려고 한다. 이 부분을 이해하지 못하면, ArcFace loss가 어떤 효과를 나타내는지를 직관적으로 이해하기가 매우 어렵다. 

 

 

Abstract에서도 언급했지만, 이전의 선행연구 중 하나인 SphereFace에서는 마지막 fully connected layer에서의 linear transformation matrix가 angular space에서 class의 중심을 나타내는 표현으로 사용될 수 있다는 것을 가정한다. ArcFace 논문에서도 이러한 가정을 받아들여 사용하게 된다. 

 

 

설명하기 쉽도록, 매우 간단한 예시를 들어보려 한다. (물론 실제 케이스에서는 이미지 feature의 차원과 구분해야 하는 얼굴의 identity의 수가 매우 크겠으나, 이는 더 쉬운 케이스에서 원리를 이해하면 확장시키는 것은 어렵지 않을 것이다.) 얼굴 이미지에 해당하는 feature가 3차원이라고 가정하고, 우리는 두 가지의 identity를 가진 얼굴을 분류하는 task를 진행하려고 한다. 그렇다면, classification task를 위해서 fully connected layer는 3차원의 데이터를 받아 2차원으로 구성된(즉, 각 identity에 대한 prediction score) 데이터를 output으로 뽑아내면 되는 케이스가 된다.

 

 

해당 예시는 xAT의 연산을 하는 경우를 생각하였으나, 상황에 따라 연산의 순서가 바뀔 수 있다. 그때는 차원을 잘 살펴보고 상황에 맞춰서 생각해보면 될 것이다. 

 

 

얼굴 이미지에 해당하는 feature를 x = [10, 20, 30]으로 정의해주고, feature를 입력으로 받았을 때, 2가지의 identity로 분류하는 task를 진행하는 fully connected layer를 구성하는 matrix A를 다음과 같이 정의해준다. (행은 class의 개수, 열은 feature의 차원을 나타낸다.)

 

(123456)

 

 

fully connected layer는 xAT의 연산 과정을 거치므로, 2가지의 identity에 대한 prediction score는 다음과 같이 계산될 수 있다.

 

(101+202+303104+205+306)

 

이러한 과정을 거쳐 얼굴 이미지의 feature들을 받았을 때, 각 identity에 대한 prediction score를 계산할 수 있다. 

 

SphereFace의 가정에 따라, 해당 예시에서 첫 번째 identity의 중심을 나타내는 값은 (123)이고, 두 번째 identity의 중심을 나타내는 값은 (456)이다. 

 

feature normalization을 거치면, 얼굴 이미지에 해당하는 feature는 x = [10/1400, 20/1400, 30/1400] 의 형식으로 normalization 해줄 수 있다. 

 

centre normalization을 거치면, 첫 번째 identity의 중심을 나타내는 값은 (1/142/143/14)

 

두 번째 identity의 중심을 나타내는 값은 (4/775/776/77)

 

자, 그럼 normalization에 기반했을 때, 모든 identity가 hypersphere에 분포된다는 의미는 무엇인가?

 

 

해당 예시가 3차원이므로, 3차원인 hypersphere는 우리가 잘 아는 도형인 '구'에 해당하게 되고, 구의 중심은 [0, 0, 0]이다.

 

이미지의 feature도 3차원, identity의 중심을 나타내는 값도 3차원이며, 각각에 대해서 구의 중심과의 거리를 계산하면 1이 된다. (normalization을 거쳤기 때문이다.) 따라서, 이미지의 feature와, identity의 중심을 나타내는 값들은 모두 반지름이 1을 만족하는 구 위에 분포하게 된다.

 

 

 

 

다시 논문으로 돌아와서....

 

 

(선행 연구 관련된 내용 한 단락 생략)

 

 

이번 연구에서, 얼굴 인식 모델의 discriminative power를 향상하고 학습 프로세스를 안정하기 위해 Additive Angular Margin Loss (ArcFace)를 제안한다. Figure 2에서 보여주는 것처럼, DCNN feature와 마지막 fully connected layer의 dot product는 feature와 weight가 normalization 된 후의 cosine distance와 같다.(이 내용은 곧 나옴.) 현재 feature와 target weight 사이의 각도를 계산하기 위해서 arc-cosine function을 활용하고, 그다음으로 우리는 추가적인 angular margin을 target angle에 더해주며, cosine function에 의해서 target logit을 다시 얻게 된다. 다음으로, 모든 logit을 고정된 feature norm에 의해서 re-scale 하고 그 후의 단계는 softmax loss와 동일하다.

 

 

제안된 ArcFace의 이점은 다음과 같이 요약될 수 있다.

 

 

매력적이다 - ArcFace는 정규화된 hypersphere에서 각도와 호 사이의 정확한 일치성을 통해(hypersphere 내에서는 반지름이 모두 같으므로 호는 각도의 영향만 받는다는 얘기인 듯) geodesic distance margin을 직접 최적화한다. 우리는 feature와 weight 사이의 angle statistics를 분석해서 512차원의 공간 내에서 어떤 일이 일어나는지를 직관적으로 나타낼 수 있다.

 

 

효과적이다 - ArcFace는 대규모 이미지와 비디오 dataset을 포함하는 10개의 얼굴 인식 benchmark에서 SOTA 성능을 나타냈다.

 

 

쉽다 - ArcFace는 Algorithm 1에서 주어진 것처럼 단지 몇 줄의 코드만 필요하며, MxNet, Pytorch, Tensorflow와 같은 deep learning framework에서 쉽게 실행할 수 있다. 게다가, 이전 선행연구와는 다르게 안정적인 성능을 가지기 위해서 다른 loss function과 결합될 필요가 없으며, 어떤 training dataset에도 쉽게 수렴할 수 있다.

 

 

효율적이다 - ArcFace는 학습하는 동안에 무시해도 될 정도의 계산 복잡도를 추가한다. 현재 GPU들은 학습을 위해 수백만 개의 identity를 쉽게 지원할 수 있으며, model parallel strategy도 더 많은 identity를 지원할 수 있다.

 

 

 

2. Proposed Approach

 

 

2.1. ArcFace

 

 

가장 널리 사용되는 분류 손실 함수인 softmax loss는 다음과 같이 나타내어질 수 있다.

 

 

softmax loss

 

xiRdi번째 sample의 deep feature를 나타내고, yi번째 class에 속해있다. 이번 연구에서는 embedding feature의 차원은 512차원으로 설정되었다. WjRd는 가중치 WRd×nj번째 column을 나타내고, bjRn은 bias term이다. Batch size는 N, class number는 n이다. 전형적인 softmax loss는 deep face recognition에서 널리 사용되고 있다. 하지만, softmax loss function은 명쾌하게 feature embedding이 클래스 내 sample들이 높은 유사성을 가지도록, 그리고 클래스 간 샘플들이 다양성을 가지도록 최적화하지 않으며, 이는 클래스 내 appearance variation이 높은 상황(포즈의 다양성이나, 나이의 차이가 많이 날 때)과 대규모 테스트 시나리오(수백만 혹은 수조 개의 쌍이 존재하는 상황)에서 performance gap을 나타낸다.

 

 

단순하게, 우리는 bias bj = 0으로 둔다. 그 후에, 우리는 logit을 WTjxi = ||Wj||||xi||cosθj로 바꾸며, 여기서 θj는 가중치 Wj와 feature xi간의 각도를 나타낸다. 이전 연구에 따르면, 우리는 L2 normalization을 이용해서 individual weight ||Wj|| = 1로 만든다. 다른 이전 연구들을 따르면, 우리는 또한 embedding feature ||xi||L2 normalization에 의해서 고정하고, s로 re-scale 한다. Feature와 weight에 대해서 normalization 하는 단계는 예측이 오직 feature와 weight 간의 각도에만 의존하도록 만든다. 따라서 학습된 embedding feature는 hypersphere에서 반지름 s의 크기로 분포된다.

 

feature, weight normalization 후 s로 re-scale까지 진행한 Loss function 수식

 

Embedding feature가 hypersphere에서 각 feature 중심 주변에 분포되기 때문에, 추가적인 angular margin penalty인 mxiWyi 사이에 추가하며, 이는 클래스 내 compactness와 클래스 간 discrepancy를 향상시킨다. 제안된 Additive angular margin penalty는 normalized hypersphere에서 geodesic distance margin penalty와 같기 때문에, 이러한 방법을 ArcFace로 명명하였다.

 

ArcFace Loss function

 

 

ArcFace loss를 적용하는 방법은 다음 그림으로 한 번에 도식화할 수 있다.

 

Figure 2. Illustration of ArcFace loss function process

 

feature xi와 weight W을 normalization 하고, 이 둘을 내적해서 cosθj (logit)을 얻는다. 앞에서 설명했던 대로, 구의 표면에 각 feature들이 존재한다고 생각한다면 이 cosθj값이 크다는 것은, 해당 class의 중심과 각도가 작다는 것이고(거리가 가깝다는 것이고), cosθj값이 작다는 것은, 해당 class의 중심과 각도가 크다는 것이다.(거리가 멀다는 것이다.) 

 

 

다음으로는, 해당 feature가 속한 class인 yi의 중심과 feature xi의 각도인 θyi에 margin인 m을 추가해준다. 이는 직관적으로 생각해보면, loss function을 계산할 때 해당 feature가 속한 class의 중심과 feature 간의 각도를 더 크게 계산하는 효과를 가져온다.(즉, 해당 class에 속할 가능성이 실제에 비해서 더 낮게 계산되도록 한다.)

 

 

다음으로는, s를 곱해줘서 Feature Re-scale을 진행하고, 이 값을 기준으로 Softmax를 취해준 뒤, Cross-entropy Loss를 계산해준다.

 

 

처음 이 논문을 읽었을 때는, 이러한 과정을 통해서 왜 클래스 내 샘플들이 더 compact 해지고, 클래스 간 거리가 더 멀어진다는 것인지 이해를 하지 못했다. 하지만 이러한 효과가 왜 나는지에 대한 핵심은 바로 cross-entropy loss라고 생각한다. cross-entropy loss의 특성은, 정답에 해당하는 class의 softmax 값은 1에 가까워지게 하고, 그에 따라서 정답이 아닌 class에 해당하는 softmax값은 0으로 만들어버린다는 것이다. 이를 ArcFace의 경우에 대입해보면, 정답에 해당하는 class의 softmax 값이 1에 가까워지게 해서 class의 중심과 sample 간의 각도가 0에 가까워지게 만들고(cos(0) = 1이다.), 정답이 아닌 class에 해당하는 softmax값은 0에 가까워지게 해서 다른 class의 중심과 sample 간의 각도가 더 멀어지게 만들어버린다. 

 

 

논문의 저자들은 softmax와 ArcFace loss의 차이를 보여주기 위해서, 1개의 class당 1500개의 이미지가 있고 8개의 class로 구성되어 있는 face image에 대해서 2D feature embedding network를 softmax loss와 ArcFace loss를 각각 학습시켰다. Figure 3에서 나타난 것처럼, softmax loss는 대략 분리될 수 있는 feature embedding을 만들지만, decision boundary에서는 현저히 애매모호함을 만들어낸다. 반면에, 제안된 ArcFace loss는 가장 가까운 클래스 간의 더욱 명백한 gap이 생기도록 만든다.

 

Figure 3

 

Figure 3에서 점들은 샘플을 나타내고, 선은 각 identity의 중심 방향을 나타낸다. Feature normalization을 기반으로, 모든 face feature들은 고정된 반지름을 가지는 호 공간에 존재한다. 가장 가까운 class 간의 Geodesic distance gap은 additive angular margin penalty가 포함되었을 때 분명해지는 것을 볼 수 있다.

 

 

 

이후 나오는 부분들은 ArcFace가 어떤 식으로 좋은지 다른 loss function들과 비교하고, 다양한 dataset에서 실험한 내용들을 다루고 있어 review에서는 생략하였다.

 

 

 

 

 

오늘은 ArcFace라고 하는 논문에 대해서 review 해보았는데, 아무래도 얼굴 인식 관련한 배경지식이 없어서인지 완벽하게 이해하는데 꽤 오랜 시간이 들었다. 그래도 며칠 간의 고생 끝에 나름대로 논문을 확실하게 정리할 수 있어서 좋은 공부가 된듯하다.

 

 

 

 

 

논문을 읽던 중, ablation study 라고 하는 단어를 종종 마주할 수 있었다.

 

따로 논문에 이게 어떤 것인지에 대해서 설명이 없어서, 이에 대해서 정리해보고자 한다.

 

 

 

In the context of deep learning, what is an ablation study?

(딥러닝에서, ablation study는 무엇인가?)

 

 

의학이나 심리학 연구에서, Ablation Study는 장기, 조직, 혹은 살아있는 유기체의 어떤 부분을 수술적인 제거 후에 이것이 없을때 해당 유기체의 행동을 관찰하는 것을 통해서 장기, 조직, 혹은 살아있는 유기체의 어떤 부분의 역할이나 기능을 실험해보는 방법을 말한다. 이 방법은, experimental ablation이라고도 알려져 있는데, 프랑스의 생리학자 Maria Jean Pierre Flourens이 19세기 초에 개척했다. Flourens은 동물들에게 뇌 제거 수술을 시행하여 신경계의 다른 부분을 제거하고 그들의 행동에 미치는 영향을 관찰했다. 이러한 방법은 다양한 학문에서 사용되어왔으며, 의학이나 심리학, 신경과학의 연구에서 가장 두드러지게 사용되었다.

 

 

 

Machine learning에서, ablation study는 "machine learning system의 building blocks을 제거해서 전체 성능에 미치는 효과에 대한 insight를 얻기 위한 과학적 실험"으로 정의할 수 있다. Dataset의 feature나 model components가 building blocks에 해당한다. (따라서 ablation study라는 용어를 feature ablation이나 model ablation 이라는 용어로도 사용할 수 있다.) 하지만 어떠한 design choice나 system의 module도 ablation study에 포함될 수 있다.

 

 

 

그림 1. Illustration of ablation study

 

 

 

우리는 ablation study가 여러 시도로 구성된 실험이라고 생각할 수 있다. 예를 들어, 각 model ablation trial은 한개 혹은 그 이상의 components가 제거된 모델을 학습하는 것을 포함한다. 유사하게, feature ablation trial은 dataset feature 들의 다른 집합을 사용하여 모델을 학습하는 것을 포함하며, 결과를 확인한다.

 

 

 

그림 1에서, 위에 나타난 표 그림은 feature ablation trial을 나타내고 있으며, 아래 나타난 layer 그림은 layer ablation trial을 나타낸다. 예를 들어, layer ablation trial에서, 우리는 base model의 마지막 hidden layer를 제거하고, 결과 모델을 학습시키고, 이것의 성능을 확인하게 된다.

 

 

 

 

[참고자료]

 

https://www.quora.com/In-the-context-of-deep-learning-what-is-an-ablation-study

 

 

 

 

 

 

강화학습 관련 논문을 읽다가, Bootstrapping 이라는 단어를 마주하게 되었다.

 

 

Bootstrapping이라는 용어는 통계학에서도 사용되고, 또한 머신러닝에서도 사용되곤 한다. 

 

 

나는 위 두가지 분야에서 사용되는 의미를 알고 있었기 때문에, 강화학습에서 과연 유사한 의미를 가지는지 의문을 가지게 되었다.

 

 

왜냐하면 강화학습은 Data-driven 방식이 아니기 때문이다.

 

 

따라서 강화학습에서는 과연 Bootstrapping은 어떤 의미로 사용되는지를 다뤄보도록 한다.

 

 

 

 

강화학습에서의 Bootstrapping은 무엇인가??

 

 

 

강화학습에서의 Bootstrapping은 같은 종류의 추정값에 대해서 업데이트를 할 때, 한 개 혹은 그 이상의 추정값을 사용하는 것이다.

 

 

강화학습의 고전 알고리즘인 SARSA를 업데이트하는 식을 보고 이것이 무슨 얘기인지 이해해보자.

 

 

 

수식 1. SARSA의 업데이트 식

 

 

Rt+1+γQ(s,a)Q(s,a)의 실제 값에 대한 추정치이고, 또한 TD(Temporal-Difference) target이라고 불린다. 

 

 

 

이렇게, 다른 Q value를 업데이트 하기 위해서 부분적으로 Q value를 사용하기 때문에 SARSA는 bootstrap 방식이다.

 

 

 

 

 

같은 업데이트 규칙을 대조적인 방법인 Monte Carlo을 사용해보면 어떤 차이가 있는지 확인할 수 있다.

 

 

 

수식 2. MC 방식으로 Q value update

 

 

Gt 는 시간 t일때, 전체 discounted reward를 의미하고, 이러한 방식의 업데이트를 가정한다면, 상태 s에서 시작해서 action a를 취하고, 그러고 나서 episode가 끝날 때까지 현재 정책을 따르게 된다. 

 

 

 

수식 3. 반환값(Return) 계산식

 

 

반환값은 수식 3과 같이 계산할 수 있으며, 여기서 T는 terminal state에 도달하는 time step을 의미한다.

 

 

 

 

특히, 이 반환값(Gt)은 다른 Q value로부터 어떠한 추정치를 전혀 사용하지 않기 때문에, 이는 오직 환경으로부터 얻게 되는 일련의 observation들만 사용하게 된다. 

 

 

 

 

 

 

Bootstrapping의 특징

 

 

 

Bootstrapping의 주요한 단점은 우리가 Q(s,a)에 대해서 어떤 starting value를 사용했는지에 따라 편향된다는 점이다.

 

 

 

이렇게 편항되는것은 대부분의 경우에 옳지 않으며, 너무 많은 자기 참조와 충분하지 않은 실제 데이터 때문에 전체적으로 업데이트 시스템이 불안정해질 수 있다.

 

 

 

이것이 바로 신경망을 이용하는 off-policy learning의 문제점이다. (Q-learning이 이에 해당한다.)

 

 

 

Bootstrapping을 사용하지 않으면, 더 긴 trajectory를 사용해야 하는데, 이는 그 대신에 종종 더 높은 분산을 가져오게 되며, 실제로 추정값이 수렴하기 전에 더 많은 샘플이 필요하게 됨을 의미한다.

 

 

 

따라서, bootstrapping의 문제점에도 불구하고, 만약 bootstrapping를 사용해서 작동이 되도록 만들 수 있다면 상당히 더 빠르게 배울 수 있을 것이고 종종 Monte Carlo approach에 비해서 더 선호된다.

 

 

 

 

[참고자료]

https://datascience.stackexchange.com/questions/26938/what-exactly-is-bootstrapping-in-reinforcement-learning

 

 

 

 

 

 

저번 글에 이어 <4> 자연어 생성부터 진행하고자 한다.

 

<4> 자연어 생성

언어 생성이란 무엇일까? 사람이 언어를 생성하는 과정을 생각해보자. 일반적으로 글을 쓰거나 말을 할 때 어떠한 주제에 대한 목적 의식을 가지고 언어에 맞는 문법과 올바른 단어를 사용해 문장을 생성한다. 신문 기사가 될 수도 있고, 상대방과의 대화, 문장 요약 등 언어를 활용해 우리는 서로 "소통(Communication)"하면서 살아간다. 그렇다면 컴퓨터가 상대방의 대화를 이해하고, 글도 쓴다면 어떨까?

 

실제로 미국 캐리포니아 LA Times에서는 "Quakebot"이 인공지능으로 지진이 난 지 3분만에 기사를 작성했다. 

 

원문 링크: https://slate.com/technology/2014/03/quakebot-los-angeles-times-robot-journalist-writes-article-on-la-earthquake.html

 

The First News Report on the L.A. Earthquake Was Written by a Robot

Ken Schwencke, a journalist and programmer for the Los Angeles Times, was jolted awake at 6:25 a.m. on Monday by an earthquake. He rolled out of bed...

slate.com

 

하지만 이런 사실 기반의 기사를 제외하고 일반적으로 감정 및 논리력이 들어가는 기사를 작성하는 데는 아직까지 한계가 있어서 보조 도구로 쓰이고 있는 상태이며, 관련 영역에 해당하는 많은 데이터가 필요하다.

 

언어 생성은 사람의 대화를 최대한 많이 수집해서 대화를 배우게 하고 지속적으로 평가하는 과정을 반복해서 특정 목적에 맞는 텍스트를 생성하는 것이 주 목적이다. 이 분야는 아직 많은 연구와 개척이 필요한 분야다. 현재 언어 생성 기술을 활용해서 성공적으로 활용하는 분야 중 하나는 기계번역 분야인데, 대표적으로 네이버의 파파고와 구글의 구글 번역기가 있다.

 

<5> 기계 이해

 

기계 이해(Machine Comprehension)는 기계가 어떤 텍스트에 대한 정보를 학습하고 사용자가 질의(Question)을 던졌을 때 그에 대해 응답하는 문제다. 다시 말하자면 기계가 텍스트를 이해하고 논리적 추론을 할 수 있는지 데이터 학습을 통해 보는 것이다. 다음 예시를 보자.

 

Text:

자연어 처리 또는 자연 언어 처리는 인간의 언어 현상을 컴퓨터와 같은 기계를 이용해서 모사 할 수 있도록 연구하고 이를 구현하는 인공지능의 주요 분야 중 하나다. 자연 언어 처리는 연구 대상이 언어이기 때문에 당연하게도 언어 자체를 연구하는 언어학과 언어 현상의 내적 기재를 탐구하는 언어 인지 과학과 연관이 깊다. 구현을 위해 수학적 통계적 도구를 많이 활용하며 특히 기계학습 도구를 많이 사용하는 대표적인 분야이다. 정보검색, QA 시스템, 문서 자동 분류, 신문기사 클러스터링, 대화형 Agent 등 다양한 응용이 이루어지고 있다.

 

Question:

자연어 처리는 어느 분야 중 하나인가?

 

예를 들어, 기계한테 위와 같은 텍스트에 대한 내용을 학습시켰다고 하자. 그리고 "자연어 처리는 어느 분야 중 하나인가?" 라는 텍스트와 관련이 있는 질문을 기계에게 한다. 그러면 기계는 위 텍스트의 내용을 토대로 추론해서 이에 대한 응답을 텍스트 내에서 하거나 정답 선택지를 선택하는 방식으로 답하게 된다. 

 

기계 이해 분야는 자연어 처리 기술에 대한 개념이 총망라된 학습 태스크여서 앞에서 알아본 다른 자연어 처리 태스크와 비교하면 어렵고, 더욱 복잡한 모델링을 필요로 한다. 기계 이해는 아직 연구 단계에 있고 QA(Question Answering) 태스크와 관련된 여러 대회를 통해 많은 모델들이 제시되고 있다. 이러한 대회 중 대표적으로 SQuAD(Stanford Question Answering Dataset)를 사례로 들 수 있다.

 

데이터셋

 

기계 이해 태스크에서는 대체로 자연 언어를 이해하는 과제에서 기계가 텍스트 내용에 대해 추론을 잘 하는지 파악하는 목적에서 학습하게 된다. 그렇기 때문에 이 태스크를 QA 태스크라고 부르기도 하며 보통 Question Answering(QA) 형태의 데이터셋을 활용해 기계에게 학습하게 한다. 이러한 데이터셋은 위키피디아나 뉴스 기사를 가지고 데이터를 구성하며 대체로 텍스트와 지문, 정답 형태로 구성돼 있다. 

 

그 중 하나의 예시로는 bAbI 데이터셋이 있는데, 페이스북 AI 연구팀에서 기계가 데이터를 통해 학습해서 텍스트를 이해하고 추론하는 목적에서 만들어진 데이터셋이다. 총 20가지 부류의 질문 내용으로 구성돼 있으며 질문 데이터셋 구성은 다음과 같다.

 

그림 1. bAbI 데이터셋 예시

 

 

위 그림에서 보여지는 것 처럼 bAbI 데이터셋은 시간 순서대로 나열된 텍스트 문장 정보와 그에 대한 질문으로 구성되어 텍스트 정보에 대해 질문을 던지고 응답하는 형태다. 

 

bAbI의 경우 기계 모델이 사람이 문제를 풀었을 때보다 더 좋은 점수를 내면서 이미 해결된 부분으로 알려져 있다.

 

<6> 데이터 이해하기

 

4장부터는 본격적으로 캐글 도전 및 모델링을 진행하게 된다. 처음 캐글 문제를 풀기 시작할 때 많은 사람들은 모델을 만들고 훈련 후에 성능을 평가하고, 생각보다 성능이 안 나온다면 모델에 문제가 있다고 판단하고 다른 모델을 사용한다. 이처럼 모델에 문제가 있는 경우도 있지만 우선적으로 해당 문제를 잘 해결하기 위해서는 데이터 이해가 선행되어야 한다. 이러한 과정을 탐색적 데이터 분석(EDA: Exploratory Data Analysis)라 한다. 이러한 과정에서 생각하지 못한 데이터의 여러 패턴이나 잠재적인 문제점 등을 발견할 수 있다.

 

그리고 문제를 해결하기 위한 모델에 문제가 없더라도 데이터마다 적합한 모델이 있는데 해당 모델과 데이터가 잘 맞지 않으면 좋은 결과를 얻을 수 있다. 즉, 아무리 좋은 모델이더라도 데이터와 궁합이 맞지 않는 모델이라면 여러 가지 문제에 직면하게 될 것이다.

 

그렇다면 EDA는 어떻게 진행되는가? 간단하게 얘기하면 정해진 틀 없이 데이터에 대해 최대한 많은 정보를 뽑아내면 된다. 데이터에 대한 정보란 데이터의 평균값, 중앙값, 최솟값, 최대값, 범위, 분포, 이상치(Outlier) 등이 있다. 이러한 값들을 확인하고 히스토그램, 그래프 등의 다양한 방법으로 시각화하면서 데이터에 대한 직관을 얻어야 한다.

 

데이터를 분석할 때는 분석가의 선입견을 철저하게 배제하고 데이터가 보여주는 수치만으로 분석을 진행해야하며, 이러한 데이터 분석 과정은 모델링 과정과 서로 상호 작용하면서 결과적으로 성능에 영향을 주기 때문에 매우 중요한 작업이다.

 

다음 그림은 EDA의 전체적인 흐름도이며, 서로의 결과에 직접적으로 영향을 줄 수 있다는 것을 확인할 수 있다.

 

그림 2. 탐색적 데이터 분석

 

 

간단한 실습을 통해 데이터 분석에 대해서 자세히 알아보자. 실습에 사용한 데이터는 영화 리뷰 데이터로, 리뷰와 그 리뷰에 해당하는 감정(긍정, 부정) 값을 가지고 있다. 

 

import os
import re

import pandas as pd
import tensorflow as tf
from tensorflow.keras import utils

data_set = tf.keras.utils.get_file(
        fname='imdb.tar.gz',
        origin='http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz',
    extract=True)

텐서플로우의 케라스 모듈의 get_file 함수를 통해 IMDB 데이터를 가져온다. 첫 번째 인자인 fname은 다운로드한 파일의 이름을 재지정하는 부분이고, 두 번째 인자인 origin은 데이터가 위치해 있는 URL을 입력하면 해당 URL에서 데이터를 다운로드하게 된다. URL의 마지막 부분은 확장자인데 확장자가 tar.gz라는 것은 압축 파일임을 의미한다. 세 번째 인자인 extract를 통해 다운로드한 압축 파일의 압축 해제 여부를 지정할 수 있다. 현재 코드에서는 True로 되어있어 압축 해제를 시행한다.

 

2장의 앞부분에서 했던 것 처럼, 데이터 분석 과정을 쉽게 하기 위해서는 Pandas로 데이터를 불러오는 것이 좋은데, 현재 IMDB 데이터의 경우, 판다스로 가져올 수 없는 상황이다.

 

압축이 풀린 데이터를 확인하면 다음과 같다.

 

그림 3. IMDB 압축 해제된 데이터

 

등 모든 데이터가 디렉터리 안에 txt 파일 형태로 있어서 판다스의 DataFrame을 만들기 위해서는 변환 작업을 진행해야 한다. 변환 작업에는 두 가지 함수가 필요한데, 하나는 각 파일에서 리뷰 텍스트를 불러오는 함수이며, 다른 하나는 각 리뷰에 해당하는 라벨값을 가져오는 함수다. 우선 첫 번째 함수부터 정의해보자.

 

def directory_data(directory):
    data = {}
    data['review'] = []
    for file_path in os.listdir(directory): # directory의 모든 파일 리스트에서 하나하나 꺼냄
        with open(os.path.join(directory, file_path), 'r', encoding = 'UTF-8') as file: # 디렉토리/파일 형식으로 만들어줌
            data["review"].append(file.read()) # 파일을 읽어서 data['review']에 하나씩 집어 넣어줌.
            
    return pd.DataFrame.from_dict(data) # data를 DataFrame으로 만들어줌.

먼저, data 라는 이름으로 딕셔너리 객체를 만들어주고, data에 'review' 키를 생성하고 거기에 값으로 빈 리스트를 생성한다. (딕셔너리는 key : value 형식이다.)

 

os.listdir(directory) 에서 사용된 os.listdir은 해당 directory에 포함된 파일 리스트를 가져오는 함수이다. 

예를 들어, fruit 라는 폴더에 apple.txt, pineapple.txt, mango.txt가 들어있다면 이 파일 리스트를 가져오는 것이다.

 

for file_path in os.listdir(directory)는 이러한 파일 리스트에서 파일 이름 하나하나를 꺼내는 코드로 생각하면 된다.

 

다음줄의 os.path.join(directory, file_path)는 디렉토리와 파일이름을 이어주는 함수이다.

예를 들어, os.path.join("C:\Temp", "pos") 를 작동시키면 C:\Temp\pos가 되는 것이다. 따라서 디렉토리 안에 있는 파일의 전체 경로를 표현할 수 있게 된다.

 

with open(... , "r") as file: 코드는 ...에 해당하는 경로에 있는 파일을 읽어서 이를 file 이라는 이름의 객체로 저장하는 코드가 된다. with를 사용하면 따로 file.close()를 하지 않아도 되는 장점이 있다. 'r'은 read이며, 파일에 쓰는 경우는 'w'(write)를 사용한다.

 

그 다음줄에 data['review'].append(file.read())는 data['review'] 에 읽어들인 file을 append(추가) 하는것이다. 

 

마지막 pd.DataFrame.from_dict(data)은 data가 딕셔너리 형태였는데 이를 이용해서 DataFrame으로 만드는 것이다.

 

전체 코드를 작동하게되면 대략 이런 형식으로 DataFrame이 나오게 될 것이다.

 

 

그림 4. directory_data 함수 사용 예시

 

 

data라는 이름의 딕셔너리 객체에서 review라는 키를 지정했었는데, 이것이 DataFrame 형태로 변환되었을때는 Column의 이름이 된다.

DataFrame 특성상 모든 자료 앞에는 index가 붙게되므로 위 그림과 같은 형태가 된다.

(리뷰는 임의로 넣었습니다.)

 

다음 함수를 살펴보자.

 

def data(directory):
    pos_df = directory_data(os.path.join(directory, "pos"))
    neg_df = directory_data(os.path.join(directory, "neg"))
    pos_df["sentiment"] = 1
    neg_df["sentiment"] = 0
    
    return pd.concat([pos_df, neg_df])

pos_df 부분의 경우, 앞에서 언급된 os.path.join을 이용하여 directory\pos에 해당하는 데이터들을 DataFrame 형태로 저장하게된다.

 

데이터가 들어있는 폴더를 살펴보면, pos와 neg로 나뉘어져있는데, 이는 label이 긍정인 것과 부정인 것을 나누어 폴더를 구성한 것이다.

 

neg_df도 마찬가지.

 

pos_df["sentiment"] = 1은 pos_df인 데이터에 sentiment라는 column을 추가하고 거기에 1 값을 집어넣는다. (즉 긍정 자료는 감정이 1)

neg_df["sentiment"] = 0은 neg_df인 데이터에 sentiment에 0 값을 집어넣는다.

 

마지막의 pd.concat([pos_df, neg_df])은 pos_df 데이터와 neg_df를 위 아래로 합치는 함수이다. (R을 다뤄보신분이 있다면 R에서 rbind()에 해당한다고 보시면 될듯하다.) 

 

위에서 정의한 data를 적용하면 다음과 같다.

 

그림 5. data 함수 사용 예시

위와 같이 위에는 긍정자료, 아래는 부정자료로 구성된 데이터셋이 완성되게 된다.

 

앞에 설명했던 두 함수를 호출해서 우리가 처음에 받았던 데이터셋을 Pandas의 DataFrame으로 반환받아보자.

 

train_df = data(os.path.join(os.path.dirname(data_set), 'aclImdb', 'train'))
test_df = data(os.path.join(os.path.dirname(data_set), 'aclImdb', 'test'))

만들어낸 DataFrame의 결과를 확인해보자.

 

train_df.head()

 

그림 6. train_df.head() 결과창

위에서 directory_data와 data를 정의할때 대략 설명했던것과 같은 출력이 예상대로 나왔다.

 

이제 이 DataFrame으로부터 리뷰 문장 리스트를 가져와보자.

 

reviews = list(train_df['review'])

reviews에는 각 문장을 리스트 형태로 담고 있다. 이 문장들을 단어 기준으로 토크나이징하고, 문장마다 토크나이징된 단어의 수를 저장하고 그 단어들을 붙여서 알파벳의 전체 개수를 저장해보자.

 

# 공백을 기준으로 쪼개서 토크나이징함. 
tokenized_reviews = [r.split() for r in reviews] 

# 토크나이징된 리스트에 대한 각 길이를 저장
review_len_by_token = [len(t) for t in tokenized_reviews] 

# 토크나이징된 것을 다시 붙여서 음절 길이 저장 
review_len_by_eumjeol = [len(s.replace(' ','')) for s in reviews] 

첫번째인 tokenized_reviews는 reviews를 .split()으로 쪼개는데 이는 .split(' ')와 동일하다. 즉 공백을 기준으로 쪼개서 단어 하나하나를 리스트의 요소로 만들어낸다.

예를 들면, "고양이가 지붕 위를 올라간다." 라는 문장에 .split()를 적용하면 ['고양이가', '지붕', '위를', '올라간다.']로 만들어지게 된다. 

 

두번째인 review_len_by_token은 위에서 토크나이징한 리스트의 요소들(즉 리뷰에 사용된 각각의 단어들)의 길이를 저장한다.

예를 들어서 "고양이가 지붕 위를 올라간다." 를 기준으로 적용하면 [4, 2, 2, 4]가 될 것이다.

 

세번째인 review_len_by_eumjeol은 reviews에 있는 공백을 ''로 대체한 후, 이것의 길이를 측정하게 되는데, 예를 들면 "고양이가 지붕 위를 올라간다."에 s.replace(' ','')를 적용하면 "고양이가지붕위를올라간다."가 되고 이것의 길이(len)를 측정해서 리스트에 저장하는 것이다.

 

위와 같이 만드는 이유는 문장에 포함된 단어와 알파벳의 개수에 대한 데이터 분석을 수월하게 만들기 위해서다.

 

데이터 분석을 위한 사전 준비 작업이 완료됐으니 실제로 데이터 분석을 진행해본다.

 

import matplotlib.pyplot as plt

plt.figure(figsize = (12, 5))

plt.hist(review_len_by_token, bins = 50, alpha = 0.5, color = 'r', label = 'word')
plt.hist(review_len_by_eumjeol, bins = 50, alpha = 0.5, color = 'b', label = 'alphabet')
plt.yscale('log', nonposy= 'clip') # nonposy = 'clip' => non-positive 값을 아주 작은 값으로 자름.

plt.title("Review Length Histogram")

plt.xlabel("Review Length")

plt.ylabel("Number of Reviews")
plt.legend()

figsize는 (가로, 세로) 형태의 튜플로 입력하며, 입력한 사이즈로 그림을 만들게 된다.

 

plt.hist는 히스토그램을 그리게 되며, bins는 출력할때 히스토그램의 칸 개수를 말한다. alpha는 그래프 색상 투명도이며, color는 색을 의미한다. 'r'은 red, 'b'는 blue다. label은 각각 그래프에 라벨을 다는 것이다.

 

plt.yscale은 y 좌표의 스케일을 지정하는것으로, log 스케일로 지정되어 있으며, nonposy = 'clip'으로 되어 있는 경우 non-positive 값을 아주 작은 값으로 자른다.

 

plt.legend()는 책에는 없었으나 그림 자체를 더 잘 표현하기 위해 내가 추가하였다. legend는 범례를 의미하며, 이를 출력하면 그래프를 보기가 더 수월하기 때문에 추가하였다.

 

그림 7. 히스토그램 

범례는 1시 방향에 띄워져 각각의 그래프가 어떤 의미를 띄는지를 보여준다.

 

빨간색 히스토그램인 word는 리뷰 하나당 단어가 몇개 있는지를 보여주는 것이고

 

파란색 히스토그램인 alphabet은 리뷰 하나당 알파벳 개수가 몇개 있는지를 보여주는 히스토그램이다.

 

 

 

다음은 데이터 분포에 대한 통계치를 알아본다.

 

import numpy as np

print("문장 최대 길이: {}".format(np.max(review_len_by_token)))
print("문장 최소 길이: {}".format(np.min(review_len_by_token)))
print("문장 평균 길이: {:.2f}".format(np.mean(review_len_by_token)))
print("문장 길이 표준편차: {:.2f}".format(np.std(review_len_by_token)))
print("문장 중간 길이: {}".format(np.median(review_len_by_token)))

print("제1사분위 길이: {}".format(np.percentile(review_len_by_token, 25)))
print("제3사분위 길이: {}".format(np.percentile(review_len_by_token, 75)))

그림 8. 위 코드 결과창

 

문장 최대 길이, 최소 길이, 평균 길이, 길이의 표준편차, 중간 길이, 1 / 3사분위 길이 등을 확인할 수 있는 코드이다.

 

사분위수의 경우 0 ~ 100 스케일로 되어 있다.

 

 

 

다음으로는 박스 플롯을 이용하여 데이터를 시각화해본다. 박스 플롯은 직관적인 시각화를 제공하는 특징이 있다.

 

plt.figure(figsize = (12, 5))
plt.boxplot([review_len_by_token], labels = ['token'], showmeans = True)

plt.boxplot은 박스 플롯을 만들어내는 함수이며, showmeans = True로 지정 시 평균값을 표현해주게 된다.

 

그림 9. 박스플롯

그려진 박스플롯에 대한 상세한 설명은 다음과 같다.

 

그림 10. 박스플롯 설명

박스플롯을 확인해 보면 전체적인 데이터 분포를 확인할 수 있으며, 특히 이상치가 심한 데이터를 확인할 수 있다는 장점이 있다.

 

이상치가 심하게 되면 데이터의 범위가 너무 넓어 학습이 효율적으로 이루어지지 않기 때문에 이를 확인하는 것이 중요하다.

 

다음으로는 워드 클라우드를 이용하여 데이터를 시각해보자.

 

from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
%matplotlib inline

wordcloud = WordCloud(stopwords = STOPWORDS, background_color = 'black', width = 800,
                     height = 600).generate(' '.join(train_df['review']))

plt.figure(figsize = (15, 10))
plt.imshow(wordcloud) # 그림 그리기
plt.axis("off") # 축 끄기
plt.show()

stopword 는 한국어로 불용어 라고 말하며, 자주 등장하지만 분석을 하는 것에 있어서는 큰 도움이 되지 않는 단어들을 말합니다. 예를 들면, I, my, me, over, 조사, 접미사 같은 단어들은 문장에서는 자주 등장하지만 실제 의미 분석을 하는데는 거의 기여하는 바가 없습니다. 

 

wordcloud 안에 STOPWORDS로 이미 불용어들이 저장되어 있으며, 이를 이용하여 불용어를 제외한 나머지 단어들만 워드 클라우드에 표현하게 된다.

 

' '.join(train_df['review'])는 train_df에 저장되어 있는 리뷰들을 ' '만큼 띄워서 연결하는 코드라고 생각하면 될거같다.

예를 들어, ' '.join('apple', 'bear') 를 적용하면 'apple bear'가 되는 것이다.

 

plt.imshow()는 그림을 그리는 함수이며, plt.show()는 실제로 그림을 나타내는 함수라고 보면 된다.

plt.axis()는 축을 설정하는 함수인데, 이 경우는 "off"로 지정하여 축을 따로 나타내지 않는것으로 보인다.

 

실제 코드를 실행한 결과 다음과 같은 워드 클라우드를 볼 수 있다.

 

 

그림 11. 워드클라우드 결과

 

워드클라우드에서 특히 br이 좀 많은데, 이는 HTML 태그를 아직 전처리 하지 않았기 때문에 그런 것으로 보인다.

 

마지막으로는 긍정, 부정의 분포를 확인해본다.

 

import seaborn as sns
import matplotlib.pyplot as plt

sentiment = train_df['sentiment'].value_counts()
fig, axe = plt.subplots(ncols = 1)
fig.set_size_inches(6, 3)
sns.countplot(train_df['sentiment'])

train_df['sentiment'].value_counts()를 적용하면 train_df['sentiment']에 들어있는 값들을 종류별로 정리해 표로 나타내준다.

예를 들어서, 자료에 긍정이 100개, 부정이 200개 있으면 긍정 100 부정 200 이런식으로 표에 나타나는 것이다.

 

다음줄에 사용된 plt.subplots()의 경우 한번에 여러 그래프를 그릴때 설정하기가 쉬워서 사용되는 코드이다.

이때 등장하는 fig는 전체 plot을 의미하며, subplot이 몇개가 그려지던지 상관없이 그것을 담는 전체 그림을 의미한다.

axe는 전체 그림 중에 낱개 plot 하나하나를 의미한다. 

여기에는 nrows와 ncols 라는 인자들을 설정할 수 있는데, 이를 설정해 subplot을 몇개나 그릴지 지정할 수 있게 된다.

 

마지막줄의 sns.countplot(train_df['sentiment])의 경우, 각 category 별로 data가 얼마나 있는지 표시할 수 있는 plot을 그리는 코드가 된다. 특히 이 코드는 자료가 DataFrame 일때만 사용가능하다는점에 유의하여야 한다.

 

위 코드를 실행시키면 다음과 같은 그림을 얻을 수 있다.

 

그림 12. 긍정 부정의 분포 결과

sentiment가 0인 것과 1인 것이 몇개씩 있는지를 나타내주는 그래프를 얻을 수 있었다.

 

둘다 개수는 12500개 이며, 긍정 부정 데이터의 균형이 아주 좋은 경우라고 볼 수 있다.

 

 

 

지금까지의 내용이 Chapter 3의 마지막 부분까지 다룬 내용이 된다.

 

이론적인 부분들은 어느정도 잘라내어 생략하였고, 코드가 있는 부분은 최대한 한줄 한줄 풀어서 설명하였다.

 

책에서는 한줄 한줄 세세하게 설명해주지 않다보니, 나 또한 코드에 사용된 함수들을 하나하나 찾아보면서 공부할 수 있는 기회가 되었고

 

혹시나 이 책을 공부하시다가 모르는 코드가 있어서 검색을 해 나의 블로그에 누군가가 들어와서 확인해주신다면 이 또한 글을 쓴 보람이 생길 것 같다. 

 

다음 글에서는 본격적으로 Kaggle 데이터를 통해 텍스트 분류를 진행해보려고 한다.

 

마지막으로 체크해보아야 할 것들을 정리해보자.

 

 

첫번째: 자연어 생성이란 무엇이고, 자연어 생성 기술을 활용하여 성공한 분야는 무엇인가?

 

두번째: 기계 이해란 무엇이고, 보통 어떤 형태의 데이터셋을 이용하여 학습하는가?

 

 

 

앞으로도 코드에 관련된 부분은 뭐 외우거나 하기는 어려울듯하여 따로 문제로 정리하지는 않을 예정이다.

 

한줄 한줄 최대한 이해해가면서 코드를 보는 것 그 자체가 큰 도움이 되는듯하여.... (물론 외우는게 어느정도 필요할 것 같지만 아직 나도 초보라서 뭘 외워야하는지 모름...)

 

 

 

 

 

 

 

 

 

 

저번 글에서는 단어 표현이 무엇인지에 대한 내용을 다루었고, 또 CBOW가 무엇인지에 대해서 조금 더 상세하게 설명을 했다.

 

이번 글에서는 그 뒤를 이어 CBOW의 정반대 메커니즘을 가지고 있는 Skip-Gram에 대해서 얘기해보고 그 뒤 내용들을 다루어보도록 하겠다.

 

Skip-Gram은 다음과 같은 과정을 통해 학습이 진행된다.

 

1. 하나의 단어를 원-핫 벡터로 만들어서 입력값으로 사용한다.

2. 가중치 행렬을 원-핫 벡터에 곱해서 N-차원 벡터를 만든다.

3. N-차원 벡터에 다시 가중치 행렬을 곱해서 원-핫 벡터와 같은 차원의 벡터로 만든다.

4. 만들어진 벡터를 실제 예측하려는 주변 단어들 각각의 원-핫 벡터와 비교해서 학습한다.

 

물론 CBOW와 거의 대부분이 똑같기 때문에, 전체적인 구조를 설명하는 그림만 추가해도 충분히 이해할 수 있을 듯하다.

(혹시나 CBOW에 대한 설명을 아직 안 보셨다면, 이전 글을 보고 오시면 충분히 이해가 되실 것 같습니다.)

(예시도 CBOW에서 사용했던 그대로를 사용합니다. 즉 예시 문장은 지금 너무 추워서 패딩 꼭 입어야 해 입니다.)

 

그림 1. Skip-Gram의 전체적인 구조도

Skip-Gram의 전체적인 구조도를 그린 그림이다. CBOW와 마찬가지로 가중치 행렬곱을 M과 M'으로 총 두 번 작업하게 된다. 다른 점이라고 한다면 CBOW의 경우 입력값이 여러 개였으므로, 입력값을 받고 나서 평균을 취해줘서 평균 N-차원 은닉층을 만든 반면, Skip-Gram의 경우는 입력값이 한 개이므로 평균을 취해주지 않아도 된다.

 

 

이러한 Word2Vec의 두 모델은 여러 가지 장점이 있는데,

 

 

첫 번째로는 기존의 카운트 기반 방법으로 만든 단어 벡터보다 단어 간의 유사도를 잘 측정한다는 점이다.

 

 

두 번째로는 단어들의 복잡한 특징까지도 잘 잡아낸다는 점이고

 

 

세 번째로는 Word2Vec을 이용해서 만들어낸 단어 벡터는 서로에게 유의미한 관계를 측정할 수 있다는 점이다. 예를 들어, 4개의 단어(엄마, 아빠, 남자, 여자)를 word2vec 방식을 사용해 단어 벡터로 만들었다고 한다면, '엄마'와 '아빠'라는 단어 벡터 사이의 cosine 거리와 '여자'와 '남자'라는 단어 벡터 사이의 cosine 거리가 같게 나온다.

 

 

Word2Vec의 CBOW와 Skip-Gram 모델 중에서는 보통 Skip-Gram이 성능이 더 좋아 일반적인 경우 Skip-Gram을 사용하는 것으로 알려져 있으나, 절대적으로 항상 좋은 것은 아니므로 두 가지 모두 고려할 만하다.

 

 

이처럼 단어 벡터를 표현하는데 카운트 기반 방법과 예측 기반 방법을 사용하는데, 보통의 경우 예측 기반 방법의 성능이 더 좋아 주로 예측 기반 방법을 사용한다. 그리고 두 가지 방법을 모두 포함하는 "Glove"라는 단어 표현 방법 또한 사용된다. (Glove도 대략 찾아보았는데 생각보다 너무 난해해 일단은 포기하고 나중을 기약한다...)

 

 

단어 표현은 자연어 처리 문제를 해결하는 기반이 되는 가장 근본적인 내용이므로 정확히 이해하는 것이 중요하며, 항상 가장 좋은 성능을 내는 유일한 방법이 있는 것이 아니라서 각 방법 간에 어떤 차이점이 있는지 항상 염두에 두고 상황에 맞게 사용하는 것이 중요하다.

 

<2> 텍스트 분류

 

텍스트 분류(Text Classification)는 자연어 처리 문제 중 가장 대표적이고 많이 접하는 문제다. 자연어 처리 기술을 활용해 특정 텍스트를 사람들이 정한 몇 가지 범주(Class) 중 어느 범주에 속하는지 분류하는 문제다. 분류해야 하는 범주의 수에 따라 문제를 구분하기도 하는데, 보통 2가지 범주에 대해 구분하는 문제는 이진 분류(Binary Classification) 문제라고 하고, 3개 이상의 범주에 대해 분류하는 문제는 다중 범주 분류(Multi Class Classification) 문제라고 한다. 다음 예시를 이용해 텍스트 분류에 대해 좀 더 알아보자.

 

텍스트 분류의 예시

 

- 스팸 분류

 

메일 중에서 일반 메일과 스팸 메일을 분류하는 문제가 스팸 분류 문제이다. 현재 이메일 서비스를 하고 있는 포털들은 이 기술들이 잘 적용되고 있어서, 스팸 메일은 알아서 잘 걸러내고 있다. 이 문제에서 분류해야 할 범주(Class)는 스팸 메일과 일반 메일로 두 가지이다.

 

 

- 감정 분류

 

감정 분류란 주어진 글에 대해서 이 글이 긍정적인지 부정적인지 판단하는 문제이다. 이 경우 범주는 긍정 혹은 부정이 된다. 경우에 따라서 범주는 긍정 혹은 부정에 한정되는 것이 아닌 중립의 범주가 추가될 수 있으며, 긍정 혹은 부정의 경우에도 정도에 따라 범주를 세분화하기도 한다. 즉 문제에서 분류하려는 의도에 따라 범주가 정해지는 것이다. 가장 대표적인 감정 분류 문제는 영화 리뷰에 대해 각 리뷰가 긍정적인지 부정적인지 판단하는 문제인데, 4장에서 자세히 설명할 예정이다.

 

 

- 뉴스 기사 분류

 

매일매일 뉴스가 계속해서 쏟아지는데, 이를 분류하지 않는다면 내가 원하는 뉴스를 선택해서 보는 것이 어려울 것이다. 따라서 뉴스 업체들은 사용자들이 원하는 뉴스를 선택해서 볼 수 있게끔 범주를 잘 구분 지어 분류해둬야 한다. 예를 들면 스포츠, 연예, 문화, 경제, 사회 등등이 있을 것이다. 여기에 자연어 처리 기술을 사용한다면 뉴스 기사를 더욱 효율적으로 분류할 수 있다.

 

 

 

텍스트 분류의 예시에 3가지 정도를 알아보았는데, 이외에도 다양한 문제들이 존재한다. 분류하는 단위를 글 전체로 하지 않고 각 단어를 한 단위로 분류하는 문제도 있다. 예를 들면 품사 태깅(POS tagging) 문제는 각 단어를 기준으로 어떤 품사를 가지는 것인지 분류해야 하는 문제이다.

 

 

그렇다면 텍스트 분류 문제는 어떻게 해결해야 하는가? 큰 기준으로 본다면 지도 학습 혹은 비지도 학습을 통해 해결할 수 있을 것이다.

 

지도 학습을 통한 텍스트 분류

 

지도학습을 통해 문장 분류를 하는 전체적인 구조는 다음 그림과 같다.

 

그림 2. 지도학습을 통한 문장 분류 과정

지도 학습의 경우 데이터에 대한 라벨이 주어져 있으므로, 데이터를 통해 학습 후 원하는 데이터에 대한 라벨을 구하면 된다.

 

 

지도 학습을 통한 문장 분류 모델에는 다양한 종류가 있고, 그중에 대표적으로 사용되는 지도 학습의 예는 다음과 같다.

 

- 나이브 베이즈 분류(Naive Bayes Classifier)

- 서포트 벡터 머신(Support Vector Machine)

- 신경망(Neural Network)

- 선형 분류(Linear Classifier)

- 로지스틱 분류(Logistic Classifier)

- 랜덤 포레스트(Random Forest)

 

 

비지도 학습을 통한 텍스트 분류

 

비지도 학습의 경우는 라벨이 없기 때문에, 특성을 찾아내서 적당한 범주를 만들어서 각 데이터를 나누면 된다.

 

어떤 특정한 분류가 있는 것이 아니라, 데이터의 특성에 따라 비슷한 데이터끼리 묶어주는 개념이다.

 

비지도 학습을 통한 텍스트 분류는 텍스트 군집화라고도 불리며, 대표적인 비지도 학습 모델은 다음과 같다.

 

- K-평균 군집화(K-means Clustering)

- 계층적 군집화(Hierarchical Clustering)

 

지도 학습과 비지도학습 중 어떤 방법을 사용할지 결정하는데 기여하는 가장 큰 기준은 데이터에 정답 라벨이 있느냐 없느냐이다. 정답 라벨이 있는 경우 지도학습 방법으로 문제를 해결하면 되고, 정답 라벨이 없는 경우 비지도 학습을 사용해서 문제를 해결하면 된다. 그리고 일반적인 분류의 경우에는 지도 학습을 사용하지만, 정확한 범주가 없고 단순히 군집화만 할 경우에는 비지도 학습을 통해 데이터를 군집화하면 된다.

 

<3> 텍스트 유사도

 

인공지능 스피커에게 다음과 같은 질문을 한다고 생각해보자.

 

  • 이 노래 누가 만들었어?
  • 지금 나오는 노래의 작곡가가 누구야?

 

위 두 문장은 똑같은 의미이지만 인공지능 스피커에게는 다른 문장으로 인식될 것이다. 따라서 각기 다른 대답을 만들어야 하는데, 좀 더 효율성을 위해 비슷한 의미를 가진 문장에 대해서는 같은 대답을 준비할 수 있을 것이다. 이때 문장이 유사한지 측정해야 하며, 텍스트 유사도(Text Similarity) 측정 방법을 사용하면 된다.

 

 

텍스트 유사도란, 말 그대로 텍스트가 얼마나 유사한지를 표현하는 방식 중 하나다. 앞에서 예로 든 두 문장의 경우 다른 구조의 문장이지만 의미는 비슷하므로 두 문장의 유사도는 높다고 판단할 수 있다. 물론, 유사도를 판단하는 척도는 매우 주관적이므로 데이터를 구성하기가 쉽지가 않으며 정량화하는데 한계가 있다.

 

 

일반적으로 유사도를 측정하기 위해 정량화하는 방법에는 여러 가지가 있다. 단순히 같은 단어의 개수를 사용해서 유사도를 판단하는 방법, 형태소를 나누어 형태소를 비교하는 방법, 자소 단위로 나누어 단어를 비교하는 방법 등 다양한 방법이 있다.

 

 

이 책에서는 그중에서도 딥러닝을 기반으로 텍스트의 유사도를 측정하는 방식을 주로 다룰 것이다. 단어, 형태소, 유사도의 종류에 상관없이, 텍스트를 벡터화한 후 벡터화된 각 문장 간의 유사도를 측정하는 방식이다. 그리고 자주 쓰이는 4개의 유사도 측정 방법에 대해 알아볼 것이다. 

 

 

우선 유사도를 측정해보기 전에 다음의 두 가지 예시 문장을 보자.

 

  • 휴일인 오늘도 서쪽을 중심으로 폭염이 이어졌는데요, 내일은 반가운 비 소식이 있습니다.
  • 폭염을 피해서 휴일에 놀러 왔다가 갑작스런 비로 인해 망연자실하고 있습니다.

 

이제 이 두 문장에 대해 각 유사도 측정 방법으로 유사도를 측정해 보겠다. 우선은 유사도 측정을 하기 전에 단어를 벡터화한다. 여기서는 TF-IDF를 통해 벡터화한다.

 

 

- 복습 -

TF-IDF를 이용하기 전에, 이전에 다루었던 내용을 잠시 복습하고 가고자 한다.

 

sklearn에서 제공하는 특징 추출 모듈을 사용할 것인데, 자연어 처리에서 특징 추출이란 텍스트 데이터에서 단어나 문장들을 어떤 특징 값으로 바꿔주는 것을 의미한다. 기존에 문자로 구성되어 있던 데이터를 모델에 적용할 수 있도록 특징을 뽑아 어떤 값으로 바꿔서 수치화하는 것이다. 

 

그중에서 TF-IDF이라는 값을 사용해 특징 추출을 하는 TfidfVectorizer를 사용한다.

 

TF는 Term Frequency의 약자로, 특정 단어가 하나의 데이터 안에서 등장하는 횟수를 의미한다.

 

DF는 Document Frequency의 약자로, 특정 단어가 여러 데이터에 자주 등장하는지를 알려주는 지표이다.

 

IDF는 Inverse Document Frequency의 약자로, DF의 역수를 취해 구할 수 있다.

 

마지막으로 TF-IDF는 TF 값과 IDF 값을 곱해서 구하게 된다. 특정 단어가 하나의 데이터 안에서는 자주 등장하고, 여러 데이터에서는 자주 등장하지 않을수록 TF-IDF의 값은 높아지게 된다.

 

 

from sklearn.feature_extraction.text import TfidfVectorizer

sent = ("휴일 인 오늘 도 서쪽 을 중심 으로 폭염 이 이어졌는데요, 내일 은 반가운 비 소식 이 있습니다.", 
        "폭염 을 피해서 휴일 에 놀러왔다가 갑작스런 비 로 인해 망연자실 하고 있습니다.")
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(sent) # 문장 벡터화 진행

print(tfidf_matrix)

print(tfidf_vectorizer.vocabulary_)

idf = tfidf_vectorizer.idf_
print(dict(zip(tfidf_vectorizer.get_feature_names(), idf)))

결과창

먼저 처음 출력한 tfidf_matrix는 각각의 단어를 TF-IDF 값을 이용하여 특징 추출한 matrix를 나타낸다.

 

 

(1, 0) 0.34287125... 이런 식으로 출력되는데, 여기서 (1, 0) 중에 앞부분인 1은 문장의 index를 의미하고(즉 index가 1인, 두 번째 문장이라는 의미), 뒷부분인 0은 단어 중에서 0에 해당하는 단어라는 말이다.(즉 이 경우 (1, 0)에 해당하는 것은 '갑작스런' 임.)

 

 

※ tfidf_matrix에 대한 자세한 설명이 책에는 없었기 때문에, https://stackoverflow.com/questions/42002859/creating-a-tf-idf-matrix-python-3-6 stackoverflow를 참고하였다.

 

 

해당 숫자에 어떤 단어가 매칭 되고 있는지를 알아보기 위해서는 tfidf_vectorizer.vocabulary_를 출력하면 확인할 수 있다. 이를 출력하면 단어와 그에 해당하는 숫자가 dictionary 형태로 출력된다.

 

 

마지막으로 출력된 내용은 각 단어들의 idf 값을 출력한 것이다. (책에서 있어서 출력한 것이지만, 사실 이게 왜 갑자기 필요한지는 모르겠음.)

 

 

 

1. 자카드 유사도

 

자카드 유사도(Jaccard Similarity), 또는 자카드 지수는 두 문장을 각각 단어의 집합으로 만든 뒤 두 집합을 통해 유사도를 측정하는 방식 중 하나다. 유사도를 측정하는 방법은 두 집합의 교집합인 공통된 단어의 개수를 두 집합의 합집합, 즉 전체 단어의 수로 나누면 된다. 결괏값은 공통의 원소의 개수에 따라 0과 1 사이의 값이 나올 것이고, 1에 가까울수록 유사도가 높다는 의미이다.

 

자카드 유사도

 

앞에서 제시한 두 예시 문장을 통해 자카드 유사도를 측정해보자. 유사도를 측정할 때 단어에서 조사는 따로 구분해서 사용하므로 두 문장 A, B는 다음과 같이 정의될 것이다.

 

A = {휴일, 인, 오늘, 도, 서쪽, 을, 중심,으로, 폭염, 이, 이어졌는데요, 내일, 은, 반가운, 비, 소식, 있습니다.

B = {폭염, 을, 피해서, 휴일, 에, 놀러 왔다가, 갑작스런, 비, 로, 인해, 망연자실, 하고, 있습니다.}

 

두 집합을 벤 다이어그램으로 그리면 다음과 같다.

 

벤 다이어그램

자카드 공식에 위의 예제를 대입해 본다면, 두 문장의 교집합은 {휴일, 폭염, 비, 을, 있습니다}로 5개, A-B는 12개, B-A는 8개로 두 문장의 합집합은 25개가 되어 자카드 유사도는 5/25 = 0.2이다.

 

2. 코사인 유사도

 

코사인 유사도는 두 개의 벡터 값에서 코사인 각도를 구하는 방법이다. 코사인 유사도 값은 -1과 1 사이의 값을 가지고 1에 가까울수록 유사하다는 것을 의미한다. 코사인 유사도는 유사도를 계산할 때 가장 널리 쓰이는 방법 중 하나이다. 다른 유사도 접근법에 비해 일반적으로 성능이 좋은데, 이는 단순히 좌표 상의 거리를 구하는 다른 유사도 측정 방법에 비해 코사인 유사도는 말 그대로 두 벡터 간의 각도를 구하는 것이기 때문에 방향성의 개념이 더해지기 때문이다. 두 문장이 유사하다면 같은 방향을 가리킬 것이고, 유사하지 않을수록 직교로 표현될 것이다.

 

 

코사인 유사도

앞서 TF-IDF로 벡터화한 문장을 사용해 코사인 유사도를 구해볼 수 있다. 코사인 유사도의 경우 직접 함수를 구현할 필요 없이 sklearn에서 유사도 측정을 위한 함수를 제공한다.

 

from sklearn.metrics.pairwise import cosine_similarity

cosine_similarity(tfidf_matrix[0], tfidf_matrix[1]) # 첫 번째와 두 번째 문장 비교

코사인 유사도에서 문장 A와 문장 B의 유사도는 대략 18% 정도로 산출된다.

 

3. 유클리디언 유사도

 

유클리디언 유사도는 가장 기본적인 거리를 측정하는 유사도 공식이며, 공식은 다음과 같다.

 

유클리디언 유사도

여기서 구하는 거리는 유클리디언 거리(Euclidean Distance) 혹은 L2거리(L2-Distance)라고 불리며, n차원 공간에서 두 점 사이의 최단 거리를 구하는 접근법이다. 

 

python 코드를 이용해서 유클리디언 유사도를 구해보자. sklearn에서 제공하는 유클리디언 거리 측정 함수를 사용하면 된다.

 

from sklearn.metrics.pairwise import euclidean_distances

euclidean_distances(tfidf_matrix[0], tfidf_matrix[1])

유클리디언 거리는 단순히 두 점 사이의 거리를 뜻하기 때문에, 이전에 소개했던 유사도 방식들이 0과 1 사이의 값을 가지는 것과 달리 유클리디언 거리는 1 이상의 값을 가질 수 있다. 

 

이러한 제한이 없는 유사도 값은 사용하기가 어려우므로 값을 제한하는 방법을 취한다. 다른 유사도 측정 방식과 동일하게 0과 1 사이의 값을 갖도록 만들어줄 것인데, 이는 벡터화된 각 문장들을 일반화(Normalize) 한 후 다시 유클리디언 유사도를 측정하면 0과 1 사이의 값을 갖게 된다. 여기서는 L1 정규화 방법(L1-Normalization)을 사용하도록 한다. L1 정규화 방법에 대해서 간단히 설명하자면 각 벡터 안의 요소 값을 모두 더한 것이 크기가 1이 되도록 벡터들의 크기를 조절하는 방법이다.

 

import numpy as np

def l1_normalize(v):
    norm = np.sum(v)
    return v / norm

tfidf_norm_l1 = l1_normalize(tfidf_matrix)
euclidean_distances(tfidf_norm_l1[0], tfidf_norm_l1[1])

정규화를 거친 후 유클리디언 유사도를 측정한 결과는 0.205 정도가 나온다. 유클리디언 유사도를 측정할 때는 편의를 위해 정규화한 후 측정하는 방법이 있다는 점을 기억하도록 하자.

 

4. 맨하탄 유사도

 

맨하탄 유사도(Manhattan Similarity)는 맨하탄 거리를 통해 유사도를 측정하는 방법이다. 맨하탄 거리란, 사각형 격자로 이뤄진 지도에서 출발점에서 도착점까지를 가로지르지 않고 갈 수 있는 최단거리를 구하는 공식이다. 유클리디언 거리를 L2 거리로 부르는 반면, 맨하탄 거리는 L1 거리로 부른다.

 

맨하탄 거리 공식

 

맨하탄 유사도 또한 유클리디언 유사도와 마찬가지로 거리를 통해 유사도를 측정하는 방법이라 값이 계속해서 커질 수 있다. 따라서 0과 1 사이의 값을 갖도록 L1 정규화 방법을 사용해 벡터 안의 요소 값을 정규화한 뒤 유사도를 측정한다.

 

from sklearn.metrics.pairwise import manhattan_distances

manhattan_distances(tfidf_norm_l1[0], tfidf_norm_l1[1])

위에서 사용한 네 가지 유사도 방법 중, 맨해튼 유사도로 측정했을 때가 가장 높게 나왔다. 측정 방법에 따라 같은 것을 재더라도 유사도가 크게 달라질 수 있으므로 의도하고자 하는 방향에 맞는 유사도 측정 방법을 고르는 것이 매우 중요하다.

 

 

 

 

이번에도 생각보다 글이 많이 길어진듯 하다.

 

여기서 글을 마무리 하고, 그 다음으로 이어가려고 한다.

 

이번 글에서도 역시 많은 것들을 다루었기 때문에, 이번 글에서 다룬 내용들을 바탕으로 정리할 수 있는 문제들을 준비했다.

 

- 점검할 내용 -

 

첫번째, Skip-Gram의 전체적인 구조에 대해서 설명하라.

 

 

두번째, Word2Vec이 가진 장점 세 가지는 무엇인가?

 

 

세번째, TF-IDF란 무엇이며, TF-IDF가 높을수록 어떤 의미를 갖고 있는가?

 

 

네번째, 텍스트 유사도를 나타내는데 사용되는 네 가지의 유사도는 무엇이며, 각각을 설명하라.

 

(티스토리 임시저장 기능 믿고 임시저장 했다가 글이 전부 날라가서 전부 다시 다 쓰고 있습니다.... 혹시나 티스토리를 이용하려는 분이 있다면 반드시 비공개로라도 글을 작성해두시는걸 추천합니다...)

 

이번 Chapter 3에서는 자연어 처리에 대한 전반적인 내용에 대해 알아본다.

 

이번 장에서는 총 4개의 문제에 대해서 알아볼탠데, 4개의 문제란 (1) 텍스트 분류, (2) 텍스트 유사도, (3) 텍스트 생성, (4) 기계이해로 자연어 처리의 핵심 문제에 해당한다. 여기서는 자연어 처리를 통해 어떤 문제를 해결할 수 있고, 어떤 방식으로 해결하는지 알아보려고 한다.

 

앞서 소개한 4가지 문제에 대해서 알아보기 전에, 단어 표현 이라는 분야에 대해서 먼저 알아본다. 단어 표현은 모든 자연어 처리 문제의 기본 바탕이 되는 개념으로, 자연어를 어떻게 표현할지 정하는 것이다.

 

<1> 단어 표현

 

자연어 처리는 컴퓨터가 인간의 언어를 이해하고 분석 가능한 모든 분야를 말한다. 따라서 자연어 처리의 가장 기본적인 문제는 '어떻게 자연어를 컴퓨터에게 인식시킬 수 있을까?' 이다.

 

기본적으로 컴퓨터가 텍스트를 인식하는 방법은 텍스트를 '유니코드' 혹은 '아스키 코드' 라는 방법을 이용해서 인식한다.

 

예를 들면 언 이라는 단어가 있다면, 이를 이진화된 값(예를 들어 11010101010) 으로 인식하는 것이다.

 

하지만 자연어 처리에 이 방식을 그대로 사용하기에는 언어적인 특성을 반영하지 않는다는 점에서 부적절하다.

 

그럼 어떤 방식으로 텍스트를 표현해야 자연어 처리 모델에 적용할 수 있는가?

 

이러한 질문의 답을 찾는 것이 단어 표현(Word Representation) 분야 이다. 텍스트를 자연어 처리를 위한 모델에 적용할 수 있게 언어적인 특성을 반영해서 단어를 수치화하는 방법을 찾는 것이다. 그리고 이렇게 단어를 수치화할 때는 단어를 주로 벡터로 표현한다. 따라서 단어 표현을 단어 임베딩(Word Embedding) 혹은 단어 벡터(Word Vector)라고 표현하기도 한다. 단어 표현에는 다양한 방법이 있고 계속해서 연구되고 있는 분야이기 때문에 하나의 정답이 있는 것은 아니지만 이 책에서는 많이 사용하는 방법 위주로 하나씩 알아본다.

 

단어를 표현하는 가장 기본적인 방법은 원-핫 인코딩(one-hot encoding) 방식이다. 단어를 하나의 벡터로 표현하는 방법인데, 각 값은 0과 1만 갖는다. 예를 들어서 4개의 단어(사과, 딸기, 개, 고양이)가 있다면 사과는 [1, 0, 0, 0], 딸기는 [0, 1 , 0, 0]과 같은 방식으로 표현하는 것이다. 방법 자체가 매우 간단하고 이해하기가 쉬운 장점이 존재한다.

 

하지만, 원-핫 인코딩 방식이 가지고 있는 결정적인 두 가지 문제점이 있는데,

 

첫번째, 실제 자연어 처리 문제를 해결할 때에는 수십만, 수백만 개가 넘는 단어를 표현해야 하므로 단어 벡터의 크기가 너무 커져 공간을 많이 사용하게 되고, 큰 공간에 비해 실제로 사용하는 값은 1이 되는 값 하나뿐이므로 매우 비효율적이다.

 

두번째, 원-핫 인코딩 방식은 단순히 단어가 무엇인지만 알려줄 수 있고, 벡터값 자체에는 단어의 의미나 특성 같은 것들이 전혀 표현되지 않는다는 점이다.

 

위에서 언급된 두가지 원-핫 인코딩 방식의 문제점을 해결하기 위해서 다른 인코딩 방법들이 제안되었다. 즉, 벡터의 크기가 작으면서도 벡터가 단어의 의미를 표현할 수 있는 방법들인데, 이러한 방법들은 분포 가설(Distributed hypothesis)을 기반으로 한다. 분포 가설이란, "같은 문맥의 단어, 즉 비슷한 위치에 나오는 단어는 비슷한 의미를 가진다"라는 개념이다.

 

 

따라서 어떤 글에서 비슷한 위치에 존재하는 단어는 단어 간의 유사도가 높다고 판단하는 방법인데, 크게 두 가지 방법으로 나뉜다.

 

첫번째, 특정 문맥 안에서 단어들이 동시에 등장하는 횟수를 직접 세는 방법인 카운트 기반(count-base)

 

카운트 기반 방법으로 단어를 표현한다는 것은 어떤 글의 문맥 안에 단어가 동시에 등장하는 횟수를 세는 방법이다. 여기서 동시에 등장하는 횟수를 동시 출현 또는 공기라고 부르고 영어로는 Co-occurrence라고 한다. 카운트 기반 방법에서는 기본적으로 동시 등장 횟수를 하나의 행렬로 나타낸 뒤, 그 행렬을 수치화해서 단어 벡터로 만드는 방법을 사용하는 방식인데 다음과 같은 방법들이 있다.

 

- 특이값 분해(Singular Value Decomposition, SVD)

- 잠재의미분석(Latent Semantic Analysis, LSA)

- Hyperspace Analogue to Langauge(HAL)

- Hellinger PCA(Principal Component Analysis)

 

위의 네 가지 방법은 모두 동시 출현 행렬(Co-occureence matrix)를 만들고 그 행렬들을 변형하는 방식인데, 이 책에서는 동시 출현 행렬까지만 만들어 보고 행렬을 통해 다시 단어 벡터로 만드는 방법에 대해서는 다루지 않는다. (따로 찾아봐야 한다는 얘기인듯하다.)

 

우선 다음 예시를 가지고 동시 출현 행렬을 만들어 보자.

 

- 성진과 창욱은 야구장에 갔다.

- 성진과 태균은 도서관에 갔다.

- 성진과 창욱은 공부를 좋아한다.

 

위의 3개의 문장을 가지고 동시 출현 행렬을 만들어본다면 다음과 같이 만들 수 있다.

 

  성진과 창욱은 태균은 야구장에 도서관에 공부를 갔다. 좋아한다.
성진과 0 2 1 0 0 0 0 0
창욱은 2 0 0 1 0 1 0 0
태균은 1 0 0 0 1 0 0 0
야구장에 0 1 0 0 0 0 1 0
도서관에 0 0 1 0 0 0 1 0
공부를 0 1 0 0 0 0 0 1
갔다. 0 0 0 1 1 0 0 0
좋아한다. 0 0 0 0 0 1 0 0

 

이렇게 만들어진 동시 출현 행렬을 토대로 4가지 방법등을 사용하여 단어 벡터를 만들면 된다.

 

카운트 기반 방법의 장점은 빠르다는 점이고, 예측 방법에 비해 좀 더 이전에 만들어진 방법이지만 데이터가 많을 경우에는 단어가 잘 표현되고 효율적이어서 아직까지도 많이 사용하는 방법이다.

 

두번째, 신경망 구조 혹은 어떠한 모델을 사용해 특정 문맥에서 어떤 단어가 나올지 예측하는 예측 방법(Predictive)

 

예측 방법에는 다음과 같은 것들이 존재한다.

 

- Word2Vec

- NNLM(Neural Network Language Model)

- RNNLM(Recurrent Neural Network Language Model)

 

여러 예측 기반 방법 중에서 단어 표현 방법으로 가장 많이 사용되는 Word2Vec에 대해 자세히 알아본다.

 

Word2Vec은 CBOW(Continuous Bag of Words)와 Skip-Gram이라는 두 가지 모델로 나뉜다. 두 모델은 각각 서로 반대되는 개념으로 생각하면 되는데, CBOW의 경우 "문맥 안의 주변 단어들을 통해 어떤 단어를 예측하는 방법"이고, 반대로 Skip-Gram의 경우 "어떤 단어를 가지고 특정 문맥 안의 주변 단어들을 예측하는 방법"이다.

 

예시를 들어서 CBOW와 Skip-Gram을 이해해보자.

 

- 민수는 냉장고에서 음식을 꺼내서 먹었다.

 

CBOW은 주변 단어를 통해 하나의 단어를 예측하는 모델인데, 이를 이용한다면 다음 문장의 빈칸을 채울 수 있다.

 

- 민수는 냉장고에서 _______ 꺼내서 먹었다.

 

반대로, Skip-Gram은 하나의 단어를 가지고 주변에 올 단어를 예측하는 모델인데, 이를 이용한다면 다음 문장의 빈칸을 채울 수 있다.

 

- _____ _____________ 음식을 ______  ________

 

 

그렇다면 CBOW와 Skip-Gram이 어떤 순서로 학습되는지 알아보도록 하자.

 

 

CBOW의 경우는 다음과 같은 순서로 학습한다.

 

1. 각 주변 단어들을 원-핫 벡터로 만들어 입력값으로 사용한다.

2. 가중치 행렬(weight matrix)을 각 원-핫 벡터에 곱해서 N-차원 벡터를 만든다.

3. 만들어진 N-차원 벡터를 모두 더한 후 개수로 나눠 평균 N-차원 벡터를 만든다.

4. N-차원 벡터에 다시 가중치 행렬을 곱해서 원-핫 벡터와 같은 차원의 벡터로 만든다.

5. 만들어진 벡터를 실제 예측하려고 하는 단어의 원-핫 벡터와 비교해서 학습한다.

 

책에는 이렇게 나와있었지만, 사실 CBOW를 5줄로 해서 이해하기란 어려울 것이다. 나 또한 이것을 읽고 그저 너무 겉핥기식으로 설명했다고 생각했다.

 

그래서 나는 CBOW와 관련된 글들을 추가적으로 읽고, 이것을 좀 더 상세히 풀어서 설명해보면 좋겠다는 생각을 했다.

 

따라서 다음에 나올 내용은 CBOW를 위에 언급된 5가지 순서를 지키되, 상세하게 어떤식으로 작동하게 되는지를 설명해보고자 한다.

 

 

 

이해를 돕기 위해 특정 예시를 들어서 설명을 하고자 하는데, 다음과 같은 예시를 사용한다.

 

예시 문장: 지금 너무 추워서 꼭 패딩 입어야 해(7글자)

 

CBOW는 주변 단어를 통해 하나의 단어를 예측하는 모델이라고 위에서 설명하였는데, 책에서는 언급이 되지 않았지만 꼭 고려해야 할 사항은 바로 주변 단어를 몇개로 잡을 것이냐의 문제이다. 즉, 주변에 있는 모든 단어를 사용하는 것이 아닌, 특정 몇 개의 단어를 이용해서 하나의 단어를 예측한다는 것이다. 주변 단어를 몇개로 잡을 것인지를 윈도우(window) 라는 단어로 표현하는데, 예시 문장에 윈도우를 2로 설정하였을 때 어떤 식으로 예측하게 되는지를 설명해보도록 하겠다. 파란색은 입력값으로 들어가는 단어, 빨간색은 예측을 하는 단어이다.

 

1. 지금 너무 추워서 꼭 패딩 입어야 해

 

2. 지금 너무 추워서 꼭 패딩 입어야 해

 

3. 지금 너무 추워서 꼭 패딩 입어야 해

 

4. 지금 너무 추워서 패딩 입어야

 

5. 지금 너무 추워서 꼭 패딩 입어야 해

 

6. 지금 너무 추워서 꼭 패딩 입어야

 

7. 지금 너무 추워서 꼭 패딩 입어야

 

예측 하려고 하는 단어(빨간색)의 양 옆으로 2개의 단어를 사용하여 예측하는 것을 볼 수 있을 것이다.

 

이런식으로 CBOW는 진행이 된다고 생각하면 된다.

 

 

 

가장 먼저 해볼 것은 CBOW의 전체적인 구조를 설명해볼 것이다. 상세한 내용은 차차 설명할 것이고, 전체적인 조망을 하기 위해 전체적인 구조를 먼저 설명하고자 한다.

 

그림 1. CBOW의 전체적인 구조

 

먼저 첫번째 순서(1. 각 주변 단어들을 원-핫 벡터로 만들어 입력값으로 사용한다.)를 적용하면, 예시 문장을 각각 원-핫 벡터로 만들어준다. 즉 지금 = [1, 0, 0, 0, 0, 0, 0]으로, 너무 = [0, 1, 0, 0, 0, 0, 0], 꼭 = [0, 0, 0, 1, 0, 0, 0], 패딩 = [0, 0, 0, 0, 1, 0, 0]으로 만들어준다. 그리고 나서 이 입력값에 가중치 행렬을 곱해서 N-차원의 은닉층을 만들어낸다. 현재 예시에서는 4개를 입력값으로 넣었으므로, 4개의 N-차원 은닉층이 생기게 될 것이다. 이 4개의 N-차원 은닉층을 평균을 취해 평균 N-차원 은닉층을 만든 뒤, 여기에 새로운 가중치 행렬을 곱해서 출력층을 만들고, 여기에 Sigmoid를 적용해 모든 값들을 0 ~ 1 사이의 값으로 만든 뒤에 가장 큰 값을 가지는 요소를 정답으로 출력하게 된다.

 

 

이제부터 전체적인 구조를 조목조목 나누어 하나하나 세세하게 볼 것이다.

 

 

그 다음으로는 두번째 순서(2. 가중치 행렬(weight matrix)을 각 원-핫 벡터에 곱해서 N-차원 벡터를 만든다.)를 적용해 보아야 할탠데, 가중치 행렬은 크기가 어떻게 되고, 어떻게 곱해지는 것인지를 설명해보겠다.

 

현재 예시에서는 입력값이 1 x 7 크기의 행렬이므로, 가중치 행렬과 곱하려면 7 x N 형태의 벡터가 필요하다.

N 으로 하면 잘 와닿지 않을 것이므로, 이 글에서는 N을 4로 두고, 4차원 은닉층을 만드는 것으로 생각해보자.

그렇다면 가중치 행렬은 7 x 4 형태가 될 것이며, 이는 다음과 같이 곱해질 것이다.

 

그림 2. 가중치 행렬 곱(1)

 

그림 3. 가중치 행렬 곱(2)

 

가중치 행렬이 곱해지는 것을 보여주기 위해서, 7 x 4 형태의 matrix를 구성하고 값은 무작위의 정수로 채워넣었다.

실제로 CBOW가 학습되는 과정에서는 이 가중치 행렬은 정답을 가장 잘 맞출 수 있게끔 값이 학습될 것이다.

 

가중치 행렬을 곱하면 결과는 4차원의 은닉층이 될 것이고, 사이즈는 1 x 4가 되는 것을 볼 수 있다.

 

 

다음으로 세번째 순서(3. 만들어진 N-차원 벡터를 모두 더한 후 개수로 나눠 평균 N-차원 벡터를 만든다.)를 실제로 적용해보도록 하겠다.

 

그림 4. 평균 은닉층 만들기

 

그림 2와 그림 3에서 만들어낸 4개의 은닉층을 모두 더한 뒤, 이를 입력층의 크기인 4로 나누어 평균 은닉층을 구한 모습이다. 

 

그 다음으로는 네번째 순서(4. N-차원 벡터에 다시 가중치 행렬을 곱해서 원-핫 벡터와 같은 차원의 벡터로 만든다.)를 실제로 적용해보도록 하겠다.

 

그림 5. 평균 은닉층과 가중치 행렬의 곱

 

그림 4에서 구한 평균 은닉층에다가 가중치 행렬 4 x 7 사이즈를 곱해서 기존의 입력값인 1 x 7 형태의 출력층을 만들어낸다. 왜 출력층이 다시 1 x 7 형태로 되게끔 만들어야 하나? 라고 한다면 결국 우리가 원하는 것은 우리의 모델이 추워서 = [0, 0, 1, 0, 0, 0, 0]를 정답으로 내야 하기 때문에, 우리가 원래 사용했던 벡터의 크기와 동일한 크기의 벡터가 출력으로 나와야 하는 것이다.

 

그림 5에서 구한 출력층의 값들은 경우에 따라서 매우 클 수도 있고, 작을 수도 있는데 우리가 원하는 것은 가장 큰 값을 가지는 값을 찾아야 하는 것이기 때문에, 여기에 Sigmoid 함수를 적용시켜 모든 벡터의 값들을 0 ~ 1 의 값으로 바꾼다.

 

그렇게 했을때 가장 큰 값이 정답이 될 것이다. (그림 5에서는 가장 마지막 벡터가 Sigmoid를 적용했을때 가장 큰 값일 것이므로 정답으로 나오게 될 것이다. 물론 이번 예시에서는 임의의 가중치 행렬을 사용했기 때문에 틀린 정답이 나오게 되었다.)

 

여기서 사용된 두번째 가중치 행렬 또한 학습 과정에서 적절한 값으로 학습이 되는 것으로 생각하면 된다.

 

 

마지막 순서(5. 만들어진 벡터를 실제 예측하려고 하는 단어의 원-핫 벡터와 비교해서 학습한다.)는 손실 함수로 Cross-Entropy 오차를 사용한다. 

 

그림 6. 실제 정답과의 비교

0 ~ 1 출력층은 Sigmoid가 적용되어 모든 벡터들이 0과 1사이의 값이 되고, 이는 예측값이므로 y hat으로 표기하였다. 또 실제 정답은 추워서 [0, 0, 1, 0, 0, 0, 0] 이므로 이는 y로 표현하였다.

 

그림 7. Cross-entropy 

 Cross-entropy가 왜 이런 식으로 구성되어 있는지에 대해서는 다른 글을 참고해보심이 좋을듯 하고, 여기서는 CBOW의 학습 과정에서 Loss function으로 Cross-entropy를 이용하여 학습한다는 점만 짚고 넘어가면 될듯 하다!

 

여기까지가 CBOW에 관련된 세부적인 설명이 되었다.

 

중간중간에도 썼지만, 결국 CBOW가 학습을 통해서 값을 찾아나가는 것은 바로 두개의 가중치 행렬이다. 즉 가장 정답을 잘 맞출 수 있는 가중치 값들로 점차적으로 학습해나간다는 말이다.

 

 

 

글이 너무 길어진 것 같아서, 여기서 한번 끊어주고 다음 글에서는 Skip-Gram에 대한 설명과 그 이후 내용들을 다뤄보도록 하겠다.

 

글을 마치기 전에, 이 내용을 읽었다면 다시 한번 정리해볼만한 내용들을 적어보고 이것들에 어느정도 답할 수 있다면 잘 기억하고 있다고 생각할 수 있을듯 하다.

 

 

 

첫번째 - 단어 임베딩 혹은 단어 표현 이라고 하는 분야는 왜 필요한가?

 

두번째 - 원-핫 인코딩 방식의 단어 표현이 가진 두 가지 문제점은 무엇인가?

 

세번째 -  분포 가설(Distributed hypothesis)란 무엇인가?

 

네번째 - 분포 가설을 기반으로 한 방법 두 가지는 각각 무엇인가?

 

다섯번째 - Word2Vec은 두 가지 모델로 나뉘는데, 각각 모델의 이름과 모델이 작동하는 방법은 무엇인가?

 

여섯번째 - CBOW의 전반적인 구조를 설명하라.

 

 

+ Recent posts