import joblib
data = joblib.load('movie.pkl')
locals().update(data)
from sklearn.decomposition import NMF
NMF
의 n_components
: 문서 주제(특성 또는 성분)의 개수를 설정
random_state
: 재현가능한 결과를 위해 임의의 숫자를 지정
nmf = NMF(n_components=20, random_state=1234)
nmf.fit(x_train)
NMF(alpha=0.0, beta_loss='frobenius', init=None, l1_ratio=0.0, max_iter=200, n_components=20, random_state=1234, shuffle=False, solver='cd', tol=0.0001, verbose=0)
테스트 데이터 x_test
는 x_train
과 동일하게 2,000차원이다.
x_test.shape
(6978, 2000)
테스트 데이터 x_text
를 차원 축소한다.
doc_emb = nmf.transform(x_test)
20차원으로 축소되었습니다.
doc_emb.shape
(6978, 20)
0번째 문서의 주제를 확인한다.. 20개의 주제에 대한 값이 출력된다. 값이 클 수록 그 주제가 차지하는 비율이 크다.
doc_emb[0]
array([0.00410407, 0. , 0. , 0. , 0.04713942, 0.15739583, 0. , 0.05560005, 0. , 0. , 0. , 0.06405412, 0.00115137, 0.02936864, 0. , 0. , 0. , 0. , 0. , 0. ])
5번 주제의 값이 가장 크다.
doc_emb[0].argmax()
5
어떤 단어들이 각각의 주제와 관련되어 있는지 확인해본다. nmf.components_
가 주제와 단어의 관계를 나타낸다. 이 행렬의 크기는 (20, 2000)
로, 20개의 주제와 2,000개의 단어의 관계를 나타낸다.
nmf.components_.shape
(20, 2000)
편리하게 다루기 위해 판다스 데이터 프레임으로 변환한다.
import pandas as pd
loading = pd.DataFrame(nmf.components_.T)
loading['word'] = cv.get_feature_names()
5번 주제과 관련이 높은 단어를 확인한다. men, killed, group, escape, kill 등의 단어가 나온다.
i = 5
loading.sort_values(i, ascending=False).loc[:, [i, 'word']].head()
5 | word | |
---|---|---|
1151 | 4.746426 | men |
986 | 3.824056 | killed |
767 | 3.338384 | group |
589 | 3.267987 | escape |
985 | 3.257324 | kill |
inverse_transform
을 하면 원래의 x_test
와 비슷한 행렬을 복원할 수 있다.
x_recover = nmf.inverse_transform(doc_emb)
이것은 축소된 행렬로부터 복원되었기 때문에 x_test
와 정확히 똑같지는 않다. NMF에서 오차는 프로베니우스 노름(Frobenius norm)으로 구한다. 복잡한 이름과 달리 실제로는 모든 원소들끼리 차이를 구해서, 그 차이를 모두 제곱하여 더한 것이다. 구하는 방법은 아래와 같다. 오차는 작을 수록 더 잘 복원된 것이다.
import numpy as np
np.linalg.norm(x_recover - x_test)
1412.176610674724