Text Analysis
본 내용은 inflearn 권철민 강사님의 파이썬 머신러닝 완벽 가이드 영상을 통해 정리해 보았습니다.
Last updated
본 내용은 inflearn 권철민 강사님의 파이썬 머신러닝 완벽 가이드 영상을 통해 정리해 보았습니다.
Last updated
NLP와 Text 분석을 구분하는데는 의미 없으나 NLP는 인간의 언어를 이해하는데 중점을 두고 있다.최근 딥러닝 기술에 의해 정교하게 발전 되었다. ex) 질의응답 (Google Translater, PAPAGO)
텍스트 분석은 개념이 광범위하지만, 비정형 데이터에서 의미있는 정보를 추출하여 인텔리전스 , 예측분석 하는 작업이다.
텍스트분석에 주로 분류, 감성분석, 텍스트 요약, 군집화와 유사도 측정이 있다.
데이터(text)를 사전에 가공하여 Feature Vectorization 후 ML 기반의 학습/예측을 진행한다.
*부가설명 :
Featuere Vectorization : 텍스트를 Feature로 만드는 방법으로 대표적인 방법으로 Bag of Words, Word2Vec이 있다. 차이는 Bag of words는 단어들을 feature로 만드는 것으로 보통 count 값이나 , 정규화된 값으로 만든다. word2vec은 Word Embedding을 통해 단어간 유사성을 알아본다.
NLTK : NLP패키지의 시조새. 딥러닝이 나오기 전까지 NLP 패키지로 가장 많이 사용했을 것이다.
방대한 모듈을 가지고 있는 패키지지만 속도가 느려 대량기반 데이터에서는 활용하기 어렵다.
Gensim : Topic Modeling에서 활용되는 패키지로 word2vec 의 구현 가능. Spacy와 함께 사용을 많이 한다.
SpaCy : 최근에 가장 주목받고 있는 패키지로, 성능이 뛰어나다.
1.클렌징: 불필요한 문자나 기호 등을 제거 ex) HTML/XML 태그, 특수 기호
2.토큰화: 단어 토큰화 , N-gram (한국어는 어려움...형태소와 품사가 중요하기 때)
3.필터링, 스톱워드 제거 : 분석에 불필요한 단어 혹은 의미없는 단어 제거 및 수정. ex) a,the is
4.Stemming/Lemmatization: 의미론적에서 단어의 원형을 추출. ex) happier -> happy, amusing-> amus
*부가설명 N-gram : 연속된 N개의 단어를 하나의 토큰화 단위로 분리해낸 것으로 N개의 단어 크기를 윈도우를 만든 후 문장의 윈도우들을 오른쪽으로 움직이며 토큰화 수행
ex)인적자원관리 => (인적),(자원),(관리) =>(인적,자원),(자원,관)
(예제 코드 출처 : https://github.com/chulminkw/PerfectGuide)
import nltk
nltk.download('punkt')
# 문장들을(text sample) 마침표(or 쉼표)로 구분 후 ,리스트 형태로 출력이 된다.
from nltk import sent_tokenize
text_sample = 'The Matrix is everywhere its all around us, here even in this room. You can see it out your window or on your television. You feel it when you go to work, or go to church or pay your taxes.'
sentences = sent_tokenize(text=text_sample)
print(type(sentences),len(sentences))
print(sentences)
문장의 공백을 기준으로 단어들을 분리 시킨다.
from nltk import word_tokenize
sentence = "The Matrix is everywhere its all around us, here even in this room."
words = word_tokenize(sentence)
print(type(words), len(words))
print(words)
문서의 문장들을 개별 단어 별로 하나씩 토큰화 할 경우 문맥적 의미가 무시될 수 밖에 없다. 이러한 문제를 조금이라도 보완하고자 도입된 것이 n-gram이다.
n-gram은 연속된 n개의 단어를 하나의 토큰화 단위로 분리해내는 것인데, n개의 단어크기 윈도우를 만들어 문장의 처음부터 오른쪽으로 움직이며 토큰화를 수행한다.
ex) I have pencil 이라는 문장을 2-gram(bigram)시키면 (I ,have) (have, pencil) 과 같이 연속적으로 2개의 단어들을 윈도우를 이동해가면서 단어들을 토큰화 시킨다.
from nltk import ngrams
sentence = "The Matrix is everywhere its all around us, here even in this room."
words = word_tokenize(sentence) #문장들에 대해 단어 토큰화
all_ngrams = ngrams(words, 2) #2-gram
ngrams = [ngram for ngram in all_ngrams]
print(ngrams)
import nltk nltk.download('stopwords')
#불필요한 대상들들을 보여
print('영어 stop words 갯수:',len(nltk.corpus.stopwords.words('english'))) print(nltk.corpus.stopwords.words('english')[:40])
import nltk
stopwords = nltk.corpus.stopwords.words('english')
all_tokens = []
for sentence in word_tokens:
filtered_words=[]
for word in sentence:
#소문자 변환
word = word.lower()
if word not in stopwords:
filtered_words.append(word)
all_tokens.append(filtered_words)
print(all_tokens)
from nltk.stem import LancasterStemmer
stemmer = LancasterStemmer()
print(stemmer.stem('working'),stemmer.stem('works'),stemmer.stem('worked')) print(stemmer.stem('amusing'),stemmer.stem('amuses'),stemmer.stem('amused')) print(stemmer.stem('happier'),stemmer.stem('happiest')) print(stemmer.stem('fancier'),stemmer.stem('fanciest'))
import nltk
nltk.download('wordnet')
from nltk.stem import WordNetLemmatizer
lemma = WordNetLemmatizer()
#불편한점 : 타겟에 대한 품사를 찾아줘야하는점과, 속도가 stemmer에 비해 느 단점
print(lemma.lemmatize('amusing','v'),lemma.lemmatize('amuses','v'),lemma.lemmatize('amused','v')) print(lemma.lemmatize('happier','a'),lemma.lemmatize('happiest','a')) print(lemma.lemmatize('fancier','a'),lemma.lemmatize('fanciest','a'))
텍스트 피처 백터화의 대표적인 유형으로 크게 BOW(Bag of Words)와 Word Embedding(Word2vec) 이 있다.
햄버거 가게에서 양념감자튀김을 주문하면 종이봉투안에 든 감자튀김을 받는다. 우리는 양념소스를 종이봉투안에 넣고 흔든다음 먹는것을 떠올려보자.
이처럼 Bag of Words는 문서 내의 모든 단어를 한꺼번에 봉투(Bag)안에 담고 흔들어 섞는다는 의미로, BOW 모델은 모든 단어들의 문맥,순서를 무시하고 단어의 빈도값을 부여하여 Feature 값을 추출한다.
BbBdfdfdfasdfasdfsddfssdsdsdfsdfdsfbbda
BOW의 구조를 간단히 명해보겠다. 아래그림과 같이 문장 2개가 있다. 문장 1(Document1)과 문장2(Document2)의 모든 단어에서 불필요한 관사 제거(Stopword) 및 중복된 단어는 1개만 살리고 각 단어(feature or term)를 컬럼 형태로 나열한다. 그리고 각 단어에 고유의 인덱스를 부여한다.
ex) aid : 0, all : 2, back : 3, brown : 4, come : 5, ...
다음 문장에서 해당 단어가 나타나는 횟수(Occurrence)를 각 단어에 기재한다. 예를들면 men은 문장1에서 0번, 문장2에서 1번 나탄다.
BOW는 word embedding 보다 쉽고 빠르게 구축할 수 있고 여러 분야에서 활용도가 높다.
하지만 문맥의미는 반영할 수 없고, 문서의 등장한 단어를 모두 feature화 하다보니 다른 문서에서 나타나지 않은 단어는 모두 0으로 채우다보니 희소행렬(Sparse matrix)의 문제가 있다.
N개의 문장 혹은 문서들을 분석 후 , N개의 차원을 만든다 . 그 개별 단어들을 차원을 개별 벡터 포인트로 가져갈 수 있다. 즉 차원의 방향성, 크기들을 문맥적으로 가져갈 수 있게 만들었다.
예를들어 위의 오른쪽 그림을 보면 king과 man은 x좌표가 같고 y좌표만 다르다. woman과 Queen도 역시 마찬가지로 x좌표는 같고 y좌표만 다르다. 그래서 man과 king의 관계는 woman과 Queen의 관계와 유사함을 유추 할 수 있다.
단순 백터 카운트 기반의 백터화와 TF-IDF 백터화로 나뉜다.
단순카운트 기반 백터화 : 단어들을 feature로 부여할 때 ,각 문서의 해당단어가 나타나는 횟수(Count 값)을 말한다. 카운트 값이 높을수록 중요 단어로 인식한다.
TF-IDF 백터화: 보통 문서가 많을 경우 TF-IDF를 많이 쓰이는데, 문서의 특성 상 자주 사용되는 단어까지 높은 값을 부여하는 문제를 보완하고자 만들었다. 예를 들면 증권 문서에서 주가,시가,종가는 업무상 많이 쓰일 수 밖에 없고 실제 이러한 단어들이 의미가 있지는 않다. 따라서 이러한 의미 없거나 중요하지 않은 단어들에게 패널티를 부여하는 방식이다.
TF(Term Frequency)는 문서에서 해당 단어가 얼마나 나왔는지 나타내는 지표로 이 값이 높을수록 해당문서의 중요한 단어임을 생각할 수 있다.
DF(Document Frequency)는 해당 단어가 몇개의 문서에서 나타났는지 나타내는 지표이고
IDF(Inverse Document Frequency)는 DF의 역수로서 전체문서에서 DF를 나눈 값이다. 이는 어떠한 단어가 특정문서에서만 나오지 않고 다른 문서에도 빈번히 나타나면 단어의 중요도를 낮게 만려고 만들었다.
피처의 갯수 제한(max_df, min_df), 토큰(token_nize), 스톱워즈(stop_words) 등 있다. 기본적으로 흔히 사용하는 옵션들만 정리해 보았다. 추후에 필요한 옵션은 찾아보길 바란다.
max_df : 전체문서에서 높은 빈도수를 가지고 있는 단어를 제외시키는 파라미터로 스톱워즈와 비슷한 문법적 특성을 가지고 있다.
ex) max_df =0.95 # 0~95%까지만 단어들을 추출하고 나머지는 제외
ex) max_df =100 # 100개 이하의 단어들만 추출
min_df : max_df와 반대로 전체문서에서 낮은 빈도수를 가지고 있는 단어를 제외시키는 파라미터로 사용방법은 max_df와 동일하다.
ex) min_df = 2 # 2번 이하로 나온 단어는 제외
ex) min_df = 0.02 # 0.02 이하 빈도의 단어들은 제외
max_features : 가장 높은 빈도의 단어순으로 피처의 갯수(정수값)를 제한시키는 파라미터
ex) max_features = 2000 # 2000개 까지만 피처를 제한
stop_wards: ex) 'englisth'로 지정하면 영어 지정된 불필요한 단어들은 제외시키고 추출
ex) a, an, the
ngram_range : BOW의 ngram 범위를 튜플형태로 설정. default=(1,1)
ex) ngram_range=(1,3) # 토큰화 단어를 한개씩 묶기 시작하여 최대 3개까지 묶
token_pattern : 토큰화를 수행하는데 필요한 정규 표현식 패턴을 지정. default=공백
lower_case: 단어들을 모두 소문자로 변경. default= True
lower_case=True # 단어 모두 소문자 변경
text_sample_01 = 'The Matrix is everywhere its all around us, here even in this room. You can see it out your window or on your television. You feel it when you go to work, or go to church or pay your taxes.'
text_sample_02 = 'You take the blue pill and the story ends. You wake in your bed and you believe whatever you want to believe You take the red pill and you stay in Wonderland and I show you how deep the rabbit-hole goes.'
text=[]
두개의 문서를 리스트로 하나로 합치기(,(콤)로 분리됨을 확인할 수 있다).
text.append(text_sample_01); text.append(text_sample_02)
print(text,"\n", len(text))
text(2개의 문서를) sckit-learn의 countvectorizer를 통해 계산을 하겠다.
(vector화를 위해 M*N 개의 matrix를 계산)
from sklearn.feature_extraction.text import CountVectorizer
cnt_vect = CountVectorizer() cnt_vect.fit(text)
#transform을 통해 피처백쳐화를 반환시킨다.
ftr_vect = cnt_vect.transform(text)
csr.matrix는 희소행렬에 대한 메모리 문제를 줄어주기 위해 바꿔진 것이라 생각하면 된다.(coo형식도 있으니 참고 바란다.)
print(type(ftr_vect), ftr_vect.shape) print(ftr_vect)
ex) 출력결과를 보면 40번째 단어는 1번째 문서와 2번째 문서에 같이 나온 것을 확인 할 수 있다.
print(cnt
vect.vocabulary
)
stopwards 를 통해 불필요한 단어 제거
cnt
vect = CountVectorizer(stop_words='english')
cnt_vect.fit(text)
ftr_vect = cnt_vect.transform(text)
print(type(ftr_vect), ftr_vect.shape) print(cnt_vect.vocabulary
)
N-GRAM 활용 (1~3 gram)
cnt
vect = CountVectorizer(ngram_range=(1,3))
cnt_vect.fit(text)
ftr_vect = cnt_vect.transform(text)
print(type(ftr_vect), ftr_vect.shape)
print(cnt_vect.vocabulary
)
이렇게 scikit-learn의 countvectorizer를 활용하는 방법을 마치겠다.
이를통해 ML학습은 각자 필요한 데이터에 적용해보길 바랍니다.
*NLP 성능을 높이는 방법
전체 데이터의 단어 분포들이 어떻게 분포 되어 있나.. (시각화, 기본 통계 봄)
1번에서 검토 결과 내가 하고자 하는 가설이 적절할까?
2번이 안된다면 데이터를 보충하거나 가설을 맞게 바꿈
3번이 완료 되면 내가 가진 데이터가 중립, 긍정, 부정으로 잘 분리되게끔 내가 학습데이터를 객관적으로 잘 나누었나 (시각화 해서 직접 단어 분포봄)
감성분석은 기존의 스트마이닝과 다르게 텍스트에 들어있는 의견이나 감성, 평가, 태도 등의 주관적인 정보를 컴퓨터를 통해 분석하는 과정이다. 감성분석은 소셜미디어, 여론조사, 온라인 리뷰, 피드백 등 다양한 분야에서 활용된다.
예를들어 기업 내부적으로 고객 피드백, 콜센터 메시지, VOC 등과 같은 데이터를 분석하여 고객의 만족도를 평가한다. 외부적으로는 기업과 관련된 뉴스나 SNS 홍보물의 댓글을 긍정적인지 부정적인지 판단하는데 감성분석이 활용된다. 개인 단위에서는 영화리뷰를 참고하고 영화를 선택하거나 특정 제품이나 서비스를 구매결정하는 데에 활용이 가능하다.
감성분석은 크게 신러닝 기반(ML-based)의 감성 분석과 어휘 기반(Lexicon-based)의 감성 분석으로 나눌 수 있습니다.
머신러닝이 발달하면서 감정어휘 기반을 이용한 분석보다는 머신러닝 기반에서도 주로 지도학습(Supervised Learning)기반에서 시행된다. 지도학습은 학습 데이터와 타겟 레이블 값(긍/부정)을 기반으로 감성 분석 학습을 수행한 뒤 이를 기반으로 다른 데이 의 감성 분석을 예측을 하는 방법으로 텍스트 기반의 분류와 유사하다.
감성 사전이란 문서의 각 단어가 가지는 긍/부정의 정도를 -1 부터 1 사이의 점수로 레이블링한 것으로 감성이 들어있는 품사인 명사, 형용사, 동사 키워드를 추출한 다음 긍/부정 레이블링을 진행한다. 하지만 도메인에 따라 사용하는 어휘가 달라지고 긍/부정 점수도 달라지기에 모든 도메인에 적용할 수 있는 감성 사전을 구축할 필요가 있다.
감정어휘사전은 감성분석을 사용하기 위한 용어와 문맥에 대한 다양한 정보를 기반으로 문서의 긍/부정의 감성을 판단하는 비지도 학습이다. 문서내 텍스트의 주관적 문맥과 어휘를 기반으로 감성(sentiment) 수치를 계산하는 방법이다.
감정어휘사전 이용 패키지로 SentiWordNet과 VADOR가 있다. SentiWordNet은 NLTK패키지의 WordNet과 유사하게 감성 단어 전용의 WordNet을 구현한 것으로 WordNet의 Synset별로 3가지 감성 점수(긍/부정, 객관성 지)를 할당한다. VADER: 주로 소설 미디어의 텍스트에 대한 감성 분석을 제공하기 위한 패키지로서 뛰어난 감성 분석 결과를 제공하며, 비교적 빠른 수행 시간을 보장해 대용량 텍스트 데이터에 잘 사용되는 패키지.
위 역삼각형 Fig1 그림은 SentiwordNet의 패키지의 그림이다. 설명하자면 위/아래는 하나의 단어가 +로 갈수록 주관적이고, O로 갈수록 객관적인 단어로 가까울 있고, 좌 우는 긍/부정을 나타내는데 좌측으로 갈수록 긍정적인 단어이고 우측으로 가까울수록 부정적임을 알 수 있다.
위의 두번째 Fig2는 Fig1을 기반으로 단어들을 평가한 그림이다. estimable이란 단어는 사전적으로 2가지 의미를 가진다. 존경할만한 단어로 쓰일 경우 Fig2의 첫번째 삼각형과 같이 주관적이면서, 긍정적인 단어로 평가(p=0.75, O=0.25) 할 수 있고 측정할만한 이란 단어로 쓰일 경우 긍/부정도 아닌 오직 객관적의미로 뜻임(p=0, O=1)을 알 수 있다.
이처럼 SentiWordNet을 이용한 감성분석 process는 다음과 같다.
문서를 문장단위로 분해한다.
다시 문장을 단어 단위로 토큰화 및 품사태깅한다.
품사태깅된 단어로 synset 객체와 senti_synset 객체를 생성한다.
senti_synset에서 긍정/부정 감성지수를 구하고, 이를 모두 합산하여 특정 임계치 이상이면 긍정, 아니면 부정으로 결한다.
많은 양의 문서가 주어졌을 때 우리는 일일이 각각의 문서를 읽고 핵심 주제를 찾기에는 많은 시간과 노력이 소모된다. 이 경우 머신러닝 기반의 분석방법을 이용하면 숨어있는 주요 주제를 찾기 쉽다.
토픽모델링은 문서 집합에 숨어 있는 주제를 찾아내는 기법으로 공통된 유사성을 도출하는 면에서 문서 군집화/사도와 유사하나, 토픽모델링은 문서들이 가지는 주요 토픽 분포도와 개별 토픽이 어떤 의미인지를(단어들의 분) 제공하고 있다.
자주 사용되는 토픽모델링 밥법으로는 LSA(Latetnt Semantic Analysis)와 LDA(Latent Dirichlet Allocation)가 있다. 이 중에서 토픽 모델인 LDA에 대해 중점적으로 다뤄보겠다.
LDA는 행렬 분기반 모델링인 LSA가 문헌 내 주제가 어떻게 분포되는지는 고려하지 못하는 한계점을 보완하기 위해 베이즈 통계학 에서 다항분포에 대한 사전 켤레확률을 쓰는 디리클레 분포(Dirichlet Distribution)를 사용하여 문헌 내 토픽분포 및 토픽 별 단어 분포를 추론 하는 생성 확률 분포모델(Generative Probabilistic Model)이다. 즉 대용량 문서에서 출현된 단어들의 가능도로 주제를 추론하기 위한 기법이다.
LDA는 비지도 학습으로 주제를 임의로 정하지 않고 각 문서의 출현된 단어를 가지고 확률분포 기반으로 주제가 학습된다. 구체적으로 LDA 형성과정에 대해 설명하면 각 문헌에 대하여 어떤 주제를 포함시키고, 해당 주제에 어떤 단어를 선택하는지 파라미터로 모델링한다. 이 과 정을 도식화해 표현하면 아래 그림과 같다.
M은 말뭉치(corpus) 공간, N은 문서공간을 뜻한다. 𝑧, 𝑤는 단어 레벨 의 변수로 𝑧는 토픽, 𝑤는 특정 토픽에 반영된 단어를 의미한다. θ는 문서 레벨의 변수로 각 문서 당 주제어 분포를 의미하고, α, β 는 말뭉치 수준 매개변수(parameter)로 모두 디리클레 분포(Dirichlet distribution)를 따른다, α는 분석 문헌의 존재하는 θ 수치를 생성하는 외부의 수치, β 는 특 정단어가 특정 토픽으로부터 생성될 때 개입되는 확률 값이다.
α값에 따라 특정 토픽 분포 형태(𝑧)가 결정되고, 𝑧값과 β값에 의해 특정 주제의 관찰된 단어(𝑤)가 결정된다. 정리하자면 LDA는 문서집합(Corpus)에 대해 파라미 터(θ)가 있고, 단어를 채울 때마다 관측된 단어는 특정한 잠재 토픽을 반영 할 확률을 갖음으로써 ‘생성적 확률모형’이라고 해석할 수 있다. 이 방법을 통해 텍스트의 주제와 그 주제의 단어들을 추정이 가능하다[2]. 이러한 LDA의단점이 있다 토픽에 대해 사람이 주관적인 해석이 필요고, 초기화 파라미터 및 Documnet -Term 행렬의 단어 필터최적화가 어렵다.
LDA 토픽 모델링의 특징을 활용하면 SNS, 뉴스, 논문, 특허 등 특정 분야의 동향 분석과 잠재주제를 파악하는데 사용이 가능하다.
국회도서관에서 우울 및 스트레스와 관련된 특정 주제를 파악한 사례를 들어보겠다.
국회도서관에 소장된 ‘우울’, ‘스트레스’ 키워드로 검색한 1,875건의 학위논문을 워드 클라우드 분석 진행한 후, 가장 많이 다루어진 청소년 키 워드를 가진 182개 논문을 대상으로 LDA 토픽 모델링을 수행하였다. LDA 토픽분석 결과 ‘자아지지’, ’치료프로그램’, ’관계효과’, ‘변인연구’ 4개의 주요 토픽을 파악하였고, 이를 통해 청소년 대상의 치료프로그램은 주로 미술치료와 인지, 심리치료를 다룬 것을 알 수 있었다[3]
[1] 권철민 "파이썬 머신러닝 완벽 가이드"
[2] 이상윤. "토픽모델링 적용 학술 검색 엔진 검색 품질 향상." 국내석사학위논문 서강대학교 정보통신대학원, 2020. 서울
[3] N. H. Jo and E. Na, “Analysis of Domestic Research on Depression and Stress: Focused on the Treatment and Subjects,” J. Converg. Inf. Technol., vol. 7, no. 6, pp. 53–59, 2017.