Intro
NLP에 대해서 제대로 공부하기 전에 NLU에 대해서 먼저 공부하는 이유는 NLP Task를 처리하기 위해서 비정형 Text Data를 Embedding하기 전 수행하는 전처리 과정에 대해서 보다 직관적으로 정리하고 이해할 수 있기 때문에 NLU를 먼저 정리하면서 공부합니다. 이 과정에 대해서 공부하고 나면 자연어의 전처리와 이후 분석 방향에 대한 방향을 잡을 수 있습니다.
형태소란?
자연어 처리를 위해서 우리는 원본 데이터를 어떤 기준에 의해서 분해하는 작업을 수행하게 됩니다. 그중 형태소 분해는 형태소를 기준으로 데이터를 분해하는 과정을 이르는 말입니다. 형태소란 의미를 가진 가장 작은 말의 단위입니다. 자연어의 문맥(Context)를 이해하기 위해서 의미 단위의 토큰을 담기 위해 형태소로 문장을 분해하는 작업이 필요합니다. 형태소는 크게 어근과 접사(조사, 어미)로 구분합니다.
- 어근:
- 의미를 가진 기본 단어 (예: 친구, 선물, 주)
- 접사:
- 조사: 명사에 붙어 문법적 관계를 나타내는 기능 (예: 에게, 을)
- 어미: 동사나 형용사에 붙어 시제나 서술 등을 나타내는 기능 (예: 었, 다)
다음 예시를 통해 한번 더 살펴보겠습니다.
'친구에게 선물을 주었다.' 라는 예문을 형태소로 분해해보면 다음과 같습니다.
원래 문장
: 친구에게 선물을 주었다
형태소 분해
- 친구 (어근, 명사)에게 (조사, 목적격 조사)
- 선물 (어근, 명사)을 (조사, 목적격 조사)
- 주 (어근, 동사 어간)었 (어미, 과거 시제)다 (어미, 서술 어미)
분해 결과
:
결과(
(친구, [어근, 명사]),
(에게, [조사, 목적격]),
(선물, [어근, 명사]),
(을,[조사, 목적격]),
(주, [어근, 동사]),
(었,[어미, 과거]),
(다,[어미, 서술])
)
단순히 어근과 접사로 구분하지 않고 세부적으로 나눠서 형태소 간의 관계나 특정 형태소의 빈도 등의 분석을 추가로 진행해볼 수 있습니다.
한글로 작성된 Text Data를 형태소로 나누려면 어떻게 해야하는가?
1. 데이터가 문장으로 나뉘어져 있는가? (Sentence Based Decompostion)
기본적으로 데이터가 문장 단위로 나뉘어져있는지 확인한 후에 문단 그 이상의 데이터로 만들어져있다면 데이터를 문장단위로 나눕니다. 보통 문장은 " . " 으로 구분된다는 점을 활용해서 직접 분해할 수도 있으며 모듈을 활용해서 분해할 수도 있습니다. 단 모듈을 사용할 때는 " . "이 문장의 끝을 나타내지않는 경우도 존재할 수 있어서 후처리를 통해 정확하게 문장만 분기하는지 확인해야합니다.
# 필요한 라이브러리 임포트
import nltk
from konlpy.tag import Kkma, Mecab, Okt
from nltk.tokenize import sent_tokenize, word_tokenize
from nltk import pos_tag
# NLTK 데이터 다운로드
nltk.download('punkt') # punkt tokenizer 다운로드
nltk.download('averaged_perceptron_tagger') # 품사 태깅용
# 예시 텍스트 (한글 문장)
text = """
친구에게 선물을 주었다. 나는 밥을 먹었다. 오늘은 날씨가 좋다. 내일은 비가 올 것이다.
"""
# === 문장 분리 ===
# 1. NLTK - 문장 분리
nltk_sentences = sent_tokenize(text)
print("NLTK 문장 분리:")
print(nltk_sentences)
# 2. Kkma - 문장 분리
kkma = Kkma()
kkma_sentences = kkma.sentences(text)
print("\nKkma 문장 분리:")
print(kkma_sentences)
# 3. Mecab - 문장 분리
mecab = Mecab()
mecab_sentences = mecab.sentences(text)
print("\nMecab 문장 분리:")
print(mecab_sentences)
# 4. Okt - 문장 분리 (Okt는 문장 분리 기능을 제공하지 않으므로 다른 방법을 사용)
sentences = text.split('.')
print("\nOkt 문장 분리 (구두점 기준):")
for i, sentence in enumerate(sentences, 1):
print(f"문장 {i}: {sentence.strip()}")
2. 문장으로 구분된 데이터에 대해서 형태소로 라벨링해줍니다.
문장에 대해서 형태소 분해를 할 때 원리는 문장을 구성하는 여러 형태소를 직접 분해하고 라벨링해주는 것과 동일하지만 이를 하나하나 하는 것은 ... 시간 소모가 너무 심하기에 빠르게 수행할 수 있는 모듈을 활용하겠습니다.
# === 형태소 분리 ===
# 1. NLTK - 단어 토큰화
nltk_tokens = word_tokenize(text)
print("\nNLTK 형태소 분리 (단어 토큰화):")
print(nltk_tokens)
# 2. Kkma - 형태소 분석
kkma_morphemes = kkma.morphs(text)
print("\nKkma 형태소 분리:")
print(kkma_morphemes)
# 3. Mecab - 형태소 분석
mecab_morphemes = mecab.morphs(text)
print("\nMecab 형태소 분리:")
print(mecab_morphemes)
# 4. Okt - 형태소 분석
okt = Okt()
okt_morphemes = okt.morphs(text)
print("\nOkt 형태소 분리:")
print(okt_morphemes)
3. 결과확인 후 후처리
아래 결과를 확인해보고 후처리를 진행하시면 됩니다. 자연어에 대한 전처리는 반드시 형태소 분해를 해서 Token으로 만들어야하는 것은 아닙니다. 분석 목표에 맞게 적절한 Token의 형태를 정하고 이에 맞게 Text데이터를 자르는 겁니다. 이런 과정을 Tokenization이라고 합니다. 먼저 막 끌어온 말뭉치(Corpus)를 명사만 추출해서 빈도분석(목표)하기 위해 말뭉치를 문장으로 자르고(Sentence Decompostion)이를 다시 한번 형태소로 자르는 과정(Morphological Decompostion)을 거쳐서 형태소(Token)을 만들었습니다. 띄어쓰기(word)를 기준으로 음절(Character)을 기준으로도 얼마든지 나눌 수 있으며 Tokenizer는 입력되는 데이터를 고려하여 요구되는 수준에 따라 적절하게 활용하여 전처리하면 됩니다. 모듈마다 지원해주는 결과에 차이가 있을 수 있으니 모듈을 통해 처리한 이후에 원하는 형태로 처리되었는지 반드시 확인하여 결과 데이터에 대한 후가공을 진행해주시기 바랍니다.
NLTK 문장 분리:
['친구에게 선물을 주었다.', '나는 밥을 먹었다.', '오늘은 날씨가 좋다.', '내일은 비가 올 것이다.']
Kkma 문장 분리:
['친구에게 선물을 주었다.', '나는 밥을 먹었다.', '오늘은 날씨가 좋다.', '내일은 비가 올 것이다.']
Mecab 문장 분리:
['친구에게 선물을 주었다.', '나는 밥을 먹었다.', '오늘은 날씨가 좋다.', '내일은 비가 올 것이다.']
Okt 문장 분리 (구두점 기준):
문장 1: 친구에게 선물을 주었다
문장 2: 나는 밥을 먹었다
문장 3: 오늘은 날씨가 좋다
문장 4: 내일은 비가 올 것이다
NLTK 형태소 분리 (단어 토큰화):
['친구에게', '선물을', '주었다', '.', '나는', '밥을', '먹었다', '.', '오늘은', '날씨가', '좋다', '.', '내일은', '비가', '올', '것이다', '.']
Kkma 형태소 분리:
['친구', '에게', '선물', '을', '주', '었', '다']
Mecab 형태소 분리:
['친구', '에게', '선물', '을', '주', '었', '다']
Okt 형태소 분리:
['친구', '에게', '선물', '을', '주었다', '.', '나', '는', '밥', '을', '먹었다', '.', '오늘', '은', '날씨', '가', '좋다', '.', '내일', '은', '비', '가', '올', '것', '이다', '.']
NLU - 형태소 분석(명사 빈도분석)
NLU에서 데이터를 형태소로 나눠서 가장 기본적으로 수행할 수 있는 분석은 형태소로 라벨링된 데이터 중에서도 명사만 추출하여 그 빈도를 비교하는 분석입니다. 보통 이를 시각화 하는 자료로 Bar Chart나 word cloud를 활용합니다. 빈도가 높다는 것은 해당 Corpus에서 주로 다루고 있는 Keword를 유추해볼 수 있는 장점이 있습니다.
import matplotlib.pyplot as plt
from wordcloud import WordCloud
from konlpy.tag import Okt
from collections import Counter
# 예시 텍스트 (형태소 분석을 거친 텍스트)
text = """
친구에게 선물을 주었다. 나는 밥을 먹었다. 오늘은 날씨가 좋다. 내일은 비가 올 것이다.
오늘은 정말 기분이 좋았다. 내일은 친구랑 영화를 보러 갈 예정이다.
"""
# 1. Okt를 사용하여 품사 태깅
okt = Okt()
tagged_words = okt.pos(text)
# 2. 품사 태깅된 값 확인
# 품사 태깅 결과 예시 (text = "친구에게 선물을 주었다.")
# [('친구', 'Noun'), ('에게', 'Josa'), ('선물', 'Noun'), ('을', 'Josa'), ('주었다', 'Verb')]
print("품사 태깅 결과:")
print(tagged_words)
# 3. 명사만 추출하기 (태깅된 값 중 'Noun' 품사만 선택)
nouns = [word for word, pos in tagged_words if pos == 'Noun']
# 4. 명사의 빈도수 계산
nouns_counter = Counter(nouns)
# 5. 워드클라우드 그리기
wordcloud = WordCloud(font_path='malgun.ttf', width=800, height=400, background_color='white').generate_from_frequencies(nouns_counter)
# 워드클라우드 출력
plt.figure(figsize=(10, 6))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
# 6. Top 20 명사 추출
top_20_nouns = nouns_counter.most_common(20)
# 7. Top 20 명사에 대한 Bar Chart
labels, values = zip(*top_20_nouns)
plt.figure(figsize=(10, 6))
plt.barh(labels, values, color='skyblue')
plt.xlabel('Frequency')
plt.ylabel('Nouns')
plt.title('Top 20 Nouns Frequency')
plt.gca().invert_yaxis() # y축을 반대로 뒤집어 내림차순으로 표시
plt.show()
Outro
이번 칼럼을 통해 간단하게 문장을 분해하는 방법과 그 중에서 가장 최소 의미단위로 나눠서 이를 Token으로 활용하는 방법에 대해서 정리해보았습니다. 또한 명사로 태깅된 값들에 대해서 빈도 추출하여 이를 시각화하는 기본적인 형태소 분석을 통해 NLU까지 수행해봤습니다. NLU의 결과 Corpus의 기술통계량을 통해 정량/정석적인 분석으로 이어질 수 있습니다.
인스타 주소 🎗
'MLOps > NLP' 카테고리의 다른 글
[NLU] 의미망 분석(Semantic Network Analysis)을 통한 관계 파악 (0) | 2024.11.10 |
---|---|
[NLU] Doc에 대한 감성 분석(Sentiment Analysis) (1) | 2024.11.10 |
[NLP] Retriver에서 사용되는 비교분석(Comparative Analysis) (0) | 2024.11.10 |
[NLU] LOR과 TF-IDF를 통한 비교분석(Comparative Analysis) (0) | 2024.11.10 |
NLP에 대한 기본 이해 (1) | 2024.11.09 |