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

Обработка естественного языка

Машинное обучение на помощь руководителю разработки

09.11.2020 10:07:58 | Автор: admin


Интро


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


С другой стороны, есть много статей о решении задач машинного обучения на примере нескольких публичных затертых до дыр датасетов: MNIST, IMDB, ENRON, TITANIC. С ними ситуация обратная все вершины уже покорены, алгоритмы известны, можно добиться рекордных цифр даже на простеньком ноутбуке. Снова мимо. Гораздо сложнее найти материал о практическим применении МО для решения повседневных задач. Данная статья, как можно догадаться, как раз из этой серии. На подробном практическом примере попробуем выяснить, можно ли собрать личного интеллектуального помощника (пусть и узкоспециализированного), сложно ли это, какие знания нужны и какие проблемы подстерегают на этом пути.


Примечание: в данной статье основное внимание уделено классическому машинному обучению. Лишь небольшой блок содержит сравнение с нейросетевым подходом (BERT).


Идея


Вплотную подойдя к итоговому заданию курса, я задался вопросом могу ли я сделать что-то более впечатляющее, чем поиск по тексту писем ENRON так называемых POI (points of interest, главных участников исторического мошенничества)? Разумно было выбрать задачу из той же самой области машинного обучения обработки естественного языка (NLP). Она включает в себя широкий спектр задач, таких как машинный перевод, создание вопросно-ответных систем, чат-ботов, суммаризации, классификации и др. Было решено остановиться на классификации: ее качество очень легко измеряется стандартными метриками (Accuracy, Precision, Recall, F1), а обучение можно начинать с сотен образцов. Примером такой задачи может быть то, с чем читатель наверняка уже сталкивался с точки зрения пользователя определить, является ли письмо нежелательным (SPAM or HAM); в этой задаче два результирующих класса, бинарный выбор. Стоит заметить, что распространенные алгоритмы машинного обучения, такие какие логистическая регрессия, метод опорных векторов, нейросети и многие другие, не ограничивают количество возможных классов, тем самым позволяя использовать десятки, сотни, а порой и тысячи возможных классов на выходе алгоритма.



Помимо верхнеуровневого выбора задачи необходимо было определиться с источником входных данных. В своей работе в НРД мы много используем RedMine: в нем хранятся заявки, задачи, ошибки, инциденты, тест-кейсы и т.д Многие сущности сопровождаются детальным описанием, текста в сумме получается в достаточном для экспериментов с ML количестве.
Первая возникшая идея а что если доверить алгоритму распределение новых задач по команде разработки? Типичная заявка на доработку проходит стадии аналитики, согласования, оценки и декомпозиции на подзадачи, разработки, тестирования, опытной и, наконец, промышленной эксплуатации. На этапе передачи заявки в разработку требуется распределить задачи по команде разработчиков. Эксклюзивными знаниями по особенностям системы в одиночку разработчики не обладают (знания стараемся распространять в командах), однако удачное распределение может повысить продуктивность команды в целом. На вход алгоритма могли бы поступать описания задач, на выходе целевые разработчики. После первых экспериментов оказалось, что краткого описания задачи недостаточно и результаты неустойчивы. Сохранив идею классификации сущностей RedMine по разработчикам, было решено перейти к дефектам (ошибкам), вместо задач по доработке. Как оказалось, в дефектах при определенной культуре тестирования вполне достаточно информации для определения целевого разработчика. Итак, формальная постановка задачи такова: имея на входе текстовое описание дефекта требуется с определенной долей вероятности определить, кому из разработчиков ей следует заняться.


Неформальная постановка (для тех, кто задается вопросом, зачем такое понадобилось)

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


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


Выбор данных


Было бы логично предположить, что большую часть времени специалист по машинному обучению проводит за питьем смузи разработкой алгоритмов, настройкой параметров и, например, созданием новых архитектур нейросетей. Однако на практике порядка 80% процентов времени уходит на работу с данными выборку, чистку, преобработку и т.д. В нашем случае источником данных является RedMine (БД развернута на MS SQL Server). Никакого rocket science на данном этапе нет методично пишется и проверяется запрос, который возвращает данные в удобном для применения в алгоритме виде (плоский CSV-файл):


TEXT DEVELOPER ID
Сломался выпадающий список... Иванов И. 123456
Неожиданно завершилось выполнение... Петров П. 654321

Для обучения алгоритма используется только склеенный текст описания и заголовка дефекта в RM. ID использовался во время разработки для контроля. Целевой переменной (т.н. label) в данном случае является фамилия и имя разработчика. Кстати, тут и далее задача классификации рассматривается в разрезе систем. В частности, будут приводится результаты работы классификатора на одной из них с примерно 3к примерами и 12 классами (разработчиками).
Имеющиеся данные целесообразно разбить на обучающую и тестовую выборки:


train_data, test_data, train_target, test_target, train_rm, test_rm = \train_test_split(csv_data[:, 0], csv_data[:, 1], csv_data[:, 2], test_size=0.2, random_state=42) 

Примечание: в более поздней версии на части разбивался непосредственно pandas.DataFrame в разрезе колонок.


Подробнее здесь.


Хронология реализации, улучшение метрик, развитие


Алгоритмы машинного обучения не умеют работать с текстом в сыром виде. Являясь по своей природе сложными математическими функциями (отображениями), они требуют на вход числа. Наиболее простым методом преобразования текста в числа является метод "сумка слов" (bag of words). Он подразумевает сопоставление каждому слову некоторого числа. В каждой строке входного корпуса подсчитывается количество уникальных слов, а так же частоты их возникновения. После применения данного метода входной корпус из N образцов превращается в двумерный массив NxM, где M количество уникальных слов. Делается это примерно следующим образом:


vectorizer = CountVectorizer(max_features=20000) # max_features ограничивает сверху M из предыдущего абзаца.train_data_features = vectorizer.fit_transform(train_data).toarray()test_data_features = vectorizer.transform(test_data).toarray()

После этого текст уже готов к подаче в классификатор. В первой версии использовался GaussianNB так называемый "наивный" Байесовский классификатор. Он учится находить зависимости между частотами возникновения в обучающем наборе тех или иных слов и целевой переменной (класса, в нашем случае разработчика). Его достоинства: простота, высокая скорость работы и интерпретируемость результатов. Кстати, наивным он называется потому, что не анализирует взаимное расположение слов, а только частоты. Например, он не поймет, что 'Chicago Bulls' это не быки в Чикаго, а название команды, употребляемое в определенных контекстах. Этот недостаток можно сгладить, об этом далее в статье.


cls = GaussianNB()cls.fit(train_data_features, train_target)prediction = cls.predict(test_data_features)

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


print('Accuracy: ' + str(accuracy_score(prediction, test_target)))print(confusion_matrix(test_target, prediction))print(classification_report(test_target, prediction))

Accuracy это процент верно предсказанных классов, confusion_matrix дает матрицу разброса предсказаний (чем больше на диагонали, тем лучше алгоритм), а classification_report формирует детальный отчет по классам в разрезе терх метрик: precision, recall и f1 score.
На момент первого измерения точность была в районе 0.32. Это намного ниже ожидания. Для исправления ситуации был разработан способ очистки входных текстовых данных.
Каждая строка входного текста была обработана процедурой prepare_line:


stemmer = SnowballStemmer("russian")russian_stops = set(stopwords.words("russian"))def prepare_line(raw_line):    # 1. Удаление ненужных символов и цифр.    raw_line = re.sub('[\';:.,<>#*"\-=/?!\[\]()_|\\\\+%]', ' ', raw_line)    raw_line = re.sub('\\b\\d+\\b', ' ', raw_line)    # 2. Конвертация в lowercase и разбиение по словам.    words = raw_line.lower().split()    # 3. Удаление stopwords + стэмминг.    meaningful_words = [stemmer.stem(w) for w in words if w not in russian_stops]    # 4. Обратное слияние в строку.    return " ".join(meaningful_words)

В ней применены стразу насколько подходов, а именно:


  • удаление незначащих символов, знаков пунктуации и прочего мусора. От чисел в чистом виде так же было решено отказаться. Числа, примыкающие к словам, было решено оставить ввиду специфики предметной области. Например, упоминание кодовых названий MT103 или ED807 вполне важная фича в нашей предметной области.
  • конвертация текста в lowercase. Напрашивается само собой, однако сам по себе CountVectorizer этого не делает.
  • удаление стоп-слов (слов, которые не несут смысловой нагрузки, таких как междометия, союзы, частицы)
  • использование основ слов (stem) вместо самих слов. Позволяет сократить словарь возможных слов за счет удаления всего многообразия окончаний русского языка. Забегая вперед можно сказать, что стемминг так же повысил стабильность скора классификатора на кроссвалидации. В перспективе возможно использование лемматизатора.
    В дальнейшем алгоритм был обернут в класс SteamCleanTransformer, который можно использовать в пайплайнах scikit-learn.

Заголовок спойлера
class StemCleanTransformer(TransformerMixin):    def __init__(self, column_num=0):    self.stemmer = SnowballStemmer("russian")    self.russian_stops = set(stopwords.words("russian"))    self.column_num = column_num    def prepare_line(self, raw_line):    # 1. Удаление ненужных символов и цифр.    raw_line = re.sub('[\';:.,<>#*"\-=/?!\[\]()_|\\\\+%]', ' ', raw_line)    raw_line = re.sub('\\b\\d+\\b', ' ', raw_line)    # 2. Convert to lower case, split into individual words    words = raw_line.lower().split()    # 3. Удаление stopwords + стэмминг.    meaningful_words = [self.stemmer.stem(w) for w in words if w not in self.russian_stops]    # 4. Обратное слияние в строку.    return " ".join(meaningful_words)    def transform(self, X, y=None, **fit_params):    result = np.array(X, copy=True)    if len(result.shape) == 1:    for i, _ in enumerate(result):    result[i] = self.prepare_line(result[i])    else:    for row in result:    row[self.column_num] = self.prepare_line(row[self.column_num])    return result    def fit_transform(self, X, y=None, **fit_params):    self.fit(X, y, **fit_params)    return self.transform(X)    def fit(self, X, y=None, **fit_params):    return self

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


Далее была выполнена попытка перейти на метод опорных векторов (в scikit-learn это класс SVC), не давшая значительного прироста. Оборачиваясь назад, можно сказать, что SVC требовал более точной настройки гиперпараметров, которая на тот момент не могла быть проведена из-за незрелости проекта. К нему еще вернемся.


Следующим шагом было использование n-грамм при векторизации. В частности, было выбрано значение 2: если ранее каждое слово представляло собой отдельный токен в словаре, то теперь так же будут учитываться все пары соседних слов. Регулируется параметром ngram_range в CountVectorizer:


vectorizer = CountVectorizer(ngram_range=(1, 2), max_features=20000)

Возвращаясь к примеру с Chicago Bulls теперь в словаре будет и Chicago, и Bulls, и Chicago Bulls (как отдельный токен). Это подняло accuracy с 0.55 до 0.63. Весомый прирост для такой простой правки.


Последним значимым шагом по повышению качества оценки до начала фактической эксплуатации был отказ от CountVectorizer в пользу TfidfVectorizer. Это немного более продвинутый алгоритм векторизации, который учитывает количество вхождений того или иного слова в документе, а так же во всем обучающем наборе. Подробнее здесь. Он имеет совместимый с CountVectorizer интерфейс и так же поддерживает n-граммы. Ввиду этого переход на него абсолютно прозрачный:


vectorizer = TfidfVectorizer(ngram_range=(1, 2), sublinear_tf=True, max_features=20000) # про sublinear_tf см. документацию

Это позволило повысить метрику оценки качества с 0.63 до 0.68.


Еще одна неудачная попытка использовать pymystem3 (Яндекс) в качестве лемматизатора (преобразование слова в исходную форму; обычно работает немного лучше стемминга). По странной причине под Windows стемминг одной строки инпута занимает примерно одну секунду при сравнимом качестве. Под Linux таких проблем не было обнаружено.


Эксплуатация


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


  • В определенный момент возникла идея получать топ Z предсказаний. Причин было несколько разработчики уходят в отпуск, на больничный, уходят от дел, переключаются между системами и так далее. В таких случаях проще точным алгоритмом выбрать наиболее подходящего кандидата. При реализации вскрылся серьезный недостаток GaussianNB классификатор не использует сглаживание (Laplassian smoothing), ввиду чего отсутствие или наличие одного единственного слова в тексте может на 100% отвечать за выбор класса. Проблема была решена использованием MultinomialNB. Кстати топ 3 предсказание дает скор в районе 0.95.


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



with shelve.open('filename') as persistent_storage:    persistent_storage[str(rm_number)] = prediction

Идеально подходит для хранения пар ключ-значение в небольшом проекте.


  • Было решено вернуться к методу опорных векторов (SVC). При подключении каждой новой системы в качестве клиента сервиса определения ошибок выполнялись контрольные замеры метрик. Выяснилось, что в некоторых системах наивный Байесовский алгоритм стремится "нагрузить" некоторых особо часто встречающихся в тестовом наборе разработчиков, оставляя без внимания остальных. Особенно это хорошо было видно в confusion matrix. Практическим путем было выявлено, что метод опорных векторов при должном выборе параметра C дает гораздо более уместную оценку. Кстати, чтобы SVC предсказывал вероятности, требуется выставить специальный параметр probability в True


  • SVC, в отличие от MultinomialNB, требовал куда больше времени на обучение. Ввиду этого пришла идея сохранять обученные модели на диск. Для этого используется joblib из sklearn.externals (внутри работает через pickle). Примерно так выглядит код сохранения и чтения:



from sklearn.externals import joblib...joblib.dump(classifier, filename)...joblib.load(filename)

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


  • Для повышения удобства работы, стандартизации операций над обучающей и тестовой выборкой, а так же более прозрачного сохранения/загрузки в файл/из файла, был использован специальный класс Pipeline из библиотеки scikit-learn. Он позволяет инкапсулировать в себе несколько шагов обучения с произвольной вложенностью. Иногда строятся очень большие цепочки. Выдержка из реализации:

pipeline = make_pipeline(    StemCleanTransformer(),    TfidfVectorizer(ngram_range=(1, 2), sublinear_tf=True, max_features=20000),    SVC(kernel = 'linear', C=10, gamma='auto', probability=True))

Очистка, векторизация и предсказание инкапсулированы в одну цепочку.


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


  • Периодически производится обновление датасетов и переобучение. В общем и целом тренд на рост количества данных, однако бывают и сокращения за счет отключившихся разработчиков.


  • За несколько лет накоплено больше данных. На той системе, по которой мы изначально измеряли accuracy, значение естественным образом поднялось до 0.76.



Результаты


На момент публикации статьи система функционирует уже 4 года. За это время произошло порядка 5500 назначений на целевого разработчика, что по факту трансформируется в ~5.2 человеко-месяца сэкономленного времени (из расчета: 10 минут в среднем на переключение контекста/чтение описания/переназначение, рабочий день 8 часов, 22 рабочих дня в месяц).



Это серьезная экономия, ведь робот не страдает от переключения контекста и реагирует практически мгновенно. Так же стоит заметить, что примерно пятая часть из назначений робота была выполнена вне рабочего времени (в нашей организации это с 10 утра по 19 вечера). В классификации участвуют 5 внутренних систем с количеством разработчиков от 6 до 27, средний скор находится в районе 0.75 (максимум 0.9, минимум 0.68).



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


Сравнение с BERT


Недавно возникла идея проверить более продвинутые и тяжелые NLP-модели на этой задаче. В качестве подопытного был взят мультиязычный BERT из пакета transformers от HuggingFace. Скор по точности получился несколько хуже, чем достигнутый по итогам описанного в статье пути. Это ни в коем случае не означает, что BERT хуже. Скорее вывод в том, что частная задача вполне может решаться классическим методом на околопредельной точности.


Аутро


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


Использованные средства: Python 3, scikit-learn, nltk, SQL.

Подробнее..

Роль логического программирования, и стоит ли планировать его изучение на 2021-й

22.12.2020 00:22:49 | Автор: admin

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

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

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

Итак, пришло время второй ссылки. Что это будет? Статья на Хабре? Может быть статья на ином ресурсе? Прочитав пару первых абзацев на разных сайтах, вы, скорее всего, мало что поймете, так как, во-первых, материал обычно ориентирован на знающего читателя, во-вторых, хорошей и понятной информации по теме не так много в русскоязычном интернете, в-третьих, там почему-то постоянно речь идёт о некоем "прологе" (речь о языке программирования Prolog, разумеется), но сам язык, кажется, использует мало кто (почётное 35 место в рейтинге TIOBE). Однако наш герой не теряет мотивации и, спустя некоторое время, натыкается на эту самую статью, желая, все-таки понять:

  • Что такое логическое программирование

  • Какова история его создания и фундаментальные основы (серьезно, какому новичку это может быть интересно?)

  • Зачем и где его применяют

  • Стоит ли лично вам его изучать

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

Что такое логическое программирование

В школе на уроках информатики многие, если не все, слышали про Pascal (а кто-то даже писал на нем). Многие также могли слышать про Python, C/C++/C#, Java. Обычно программирование начинают изучать именно с языков из этого набора, поэтому все привыкли, что программа выглядит как-то так:

НачатьКоманда1Команда2Если УСЛОВИЕ  Команда3Иначе  Команда4Закончить

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

Давайте устроимся поудобнее рядом со своим компьютером и порассуждаем о жизни и смерти вместе с Аристотелем:

Всякий человек смертен.

Сократ - человек.

Следовательно, Сократ смертен.

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

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

% Всё, что после знака процента в строке - комментарииhuman('Plato'). % Платон - человекhuman('Socrates'). % Сократ - тоже человекhuman('Aristotle'). % Конечно, человеком был и Аристотель% ...и др. философыmortal(X) :- human(X). % Читаем так: "X смертен, если X - человек"

Что ж, давайте спросим у компьютера, смертен ли Сократ:

?- mortal('Socrates').true.

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

Так, теперь стоит успокоиться и разобраться, что же произошло. Вначале мы записали т. н. факты, то есть знания нашей программы о мире. В нашем случае ей известно лишь то, что Платон, Сократ и Аристотель - люди. Но что за странная запись "human('Socrates')." и почему это выглядит как функция? На самом деле "human" и "mortal" - предикаты от одной переменной. Да, тут уже пошли термины, но постараюсь объяснять их просто и понятно для тех, кто привык к императивному нормальному программированию.

Логическое программирование основано на логике предикатов. Предикатом называется (и здесь я позволю себе вольность) функция от нуля или более переменных, возвращающая значение логического типа (истина (true) или ложь (false)). Факт - это предикат с определенным набором параметров и заведомо известным значением.

% слова с большой буквы Prolog считает переменными, поэтому их следует заключать в кавычкиlike('Petya', 'Milk'). % программа знает, что Петя любит молокоgood('Kesha'). % Кеша хорошийnumber_of_sides('Triangle', 3). % у треугольника три вершиныlike('Misha', X). % не является фактом, так как значение переменной X не определено

Помимо фактов в логической программе присутствуют правила вывода. В данном случае это "mortal(X) :- human(X).". Набор правил вывода - это знания нашей программы о том, как выводить (искать/подбирать) решение. Правила записываются следующим образом:

a(X,Y,Z) :- b(X), c(Y,Z), d().

Предикат a от трех аргументов вернет истину, если удастся доказать истинность предикатов b, c и d. Читаются правила справа налево следующим образом: "Если b от X истинно И c от X, Y истинно И d истинно, то a от X, Y, Z истинно".

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

% Опишем набор фактов о том, кто что обычно ест на завтрак в семье Петиeat(father, cheese).eat(father, apple).eat(father, melon).eat(mother, meat).eat(sister, meat).eat('Petya', cheese).eat(brother, orange).

Теперь начнём делать запросы к программе (всё те же предикаты):

?- eat(father, apple). % ест ли отец яблокиtrue.?- eat(father, meat).  % ест ли отец мясоfalse.?- eat(sister, X). % что ест сестраX = meat.?- eat(X, cheese). % кто ест сырX = father ;X = 'Petya'.?- eat(X, Y). % кто что естX = father,Y = cheese ;X = father,Y = apple ;X = father,Y = melon ;X = mother,Y = meat ;X = sister,Y = meat ;X = 'Petya',Y = cheese ;X = brother,Y = orange.

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

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

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

d(X,X,1) :- !. % производная X по X = 1d(T,X,0) :- atomic(T). % производная константы = 0d(U+V,X,DU+DV) :- d(U,X,DU), d(V,X,DV). % производная суммы = сумме производныхd(U-V,X,DU-DV) :- d(U,X,DU), d(V,X,DV). d(-T,X,-R) :- d(T,X,R).d(C*U,X,C*W) :- atomic(C), C\=X, !, d(U,X,W). % производная константы, умноженной на выражение = константе на производную от выраженияd(U*V,X,Vd*U+Ud*V) :- d(U,X,Ud), d(V,X,Vd). % производная произведенияd(U/V,X,(Ud*V-Vd*U)/(V*V)) :- d(U,X,Ud), d(V,X,Vd). 

Запустим:

?- d((x-1)/(x+1),x,R).   R =  ((1-0)*(x+1)-(1+0)*(x-1))/((x+1)*(x+1)).

Пусть производная получилась довольно громоздкой, но мы и не ставили цель её упростить. Главное, из примера видно, что правила вывода производной на Prolog-е описываются очень близким образом к их математическому представлению. Чтобы сделать подобное на привычных языках программирования, пришлось бы вводить понятие дерева выражений, описывать каждое правило в виде функции и т. д. Тут же мы обошлись 8-ю строками. Но здесь важно остановиться и задуматься: компьютер не начал работать как-то иначе, он все ещё обрабатывает последовательности команд. Стало быть, те самые деревья, которые где-то все-таки должны быть зашиты, чтобы программа работала, действительно присутствуют, но в неявном виде. Деревья эти именуют "деревьями вывода", именно они позволяют подбирать нужные значения переменных, перебирая все возможные варианты их значений (существует механизм отсечения, который является надстройкой над логической основой языка, но не будем об этом).

Давайте на простом примере рассмотрим, что из себя представляет перебор, а затем то, чем он может быть опасен.

speciality(X,tech_translator) :- studied_languages(X), studied_technical(X). % X - технический переводчик, если изучал языки и технические предметыspeciality(X,programmer) :- studied(X,mathematics), studied(X, compscience). % X - программист, если изучал математику и компьютерные наукиspeciality(X,lit_translator) :- studied_languages(X), studied(X,literature). % X - литературный переводчик, если изучал языкиstudied_technical(X) :- studied(X,mathematics). % X изучал технические предметы, если изучал математикуstudied_technical(X) :- studied(X,compscience). % ...или компьютерные наукиstudied_languages(X) :- studied(X,english). % X изучал языки, если изучал английскийstudied_languages(X) :- studied(X,german). % ...или немецкийstudied(petya,mathematics). % Петя изучал математикуstudied(petya,compscience). % ...компьютерные наукиstudied(petya,english). % ...и английскиstudied(vasya,german). % Вася изучал немецкийstudied(vasya,literature). %...и литературу

Спросим, кто из ребят, известных компьютеру - технический переводчик:

?- speciality(X,tech_translator).X = petya ;X = petya ;false.

Агато есть Петя, Петя и ложь Что-то не так, подумает программист и попробует разобраться. На самом деле, перебирая все варианты значений X, Пролог пройдёт по такому дереву:

Дерево будет обходиться в глубину, то есть сначала рассматривается всё левое поддерево для каждой вершины, затем правое. Таким образом, Пролог дважды докажет, что Петя - технический переводчик, но больше решений не найдёт и вернёт false. Стало быть, половина дерева нам, в общем-то, была не нужна. В данном случае, перебор не выглядит особенно страшным, всего-то обработали лишнюю запись в базе. Чтобы показать "опасность" перебора, рассмотрим другой пример:

Представим, что перед нами в ячейках расположены три чёрных и три белых шара (как на картинке выше), которые требуется поменять местами. За один ход шар может или передвинуться в соседнюю пустую клетку, или в пустую клетку за соседним шаром ("перепрыгнуть" его). Решать будем поиском в ширину в пространстве состояний (состоянием будем считать расположение шаров в ячейках). Суть этого метода заключается в том, что мы ищем все пути длины 1, затем все их продления, затем продления продлений и т. д., пока не найдем целевую вершину (состояние). Почему поиск в ширину? Он первым делом выведет самый оптимальный путь, то есть самый короткий. Как может выглядеть код решения:

% Обозначения: w - белый шар, b - чёрный, e - пустая ячейкаis_ball(w). % w - шарis_ball(b). % b - шарnear([X,e|T],[e,X|T]) :- is_ball(X). % если фишка рядом с пустой ячейкой, то можно переместитьсяnear([e,X|T],[X,e|T]) :- is_ball(X).jump([X,Y,e|T],[e,Y,X|T]) :- is_ball(X), is_ball(Y). % если за соседним шаром есть пустая ячейка, то можно переместитьсяjump([e,Y,X|T],[X,Y,e|T]) :- is_ball(X), is_ball(Y).% предикат перемещения. Мы или рассматриваем первые элементы списка, или убираем первый элемент и повторяем операциюmove(L1,L2) :- near(L1,L2). move(L1,L2) :- jump(L1,L2).move([X|T1],[X|T2]) :- move(T1,T2).% предикат продления текущего пути. Если из состояния X можно перейти в состояние Y и% Y не содержится в текущем пути, то Y - удачное продлениеprolong([X|T],[Y,X|T]) :- move(X,Y), not(member(Y,[X|T])).% Первый аргумент - очередь путей, второй - целевое состояние, третий - результат, то есть найденный путьbdth([[X|T]|_],X,R) :- reverse([X|T], R). % Поиск в ширину нашел решение, если первый элемент пути совпадает с целью (путь наращивается с начала, так что перевернем результат)bdth([P|QI],Y,R) :- bagof(Z,prolong(P,Z),T), append(QI,T,QO), !, bdth(QO,Y,R). % Ищем все возможные продления первого пути и кладём в очередь, рекурсивно запускаем поискbdth([_|T],Y,R) :- bdth(T,Y,R). % Если продлений на предыдущем шаге не нашлось, то есть bagof вернул false, убираем первый путь из очередиbsearch(X,Y,R) :- bdth([[X]],Y,R). % Удобная обёртка над предикатом bdth% Предикат, который решает нашу задачу и выводит результат и длину найденного пути на экранsolve :- bsearch([w,w,w,e,b,b,b],[b,b,b,e,w,w,w],P), write(P), nl, length(P, Len), write(Len), nl.

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

Со стороны улучшения алгоритма можно предложить использовать поиск в глубину. Но как же, он ведь не даст оптимального результата? Сделаем просто: ограничим глубину поиска. Так мы точно не забьём стек и, возможно, получим ответ. Поступим так: проверим, есть ли пути длины 1, затем длины 2, затем длины 4 и т. д. Получим так называемый поиск с итерационным заглублением:

% Первый аргумент - текущий путь, второй - целевое состояние, третий - результат, то есть найденный путьdpth_id([X|T],X,R,0) :- reverse([X|T], R). % Успешное окончание поискаdpth_id(P,Y,R,N) :- N > 0, prolong(P,P1), N1 is N - 1, dpth_id(P1,Y,R,N1). % Если счётчик >0, то уменьшаем его и продолжаем поиск рекурсивноgenerator(1). % Изначально предикат вернет 1generator(N) :- generator(M), N is M + 1. % Рекурсивно получаем 2, 3, 4 и т. д.isearch(X,Y,R) :- generator(D), dpth_id([X],Y,R,D). % Удобная обертка, которая будет вызывать поиск от каждого натурального значения глубины.

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

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

near([w,e|T],[e,w|T]).near([e,b|T],[b,e|T]).jump([w,X,e|T],[e,X,w|T]) :- is_ball(X).jump([e,X,b|T],[b,X,e|T]) :- is_ball(X).

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

Зачем и где применяют логическое программирование

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

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

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

  • Мета-программирование: С помощью того же Пролога можно описать свой собственный язык, соответственно, со своими правилами. Это достаточно важный момент для многих проектов, где сталкиваются специалисты разных сфер. Например, стоит задача анализа неких химических данных. В команде работают химик и программист. Получается, программист должен постоянно советоваться с химиком о том, что и как ему делать, ведь химическим формулам то в IT не учат. А разговаривать - это, в общем-то, не особенно приятно, особенно если кто-то умнее тебя. И тут программисту было бы очень удобно написать простой язык, на котором химик сможет сам записать свои формулы, не трогая разработчика. Вот тут и пригодится логическое программирование.

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

Стоит ли планировать его изучение на 2021-й

Тут оставлю своё субъективное мнение, разделённое на две части:

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

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

И здесь остаётся лишь пожелать продуктивного 2021-го года!

Подробнее..

Культурные рекомендации опыт московского хакатона

23.02.2021 00:18:51 | Автор: admin

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

Принцип работы нашей системы - и пример кусочка её выдачиПринцип работы нашей системы - и пример кусочка её выдачи

О хакатоне

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

Наша команда

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

Наше решение

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

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

Для мероприятий я решил делать content-based рекомендации, потому что о них нам известно немного: название, описание, место и время. Из названия и описания я тоже слепил векторные представления, усреднив fasttext-эмбеддинги всех их слов. Справка: fasttext - это векторная модель слов, как word2vec, только ещё способная угадывать смысл незнакомых слов по их написанию, и за счёт этого сжимаемая до весьма малых размеров без существенной потери в точности. Для книг я скачал в интернете базу аннотаций, и слепил из них точно такие же векторные представления. Теперь книги и мероприятия представлены в одном и том же пространстве, и их можно сравнивать друг с другом. Например, если юзер любит исторические книги, их векторы окажутся геометрически близкими к вектору фестиваля реконструкторов, и юзеру можно предложить посетить этот фестиваль. И, конечно, при ранжировании мероприятий надо учитывать географию, отдавая предпочтения тем, что проходят рядом с домом пользователя. К кружкам это тоже относится, а ещё для кружков важен возраст юзера. Поэтому и адрес, и возраст перед началом работы мы спрашиваем.

Чем всё закончилось

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

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

Заключение

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

Будем хакатонить дальше!

Подробнее..

Как предсказать гипероним слова (и зачем). Моё участие в соревновании по пополнению таксономии

21.06.2020 18:22:59 | Автор: admin

Как может машина понимать смысл слов и понятий, и вообще, что значит понимать? Понимаете ли вы, например, что такое спаржа? Если вы скажете мне, что спаржа это (1) травянистое растение, (2) съедобный овощ, и (3) сельскохозяйственная культура, то, наверное, я останусь убеждён, что вы действительно знакомы со спаржей. Лингвисты называют такие более общие понятия гиперонимами, и они довольно полезны для ИИ. Например, зная, что я не люблю овощи, робот-официант не стал бы предлагать мне блюда из спаржи. Но чтобы использовать подобные знания, надо сначала откуда-то их добыть.


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



Про гиперонимы и таксономию


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



Пример подграфа таксономии RuWordNet, связанного со спаржей


Готовые пары гипоним-гипероним можно найти в специальных словарях, тезаурусах, куда включены целые графы гиперонимов, таксономии. Это, например, wiktionary (есть питонячья обёртка), или WordNet и RuWordNet. Обычно единицей такого словаря является синсет множество слов, обладающих примерно одинаковым смыслом. Многозначные слова входят в несколько синсетов сразу. Отношения гипоним-гипероним (и некоторые другие, например часть-целое или тема-объект темы) устанавливаются именно между синсетами.


Нафига?


У читателя может возникнуть закономерный вопрос: а зачем вообще в 2к20 нужны какие-то тезаурусы? Есть же машиннообученные word2vec, fastText, и даже простите BERT, почему бы не использовать их напрямую для всех задач? На самом деле, конечно, делать так можно, и все так обычно и делают. Но есть несколько "но":


  1. Модели, основанные на статистике со-встречаемости слов, смешивают в одну кучу разные виды связей между словами: схожесть написания, общую тему, отношения "общее/частное", "часть/целое", синонимы, антонимы Если хочется работать с одним конкретным видом связанности слов, нужен дополнительный сигнал, и тезаурус проверенный источник такого сигнала.
  2. Чисто статистические модели часто выдают непрозрачные результаты, а в некоторых задачах важна полная интерпретируемость. Опять же, проверенность словаря решает.
  3. Как было видно из того же примера с кудахтаньем, статистические модели выдают довольно шумные результаты, и если есть способ дополнительно отфильтровать этот шум, то почему бы им не воспользоваться.

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


for sense in wn.get_senses('замок'):    print(sense.synset)# Synset(id="126228-N", title="СРЕДНЕВЕКОВЙ ЗАМОК")# Synset(id="114707-N", title="ЗАМОК ДЛЯ ЗАПИРАНИЯ")

Для каждого синсета можно глядеть на гиперонимы...


wn.get_senses('спаржа')[0].synset.hypernyms# [Synset(id="348-N", title="ОВОЩИ"),#  Synset(id="4789-N", title="ТРАВЯНИСТОЕ РАСТЕНИЕ"),#  Synset(id="6878-N", title="ОВОЩНАЯ КУЛЬТУРА")]

или, наоборот, на гипонимы


wn.get_senses('спаржа')[0].synset.hypernyms[0].hyponyms# [Synset(id="107993-N", title="АРТИШОК"),# Synset(id="108482-N", title="СПАРЖА"),# Synset(id="118660-N", title="ЗЕЛЕНЙ ГОРОШЕК"),# ...

Одно из забавных применений таксономии измерять непохожесть между понятиями как сумму расстояний до ближайшего общего гиперонима. Возьмём, например, такую детскую задачку: нужно исключить одно из слов ДИВАН, ШКАФ, ЛАМПА, СТОЛ. Нарисуем подграф их гиперонимов (хоть он и странный):



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


        ДИВАН   ШКАФ    ЛАМПА   СТОЛДИВАН   0       3       10      3       ШКАФ    3       0       5       2       ЛАМПА   10      5       0       7       СТОЛ    3       2       7       0

Задача


Словари типа RuWordNet очень качественные, потому что собраны лингвистами вручную. Но поэтому же наполнение таких словарей не очень высокое. Хотелось бы научиться добавлять новые понятия в таксономии автоматически, или хотя бы полуавтоматически (машина предлагает варианты, лингвист их утверждает). Для этого компьютерные лингвисты из Сколтеха и Вышки организовали соревнование (раз два три), приуроченное к конференции Диалог, и уже даже написали про это статью. Идея соревнования: для слова, пока не включённого в таксономию RuWordNet, надо найти его гиперонимы из этой таксономии, предложив 10 вариантов. Засчитывались как прямые гиперонимы слова-запроса, так их их гиперонимы (т.е. гиперонимы второго порядка). Искались гиперонимы и оценивались результаты раздельно для существительных и для глаголов. Подробнее на данные, скрипты для оценки и бейзлайны можно посмотреть в репозитории соревнования.


Наш алгоритм


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


  1. Используя модель word2vec, вычислить эмбеддинги (представления в виде числовых векторов) для всех понятий в таксономии;
  2. Найти 100 ближайших соседей по сходству этих эмбеддингов с эмбеддингом слова-запроса;
  3. Заставить каждого из этих соседей "голосовать" за свои гиперонимы 1 и 2 порядка;
  4. Отранжировать гиперонимы-кандидаты по взвешенной сумме набранных голосов и отобрать первые 10.

Почему такое решение вообще может работать? Оказалось, что у 90% новых существительных и 99% новых глаголов есть "сёстры" в имеющейся таксономии, т.е. понятия с хотя бы одним общим гиперонимом. Эти "сёстры" по смыслу тесно связаны с запросом, а потому, согласно дистрибутивной гипотезе, часто встречаются рядом с теми же словами, рядом с которыми встречается и запрос. Значит, если сопоставить словам векторы из модели, обученной угадывать слово по контексту (например, word2vec, FastText, ELMO или BERT), то среди ближайших соседей слова по таким представлениям будет много "сестёр", и в качестве ответа можно использовать их гиперонимы.


Ещё несколько деталей алгоритма:


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

Более подробное описание и обсуждение моего алгоритма и решений других участников можно найти в сборнике конференции "Диалог".


Упрощённый код


Здесь разобран упрощённый питонячий код моего алгоритма. Полную версию модели, которую я засабмитил на лидерборд, можно посмотреть на гитхабе, но код там довольно грязный. Более простую и опрятную версию можно запустить, склонировав себе репозиторий python-ruwordnet.


Для работы с тезаурусом я пользуюсь самописной библиотекой ruwordnet, которая скоро появится на PyPI.


from ruwordnet import RuWordNetwn = RuWordNet()wn.load_from_xml(root='data')

Для получения векторов слов я использовал модель word2vec с сайта RusVectores. В качестве более легковесной альтернативы можно использовать сжатые вектора fastText. Вектор текста нормализованная сумма векторов всех слов длиной хотя бы в 3 символа.


import numpy as npimport compress_fasttextft = compress_fasttext.models.CompressedFastTextKeyedVectors.load(    'https://github.com/avidale/compress-fasttext/releases/download/v0.0.1/ft_freqprune_100K_20K_pq_100.bin')def vectorize(text):    vec = np.sum([ft[word] for word in text.lower().split() if len(word) >= 3], axis=0)    vec /= sum(vec**2) ** 0.5     return vec

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


from sklearn.neighbors import KDTreewords, vectors, synset_ids = [], [], []for synset in wn.synsets:    if synset.part_of_speech != 'V':        continue    for sense in synset.sense:        words.append(sense.name)        vectors.append(vectorize(sense.name))        synset_ids.append(synset.id)vectors = np.stack(vectors)tree = KDTree(vectors)

Веса для соседей, найденных в дереве, будем вычислять в зависимости от расстояния до соседа, по вот такой формуле (я придумал её довольно стихийно, а параметры подобрал по сетке):


def distance2vote(d, a=3, b=5):    sim = np.maximum(0, 1 - d**2/2)    return np.exp(-d**a) * sim **b


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


votes = Counter()dists, ids = tree.query(vectorize('кудахтать').reshape(1, -1), k=100)for idx, distance in zip(ids[0], dists[0]):    for hyper in wn[synset_ids[idx]].hypernyms:        votes[hyper.id] += distance2vote(distance)    print(words[idx], [t.title for t in wn[synset_ids[idx]].hypernyms])# БАРАХТАТЬСЯ ['ДВИЖЕНИЕ, ПЕРЕМЕЩЕНИЕ', 'ПЛЕСКАТЬСЯ В ВОДЕ']# ГОГОТАТЬ ['СМЕЯТЬСЯ (ИЗДАВАТЬ СМЕХ)', 'РАЗРАЗИТЬСЯ (БУРНО ВРАЗИТЬ)']# ГУКАТЬ ['ПРОИЗНЕСТИ, ВГОВОРИТЬ, ПРОГОВОРИТЬ']# ...

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


for sid, score in votes.most_common(10):    print(score,  wn[sid].title)# 0.6925543543920146 ИЗДАТЬ ЗВУК# 0.4306341411813687 ПРОИЗНЕСТИ, ВГОВОРИТЬ, ПРОГОВОРИТЬ# 0.2957854226709537 ДВИЖЕНИЕ, ПЕРЕМЕЩЕНИЕ# ...

Результаты


При оценке на тестовой выборке около 40% предложенных моделью кандидатов оказались настоящими гиперонимами слов-запросов. Это на 15% хуже, чем наилучшее решение для существительных (оно использовало кучу дополнительных источников данных wordnet, викисловарь, результаты поиска в Яндексе и Гугле). Однако моё решение оказалось наилучшим для глаголов. Скорее всего, это означает, что искать гиперонимы для глаголов в целом непростая задачка, и никто ещё не придумал, как решать её достаточно круто. Ну, что ж \_()_/.


Какого рода косяки делает моя модель в тех 60% случаев, когда она не права? Есть несколько важных видов ошибок:


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

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


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


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

Подробнее..

Как объединить 10 BERT-ов для задач общего понимания текста?

22.07.2020 16:08:14 | Автор: admin

Всем привет! В этом посте я расскажу о проекте, который выполнил совместно с командой Google Brain во время исследовательской стажировки в Цюрихе. Мы работали над моделью обработки естественного языка, которая решает задачи на общее понимание текста (задачи из набора GLUE: General Language Understanding Evaluation).


BERT-подобные модели мы комбинировали с помощью маршрутизирующих сетей и добились того, что при увеличении мощности скорость вывода почти не изменилась. Финальная модель объединяет 10 BERTlarge моделей и имеет более 3,4 миллиарда параметров. Подробности под катом!



Меня зовут Никита Сазанович, я учусь на 2-м курсе магистратуры Программирование и анализ данных в Питерской Вышке. В январе я поехал на 17-недельную стажировку в Google Brain (правда, физически находился в Цюрихе только 11 из них: из-за карантина последние 6 недель пришлось работать удаленно). Мой проект был связан с обработкой естестественного языка с использованием маршрутизирующих сетей а это новое понятие в машинном обучении, с которым мы далее и разберемся.


Начнем издалека


Задачи обработки естественного языка (Natural Language Processing, NLP) включают в себя машинный перевод, распознавание текста, анализ тональности текстов и др. В этой статье мы поговорим про задачи из набора GLUE. Они были разработаны экспертами машинного обучения, чтобы оценивать, насколько модель способна к общему пониманию текста (откуда и название набора: General Language Understanding Evaluation).


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


Рассмотрим одну из них: Quora Question Pairs, или QQP. Как ясно из названия, нужно будет что-то сказать про пару вопросов из сайта Quora. Вопросы необходимо сравнить и сообщить, семантически эквивалентны ли они / является ли один дубликатом другого. Сразу приведу два примера:


1) Вопросы "What are natural numbers?" и "What is a least natural number?" не являются дубликатами.


2) А вопросы "How do you start a bakery?" и "How can one start a bakery business?" семантически эквивалентны.


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


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


BERT: Теория


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


1) Входное предложение: "Jet makers feud over seat width with big orders at stake".


2) Разбиение на токены (где символ _ приписывается к токенам, с которых слова начинаются): "_J et _makers _fe ud _over _seat _width _with _big _orders _at _stake". Здесь можно видеть, что слов "Jet" и "feud" не оказалось целиком в словаре, поэтому они были разбиты на части, присутствующие в словаре.


В словаре WordPiece содержится несколько десятков тысяч токенов. Большинство слов, которые встречаются наиболее часто, он содержит целиком.


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


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



Если мы посмотрим на слово making в предложении It is in this spirit that a majority of American governments have passed new laws since 2009 making the registration or voting process more difficult, мы можем обратить внимание также на слова more difficult, laws, 2009: они помогают понять смысл. В предложении говорится о том, как с 2009 года правительства США принимали законы, которые делают процесс голосования сложнее. В таком контексте представление слова making в механизме внимания формируется в основном из представлений предыдущего слоя для слов making, more и difficult. Математически механизм внимания представляет собой блок модели, который отображает последовательность векторов токенов. При этом блок можно устроить таким образом, чтобы выходное представление каждого токена формировалось как взвешенная сумма всех представлений токенов с предыдущего слоя. В каком-то смысле у каждого слова есть ограниченное внимание, которое оно разделяет между всеми словами предыдущего слоя.


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


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


Вернемся к вопросу представления текста. Допустим, после каких-то преобразований мы получили последовательность векторов $z_1$, $z_2$, ..., $z_{n_i}$ для токенов текста $x_1$, $x_2$, ..., $x_{n_i}$. Как теперь проводить классификацию или регрессию на всем тексте? В BERT модели предлагают элегантное решение: к каждой последовательности токенов добавить токен $x_0$ и получить по нему представление $z_0$ описанным выше способом. Благодаря специальному устройству функции обучения оно будет представлять информацию, которая содержится во всем тексте. Для более подробного устройства можно обратиться к самой статье о BERT.


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


Маршрутизирующие сети


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


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


Формулировать и обучать процесс маршрутизации можно по-разному. К примеру, в статье по ссылке для каждой возможности маршрутизации задачи через модуль модели использовалось распределение Бернулли, которое обучалось с помощью Gumbel Softmax. Ниже вы можете видеть пример такой модели с двумя задачами. Цветами отмечено, какое подмножество модулей используется для каждой из них: красным обозначены модули, которые вычисляются на маршруте для первой задачи, синим для второй, а фиолетовым для обеих. Если в одном слое таких модулей для одной задачи несколько, нужна агрегация результатов, например, усреднение. Наличие общих модулей и позволяет задачам совместно обучать модули, которые выгодно разделять.



Как видно на картинке, модули этой сети состоят из сверточных слоев (Conv 3x3, Conv 5x5, Conv 7x7). Значит, задачи относились к области компьютерного зрения. А что если в качестве таких модулей мы будем использовать BERT-подобные модели и попытаемся искать маршруты для задач из набора GLUE? Это и стало идеей для моего проекта.


Маршрутизация и процесс тренировки


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



Представления задач моделируются как вектора размерности M (в экспериментах M было равно 10):


$v_t\ для\ t = [1, .., T],\ где\ T - число\ задач \\ v_t\in R^M,\ где\ M - размерность\ представлений$


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


$G(v) = Softmax(KeepTopK(H(v), k)) \\ KeepTopK(h, k)_i = \begin{cases} h_i & \text{если $h_i$ в k наибольших элементах в h} \\ 0 & \text{иначе} \end{cases} \\ G(v) \in R^N, где\ N - число\ экспертов$


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


$x \in X_t\ для\ t = [1, .., T] \\ f(x, t) = \sum\limits_{i=1}^{M} G(v_t)_i E_i(x),\ где\ E_i - функция\ i{\text -}ого\ эксперта$


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


Далее под спойлером для любителей описаны технические подробности настройки этой модели. Для полноты картины расскажу про нее в нескольких словах. Лучший результат достигается, если использовать 10 BERT large экспертов, а не трех или одного (из-за затрат времени и ресурсов мы провели эксперименты только для 1, 3 и 10 экспертов). Оптимальное количество путей два. Оптимизировать выгодно стохастическим градиентным спуском с импульсом. При такой настройке модель имеет 3,4 миллиарда параметров и обучать ее пришлось на 24 тензорных процессорах Google.


Детали настройки модели

Процесс тренировки проходит параллельно на батчах из всех задач GLUE и требует какой-то агрегации между задачами. Один из вариантов линейная комбинация потерь $L_t$ каждой из задач:

$L_{total} = \sum\limits_{t=1}^{T} C(t) L_t$


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


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

$L_{total} = \sum\limits_{t=1}^{T} \frac{1}{\mathcal{N}} |D_t|^p L_t$


то можно было достичь GLUE оценки в 80.06 при значении p=0.3. Я использую такую агрегацию функций потерь каждой из задач в дальнейших экспериментах по настройке модели.

Важным элементов при масштабировании модели является метод оптимизации, так как он увеличивает размер необходимой памяти, тем самым уменьшая мощность модели при фиксированном бюджете. Я проверил 6 оптимизаторов: Adam, Adam с weight decay (угасание весов к нулю), LAMB, LAMB с weight decay, стохастический градиентный спуск (SGD) и стохастический градиентный спуск с импульсом (SGDM). SGD практически не расходует дополнительной памяти, SGDM запоминает импульсы для каждой переменной, поэтому расходует x2 памяти, а остальные рассмотренные оптимизаторы расходуют x3 памяти. Результаты оптимизации маршрутизируемого BERT-а с одним BERTlarge экспертом можно видеть в таблице. SGDM достигает лучшего результата в исследуемом пространстве, при этом используя лишь в два раза больше памяти. Я использую его в финальной модели.



Рассматривая зависимость качества от числа BERTlarge экспертов и путей, я заметил, что качество улучшается при увеличении числа экспертов, а при увеличении количества путей не всегда. Лучший результат дало использование 10 экспертов и 2 путей.



В такой модели 10 x 340 миллионов = 3,4 миллиарда параметров, а ее обучение происходило на 24 TPUv2 (тензорных процессорах Google) с параллелизмом по данных равным 4 и параллелизмом модели равным 6. Параллелизм модели равен 6, так как 5 процессоров держат по 2 BERTlarge модели, а еще один отвечает за все сети задач после агрегации.


Анализ результатов


Для сравнения результатов в наборе GLUE был предложен способ комплексной оценки, который совмещает результаты на каждой из задач набора. Эта оценка лежит в диапазоне от 0 (все неверно) до 100 (идеальные ответы). Я сравнил маршрутизируемый BERT с опубликованными результатами мультизадачного обучения BAM! (оно основано на дистилляции) и настройкой одного BERTа под каждую задачу. Маршрутизируемый BERT достиг лучшей оценки общего понимания языка.


Интересны также различия между результатами на задачах. Например, выгоду при тренировке с маршрутизируемыми сетями получает задача RTE, которая имеет наименьший тренировочный набор (2,5 тысячи примеров). Сравните ее оценки в 1-й и 3-й строках. Эта выгода на задаче достигается засчет использования дополнительных знаний из других задач, тренировочный набор которых больше по размеру.



Еще один результат этой модели возможность исследовать отношения между задачами. Ниже представлен результат сокращения размерности векторов для представлений задач. Вектора, которые изначально находятся в 10-мерном пространстве (M=10), сокращены до векторов размера 2, т. е. точек. Это сделано при помощи метода t-SNE, который очень популярен для таких визуализаций и про который можно почитать в другом блоге на Хабре. Здесь каждая из задач располагается на плоскости, а расстояние между ними позволяет судить о том, насколько они близки с точки зрения маршрутизатора. Как и ожидалось, mnli-m и mnli-mm задачи с одним и тем же тренировочным набором располагаются близко. Так же близко расположены sst-2 и qqp, cola и rte пары, сходство которых отмечали и другие исследователи.



Итоги


В результате мы разобрались, как можно использовать маршрутизирующие сети для обучения представлений языка. С их помощью мы улучшили результат работы исходной модели с точки зрения оценки GLUE на 1,75 пункта и в качестве бонуса смогли визуализировать отношения между задачами набора. Важное свойство такого подхода сохранение скорости вывода в конечной модели. Было бы интересно продолжить работу над проектом, использовав разнородных экспертов с одинаковым интерфейсом (таких как ALBERT и RoBERTa), а также добавить к набору задачи предобучения представлений (таких как MLM BERT-а) для тренировки сети с нуля без использования предобученных моделей.


Код этой модели остался в Google, но могу сказать, что написан он был на любимом в компании TensorFlow.

Подробнее..

Sibur Challenge 2020 или как мы фичи придумывали

21.12.2020 14:15:23 | Автор: admin

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

Кто мы такие?

Мы студенты которые очень сильно увлеклись темой DS и ML. Впервые мы узнали об этой сфере на конференции AI Journey, проходившей в нашем вузе. С того момента прошли не один, и не два, и не три курса (от Омского Государственного Технического университета до Andrew NG) и теперь постоянно участвуем в хакатонах и соревнованиях(в некоторых даже заняли призовые места), параллельно ищем стажировку.

О задаче

Мы взялись за вторую задачу соревнования - "сопоставление названий".

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

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

name_1

name_2

is_duplicate

Japan Synthetic Rubber Co

Jsr Bst Elastomer

1

JSR Corporation

BST ELASTOMERS CO.

0

Примерно так выглядел датасет.

Предобработка данных

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

from unidecode import unidecodeimport redef preprocess(text: str):    text = unidecode(text)    text = text.lower()    text = re.sub(r'[\.,]+', '', text)    text = re.sub(r"\(.*\)", ' ', text)    text = re.sub(r"[^\w\s]", ' ', text)    text = re.sub(r'\b\w\b', ' ', text)    text = ' '.join(text.split())    return text

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

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

Поиск фичей

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

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

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

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

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

Остальные признаки, которые не оказались столь удачными, приведены в списке ниже:

  • количество совпадающих первых гласных, согласных

  • количество совпадающих первых буквы сокращения (аббревиатуры)

  • стандартные метрики: левенштейн, яро винклер, фузи вузи

  • tfidf - при подсчёте жаккара (или косинусного расстояния с нграммами)

  • сортировать уникальные слова в имени и считать наши метрики

  • процент пересекающихся ngram

  • количество совпадающих первых букв каждого слова (брать максимум)

  • количество букв первого, количество букв второго

  • количество слов первого, количество второго

Модель и блендинг решений

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

Далее мы понимали, что было бы неплохо обменяться с кем-то идеями и объединить решения. Мы познакомились с двумя другими участниками(Алехандро, Дмитрий, привет!), и заблендили наши решения, получив скор 0.69 на лидерборде. Так получилось, что все три наших решения имели разные подходы к задаче, поэтому их объединение и улучшило результат.

Выводы

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

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

Что можно было доработать?

Можно было учитывать семантику слов или выдавать словам веса: если слово в двух названиях совпало и оно полезно (относится к названию компании) - имеет вес, мы автоматически считаем что оно настолько же вредно в "разнице" слов; использование как можно больше внешних данных с названием компаний и т. д. Также не забывать анализировать наблюдения, на которых ошибается модель (False Positive, False Negative), и на основе этого конструировать новые признаки.

P.S.

Весь код лежит здесь

Если хотите связаться с нами : matnik2001@gmail.com , domonion@list.ru

Подробнее..

Научно-исследовательские инициативы JetBrains

03.03.2021 14:04:54 | Автор: admin
Develop with pleasure, The drive to develop об этом вы наверняка от нас слышали. Но наши интересы далеко не ограничиваются разработкой и созданием мощных инструментов для повышения продуктивности. Мы верим, что можем многое изменить и сделать мир лучше. Один из верных способов проведение исследований в области передовых технологий и образования. Совместно с ведущими научными учреждениями мира мы занимается прикладными исследованиями, способными влиять на жизни людей и двигать нас всех вперед.

Наши научные исследования объединены в рамках направления JetBrains Research.

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

Наука сегодня для технологий будущего



JetBrains Research объединяет более 150 исследователей, участвующих в проектах более 19 лабораторий и групп. Лаборатории и группы ведут работу в самых разных направлениях от физики элементарных частиц до разработки ПО.


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



Исследовательские группы



BioLabs


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


Задача BioLabs раскрыть механизмы эпигенетической регуляции у людей и животных и понять, какое значение эти механизмы играют в процессах дифференцировки и старения клеток. Самым крупным является проект старения, реализуемый BioLabs совместно с Университетом Вашингтона в Сент-Луисе. Другие исследовательские проекты посвящены различным темам, включая новые алгоритмы анализа данных, эффективные инструменты обработки данных для секвенирования нового поколения (Next Generation Sequencing), масштабируемые конвейеры данных, подходы к визуализации и мета-анализу существующих баз данных с информацией о механизмах эпигенетической регуляции. BioLabs также отвечает за PubTrends новый сервис для анализа научных публикаций, позволяющий быстрее анализировать тренды и находить значимые работы. Такой сервис необходим, поскольку число работ, публикуемых каждый год, неуклонно растет, и уследить за всеми публикациями по выбранной теме практически невозможно.


Вернуться к списку исследовательских групп


Группа биоинформатики


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

Группа биоинформатики занимается разработкой эффективных вычислительных методов для решения важных проблем в области биологии и медицины. Группа базируется на кафедре компьютерных технологий Университета ИТМО. Группа активно взаимодействует с лабораторией Максима Артемова (Университет Вашингтона в Сент-Луисе). Проекты лаборатории охватывают широкий спектр тем от анализа данных метагеномного секвенирования до анализа экспрессии генов и метаболомики. Фундаментальные знания в области алгоритмов и компьютерных наук позволяют группе заниматься решением задач биологии, сводя их к известным вычислительным задачам и создавая инструменты визуализации и анализа данных для биологов.


Вернуться к списку исследовательских групп


Лаборатория нейробиологии и физиологии развития


Нейробиология и физиология развития прошли долгий путь и накопили фундаментальную базу исследований. И тем не менее многое в этой науке по-прежнему остается неизведанным. А ведь эти науки таят в себе огромный потенциал к пониманию человеческого мозга.
Задача лаборатории нейробиологии и физиологии развития разработать вычислительный фреймворк для создания динамических пространственных моделей структуры нервных тканей и динамики базовых стимулов. Проект Biological Cellular Neural Network Modeling (BCNNM) использует последовательности биохимических реакций для запуска сложных моделей нейронных сетей при формировании исходных стволовых клеток. Фреймворк можно использовать для in silico репликации экспериментов, проведенных in vitro, чтобы получать измерения с ключевых компонентов, а также выполнять предварительную вычислительную проверку новых гипотез.


Вернуться к списку исследовательских групп



Лаборатория прикладного машинного обучения и глубокого обучения
и
Лаборатория агентных систем и обучения с подкреплением


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


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


Вернуться к списку исследовательских групп


Исследовательская группа Paper-Analyzer


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


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


Вернуться к списку исследовательских групп


Лаборатория криптографии


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


Лаборатория криптографии занимается исследованиями современных задач в области криптографии и информационной безопасности. Она сотрудничает с COSIC исследовательской группой компьютерной безопасности и промышленной криптографии в Левене (Бельгия), Selmer Center в Университете Бергена (Норвегия) и INRIA (Франция). Исследования ведутся по различным направлениям: криптографические логические функции, симметричные шифры, легковесная криптография, технология блокчейна, квантовая криптография и информационная безопасность. Помимо публикации монографий и статей в ведущих журналах о криптографии, сотрудники лаборатории преподают криптографию в Новосибирском государственном университете и организуют NSUCRYPTO Международную студенческую олимпиаду по криптографии.


Вернуться к списку исследовательских групп


Группа HoTT и зависимых типов


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


Исследовательская группа занимается созданием Arend зависимо-типизированного языка и инструмента доказательства теорем, основанного на гомотопической теории типов. HTT является более продвинутым фреймворком, чем те, на которых основаны инструменты вроде Agda и Coq. Конечная цель создать онлайн-помощник для доказательства теорем, основанный на современной теории типов, который бы позволил формализовать определенные разделы математики.


Вернуться к списку исследовательских групп


Лаборатория методов ядерно-физических экспериментов


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


Лаборатория методов ядерно-физических экспериментов базируется в МФТИ. Основной интерес лаборатории методологии и ПО для решения задач в области физики элементарных частиц. На данный момент команда программистов лаборатории занимается разработкой нового поколения инструментов для получения данных (медленное управление, обработка сигналов) и анализа данных. Исследования лаборатории охватывают три сферы: физика элементарных частиц без ускорителя (эксперименты GERDA, Троицк ню-масс, KATRIN и IAXO), численное моделирование в физике элементарных частиц (эксперименты с ускорителем и без, атмосферное электричество, физика рентгеновского излучения) и разработка программного обеспечения для экспериментальной физики (системы получения и анализа данных, проекты по разработке инфраструктур, научные библиотеки для языка Kotlin). Огромное внимание уделяется и обучению: лаборатория старается дать молодым студентам возможность получить реальный опыт в физике и разработке.


Лаборатория методов ядерно-физических экспериментов


Вернуться к списку исследовательских групп


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


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


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


  1. Кто идет в STEM и программирование?
  2. Какие факторы (когнитивные возможности, история семьи и т. д.) приводят человека к лучшим результатам и уменьшают вероятность забросить учебу?
  3. Существуют ли характерные установки (мотивация, вовлеченность и т.д.), способные пересилить первоначальные данные?
  4. Какие методологии обучения приносят успех, а какие повышают вероятность неудачи?

Вернуться к списку исследовательских групп


Лаборатория алгоритмов мобильных роботов


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


Лаборатория алгоритмов мобильных роботов объединяет исследования в области разработки эффективных алгоритмов для мобильных роботов. В лаборатории имеется единственный в России экземпляр Duckietown платформы и среды, позволяющих разрабатывать алгоритмы для мобильных роботов. В центре внимания лаборатории задача одновременной локализации и построения карты (SLAM). SLAM подразумевает составление и последующее поддержание карты неизвестной среды; при этом благодаря анализу данных с различных датчиков можно отслеживать местонахождение агента в среде. Сложность задачи SLAM связана с шумами, свойственными физическим датчикам, а также с необходимостью следить за изменениями в динамической среде. Кроме того, многие алгоритмы SLAM рассчитаны на недорогое оборудование, которое задает строгие требования к производительности. В 2019 году лаборатория роботов участвовала в третьих AI Driving Olympics соревнованиях роботов, управляющих беспилотным транспортом. Эти престижные соревнования считаются местом силы для развития знаний в сфере беспилотных автомобилей. Наша лаборатория заняла первое место во всех трех состязаниях. Примечательно, что это был первый прецедент победы алгоритма глубокого обучения на этих соревнованиях.


Исследователи лаборатории преподают множество курсов по дисциплинам STEM в университетах. Также они предлагают курсы по мобильной разработке для школьников и принимают студентов из MIT, приезжающих по программе MISTI.


Лаборатория алгоритмов мобильных роботов


Вернуться к списку исследовательских групп


Проблемы оптимизации в программной инженерии


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


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


Вернуться к списку исследовательских групп


Группа параметризованных алгоритмов


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


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


Вернуться к списку исследовательских групп


Лаборатория параллельных вычислений


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


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


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


Вернуться к списку исследовательских групп


Лаборатория киберфизических систем


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


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


Вернуться к списку исследовательских групп


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


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


Группа методов машинного обучения в области программной инженерии занимается разработкой и тестированием методов улучшения инструментов и процессов разработки путем применения анализа данных (включая машинное обучение) к данным из программных репозиториев. Совместно с командами нескольких продуктов JetBrains группа занимается интеграцией в продукты современных методов на основе данных. В данный момент группа работает над десятком исследовательских проектов на различные темы от поддержки библиотек сбора данных до генерации кода из описаний на естественном языке. Недавние результаты работы группы включают новый подход к рекомендации рефакторингов Move method, исследование нарушений лицензий в заимствованном коде на GitHub, современный подход к присуждению авторства исходного кода и метод построения векторных представлений стиля кода без явных признаков.


Вернуться к списку исследовательских групп


Лаборатория языковых инструментов


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


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


Вернуться к списку исследовательских групп


Лаборатория верификации и анализа программ (VorPAL)


Одним из главных направлений работы над инструментами JetBrains является повышение продуктивности при их использовании. И тот уровень, которого наши инструменты достигли сегодня, во многом заслуга тех исследований, которыми занимаются наши лаборатории.


В лаборатории верификации и анализа программ студенты, аспиранты и исследователи занимаются разработкой программных технологий, основанных на формальных методах, таких как верификация, статический анализ и методы трансформации программ. Эти методы помогают повысить продуктивность разработчиков при использовании ими автономных инструментов, расширений языков программирования и плагинов для IDE.
Существенная часть исследований посвящена изучению возможностей по расширению Kotlin. Мы верим, что Kotlin можно продолжать улучшать и расширять. Например, это могут быть макросы, liquid-типы, pattern matching, вариативные дженерики. Также в лаборатории занимаются исследованием применения
конколического тестирования в Kotlin, различных техник фаззинга компилятора и других областей.


Лаборатория верификации и анализа программ (VorPAL)


Вернуться к списку исследовательских групп


Лаборатория инструментов совместной работы


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


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


Вернуться к списку исследовательских групп



Если вы хотите присоединиться к какой-либо из групп, создать совместный проект или у вас есть общие вопросы, пишите нам по адресу info@research.jetbrains.org.


Благодарим Ольгу Андреевских за помощь в подготовке этой публикации.



Ваша команда JetBrains Research
The Drive to Develop
Подробнее..

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

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 энергосберегающей системы, предназначенной для ускорения машинного обучения.

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

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

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

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.

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

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

Категории

Последние комментарии

  • Имя: Макс
    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