정수 인코딩(Integer Encoding)

애늙은이·2023년 8월 29일
0

NLP 여행기

목록 보기
10/13
post-thumbnail

컴퓨터가 텍스트를 효율적으로 처리하는 방법에는 무엇이 있을까요?
컴퓨터는 텍스트보다 숫자를 효율적으로 처리합니다. 텍스트도 인코딩되어 인식되기 때문이죠. 그렇기에 자연어 처리에서는 텍스트를 숫자와 대응시켜 컴퓨터가 쉽게 처리할 수 있겠끔 변환하는 과정이 필요합니다. 이러한 과정 중 하나가 바로 정수 인코딩(Integer Encoding)입니다.

💡 텍스트 벡터화(Text Vectorization)

정수 인코딩처럼 텍스트를 컴퓨터가 쉽게 처리할 수 있도록 수치화해주는 과정을 텍스트 벡터화라고 합니다. 이번 시간에 배울 정수 인코딩이나 원-핫 인코딩, TF-IDF 등이 텍스트 벡터화에 해당합니다.

🤔 정수 인코딩이란?

정수 인코딩이란 단어에 정수를 일대일로 대응시켜 컴퓨터가 쉽게 처리할 수 있게 만드는 작업을 말합니다. 예를 들어, "I have many ideas."라는 문장이 있다 하면, i에는 12번, have에는 10번, many엔 5번, ideas에는 17번을 지정하는 것이죠.

정수 인코딩을 통해 모든 단어를 숫자로 바꾸기 위해서는 그만큼의 숫자가 필요합니다. 만약 100개의 단어로 이루어진 텍스트가 있다고 하면, 1번부터 100번까지 번호를 부여해야 하는 것이죠. 때문에 정수 인코딩은 단어 집합(Vocabulary) 내에서 빈도수가 높은 상위 몇 개의 단어만 숫자로 변환하고, 나머지는 OOV(Out Of Vocabulary)로 처리하는 경우가 많습니다.

✔ 단어 집합이란?

단어 집합(Vocabulary)은 코퍼스 내 모든 텍스트 데이터를 토큰화한 후, 중복을 제거한 집합을 말합니다.

예를 들어, 빈도수 상위 5개의 단어에 한정하여 정수 인코딩을 진행한다고 하면 다음과 같이 이루어질 수 있습니다.

{'word1': 1, 'word2': 2, 'word3': 3, 'word4': 4, 'word5':5, 'OOV': 6}

💻 정수 인코딩의 구현

만약 아래와 같은 텍스트로 정수 인코딩을 한다 가정한다면, 다음과 같은 과정으로 이루어집니다.

txt = """
    The sun rose early in the morning, casting a warm glow over the tranquil lake.
    Birds chirped happily as they greeted the new day, their new songs echoing through the stillness.
    A gentle breeze rustled the leaves in the nearby trees, creating a soothing melody.
    As I sat on the wooden dock, I sipped my coffee and watched the ripples on the water.
    In the distance, a fisherman in a small boat cast his line, hoping for a bountiful catch.
    The scent of wildflowers from the meadow nearby wafted towards me, filling the day with fragrance.
    A family of ducks swam by, their ducklings following closely in a neat row.
    A pair of swans glided gracefully across the lake, their white plumage shimmering in the morning light.
    I felt a sense of peace and contentment, grateful for this moment of serenity.
    As a new day unfolded, I knew it would bring new adventures and challenges, but for now, I embraced the beauty of the present.
    """

우선 텍스트를 문장 별로 나누고, 전처리를 진행해줍니다.

from nltk.tokenize import word_tokenize
from nltk.tokenize import sent_tokenize
from nltk.corpus import stopwords

min_len = 2 # 길이가 2 이하의 단어는 제거합니다.
stopword_lst = stopwords.words('english')

txt = txt.strip() # 좌우 공백을 제거해줍니다.
txt = txt.lower() # 대소문자 구별을 없애줍니다.
sent_lst = sent_tokenize(txt) # 문장 별로 나누어줍니다.

tokens = []
for sent in sent_lst:
	word_lst = word_tokenize(sent)
    temp = []
    for word in word_lst:
    	if len(word) > min_len and word not in stopword_lst:
        	temp.append(word) # 글자수 2 초과, 불용어 리스트에 없는 단어만 필터링합니다.
    tokens.append(temp)

빈도수 순으로 번호를 부여하기 위해 빈도수를 측정합니다.

from nltk import FreqDist

freq = FreqDist(sum(tokens, [])) # sum(tokens, []): FreqDist 사용을 위해 이중리스트를 단일 리스트로 바꿔줘야 합니다.
vocab = freq.most_common(5) # 빈도수 상위 다섯 개의 단어만 정수 인코딩을 사용합니다.

word_index = {word: i + 1 for i, (word, freq) in enumerate(vocab)}
word_index['OOV'] = len(word_index) + 1
print(word_index) 


# 결과
# {'new': 1, 'day': 2, 'morning': 3, 'lake': 4, 'nearby': 5, 'OOV': 6}

word_index를 활용하여 기존의 문장을 정수로 인코딩합니다.

encoding_result = []
for sent in tokens:
	temp = []
	for token in sent:
    	if token in word_index.keys():
        	temp.append(word_index[token])
        else:
        	temp.append(word_index['OOV'])
    encoding_result.append(temp)

print(encoding_result)


# 결과
# [[6, 6, 6, 3, 6, 6, 6, 6, 4], [6, 6, 6, 6, 1, 2, 1, 6, 6, 6], [6, 6, 6, 6, 5, 6, 6, 6, 6], [6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 5, 6, 6, 6, 2, 6], [6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 6, 6, 4, 6, 6, 6, 3, 6], [6, 6, 6, 6, 6, 6, 6], [1, 2, 6, 6, 6, 6, 1, 6, 6, 6, 6, 6]]

함수로 정리하면 다음과 같습니다.

from nltk.tokenize import word_tokenize, sent_tokenize
from nltk.corpus import stopwords
from nltk import FreqDist

def integer_encoding(txt: str, min_len: int =2, top: int =5) -> list[list[int]]:
	stopword_lst = stopwords.words('english')

    sent_lst: list[str] = sent_tokenize(txt.strip().lower())    
    tokens: list[list[str]] =  [[word for word in word_tokenize(sent) if len(word) > min_len and word not in stopword_lst] for sent in sent_lst]
	
    vocab: dict[str, int] = FreqDist(sum(tokens, [])).most_common(top)
    word_index: dict[str, int] = {word: i + 1 for i, (word, freq) in enumerate(vocab)}
    word_index["OOV"] = len(word_index) + 1
    
    result: list[list[int]] = [[word_index[word] if word in word_index.keys() else word_index['OOV'] for word in sent ] for sent in tokens]
    
    return result
profile
글쓰는 개발자입니다.

0개의 댓글