[토픽 모델링, tomotopy] sLDA를 이용하여 스팸 메일 분류하기

Posted by 적분 ∫2tdt=t²+c
2019.08.21 02:40 그냥 공부

sLDA(supervised LDA)라는 토픽 모델링 기법에 대해서는 아주 옛날에 제 블로그 포스팅에서 짧게 소개한 적이 있습니다. LDA가 문헌 집합 내에서 주제들을 찾아서 각 문헌에는 어떤 주제가 얼만큼씩 들어있는지는 찾아내는 작업이라면, sLDA에서는 문헌에서 각 주제가 얼만큼 있는지를 찾아낸 다음, 그 주제 분포를 바탕으로 문헌의 특성(response variable)을 예측하는 작업이라고 할 수 있습니다. 통계로 비유로 들자면 LDA는 문헌들 안에 있는 내용을 잘 정리해서 보여주는 기술 통계와 같은 것이고, sLDA는 LDA로 얻은 정보를 바탕으로 새로운 사실을 예측하는 추리 통계와 같은 것입니다.


수학적으로 풀어헤쳐보면, sLDA는 결국 LDA에 회귀 모형(regression)을 덧붙인 것과 같습니다. 즉, 회귀 모형으로 할 수 있는 모든 것들을 sLDA도 할 수는 있는 것이죠. 대표적인게 텍스트로부터 별점 예측하기, 텍스트를 여러 클래스 분류하기 등이 있겠네요. 사실 요즘 이런 작업에서는 최신 유행하는 딥러닝 기반의 언어모형들이 훨씬 높은 성능을 보이고 있지만, 고전을 복습하는 기분으로 토픽 모델링 기반의 접근방법도 가볍게 살펴보는것도 좋을 것 같습니다.


sLDA에 대한 간단한 설명

스팸 메일인지 아닌지가 표시된 텍스트 집합이 있습니다.

0: go until jurong point crazy available only in bugis great world la buffet cine there got amore wat

0: ok lar joking wif oni

1: free entry in wkly comp to win fa cup final tkts 21st may 2005 text fa to 87121 to receive entry question std txt rate t&c apply 08452810075over18

0: dun say so early hor already then say

0: nah don think he goes to usf he lives around here though

...

왼쪽의 0은 스팸이 아님을, 1은 스팸임을 뜻합니다. 이런 텍스트 집합을 대상으로 LDA 토픽 모델링을 실시하면 다음과 같은 토픽 결과를 뽑을 수가 있습니다. (토픽 개수는 10개로 지정했습니다)

#0: reply, 100, free, video, phone

#1: you, the, and, to, it

#2: send, to, ur, www, statements

#3: happy, gud, you, day, good

#4: ok, to, me, da, in

#5: im, on, hi, me, at

#6: free, ur, nokia, tone, txt

#7: lor, go, then, wat, my

#8: call, to, your, free, mobile

#9: later, ll, sorry, ur, call

(전체 텍스트양이 적고, 길이도 짧으며, 워낙 비표준어가 남발하는 메일이라는 매체의 특성 상, 사실 토픽 모델링을 사용하기에 적합하지는 않습니다만, 이진 분류를 실험하기에 제일 쉬운 예제가 스팸 메일 분류라서 이렇게 진행한 점 양해바랍니다.)

보시면 조금 잘 모르겠는 주제들도 있지만, #0, #6, #8 같은걸 보면 뭔가 공짜 뭐시기~하며 광고성 메일에 자주 나올법한 단어들이라는 걸 알 수가 있습니다. 저런 주제가 많이 나오는 문헌이라면 스팸일 가능성이 높다고 볼 수 있겠죠. 이처럼 어떤 문헌의 주제분포는 그 문헌에 붙은 라벨(여기서는 스팸 유무)와 깊은 연관관계가 있을것이라고 생각할 수 있습니다. 여기서 착안해 sLDA에서는 각 문헌의 주제분포를 그 문헌의 라벨과 연관시킵니다. 좀 더 수학적으로 쓰면,

(y는 문헌의 라벨, Z는 문헌의 주제 분포, η는 회귀 계수입니다. σ는 오차의 표준편차이구요)

Z bar는 이 문헌에 각각의 주제가 얼만큼씩 들어있는지를 반영합니다. 예를 들어 총 토픽의 수를 3이라고 하고, 어떤 문헌에서 첫번째 토픽은 30%, 두번째 토픽은 60%, 세번째 토픽은 10% 포함하고 있다면 이 문헌의 Z-bar는 [0.3, 0.6, 0.1]이 될겁니다.

η는 Z-bar와 내적하여 y를 예측하는데에 쓰일 계수입니다. sLDA 학습 과정에서 적당한 η을 찾아내게 됩니다. 예를 들어 이 값은 [0, 1, -1]과 같을 수 있겠죠.

그럼 최종적으로 는 두 벡터의 내적이므로 0.3 * 0 + 0.6 * 1 + 0.1 * (-1) = 0.5가 됩니다. 따라서 이 문헌의 라벨은 0.5를 평균으로 하고 표준편차를 σ로 하는 정규분포에서 뽑힌다는 것이죠. 이 문헌의 실제 라벨 y가 정확하게 0.5와 같다면 아주 훌륭하게 예측을 한것이고 y가 0.5에서 멀리 떨어져 있다면 예측을 크게 틀리게 한 것이 됩니다. sLDA는 이 정규분포가 실제 라벨과 잘 일치하면 칭찬해주고, 크게 틀리면 혼을 내서 η를 조절합니다. 따라서 학습을 반복할수록 실제 라벨 η는 y가 와 일치되도록 값이 변하게 됩니다.


y값은 일반적으로는 실수가 되는데요, 경우에 따라서 실수가 아니라, 0 혹은 1 둘 중에 하나여야 할 수도 있습니다. 이 경우는 통계에서 선형 회귀 대신에 로지스틱 회귀를 사용하는 것처럼 Generalized Linear Model을 사용하여 실수 전체 범위를 특정한 구간으로 한정하여 변환된 y를 쓸 수도 있습니다. 스팸 분류가 딱 그 경우에 맞는 상황입니다.


tomotopy를 이용해 실험해보자

tomotopy 0.2. 버전부터 sLDA 모형이 추가되었습니다. Python 3.5 이상을 사용하신다면 

$ pip install tomotopy>=0.2.0

를 이용해 쉽게 설치할 수 있습니다.


한 번 돌려보시면 다음과 같은 결과를 얻으실 수 있을 겁니다.

Num docs: 5468 , Vocab size: 2825 , Num words: 70904

(50) llpw: -28.592

(100) llpw: -28.012

(150) llpw: -27.781

(200) llpw: -27.648

(250) llpw: -27.604

(300) llpw: -27.572

(350) llpw: -27.539

(400) llpw: -27.517

(450) llpw: -27.502

(500) llpw: -27.486

(550) llpw: -27.484

(600) llpw: -27.481

(650) llpw: -27.482

(700) llpw: -27.464

(750) llpw: -27.451

(800) llpw: -27.442

(850) llpw: -27.441

(900) llpw: -27.428

(950) llpw: -27.425

(1000) llpw: -27.424

로그 가능도 값이 계속 증가하는걸 보니 학습이 잘 되고 있습니다.

어떤 주제가 뽑혔는지 확인해볼까요? 각 주제와 그 주제의 회귀 계수 η를 같이 출력해보겠습니다.


#0 (0.385) [reply, 100, free, video, phone]

#1 (-4.932) [you, the, and, to, it]

#2 (1.634) [send, to, ur, www, statements]

#3 (-3.598) [happy, gud, you, day, good]

#4 (-5.392) [ok, to, me, da, in]

#5 (-3.860) [im, on, hi, me, at]

#6 (1.583) [free, ur, nokia, tone, txt]

#7 (-5.346) [lor, go, then, wat, my]

#8 (8.441) [call, to, your, free, mobile]

#9 (-6.463) [later, ll, sorry, ur, call]

이진 변수(로지스틱 회귀)에서는 계수가 음수일수록 결과가 0에 가깝게 나오고, 계수가 양수일수록 결과가 1에 가깝게 나옵니다. 즉 값이 클수록 문헌이 1(스팸)일 가능성에 크게 기여하는 것이죠. 보시면 #1, #3, #4, #5, #7, #9 주제의 경우 아주 일상적인 어휘가 많이 나오는것에서 볼수 있듯이 계수가 -3, -4와 같이 작은 값입니다. 반면 #0, #2, #6, #8의 경우 계수가 큰 양수값이고, 딱 봐도 광고티가 많이 나는 주제입니다. 각 주제별로 η가 나오므로, 이걸 이용해서 어떤 주제의 스팸성을 계량할 수도 있습니다. 제일 스팸성이 높은 주제는 #8이며, 제일 스팸성이 낮은 주제는 #9입니다. 그럴싸하지요?

더 나아가 새로운 문헌에 대해 예측을 실시할 수도 있습니다. test 데이터를 가지고 우리의 sLDA 모형이 얼마나 정확하게 스팸을 분류해내는지 봅시다.


0.0 0.006124 [0.0013, 0.4361, 0.0013, 0.0013, 0.0013, 0.0013, 0.5312, 0.0013, 0.0013]

... (중략) ...

Accuracy: 0.933

먼저 각 줄에는 정답예측값이 나오며, 그 뒤에는 해당 문헌의 주제분포가 나옵니다.

실제 사례를 보시죠.

free entry in wkly comp to win fa cup final tkts 21st may 2005 text fa to 87121 to receive entry question std txt rate t&c apply 08452810075over18

문헌 라벨: 1(스팸)

예측된 주제 분포: [0.0009, 0.0009, 0.0009, 0.0009, 0.0009, 0.0009, 0.0009, 0.0009, 0.9305, 0.0009]

예측 라벨: 0.9996

보시면 위 메일은 누가봐도 광고메일인지라 주제 분포 추론 결과 #8이 비중이 93%에 가깝게 나왔습니다. 따라서 이를 바탕으로 라벨을 예측한 결과 0.9996이 나왔고, 실제 라벨 1과 거의 유사하죠.

even my brother is not like to speak with me they treat me like aids patent

문헌 라벨: 0(스팸 아님)

예측된 주제 분포: [0.0020, 0.9677, 0.0020, 0.0020, 0.0020, 0.0020, 0.0020, 0.0020, 0.0020, 0.002]

예측 라벨: 0.0071

이 경우는 #1 주제가 우세한 메일입니다. #1 주제는 스팸이 아닐 경향이 크기 때문에 결국 0.071로 예측되어 스팸이 아님에 가까운 결과가 나왔습니다.

이렇듯, sLDA에서는 학습 데이터로부터 여러 개의 토픽을 추출하고, 또 토픽별로 최적의 회귀 계수인 η를 찾아냄으로써, 이를 통해 새로운 문헌에 대해서도 응답 변수를 예측할 수 있게 해줍니다. 제가 사용한 스팸 메일 데이터 약 94%정도의 성능이 나왔는데, 사실 대부분의 메일이 스팸이 아닌, 데이터 분포가 불균형한 경우였기 때문에 저정도 성능이 높은 성능이라고 말할 수는 없습니다. 'sLDA가 작동은 잘하는구나!' 라고 말할 수 있는 정도라고 할 수 있겠습니다.

지금까지 이진 변수 한 개를 포함하는 문헌을 sLDA로 예측하는 작업을 함께 살펴보았습니다. 이 포스팅에서는 단순히 라벨(응답 변수)이 한 개인 경우만 다루었지만, 여러 개인 경우도 똑같이 적용할 수 있습니다. 굉장히 유연한 모형이기 때문에 다양한 상황에 널리 적용할 수가 있습니다! 

그렇지만 사실 단순 텍스트 분류를 하기 위해서 굳이 sLDA를 사용할 필요는 없습니다. 더 간단하면서도 성능이 잘 나오는 기법들이 많기 때문이죠. 그러나 다음과 같은 상황이라면 sLDA를 고려해봄직합니다.

  • 문헌의 양이 많고 그 안에 어떤 내용이 있는지 몰라서, 전반적인 탐색을 할 필요가 있는 경우

  • 문헌의 라벨이 문헌의 주제와 깊은 연관이 있을 것이라 예상되는 경우

  • 문헌 집단 내의 각 주제들이 문헌의 라벨과 어떤 연관이 있는지 알아보고 싶은 경우


다음 번에는 응답변수가 여러 개인 경우 tomotopy에서 sLDA를 어떻게 사용할 수 있는지 살펴보도록 하겠습니다.


이 댓글을 비밀 댓글로