본문 바로가기

공부

[NLP] Text Vectorization (작성중)

반응형
<기본용어 >
말뭉치(Corpus) : 특정한 목적을 가지고 수집한 텍스트 데이터
문서(Document) : 문장(Sentence)들의 집합
문장(Sentence) : 여러 개의 토큰(단어, 형태소)으로 구성된 문자열, 마침표, 느낌표 등의 기호로 구분
어휘집합(Vocabulary) : 코퍼스에 있는 모든 문서, 문장을 토큰화 하고 중복을 제거한 토큰의 집합
토큰(Token) : 의미를 가진 가장 작은 단위
토큰화(Tokenization) : 토큰단위로 나누는 것

자연어 처리(Natural Language Processing, NLP)

자연어(사람들이 일상적으로 사용하는 언어를 인공적으로 만들어진 언어인 인공어와 구분하여 부르는 개념)를 컴퓨터로 처리하는 기술


Text Vectorization

자연어 처리에서는 기계가 문자(자연어)를 이해 할 수 있도록 수치화해주는 과정이 반드시 필요하다. 자연어를 벡터화화하는 방법은 크게 두가지로 나뉜다.

 

1. 등장 횟수 기반의 단어 표현(Counte-based Representation) : 단어가 문서(혹은 문장)에 등장하는 횟수를 기반으로 벡터화 하는 방법
	- Bag-of-Words
	- TF-IDF
    
2. 분포 기반의 단어 표현 ( Distributed Representation) : 타겟 단어 주변에 있는 단어를 벡터화 하는 방법
	- Word2Vec
    	- Glove
    	- fastText

 


텍스트 전처리(Text Preprocessing)

자연어 처리 에서는 텍스트 전처리 과정이 절반 이상을 차지하는 중요한 과정이다.

1. 차원의 저주(Curse of Dimensionality)

  • 차원이 늘어날수록 설명력이 떨어진다.
  • 횟수 기반의 벡터 표현에서는 전체 말뭉치에 존재하는 단어 종류가 데이터셋의 Feature, 즉 차원이 된다. 따라서 단어의 종류를 줄여야 차원을 줄일 수 있다.

2. 대,소문자 통일 + 정규표현식(Regex) 사용

3. SpaCy라이브러리를 사용하여 토큰화

# 자연어 처리를 위한 모듈과 모델 가져오기
import spacy
from spacy.tokenizer import Tokenizer
nlp = spacy.load("en_core_web_sm")
tokenizer = Tokenizer(nlp.vocab)

# 토큰화를 위한 파이프라인 구성
tokens = []

# text를 토큰화
for doc in tokenizer.pipe(df['reviews.text']):
	# re.sub(정규표현식, 대상 문자열, 치환 문자)
	doc_tokens = [re.sub(r"[^a-z0-9]", "", token.text.lower()) for token in doc]
	tokens.append(doc_tokens)
    
df['tokens'] = tokens
df['tokens'].head()


>>>
0    [though, i, have, got, it, for, cheap, price, ...
1    [i, purchased, the, 7, for, my, son, when, he,...
2    [great, price, and, great, batteries, i, will,...
3    [great, tablet, for, kids, my, boys, love, the...
4    [they, lasted, really, little, some, of, them,...
Name: tokens, dtype: object

4. 불용어 처리

'i', 'and', 'of' 와 같은 단어들은 리뷰텍스트 데이터 관점에서 아무런 의미가 없다. 이러한 단어들을 'Stop words(불용어)'라고 한다.

대부분의 NLP라이브러리는 접속사, 관사, 부사, 대명사, 일반동사등을 포함해서 불용어를 내장하고 있다.s
spacy를 통해 기본으로 제공하는 불용어를 알아볼 수 있다.

print(nlp.Defaults.stop_words)

불용어 제외하고 토크나이징

tokens = []

# 토큰에서 불용어 제거, 소문자화 후 업데이트
for doc in tokenizer.pipe(df['reviews.text']):
	doc_tokens = []
    
    # 토큰이 불용어와 구두점이 아닌경우 소문자로 변경후 적용
    for token in doc:
    	if (token.is_stop == False) & (token.is_punct == False):
        	doc_tokens.append(token.text.lower())
            
    tokens.append(doc_tokens)
    
df['tokens'] = tokens
df['tokens'].head()



tokens = []
# 토큰에서 불용어 제거, 소문자화 하여 업데이트
for doc in tokenizer.pipe(df['reviews.text']):
    doc_tokens = []

    # A doc is a sequence of Token(<class 'spacy.tokens.doc.Doc'>)
    for token in doc:
        # 토큰이 불용어와 구두점이 아니면 저장
        if (token.is_stop == False) & (token.is_punct == False):
            doc_tokens.append(token.text.lower())

0    [got, cheap, price, black, friday,, fire, grea...
1    [purchased, 7", son, 1.5, years, old,, broke, ...
2    [great, price, great, batteries!, buying, anyt...
3         [great, tablet, kids, boys, love, tablets!!]
4    [lasted, little.., (some, them), use, batterie...
Name: tokens, dtype: object

+ 데이터에 따라 불용어가 다른데, 몇몇 단어들을 불용어 처리 할 수 있음.

STOP_WORDS = nlp.Defaults.stop_words.union(['batteries','I', 'amazon', 'i', 'Amazon', 'it', "it's", 'it.', 'the', 'this'])

tokens = []

for doc in tokenizer.pipe(df['reviews.text']):
	doc_tokens = []
   
	for token in doc:
            if token.text.lower() not in STOP_WORDS:
            	doc_tokens.append(token.text.lower())
        tokens.append(doc_tokens)
df['tokens'] = tokens

5. 통계적 트리밍(Trimming)

불용어를 직접적으로 제거하는 대신 통계적인 방법을 통해 말뭉치 내에서 너무 많거나, 너무 적은 토큰을 제거하는 방법도 있다.

6. 어간 추출(Stemming)과 표제어 추출(Lemmatization)

위에서 토큰화 된 단어들을 보면, 조금 더 수정이 필요한 부분이 보인다.
예를 들어 'batteries'와 'battery'를 보면 이 둘은 어근(root)이 같은 다어이다.
이런 단어는 어간 추출(stemming)이나 표제어 추출(lemmatization)을 통해 정규화(Normalization)해준다.

  • 어간 추출(Stemming)
    단어의 의미가 포함된 부분으로 접사등이 제거된 형태, 어근이나 단어의 원형이 같지 않을 수 있다.
    예를 들어, argue, argued, arguing, argus의 어간은 단어들의 뒷 부분이 제거된 argu가 어간이다.
    어간 추출은 'ing', 'ed', 's' 등과 같은 부분을 제거하게 된다.
    어간 추출의 경우 Spacy는 Stemming을 제공하지 않고 Lemmatization만 제공하기 때문에 nltk를 사용해서 Stemming을 해볼 수 있다.
from nltk.stem import PorterStemmer
ps = PorterStemmer()
words = ['wolf', 'wolves']

for word in words:
	print(ps.stem(word))

>>>
wolf
wolv

Steeming에서 해본 Porter 알고리즘은 단어의 끝 부분을 자르는 역할을 한다. 그러나 batteries를 batteri라고 출력되는 이상한 경우들이 있다. 현실적으로 사용하기에 알고리즘이 간단하여 속도가 빨라 검색 분야에서 많이 사용한다.

  • 표제어 추출(Lemmatization)
    표제어 추출(Lemmatization)은 어간추출보다 체계적입니다.
    단어들은 기본 산전형 단어 형태인 Lemma(표제어)로 변환됩니다.
    명사의 복수형은 다수형으로, 동사는 모두 타동사로 변환됩니다.
    이런식으로 단어에서 표제어로 찾아가기 때문에 Stemming보다는 많은 연산을 필요로 한다.
lem = 'The social wolf. Wolves are complex.'
nlp = spacy.load('en_core_web_sm')
doc = nlp(lem)

# 그냥 추출한것과 Lemma로 추출된것 비교
for token in doc:
	print(token.text, '  ', token.lemma_)
>>>
The    the
social    social
wolf    wolf
.    .
Wolves    wolf
are    be
complex    complex
.	 .

Stemming과 Lemmatization의 출력물을 비교해 보면 Stemming의 경우 wolf -> wolf, wolves -> wolv로 변형되었고, Lemmatization에서는 wolf -> wolf, wolves -> wolf로 변형된 차이가 있다.

 

Lemmatization 과정 함수화

def get_lemmas(text):
	lemmas = []
	doc = nlp(text)
    
    	for token in doc:
        	if ((token.is_stop == False) and (token.is_punct == False)) and (token.pos_ != 'PRON'):
            		lemmas.append(token.lemma_)
        return lemmas

 


1. 원-핫 인코딩(One-hot encoding)

원-핫 인코딩은 텍스트를 유의미한 숫자(벡터)로 바꾸는 가장 쉬운 방법이다. N개의 단어를 각각 N차원의 벡터로 표현하는 방식이다. 단어에 해당되는 차원(인덱스)에 1을 넣고 나머지에는 0을 넣는다. 원-핫 인코딩은 단어 또는 문자(characters)를 기준으로 벡터화 할 수 있다. 다음은 원-핫 인코딩을 이용한 단어 벡터화의 예이다.

그림 1  원-핫 인코딩을 이용한 단어의 벡터화

원-핫 벡터의 특징

  • 하나의 요소만 1이고 나머지는 모두 0인 희소 벡터(sparse vector)이다.
  • 원-핫 벡터의 차원은 말뭉치(corpus) 내 단어의 수와 같다.
  • 두 원-핫 벡터 간의 내적(inner product)이 0이다. 즉, 두 벡터는 직교(orthogonal)이며 서로 독립(independent)이다.
  • 단어의 특정한 관계나 의미를 포함하지 않는다.

 

2. TF-IDF: 빈도수 기반 텍스트(문서) 벡터화 방법

TF-IDF(Term Frequency - Inverse Document Frequency)는 특정 단어가 문서 내에 등장하는 빈도(TF: 단어빈도)와 그 단어가 문서 전체 집합에서 등장하는 빈도(IDF: 역문서 빈도)를 고려하여 벡터화 하는 방법이다. 하나의 문서 단위로 벡터를 만들며 각 인덱스에 해당하는 단어가 문서에 등장하는 빈도와 문서 집합 전체에 등장하는 빈도의 역수를 곱하여 구하게 된다. 

# TFIDF, unigrams and bigrams
vect = TfidfVectorizer(tokenizer=preprocess_and_tokenize, sublinear_tf=True, norm='l2', ngram_range=(1, 2))

 

3. 단어 임베딩(Word Embedding)

단어를 벡터화하는 또 다른 방법으로 분산 표상(Distributedsimilarity based representation) 개념을 바탕으로 의미를 포함하는 단어 벡터로 바꾸는 단어 임베딩 기법이 있다. 이는 비슷한 분포를 가진 단어의 주변 단어들도 비슷한 의미를 가진다는 것을 가정한다. 원-핫 인코딩과는 달리, 분산 표상에서는 하나의 단어가 미리 정한 차원(200~300차원 정도)에서 연속형의 값을 갖는 벡터로 표현된다. 이렇게 만들어진 단어 벡터는 단어의 의미 담고있으며, 단어 벡터 간의 연산도 가능하다.

그림4  단어 임베딩

많은 양의 문서를 학습하여 얻어진 단어 벡터는 단어 간의 관계를 보다 정확하게 나타낸다. 단어를 벡터로 임베딩하는 방식은 머신러닝을 통해 학습된다. 신경망을 기반으로 한 단어 벡터화의 대표적 방법은 Word2Vec이 있다. Word2vec은 CBOW(continuous bag of words)와 Skip-gram(SG)의 두 가지 알고리즘이 있고 일반적으로 SG가 CBOW 알고리즘보다 학습이 잘 이루어진다고 알려져 있다. 그 외 GloVe, FastText과 같은 방법론도 있다.

 

 

 

출처 및 참고 : 

https://velog.io/@ljs7463/%EC%9E%90%EC%97%B0%EC%96%B4%EC%B2%98%EB%A6%ACNLP-%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%A0%84%EC%B2%98%EB%A6%AC%EB%B2%A1%ED%84%B0%ED%99%94

http://junyelee.blogspot.com/2018/01/deep-learning-with-python.html

https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/rvecs.pdf

https://icim.nims.re.kr/post/easyMath/841

반응형

'공부' 카테고리의 다른 글

[NLP] 한글 임베딩  (0) 2022.12.28
머신러닝 강의 1편  (1) 2022.12.21
Voting, Bagging, Boosting  (0) 2022.10.28
상관계수의 종류  (0) 2022.08.30
[NLP] 모델 정확도 향상(keras.preprocessing vs nltk.tokenize)  (0) 2022.07.26