Yet Never Lose Faith

- Good to Great , Jim Collins

How To Preprocess Image Data 자세히보기

Deep Learning/[Books] 머신러닝, 딥러닝 실전 앱 개발

자연어 처리(문서 카테고리 분류)의 원리

Kellyyyy 2020. 5. 25. 08:00

 

자연어란 인간의 언어를 의미한다. 

기계어는 컴퓨터의 언어를 의미한다.

 

자연어 처리란 인간의 언어 현상을 컴퓨터와 같은 기계를 이용해서 모사할 수 있도록 연구하고,

이를 구현하는 인공지능의 한 분야이다.

정보검색, 문서자동분류, 신문기사 클러스터링, 대화형 Agent등이 있다.

 

이번 포스팅에서는 문서자동분류, 신문기사 클러스터링 같이 인공지능으로 자연어로 이루어진 문서의 카테고리를 분류하는 작업의 원리에 대해서 다룬다.

 

전체적인 과정을 이미지로 표현해보았다.

자연어처리 클러스터링의 원리

먼저 자연어로 된 문서들을 컴퓨터가 이해할 수 있도록 입력값(X)과 Target(Y)을 숫자 형태로 변환한다. (①)

적절한 형태의 학습 데이터를 확보하면 머신러닝, 딥러닝 모델을 생성한다. (②)

새로운 문서가 주어지면 이를 다시 숫자형태로 변환하고, 모델을 사용하여 어떤 카테고리인지 예측한다. (③)

 

 

이번 포스팅에서는 ① 단계를 다룬다. 

 

① 자연어를 숫자로 변환하기

 

자연어를 어떻게 수치로 만들면 좋을까?

 

다양한 방식을 사용할 수 있지만,

문서의 카테고리를 분류하는 작업에서는

문서에 포함된 단어들의 출현빈도를 활용하여 수치화 시키는 것이 효과적이다.

 

문서의 카테고리를 판단할 때,

특정 단어의 빈도가 중요한 단서가 되는 경우가 많기 때문이다. 

예컨대, 미래한국당, 더불어민주당 같은 단어가 많이 나오면

해당 문서의 카테고리를 정치일 확률이 높다.

 

문서에 포함된 단어들의 출현빈도를 계산하기 위해서는

우선 문서를 의미를 가진 단어 단위 즉, 형태소로 쪼개는 형태소 분석이 필요하다.

 

영어의 경우에는 상당히 간단하다.

영어는 형태소마다 띄어쓰기를 해서 문장을 구성하는 것이 기본이기 때문이다.

 

반면 아시아권 언어는 띄어쓰기 단위로 형태소를 구분할 수 없기 때문에 추가적인 작업이 필요하다. 

이를 위해서 다양한 형태소 분석 라이브러리들이 오픈소스로 배포되고 있다.

나는 KoNLPy의 Okt분석기를 사용하겠다.

 

간단한 예시를 통해 Okt로 형태소 분석을 해보겠다.

 

 

from konlpy.tag import Okt

#Okt 객체 생성
okt = Okt()

#형태소 분석
malist = okt.pos("아버지가 방에 들어가신다.", norm=True, stem=True)

print(malist)

 

 

Okt 객체를 생성한 후 pos()를 통해 형태소 분석을 하면 

아래와 같이 명사, 조사, 동사 등으로 형태소 분석을 해서 배열형태로 출력한다. 

 

 

 

 

 

문서를 형태소 단위로 분류했다면

이제 각 형태소를 수치로 변환하는 작업의 원리를 살펴볼 차례이다.

 

여기에도 다양한 계산식을 사용할 수 있는데, 나는 TF-IDF라는 방식을 사용하겠다. 

TF-IDF는 단어의 출현빈도와 함께 문장 전체에서 단어가 어느 정도로 중요한지 고려한다. 

 

TF-IDF방식은 문서 내부의 특정 단어가 어느 정도의 빈도로 사용되는지 확인해

문서 내부에서 중요하다고 판단되는 특정적인 단어를 찾는다.

 

예를 들어, '이다', '가', '를' 등 흔히 존재하는 단어들의 중요도는 낮추고

가끔 존재하는 단어의 중요도를 높인다.

 

다음 계산식은 TF-IDF가 각 단어의 값을 어떻게 계산하는지 보여준다.

tf(t,d)는 한 문서 내부에서 단어 t의 출현 빈도를 나타내고, idf(t)는 모든 문서 내부에서 단어 t의 출현 빈도를 나타낸다.

 

 

idf(t)는 아래와 같이 계산한다.

df(d,t)는 문장 전체에서의 단어 t 출현 횟수이며 분자에 있는 D는 문서의 총 수를 나타낸다.

 

 

개념이 어려울 수 있는데 간단한 예제를 실행해보겠다.

우선 문서 단위의 개념을 문장으로 단순화하여 생각해보자.

아래와 같은 간단한 문장 4개를 TF-IDF로 수치로 변환해보겠다.

 

'비'

'오늘은 비가 내렸어요.'

'오늘은 더웠지만 오후부터 비가 내렸다.'

'비가 내리는 일요일이다.'

 

먼서 형태소 분석부터 해야한다.

위에서 다룬 형태소 분석 과정을 tokenize()라는 함수로 만든다.

이때, 명사, 동사, 형용사가 아니면 거의 의미가 없기 때문에 제외했다.

 

from konlpy.tag import Okt
okt = Okt()

def tokenize(text) :
    result = []
    word_s = okt.pos(text, norm=True, stem=True)
    for n, h in word_s :
        if not (h in ['Noun', 'Verb', 'Adjective']) : continue
        result.append(n)
    print("형태소 분석결과 : ", result)
    return result

 

 

각 형태소별로 빈도를 구해야하기 때문에 형태소를 id로 변환하여 기록해두어야한다.

그 과정을 words_to_ids()로 만들었다.

그리고 각 id를 word_dic이라는 배열에 기록해둔다.

 

 

#'_id' : 전체 id의 갯수
word_dic = {'_id' : 0} 

def words_to_ids(words) :
    result = []
    for w in words :
        if w in word_dic :
            result.append(word_dic[w])
            continue
        else :
            word_dic[w] = word_dic['_id']
            #전체 id의 갯수 기록(몇 번째 id인지 알아야하니까!)
            word_dic['_id'] += 1 
            result.append(word_dic[w])
    return result         

 

 

위의 두 단계를 add_text()로 만들고 결과를 files라는 배열로 출력하도록 한다.

 

 

files = []

def add_text(text) :
    ids = words_to_ids(tokenize(text))
    files.append(ids)

 

 

우선, 여기까지의 과정을 실행해보고 결과를 출력해봤다.

 

 

if __name__ == '__main__' :
    add_text('비')
    add_text('오늘은 비가 내렸어요.')
    add_text('오늘은 더웠지만 오후부터 비가 내렸다.')
    add_text('비가 내리는 일요일이다.')
    print("---------------------------------")
    print("word_dic : " , word_dic)
    print("---------------------------------")
    print("files : " , files)

 

 

 

이제 TF-IDF를 구해보겠다.

 

위에서 다룬 식을 사용하기 위해서는 먼저 아래 3가지 값을 구해야한다.

 

(1) df(d,t) : 문장(문서) 전체에서의 단어 t 출현 횟수

(2) D : 전체 문장(문서)의 수

(3) tf(t,d) : 한 문장(문서) 내부의 단어 t 출현 빈도

 

각각 dt_dic, doc_count 그리고 result로 구한 후 TF-IDF를 공식에 맞춰 구했다.

 

 

def calc_files() :
	# (1) 문장(문서) 전체에서의 단어 t 출현 횟수
    dt_dic = {}
    
    # (2) 전체 문장(문서)의 수
    doc_count = len(files) # 전체 문서의 수 (D)
    
    # (3) 한 문장(문서) 내부에서 단어 t 출현 빈도
    result = []
    
    # 단어 출현 횟수 세기
    for words in files :
        used_word = {}
        data = np.zeros(word_dic['_id'])
        for id in words :
            data[id] += 1
            used_word[id] = 1
        #단어 t가 사용되고 있을 경우 dt_dic의 수를 1 더하기
        for id in used_words :
            if not(id in dt_dic) : dt_dic[id] = 0
            dt_dic[id] += 1
        # words의 길이로 나눠 빈도 구하기
        data = data/len(words)
        result.append(data)
    
    #TF-IDF 계산하기
    for i, doc in enumerate(result) :
        for id, v in enumerate(doc) :
            idf = np.log(doc_count/dt_dic[id]) +1
            doc[id] = min([doc[id]*idf, 1.0])
        result[i] = doc
    return result

 

실행 결과는 다음과 같다.

 

tf-idf 계산 결과

 

출력결과를 보면 각 문장이 어떤 형태소를 담고 있는지와

각 형태소의 중요도를 확인할 수 있다.

 

마지막 문장 '비가 내리는 요일은 일요일이다'라는 문장에서

'일요일'의 중요도는 굉장히 높다.

 

이번 포스팅에서는 자연어처리(문서카테고리 분류) 작업에서

자연어를 수치로 변환하는 ① 단계에 대해서 다루었다.

 

자연어처리 클러스터링의 원리

 

간단한 문장단위로 실습을 해보았는데,

다음 포스팅에서는 문서 단위로 수치로 변환하는 방법에 대해서 다루겠다.

 

 

참고문헌 : 

[위키백과 : 자연어처리의 정의]

https://ko.wikipedia.org/wiki/%EC%9E%90%EC%97%B0%EC%96%B4_%EC%B2%98%EB%A6%AC

 

자연어 처리 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 자연어 처리(自然語處理) 또는 자연 언어 처리(自然言語處理)는 인간의 언어 현상을 컴퓨터와 같은 기계를 이용해서 모사 할수 있도록 연구하고 이를 구현하는 인공지능의 주요 분야 중 하나다. 자연 언어 처리는 연구 대상이 언어 이기 때문에 당연하게도 언어 자체를 연구하는 언어학과 언어 현상의 내적 기재를 탐구하는 언어 인지 과학과 연관이 깊다. 구현을 위해 수학적 통계적 도구를 많이 활용하며 특히 기계학습 도구를 많이 사용하

ko.wikipedia.org

[책 파이썬을 이용한 머신러닝, 딥러닝 실전 앱 개발]