Мироздания огромная сфера,
Бесконечный пространства пузырь,
Здесь материи сущность – пространство,
Из неё соткан весь этот мир.
(C) Н. Штирман
Не так давно на просторах ресурса «Малоизвестное интересное», я прочитал комментарий одного посетителя о том, что представление слов векторами не вполне соответствует даже чисто словесно-логической форме мышления, не говоря о том, что никак не связано с образным мышлением, на которое словесное всегда опирается.
Процитирую:
«В пространстве единиц речи бессмысленны операции сложения и умножения на число, без которых невозможно никакое векторное представление. Скорее это топологическое пространство с некоторыми отношениями эквивалентности, принадлежности подмножествам «близости» элементов, даже без явного отношения порядка. В таком пространстве невозможно задать конечномерный базис и адекватно представлять слова векторами. Если это не учитывать, всегда останется огромный произвол, набирающийся из чисто статистических (т. е. случайных) свойств языка, которые, к тому же, будут существенно зависеть от того конкретного корпуса текстов, на которых обучалась нейросеть. Моделировать нужно не речевую деятельность саму по себе, а совместно со стадией «образного представления» когнитивного процесса. Применяемые в LLM, основанных на векторном представлении, вероятности соответствия слов - это всё ещё не семантические (связи слов со смыслами) и не прагматические (связи слов с предметами внешнего мира) свойства языка, а всего лишь синтаксические (связи слов с другими словами). Они отражают лишь статистические закономерности словоупотребления, но никак не сущность понимания этих слов».
По правде сказать мне пока что не достаёт знаний, чтобы оценить верность приведённых выше утверждений. Но это служит стимулом для более глубокого погружения в тему векторного представления слов, используемого языковыми моделями сейчас. С попыткой также понять что-нибудь в феномене человеческого мышления из литературы по психологии. В качестве источника для первого у меня есть ИИ: ChatGPT, Mistral, Llama и ресурсы Интернет. Кстати среди ИИ сервисов появился ещё один интересный экземпляр — Topology Chat, который по заявлениям разработчиков постоянно учится на материалах пользователей, способен со временем выводить из истории взаимодействия с ними более общие занания о предмете и помнит всё существенное из этих взаимодействий. В тестовом режиме можно опробовать его бесплатно, обратившись к сайту topology ai:
В качестве источника для второго у меня под рукой оказалась книга Л.С. Выготского «Мышление и речь», которая, как я надеюсь, сделает мои представления о предмете более полными.
Итак, вот что я почерпнул из этого источника.
Слова — единицы речи, которые служат людям для именования предметов, их качеств, характеристик и взаимодействий . Речь и мышление вместе образуют речевое мышление, которое, в свою очередь, имеет свою единицу — значение слова.
Лев Выготский отмечает неразложимое единство этих составляющих.
«Слово, лишённое значения, не есть слово, оно есть звук пустой, следовательно, значение есть необходимый, конституирующий признак самого слова. Оно есть само слово, рассматриваемое с внутренней стороны. Таким образом, мы как будто вправе рассматривать его с достаточным основанием как феномен речи. Но значение слова с психологической стороны есть не что иное, как обобщение или понятие. Всякое же обобщение, всякое образование понятия есть самый специфический, самый подлинный, самый несомненный акт мысли. Следовательно мы вправе рассматривать значение слова как феномен мышления…
Наличие внутреннего плана речи, стоящего за словами, самостоятельность грамматики мысли, синтаксиса словесных значений, заставляют нас в самом простом речевом высказывании видеть не раз навсегда данное, неподвижное и константное отношение между смысловой и звуковой стороной речи, но движение, переход от синтаксиса значений к словесному синтаксису, превращение грамматики мысли в грамматику слов, видоизменение смысловой структуры при ее воплощении в словах…
Наш обычный разговорный язык в силу присущих ему колебаний и несоответствий грамматического и психологического характера находится в состоянии подвижного равновесия между идеалами математической и фантастической гармонии и в непрестанном движении, которое мы называем эволюцией.
Полное устранение несоответствий в пользу общего и безусловно правильного выражения достигается лишь по ту сторону языка и его навыков — в математике. Первым, кто увидел в математике мышление, происходящее из языка, но преодолевающее его, был, по-видимому, Декарт…»
Пытаясь автоматизировать какие-то части умственного труда мы как раз и занимаемся тем, что ищем те или иные способы математического представления предметов и средств этого труда. Удобной формой такого представления являются таблицы, строки которых могут быть, например, словами, а столбцы - какими-либо существенными признаками этих слов. Количество столбцов в такой таблице будет равно общему числу уникальных признаков всех слов. Для каждого слова, если у него есть этот признак в соответствующем столбце можно поставить 1, а если нет — 0. Теперь если взять всю строку этой таблицы для какого-нибудь из слов, получится строка с нулями и единицами. Такая строка — вектор слова. А вся полученная таблица — векторное пространство слов.
Признаки слов можно получать исследуя статистику появления слов рядом с исследуемым словом в текстах. Чем больше будет текстовая выборка, тем больше различных связей можно выявить. Например:
> слово «кумысолечебница»:
кумыс;
лечение;
бассейн;
санаторий;
поликлиника;
рецепт;
минеральная;
вода;
рефлексотерапия;
ультразвуковой;
массаж.
>слово «главврач»:
врач;
главный;
сокращение;
лечебный;
лечение;
учреждение;
больница;
поликлиника;
санаторий;
приём;
записаться;
Каждое слово в сформированных множествах "кумысолечебница" и "главврач"- ниточки, связывающее эти ключи с другими ключами. В машинном обучении подобные связи также называют семантическими. Слова «кумысолечебница» и «главврач» семантически связаны между собой словами: лечение и поликлиника. Недостаток таких векторов — их большая длинна, равная длине словаря. И низкая плотность. Отличных от нуля признаков на порядки меньше общего числа слов. Такие вектора называются разрежёнными.
Вектора, свободные от этого недостатка называются плотными. В них каждая отдельная ячейка содержит значительно больше информации. Это как правило значения типа float32 нормированные в диапазоне от -1 до 1. Существуют различные подходы к получению таких векторов. Один из простейших может заключаться в следующем (допустим для словаря из 140 000 слов):
1. Для каждой пары слов вычисляем коэффициент Жаккара между их множествами связанных слов.
2. Создаём матрицу сходства размером 140 000 x 140 000, где каждый элемент — это коэффициент Жаккара между соответствующими словами.
3. Применяем методы снижения размерности (например, SVD или t-SNE) к этой матрице, чтобы получить векторные представления меньшей размерности.
Для примера: сходство Жаккара между "кумысолечебница" и "главврач" = 2/22 = 0.090909091 (количество общих слов / количество всех слов двух множеств)
Вектор слова кумысолечебница будет представлять собой последовательность чисел 0.090909091, 0.00100011, 0.00000050… N, где N- общее количество слов, например 140 000.
Теперь в каждой ячейке отражено значительно больше информации и размерность вектора можно уменьшить с меньшими потерями точности.
Вычисляя косинус угла между векторами «кумысолечебница» и «главврач» мы получим значения от 0 до 1 в зависимости от того насколько они окажутся похожими (0 — совершенно не похожи, 1 — полностью совпадают).
Пример вычисления косинусного сходства слов на основе векторов, полученных при обработке 500 МБ текстов, сгенерированных ruGPT2-large.
Определение оптимальной длины векторов — это сложная задача, которая зависит от нескольких факторов:
а) цель использования: Если векторы будут использоваться для конкретной задачи (например, классификации текстов), можно экспериментально подобрать ту размерность которая даёт лучшие результаты.
б) сохранение информации: Можно использовать метрики, оценивающие, насколько хорошо сохраняется информация при снижении размерности. Например, процент объяснённой дисперсии в PCA.
в) вычислительные ресурсы: Более длинные векторы требуют больше памяти и вычислительной мощности
г) эмпирические правила: В области обработки естественного языка часто используются векторы размерностью от 100 до 300. Например, Word2Vec часто использует 300-мерные векторы.
д) метод «локтя»: Можно построить график зависимости какой-либо метрики качества от размерности и выбрать точку, где увеличение размерности перестаёт давать значительное улучшение.
Полученные векторы можно использовать не только для сравнения слов, но и предложений, находя вектор-центроид входящих в эти предложения слов. И документы, путём подсчёта релевантных друг другу предложений.
Пример. Выделение наиболее релевантных друг другу предложений, взятых из различных новостных заметок.
Текст разбит на предложения. В цикле каждое предложение сравнивается с каждым и на экран выводятся только те, релевантность между которыми больше 0.7.
Эталонное предложение заключено между значками > <.
Сходные, преодолевшие порог 0.7 перечислены под ним. Напротив каждого дана мера сходства от 0.7 до 1. Чем больше, тем более похожи предложения.
В этом примере использовались 300-мерные векторы полученные на основе расчёта сходства Жаккара. Признаки слов были извлечены путём обработки 500 Мб текстов-галлюцинаций модели RuGPT2-Large с помощью TF-IDF. Как видно сходство некоторых предложений распознано хорошо, а некоторых — нет.
Причин такого результата несколько:
- - словаря из 140 000 слов оказалось не достаточно и многие слова при подсчёте сходства просто не учитывались - «биполярным», «психоз», «маниакальными», «маниакально-депрессивный», «мирамистина», «Радиоточка», «блоггер»;
- - не все признаки слов, выделенные при помощи TF-IDF являются верными, некоторые представляют собой шум (никак не связанные по смыслу с этим словом, т. е. случайные слова);
- - при работе с предложением мы получаем вектор той же размерности, представляющий собой среднее значение всех входящих в него слов, но на самом деле не все слова в предложении имеют одинаковый вклад в его смысл;
- - что-то потерялось при сжатии 140 000 мерного вектора в 300-мерный, для некоторых слов несущественное, а для других может быть важное.
Если ограничения такого подхода не получается преодолеть тем или иным способом, можно попробовать другие подходы. Следующие по сложности векторизации слов подходы называются Word2Vec и GloVe.
Word2Vec — это несколько моделей, представляющих собой мелкие нейронные сети прямого распространения (Feed Forward), имеющие один входной слой, один скрытый слой и один выходной слой. Исследователи из Google предложили использовать две архитектуры таких моделей для представления слов в виде векторов в непрерывном векторном пространстве:
1. CBOW (непрерывный набор слов): модель прогнозирует текущее слово с учётом слов контекста в определённом окне. Входной слой содержит слова контекста, а выходной слой содержит текущее слово. Скрытый слой содержит измерения, которые мы хотим представить текущему слову, присутствующему в выходном слое.
На приведенной выше диаграмме W(t-2) и W(t-1) представляют слова перед нашим ключевым словом. Для того, чтобы предсказать фокусное слово (слово, которое нас интересует), сеть учится учитывать окружающие его слова. Например, предположим, у нас есть предложение «Я ем пиццу каждый день», если у нас контекстное окно равно 2, входными данными будут: (['я', 'пиццу'], 'ем'), (['ем', 'каждый'], 'пиццу'), (['пиццу', 'день'], 'каждый').
2. Skip n-gram: модель прогнозирует окружающие контекстные слова в контекстном окне с учётом текущего слова. Входной слой содержит текущее слово, а выходной слой содержит контекстные слова. Скрытый слой содержит количество измерений, в которых мы хотим представить текущее слово, присутствующее во входном слое.
Skip n-gram часто работает лучше для редких слов, CBOW работает быстрее и обычно лучше работает с частыми словами.
Скрытый слой этих моделей называется вложением (embedding), а метод языкового моделирования для сопоставления слов с векторами действительных чисел называется встраиванием слов (word embedding).
Основная идея встраивания слов заключается в том, что слова, встречающиеся в схожем контексте, имеют тенденцию быть ближе друг к другу в векторном пространстве.
Вложения — это вещественные плотные векторы (многомерные массивы), которые несут значение слов. Они могут фиксировать контекст слова/предложения в документе, семантическое сходство, связь с другими словами/предложениями и т. д. Популярный пример того, как они извлекают контекст из слов, — это если вы удалите мужчину из короля и добавите женщину, он выведет вектор, похожий на королеву.
Согласно документации Pytorch вложения можно рассматривать как простую таблицу поиска, в которой хранятся векторы всех слов. По этой таблице можно сопоставлять значение слова с весовой матрицей некоторого измерения. Эта весовая матрица дополнительно оптимизируется во время обучения (обновляется во время обратного распространения ошибки) для повышения точности модели.
Два основных параметра таблицы встраивания — размер словаря (num_embeddings) и размер вектора (embedding_dim). Исследователи Ричард Сочер, Кристофер Д. Мэннинг и Джеффри Пеннингтон в 2014 году представили другой алгоритм обучения без учителя для получения плотных векторов слов, который назвали глобальными векторами представления слов, или сокращённо GloVe.
Фундаментальная концепция, лежащая в основе Glove, - это представление слов в виде векторов в непрерывном векторном пространстве, где угол и направление векторов соответствуют семантическим связям между соответствующими словами. Для этого Glove строит матрицу совпадений с использованием пар слов, а затем оптимизирует векторы слов, чтобы минимизировать разницу меду поточечной взаимной информацией соответствующих слов и скалярным произведением векторов. Всего GloVe содержит плотные векторы полученные при обработке примерно 6 млрд слов английской литературы, а также множество других общеупотребительных символов, таких как запятые, фигурные скобки и точки с запятой. Размерность предварительно обученных векторов встраивания — 100, 200 и 300. Файлы моделей GloVe представляют собой простые словари записанные в текстовом формате, где слова являются ключами, а плотные векторы — значениями ключей.
- Вложения можно использовать:
- для представления слов на исходном и целевом языках в системах машинного перевода, целью которых является перевод текста с одного языка на другой; - - чтобы помочь моделям понять контекст и связи между словами и дать более точные ответы, в задачах ответов на вопросы пользователя;
- - для поиска информации и организации документов путём измерения семантического сходства между документами или группировки документов в соответствии с их содержанием; - в задачах на аналогии;
- - в семантическом поиске, где целью является извлечение документов или отрывков в соответствии с их семантической релевантностью запросу пользователя.
Следующим шагом в совершенствовании векторного представления слов является построение разных векторов одного и того же слова в зависимости от того в окружении каких слов целого предложения оно находится. Такой моделью является, например, ELMo, которая будет выдавать разные векторы одного и того же слова в зависимости от контекста, в котором оно использовано.
Подобным образом можно в виде вектора представить и всё предложение, что и делается с помощью Universal Sentence Encoder. Например, рассмотрим два предложения: «Сколько тебе лет?» и «Каков ваш возраст?». Они схожи по смыслу, в обоих мы пытаемся узнать возраст человека. Векторы отдельных слов могут не дать хорошего понимания того, что пытается передать целое предложение и хорошо определить их похожесть.
Встраивание предложений целиком возможно позволит решать подобные задачи точнее. Universal Sentence Encoder — предварительно обученная модель, которую познакомили с большим количеством фраз, предложений и абзацев текста с использованием кодера сети глубокого устреднения (DAN).
Существуют и другие модели встраивания предложений — Doc2Vec, SentenceBERT (SBERT). Модели SBERT состоят из каскадных преобразователей (transformers), обученных предсказывать замаскированные слова в предложении. Это заставляет модель собирать более сложную контекстную информацию.
Модель SBERT может эффективно обрабатывать слова вне словарного запаса (OOV), разбивая их на токены подслов с использованием методов токенизации подслов, таких как кодирование фрагментов слова или байтовых пар (BPE).
Это одно из её преимуществ по сравнению с моделями Word2vec. Ещё одно преимущество SBERT — возможность обработки полисемии: когда одно слово несет несколько значений (сарказм или ирония), это можно назвать полисемией. SBERT может эффективно справиться с этим, поскольку эта модель фиксирует весь контекст предложения.
Модель Word2vec может не отражать все значения, поскольку одному слову назначается только один вектор.
Такая модель имеет две идентичные подсети, которые обобщают некоторые существующие весовые коэффициенты. На вход модели при обучении подаётся пара предложений или любых фрагментов текста. Входной текст преобразуется в токены, включая специальные токены, которые служат для разметки последовательностей. Затем генерируются векторы (по умолчанию 768-мерные, но можно выбрать другую размерность).
Используя пару вложений, вычисляется оценка косинусного сходства, указывающая насколько они семантически похожи. И благодаря этой архитектуре и полученной в конечном итоге обратной связи по оценке сходства, модель BERT учится корректировать свои вложения таким образом, что пара предложений со схожей семантикой будет иметь схожие векторы в контекстном векторном пространстве выбранной размерности.
Модели SBERT, обученные для кодирования русских текстов можно найти на площадке Hugging face.
Рассмотрим как можно загрузить и применить одну из таких моделей — cointegrated/rubert-tiny2. Из описания, которое предоставил разработчик мы можем узнать что размер её словаря 83828, максимальная длина обрабатываемой последовательности — 2048.
Сначала модель нужно загрузить. Для этого используем следующий код:
В path, понятное дело, нужно подставить свой путь к каталогу, в котором вы будете хранить данную модель. В дальнейшем, чтобы не обращаться каждый раз к платформе Hugging face, нужно в SentenceTransformer напрямую подставлять путь к папке с моделью:
Дальше создадим список с несколькими предложениями, которые преобразуем в векторы и сравним их сходство.
И вот что получилось:
Модель вернула таблицу вложений имеющую 6 строк и 312 столбцов. Т.е. 6 предложений, которые мы подали на вход, превратились в 6 векторов длинной 312 значений каждый.
Далее мы посчитали косинусное сходство каждого вектора с каждым и получили результат в виде тензора — матрицы, имеющей 6 списков по 6 значений в каждом. В каждом списке находятся числа, отражающие меру сходства соответствующего ему предложения с остальными предложениями. Само с собой предложение наиболее похоже, поэтому стоит 1, со следующим оно похоже по смыслу, хотя ни одно слово не совпадает, но модель с этим справилась поставив довольно высокую оценку схожести — 0.8606. Дальше идёт предложение отличающееся и по смыслу и по словам и сходство соответственно ниже — 0.6695. Затем идут те же предложения на английском и, как это не странно, они тоже правильно ранжированы по сходству .
Для наглядности эти сопоставления выведены текстом, с проставлением рангов сходства. Посмотрим как выглядят сами вложения.
Посмотрим что ещё можно сделать с помощью библиотеки sentence_transformers.
Узнать максимальную длину входной последовательности можно следующим образом:
Для измерения сходство между векторами можно вместо расчёта косинуса угла между ними рассчитывать евклидово расстояние (евклидова норма). В алгоритмах машинного обучения оно используется для оценки того, насколько похожи данные, выраженные этими векторами.
Евклидово расстояние определяется как квадратный корень из суммы квадратов разностей между компонентами двух векторов:
Судя по результату — чем меньше число, тем меньше степень сходства между векторами. При полном совпадении евклидово расстояние между векторами равно нулю.
Узнать доступные методы определения сходства можно обратившись к свойству possible_values() из SimilarityFunctions:
Если экземпляр Sentence Transformer заканчивается модулем Normalize, то разумно выбрать метрику «dot» вместо «cosine».
Скалярное произведение на нормализованных вложениях эквивалентно сходству косинуса, но «cosine» снова нормализует вложения. В результате метрика «dot» будет быстрее, чем «cosine».
С помощью метода model.similarity_pairwise() можно вычислить сходство между двумя коллекциями вложений. Выходным результатом будет вектор с оценками сходства между каждой парой вложений.
Библиотека sentence_transformers позволяет токенизировать предложения. Для этого имеется метод tokenize:
input_ids — это тензор, содержащий идентификаторы токенов для каждого предложения. Каждый токен в предложении представлен уникальным числом, которое соответствует его позиции в словаре модели. Например, 2 обычно обозначает специальный токен начала последовательности (CLS), а 3 — токен конца последовательности (SEP). Остальные числа представляют собой токены слов в предложении. Интересно отметить, что меньшее по количеству слов предложение «каков ваш возраст» превратилось в большее число токенов «14430, 887, 31495, 21331, 35». Числом 35 видимо закодирован знак «?»
token_type_ids — тензор, который указывает, к какому предложению принадлежит каждый токен. В данном случае все значения равны нулю, что означает, что все токены относятся к одному предложению.
attention_mask — этот тензор указывает, какие токены следует учитывать при обработке. Значение 1 означает, что токен должен быть обработан, а 0 — что токен игнорируется.
Вот как выглядит вывод для первого предложения "Сколько тебе лет?":
Таким образом, вывод программы показывает, как каждое предложение было преобразовано в токены, которые затем могут быть использованы для дальнейшей обработки, например, для получения векторных представлений предложений.
Для приложений, в которых используются вложения разных размерностей имеется возможность выводить усечённые по размеру вложения. Например, получим вложения размерностью 16:
Вот ещё вариант вычисления косинусного сходства между векторами, с помощью библиотеки torch.
Следует взять на заметку, что Sentence Transformers — это библиотека Python не только для использования моделей встраивания, но и их обучения для широкого спектра приложений, таких как генерация дополненного поиска, семантический поиск, семантическое текстовое сходство, анализ парафраз и многое другое.
На этом оставим пока что в покое SBERT и поработаем с Word2Vec моделями. Они хоть и проще, но в своей области также, возможно, могут быть полезными. Например, с помощью них можно легко получить список слов, наиболее близких данному.
Для работы с Word2Vec моделями нам понадобятся nltk и gensim. Если они ещё не установлены, то сделать это можно выполнив в терминале команды:
pip install nltk pip install gensim
NLTK предлагает удобные интерфейсы для более чем 50 лексических ресурсов и корпусов, включая WordNet. В его состав также включён набор библиотек обработки текста для таких задач как категоризация, токенизация, стемминг, тегирование, синтаксический анализ и семантическое рассуждение. Примеры работы с этим пакетом уже приводились здесь:
«Датамайниг текстов» и «О начункивании текстов и их анализе».
GENSIM — это библиотека Python, которая использует тематическое моделирование и моделирование сходства документов для управления и анализа больших объёмов неструктурированных данных. Он особенно известен благодаря применению алгоритмов моделирования векторного пространства слов, таких как Word2Vec и скрытое распределение Дирихле (LDA).
Скрытое распределение Дирихле — это порождающая модель в машинном обуении и информационном поиске, которая повзоляет объяснить результаты наблюдений с помощью неявных групп. Она выявляет причины сходства некоторых частей данных, напремер, слов в документах. Впервые технология LDA была представлена в 2003 году Дэвидом Блейном, Эндрю Ыном и Майклом Джорданом как графовая модель обнаружения тематик. Каждый документ в LDA рассматривается как набор различных тематик, а распределение этих тематик имеет априори в виде распределения Дирихле. К этому мы ещё вернёмся в следующих статьях, а пока что сосредоточимся на Word2Vec.
В обработке естественного языка (NLP) Word2Vec — популярный метод представления слов в виде векторов в непрерывном векторном пространстве. С помощью этого инструмента слова получаются представлены в векторном пространстве так, что похожие слова находятся рядом друг с другом. Это позволяет модели интерпретировать слова в соответствии с их контекстом внутри конкретного корпуса текстов. В машинном обучении это называется семантическими представлениями.
В основе Word2Vec лежит дистрибутивная гипотеза, согласно которой слова со схожим значением с большей вероятностью встречаются в схожих контекстах. Word2Vec генерирует векторные представления, отражающие семантическое сходство, изучая закономерности распределения слов в большом корпусе текстов.
Получив правильно векторное пространство слов, перед разработчиками отрывается возможность использовать векторную арифметику для записи отношений между словами. Одним из известных примеров является выражение векторного представления «королевы» как векторного представления «короля» из которого вычли векторное представление «мужчина».
Для вывода списка доступных для загрузки моделей Word2Vec можно воспользоваться следующим кодом:
Из этого списка мы видим по-крайней мере одну, которая обучена на текстовом корпусе, содержащем русский язык: word2vec-ruscorpora-300. Её-то мы и загрузим, чтобы пощупать.
Нужно найти каталог, в который загрузилась модель и использовать этот путь для загрузки в последующем. Посмотрим что за слова заложены в word2vec-ruscorpora-300.
Мы видим, что здесь в качестве ключей к векторам используются не просто слова русского языка, а их комбинация с обозначением части речи. Это не очень удобно. Попробуем исправить ситуацию сформировав промежуточный словарь, в котором ключами будут просто слова, а значениями — ключи этой модели.
Теперь, имея нормальные слова-ключи, посмотрим как одно слово похоже на другое в этой модели.
Довольно неплохо — учебный скорее образовательный чем кисейный, барышня скорее девочка, чем мальчик или курица. Посмотрим сколько всего слов в данной модели:
Выведем 10 слов, наиболее близких слову барышня:
Странно, но здесь барыня и барышня значительно более похожи чем в предыдущем примере. Посмотрим, правильно-ли преобразовались ключи модели в ключи-слова.
Похоже, что в модели есть ключ «барыня» без «::» и он затёрся в процессе преобразования. Нужно переделать функцию getkeys.
Уже лучше, но пока что непонятно,почему в словаре после преобразования getkeys 176725 слов, а ключей в модели 184973.
Похоже есть ключи, которые нужно обработать по-другому.
В словарь попал ключ пустыня_NOUN, а ключ пустныня::негель_VERB оказался пропущен. Перепишем функцию getkeys ещё раз.
Вот в чём дело. Есть слова, одинаковые по написанию, но являющиеся разными частями речи. Первое такое слово попадает в словарь, а второе — нет. Исправим это, добавив дополнительный словарь:
Уже значительно лучше. Потерялось всего 20 слов. В этой модели слова приведены к нормальной форме, поэтому для адекватной обработке текстов, по-видимому необходимо предварительно лемматизировать тексты с помощью того же пакета Natasha или Pymorphy. Примеры их использования были вы указанных выше более ранних статьях.
Мы же пойдём дальше.
Вот здесь содержится большое количество моделей, обученных на корпусах русских текстов. Самые новые из них — модели ELMo:
А вот здесь есть описание библиотеки, с помощью которой можно работать с этими моделями.
Скачаем не лемматизированную модель ruwikiruscorpora_tokens_elmo_1024_2019 и посмотрим что она может. Предварительно нужно будет установить библиотеку simple-elmo и пакет tensorflow:
pip install --upgrade simple_elmo
pip install tensorflow==2.10.0
Не самая новая версия tensorflow нужна потому, что в более новой в связанной с ней библиотеке Keras 3.0 нет поддержки LSTMCell.
По информации разработчиков библиотека simple_elmo основана на оригинальной реализации ELMo, которая была нацелена на более ранние версии TensorFlow, на сегодняшний день сильно устаревшие и simple_elmo ещё не полностью переведён на современные версии TensorFlow.
Итак, получим векторные встраивания предложений с помощью модели Elmo:
Посмотрим насколько хорошо с помощью этих векторов определяется схожесть предложений.
Для вычисления косинусного сходства между этими векторами воспользуемся возможностями библиотеки Numpy.
Здесь мы используем функцию np.dot() для вычисления скалярного произведения векторов и функцию norm() для вычисления нормы векторов. Затем мы делим скалярное произведение на произведение норм векторов, чтобы получить косинусное сходство. Вот что получилось:
В общем-то правильно, но по сравнению со SBERT выглядит как-то слабовато, или нужно лучше разобраться с чем мы имеем дело.
Когда исследование заходит в тупик, самое время начать читать документацию.
Вот что там написано.
Метод get_elmo_vectors() создаёт тензор контекстуализированных вложений слов. Его форма — количество предложений, длина самого длинного предложения, размерность ELMo. Количество предложений 6, размерность ELMo — 1024, а откуда взялось число 30? По-видимому это количество символов в предложениях и предложение "What are your plans for today?" имеет как раз 30 символов.
Т.е. для каждого из 6 предложений мы получили 30 векторов, каждый размерностью 1024. Тогда применённое выше сравнение предложений — совсем не то, что нужно.
Посмотрим что ещё есть в библиотеке simple-elmo. Метод get_elmo_vector_average() создаёт тензор с одним вектором на каждое входное предложение, построенный путём усреднения отдельных контекстуализированных вложений слов. Его форма — количество предложений, размерность ELMo. Похоже этот метод как раз и нужен для нашей задачи сравнения предложений.
Ну тоже так себе результат. Снова читаем документацию, на сей раз повнимательнее. И обнаруживаем, что этому методу, как и предыдущему, нужно передавать не строки, а списки слов (т. е. предварительно токенизировать текст). Можно это сделать обратившись к соответствующим методам из NLTK.
Попробуем ещё раз:
И снова результат не впечатляет. Или всё-таки я что-то делаю не так, или эти векторы не очень хороши. К тому-же Elmo с размерами векторов 1024 работает у меня заметно медленнее 300-мерных статических векторов Word2Vec и 312-мерных SBERT. При наличии альтернатив, пока что не вижу причин более настойчиво пытаться получить хороший результат именно от этой модели. Возможно, вернусь к ней в будущем, если будет такое желание.
Библиотеки, которые использовались для работы с вышеперечисленными моделями, позволяют по мимо инференса, ещё и обучение на своих данных. Воспользуемся этой возможностью, чтобы сравнить результаты.
Для этого у меня имеется набор галлюцинаций модели ruGPT2-large, которая при определённом сочетании параметров генерирует их очень плодотворно и разнообразно.
Примеры фрагментов, содержащихся в этом файле:
Программа, обучающая Word2Vec CBOW модель с нуля на выбранном наборе текстов.
После завершения процесса, который занял на моём компьютере примерно 4 часа, получаем три файла:
Теперь можно приступать к её тестированию.
Все слова, которые мы захотели протестировать есть в модели. И все сходства в общем-то получились неплохо. Можно отметить, что в нашей модели судя по результату нет знакомой нам по классической литературе связи между словами кисейный и барышня. Барышня значительно больше девочка, чем мальчик и это правильно. В то же время она больше курица чем мальчик и это уже немного странно.
Посмотрим какие 10 наиболее близких слов привязались к барышне.
Это уже заметно отличается от того, какие связи сформировались с барышней у модели word2vec-ruscorpora-300. Больше 500 МБ текста — слишком большой объём, чтобы его вычитать и исключить из него всё непотребное. Но применив некоторые шаблоны можно попытаться выкинуть хотя-бы наиболее легко обнаруживаемые вещи. Например, поиск по ключевым словам показал, что модель нагенерила мне текстов, в части из которых присутствовали всем известные слова великого и могучего, которые не для литературного письма. Там же оказались в небольшом количестве различные сцены, которые подошли бы для сценариев фильмов для взрослых. Например в одной из них барышня зачем-то засунула себе в причинное место ложку. Ох, и на чём только обучали эту модель наши датасаентисты-приколисты. Всё это надо удалять перед обучением, чтобы потом было меньше работы по борьбе с предвзятостями и прочим непотребством готовой модели. С другой стороны бесплатная модель для сообщества, может быть и не должна быть изначально огранённым бриллиантом, чтобы людям было чем заниматься и самим, а не получать всё готовеньким совершенно не напрягаясь.
Посмотрим, что у нас получится с предложениями.
Ни одно предложение на английском языке не обработано, т. к. в ней нет таких слов. Предложения на русском язык обработаны, но сходство определено не смысловое, а синтаксическое. Предложения «сколько тебе лет» и «какие у тебя планы на сегодня» оказались ближе друг другу чем «сколько тебе лет» и «каков ваш возраст», потому что в первом случае нашлись два похожих слова «тебе» и «тебя».
Опробуем ещё одну самодельную модель, речь о которой была в самом начале статьи. Здесь не используются искусственные нейронные сети (ИНС), а сначала создаётся коллекция признаков каждого слова с помощью TF-IDF, затем рассчитываются векторы для каждого слова путём вычисления сходства Жаккара этого слова со всеми остальными словами. Полученная матрица высокой размерности затем снижается до произвольной меньшей. В нашем случае 140733 x 140733 преобразуется в 140733 x 522. Полученный набор векторов представляет собой словарь, в котором каждое слово — ключ, а вектор — значение. Он сохранён в бинарном виде и его можно загрузить с помощью библиотеки pickle.
Продолжение
Продолжение
Запускаем, и чуть более, чем за час получаем набор векторов.
Загрузим, посмотрим на то, что там имеется.
И т. д.
Посмотрим, как она определяет схожесть слов и предложений.
Исключаем из набора слова, которых нет в модели: кисейный и барыня.
Со словами неплохо. Посмотрим как с предложениями.
Тоже неплохо, предложения «сколько тебе лет» и «какие каков ваш возраст» оказались всё-таки ближе друг другу чем «сколько тебе лет» и «Какие у тебя планы на сегодня». Хотя, может быть это случайное совпадение, поэтому проверим на других предложениях.
Все сопоставления по степени похожести в общем-то получились правильными, хотя результат сопоставления первого и последнего предложений не связанных ни синтаксически ни семантически равный 0.5539 намекает на то, что точность такого метода определения схожести предложений может быть на большей выборке невысокой.
Сравним с тем, что получается у SBERT.
Здесь оценка схожести первого и последнего предложений получилась лучше. Но первого с четвёртым и первого с пятым — хуже. Хотя в общем-то тоже всё правильно.
Возможно точность модели на основе сходства TF-IDF и Жаккара удастся поднять, увеличим словарь или подобрав другую размерность векторов. Потому что во-первых при 140 тыс. слов далеко не все слова в предложениях учитываются. А во-вторых при уменьшении размерности теряется часть информации. Если увеличить размер словаря до 1-2 млн. слов, учитываться будут практически все слова. А с размерностью векторов всё сложнее, т. к. что конкретно теряется, а что остаётся сложно сказать сходу. Оставим это для будущих экспериментов.
В следующей статье я намерен погрузиться в тему машинного обучения и построения искусственных нейронных сетей различной архитектуры, начиная сетей прямого распространения (Feed forward neural network).
Список источников по теме статьи:
1. Л. С. Выготский "Мышление и речь", с. 352 - 429
2. https://huggingface.co/cointegrated/rubert-tiny2
3. https://git.lnyan.com/blog/train-sentence-transformers
4. https://sbert.net/docs/package_reference/sentence_transformer/SentenceTransformer.html
5. https://developer.dataiku.com/latest/tutorials/machine-learning/code-env-resources/sentence-transformers-resources/index.html
6. https://habr.com/ru/articles/704592/
7. https://dzen.ru/a/X9EqLPiwyiBtIep1?ysclid=lzejfwf7fm85508176
8. https://en.wikipedia.org/wiki/Word_n-gram_language_model
9. https://topologychat.com/chat/
10. https://github.com/m3cinc/skipgram
11. https://github.com/m3cinc/skipgram
12. https://cran.r-project.org/web/packages/tokenizers/vignettes/introduction-to-tokenizers.html
13. Youtube:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22
15. https://webdevblog.ru/gensim-rukovodstvo-dlya-nachinajushhih/?ysclid=lzjol38910990655976
16. https://radimrehurek.com/gensim/models/word2vec.html
17. https://pythonru.com/biblioteki/gensim?ysclid=lzjom2nlse948718587
18. https://rusvectores.org/ru/models/
19. https://sysblok.ru/knowhow/obuchaem-word2vec-praktikum-po-sozdaniju-vektornyh-modelej-jazyka/
20. https://python-school.ru/blog/nlp/word2vec-with-examples-in-gensim/
21. https://gitflic.ru/project/utrobinmv/simple-elmo-pytorch
22. https://codingwithfun.com/pip/simple-elmo/551908/
23. https://piwheels.org/project/simple-elmo/
24. https://www.wheelodex.org/projects/simple-elmo/
25. https://typeoverflow.com/developer/docs/tensorflow~2.9/compat/v1/nn/rnn_cell/lstmcell