본문 바로가기

ChatGPT/인공지능

[자연어처리] 파이썬으로 CRF를 이용한 품사 태깅 구현하기

반응형

들어가며

본 포스트에서는 파이썬을 사용하여 조건부 랜덤 필드(Conditional Random Fields, CRF) 모델을 구현하여 품사 태깅(Part-of-Speech Tagging) 문제를 해결하는 방법에 대해 다루고자 합니다.

자연어 처리 분야에서 품사 태깅은 매우 중요한 작업 중 하나입니다. 이를 통해 문장의 구조를 파악하거나 문맥에 따른 단어의 의미를 구분하는 등의 다양한 작업을 수행할 수 있습니다.

이 포스트에서는 먼저 사용할 데이터셋과 CRF 모델의 개념에 대해 간략하게 소개한 후, 주요 코드 분석과 함께 구현 방법을 자세히 설명하겠습니다.

해당 코드는 nltk와 sklearn_crfsuite 라이브러리를 사용하며, Python 3.7 버전에서 작성되었습니다.

데이터셋 소개

본 포스트에서는 nltk에서 제공하는 Treebank 코퍼스를 사용하여 학습 및 테스트 데이터를 구성합니다. Treebank 코퍼스는 Wall Street Journal에서 발췌한 39,000개의 문장을 포함하며, 이들 문장은 Penn Treebank 품사 태그셋을 사용하여 태그가 지정되어 있습니다.

Penn Treebank 품사 태그셋은 약 45개의 태그로 구성되어 있으며, 대부분의 태그는 명사, 동사, 형용사, 부사, 전치사, 대명사, 한정사, 관사 등과 같은 품사를 나타내는 기능적인 태그입니다.

해당 코드에서는 Treebank 코퍼스에서 제공되는 태그셋을 사용하여 학습 및 테스트 데이터를 구성하며, 이를 활용하여 CRF 모델을 학습시키고 품사 태깅을 수행합니다.

품사 태깅 문제

자연어 처리 분야에서 품사 태깅이란 단어에 해당하는 품사를 태그로 붙이는 작업을 의미합니다. 이를 통해 문장의 구조를 파악하거나 문맥에 따른 단어의 의미를 구분하는 등의 다양한 작업을 수행할 수 있습니다.

예를 들어, "The cat is sitting on the mat"과 같은 문장에서, "The"는 한정사, "cat"과 "mat"은 명사, "is"와 "sitting"은 동사, "on"은 전치사를 나타내는 태그로 태깅됩니다. 이러한 태그 정보를 활용하여 자연어 처리 분야에서 다양한 작업을 수행할 수 있습니다.

품사 태깅은 자연어 처리 분야에서 매우 중요한 작업 중 하나로, 다양한 자연어 처리 애플리케이션에서 사용됩니다. 이러한 애플리케이션에는 문장의 의미를 이해하는 자연어 이해(Natural Language Understanding, NLU)나 검색 엔진에서의 정보 검색 등이 있습니다.

CRF 모델 소개

CRF 모델이란 시퀀스 레이블링(sequence labeling) 문제를 해결하기 위한 확률적 모델입니다. 시퀀스 레이블링 문제는 입력 시퀀스에 대해 각 위치마다 하나의 레이블을 예측하는 문제를 의미합니다.

예를 들어, 자연어 처리에서의 품사 태깅은 시퀀스 레이블링 문제입니다. 여기서 입력 시퀀스는 문장의 단어들이고, 각 위치에 해당하는 레이블은 해당 단어의 품사입니다.

CRF 모델은 입력 시퀀스와 출력 시퀀스 간의 조건부 확률을 모델링하는데, 이를 위해 입력 시퀀스의 각 위치에서 가능한 출력 레이블들의 확률 분포를 모델링합니다. CRF 모델은 또한 출력 레이블들 사이의 종속성을 모델링하기 때문에, 이전 위치에서 선택한 레이블이 현재 위치에서 선택할 수 있는 레이블에 영향을 줍니다.

CRF 모델은 품사 태깅 외에도 다양한 자연어 처리 문제에서 사용될 수 있으며, 이를 통해 다양한 자연어 처리 애플리케이션에서 높은 성능을 보입니다.

코드 분석

본 포스트에서는 CRF 모델을 이용하여 품사 태깅 문제를 해결하는 코드를 작성합니다. 해당 코드는 다음과 같은 단계로 구성됩니다:

  1. 데이터 전처리
    • nltk에서 제공하는 Treebank 코퍼스를 이용하여 학습 데이터를 구성합니다.
  2. 피처 추출
    • 학습 데이터에서 각 단어의 피처를 추출하는 함수(word2features)를 정의합니다.
  3. 학습 데이터 변환
    • 추출한 피처들을 이용하여 학습 데이터를 피처 리스트와 레이블 리스트로 변환합니다.
  4. CRF 모델 학습
    • 변환된 학습 데이터를 이용하여 CRF 모델을 학습시킵니다.
  5. 테스트 데이터 태깅
    • 새로운 문장에 대해 품사 태깅을 수행하는 코드를 작성합니다.

위의 단계들을 자세히 살펴보면, 학습 데이터에서 각 단어의 피처를 추출하고 이를 CRF 모델에 입력으로 제공하여 품사 태깅 문제를 해결하는 것을 알 수 있습니다. 이러한 방식으로 CRF 모델은 훌륭한 품사 태깅 성능을 보이며, 다양한 자연어 처리 애플리케이션에서 사용됩니다.

전체 코드

import nltk
import sklearn_crfsuite

# 학습 데이터 로드
tagged_sentences = nltk.corpus.treebank.tagged_sents()

# 피처 추출
def word2features(sent, i):
    word = sent[i][0]
    features = {
        'bias': 1.0,
        'word.lower()': word.lower(),
        'word[-3:]': word[-3:],
        'word[-2:]': word[-2:],
        'word.isupper()': word.isupper(),
        'word.istitle()': word.istitle(),
        'word.isdigit()': word.isdigit(),
    }
    if i > 0:
        prev_word = sent[i-1][0]
        features.update({
            'prev_word.lower()': prev_word.lower(),
            'prev_word.istitle()': prev_word.istitle(),
            'prev_word.isupper()': prev_word.isupper(),
        })
    else:
        features['BOS'] = True
    if i < len(sent)-1:
        next_word = sent[i+1][0]
        features.update({
            'next_word.lower()': next_word.lower(),
            'next_word.istitle()': next_word.istitle(),
            'next_word.isupper()': next_word.isupper(),
        })
    else:
        features['EOS'] = True
    return features

def sent2features(sent):
    return [word2features(sent, i) for i in range(len(sent))]

def sent2labels(sent):
    return [tag for word, tag in sent]

# 데이터 변환
X = [sent2features(s) for s in tagged_sentences]
y = [sent2labels(s) for s in tagged_sentences]

# CRF 모델 학습
crf_trainer = sklearn_crfsuite.CRF()
crf_trainer.fit(X, y)

# 테스트 문장 태깅
test_sentence = "This is a test sentence."
test_sent_features = sent2features(nltk.pos_tag(nltk.word_tokenize(test_sentence)))
tagged_words = crf_trainer.predict_single(test_sent_features)
print(list(zip(nltk.word_tokenize(test_sentence), tagged_words)))

먼저 코드에서 사용된 라이브러리를 살펴보겠습니다.

import nltk
import sklearn_crfsuite

 

해당 코드에서는 nltk와 sklearn_crfsuite 라이브러리를 사용합니다. nltk 라이브러리는 자연어 처리를 위한 파이썬 라이브러리로, 말뭉치, 형태소 분석, 품사 태깅 등 다양한 자연어 처리 기능을 제공합니다. sklearn_crfsuite 라이브러리는 CRF 모델을 구현하기 위한 라이브러리로, scikit-learn 라이브러리의 API를 따르며 CRF 모델을 쉽게 구현할 수 있습니다.

다음으로는 학습 데이터를 전처리하는 코드를 살펴보겠습니다.

tagged_sentences = nltk.corpus.treebank.tagged_sents()

해당 코드에서는 nltk 라이브러리에서 제공하는 Treebank 코퍼스를 사용하여 학습 데이터를 구성합니다. Treebank 코퍼스는 Wall Street Journal에서 발췌한 39,000개의 문장을 포함하며, 이들 문장은 Penn Treebank 품사 태그셋을 사용하여 태그가 지정되어 있습니다. tagged_sents() 함수를 사용하면 각 문장에 대해 토큰화와 품사 태깅이 수행된 리스트가 반환됩니다.

다음으로는 학습 데이터에서 각 단어의 피처를 추출하는 함수를 살펴보겠습니다.

def word2features(sent, i):
    word = sent[i][0]
    features = {
        'bias': 1.0,
        'word.lower()': word.lower(),
        'word[-3:]': word[-3:],
        'word[-2:]': word[-2:],
        'word.isupper()': word.isupper(),
        'word.istitle()': word.istitle(),
        'word.isdigit()': word.isdigit(),
    }
    if i > 0:
        prev_word = sent[i-1][0]
        features.update({
            'prev_word.lower()': prev_word.lower(),
            'prev_word.istitle()': prev_word.istitle(),
            'prev_word.isupper()': prev_word.isupper(),
        })
    else:
        features['BOS'] = True
    if i < len(sent)-1:
        next_word = sent[i+1][0]
        features.update({
            'next_word.lower()': next_word.lower(),
            'next_word.istitle()': next_word.istitle(),
            'next_word.isupper()': next_word.isupper(),
        })
    else:
        features['EOS'] = True
    return features

해당 함수에서는 sent 리스트에서 i 번째 단어의 피처를 추출합니다. 피처는 딕셔너리 형태로 정의되어 있으며, 다음과 같은 키-값 쌍으로 이루어져 있습니다.

  • 'bias': 상수값 1.0
  • 'word.lower()': 단어의 소문자 형태
  • 'word[-3:]': 단어의 뒤에서 3글자
  • 'word[-2:]': 단어의 뒤에서 2글자
  • 'word.isupper()': 단어가 모두 대문자인지 여부
  • 'word.istitle()': 단어가 제목어인지 여부 (첫 글자 대문자, 나머지 소문자)
  • 'word.isdigit()': 단어가 숫자인지 여부

또한 i가 0보다 큰 경우, 이전 단어의 정보를 추가합니다. 추가되는 피처는 다음과 같습니다.

  • 'prev_word.lower()': 이전 단어의 소문자 형태
  • 'prev_word.istitle()': 이전 단어가 제목어인지 여부
  • 'prev_word.isupper()': 이전 단어가 모두 대문자인지 여부

i가 리스트의 마지막 원소가 아닌 경우, 다음 단어의 정보를 추가합니다. 추가되는 피처는 다음과 같습니다.

  • 'next_word.lower()': 다음 단어의 소문자 형태
  • 'next_word.istitle()': 다음 단어가 제목어인지 여부
  • 'next_word.isupper()': 다음 단어가 모두 대문자인지 여부

마지막으로, i가 리스트의 마지막 원소인 경우에는 'EOS' 피처를 추가합니다. BOSEOS는 각각 문장의 시작과 끝을 나타내는 플래그로, CRF 모델에서는 이러한 정보를 활용하여 문장의 구조를 파악할 수 있습니다.

결론

본 포스트에서는 파이썬을 사용하여 CRF 모델을 구현하여 품사 태깅 문제를 해결하는 방법에 대해 다루었습니다. CRF 모델은 시퀀스 레이블링 문제를 해결하는 데 매우 효과적이며, 품사 태깅 외에도 다양한 자연어 처리 문제에서 사용될 수 있습니다.

해당 코드에서는 nltk와 sklearn_crfsuite 라이브러리를 사용하여 CRF 모델을 학습하고 새로운 문장에 대해 품사 태깅을 수행하는 방법을 다루었습니다. 이를 통해 자연어 처리 분야에서 품사 태깅 문제를 해결하는 방법에 대해 이해할 수 있었습니다.

자연어 처리 분야에서 품사 태깅은 매우 중요한 작업 중 하나이며, 이를 통해 다양한 자연어 처리 애플리케이션에서 높은 성능을 보일 수 있습니다. 따라서, CRF 모델과 같은 통계적 기법을 이용하여 품사 태깅 문제를 해결하는 것은 자연어 처리 분야에서 매우 유용합니다.

이 글을 ChatGPT의 도움을 받아 작성되었습니다.

반응형