전처리
분산표현
기본 토큰화 방법 학습
토큰화의 다른 방법들
토큰의 의미 부여
일상에서 자연히 발생하는 언어
↔️ Artificial language
언어 문법 이론
자연어 파싱의 어려움
Hi,
에서 ,까지를 알파벳으로 생각할 수 있음replace
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, [".", "?", "!", ","]))
lower()
sentence = "First, open the first chapter."
print(sentence.lower())
import re
sentence = "He is a ten-year-old boy."
sentence = re.sub("([^a-zA-Z.,?!])", " ", sentence)
print(sentence)
# From The Project Gutenberg
# (https://www.gutenberg.org/files/2397/2397-h/2397-h.htm)
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.
"""
def cleaning_text(text, punc, regex):
# 노이즈 유형 (1)
for p in punc:
text = text.replace(p, " " + p + " ")
# 노이즈 유형 (2), (3)
text = re.sub(regex, " ", text).lower()
return text
print(cleaning_text(corpus, [".", ",", "!", "?"], "([^a-zA-Z0-9.,?!\n])"))
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 .
남자: [-1], 여자: [1]
(or 남자: [1], 여자: [-1])소년: [-1, -1], 소녀: [1, -1]
할아버지: [-1, 1], 할머니: [1, 1]
{
// [성별, 연령]
남자: [-1.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]
}
{
// [성별, 연령, 과일, 색깔]
남자: [-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] // 노랗게 익은 바나나
}
embedding_layer = tf.keras.layers.Embedding(input_dim=100, output_dim=256)
정리
- 희소 표현
- 이진화 or 빈도수 등 방식으로 표현(단어를 고정된 크기의 벡터로 표현하지 않음)
- 단어 존재 유무만
- 벡터 공간 상 거리 측정 불가
- 단어 간 의미 관계 파악 어려움
- 분산 표현
- 단어를 고정된 크기 벡터로 표현
- 1개의 단어 ➡️ 여러 차원 값으로 나타냄
- 단어 간 거리 측정 ➡️ 단어 간 의미 관련 파악 가능
토큰화의 필요성
- 텍스트 전처리, 문장 의미 파악, 모델 성능 향상, 정보 검색 등을 이유로 필요
- 적절한 토큰화 기법을 사요해야 자연어 처리 모델 성능이 향상될 것
split()
사용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', '.']
오늘
, 도
, 공부
, 만
, 한다
Q. 100만 문장 데이터를 형태소 분석, 속도 측면에서 가장 뛰어난 분석기는?
A. mecab
Q. 정확도 기준(시간 오래 걸려도 됨) + 띄어쓰기, 오탈자에 강한 분석기?
A. KOMORAN을 사용하고 추가적으로 mecab, 꼬꼬마도 고려
$ pip install konlpy
$ git clone https://github.com/SOMJANG/Mecab-ko-for-Google-Colab.git
$ bash install_mecab-ko_on_colab190912.sh
$ sudo apt update
$ sudo apt install default-jre
tokenizer_list = [Hannanum(),Kkma(),Komoran(),Mecab(),Okt()]
kor_text = '코로나바이러스는 2019년 12월 중국 우한에서 처음 발생한 뒤 전 세계로 확산된, 새로운 유형의 호흡기 감염 질환입니다.'
for tokenizer in tokenizer_list:
print('[{}] \n{}'.format(tokenizer.__class__.__name__, tokenizer.pos(kor_text)))
[Hannanum]
[('코로나바이러스', 'N'), ('는', 'J'), ('2019년', 'N'), ('12월', 'N'), ('중국', 'N'), ('우한', 'N'), ('에서', 'J'), ('처음', 'M'), ('발생', 'N'), ('하', 'X'), ('ㄴ', 'E'), ('뒤', 'N'), ('전', 'N'), ('세계', 'N'), ('로', 'J'), ('확산', 'N'), ('되', 'X'), ('ㄴ', 'E'), (',', 'S'), ('새롭', 'P'), ('은', 'E'), ('유형', 'N'), ('의', 'J'), ('호흡기', 'N'), ('감염', 'N'), ('질환', 'N'), ('이', 'J'), ('ㅂ니다', 'E'), ('.', 'S')]
[Kkma]
[('코로나', 'NNG'), ('바', 'NNG'), ('이러', 'MAG'), ('슬', 'VV'), ('는', 'ETD'), ('2019', 'NR'), ('년', 'NNM'), ('12', 'NR'), ('월', 'NNM'), ('중국', 'NNG'), ('우', 'NNG'), ('하', 'XSV'), ('ㄴ', 'ETD'), ('에', 'VV'), ('서', 'ECD'), ('처음', 'NNG'), ('발생', 'NNG'), ('하', 'XSV'), ('ㄴ', 'ETD'), ('뒤', 'NNG'), ('전', 'NNG'), ('세계', 'NNG'), ('로', 'JKM'), ('확산', 'NNG'), ('되', 'XSV'), ('ㄴ', 'ETD'), (',', 'SP'), ('새', 'NNG'), ('롭', 'XSA'), ('ㄴ', 'ETD'), ('유형', 'NNG'), ('의', 'JKG'), ('호흡기', 'NNG'), ('감염', 'NNG'), ('질환', 'NNG'), ('이', 'VCP'), ('ㅂ니다', 'EFN'), ('.', 'SF')]
[Komoran]
[('코로나바이러스', 'NNP'), ('는', 'JX'), ('2019', 'SN'), ('년', 'NNB'), ('12월', 'NNP'), ('중국', 'NNP'), ('우', 'NNP'), ('한', 'NNP'), ('에서', 'JKB'), ('처음', 'NNG'), ('발생', 'NNG'), ('하', 'XSV'), ('ㄴ', 'ETM'), ('뒤', 'NNG'), ('전', 'MM'), ('세계로', 'NNP'), ('확산', 'NNG'), ('되', 'XSV'), ('ㄴ', 'ETM'), (',', 'SP'), ('새롭', 'VA'), ('ㄴ', 'ETM'), ('유형', 'NNP'), ('의', 'JKG'), ('호흡기', 'NNG'), ('감염', 'NNP'), ('질환', 'NNG'), ('이', 'VCP'), ('ㅂ니다', 'EF'), ('.', 'SF')]
[Mecab]
[('코로나', 'NNP'), ('바이러스', 'NNG'), ('는', 'JX'), ('2019', 'SN'), ('년', 'NNBC'), ('12', 'SN'), ('월', 'NNBC'), ('중국', 'NNP'), ('우한', 'NNP'), ('에서', 'JKB'), ('처음', 'NNG'), ('발생', 'NNG'), ('한', 'XSV+ETM'), ('뒤', 'NNG'), ('전', 'NNG'), ('세계', 'NNG'), ('로', 'JKB'), ('확산', 'NNG'), ('된', 'XSV+ETM'), (',', 'SC'), ('새로운', 'VA+ETM'), ('유형', 'NNG'), ('의', 'JKG'), ('호흡기', 'NNG'), ('감염', 'NNG'), ('질환', 'NNG'), ('입니다', 'VCP+EF'), ('.', 'SF')]
[Okt]
[('코로나바이러스', 'Noun'), ('는', 'Josa'), ('2019년', 'Number'), ('12월', 'Number'), ('중국', 'Noun'), ('우한', 'Noun'), ('에서', 'Josa'), ('처음', 'Noun'), ('발생', 'Noun'), ('한', 'Josa'), ('뒤', 'Noun'), ('전', 'Noun'), ('세계', 'Noun'), ('로', 'Josa'), ('확산', 'Noun'), ('된', 'Verb'), (',', 'Punctuation'), ('새로운', 'Adjective'), ('유형', 'Noun'), ('의', 'Josa'), ('호흡기', 'Noun'), ('감염', 'Noun'), ('질환', 'Noun'), ('입니다', 'Adjective'), ('.', 'Punctuation')]
코로나바이러스는 2019년 12월 중국 우한에서 처음 발생한 뒤
전 세계로 확산된, 새로운 유형의 호흡기 감염 질환입니다.
<unk>는 2019년 12월 중국 <unk>에서 처음 발생한 뒤
전 세계로 확산된, 새로운 유형의 호흡기 감염 질환입니다.
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 # 압축이 완료되었습니다!
Q. BPE가 OOV 문제를 해결했다고 하는 이유?
A. BPE를 통해 기존에 없던 단어가 등장해도 그 단어를 구성하는 문자 조합들이 기존에 등장한 문자 쌍과 일치하는지 확인할 수 있다면 -> 문제 해결이 가능하다는 의미
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):
"""
문자 쌍(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("Merged 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}
Merged Vocab: ['es', 'est', 'lo', 'low', 'ne']
lowest
라는 새 단어 등장 ➡️ 알고리즘 적용 ➡️ low
, est
결합으로 표현 가능차별성
- 공백 복원 : 단어 시작 부분에
_
추가
ex)[_i, _am, _a, _b, o, y, _a, n, d, _you, _are, _a, _gir, l]
: 문장 복원 시 모든 토큰 합치기 + _를 공백으로 치환하면 되기 때문에 간단
- 가능도(Likelihood) 증가 방향으로 문자싸 합침
자동차
➡️ _자동 / 차
이전의 문제들
ELMo : 데이터에 단어가 등장하면, 그때 바로 그 주변 단어 정보를 이용해 임베딩을 구축
Contextual Word Embedding이 되는 벡터 3가지