AIFFEL/Going Deeper(NLP)

[Going Deeper(NLP)] 1. 텍스트 데이터 다루기

알밤바 2022. 3. 15. 18:44
728x90
반응형

해당 포스팅은 AIFFEL에서 제공한 학습자료를 통해 공부한 것을 정리한 것임을 밝힙니다.

학습목표

  • 분산 표현에 대한 직관적 이해를 얻는다.
  • 문장 데이터를 정제하는 방법을 배운다.
  • 토큰화의 여러 가지 기법들을 배운다.
  • 단어 Embedding을 구축하는 방법에 대해 가볍게 맛본다.

1. 들어가며

자연어(Natural Language)는 일상에서 자연히 발생하여 쓰이는 언어를 뜻한다. 

자연어와 반대되는 언어 중 하나는 프로그래밍 언어라고 할 수 있다. 이 두 개의 언어의 본질적인 차이는 자연어는 '문맥 의존 문법(Context-sensitive Grammar), 프로그래밍 언어는 '문맥 자유 문법(Context-free Grammar)'이라는 각 문법을 따른다는 것이다.

기계적으로 언어를 해석하는 파서를 만들었을 때, 해석상 의미의 모호성이 생기지 않으려면 '문맥 자유 문법(Context-free Grammar)'을 따르는 언어여야 한다. 그래야 모호성이 없이 파싱트리를 만들 수 있다.

하지만 자연어의 경우 문맥에 따라 얼마든지 해석이 달라질 수있다.

그러므로 자연어 처리 모델을 만들기 위해서, 단순히 언어의 문법만으로 충분하지 않고 그 언어의 의미까지 이해하는 과정이 필요하다.

자연어를 의미 단위로 쪼개는 토큰화(Tokenization) 기법은 자연어처리 모델의 성능에 결정적 영향을 미친다. 특히 Wordpiece Model 같은 Subword level의 처리 기법은 최신 자연어처리 모델에 필수적으로 자리 잡고 있으므로, 이러한 아이디어가 나오기까지의 자연어 전처리, 토큰화 과정을 이해하는 것은 매우 중요하다.

 

[+] 임베딩(Embedding)이란?

임베딩(embedding)은 자연어를 숫자의 나열인 벡터로 바꾼 결과 혹은 그 일련의 과정 전체를 가리키는 용어

 

한국어 임베딩 · ratsgo's blog

임베딩(embedding)은 자연어를 숫자의 나열인 벡터로 바꾼 결과 혹은 그 일련의 과정 전체를 가리키는 용어입니다. 단어나 문장 각각을 벡터로 변환해 벡터 공간에 ‘끼워 넣는다(embed)’는 취지에

ratsgo.github.io


2. 전처리 : 자연어의 노이즈 제거

2.1 이상적이지 않은 데이터에서 발견할 수 있는 노이즈 유형

① 불완전한 문장으로 구성된 대화

메신저의 경우, 한 문장을 여러 번에 나눠 전송하거나 여러 문장을 한 번에 전송하는 경우가 있다.

 

② 문장의 길이가 너무 길거나 짧은 경우

'ㅋㅋㅋ, ㅠㅠㅠ'와 같은 리액션에 해당하는 경우는 언어 모델을 왜곡시킬 우려가 있기에 제외하는 것이 좋다.

또한 아주 긴 문장은 대화와는 관계가 없는 문장일 수도 있다.

 

③ 채팅 데이터에서 문장 시간 간격이 너무 짧거나 긴 경우

메신저의 경우, 누가 지금 자판을 치고 있는지 모르기에 서로의 말이 얽히는 경우가 있다. 

말의 간격이 짧으면 더욱 말이 얽히고 서로 할 말만 하는 경우일 수도 있다. 말의 간격이 길면 연속된 대화로 보기 어렵다.

 

④ 바람직하지 않은 문장의 사용

욕설 또는 오타의 비율이 높은 문장은 자연어 모델 학습에 사용하지 않은 것이 좋다.

 

2.2 노이즈 유형(1) 문장부호

Hi, 가 Hi 와 , 의 결합인 것을 우리는 알지만 컴퓨터에 명시하지 않는다면 알파벳에 ,가 포함되어있다고 생각할 수 있다. 그렇기에 문장 부호 양쪽에 공백을 추가하여 문장부호를 단어와 분리하면 해결이 된다.

def pad_punctuation(sentence, punc):
    for p in punc:
        sentence = sentence.replace(p, " " + p + " ")

    return sentence

sentence = "Hi, my name is john."

print(pad_punctuation(sentence, [".", "?", "!", ","]))

Hi ,  my name is john . 

 

 

2.3 노이즈 유형(2) 대소문자

영어에서 발생하는 문제로 'First'와 'first'는 같은 의미를 가지고 있지만 대소문자 차이로 컴퓨터는 다르다고 판단할 수 있다.  이를 방지하기 위해 모든 문자를 소문자로 바꾸는 방법을 사용하면 된다.

# 모든 단어를 소문자로 바꾸는 방법
sentence = "First, open the first chapter."

print(sentence.lower())

first, open the first chapter.

 

# 모든 단어를 대문자로 바꾸는 방법
sentence = "First, open the first chapter."

print(sentence.upper())

FIRST, OPEN THE FIRST CHAPTER.

 

2.4 노이즈 유형(3) 특수문자

'ten-year-old' 또는 수많은 나이 표현들을 각각의 단어 취급을 할 수 있기에 이것을 방지하기 위해 특수문자를 제거하려고 한다. 하지만 모든 특수문자를 정의하여 제거할 수 없으니 우리가 사용하고자 하는 알파벳과 기호를 정의하여 이를 제외하고는 모두 제거하도록 한다.

import re   # 정규표현식 사용을 도와주는 패키지

sentence = "He is a ten-year-old boy."
sentence = re.sub("([^a-zA-Z.,?!])", " ", sentence)

print(sentence)

He is a ten year old boy.


3. 분산표현 : 바나나와 사과의 관계를 어떻게 표현할까?

3.1 희소 표현

희소 표현 방식은 벡터의 각 차원마다 단어의 특정 의미 속성을 대응시키는 방식이다.

 

[ex]

성별로 나눔 → 남자 : [-1], 여자 : [1]로 표현

소년과 소녀는? (성별 + 나이 속성) → 소년 : [-1, -1], 소녀 : [1, -1]로 표현

할아버지와 할머니는? (성별 + 나이 속성) → 할아버지 : [-1, 1]. 할머니[1, 1]로 표현

그렇다면 아저씨와 아줌마는? → 속성값들이 정수가 아닌 실수라면 표현이 가능하지 않을까!

▼▼▼

{
    //     [성별, 연령]
    남자: [-1.0, 0.0], // 이를테면 0.0 이 "관계없음 또는 중립적" 을 의미할 수 있겠죠!
    여자: [1.0, 0.0],
    소년: [-1.0, -0.7],
    소녀: [1.0, -0.7],
    할머니: [1.0, 0.7],
    할아버지: [-1.0, 0.7],
    아저씨: [-1.0, 0.2],
    아줌마: [1.0, 0.2]
}

희소 표현은 단어를 고차원 벡터로 변환하는 것이다. 

그리고 사람을 나이와 성별로 구분하기 위해선 적어도 2차원이 필요하다는 것도 알 수 있다.

 

위의 예시는 모든 단어가 '사람'이라는 속성을 가진다고 전제 하에 희소 표현을 하고 있다. 하지만 세상엔 다양한 속성이 있다.

그렇기에 '과일'이라는 속성을 추가하고 '색깔' 속성을 추가하여 구분해보자.

[ex]

{
// [성별, 연령, 과일, 색깔]
남자: [-1.0, 0.0, 0.0, 0.0],
여자: [1.0, 0.0, 0.0, 0.0],
사과: [0.0, 0.0, 1.0, 0.5], // 빨갛게 잘 익은 사과
바나나: [0.0, 0.0, 1.0, -0.5] // 노랗게 잘 익은 바나나
}

속성의 종류가 늘어나고 워드 벡터의 차원이 따라서 늘어나기 시작하니 벡터에서 '0.0'이 자주 나오는 것을 확인할 수 있다.

엄청난 고 차원의 벡터를 이런 방식으로 만들어낼 경우, 워드 벡터의 대부분의 차원은 0.0으로 채워진 희소 표현이 만들어질 것이다. 또, 희소 표현의 워드 벡터끼리는 단어들 간의 의미적 유사도를 계산할 수 없다는 점이다.

 

두 고차원 벡터의 유사도는 코사인 유사도(Cosine Similarity)를 통해 구할 수 있다.

공유하는 의미속성이 없는 두 벡터의 내적은 0이 되므로, 코사인 유사도도 역시 0이 된다. 이렇게 되면 희소표현으로 본 두 단어 사이에는 아무런 의미적 유사도가 없게 된다.

 

1) 코사인 유사도(Cosine Similarity)

BoW에 기반한 단어 표현 방법인 DTM, TF-IDF, 또는 뒤에서 배우게 될 Word2Vec 등과 같이 단어를 수치화할 수 있는 방법을 이해했다면 이러한 표현 방법에 대 ...

wikidocs.net

 

3.2 분산 표현

Embedding 레이어를 사용해 각 단어가 몇 차원의 속성을 가질지 정의하는 방식으로 단어의 분산 표현을 구현하는 방식을 주로 사용한다.

 

[ex]

고양이와 개를 분산 표현으로 나타내보았다.

(출처 : https://needjarvis.tistory.com/664)

Cat [0.583, 0.157, 0.947]
Dog [0.583, 0.347, 0.934]

 

만약 100개의 단어를 256차원의 속성을 표현하고 싶다면 Embedding 레이어는 아래와 같이 정의된다.

embedding_layer = tf.keras.layers.Embedding(input_dim=100, output_dim=256)

분산표현에는 우리가 일일이 정의할 수 없는 추상적인 속성들이 256차원 안에 골고루 분산되어 표현이된다.

희소표현처럼 속성값을 임의로 지정해주는 것이 아니라 수많은 텍스트 데이터를 읽어가며 적합한 값을 찾아간다.

 

훈련된 분산 표현 모델을 통해 우리는 단어 간의 의미 유사도를 계산하거나 이를 feature로 삼아 복잡한 자연어처리 모델을 훈련시킬 수 있게 된다.

 


4. 토큰화 : 그녀는? 그녀+는?

문장을 어떤 기준으로 쪼개었을 때, 쪼개진 각 단어들을 '토큰(Token)'이라고 부른다. 그리고 그 쪼개진 기준이 '토큰화(Tokenization) 기법'에 의해 정해진다.

 

4.1 공백 기반 토큰화

corpus = \
"""
in the days that followed i learned to spell in this uncomprehending way a great many words ,  among them pin ,  hat ,  cup and a few verbs like sit ,  stand and walk .  
but my teacher had been with me several weeks before i understood that everything has a name . 
one day ,  we walked down the path to the well house ,  attracted by the fragrance of the honeysuckle with which it was covered .  
some one was drawing water and my teacher placed my hand under the spout .  
as the cool stream gushed over one hand she spelled into the other the word water ,  first slowly ,  then rapidly .  
i stood still ,  my whole attention fixed upon the motions of her fingers .  
suddenly i felt a misty consciousness as of something forgotten a thrill of returning thought  and somehow the mystery of language was revealed to me .  
i knew then that  w a t e r  meant the wonderful cool something that was flowing over my hand .  
that living word awakened my soul ,  gave it light ,  hope ,  joy ,  set it free !  
there were barriers still ,  it is true ,  but barriers that could in time be swept away . 
"""

tokens = corpus.split()

print("문장이 포함하는 Tokens:", tokens)

문장이 포함하는 Tokens: ['in', 'the', 'days', 'that', 'followed', 'i', 'learned', 'to', 'spell', 'in', 'this', 'uncomprehending', 'way', 'a', 'great', 'many', 'words', ',', 'among', 'them', 'pin', ',', 'hat', ',', 'cup', 'and', 'a', 'few', 'verbs', 'like', 'sit', ',', 'stand', 'and', 'walk', '.', 'but', 'my', 'teacher', 'had', 'been', 'with', 'me', 'several', 'weeks', 'before', 'i', 'understood', 'that', 'everything', 'has', 'a', 'name', '.', 'one', 'day', ',', 'we', 'walked', 'down', 'the', 'path', 'to', 'the', 'well', 'house', ',', 'attracted', 'by', 'the', 'fragrance', 'of', 'the', 'honeysuckle', 'with', 'which', 'it', 'was', 'covered', '.', 'some', 'one', 'was', 'drawing', 'water', 'and', 'my', 'teacher', 'placed', 'my', 'hand', 'under', 'the', 'spout', '.', 'as', 'the', 'cool', 'stream', 'gushed', 'over', 'one', 'hand', 'she', 'spelled', 'into', 'the', 'other', 'the', 'word', 'water', ',', 'first', 'slowly', ',', 'then', 'rapidly', '.', 'i', 'stood', 'still', ',', 'my', 'whole', 'attention', 'fixed', 'upon', 'the', 'motions', 'of', 'her', 'fingers', '.', 'suddenly', 'i', 'felt', 'a', 'misty', 'consciousness', 'as', 'of', 'something', 'forgotten', 'a', 'thrill', 'of', 'returning', 'thought', 'and', 'somehow', 'the', 'mystery', 'of', 'language', 'was', 'revealed', 'to', 'me', '.', 'i', 'knew', 'then', 'that', 'w', 'a', 't', 'e', 'r', 'meant', 'the', 'wonderful', 'cool', 'something', 'that', 'was', 'flowing', 'over', 'my', 'hand', '.', 'that', 'living', 'word', 'awakened', 'my', 'soul', ',', 'gave', 'it', 'light', ',', 'hope', ',', 'joy', ',', 'set', 'it', 'free', '!', 'there', 'were', 'barriers', 'still', ',', 'it', 'is', 'true', ',', 'but', 'barriers', 'that', 'could', 'in', 'time', 'be', 'swept', 'away', '.']

 

split() 함수를 호출하기만 하면 자동으로 토큰들의 List로 만들어준다.

 

4.2 형태소 기반 토큰화

한국어는 '은/는/이/가' 양 옆에 공백을 붙이게 된다면 의도치 않은 변형이 일어날 것이다.

이를 해결하기 위해서는 '형태소'를 활용하면 된다.

 

형태소 : (명사) 뜻을 가진 가장 작은 말의 단위
ex) 오늘도 공부만 한다 → 오늘, 도, 공부, 만, 한다

 

대표적인 한국어 형태소 분석기로 KoNLPy(코엔엘파이)가 있다.

KoNLPy는 내부적으로 5가지의 형태소 분석 Class를 포함하고 있다.

 

KoNLPy: 파이썬 한국어 NLP — KoNLPy 0.4.3 documentation

KoNLPy: 파이썬 한국어 NLP KoNLPy(“코엔엘파이”라고 읽습니다)는 한국어 정보처리를 위한 파이썬 패키지입니다. 설치법은 이 곳을 참고해주세요. NLP를 처음 시작하시는 분들은 시작하기 에서 가

konlpy-ko.readthedocs.io

 

그 외에도 다양한 한국어 형태소 분석기가 있다.

 

한국어 형태소 분석기 성능 비교

korean-tokenizer-experiments 형태소 분석기 비교실험 환경하드웨어 (MacBook Pro Mid 2015)소프트웨어데이터실험 내용실행 시간 비교로딩 시간형태소 분석 시간문장 분석 품질 비교띄어쓰기가 없는 문장자

iostream.tistory.com

위의 포스팅을 보면 한국어 형태소 분석기들의 성능을 비교해놨다.

 

결론을 간단히 말하자면,

연산속도가 제일 중요하다면 'macab'을 사용하면 좋다. (뿐만 아니라 분석 품질도 상위권이다.)

자소분리, 오탈자에 대해서 분석 품질이 고려되어야 하면 'KOMORAN'을 사용하면 좋다.

 

이렇듯 각 형태소 분석기 별로 성능이 다르기에 각자의 목적에 맞게 선택하면 좋을 듯 싶다.

 

4.3 사전에 없는 단어의 문제

공백 기반, 형태소 기반의 토큰화 기법들은 모두 의미를 가지는 단위로 토큰을 생성한다. 이 기법의 경우, 자주 등장한 상위 n개의 단어만 사용하고 나머지는 <unk>같은 특수한 토큰(Unknown Token)으로 치환해버린다. 이를 OOV(Out-Of-Vocabulary) 문제라고 한다.

새로 등장한 단어를 처리하지 못하는 기법이기에 이를 해결하기 위한 기법들이 많이 나오는데 그 기법은 Wordpiece Model이다.

코로나바이러스는 2019년 12월 중국 우한에서 처음 발생한 뒤 전 세계로 확산된, 새로운 유형의 호흡기 감염 질환입니다.

<unk>는 2019년 12월 중국 <unk>에서 처음 발생한 뒤 전 세계로 확산된, 새로운 유형의 호흡기 감염 질환입니다.

5. 토큰화 : 다른 방법들

5.1 Byte Pair Encoding (BPE)

aaabdaaabac # 가장 많이 등장한 바이트 쌍 "aa"를 "Z"로 치환합니다.
→ 
ZabdZabac   # "aa" 총 두 개가 치환되어 4바이트를 2바이트로 압축하였습니다.
Z=aa        # 그다음 많이 등장한 바이트 쌍 "ab"를 "Y"로 치환합니다.
→ 
ZYdZYac     # "ab" 총 두 개가 치환되어 4바이트를 2바이트로 압축하였습니다.
Z=aa        # 여기서 작업을 멈추어도 되지만, 치환된 바이트에 대해서도 진행한다면
Y=ab        # 가장 많이 등장한 바이트 쌍 "ZY"를 "X"로 치환합니다.
→ 
XdXac
Z=aa
Y=ab
X=ZY       # 압축이 완료되었습니다!

모든 단어를 문자(바이트)들의 집합으로 취급하여 자주 등장하는 문자 쌍을 합치면, 접두어나 접미어의 의미를 캐치할 수 있고, 처음 등장하는 단어는 문자(알파벳)들의 조합으로 나타내어 OOV 문제를 완전히 해결할 수 있다.

 

import re, collections

# 임의의 데이터에 포함된 단어들입니다.
# 우측의 정수는 임의의 데이터에 해당 단어가 포함된 빈도수입니다.
vocab = {
    'l o w '      : 5,
    'l o w e r '  : 2,
    'n e w e s t ': 6,
    'w i d e s t ': 3
}

num_merges = 5

def get_stats(vocab):
    """
    단어 사전을 불러와
    단어는 공백 단위로 쪼개어 문자 list를 만들고
    빈도수와 쌍을 이루게 합니다. (symbols)
    """
    pairs = collections.defaultdict(int)
    
    for word, freq in vocab.items():
        symbols = word.split()

        for i in range(len(symbols) - 1):             # 모든 symbols를 확인하여 
            pairs[symbols[i], symbols[i + 1]] += freq  # 문자 쌍의 빈도수를 저장합니다. 
        
    return pairs

def merge_vocab(pair, v_in):
    v_out = {}
    bigram = re.escape(' '.join(pair))
    p = re.compile(r'(?<!\S)' + bigram + r'(?!\S)')
    
    for word in v_in:
        w_out = p.sub(''.join(pair), word)
        v_out[w_out] = v_in[word]
        
    return v_out, pair[0] + pair[1]

token_vocab = []

for i in range(num_merges):
    print(">> Step {0}".format(i + 1))
    
    pairs = get_stats(vocab)
    best = max(pairs, key=pairs.get)  # 가장 많은 빈도수를 가진 문자 쌍을 반환합니다.
    vocab, merge_tok = merge_vocab(best, vocab)
    print("다음 문자 쌍을 치환:", merge_tok)
    print("변환된 Vocab:\n", vocab, "\n")
    
    token_vocab.append(merge_tok)
    
print("Merge Vocab:", token_vocab)

>> Step 1
다음 문자 쌍을 치환: es
변환된 Vocab:
 {'l o w ': 5, 'l o w e r ': 2, 'n e w es t ': 6, 'w i d es t ': 3} 

>> Step 2
다음 문자 쌍을 치환: est
변환된 Vocab:
 {'l o w ': 5, 'l o w e r ': 2, 'n e w est ': 6, 'w i d est ': 3} 

>> Step 3
다음 문자 쌍을 치환: lo
변환된 Vocab:
 {'lo w ': 5, 'lo w e r ': 2, 'n e w est ': 6, 'w i d est ': 3} 

>> Step 4
다음 문자 쌍을 치환: low
변환된 Vocab:
 {'low ': 5, 'low e r ': 2, 'n e w est ': 6, 'w i d est ': 3} 

>> Step 5
다음 문자 쌍을 치환: ne
변환된 Vocab:
 {'low ': 5, 'low e r ': 2, 'ne w est ': 6, 'w i d est ': 3} 

Merge Vocab: ['es', 'est', 'lo', 'low', 'ne']

 

 

lowest라는 단어를 처음보더라도, 위의 알고리즘을 따르면 어느 정도 의미가 파악된 low와 est의 결합으로 표현할 수 있다. 또 아무리 큰 데이터도 원하는 크기로 OOV 문제없이 사전을 정의할 수 있다는 것이다.

Embedding 레이어는 단어의 개수 x Embedding 차원 수의 Weight를 생성하기 때문에 단어의 개수가 줄어드는 것은 곧 메모리의 절약으로 이어진다. 많은 데이터가 곧 정확도로 이어지기 때문에 이런 기여는 굉장히 의미가 있다.

 

하지만 BPE로 생성한 문장이 [i, am, a, b, o, y, a, n, d, you, are, a, gir, l]라면, 어떤 기준으로 결합해서 문장을 복원할 지에 대한 해결책이 크게 없기에 완벽하다 할 수 없다.

 

5.2 Wordpiece Model(WPM)

Worldpiece Model(WPM)는 한 단어를 여러 개의 Subword의 집합으로 보는 방법이다.

구글에서 BPE를 변형해 제안한 알고리즘이 바로 WPM이다. WPM은 BPE에 대해 두 가지 차별성을 가진다.

1. 공백 복원을 위해 단어의 시작 부분에 언더바_를 추가한다.
2. 빈도수 기반이 아닌 가능도(Likelihood)를 증가시키는 방향으로 문자 쌍을 합친다.

 

1번 문항의 예시는 [_i, _am, _a, _b, o, y, _a, n, d, _you, _are, _a, _gir, l]로 토큰화를 한다. 이렇게 하면 모든 토큰을 합친 후 언더바_를 공백으로 치환하여 문장을 복원하면 된다.

 

가능도(Likelihood)란, 관측값이 고정되고, 그것이 주어졌을 때 해당 확률분포에서 나왔을 확률을 뜻한다. 간단히 하면 확률의 확률이라고 할 수도 있다.

(확률분포의 모수가 어떤 확률변수의 표집값과 일관되는 정도를 나타내는 값. 주어진 표집값에 대한 모수의 가능도는 이 모수를 따르는 분포가 주어진 관측값에 대하여 부여하는 확률)

 

확률(probability)과 가능도(likelihood) 그리고 최대우도추정(likelihood maximization)

* 우선 본 글은 유투브 채널StatQuest with Josh Starmer 님의 자료를 한글로 정리한 것 입니다. 만약 영어듣기가 되신다면 아래 링크에서 직접 보시는 것을 추천드립니다. 이렇게 깔끔하게 설명한 자료

jjangjjong.tistory.com

 

(+) 구글에서 WPM을 한국어, 일본어 텍스트 처리를 위해 고려했다는 점은 아래의 2가지를 시사한다.

1. 조사, 어미 등의 활용이 많고 복잡한 한국어 같은 모델의 토크나이저로 WPM이 좋은 대안이 될 수 있다.
2. WPM은 어떤 언어든 무관하게 적용 가능한 langauge-neutral하고 general한 기법이다.
   한국어 형태소 분석기처럼 한국어에만 적용 가능한 기법보다 훨씬 활용도가 크다.

 

WPM은 아쉽게도 공개가 되어있지 않다. 그 대신 구글의 SentencePiece 라이브러리를 통해 고성능의 BPE를 사용할 수 있다. SentencePiece에는 전처리 과정도 포함하고 있어 데이터를 따로 정제할 필요가 없어 간편하기도 하다.

 

GitHub - google/sentencepiece: Unsupervised text tokenizer for Neural Network-based text generation.

Unsupervised text tokenizer for Neural Network-based text generation. - GitHub - google/sentencepiece: Unsupervised text tokenizer for Neural Network-based text generation.

github.com

 

5.3 soynlp

soynlp는 한국어 자연어 처리를 위한 라이브러리이다. 토크나이저 외에도 단어 추출, 품사 판별, 전처리 기능도 제공한다.

 

GitHub - lovit/soynlp: 한국어 자연어처리를 위한 파이썬 라이브러리입니다. 단어 추출/ 토크나이저 /

한국어 자연어처리를 위한 파이썬 라이브러리입니다. 단어 추출/ 토크나이저 / 품사판별/ 전처리의 기능을 제공합니다. - GitHub - lovit/soynlp: 한국어 자연어처리를 위한 파이썬 라이브러리입니다.

github.com

형태소 기반의 토크나이저가 미등록 단어에 취약하기 때문에 WordPiece Model을 사용하는 것처럼,

형태소 기반인 KoNLPy의 단점을 해결하기 위해 soynlp를 사용할 수 있다.

 

soynlp의 토크나이저는 학습데이터를 이용하지 않으면서 데이터에 존재하는 단어를 찾거나, 문장을 단어열로 분해, 혹은 품사 판별을 할 수 있는 비지도학습 접근법을 지향한다.

문장에서 처음 단어를 받아들일 때, 단어의 경계를 알아야 올바르게 토큰화를 할 수 있다. 이 때 단어의 경계를 비지도학습을 통해 결정한다. 비지도학습을 통한 방법이기에 미등록 단어도 토큰화가 가능하다.

비지도학습을 가능케하는 것이 통계적인 방법이라서 soynlp를 통계 기반 토크나이저로 분류하기도 한다.

 


6. 토큰에게 의미를 부여하기

각 토큰들이 랜덤하게 부여된 실수로 살아가지 않게, 그들끼리 유사도 연산을 할 수 있게 의미를 부여하는 알고리즘이 많이 있다. 그 중 대표적인 3가지만 알아보자.

 

6.1 Word2Vec

원-핫 벡터는 단어 벡터 간의 유의미한 유사도를 계산할 수 없다. 그래서 단어 벡터 간 유의미한 유사도를 반영할 수 있도록 단어의 의미를 수치화 할 수 있는 방법이 필요한다. 이를 위해서 사용되는 대표적인 방법이 `Word2Vec(워드투벡터)'이다.

 

02) 워드투벡터(Word2Vec)

앞서 원-핫 벡터는 단어 벡터 간 유의미한 유사도를 계산할 수 없다는 단점이 있음을 언급한 적이 있습니다. 그래서 단어 벡터 간 유의미한 유사도를 반영할 수 있도록 단어의 ...

wikidocs.net

Word2Vec에는 'CBOW'와 'Skip-gram' 2가지 방식이 존재한다. 논리적으로는 'CBOW'가 좋아 보이지만, 'Skip-gram'이 실제 실험에선 다소 우세하다.

 

그리고 Word2Vec은 딥러닝 모델이 아니라고 하는데, 그 이유는 은닉층이 1개인 경우 Deep Neural Network가 아닌 Shallow Neural Network라 칭한다.

 

6.2 FastText

Word2Vec는 자주 등장하지 않는 단어는 최악의 경우 단 한 번의 연산만을 거쳐 랜덤하게 초기화된 값과 크게 다르지 않은 상태로 알고리즘이 종료될 수 있다. FastText는 이러한 Word2Vec의 문제점을 해결하기 위해 BPE와 비슷한 아이디어를 적용하였다.

 

기존의 단어마다 Embedding을 할당하던 방식과 FastText의 큰 차이점은 FastText는 한 단어를 n-gram의 집합이라고 보고 단어를 쪼개어 각 n-gram에 할당된 Embedding의 평균값을 사용한다는 것이다.

 

한국어를 위한 어휘 임베딩의 개발 -1-

한국어 자모의 FastText의 결합 | 이 글은 Subword-level Word Vector Representations for Korean (ACL 2018)을 다룹니다. 두 편에 걸친 포스팅에서는 이 프로젝트를 시작하게 된 계기, 배경, 개발 과정의 디테일을 다

brunch.co.kr

 

6.3 ELMo - the 1st Contextualized Word Embedding

Word2Vec와 FastText는 동음이의어를 처리할 수 없다. 동음이의어는 동일한 벡터값이다.

동음이의어를 명확하게 해석하려면 어떻게 해야 할까. 단어의 의미 벡터를 구하기 위해서는 그 단어만 필요한 것이 아니라 그 단어가 놓인 주변 단어 배치의 맥락이 함께 고려되는 Word Embedding이 필요하다. 이것을 'Contextualized Word Embedding'이라고 한다.

 

기존 어휘 임베딩(입력 토큰의 word vector), 순방향 LSTM의 hidden state vector, 역방향 LSTM의 hidden state vector를 concatenate한 벡터가 ELMo의 Contextual Word Embedding이 된다.

 

 

전이 학습 기반 NLP (1): ELMo

Deep Contextualized Word Representations | 2018년은 기계학습 기반 자연어 처리 분야에 있어서 의미 있는 해였습니다. 2015년 이후로 컴퓨터 비전 분야에서 전이 학습(transfer learning)이 유행하기 시작한 이후

brunch.co.kr

 

728x90
반응형