NLP(자연어 처리) : 머신이 인간의 언어를 이해하고 해석
TA(텍스트 분석) : 비정형 데이터에서 의미있는 정보를 추출
→ NLP 는 텍스트 분석을 향상하게 하는 기반 기술
[머신러닝에 기반한 텍스트 분석 기술 종류]
- 텍스트 분류
- 감성 분석
- 텍스트 요약
- 텍스트 군집화
1. 텍스트 분석 이해
- 텍스트 분석은 비정형 데이터인 텍스트를 분석
- 머신러닝 알고리즘은 숫자형의 피처기반 데이터만 입력받을 수 있기 때문에 ‘비정형 텍스트 데이터를 어떻게 피처 형태로 추출하고 추출된 피처에 의미있는 값을 부여하는가’ 하는 것이 매우 중요한 요소
- 피처 벡터화, 피처 추출 : 텍스트를 벡터값을 가지는 피처로 변환하는 것
1.2 텍스트 분석 프로세스
- 텍스트 전처리 : 텍스트를 피처로 만들기 전 클렌징 작업, 토큰화 작업, 어근 추출 등의 텍스트 정규화 작업을 통칭
- 피처 벡터화/추출 : 전처리된 텍스트에서 피처를 추출하고 벡터값 할당. 대표적인 방법 BOW, Word2Vec
- ML 모델 수립 및 학습/예측/평가 : 피처 벡터화된 데이터 세트에 ML 모델 적용해 학습/예측/평가 수행
1.3 파이썬 기반의 NLP, 텍스트 분석 패키지
- NLTK
- Genism
- SPaCy
2. 텍스트 전처리 - 텍스트 정규화
<텍스트 정규화 작업>
- 클렌징(Cleansing)
- 토큰화(Tokenization)
- 필터링/스톱 워드 제거/철자 수정
- Stemming
- Lemmatization
2.1 클렌징
- 불필요한 문자, 기호 등을 사전에 제거 (ex. HTML, XML 태그나 특정기호)
2.2 텍스트 토큰화
2.2.1 문장 토큰화
- 마침표(.), 개행문자(\n) 등의 문장의 마지막을 뜻하는 기호에 따라 분리
- NLTK 에서는 sent_tokenize()를 일반적으로 많이 사용
- sent_tokenize() 가 반환하는 것은 각각의 문장으로 구성된 list 객체
# 문장 토큰화
from nltk import sent_tokenize
import nltk
#nltk.download('punkt')
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)
[output]
<class 'list'> 3
['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.']
2.2.2 단어 토큰화
- 문장을 단어로 토큰화
- NLTK 에서 word_tokenize()를 이용
- sent_tokenize와 word_tokenize의 조합으로 단어 단위로 토큰화 가능
- 문장의 개수만큼의 개별 리스트를 내포하는 리스트를 반환함 (list in list)
- 내포된 개별 리스트 객체는 각각 문장별로 토큰화된 단어를 요소로 가짐
#단어 토큰화
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)
[output]
<class 'list'> 15
['The', 'Matrix', 'is', 'everywhere', 'its', 'all', 'around', 'us', ',', 'here', 'even', 'in', 'this', 'room', '.']
# 각 문장을 단어로 토큰화
from nltk import word_tokenize, sent_tokenize
#여러 개의 문장으로 된 입력데이터를 문장별로 단어 토큰화하게 만드는 함수 생성
def tokenize_text(text):
#문장별로 분리 토큰
sentences = sent_tokenize(text)
#분리된 문장별 단어 토큰화
word_tokens = [word_tokenize(sentence) for sentence in sentences]
return word_tokens
# 여러 문장에 대해 문장별 단어 토큰화 수행
word_tokens = tokenize_text(text_sample)
print(type(word_tokens), len(word_tokens))
print(word_tokens)
[output]
<class 'list'> 3
[['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', '.']]
2.3 스톱워드 제거
- 스톱워드(stop word) : 분석에 큰 의미가 없는 단어 (ex. is , the, a, will..)
- NLTK 에는 다양한 스톱 워드가 목록화 되어있음
- 영어의 경우 스톱워드 개수가 179개
# 스톱워드 영어 단어 개수, 목록 확인
import nltk
#nltk.download('stopwords')
print('stop words 개수:', len(nltk.corpus.stopwords.words('english')))
print(nltk.corpus.stopwords.words('english')[:20])
[output]
stop words 개수: 179
['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his']
- word_tokens 리스트에 대해서 stopwords를 필터링해 분석을 위한 의미 있는 단어만 추출
# stopwords 필터링 하여 의미있는 단어만 추출하는 함수
stopwords = nltk.corpus.stopwords.words('english')
all_tokens = []
#위 예제에서 3개의 문장별로 얻은 word_tokens list에 대해 스톱워드를 제거하는 반복문
for sentence in word_tokens:
filtered_words=[]
#개별 문장별로 토큰화된 문장 list 에 대해 스톱워드를 제거하는 반복문
for word in sentence:
#소문자로 모두 변환
word = word.lower()
#토큰화된 개별 단어가 스톱 워드에 포함되지 않는다면 word_tokens에 추가
if word not in stopwords:
filtered_words.append(word)
all_tokens.append(filtered_words)
print(all_tokens)
[output]
[['matrix', 'everywhere', 'around', 'us', ',', 'even', 'room', '.'], ['see', 'window', 'television', '.'], ['feel', 'go', 'work', ',', 'go', 'church', 'pay', 'taxes', '.']]
2.4 Stemming 과 Lemmatization
- 문법적, 의미적으로 변화하는 단어의 원형을 찾는 작업
- Lemmatization이 Stemming 보다 정교하여 의미론적인 기반에서 단어의 원형을 찾음
- 따라서 Lemmatization이 변환에 더 오랜시간을 필요로 함
- NLTK에서의 대표적인 Stemmer 클래스 : Porter, Lancaster, Snowball stemmer
- NLTK에서의 대표적인 Lemmiatization 클래스 : WordNetLemmatizer
<LancasterStemmer 를 이용한 Stemming>
- Stemmer 객체를 생성한뒤, stem(’단어’) 메서드를 통해 원하는 단어 stemming
- amuse와 비교형, 최상급형으로 변형된 단어의 정확한 원형을 찾지 못하고 철자가 다른 어근단어로 인식하는 경우 발생
# stemming
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'))
[output]
work work work
amus amus amus
happy happiest
fant fanciest
<WordNetLemmatizer를 이용한 Lemmatization>
- Lemmatization 객체를 생성한 뒤 lemmatize() 메서드의 인자로 품사를 입력해주어야함
- Stemmer 보다 정확하게 원형 단어 추출
from nltk.stem import WordNetLemmatizer
#nltk.download('wordnet')
lemma = WordNetLemmatizer()
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'))
[output]
amuse amuse amuse
happy happy
fancy fancy
3. 피처 벡터화/추출 - BOW(Bag of Words)
- Bag of words : 문서가 가지는 모든 단어의 문맥이나 순서를 무시하고 단어에 대해 빈도 값을 부여해 피처 값을 추출하는 모델
- 장점 : 쉽고 빠른 구축
- 단점:
- 문맥 의미 반영 부족 : 단어의 문맥, 순서를 반영하지 않기 때문. n_gram 기법을 활용할 수 있지만 제한적임
- 희소 행렬 문제 : 매우 많은 문서에 단어의 개수는 수만~수십만개 인 것에 비해 하나의 문서에 있는 단어는 이 중 극히 일부분임. 따라서 대규모의 칼럼으로 구성된 행렬에서 대부분의 값이 0으로 채워져 ML 알고리즘의 수행시간, 예측성능을 떨어트림
3.1 BOW 피처 벡터화
- 모든 문서에서 모든 단어를 칼럼형태로 나열하고 각 문서에서 해당 단어의 횟수, 정규화된 빈도 값으로 데이터 세트 모델로 변경하는 것
- ex) 문서 개수 M , 단어 개수 N → M x N 의 데이터 세트 행렬 구성
- 카운트 기반 벡터화 : 해당 단어가 나타나는 횟수 기반
- TF-IDF : 자주 나타나는 단어에 높은 가중치를 주되, 모든 문서에서 전반적으로 자주 나타나는 단어에 대해서는 패널티 부여
3.1.2 사이킷런의 Count 및 TF-IDF 벡터화 구현 : CountVectorizer, TfidfVectorizer
<Countvectorizer>
countvectorizer 은 카운트 기반의 피처 벡터화 방법, 텍스트 전처리 수행 후 fit(), transform()을 통해 피처 벡터화된 객체 반환
- 사전 데이터 가공 : 모든 문자를 소문자로 변환하는 등의 사전 작업 수행
- 토큰화 : Default 는 단어 기준, n_gram_range 를 사용하여 토큰화 수행
- 텍스트 정규화 : Stop words 필터링만 수행, Stemmer, lemmatize를 수행하기 위해서는 tokenizer 파라미터에 해당 함수 적용
- 피처 벡터화 : max_df, min_df, max_features 등의 파라미터를 반영하여 token 된 단어들을 피처 추출한 후 벡터화 진행
3.3 BOW 벡터화를 위한 희소 행렬
- BOW 피처 벡터화 모델은 대부분 희소 행렬
- 불필요한 0값이 많이 할당되어 많은 메모리 공간 필요, 연산 수행 시간 오래걸림
- 희소행렬을 물리적으로 적은 메모리 공간을 차지할 수 있도록 변환해주는 방법이 COO, CSR
3.3.1 희소행렬 - COO 형식
- 0이 아닌 데이터만 별도의 배열에 저장하고, 그 데이터가 가르키는 행, 열 위치를 별도의 배열로 저장하는 방식
- 희소행렬 변환은 scipy의 coo_matrix 클래스를 이용
# 희소행렬 - COO형식
import numpy as np
dense = np.array([[3,0,1], [0,2,0]])
from scipy import sparse
#0이 아닌 데이터 추출
data = np.array([3,1,2])
#행 위치와 열 위치를 각각 배열로 생성
row_pos = np.array([0,0,1])
col_pos = np.array([0,2,1])
#sparse 패키지의 coo_matrix 를 이용해 COO 형식으로 희소행렬 생성
sparse_coo = sparse.coo_matrix((data, (row_pos, col_pos)))
sparse_coo.toarray()
[output]
array([[3, 0, 1],
[0, 2, 0]])
3.3.2. 희소행렬-CSR 형식
- COO 형식이 반복적인 행,열 위치 데이터를 사용해야하는 문제점 해결
- 행 위치 배열 대신 행 위치 배열의 고유값 시작 인덱스 배열 + 총 항목 개수 배열로 변환 → COO 보다 메모리가 적게들고 빠른 연산 가능
- 희소행렬 변환은 scipy 의 csr_matrix 클래스를 이용
- 사이킷런의 CountVectorizer, TfidfVectorizer 클래스로 변환된 피처 벡터화 행렬은 모두 CSR 형태의 희소 행렬
#희소행렬 - CSR 형식
dense2 = np.array([[0,0,1,0,0,5],
[1,4,0,3,2,5],
[0,6,0,3,0,0],
[2,0,0,0,0,0],
[0,0,0,7,0,8],
[1,0,0,0,0,0]])
#0이 아닌 데이터 추출
data2 = np.array([1,5,1,4,3,2,5,6,3,2,7,8,1])
#행 위치와 열 위치를 각각 array로 생성
row_pos = np.array([0,0,1,1,1,1,1,2,2,3,4,4,5])
col_pos = np.array([2,5,0,1,3,4,5,1,3,0,3,5,0])
#COO 형식으로 변환
sparse_coo = sparse.coo_matrix((data2, (row_pos, col_pos)))
#행 위치 배열의 고유한 값의 시작 위치 인덱스를 배열로 생성
row_pos_ind = np.array([0,2,7,9,10,12,13])
#CSR 형식으로 변환
sparse_csr = sparse.csr_matrix((data2, col_pos, row_pos_ind))
print('COO 변환된 데이터가 제대로 되었는지 다시 Dense 로 출력 확인')
print(sparse_coo.toarray())
print('CSR 변환된 데이터가 제대로 되었는지 다시 Dense 로 출력 확인')
print(sparse_csr.toarray())
[output]
COO 변환된 데이터가 제대로 되었는지 다시 Dense 로 출력 확인
[[0 0 1 0 0 5]
[1 4 0 3 2 5]
[0 6 0 3 0 0]
[2 0 0 0 0 0]
[0 0 0 7 0 8]
[1 0 0 0 0 0]]
CSR 변환된 데이터가 제대로 되었는지 다시 Dense 로 출력 확인
[[0 0 1 0 0 5]
[1 4 0 3 2 5]
[0 6 0 3 0 0]
[2 0 0 0 0 0]
[0 0 0 7 0 8]
[1 0 0 0 0 0]]
'데이터 > 머신러닝' 카테고리의 다른 글
[NLP] Chapter 8 | 텍스트 분석 (지도학습 기반 감성 분석) (0) | 2023.02.04 |
---|---|
[NLP] Chapter 8 | 텍스트 분석 (텍스트 분류) (1) | 2023.02.03 |
[RecSys] Chapter 9 | 추천시스템 (0) | 2023.02.01 |
[Clustering] Chapter 7 | 군집화 실습 - 고객 세그멘테이션 (1) | 2023.01.28 |
[Clustering] Chapter 7 | 군집화 (02. 군집 평가) (0) | 2023.01.23 |