-
[Deep Learning] NLP - 통계적 트리밍(Trimming)Data Science/Machine Learning & Deep Learning 2021. 4. 15. 00:58
자연어 처리에서 불용어 사전을 이용해서 불용어를 직접 제거해주는 방법도 있지만,
통계적인 방법을 이용해서 전체 말뭉치에서 너무 많이 쓰였거나 너무 적게 쓰인 토큰을 제거해주는 방법도 있습니다.
위 왼쪽 그래프는 토큰화 후 토큰의 누적 분포를 나타낸 그래프입니다.
그래프를 통해 소수의 단어들이 전체 텍스트 데이터의 약 90%를 차지하고 있음을 알 수 있습니다.
이는 전체 말뭉치에서 자주 나타나는 단어들(그래프의 왼쪽)이거나 자주 나타나지 않는 단어들(그래프의 오른쪽)로
너무 많이 또는 너무 드물게 나타나기 때문에 텍스트 큰 의미를 가지지 못하고 통찰력을 제공하지 못할 확률이 높습니다.
따라서 통계적 트리밍으로 너무 많이 쓰인 토큰과 너무 적게 쓰인 토큰을 제거하여 우측 그래프와 같은 분포를 만들어 주겠습니다.
아래 Kaggle의 Women's E-Commerce Clothing Reviews 데이터를 사용하였습니다.
Women's E-Commerce Clothing Reviews
23,000 Customer Reviews and Ratings
www.kaggle.com
데이터는 다음과 같이 구성되어 있습니다.
df.head(3)
데이터에서 리뷰에 해당하는 'Review Text' 를 토큰화 하기 위해서 토큰화 함수 tokenize를 만들어 사용하겠습니다.
import spacy from spacy.tokenizer import Tokenizer nlp = spacy.load("en_core_web_sm") tokenizer = Tokenizer(nlp.vocab) import re def tokenize(text): """ 토큰화 함수 : text 문자열을 의미있는 단어 단위로 list에 저장합니다. """ tokens = str(text).lower() tokens = re.sub(r"[^a-z0-9 ]", "", tokens) return [token.text for token in tokenizer(x)]
df['tokens'] = df['Review Text'].apply(lambda x : tokenize(x)) df.head()
통계적 전처리를 위해 말뭉치 전체의 토큰 카운트하고, 순위 등 정보를 담은 데이터 프레임을 리턴하는 함수를 만들어 주겠습니다.
def word_count(docs): """ 토큰화된 문서들을 입력받아 토큰을 카운트 하고 관련된 속성을 가진 데이터프레임을 리턴합니다. Args: docs (series or list): 토큰화된 문서가 들어있는 list Returns: list: Dataframe """ # 전체 코퍼스에서 단어 빈도 카운트 word_counts = Counter() # 단어가 존재하는 문서의 빈도 카운트, 단어가 한 번 이상 존재하면 +1 word_in_docs = Counter() # 전체 문서의 갯수 total_docs = len(docs) for doc in docs: word_counts.update(doc) word_in_docs.update(set(doc)) temp = zip(word_counts.keys(), word_counts.values()) wc = pd.DataFrame(temp, columns = ['word', 'count']) # 단어의 순위 # method='first': 같은 값의 경우 먼저나온 요소를 우선 wc['rank'] = wc['count'].rank(method='first', ascending=False) total = wc['count'].sum() # 코퍼스 내 단어의 비율 wc['percent'] = wc['count'].apply(lambda x: x / total) wc = wc.sort_values(by='rank') # 누적 비율 # cumsum() : cumulative sum wc['cul_percent'] = wc['percent'].cumsum() temp2 = zip(word_in_docs.keys(), word_in_docs.values()) ac = pd.DataFrame(temp2, columns=['word', 'word_in_docs']) wc = ac.merge(wc, on='word') # 전체 문서 중 존재하는 비율 wc['word_in_docs_percent'] = wc['word_in_docs'].apply(lambda x: x / total_docs) return wc.sort_values(by='rank')
많이 쓰인 토큰 상위 10개를 알아보고 토큰의 누적 분포, 빈도를 살펴보겠습니다.
wc = word_count(df['tokens']) # 상위 토큰 10개 단어 wc.head(10)
import seaborn as sns # 누적분포그래프 sns.lineplot(x='rank', y='cul_percent', data=wc);
# 문서에 나타나는 빈도 sns.displot(wc['word_in_docs_percent'],kind='kde');
상위 10 안에 랭크된 토큰은 대부분 the, i, and, a 등과 같이 해석에 큰 의미를 주지 못하는 불용어입니다.
토큰의 누적 분포를 그래프로 살펴보면 소수의 단어들이 전체 텍스트 데이터의 약 90%를 차지하고 있으며,
많은 수의 토큰이 문서에 등장하는 빈도가 0.05 이하로 빈도가 낮은 것을 알 수 있습니다.
따라서 문서에 너무 많이 등장하는 토큰, 너무 드물게 쓰인 토큰을 제거하기 위해서
문서에 등장하는 비율이 10% 미만이고, 최소 2.5% 이상은 등장하는 토큰만을 선택고 다시 상위 10개 토큰을 살펴보겠습니다.
# 문서 등장 비율이 10% 미만이고, 최소 2.5% 이상 등장하는 단어만 선택 wc_trim = wc[(wc['word_in_docs_percent'] < 0.10)&(wc['word_in_docs_percent'] >= 0.025)] # 통계적 트리밍 후 상위 토큰 10개 단어 wc_trim.head(10)
# 통계적 트리밍 후 누적분포그래프 sns.lineplot(x='rank', y='cul_percent', data=wc_trim);
통계적 트리밍 이전 대부분 불용어가 차지하고 있었던 상위 10개의 토큰이
통계적 트리밍 이후 불용어 보다 텍스트 분석에 의미를 가질 듯한 토큰들이 차지한 것을 볼 수 있습니다.
또한 누적 분포 그래프의 형태를 통해서 빈번하거나 드물게 나타나는 토큰이 제거된 것을 확인할 수 있습니다.
통계적 트리밍은 불용어 제거 대신 사용할 수도 있고, 불용어를 제거한 후 통계적 트리밍을 함께 사용할 수도 있습니다.
'Data Science > Machine Learning & Deep Learning' 카테고리의 다른 글
[Deep Learning] Bag-of-Words(BoW) - CountVectorizer, TF-IDF Vectorizer (0) 2021.04.18 [Deep Learning] NLP - 어간추출(stemming) & 표제어추출(lemmatization) (0) 2021.04.16 [Deep Learning] NLP - 불용어 ( Stop Words ) 처리 (0) 2021.04.15 [Deep Learning] NLP - 텍스트 토큰화 ( Tokenization ) (0) 2021.04.13 [Deep Learning] 손실함수(Loss Function) (0) 2021.04.13