Введение
В этом лабе мы применим Негативный матричный разложение (Non-negative Matrix Factorization, NMF) и латентное Дирихлево распределение (Latent Dirichlet Allocation, LDA) к корпусу документов, чтобы извлечь аддитивные модели структуры тем корпуса. Результатом будет график тем, каждая из которых представлена в виде столбчатой диаграммы с использованием нескольких первых слов по весам.
Советы по работе с ВМ
После запуска ВМ нажмите в левом верхнем углу, чтобы переключиться на вкладку Notebook и получить доступ к Jupyter Notebook для практики.
Иногда вам может потребоваться подождать несколько секунд, пока Jupyter Notebook загрузится. Валидация операций не может быть автоматизирована из-за ограничений в Jupyter Notebook.
Если вы сталкиваетесь с проблемами во время обучения, не стесняйтесь обращаться к Labby. Оставьте отзыв после занятия, и мы оперативно решим проблему для вас.
Загрузка датасета
Мы загрузим датасет 20 newsgroups и векторизуем его. Мы используем несколько эвристик для предварительного фильтрации бесполезных терминов: из постов удаляются заголовки, подписи и цитируемые ответы, а также общие английские слова, слова, встречающиеся только в одном документе или в по крайней мере 95% документов.
from sklearn.datasets import fetch_20newsgroups
n_samples = 2000
n_features = 1000
print("Loading dataset...")
data, _ = fetch_20newsgroups(
shuffle=True,
random_state=1,
remove=("headers", "footers", "quotes"),
return_X_y=True,
)
data_samples = data[:n_samples]
Извлечение признаков
Мы будем извлекать признаки из датасета, используя tf-idf признаки для NMF и признаки подсчета исходных терминов для LDA.
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
## Используем tf-idf признаки для NMF.
print("Extracting tf-idf features for NMF...")
tfidf_vectorizer = TfidfVectorizer(
max_df=0.95, min_df=2, max_features=n_features, stop_words="english"
)
tfidf = tfidf_vectorizer.fit_transform(data_samples)
## Используем признаки подсчета исходных терминов для LDA.
print("Extracting tf features for LDA...")
tf_vectorizer = CountVectorizer(
max_df=0.95, min_df=2, max_features=n_features, stop_words="english"
)
tf = tf_vectorizer.fit_transform(data_samples)
Применение NMF
Мы применим NMF с двумя различными целевыми функциями: нормой Фробениуса и обобщенным дивергенцией Куллбека-Лейблера. Последняя эквивалентна Вероятностному латентному семантическому индексированию (Probabilistic Latent Semantic Indexing).
from sklearn.decomposition import NMF
n_components = 10
n_top_words = 20
init = "nndsvda"
## Обучаем модель NMF
print(
"Обучаем модель NMF (норма Фробениуса) с tf-idf признаками, "
"n_samples=%d и n_features=%d..." % (n_samples, n_features)
)
nmf = NMF(
n_components=n_components,
random_state=1,
init=init,
beta_loss="frobenius",
alpha_W=0.00005,
alpha_H=0.00005,
l1_ratio=1,
).fit(tfidf)
## Строим график топовых слов для модели NMF
def plot_top_words(model, feature_names, n_top_words, title):
fig, axes = plt.subplots(2, 5, figsize=(30, 15), sharex=True)
axes = axes.flatten()
for topic_idx, topic in enumerate(model.components_):
top_features_ind = topic.argsort()[: -n_top_words - 1 : -1]
top_features = [feature_names[i] for i in top_features_ind]
weights = topic[top_features_ind]
ax = axes[topic_idx]
ax.barh(top_features, weights, height=0.7)
ax.set_title(f"Тема {topic_idx +1}", fontdict={"fontsize": 30})
ax.invert_yaxis()
ax.tick_params(axis="both", which="major", labelsize=20)
for i in "top right left".split():
ax.spines[i].set_visible(False)
fig.suptitle(title, fontsize=40)
plt.subplots_adjust(top=0.90, bottom=0.05, wspace=0.90, hspace=0.3)
plt.show()
tfidf_feature_names = tfidf_vectorizer.get_feature_names_out()
plot_top_words(
nmf, tfidf_feature_names, n_top_words, "Темы в модели NMF (норма Фробениуса)"
)
## Обучаем модель NMF с обобщенной дивергенцией Куллбека-Лейблера
print(
"\n" * 2,
"Обучаем модель NMF (обобщенная дивергенция Куллбека-Лейблера) "
"с tf-idf признаками, n_samples=%d и n_features=%d..."
% (n_samples, n_features),
)
nmf = NMF(
n_components=n_components,
random_state=1,
init=init,
beta_loss="kullback-leibler",
solver="mu",
max_iter=1000,
alpha_W=0.00005,
alpha_H=0.00005,
l1_ratio=0.5,
).fit(tfidf)
## Строим график топовых слов для модели NMF с обобщенной дивергенцией Куллбека-Лейблера
tfidf_feature_names = tfidf_vectorizer.get_feature_names_out()
plot_top_words(
nmf,
tfidf_feature_names,
n_top_words,
"Темы в модели NMF (обобщенная дивергенция Куллбека-Лейблера)",
)
## Обучаем модель MiniBatchNMF
from sklearn.decomposition import MiniBatchNMF
batch_size = 128
print(
"\n" * 2,
"Обучаем модель MiniBatchNMF (норма Фробениуса) с tf-idf "
"признаками, n_samples=%d и n_features=%d, batch_size=%d..."
% (n_samples, n_features, batch_size),
)
mbnmf = MiniBatchNMF(
n_components=n_components,
random_state=1,
batch_size=batch_size,
init=init,
beta_loss="frobenius",
alpha_W=0.00005,
alpha_H=0.00005,
l1_ratio=0.5,
).fit(tfidf)
## Строим график топовых слов для модели MiniBatchNMF с нормой Фробениуса
tfidf_feature_names = tfidf_vectorizer.get_feature_names_out()
plot_top_words(
mbnmf,
tfidf_feature_names,
n_top_words,
"Темы в модели MiniBatchNMF (норма Фробениуса)"
)
## Обучаем модель MiniBatchNMF с обобщенной дивергенцией Куллбека-Лейблера
print(
"\n" * 2,
"Обучаем модель MiniBatchNMF (обобщенная дивергенция Куллбека-Лейблера) "
"с tf-idf признаками, n_samples=%d и n_features=%d, "
"batch_size=%d..." % (n_samples, n_features, batch_size),
)
mbnmf = MiniBatchNMF(
n_components=n_components,
random_state=1,
batch_size=batch_size,
init=init,
beta_loss="kullback-leibler",
alpha_W=0.00005,
alpha_H=0.00005,
l1_ratio=0.5,
).fit(tfidf)
## Строим график топовых слов для модели MiniBatchNMF с обобщенной дивергенцией Куллбека-Лейблера
tfidf_feature_names = tfidf_vectorizer.get_feature_names_out()
plot_top_words(
mbnmf,
tfidf_feature_names,
n_top_words,
"Темы в модели MiniBatchNMF (обобщенная дивергенция Куллбека-Лейблера)"
)
Применение LDA
Мы применим модели LDA с tf признаками.
from sklearn.decomposition import LatentDirichletAllocation
print(
"\n" * 2,
"Обучаем модели LDA с tf признаками, n_samples=%d и n_features=%d..."
% (n_samples, n_features),
)
lda = LatentDirichletAllocation(
n_components=n_components,
max_iter=5,
learning_method="online",
learning_offset=50.0,
random_state=0,
)
t0 = time()
lda.fit(tf)
print("завершено за %0.3fs." % (time() - t0))
tf_feature_names = tf_vectorizer.get_feature_names_out()
plot_top_words(lda, tf_feature_names, n_top_words, "Темы в модели LDA")
Резюме
В этом практическом занятии мы научились применять Негативный матричный разложение (Non-negative Matrix Factorization) и латентное Дирихлево распределение (Latent Dirichlet Allocation) к корпусу документов для извлечения аддитивных моделей структуры тем корпуса. Мы также научились строить графики тем, каждая из которых представлена в виде столбчатой диаграммы с использованием нескольких первых слов, отсортированных по весам.