앞서 Part I에서 기본적인 데이터 불러오기와 간단한 text처리 방법에 대해서 익혀볼 수 있었습니다. 이제 본격적으로 문장을 분석하기 위해서 모든 리뷰들에 등장하는 단어들에 대해서 각 리뷰가 가진 단어들의 현황을 matrix형태로 가져가고 싶습니다. 즉,

  단어1 단어2
리뷰1 2 1
리뷰2 0 5

이런식으로 feature matrix를 만들고 싶은 것이죠. 이를 이용해서 sentiment에 대해서 supervised learning을 진행하고자 합니다. Part I에서 잘 정리해 놓은 train_filtered를 가지고 계속해서 진행하겠습니다. 앞서 우리는 직접 모든 리뷰에 대해서 일일이 split을 했었고, 각 단어의 overall 빈도수를 체크했었습니다. 물론 세세하게 하나씩 다시 구하고 할 수는 있지만, 좋은 라이브러리를 대신해서 소개해 드릴려고 합니다. 물론 간단한 라이브러리가 아니기 때문에 충분히 숙지가 필요하지만, 정말 간단하고 빠르게 여러분이 원하시는 기능을 담아내기 때문에 익힐만한 가치가 있다고 생각합니다! :)

그 좋은 라이브러리는 바로 sklearnfeature_extractiontextCountVectorizer라는 놈입니다. 일단 기본적으로 default로 생성한 후에 어떤 역할을 하는지 알아보도록 합시다 :)

>>> from sklearn.feature_extraction.text import CountVectorizer

#디폴트 값으로 CountVectorizer를 만들어줍니다.
>>> cv = CountVectorizer()

#그러고 나서 fit함수를 통해서 각 리뷰들을 대입합니다.
>>> cv.fit(train_filtered['Phrase'])

우리가 가지고 있는 corpus(즉, 각 리뷰들의 집합)로 fitted되었다는 것은 어떤 의미일까요? 바로 해당 corpus에만 등장했던 단어들을 바탕으로, 그리고 지금은 디폴트로 만들었지만, 다양한 옵션들을 통해 더 복잡하면서 강력한 vectorizer가 되서 input corpus에 대해서 우리가 원하는 matrix형태를 만들어주게 됩니다. 그렇다면 이게 어떤 의미인지 코드를 통해 알아보죠.

#두개의 새로운 문장을 리뷰들로 fit했던 vectorizer로 변형시킵니다.
>>> X = cv.transform(['A whole new text zzzzzzzzz', 'Something very new new new phrase uh ?'])

>>> print X
  (0, 8975)	1
  (0, 13428)	1
  (0, 14844)	1
  (0, 15164)	1
  (1, 8975)	3
  (1, 9807)	1
  (1, 12374)	1
  (1, 13980)	1
  (1, 14482)	1

입력됐던 문장들이 sparse matrix형태로 변형된 것을 확인하실 수 있죠? 앞의 tuple은 (i번째 document, j번째 단어)를 의미하고, 그냥 int값은 등장 횟수를 의미합니다. matrix이다보니 숫자로만 index가 되어 무엇을 의미하는지는 잘 모르시겠죠? 일단 sparse matrix의 몇가지 attributes에 대해 알아봅시다.

>>> for i in X:
...    print i.indices
...    print i.data
...    print i.indptr

[ 8975 13428 14844 15164]
[1 1 1 1]
[0 4]
[ 8975  9807 12374 13980 14482]
[3 1 1 1 1]
[0 5]

일단 X를 for문으로 돌리게 되면 각 row별로 묶여서 나오게 됩니다.(여기서는 row는 document를 의미하겠죠?) 우리는 두 개의 document를 넣었기 때문에 두 개의 row가 있었던 것이지요. 그리고 각 row당 column의 index list와 그에 따른 value, 그리고 마지막으로 해당 row에 몇 개의 값들이 있는지를 저장하고 있습니다. 여기서의 column은 등장하는 단어들이기 때문에 매우 중요한 정보입니다. 이를 이용해서 각 document가 어떤 단어를 포함하는지 확인할 수 있습니다.

>>> for i in X:
...     for j in i.indices:
...         print cv.get_feature_names()[j]

new
text
whole
zzzzzzzzz
new
phrase
something
uh
very

어라? 그런데 뭔가 이상한 점이 있지 않나요? 분명히 우리 문장에는 ‘A’와 ‘?’가 있었는데, 왜 표시되지 않는 것일까요? 이것은 train corpus에도 분명히 존재했는데요! 그 이유는 바로, ‘A’나 ‘?’는 모든 리뷰에 존재하기 때문에 predictor로서의 역할을 하지 못하기 때문입니다. 그래서 이런것들까지 알아서 신경써주는 참 편리한 라이브러리네요.(혹시 이런것에 더 관심있으시면 max_df, min_df parameters를 참고하세요.)

자, 그럼 이제 어떻게 동작하는지 대충 파악했으니 우리의 dataset을 변형시켜봅시다.

>>> CountMat = cv.transform(train_filtered['Phrase'])
>>> CountMat
<8529x15165 sparse matrix of type '<type 'numpy.int64'>'
	with 128686 stored elements in Compressed Sparse Row format>

sparse matrix의 차원이 뭔가 친숙한 숫자죠? 바로 우리의 데이터셋의 리뷰 수 X 데이터셋에 등장하는 단어들 수입니다. 참고로 이러한 sparse matrix는 행렬 덧셈과 행렬 곱셈에 대해서 연산이 정의되어있으므로 편리하게 사용하시면 됩니다 :) 그리고 원하시면 다시 dense matrix로 변환이 가능합니다.

>>> CountMat.toarray() #손쉽게 sparse에서 dense matrix로 가능합니다.
array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ..., 
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]])

정말 편리하면서 강력한 기능을 갖추고 있죠!? 여기서 더 나아가서 생각해볼 게 있습니다. 전체 단어의 수가 15165개라는 건 상당히 많은 수이고, 결국 feature space의 차원만 키우게 되는 결과를 가져올 수 있습니다. 따라서 자주 등장하는 n개의 단어만 추출하는 방법이 종종 사용되는데요, 그럼 예를 들어서 top 100개의 단어만 추출하여 matrix를 만드는 법을 살펴보겠습니다. 비결은 max_features라는 parameter에 있습니다 :)

>>> cv100 = CountVectorizer(max_features=100) #max_features는 corpus중 빈도수가 가장 높은 순으로 해당 개수만큼만 뽑아냅니다.
>>> cv100.fit(train_filtered['Phrase'])

>>> print len(cv100.get_feature_names())
100 #우리가 원하는대로 100개의 column만 가지고 있죠?
>>> print cv100.get_feature_names()
[u'about', u'action', u'all', u'an', u'and', u'any', u'are', u'as', u'at', u'bad', u'be', u'been', u'best', u'but', u'by', u'can', u'characters', u'comedy', u'could', u'director', u'do', u'does', u'drama', u'enough', u'even', u'film', u'for', u'from', u'funny', u'good', u'has', u'have', u'he', u'his', u'how', u'if', u'in', u'into', u'is', u'it', u'its', u'just', u'life', u'like', u'little', u'love', u'lrb', u'made', u'make', u'makes', u'may', u'more', u'most', u'movie', u'movies', u'much', u'never', u'new', u'no', u'not', u'of', u'on', u'one', u'only', u'or', u'out', u'rrb', u'so', u'some', u'something', u'story', u'than', u'that', u'the', u'their', u'there', u'they', u'this', u'through', u'time', u'to', u'too', u'up', u'us', u'very', u'was', u'way', u'we', u'well', u'what', u'when', u'which', u'while', u'who', u'will', u'with', u'work', u'would', u'you', u'your']
# 실제 단어들을 보면 굉장히 자주 등장하는 단어들만 뽑혀있음을 알 수 있습니다.

진짜 강력하죠?? 이제 matrix까지 만들었으니 다음번엔 본격적인 machine learning을 할 수 있겠네요 :)