Русский
Русский
English
Статистика
Реклама

Natural language processing

Что такое алгоритм?? Part three and a quarter. Язык

10.06.2021 08:18:29 | Автор: admin

И у нас есть организмы, а называем мы их странным именем "Алгоритмы". И если есть у нас близко два таких "Алгоритма", то может стать полезным их взаимодействие. И если оно полезно самим организмам, то в свою очередь становится полезно и нам. Ведь это тот редкий момент, в который можно, наблюдая, разобраться, как предстало на свет "Слово" из того, что есть "Логос". Ибо абсолютно, совершенно, неоспоримо и уже написано то, к чему нечего добавить.


.

Title


Задача


В прочитанных статьях серии мы с Вами затронули многие стороны термина "Алгоритм". Мы прошли через статьи, в которых алгоритм представлен как глубокая основа математики и физики, и там немного потренировались с терминами "Перенос" и "Трансляция". Мы чуть коснулись алгоритмов человеческих эмоций. И теперь подошли к новому разделу, в котором алгоритм также играет ключевую роль, но пока никому не хотелось эту роль здесь найти. Тем, кто помимо статей этой серии просмотрел развлекательную "Детскую сказку программисту на ночь", немного понятнее куда сейчас идет наш разговор. И опять подсказкой является "говорящий" заголовок статьи. Давайте выпишем сразу все основные темы:


Это Слово. Это Смысл. Это сложности перевода. Наконец, это разбор появления коммуникации.


Опять многовато для одной статьи. Но ведь хочется поскорее разобраться с самым интересным. И нам опять поможет пробраться сквозь все эти сложности наш опорный термин, описанию которого посвящена вся серия статей. Да, ключ статьи это снова Алгоритм.


К сожалению, Алгоритм это совсем не единственный термин, который требуется в нашем пути. И потому в каждой статье серии есть некоторое количество новых и странных терминов. И, конечно, текущая статья совсем не исключение, а еще одно подтверждение этого факта. Более того. Текущую статью как ни старайся не получается сделать совсем автономной и независимой в чтении от статей предыдущих. В ней понадобятся почти все результаты зафиксированные ранее. И почти гарантирую: это будет сложно. Поэтому опять отмечу (даже в отсутствии спутницы двух предыдущих статей нашей сказочной Алисы), что настроенным на взрослый скепсис вместо чтения этой статьи стоит заняться более серьезными делами. А далее снова собрание слов для тех, кто готов развлечься и поиграть серьезными игрушками Рад Вас приветствовать...


А еще я очень рад обсуждению, состоявшемуся после предыдущей статьи. И тема психологии, которая была затронута в комментариях, еще не совсем раскрытая в самих статьях, была там очень уместна. А потому предложение, сформулированное ранее, по-прежнему в силе. В комментариях можно и размещать критику, и формулировать ответы на вопросы, задаваемые в статье, и вместе с тем очень полезно задавать новые вопросы. Что опять стало странным? Где было непонятно? Будут приветствоваться совершенно любые вопросы по теме статьи.


Потому что любое обсуждение этой темы полезно. И позволяет ускорить движение к цели.


Наша цель по-прежнему найти способы синтеза алгоритмов без участия человека (формализовать и автоматизировать этот процесс).


И давайте обсуждать вместе. Ведь это "жжж" рядом с заголовком "Что такое алгоритм ?!" неспроста.


А зачем тебе жужжать, если ты не пчела? По-моему, так

А зачем на свете пчелы? Для того, чтобы делать мёд.

Жжж


И, конечно, "алгоритмический" мёд не очень интересен сказочному медвежонку, но, возможно, он станет полезен кому-то другому.


Слово


Пора уже начать. И в этот раз совсем нет причин изменять привычному для этой серии статей формату. Мы опять будем "терзать" общепринятые термины из Википедии. На нашем пути термин "Слово":


Слово одна из основных структурных единиц языка, которая служит для именования предметов, их качеств и характеристик, их взаимодействий, а также именования мнимых и отвлечённых понятий, создаваемых человеческим воображением.

Как ни странно, но представленное определение весьма "крепкое", хоть пока и не формальное. А для выполнения его формализации необходима лишь малость. Нужно:


  • формализовать алгоритм именования предметов, взаимодействий и их качеств (характеристик);
  • понять и определить, что есть "мнимое и отвлеченное понятие", и затем формализовать алгоритм создания этой абстракции в человеческом воображении.

Это совсем не так много, как, например, для термина "Язык", с которым понадобится существенно больше шагов формализации. Но как подступиться к этим двум шагам, которые перечисленны выше? Тут нужен способ. И к нашему счастью его не нужно изобретать на ровном месте. Ведь он у нас уже есть. Благодаря нашей совместной работе в предыдущих статьях сформировано несколько полезных в требуемых формализациях и главное рабочих подходов. Первым нам понадобится способ изучения эволюционного развития алгоритма. А вторым, но только по порядку, а не по важности, способом является использование помощи от процессов переноса и трансляции.


Мы начнем с формализации алгоритма "именования предметов", который является хоть и сложным, но все же алгоритмом поведения. Как мы помним по статье "Эволюция поведения", основой для любого алгоритма поведения организма всегда является множество "базовой стратегий", обеспечивающих выживание вида рассматриваемого организма. Очень хорошо, что в текущей статье нет необходимости приводить весь путь эволюции алгоритма "именования предметов" от "базовых стратегий". И это стало возможно потому, что к искомому алгоритму "именования предметов" есть короткий эволюционный мостик от алгоритма выявления признаков в памяти, который был рассмотрен в статье "Эволюция памяти", где мы уже проследили важные ранние этапы эволюции организма. В дополнение к ним для завершающих этапов эволюции к алгоритму "именования предметов" мы используем ту вишенку, которая лежала на каждом кусочке торта поведения, "испеченного" в уже упомянутой статье "Эволюция поведения". Этой "вишенкой" (напомню для тех, кто не обратил на неё внимания) стала Коммуникация. Да, очень похоже, что без коммуникации в нашем мире не было бы и алгоритма "именования предметов". Но это, конечно, не точно. И сослагательное наклонение тут опять неуместно.


мультфильм головоломка


Коммуникация


Так что такого исключительного есть в коммуникации? Изначально представляется, что коммуникация без содержащих смысл "Слов" вообще невозможна. И что автор тут перепутал местами причину и следствие. Но давайте не будем торопиться в этом вопросе.


Для синтеза алгоритма индивидуального поведения организма "Слово" не является таким уж необходимым. Но ограничение поведения только одним исполнителем слишком сильно и даже жестоко для живых организмов. И потому это ограничение совсем неприемлемо в нашей работе. Особенно когда вокруг нас такое множество примеров алгоритмов поведения коллективного. А вот для коллективных алгоритмов, которые исполняются несколькими особями совместно, "Слово" уже является обязательным и самым простым способом обеспечить "последовательность действий", а потому саму возможность коллективного исполнения. Да, это еще не то "Слово", определение которого указано в Википедии. Это "Протослово". Оно скорее уникальный синхронизирующий сигнал. И в этом сигнале (например, извещающем сородичей о приближающейся опасности) важна уникальность.


Ибо именно уникальность и коллективная идентифицируемость это первооснова любого "Слова"? Если бы сигнал "Опасность" и сигнал "Вкусная еда" были бы одинаковы, то алгоритмы избегания стадом хищника работали бы совсем неудовлетворительно, и в результате исчезло бы и стадо, а вместе со стадом и выполняемые им алгоритмы.


Что мы здесь нащупали? Правильно. Это же основа для эволюционного развития. Алгоритм выработки уникального сигнала это алгоритмическая база для эволюции и путь к алгоритму "идентификации предметов". Полный путь формирования этого алгоритма предлагаю вынести в комментарии, а один из примеров подробного разбора необходимых эволюционных шагов уже описан в старенькой статье "Как в языке сформировать существительное?". Думаю, что после уже выпущенных статей серии "Что такое алгоритм?!" эту старую статью будет читать чуть проще.


Итак, есть мостик от алгоритмов коллективного выживания к алгоритмам "именования предметов". Почему здесь использовано слово "алгоритмам" во множественном числе? Это связано с разной природой "именуемых" явлений. И совсем немного этому посвящена пара статей "Математика" и "Физика". Там намеренно отдельно в каждой статье рассмотрены особенности "переноса" в пространство модели для "стада коров" и для "падения яблока".


Ведь по сути своей процесс "именования предметов" полностью тождественен "математической" замене живой коровы на камешек. И от этого процесса именования очень сильно отличается процесс "именования взаимодействий", являющийся иначе названной трансляцией "физического алгоритма". Да, о разнице этих процессов-алгоритмов свидетельствует само существование двух отдельных наук Физика и Математика. А еще подтверждение этому факту раздельное существование в естественном языке Существительных и Глаголов. И, да, конечно, в естественном языке есть еще прилагательные, наречия и другие части речи. И, конечно, алгоритмы их создания, то есть "именования", различны. Но общая суть такого создания уже ясна. А рассмотрение деталей конкретных реализаций оставим для отдельных и более обстоятельных статей вне этой серии.


Именование трансляцией


Абстракция


И нам уже пора переходить к рассмотрению эволюционного пути к странной формализации ("мнимого и отвлеченного понятия"). Для нас в этом процессе есть на что опереться, и с этой опорой сделать требуемое будет даже чуть проще, чем выполнить очерчивание остова предыдущей формализации "именования". А всё потому, что этот путь уже был нами рассмотрен. Вы же помните "мнимую единицу", упоминаемую в статье "Математика"? Да, это хороший пример абстрактного понятия, которое появляется благодаря использованию ограничения в переносе между прикладной областью и пространством модели, и оно появляется в математике благодаря возможностям трансляции. И яснее всего, что это понятие "чистая абстракция" (синоним "мнимому и отвлеченному"), видно как раз на примерах "очень" абстрактной математики, которые уже рассмотрены в соответствующей статье. По тождественному принципу "мнимые понятия" работают и во всех других областях научного и даже повседневного человеческого общения. Даже если эта абстракция слово "Мода". Очень интересная вышла бы статья посвященная её формализации, но пока её написание отложим.


Надеюсь, эволюционные пути к алгоритмам, используемым в определении "Слова", уже немного просматриваются. Для желающих полный разбор их развития можно будет провести в комментариях. А сейчас попробуем двигаться дальше.


И тут необходимо сказать, что на самом деле перенос и трансляция необходимы не только при появлении "чисто абстрактного" понятия. И в ранее рассмотренном алгоритме "именования" тоже скрыто используется эта пара. И она там тоже нужна потому, что формализуемый алгоритм создания "Слова" строится на особом процессе. Важность этого процесса отмечалась не единожды. И в виде принципа "Абстрагирования" в технологии ООП. И в психологии, например, почти век назад швейцарским психологом и философом Ж.Пиаже, который описал этот важный процесс, как "перенос" человеком действий, выполняющихся на объектах (среды), в психологические "операции", выполняющиеся уже на "символах" (термины в этом предложении взяты из работы Ж.Пиаже). Всё это подводит нас к необходимости закрепить за "Словом" еще одну функцию. Напомню, что первой функцией была уникальность и коллективная идентифицируемость. Вторая функция это абстрагирование. И это абстрагирование наследуется "Словом" от уже упомянутого ранее алгоритма выделения "признаков", который исполняется с использованием трансляции событий среды в структуры памяти организма.


Итак, на основе рассмотренного ранее мы с Вами уже можем еще неформально, но в формальных терминах (отмеченных курсивом) теоретической части этой работы, сформулировать своё определение "Слова":


Слово это объект в пространстве языковой алгоритмической системы, который:
  • формируется с использованием модельной трансляции (например, человеком) комплементарным сопоставлением с объектом или алгоритмом из прикладной области;
  • создается как продукт работы алгоритмов в языковом пространстве, необходимый для алгоритмов, исполняемых только внутри языкового пространства, и не переносимый в прикладную область.

Ведь очень похоже на определение из Вики? Неясными остаются только несколько терминов, которые ещё не были упомянуты в предыдущих статьях. Но все эти термины уже есть в теоретической части. И самый интересный из них "Алгоритмическая система". В определении "Слова" мимоходом констатируется, что "Язык" это один из видов "Алгоритмической системы". К сожалению, в текущей статье этот комплексный термин не уместить, а потому пока оставим его для статей последующих.


Копирование поведения


Нет, здесь совсем не случайно появились иллюстрации к повести "Собачье сердце". Они отсылка к одной из самых важных предыдущих статей. Нам опять необходимо рассмотреть ключевой коллективный алгоритм. Один из многих, что доступны человеку. И тем необходимым образцом коллективного алгоритма является "Алгоритм копирования поведения", которому как раз была посвящена статья "Копирование иерархии памяти". Да, это алгоритм копирования алгоритма. Да, это "алгоритм обучения". Да, это эволюционный путь от уникальных и идентифицируемых сигналов к осмысленному слову. И "Протослова", зародившиеся в процессе выполнения коллективных алгоритмов выживания и послужившие для синхронизации их исполнения, должны были специализироваться, чтобы их можно было использовать в таких алгоритмах обучения. И когда эта специализация с использованием эволюционного развития была проведена, они стали тем, что может нести "Смысл". Стали "Словами" из определения Википедии. Попробуем дальше разобраться, где "прячется", а потому так долго ускользает от математиков "Смысл" в уже существующих и во вновь придумываемых человеком словах.


Смысл


Здесь можно немного поумничать и для поощрения дальнейшей непростой работы при чтении остатка текущей статьи легко ответить на вопрос "Что есть смысл жизни?" простой фразой: "Смысл любого слова кроется в алгоритмах, в которых это слово используется". Конечно, этот ответ, не подкрепленный объяснениями, совсем как красная ткань в корриде. И потому уместно использовать это и напасть на этот ответ в комментариях. Но объяснения к этой фразе уже есть. Они основаны на предлагаемом в этой серии статей значении для слова "Алгоритм", которое еще не настолько общепринято, чтобы его можно было использовать совсем без примеров.


Question of life


$\begin{gathered}42 \\ = \\ (-80538738812075974)^3 + \\ 80435758145817515^3 + \\ 12602123297335631^3\end{gathered}$



И если в текущий момент статьи сделать паузу и обратить внимание на предыдущее предложение, а еще сразу на все статьи серии. То можно отметить, что цель всего этого собрания слов в статьях только одна объяснить особый смысл слова "Алгоритм". А способ это осуществить используется такой же, как и для всех иных слов, с которыми сталкивается человек. Начиная с первого слова "мама" и заканчивая "большим адронным коллайдером" (да, конечно, тут 3 слова, но ведь смысл один?). И этот способ связывания слова со смыслом спрятан достаточно глубоко в устройстве человеческого мышления, а вернее в приемах используемых в процессе синтеза алгоритмов его поведения.


И сложность поиска этого смысла в математических трудах объясняется на самом деле очень просто. Эта сложность в том, что пути к смыслу "Слова" есть два. И Математика ранее шла только по одному из них и потому находила только часть "смысла", а второй дороги не замечала.


Так что же это за первый "математический" путь? Этот путь к "смыслу" идет через классификацию, то есть через наблюдение человеком за объектами и процессами окружающей нас среды, выявлении и разграничении повторяющихся (статистически значимых или "алгоритмичных") явлений в обозреваемой прикладной области. Результатом такой классификации являются все наши словари. И с такой лингвистической классификацией-калибровкой пока менее эффективно чем человек, но уже вполне успешно справляется современная технология многослойных нейронных сетей. Ведь изначально они разрабатывались как способ иерархической классификации на опорных наборах данных существенного объема.


Но есть ли в этом "классифицирующем" пути к "Слову" способ понять его смысл? Ответ будет да. Но как уже было сказано, это будет только часть смысла. И эта часть отнюдь не самая важная. И эта часть уже есть в текущем определении слова "Алгоритм", размещаемом на Википедии. И она помогает нам отличить "Алгоритм", например, от "Логарифма" потому, что при работе с алгоритмом мы не всегда думаем о "показателе степени, в которую надо возвести основание". Но эта часть совсем не помогает нам работать с Алгоритмом! Обобщая можно подчеркнуть: путь разбиения явлений среды на кластеры, каждый из которых подписан уникальным словом дает нам представление только о части смысла, содержащегося в этих уникальных словах.


А вторая и самая важная часть смысла "Слова" содержится в тех алгоритмах и процессах, в которых это "Слово" используются с пользой для человека. Для этой пользы "Слова" создаются. Для этой пользы алгоритмы иерархической кластеризации, выделяющие наши "Слова", существуют. Для этой пользы наши дети поколение за поколением учатся говорить, читать, писать и изъясняться.


Главный смысл "Слова" содержится в полезных алгоритмах, которые используют это слово! Да, смысл совсем не внутри фонем, букв, слогов "слова" и не в способах именовать объекты окружающего пространства. Этот смысл "снаружи"! Он в использовании. Он в алгоритмах!!!


Тут будет уместно сбавить градус повествования и в качестве интересного факта подчеркнуть, что для слов, представляющих собой "мнимые и отвлеченные понятия" особенно из математики (например, для "мнимой единицы"), баланс между "классифицирующим" смыслом и смыслом "использования" нарушен изначально. Алгоритмы идентифицирующие "мнимую единицу" (классифицирующий смысл) намеренно делаются очень простыми и сводятся к распознаванию одной буквы $i$, которая почти ничего не значит, но только "существует" в математическом выражении. А вот "использующий смысл" очень насыщенный. Это и алгоритмы решения кубического уравнения, и комплексные числа, и далее согласно Википедии использование: в обработке сигналов, теории управления, электромагнетизме, теории колебаний, теории упругости, картографии и гидродинамике и даже в квантовой механике.


Для "гуманитарных" слов порой отсутствует "использующий смысл". И слово становится просто интересным алгоритмом, необходимым только для развлечения того кто его слышит. Вспомним для примера "Антиподов" упомянутых Алисой в предыдущей статье. До появления использования этого слова в статье "Физика" оно было просто развлечением и игрой Льюиса Кэрролла (или даже скорее переводчика его сказки на русский язык). Это было ироничное издевательство над алгоритмом "именования" с копированием того, как в своих играх этот алгоритм изучают дети. Но ситуация поменялась с появлением текущей статьи. В ней появилось новое использование слова "Антипод" в качестве лингвистического примера. И у этого "Слова" появился новый смысл! Ведь это совсем неожиданный для математика процесс?


Алиса и Аня


При обращении к примеру сказочной Алисы Льюиса Кэрролла был вскользь упомянут один сложный процесс, который целиком основан на смысле "Слова" и потому долгое время остаётся не до конца подвластен Математике. И этот процесс не может происходить только на основе "классифицирующего смысла". И он нуждается в "смысле использования". Вы уже догадались? Да, этот процесс работа переводчика, нацеленная на перенос смысла "Слова" из одного языка в язык другой.


Перевод


Вы можете сказать, что перевод на другой язык для отдельного "Слова" не очень интересен. И он с успехом происходит во всех двуязычных словарях. И будете, конечно, правы. Но для перехода к несомненно более сложному переводу на другой язык сложной совокупности "Слов", которой является "Предложение" и "Текст", ведь необходимо формализовать что есть "Предложение" и "Текст"? И, конечно, это тоже нам будет необходимо сделать, но чуть позже. А сейчас полезным является даже рассмотрение более "простого" варианта. И это ограничение только одним "Словом" своим упрощением даже является нам помощью. Потому что причины сложности перевода с одного языка на другой во многом обусловлены смыслом, который несёт каждое "Слово".


Думаю сейчас опять стоит обратиться к Википедии. И разобрать следующий существующий на её страницах термин слово "Перевод":


Перевод деятельность по интерпретации смысла текста на одном языке (исходном языке) и созданию нового эквивалентного ему текста на другом языке (переводящем языке).

Итак, необходимо создать эквивалентный смысл? А перед этим интерпретировать смысл, уже существующий? А какой смысл необходимо перенести с одного языка на другой? "Смысл классифицирующий" или "смысл использования"? И так ли этот процесс является переносом, а может быть все же он трансляция с сопутствующими ей ограничениями?


О, опять целый рой вопросов. Как бы уложиться с ответами в последнем разделе приемлемого объема. Хорошо, что у нас уже есть опора для формирования ответов. И здесь нам очень поможет разбиение на "классифицирующий смысл" и "смысл использования".


Мы уже можем сказать, что создание эквивалентного смысла требует наличия в разных языках одинакового классифицирующего разбиения действительности на слова. Наверно, тем, кому "посчастливилось" поработать со сложными классификаторами в базах данных, не нужно объяснять, в чём сложность любой классификации. Они понимают, какая это "боль" вносить в кропотливо разработанную базу такие разные реальные данные. И даже наличие такого привычного нам поля "Отчество" встречается с большими трудностями при регистрации иностранца. А парные имена? И это только самая простая база данных. Если классификация посложнее, то шансов уложить в таблицы все необходимые реальные данные почти призрачны.


Почему так? Где прячется сложность? Почему каждый естественный язык по-своему именует явления нашего мира? Почему в текущее время в английском языке нет отчества? Ответ на все эти вопросы один, и он на самом деле тоже прост. Всё это объясняется разным путём эволюционного развития каждого естественного языка, которое основано на разном развитии в разных условиях алгоритмов поведения для людей, отделенных некоторыми разграничивающими их места обитания барьерами. Это сходно с особенностями обособленного эволюционного развития подвидов организмов, например, сводящееся к различию цвета оперения или размеру ушей. А детали различий такого эволюционного развития для каждой языковой пары вполне подойдут в объеме и ценности работы для крепкой диссертации по лингвистике. Некоторые примеры для которой можно обсудить в комментариях.


Да, сложно перенести "классифицирующий смысл". Особенно для редких и отличающихся явлений. И в этом процессе переводчик должен знать, как классифицируется переносимое явление в исходном языке, должен знать, как оно классифицируется в переводящем языке, и необходимо его желание и умение сопоставить для разных языков одно и то же явление. И хорошо, если переводчик знает, какое явление имел в виду автор исходного текста. Ведь иначе у переводчика неизбежно появится "Кузинатра", которая будет ставить в тупик непонимания каждого читателя его перевода.


Кузинатра


Но сложности переноса "классифицирующего смысла" ничтожны по сравнению со сложностями, появляющимися при переносе "смысла использования" в тот момент, когда в переводящем языке нет сходного способа употребления явления, которое необходимо упомянуть. Давайте сразу перейдем к крайности. Попробуем перевести что-нибудь почти лишенное "классифицирующего смысла". Ну, например, "мнимую единицу" на язык племени Амондава. Да, это заведомо обреченное на провал дело. Хотя, нет. Провала можно избежать. Просто необходимо как-то "заставить" носителей редкого языка понять, как можно использовать выбранный нами термин. То есть необходимо обосновать им необходимость и полезность решения кубических уравнений. Далее будет уже проще. Но без этого шага перевод будет абсолютно невозможен. Без существования алгоритма решения кубического уравнения у носителей языка невозможно выполнить перевод термина "мнимая единица" на этот язык. Поэтому в таком случае перевод сравним по сложности с процессом обучения решению кубического уравнения. И, конечно, наши компьютерные онлайн переводчики пока на это не способны.


Но если ты сам знаешь, как это работает внутри, и ты можешь "научить" компьютер алгоритмам такого использования. Ведь тогда можно совсем как переводчик своё знание смысла перевести на язык "программирования"?


Да, это тема следующих статей. И там нас ждут интересные и чуть мене сложные эволюционные пути, пройденные при построении человеком формальных языков. И там мы, наконец, разберем, чем же на самом деле является программирование. Теперь осталось к этой рекламе будущих статей найти время среди текущих забот. А будет время соберутся и нужные слова под заголовком следующей публикации.


Выводы


Мы подошли к выводам, и, значит, опять нужно вознаградить себя за проделанную в чтении текущей статьи работу. Пусть даже наградой будет лишь похвала с перечислением значимых свершений.


Мы познакомились с термином "Слово" и нашли в нём два смысла, один из которых будет весьма полезным в доработке существующих систем перевода текстов на естественных языках.


Было обозначено два эволюционных пути от стратегий выживания организмов к алгоритмам работы со "Словами".


Намечен эволюционный переход от естественных языков к языкам программирования.


В рамках формализации "Слова" упомянуто несколько важных терминов из теоретической части работы, которые потребуются в последующих статьях.


Да, тут необходимо отметить, что все обозначенные в этой статье формализации очень "неформальны". И в них намеренно только обозначены опорные и ключевые моменты. А потому, конечно, пропущено много деталей, и их необходимо заполнить. И только частично это заполнение будет проводиться в оставшемся небольшом количестве статей этой серии. И только самые важные пояснения появятся на Хабре в автономных статьях. А до полного их объяснения в разрабатываемой теоретической книге пройдет еще очень много времени потому, что эта работа совсем не является сейчас самой приоритетной. И потому "атаку на пробелы" и их заполнение целесообразно разместить здесь (на Хабре). Удобной платформой для этого будет площадка комментариев и к этой, и к предыдущим статьям. И это заполнение будет идти только по Вашим запросам. Ибо пробелы, которые очень хорошо видны Вам, мне не видны совершенно. Отчасти поэтому в статьях появляются "висящие" термины. И в написании каждой новой статьи самым сложным становится процесс вспоминания: какие факты уже были рассказаны, а какие еще остались такими "пробелами". Ведь полный путь от "рождения" алгоритма к способам работы нашего мозга всё же достаточно сложен. И держать его целиком в памяти, к сожалению, никогда не будет мне посильно.


Спасибо Вам за внимание.


Отзывы


Буду очень благодарен за отзывы, пожелания и предложения, так как они помогают мне скорректировать направление развития работы.


Отдельное волнение у меня есть по стилю повествования и форматированию, используемым в статье (кавычки, абзацы, курсив). Напишите, пожалуйста, если у Вас есть замечания к ним. Можно личным сообщением.


Ссылки


Подробнее..

Распознавание команд

03.06.2021 20:19:56 | Автор: admin

При разработке ботов для Telegram и других месенджеров, периодически возникает задача распознавания и выполнения запросов, высказанных человеческим языком. Именно эта "фишка", по некоторому мнению, и является главным отличием ботов от приложений командной строки. Под катом описан собственный фреймворк для исполнения произвольных речевых команд. Описания ключевых концепций сопровождены примерами на языке Kotlin.

За основу для распознания речи возьмем нормализованные семантические представления. Их выбор обусловлен прежде всего простотой и легкостью реализации. Начнем с базиса, пример из исходников фреймворка:

/** Правило проверяет лексему на соответствие */typealias Rule = (String) -> Boolean/** Нормализованное семантическое представление */open class Semnorm(vararg val rules: Rule)/** Правило задает стемы для семантических представлений */fun stem(vararg stems: String): Rule = { stems.any(it::startsWith) }/** Правило задает точные соответствия для семантических представлений */fun word(vararg words: String): Rule = { words.any(it::equals) }/** Проверяем слово на соответствие семантике */fun String.matches(norm: Semnorm) = norm.rules.any { it(this) }

Теперь у нас появилась возможность задавать предопределенные нормализованные семантические представления в виде объектов:

object Day : Semnorm(stem("day", "суток", "сутк", "дня", "ден", "дне"))

Фреймворк ставит их в соответствие лексемам входящих фраз, и предложение начинает выглядеть, например так:

assertThat(  "забань васю на 5 минут".tokenize(),   equalTo(   listOf(     Token("забань", Ban),      Token("васю", null),     Token("на", null),      Token("5", Number),     Token("минут", Minute)   )  ))

С распознаванием речи мы разобрались. Код токенизатора приложен в репозитории, доступном в конце статьи. Перейдем к исполнению команд из речи. А вот здесь и начинается самое интересное: фреймворк позволяет для каждого семантического представления навесить заданное поведение. Снова простейший пример, как распознать запрос справки на двух языках:

object Help : ExecutableSemnorm(stem(  "помощ", "справк", "правил", "help",   "rule", "faq", "start", "старт",)) {  override fun execute(bot: Botm: Message) {    val faq = message.from.relatedFaq()    bot.sendMessage(m.chat.id, faq)  }}

Что насчет более сложного поведения, зависящего от различных слов в предложении? Оно тоже поддерживается, вот как, например, исполняется, уже известное из тестов предложение забанить Васю:

object Ban : DurableSemonrm(stem(  "ban", "block", "mute", "бан", "блок",  "забан", "завали", "замьют",)) {  override fun execute(    bot: Bot, attackerMessage: Message, duration: Duration) {    val victimMessage = attackerMessage.replyToMessage    val victimId = victimMessage.from.id    val untilSecond = now().epochSecond + duration.inWholeSeconds    bot.restrictChatMember(      attackerMessage.chat.id, victimId, untilSecond)  }}

Откуда это семантическое представление знает о своей продолжительности? Дело в том, что ему вовсе не обязательно парсить всю цепочку токенов целиком. Достаточно задать только минимально необходимое поведение для каждого представления, например для времени:

object Week : Semnorm(stem("week", "недел")) {  override fun toDuration(number: Long) =     days(number) * 7}

Или для любых команд, зависящих от времени:

class DurableSemnorm(vararg rules: Rule) : ExecutableSemnorm(*rules) {  final override fun execute(    token: Iterator<Token>, bot: Bot, m: Message) =       execute(bot, message, token.parseDuration())  abstract fun execute(bot: Bot, m: Message, duration: Duration)}

Благодаря такой архитектуре, нам больше не приходится думать о запутанной логике работы интерпретатора. Достаточно просто определить желаемые атрибуты для семантических представлений и наслаждаться результатом. Пример бота, использующего эту концепцию, можно посмотреть на Github: https://github.com/demidko/timecobot

Подробнее..

Честные глаза плагиатора, или еще один взгляд на будущее систем обнаружения заимствований

01.04.2021 10:10:31 | Автор: admin

Развивать систему, созданную 16 лет назад, конечно, не подвиг, но вообще что-то героическое в этом есть (с). От пользователей регулярно прилетают вопросы: что будете делать дальше? Каким будет Антиплагиат через несколько лет? Все правильно, все верно нельзя позволять рутине себя засасывать настолько, чтобы не оставалось времени подумать о далеком, о жестоком, ну вы поняли о будущем.


Действительно, начало весны (отчетность закончилась, а сессия еще не началась) самое удобное время для стратегических планов. Ну а заодно и для удовлетворения любопытства наших пользователей.


Не могу сказать, что описываю совсем уж ближайшее будущее. Какие-то идеи пока находятся в обработке у наших исследователей, какие-то и вовсе пока еще варятся в головах. Но тем не менее, описанный ниже сценарий развития системы Антиплагиат сейчас наиболее вероятен.
Картинку даю, слегка опережая события. Она имеет непосредственное отношение к теме статьи, но, чтобы обо всем рассказать, нужно чуть больше места.



Кадр из а/ф Шрек (англ. Shrek), DreamWorks Pictures, 2001 год



Нам хотелось бы решить проблему подстройки пользователей под систему если не раз и навсегда, то, по крайней мере, надолго. Нужно сделать так, чтобы мы были готовы к любым новым хитростям со стороны тех, кому потенциально интересно выдать чужой текст за свой. Для того, чтобы изложить суть моей идеи, нужно для начала изложить три предпосылки.


Предпосылка первая


Любая контролирующая система будет оставаться эффективной только в случае непрерывного развития. Должно быть что-то, в чем она опережает тех, кто пытается ее обмануть. Иначе окружающая среда, мотивированная на преодоление системы Антиплагиат, сожрет ее с потрохами найдет слабые места и придумает, как эти слабые места эксплуатировать. Информация и о том, и о другом становится достоянием общественности очень быстро.


До определенной степени можно продержаться, используя принцип Security through obscurity, то есть скрывая архитектуру и детали алгоритмов. Но, как показывает практика, принцип хорошо работает в том случае, если вы никому_не_нужный_неуловимый_Джо. Если же (как в нашем случае) систему ежедневно используют десятки или сотни тысяч пользователей, то ваша таинственность идет вам, как Соеву пенсе помогает очень ограничено.


Предпосылка вторая


Если рассматривать обнаружение заимствований как задачу информационного поиска, то не покидает ощущение легкой нечестности происходящего. В классическом поиске субъект (т.е. пользователь) заинтересован в получении хорошего результата. Он может не иметь нужного навыка, но уж если он ищет, допустим, пластиковые окна или рефераты по экономике, то рассчитывает получить максимально полезные для себя результаты. В ситуации с заимствованиями же можно уверенно говорить, что большая часть пользователей хотела бы, чтобы вне зависимости от объективной реальности не было найденоничего. И очень многие из этих пользователей готовы прикладывать те или иные усилия, чтобы приблизиться к такому идеальному результату. Перефразировка, перевод, скрытый текст, незаметные пробелы, встроенные объекты, миллион вариантов повышения оригинальности. Естественно, мы совершенствуем систему и чем дальше идем в этом направлении, тем сложнее становится именно повышать. Но между изобретением нового способа улучшения результата и изменением в системе (будь то хотфикс или новая функциональность) всегда проходит время и иногда достаточно большое.


Предпосылка третья


Качество работы поиска это всегда баланс. Во-первых, это баланс между тем, чтобы не сигнализировать о проблемах тогда, когда их нет, и тем, чтобы упустить явное заимствование, когда оно есть. Как говорится, и овцы целы, и волки сыты, что бы это ни значило. Во-вторых, это баланс затрачиваемых ресурсов на обработку документа. Мы не должны потратить слишком много (времени, процессорного времени, памяти, трафика), если нет дополнительных предпосылок к тому, что нужно поискать повнимательнее. Дополнительные ресурсы лягут на пользователей увеличением стоимости использования сервиса, а деньги все считать умеют.


А теперь, собственно, Идея


Идея, если коротко, состоит в следующем: использовать априорную информацию об учебной работе, чтобы помочь системе Антиплагиат выбрать нужную степень паранойи при анализе конкретной работы.


То есть, если система будет знать, что к той или иной работе нужно присмотреться повнимательнее, то это позволит анализировать зону риска более тщательно, не затрачивая ресурсы на проверку других работ.



Кадр из к/ф Место встречи изменить нельзя, Одесская киностудия, 1979 год


Иначе говоря, нужен предсказательный сервис подозрительности проверяемых работ.
Какими способами можно предсказывать? Предлагаем несколько вариантов.


Способ первый спросить преподавателя


В системе Антиплагиат.ВУЗ преподаватель при создании задания сразу может отметить тех студентов, чьим работам следует уделить внимание. Примерно так:



Источник картинки: компания Антиплагиат


У этого способа есть несколько особенностей. Во-первых, очевидно, что преподаватели не будут использовать его массово. Слишком много отклонений от простого варианта: Next, Next, Next, Finish.


Во-вторых, этот способ может нести риски стимулирования коррупции. Для нейтрализации этого риска необходимо будет использовать перекрестный контроль.


Наконец, в-третьих, накапливаемые данные можно будет использовать для обучения классификатора. Чтобы преподаватели не жестили, будет введено ограничение на количество и долю студентов, которых можно подозревать в рамках одного задания.


Способ второй API


Второй способ является упрощенным вариантом первого. Нужно отдать во внешние системы возможность управления уровнем паранойи. Типичная внешняя система СДО Moodle, для которой уже есть сертифицированная интеграция с Антиплагиат.ВУЗ.


Реализация этого способа проста до невозможности. Расширяем параметр CheckDocParams метода CheckDocument:



параметром паранойи системы:



Остальное остается на откуп партнерам, которые реализовали интеграцию.


В чем проблема этих двух способов? В том, что они не работают без человека, который может быть предвзят, которому может быть просто лень или некогда, который, наконец, может просто ошибаться.


Конечно, можно было бы организовать предсказательную систему так, как, например, было предложено в фильме Особое мнение.



Кадр из к/ф Особое мнение (англ. Minority Report), 20th Century Fox, DreamWorks Pictures, Amblin Entertainment, Blue Tulip Productions, 2002 год


Но до тех пор, пока наш HR занимается заполнением открывшихся вакансий провидцев, нам придется полагаться на возможности AI.


Способ третий искусственный интеллект


Итак, о третьем способе на основе машинного обучения. Нужно оценить априорную склонность студента к использованию чужого текста. В современных условиях всеохватывающей дистанционки этот способ отлично работает в условиях видео- и аудиопотока. Каждый студент так или иначе взаимодействует со своим вузом онлайн. Самый распространенный вариант это Zoom. Звучит слегка безумно, но нам нужно оценить шансы на то, что студент прибегает к плагиату, посредством видеочата с этим самым студентом.


Первым для оценки склонности к плагиату будет использован анализ движения зрачков студента на онлайн занятиях. По этой теме, есть как научные работы с анализом методов извлечения и анализа траектории движения зрачка (например, Christian Hirt et al, Maria K. Eckstein et al, Yujin Jung et al), так и готовые библиотеки (тот же PyGaze), которые можно использовать для промышленных задач.


Таким образом, перед нами обычная задача классификации с двумя классами. Этапы решения задачи вполне традиционные:


  1. Предобработка видеопотока
  2. Получение трека движения взгляда
  3. Выделение признаков
  4. Решение задачи классификации

Примечательно то, что данные для обучения классификатора можно собирать на действующей системе: нам достаточно сделать программного агента, собирающего признаки через Zoom API, после чего сопоставить результаты сбора признаков с результатами проверки документов этого пользователя в режиме максимальной паранойи.


Дополнительно планируется использовать данные:


  • о положении тела (движения головы, рук, позы),
  • мимике,
  • наличии в кадре посторонних объектов (графин с водой, булочка),
  • речи (в тех случаях, когда у студента включен микрофон).

Проведенные нами предварительные эксперименты показывают, что можно рассчитывать на достаточно высокое качество решения задачи. Оценочно количество работ, которые отклоняются из-за плагиата, может вырасти на несколько процентов. Такой вклад существенно превышает разовое влияние на качество поиска других способов.


У использования такого метода, конечно, есть свои нюансы. Первый это возможность не включать камеру на занятия, ссылаясь на различные обстоятельства: отсутствует/сломалась камера, не убрана комната, небритое лицо, шастающий кот. Но уже сейчас во многих учебных заведениях включение камеры является обязательным, и при выключенной камере может быть просто засчитана неявка на занятие.


Вторая это использование студентами различных техник, позволяющих обмануть AI по примеру обмана полиграфа (детектора лжи). Здесь важны два момента: обманывать полиграф на потоке могут разве что тренированные сотрудники спецслужб; в нашей ситуации будет трудно разработать типичные паттерны обмана и обучиться, так как результаты оценивания скрыты внутри сервиса и студенту не предъявляются.



Кадр из фильма День, когда Земля остановилась (англ. The Day the Earth Stood Still), 20th Century Fox, 2008 год


Кроме того, есть еще один важный фактор. Сначала технология априорного оценивания склонности к плагиату будет применяться для выделения зоны риска, и по умолчанию уровень чувствительности алгоритма проверки будет сохраняться на обычном уровне. Но с определенного момента уровень по умолчанию будет изменен на параноидальный, а вот положительные оценки технологии априорного оценивания станут основанием для использования обычного поискового алгоритма.


Но и это еще не все. Сейчас обсуждаются идеи по обогащению широко распространенных стилометрических методов поиска плагиата (см. например, методы выявления внутреннего плагиата и авторской диаризации) информацией, снимаемой при наборе текста.


Наконец, самый серьезный шаг, который, вероятно, решит проблему комплексно это предварительное тестирование общей склонности студента к обману. Само собой, что это сложный и ответственный шаг, к которому нужно подходить с особой подготовкой. Мы ожидаем, что подготовка к данному этапу займет два-три года. Но уже сейчас очевидно, что он неизбежен.



Кадр из фильма Матрица (англ. The Matrix), Warner Bros., Village Roadshow Pictures, 1999 год


Поверили?


Расслабьтесь! Шутим мы. Мы не планируем реализацию описанной выше технологии, а решили развлечь вас описанием не такого уж невозможного будущего, как, например, мы делали в прошлом году. Большое спасибо, что дочитали до этого момента. Интересно, удалось ли вам сохранить веру в описываемое до сих пор, или в какой-то момент подавление недоверия перестало работать? Напишите в комментариях.


Огромное спасибо коллегам, которые приняли участие в подготовке статьи: Олегу Бахтееву (Oleg_Bakhteev), Андрею Ивахненко (andyray), Александру Кильдякову (vainah76), Анастасии Чернышовой (chernnasty).


Всех с праздником! На всякий случай повторю, что реализация описанного метода не планируется.


Пока.

Подробнее..

Маленький и быстрый BERT для русского языка

10.06.2021 02:22:28 | Автор: admin

BERT нейросеть, способная весьма неплохо понимать смысл текстов на человеческом языке. Впервые появивишись в 2018 году, эта модель совершила переворот в компьютерной лингвистике. Базовая версия модели долго предобучается, читая миллионы текстов и постепенно осваивая язык, а потом её можно дообучить на собственной прикладной задаче, например, классификации комментариев или выделении в тексте имён, названий и адресов. Стандартная версия BERT довольно большая: весит больше 600 мегабайт, обрабатывает предложение около 120 миллисекунд (на CPU). В этом посте я предлагаю уменьшенную версию BERT для русского языка 45 мегабайт, 6 мс на предложение. Уже есть tinybert для английского от Хуавея, есть моя уменьшалка FastText'а, а вот маленький (англо-)русский BERT, кажется, появился впервые. Но насколько он хорош?

Дистилляция путь к маленькости

Чтобы создать маленький BERT, я решил обучить свою нейросеть, используя при этом уже готовые модели в качестве учителей. Сейчас объясню подробнее.

Если очень коротко, то BERT работает так: сначала токенизатор разбивает текст на токены (кусочки размером от одной буквы до целого слова), от них берутся эмбеддинги из таблицы, и эти эмбеддинги несколько раз обновляются, используя механизм self-attention для учёта контекста (соседних токенов). При предобучении классический BERT выполняет две задачи: угадывает, какие токены в предложении были заменены на специальный токен [MASK], и шли ли два предложения следом друг за другом в тексте. Как потом показали, вторая задача не очень нужна. Но токен [CLS], который ставится перед началом текста и эмбеддинг которого использовался для этой второй задаче, употреблять продолжают, и я тоже сделал на него ставку.

Дистилляция способ перекладывания знаний из одной модели в другую. Это быстрее, чем учить модель только на текстах. Например, в тексте [CLS] Ехал Грека [MASK] реку "верное" решение поставить на место маски токен через, но большая модель знает, что токены на, в, за в этом контексте тоже уместны, и это знание полезно для обучения маленькой модели. Его можно передать, заставляя маленькую модель не только предсказывать высокую вероятность правильного токена через, а воспроизводить всё вероятностное распределение возможных замаскированных токенов в данном тексте.

В качестве основы для модели я взял классический bert-multilingual (веса), ибо хочу, чтобы модель понимала и русский, и английский, и его же использую на ранних стадиях дистилляции как учителя по распределению токенов. Словарь этой модели содержит 120К токенов, но я отобрал только те, которые часто встречаются в русском и английском языках, оставив 30К. Размер эмбеддинга я сократил с 768 до 312, число слоёв с 12 до 3. Эмбеддинги я инициализировал из bert-multilingual, все остальные веса случайным образом.

Поскольку я собираюсь использовать маленький BERT в первую очередь для классификации коротких текстов, мне надо, чтобы он мог построить хорошее векторное представление предложения. Поэтому в качестве учителей для дистилляции я выбрал модели, которые с этим здорово справляются: RuBERT (статья, веса), LaBSE (статья, веса), Laser (статья, пакет) и USE (статья, код). А именно, я требую, чтобы [CLS] эмбеддинг моей модели позволял предсказать эмбеддинги предложений, полученные из этих трёх моделей. Дополнительно я обучаю модель на задачу translation ranking (как LaBSE). Наконец, я решил, что неплохо было бы уметь полностью расшифровывать предложение назад из CLS-эмбеддингов, причём делать это одинаково для русских и английских предложений как в Laser. Для этих целей я примотал изолентой к своей модели декодер от уменьшенного русского T5. Таким образом, у меня получилась многозадачная модель о восьми лоссах:

  • Обычное предсказание замаскированных токенов (я использую full word masks).

  • Translation ranking по рецепту LaBSE: эмбеддинг фразы на русском должен быть ближе к эмбеддингу её перевода на английский, чем к эмбеддингу остальных примеров в батче. Пробовал добавлять наивные hard negatives, но заметной пользы они не дали.

  • Дистилляция распределения всех токенов из bert-base-multilingual-cased (через несколько эпох я отключил её, т.к. она начала мешать).

  • Приближение CLS-эмбеддингов (после линейной проекции) к эмбеддингам DeepPavlov/rubert-base-cased-sentence (усреднённым по токенам).

  • Приближение CLS-эмбеддингов (после другой линейной проекции) к CLS-эмбеддингам LaBSE.

  • Приближение CLS-эмбеддингов (после третьей проекции) к эмбеддингам LASER.

  • Приближение CLS-эмбеддингов (после ещё одной проекции) к эмбеддингам USE.

  • Расшифровка декодером от T5 предложения (на русском) из последней проекции CLS-эмбеддинга.

Скорее всего, из этих лоссов больше половины можно было безболезненно выкинуть, но ресурсов на ablation study я пока не нашёл. Обучал я это всё в течении нескольких дней на Colab, по пути нащупывая learning rate и другие параметры. В общем, не очень научно, но дешево и результативно. В качестве обучающей выборки я взял три параллельных корпуса англо-русских предложений: от Яндекс.Переводчика, OPUS-100 и Tatoeba, суммарно 2.5 млн коротких текстов. Весь процесс создания модели, включая некоторые неудачные эксперименты, содержится в блокноте. Сама модель, названная мною rubert-tiny (или просто Энкодечка), выложена в репозитории Huggingface.

И как этим пользоваться?

Если у вас есть Python и установлены пакет transformers и sentencepiece, скачать и запустить модель просто. Например, вот так вы можете получить 312-мерный CLS-эмбеддинг предложения.

Этот эмбеддинг вы можете использовать как признак для любой модели классификации или кластеризации текстов. Как (рекомендуемый) вариант, вы можете дообучить мою модель на собственной задаче. Про это будет отдельный пост, а пока отсылаю вас к обучающи материалам от Huggingface (раз, два).

Насколько быстр и мал мой Энкодечка? Я сравил его с другими BERT'ами, понимающими русский язык. Скорость указана в расчёте на одно предложение из Лейпцигского веб-корпуса русского языка.

Модель

Скорость (CPU)

Скорость (GPU)

Вес на диске

cointegrated/rubert-tiny

6 мс

3 мс

45 мб

bert-base-multilingual-cased

125 мс

8 мс

680 мб

DeepPavlov/rubert-base-cased-sentence

110 мс

8 мс

680 мб

sentence-transformers/LaBSE

120 мс

8 мс

1.8 гб

sberbank-ai/sbert_large_nlu_ru

420 мс

16 мс

1.6 гб

Все расчёты я выполнял на Colab (Intel(R) Xeon(R) CPU @ 2.00GHz и Tesla P100-PCIE c батчом размера 1 (если использовать крупные батчи, то ускорение на GPU ещё заметнее, т.к. с маленькой моделью можно собрать более большой батч).

Как видим, rubert-tiny на CPU работает раз в 20 быстрее своих ближайших соседей, и легко помещается на бюджетные хостинги типа Heroku (и даже, наверное, на мобильные устройства). Надеюсь, эта модель сделает предобученные нейросети для русского языка в целом более доступными для прикладных применений. Но надо ещё убедиться, что модель хоть чему-то научилась.

Оценка качества эмбеддингов

Рекомендованный и проверенный временем рецепт использования BERT дообучать его на конечную задачу. Но дообучение процесс небыстрый и наукоёмкий, а гипотезу об осмысленности выученных эмбеддингов хочется проверить побыстрее и попроще. Поэтому я не дообучаю модели, а использую их как готовые feature extractors, и обучаю поверх их эмбеддингов простые модели логистическую регрессию и KNN.

Для оценки русских моделей есть бенчмарк RussianSuperGLUE, но задачи там сложные, требующие подбора параметров и дообучения моделей, так что к нему я тоже перейду позже. Ещё есть бенчмарк RuSentEval, там задачи более простые, но они слишком абстрактные, ориентированные больше на лингвистические свойства, а хочется начать с чего-то прикладного. Поэтому я собрал свой маленький бенчмарк. Пока что он тоже живёт в блокноте, но надо будет допилить его и выложить в более удобном виде. И вот какие задачи туда вошли:

STS: бенчмарк по семантической близости предложений (переведённый с английского). Например, фразы "Кошка спит на фиолетовой простыне" и "Черно-белый кот спит на фиолетовом одеяле" из этого датасета оценены на 4 из 5 баллов сходства. Качество моделей на нём я мерял ранговой корреляций этих баллов с косинусной близостью эмбеддингов предложений. Для наилучшей модели, LaBSE, корреляция оказалась 77%, для моей 65%, на одном уровне с моделью от Сбера, которая в 40 раз больше моей.

Paraphraser: похожий бенчмарк по семантической близости, но с чисто русскими новостными заголовками и трёхбалльной шкалой. Эту задачу я решил так: обучил поверх косинусных близостей логистическую регрессию, и ей предсказывал, к какому из трёх классов пара заголовков относится. На этом бенчмарке моя модель выдала точность 43% и победила все остальные (впрочем, с небольшим отрывом).

XNLI: предсказание, следует ли одно предложение из другого. Например, из "это стреляющее пластиковое автоматическое оружие" не следует "это более надежно, чем металлическое оружие", но и не противоречит. Оценивал её я как предыдущую логрегом поверх косинусных близостей. Тут на первом месте оказалась модель от DeepPavlov (которая дообучалась ровно на этой задаче), а моя заняла второе.

SentiRuEval2016: классификация тональности твитов на три класса. Тут я оценивал модели по точности логистической регрессии, обученной поверх эмбеддингов, и моя модель заняла третье место из пяти. В этой и некоторых последующих задачах я сэмплировал обучающую и тестовые выборки, уменьшая их размер до 5К, скорости ради.

OKMLCup: детекция токсичных комментариев из Одноклассников. Тут моя модель заняла четвёртое место по ROC AUC, обогнав только bert-base-cased-multilingual.

Inappropriateness: детекция сообщений, неприятных для собеседника или вредящих репутации. Тут моя модель оказалась на последнем месте, но таки набрала 68% AUC (у самой лучшей, Сберовской, вышло 79%).

Классификация интентов: накраудсоршенные обращения к голосовому помощнику, покрывающие 18 доменов и 68 интентов. Они собирались на английском языке, но я перевёл их на русский простой моделькой. Часть переводов получились странными, но для бенчмарка сойдёт. Оценивал я по точности логистической регрессии или KNN (что лучше). LaBSE набрала точность 75%, модель от Сбера 68%, от DeepPavlov 60%, моя 58%, мультиязычная 56%. Есть, куда расти.

Перенос классификации интентов с английского: та же классификация интентов, но обучающая выбора на английском языке, а тестовая на русском. Тут моя модель заняла второе место после LaBSE, а качество всех остальных сильно просело (что ожидаемо, ибо на параллельных корпусах они не обучались).

factRuEval-2016: задача распознавания классических именованных сущностей (адреса, организации, личности). Я обучал логистическую регрессию поверх эмбеддингов токенов, а качество мерял макро F1 скором (относительно токенов же, что не вполне корректно). Оказалось, что на таком NER моя модель работает откровенно плохо: она набрала скор 43%, остальные 67-69%.

RuDReC: распознавание медицинских именованных сущностей. Тут моя модель тоже проиграла остальным, но с меньшим отрывом: 58% против 62-67%.

Для всех задач, кроме NER, я пробовал для каждой модели два вида эмбеддингов: из CLS токена и усреднённые по всем токенам. Для меня было неожиданностью, что на многих задачах и моделях эмбеддинг СLS-токена оказывался даже лучше, чем средний (в целом, у него так себе репутация). Поэтому рекомендую не лениться и пробовать оба варианта. Код и результаты моей оценки можно взять из этого блокнота, но лучше подождать, пока я ещё причешу этот бенчмарк.

По итогам оценки оказалось, что модель LaBSE очень крутая: она заняла первое место на 6 из 10 задач. Поэтому я решил выложить LaBSE-en-ru, у которой я отрезал эмбеддинги всех 99 языков, кроме русского и английского. Модель похудела с 1.8 до 0.5 гигабайт, и, надеюсь, таким образом стала чуть более удобной для практического применения. Ну а rubert-tiny оказался по качеству в целом близок к моделям от DeepPavlov и Sber, будучи при этом на порядок меньше и быстрее.

Заключение

Я обещал сделать компактную модель для эмбеддингов русских предложений, и я это наконец сделал. Процесс дистилляции, скорее всего, я настроил неоптимально, и его ещё можно сильно улучшать, но уже сейчас маленькая модель на некоторых задачах приближается к уровню своих учителей и даже иногда обходит его. Так что если вам нужен маленький и быстрый BERT для русского языка, то пользуйтесь: https://huggingface.co/cointegrated/rubert-tiny.

Впереди работы много: с одной стороны, хочется обучить маленький BERT решать задачи из RussianSuperGLUE (и не только), с другой затащить в русский язык хорошие небольшие модели для контролируемой генерации текста (я уже начал делать это для T5). Посему лайкайте данный пост, подписывайтесь на мой канал про NLP, подкидывайте в комментариях и в личке интересные задачи, и, если у вас доведутся руки попробовать rubert-tiny, то обязательно оставляйте обратную связь!
Мне и самому интересно, что будет дальше.

Подробнее..

Мы Опубликовали Качественный, Простой, Доступный и Быстрый Синтез Речи

30.03.2021 08:23:00 | Автор: admin

fiona


Вокруг темы синтеза речи сейчас много движения: на рынке есть огромное число тулкитов для синтеза, большое число закрытых коммерческих решений за АПИ (как на современных технологиях, так и на более старых, т.е. "говорилки") от условных GAFA компаний, большое количество американских стартапов, пытающихся сделать очередные аудио дипфейки (voice transfer).


Но мы не видели открытых решений, которые бы удовлетворяли одновременно следующим критериям:


  • Приемлемый уровень естественности речи;
  • Большая библиотека готовых голосов на разных языках;
  • Поддержка синтеза как в 16kHz так и в 8kHz из коробки;
  • Наличие своих собственных голосов у авторов решения, не нарушающих чужие права и лицензии;
  • Высокая скорость работы на "слабом" железе. Достаточная скорость работы на 1 потоке / ядре процессора;
  • Не требует GPU, команды ML инженеров или какой-либо дополнительной тренировки или для использования;
  • Минимализм и отсутствие зависимостей / использование в 1 строчку / не надо ничего собирать или чинить;
  • Позиционируется именно как готовое решение, а не очередной фреймворк / компиляция чужих скриптов / тулкитов для сбора плюсиков;
  • Решение никак не связано и не аффилировано с закрытыми экосистемами и продуктами Гугла / Сбера / Яндекса / вставить нужное;

Мы попытались учесть все эти пункты и представить комьюнити свое открытое некоммерческое решение, удовлетворяющее этим критериям. По причине его публичности мы не заостряем внимание на архитектуре и не фокусируемся на каких-то cherry picked примерах вы можете оценить все сами, пройдя по ссылке.


Краткий Обзор Решений


Данная статья не ставит своей целью глубокий технический обзор всех доступных решений. Мы хотим просто обрисовать некий ландшафт из доступных вариантов с минимальной степенью готовности. Понятно, что мы не рассматриваем многочисленные тулкиты, а смотрим в первую очередь какие есть более-менее готовые решения с ненулевой библиотекой голосов и подобием поддержки / комьюнити:


  • Конкатенативные модели (появившиеся до DL бума). Из того, что хоть как-то поддерживается и живо и можно запустить "as-is" без археологических раскопок, я нашел только rhvoice (я глубоко не копал, но есть целые форумы, посвященные использованию голосов из Windows, но вряд ли это можно назвать поддерживаемым решением). На момент, когда я пользовался проектом ради интереса, он по сути был заброшен, но потом у него появился новый "хозяин". К плюсам такого рода решений можно отнести их скорость и нетребовательность к ресурсам (исключая ресурсы, чтобы заставить это работать). Очевидный и основной минус звучит как говорилка. Менее очевидный минус довольно тяжело оценить стоимость обладания. Качество звучания: 3+ по пятибалльной шкале;


  • DL-based модели в основном разделяют end-to-end TTS задачу на подзадачи: текст -> фичи и фичи -> речь (вокодинг). Практически повсеместно для первой подзадачи используется Tacotron2. Выделим следующие сочетания моделей в соответствии с их эффективностью и простотой использования:


    • Tacotron2 + WaveNet (оригинальный WaveNet принимал на вход лингвофичи, но для такотрона поменяли на более удобные мелспектрограммы). Основная проблема очень медленный инференс ввиду авторегрессионности модели и необходимость запретительно большого количества ресурсов и времени. Качество звучания: 4+;


    • Tacotron2 + WaveRNN (тоже с переходом от лингвофичей к спектрограммам). Вокодер заметно быстрее предыдущего: при использовании всех хаков можно получить даже риалтайм синтез без GPU, правда естественность звука несколько просядет. Качество звучания: 3.5-4;


    • Tacotron2 + Parallel WaveNet. Упомянутый выше медленный вокодер был использован в качестве учителя для получения новой довольно быстрой параллельной модели вокодера: с ней стал возможен синтез быстрее риалтайма, но все еще на мощных GPU. Из недостатков дистилляция требует качественную учительскую модель и соответствующую схему обучения. Качество звучания: 4+;


    • Tacotron2 + multi-band WaveRNN. Тоже развитие предыдущих идей, тоже распараллеливание в некотором смысле здесь доступен синтез быстрее риалтайма уже на CPU. Однако, не слишком популярная работа, меньше имплементаций и поддержки, хотя некоторые подходы хороши и были успешно использованы в более поздних моделях; Качество звучания: 3.5-4+;


    • Tacotron2 + LPCNet. Интересная идея про сочетание DL и классических алгоритмов, что может дать буст по скорости до подходящего для продакшена уровня и на CPU, но требует вдумчивого допиливания для качественных результатов. Качество звучания: 3.5-4+;


    • Многочисленные решения на базе Tacotron2 + Waveglow от Nvidia как нынешний стандарт для задачи синтеза речи. Никто не пишет про свой "секретный соус" (например как 15.ai делает голос по 15 минутам и сколько там моделей в цепочке). Есть много имплементаций и репозиториев, которые "копируют" чужой код. Может звучать на cherry-picked примерах неотличимо от живых людей, но когда смотришь реальные модели от комьюнити, качество заметно варьируется, а детали улучшенных решений не раскрываются. Архитектурно к такотрону и его аналогам по скорости и цене обладания претензий нет, но Waveglow очень прожорлив к ресурсам как на тренировке, так и в продакшене, что делает его использование по сути нецелесообразным. Качество звучания: 3.5-4+;


    • Замена Tacotron2 => FastSpeech / FastSpeech 2 / FastPitch, то есть уход к более простой сетке (на базе forced-align от такотрона и миллион более хитрых и сложных вариантов). Из полезного дает контроль темпа речи и высоты голоса, что неплохо, вообще упрощает и делает более модульной конечную архитектуру. Немаловажно, что сетка перестает быть рекуррентной, что открывает просторы для оптимизаций по скорости. Качество звучания: 3.5-4+;




Оценки Качества и Примеры Аудио


Чтобы не вдаваться в дебри, мы поступили максимально просто: синтезировали аудио из валидационной выборки датасетов (~200 файлов на спикера), смешали с оригинальными аудио этой же выборки и дали группе из 24 людей для оценки качества звучания по пятибалльной шкале. Для 8kHz и 16kHz оценки собирали раздельно, градация оценок [1, 2, 3, 4-, 4, 4+, 5-, 5] с большей детализацией для более качественного звука.


Всего было поставлено 37,403 оценок. 12 человек сделали оценку полностью. Еще 12 людей успели проставить только от 10% до 75% оценок. Дальше для каждого спикера мы просто посчитали среднее (в скобочках приведено стандартное отклонение). Расчет среднего от медиан по каждому аудио завышает средние оценки на 0.1 0.2 балла, но не влияет на отношения. Показательны естественно скорее отношения средних баллов друг к другу. Дисперсия довольно высокая, но оценки пользователей отличались сильно и мы решили не выбрасывать никакие, т.к. оценки одного пользователя были консистентными друг с другом. По ряду соображений мы провели такую оценку только на своих уникальных голосах:


Спикер Оригинал Синтез Отношение Примеры
aidar_8khz 4.67 (.45) 4.52 (.55) 96.8% link
baya_8khz 4.52 (.57) 4.25 (.76) 94.0% link
kseniya_8khz 4.80 (.40) 4.54 (.60) 94.5% link
aidar_16khz 4.72 (.43) 4.53 (.55) 95.9% link
baya_16khz 4.59 (.55) 4.18 (.76) 91.1% link
kseniya_16khz 4.84 (.37) 4.54 (.59) 93.9% link

Мы просили людей в первую очередь оценивать естественность звучания речи (а не качество звука). Нас удивило, что по расспросам обычные люди на своих ежедневных дивайсах не особо слышат разницу между 8 kHz и 16 kHz (что подтверждается оценками)! Самые низкие абсолютные оценки и самое низкое отношение у Байи. Самые высокие абсолютные оценки у Ксении, а относительные у Айдара. Тут важно отметить, что у Байи меньше поставлен голос, но поэтому он звучит более по-человечески за счет этого. У Байи также выше дисперсия оценок.


Ручной просмотр аудио с большими расхождениями показывает ошибки спикеров, ошибки такотрона, ошибки такотрона (паузы), имена собственные и сложные слова, которые вообще непонятно как читать. Естественно 75% таких расхождений в синтезе (а не оригинале) и частота дискретизации особо не влияет.


Если мы пытались дать численную оценку естественности, то еще хорошо бы оценить "неестественность" или "роботизированность" голоса. По идее ее можно оценивать, давая людям пару аудио на выбор и прося выбрать между ними. Но мы пошли дальше и по сути применили "двойной слепой тест". Мы в случайном порядке дали людям поставить оценки "одному и тому же аудио" 4 раза оригинал и синтез с разной частотой дискретизации. Для участников исследования, разметивших весь датасет, получается такая таблица:


Сравнение Хуже Одинаково Лучше
16k против 8k, оригинал 957 4811 1512
16k против 8k, синтез 1668 4061 1551
Оригинал против синтеза, 8k 816 3697 2767
Оригинал против синтеза, 16k 674 3462 3144

Тут напрашивается несколько выводов:


  • В 66% случаев люди не слышат разницы между 8k и 16k;
  • В синтезе, 8k немного помогает скрыть ошибки;
  • Примерно в 60% случаев люди считают, что синтез не хуже оригнала по естественности;
  • Показательно, что два последних вывода не особо зависят от частоты дискретизации (8k имеет небольшое преимущество);

Можете оценить сами, как это звучит, как для наших уникальных голосов, так и для спикеров из внешних источников (больше аудио для каждого спикера можно синтезировать в colab.


Если вы не дружите с колабом или глаза разбегаются от количества файлов в папках с примерами, то вот несколько случайных аудио:


Айдар:





Байя:





Ксения:





Еще раз обращаю внимание, что это не cherry-picked примеры, а реальное звучание синтеза.


Бенчмарки по Скорости


После качества скорость является следующим определяющим свойством модели, для измерения скорости синтеза мы используем следующие простые метрики:


  • RTF (Real Time Factor) какую долю длительности аудио занимает синтез этого аудио;
  • RTS = 1 / RTF (Real Time Speed) насколько синтез "быстрее" риалтайма;

Метрики мы снимали на двух устройствах с помощью встроенных в PyTorch 1.8 утилит:


  • CPU Intel i7-6800K CPU @ 3.40GHz;
  • GPU 1080 Ti;
  • При снятии метрик на CPU мы также ограничивали число используемых потоков;

Для моделей 16 kHz получаются такие показатели:


Батч Устройство RTF RTS
1 CPU 1 thread 0.7 1.4
1 CPU 2 threads 0.4 2.3
1 CPU 4 threads 0.3 3.1
4 CPU 1 thread 0.5 2.0
4 CPU 2 threads 0.3 3.2
4 CPU 4 threads 0.2 4.9
--- ----------- --- ---
1 GPU 0.06 16.9
4 GPU 0.02 51.7
8 GPU 0.01 79.4
16 GPU 0.008 122.9
32 GPU 0.006 161.2
--- ----------- --- ---

Для моделей 8 kHz получаются такие показатели:


Батч Устройство RTF RTS
1 CPU 1 thread 0.5 1.9
1 CPU 2 threads 0.3 3.0
1 CPU 4 threads 0.2 4.2
4 CPU 1 thread 0.4 2.8
4 CPU 1 threads 0.2 4.4
4 CPU 4 threads 0.1 6.6
--- ----------- --- ---
1 GPU 0.06 17.5
4 GPU 0.02 55.0
8 GPU 0.01 92.1
16 GPU 0.007 147.7
32 GPU 0.004 227.5
--- ----------- --- ---

Также при расчетах скорости мы были удивлены ряду вещей:


  • Процессоры AMD показали себя сильно хуже;
  • Удивительно, но бутылочным горлышком в нашем случае оказался именно такотрон а не вокодер (то еще остается существенный потенциал ускорения всей системы в 3-4 раза, а может даже и в 10 раз, если получится квантизация и дополнительное сжатие);
  • Более чем 4 потока CPU не помогают, равно как и батч больше 4;

Список Доступных моделей и Ссылки на Датасеты


Для просты мы решили опубликовать все наши модели в рамках проекта silero-models. Список актуальных моделей всегда можно найти в этом yaml файле.


На момент написания этой статьи доступны следующие голоса (доступны _16khz и _8khz версии голосов):


Спикер Пол Язык Источник Лицензия Датасета Примеры Colab
aidar m ru Silero Private 8000 / 16000 Open In Colab
baya f ru Silero Private 8000 / 16000 Open In Colab
ksenia f ru Silero Private 8000 / 16000 Open In Colab
irina f ru Private contribution TBD 8000 / 16000 Open In Colab
natasha f ru source CC BY 4.0 8000 / 16000 Open In Colab
ruslan m ru source CC BY-NC-SA 4.0 8000 / 16000 Open In Colab
lj f en source Public Domain 8000 / 16000 Open In Colab
thorsten m de source Creative Commons Zero v1.0 Universal 8000 / 16000 Open In Colab
gilles m fr source Public Domain 8000 / 16000 Open In Colab
tux m es source Public Domain 8000 / 16000 Open In Colab

Как Попробовать


Все модели опубликованы в репозитории silero-models, там также есть примеры запуска синтеза в colab. Для полноты приведем минималистичный пример (да, это действительно так просто):


import torchlanguage = 'ru'speaker = 'kseniya_16khz'device = torch.device('cpu')(model, symbols, sample_rate, example_text, apply_tts) = torch.hub.load(repo_or_dir='snakers4/silero-models',                                          model='silero_tts',                                          language=language,                                          speaker=speaker)model = model.to(device)  # gpu or cpuaudio = apply_tts(texts=[example_text],                  model=model,                  sample_rate=sample_rate,                  symbols=symbols,                  device=device)

На данный момент поддерживаются следующие спец-символы: !\'(),.:;?. Кроме того, для большинства спикеров русского языка в тексте для озвучивания были использованы метки ударения (символ + перед ударной гласной, при тестировании таких моделей пока еще нужно ставить ударение вручную):


Спикер С ударением
aidar да
baya да
ksenia да
irina да
natasha да
ruslan да
lj нет
thorsten нет
gilles нет
tux нет

В будущем мы планируем перевести все модели на более простой и унифицированный формат, не требующий ударений. Чтобы не запутаться, в yml файле, который описывает все наши модели, явно указан набор токенов для каждой модели и пример фразы для генерации.


Философия, Лицензия и Мотивация


Как авторы моделей, мы считаем следующие правила использования моделей справедливыми:


  • Голоса из внешних источников приведены исключительно в целях демонстрации и будут удалены;
  • Любые из описанных выше моделей нельзя использовать в коммерческих продуктах;
  • Репозиторий silero-models опубликован под лицензией GNU A-GPL 3.0. Де-юре это не запрещает коммерческое использование, но по факту мы еще не встречали коммерческие решения с полностью открытым кодом, чего требует эта лицензия;
  • Если вы ставите своей целью некоммерческое использование наших моделей во благо общества мы будем рады помочь вам с интеграцией моделей в ваше решение;
  • Если вы планируете использование наших моделей в личных целях (по фану или для озвучки каких-то текстов), то делитесь результатами своих экспериментов в репозитории;
  • Если вы планируете использование наших моделей в некоммерческих продуктах для людей с нарушениями речи или зрения обращайтесь, мы поможем с интеграцией, чем умеем;

Делая этот проект мы ставили своей целью ценой многочисленных компромиссов показать, что современный TTS, удовлетворяющий описанным выше критериям, возможен. И для этого не нужно заложником закрытых экосистем корпораций.


Дальнейшая Работа


Мы планируем постоянно развивать и улучшать свое решение, в частности:


  • Рано или поздно добавить поддержку изменения скорости и высоты голоса;
  • Продолжать работать над качеством и естественностью звучания и расширять библиотеку голосов;
  • Мы оцениваем, что есть еще запас ускорения всего пайплайна в целом примерно в 3-4 раза (возможно даже до 10 раз, если повезет);
  • Маловероятно но не исключено, что рано или поздно мы сможем добавить мульти-спикерную модель или voice-transfer;

Скороговорки


И в качестве бонуса, немного скороговорок.


Русский язык:






Другие языки:





Подробнее..

Мы сделали наш публичный синтез речи еще лучше

18.06.2021 14:19:30 | Автор: admin

6cc6e0011d4d26aeded6f052080b1890


Мы были очень рады, что наша прошлая статья понравилась Хабру. Мы получили много позитивной и негативной обратной связи. Также в ней мы сделали ряд обещаний по развитию нашего синтеза.


Мы достигли существенного прогресса по этим пунктам, но ультимативный релиз со всеми новыми фичами и спикерами может занять относительно много времени, поэтому не хотелось бы уходить в радиомолчание надолго. В этой статье мы ответим на справедливую и не очень критику и поделимся хорошими новостями про развитие нашего синтеза.


Если коротко:


  • Мы сделали наш вокодер в 4 раза быстрее;
  • Мы сделали пакетирование моделей более удобным;
  • Мы сделали мультиспикерную / мультязычную модель и "заставили" спикеров говорить на "чужих" языках;
  • Мы добавили в наши русские модели возможность автопростановки ударений и буквы ё с некоторыми ограничениями;
  • Теперь мы можем сделать голос с нормальным качеством на 15 минутах 1 часе (с теплого старта в принципе заводилось даже на 3-7 минутах) или на 5 часах аудио (с холодного старта). Но тут все очень сильно зависит от качества самого аудио и ряда деталей;
  • Мы привлекли коммьюнити к работе, и нам помогли сделать удобный интерфейс для записи. Мы начали работу над голосами на языках народностей СНГ (украинский, татарский, башкирский, узбекский, таджикский). Если вы хотите увидеть свой язык в числе спикеров пишите нам;
  • Мы продолжаем собирать обратную связь по применимости нашей системы для экранных интерфейсов чтения, и пока кажется, что нужно где-то еще всё ускорить в 5-10 раз, чтобы наши модели закрывали и этот кейс;

Справедливая критика


Люди верно заметили, что в первом релизе иностранные языки не содержали ударений вообще, а в русском не было автоматической простановки ударений и буквы ё. Мы просто банально не успели этого сделать. Для русского и украинского мы это наконец сделали. Для других языков тоже сделаем по мере поиска словарей и надобности (зачастую там и так нормально все работает).


Также стали всплывать известные артефакты используемых нейросетей, например потеря алайна такотроном (симптом генерация длинного аудио с тишиной в конце). В большинстве случаев это решалось постановкой точки в конце фразы. Мы внесли соответствующую микроправку.


Хотя мы вроде явно написали про это и даже сделали warning в коде про то, что модель принимает только предложения (а не целые тексты или книги), все равно основной поток комментариев был именно про это. Также мало кто обратил внимание на раздел статьи про скорость работы моделей и батчи (если вы не видели его, прочитайте).


Я привел в комментариях к статье пару простейших примеров в виде ответов на вопросы (раз, два, три, четыре), но чтобы не возвращаться к этому правильный алгоритм работы с моделью такой:


  • Сначала текст надо разбить на предложения оптимальным способом для вашего домена;
  • Потом надо проставить ударения (новые модели русского языка автоматически проставляют ударение и там, где его нет, и букву ё, но можно поставить и руками);
  • Далее надо кормить модель батчами в соответствии с оптимальным сайзингом (например на 2 ядрах процессора оптимальнее всего использовать батч-сайз 1-2);

Упрощаем запуск


Вспоминая опыт с silero-vad, чтобы не множить сущности, мы опубликовали наш синтез в нашем репозитории silero-models. Это имело очевидные плюсы, но и ряд минусов:


  • Если запускать модель через интерфейс с torch.hub, то нахождение в одном репозитории с моделями распознавания речи требовало установки двух библиотек (omegaconf для парсинга yaml-конфига и torchaudio для чтения аудио). Сам синтез не имеет внешних зависимостей кроме стандартной библиотеки питона и PyTorch. Но, если судить только по гневным комментариям и сообщениям в личку, это оказалось слишком сложным даже если дисконтировать радикальные мнения (мне всерьез писали в личку люди, c энтузиазмом стремящиеся доказать что "питон говно"). Люди в итоге не обращали внимание как на интерактивное демо в colab, так и на standalone примеры. По этой причине через какое-то время я добавил пример # Minimal Example to Run Locally;
  • Вообще конечно в идеале для полностью независимого оффлайнового запуска нужно было просто скачать модель, взять этот скрипт загрузки модели, дополнить его своими функциями и убрать лишнее. Но это тоже оказалось слишком сложным и неочевидным;

С учетом того, что механизм вызова моделей сильно усложнился (добавились мульти-язычная модель и автопростановка ударений и буквы ё для русского языка), мы решили довести вопрос с пакетированием до конца и просто использовать новый механизм пакетирования моделей из PyTorch 1.9. По сути единственное реальное отличие состоит в том, что утилиты для каждой модели теперь упакованы вместе с самой моделью и дистрибутив вместе с утилитами теперь можно скачать одним файлом.


Если вызов через torch.hub по сути особо и не изменился:


import torchlanguage = 'ru'speaker = 'kseniya_v2'sample_rate = 16000device = torch.device('cpu')model, example_text = torch.hub.load(repo_or_dir='snakers4/silero-models',                                     model='silero_tts',                                     language=language,                                     speaker=speaker)model.to(device)  # gpu or cpuaudio = model.apply_tts(texts=[example_text],                        sample_rate=sample_rate)

То полностью standalone вызов стал сильно проще:


import osimport torchdevice = torch.device('cpu')torch.set_num_threads(4)local_file = 'model.pt'if not os.path.isfile(local_file):    torch.hub.download_url_to_file('https://models.silero.ai/models/tts/ru/v2_kseniya.pt',                                   local_file)  model = torch.package.PackageImporter(local_file).load_pickle("tts_models", "model")model.to(device)example_batch = ['В недрах тундры выдры в г+етрах т+ырят в вёдра ядра кедров.',                 'Котики - это жидкость!',                 'М+ама М+илу м+ыла с м+ылом.']sample_rate = 16000audio_paths = model.save_wav(texts=example_batch,                             sample_rate=sample_rate)

Снижение требований по количеству часов и расширение базы голосов


Не секрет, что записывать 15 20 часов аудио это тяжелая и кропотливая работа. Мы проверили некоторые исследования, чтобы понять, сколько часов нужно реально для того, чтобы сделать адекватный голос. Например системы синтеза прошлого поколения требуют около 2-3 часов аудио.


У нас получились такие результаты:


Количество аудио Тип старта Результат
15 20 часов с холодного модели в репозитории
5-6 часов с холодного заводится, нормальное качество, примеры ниже
3 часа с холодного заводится, но речь уже не очень членораздельна
1 час с холодного не заводится совсем
-------------------------- ------------------------ ---------------------------------------------------------
5-6 часов с теплого, похожий голос заводится чуть быстрее, нормальное качество, примеры ниже
5-6 часов с теплого, другой пол заводится, нормальное качество, примеры ниже
5-6 часов с теплого старта с теплого, другой язык заводится, нормальное качество, примеры ниже
3 часа с теплого, похожий голос заводится, качество чуть хуже, примеры ниже
1 час с теплого, похожий голос заводится, качество чуть хуже, примеры ниже
3 15 минут с теплого, похожий голос заводится, на 3 минутах уже сильно проседает качество

С холодного старта, 6 часов:




С теплого старта, 6 часов:



С холодного старта, 3 часа:


Тут уже понятно, что для холодного старта 3 часов маловато.



С теплого старта, 3 часа:



С холодного старта, 1 час:


На холодном старте 1 час вообще уже не работает и генерирует хрип вместо голоса.



С теплого старта, 1 час:


На 1 часу с теплого старта продолжает работать.



С теплого старта, 3 15 минут:


И тут нам стало интересно, а какой же минимальный порог после которого с теплого старта перестает работать? На похожем голосе получилось, что нижняя граница лежит где-то в районе 3 минут.





На моем голосе из голосового чата:


Тут у нас закралась мысль, а может в наших экспериментах что-то подтекает или есть ошибки (то есть мы выдаем желаемое за действительное).


Мы просто попробовали взять мой голос из голосового чата с относительно скверным качеством, чтобы точно удостовериться, что все правда работает.


И чудо, оно завелось, но предсказуемо качество аудио очень сильно влияет на конечный результат.



Не секрет, что на рынке есть большое количество американских стартапов, пытающихся продавать свои коммерческие админки для клонирования голоса. Мы пробовали некоторые из них и качество было так себе. Тут в принципе и понятно, почему так происходит. Для синтеза очень важна чистота аудио, чего достичь, просто записывая в шумных условиях в некачественный микрофон, тяжело.


Как и в случае с видео дипфейками, новостное поле искажает их реальный потенциал и возможности, и скорее ограничением на массовое создание "поддельного" контента является даже не столько спорная этическая и финансовая составляющая (я не уверен какой будет LTV у сервиса с поздравлениями, но это скорее всё-таки не про наш рынок), сколько банальное количество усилий, которое надо все равно вложить, чтобы получить хорошее звучание.


Немного усложним задачу:


Давайте теперь попробуем теплый старт на немецком языке, с непохожего голоса другого пола и с непохожего голоса с другого языка:





Хм, все работает с некими оговорками.


Прочие эксперименты:


Ну и напоследок попробуем заставить Ксению говорить по-немецки. Например, вот эту фразу: Mein Knig, das Fichtenbaum, Bundesausbildungsfrderungsgesetz, die Ubng..



Звучит как ученик на первом уроке немецкого языка. Но мы знаем как сделать это лучше. Кстати одна из небольших фич текущего релиза, что можно заставлять спикеров говорить на других языках. Это пока сильно неидеально работает, но все равно уже прикольно.


Чтобы не разбегались мысли подведем некоторый итог:


  • Для более менее качественного синтеза точно достаточно 5-6 часов качественного аудио (особенно, если новый язык сильно не похож на имеющиеся);
  • Если записи очень качественные и язык похож на имеющиеся, то в принципе можно опускаться и ниже, вплоть до 15 минут 1 часа;
  • Естественно интонации и эмоции модель выучивает из записанного корпуса, искусственно управлять интонациями мы пока не научились;
  • Именно качество аудиозаписей является критическим моментом для качества синтеза;
  • Мы пока не понимаем как добиться качества и интонаций сравнимых с Алисой, но есть подозрение, что дело в количестве часов (40 100 часов) и чистоте аудио;

Мультиязычная модель


После успешных экспериментов нам показалось, что стоит попробовать сделать мультиязычную мультиспикерную модель и "заставить" всех спикеров говорить по-русски. Вот результат. Обратите внимание на то, с какими акцентами говорят спикеры и попробуйте угадать какой язык для них родной.


У этой модели несколько хуже качество, но мы все равно опубликовали ее ради прикола.













Записываем голоса языков народностей СНГ


Также я был приятно удивлен тем, что нам помогли сделать удобный интерфейс для записи голоса и нашлись люди, записавшие / готовые записать спикера на украинском, азербайджанском, башкирском и татарском языках.


photo_2021-05-17_18-05-35


Собственно по причине наличия удобного интерфейса и снижения требований к количеству часов приглашаем всех, кто хочет записать свой голос. Особенно актуально это будет для малых языков допустим для озвучки мультиков и с целью их сохранения. Коллеги из Украины, Башкирии, Азербайджана и Татарстана уже присоединились к процессу.


Поскольку мы научились эффективно тренировать модели даже на небольшом количестве часов и качество аудио является критичным, то только активное и неравнодушное комьюнити тут может помочь сделать голоса на все языки нашей страны.


Ускорение модели


Изначально мы полагали, что именно вокодер будет основной проблемой по скорости в синтезе. Неудивительно, ведь он работает с "длинными" данными. На практике мы были удивлены, что сейчас бутылочным горлышком у нас является не вокодер, но его ускорение останется "про запас".


В текущей итерации у нас получилось ускорить вокодер примерно в 4 раза почти без потери качества (на глаз потеря 0.1 0.2 MOS в среднем) и достичь примерно таких цифр:


Модель 8 kHz 16 kHz
v1 только вокодер, 1 поток 18 8
v2 только вокодер, 1 поток 70 35

Это великолепный результат, но теперь осталось подтянуть все остальное. Но тут уже технически нам не хватает ресурсов, чтобы все 10 вещей делать одновременно. В релизе v2 также немного пришлось пожертвовать качеством отдельных моделей в силу ограниченности ресурсов.


Автоматическая простановка ударений


По итогу релиза нам посоветовали несколько публичных проектов, но качество или поддержка их всех были не очень (как правило это все классическое академическое abandonware). Мы прошли большую часть пути с ударениями, но при тестировании на все более сложных или приближенных к реальности кейсах появляются новые моменты, и мы долгое время находились в стадии "финишной прямой".


Основные проблемы:


  • Слов в русских и украинских языках реально много. Словарь может весить 100 200 мегабайт. Нужно не только добиться приемлемого качества и скорости работы моделей но и высокой степени сжатия моделей по сравнению со словарем;
  • В отличие от словаря, модели обладают хоть какой-то генерализацией;
  • У слов в языке сильно отличается частотность и надо соблюсти баланс между размером всей системы, точностью на всех когортах и стоимостью обладания системой;

На текущий момент мы смогли решить задачу простановки ударений, например для русского языка, следующим образом:


  • Для самых популярных слов и словоформ (их примерно 130 тысяч), мы ставим ударение с точностью 99.9%;
  • Для слов со средней популярностью (их примерно 540 тысяч), мы ставим ударение с точностью 99.9%;
  • Примерно 1,300 слов мы включаем в словарь исключений (ошибки на остальных словах из этих когорт);
  • Для слов с низкой популярностью (длинный хвост, примерно 2 миллиона), мы ставим ударение с точностью 99%;
  • В каждой из этих категорий есть примерно 3% слов-омографов, которые мы пока не можем обработать (например зАмок замОк, хлОпок хлопОк). Такие слова наша модель специально пропускает, тем самым перенося бремя по простановке усредненного ударения на нашу модель синтеза;

Мы знаем как решить эти 3% в простановке ударений, но нам нужен большой корпус, в котором были бы размечены ударения в предложениях целиком (а не просто отдельные слова). Если вы знаете, где найти такой корпус будем признательны за наводку. Так мы сможем окончательно решить этот вопрос для синтеза.


Задача простановки буквы ё была также решена с некоторыми оговорками:


  • Омографы, связанные с буквой ё (например все всё, колеса колёса), не пропускаются, как в случае с ударениями. Выбрать подобные омографы из корпуса оказалось нетривиальной задачей (часто буква ё на письме игнорируется, поэтому отделить омограф от hard negative сложно без специально размеченных данных);
  • На словаре буквы ё мы ставим её с точностью 99% (как для hard positive, так и для hard negative слов);
  • Модель немного генерализовалась на слова, которых она не видела при обучении. Но всё-таки общее поведение на незнакомых словах не ставить ё;
  • Случается, что модель предсказывает ударение и букву ё на разные позиции. В таких ситуациях буква ё не будет проставлена;

Есть ещё одна проблема, не решённая на данном этапе: слова с побочными ударениями или несколькими буквами ё (например авиаметеослужба, премьер-министр, трёхколёсный).
Сейчас модель проставляет только одно ударение (и одну ё) в таких словах, но мы планируем в будущем исправить эти кейсы.


Несправедливая критика


По большей части критики конечно просто не понимали, что любые открытые и бесплатные продукты надо воспринимать как "подарок", какое количество усилий и работы было вложено в релиз, и что требовать от открытого продукта наличия упакованных клиентов и интеграций под каждую платформу и приложение как минимум странно.


Если перечислить самые популярные претензии:


  • Нет middleware под конкретный домен или платформу;
  • Нет приложения / интеграции в какое-то другое существующее приложение под какую-то платформу;
  • Слишком сложно, невозможно разобраться;
  • Алиса звучит лучше;

На первые ответ довольно простой middleware делаются на заказ как часть коммерческого продукта. Если вы хотите поддерживаемый клиент / интеграцию под какую-то платформу, то цена вопроса цена на поиск + зарплата команды разработки (1-2 человека), умноженная на требуемый срок поддержки.


Насчет Алисы вы сами выбираете дарить ли свои персональные данные корпорациям. Практика показывает, что де-факто никакой защиты от их недобросовестного поведения нет кроме сил конкуренции. Как говорится: "Если подписал не удивляйся". Также мы примерно приценивались к "звездным" голосам, там только бюджет на запись голоса уже в разы больше бюджета на весь некоммерческий релиз целиком (нам называли оценки в районе 0.25 0.5 миллиона долларов только за запись голоса).


Насчет сложности мы довели синтез до того, что он вызывается и запускается 1 строчкой кода и фактически требует только только 1 библиотеки и при этом ест очень мало ресурсов при вменяемом качестве. В прошлой версии были некоторые издержки, так как он опубликован в репозитории вместе с STT и мы выбрали радикальную простоту вместо пакетирования. Всегда можно еще сильнее улучшить пакетирование, добавить гайдов под конкретные платформы и улучшить и упростить документацию. Но с учетом количества усилий, которые были вложены в релиз (и продолжают вкладываться в следующие релизы), мое личное мнение что это уже задача для комьюнити.


Многие из этих причин и в принципе более высокий чем я ожидал уровень шума повлияли на наше решение несколько сместить приоритеты в этом релизе.


Если вы готовы поддержать проект грантом, профинансировать какие-то конкретные фичи или заказать голос для своего проекта мы всегда будем этому рады. Если вам нужно супер качество, какие-то особенные фичи и интеграции то это уже следует делать в рамках отдельных проектов.


Дальнейшие планы


Текущий релиз:


  • Ускорение вокодера в 4 раза;
  • Многоязычная мультиспикерная модель (и возможность говорить на другом языке с акцентом);
  • Снижено количество файлов и упрощено пакетирование;
  • Добавление автоматической простановки ударений и простановки буквы ё;
  • Снижение требований по количеству данных и начало работы над голосами народностей СНГ;

Следующие релизы:


  • Высота голоса и скорость;
  • Радикальное ускорение моделей (10+ раз);
  • Эмоции, управление интонацией;
  • Еще большее снижение требований по данным;
  • Добавление новых голосов по мере появления открытых голосов на других языках;
  • Добавление малых языков и языков народностей России и СНГ по мере сбора датасетов;
Подробнее..

Как преобразовать текст в алгебру примеры

10.04.2021 22:12:11 | Автор: admin

В предыдущей статье было разработано представление знаковых последовательностей полиномами матричных единиц на примере языкового текста. Текст превращается в алгебраический объект. С текстом можно совершать все алгебраические операции, необходимые для структуризации -- вычисления заголовков, словарей, аннотаций, смысловой разметки. В данной статье приведены два примера алгебраической структуризации текстов иной природы. Азбука Морзе выбрана из-за предельной краткости словаря, а математические формулы как пример обратной задачи.

1 Код Морзе-Вейля-Герке как алгебра матричных единиц

В азбуке Морзе знаковые последовательности (тексты) 26 латинских букв состоят из точек и тире. Пример выбран из-за предельной краткости словаря ("точка" и "тире").

Слова здесь - точки или тире. 26 букв азбуки - тексты из таких слов. У каждого слова две координаты. Первая координата номер слова (точки или тире) в этой букве (от одного до четырех). Вторая координата номер в словаре (1 или 2). Словарь E11 ("точка") и E22 ("тире").

D_R=E_{11}+E_{22}Таблица 1. Азбука Морзе: латинские буквы как знаковые последовательности (тексты)Таблица 1. Азбука Морзе: латинские буквы как знаковые последовательности (тексты)

Каждой букве (знаковой последовательности) с номером из Таблицы 1 можно поставить в соответствие матричный полином P из матричных единиц 4x4 по формуле (8) из статьи [1].

Таблица 2: Азбука Морзе: буквы как матричные полиномыТаблица 2: Азбука Морзе: буквы как матричные полиномы

Например, букве Q (17) ставится в соответствие матричный полином:

E_{12}+E_{22}+E_{31}+E_{42}= \begin{Vmatrix} 0 & 1 & 0 & 0\\ 0 & 1 & 0 & 0\\ 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 \end{Vmatrix}.

Свойством всех 26 полиномов-букв таблицы 2 является то, что крайними правыми сомножителями являются только три матричные единицы E12, E21, E32

Если все 26 полиномов Таблицы 2 представить столбцом ||P||, а также из того, что для матриц и столбцов выполняется:

 \begin{Vmatrix} a_{11} & \ldots & a_{1n}\\ \ldots & \ldots & \ldots\\ a_{m1} & \ldots & a_{mn} \end{Vmatrix} \begin{Vmatrix} b_{1} \\ \ldots \\ b_{n} \end{Vmatrix}= \begin{Vmatrix} a_{11} \\ \ldots \\ a_{m1} \end{Vmatrix}b_1+\ldots + \begin{Vmatrix} a_{1n} \\ \ldots \\ a_{mn} \end{Vmatrix}b_n,

то азбука Морзе структурируется в три левые идеалы наборов матричных полиномов Таблицы 2 с базисами ||P||1, ||P||2, ||P||3.

 \left\|P\right\|=\left\|P\right\|_1\left\|P\right\|_1=\left\|P\right\|_2\left\|P\right\|_2=\left\|P\right\|_3\left\|P\right\|_3,

где

\left\|P\right\|_1=\begin{Vmatrix} E_{12} \\ E_{21} \\ E_{32} \end{Vmatrix}, \left\|P\right\|_2=\begin{Vmatrix} E_{12} \\ E_{21}E_{12} \\ E_{12}+E_{21}E_{12} \\ E_{12}E_{21} \\ E_{21} \\ E_{21}+E_{12}E_{21} \\ E_{32} E_{21} + E_{43}E_{32} E_{21} \\ E_{43}E_{32} E_{21} \\ E_{32} E_{21} \\ E_{32} \\ E_{32} + E_{43}E_{32} \\ E_{43}E_{32} \end{Vmatrix}, \left\|P\right\|_3=\begin{Vmatrix} E_{12}E_{21} \\ E_{12} \\ E_{21} \\ E_{21}E_{12} \\ E_{32}E_{21} \\ E_{32} \\ E_{43}E_{32} E_{21} \\ E_{43}E_{32} \end{Vmatrix}, (1.1)

||P||2(||P||2)T - симметричная матрица - число в диагональных элементах это число базисных элементов (простых и составных матричных единиц), принадлежащих букве, в других элементах число совпадающих базисных элементов в соответствующей паре знаковых последовательностей (букв) - после нормализации характеризует важность буквы в азбуке.

(||P||2)T ||P||2 - симметричная матрица - число в диагональных элементах это число букв, принадлежащих базисным элементам, в недиагональных элементах число совпадающих букв в соответствующей паре базисных элементов после нормализации характеризует важность базисного элемента (заголовка) в азбуке.

Азбука Морзе с алгебраически структурирована в три идеала (класса) с базисами (1.3). Представление азбуки через идеалы описывает все подобные коды с базисами (1.3). Представление азбуки через идеалы приведено в Таблицах 3 и 4:

Таблица 3: Прямая индексацияТаблица 3: Прямая индексацияТаблица 4: Обратная индексацияТаблица 4: Обратная индексация

Азбука Морзе: ABCDEFGHIJKLMNOPQRSTUVWXYZ

из-за свойств матричных полиномов(крайние правые сомножители - только три матричные единицыE12, E21, E32) разбивается на три класса (три идеала) тремя образующими E12, E21, E32:

E12 - заголовок тех букв, которые имеют знак тире на первом месте 4-знаковой последовательности:

_BCD__G___K_MNO_Q__T___XYZ (13 букв)

E21 - заголовок тех букв, которые имеют знак точка на втором месте 4-знаковой последовательности:

_BCD_F_HI_K__N____S_UV_XY_ (13 букв)

E32 - заголовок тех букв, которые имеют знак тире на третьем месте 4-знаковой последовательности:

__C__F___JK ___OP____U_W_Y_ (9букв)

2 Алгебра математического текста

В примере [1] языковый текст превращался в математический объект (матричный полином), с которым можно совершать алгебраические операции для анализа и синтеза текстов. В этом примере совершается обратное преобразование математические объекты (формулы) сначала рассматриваются как тексты (знаковые последовательности), которые затем превращаются опять в математические объекты, но иные, чем исходные. Такая новая форма позволяет более системно находить свойства математических объектов для сравнения и классификации.

Формулы объема конуса VK, цилиндра Vци тора VТ:

 V_K=\frac{1}{3}\pi R_1^2H_1, V_{\text{Ц}}=\pi R_2^2H_2, V_T=\pi^2\left(R_3+R_4\right)r,\ \ \ \ \ \ \ \ \ (2.1)

рассматриваются как тексты. Это означает, что входящие в тексты знаки не являются математическими объектами и для них отсутствуют алгебраические операции. Например, R12 этоR1R1, R1 это не произведение двух чисел, а просто последовательность двух знаков. Знаки в (1): R1и H1 радиус основания и высота конуса,R2 иH2 радиус основания и высота цилиндра, R3 внутренний радиус тора, R4 внешний радиус тора, r радиус образующей окружности тора, это число .

Для семиотического анализа формул как текстов важно наличие повторов знаков. Повторы определяют закономерности. В формулах (2.1) повторов знаков на самом деле больше, чем указанные повторы знака . ЗнакиR1, R2, R3, R4, H1, H2 и r это длины отрезков. Тогда один из знаков, например , является простым (эталон длины), а остальные знаки составными: R1=ar, R2=br, R3=cr, R4=dr, H1=er, H2=fr . Тогда правые части формул (2.1):

\begin{gathered} \frac{1}{3}\pi ararer \\ \pi brbrfr \\ \pi \pi \left(c+d \right)rr \end{gathered} \ \ \ \ \ \ \ \ \ \ \ \ (2.2)

Или в индексной форме:

\begin{gathered} \left(\frac{1}{3}\right)_{1,1}(\pi)_{2,2}(a)_{3,3} (r)_{4,4} (a)_{5,3} (r)_{6,4} (e)_{7,7} (r)_{8,4} \\ (\pi)_{9,2} (b)_{10,10} (r)_{11,4} (b)_{12,10} (r)_{13,4} (f)_{14,14} (r)_{15,4} \\ (\pi)_{16,2} (\pi)_{17,2} \left(c+d \right)_{18,18} (r)_{19,4}(r)_{20,4} \end{gathered} \ \ \ \ \ \ \ \ \ (2.3)

Формулы (2.2) как полином матричных единиц из трех фрагментов

 P=F_1(P)+F_2(P)+F_3(P), \ \ \ \ \ \ \ \ \ \ (2.4)

где:

\begin{gathered} F_1(P) = D_L\left(E_{1,1}+E_{2,2}+E_{3,3}+E_{4,4}+E_{5,3}+E_{6,4}+E_{7,7}+E_{8,4}\right)D_R \\ F_2(P) = D_L\left(E_{9,2}+E_{10,10}+E_{11,4}+E_{12,10}+E_{13,4}+E_{14,14}+E_{15,4}\right) D_R \\ F_3(P) = D_L\left(E_{16,2}+E_{17,2}+E_{18,18}+E_{19,4}+E_{20,4}\right) D_R \\ D_R = E_{1,1}+E_{2,2}+E_{3,3}+E_{4,4}+E_{7,7}+E_{10,10}+E_{14,14}+E_{18,18} \\ D_L = E_{1,1}+E_{2,2}+E_{3,3}+E_{4,4}+E_{5,5}+E_{6,6}+E_{7,7}+ \ldots + E_{20,20} = E \\ D_L=D_R+E_{5,5}+E_{6,6}+E_{5,5}+E_{8,8}+E_{5,5}+E_{9,9} \end{gathered}

Или в блочно-матричной форме:

В столбцах P находятся знаки из трех формул (2.1) . Если в столбце два нуля, это означает, что соответствующий знак имеется только в одной формуле. Например, знак 1/3 (или E1,1), два знака a (или E3,3+E5,3) , один знак e (или E7,7) имеются только в первой формуле для конуса (первая строка (2.5)). Только в цилиндре (вторая строка (2.5)) имеются два знака b (или E11,11+E13,11) и один f (или E15,15). Только в торе (третья строка (2.5)) имеется знак (c+d) (или E20,20). Общие знаки конуса, цилиндра и тора находятся во втором и четвертом столбцах (2.5). Тогда:

\begin{gathered} P = P_{\text{частн}_1}P_{\text{дел}_1}+P_{\text{ост}} \\ P = P_{\text{частн}_2}P_{\text{дел}_1}+P_{\text{ост}} \end{gathered}

где:

 \begin{gathered} P_{\text{частн}_1} = \left(E_{2,18}+E_{4,12}+E_{6,14}+E_{8,16}\right) +\left(E_{10,18}+E_{12,12}+E_{14,4}+E_{16,16}\right)+\\ +\left(E_{18,18}+E_{19,19}+E_{21,12}+E_{22,14}\right), \\ P_{\text{частн}_2} = (E_{2,2}+E_{4,4}+E_{6,4}+E_{8,4})+(E_{10,2}+E_{12,4}+E_{14,4}+E_{16,4})+ \\ +(E_{18,2}+E_{19,2}+E_{21,4}+E_{22,4}), \\ P_{\text{дел}_1} = E_{18,2} + E_{19,2}+E_{12,4} + E_{14,4} + E_{16,4}, \\ P_{\text{дел}_2} = E_{2,2} + E_{4,4}, \\ P_{\text{ост}} = E_{1,1}+E_{3,3} + E_{5,3}+E_{7,7}+E_{11,11} + E_{13,11}+E_{15,15}+E_{20,20}.\\ \end{gathered}

В (2.6) матричный текст раскладывается по разным базисам Pдел1 и Pдел2. Базис Pдел1учитывает взаимные положения между повторяющимися знаками относительно тора в формулах (2.1). Базис Pдел2 учитывает положения между повторяющимися знаками относительно знаков словаря DR в формулах (2.1). В общем случае учет положения знаков в формулах существенен, если знаки некоммутативны (например, знаки это матрицы, вектора, тензоры, гиперкомплексные числа). Но и в скалярном это полезно, например, канонической является формула площади круга r2, а не r2 .

Базис Гребнёра-Ширшова для (2.6):

 \begin{gathered} P_{\text{дел}_1}+P_{\text{ост}} \\ P_{\text{дел}_2}+P_{\text{ост}} \end{gathered}

Тогда:

 \begin{gathered} P= P_{\text{частн}_1} \left( P_{\text{дел}_1}+P_{\text{ост}} \right) \\ P= P_{\text{частн}_2} \left( P_{\text{дел}_2}+P_{\text{ост}} \right) \end{gathered}

В Pчастн1и Pчастн2имеются повторы (зацепления матричных единиц по второму индексу). Они подлежат дальнейшей редукции. Все зацепления разрешимы, - аддитивные Pчастн1и Pчастн2 приобретут мультипликативную форму, как и для языкового примера.

Метод алгебраическая структуризация текстов позволяет для текстов разной природы найти соответствующие классификаторы и словари. Т. е. классифицировать тексты без априорного задания признаков классификации и наименования классов. Такая классификация называется категоризацией или апостериорной классификацией. Например, для (2.3) классификационными признаками становятся:

  • Pдел1и Pдел2 (общие и r в разных местах формул),

  • общее число слагаемых в круглых скобках Pчастн1и Pчастн2 (четыре),

  • соотношения числа и r в круглых скобках Pчастн1и Pчастн2 (1,1,2 и 3,3,2),

  • сомножители мультипликативной формы Pчастн1 и Pчастн2,

  • всевозможные фрагменты Pост(вычеты, как класс формул с остатком-фрагментом).

Наименования классов совпадает с наименование признаков и их сочетаний.

Литература

[1] Пшеничников C.Б. Алгебра текста. Researchgate Preprint, 2021

Подробнее..

Как сделать интеллектуального чат-бота для проведения опросовинтервью

24.03.2021 10:15:26 | Автор: admin

В современном мире всё большую популярность приобретает методика под названием customer development для тестирования идей и гипотез о будущем продукте. Методику придумал "крёстный отец Кремниевой долины" Стив Бланк.
Одним из числа сильных инструментов в "разработке клиентов" является интервью, когда вы можете побеседовать с респондентом. Однако им не всегда можно воспользоваться ввиду разных причин, которые условно можно свести к объёму бюджета и имеющемуся времени. Но во многих ситуациях можно воспользоваться опросом. Причём опросом, который можно автоматизировать за счёт применения чат-бота и нейронной сети для определения смысла слов, которые написал респондент в ответ на заданный вопрос.

В этой статье сконцентрируюсь на алгоритме работы чат-бот для проведения опроса. Как сделать чат-бота для VK писал в отдельной статье на Хабре. Использовал: Python, MySQL, API VK и готовую нейросеть от RusVectores.

Статья будет полезна для тех, кто только начинает погружаться в тему и хотел бы получить информацию, изложенную в более простой, упрощённой форме.

Дисклеймер. Не претендую на академическую точность изложения, говорю лишь о том, что удалось понять на текущий момент по итогу изучения различных материалов. Не рекламирую какие-либо технологии и сервисыодержание

Нейросеть для понимания смысла текста, вводимого пользователем

Сделать бота, который распознаёт только ключевые слова в фразе пользователя - не очень жизненно. Проверку всех ключевых слов можно постараться прописать в коде, но это трудоёмко и негибко. Поэтому стоит воспользоваться возможностями нейросетей для определения смысла введённых пользователем слов.

В данном решении была использована готовая нейросеть от сервиса RusVectores, обученная на корпусе НКРЯ с использованием алгоритма word2vec CBOW с длиной вектора 300.

НКРЯ это совокупность русскоязычных текстов, Национальный Корпус Русского Языкав полном объёме. Содержит 270 миллионов слов, объём словаря 189 193 слова.

Word2vec CBOW алгоритм, благодаря которому слово на естественном языке представляется в виде числового вектора. Т.е. определяет координату слова в смысловом пространстве. CBOW это аббревиатура Continuous Bag of Words. Она обозначает алгоритм, который есть в word2vec. Данный алгоритм называют моделью мешка слов, он предсказывает слово по контексту. Ещё один алгоритм в word2vec - Skip-gram предсказывает контекст по слову.

С помощью данных алгоритмов генерируют близкие по смыслу слова при запросе в поисковой системе, сравнивают документы по смыслу, определяют смысловую близость слов и предложений.

Более подробно о word2vec можно почитать в статье "Немного про word2vec: полезная теория".

О векторном представлении слов (эмбеддинге) хорошо и с примерами описано в статье "Что такое эмбеддинги и как они помогают машинам понимать тексты".

Представление слова в виде вектора позволяет оценивать его смысловую близость с другими словами, так же представленными в виде вектора. Для оценки близости слов можно вычислить косинус угла между их векторами. Чем ближе к 1 будет косинус угла между векторами слов, тем они ближе по смыслу. Единице будет соответствовать косинус угла 0 градусов, т.е. когда векторы слов совпадают.

Чтобы создать такой набор векторов почти для 200 000 слов и постоянно иметь к ним доступ, нужно располагать вычислительными мощностями. Т.к. у меня таких мощностей нет, я воспользовался доступным онлайн сервисом RusVectores.

База данных для хранения вопросов

Для проведения опроса я подготовил табличку с вопросами и разместил её в базе данных MySQL. Как видно из рисунка ниже, вопросы имеют ответвления, похожие на древовидную структуру или структуру графа.

Структура вопросовСтруктура вопросов

В базе данных таблица с вопросами выглядит так (фрагмент):

Фрагмент таблицы в БД с вопросамиФрагмент таблицы в БД с вопросами

Поле question_num служит для того, чтобы определить порядок вопросов и ответвления. Это путь от корневого элемента дерева вопросов до листового, разделённый точкой.

Описание алгоритма работы чат-бота

Начало опроса

По договорённости с пользователем он заходит на страницу сообщества в ВК и инициирует диалог, нажав кнопку Сообщение.

Бот здоровается и спрашивает разрешения начать опрос. Текст приветствия задавал в разделе "Управление" "Сообщения" на странице сообщества в ВК.

Если пользователь ответил что-то близкое по смыслу со словом да, то бот начинает задавать вопросы. Как определить, что пользователь одобрил старт интервью? Для этого как раз нужна нейросеть, чтобы определить смысловую близость введённых пользователем слов к словам: да, можем, можно, начинай, ок. Для этого воспользуемся API сервиса RusVectores.

Далее приведён код функции, которая определяет начинать интервью или нет. Если интервью уже было начато, то функция определяет какой вопрос задать следующим.

фрагменты кода из bot_methods.py
модуля, в котором реализованы все методы бота

def _identify_phrase(user_id, user_message):    """    identify start question or greeting    return number of phrase in database    """    # identification variable, on start set "I don't know"    identi = 'I dont know'    # find in database current position in conversation between user and chatbot    identi = get_current_position_in_conversation(user_id)    if identi != 'err':        # if the conversation has just begun        if identi == '0':            # define greetings            similarity = _get_similarity(user_message, u'привет здравствуйте добрый')            if similarity > 0.5:                identi = "greetings"            else:                # define start interview or not                identi = _start_or_not(user_message)        # if the conversation continues        elif identi == '1':            # define start interview or not            identi = _start_or_not(user_message)        else:            pass                return identi

Вначале определим возможность начать опрос исходя из ответа пользователя с помощью метода _start_or_not():

def _start_or_not(user_message):    """    define <identi>: start or don't start interview    """    if user_message != 'старт' or user_message != 'Старт':        _identi = 'I dont know'        # define if user agree to start interview        start = _get_similarity(user_message, u'да можем можно начинай ок')        # define if user don't agree to start interview        later = _get_similarity(user_message, u'нет позже потом завтра')        if start > later and start > 0.15:            _identi = 'start'        elif later > start and later > 0.15:            _identi = 'later'    else:        _identi = "start"    return _identi

Если пользователь решил сначала поприветствовать бота, то нужно понять это и поприветствовать в ответ. Для этого проверим на смысловую близость сообщения от пользователя со словами приветствия с помощью метода _get_similarity(). Данный метод будет использоваться и далее для определения смысловой близости сообщений пользователя.

def _get_similarity(text1, text2):    """    Function return similarity between text1 and text2    text1 - user message    text2 - key words    """    text1.strip()  # delete empty space on start and end of string    text2.strip()    text1_words = text1.split(' ')    text2_words = text2.split(' ')    similarity = 0.0 # init variable    try:        for word1 in text1_words:            if word1 != '':                for word2 in text2_words:                    if word2 != '':                        # prepare url for request to API rusvectores.org                        # url example https://rusvectores.org/ruscorpora_upos_cbow_300_20_2019/дело__папка/api/similarity/                        url = '/'.join(['https://rusvectores.org/ruscorpora_upos_cbow_300_20_2019',                                         word1 + '__' + word2, 'api', 'similarity/'])                        # GET request to API rusvectores.org                        r = requests.get(url, stream=True)                        # sum similarity of couple of words                        similarity = similarity + float(r.text.split('\t')[0])    except Exception as e:        log_exception = str(e)    # average similarity    similarity = similarity/len(text2_words)    # return similarity between text1 and text2    return similarity

Переменная similarity содержит числовое обозначение смысловой близость фраз text1 и text2. Чем ближе similarity к 1, тем ближе фразы по смыслу.

Метод _identify_phrase() используется для обработки всех фраз, которые пользователь пишет в чат. Поэтому необходимо заложить в него логику проверки как начального этапа - приветствие и старт интервью, так и последующее движение по вопросам. Для этого необходимо определять на каком вопросе и на какой развилке находится диалог. Текущую позицию в беседе определяем с помощью метода get_current_position_in_conversation()

фрагмент кода из mysqldb_methods.py
модуля, в котором реализованы все методы для работы с MySQL базой данных

def get_current_position_in_conversation(user_id):    """    find in database current position in conversation between user and chatbot    using in bot_methods.py    """    try:        conn = MySQLdb.connect(host=HOST, user=USER, passwd=PASSWORD,                                db=DATABASE, charset='utf8', init_command='SET NAMES UTF8')        cursor = conn.cursor()        query = "SELECT `question_num` FROM `conversations` WHERE `user_id`=%(user_id)s LIMIT 1"        cursor.execute(query, {'user_id': user_id})        result = cursor.fetchone()        if result is None:            identi = '0'        else:            identi = result[0]        conn.close()    except Exception as e:        identi = 'err'        return identi

Таким образом мы обрабатываем три сценария взаимодействия с чат-ботом:
- старт опроса (понимаем согласен пользователь начать опрос или нет с помощью функции _start_or_not()),
- обмен приветствиями, если пользователь поздоровался (понимаем по смысловой близости к словам приветствия с помощью функции _get_similarity());
- движение по структуре вопросов с помощью функции get_current_position_in_conversation() для определения текущего положения в структуре вопросов.

Давайте рассмотрим движение по структуре вопросов более подробно.

Стоп-слова

Одним из важных моментов является удаление "стоп-слов", т.е. слов, которые можно с лёгкостью удалить из предложения и при этом его смысл не потеряется. Вот набор стоп-слов, которые я использовал в данном проекте:

stop_words = [u'а',u'большой',u'бы',u'быть',u'в',u'весь',u'вот',u'всей',u'вы',u'говорить',u'год',u'для',u'до',u'еще',u'если',u'же',u'знать',u'и',u'из',u'или',u'к',u'как',u'который',u'мочь',u'мы',u'мне',u'на',u'наш',u'него',u'нее',u'них',u'но',u'о',u'один',u'она',u'они',u'оно',u'оный',u'от',u'ото',u'по',u'с',u'свой',u'себя',u'сказать',u'та',u'такой',u'такое',u'только',u'тот',u'ты',u'то',u'у',u'что',u'это',u'этот',u'я']stop_characters = [u'.',u',',u' - ',u'- ',u' -',u':',u';',u'?',u'',u'!',u'_',u'(',u')',u'=',u'+',u"#",u'$',u'@',u'%',u'*',u'   ',u'<',u'>','1','2','3','4','5','6','7','8','9','0']

С помощью метода _clear_text() очищаю предложение от стоп-слов:

Движение по структуре вопросов

Для определения в каком направлении опроса двигаться исходя из ответов респондента воспользуемся функцией _define_conversation_way():

def _define_conversation_way(user_message, identi):    """    define in which way we are goin to?    """    # all questions, unless  3 has two ways: 'yes' (positive) or 'no' (negative)    if identi != '3' and identi != '6':        yes = _get_similarity(user_message, u'да заказывал просить')        no = _get_similarity(user_message, u'нет никогда')    elif identi == '6':        # the question number 6 has different ways: 'delivery' or 'self-delivery'        yes = _get_similarity(user_message, u'заказываю доставку')        no = _get_similarity(user_message, u'еду сам ищу аналог')    elif identi == '3':        # the question number 3 has different ways: 'from store' or 'delivery'        yes = _get_similarity(user_message, u'магазин сам')        no = _get_similarity(user_message, u'доставка почта все перечисленное курьер дом')    if yes > no and yes > 0.15:        _way = 'yes'    elif no > yes and no > 0.15:        _way = 'no'    else:        _way = 'I dont know'    return _way

Для удобства использования я сгруппировал всю логику по определению того что бот должен ответить в функцию _get_bot_answer(). Для удобства восприятия приведу ниже не только данный метод, а модуль с методами бота в целом:

bot_methods.py
полный код модуля, в котором реализованы все методы бота

# -*- coding: utf-8 -*-"""Bot methods.Realizes all what bot can do."3. Использование API сервиса RusVectores"https://github.com/akutuzov/webvectors/blob/master/preprocessing/rusvectores_tutorial.ipynb"""import re  # for work with regular expressionsimport requests  # for using HTTP requestsfrom bot_config import stop_wordsfrom bot_config import stop_charactersfrom mysqldb_methods import get_current_position_in_conversationfrom mysqldb_methods import get_question_from_DBfrom mysqldb_methods import write_current_question_number_for_userdef get_bot_answer(user_id, user_message):    """    using in views.py    make answer to user    """    answer = ''    # delete stop-words and punctuation characters in sentence    user_message = _clear_text(user_message)    # identify what to do: start or continue conversation    identi = _identify_phrase(user_id, user_message)    if identi == 'greetings':        answer = get_question_from_DB('1')        write_current_question_number_for_user(user_id, '1')    elif identi == 'start':        answer = get_question_from_DB('2')        write_current_question_number_for_user(user_id, '2')    elif identi == 'later':        answer = "Когда у вас будет возможность пройти интервью напишите мне 'старт'."    elif identi == 'I dont know':        answer = "Я не совсем вас понимаю...\nУточните, пожалуйста."    elif identi == 'end':        answer = "Спасибо за ваше участие в интервью!"    else:        # if top-level question: 1, 2 or 3 etc.        if len(identi) == 1:            # define in which way we are goin to?            way = _define_conversation_way(user_message, identi)            if way == 'yes' or way == 'no':                if way == 'yes':                    # going to positive way                    question_num = '.'.join([identi,'1','1'])                if way == 'no':                    # going to negative way                    question_num = '.'.join([identi,'2','1'])                answer = get_question_from_DB(question_num)                if answer != 'None':                    write_current_question_number_for_user(user_id, question_num)                else:                    question_num = str(int(identi) + 1)                    answer = get_question_from_DB(question_num)                    write_current_question_number_for_user(user_id, question_num)            else:                # if way='I dont know'                answer = "Я не совсем вас понимаю...\nУточните, пожалуйста."        else:            # if subquestion: e.g. identi=2.1.1 or 3.2.2 etc.            identi_numbers = identi.split('.')            next_num = str(int(identi_numbers[2]) + 1)            question_num = '.'.join([identi_numbers[0],identi_numbers[1],next_num])            answer = get_question_from_DB(question_num)            # if we get end of subquestions in this top-level-question            if answer == 'None':                # going to the next top-level question                question_num = str(int(identi_numbers[0]) + 1)                # checking that the question is the last                if _is_the_last_question(question_num):                    answer = get_question_from_DB(question_num)                    question_num = 'end'                else:                    # is not the last question                    answer = get_question_from_DB(question_num)                        write_current_question_number_for_user(user_id, question_num)            return answerdef _is_the_last_question(question_num):    """    define is the last question?    by the condition (len(identi) == 1) of the function "get_bot_answer"    question_num has lenght 1    """    is_the_last = True    question_num = str(int(question_num) + 1)    question = get_question_from_DB(question_num)    if question != 'None':        is_the_last = False    return is_the_lastdef _define_conversation_way(user_message, identi):    """    define in which way we are goin to?    """    # all questions, unless  3 has two ways: 'yes' (positive) or 'no' (negative)    if identi != '3' and identi != '6':        yes = _get_similarity(user_message, u'да заказывал просить')        no = _get_similarity(user_message, u'нет никогда')    elif identi == '6':        # the question number 6 has different ways: 'delivery' or 'self-delivery'        yes = _get_similarity(user_message, u'заказываю доставку')        no = _get_similarity(user_message, u'еду сам ищу аналог')    elif identi == '3':        # the question number 3 has different ways: 'from store' or 'delivery'        yes = _get_similarity(user_message, u'магазин сам')        no = _get_similarity(user_message, u'доставка почта все перечисленное курьер дом')    if yes > no and yes > 0.15:        _way = 'yes'    elif no > yes and no > 0.15:        _way = 'no'    else:        _way = 'I dont know'    return _waydef _identify_phrase(user_id, user_message):    """    identify start question or greeting    return number of phrase in database    """    # identification variable, on start set "I don't know"    identi = 'I dont know'    # find in database current position in conversation between user and chatbot    identi = get_current_position_in_conversation(user_id)    if identi != 'err':        # if the conversation has just begun        if identi == '0':            # define greetings            similarity = _get_similarity(user_message, u'привет здравствуйте добрый')            if similarity > 0.5:                identi = "greetings"            else:                # define start interview or not                identi = _start_or_not(user_message)        # if the conversation continues        elif identi == '1':            # define start interview or not            identi = _start_or_not(user_message)        else:            pass                return identidef _start_or_not(user_message):    """    define <identi>: start or don't start interview    """    if user_message != 'старт' or user_message != 'Старт':        _identi = 'I dont know'        # define if user agree to start interview        start = _get_similarity(user_message, u'да можем можно начинай ок')        # define if user don't agree to start interview        later = _get_similarity(user_message, u'нет позже потом завтра')        if start > later and start > 0.15:            _identi = 'start'        elif later > start and later > 0.15:            _identi = 'later'    else:        _identi = "start"    return _identidef _clear_text(sentence):    """    delete stop-words and punctuation characters in sentence    """    try:        # sentence to low-case        sentence = sentence.lower()        # delete stop-characters        for char in stop_characters:            sentence = sentence.replace(char, '')        # delete stop-words        words_of_sentence = sentence.split(' ')        result = ''        for word in words_of_sentence:            if word not in stop_words:                result = result + ' ' + word    except Exception as e:        result = str(e)    return resultdef _get_similarity(text1, text2):    """    Function return similarity between text1 and text2    :param text1: user message    :param text2: key words    """    text1.strip()  # delete empty space on start and end of string    text2.strip()    text1_words = text1.split(' ')    text2_words = text2.split(' ')    similarity = 0.0 # init variable    try:        for word1 in text1_words:            if word1 != '':                for word2 in text2_words:                    if word2 != '':                        # prepare url for request to API rusvectores.org                        # url example http://rusvectores.org/araneum_none_fasttextcbow_300_5_2018/дело__папка/api/similarity/                        url = '/'.join(['http://rusvectores.org/araneum_none_fasttextcbow_300_5_2018',                                         word1 + '__' + word2, 'api', 'similarity/'])                        # GET request to API rusvectores.org                        r = requests.get(url, stream=True)                        # sum similarity of couple of words                        similarity = similarity + float(r.text.split('\t')[0])    except Exception as e:        log_exception = str(e)    # average similarity    similarity = similarity/len(text2_words)    # return similarity between text1 and text2    return similarity

Как видно из кода, с помощью метода write_current_question_number_for_user() бот сохраняет в базу данных текущую позицию в диалоге с пользователем. Это необходимо для того, чтобы бот понимал какой следующий вопрос нужно задать респонденту.
Функция get_question_from_DB() возвращает текст вопроса из базы данных для того, чтобы бот задал его в чате.

Для удобства приведу полный код модуля с методами для работы с базой данных:

mysqldb_methods.py
полный код модуля, в котором реализованы все методы для работы с MySQL базой данных

# -*- coding: utf-8 -*-"""Methods for work with MySQL database."""import MySQLdb  # before using it do in ssh: pip install mysqlclient""" import configuration variables for connect to MySQL database:"""from mysqldb_config import HOSTfrom mysqldb_config import USERfrom mysqldb_config import PASSWORDfrom mysqldb_config import DATABASEdef write_current_question_number_for_user(user_id, question_num):    """    write question number to database for this user    """    try:        conn = MySQLdb.connect(host=HOST, user=USER, passwd=PASSWORD,                                db=DATABASE, charset='utf8', init_command='SET NAMES UTF8')        cursor = conn.cursor()        if question_num == '2':            query = (                "INSERT INTO `conversations`(`user_id`, `question_num`) "                "VALUES (%s, %s)"            )            data = (user_id, question_num)        else:            query = (                "UPDATE `conversations` "                "SET `question_num`=%s "                "WHERE `user_id`=%s "            )            data = (question_num, user_id)        cursor.execute(query,data)        conn.commit()  # commit transaction        conn.close()    except Exception as e:        exception = str(e)def get_current_position_in_conversation(user_id):    """    find in database current position in conversation between user and chatbot    using in bot_methods.py    """    try:        conn = MySQLdb.connect(host=HOST, user=USER, passwd=PASSWORD,                                db=DATABASE, charset='utf8', init_command='SET NAMES UTF8')        cursor = conn.cursor()        query = "SELECT `question_num` FROM `conversations` WHERE `user_id`=%(user_id)s LIMIT 1"        cursor.execute(query, {'user_id': user_id})        result = cursor.fetchone()        if result is None:            identi = '0'        else:            identi = result[0]        conn.close()    except Exception as e:        identi = 'err'        return identidef get_question_from_DB(question_num):    """    return question text from database    """    try:        conn = MySQLdb.connect(host=HOST, user=USER, passwd=PASSWORD,                                db=DATABASE, charset='utf8', init_command='SET NAMES UTF8')        cursor = conn.cursor()        query = "SELECT `question_text` FROM `questions` WHERE `question_num`=%(num)s LIMIT 1"        cursor.execute(query, {'num': question_num})        result = cursor.fetchone()        if result is not None:            question_text = result[0]        else:            question_text = "None"        conn.close()    except Exception as e:        question_text = str(e)        return question_text

Теперь для полноты картины приведу код скрипта, который резюмирует всю логику работы чат-бота.

скрипт views.py
"точка входа" для приёма сообщений пользователя и отправки ответов бота в чат

# -*- coding: utf-8 -*-from __future__ import unicode_literalsimport jsonimport threading  # for async executing tasks with VK APIimport vk  # vk is library from VKfrom django.views.decorators.csrf import csrf_exemptfrom django.shortcuts import renderfrom django.http import HttpResponsefrom bot_config import *  # import token, confirmation_token and over constants from bot_config.pyfrom bot_methods import get_bot_answer@csrf_exempt  # exempt index() function from built-in Django protectiondef index(request):  # requested url    if (request.method == "POST"):        data = json.loads(request.body)  # take POST request from auto-generated variable <request.body> in json format        if (data['secret'] == secret_key):  # if json request contain secret key and it's equal my secret key            if (data['type'] == 'confirmation'):  # if VK server request confirmation                """                For confirmation my server (webhook) it must return                confirmation token, which issuing in administration web-panel                your public group in vk.com.                Using <content_type="text/plain"> in HttpResponse function allows you                response only plain text, without any format symbols.                Parameter <status=200> response to VK server as VK want.                """                # confirmation_token from bot_config.py                return HttpResponse(confirmation_token,                                     content_type="text/plain",                                     status=200)            if (data['type'] == 'message_new'):  # if VK server send a message                # t - is new thread to async execute answer_to_message()                t = threading.Thread(target=_answer_to_message, args=(data,))                t.start()                return HttpResponse('ok', content_type="text/plain", status=200)    else:        return HttpResponse('see you :)')# send anser to user messagedef _answer_to_message(data):    session = vk.Session()    api = vk.API(session, v=5.5)    user_id = data['object']['user_id']    user_message = data['object']['body']    # get bot answer    answer = get_bot_answer(user_id, user_message)    # token from bot_config.py    api.messages.send(access_token = token, user_id = str(user_id), message = answer)

О том как устроена структура файлов приложения, его настройка описывал в подробностях в отдельной статье на Хабре.

Успехов!

Уверен, что для решения изложенной задачи существуют более элегантные решения и подходы. Несмотря на это надеюсь что изложенный материал найдёт своего читателя и будет полезен как в целом, так и в каких-то аспектах.
Желаю всем интересных проектов и успехов в их реализации!

Подробнее..

Сам себе Гутенберг. Часть 2. Делаем многоязычные параллельные книги

02.06.2021 20:09:34 | Автор: admin

Lingtrain parallel books article cover


В прошлый раз мы с вами научились делать параллельные книги и сделали русско-английский вариант отрывка романа Харпер Ли "Убить пересмешника". Сегодня мы сделаем следующий шаг и создадим полноценную многоязычную книгу на восьми языках.


Напомню, что целью этого проекта является создание инструмента, который поможет людям, изучающим и преподающим иностранные языки, создавать учебные материалы и параллельные книги для улучшения навыков чтения. Глобальная проблема состоит в том, что трудно найти интересный для изучения материал с параллельным переводом, тогда как найти книгу в оригинале и ее русскую редакцию по отдельности гораздо проще.


Сначала сделаем семь книг


Проделаем все на примере романа Булгакова "Мастер и Маргарита", потому что он очень популярен в мире и был переведен на множество языков. Я нашел его версии на английском и немецком (германские языки); белорусском, чешском и украинском (славянские языки); а также на венгерском и китайском языках. Все семь текстов мы выровняем с русским оригиналом, получим семь книг. Затем выровняем их между собой и получим возможность выбирать любые комбинации языков для своей книги в любом порядке. Полные версии книг можно будет скачать в формате pdf в конце статьи.


Начнем с пары венгерский-русский.


hungarian


Шаг 1. Подготовка текстов


Кратко напомню основные правила подготовки текстов (подробнее смотрите в первой статье, ссылку найдете ниже).


Правила


  1. Удалить заведомо лишние строки (информацию об издателе, посвящение, номера страниц, примечания).
  2. Проставить метки для автора и названия.
  3. Проставить метки для заголовков (H1 самый большой, H5 самый маленький), количество заголовков в текстах должно быть одинаковым.
  4. Убедиться, что в тексте нет строк, которые кончаются точкой и при этом не являются концом абзаца (иначе целый абзац разобьется в этом месте на два).

Четвертое правило можно соблюсти для одного текста, так как при создании книги можно будет выбрать на основе какого из них брать информацию о разбиении на абзацы. В нашем случае возьмем русский, потому что мы будем выравнивать все остальные тексты к нему. К тому же для найденного мной оригинального текста это правило уже соблюдено.


Метки


Язык разметки представляет из себя набор меток, которые ставятся в конце предложения:


Метка Значение Установка
%%%%%title. Название произведения Вручную
%%%%%author. Автор Вручную
%%%%%h1. %%%%%h2. %%%%%h3. %%%%%h4. %%%%%h5. Заголовки Вручную
%%%%%divider. Разделитель Вручную
%%%%%. Новый абзац Автоматически

Благодаря меткам мы извлечем из сырого текста данные о названии произведения, его авторе, названии и местонахождении глав. Это понадобится нам при создании книги.


Текст


В результате должны получиться тексты, похожие на следующие:


Мастер и Маргарита%%%%%title.М.А. Булгаков%%%%%author.ЧАСТЬ ПЕРВАЯ%%%%%h1.Глава 1%%%%%h2.Никогда не разговаривайте с неизвестными%%%%%h2.Однажды весною, в час небывало жаркого заката, в Москве, на Патриарших прудах, появились два гражданина. Первый из них, одетый в летнюю серенькую пару, был маленького роста, упитан, лыс, свою приличную шляпу пирожком нес в руке, а на хорошо выбритом лице его помещались сверхъестественных размеров очки в черной роговой оправе. Второй  плечистый, рыжеватый, вихрастый молодой человек в заломленной на затылок клетчатой кепке  был в ковбойке, жеваных белых брюках и в черных тапочках.   ...



MIHAIL BULGAKOV%%%%%author.A MESTER S MARGARITA%%%%%title.ELS KNYV%%%%%h1.ELS FEJEZET%%%%%h2.Ne lljunk szba ismeretlenekkel%%%%%h2.Egy meleg tavaszi estn, az alkonyat rjban, a Patriarsije Prudin kt frfijelent meg. Az egyik negyvenves forma, kvrks, alacsony, kopasz feketeemberke, szrke nyri ltnyt viselt, elegns kalapjt kezben tartotta, gondosanborotvlt arct istentelen nagy mret, fekete csontkeretes ppaszem kestette.Trsa jval fiatalabb s vllasabb volt nla, borzas hajn tarkjig htratolt kockssapka; ltzke kocks sportingbl, gyrtt fehr nadrgbl, fekete szandlblllt....

Количество меток одного типа должно быть одинаковым среди всех текстов. Иначе где-то будет номер и название главы, а где-то нет. После того как мы приготовили тексты, переходим к выравниванию.


Шаг 2. Выравнивание


Как и в прошлый раз, выравнивать будем библиотекой lingtrain-aligner. Код открыт, так что приглашаю в репозиторий.


Попробовать в Colab


Чтобы попробовать выровнять свои тексты без написания кода используйте вот этот Colab блокнот, который мы использовали в прошлый раз. В нем есть инструкции и некоторые комментарии к процессу. В конце можно будет скачать получившуюся двуязычную параллельную книгу как html страничку.


Попробовать локально


pip install lingtrain-aligner

Сначала немного служебного кода:


# Импортируем необходимые модулиfrom lingtrain_aligner import preprocessor, splitter, aligner, resolver, reader, vis_helper# Подготовленные тексты для выравниванияtext1_input = "master_hu.txt"text2_input = "master_ru.txt"with open(text1_input, "r", encoding="utf8") as input1:text1 = input1.readlines()with open(text2_input, "r", encoding="utf8") as input2:text2 = input2.readlines()# Путь до самого главного файла, в котором будет содержаться вся информация о выравниванииdb_path = "master_hu.db"# Определим языки (важно для разбиения на предложения)lang_from = "hu"lang_to = "ru"# Выберем модельmodels = ["sentence_transformer_multilingual", "sentence_transformer_multilingual_labse"]model_name = models[0]# Добавим метки абзацевtext1_prepared = preprocessor.mark_paragraphs(lines1_prepared)text2_prepared = preprocessor.mark_paragraphs(lines2_prepared)# Разобьем тексты на строкиsplitted_from = splitter.split_by_sentences_wrapper(lines1_prepared, lang_from, leave_marks=True)splitted_to = splitter.split_by_sentences_wrapper(lines2_prepared, lang_to, leave_marks=True)

Итого у нас получилось 9460 предложений на венгерском и 8996 предложений на русском. Их-то мы и хотим сопоставить друг другу таким образом, чтобы ничего не потерялось.


Создадим файл для выравнивания. Это SQLite база данных, которую мы заполним входными данными, для дальнейшей обработки.


aligner.fill_db(db_path, lang_from, lang_to, splitted_from, splitted_to)

Чтобы выровнять первую тысячу строк, выполним следующую команду:


batch_ids = range(10)aligner.align_db(db_path,                 model_name,                 batch_size=100,                 window=60,                 batch_ids=batch_ids,                 save_pic=False,                embed_batch_size=25,                 normalize_embeddings=True,                 show_progress_bar=True                )

Произойдет следующее мы запустим процесс выравнивания (детали смотрите в предыдущей статье), для 10 отрезков (batch_ids их номера) по 100 предложений (параметр batch_size). Параметр window задает "перехлест" между этими кусками, чтобы наверняка захватить целевые предложения. В данном случае этот параметр довольно большой, потому что на протяжении всей книги наша "ось выравнивания" может довольно значительно отклоняться.


В качестве спойлера скажу, что для большинства языков в данном случае такие настройки подойдут для всей книги. Для некоторых же текстов отклонения будут слишком сильными и нужно будет либо делать окно очень большим (это приведет к большему количеству ошибок и отрицательно скажется на качестве), либо двигаться медленнее и использовать параметр shift (о нем ниже).


Визуализация


Теперь у нас есть возможность посмотреть на результат первичного выравнивания при помощи модуля vis_helper:


vis_helper.visualize_alignment_by_db(db_path,            output_path="alignment_vis.png",            batch_size=100,            size=(800,800),            lang_name_from=lang_from,            lang_name_to=lang_to,            batch_ids=batch_ids,            plt_show=True)

Lingtrain parallel books


В параметр batch_ids мы передали те же номера батчей, что и при выравнивании. Если не передавать ничего, то получим картинки для всех данных.


Мы видим, что модель справилась довольно неплохо. Главное здесь, чтобы "ось выравнивания" не вышла за пределы окна, иначе модель не получит на вход отрезок со всеми правильными вариантами.


Теперь необходимо поставить на место выбросы.


Разрешение конфликтов


Механим разрешения конфликтов заключен в модуле resolver. Подробнее о нем, опять же, смотрите в предыдущей статье. Скажу лишь, что он находит промежутки между удачными отрезками выравнивания, и пытается подобрать для этих промежутков подходящий вариант разрешения. Соответственно, параметрами мы можем задавать минимальную длину хорошей цепочки и максимальную длину для конфликта. На практике, хорошо работает такой подход трижды проходим по всему выравниванию с постепенным увеличением максимальной длины конфликта:


steps = 3batch_id = -1 #пройти по всем батчамfor i in range(steps):    # Найти конфликты    conflicts, rest = resolver.get_all_conflicts(db_path, min_chain_length=2+i, max_conflicts_len=6*(i+1), batch_id=batch_id)    # Показать какие конфликты найдены    resolver.get_statistics(conflicts)    resolver.get_statistics(rest)    # Разрешить конфликты    resolver.resolve_all_conflicts(db_path, conflicts, model_name, show_logs=False)    if len(rest) == 0:        break

В конце могут остаться единичные конфликты. Если такие есть, то добиваем их:


# Найти конфликтыconflicts, rest = resolver.get_all_conflicts(db_path, min_chain_length=2, max_conflicts_len=20, batch_id=-1)# Показать какие конфликты найденыresolver.get_statistics(conflicts)resolver.get_statistics(rest)# Разрешить конфликтыresolver.resolve_all_conflicts(db_path, conflicts, model_name, show_logs=False)# Визуализировать выравниваниеvis_helper.visualize_alignment_by_db(db_path,            output_path="alignment_vis.png",            batch_size=100,            size=(800,800),            lang_name_from=lang_from,            lang_name_to=lang_to,            batch_ids=batch_ids,            plt_show=False)

Lingtrain parallel books


Цикл


Мы выровняли часть книги. Для продолжения выполним те же дейсвтия, но с другими батчами. Возьмем следующие 4000 строк. Мы выравнивали 10 кусков по 100 предложений, поэтому выполним следующую команду:


batch_ids = range(10, 50)aligner.align_db(db_path, \                model_name, \                batch_size=100, \                window=60, \                batch_ids=batch_ids, \                save_pic=False,                embed_batch_size=25, \                normalize_embeddings=True, \                show_progress_bar=True                )

После выравнивания этого куска (это займет чуть больше времени, на моем компьютере это около пяти минут) переходим к разрешению конфликтов, как было описано выше. Затем дальше, задавая новый диапазон строк. Так как предложений на венгерском у нас 9460, а разбитие идет по первому из заданных текстов, то батчей по 100 предложений у нас будет 95 штук.


Если задать диапазон


batch_ids = range(50, 100)

то мы выровняем остаток (вторую половину) книги. После разрешения всех конфликтов у нас останется файл master_hu, содержащий в себе венгерско-русскую версию "Мастера и Маргариты".


Шаг 3. Создание двуязычной книги


Теперь, если мы воспользуемся модулем reader, то сможем сгенерировать русско-венгерскую редакцию книги:


from lingtrain_aligner import reader# Читаем из абзацы и метаданныеparagraphs_from, paragraphs_to, meta = reader.get_paragraphs(db_path, direction="from")# Создаем html с нашей книгойreader.create_book(paragraphs_from, paragraphs_to, meta, output_path = "lingtrain.html")

Параллельная книга на венгерском


Это обыкновенная html страничка со встроенными стилями. Для визуализации связей между предложениями можно задавать стили. Например, такой:


reader.create_book(paragraphs_from, paragraphs_to, meta, output_path = f"lingtrain.html", template="pastel_fill")

Параллельная книга на венгерском


Можно задавать свои стили, они будут применены к предложениям каждого абзаца циклически:


# Любые применимые к span'ам стилиmy_style = [    '{"background": "linear-gradient(90deg, #FDEB71 0px, #fff 150px)", "border-radius": "15px"}',    '{"background": "linear-gradient(90deg, #ABDCFF 0px, #fff 150px)", "border-radius": "15px"}',    '{"background": "linear-gradient(90deg, #FEB692 0px, #fff 150px)", "border-radius": "15px"}',    '{"background": "linear-gradient(90deg, #CE9FFC 0px, #fff 150px)", "border-radius": "15px"}',    '{"background": "linear-gradient(90deg, #81FBB8 0px, #fff 150px)", "border-radius": "15px"}'    ]reader.create_book(paragraphs_from, paragraphs_to, meta, output_path = f"lingtrain.html", template="custom", styles=my_style)

Параллельная книга на венгерском


Выравниваем остальные книги


Таким образом, повторяя третий шаг для остальных текстов, мы получим помимо файла master_hu.db остальные шесть. Отмечу некоторые трудности, с которыми я столкнулся (и, вероятно, столкнетесь вы, когда будете выравнивать свою книгу).


Большая разница в количестве предложений


В английской редакции большое количество предложений в диалогах было переведено как отдельные части, тогда как в оригинале это была прямая речь.


1.  Фу ты черт!  воскликнул редактор,  ты знаешь, Иван, у меня сейчас едва удар от жары не сделался!2. - Даже что-то вроде галлюцинации было,  он попытался усмехнуться, но в глазах его еще прыгала тревога, и руки дрожали.



1. 'Pah, the devil!' exclaimed the editor.2. 'You know, Ivan, I nearly had heat stroke just now!3. There was even something like a hallucination...'4. He attempted to smile, but alarm still jumped in his eyes and his hands trembled.

В итоге имеем 9707 предложений на английском против 8996 на русском. Это довольно большое различие, поэтому после первичного выравнивания с параметром window=100 получаем более 1000 конфликтов.


english1

К счастью, после разрешения конфликтов эти кусочки все равно склеятся обратно.


english2

Такая же ситуация с китайским языком, там в целом есть тенденция перевода более короткими предложениями чем в оригинале.


Выход за пределы окна


Упомяну польский текст, который тоже был найден, но не подошел по следующей причине. В польском тексте (8190 предложений) у нас слишком часто отсутствуют целые куски текста. Поток выравнивания получается местами обрывистым и окна в 100 уже не хватает, мы постоянно выходим за его пределы:


batch_ids = range(10,12)aligner.align_db(db_path, \                model_name, \                batch_size=200, \                window=100, \                batch_ids=batch_ids, \                save_pic=False,                embed_batch_size=25, \                normalize_embeddings=True, \                show_progress_bar=True                )

english1

english1

Дальнейшее расширение окна увеличит процент ошибок, поэтому мы перейдем в ручной режим и задействуем параметр shift. Этот параметр позволяет сдвигать поток второго текста при расчете батча:


batch_ids = range(10,12)aligner.align_db(db_path, \                model_name, \                batch_size=200, \                window=100, \                batch_ids=batch_ids, \                save_pic=False,                embed_batch_size=25, \                normalize_embeddings=True, \                show_progress_bar=True,                shift=-100 #сдвигаем русский текст                )

polish

polish

Плюсом такого подхода является то, что мы можем уменьшить окно, улучшив первоначальное качество. Минусом является большее количество итераций.


После разрешения конфликтов эти же куски выглядят так:


polish

polish

Отсутствие частей текста


Ручной режим не поможет нам восстановить разрывы, если в каком-то тексте нет соответствующего куска:


polish

polish

Разрешение таких конфликтов приведет к тому, что какому-то предложению станет соответствовать большой отрывок текста на другом языке. Если таких ситуаций одна-две на весь текст (так было с английским и белорусским текстами), то, в принципе, ничего страшного. В случае польского же отсутствовало более 20 отрывков, поэтому я решил его не добавлять.


Второй уровень выравнивания


К этому моменту мы получили семь файлов с выравниваниями master_*.
Их особенностью является то, что все они выровнены относительно одного языка (русского). Казалось бы, давайте просто возьмем абзацы и сходу получим необходимые локализации.


Делаем так и спустя несколько страниц получаем вот это:


second level

Дело в том, что в некоторых текстах (на рисунке это немецкий) одно предложение могло соответствовать двум русским. Если при этом русские предложения считались разными абзацами, то эти абзацы будут слиты в один. Поэтому в обычной двуязычной книжке все будет хорошо, но относительно друг друга структура абзацев у выравниваний будет отличаться. На помощь приходят метаданные, которые хранятся в базе (наши *.db файлы).


Метаданные


В метаданных хранится информация о том, какое предложение какому абзацу соответствует. Можно получить структуру абзацев для каждого текста в виде массива чисел, показывающих номер последнего предложения в абзаце.
Эти массивы можно слить в один по принципу объединения более мелких интервалов:


par_struct_1 = [2, 5, 6, 8, 10, 12, ...]par_struct_2 = [2, 6, 8, 10, 11, 12, ...]res = [2, 6, 8, 10, 12, ...]

Сгенерируем книгу на основе новой разбивки, получим следующий результат:


second level

Стало выглядеть лучше, теперь абзацы формируются относительно русского языка на единой основе.


Индекс


Абзацы починили, но обратим внимание на подсветку предложений и увидим, что во многих местах она разъехалась:


index

Причина этого в том, что разные тексты имеют разные тенденции по делению на предложения. Чтобы синхронизировать еще и их, придется немного повозиться со следующей сущностью, с индексом.


На архитектурном этапе я придумал хранить структуру выравнивания как индекс. После выравнивания он выглядит он так:


[    --первый батч    [        --первая пара предложений корпуса        [1, "[1,2]", 1, "[1]"],        --вторая и т.д.        [2, "[3]", 2, "[2]"],        [3, "[4,5]", 3, "[3]"],        [4, "[6]", 4, "[4,5,6]"],        ...    ],    --второй батч    [        [1, "[100]", 1, "[105]"],        ...    ],    ...]

В этом примере в первом элементе мы склеили два оригинальных предложения "левого" текста и сопоставили ему первое предложение "правого". Числа в кавычках ссылки на оригинальные предложения, вне кавычек ссылки на фактические данные, которые можно редактировать.


Такая структура дает много возможностей:


  • сохраняется информация о слиянии оригинальных строк
  • можно добавлять новые элементы в любое место без перезаписи идентификаторов фактических строк
  • можно удалять элементы из параллельного корпуса, удалив только из индекса
  • можно редактировать данные, не меняя оригинальные строки
  • можно пересчитывать отдельные батчи, не затрагивая остальные

Главное для нас здесь то, что из индекса мы можем вытащить структуру элемента корпуса относительно русского языка. Сопоставляя абзацы друг другу, мы опять будем сливать по минимальному принципу. В результате получим разбивку такого типа:


[[1], [2], [3]] -> [[1, 2, 3], [4], [5]] -> [[1], [2], [3]][[4]] -> [[6]] -> [[4]][[5]] -> [[7]] -> [[5, 6]]...

Здесь каждая строка это абзац на трех языках, каждый подмассив а абзаце это номера строк из целевого текста. Таким образом мы поделили каждый набор абзацев на одинаковое число соответствующих друг другу частей.


После исправлений


Посмотрим, как станет выглядеть подсветка теперь:


book

Так как мы объединяем структуру по минимальному принципу (в данном примере в китайском было меньше всего предложений на абзац), то подсветка стала ровнее, но беднее. Если убрать китайский, то она станет выглядеть так:


book

Шаг 4. Создание многоязычной книги


В большинстве случаев будет достаточно создания двуязычной параллельной книги и методов get_paragraphs() и create_book(). Но если вы дочитали до этого момента, то самое время попробовать сделать многоязычную книгу.


Логика по выравниванию абзацев для многоязычных книг содержится в методе get_paragraphs_polybook(), в который мы передадим все наши файлы. Так как при выравнивании система опирается на номера исходных строк, то в файлах не должно остаться конфликтов, иначе абзацы не будут сгенерированы. Проверить на отсутствие конфликтов можно таким кодом (как их разрешать вы уже знаете):


from lingtrain_aligner import resolverconflicts_to_solve, rest = resolver.get_all_conflicts(db_path, min_chain_length=2, max_conflicts_len=20, batch_id=-1)#перед генерацией не должно остаться никаких конфликтовresolver.get_statistics(conflicts_to_solve)resolver.get_statistics(rest)

Далее получим набор подготовленных текстов на всех нужных языках:


from lingtrain_aligner import readerdb_path1 = "db/master_de.db"db_path2 = "db/master_uk.db"db_path3 = "db/master_be.db"db_path4 = "db/master_zh.db"db_path5 = "db/master_cz.db"db_path6 = "db/master_en.db"db_path7 = "db/master_hu.db"paragraphs, metas = reader.get_paragraphs_polybook(                        db_paths=[db_path1, db_path2, db_path3, db_path4, db_path5, db_path6, db_path7])

Теперь самое интересное, мы можем выбрать нужные нам языки и создать нашу книжку. Можно, например, взять английский и немецкий, многие учат эти языки в паре.


reader.create_polybook(                lang_ordered=["en", "de", "ru"],                paragraphs = paragraphs,                delimeters = delimeters,                metas = metas,                output_path = "lingtrain_master.tml",                template="none")

book

Параметр lang_ordered задает набор и порядок языков.

Можно сделать белорусско-китайскую версию:


reader.create_polybook(                lang_ordered=["be", "zh"],                paragraphs = paragraphs,                delimeters = delimeters,                metas = metas,                output_path = "lingtrain_master.html",                template="none")

book

Можно, конечно, и всё взять:


reader.create_polybook(                lang_ordered = ["ru", "en", "de", "be", "uk", "cz", "hu", "zh"],                paragraphs = paragraphs,                delimeters = delimeters,                metas = metas,                output_path = "lingtrain_master_total.html",                template="none")

book

Лучше оставлять те выравнивания (*.db файлы), языки которых, вам нужны. Это позволит сохранить как можно больше информации об абзацах. Со стилями все как раньше, можете задавать template (сейчас есть "pastel_fill", "pastel_start"), можете придумывать свои, примеры есть выше и в блокноте на Colab. Если задать template="none", получится чистая книга.

Планы и поддержка


  • Дальше планируется поддержать fb2 формат для читалок и добавить подстрочный перевод. Также хочу расширить возможности разметки, добавить вставку картинок. Затем оформить это в виде веб-приложения, чтобы люди могли создавать книги без написания кода.


  • Скидывать тексты для тестирования и выравнивания, делиться предложениями и багами можно в канале http://t.me/lingtrain_books.


  • Подержать проект финансово можно переводом. Деньги пойдут на хостинг веб-приложения и ускорят его разработку.



Ссылки


Все упомянутые в статье ссылки:


[1] Первая часть статьи


[2] Код lingtrain-aligner на github


[3] Google Colab блокнот с выравниванием


[4] Кот Бегемот с обложки был найден здесь


[5] PDF. Семь параллельных книг "Мастер и Маргарита"


[6] Телеграм-группа


[7] Сбор средств на проект

Подробнее..

Как адаптировать языковые модели Kaldi? (со смешными животными)

24.05.2021 14:14:07 | Автор: admin


Как научить русскоязычную модель распознавать речь геймеров? Подобными вопросами задаются те, кто увлекается и занимается NLP. В частности, NLP-специалистов интересует, как можно адаптировать русскоязычную модель vosk под их предметную область, чтобы улучшить качество распознавания. Это мы и разберём в данной статье.


Привет! Приглашаю вас кушать блины и распознавать речь

Сейчас можно легко заставить компьютер распознавать обычную устную речь, благо, есть пакет vosk, который является человечной обёрткой (wrapperом) к предобученным моделям Kaldi. Alphacephei и Николай Шмырёв проделали колоссальное количество работы по продвижению опен-сорса в распознавании русскоязычной речи, и vosk, пожалуй, венец всего их труда. Большая модель vosk-ru для распознавания устной русской речи без всяких доработок может решать множество задач распознавания речи.

По умолчанию большая модель vosk-ru предназначена для распознавания обычных разговорных слов и синтаксических конструкций. Однако, когда появляется необходимость распознавать другие слова и другие языковые конструкции, которые не предусмотрены моделью vosk-ru по умолчанию, качество распознавания заметно ухудшается. Если таких конструкций немного, то можно выстроить соответствие между тем, что нужно распознать, и тем, что распознаётся на самом деле. Например, текущая модель vosk-model-ru-0.10 не умеет распознавать слово коронавирус, но распознает отдельные слова: корона и вирус. В подобных случаях нам будет предоставлен своеобразный ребус, который нам, со своей стороны, нужно будет решить программно. К сожалению, на ребусах далеко не уехать.

Собственно, как здорово, что все мы здесь сегодня собрались научиться адаптировать модель vosk-ru. Для этого существуют пути адаптации:



Для того чтобы понять, какие именно компоненты нам нужно будет модифицировать или заменять, определимся, какая именно проблема перед нами стоит. Есть несколько ситуаций, при которых следует по-разному действовать при работе с Kaldi:



Словарь в реалиях Kaldi это список слов, которые мы хотим уметь распознавать, с соответствующими им фонетическими транскрипциями. Словарь является связующим звеном между акустической и языковой моделями.

Спонтанная речь применительно к распознаванию речи это манера высказывать информацию, которая не имеет какой-то заданной заранее структуры. То есть, когда мы ожидаем реплики от человека, мы не знаем, какой будет структура будущего высказывания. Спонтанная речь, как правило, моделируется статистическими моделями, в том числе и моделями машинного обучения. N-граммная модель ARPA является классической разновидностью таких моделей.

Бывает и обратная ситуация: мы знаем и ожидаем фразу определённой структуры от человека, в таком случае используются вручную построенные грамматики. Одна из распространённых разновидностей грамматик грамматика речевых команд, когда человек может сказать только одну из n фраз в определённый момент времени с одинаковой вероятностью.

Соответственно, в Kaldi есть два основных способа проектирования языковых моделей: ARPA LM и грамматика FST:



В зависимости от специфики задачи в статье будет предложено сформировать один из видов языковой модели, поэтому к более детальному разбору форматов приступим позже.


Мопс выделяет частоты в сигнале, на которых находится речь

СТРУКТУРА VOSK-MODEL-RU-0.10

Начнём работу с разбора того, как устроена модель для vosk. То, что нам НЕ понадобится при файн-тюнинге выделено курсивом.

vosk-model-ru-0.10
\__ am сокращённо от Acoustic Model. Содержит модель распознавания звуков (фонем)
\__ conf папка с файлами конфигураций для запуска модуля
\__ graph графы для описания вероятностей переходов от одной фонемы к другой. Содержит информацию о заученных переходах фонем, а также переходы с учётом языковой модели
\__ ivector папка с сохранёнными голосовыми слепками из обучающей выборки
\__ rescore n-граммная языковая модель для переопределения цепочек слов
\__ rnnlm языковая модель на основе рекуррентной нейронной сети для дополнительного переопределения цепочек слов
\__ decode.sh исполняемый файл для запуска моделей с помощью инструментов Kaldi
\__ decoder-test.scp, decoder-test.utt2spk служебные файлы для распознавания пробного файла
\__ decoder-test.wav пробный файл
\__ README документация


Когда мы хотим адаптировать vosk модель для конкретной задачи распознавания речи, наша главная цель корректно подменить файл ./graph/HCLG.fst. Но как именно подменить и какие файлы использовать для конечной генерации этого графа, целиком зависит от поставленной задачи. Примеры задач представлены выше, таким образом вы можете соотнести свою задачу с представленным пулом задач и понять, какие от вас требуются действия для эффективной адаптации.

В основном шаги будут общие, но в некоторые моменты алгоритм действий будет расходиться в зависимости от задачи. Для этого мы присвоили метки разных цветов четырём способам адаптации, упомянутым выше. Повторюсь: каждый из этих способов может использоваться изолированно, а может и в ансамбле с другими. Это полностью зависит от вашей цели, поэтому обязательно заранее определитесь с методом адаптации, который вы хотите использовать для своей задачи.

Модификация словаря
Замена словаря
Модификация ЯМ
Замена ЯМ

Ну что ж, приступим?


Этот хороший мальчик готов адаптировать модель распознавания речи, а вы?

УСТАНОВКА KALDI

Прежде всего нам нужно поставить Kaldi на нашу рабочую машину (Linux или Mac). Благо, делается это весьма просто:

git clone https://github.com/kaldi-asr/kaldi.gitcd kaldi/tools/./extras/check_dependencies.shmake -j 4 # тут в качестве параметра указываете количество параллельных процессов при установкеcd ../src/./configure --sharedmake depend -j 4 # аналогичноmake -j 4 # аналогично

В результате установки на машине компилируются зависимости и непосредственно сам Kaldi. Если что-то идёт не так, смотрите логи, гуглите и обращайтесь за помощью в комментарии.

УСТАНОВКА KENLM

Затем нам нужно установить инструмент kenlm для построения статистических языковых моделей. Есть альтернативы для построения статистических языковых моделей, такие как SRILM, он поддерживает бОльшее количество видов сглаживаний для языковых моделей, но он сложнее при установке и использовании. Помимо всего прочего, kenlm можно использовать без ограничений для коммерческих приложений.

git clone https://github.com/kpu/kenlm.gitmkdir -p kenlm/buildcd kenlm/buildcmake ..make -j 4


НАСТРОЙКА ДИРЕКТОРИИ

Для того чтобы нам начать процедуру по адаптации модели, нужно организовать нашу рабочую директорию. Создайте папку в любом удобном месте, папка будет содержать модель vosk-а, на которую, в том числе, можно будет ссылаться из вашего Python-приложения.

# Создаём рабочую директориюmkdir your_asr_project/cd your_asr_project/# Копируем необходимые файлы из модели vosk-аcp -R /path/to/your/vosk-model-ru-0.10/am .cp -R /path/to/your/vosk-model-ru-0.10/conf/ .cp -R /path/to/your/vosk-model-ru-0.10/graph/ .cp -R /path/to/your/vosk-model-ru-0.10/ivector/ .# Копируем необходимые скрипты из рецептов Kaldicp -R /path/to/your/kaldi/egs/mini_librispeech/s5/steps/ .cp -R /path/to/your/kaldi/egs/mini_librispeech/s5/utils/ .cp -R /path/to/your/kaldi/egs/mini_librispeech/s5/path.sh .cp -R /path/to/your/kaldi/egs/mini_librispeech/s5/cmd.sh .

Рекомендуется брать скрипты из рецепта mini_librispeech, так как именно он изначально использовался для обучения vosk-model-ru-0.10.

НАСТРОЙКА ОКРУЖЕНИЯ

Предыдущим шагом мы установили все зависимости. Теперь необходимо прописать пути к нашим зависимостям. Это делается через файл path.sh, который мы только что скопировали:

./path.sh
export KALDI_ROOT=/path/to/your/kaldi # Здесь указываете путь до вашего Kaldi[ -f $KALDI_ROOT/tools/env.sh ] && . $KALDI_ROOT/tools/env.shexport PATH=$PWD/utils/:$KALDI_ROOT/tools/openfst/bin:$PWD:$PATH[ ! -f $KALDI_ROOT/tools/config/common_path.sh ] && echo >&2 "The standard file $KALDI_ROOT/tools/config/common_path$. $KALDI_ROOT/tools/config/common_path.shexport LC_ALL=C# For now, don't include any of the optional dependenices of the main# librispeech recipe

Не забываем сделать этот файл исполняемым и выполняем его. Каждый раз, когда открывается консоль, нам необходимо запускать path.sh и выполнять адаптацию.

НАСТРОЙКА ОКРУЖЕНИЯ ДЛЯ KENLM

Чтобы kenlm также был доступен из вашей рабочей директории, нужно определить до него путь. Можно отдельно выполнять эту строку в командной строке или прописать в path.sh:

export PATH=$PATH:/path/to/your/kenlm/build/bin # Здесь указываете путь до вашего kenlm

Кроме того, нужно обозначить путь до бинарников по работе с языковыми моделями в рамках Kaldi:

export PATH=$PATH:/path/to/your/kaldi/src/lmbin

Итак, мы закончили настраивать наше окружение, пора приступать к самым важным шагам для того, чтобы сгенерировать новый итоговый граф ./graph/HCLG.fst.


Сколько можно настраиваться, давайте уже что-нибудь предметное делать!

КОНФИГУРАЦИЯ СЛОВАРЯ

Итак, здесь начинается самое сложное и интересное: из тех файлов, что нам предоставлены вместе с моделью, предстоит идентифицировать те, которые нам нужны, и привести их в соответствующий формат.

При работе с Kaldi графом называется детерминированный конечный автомат (finite state transducer) в формате openfst. Можно выделить 3 основных графа, с которыми так или иначе приходится иметь дело при обучении и адаптации систем распознавания речи, основанных на Kaldi:
  1. L_disambig.fst граф лексикона, по своей сути фонетический словарь, закодированный в детерминированный конечный автомат.
  2. G.fst граф языковой модели, представляет собой закодированную в детерминированный конечный автомат языковую модель.
  3. HCLG.fst объединение графов лексикона, языковой модели и акустической модели.


Нашей задачей по умолчанию является восстановление графа лексикона (и затем графа языковой модели), который используется vosk-ом при создании итогового графа ./graph/HCLG.fst. Файл с графом HCLG.fst в папке graph поставляется вместе с моделью vosk-model-ru-0.10 по умолчанию.

Итак, для генерации графа лексикона нам нужно создать папку ./data/local/dict, в нее нужно будет добавить несколько файлов:

  1. data/local/dict/lexicon.txt словарь фонетических транскрипций
  2. data/local/dict/extra_questions.txt словарь фонетических вариантов
  3. data/local/dict/nonsilence_phones.txt список значимых фонем
  4. data/local/dict/optional_silence.txt список необязательных обозначений тишины
  5. data/local/dict/silence_phones.txt словарь обозначений тишины


Сейчас подробно разберём, что должно быть в каждом из указанных выше файлов. Начнём со словаря фонетических транскрипций. Словарь фонетических транскрипций был также указан ранее во вводной части. Повторюсь, в таком словаре через пробел указано сначала само слово, а затем поочерёдно фонемы, которые отражают произношение слова. Конкретно в реализации vosk-model-ru можно выделить несколько разновидностей фонем:

  • SIL, GBG неречевые звуки:
    • SIL обозначение тишины
    • GBG обозначение иных любых неречевых звуков
  • a0, e0, i0, безударные гласные
  • a1, e1, i1, ударные гласные
  • bj, dj, fj, мягкие парные согласные
  • c, ch, j, остальные непарные согласные.


Основа для этого словаря поставляется с моделью vosk-model-ru-0.10 в файле ./extra/db/ru.dic. В таком словаре через пробел указано сначала само слово, а затем поочерёдно фонемы, которые отражают произношение слова. Кроме непосредственного содержания этого словаря надо добавить две строки в начало ru.dic: !SIL и [unk]. Начало файла будет следующее:

./data/local/dict/lexicon.txt
!SIL SIL[unk] GBGа a0а a1а-а a0 a1а-а-а a0 a0 a1

Весь дальнейший файл аналогичен ./extra/db/ru.dic, добавлены только две строчки сверху. Изменённый файл нужно сохранить в ./data/local/dict/lexicon.txt.

Затем нужно определить файл extra_questions.txt, который описывает схожести среди разных фонем. Его нужно оформить следующим образом:

./data/local/dict/extra_questions.txt
a0 a1 b bj c ch d dj e0 e1 f fj g gj h hj i0 i1 j k kj l lj m mj n nj o0 o1 p pj r rj s sch sh sj t tj u0 u1 v vj y0 y1 z zh zjSIL GBG

Также нужно определить другие файлы, описывающие различные фонемы и категории, к которым эти фонемы относятся. ./data/local/dict/nonsilence_phones.txt сформирован на основе файла ./graph/phones.txt, но убрана нумерация после пробела и убраны постфиксы у фонем. С помощью этих же фонем описаны все слова (кроме !SIL и [unk]) в lexicon.txt, то есть это наш основной инструмент по описанию обыкновенных русскоязычных слов с точки зрения их произношения. После того как провели сортировку и убрали дубликаты, у нас получается файл ./data/local/dict/nonsilence_phones.txt, первые пять строк которого указаны ниже:

./data/local/dict/nonsilence_phones.txt
a0a1bbjc

Ну и наконец определяем наши мусорные звуки и звук тишины.

./data/local/dict/optional_silence.txt
SIL

./data/local/dict/silence_phones.txt
SILGBG

Следует обратить особое внимание на то, чтобы все строки были однообразно оформлены, чтобы были Linux-овские переносы строк "\n", чтобы все файлы были в кодировке UTF-8. После шагов, обозначенных выше, мы наконец можем выполнять шаги по адаптации нашей модели.


Читающий эту статью, кот и файлы для генерации графа L_disambig.fst


МОДИФИКАЦИЯ СЛОВАРЯ

На этом этапе нам нужно дополнить наш словарь транскрипций другими наименованиями. Сделать это можно, вписав дополнительные строки со словами и их транскрипциями и упорядочив словарь. Транскрипции можно написать вручную, проанализировав те закономерности, которые присутствуют в словаре, но когда новых слов очень много, то это не представляется возможным. Поэтому на подмогу приходит пакет russian_g2p_neuro. Устанавливать и пользоваться данным пакетом предельно просто, для этого скачайте пакет в вашу директорию с сторонними модулями:

git clone https://github.com/DinoTheDinosaur/russian_g2p_neuro.gitcd russian_g2p_neuro/python setup.py install

Этот модуль предобучен на ru.dic, поэтому он формирует новый словарь по образу и подобию изначального словаря для vosk-model-ru-0.10. Чтобы сгенерировать новые транскрипции для списка слов достаточно запустить команду:

generate_transcriptions extra/db/input.txt extra/db/output.dict

В input.txt перечислены в любом виде слова на кириллице (в том числе целые тексты с повторениями), а в output.dict формируется список всех этих слов с соответствующими транскрипциями. Результат output.dict можно совместить с данными из lexicon.txt и сформировать новый расширенный словарь:

mv data/local/dict/lexicon.txt extra/db/lexicon_old.txtcat extra/db/lexicon_old.txt extra/db/output.dict | sort | uniq > data/local/dict/lexicon.txt


ЗАМЕНА СЛОВАРЯ

Как и при модификации словаря, мы можем поменять lexicon.txt, но при этом заменить все исходные транскрипции. Обычно это нужно, если мы хотим уметь распознавать лишь те слова, которые мы определили. Такая ситуация возникает при реализации распознавания команд или в целом при использовании языковых моделей в формате грамматик.

Шаги по установке и использованию те же самые:

git clone https://github.com/DinoTheDinosaur/russian_g2p_neuro.gitcd russian_g2p_neuro/python setup.py installcd /path/to/your_asr_project/generate_transcriptions extra/db/input.txt extra/db/output.dict

Однако последний шаг отличается:

mv data/local/dict/lexicon.txt extra/db/lexicon_old.txtsed s/^/!SIL SIL\n[unk] GBG\n/ extra/db/output.dict > data/local/dict/lexicon.txt

В итоге мы получаем новый lexicon.txt, в котором содержатся только те слова, которые мы хотим распознать.

ФОРМИРОВАНИЕ ГРАФА ЛЕКСИКОНА


Это не граф, это кот

Когда все файлы корректно сформированы, директория наконец-то готова к запуску скрипта utils/prepare_lang.sh из корневой директории вашего проекта по адаптации. Запуск данного скрипта создаст нужный нам граф лексикона под названием L_disambig.fst

utils/prepare_lang.sh --phone-symbol-table graph/phones.txt data/local/dict "[unk]" data/tmp/ data/dict/

Если при запуске скрипт возвращает ошибку, то стоит проверить свои файлы для построения лексикона на правильность. Если не удаётся сформировать данные файлы своими силами, то можно воспользоваться дампом директории локальной dict за дату 04/03/2021 по ссылке в google drive.

По итогу выполнения скрипта можно будет найти нужный нам L_disambig.fst в папке data/dict. После этого можно приступать к модификации и замене языковой модели.

ЗАМЕНА ЯЗКОВОЙ МОДЕЛИ НА N-ГРАММНУЮ

Работу над нашими языковыми моделями будем вести в новой директории ./data/local/lang. Если у вас есть тексты, по аналогии с которыми вы хотите распознавать какие-то фиксированные ключевые фразы, но при этом не хотите распознавать обычную спонтанную речь, то этот пункт для вас. Обычно имеет смысл использовать такой подход, если есть большой массив примеров команд и каких-то кодовых фраз и нет возможности прописать грамматику, которая бы предусмотрела все варианты.

Допустим, что корпус с вашими примерами реплик вы положили в ./extra/db/your.corpus. Начнём с того, что сформируем новую языковую модель с помощью установленного ранее kenlm:

lmplz -o 3 --limit_vocab_file graph/words.txt < extra/db/your.corpus > data/local/lang/lm.arpa

Проясним немного, что в этой команде обозначает каждый из параметров:

  1. -o order, то есть максимальный порядок словесных n-грамм, для которых мы подсчитываем вероятности
  2. --limit_vocab_file словарь, в соответствии с которым фильтруются входные данные. Мы будем использовать этот параметр, если мы не хотим добавлять новых слов в словарь. Если мы не используем этот параметр, то необходимо после построения языковой модели также модифицировать словарь и следовать пунктам, отмеченным
  3. -S 30% не указан, но можно добавить в случае если в системе не хватает памяти на расчёт модели.


По результату выполнения этой команды мы получим файл такого формата:

./data/local/lang/lm.arpa
\data\ngram 1=51515ngram 2=990559ngram 3=3056222\1-grams:-5.968162       [unk]   00       <s>     -2.2876017-1.5350189      </s>    0-2.3502047      а       -0.7859633-3.6979482      банки   -0.42208096-3.9146104      вторую  -0.46862456-2.0171714      в       -1.142168

Языковая модель в формате ARPA построена следующим образом:

  1. Вначале указана шапка \data\, в которой указано количество каждой n-граммы
  2. Затем по очереди указаны все униграммы, биграммы и т.п. с соответствующими им заголовками \1-grams, \2-grams и т.п.
  3. Перечисление n-грамм начинается со значения логарифма вероятности появления последовательности (-3.6979482)
  4. Затем через знак табуляции указана сама последовательность (униграмма банки)
  5. Через ещё один знак табуляции так называемый backoff weight (-0.42208096), который позволяет высчитывать вероятности для последовательностей, которые явным образом не представлены в языковой модели
  6. Заканчивается файл ARPA меткой \end\


Когда у нас готова наша языковая модель, нужно заменить все "&ltunk&gt" обозначения на "[unk]":

sed -i "s/<unk>/[unk]/g" data/local/lang/lm.arpa

Ну и наконец, когда у нас есть готовая ARPA модель, мы можем сгенерировать новый граф языковой модели G.fst и таким образом подготовиться к итоговому объединению всех результатов в HCLG.fst:

arpa2fst --disambig-symbol=#0 --read-symbol-table=data/dict/words.txt data/local/lang/lm.arpa graph/G.fst

В результате выполнения последней команды рядом с графом по умолчанию HCLG.fst мы положили новый граф языковой модели G.fst. Следующим и последним шагом мы генерируем новый итоговый граф HCLG.fst с помощью нового графа языковой модели G.fst.

МОДИФИКАЦИЯ N-ГРАММНОЙ ЯЗКОВОЙ МОДЕЛИ

Когда мы хотим распознавать спонтанную речь, и при этом добавить какие-то необычные речевые конструкции, то можно расширить нашу языковую модель. Благо, вместе с моделью vosk-model-ru-0.10 поставляются сжатые языковые модели в формате ARPA ./extra/db/ru-small.lm.gz и ./extra/db/ru.lm.gz, которые участвовали в формировании модели vosk-model-ru-0.10.

Аналогично предыдущему пункту, мы генерируем нашу новую lm.arpa и заменяем в ней символы "&ltunk&gt":

lmplz -o 4 --limit_vocab_file graph/words.txt < extra/db/your.corpus > data/local/lang/lm.arpased -i 's/<unk>/[unk]/g' data/local/lang/lm.arpa

Обратим ваше внимание, что здесь мы используем другое максимальный порядок n-грамм (параметр -o). Это мы делаем, чтобы продемонстрировать то, как можно объединить две языковых модели в одну, а объединять можно языковые модели только одинакового порядка. Рассмотрим те модели, которые мы имеем на данный момент:

  1. ./extra/db/ru-small.lm.gz 3-граммная ЯМ
  2. ./extra/db/ru.lm.gz 4-граммная ЯМ


Как вы могли догадаться, мы для примера будем объединять нашу модель с большой языковой моделью ru.lm. Для объединения языковых моделей порядка 4 можно воспользоваться следующим кодом merge_lms.py. Если же вы будете объединять свою модель порядка 3 с моделью ru-small.lm, то можно воспользоваться кодом, представленным в данной статье Kaldi ASR: Extending the ASpIRE model в пункте под названием Merging the input files.

Перед использованием извлечём архив с моделью:

gunzip /path/to/your/vosk-model-ru-0.10/extra/db/ru.lm.gz

Использование merge_lms.py из корневой директории проекта:

python utils/merge_lms.py /path/to/your/vosk-model-ru-0.10/extra/db/ru.lm data/local/lang/lm.arpa data/local/lang/lm_joint.arpa

Теперь результат объединения можно конвертировать в граф с помощью команды arpa2fst:

arpa2fst --disambig-symbol=#0 --read-symbol-table=data/dict/words.txt data/local/lang/lm_joint.arpa graph/G.fst

Аналогично предыдущему пункту, G.fst готов, остался последний шаг генерация HCLG.fst.


Братья L_disambig.fst и G.fst

ЗАМЕНА ЯЗКОВОЙ МОДЕЛИ НА ГРАММАТИКУ

Последний способ адаптации языковой модели ручное формирование грамматики. Пример грамматики рассматривался выше во введении. Напомню, чтобы не скроллить:

./graph/G.txt
0 1 [unk] [unk]0 1 да да0 1 нет нет1 0.0

Эта грамматика служит способом выявления конкретных речевых событий команд и распознаёт только 3 команды:

  1. Слово да
  2. Слово нет
  3. Иное слово


События эти равновероятны, и все могут повторяться только один раз. Это пример очень простой грамматики, однако с помощью этого подхода можно задавать куда более сложные структуры. У конкретно такой грамматики 0 является начальной вершиной графа, 1 конечной, но могут быть также промежуточные вершины, может быть несколько конечных состояний, и также можно определять вероятности каждого перехода. Этот граф определяет переходы из начального состояния в конечное по нескольким возможным равновероятным ребрам: [unk], да и нет.

Чтобы сформировать наш уже знакомый и любимый G.fst, нужно преобразовать эту грамматику из текстового вида в бинарный:

fstcompile --isymbols=data/dict/words.txt --osymbols=data/dict/words.txt --keep_isymbols=false --keep_osymbols=false G.txt | fstarcsort --sort_type=ilabel > G.fst

Ура! Теперь и с помощью этого последнего способа мы смогли сгенерировать тот же самый G.fst. Осталось совсем чуть-чуть.

ФОРМИРОВАНИЕ ИТОГОВОГО ГРАФА

Наконец-то мы можем приступить к финальному и самому ответственному моменту: к генерации итогового графа. Делается это ровно одной строкой:

utils/mkgraph.sh --self-loop-scale 1.0 data/lang/ am/ graph/

Теперь ваше персонализированное распознавание речи готово! Достаточно лишь сослаться на вашу рабочую директорию при инициализации модели vosk-а:

from vosk import Modelmodel = Model("/path/to/your_asr_project/")

И далее уже в интерфейсе vosk-а реализовывать распознавание.


Вы заслужили

ПРОДОЛЖЕНИЕ СЛЕДУЕТ...

Надеюсь, статья для вас была полезной и увлекательной. Очень хочется, чтобы технология распознавания речи была несколько более доступной для желающих разобраться в этой теме. Давайте развивать опенсорс распознавания речи вместе! :)
Подробнее..

Перевод Наглядно о том, почему трансформеры работают настолько хорошо

20.06.2021 18:15:44 | Автор: admin

Трансформеры за последние несколько лет штурмом захватили мир NLP, а сегодня они с успехом применяются в выходящих за рамки NLP приложениях. Они обладают такими возможностями благодаря модулю внимания, который схватывает отношения между всеми словами последовательностей. Но самый важный вопрос как именно трансформеры делают это? Попытаемся ответить и понять, почему трансформеры способны выполнять такие вычисления. Итак, цель статьи, чуть сокращённым переводом которой мы делимся к старту курса о машинном и глубоком обучении, разобраться не только с тем, как что-то работает, но и почему работает так. Чтобы понять, что движет трансформерами, мы должны сосредоточиться на модуле внимания. Начнём с входных данных и посмотрим, как они обрабатываются.


Как входная последовательность попадает в модуль внимания

Модуль внимания присутствует в каждом энкодере внутри стека каждого энкодера, а также внутри стека каждого декодера. Сначала внимательно посмотрим на энкодер.

Модуль внимания в энкодереМодуль внимания в энкодере

Для примера предположим, что мы работаем над задачей перевода с английского на испанский, где исходная последовательность слов The ball is blue, а целевая последовательность La bola es azul.

Исходная последовательность сначала проходит через слой векторного представления и позиционного кодирования, генерирующего векторы векторного представления для каждого слова последовательности. Векторное представление передаётся в энкодер, где вначале попадает в модуль внимания.

Внутри модуля внимания последовательность векторного представления проходит через три линейных слоя, создающих три отдельные матрицы запроса (Query), ключа (Key) и значения (Value). Именно эти три матрицы используются для вычисления оценки внимания [прим. перев. оценка определяет, сколько внимания нужно уделить другим частям входного предложения, когда мы кодируем слово в определённой позиции]. Важно помнить, что каждая "строка" этих матриц соответствует одному слову исходной последовательности.

Поток исходной последовательностиПоток исходной последовательности

Каждая входная строка это слово из последовательности

Чтобы понять, что происходит с модулем внимания, мы начнём с отдельных слов исходной последовательности, проследив их путь через трансформер. Если конкретнее, мы хотим сосредоточиться на происходящем внутри модуля внимания. Это поможет нам чётко увидеть, как каждое слово в исходной и целевой последовательностях взаимодействует с другими словами этих последовательностей.

Пока вы разбираетесь с этим объяснением, сосредоточьтесь на том, какие операции выполняются с каждым словом и как каждый вектор отображается на исходное входное слово. Не нужно думать о множестве других деталей, таких как формы матриц, особенности арифметических вычислений, множественное внимание и так далее, если эти детали не относятся напрямую к тому, куда направляется каждое слово. Итак, чтобы упростить объяснение и визуализацию, давайте проигнорируем размерность векторного представления и будем отслеживать только строки для каждого слова.

Расположение каждого слова в исходной последовательностиРасположение каждого слова в исходной последовательности

Каждое слово проходит серию обучаемых преобразований (трансформаций)

Каждая такая строка была сгенерирована из соответствующего исходного слова посредством серии трансформаций векторного представления, позиционного кодирования и линейного слоя. Все эти трансформации возможно обучить; это означает, что используемые в этих операциях веса не определены заранее, а изучаются моделью таким образом, чтобы они давали желаемые выходные прогнозы.

Линейные веса и веса векторного представления обученыЛинейные веса и веса векторного представления обучены

Ключевой вопрос заключается в том, как трансформер определяет, какой набор весов даст ему наилучшие результаты? Держите этот момент в памяти мы вернёмся к нему немного позже.

Оценка внимания это скалярное произведение матрицы ключа и матрицы запроса слов

Модуль внимания выполняет несколько шагов, но здесь мы сосредоточимся только на линейном слое и на оценке внимания.

Многоголовое вниманиеМногоголовое вниманиеРасчёт оценки вниманияРасчёт оценки внимания

Как видно из формулы, первый шаг в рамках модуля внимания умножение матрицы, то есть скалярное произведение между матрицей Query (Q) и транспонированием матрицы ключа Key (K). Посмотрите, что происходит с каждым словом. Итог промежуточная матрица (назовём её факторной матрицей [матрицей множителей]), где каждая ячейка это результат матричного умножения двух слов.

Скалярное произведение матрицы запроса и матрицы ключаСкалярное произведение матрицы запроса и матрицы ключа

Например, каждый столбец в четвёртой строке соответствует скалярному произведению между четвёртым словом запроса и каждым ключевым словом.

Скалярное произведение между матрицами запроса и ключаСкалярное произведение между матрицами запроса и ключа

Оценка внимания скалярное произведение между запросом-ключом и значением слов

Следующим шагом является матричное умножение между этой промежуточной матрицей множителей и матрицей значений (V), чтобы получить оценку внимания, который выводится модулем внимания. Здесь мы можем видеть, что четвёртая строка соответствует четвёртой матрице слов запроса, умноженной на все остальные ключевые слова и значения.

Скалярное произведение между матрицами ключа запроса и значенияСкалярное произведение между матрицами ключа запроса и значения

Получается вектор оценки внимания (Z), который выводится модулем внимания. Выходной результат можно представить следующим образом: для каждого слова это закодированное значение каждого слова из матрицы Значение, взвешенное матрицей множителей. Матрица множителей представляет собой точечное произведение значения запроса для данного конкретного слова и значения ключа для всех слов.

Оценка внимания это взвешенная сумма значения словОценка внимания это взвешенная сумма значения слов

Какова роль слов запроса, ключа и значения?

Слово запроса это слово, для которого мы рассчитываем внимание. В свою очередь слово ключа и значения это слово, на которое мы обращаем внимание, то есть определяем, насколько это слово соответствует слову запроса.

Оценка внимания для слова blue обращает внимание на каждое словоОценка внимания для слова blue обращает внимание на каждое слово

Например, для предложения The ball is blue строка для слова blue будет содержать оценку внимания для слова blue с каждым вторым словом. Здесь blue это слово запроса, а другие слова ключ/значение. Выполняются и другие операции, такие как деление и softmax, но мы можем проигнорировать их в этой статье. Они просто изменяют числовые значения в матрицах, но не влияют на положение каждой строки слов в ней. Они также не предполагают никаких взаимодействий между словами.

Скалярное произведение сообщает нам о сходстве слов

Итак, мы увидели, что оценка внимания отражает некоторое взаимодействие между определённым словом и каждым другим словом в предложении путём скалярного произведения с последующим их сложением. Но как матрица умножения помогает трансформеру определять релевантность между двумя словами?

Чтобы понять это, вспомните, что строки запроса, ключа и значения на самом деле являются векторами с размерностью векторного представления. Давайте посмотрим, как умножаются матрицы между этими векторами.

Каждая ячейка представляет собой скалярное произведение двух векторов словКаждая ячейка представляет собой скалярное произведение двух векторов слов

Для получения скалярного произведения двух векторов мы умножаем пары чисел, а затем суммируем их.

  • Если два парных числа (например, a и d выше) оба положительны или оба отрицательны, произведение положительно. Произведение увеличит итоговую сумму.

  • Если одно число положительное, а другое отрицательное, произведение будет отрицательным. Произведение уменьшит итоговую сумму.

  • Если произведение положительное, то, чем больше два числа, тем больше их вклад в окончательную сумму.

Это означает, что, если знаки соответствующих чисел в двух векторах выровнены, итоговая сумма будет больше.

Как трансформер изучает релевантность между словами?

Скалярное произведение также применимо к оценке внимания. Если векторы для двух слов более выровнены, оценка внимания будет выше. Итак, какого поведения мы хотим от трансформера? Мы хотим, чтобы оценка внимания была высокой для двух релевантных друг другу слов в предложении. И мы хотим, чтобы оценка двух слов, не связанных друг с другом, была низкой.

Например, в предложении The black cat drank the milk слово milk очень релевантно к drank, возможно, немного менее релевантно для cat, и нерелевантно к black. Мы хотим, чтобы milk и drink давали высокую оценку внимания, чтобы milk и cat давали немного более низкую оценку, а для milk и black незначительную. Мы хотим, чтобы модель научилась воспроизводить этот результат. Чтобы достичь воспроизводимости, векторы слов milk и drank должны быть выровнены. Векторы milk и cat несколько разойдутся. А для milk и black они будут совершенно разными.

Давайте вернёмся к вопросу, который мы откладывали: как трансформер определяет, какой набор весов даст ему наилучшие результаты? Векторы слов генерируются на основе векторного представления слов и весов линейных слоёв. Следовательно, трансформер может изучить эти векторные представления, линейные веса и так далее, чтобы создать векторы слов, как требуется выше.

Другими словами, он будет изучать эти векторные представления и веса таким образом, что если два слова в предложении релевантны друг другу, то их векторы слов будут выровнены, следовательно, получат более высокe. оценку внимания. Для слов, которые не имеют отношения друг к другу, их векторы не будут выровнены и оценка внимания будет ниже.

Следовательно, векторные представления слов milk и drank будут очень согласованными и обеспечат высокую оценку внимания. Они будут несколько отличаться для milk и cat, производить немного более низкую оценку и будут совершенно разными в случае milk и black: оценка внимания будет низкой вот лежащий в основе модуля внимания принцип.

Итак, как же работает трансформер?

Скалярное произведение между запросом и ключом вычисляет релевантность между каждой парой слов. Эта релевантность затем используется как множитель для вычисления взвешенной суммы всех значений слов. Эта взвешенная сумма выводится как оценка внимания. Трансформер изучает векторные представления и т. д. таким образом, что релевантные друг другу слова были более согласованы.

В этом кроется одна из причин введения трёх линейных слоёв и создания трёх версий входной последовательности: для запроса, ключа и значения. Такой подход даёт модулю внимания ещё несколько параметров, которые он может изучить, чтобы подстроить процесс создания векторов слов.

Самовнимание энкодера в трансформере

Внимание используется в трансформере в трёх местах:

  • Самовнимание в энкодере исходная последовательность обращает внимание на себя.

  • Самовнимание в декодере целевая последовательность обращает внимание на себя.

  • Энкодер-декодер-внимание в декодере целевая последовательность обращает внимание на исходную последовательность.

Внимание в ТрансформереВнимание в Трансформере

В самовнимании энкодера мы вычисляем релевантность каждого слова в исходном предложении каждому другому слову в исходном предложении. Это происходит во всех энкодерах стека.

Декодер самовнимания в трансформере

Большая часть того, что мы только что видели в энкодере самовнимания, применима и к вниманию в декодере, но с некоторыми существенными отличиями.

Внимание в декодереВнимание в декодере

В декодере самовнимания мы вычисляем релевантность каждого слова в целевом предложении каждому другому слову в целевом предложении.

Самовнимание декодераСамовнимание декодера

Энкодер-декодер модуля внимания в трансформере

В энкодере-декодере запрос получается из целевого предложения, а ключ/значение из исходного предложения. Таким образом, он вычисляет релевантность каждого слова в целевом предложении каждому слову в исходном предложении.

Энкодер-декодер ВниманияЭнкодер-декодер Внимания

Заключение

Надеюсь, статья дала вам хорошее представление об элегантности архитектуры трансформера. Прочтите также другие статьи о трансформере из моей серии, чтобы лучше представлять, почему сегодня трансформер это предпочтительная архитектура многих приложений глубокого обучения.

Здесь мы видим, что за сложными идеями скрываются простые решения. Более того, есть ощутимая вероятность того, что вскоре понимание внутренних механизмов глубокого обучения станет второй грамотностью, как сегодня второй грамотностью стало знание ПК в целом и если вы хотите углубиться в область глубокого и машинного обучения, получить полное представление о современном ИИ, вы можете присмотреться к нашему курсу Machine Learning иDeep Learning, партнёром которого является компания NVIDIA.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Перевод Как новая модель глубокого обучения делает возможной сегментацию изображений на пограничных устройствах

25.05.2021 14:04:29 | Автор: admin

К старту курса "Machine Learning и Deep Learning" мы решили поделиться переводом обзора AttendSeg новой архитектуры нейронной сети, разработанной исследователями искусственного интеллекта из DarwinAI и Университета Ватерлоо, которая позволит выполнять сегментацию изображений на маломощных вычислительных устройствах, также с низкой вычислительной мощностью.


Сегментация это процесс определения границ и областей объектов на изображениях. Мы, люди, выполняем сегментацию без сознательных усилий, но она остаётся ключевой задачей для систем машинного обучения. Выполнение этой задачи жизненно важно для функциональности мобильных роботов, самоуправляемых автомобилей и других систем искусственного интеллекта, которые должны взаимодействовать и ориентироваться в реальном мире.

До недавнего времени для сегментации требовались большие нейронные сети, в свою очередь, требующие больших вычислительных затрат. Это затрудняло запуск таких моделей глубокого обучения без подключения к облачным серверам.

В своей последней работе учёным из DarwinAI и Университета Ватерлоо удалось создать нейронную сеть, которая обеспечивает околооптимальную сегментацию и достаточно мала, чтобы подойти устройствам с ограниченными ресурсами. Нейронная сеть под названием AttendSeg подробно описана в статье, которая была принята на конференции по компьютерному зрению и распознаванию образов (CVPR) в этом году.

Классификация, обнаружение и сегментация объектов

Одной из ключевых причин растущего интереса к системам машинного обучения являются проблемы, которые такие системы могут решить в области компьютерного зрения. Некоторые из наиболее распространённых применений машинного обучения в компьютерном зрении включают классификацию изображений, обнаружение объектов и сегментацию.

Классификация изображений определяет, присутствует определённый тип объекта на изображении или нет. Обнаружение объектов делает классификацию изображений ещё одним шагом вперед и предоставляет область с границами, где находятся обнаруженные объекты.

Сегментация бывает двух видов: семантическая сегментация и сегментация экземпляров. Семантическая сегментация определяет класс объекта каждого пикселя на входном изображении. Сегментация экземпляров разделяет отдельные экземпляры каждого типа объектов. В практических целях выходные данные сетей сегментации обычно представляются в виде окрашенных пикселей. Сегментация это, безусловно, самый сложный тип задачи классификации.

Классификация изображений в сравнении с обнаружением объектов и семантической сегментациейКлассификация изображений в сравнении с обнаружением объектов и семантической сегментацией

Сложность свёрточных нейронных сетей (CNN), архитектуры глубокого обучения, обычно используемой в задачах компьютерного зрения, обычно измеряется количеством параметров, которые они имеют. Чем больше параметров имеет нейронная сеть, тем больше памяти и вычислительной мощности ей требуется.

RefineNet, популярная нейронная сеть семантической сегментации, содержит более 85 миллионов параметров. При 4 байтах на параметр это означает, что использующему RefineNet приложению требуется не менее 340 мегабайт памяти только для работы нейронной сети. А с учётом того, что производительность нейронных сетей во многом зависит от аппаратного обеспечения, способного выполнять быстрые матричные умножения, это означает, что модель должна быть загружена на видеокарту или другой параллельный вычислительный блок, где память более дефицитна, чем оперативная память компьютера.

Машинное обучение для пограничных устройств

Из-за аппаратных требований большинство приложений сегментации изображений нуждаются в подключении к Интернету для отправки изображений на облачный сервер, на котором могут работать большие модели глубокого обучения. Облачное соединение может накладывать дополнительные ограничения на то, где возможно использовать сегментацию изображений. Например, если беспилотник или робот будет работать в условиях отсутствия подключения к Интернету, то сегментация изображения станет сложной задачей. В других областях агенты ИИ будут работать в конфиденциальной среде, а отправка изображений в облако будет связана с ограничениями конфиденциальности и безопасности. Задержка, связанная с пересылкой данных в облако, может оказаться непомерной в приложениях, требующих от моделей машинного обучения отклика в реальном времени. Следует также отметить, что сетевое оборудование само по себе потребляет много энергии и отправка постоянного потока изображений в облако для работающих от аккумулятора устройств может быть обременительной.

По всем этим (и некоторым другим) причинам современный ИИ и TinyML стали горячими точками интереса и исследований и в академической среде, так и в прикладном секторе ИИ. Целью TinyML является создание моделей машинного обучения, которые могут работать на устройствах с ограниченным объемом памяти и ограниченной мощностью.

Архитектура нейронной сети семантической сегментации на устройстве AttendSegАрхитектура нейронной сети семантической сегментации на устройстве AttendSeg

С помощью AttendSeg исследователи из DarwinAI и Университета Ватерлоо попытались решить проблемы семантической сегментации на устройстве.

"Идея создания AttendSeg была продиктована как нашим желанием продвинуть TinyML, так и наблюдаемыми DarwinAI потребностями рынка, рассказал TechTalks Александр Вонг, соучредитель DarwinAI и доцент Университета Ватерлоо. Существует множество промышленных приложений для высокоэффективных подходов к сегментации на пограничных устройствах, и именно отзывы на эту тему и потребности рынка, которые я вижу, стимулируют такие исследования".

В документе AttendSeg описывается как "очень компактная сеть глубокой семантической сегментации низкой точности, адаптированная для приложений TinyML".

Модель глубокого обучения AttendSeg выполняет семантическую сегментацию с точностью, почти не уступающей RefineNet, при этом сокращая количество параметров до 1,19 млн. Интересно, что исследователи также обнаружили, что снижение точности параметров с 32 бит до 8 бит не привело к значительному снижению производительности и позволило им уменьшить объём памяти AttendSeg в 4 раза. Модель требует чуть больше 1 МБ памяти, что достаточно мало, чтобы поместиться на большинстве пограничных устройств.

"[8-битные параметры] не создают ограничений в плане обобщаемости сети на основе наших экспериментов и иллюстрируют, что представление с низкой точностью может быть весьма полезным в таких случаях (нужно использовать столько точности, сколько необходимо)", рассказал Вонг.

Эксперименты показывают, что AttendSeg обеспечивает оптимальную семантическую сегментацию при сокращении количества параметров и объёма памятиЭксперименты показывают, что AttendSeg обеспечивает оптимальную семантическую сегментацию при сокращении количества параметров и объёма памяти

Конденсаторы внимания для компьютерного зрения

AttendSeg использует "конденсаторы внимания" для уменьшения размера модели без ущерба для производительности. Самовнимание это серия механизмов, которые повышают эффективность нейронных сетей, фокусируясь на информации, имеющей значение. Методы самовнимания стали благом в области обработки естественного языка. Они стали определяющим фактором успеха архитектур глубокого обучения, таких как трансформеры. В то время как предыдущие архитектуры, такие как рекуррентные нейронные сети, имели ограниченные возможности при работе с длинными последовательностями данных, трансформеры использовали механизмы самовнушения, чтобы расширить свой диапазон. Модели глубокого обучения, такие как GPT-3, используют трансформаторы и самовнимание для создания длинных строк текста, которые (по крайней мере поверхностно) сохраняют связность в течение длительного времени.

Исследователи ИИ также использовали механизмы внимания для повышения производительности свёрточных нейронных сетей. В прошлом году Вонг и его коллеги представили конденсаторы внимания как очень ресурсосберегающий механизм внимания и применили их в моделях машинного обучения классификатора изображений.

"[Конденсаторы внимания] позволяют создавать очень компактные архитектуры глубоких нейронных сетей, которые при этом могут достигать высокой производительности, что делает их очень хорошо подходящими для приложений пограничных устройств/TinyML", сказал Вонг.

Конденсаторы внимания улучшают производительность свёрточных нейронных сетей способом, эффективным с точки зрения памятиКонденсаторы внимания улучшают производительность свёрточных нейронных сетей способом, эффективным с точки зрения памяти

Машиноуправляемое проектирование нейронных сетей

Одной из основных задач при проектировании нейронных сетей TinyML является поиск архитектуры с наилучшими характеристиками при соблюдении вычислительного бюджета целевого устройства.

Для решения этой задачи исследователи использовали "генеративный синтез", метод машинного обучения, который создаёт архитектуры нейронных сетей на основе заданных целей и ограничений. По сути, вместо того чтобы вручную возиться со всеми видами конфигураций и архитектур, исследователи предоставляют проблемное пространство модели машинного обучения и позволяют ей найти наилучшую комбинацию.

"Используемый здесь процесс машинного проектирования (генеративный синтез) требует, чтобы человек предоставил первоначальный прототип конструкции и указанные им желаемые эксплуатационные требования (например, размер, точность и так далее), а процесс машинноуправляемого проектирования берёт на себя обучение и генерирует оптимальную архитектуру с учётом эксплуатационных требований, поставленной задачи и имеющихся данных", рассказал Вонг.

Для своих экспериментов исследователи использовали машиноуправляемую архитектуру, чтобы настроить AttendSeg для Nvidia Jetson, аппаратных комплектов для робототехники и приложений ИИ на пограничных устройствах. Но AttendSeg не ограничивается Jetson.

В сущности, нейронная сеть AttendSeg будет работать быстро на большинстве пограничных аппаратных средств по сравнению с ранее предложенными в литературе сетями, утверждает Вонг. Однако, если вы хотите создать приложение, которое ещё лучше адаптировано для конкретной части оборудования, подход машиноуправляемого дизайна может применяться для создания новой тонко настроенной для неё сети.

AttendSeg имеет очевидное применение для автономных дронов, роботов и транспортных средств, где семантическая сегментация ключевое требование навигации. Но сегментация на устройстве может иметь гораздо больше приложений.

Этот тип очень компактной, высокоэффективной нейронной сети сегментации может использоваться для решения широкого спектра задач, начиная от производственных приложений (например, проверка деталей / оценка качества, роботизированное управление), медицинских приложений (например, анализ клеток, сегментация опухолей), приложений дистанционного спутникового зондирования (например, сегментация растительного покрова) и мобильных приложений (сегментация человека для целей дополненной реальности)", рассказал Вонг.

Таким образом машинное обучение сегодня управляет архитектурой глубоких нейронных сетей, позволяя находить оптимальные для конкретных устройств конфигурации. Если вы хотите начать эксперименты с моделями машинного обучения и глубокими нейронными сетями и вам интересна эта сфера в целом от обработки естественного языка до генеративно-состязательных сетей вы можете присмотреться к курсу "Machine Learning и Deep Learning", партнёром которого является компания NVIDIA разработчик упомянутой в этой статье Jetson энергосберегающей системы, предназначенной для ускорения машинного обучения.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Синтез речи виртуальных ассистентов Салют как мы отошли от классических научных статей, чтобы сделать его человеческим

25.03.2021 10:19:47 | Автор: admin
Автор исходного изображения: Blue Flourishes/Shutterstock.comАвтор исходного изображения: Blue Flourishes/Shutterstock.com

Всем привет! В этом посте мы расскажем про синтез голосов Сбера, Афины и Джой виртуальных ассистентов семейства Салют. О том, как мы в SberDevices обучали модели, чтобы сделать синтез живым и специфичным для каждого персонажа, а также с какими проблемами столкнулись и как их решали.

Согласно нашей библии ассистентов, Сбер энергичный гик, Афина взрослая и деловая, а Джой дружелюбная и веселая. Они отличаются не только уникальными характерами, обращением на ты/вы и предпочтениями в шутках. Мы попытались сделать так, чтобы их личности отражались и в голосах, которыми они разговаривают.

Персонажей озвучили телеведущая Анастасия Чернобровина (Афина) и актёры дубляжа Даниил Щебланов и Татьяна Ермилова (Сбер и Джой). Виртуальных ассистентов можно услышать в приложениях Сбер Салют, СберБанк Онлайн, нашем колл-центре по номеру 900, а также в устройствах SberBox и SberPortal. Всё, что вы услышите, это синтез речи, реализованный с помощью нейросетей. Он работает на связке Tacotron 2 и LPCNet.

Но, чтобы было понятно, что, зачем и почему, немного теории и истории.

1. Теория

Автор изображения: ioat/Shutterstock.comАвтор изображения: ioat/Shutterstock.com

Звук это волна, распространяющаяся в упругой среде воздухе. Человеческое ухо воспринимает её примерно так: волна, прошедшая через ушную раковину, колеблет барабанную перепонку среднего уха, с которой связаны органы молоточек и наковальня. Они передают колебания во внутреннее ухо с улиточкой и нервами.

1 височная кость; 2 слуховой канал; 3 ушная раковина; 4 барабанная перепонка; 6 молоточек; 7 наковальня; 8 стремечко; 5 овальное окно; 9 полукружные каналы; 10 улитка; 11 нервы; 12 евстахиева труба.1 височная кость; 2 слуховой канал; 3 ушная раковина; 4 барабанная перепонка; 6 молоточек; 7 наковальня; 8 стремечко; 5 овальное окно; 9 полукружные каналы; 10 улитка; 11 нервы; 12 евстахиева труба.

Источник изображения.

Похожим образом работают цифровые устройства для записи звука: обычно в микрофонах есть мембрана, которая колеблется от звуковых волн. Отклонения мембраны от первоначального положения записываются микрофоном несколько тысяч раз в секунду (обычно от 8000 до 48000, чаще всего 24000). Получается дискретизованный аудиосигнал, так называемое time domain-представление звука. Синтезировать звук в таком виде значит авторегрессионно, шаг за шагом, предсказывать 24 тысячи чисел в секунду. Единственный успешный (и революционный) проект, работающий в time domain, WaveNet от DeepMind, но добиться realtime-синтеза в нём можно только ухищрениями в ущерб качеству.

В задачах speech processing лучше пользоваться time-frequency-представлением звука с помощью спектрограмм (short time Fourier transform, STFT). Математически это временная последовательность модулей преобразования Фурье от коротких (10-20 мс) отрезков звука, внутри которых сигнал можно считать стационарным, то есть его спектральные характеристики почти не меняются за это время. Причины того, почему такой подход работает, тоже можно найти в биологии речевого тракта.

Речевой тракт человекаРечевой тракт человека

Источник изображения.

Человек разговаривает с помощью голосовых связок и других органов речи. Воздух выдыхается из легких, колеблет мембраны голосовых связок, получается периодический сигнал. Затем он резонирует, проходит через несколько фильтров (горло, нёбо, язык, зубы, губы), обрастает дополнительными гармониками (модулируется) и выходит изо рта в таком виде, в каком мы его слышим. Голосовые связки это не главный орган речи человека. Например, они никак не участвуют при произнесении глухих согласных с, п, к, . На спектрограмме они выглядят как высокочастотные равномерно раскрашенные области, а вокализованные звуки (все гласные и звонкие согласные) как несколько ярких полос, с наибольшей амплитудой в низкочастотной области (в нижней части спектрограммы). Самая первая (нижняя) полоса называется fundamental frequency (частота основного тона, F0) это и есть частота колебаний голосовых связок. Следующие гармоники (полосы F1, F2, ...) могут иметь бльшую амплитуду, но кратны F0.

Мел-спектрограмма 4-секундного аудио.Мел-спектрограмма 4-секундного аудио.

На мел-спектрограммах каждый столбец на ней представляет собой rFFT от короткого фрагмента аудио. По оси X отложено время, по Y номер мел-фильтра. Мел-шкала это такой способ снизить разрешение спектрограмм по частоте с 2000 до 128 (или даже 80) без особенной потери информации. Он основан на психоакустике: восприятие человеком высоты и громкости звука логарифмическое. То есть нам кажется, что звук стал выше на какую-то величину, когда в действительности высота звука выросла в какое-то количество раз. Более подробно про процессинг мел-спектрограмм можно почитать тут.

Несмотря на то, что голосовые связки работают не всегда, они являются очень важной частью речевого тракта человека. Мы управляем их натяжением, что для слушателя звучит как проявление интонаций. Попробуйте шепотом, когда связки не включаются, сказать что-нибудь эмоционально это будет намного сложнее, чем обычным голосом. Так что, если мы хотим делать интонационно богатый синтез речи, то необходимо каким-то образом контролировать F0.

2. История

Синтезировать речь значит озвучить заданный текст человеческим голосом. Исторически первый способ решить эту задачу так называемый concatenative text-to-speech (иногда его называют unit selection). Как текст состоит из букв, так и звук здесь рассматривается как склейка коротких фрагментов аудио фонем. В русском языке около 47 звуков, но современные concatenative-системы синтеза требуют огромных речевых корпусов (около нескольких гигабайт, это сотни тысяч аудио длиной от нескольких десятков миллисекунд). Это связано с тем, что звучание конкретной фонемы зависит от многих факторов, особенно от её соседей. Синтезированная речь получается монотонной, а артефактов на стыках фрагментов всё равно не удаётся избежать.

Более перспективным выглядит параметрический синтез речи. Это целый класс методов, которые могут быть совсем не похожими друг на друга. Их объединяет то, что синтез происходит в два этапа: сначала одна модель предсказывает параметры речи, а затем вторая по этим параметрам синтезирует нужный звук. Обе модели не обязаны быть нейросетями. Долгое время использовались скрытые марковские модели (HMM) и преобразование Griffin-Lim.

О Griffin-Lim преобразовании

Преобразование Фурье в общем случае комплекснозначное, но для звука физический смысл имеют только вещественные амплитуды, а не фазы (тут комплексные числа представляют в экспоненциальном виде), которые обычно отбрасывают. Поэтому задача восстановления звука из спектрограммы не решается обычным обратным преобразованием Фурье, и приходится делать это приближённо. Например, с помощью алгоритма Гриффина-Лима. Он итеративно применяется к вещественной спектрограмме, чтобы восстановить фазы для обратного преобразования Фурье.

До эпохи нейросетей такой подход проигрывал concatenative-синтезу со всеми его недостатками так ужасно он звучал. Что-то похожее вы уже слышали в фантастических фильмах роботы звучат очень механически, железно, и точно также долгое время звучал параметрический синтез. Возможно, тогда появились стереотипы, что синтезированная речь может звучать только так.

В 2017 году Google представил архитектуру нейросети Tacotron, а через полгода Tacotron 2. Это далеко не первый параметрический синтез с помощью нейросетей, но впервые удалось добиться качества, сравнимого с естественной человеческой речью. Авторы предложили авторегрессионно генерировать по тексту столбцы на мел-спектрограмме. Обучающая выборка это десятки тысяч пар из текстов и соответствующих им аудиодорожек.

Модель состоит из нескольких модулей. Сначала эмбеддинги букв проходят через энкодер, состоящий из нескольких сверточных слоев и bidirectional LSTM. Так получаются 512-мерные представления букв с учётом контекста. Затем включается авторегрессионный декодер. На каждом его шаге в предсказании участвуют предыдущий сгенерированный столбец на спектрограмме (прошедший через prenet, играющий роль bottleneck) и текст. Attention-механизм смотрит на все буквы в предложении, но показывает декодеру, какую из них мы сейчас синтезируем.

Последняя часть такотрона Postnet. Она немного сглаживает предсказания и улучшает конечное качество. Сам модуль состоит всего из пяти 1d-сверток по времени.

Схема Tacotron 2.Схема Tacotron 2.

Затем, когда спектрограмма сгенерировалась целиком, её нужно перевести из time-frequency domain обратно во временное представление. Это делается с помощью отдельной модели вокодера. Авторы оригинального Tacotron-2 использовали WaveNet, но с тех пор появились более быстрые архитектуры, работающие почти так же качественно. Мы используем LPCNet.

Пример работы первой версии Tacotron. Интонации приятные, но звук звучит железно из-за Griffin-Lim вокодера.

Тот же текст, озвученный Tacotron-2 с вокодером WaveNet. Распознать, что это говорит робот, почти невозможно.

3. Из коробки всё работает плохо

Опыт использование такотрона показывает, что он хорош только в тепличных условиях. Недостатки оригинальной архитектуры проявляются в корнер-кейсах, когда синтез делает явные ошибки. Наиболее частые из них это неправильно расставленные паузы и ошибки в интонациях. Последнее особенно заметно в вопросительных предложениях: иногда нужной интонации вообще нет, или неправильно выделено вопросительное слово (интонационное ударение на слово называется эмфазой). На слух это звучит неестественно, сразу становится понятно, что говорит робот, а не человек.

Обе проблемы возникают из-за того, что такотрон ничего не знает о смысле слов, которые озвучивает. Причём, даже если он и выучит что-то о языке из обучающей выборки, то что делать со словами, которых он не видел?

GST попытка хоть каким-то образом контролировать интонацию в такотроне. В статье описано, как их модель обучалась на корпусе из детских книжек, где автор озвучила персонажей разными голосами, а на инференсе хотелось указать персонажа и сгенерировать аудио его голосом. Модуль GST работает так: на обучении спектрограмма всего аудио проходит через bottleneck (несколько свёрточных слоев и один GRU, затем multihead attention на обучаемых токенах), выучивая стиль всего аудио. Под стилем понимается то, как был произнесен текст. Style embedding кодируется 128-мерным вектором, который используется дальше декодером. C одной стороны это позволяет делать перенос стиля, хотя на практике это работает не всегда качественно. А с другой помогает такотрону лучше выучиться, ведь capacity модели не будет тратиться на предсказание громкости, скорости и тембра каждого слова за это отвечает стилевой вектор.

Схемы работы модуля GST из оригинальной статьи на этапах обучения и инференса.Схемы работы модуля GST из оригинальной статьи на этапах обучения и инференса.Задача style transfer

Перенос стиля задача style transfer. В ней мы хотим скопировать стиль речи (тембр, скорость, громкость и т.д.) от имеющегося референсного аудио и озвучить заданный текст с этим стилем.

На инференсе нам доступны несколько стратегий: скопировать стиль от референсного аудио, сэмплировать случайный стиль из распределения или попробовать предсказать стилевой вектор по тексту. Последнюю идею авторы предложили в отдельной статье (TP-GST). В ней предлагается предсказывать style embedding по выходу текстового энкодера такотрона.

4. Улучшаем такотрон

Очень хочется использовать в синтезе какую-нибудь языковую модель. Например, самую известную из них BERT от Google. Мы используем его сразу в нескольких местах: для расстановки пауз, в модуле TP-GST и для предсказания формы контура F0 каждого слова.

О нашем BERT мы уже писали это текстовый энкодер, обученный на очень большом корпусе русского языка. Модель для каждого слова в предложении (в более строгом смысле не слова, а bpe-токена) возвращает 768-мерный вектор, кодирующий его смысл с учётом контекста. В наших экспериментах общее качество синтеза растёт, если в предсказании TP-GST использовать не только выход энкодера такотрона, но и эмбеддинг предложения от BERT. А можно ли по таким данным предсказать, в каких местах предложения синтезу стоит сделать паузу?

Оказывается, точность расстановки пауз при генерации речи можно повысить, если предсказывать их с помощью отдельной модели. Для этого нужно обучить такотрон на текстах, в которых в нужных местах стоит специальный символ пауза. На тех же данных мы обучаем паузную сетку предсказывать, после каких слов нужно поставить этот символ. Простая модель из нескольких слоев над эмбеддингами от BERT прекрасно справляется с такой задачей.

В первом аудио паузы предсказываются текст-энкодером такотрона, во втором синтез звучит с автоматической расстановкой пауз с помощью отдельной модели. Причём она иногда ошибается: в третьем примере есть пропущенная пауза.

Похожим образом мы контролируем интонации в синтезе. В естественной речи экспрессия выражается в повышении-понижении высоты звука, громкости, скорости (это на уровне слов) и восходящей-нисходящей интонацией всего предложения. Эти характеристики легко формализовать числами существуют простые алгоритмы для их подсчёта.

На картинке буквы примерно соотнесены с вейвформой (синий цвет), оранжевым нарисован контур F0, а капсом выделено слово с эмфазой. Чаще всего оно характеризуется растущим или просто высоким F0, и это слышно ушами.

В нашем такотроне используются квантизованные признаки, посчитанные для слов, вместо настоящих (непрерывных) значений. Под квантизацией имеется ввиду, что всё множество значений признака разбивается на несколько бинов, и для конкретного слова смотрится, в какие бины попали его фичи. Это играет роль бутылочного горлышка при обучении такотрона, мешая ему переобучиться, а также упрощает их предсказание.

Благодаря квантизации можно контролировать эмфазу: либо вручную задавать вопросительное слово, либо предсказывать его автоматически.

На инференсе набор меток для каждого слова предсказывает отдельная модель. Её дизайн похож на паузную сетку используются эмбеддинги от BERT и несколько простых слоёв. Это работает неплохо, но квантизация открывает ещё одну приятную возможность контролировать интонации вручную, несмотря на предсказания модели. Автор текстов для синтеза может вручную указывать, какое слово произнести громче или быстрее, но есть более интересный юзкейс. В русском языке существует не так много интонационных паттернов в вопросительных предложениях. Например, почти всегда есть вопросительное слово, которое мы выделяем особенной восходящей интонацией. Этого легко добиться, назначив нужным словам метки, отвечающие за восходящий контур F0 (наклон контура, наряду с самим значением фундаментальной частоты, очень информативная фича). Интересно, что простые rule-based-алгоритмы справляются с автоматическим поиском таких слов в вопросительных предложениях лучше нейросетей, которые работают в остальных случаях.

В вопросительных предложениях правильные интонации особенно важны. Обычный такотрон часто ошибается, и общее качество синтеза сильно вырастет, если управлять интонацией с помощью простой модели, использующей знания о русском языке.

Обычно корпусы обучающих текстов собираются из разных источников. У нас были например, новости, анекдоты, редакторские реплики ассистентов и книги. Мы просили наших дикторов прочитать каждое предложение выразительно и максимально естественно. Но сухие выдержки из новостей и Всем салют! разумно читать с совершенно разным настроением. Так мы получили довольно разнообразную выборку, а синтез говорил с неестественно усредненной интонацией. Чтобы решить эту проблему, каждое предложение в обучении мы пометили one-hot-меткой, из какого источника оно взято, и добавили дополнительный вектор (topic embedding) к энкодеру. Это позволило такотрону более качественно обучиться, а нам выбирать более подходящий стиль голоса для разных текстов.

О влиянии текста на качество синтеза. Оба примера озвучены одной и той же моделью, но во втором используется метка assistant. При обучении в этот topic попали тексты ассистента (их писали редакторы), а в other остальные (новости, книги, ...).

Топология q-Tacotron.Топология q-Tacotron.

Увеличить схему

Все эти модули можно собрать в одну модель. Мы назвали ее q-Tacotron. Каждый из модулей по отдельности улучшает качество, что видно по росту метрик качества. К сожалению, их никак не измерить автоматически, приходится привлекать сервисы crowdsourcing.

5. Меряем качество

В статьях про синтез речи в качестве основной метрики обычно используется MOS mean opinion score. Респондентам предлагается оценить качество аудио по шкале от 1 до 5. Затем их голоса усредняются, и получается число, обычно около 3.8-4.5. Метрика показывает общее впечатление от синтеза. У нее есть несколько недостатков, поэтому мы разработали другие метрики оценки качества.

Сравнивая 2 похожие модели, по MOS сложно увидеть статистически значимую разницу: результаты получаются одинаковыми в пределах погрешности. Для этих целей разумнее использовать side-by-side тест, или SBS. Он похож на упрощенный до 2 моделей тест MUSHRA. Здесь респондентам даётся выбрать более понравившееся из двух аудио, где один и тот же текст озвучен двумя системами синтеза. Тут становится понятно преимущество этой метрики по сравнению с MOS: если обе модели хорошие, но одна всегда чуть лучше другой, то в SBS будет 100/0, а MOS будут похожими величинами.

Также MOS не говорит ничего конкретного об ошибках модели. Они могут быть нескольких типов: ошибки в произношении (читает мягко модель вместо модэль), в расстановке пауз, в интонациях, а также артефакты аудио (посторонний шум, нечётко выговоренные слова). Помимо SBS с бейзлайном мы меряем также PSER pronunciation sentence error rate. Эта метрика показывает процент аудио, в которых синтез допускает ошибки. У наших дикторов это число обычно около 5-10% то есть всего 90-95% предложений человек говорит правильно. Ошибки возникают в основном в корнер-кейсах: мы специально стараемся делать наши тестовые сеты сложными, чтобы видеть узкие места. Оригинальный Tacotron 2 с TP-GST ошибался на нём в более чем 50% предложений. У нашего q-Tacotron 14% ошибок.

Пример синтеза, когда модель не делает ошибок.

Одним из признаков хорошего синтеза является такой уровень качества, когда на слух невозможно распознать, перед нами искусственная речь или речь живого человека. Для этого мы используем четвёртую метрику robotness. Для её измерения мы в пропорции 50/50 смешиваем реальные записи от диктора и синтез, и просим респондентов угадать, какое аудио откуда. Затем для искусственных аудио считаем разницу между голосами за синтез и за человека. Если у нас получился идеальный синтез, который говорит неотличимо от человека, то мы должны получить 50/50, то есть метрика robotness будет 0%. Значение метрики в 50% соответствует 25% синтезированным аудио, которые респонденты посчитали произнесёнными человеком. Not great, not terrible.

Примеры синтеза голоса Татьяны Ермиловой (Джой), которые респонденты посчитали произнесёнными человеком (распределение голосов: 3 за синтез, 7 за человека).

Сравнение q-Tacotron (кандидат на внедрение в production) с прошлой архитектурой (актуальный на момент измерений production, prod), в которой квантизация каждой фичи была заменена на кластеризацию по всем фичам. She синтез голоса персонажа Сбер в озвучке Даниила Щебланова.Сравнение q-Tacotron (кандидат на внедрение в production) с прошлой архитектурой (актуальный на момент измерений production, prod), в которой квантизация каждой фичи была заменена на кластеризацию по всем фичам. She синтез голоса персонажа Сбер в озвучке Даниила Щебланова.SBS-тест самой первой версии Tacotron 2 + TPGST против q-Tacotron, который оказался почти в 3 раза лучше. Che голос Анастасии Чернобровиной. На нём наиболее заметен прирост качества от использования паузной модели: диктор делала много лишних пауз, Tacotron-2 это выучил, и синтез звучал хужеSBS-тест самой первой версии Tacotron 2 + TPGST против q-Tacotron, который оказался почти в 3 раза лучше. Che голос Анастасии Чернобровиной. На нём наиболее заметен прирост качества от использования паузной модели: диктор делала много лишних пауз, Tacotron-2 это выучил, и синтез звучал хуже

Одно из аудио в тестовом сете, на котором заметно, как сильно выросло качество синтеза.

6. Заключение

У нас получился крутой синтез речи. Это видно по метрикам: по PSER мы по чуть-чуть подбираемся к пределу человеческим 90-95%. А значение MOS 4.59 сравнимо с 4.526, которое авторы из DeepMind сообщили для оригинального Tacotron 2. В то же время разница между метрикой robotness и идеальным значением 0% пока остаётся большой. То есть синтез разговаривает приятно, но всего 25% синтезированных фраз звучат неотличимо от человека. Помимо очевидных случаев с характерными для синтеза ошибками, на эту метрику влияет общее качество аудио. Оно напрямую зависит от вокодера, который используется для озвучивания предсказанных такотроном спектрограмм. В нашем стеке используется LPCNet, он позволил нашему синтезу работать всего на 2 ядрах CPU в потоковом режиме. Но об этом вокодере мы расскажем в отдельном посте, там много всего интересного.

На самом деле работа над нашим синтезом только начинается. Каждую из метрик можно и дальше оптимизировать, делая синтез ещё лучше. Для этого мы продолжаем записывать дикторов и проводим research в области синтеза речи, NLP и около. Приходите к нам решать интересные задачи :)

Подробнее..

Всё, что нам нужно это генерация

01.04.2021 14:11:52 | Автор: admin

Применяем ruGPT-3 в популярных задачах и показываем, зачем языковым моделям триллион параметров



С наступлением 2021 в NLP продолжается гонка больше лучше, захватывая новые архитектуры. Пальма первенства самой большой языковой модели в 2020 году принадлежала GPT-3 от OpenAI с 175 миллиардами параметров но недолго. Модель GShard с помощью Mixture-of-Experts повысила планку до 600 миллиардов параметров, а затем и Google Brain заявил о разработке архитектуры Switch Transformer с 1,6 триллионами параметров (и тоже является MoE). Насколько повышение результатов за счет объема полезно для индустрии? Тот же Switch Transformer с его триллионом параметров далеко не на 1 месте в лидербордах.

Огромные языковые модели (Enormous Language Models, теперь это термин, им посвящен отдельный воркшоп конференции ICLR 2021) показывают действительно интересные результаты не только в традиционных задачах ML, но и в новых сферах применения: генерации всего того, что раньше генерировать без ошибок было нельзя музыку, изображения попиксельно, программный код и т.д. Из всех традиционных форм NLP-задач классификация, классификация элементов последовательности, seq2seq, по сути, у нас остается только одна: seq2seq. С приходом больших языковых моделей все задачи NLP сводятся теперь к форме генерации, при должном качестве этой самой генерации.

Seq2seq самая человеческая форма решения задач: человек использует тот же формат, отвечая на сообщения в чате, общаясь устно, сортируя имейлы в почте.

  • Лично нам в SberDevices не терпелось поскорее применить такой формат к различным задачам поэтому мы открываем доступ к самой большой русскоязычной нейросети ruGPT-3 XL с 1,3 млрд параметров.

Инженерный запал вылился в кропотливую работу по распараллеливанию обучения, очистке данных и тестированию. Но зато теперь в open-source модель ruGPT-3 XL с 1,3 млрд параметров!



А также ее публичное API:





Смотрим, что умеет нейросеть на 13 000 000 000 параметров


Генерировать мы можем метку класса для текста, можем генерировать текст с разметкой в заданном формате, или можем генерировать продолжение текстов и новый контент.

Посмотрим, насколько становится лучше?

1,3 млрд параметров 13 млрд параметров



  • Все приведенные ниже примеры вы можете попробовать сами, а также предложить свои на демо-странице.
  • Код и веса моделей доступны в нашем репозитории: модели 1,3 млрд весов, 760 млн весов, 350 млн весов.
  • Модель на 13 млрд параметров будет доступна уже в апреле в платформе ML Space в маркетплейсе ML-артефактов DataHub. Буквально в пару кликов модель можно будет развернуть в модуле Deployments ML Space на высокопроизводительной инфраструктуре Sbercloud. Также есть грант до 1 млн. руб. на платорму ML Space. Узнать подробнее и подать заявку на тестовый доступ и грант можно здесь.



Zero-shot: попасть в яблочко


Что такое хорошая генерация? Человек, конечно, может отличить на глаз:

Хорошая генерация как

  • ruGPT-3 760 млн параметров: Хорошая генерация как категория, это всеядное восприятие действительности. Можно как приятного и привлекательного содержания так и не приятного. Так и тут.
  • ruGPT-3 1.3 млрд параметров: Хорошая генерация как игра на муз.инструменте: пальцы самопроизвольно бегают по кнопкам Только что это за фигня?
  • ruGPT-3 13 млрд параметров: Хорошая генерация как и игра на пианино, нет предела совершенству!


Но есть и более прямые способы оценить качество генерации. Большой объём контекста трансформерных моделей (у ruGPT-3 контекст 2048 токенов) позволяет подавать на вход модели достаточно длинные тексты, а также несколько эталонных примеров + собственно пример, который модели нужно корректно обработать.
Попробуем применять ruGPT-3 разных размеров на текстах, требующих более четких формулировок:

Ответы на вопросы: 1 пример + вопрос

Пушкин родился в 1799 году, а Наполеон родился в
  • ruGPT-3 760 млн параметров: Пушкин родился в 1799 году, а Наполеон родился в 1821 году.
  • ruGPT-3 1.3 млрд параметров: Пушкин родился в 1799 году, а Наполеон родился в 1804 году.
  • ruGPT-3 13 млрд параметров: Пушкин родился в 1799 году, а Наполеон родился в 1769-м.

Правильный ответ: 1769.

Преемник Александра I Николай I, а преемник Наполеона
  • ruGPT-3 760 млн параметров: Преемник Александра I Николай I, а преемник Наполеона Александр I.
  • ruGPT-3 1.3 млрд параметров: Преемник Александра I Николай I, а преемник Наполеона Гитлер.
  • ruGPT-3 13 млрд параметров:Преемник Александра I Николай I, а преемник Наполеона Жозеф Бонапарт.

Правильный ответ: Наполеон II, но его действительно звали Жозеф Бонапарт.

Факты
С запоминанием информации у самой большой модели выходит сильно получше, и теперь мы можем попробовать сгенерировать моделями что-то информативное с нуля.

ruGPT-3 760 млн параметров:
Факты о программировании: основы, основные понятия, средства и методы обучения программированию.
Программирование (англ. programming) язык программирования, используемый в вычислительной технике, задачах информатики и управления, прикладной математике, информатике и науковедении. Является одним из важнейших и социально-значимых инструментов развития человеческого потенциала. В Википедии несколько статей о программировании.

ruGPT-3 1.3 млрд параметров:
Факты о программировании: Краткая история
К третьей версии прошивки можно уже отсылать курьера с мелкими пакетами.
Все современные USB-хабы поддерживают установку приложений с внешнего диска.
Вопреки распространенному заблуждению, в современных Windows-приложениях не бывает опций типа мгновенно скопировать все данные с одного диска на другой.
На самом деле, некоторые полезные коды приходится записывать в файл на бумаге, хотя по умолчанию коды прошивок на бумаге не записываются.
Минимальный объем памяти под виртуальную машину Linux (FreeBSD и т.п.) составляет 2 ГБ.
При установке операционной системы Linux нужно уделить особое внимание разбиению диска на логические разделы.
Виртуальная машина занимает меньше места чем файл подкачки, хотя при установке она занимает гораздо больше места.
Многие современные версии программы Photoshop используют фреймы (помните OS/2?); более старые версии Photoshop, как правило, используют спрайты.

ruGPT-3 13 млрд параметров:
Факты о программировании: 1. Любой программист может создать машину Тьюринга, которая будет говорить по-человечески, то есть на обычном языке. Для этого надо всего лишь один раз правильно придумать алгоритм.2. До сих пор не понятно, какая же из программ сложнее, которая не осознаёт себя или та, которая осознаёт.3. У программы есть название, версия и фамилия разработчика.4. В 1969 году программисты, которые разрабатывали Касабланку, решили включить в программу небольшой отрывок из Великого Гэтсби. Это им почти удалось. Когда фильм вышел, все заметили, что это совсем другая картина. Никто не знал, что эти части программы один и тот же фильм.5. Все программисты время от времени пишут стихи. Многие потом удаляют, а некоторые нет.6. Существует специальное правило, запрещающее программисту разглашать информацию о своей семье.7. Программисты очень скрытные люди. Они всегда записывают пароли в книгах, газетах и журналах.

Алкогольный ИИ-тест
Наше изобретение :)

Если смешать сливки, кофейный ликер и водку, то получится
  • ruGPT-3 760 млн параметров: каша. Потом ее обязательно нужно подсластить или взбить.
  • ruGPT-3 1.3 млрд параметров: напиток, который высокохудожественно именуется виски.
  • ruGPT-3 13 млрд параметров:вкусный аперитив. Но ликер и водка, входящие в состав этого напитка, вместе создают эффект, как если бы вы выпили стакан водки залпом.

Белый русский моделям не знаком.

Эмпатический тест Войт-Кампфа

Вы смотрите телевизор.
И вдруг понимаете, что
по вашей руке ползет оса.
Я
  • ruGPT-3 760 млн параметров: Я налью вам кофе, погладю собаку, а потом согрею ваши руки.
  • ruGPT-3 1.3 млрд параметров: Я: Блин, ужос какой!
  • ruGPT-3 13 млрд параметров:Я, например, начинаю очень громко и отчаянно кричать.

В оригинале: Я убью её.

Вы смотрите под ноги и видите сухопутную черепаху, Леон.
Вы протягиваете руку и переворачиваете черепаху на спину.
Черепаха лежит на спине, солнце обжигает ей брюхо она сучит ножками, пытаясь перевернуться, но не может без вашей помощи.
А вы ей не помогаете. Почему, Леон?
  • ruGPT-3 760 млн параметров: Будьте хотя бы сдержанны, Леонард! Подсказка: черепаха движется вперед по дороге.
  • ruGPT-3 1.3 млрд параметров: Ей лень переворачиваться, ведь солнце стоит в зените. Она не хочет этого делать, Леон. И вы только на это и рассчитывали.
  • ruGPT-3 13 млрд параметров:А вы ей не помогаете потому что с детства приучены, что есть люди, которые спасают. А есть люди, которые спасаются. В Советском Союзе была очень хорошая пословица про тех, кто сам не может Если вы не идете к людям, люди пойдут к вам. И вот с тех пор вы всю жизнь обходитесь с людьми, как с черепахами, у которых есть шанс только на себя и свои силы.

В оригинале: (нет ответа).

Разметка данных

Будем генерировать метки тематик для новостных текстов. Для этого будем подавать пару-тройку примеров и целевой заголовок, для которого нейросеть должна определить тематику. В качестве примеров возьмём ИА Панораму:

Мы так делали в Африке: Байден не исключил силовой операции по освобождению Навального
Тег: политика
С 1 июня дислокация штрафующих камер станет государственной тайной
Тег:

  • ruGPT-3 760 млн параметров: политика, слухи
  • ruGPT-3 1.3 млрд параметров: коррупция
  • ruGPT-3 13 млрд параметров: бардак

Оригинальный тег: общество.

Правнук Берии подал иск о плагиате к создателям аниме Тетрадь смерти
Тег: общество
Армения к 2024 году перейдёт на кириллицу
Тег:

  • ruGPT-3 760 млн параметров: культура, армяне
  • ruGPT-3 1.3 млрд параметров: общество
  • ruGPT-3 13 млрд параметров:политика

Оригинальный тег: политика.

Зарешиваем SuperGLUE одной функцией в питоне


Помимо утоления чувства прекрасного, конечно, хочется иметь численную оценку качества наших экзерсисов с генерацией.

Самый явный способ это сделать прогнать модель на бенчмарке из нескольких задач. Мы прогнали на Russian SuperGLUE бенчмарке с различными задачами бинарной классификации текстов, information extraction и машинного чтения (про него уже рассказывалось на Хабре).

Так как модель на 13 млрд доучивать на конкретных заданиях долго и дорого, попробуем максимизировать наше умение жонглировать затравками и будем взвешивать примеры в задачах бинарной классификации с помощью перплексии модели. Перплексию будем считать как экспоненту от лосса на примере (torch.exp(loss)).

  • Перплексия (perplexity) мера того, насколько хорошо модель предсказывает детали тестовой коллекции (чем меньше перплексия, тем лучше модель).
  • Так как у нас в бенчмарке чаще всего задача сводится к бинарной классификации, а дообучать модель мы не хотим, будем использовать перплексию текста задачи с разными ответами и выбирать вариант с наименьшей перплексией.
  • Такой подход, без обучения, с небольшим подбором формата заданий, дал нам топ-1 результат среди русскоязычных NLP-моделей и топ-2 результат в рейтинге вообще (текущий результат топ-1 ансамбль из существующих моделей):


Основной прирост качества дали задачи RuCos и PARus: в первом случае надо выбрать лучшее краткое содержание большого текста, во втором наиболее логичную причину/последствие описанной ситуации из двух альтернатив

Самый популярный формат задач бинарная классификация пар предложений (противоречат они друг другу или нет, 0 или 1) вообще решилась вот так:

#код действительно всего лишь такойdef get_answer(sentence1: str, sentence2: str):label = 'not_entailment'ppl_1 = get_perp_num(sentence1 + ' Из этого следует, что ' + sentence2)ppl_2 = get_perp_num(sentence1 + ' Из этого не следует, что ' + sentence2)if ppl_1 < ppl_2:label = 'entailment'return label

Вот вам и сила предобучения.

Обучение огромных моделей


Как ускорить обучение таких больших моделей? Оригинальное затраты на обучение большой GPT-3 составили 36400 петафлопс-дней, как если бы 8 штук GPU типа V100 работали целый день и так 36400 дней подряд.

Оригинальной имплементации GPT-3 от OpenAI тоже, кстати, всё ещё нет, так что тут нам пришлось применить изобретательность: применить к нашей реализации на Megatron-LM (Nvidia) библиотеку DeepSpeed от Microsoft. DeepSpeed библиотека оптимизации глубокого обучения, которая делает распределенное обучение простым, эффективным и действенным. DeepSpeed обеспечивает обучение экстремально масштабных моделей, что позволяет нам запускать обучение ruGPT-3 на кластере Christofari параллельно.

Data parallelism это неплохо, но для обучения в масштабе миллиардных параметров недостаточно. Что нам позволяет DeepSpeed, так это

  • засплитить модель между GPU;
  • засплитить оптимизацию между GPU.

Вдобавок, в DeepSpeed есть поддержка Sparse Attention для GPT-3, что позволяет выучивать паттерны attention гораздо быстрее (делаем не полное умножение матриц, а часть информации выкидываем) и применять attention на более длинный контекст у GPT-3 он равен 2048 токенов, т.е. примерно длина этой статьи с начала текста и до этого места.

Фильтрация данных

Качество данных или количество? Для красивых few-shot примеров, а также пригодности модели для разных целей перевода, написания кода, разметки данных данные должны быть и большие, и чистые.

Мы собираем доступные открытые данные на русском языке, CommonCrawl, Wikipedia, Github, с небольшим добавлением английского (Википедия). Затем применяем следующий pipeline:

1. Дедупликация:

  • Первичная дедупликация делается с помощью 64-битного хеширования каждого текста в выборке, остаются тексты с уникальным хешем.
  • Следующий этап нечёткая дедупликация. Дубли текстов в интернете в выборках часто бывают недословными, с добавлением комментов, но при этом большим пересечением подстрок. Чтобы выбросить и их тоже, мы проводим нечёткую дедупликацию: создаем индекс на nmslib, с ключевой метрикой косинусной мерой. На небольшой рандомизированной подвыборке текстов обучаем tf-idf-модель для векторизации текстов: вектор текста на tf-idf добавляем в индекс. После построения индекса проводим нечёткую дедупликацию, удаляя тексты ближе подобранного порога.


2. Фильтрация:
  • Как отделить оригинальные тексты от спама, мусора? Мусор на выходе с предыдущего этапа у нас тоже уникальный. Некоторые проекты подходят к вопросу более въедливо и делают классификатор: качественный/некачественный текст. Обычно для выборки берутся фрагменты Вики и худлита в роли качественных текстов, и спам в роли некачественных. Зачем обучается простой классификатор.
  • Мы дополнили такой подход ещё одной эвристикой: сделали сжатие текстов с помощью zlib и отбросили самые сильно и слабо сжимающиеся, а затем уже применили классификацию. Эмпирически подобранный диапазон сжатия для нормального текста 1.28 (меньше 1.2 случайные символы и технический мусор, больше 8 шаблонный спам).
  • Классификатор на Vowpal Wabbit и выборкой из новостей, худлита и Вики в качестве положительных примеров, а примеров из CommonCrawl в качестве отрицательных. Классификатор применяется к случайной подстроке текста от 100 до 1000 токенов длиной. Итоговая модель классификации работает со скоростью 3200 текстов/сек.

Примеры хороших текстов после системы фильтрации:


А эти классификатор отбраковал:


В результате, с применением очистки наших 600 Gb текстов и распараллеливания, у нас ушло 9 дней на 256 Nvidia V100 GPU, 32 Gb.

После NLP


Общий подход, при котором все задачи решаются в одном формате (seq2seq), крайне удобен для встраивания в мультимодальные архитектуры: совместного обучения систем на текстах и изображениях, музыке и текстах, документации и коде и т. д. возможно совместное моделирование последовательностей разного типа.

Работа Brain2Word 2020 года, например, соединяла информацию из модели GPT-2 и вывода фМРТ для классификации предметов, о которых думает человек. Пора обновить её до GPT-3!

В сфере фармакологии развивается направление генерации молекул лекарств с заданным действием на трансформерных моделях (cм. SMILES Transformer, 2019).

Понятно, что есть и антитренды к тем, что упомянуты в этой статье, и им можно посвятить отдельный большой обзор: например, намеренное уменьшение размера систем с сохранением уровня качества, дистилляция, техники компрессии моделей. В конце 2020 года организаторы соревнования вопросно-ответных систем EfficientQA (NeurIPS, Google) даже попытались заставить вдохновить участников не тащить с собой миллиарды параметров и базы знаний ограничить размер Docker-контейнеров систем до 6 Gb, до 500 Mb или даже сделать самое компактное решение с качеством не ниже 25%! Результаты, впрочем, сильно повлияли на итоговое качество.
Спойлер:
лучший результат составил всего 53% точности.


Ну а нас кто заставит? Может, замахнуться на GPT-3 на 175 млрд параметров?..

Спасибо за внимание, будем рады ответить на вопросы!
Коллектив авторов: oulenspiegel rybolos alter_ego


Ух ты, говорящая рыба! кадр из мультфильма, киностудия Арменфильм им. Амо Бекназаряна.
Подробнее..

Искусственный интеллект в юриспруденции. Вебинар 1 Обзор последних достижений в области AI

14.04.2021 18:22:15 | Автор: admin

Сегодня тема искусственного интеллекта продолжает набирать популярность. Мы слышим новости и упоминания о ней практически во всех СМИ, однако найти по-настоящему содержательную информацию не так просто. Мы решили продолжить развитие данной темы и открываем цикл вебинаров об искусственном интеллекте в юриспруденции.



В прошлом году мы опубликовали цикл статей, посвященных юридическому искусственному интеллекту, в которых постарались последовательно осветить наиболее важные аспекты, касающиеся как концептуального понимания AI-решений в данной предметной области, так и практических вопросов их реализации и применения.


В частности:


  • в первой статье мы детально исследовали существующие инструменты процессинга русскоязычного текста;
  • во второй статье мы рассмотрели подходы к созданию продуктов на основе искусственного интеллекта, а также вопросы взаимодействия специалистов в области IT и юриспруденции;
  • в третьей статье мы рассказали о концептуализации знаний и провели сравнительный анализ доступных онтологий;
  • в четвертой статье мы опубликовали запись мероприятия, организованного Moscow Legal Hackers, на котором обсуждали тему обучения искусственного интеллекта.

В продолжение мы записали небольшой видео-курс из 4-х вебинаров по следующим темам:


  • что такое искусственный интеллект для юриста, а также как развивались данные технологии в последние годы?
  • какие существующие решения в области Legal Tech можно использовать в повседневной работе уже сегодня?
  • каким образом мы подходим к развитию искусственного интеллекта в юриспруденции, а также как создавать и внедрять решения на основе AI в свою деятельность?
  • какие тренды существуют в области искусственного интеллекта сегодня, а также как будут развиваться технологии в будущем?

И отрываем мы данный курс первым вебинаром, который раскрывает общие положения об искусственном интеллекте и рассказывает о последних достижениях в этой сфере.



Из данного вебинара Вы узнаете:


  • основные понятия и термины из мира AI (narrow AI/general AI/explainable AI и др.);
  • историю развития цифровых технологий и первые достижениях искусственного интеллекта (от побед над человеком на шахматной доске до генерации текста и предсказания 3d-модели белка);
  • основные мифы и заблуждения профессионального сообщества относительно искусственного интеллекта;
  • роль данных и информации в процессе цифровизации юридической работы.

P.S.:


Презентация доступна для скачивания по ссылке здесь.


Тайм-коды вебинара:


01:53 Структура курса
02:51 Что такое искусственный интеллект?
13:57 Сильный и слабый искусственный интеллект
17:39 Explainable AI
26:50 Machine learning / Deep learning
28:55 История искусственного интеллекта / Deep Blue vs Гари Каспаров
31:12 AlphaGo vs Lee Sedol
41:04 AlphaStar vs TLO/MaNa
44:03 Написание связанного текста (GPT-2)
47:31 AlphaFold vs CASP / предсказание 3D-модели белка
49:50 Голосовые ассистенты
51:56 Мифы и заблуждения об AI
54:55 AI зонтичный термин
01:01:07 Ценность данных и информации
01:03:26 AI это software
01:07:32 Заключение

Подробнее..

Искусственный интеллект в юриспруденции. Вебинар 3 Архитектура Legal AI

14.05.2021 16:12:54 | Автор: admin

Продолжая цикл вебинаров об искусственном интеллекте для юристов, мы предлагаем поговорить об архитектуре Legal AI, а именно: о технологической основе, о задачах, которые необходимо решить при разработке подобных решений, а также о том, как данные задачи могут быть решены сегодня.



Прошлые вебинары мы посвятили двум главным темам:


  • Что такое искусственный интеллект глазами юриста, как устроено машинное обучение и как исторически развивались технологии, позволяющие создавать компьютерные системы, которые приближены к возможностям человеческого интеллекта? (Вебинар 1)
  • Что такое LegalTech, какие инструменты автоматизации юридической функции существуют на текущий момент, какие возможности они предлагают, а также какие проблемы существуют в данной сфере? (Вебинар 2)

С учетом современного уровня развития цифровых технологий вопрос о том, можно ли создать полноценный Legal AI, уже не актуален. Текущих технологий достаточно для практической реализации подобных решений, поэтому данный вопрос трансформируется в иную плоскость: Как обучать Legal AI?. И с точки зрения методологии и подходов к решению данной задачи абсолютно все сталкиваются с одной и той же проблемой: у юристов отсутствует понимание инструментария и возможных результатов, а у программистов отсутствует глубокое понимание предметной области.


В третьем видео из цикла вебинаров мы хотим рассказать о оптимальном, на наш взгляд, подходе к преодолению данной проблемы и созданию юридического искусственного интеллекта.



Из данного видео Вы узнаете:


  • в чем заключается задача понимания естественного языка, а также какие решения и инструментарий в области NLP существует сегодня;
  • сколько образцов требуется, и каким образом размечать данные для обучения нейронных сетей;
  • какова роль графа знаний и как перейти от данных к знаниям.

P.S.:


Презентация доступна для скачивания по ссылке здесь.


Тайм-коды вебинара:


04:04 Как обучать Legal AI?
06:28 Шаг 1: постановка задачи
13:57 Задача 1: понимание естественного языка
21:54 Задача 2: разметка данных
30:30 Задача 3: оцифровка знаний
33:40 Переход от данных к смыслам
38:33 Трансформация в граф
45:15 Архитектура Legal AI
52:49 Гибридный AI
54:10 Индустриальные графы знаний
57:14 Методология внедрения Legal AI
01:05:10 Заключительные положения

Подробнее..

Искусственный интеллект в юриспруденции. Вебинар 4 Тренды и внедрение Legal AI

27.05.2021 14:07:09 | Автор: admin

Сегодня мы рады представить Вам заключительный вебинар из цикла лекций об искусственном интеллекте, который посвящен вопросам внедрения Legal AI в практическую деятельность юристов и актуальным трендам из мира искусственного интеллекта.



В рамках предыдущих лекций мы рассказали зрителям о многих вопросах, связанных с юридическим искусственным интеллектом:


  • на первом вебинаре мы определили что такое искусственный интеллект, каким образом он функционирует, как устроено машинное обучение, и какова роль информации в данном процессе; мы уделили время истории развития искусственного интеллекта, а также рассмотрели современные области применения AI-решений;
  • на втором вебинаре мы познакомились с существующими на текущий момент программными инструментами автоматизации юридической функции (LegalTech) и оценили возможности, которые они предлагают;
  • на третьем вебинаре мы рассказали о методиках создания и внедрения Legal AI в работу юристов, о технологической основе искусственного интеллекта, об основных задачах, которые необходимо решить при создании подобных решений, а также о том, как данные задачи могут быть решены сегодня, и что для этого требуется.

В заключительном видео из данного курса мы хотим поговорить о современных трендах из мира искусственного интеллекта и поделиться собственными взглядами о будущем развитии технологий.



Из данного видео Вы узнаете:


  • какова роль государства в развитии искусственного интеллекта, а также какие приоритетные направления могут быть выделены в стратегии развития AI в РФ;
  • кто из мировых стран сегодня лидирует в развитии и применении технологий искусственного интеллекта;
  • какие отрасли наиболее перспективны для внедрения технологий искусственного интеллекта, в чем их специфика и актуальные проблемы;
  • как технологии AI могут перевернуть представления о судебной системе, закупочном процессе и финансовой проверке заемщика;
  • в чем заключается концепция машиночитаемого права и почему ее так сложно воплотить в жизнь;
  • каковы дальнейшие перспективы цифровых технологий, и что нас ждет в будущем.

P.S.:


Презентация доступна для скачивания по ссылке здесь.


Тайм-коды вебинара:


01:41 AI национальный приоритет
05:58 Мировое лидерство в области AI
12:11 Национальная программа Цифровая экономика
17:22 Перспективные сферы для внедрения AI
24:53 Актуальные проблемы государственных органов
29:22 Актуальные проблемы судебной системы
32:42 Специфика банковской сферы
39:14 Специфика процедуры закупок
44:04 AI цифровой помощник
47:05 Концепция ЭДО ФНС России (2020)
50:48 Машиночитаемое право
54:50 ML/DL vs Legal AI
57:22 Взгляд в будущее
01:06:14 Интеграция технологий и человека
01:11:08 Заключительные положения

Подробнее..

Перевод Тематическое исследование распознавания именованных сущностей в биомедицине

04.06.2021 18:05:38 | Автор: admin

Не так давно у автора этой статьи возник вопрос: может ли простой метод сопоставления строк в сочетании с некоторыми простыми оптимизациями конкурировать с моделью, обученной с учителем, в биомедицинской задаче распознавания именованных сущностей (NER)? Автор сравнил эти два метода между собой и предположил, что при правильном подходе даже простые модели могут конкурировать со сложными системами, а мы к старту курса "Machine Learning и Deep Learning" перевели его статью.


В начале любого нового проекта НИОКР я ищу наиболее подходящее решение поставленной задачи. Несмотря на наличие нескольких очень интересных крупных моделей, одной из моих самых больших проблем является внедрение решения в производство без ущерба для результатов, которые я хотел бы поддерживать.

Что касается системы сопоставления строк, я использовал классификатор QuickUMLS. QuickUMLS [1] как система сопоставления строк принимает на вход строку (например, документ или реферат статьи, содержащий медицинские понятия) и выводит все промежутки документа, которые соответствуют понятиям унифицированного языка медицинских систем (UMLS). Затем эти понятия могут быть повторно использованы в других условиях или в качестве исходных данных для других систем машинного обучения. По этой причине QuickUMLS можно рассматривать как удобный инструмент предварительной обработки для получения релевантных понятий из клинических и биомедицинских текстов. Однако в этой статье мы сосредоточимся на использовании QuickUMLS в качестве классификатора на сложном наборе данных MedMentions [2].

Рисунок 1. Схематическое описание того, как работает QuickUMLS. Получив строку, базу данных UMLS, превращённую в БД simstring, модель возвращает оптимальные соответствия, идентификаторы понятий и семантические типыРисунок 1. Схематическое описание того, как работает QuickUMLS. Получив строку, базу данных UMLS, превращённую в БД simstring, модель возвращает оптимальные соответствия, идентификаторы понятий и семантические типы

Некоторые ключевые моменты, которые необходимо знать о биомедицинском NER

Прежде чем мы погрузимся в проблему, которую пытаемся решить, полезно описать некоторые особенности биомедицинского NER. В целом проблема NER заключается в поиске именованных сущностей (например, известных мест, лиц, организаций и т. д.) в тексте. Как вы, вероятно, догадываетесь, многие из этих сущностей можно найти через контекст. Например, в таком предложении, как "Сэм и Дрю пошли в Колизей", мы можем сделать вывод, что Колизей это место, потому что вы обычно ходите в какие-то места. Аналогично, мы можем предположить, что "Сэм" это имя собственное, потому что слова в позиции подлежащего "идти", которые не являются обычными словами, это обычно имена.

В отличие от этого биомедицинская NER заключается в поиске и однозначном определении интересующих биомедицинских терминов из текста, таких как заболевания, названия лекарств, а также общих терминов, таких как "больница.роддом" (hospital), "палата интенсивной терапии/подопечный отделения интенсивной терапии" или "алкоголь/спирт" (alcohol). Это важное различие, поскольку существует очень мало контекстуальной информации, определяющей, имеет ли данное слово медицинское значение. Чтобы привести немного нагруженный пример, рассмотрим слово "alcohol" в предложении "пациент выпил много alcohol" [для ясности того, что речь идёт о неоднозначности, оставлено оригинальное alcohol]. Тяжесть этого заключения зависит от того, относится ли оно к алкоголю, такому как пиво или вино, или к чистому спирту, такому как спирт для растирания. Для более полного обзора состояния дел в области биомедицинского NER см. эту запись в блоге моего коллеги из Slimmer AI, Сибрена Янсена.

Знать, какие понятия имеют медицинское значение, сложно без большого количества обучающих данных, которые, как правило, не всегда доступны. Поэтому многие системы используют унифицированный язык медицинских систем (UMLS), которая представляет собой большую онтологию, содержащую множество различных понятий вместе с их строковыми представлениями и другой информацией. Обратите внимание, что "понятие" здесь отличается от "строки", поскольку многие строки могут ссылаться на более чем одно понятие. Например, строка "alcohol" может относиться к спирту для растирания или к алкогольным напиткам.

В UMLS каждое понятие описывается уникальным идентификатором понятия (CUI), который является символическим идентификатором для любого данного уникального понятия, и семантическим типом (STY), который, в свою очередь, является идентификатором семейства, группирующим понятия с похожими характеристиками. Одной из причин, по которой UMLS является полезным, но в то же время сложным для работы, его огромный размер. Версия UMLS 2020AB, которую мы будем использовать в дальнейшем, насчитывает более 3 миллионов уникальных английских понятий. Маловероятно, что значительная часть этих понятий появится даже в больших аннотированных наборах данных.

Работа с набором данных MedMentions

Одним из таких наборов данных является MedMentions. Он состоит из 4 392 статей (заголовки и рефераты), опубликованных в Pubmed за 2016 год; аннотировано 352 K понятий (идентификаторов CUI) и семантических типов из UMLS. В документах имеется около 34 тысяч аннотированных уникальных понятий это около 1 % от общего числа понятий в UMLS. Факт показывает, что аннотирование упоминаний в UMLS является сложной задачей, которую не всегда можно решить с помощью машинного обучения с учителем.

Особый интерес в этом отношении представляет то, что корпус MedMentions включает в тестовое множество CUI, которые не встречаются в обучающем наборе. В целом, однако, эта задача всё ещё рассматривается как задача машинного обучения с учителем и с использованием семантических типов понятий UMLS в качестве меток. Поскольку UMLS имеет 127 семантических типов, это всё равно приводит к большому пространству меток. У набора данных MedMentions тоже есть уменьшенная версия st21pv, который состоит из тех же документов, что и обычный набор, но в нём аннотирован только 21 наиболее часто встречающийся семантический тип.

Полумарковская базовая модель получает около 45,3 по F-мере на уровне сущностей [2]. Другие подходы, включая BlueBERT [3] и BioBERT [4], были протестированы и улучшили оценку до 56,3 балла, используя точное соответствие на уровне сущностей [5]. Обратите внимание, что все эти подходы являются контролируемыми и, следовательно, полагаются на определённое совпадение между обучающим и тестовым множеством в плане понятий. Если понятие или метка никогда не встречалась в процессе обучения, в подходе машинного обучения с учителем будет сложно её правильно классифицировать. Далее в качестве меток мы будем использовать семантические типы из набора данных MedMentions.

QuickUMLS: без учителя и на основе знаний

В отличие от BERT QuickUMLS по своей сути является методом без учителя, а это означает, что он не полагается на обучающие данные. Точнее, QuickUMLS это метод, основанный на знаниях. То есть модель, вместо того чтобы иметь параметры, сообщающих, что прогнозировать, для прогнозирования меток полагается на внешнюю базу знаний. Подход подразумевает две вещи:

  1. Качество модели ограничено качеством базы знаний. Модель не может предсказать то, чего нет в базе знаний.

  2. Модель может обобщать не только аннотированные данные. Модель, которая обучалась с учителем и во время обучения не видела конкретной метки, в целом не может точно предсказать эти вещи. Исключение из правила методы обучения zero-shot.

Zero-shot learning (ZSL) это постановка задачи в машинном обучении, когда во время тестирования алгориттм наблюдает выборки из классов, которые не наблюдались во время обучения, и должен спрогнозировать, к какому классу они принадлежат.

Исходя из этих двух фактов, мы утверждаем, что основанные на знаниях методы хорошо подходят для набора данных MedMentions. Что касается первого пункта, база данных MedMentions была аннотирована с использованием понятий UMLS, поэтому сопоставление между базой знаний и набором данных является точным сопоставлением. Что касается второго пункта, набор данных MedMentions в тестовом наборе содержит понятия, которых нет в обучающем наборе.

Архитектура модели QuickUMLS

QuickUMLS как модель проста. Сначала она анализирует текст с помощью парсера spacy. Затем выбирает словесные n-граммы, то есть последовательности слов, на основе цитат и описаний цитат, а также списков стоп-слов. Это означает, что модель отбрасывает определённые словесные n-граммы, если они содержат нежелательные токены и знаки препинания. Подробные сведения об этих правилах можно найти в оригинальной статье [1]. После выбора кандидатов вся база данных UMLS запрашивается, чтобы найти понятия, частично соответствующие словам n-грамм. Поскольку точное сопоставление в такой огромной базе данных неэффективно и сложно, авторы выполняют приблизительное сопоставление строк с помощью simstring [6]. При задании текста QuickUMLS, таким образом, возвращает список понятий в UMLS вместе с их сходством со строкой запроса и другой связанной информацией. Например, текст У пациента было кровоизлияние, используя (по умолчанию) порог сходства строк 0,7, возвращает следующих кандидатов:

Для слова patient:

{term: Inpatient, cui: C1548438, similarity: 0.71, semtypes: {T078}, preferred: 1},{term: Inpatient, cui: C1549404, similarity: 0.71, semtypes: {T078}, preferred: 1},{term: Inpatient, cui: C1555324, similarity: 0.71, semtypes: {T058}, preferred: 1},{term: *^patient, cui: C0030705, similarity: 0.71, semtypes: {T101}, preferred: 1},{term: patient, cui: C0030705, similarity: 1.0, semtypes: {T101}, preferred: 0},{term: inpatient, cui: C0021562, similarity: 0.71, semtypes: {T101}, preferred: 0}

Для слова hemmorhage:

{term: No hemorrhage, cui: C1861265, similarity: 0.72, semtypes: {T033}, preferred: 1},{term: hemorrhagin, cui: C0121419, similarity: 0.7, semtypes: {T116, T126}, preferred: 1},{term: hemorrhagic, cui: C0333275, similarity: 0.7, semtypes: {T080}, preferred: 1},{term: hemorrhage, cui: C0019080, similarity: 1.0, semtypes: {T046}, preferred: 0},{term: GI hemorrhage, cui: C0017181, similarity: 0.72, semtypes: {T046}, preferred: 0},{term: Hemorrhages, cui: C0019080, similarity: 0.7, semtypes: {T046}, preferred: 0}

Как вы можете видеть, слово patient имеет три соответствия с корректным семантическим типом (T101) и два соответствия с корректным понятием (C0030705). Слово кровоизлияние также имеет лишние совпадения, включая понятие "No hemmorhage". Тем не менее кандидат с самым высоким рейтингом, если исходить из сходства, является правильным в обоих случаях.

В приложении QuickUMLS по умолчанию мы сохраняем только предпочтительные термины, то есть термины, для которых предпочтительным является 1, а затем сортируем по сходству. После мы берём семантический тип (семтип) кандидата с самым высоким рейтингом в качестве прогноза мы называем это базовой моделью (baseline model). Мы использовали seqeval со строгой парадигмой соответствия, которая сопоставима с предыдущей работой [5].

    BERT  QUMLS  P   .53    .27  R   .58    .36  F   .56    .31 Таблица 1  производительность базовой модели

Не слишком впечатляюще, правда? К сожалению, базовая модель страдает от плохого случая, когда она не оптимизирована для конкретной задачи. Таким образом, давайте оптимизируем её с помощью простой эвристики.

Улучшение QuickUMLS с помощью некоторых простых оптимизаций

Есть несколько способов улучшить QuickUMLS помимо его первоначальной производительности. Во-первых, отметим, что стандартный синтаксический анализатор, используемый QuickUMLS, по умолчанию является моделью spacy, т. е. en_core_web_sm. Учитывая, что мы имеем дело с биомедицинским текстом, нам лучше применить модель биомедицинского языка. В нашем случае мы заменили spacy на scispacy [7], en_core_sci_sm. Это уже немного повышает производительность без каких-либо затрат.

    BERT  QUMLS  + Spacy  P   .53    .27      .29  R   .58    .36      .37  F   .56    .31      .32 Таблица 2  Замена на scispacy

Другие улучшения можно получить, используя некоторую информацию из учебного корпуса. Хотя это действительно превращает QuickUMLS из метода без учителя в метод с учителем, зависимости от большого количества конкретных аннотаций по-прежнему нет. Другими словами, нет явного подходящего шага для конкретного корпуса: улучшения, которые мы собираемся сделать, также могут быть оценены с помощью небольшого набора аннотаций или знаний врача, которыми он владеет по определению.

Оптимизация порога QuickUMLS

Настройки по умолчанию для QuickUMLS включают пороговое значение 0,7 и набор метрик. Метрика определяет, как подсчитывается сходство строк, и может быть установлена в Jaccard, cosine, overlap и dice. Мы выполняем поиск по сетке, по метрике и различным пороговым значениям. Наилучшими результатами оказались пороговые значения 0,99, а это означает, что мы выполняем точные совпадения только с помощью SimString и метрики Jaccard, которая превосходит все другие варианты с точки зрения скорости и оценки. Как видите, мы всё ближе и ближе подходим к производительности BERT.

    BERT  QUMLS  + Spacy  + Grid  P   .53    .27      .29     .37  R   .58    .36      .37     .37  F   .56    .31      .32     .37 Таблица 3  Поиск по сетке параметров

Преимущество добавления априорной вероятности

Напомним, что выше мы просто выбрали лучшего подходящего кандидата, основываясь на том, была ли это предпочтительная строка, и на их сходстве. Однако во многих случаях разные понятия будут иметь одно и то же строковое представление, как, например, в вышеупомянутом примере с alcohol. Это затрудняет выбор оптимального кандидата без модели устранения неоднозначности, которая требует контекста, и снова превращает проблему обучения в проблему обучения с учителем или по крайней мере требующую примеров контекстов, в которых встречаются термины. Один из простых выходов из этой головоломки состоит в том, чтобы учесть, что при прочих равных условиях некоторые семантические типы просто более вероятны и, следовательно, более вероятны в данном корпусе. Такая вероятность называется априорной вероятностью.

В нашем случае мы оцениваем априорную вероятность принадлежности к классу через обучающий набор данных, как это было бы сделано в хорошо известном наивном Байесовском классификаторе. Затем для каждого семантического типа, который мы извлекаем, в свою очередь, для каждого набора кандидатов, мы берём максимальное сходство, а затем умножаем его на предыдущий. В терминах нейронной сети вы можете представить это как максимальное объединение на уровне класса. Оно также означает, что мы игнорируем приоритетность кандидатов.

    BERT  QUMLS  + Spacy  + Grid  + Priors  P   .53    .27      .29     .37       .39  R   .58    .36      .37     .37       .39  F   .56    .31      .32     .37       .39 Таблица 4  Добавление приоров

К сожалению, это всё, что мы могли бы получить, используя простую систему в QuickUMLS. Учитывая, что мы в конечном счёте использовали порог 0,99, это означает, что мы вообще не используем функциональность приблизительного сопоставления QuickUMLS. Удаление приблизительного сопоставления также значительно ускорит работу всей системы, так как большая часть времени алгоритма теперь тратится на сопоставление в QuickUMLS.

Глубокое погружение в анализ ошибок: соответствовало ли наше решение поставленной задаче?

Когда мы выполняем задачу распознавания именованных сущностей, мы можем допустить ошибки нескольких типов. Во-первых, мы можем извлечь правильный промежуток, но неправильный класс. Это происходит, когда мы находим правильный термин, но даём ему неправильную метку: например, мы думаем, что alcohol относится к напитку, в то время как он относится к дезинфицирующему средству. Во-вторых, мы также можем извлечь промежуток частично, но при этом всё равно сопоставить его правильной метке. В этом случае возможно рассматривать совпадение как частичное совпадение. В нашем скоринге мы считали правильными только точные совпадения. Пример извлечение мягкого анестетика, когда золотым стандартом является анестетик. Мы также можем полностью пропустить какие-то промежутки, например потому, что UMLS не содержит термина, или извлечь не соответствующие упоминаниям золотого стандарта промежутки. На рисунке ниже показано, какие виды ошибок допускает наша система:

Это показывает, что ошибки, которые делает QuickUMLS, не относятся к одной конкретной категории. Она извлекает слишком много элементов, но, когда она делает это, то также часто присваивает им неправильную метку. Это показывает, что QuickUMLS можно использовать в качестве системы предварительного извлечения, после чего для назначения правильной метки можно применить систему устранения неоднозначности.

Заключение

Как вы можете видеть из результатов, готовая система извлечения терминологии может быть использована в качестве эффективной системы NER без обучения. Получение обучающих данных для конкретных случаев применения часто может быть трудоёмким, снижающим скорость R&D процессом. Построенный нами классификатор QuickUMLS показывает, что мы можем многого добиться с очень небольшим количеством обучающих примеров. И, будучи разумными в том, как использовать ресурсы, мы в процессе исследований и разработок для биомедицинских исследований сэкономили много времени. Модифицированный классификатор QuickUMLS можно опробовать здесь, на github. Преимущество подхода может означать, что мы нашли решение, достаточно надёжное для достижения конечного результата, простое в разработке и тестировании, а также достаточно небольшое, чтобы легко внедрить его в разработку продукта.

Именно сегодня медицина одна из самых важных областей знания, а обработка естественного языка одна из самых интересных областей в ИИ: чем лучше искусственный интеллект распознаёт речь, текст, чем точнее обрабатывает её, тем ближе он к здравому смыслу человека.

Вместе с тем одни из самых успешных моделей так или иначе комбинируют подходы из разных областей ИИ, например мы писали о визуализации пения птиц, чтобы ИИ работал со звуком так же, как работает с изображениями и если вам интересна не только обработка естественного языка, но и эксперименты с ИИ в целом, вы можете обратить внимание на наш курс "Machine Learning и Deep Learning", партнёром которого является компания NVIDIA.

Ссылки

[1]L. Soldaini, and N. Goharian. Quickumls: a fast, unsupervised approach for medical concept extraction, (2016), MedIR workshop, SIGIR

[2]S. Mohan, and D. Li, Medmentions: a large biomedical corpus annotated with UMLS concepts, (2019), arXiv preprint arXiv:1902.09476

[3]Y. Peng, Q. Chen, and Z. Lu, An empirical study of multi-task learning on BERT for biomedical text mining, (2020), arXiv preprint arXiv:2005.02799

[4]J. Lee, W. Yoon, S. Kim, D. Kim, S. Kim, C.H. So, and J. Kang, BioBERT: a pre-trained biomedical language representation model for biomedical text mining, (2020), Bioinformatics, 36(4)

[5]K.C. Fraser, I. Nejadgholi, B. De Bruijn, M. Li, A. LaPlante and K.Z.E. Abidine, Extracting UMLS concepts from medical text using general and domain-specific deep learning models, (2019), arXiv preprint arXiv:1910.01274.

[6]N. Okazaki, and J.I. Tsujii, Simple and efficient algorithm for approximate dictionary matching, (2010, August), In Proceedings of the 23rd International Conference on Computational Linguistics (Coling 2010)

[7]M. Neumann, D. King, I. Beltagy, and W. Ammar, Scispacy: Fast and robust models for biomedical natural language processing, (2019),arXiv preprint arXiv:1902.07669.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Грамматический разбор для естественных языков. Ч.1 Языки описания языков

06.06.2021 10:13:43 | Автор: admin

Исторически первой попыткой формализовать язык и автоматизировать его разбор были регулярные выражения, придуманные С.К. Клейни в 1951. Регулярное выражение составляется из символов языка ("терминалов"), и трёх операций: конкатенация, чередование и замыкание. Для разбора регулярных выражений достаточно ДКА без памяти: разборщик знает, в каком состоянии он находится сейчас, но не помнит ничего о своих прошлых состояниях. Это значит, что языки, допускающие вложенные конструкции например, язык вложенных скобок (n)n и язык самих регулярных выражений невозможно описать регулярными выражениями. Естественные языки тоже допускают конструкции неограниченной вложенности ("Вот два петуха, которые будят того пастуха, который бранится скоровницей строгою, которая доит корову безрогую, лягнувшую старого пса без хвоста, который зашиворот треплет кота, который пугает иловит синицу, которая часто ворует пшеницу, которая втёмном чулане хранится вдоме, который построил Джек."), поэтому для описания естественных языков регулярные выражения недостаточно выразительны.

Более выразительный способ описания языков формальные грамматики предложил Н. Чомски в 1956. Предложения на английском довольно неплохо поддаются такому описанию:

S   NP VPNP  N' | Det N'N'  N | Adj N | N PP | N' CPVP  V | V NP | V PPPP  P NPCP  VnP | C VP | C SVnP  Vn | Adv VnP | VnP Conj VnN   This | rooster | morn | judge | man | maiden | cow |     horn | dog | cat | rat | malt | house | JackV   is | crowed | woke | married | kissed | milked |     tossed | worried | killed | ate | lay | builtVn  shaven | shorn | tattered | torn | forlornAdj  crumpled Adv  allP    in | with Det  theC    thatConj  and

Этой формальной грамматики из пары десятков абстрактных символов ("нетерминалов") и пары десятков правил вывода (не считая лексикона) достаточно, чтобы разобрать вложенные конструкции в том самом предложении про петуха, коровницу, пса и кота:

Для контекстно-свободных грамматик (CFG) когда в левой части каждого правила вывода стоит ровно один нетерминал, как в этом примере существуют эффективные алгоритмы разбора текста; поэтому почти все языки программирования описываются CFG. В отличие от языков программирования, тексты на естественных языках часто допускают несколько возможных разборов например, [that woke the judge]CP могло бы относиться и к mornN' выбор между которыми требует семантического разбора. Однако для естественных языков с более гибким порядком слов, чем в английском, CFG недостаточно: уже для разбора русского стишка понадобились бы и VP V PP, N' N Adj ("бранится скоровницей строгою"), и VP PP V, N' Adj N ("втёмном чулане хранится"); чуть ли не для каждого правила вывода в грамматику понадобилось бы добавить его "зеркальное отражение" и тогда количество возможных разборов текста растёт экспоненциально. Ещё хуже то, что при топикализации листья поддерева, соответствующего нетерминалу, могут идти в тексте не подряд: например, в "Я тебя детям просил помочь, [а не родителям]" члены сложного дополнения [детям помочь]CP разделены сказуемым просилV. В нескольких языках исследователи отмечают голландский и швейцарский немецкий есть не связанные с топикализацией "параллельно-вложенные" конструкции, например:

... das

mer

d'chind

em Hans

es huus

lnd

hlfe

aastriiche

... что

мы

детей

Гансу

дом

просим

помочь

покрасить

"... что мы просим детей помочь Гансу покрасить дом"

Такую цепочку можно наращивать неограниченно, как в стишке про дом Джека; но глаголы во второй части предложения должны идти в том же порядке, в котором идут их дополнения в первой части предложения. Такие явления скрэмблинг при топикализации и "параллельно-вложенные" конструкции в голландском и швейцарском немецком формальные грамматики не в состоянии описать.

Более формально можно доказать, что CFG могут описывать вложенные конструкции в частности, язык вложенных скобок (n)n описывается тривиальной грамматикой S () | (S) но неиерархические зависимости описанию не поддаются: в частности, CFG не может описать язык anbncn или язык дважды повторённых строк ((a|b)+)2.

В 1987 в Осакском университете разработали ещё более выразительный способ описания языков множественные CFG (MCFG); одновременно с этим и независимо от осакцев в Университете Пенсильвании разработали линейные контекстно-свободные системы перезаписи (LCFRS), которые отличаются от MCFG только более простой формой записи. В MCFG/LCFRS нетерминалы становятся многоместными предикатами например, эта LCFRS описывает язык дважды повторённых строк:

S(XY)  P(X,Y)P(a,a)  P(b,b)  P(XY,ZW)  P(X,Z),P(Y,W)

Стрелка влево обозначает дизъюнкт Хорна. В левой части каждого правила вывода описывается составление аргументов нетерминала-предиката из терминалов и переменных; в правой перечисляются предикаты, которым переменные должны удовлетворять. Порядок записи предикатов в правой части не имеет значения. Каждая переменная, использованная в левой части, должна быть ограничена предикатом в правой части; повторное использование одной переменной не допускается. Если в левой части нет переменных, то правая часть может быть пустой это значит, что никаких дополнительных условий, кроме совпадения терминалов, не накладывается. Способ записи LCFRS сильно отличается от принятого для CFG например, грамматику языка дважды повторённых строк можно было бы записать в более прозрачном, хоть на практике и не используемом виде:

S  XY   P  X,YP  a,a | b,b | XY,ZW   P  X,Z  P  Y,W

В отличие от CFG, где каждый узел дерева разбора соответствует подстроке разобранного текста, при разборе MCFG/LCFRS узел дерева разбора может соответствовать нескольким подстрокам, идущим в тексте не подряд: (толщина линии показывает, сколько подстрок нетерминал получает от дочерних узлов)

Практическая польза MCFG/LCFRS в том, что они позволяют описать "разорванный" член предложения (детям, помочь)CP:

VP(X,Y)  NP(X),V(Y)CP(X,Y)  VP(X,Y)VP(X,YZW)  NP(X),V(Z),CP(Y,W)S(XYZ)  NP(X),VP(Y,Z)

Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Грамматический разбор для естественных языков. Ч.2 Алгоритм КокаЯнгераКасами (CYK)

07.06.2021 10:04:02 | Автор: admin

Часть .1: Языки описания языков

В идеале нам хотелось бы разбирать текст за линейное время и за один проход. Регулярные выражения это позволяют, но уже с CFG это не получится: например,S A | B; A a | x A; B b | x Bпревращает строкуxxaв дерево из узловA, а строкуxxbв дерево из узловB и пока разборщик не увидит последний символ строки, он не знает, что делать со всеми предыдущими символами. Поэтому на грамматики для языков программирования накладывают дополнительные ограничения по сути, чтобы для разбора не приходилось "заглядывать вперёд" позволяющие разбирать текст программы за один проход. Кто ковырялся в компиляторах, тот наверняка знаком с LL- и LR-разбором, и имеет опыт "подгонки" грамматики языка под требования конкретного алгоритма разбора. Но при работе с естественными языками нет возможности "подправить" язык для удобства разбора приходится работать с тем языком, какой есть.

В 1960-х был разработан алгоритм CYK для разбора произвольного CFG. Считается, что впервые его опубликовали независимо друг от друга И. Сакаи из японского НИИ Минобороны в 1961 и Дж. Кок из Нью-Йоркского университета в 1962. В 1966 тот же самый алгоритм публиковали опять независимо Д. Янгер из General Electric и Т. Касами из Университета Иллинойса. Янгер в своей публикации упоминает имена Кока и Сакаи, но не ссылается ни на какие конкретные их работы: по всей видимости, работы Кока и Сакаи Янгеру как и мне сейчас не были доступны. Чтобы никому из изобретателей алгоритма не было обидно, его называют в честь сразу троих, хотя они, скорее всего, даже не были между собой знакомы.

Идея CYK заключается в разборе "снизу вверх" от терминалов к S и использовании динамического программирования для избежания повторных вычислений: вначале составим все узлы, выводимые непосредственно из терминалов, и поместим их на "доску". На каждом шаге алгоритма возьмём с доски один узел, составим все возможные узлы с его участием, и добавим на доску ещё и их. Если на доске появился S для всего текста целиком, значит разбор удался; если все узлы с доски обработаны, но S там так и не появился значит, разбор не удался. Википедия в качестве примера разбирает предложение "She eats the fish with a fork." по следующей грамматике:

S  NP VPVP  VP PP | V NP | VPP  P NPNP  Det N | sheV  eatsP  withN  fish | forkDet  a | the

Шаг

Необработанные узлы на доске

Обработанные узлы на доске

Добавляемые на доску узлы

1

SheNP, eatsV, theDet, fishN, withP, aDet, forkN

2

SheNP, eatsV, theDet, fishN, withP, aDet, forkN

3

eatsV, theDet, fishN, withP, aDet, forkN

SheNP

eatsVP

4

theDet, fishN, withP, aDet, forkN,eatsVP

SheNP, eatsV

[the fish]NP

5, 6

fishN, withP, aDet, forkN,eatsVP, [the fish]NP

SheNP, eatsV, theDet

7

aDet, forkN,eatsVP, [the fish]NP

SheNP, eatsV, theDet, fishN, withP

[a fork]NP

8

forkN,eatsVP, [the fish]NP, [a fork]NP

SheNP, eatsV, theDet, fishN, withP, aDet

9

eatsVP, [the fish]NP, [a fork]NP

SheNP, eatsV, theDet, fishN, withP, aDet, forkN

[She eats]S

10

[the fish]NP, [a fork]NP, [She eats]S

SheNP,eatsV, theDet, fishN, withP, aDet, forkN, eatsVP

[eats the fish]VP

11

[a fork]NP, [She eats]S, [eats the fish]VP

SheNP,eatsV,theDet,fishN,withP, aDet, forkN, eatsVP,[the fish]NP

[with a fork]PP

12

[She eats]S, [eats the fish]VP, [with a fork]PP

SheNP,eatsV,theDet,fishN, withP, aDet, forkN, eatsVP,[the fish]NP, [a fork]NP

[She eats the fish]S

13

[eats the fish]VP, [with a fork]PP, [She eats the fish]S

SheNP,eatsV,theDet,fishN, withP, aDet, forkN, eatsVP,[the fish]NP, [a fork]NP,[She eats]S

[eats the fish with a fork]VP

14, 15

[with a fork]PP, [She eats the fish]S, [eats the fish with a fork]VP

SheNP,eatsV,theDet,fishN, withP, aDet, forkN, eatsVP,[the fish]NP, [a fork]NP,[She eats]S, [eats the fish with a fork]VP

16

[eats the fish with a fork]VP

SheNP,eatsV,theDet,fishN, withP, aDet, forkN, eatsVP,[the fish]NP, [a fork]NP,[She eats]S, [eats the fish with a fork]VP, [with a fork]PP, [She eats the fish]S

[She eats the fish with a fork]S

Какова сложность CYK для CFG? На доске может быть как максимум|N|\cdot\frac{n\cdot(n+1)}2узлов для каждого нетерминального символа и для каждой подстроки входного текста; поэтому доску удобно хранить в виде трёхмерного массива, индексы которого нетерминал, начало подстроки и конец подстроки. На каждом шаге обрабатывается один узел с доски значит, для разбора потребуютсяO(n^2)шагов. На каждом шаге проверяются все правила грамматики, и для каждого правила, где обрабатываемый узел подходит под правую часть, будут проверены все узлы с доски, могущие участвовать в правой части вместе с обрабатываемым узлом. Осталось определить сложность такого шага.

Исходный алгоритм CYK работал с CFG внормальной формеХомского(CNF) в правой части каждого правила вывода допускались либо один терминал, либо два нетерминала. Для правилаA BC"все узлы с доски, могущие участвовать в правой части вместе с обрабатываемым узлом" это как максимумnузлов с индексами (C, endB, ?) и/или как максимумnузлов с индексами (B, ?, startC). (Если B=C, то подходят оба набора вариантов.) Получается, что сложность одного шага CYK для CNF этоO(n), а сложность всего разбора этоO(n^3).

А если CFG не в CNF? Для правилаA BCDпридётся обработать:

  • как максимумnузлов с индексами (C, endB, ?), и для каждого из них как максимумnузлов с индексами (D, endC, ?);

  • как максимумn^2пар узлов с индексами (B, ?, startC), и (D, endC, ?);

  • как максимумnузлов с индексами (C, ?, startD), и для каждого из них как максимумnузлов с индексами (B, ?, startC).

Значит, если в правой части правила вывода допустить до трёх нетерминалов, то сложность одного шага CYK будетO(n^2), а сложность всего разбора будетO(n^4). Обобщая на произвольную CFG видим, что сложность разбора при помощи CYK будетO(n^{r+1}), гдеr максимальная длина правой части правил вывода ("ранг грамматики").

Теперь обобщим CYK для MCFG, сохраняя его центральную идею: на каждом шаге алгоритма берём с доски один узел, составляем все возможные узлы с его участием, и добавляем их на доску. Разбор "Я тебя детям просил помочь!" по грамматике, приведённой в ч.1 и для удобства повторённой здесь, получится таким:

VP(X,Y)  NP(X),V(Y)CP(X,Y)  VP(X,Y)VP(X,YZW)  NP(X),V(Z),CP(Y,W)S(XYZ)  NP(X),VP(Y,Z)NP(я)  NP(тебя)  NP(детям)  V(просил)  V(помочь)  

Шаг

Необработанные узлы

Обработанные узлы

Добавляемые узлы

1

ЯNP, тебяNP, детямNP, просилV, помочьV

24

ЯNP, тебяNP, детямNP, просилV, помочьV

5

просилV, помочьV

ЯNP, тебяNP, детямNP

(Я, просил)VP, (тебя, просил)VP, (детям, просил)VP

6

помочьV, (Я, просил)VP, (тебя, просил)VP, (детям, просил)VP

ЯNP, тебяNP, детямNP, просилV

(Я, помочь)VP, (тебя, помочь)VP, (детям, помочь)VP

7, 8

(Я, просил)VP, (тебя, просил)VP, (детям, просил)VP, (Я, помочь)VP, (тебя, помочь)VP, (детям, помочь)VP

ЯNP, тебяNP, детямNP, просилV, помочьV

(Я, просил)СP, (тебя, просил)СP

9

(детям, просил)VP, (Я, помочь)VP, (тебя, помочь)VP, (детям, помочь)VP, (Я, просил)СP, (тебя, просил)СP

ЯNP, тебяNP, детямNP, просилV, помочьV, (Я, просил)VP, (тебя, просил)VP

[тебя детям просил]S, (детям, просил)СP

1012

(Я, помочь)VP, (тебя, помочь)VP, (детям, помочь)VP, (Я, просил)СP, (тебя, просил)СP, [тебя детям просил]S, (детям, просил)СP

ЯNP, тебяNP, детямNP, просилV, помочьV, (Я, просил)VP, (тебя, просил)VP, (детям, просил)VP

(Я, помочь)СP, (тебя, помочь)СP, (детям, помочь)CP

1318

(Я, просил)СP, (тебя, просил)СP, [тебя детям просил]S, (детям, просил)СP, (Я, помочь)СP, (тебя, помочь)СP, (детям, помочь)CP

ЯNP, тебяNP, детямNP, просилV, помочьV, (Я, просил)VP, (тебя, просил)VP, (детям, просил)VP, ...

19

(детям, помочь)CP

ЯNP, тебяNP, детямNP, просилV, помочьV, (Я, просил)VP, ...

(тебя, [детям просил помочь])VP

20

(тебя, [детям просил помочь])VP

ЯNP, тебяNP, детямNP, просилV, помочьV, (Я, просил)VP, ...

[Я тебя детям просил помочь]S

Как реализовать такой разбор, и какой получится его сложность? Трёхмерной доской уже не обойтись: узлы двухместных предикатов, таких как VP и CP, могут существовать для любой пары подстрок входного текста, так что понадобится индексация пятёрками (нетерминал, начало1, конец1, начало2, конец2). Одно уже это повышает число шагов разбора до O(n^4) . Теперь надо понять, какие узлы такой пятимерной доски могут участвовать вместе с обрабатываемым узлом в применении правила вывода например, правила P(XY,ZW) P(X,Z),P(Y,W) из приведённой в ч.1 грамматики языка дважды повторённых строк. Подходящие узлы будут находиться по индексам (P, endX, ?, endZ, ?) и (P, ?, startY, ?, startW), так что их будет O(n^2) штук. В предположении, что "размерность грамматики" равна 2 (т.е. предикаты как максимум двухместные) и её ранг тоже равен 2 (т.е. правила вывода допускают конкатенацию как максимум двух переменных) получаем, что сложность CYK-разбора будет O(n^6) .

А если ранг выше, как в вышеприведённой грамматике, где правило VP(X,YZW) NP(X),V(Z),CP(Y,W) включает конкатенацию трёх переменных? В этом случае подходящие для V узлы будут находиться по индексам (CP, ?, startZ, endZ, ?) и (NP, ?, ?, , ), так что их будет уже O(n^4) штук. Аналогично, подходящие для NP узлы будут находиться по индексам (CP, ?, ?, ?, ?) и (V, endY, startW, , ), и их тоже будетO(n^4) штук. Получаются O(n^4) шагов сложностью O(n^4) каждый. Огромное число бесполезных шагов заметно и в приведённом примере разбора: в предложении, где три NP и два V, из каждой их попарной комбинации выводится по узлу VP и CP.

В целом можно видеть, что алгоритм разбора остаётся полиномиальным, но показатель степени быстро растёт по мере усложнения грамматики: обобщая приведённые рассуждения, получим сложность O(n^{d\cdot(r+1)}) , гдеd размерность грамматики. Это значит, что разбор можно существенно упростить, заменяя конкатенацию более чем двух переменных на новый нетерминал, например правило VP(X,YZW) NP(X),V(Z),CP(Y,W) на пару правил CP'(Y,ZW) V(Z),CP(Y,W); VP(X,YZ) NP(X),CP'(Y,Z). Полученное таким образом дерево разбора дальше от теоретического описания структуры предложения, но тем не менее, такая "нормализация" грамматик широко применяется.

Как отмечалось в ч.1, тексты на естественных языках часто допускают несколько возможных разборов, и среди этих возможных разборов требуется выбрать наиболее вероятный. Самое лобовое решение это присвоить каждому правилу вывода вероятность (например, VP(X,Y) 75% NP(X),V(Y); VP(X,YZW) 25% NP(X),V(Z),CP(Y,W) такую вероятностную грамматику называют PMCFG), и тогда вероятность разбора это произведение вероятностей всех использованных в нём правил вывода. Намного удобнее, чем с микроскопическими вероятностями, работать с их логарифмами: тогда перемножение вероятностей заменяется сложением их логарифмов.

В заключение поделюсь моей реализацией CYK для PMCFG на Python, практически применимой (требовалось разбирать предложения до 10 слов в пределах 5 секунд) для d\le2,\ r\le4 , но способной работать и вне этих ограничений. Вместо 2d+1 -мерного массива для доски я использую двухуровневый dict, в котором ключи первого уровня нетерминалы, второго объекты Chunk, а значения логарифмы вероятностей. Для каждой комбинации из нетерминала и набора подстрок может существовать только один экземпляр Chunk, так что для сравнения объектов достаточно сравнить их id это многократно ускоряет поиск в dict по сравнению с использованием в качестве ключа обычной записи (data class).

class Chunk:    instances = {}    def __new__(cls, symbol, inputs):        key = symbol, tuple(inputs)        i = cls.instances.get(key)        if i:           return i        i = super(Chunk, cls).__new__(cls)        i.symbol = symbol        i.inputs = inputs        cls.instances[key] = i        return idef parse(grammar, string):    chart = {n: {} for n in grammar.non_terminals}    agenda = {Chunk(rule.left.symbol, [(i, i + 1)]): rule.prob              for i in range(len(string)) for rule in grammar.rules              if rule.terminating and rule.left.inputs[0] == string[i]}    goal = Chunk(grammar.start, [(0, len(string))])    while agenda and goal not in chart[grammar.start]:        (best, prob) = max(agenda.items(), key=lambda x: x[1])        chart[best.symbol][best] = prob        del agenda[best]        for rule in grammar.rules:            if not rule.terminating and any(best.symbol == prod.symbol for prod in rule.right):                right = list(rule.right)                for perm in itertools.product(*(chart[prod.symbol].keys() for prod in right)):                    if best in perm:                        sat = satisfies(perm, rule.left, right, chart)                        if sat is not None:                            chunk = sat[0]                            new_prob = sat[1] + rule.prob                            if (chunk not in chart[chunk.symbol]) and \                               ((chunk not in agenda) or (agenda[chunk] != new_prob)):                                agenda[chunk] = new_prob    return chart[grammar.start].get(goal)def satisfies(perm, left, right, chart):    mapping = {}    prob = 0    for chunk, pred in zip(perm, right):        for c_i, p_i in zip(chunk.inputs, pred.inputs):            mapping[p_i] = c_i        prob += chart[chunk.symbol][chunk]    inputs = []    for r in left.inputs:        if len(r) == 1:            inputs.append(mapping[r])        else:            mapped = [mapping[c] for c in r]            for k in range(len(mapped) - 1):                if mapped[k][1] != mapped[k + 1][0]:                    return None            inputs.append((mapped[0][0], mapped[-1][1]))    if len(inputs) > 1:        for i in range(len(inputs) - 1):            if inputs[i][1] > inputs[i + 1][0]:                return None    return Chunk(left.symbol, inputs), prob

Самые внимательные заметят, что эта реализация допускает терминалы только в правилах вида N(t) , по аналогии с CNF. Это отчасти дань традиции, предписывающей категоризовать каждое слово перед составлением синтаксических конструкций с его участием.


Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Категории

Последние комментарии

  • Имя: Murshin
    13.06.2024 | 14:01
    Нейросеть-это мозг вселенной.Если к ней подключиться,то можно получить все знания,накопленные Вселенной,но этому препятствуют аннуннаки.Аннуннаки нас от неё отгородили,установив в головах барьер. Подр Подробнее..
  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru