엄청나게 많이 쌓여있는 문서들, 그리고 그 안의 핵심 문장들을 뽑아 써놓은 수 백개의 포스트잇 메모. 제가 인턴으로 출근한 첫 직장은 알록달록한 포스트잇들과의 사투였습니다. 프로젝트의 데드라인이 있는 날에는 제 몸에 하나가 붙어 있어도 이상하지 않을 정도로 여러 번 포스트잇을 띄었다가 뗐다가 했어야 했습니다.

저의 주요 업무는 문서 정리 및 분류였기 때문이죠. 나중에는 정말 문서가 나인지, 내가 문서인지 모를 정도로 지칠 때까지 읽고 또 읽어야만 했습니다.

그때의 추억 때문인지 6년 후 제가 쓴 석사 논문의 큰 주제 중 하나는 어떻게 하면 문서 분류를 잘할 수 있을까였습니다. 물론 이제는 인간인 제가 하는 게 아니라, 인공지능, 즉 기계가 잘할 수 있도록 말이죠.

NLP 분야에서는 이 주제를 document(text) classification이라고 합니다. 오늘은 박 대리를 도와 문서 분류를 자동으로 처리할 수 있나 공부를 해보겠습니다.

0이냐 1이냐: Binary Classification‌‌

Classification에서 가장 기본적이고 쉬운 것은 이분법 (binary) 입니다. 즉, 정답은 모 아니면 도, 두 가지 안에서 선택해야 하는 것입니다.

긍정적인가 부정적인가 ,‌‌스포츠 기사인가 아닌가,‌‌악플인가 아닌가.

세상을 무조건 이분법으로 나누는 것은 잘 못된 일이지만, 머신러닝에서는 수학이 좀 더 쉬워지는 좋은 방법입니다.

머신러닝에서 가장 간단한 모델인 Linear Regression은 여러 개의 (X, Y) 데이터가 주어졌을 때, 가장 잘 맞는 직선을 찾는 (fit 하는) 알고리즘입니다. Y는 보통 한 개의 숫자 (scalar value)이고, X는 n차원의 vector입니다. 밑의 예제는 X가 1차원인 예시이죠.

https://en.wikipedia.org/wiki/Linear_regression

이름에서 나타나듯이 Linear Regression은 분류 (classification)가 아닙니다. 숫자를 예측하는 회귀 (regression) 입니다. 그렇다면 Linear Regression을 어떻게 분류 모델로 만들 수 있을까요? 정답은 바로 이 sigmoid (logistic라고도 불리는) 함수에 있습니다.

https://en.wikipedia.org/wiki/Sigmoid_function

보시다시피 sigmoid 함수는 들어오는 어떤 숫자 (x축) 든 전부 0과 1 사이로 눌러 넣어버리는 특성을 가지고 있습니다. 그렇기 때문에 linear regression을 sigmoid와 함께 쓴다면 0과 1 사이의 y값을 만들어, binary classification 모델로 변신할 수 있는 것이죠. 이를 logistic regression classifier라고 부릅니다.

학습을 위해 어떤 데이터가 필요할까

아직까지 너무 수학적인 얘기만 해서 이게 뭔 소리야 싶은 분들도 있을 겁니다. 저도 사실은 논문 읽을 때 그럴 때가 많은데, 그럴 때 저는 데이터를 뜯어 봅니다. "X가 뭐고, Y가 뭐냐!? 도대체 데이터가 어떻게 생겨 먹은 거야?" 보통은 보고 나면 직관적으로 문제가 보입니다.

영화 리뷰 긍정 (y=1), 부정 (y=0) 문장 분류 데이터의 예시입니다:

("정말 재밌게 본 영화. 스토리도 연출도 정말 대박!", 1)‌‌
("배우들이 연기를 너무 잘하는데? 두 번 봐도 좋음.", 1)‌‌
("올해 최고의 작품!", 1)‌‌
("나는 그냥 별로. 졸다가 나왔다.", 0)‌‌
("도대체 이 뻔한 스토리는...", 0)‌‌
("정말 돈 아깝다. 인생 최악의 영화.", 0)

머신러닝 모델을 학습 위한 데이터 셋은 이러한 (x, y) 쌍이 적게는 수 백개에서, 많게는 수 백만 개로 구성되어 있습니다. 근데 위 예시에서 주어진 X는 숫자가 아니라 문자열 (string)입니다. logistic regression 모델은 X가 n차원의 vector여야 학습시킬 수 있는데 어떻게 해야할까요?

문장을 어떻게 N차원 vector로 만들지?

Week 2에서 배운 tf-idf BoW vector가 떠오르셨죠? 우리가 전에 문장을 vector로 만들었던 이유는 logistic regression 같은 통계 모델의 input으로 넣을 수 있는 형태이기 때문입니다 (이를 vectorize 한다고 표현하기도 합니다).

NLP가 아닌 다른 종류의 데이터로 linear/logistic regression을 써보신 분들은 이미 tf-idf BoW vector의 각 열이 하나의 feature로 취급된다는 것을 파악하셨을겁니다. 만약 vocabulary가 5000개인 corpus가 있다면 각 문장은 5000 x 1 column vector로 표현될 것이고, logistic regression은 5000차원의 X를 주어진 Y value들에 잘 맞는 직선을 찾는학습 과정을 거칩니다.

학습이 완료된 이후에 새로운 문장들 (검증 셋; evaluation set)이 주어졌을 때에는, logistic regression 모델은 다음과 같은 결과를 내뱉습니다:

("배우 연기 대박! 진짜 좋다", 0.95)
‌‌("최고다 최고. 스토리 작품성 모두", 0.85)‌‌
("괜찮았어. 재미는 있었거든", 0.63)‌‌
("최악의 작품", 0.05)‌‌
("왜내가 이 시간을 아깝게..", 0.1)‌‌
("아쉽다 뭔가. 2% 부족한 영화", 0.4)

여기서 나오는 숫자는 전부 0~1 사이의 숫자인데, 점수라고 생각하셔도 되고, 확률이라고 생각하셔도 좋습니다. probability score라고 불리는 이 숫자는 0에 가까울수록 부정 평가, 1에 가까울수록 긍정 평가에 가까운 예측을 나타냅니다.

최종 결정은 보통은 0.5 이하의 점수를 가진 문장은 부정으로, 나머지는 긍정으로 예측 값을 결정하고, 그러고 나서 정확도를 계산하면 됩니다.

어떤 단어들이 중요할까

예시의 이러한 긍정, 부정 감성 분석 (sentiment analysis) 모델을 학습시켰을 때, 모델이 가장 중요하게 보는 단어들이 어떤 것인지 알아볼 수 있습니다. 이것을 feature importance라고 하는데, 주로 Y 예측 값을 계산할 때 X에서 각 행 (각 feature)들에게 주는 가중치 (weight)를 계산한 것을 보면 됩니다. 이러한 그래프를 그려보면 모델이 무엇을 학습했는지 이해하는데 도움이 됩니다.

위 예시는 영문 데이터에서의 예시인데, 긍정 부분에 있어서 ["excellent", "wonderful", "Liked"] 같은 단어들이 가장 높은 가중치를 받고 있다는 것을 알 수 있습니다. 반대로 ["terrible", "bad", "trash"] 같은 단어들이 낮은 가중치를 받고 있고요.

https://medium.com/@aneesha/visualising-top-features-in-linear-svm-with-scikit-learn-and-matplotlib-3454ab18a14d

코드는 어떻게 짜나요?

저의 위클리 엔엘피는 코드 없이 글, 그림 그리고 수식으로 모든 것을 high level로 설명하는 것을 목표로 하고 있습니다. 하지만 많은 분들이 이걸 직접 코드로 짜면 어떤 라이브러리를 써서 무슨 함수를 써야 하는지 궁금해하실 것 같습니다.

저의 추천은 sci-kit learn이라는 python library입니다. 정말 많은 머신러닝 알고리즘들이 미리 구현되어 있기에 초보자들도 데이터만 있으면 코드 몇 줄로 성능이 좋은 모델을 학습시킬 수 있습니다. 이 글에는 제가 설명을 위하여 가장 기본적인 Logistic regression classifier를 소개해드렸지만, RandomForestClassifier, Support Vector Machine (SVM), Naive Bayes Classifier 등 다양한 머신 러닝 모델을 바로 쓰실 수 있습니다.

이 정도라면 지친 박 대리의 일을 도울 수 있는 모델을 만들 수 있는걸까요? 아니면 더 똑똑한 방법이 더 있을까요? 앞서 배운 word embedding은 어떻게 쓸 수 있는 것일까요?

다음 주에 계속됩니다! 놓치지 않도록 이메일로 구독해보세요.