딥러닝을 이용한 자연어 처리 입문 교재를 바탕으로 작성되었습니다.
노션에 정리된 것을 거의 그대로 옮겨왔습니다.일부 텍스트 깨짐 등이 있을 수 있습니다.
02-01. 토큰화(Tokenization)
- 코퍼스 데이터 전처리는 용도에 맞게 토큰화 & 정제 & 정규화 작업을 하게 된다.
- 토큰화는 코퍼스를 토큰이라는 단위로 나누는 작업이다. 토큰의 단위는 상황에 따라 다르지만, 보통 ‘의미있는 단위’로 정의한다.
1. 단어 토큰화(Word Tokenization)
- 토큰의 기준을 단어로 하는 경우, 단어 토큰화라고 한다.
- 여기서 단어는 단어구, 의미를 갖는 문자열로 간주되기도 한다.
- 입력으로부터 구두점(punctuation)과 같은 문자를 제외시키는 것도 간단한 단어 토큰화 작업에 해당한다.
- 입력 : Hello, nice to meet you!
- 출력 : “Hello”, “nice”, “to”, “meet”, “you”
2. 토큰화 중 생기는 선택의 순간
- 위와 같이 구두점을 그냥 제거해버리면 토큰이 의미를 잃어버리는 경우가 발생할수도 있다.
- 또한 아포스트로피를 어떻게 토큰으로 분류해야하는지는 매우 복잡한 문제이다.
- 이럴때는 어떤 용도로 사용할 것인지에 따라 다른데, 용도에 영향이 없는 것을 기준으로 정하면 된다.
print('word_tokenize:', word_tokenize("Don't go Jone's pizza shop."))
print('WordPunctTokenizer:', WordPunctTokenizer().tokenize("Don't go Jone's pizza shop."))
print('Keras text_to_word_sequence:', text_to_word_sequence("Don't go Jone's pizza shop."))
word_tokenize: ['Do', "n't", 'go', 'Jone', "'s", 'pizza', 'shop', '.']
WordPunctTokenizer: ['Don', "'", 't', 'go', 'Jone', "'", 's', 'pizza', 'shop', '.']
Keras text_to_word_sequence: ["don't", 'go', "jone's", 'pizza', 'shop']
- word_tokenize : 아포스트로피의 의미를 분리하였다.
- WordPunctTokenizer : 아포스트로피를 별개의 문자로 처리했다.
- Keras text_to_word_sequence : 아포스트로피를 포함하여 하나의 문자로 처리했으며, 다른 구두점 및 영어 대문자는 제거하였다.
3. 토큰화에서 고려해야할 사항
1) 구두점이나 특수 문자를 단순 제외해서는 안된다.
- 마침표는 문장의 경계를 알 수 있고, 아포스트로피는 여러 의미를 내포
- 문자 자체에 구두점이나 특수문자를 갖고 있는 경우가 있다.
2) 줄임말과 단어 내의 띄어쓰기가 있는 경우
- 영어에는 접어(clitic)이라는 개념이 있다. we are을 줄여서 we’re, what are을 줄여셔 what’re라고 하는데, 여기서 re를 접어라고 한다.
- New York이나 rock ‘n’ roll 처럼 단어 내에 띄어쓰기가 있는 경우가 있다. 토큰화 작업때에도 이들을 단어 하나로 인식할 수 있는 능력도 가져야한다.
3) 표준 토큰화 예제
- Penn Treebank Tokenization 규칙
- 표준으로 쓰이고 있는 토큰화 방법 중 하나이다.
- 하이픈으로 구성된 단어는 하나로 유지한다.
- doesn’t와 같이 아포스트로피로 접어가 함께하는 단어는 분리해준다.
4. 문장 토큰화(Sentence Tokenization)
- 토큰의 단위가 문장인 경우이다.
- 마침표, 느낌표, 물음표가 항상 문장의 끝을 나타내지는 않기 때문에 이를 고려해야한다.
- 사용하는 코퍼스가 어떤 국적의 언어인지, 해당 코퍼스에서 특수문자가 어떻게 사용되는지에 따라 규칙을 정의할 수는 있으나 100% 정확도를 얻는 것은 쉬운 일이 아니다.
- 코퍼스가 정제되어 있지 않은 상태일 경우, 우선 사용하는 용도에 맞게 문장 토큰화가 필요할 수 있다.
- NLTK는 영어 문장의 토큰화를 수행하는 sent_tokenize를 지원하고 있다.
- 한국어의 경우 박상길님이 개발한 KSS(Korean Sentence Splitter)를 추천한다.
!pip install kss
import kss
text_ko = "딥 러닝 자연어 처리가 재미있기는 합니다. 그런데 문제는 영어보다 한국어로 할 때 너무 어렵습니다. 이제 해보면 알걸요?"
print("한국어 문장 토큰화: ", kss.split_sentences(text))
한국어 문장 토큰화: ['딥 러닝 자연어 처리가 재미있기는 합니다.',
'그런데 문제는 영어보다 한국어로 할 때 너무 어렵습니다.',
'이제 해보면 알걸요?']
5. 한국어에서의 토큰화의 어려움
- 한국어는 영어와 달리 띄어쓰기만으로는 토큰화를 하기에 부족하다. 이는 한국어가 교착어라는 점에서 기인한다.
1) 교착어의 특성
- ‘그는’, ‘그가’, ‘그에게’, ‘그의’와 같은 조사가 띄어쓰기 없이 붙게 된다. 이러한 경우 같은 단어임에도 조사로 인해 다른 단어로 인식이 되어 자연어 처리가 힘들고 번거로워지는 경우가 많다.
- 한국어 NLP에서 조사는 분리해줄 필요가 있다.
- 한국어 토큰화를 위해 형태소(morpheme)란 개념을 반드시 이해해야 한다.
- 자립 형태소 : 접사, 어미, 조사와 상관 없이 자립해서 사용할 수 있는 형태소. 그 자체로 단어가 된다.
- 의존 형태소 : 다른 형태소와 결합하여 사용되는 형태소. 접사, 어간, 어미, 조사를 말한다.
- 예시 : 에디가 책을 읽었다.
- 자립 형태소 : “에디”, “책”
- 의존 형태소 : “가”, “을”, “읽-”, “-었”, “-다”
- 한국어에서 단어 토큰화와 유사한 형태를 얻으려면 형태소 토큰화를 수행해야 한다.
2) 한국어는 띄어쓰기가 영어보다 잘 지켜지지 않는다.
- 한국어 코퍼스의 많은 경우는 띄어쓰기가 틀렸거나 지켜지지 않는 경우가 많다.
6. 품사 태깅(Part-of-speech tagging)
- 단어의 표기는 같지만, 품사에 따라서 단어의 의미가 달라지기도 한다.
- ex) 영어 fly는 동사 - ‘날다’, 명사 - ‘파리’ 두가지의 의미를 갖고 있다.
- ex) 한국어 ‘못’은 명사-’못’, 부사 - ‘할수없다’ 두가지의 의미를 갖고 있다.
- 이러한 특성으로 단어 토큰화 과정에서 각 단어가 어떤 품사로 쓰였는지를 구분해 놓기도 한다. 이러한 작업을 품사 태깅이라고 한다.
7. NLTK와 KoNLPy를 이용한 영어, 한국어 토큰화 실습
- NLTK에서는 Penn Treebanck POS Tags라는 기준을 사용하여 품사를 태깅한다.
- PRP : 인칭 대명사
- VBR : 동사
- RB : 부사
- VBG : 현재부사
- IN : 전치사
- NNP : 고유명사
- NNS : 복수명사
- CC : 접속사
- DT : 관사
- KoNLPy에서 사용할 수 있는 형태소 분석기는 Okt(Open Korea Text), 메캅(Mekab), 코모란(Komoran), 한나눔(Hannanum), 꼬꼬마(KKma)가 있다.
- koNLPy에서 제공하는 형태소 분석기들은 공통적인 메소드들을 제공한다.
- morphs : 형태소 추출
- pos : 품사 태깅
- nouns : 명사 추출
text_ko = "아름답지만 다소 복잡하기도한 한국어는 전세계에서 13번째로 많이 사용되는 언어입니다."
print('OKT 형태소 추출 :', okt.morphs(text_ko))
print('OKT 품사 태깅 :', okt.pos(text_ko))
print('OKT 명사 추출 :', okt.nouns(text_ko))
print('꼬꼬마 형태소 추출 :', kkma.morphs(text_ko))
print('꼬꼬마 품사 태깅 :', kkma.pos(text_ko))
print('꼬꼬마 명사 추출 :', kkma.nouns(text_ko))
OKT 형태소 추출 : ['아름답지만', '다소', '복잡하', '기도', '한', '한국어', '는', '전세계', '에서', '13', '번', '째', '로', '많이', '사용', '되는', '언어', '입니다', '.']
OKT 품사 태깅 : [('아름답지만', 'Adjective'), ('다소', 'Noun'), ('복잡하', 'Adjective'), ('기도', 'Noun'), ('한', 'Josa'), ('한국어', 'Noun'), ('는', 'Josa'), ('전세계', 'Noun'), ('에서', 'Josa'), ('13', 'Number'), ('번', 'Noun'), ('째', 'Suffix'), ('로', 'Josa'), ('많이', 'Adverb'), ('사용', 'Noun'), ('되는', 'Verb'), ('언어', 'Noun'), ('입니다', 'Adjective'), ('.', 'Punctuation')]
OKT 명사 추출 : ['다소', '기도', '한국어', '전세계', '번', '사용', '언어']
꼬꼬마 형태소 추출 : ['아름답', '지만', '다소', '복잡', '하', '기', '도', '한', '한국어', '는', '전세계', '에서', '13', '번째', '로', '많이', '사용', '되', '는', '언어', '이', 'ㅂ니다', '.']
꼬꼬마 품사 태깅 : [('아름답', 'VA'), ('지만', 'ECE'), ('다소', 'MAG'), ('복잡', 'NNG'), ('하', 'XSV'), ('기', 'ETN'), ('도', 'JX'), ('한', 'MDN'), ('한국어', 'NNG'), ('는', 'JX'), ('전세계', 'NNG'), ('에서', 'JKM'), ('13', 'NR'), ('번째', 'NNB'), ('로', 'JKM'), ('많이', 'MAG'), ('사용', 'NNG'), ('되', 'XSV'), ('는', 'ETD'), ('언어', 'NNG'), ('이', 'VCP'), ('ㅂ니다', 'EFN'), ('.', 'SF')]
꼬꼬마 명사 추출 : ['복잡', '한국어', '전세계', '13', '13번째', '번째', '사용', '언어']
- 형태소 분석기별로 성능과 결과가 다르게 나오기 때문에, 필요 용도에 따라 적합한 형태소 분석기를 판단하고 사용해야 한다.
- 예를들어 속도가 중요하다면 메캅을 사용할 수 있다.
02-02. 정제(Cleaning) and 정규화(Normalization)
- 토큰화 작업 전, 후에는 텍스트 데이터를 용도에 맞게 정제 및 정규화하는 작업이 항상 존재한다.
- 정제 : 갖고 있는 코퍼스의 노이즈 데이터를 제거
- 정규화 : 표현 방법이 다른 단어들을 통합시켜 같은 단어로 만들어 준다.
1. 규칙에 기반한 표기가 다른 단어들의 통합
- 같은 의미이지만, 표기가 다른 단어들을 하나의 단어로 정규화하는 방법을 사용할 수 있다.
- 필요에 따라 직접 코딩을 통해 정의할 수 있다.
2. 대, 소문자 통합
- 영어권 언어에서 대,소문자를 통합하여 단어의 개수를 줄일 수 있다.
- 대문자는 문장의 첫글자 등 특정 상황에서만 쓰이기 때문에 주로 소문자로 변환된다.
- 다만 고유명사를 나타내는 대문자의 경우 대문자로 유지되어야 하는 등 무작정 통합해서는 안된다.
- 하지만 결국 코퍼스는 사용자들로 부터 나온 것이고, 사용자들이 대소문자를 올바른 방법과 상관없이 사용한다면, 아무리 규칙을 잘 세워도 그다지 도움이 되지 않을 수 있다. 이러한 경우는 코퍼스 전체를 소문자로 바꾸는 것이 종종 더 실용적인 해결책이 되기도 한다.
3. 불필요한 단어의 제거
- 노이즈 데이터는 자연어가 아니면서 아무 의미도 갖지 않는 글자들(특수 문자 등)을 의미한다.
- 불필요한 단어를 제거하는 방법으로는 불용어 제거, 등장 빈도가 적은 단어, 길이가 짧은 단어를 제거하는 방법이 있다. 불용어는 이후 챕터에서따로 다룰 것이다.
1) 등장빈도가 적은 단어
- 텍스트 데이터에서 너무 적게 등장해서 자연어 처리에 도움이 되지 않는 경우
- 10만개의 데이터에서 단 5번밖에 등장하지 않았다면 직관적으로 분류에 거의 도움이 되지 않는다.
2) 길이가 짧은 단어
- 영어권 언어에서 길이가 짧은 단어는 대부분 불용어에 해당하기 때문에 이를 삭제하는 것만으로 효과를 볼 수 있다.
- 하지만 한국어에서는 이런 방법이 크게 유효하지 않을 수 있다.
- 한국어 단어의 평균 길이는 2~3 정도로, 짧은 단어를 나누기 어렵다.
- 한자어나, 한 글자만으로 의미를 나타내는 단어가 많다.
- 길이가 짧은 단어는 정규 표현식을 이용하여 삭제할 수 있다.
4. 정규 표현식(Regular Expression)
- HTML문서로 가져온 코퍼스라면 HTML 태그가 달려있을 것이고, 기사를 태그했다면 기자 이름, 게시일 등 필요없는 정보다 다수 존재할 수 있다.
- 정규표현식을 통해 이러한 글자들을 규칙에 기반해 한 번에 제거할 수 있다.
02-03 어간 추출 및 표제어 추출
- 눈으로 봤을 때는 서로 다른 단어이지만 의미가 같을 때, 하나의 단어로 일반화하면 단어 수를 줄일 수 있다.
- 이는 빈도수 기반 학습에 사용되는 BoW 표현을 위해 자주 사용된다.
- 정규화 작업의 가장 큰 목표는 코퍼스의 복잡성을 줄이는 것임을 잊지 말자.
1. 표제어 추출(**Lemmatization)**
- 표제어(Lemma)는 기본 사전형 단어 정도의 의미를 갖는다.
- 표제어 추출은 서로 다른 형태를 가진 단어를 하나의 표제어로 통일하여 단어의 개수를 줄일 수 있는지 판단하는 과정이다.
- 예를들어 am, are, is는 be라는 표제어로 통일 할 수 있다.
- 표제어를 추출하는 가장 섬세한 방법은 형태소로부터 단어들을 만들어가는 형태학(morphology)적 파싱을 먼저 진행하는 것이다. 형태소에는 어간과 접사가 존재한다.
- 어간(stem) : 단어의 의미를 담고 있는 단어의 핵심 부분
- 접사(affix) : 단어에 추가적인 의미를 주는 부분
- 예를들어 cats는 어간 cat과 접사 -s로 나눌 수 있다.
- NLTK에서는 WordNetLemmatizer라는 표제어 추출을 위한 도구를 제공한다.
- WordNetLemmatizer를 효과적으로 사용하기 위해서는 본래 단어의 품사 정보를 제공해주어야 한다.
- 예를들어 has, dies, watched가 동사로 쓰였다는 것을 알려줘야, have, die, watch라는 정확한 동사 Lemma를 출력하게 된다.
- 단어를 표제어로 변경하면 품사 정보를 잃게 된다.
2. 어간 추출(Stemming)
- 어간(Stem)을 추출하는 작업이다.
- 이 작업은 정해진 규칙만 보고 단어의 어미를 자르는 어림짐작의 방식을 사용하기 때문에 어간 추출 후 나오는 단어는 사전에 존재하지 않는 단어일 수 있다.
- 어간 추출을 위한 알고리즘 중 하나로 포터 알고리즘(Poter Algorithm)을 사용할 수 있다.
from nltk.stem import PorterStemmer
from nltk.tokenize import word_tokenize
stemmer = PorterStemmer()
sentence = "This was not the map we found in Billy Bones's chest, but an accurate copy, complete in all things--names and heights and soundings--with the single exception of the red crosses and the written notes."
print("어간 추출 전 :", word_tokenize(sentence))
print("어간 추출 후 :", [stemmer.stem(word) for word in word_tokenize(sentence)])
어간 추출 전 : ['This', 'was', 'not', 'the', 'map', 'we', 'found', 'in', 'Billy', 'Bones', "'s", 'chest', ',', 'but', 'an', 'accurate', 'copy', ',', 'complete', 'in', 'all', 'things', '--', 'names', 'and', 'heights', 'and', 'soundings', '--', 'with', 'the', 'single', 'exception', 'of', 'the', 'red', 'crosses', 'and', 'the', 'written', 'notes', '.']
어간 추출 후 : ['thi', 'wa', 'not', 'the', 'map', 'we', 'found', 'in', 'billi', 'bone', "'s", 'chest', ',', 'but', 'an', 'accur', 'copi', ',', 'complet', 'in', 'all', 'thing', '--', 'name', 'and', 'height', 'and', 'sound', '--', 'with', 'the', 'singl', 'except', 'of', 'the', 'red', 'cross', 'and', 'the', 'written', 'note', '.']
- accurate → accur (-ate 제거)
- exception → except (-ion 제거)
- 그외 -ize, -ance, -ic, -y를 제거한다.
- nltk에서는 LancasterStemmer라는 전혀 다른 어간 추출 알고리즘도 제공한다.
- 동일한 단어라도 두 스태머는 전혀 다른 결과를 보여준다.
- 때문에 어간 추출을 할 경우 스태머를 각각 적용해보고 어떤 스태머가 해당 코퍼스에 적합한지를 판단 후에 사용해야 한다.
- 어간 추출 후에는 일반화가 지나치게 되거나, 덜 되거나 하는 경우가 있을 수 있다.
- 이 경우 의미가 동일한 경우만 얻기를 원하는 정규화의 목적에 맞지 않게 된다.
3. 한국어에서의 어간 추출
- 한국어는 5언 9품사의 구조를 갖고 있다.
- 체언 : 명사, 대명사, 수사
- 수식언 : 관형사, 부사
- 관계언 : 조사
- 독립언 : 감탄사
- 용언 : 동사, 형용사
- 이중 용언에 해당하는 동사와 형용사는 어간(stem)과 어미(ending)의 결합으로 구성된다.
1) 활용(conjugation)
- 활용이란 용언의 어간이 어미를 가지는 일을 말한다.
- 어간 : 용언을 활용할 때 원칙적으로 모양이 변하지 않는 부분을 말한다. 모양이 변하는 예외도 존재한다 (긋다, 긋고, → 그어서, 그어라)
- 어미 : 용언의 어간 뒤에 붙어서 활용하면서 변하는 부분이며, 여러 문법적 기능을 수행한다.
- 활용은 두가지 종류로 나뉜다.
- 규칙 활용 : 어간의 모습이 일정한 활용
- 불규칙 활용 : 어간의 모습이 변하는 활용
2) 규칙 활용
- 규칙활용은 어간이 어미를 취할 때, 어간의 모습이 일정하다.
- 잡(어간) + 다(어미) → 잡아서, 잡고 등
3) 불규칙 활용
- 어간이 어미를 취할 때, 어간의 모습이 바뀌거나 취하는 어미가 특수한 어미일 경우를 말한다.
- 듣(어간) + 다(어미) → 들어라 등 - 어간이 바뀌는 경우
- 오르(어간) + 아/어(어미) → 올라, 푸르(어간) + 아/어(어미) → 푸르러 - 특수한 어미를 취하는 경우
- 단순한 분리만으로는 어간이 추출되지 않고, 좀 더 복잡한 규칙을 필요로 한다.
02-04. 불용어 (Stopword)
- 불용어는 큰 의미가 없는 단어 토큰을 말한다.
- 예를 들어 I, my, me, over, 조사, 접미사는 문장에서는 자주 등장하지만, 실제 의미 분석에는 거의 기여하지 않는다.
- NLTK는 100여개 이상의 영어 단어를 불용어로 패키지 내에서 미리 정의하고 있다. 또한 개발자가 직접 정의하기도 한다.
nltk.stopwords.words(”english”)
를 통해 NLTK가 정의한 영어 불용어 리스트를 확인할 수 있다.
1) NLTK에서 불용어 확인하기
stopwords_list = stopwords.words('english')
print("불용어 개수:", len(stopwords_list))
print("불용어 확인:", stopwords_list[:10])
불용어 개수: 179
불용어 확인: ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're"]
2) NLTK를 통해서 불용어 제거하기
- 불용어 리스트를 저장한 뒤, for ~ in loop를 돌면서 불용어가 아닌 단어만 결과에 추가하는 식으로 불용어를 제거할 수 있다.
- 빠른 연산을 위해 set을 사용하는 것을 추천
stopwords_list = set(stopwords.words('english'))
sentence = "Family is not an important thing. It's everything."
word_tokens = word_tokenize(sentence)
result = []
for word_token in word_tokens:
if word_token not in stopwords_list:
result.append(word_token)
print("불용어 제거 결과:", result)
불용어 제거 결과: ['Family', 'important', 'thing', '.', 'It', "'s", 'everything', '.']
3) 한국어에서 불용어 제거하기
- 한국어에서 불용어를 제거하는 방법으로는, 토큰화 후 조사와 접속사 등을 제거하는 방법이 있다.
- 하지만 명사와 형용사 단어 중에서도 불용어로 제거하고 싶은 단어가 생기는 경우가 많기 때문에, 사용자가 직접 불용어 사전을 만들게 된다.
- 보편적으로 선택할 수 있는 한국어 불용어 리스트는 아래 링크를 참조하자 (절대적 기준 아님!)
02-06 정수 인코딩 (Interger Encoding)
- 컴퓨터는 텍스트보다 숫자를 더 잘 처리할 수 있으므로, 자연어처리는 텍스트를 숫자로 바꾸는 여러가지 기법이 있다.
- 예를들어 텍스트에 단어가 5000개가 있다면 1~5000번까지 각각의 단어와 맵핑되는 정수 인덱스를 부여할 수 있다. 일반적으로 등장 빈도수 기준으로 정렬한 뒤에 부여한다.
1. 정수 인코딩 (Interger Encoding)
- 단어를 빈도수 순으로 정렬한 단어 집합(vocabulary)를 만든다.
- 빈도수가 낮거나 높은 숫자부터 정수를 부여한다.
1) dictionary 사용하기
- 텍스트를 문장 토큰화한다.
sent_tokenize
함수를 이용한다.
- 단어 토큰화를 진행한다.
- 단어에 대해 정규화를 진행한다.
- 단어를 소문자화 한다.
- 불용어를 제거한다
- 길이가 2이 하인 경우에 대해 단어를 제거한다.
- 전처리가 끝난 토큰을 딕셔너리와 리스트에 각각 저장한다.
- 이렇게 정수 인코딩을 하면 상위 n개의 단어만 사용할 수 있다.
- 토큰화 된 단어를 정수로 바꾸는 작업을 한다.
- 빈도수가 낮거나 불용어 등 제거된 단어는 인덱스 딕셔너리에 존재하지 않게 되는데, 이러한 단어들을 OOV(Out-Of-Vocabulary)라고 한다.
- 이러한 단어들은 OOV라는 단어를 새로 추가하여 관리한다고 하자.
- 이러한 원리로 정수 인코딩이 진행된다.
- 이보다는 좀더 쉽게 하기 위해서 Counter, FreqDist, enumerate나 keras tokenizer를 사용할 수 있다.
💡 나중에 필요 시 공부하자
[https://wikidocs.net/31766](https://wikidocs.net/31766)
02-07. 패딩(Padding)
- 각 문장(또는 문서)는 서로 길이가 다를 수 있다. 그런데 기계는 길이가 전부 동일한 문서들에 대해서는 하나의 행렬로 보고, 한꺼번에 묶어서 처리할 수 있다.
- 즉 병렬 연산을 위해 여러 문장의 길이를 임의로 동일하게 맞춰주는 작업을 패딩이라고 한다.
1. Numpy로 패딩하기
- 먼저 정수 인코딩이 완료된 문장을 준비한다.
- 최대길이 문장을 기준으로 가상의 문제
‘PAD’ : 0
을 만들어서 길이가 짧은 문장에 숫자 0을 채워준다. (0은 관습)
- 이렇게 숫자 0을 사용하여 데이터의 shape을 조정하는 것을 제로 패딩이라고 한다.
2. 케라스 전처리 도구로 패딩하기
- 케라스는 패딩을 위한 도구로
pad_sequences()
를 제공한다.
케라스 전처리 도구 다루기
- 기본적으로 케라스 전처리 도구는 문장의 앞을 0으로 채운다.
- 문장의 뒤를 0으로 채우고 싶다면
padding = 'post'
로 가능하다.
- 또한 문장의 최대 길이를 기준으로 패딩하지 않아도 된다.
maxlen=n
인자를 통해 n의 길이로 패딩할 수 있다. 이 때 n보다 긴 길이의 문자는 정보가 손실된다.
- 기본적으로 뒤에있는 단어를 남기고, 앞에 있는 단어를 삭제한다.
- 앞쪽에 있는 단어를 남기고 싶다면
truncation='post'
인자를 사용할 수 있다.
- 0으로 패딩하고 싶지 않다면
value=n
으로 다른 숫자로 패딩이 가능하다.
02-08. 원-핫 인코딩(One-Hot Encoding)
- 단어 집합(vocabulary)은 서로 다른 단어들의 집합이다.
- 단어 집합에 있는 단어들을 벡터로 바꾸는 것을 작업에는 여러가지 방법이 있는데, 원-핫 인코딩은 그 방법 중 하나이다.
- 텍스트를 단어 토큰화 한 뒤, 정수 인코딩 까지 진행한 데이터에 대해 원-핫 인코딩을 진행한다.
1. 원-핫 인코딩이란?
- 단어 집합의 크기를 벡터의 차원으로 하고, 표현하고 싶은 단어의 인덱스에 1의 값을, 다른 인덱스에는 0을 부여하는 단어의 벡터 표현 방식이다.
- 이렇게 표현된 벡터를 원-핫 벡터(One-Hot Vector)라고 한다.
2. 케라스를 이용한 원-핫 인코딩
- 케라스가 제공하는 to_categorical 함수를 이용하여 원-핫 인코딩이 가능하다.
3. 원-핫 인코딩의 한계
- 단어의 개수가 늘어날 수록, 벡터를 저장하기 위해 필요한 공간이 계속 늘어난다는 단점이 있다. (공간 측면에서 매우 비효율적인 표현 방법)
- 단어의 유사도를 표현할 수 없다는 단점도 존재한다.
- 강아지, 개, 냉장고라는 단어가 있을 때 강아지와 개가 유사하다는 것이 명확하지만, 원-핫 인코딩을 한다면 어떤 단어가 더 유사한지 알 수 없다.
- 유사도를 표현할 수 없을 때 발생하는 문제점은, 삿포로 숙소를 검색했을 때 료칸, 호텔, 게스트 하우스를 보여줘야하는데 숙소와 유사한 단어를 알 수 없어 이를 보여줄 수 없다.
- 이러한 단점을 해결하기 위해 단어의 잠재 의미를 반영하여 다차원 공간에 벡터화 하는 기법이 있다.
- LSA(잠재 의미 분석), HAL : 카운트 기반의 벡터화 방법
- NNLM, RNNLM, Word2Vec, FastText : 예측 기반의 벡터화 방법
- GloVe : 두 가지 방법을 모두 사용하는 방법
- 이들 기법에 대해서는 워드 임베딩 챕터에서 다루게 될 것이다.
02-09. 데이터의 분리(Splitting Data)
- 머신 러닝 모델을 학습시키고 평가하기 위해서는 데이터를 적절하게 분리하는 작업이 필요하다.
- 이번에는 지도 학습을 위한 데이터 분리 작업에 대해 배워보자
1. 지도 학습 (Supervised Learning)
- 지도 학습의 훈련 데이터는 ‘문제’에 해당하는 데이터(X)와 레이블이라고 부르는 정답 데이터(y)로 구성되어있다.
- 머신은 문제와 정답을 통해 학습한 뒤, 정답이 없고 문제만 존재하는 데이터에 대해 정답을 잘 예측할 수 있어야 한다.
- 이를 위해 기계를 학습시키는 데에 사용하는 학습 데이터와 기계의 성능을 평가하기 위해 사용하는 테스트 데이터를 분리해 놓아야 한다.
2. X와 y분리하기
1) zip 함수를 이용한 분리
- zip() 함수는 동일한 개수를 가지는 시퀀스 자료형에서, 각 순서에 등장하는 원소들끼리 묶어주는 역할을 한다.
sequences = [['a', 1], ['b', 2], ['c', 3]]
X, y = zip(*sequences)
print('X 데이터 :',X)
print('y 데이터 :',y)
X 데이터 : ('a', 'b', 'c')
y 데이터 : (1, 2, 3)
2) 데이터프레임을 이용하여 분리하기
- 리스트 또는 nparray를 데이터프레임으로 변환하면 손쉽게 분리가 가능하다.
values = [['당신에게 드리는 마지막 혜택!', 1],
['내일 뵐 수 있을지 확인 부탁드...', 0],
['도연씨. 잘 지내시죠? 오랜만입...', 0],
['(광고) AI로 주가를 예측할 수 있다!', 1]]
columns = ['메일 본문', '스팸 메일 유무']
df = pd.DataFrame(values, columns=columns)
df
3) Numpy를 이용하여 분리하기
- Numpy의 슬라이싱을 사용하여 데이터를 분리할 수 있다.
np_array = np.arange(0,16).reshape((4,4))
print('전체 데이터 :')
print(np_array)
전체 데이터 :
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
X = np_array[:, :3]
y = np_array[:,3]
print('X 데이터 :')
print(X)
print('y 데이터 :',y)
X 데이터 :
[[ 0 1 2]
[ 4 5 6]
[ 8 9 10]
[12 13 14]]
y 데이터 : [ 3 7 11 15]
3. 테스트 데이터 분리하기
💡 알고 있는 내용이므로 다소 간단하게 짚고 넘어가자
1) 사이킷 런을 이용하여 분리하기
train_test_split(X, y, {test_size | train_size}, random_state)
- X : 독립 변수 데이터 (배열이나 데이터프레임)
- y : 종속 변수 데이터 (레이블)
- test_size : 테스터용 데이터 개수 지정 (1보다 작은 경우 비율을 나타냄)
- train_size : 학습용 데이터 개수 지정 (1보다 작은 경우 비율을 나타냄)
- test_size, train_size 둘 중 하나만 기재하면 된다.
- random_state : 난수 시드
2) 수동으로 분리하기
-
학습데이터와 테스트 데이터의 개수를 정한다. 한 쪽을 정한 뒤 다른 쪽은 전체에서 한 쪽을 빼는 식으로 구할 수 있다.
-
슬라이싱을 통해서 데이터를 구분할 수 있다.
X_test = X[num_of_train:]
y_test = y[num_of_train:]
X_train = X[:num_of_train]
y_train = y[:num_of_train]
-
단 이렇게 수동으로 분리하는 경우 train_test_split과 달리 데이터가 뒤섞이지 않는다. 데이터를 섞는 과정은 추후 진행할 것이다.
2-10. 한국어 전처리 패키지
1. PyKoSpacing
!pip install git+https://github.com/haven-jeon/PyKoSpacing.git
- 띄어쓰기가 되어있지 않은 문장을 띄어쓰기 한 문장으로 변환해주는 패키지
- 대용량 코퍼스 학습을 통해 만들어져 있어 준수한 성능을 갖고 있다.
2. Py-Hanspell
!pip install git+https://github.com/ssut/py-hanspell.git
- 네이버 한글 맞춤법 검사기를 바탕으로 만들어진 패키지이다.
- 띄어쓰기 또한 보정해준다.
3. SOYNLP
!pip install soynlp
- 품사 태깅, 단어 토큰화 등을 지원하는 단어 토크나이저이다.
- 비지도 학습으로 단어 토큰화를 한다는 특징을 갖고 있으며, 데이터에 자주 등장하는 단어들을 단어로 분석한다.
- 응집확률과 브랜칭 엔트로피를 활용한다.
1) 신조어 문제
- 기존의 형태소 분석기들은 분석기에 등록되지 않은 신조어와 같은 단어를 제대로 구분하지 못한다는 단점이 있다.
- 르세라핌 → 르 + 세라핌
- 총공 → 총 + 공
- soynlp에서는 텍스트 데이터에서 자주 연결되어 등장하는 문자열을 한 단어로 판단하고, 또 단어 앞뒤에 붙는 독립된 다른 단어들이 계속 등장하면 판탄하는 식이다.
2) 학습하기
- soynlp는 학습 기반의 단어 토크나이저로, 학습 과정을 통해 전체 코퍼스에서 응집 확률과 브랜칭 엔트로피 단어 점수표를 만들어야 한다.
WordExtractor.extract()
를 통해 전체 코퍼스에 대해 단어 점수표를 계산한다.
3) SOYNLP의 응집 확률(cohesion probability)
- 응집 확률은 문자열이 얼마나 자주 응집하여 등장하는지 판단하는 척도이다.
인스턴스[”문자열”].cohesion_forward
로 확인할 수 있다.
- 문자 단위로 분리하여 내부 문자열을 만드는 과정에서, 어떤 문자열이 주어졌을 때 그 다음 문자가 나올 확률을 계산하여 누적곱 한다.
- 르세라핌최고라는 단어가 있다고 할 때
- cohesion(2) = ‘르’가 나왔을 때 ‘르세’가 나올 확률(P1)
- cohesion(3) = P1 * ‘르세’가 나왔을 때 ‘르세라’가 나올 확률(P2) → 제곱근을 씌움
- cohesion(4) = P2 * ‘르세라’가 나왔을 때 ‘르세라핌’이 나올 확률(P3) → 세제곱근
- 이런 식이다.
- 이렇게 응집 확률을 계산하면 아마 다음과 같은 결과가 나올 것이다.
- 르세라는 르세보다 응집 확률이 높다
- 르세라핌은 르세라보다 응집 확률이 높다
- 르세라핌최는 르세라핌보다 응집 확률이 낮다
- 이때 르세라핌의 결합도가 가장 높다고보고, 르세라핌을 하나의 단어로 보기에 가장 적합한 문자열이라고 판단할 수 있다.
4) SOYNLP의 브랜칭 엔트로피(branching entropy)
인스턴스[”문자열”].right_branching_entropy
로 확인할 수 있다.
- 브랜칭 엔트로피는 주어진 문자열에서 얼마나 다음 문자가 등장할 수 있는지를 판단하는 척도이다. 이를 위해 확률 분포의 엔트로피값을 사용한다.
- ‘초’ 다음에 등장할 문자는 무엇일까?
- ‘초보’ 다음에 등장할 문자는 무엇일까?
- ‘초보운’ 다음에 등장할 문자는 무엇일까?
- 이렇게 완성된 단어에 가까워 질수록 문맥에 따라 정답 후보가 줄어든다는 것을 의미한다.
- 브렌칭 엔트로피 값이 0이면 다음에 올 글자가 명백하다는 뜻이다.
- 다음에 올 글자의 가능성이 많을 수록 큰 값을 가진다.
5) SOYNLP의 L tokenizer
- 한국어에서 띄어쓰기 단위로 나눈 어절은 L토큰 + R토큰의 형식을 가질때가 많다
- 밥 + 을, 나 + 는, 시내 + 에서, 경찰 + 을
- LTokenizer는 L토큰 + R토큰으로 나누되, 분리 기준으로 점수가 가장 높은 것을 찾아낸다.
6) 최대 점수 토크나이저
- 최대 점수 토크나이저는 띄어쓰기가 되어있지 않은 문장에서 점수를 바탕으로 글자 시퀀스를 찾아 낸다.
4. SOYNLP를 이용한 반복되는 문자 정제
- SNS나 채팅데이터는 이모티콘이 불필요하게 연속되는 경우가 많은데, 이를 서로 다른 단어로 처리하는 것을 불필요하다.
- SOYNLP에서는
emoticon_normalize()
를 통해 반복되는 문자를 하나로 정규화 시킨다.
num_repeats=
인자를 통해 반복되는 문자를 몇글자까지 정규화 시킬지 지정할 수 있다.
5. Customized KoNLPy
!pip install customized_konlpy
- 위와 같은 노력에도 불구하고, 형태소 분석기를 통한 단어 토큰화가 잘 이루어지지 않는 경우가 많다.
- ex) 이강철 → 이 + 강철, 은경이 → 은 + 경이
- 이렇게 잘못 분류되는 경우를 막기 위해서 형태소 분석기에 사용자 사전을 추가해 줄 수 있다.
- Customized_Konlpy 패키지를 이용하면 사용자 사전을 매우 쉽게 추가할 수 있다.
- 사용법
-
먼저 사용할 형태소 분석기를 선언한다.
-
형태소 분석기의 add_dictionary('단어', '품사')
를 메소드 형식으로 실행하면 사전 추가를 해줄 수 있다.
from ckonlpy.tag import Twitter
twitter = Twitter()
twitter.morphs('나균안이라는 이름이 있다니 정말 특이하다.')
>> ['나균', '안이', '라는', '이름', '이', '있다니', '정말', '특', '이하', '다', '.']
twitter.add_dictionary('나균안', 'Noun')
twitter.morphs('나균안이라는 이름이 있다니 정말 특이하다.')
>> ['나균안', '이라는', '이름', '이', '있다니', '정말', '특', '이하', '다', '.']
💡 궁금한 점 : 특이하다를 특 + 이하 + 다로 분류하는 것이 맞을까?
사전에 ‘특이’라는 단어를 추가해봤지만 여전히 분리되어 토큰화가 이루어진다.