머신러닝 & 딥러닝/NLP(자연어 처리)

[NLP] 한국어 텍스트 전처리 과정(2)

Haru_29 2023. 5. 12. 22:41

한국어 자연어 처리에 필요한 전처리 과정

1. 한국어에서 정수 인코딩하기 : 한국어에서 정수 인코딩은 자연어 처리 과정 중 하나로, 각 단어를 고유한 정수로 매핑하는 과정입니다. 이를 통해 모델이 텍스트 데이터를 처리하고 예측하는 데 용이해집니다. 정수 인코딩을 수행하기 위해서는 먼저 텍스트 데이터에 대해 토큰화와 같은 전처리 과정이 필요합니다. 이후, 각 단어에 고유한 인덱스를 부여하는 방법으로 정수 인코딩을 수행합니다.

가장 간단한 방법은 파이썬의 기본 자료형인 dict 이용하여 단어와 인덱스를 매핑하는 것입니다. 아래는 예시 코드입니다.

# 예시 문장
text = "나는 자연어 처리를 배운다"
# 문장을 띄어쓰기로 토큰화
tokens = text.split()
# 단어에 고유한 정수 인덱스 부여
word2idx = {}
for token in tokens:
    if token not in word2idx:
        word2idx[token] = len(word2idx)
# 결과 출력
print(word2idx)
{'나는': 0, '자연어': 1, '처리를': 2, '배운다': 3}

다만, 이렇게 단어와 정수 인덱스를 dict 직접 구현하는 방법은 비효율적입니다. 따라서 실제 자연어 처리에서는 더욱 효율적인 방법을 사용합니다. 예를 들어, Keras에서는 Tokenizer 클래스를 제공하여 토큰화와 정수 인코딩을 한번에 수행할 있습니다. 외에도 gensim 라이브러리에서도 Dictionary 클래스를 이용하여 단어와 정수 인덱스를 매핑할 있습니다.

 

2. 패딩은 자연어 처리에서 시퀀스 데이터의 길이를 일정하게 맞추는 과정으로, 모델 학습 시 입력 데이터의 크기를 동일하게 만들어주어야 하는 경우에 유용하게 사용됩니다. 한국어 자연어 처리에서도 패딩을 적용할 수 있습니다. 파이썬에서는 pad_sequences 함수를 사용하여 패딩을 수행할 수 있습니다. 이 함수는 Keras와 TensorFlow 패키지에서 모두 제공되며, 입력 데이터에 대해 지정한 길이로 패딩을 추가해주는 역할을 합니다. 아래의 코드는 한국어 문장을 토큰화한 패딩을 적용하는 예시 코드입니다.

from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.preprocessing.text import Tokenizer
# 입력 문장
texts = ["나는 자연어 처리를 배운다", "자연어 처리는 재미있다", "자연어 처리를 공부하고 있다"]
# 토큰화
tokenizer = Tokenizer()
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)
# 패딩
maxlen = 6 # 패딩을 추가한 이후의 시퀀스 길이 (최대 길이)
padded_seqs = pad_sequences(sequences, maxlen=maxlen, padding='post', truncating='post')
# 결과 출력
print("원래 시퀀스:", sequences)
print("패딩 후 시퀀스:", padded_seqs)
원래 시퀀스: [[1, 2, 3, 4], [3, 4, 5, 6], [3, 4, 1, 7, 8]]
패딩 후 시퀀스: 
[[1 2 3 4 0 0]
 [3 4 5 6 0 0]
 [3 4 1 7 8 0]]

3. 한국어 전처리 과정에서 원-핫 인코딩을 적용하는 방법은 영어와 동일합니다. 원-핫 인코딩은 단어를 벡터 형태로 변환하는 방법 중 하나로, 각 단어를 고유한 정수값으로 매핑한 뒤, 해당 정수값을 인덱스로 하는 위치에 1을 채우고, 나머지 위치에는 0을 채워서 벡터 형태로 변환합니다. 파이썬에서는 keras.preprocessing.text 패키지에서 제공하는 one_hot 함수를 사용하여 원-핫 인코딩을 적용할 수 있습니다.

아래의 예시는 한국어 문장을 토큰화하고 - 인코딩을 적용하는 예시 코드입니다.

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical
# 입력 문장
texts = ["나는 자연어 처리를 배운다", "자연어 처리는 재미있다", "자연어 처리를 공부하고 있다"]
# 토큰화
tokenizer = Tokenizer()
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)
# 원-핫 인코딩
vocab_size = len(tokenizer.word_index) + 1
one_hot_seqs = [to_categorical(seq, num_classes=vocab_size) for seq in sequences]
# 결과 출력
print("원래 시퀀스:", sequences)
print("원-핫 인코딩 후 시퀀스:", one_hot_seqs)
원래 시퀀스: [[3, 1, 2, 4], [1, 5, 6], [1, 2, 7, 8]]
원-핫 인코딩 후 시퀀스: [array([[0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0.]], dtype=float32), array([[0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 1., 0., 0.]], dtype=float32), array([[0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 1.]], dtype=float32)]

다만, 원-핫 인코딩 표현 방식은 아래와 같은 문제점이 있습니다.

 1) 단어의 개수가 늘어날 수록, 벡터를 저장하기 위해 필요한 공간이 계속 늘어난다는 단점이 있습니다.

 2) 원-핫 벡터는 단어의 유사도를 표현하지 못한다는 단점이 있습니다. 

 3) 단어 간 유사성을 알 수 없다는 단점은 검색 시스템 등에서는 문제가 될 소지가 있습니다.

 

이러한 단점을 해결하기 위해 단어의 잠재 의미를 반영하여 다차원 공간에 벡터화 하는 기법으로 크게 두 가지가 있습니다.

1) 카운트 기반의 벡터화 방법인 LSA(잠재 의미 분석), HAL 등이 있으며,

2) 예측 기반으로 벡터화하는 NNLM, RNNLM, Word2Vec, FastText 등이 있습니다.

3) 카운트 기반과 예측 기반 두 가지 방법을 모두 사용하는 방법으로 GloVe라는 방법이 존재합니다.

 

기타 한국어 전처리 과정

1. PyKoSpacing : 전희원님이 개발한 PyKoSpacing은 띄어쓰기가 되어있지 않은 문장을 띄어쓰기를 한 문장으로 변환해주는 패키지입니다.

 

2. Py-Hanspell : Py-Hanspell은 네이버 한글 맞춤법 검사기를 바탕으로 만들어진 패키지입니다.

 

3. SOYNLP

1) SOYNLP의 응집 확률(cohesion probability) : 응집 확률은 내부 문자열(substring)이 얼마나 응집하여 자주 등장하는지를 판단하는 척도입니다. 응집 확률은 문자열을 문자 단위로 분리하여 내부 문자열을 만드는 과정에서 왼쪽부터 순서대로 문자를 추가하면서 각 문자열이 주어졌을 때 그 다음 문자가 나올 확률을 계산하여 누적곱을 한 값입니다. 이 값이 높을수록 전체 코퍼스에서 이 문자열 시퀀스는 하나의 단어로 등장할 가능성이 높습니다. 수식은 아래와 같습니다.

2) SOYNLP의 브랜칭 엔트로피(branching entropy) : Branching Entropy는 확률 분포의 엔트로피값을 사용합니다. 이는 주어진 문자열에서 얼마나 다음 문자가 등장할 수 있는지를 판단하는 척도입니다. 

 

3) SOYNLP의 L tokenizer : 한국어는 띄어쓰기 단위로 나눈 어절 토큰은 주로 L 토큰 + R 토큰의 형식을 가질 때가 많습니다. 예를 들어서 '공원에'는 '공원 + 에'로 나눌 수 있겠지요. 또는 '공부하는'은 '공부 + 하는'으로 나눌 수도 있을 것입니다. L 토크나이저는 L 토큰 + R 토큰으로 나누되, 분리 기준을 점수가 가장 높은 L 토큰을 찾아내는 원리를 가지고 있습니다.

 

4) 최대 점수 토크나이저 : 최대 점수 토크나이저는 띄어쓰기가 되지 않는 문장에서 점수가 높은 글자 시퀀스를 순차적으로 찾아내는 토크나이저입니다.

 

5) 반복되는 문자 정제 : SNS나 채팅 데이터와 같은 한국어 데이터의 경우에는 ㅋㅋ, ㅎㅎ 등의 이모티콘의 경우 불필요하게 연속되는 경우가 많은데 ㅋㅋ, ㅋㅋㅋ, ㅋㅋㅋㅋ와 같은 경우를 모두 서로 다른 단어로 처리하는 것은 불필요합니다. 이에 반복되는 것은 하나로 정규화시켜줍니다.

 

4. Customized KoNLPy : 영어권 언어는 띄어쓰기만해도 단어들이 잘 분리되지만, 한국어는 그렇지 않습니다.

형태소 분석 입력 : '선영이는 사무실로 갔습니다.'
형태소 분석 결과 : ['선', '영이', '는', '사무실', '로', '갔습니다', '.']

 

위와 같은 결과에는 '선영이'는 사람 이름이므로 최소한 '선영'이라는 단어 토큰을 얻어야 합니다. 이때, '선영이'는 하나의 단어이므로 형태소 분리기에 알려줍니다.

from ckonlpy.tag import Twitter
twitter = Twitter()
twitter.add_dictionary('선영이', 'Noun')
twitter.morphs('선영이는 사무실로 갔습니다.')
['선영이', '는', '사무실', '로', '갔습니다', '.']

 

출처: https://wikidocs.net/21698