Data Science/자연어 처리 (NLP)

[자연어 처리] KoNLPy를 사용하여 형태소 분석 및 DTM 만들기 - Python3, Windows 10 (feat. Okt, Pandas, Scikit-Learn)

 

아래 글에서 이어지는 내용이다.

foreverhappiness.tistory.com/28

 

BeautifulSoup를 활용한 웹 크롤링(Web Crawling) - Python3, Windows 10 (2)

아래 글에서 이어지는 내용이다. https://foreverhappiness.me/27 BeautifulSoup를 활용한 웹 크롤링(Web Crawling) - Python3, Windows 10 (1) 아마 웹 크롤링이라는 용어는 Data Science 분야가 활성화되면서 많..

foreverhappiness.tistory.com

 

이전 게시글에서 BeautifulSoup 모듈을 통해 웹 페이지를 크롤링하는 방법을 알아보았다.

크롤링하여 간단한 정규 표현식으로 필터링 한 데이터를 어떻게 처리하여 사용할 것인가?

 

수많은 데이터들을 공백이나 엔터를 기준으로 여러 개의 토큰으로 나누고, 필요 없는 데이터들을 제거하고, 필요한 데이터들을 추출해내는 이러한 작업을 데이터 전처리 (Data Processing)이라고 하는데 데이터를 가공하는 과정 중 하나이다. 

 

데이터를 전처리하는 단계 중 자연어를 처리하는 것은 분석에 있어 대체로 기본적이고 필수적인 사항이다.

 

이번에는 자연어 처리 방법 중 데이터의 형태소를 분석하고 추출해내고자 한다.

 


 

형태소로 나눈다면 크게 명사, 동사, 형용사, 부사, 조사, 접사, 관형사, 어미 등등..으로 나뉠 것이다.

 

한국어 자연어 처리를 할 때는 대체로 Python3의 KoNLPy(코엔엘파이) 라이브러리를 많이 사용한다.

 

KoNLPy는 Korean Natural Language Processing in Python의 준말이다.

 

 

운영체제는 Windows10에서 작업하였다.

 

혹시나 Python이 설치되어있지 않다면 Python 먼저 설치하고 오자.

foreverhappiness.tistory.com/25

 

파이썬 3 (Python 3) 설치하기 (For Windows)

최근 들어 파이썬의 비중이 많이 높아지고 있다. 파이썬으로 게임을 제작하기도 하고, 인공지능, 빅데이터 분석에도 많은 라이브러리들이 제공되기도 한다. 삼성 SW 역량 평가에 Python 언어도 포

foreverhappiness.tistory.com

 


 

바로 KoNLPy를 설치할 것은 아니고, Java를 먼저 설치해야 한다. 정확히는 JDK(Java Development Kit)을 설치해야 한다.

 

버전은 1.7 이상이어야 하며 JDK 다운로드 방법은 아래 링크를 참고하면 된다.

 

foreverhappiness.tistory.com/33

 

Java 설치하기 (For Windows)

최근 들어 많은 프로그래밍 언어들이 생겨나고 있다. 안드로이드 분야에서도 코틀린이 JAVA를 대신하고 있으며 GUI 쪽에서도 파이썬이 많은 라이브러리가 제공되고 있어 Java의 영역이 조금씩 좁

foreverhappiness.tistory.com


 

Java와 Python을 연동시켜주기 위한 JPype1도 함께 설치해줘야 한다. (Windows 10 기준)

JPype1은 아래 링크에서 다운로드할 수 있으며 자신의 컴퓨터가 32비트 운영체제이면 win32, 64비트라면 win amd64를 다운로드하면 된다.

cp37은 python3.7, cp36은 python3.6이므로 플랫폼을 맞춰 다운로드 하자.

 

https://www.lfd.uci.edu/~gohlke/pythonlibs/#jpype

 

Python Extension Packages for Windows - Christoph Gohlke

by Christoph Gohlke, Laboratory for Fluorescence Dynamics, University of California, Irvine. Updated on 10 January 2020 at 19:57 UTC. This page provides 32- and 64-bit Windows binaries of many scientific open-source extension packages for the official CPyt

www.lfd.uci.edu

 

혹시나 자신의 컴퓨터가 몇 비트 운영체제인지 모른다면 아래 링크를 참고하면 된다.

 

foreverhappiness.tistory.com/26

 

사용중인 컴퓨터 프로세서가 몇 비트인지 확인하려면? (Windows 10)

현재 사용 중인 PC의 프로세서가 32비트? 64비트? 아마 프로그램 설치를 진행하면서 비트 수가 안맞아 설치하는데 고역을 겪었던 경험이 있을 것이다. 설치 프로그램이 요구하는 비트 수에 맞춰

foreverhappiness.tistory.com

 

이제 왼쪽 하단의 시작 버튼 옆 검색창에서 CMD 혹은 명령 프롬프트를 검색하여 실행시키자.

 

그리고 방금 다운로드했던 JPype 파일이 있는 경로로 이동한 후 아래와 같이 입력하여 설치할 수 있다.

 

pip install JPype1-0.7.1-cp37-cp37m-win_amd64.whl

 


 

이제 아래 명령문을 실행시켜 KoNLPy를 설치하자.

 

pip install konlpy

 

설치가 끝났다면 사용하는 편집기로 가서 아래 코드를 실행해봤을 때 에러가 나지 않는다면 성공한 것이다.

 

import konlpy

 

혹시나 아래와 같은 에러가 발생한다면 링크를 참고하여 해결할 수 있을 것이다.

 

Error occurred during initialization of VM
Could not reserve enough space for object heap
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

foreverhappiness.tistory.com/34

 

자바 가상머신 힙 메모리 공간 확장

자바로 큰 프로그램이나 수많은 데이터를 처리하다 보면 아래와 같은 에러 현상을 볼 수 있다. Error occurred during initialization of VM Could not reserve enough space for object heap Error: Could not cr..

foreverhappiness.tistory.com

 


 

여기까지 왔다면 이제 KoNLPy를 사용할 수 있다.

 

KoNLPy에는 Twitter (현재는 Okt로 바뀜), Komoran, Mecab (윈도우에서는 지원하지 않음), Kkma, Hannanum 등 여러 가지 형태소 분석기를 담고 있다. 

 

각각 장단점과 특징이 있으니 필요에 따라 사용하면 된다.

 

 

이 중에서 Twitter을 사용하려고 한다. 현재는 Okt로 명칭이 바뀌었다.

 

Okt를 사용하려면 먼저 아래와 같이 import 해줘야 한다.

 

from konlpy.tag import Okt

 

이제 이 모듈을 사용해서 수많은 데이터들을 여러 가지 형태소로 분석할 것이다.

각 형태소 별로 분류하는 작업을 태깅(Tagging)이라고 하는데 앞전에 분석했던 데이터를 사용하기 전에 간단한 예제를 통해 Okt의 기능을 알아보자.

 

from konlpy.tag import Okt

if __name__ == '__main__':
    # 각 형태소별로 분류(Tagging)해주는 Okt 객체를 불러온다.
    tagger = Okt()

    # pos함수를 통해 문자열을 각각 어떤 품사에 해당되는지 나누어 튜플 리스트 형태로 반환한다.
    part_of_speech = tagger.pos('KoNLPy에 오신 것을 환영합니다.')

    print(part_of_speech)

 

위 코드의 결과는 어떻게 될까?

먼저 tagger 변수에 Okt의 새로운 객체를 생성해주었다.

 

그리고 pos함수를 통해 파라미터로 넘겨준 문자열이 어떤 형태소에 해당되는지 튜플 리스트 형식으로 출력해보았다.

 

[('KoNLPy', 'Alpha'), ('에', 'Josa'), ('오신', 'Noun'), ('것', 'Noun'), ('을', 'Josa'), ('환영', 'Noun'), ('합니다', 'Verb'), ('.', 'Punctuation')]

 

결과는 다음과 같이 나온다.

 

리스트 안에는 형태소로 분류된 각 문자열과 그 품사가 튜플 형태로 저장된다.

 

이제 이 중 명사만 추출해내고 싶다면 다음과 같이 코드를 짜주면 된다.

 

from konlpy.tag import Okt

if __name__ == '__main__':
    # 각 형태소별로 분류(Tagging)해주는 Okt 객체를 불러온다.
    tagger = Okt()

    # nouns 함수를 통해 명사에 해당하는 부분만 리스트 형태로 반환한다.
    noun_list = tagger.nouns('KoNLPy에 오신 것을 환영합니다.')

    print(noun_list)

 

nouns함수를 사용하면 명사에 해당하는 부분만 리스트로 반환해준다.

 

결과는 아래와 같다.

 

['오신', '것', '환영']

 

데이터를 분석할 때는 아마 명사 이외에 다른 품사를 분류하는 일은 거의 없을 것이다.

 

만약 다른 품사에 해당하는 것들도 분류하고 싶다면 Okt에서는 제공하는 함수가 없으므로 따로 함수를 만들어 분류를 해야 한다.

 


 

이제는 앞서 크롤링했던 csv 데이터를 분류하려 한다.

 

명사만 추출해낼 것이며 추출해낸 데이터로 DTM (Document Term Matrix, 문서 단어 행렬)을 만들 것이다.

 

DTM, 문서 단어 행렬은 TDM(Term Document Matrix, 단어 문서 행렬)이라고도 한다.

 

각 문서별로 나타난 단어의 빈도수를 Matrix, 즉 행렬(표) 형태로 나타낸 것을 말한다.

 

DTM을 만들어주기 위해 Scikit-Learn(사이킷런)을 설치해줄 것이다.

 

빈도수를 체크해줄 때 파이썬에 기본적으로 내장되어 있는 Counter를 사용해도 되지만 머신러닝, 딥러닝을 학습하는데 아주 유용한 Scikit-Learn을 설치해두면 추후에도 사용할 일이 많을 것이다.

 

CMD, 명령 프롬프트로 돌아가서 아래 명령을 실행해주자.

 

pip install scikit-learn

 

이제 DTM을 만들어주기 위해 앞서 크롤링했던 csv 파일을 불러와야 한다.

크롤링했던 데이터 파일은 아래와 같은 형태로 6771행까지 들어있을 것이다.

 

 

제일 첫 행이 크롤링했던 데이터들의 타이틀에 해당하는 것이고, 그 아래부터 6771행까지 이에 대한 내용일 것이다.

 

이 중 타이틀명이 "화재 및 폭발 가능성"에 해당하는 내용들만 가져오려 한다.

 

효율적으로 작업하기 위해 이 내용들을 Title_list.txt 파일에 저장하였다.

 

 

이제 본격적으로 시작해보자.

 

작업할 순서는 다음과 같다.

 

  1. Title_List.txt 로부터 크롤링할 타이틀 목록을 가져온다.

  2. 앞서 크롤링한 csv파일을 pandas 모듈로 읽어온다.

  3. 이 중 타이틀 목록에 해당하는 데이터들에서 명사만 추출해낸 다음 6770개의 문서에 대해 DTM을 만든다.

 

import pandas as pd
from konlpy.tag import Okt
from sklearn.feature_extraction.text import CountVectorizer
# DTM을 편리하게 만들어주기 위해 Scikit-Learn에서 제공하는 CountVectorizer를 import 한다.


if __name__ == '__main__':
    # 타이틀 리스트를 불러와서 title_list 변수에 저장한다.
    t_file_name = open('D:\crawling\Title_List.txt', 'r', encoding='utf-8')

    title_list = []
    for line in t_file_name.readlines():
        # txt파일을 readlines로 불러오면 개행 문자도 함께 읽어오기 때문에 인덱싱으로 처리해준다.
        title_list.append(line[:-1])

    t_file_name.close()

    # pandas의 read_csv 함수를 이용하여 csv 파일을 불러온다.
    dataset = pd.read_csv('D:\crawling\crawling_data.csv')

    # 각 형태소별로 분류(Tagging)해주는 Okt 객체를 불러온다.
    tagger = Okt()

    for title in title_list:        # title_list에 대해 반복문을 실행
        # 각 타이틀에 대한 6770개 문서의 DTM을 표현하기 위해
        # CountVectorizer 객체를 선언
        cv = CountVectorizer()
        
        # 각 문서들의 말뭉치(corpus)를 저장할 리스트 선언
        corpus = []

        # 각 타이틀에 대한 문서들의 말 뭉치를 저장한다. (데이터가 많으면 이 부분에서 장시간이 소요될 수 있다.)
        for doc_num in range(6770):
            # 각 말뭉치에서 명사 리스트를 만든다.
            noun_list = tagger.nouns(dataset[title].loc[doc_num])
            
            # 이를 문자열로 저장해야하기 때문에 join함수로 공백으로 구분해 corpus에 append한다.
            corpus.append(' '.join(noun_list))

        # CountVectorizer의 fit_transform 함수를 통해 DTM을 한번에 생성할 수 있다.
        DTM_Array = cv.fit_transform(corpus).toarray()

        # feature_names 함수를 사용하면 DTM의 각 열(column)이 어떤 단어에 해당하는지 알 수 있다.
        feature_names = cv.get_feature_names()

        # 추출해낸 데이터를 DataFrame 형식으로 변환한다.
        DTM_DataFrmae = pd.DataFrame(DTM_Array, columns=feature_names)

        # 최종적으로 DTM을 csv 파일로 저장한다.
        DTM_DataFrmae.to_csv('D:\crawling\DTM.csv', encoding='utf-8-sig')

 

먼저 Title_List.txt에 있는 목록을 불러와서 title_list 변수에 저장한다.

이때 주의해야 할 점은 readlines를 통해 개행 문자를 기준으로 한 라인 씩 읽어 들일 수 있지만 개행 문자인 "\n"까지 같이 들어오기 때문에 인덱싱 처리를 통해서 개행 문자를 없애준다.

 

그리고 Pandas모듈의 read_csv 함수를 사용하여 csv를 불러오면 DataFrame형태로 불러올 수 있어서 행, 열에 대한 접근이 편하다.

 

csv파일을 불러왔다면 각 타이틀에 대해 모든 문서에 대한 말뭉치(corpus)를 저장해 줄 리스트를 생성한다.

 

반복문을 사용해 해당 타이틀에 대해 한 열씩, 즉 한 문서씩 데이터에 접근할 수 있으며 이 중 명사만 뽑아내어 corpus 리스트에 문자열 형태로 저장한다.

 

여기까지 했다면 corpus에는 다음과 같이 작업한 문자열 리스트가 들어있을 것이다.

 

크롤링했던 6770개 문서에서 모든 title에 대한 데이터 -> 크롤링했던 6770개 문서에서 하나의 title에 대해 명사만 분류해낸 데이터

 

즉, 이렇게 저장되어 있을 것이다.

['첫 번째 문서에서 title에 해당하는 명사들', 
 '두 번째 문서에서 title에 해당하는 명사들',
 ...
 ,'마지막 문서에서 title에 해당하는 명사들']

 

이제 이것을 CountVectorizer의 fit_transform 함수에 넣어주기만 하면 DTM 생성이 끝난다.

toarray 함수로 변환하여 출력해보면 다음과 같이 나올 것이다.

 

[첫 번째 문서에 대한 각 단어들의 빈도수 리스트, 
 두 번째 문서에 대한 각 단어들의 빈도수 리스트,
 ...
 마지막 문서에 대한 각 단어들의 빈도수 리스트]

 

이제 get_feature_names 함수를 통해 단어들을 리스트 형태로 받을 수 있다.

이 리스트를 columns(열 목록)로하여 DataFrame을 만들 수 있으며

 

최종적으로  DTM은 아래와 같은 형태를 띠고 있다.

 

 

이를 csv로 저장하면 형태소 분석을 통한 데이터 가공이 끝난 것이다.

 

참고로 아래 코드를 통해 각 단어가 몇 번째 인덱스에 속해있는지 알 수 있다.

 

print(cv.vocabulary_)

 


 

여기까지 제대로 왔다면 이제 정말 데이터 분석을 한다는 느낌이 들 것이다.

 

하지만 아직은 문제점이 있다.

방금 전에 가공한 데이터의 단어 수가 너무 많다는 점이다.

 

다음 포스팅에서는 데이터에서 유의미한 단어 토큰을 구별하기 위해 불용어(Stop Words) 처리에 대해 알아볼 것이다.