: 비정형 데이터인 텍스트를 분석하는 것. 머신러닝 알고리즘은 숫자형의 피처 기반 데이터만 입력받을 수 있기 때문에 비정형 텍스트 데이터를 피처 형태로 추출하고, 추출된 피처에 의미있는 값을 부여하는 게 중요하다.
NLTK(Natural Language Toolkit for Python): 가장 대표적인 NLP 패키지. 수행 성능, 속도, 신기술, 엔터프라이즈한 기능 지원 등에서 아쉬워 실제 업무에서는 잘 안쓰임
Gensim: 토픽 모델링 분야에서 가장 두각을 나타내는 패키지
SpaCy: 뛰어난 수행 성능으로 최근 가장 주목을 받는 NLP 패키지
⇒ 사이킷런과 더불어 이러한 NLP 전용 패키지와 결합해 애플리케이션을 작성하는 경우가 많다.
nltk.download('punkt')
---
from nltk import sent_tokenize
sent_tokenize(text=텍스트 파일) # 각각의 문장으로 구성된 list 객체 반환
문장을 단어로 토큰화 하는 것
공백, 콤마(,), 마침표(.), 개행문자 등으로 단어를 분리하지만, 정규표현식으로 다양한 유형으로 토큰화 수행 가능
from nltk import word_tokenize
word_tokenize(문장) # 각각의 단어로 구성된 list 객체 반환
단점 : 문맥적 의미가 무시된다.
n-gram
불용어 : 분석에 큰 의미가 없는 단어 (예: 영어에서 is, the, a, will 등)
제거하는 이유 : 스톱워드는 문법적인 특성으로 인해 빈번하게 텍스트에 나타나므로 제거하지 않으면 빈번함으로 인해 중요 단어로 인식된다.
언어별로 불용어가 목록화 되어 있다.
nltk.download('stopword')
nltk.corpus.stopwords.words('english')
# 이후 for, if문을 이용하여 스톱워드를 제거한다.
어간 추출은 품사 정보를 갖고 있지 않음 : 단어의 뜻이 분명한 경우
from nltk.stem import LancasterStemmer
stemmer = LancasterStemmer()
stemmer.stem('working') # -> work 로 변환
정확한 원형 단어 추출을 위해 단어의 품사를 입력해줘야 한다. 동사=’v’, 형용사=’a’
명사 / 동사로 쓰일 때 반어의 뜻이 완전히 달라지는 경우 ex) bear, taxi,
시간은 좀 더 걸린다.
from nltk.stem import WordNetLemmatizer
lemma = WordNetLemmatizer()
lemma.lemmatize('amusing', 'v') # -> amuse로 변환
피처 벡터화(Encoding) : ML알고리즘에 입력할 수 있도록, 텍스트를 특정 의미를 갖는 숫자형인 벡터값으로 변환하는 것
각 문서의 텍스트를 단어로 추출해 피처로 할당
각 단어의 발생빈도와 같은 값을 피처 값으로 부여
⇒ 각 문서를 단어 피처의 발생 빈도값으로 구성된 벡터로 만드는 기법
: 기존 텍스트 데이터를 또 다른 형태의 피처의 조합으로 변경하는 것이기 때문에 피처 추출에 포함하기도 한다. (TA에서는 피처 벡터화와 피처 추출을 같은 의미로 사용하곤 한다.)
BOW : 문서가 갖는 모든 단어(words)를 문맥이나 순서를 무시하고 일괄적으로 단어의 빈도 값을 부여해 피처값을 추출하는 모델
CountVectorizer : 단어에 피처 값을 부여 할 때, 각 문서에 해당 단어가 등장하는 횟수 Count로 Vector화
TF-IDFVectorizer : 개별 문서에서 자주 나타나는 단어에 높은 가중치를 주되, 모든 문서에 전반적으로 자주 나타나는 단어에 대해서는 패널티를 주는 방식으로 값을 부여
문서마다 텍스트 길이가 길고, 문서의 갯수가 많은 경우에는, Count보다 TF-IDF 방식을 사용하느 것이 더 좋은 성능을 낼 가능성이 높음
: 단어 피처에 값을 부여할 때 각 문서에서 해당 단어가 나타나는 횟수를 부여하는 경우
from sklearn.feature_extraction.text import CountVectorizer
파라미터 | 설명 |
---|---|
max_df | 전체 문서에 걸쳐서 너무 높은 빈도수를 가지는 단어 피처를 제외하기 위한 파라미터, int 입력: 주어진 값 이하로 나타나는 단어만 피처로 추출, float 입력: 빈도가 0~주어진 값% 까지만 피처로 추출 |
min_df | 전체 문서에 걸쳐서 너무 낮은 빈도수를 가진 단어 피처를 제외하기 위한 파라미터, int 입력: 주어진 값 이하로 나타나는 단어는 피처로 추출하지 않음, float 입력: 하위 주어진 값% 이하의 빈도를 가지는 단어는 피처로 추출하지 않음 |
max_features | int 입력 : 추출하는 피처의 개수를 제한, 가장 높은 빈도수를 가지는 단어순으로 정렬해 주어진 값 개수까지만 피처로 추출 |
stop_words | ‘english’로 지정하면 영어의 스톱워드로 지정된 단어는 추출에서 제외 |
n_gram_range | 단어 순서를 어느 정도 보강하기 위한 n_gram 범위 설정, 튜플 형태 (범위 최소값, 범위 최대값) |
analyzer | default = ‘word’, 피처 추출을 수행할 단위 지정, character의 특정 범위를 피처로 만드는 특정 경우에 사용 |
token_pattern | default = ‘\b\w\w+\b’ 공백 또는 개행 문자 등으로 구분된 단어 분리자(\b) 사이의 두 문자(영숫자) 이상의 단어를 토큰으로 분리, 정규 표현식 패턴 지정, analyzer=’word’일때만 변경 가능 (거의 변경X) |
tokenizer | 토큰화를 별도의 커스텀 함수로 이용시 적용, 일반적으로 CountTokenizer 클래스에서 어근 변화시 이를 수행하는 별도의 함수를 tokenizer 파라미터 적용하면 된다. |
반드시 학습 데이터를 이용해 fit()이 수행된 객체를 이용해 테스트 데이터를 변환(transform)해야 한다.
그래야만 학습시 설정된 CountVectorizer의 피처 개수와 테스트 데이터를 CountVectorizer로 변환한 피처 개수가 같아진다.
테스트 데이터의 피처 벡터와 시 fit_transform() 사용 X
cnt_vect = CountVectorizer()
cnt_vect.fit(X_train)
X_train_cnt_vect = cnt_vect.transform(X_train)
X_text_cnt_vect = cnt_vect.transform(X_test)
stop words 필터링 수행
Stemmer, Lemmatizer는 지원 X
이를 위한 함수를 만들어 tokenizer 파라미터에 적용하거나 외부 패키지로 미리 텍스트 정규화 수행 필요
피처 벡터화: 토큰화 된 단어 피처로 추출, 단어 빈도수 벡터 값을 적용
: 개별 문서에서 자주 나타나는 단어에 높은 가중치를 주되, 모든 문서에서 전반적으로 자주 나타나는 단어에 대해서는 패털티를 주는 방식으로 값을 부여한다.
파라미터와 변환 방법은 CountVectorizer와 동일
from sklearn.feature_extraction.text import TfidfVectorizer
: 희소행렬(Sparse Matrix)은 행렬의 값이 대부분 0인 경우를 가리키는 표현 ↔ 밀집행렬(Dense Matrix)
: 모든 문서로 피처 벡터화를 수행하면 + n-gram (1,2) , (1,3) 등 주면 칼럼 수가 더욱 증가
: 희소행렬은 일반적으로 ML알고리즘의 수행시간과 예측 성능을 떨어뜨림
→ 메모리 공간이 많이 필요하고, 연산 시간이 오래 걸린다. 따라서, 물리적으로 적은 메모리 공간을 차지할 수 있도록 변환해야 한다.
희소행렬을 COO, CSR 형태의 희소행렬로 압축해줘야 함 (CSR을 더 많이 사용함)
: CountVectorizer, TfidfVectorizer 은 희소행렬을 반환(CSR 형태)
: 0이 아닌 데이터만 별도의 데이터 배열에 저장하고, 그 데이터가 가르키는 행과 열의 위치를 별도의 배열에 저장하는 방식
예) [ [3, 0, 1], [0, 2, 0] ] → (row, col): (0, 0), (0, 2), (1, 1) → row: [0, 0, 1], col: [0, 2, 1]
import numpy as np
# BOW에서 좌표 기반으로 밀집행렬 추출
dense = np.array( [ [ 3, 0, 1 ], [0, 2, 0 ] ] )
from scipy import sparse
# 0 이 아닌 데이터 추출
data = np.array([3,1,2])
# 행 위치와 열 위치를 각각 array로 생성
row_pos = np.array([0,0,1])
col_pos = np.array([0,2,1])
# sparse 패키지의 coo_matrix를 이용하여 COO 형식으로 희소 행렬 생성
# 매개변수로 채워넣을 숫자인 순차적 data와, (row_pos, col_pos)의 좌표 정보를 tuple 형태로 주는 듯
sparse_coo = sparse.coo_matrix((data, (row_pos,col_pos)))
sparse_coo.toarray()
> array([3, 0, 1], [0, 2, 0])
: 행 위치 배열 내에 있는 고유한 값의 시작 위치만 다시 별도의 위치 배열로 가지는 변환 방식
from scipy import sparse
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로 생성
col_pos = np.array([2, 5, 0, 1, 3, 4, 5, 1, 3, 0, 3, 5, 0])
row_pos = np.array([0, 0, 1, 1, 1, 1, 1, 2, 2, 3, 4, 4, 5])
# 행 위치 배열의 고유한 값들의 시작 위치 index를 배열로 생성
row_pos_ind = np.array([0, 2, 7, 9, 10, 12, 13])
# sparse 패키지의 csr_matrix를 이용하여 CSR 형식으로 희소 행렬 생성
# 매개변수로 채워넣을 숫자인 순차적 (data, row_pos_ind인 좌표의 위치 정보, col_pos인 좌표 정보) 를 tuple로
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())
> 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]]