단어 빈도수 기반
CountVectorizer
단어 문서 행렬(term-document matrix: 이하 TDM)는 문서별로 나타난 단어의 빈도를 표(행렬) 형태로 나타낸 것이다.
- 문장들
corpus = [
'you know I want your love',
'I like you',
'what should I do'
]
- split 혹은 dictionary
str = " ".join(corpus)
print(str)
words = str.split(' ')
print(words)
freq = {} #dictionary는 초기화 필수
for w in words :
#freq[w] +=1 #오류남
freq[w] = freq.get(w,0) + 1 #default 값을 주기 위해
print(freq)
freq[w]=freq[w]+1
: 딕셔너리는 없는 dict 넣으면 자동으로 생성되나, 이건 오류난다. 읽고쓰기를 동시에 하는데 읽을 때 값이 없기 때문이다.
#result
you know I want your love I like you what should I do
['you', 'know', 'I', 'want', 'your', 'love', 'I', 'like', 'you', 'what', 'should', 'I', 'do']
{'you': 2, 'know': 1, 'I': 3, 'want': 1, 'your': 1, 'love': 1, 'like': 1, 'what': 1, 'should': 1, 'do': 1}
- vectorize
CountVectorizer()
: 문장을 count로 변환해서 벡터화. one-hot encoding과 비슷. 1은 하나 이상 있다는 뜻
fit_transform(문장)
: fit과 transform 두 개의 메소드를 합친 것이다.
CountVectorizer().fit_transform(문장).toarray()
: 요소들은 feature의 순서이다.
vector = CountVectorizer()
tf = vector.fit_transform(corpus)
print(tf)
#result
(0, 7) 1
(0, 1) 1
(0, 5) 1
(0, 8) 1
(0, 3) 1
(1, 7) 1
(1, 2) 1
(2, 6) 1
(2, 4) 1
(2, 0) 1
print(tf.toarray())
#result
[[0 1 0 1 0 1 0 1 1]
[0 0 1 0 0 0 0 1 0]
[1 0 0 0 1 0 1 0 0]]
print(type(tf)) # <class 'scipy.sparse.csr.csr_matrix'>
print(tf.shape) # (3, 9) = (문장, 단어수)
- CountVectorizer() 객체 출력
print(vector)
#각 단어의 인덱스가 어떻게 부여되었는지 보여준다.
#result
CountVectorizer(analyzer='word', binary=False, decode_error='strict',
dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
lowercase=True, max_df=1.0, max_features=None, min_df=1,
ngram_range=(1, 1), preprocessor=None, stop_words=None,
strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
tokenizer=None, vocabulary=None)
print(vector.vocabulary_['you']) #7
print(vector.vocabulary_.get('you')) #7
- feature name : TDM에 저장된 단어들
words = vector.get_feature_names()
for word in words: print(word)
for key in vector.vocabulary_:
print(key, vector.vocabulary_[key])
res = sorted([vector.vocabulary_.items])
#result
do
know
like
love
should
want
what
you
your
you 7
know 1
want 5
your 8
love 3
like 2
what 6
should 4
do 0
한 글자 수 단어는 추출이 안 된다.
TF-IDF
(Term Frequency - Inverse Document Frequency)
TF : 현재 문서에서 단어 A가 나타난 횟수
DF : 단어가 나타난 문서의 수. a, the,...
특정 단어의 상대적인 빈도를 나타내주는 값
값이 클 수록 내 문서에만 많이 언급되는 단어(=다른 문서에서는 잘 언급 안됨)
값이 작을수록 다른 문서에 잘 언급하는 단어를 의미(=현재 문서와 관련 없음)
내 문서에는 많이 나오고 다른 문서에는 없는 -> 문자를 대표할 수 없다.
sent = ["오늘 휴일",
"휴일 오늘",
"휴일 인 오늘 도 서쪽 을 중심 으로 폭염 이 이어졌는데요, 내일 은 반가운 비 소식 이 있습니다.",
"폭염 을 피해서 휴일 에 놀러왔다가 갑작스런 비 로 인해 망연자실 하고 있습니 다.",
" 내일 은 반가운 비 소식 이 있습니다."]
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(sent) #문장 벡터화 진행
print(tfidf_matrix) #이해할 수 없는 벡터들
#result
(0, 17) 0.6437444595062429
(0, 7) 0.7652405313723362
(1, 17) 0.6437444595062429
...
print(type(tfidf_matrix)) #<class 'scipy.sparse.csr.csr_matrix'>
print(tfidf_matrix.toarray().shape) #(5, 18) 문서 수 x 단어 수
print(tfidf_matrix.toarray())
#result
[[0. 0. 0. 0. 0. 0.
0. 0.76524053 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.64374446]
[0. 0. 0. 0. 0. 0.
0. 0.76524053 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.64374446]
[0. 0.28487999 0. 0. 0.28487999 0.3531014
0.28487999 0.23647612 0.3531014 0.3531014 0. 0.
0.28487999 0.3531014 0.28487999 0. 0. 0.19893117]
[0.3542556 0. 0.3542556 0.3542556 0. 0.
0. 0. 0. 0. 0.3542556 0.3542556
0. 0. 0.28581119 0.3542556 0.3542556 0.19958143]
[0. 0.5 0. 0. 0.5 0.
0.5 0. 0. 0. 0. 0.
0.5 0. 0. 0. 0. 0. ]]
features = tfidf_vectorizer.get_feature_names()
print(features)
#result
['갑작스런', '내일', '놀러왔다가', '망연자실', '반가운', '서쪽', '소식', '오늘', '으로', '이어졌는데요', '인해', '있습니', '있습니다', '중심', '폭염', '피해서', '하고', '휴일']
mat = np.asarray(tfidf_matrix.toarray())
srch = ['오늘','휴일']
srch_dtm = mat[:, [ tfidf_vectorizer.vocabulary_.get(i) for i in srch ] ]
#srch_dtm = mat[:, [7,17]] #slicing. [[]] 는 index 가져오는 것임.
print(srch_dtm)
#result
[[0.76524053 0.64374446]
[0.76524053 0.64374446]
[0.23647612 0.19893117]
[0. 0.19958143]
[0. 0. ]]
4번째 문장: 약간의 관련성이 있다. 5번째 문장: 아예 관련성이 없다.
- 보기 쉽게 ranking
score = srch_dtm.sum(axis=1)
print(score)
for i in range(len(score)):
if score[i] > 0:
print('{} / score : {}'.format(sent[i], score[i]))
#result
[1.40898499 1.40898499 0.43540729 0.19958143 0. ]
오늘 휴일 / score : 1.408984990878579
휴일 오늘 / score : 1.408984990878579
휴일 인 오늘 도 서쪽 을 중심 으로 폭염 이 이어졌는데요, 내일 은 반가운 비 소식 이 있습니다. / score : 0.4354072935753253
폭염 을 피해서 휴일 에 놀러왔다가 갑작스런 비 로 인해 망연자실 하고 있습니 다. / score : 0.1995814265359179
예측
sentences = ['This is the first document.',
'This is the second document.',
'And the third one.',
'Is this the first document?']
vect = TfidfVectorizer()
X = vect.fit_transform(sentences)
y = [1,2,3,4]
model = SGDClassifier(loss='perceptron')
model.fit(X,y)
X_pred = vect.transform(['My new document third'])
y_pred = model.predict(X_pred)
print(y_pred) #[3]
TF-IDF 감정분류
(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=10000) #단어 빈도수 상위 10000개
# 영화 리뷰는 X_train, 감성 정보는 y_train에 저장된다.
#테스트용 리뷰는 X_test, 테스트용 리뷰의 감성 정보는 y_test에 저장된다.
#위에서 num_words는 이 데이터에서 등장 빈도 순위로 몇 번째에 해당하는 단어까지를 사용할 것인지 조절하는 것입니다.
print(X_train[0]) #[1, 14, 22, 16, 43, ...
print(X_train[1]) #[1, 194, 1153, 194, ...
print(len(X_train[0])) #218
print(len(X_train[1])) #189
T=Term. index.
0번째, 1번째 행의 길이가 다르다. numpy는 행렬 형태의 데이터만 취급 가능하여 문장들은 numpy 객체로 리턴할 수 없다.
같은 단어가 여러 번 반복될 수 있으므로, 중복이 허용되어야 한다.
불필요한 단어(불용어)는 빠지고 실제로 의미있는 단어들만 리턴한 것이다.
빈도수 상위 10000개만 저장했으므로 빈도수가 낮은 단어들은 제거된 데이터이다.
-> 디코딩해서 다시 해석할 수 없다. 문법이 안 맞는 부분들이 있을 것이다.
print('리뷰의 최대 길이 : {}'.format(max(len(l) for l in X_train)))
print('리뷰의 평균 길이 : {}'.format(sum(map(len, X_train))/len(X_train)))
plt.hist([len(s) for s in X_train], bins=50)
plt.xlabel('length of samples')
plt.ylabel('number of samples')
plt.show()
#result
리뷰의 최대 길이 : 2494
리뷰의 평균 길이 : 238.71364
to words
word_to_index = imdb.get_word_index() # 없으면 다운로드함
word_to_index #각 단어들의 딕셔너리. 맵핑값.
#result
{'fawn': 34701,
'tsukino': 52006,
'nunnery': 52007,
...
#key, value 값을 뒤바꾼 자료
index_to_word={}
for key, value in word_to_index.items():
index_to_word[value] = key
#result
{34701: 'fawn',
52006: 'tsukino',
52007: 'nunnery',
...
print(index_to_word[1],index_to_word[14],index_to_word[22],index_to_word[16])
#the as you with
print(' '.join([index_to_word[X] for X in X_train[0]]))
#result
the as you with out themselves powerful lets loves their becomes reaching had journalist of lot from anyone to have after out atmosphere never more room and it so heart shows to years of every never going and help moments or of every chest visual movie except her was several of enough more with is now current film as you of mine potentially unfortunately of you than him that with out themselves her get for was camp of you movie sometimes movie that with scary but and to story wonderful that in seeing in character to of 70s musicians with heart had shadows they of here that with her serious to have does when from why what have critics they is you that isn't one will very to as itself with other and in of seen over landed for anyone of and br show's to whether from than out themselves history he name half some br of and odd was two most of mean for 1 any an boat she he should is thought frog but of script you not while history he heart to real at barrel but when from one bit then have two of script their with her nobody most that with wasn't to with armed acting watch an for with heartfelt film want an
불용어, 하위빈도수 단어는 제외된 상태이다.
X_train_s = [' '.join([index_to_word[X] for X in X_train[i]]) for i in range(25000) ]
X_test_s = [' '.join([index_to_word[X] for X in X_test[i]]) for i in range(25000) ]
X_train_s[300] #300번째 리뷰
#result
"the like way as you was did as you lovers there is sneak actor br quality i i as herrings makeup gives br you minutes it depressing not that it each in pay it's actual in poorly for destroy to films like look one distance he when own should is you control comedy for is must ...
transform data to count vectorizer object
count_vect = CountVectorizer(analyzer='word')
count_vect.fit(X_train_s+X_test_s) #두 데이터 합침
# transform the training and test data using count vectorizer object
xtrain_count = count_vect.transform(X_train_s)
xtest_count = count_vect.transform(X_test_s)
print(xtrain_count.shape) #numpy object
#(25000, 9774) 문장 25000개, 각 문장의 단어 개수는 9774개
train data를 test data와 섞어쓰는데 큰 문제가 없어서.
섞어서 단어 벡터를 만들었다.X_train_s 와 X_test_s는 일반 리스트로 합(+)은 두 개의 리스트를 append하라는 의미이다.
numpy 객체의 합(+)은 각 원소끼리의 합을 의미한다.
[1,2,3,4]+[5,6]
#[1, 2, 3, 4, 5, 6]
print(xtrain_count.shape) #(25000, 9774)
print(len(X_train[0])) #218
print(xtrain_count[0])
#result
(0, 138) 1
(0, 224) 1
(0, 312) 1
(0, 456) 3
...
첫번째 문장의 단어는 218개인데 매트릭스는 9774개.
직접적으로 신경망에 넣지는 못해
xtrain_count가 sparse matrix. numpy 객체가 아니라 keras 쓸 수 없어.
다시 numpy 객체로 바꿔야 기존 신경망에 인풋 가능.
sparse matrix는 선형SVM 많이 쓴다. 비선형 쓰면 성능이 더 안좋아져.
선형SVC가 단층 퍼셉트론보다는 성능이 조금 더 잘 나온다.
modeling and predict
model = LinearSVC()
model.fit(xtrain_count, y_train)
#result
LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
intercept_scaling=1, loss='squared_hinge', max_iter=1000,
multi_class='ovr', penalty='l2', random_state=None, tol=0.0001,
verbose=0)
y_pred = model.predict(xtest_count)
print(len(y_pred)) #25000
print(y_pred) #[0 1 1 ... 0 0 1]
sum((y_test == y_pred) *1) / len(y_pred) #0.83 #긍정, 부정 확률
TF-IDF vectorizer
tfidf_vect = TfidfVectorizer(analyzer='word')
tfidf_vect.fit(X_train_s+X_test_s)
xtrain_tfidf = tfidf_vect.transform(X_train_s)
xtest_tfidf = tfidf_vect.transform(X_test_s)
model = LinearSVC()
model.fit(xtrain_tfidf, y_train)
y_pred = model.predict(xtest_tfidf)
sum((y_test == y_pred)*1) / 25000 #0.87456
TF-IDF 사용하니 count vectorizer 사용 시보다 성능이 4퍼센트 향상되었다.
그렇게 드라마틱한 차이는 없었다.
'Python > NLP 자연어처리' 카테고리의 다른 글
Word Embeddimg (0) | 2020.02.14 |
---|---|
형태소 분석 (0) | 2020.02.10 |
자연어 처리 소개와 char 코드 기반 자연어 처리 (0) | 2020.02.10 |