Использование NMF и SVD для тематического моделирования
Тематическое моделирование — это хороший способ начать использовать матричные разложения. Мы начнём с термина «матрица документов»:
Источник: «Введение в информационный поиск» (http://player.slideplayer.com/15/4528582/#)
Мы можем разложить его на высокую и узкую матрицы (возможно, с диагональной матрицей посередине).
Обратите внимание, что это представление не учитывает порядок слов или структуру предложений. Это пример «мешка слов».
Рассмотрим крайний случай — использование внешнего произведения двух векторов для восстановления матрицы. Очевидно, что в большинстве случаев мы не сможем точно восстановить матрицу. Однако если у нас есть вектор, содержащий относительную частоту каждого слова во всех словах, и другой вектор, имеющий среднее количество слов в каждом документе, то произведение будет максимально приближено к исходному.
Теперь рассмотрим увеличение матрицы до двух столбцов и двух строк. Теперь наилучшее разложение состоит в том, чтобы разделить документы на две группы, каждая из которых имеет как можно более различное распределение слов, но как можно более похожее внутри группы. Эти две группы мы будем называть «темами». Мы разделим эти слова на две группы в соответствии с наиболее часто встречающимися словами в каждой теме.
В этом курсе мы будем использовать несколько различных категорий наборов данных документов и искать темы, состоящие из слов. Знание фактической категории поможет нам оценить, имеют ли обнаруженные темы смысл.
Мы попытаемся использовать два разных матричных разложения: сингулярное разложение (SVD) и неотрицательное матричное разложение (NMF).
import numpy as np
from sklearn.datasets import fetch_20newsgroups
from sklearn import decomposition
from scipy import linalg
import matplotlib.pyplot as plt
%matplotlib inline
np.set_printoptions(suppress=True)
Scikit Learn поставляется со множеством встроенных наборов данных, а также инструментами для загрузки множества стандартных внешних наборов данных. Это отличный ресурс, включающий наборы данных о ценах на жилье в Бостоне, изображениях лиц, пятнах леса, диабете, раке молочной железы и т. д. Мы будем использовать набор данных новостных групп.
Новостные группы — это дискуссионные группы Usenet, популярные до того, как интернет действительно взлетел в 80-е и 90-е годы. Этот набор данных включает в себя 18 000 сообщений новостных групп с 20 различными темами.
categories = ['alt.atheism', 'talk.religion.misc', 'comp.graphics', 'sci.space']
remove = ('headers', 'footers', 'quotes')
newsgroups_train = fetch_20newsgroups(subset='train', categories=categories, remove=remove)
newsgroups_test = fetch_20newsgroups(subset='test', categories=categories, remove=remove)
newsgroups_train.filenames.shape, newsgroups_train.target.shape
# ((2034,), (2034,))
Давайте посмотрим на некоторые данные. Можете ли вы угадать, к какой категории относятся эти сообщения?
print("\n".join(newsgroups_train.data[:3]))
'''
Hi,
I've noticed that if you only save a model (with all your mapping planes
positioned carefully) to a .3DS file that when you reload it after restarting
3DS, they are given a default position and orientation. But if you save
to a .PRJ file their positions/orientation are preserved. Does anyone
know why this information is not stored in the .3DS file? Nothing is
explicitly said in the manual about saving texture rules in the .PRJ file.
I'd like to be able to read the texture rule information, does anyone have
the format for the .PRJ file?
Is the .CEL file format available from somewhere?
Rych
Seems to be, barring evidence to the contrary, that Koresh was simply
another deranged fanatic who thought it neccessary to take a whole bunch of
folks with him, children and all, to satisfy his delusional mania. Jim
Jones, circa 1993.
Nope - fruitcakes like Koresh have been demonstrating such evil corruption
for centuries.
>In article <1993Apr19.020359.26996@sq.sq.com>, msb@sq.sq.com (Mark Brader)
MB> So the
MB> 1970 figure seems unlikely to actually be anything but a perijove.
JG>Sorry, _perijoves_...I'm not used to talking this language.
Couldn't we just say periapsis or apoapsis?
'''
Подсказка: определение периджове — это ближайшая точка на орбите спутника Юпитера.
np.array(newsgroups_train.target_names)[newsgroups_train.target[:3]]
'''
array(['comp.graphics', 'talk.religion.misc', 'sci.space'],
dtype='<U18')
'''
Атрибут target
представляет собой целочисленный индекс категории.
newsgroups_train.target[:10]
# array([1, 3, 2, 0, 2, 0, 2, 1, 2, 1])
num_topics, num_top_words = 6, 8
Далее scikit learn предоставляет метод извлечения всех слов. TF-IDF
Метод TF-IDF (Term Frequency — Inverse Document Frequency) — это метод нормированного подсчёта частоты терминов, который учитывает частоту появления термина в документе и обратную частоту документа.
TF = (частота термина t в документе) / (общее количество слов в документе).
IDF = log (количество документов / количество документов, содержащих термин t).
vectorizer_tfidf = TfidfVectorizer(stop_words='english')
vectors_tfidf = vectorizer_tfidf.fit_transform(newsgroups_train.data) # (documents, vocab)
W1 = clf.fit_transform(vectors_tfidf)
H1 = clf.components_
show_topics(H1)
['Люди просто думают, что знают, как сказать, религия', 'Спасибо за файлы с графическими изображениями, формат программы Windows', 'Запуск космических челноков НАСА, орбита, лунный, земной', 'Айко Боббин Тек Беаучайн Бронкс Манхэттен Санк Куинс', 'Бог Иисус Библия Атеизм Вера Религия Атеист', 'Объективная моральность Ценности Моральные Субъективные Наука Абсолютные Заявления']
plt.plot(clf.components_[0])
<matplotlib.lines.Line2D at 0x7f0e1039a7b8>
clf.reconstruction_err_
43.71292605795277
NMF: Резюме
Преимущества: быстрый и простой в использовании!
Недостатки: требуется много лет исследований и профессиональных знаний для создания.
Примечание:
Использование SGD для написания NMF с нуля в NumPy
Градиентный спуск
Основная идея градиентного спуска:
Важно: Мы хотим уменьшить потери, производная говорит нам о самом крутом направлении спуска.
Обратите внимание, что потери, ошибки и затраты — это термины, используемые для описания одного и того же.
Давайте посмотрим на заметки курса по градиентному спуску (первоначально из курса глубокого обучения fast.ai).
Стохастический градиентный спуск (SGD)
Стохастический градиентный спуск — это очень полезный метод оптимизации (он также является основой глубокого обучения, он используется для обратного распространения).
Для стандартного градиентного спуска мы используем все данные для расчёта потерь, что может быть очень медленным. В стохастическом градиентном спуске мы вычисляем нашу функцию потерь только на основе нашей выборки данных (иногда называемой мини-пакетом). Мы получим разные значения потерь на разных выборках данных, поэтому это случайное явление. Оказывается, это всё ещё эффективный способ оптимизации, и он более эффективен!
Мы можем увидеть, как это работает в этой электронной таблице (первоначально из курса глубокого обучения fast.ai).
Ресурсы:
Применение SGD к NMF
Цель: Разложить V(mxn) на:
V ≈ WH
где W(mxd) и H(dxn), W, H >= 0, мы минимизируем Frobenius-норму V − WH.
Метод: Мы случайным образом выберем положительные W и H, а затем оптимизируем их с помощью SGD.
Чтобы использовать SGD, нам нужно знать градиент функции потерь.
Источники:
lam=1e3
lr=1e-2
m, n = vectors_tfidf.shape
W1 = clf.fit_transform(vectors)
H1 = clf.components_
show_topics(H1)
['Изображение JPEG, изображение GIF, файл, цветные изображения, качество формата',
'Edu Graphics Pub Mail 128 Ray FTP Send',
'Запуск спутника в космос, спутник НАСА, коммерческий спутник, рынок, год',
'Иисус, Матфей, пророчество, люди, сказал Мессия, Давид, Исайя',
'Данные изображения доступны, обработка программного обеспечения, анализ образования, анализ',
'Атеисты, атеизм, религиозный, верят, люди, религия, делает']
mu = 1e-6 def grads(M, W, H): R = W@H-M return R@H.T + penalty(W, mu)*lam, W.T@R + penalty(H, mu)*lam # dW, dH
def penalty(M, mu): return np.where(M>=mu,0, np.min(M - mu, 0))
def upd(M, W, H, lr): dW,dH = grads(M,W,H) W -= lrdW; H -= lrlr*dH
def report(M,W,H): print(np.linalg.norm(M-W@H), W.min(), H.min(), (W<0).sum(), (H<0).sum())
W = np.abs(np.random.normal(scale=0.01, size=(m,d))) H = np.abs(np.random.normal(scale=0.01, size=(d,n)))
report(vectors_tfidf, W, H)
44.4395133509 5.67503308167e-07 Перевод текста на русский язык:
Этот тренинг очень медленный! Требуется настроить большое количество параметров, но он всё равно очень медленный (или взрывается).
PyTorch — это фреймворк для работы с тензорами и динамическими нейронными сетями на Python с поддержкой GPU. Многие ключевые участники работают в команде AI Facebook. Во многих отношениях он похож на Numpy, но добавляет параллелизацию с использованием GPU.
Как видно из документации PyTorch:
Для дальнейшего изучения: если вы хотите узнать, что такое динамические нейронные сети, вам, возможно, захочется посмотреть выступление Soumith Chintala, исследователя AI Facebook и одного из основных участников PyTorch.
Если вы хотите больше узнать о PyTorch, вы можете попробовать этот учебник или изучить примеры.
Примечание по использованию GPU: если вы не используете GPU, то необходимо удалить .cuda()
из следующих методов. Этот курс не требует использования GPU, но я думаю, что некоторые люди будут заинтересованы. Чтобы узнать, как создать экземпляр AWS с помощью GPU, вы можете посмотреть курс конфигурации fast.ai.
import torch
import torch.cuda as tc
from torch.autograd import Variable
def V(M): return Variable(M, requires_grad=True)
v=vectors_tfidf.todense()
t_vectors =
torch.Tensor(v.astype(np.float32)).cuda()
mu = 1e-5
def grads_t(M, W, H):
R = W.mm(H)-M
return (R.mm(H.t()) + penalty_t(W, mu)*lam,
W.t().mm(R) + penalty_t(H, mu)*lam) # dW, dH
def penalty_t(M, mu):
return (M<mu).type(tc.FloatTensor)*torch.clamp(M - mu, max=0.)
def upd_t(M, W, H, lr):
dW,dH = grads_t(M,W,H)
W.sub_(lr*dW); H.sub_(lr*dH)
def report_t(M,W,H):
print((M-W.mm(H)).norm(2), W.min(), H.min(), (W<0).sum(), (H<0).sum())
t_W = tc.FloatTensor(m,d)
t_H = tc.FloatTensor(d,n)
t_W.normal_(std=0.01).abs_();
t_H.normal_(std=0.01).abs_();
d=6; lam=100; lr=0.05
for i in range(1000):
upd_t(t_vectors,t_W,t_H,lr)
if i % 100 == 0:
report_t(t_vectors,t_W,t_H)
lr *= 0.9
'''
44.392791748046875 -0.0060190423391759396 -0.0004986411076970398 1505 2282
43.746803283691406 -0.009054708294570446 -0.011047963984310627 2085 23854
43.702056884765625 -0.008214150555431843 -0.014783496037125587 2295 24432
43.654273986816406 -0.006195350084453821 -0.006913348101079464 2625 22663
43.646759033203125 -0.004638500511646271 -0.003197424579411745 2684 23270
43.645320892333984 -0.005679543130099773 -0.00419645756483078 3242 24199
43.6449089050293 -0.0041352929547429085 -0.00843129213899374 3282 25030
43.64469528198242 -0.003943094052374363 -0.00745873199775815 3129 26220
43.64459991455078 -0.004347225651144981 -0.007400824688374996 3031 26323
43.64434051513672 -0.0039044099394232035 -0.0067480215802788734 3930 33718
'''
show_topics(t_H.cpu().numpy())
'''
['objective morality values moral subjective science absolute claim',
'god jesus bible believe atheism christian does belief',
'ico bobbe tek bronx beauchaine manhattan sank queens',
'thanks graphics files image file program windows know',
'space nasa launch shuttle orbit lunar moon earth',
'don people just think like know say religion']
'''
plt.plot(t_H.cpu().numpy()[0])
# [<matplotlib.lines.Line2D at 0x7fe4173f1d68>]
t_W.mm(t_H).max()
0.43389660120010376
t_vectors.max()
0.9188119769096375
Выше мы использовали наше знание градиента функции потерь для написания SGD с нуля в PyTorch. Однако у PyTorch есть пакет автоматического градиента, который мы можем использовать. Это очень полезно, потому что мы можем использовать автоматический градиент, когда мы не знаем, что такое производная.
Используемый нами метод очень универсален и может быть использован практически для любой задачи оптимизации.
В PyTorch переменные и тензоры имеют один и тот же API, но переменные запоминают операции, которые использовались для их создания. Это позволяет нам брать производные.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )