Привет, Хабр! Меня зовут Даниил Погуляка. Я студент четвертого курса МГТУ им. Н.Э. Баумана, факультета "Информатика, искусственный интеллект и системы управления". Уже некоторое время я занимаюсь изучением методов машинного обучения, в частности, касающихся автоматической обработки текстов (Natural Language Processing, NLP). В конце прошлого года мне удалось попасть на стажировку в Sber AI Lab. Оказавшись в команде проекта MedBench, у меня появилась возможность поработать над практическими NLP задачами. В этой статье я бы хотел рассказать вам о проекте, которым занимался на протяжении своей стажировки. Проект связан с использованием нейронных сетей в сфере медицины, но подробнее о нём после небольшого введения. Как известно, медицина относится к одной из фундаментальных областей человеческих знаний. Несмотря на богатую историю, медицинская наука и практика динамично развиваются и в настоящее время. Большие надежды на ближайшие успехи связывают с внедрением моделей Искусственного Интеллекта (ИИ). Значительный прогресс уже заметен в направлениях медицинского компьютерного зрения и методов сложных инструментальных исследований. Гораздо скромнее дело пока обстоит с моделями, работающими со слабо структурированными данными, прежде всего с медицинскими текстами, хотя ключевые технологические предпосылки уже сложились:  + Инфраструктура: С учётом проводимой цифровизации в области здравоохранения, накоплены значительные массивы текстовых данных, объём которых будет только увеличиваться со временем. Сегодня вся информация о пациенте (анализы / записи с приёмов / анамнез) хранится в Электронной Медицинской Карте (ЭМК).  + Методы: Область обработки текстов на естественном языке в последние годы переживает очередной подъём после появления больших нейросетевых языковых моделей. Кроме этого, существуют запросы врачебного сообщества как на интеллектуальную автоматизацию существующих рутинных задач (например, генерация текстовых заключений по мед. снимкам), так и запросы на решение принципиально новых задач, возникающих в медицине (например, предсказание индивидуальных рисков пациентов в группе диспансерного наблюдения). В совокупности, такие предпосылки создают потенциально интересные направления для исследований и внедрения новых продуктовых решений на основе технологий ИИ в медицине. Теперь поговорим об ограничениях... Специалистам, работающим в области NLP, известно что подавляющее большинство научных работ / наборов данных существует для английского языка. Размеченных текстовых корпусов на других языках на порядок меньше, и, наверное, ещё на порядок меньше таких данных по медицине. Причина в строгом законодательном регулировании, т.к. мед. записи могут содержать персональную информацию и элементы врачебной тайны, поэтому довольно сложно опубликовать их в открытом доступе. Чтобы выложить такие данные, нужно предварительно выполнить процедуры деперсонификации, а это громадный объём работы. Поэтому отдельные энтузиасты, работающие над такими задачами, обходятся либо закрытыми данными, либо тем до чего можно дотянутся в интернете (например тексты с мед форумов – корпус Medical_QA_ru_data, мед. статьи из Википедии, сообщения из соцсетей – корпус RuDReC). Однако понятно, что без единого большого набора открытых данных невозможно формирование устойчивого и успешного Data Science сообщества исследователей и разработчиков на стыке ИИ и медицины. По опыту англоязычного сообщества, появление в начале 2000-х годов открытого корпуса данных о пациентах, находившихся в отделении интенсивной терапии (Medical Information Mart for Intensive Care (MIMIC), >40 тыс пациентов, за период наблюдений ~10 лет), привело к существенному росту исследований в направлении обработки ЭМК (одна из последних работ об уже третьей версии датасета процитирована ~5000 раз по данным с сайта scholar.google.com). Эти работы также породили множество хорошо формализованных задач машинного обучения, ставших эталоном для проверки медицинских ML-моделей. В качестве примеров таких задач можно привести: автоматическое выставление кодов болезней и услуг по результатам проведённого лечения; предсказание госпитальной летальности; оценка вероятности осложнений и повторной госпитализации и т.п. Хотя для русского языка в последнее время стали появляться некоторые похожие данные, например, корпус амбулаторных визитов пациентов RuMedPrimeData, появление полноценного медицинского корпуса стационарных данных (и ML-задач учитывающих специфику нашей системы здравоохранения) всё ещё дело ближайшей перспективы. Чтобы начать/ускорить движение в этом направлении команда из группы медицинских исследований лаборатории искусственного интеллекта Сбера запустила инициативу по созданию платформы открытых медицинских русскоязычных данных – medbench.ru. Хотя идея такой платформы не нова, для русского языка уже существуют аналоги: Russian SuperGLUE или RuCoLA, однако, насколько нам известно это первый подобного рода ресурс именно с медицинскими данными на русском языке (подробнее о предложенном медицинском бенчмарке можно прочитать в статье). На текущий момент на платформе уже доступны три задачи из области автоматической обработки естественного языка: RuMedDaNet – задача на оценку способности модели "понимать" медицинский текст и правильно отвечать на уточняющие вопросы. RuMedNLI – задача в которой при заданном контексте и гипотезе нужно определить их логическую взаимосвязь, как: следствие, противоречие или нейтральность. RuMedTest – задача на проверку "знаний" модели в рамках специальности "Общая врачебная практика". Модель должна выбрать один из четырех предложенных ответов на вопрос. (Для своего исследования я выбрал задачу RuMedDaNet, она показалась мне наиболее прикладной). Таким образом, участникам предлагается создать интеллектуальные системы способные выполнять задания на логику и знания, целеполагание и понимание смысла медицинского текста. Разумеется, прогресс в такой области исследований невозможен без здоровой конкуренции, поэтому платформа предлагает публичный лидерборд, на котором будут появляться лучшие решения предложенных задач. Особое внимание уделено воспроизводимости получаемых результатов, поэтому попадание на лидерборд реализовано через дополнительный процесс проверки кода решений.  Для современных моделей ИИ пока недостижимым остаётся уровень интеллекта человека, который является своего рода эталонным результатом в таких задачах. Поэтому усилиями команды проекта, совместно с коллегами из Сибирского государственного медицинского университета для каждой из предложенных задач подготовлены ответы асессоров и выполнена оценка качества таких решений. Стоит отметить что задачи получились довольно сложными даже для больших нейросетевых моделей. Так, в вопросно-ответной задаче (RuMedDaNet) разрыв по метрике Точность (Accuracy) между лучшей моделью и уровнем человека составляет более 24%. А это значит, что существует большой потенциал для улучшения текущих решений и разработки новых подходов и методов прикладного ИИ. Для решения медицинских задач, в том числе задач из пула MedBench, моими коллегами были разработаны модели глубокого машинного обучения. При их создании за основу были взяты общедоступные чекпоинты BERT и RoBERTa (подробнее об этой архитектуре далee), заранее предобученные на общеязыковых корпусах. Первый из чекпоинтов — это адаптированная мультиязыковая версия BERT, обученная на русской Википедии усилиями команды DeepPavlov. Остальные два чекпоинта представляют собой версии архитектур BERT и RoBERTa, также предобученные на данных русскоязычной Википедии, но с добавлением корпуса текстов Taiga (эти работы были проделаны коллегами из команды SberDevices). В довершение ко всему вышеперечисленному, такие модели были дообучены на открытых биомедицинских текстах, в результате чего и были получены модели RuBioBERT и RuBioRoBERTa. Модели показывают высокую точность при решении мед. задач, но от чего эта точность зависит? Какие параметры влияют на конечный результат? Насколько этот результат воспроизводим? Именно на эти вопросы и предстоит ответить в моём исследовании. Для тех, кто не знаком с принципами работы нейронных сетей и впервые слышит о понятии "трансформеры", я советую изучить данную статью. Для остальных я вкратце упомяну основные преимущества Bidirectional Encoder Representations from Transformers (BERT) архитектур.  Принцип работы BERT заключается в том, что при обучении нейронной сети маскируется слово не только в конце предложения, а внутри него. Такая задача называется предсказанием маскированного токена (Masked Language Modeling task). Этот подход позволяет  нейросети одновременно обучаться (выучивать эмбеддинги токенов) в обе стороны, таким образом достигается  "глубокая двунаправленность" – модель учитывает контекст с двух сторон от слова. Также данная архитектура использует механизм внимания, помогающий лучше выделить контекст и взаимосвязь слов между собой. В свою очередь архитектура RoBERTa (Robustly optimized BERT approach)  является усовершенствованной версией BERT. Это та же BERT, но с оптимизированными гиперпараметрами и динамической маскировкой слов. Во время предобучения нейросеть обучается только предсказанию замаскированного слова, тогда как в архитектуре BERT она также предобучалась на задаче next sentence prediction, т.е предсказывала является ли в паре предложений второе продолжением первого.  Одним из вариантов формирования данных для вопросно-ответной задачи является майнинг примеров из поисковых запросов на медицинскую тематику. Но потенциальная проблема такого подхода заключается в том, что запросы зачастую имеют повторяющиеся/похожие паттерны, что может привести к нежелательным корреляциям в данных. Таким образом, при подготовке примеров было решено прибегнуть к генерации вопросов с нуля при помощи асессоров.  Изначально из открытых источников выбирался медицинский контекст, т.е. небольшой фрагмент текста (до ~300 слов). При формировании таких контекстов создатели постарались включить максимально широкий спектр областей здравоохранения: терапевтическая медицина, физиология и анатомия человека, фармакология, биохимия и т.п. Затем для каждого такого фрагмента генерировался контекстно-зависимый вопрос с возможностью ответа только да или нет и соответственно сам ответ. Таким образом и были получены триплеты – контекст, вопрос, ответ. Датасет разделен на части "train", "test" и "dev" объемом 1308, 512 и 256 триплетов соответственно, каждая часть в отдельном файле формата json. Внутри текст на русском… Вот несколько примеров из тренировочного датасета: Context Question Answer Ощущение положения тела в пространстве связано с деятельностью вестибулярного, двигательного анализаторов и их взаимодействия со зрительным анализатором. Двигательный анализатор вносит значительный вклад в восприятие положения тела, действительно ли это? Да Нуждаемость в санитарной обработке не определяется стойкостью очага, скорее - "агрессивностью" конкретного вещества. В целом, можно сказать, что в проведении санитарной обработки будут нуждаться пораженные, доставляемые из очагов поражения жидкими органическими и неорганическими кислотами и сильными щелочами. Сильные кислоты нельзя отнести к "агрессивным" веществам, после поражения которыми необходимо проведение санитарной обработки, но так ли это? Нет В 1900 году австрийский иммунолог Карл Ландштейнер впервые выделил агглютинин из состава крови, в 1930 году он получил Нобелевскую премию за открытие групп крови. Впоследствии было определено, что в крови новорожденных не содержится агглютинина, он приобретается со временем, достигая своего максимального титра в возрасте от 10 до 14 лет, то есть в период полного формирования иммунной системы человека. Была ли выдана Нобелевская премия за открытие групп крови? Да Действие проявляется через 20 мин после закапывания в конъюнктивальную полость. Максимальное снижение внутриглазного давления наступает через 1–2 ч и сохраняется в течение 24 ч. После использования капель эффект снижения внутриглазного давления сохраняется на протяжении двух суток? Нет Посмотрим что с длинами этих текстов между частями:  train test dev Средняя длина в символах (контекст / вопрос) 492 / 65 497 / 74 482 / 63 Средняя длина в словах (контекст / вопрос) 58 / 8 60 / 9 57 / 7 Отлично, выглядит похоже. Теперь посмотрим на распределение ответов да/нет:  train dev Количество примеров с ответом да / нет 654 / 654 128 / 128 Замечательно, соблюдается идеальный баланс между ответами "да" и "нет"; по условиям задачи для оценки качества решений используется очевидная метрика Accuracy, т.е. доля правильных ответов алгоритма. Исследование работы модели было разделено на две части, которые я условно назвал:  алгоритмическая (стабильность модели, подбор и варьирование гиперпараметров) и сущностно-содержательное (манипуляции со входными данными). Начну с алгоритмической и, в первую очередь, затрону проблему "Stability of Fine-tuning BERT". Её суть заключается в том, что дообучение BERT моделей, хоть и приводит к высокому результату, но зачастую является нестабильным процессом, тем самым варьируя этот результат. Основными причинами данной проблемы называют "забывчивость" нейронной сети и небольшой объем данных при дообучении. Эту проблему исследуют уже давно, способы её решения заслуживают отдельной статьи, которую я оставлю тут. Сам я лишь продемонстрирую ее: Дообучать я буду модель RuBioRoBERTa (новейшая на текущий момент из русскоязычных моделей, дообученных на данных медицинско-биологического домена) на задаче RuMedDaNet, как было упомянуто выше. Целевой метрикой будет Accuracy, обучение происходит при следующих гиперпараметрах: epochs = 25, batch_size = 16, lr = 3e-5. Значение seed (параметр инициализации генератора случайных чисел) варьируется в диапазоне [2; 4096], что приводит к различным результатам: seed 2 4 8 16 32 64 dev_acc 79.68 77.73 77.34 77.73 79.68 80.08 seed 128 256 512 1024 2048 4096 dev_acc 79.68 75.78 78.91 75.00 75.00 75.78 Таким образом, хоть и удается достигнуть высокой точности, но результат не всегда стабилен и в моем случае колеблется в промежутке 75-80%. Это довольно широкий диапазон, такая вариативность вероятно является следствием небольшого объема данных (как помните в dev наборе всего 256 примеров).  Теперь посмотрим, как значение коэффициента скорости обучения влияет на результат. Learning rate (LR) – это параметр оптимизации, который определяет размер шага на каждой итерации при движении к минимуму функции потерь. Коэффициент LR я варьирую в диапазоне от 5е-6 до 15e-5: Learning Rate 5e-6 6e-6 7e-6 8e-6 9e-6 dev_acc 78.51 78.91 78.91 80.08 77.73 Learning Rate 1e-5 2e-5 3e-5 4e-5 5e-5 dev_acc 79.29 80.47 79.68 78.51 78.51 Learning Rate 6e-5 7e-5 8e-5 9e-5 10e-5 dev_acc 73.43 73.04 69.14 51.95 72.27 Learning Rate 11e-5 12e-5 13e-5 14e-5 15e-5 dev_acc 75.78 73.43 67.58 51.95 54.69   Само по себе изменение гиперпараметра LR приводит, как к уменьшению, так и к увеличению точности. В лучшем случае точность удалось увеличить на ~1%. Вообще, параметр LR тесно связан с batch_size (определяет количество примеров, которые мы используем для вычисления частичного значения функции потерь). И при изменении одного из них в k раз другой советуют также изменять в k раз, что теоретически должно приводить к лучшему результату. Но на практике это работает далеко не всегда, поэтому я решил рассмотреть эти параметры независимо друг от друга. Перейдем к параметру batch_size: batch_size 4 8 16 32 dev_acc 80.08 78.90 79.68 80.47 В моём случае вышло так, что максимальная точность была достигнута при большем batch_size, что также соответствует меньшему времени обучения. Если попробовать увеличить параметр LR пропорционально batch_size, то точность упадет. Это говорит о том, что на практике, в зависимости от моделей, данных и методов обучения, вклад и зависимость гиперпараметров друг от друга может меняться.  Это заставило меня задуматься о том, как вообще можно объективно сравнивать модели друг с другом? Интересное решение предлагается в данной статье. Вдохновившись им я решил сравнить эффективность текущей открытой модели – RuBioRoBERTa с её новой версией – RuEHRRoBERTa (приватная версия дообученная на текстах из ЭМК).  Решено было рассматривать среднее по точности моделей при переборе параметра seed. Для каждой из моделей был сгенерирован список фиксированной длины со случайными seed’ами в диапазоне             [2; 4096]. Результаты получились следующими: Действительно, несмотря на разные параметры обучения новая модель в среднем показывает лучший результат при том же времени обучения. Исходя из этого можно сделать однозначный вывод о ее эффективности в этой задаче.  Далее рассмотрим то, как на результат влияют сами входные данные (перестановки слов, полнота вопроса и контекста). Так как примеры состоят из контекста и вопроса к нему, я решил отдельно рассмотреть влияние каждого из этих компонентов: Перестановки слов  вопроса – будем несколькими способами менять порядок слов в вопросе. Первый способ – случайная перестановка слов (Random), второй способ – перестановка соседних слов друг с другом (Neighbours). Для единообразия здесь и далее зафиксируем основные параметры обучения: epochs = 25, batch_size = 16, lr = 3e-5. Результат: Type Default Random shuffle Neighbours shuffle dev_acc 79.68 69.53 71.48 Видно, что при перестановках качество падает, но это падение не критично и точность остается в диапазоне 69-80%. Причиной тому служит небольшая длина вопроса, которая позволяет сохранить основную мысль вопроса даже после  перестановки слов.  Перестановки слов контекста – выполняем аналогичные операции перестановок (Random и Neighbors) над контекстом.  Результат: Type Default Random shuffle Neighbours shuffle dev_acc 79.68 50.00 57.03 В случае с контекстом качество модели значительно снижается. Контекст имеет большую длину, поэтому перестановки влияют на семантику всего примера в большей степени. Также в контексте по сути содержится ответ на вопрос и если он сильно искажен модели просто неоткуда получить ответ. Полнота вопроса – вопрос разбивается на 5 равных частей и выполняется 5 циклов дообучения модели (от 0 частей, т.е. полное отсутствие вопроса до 5 частей, т.е. наличие полноценных законченных примеров). Результат: parts 0/5 1/5 2/5 3/5 4/5 5/5 dev_acc 57.42 59.37 60.39 60.93 66.79 79.68 На графике видна явная зависимость – чем больше частей вопроса используется, тем выше точность. Удивительно что какую-то точность отличающуюся от случайной (50%) можно получить совсем без вопроса просто по контексту. Я думаю что это артефакты формирования датасета и в контекстах присутствуют слова по которым заранее можно определить каким будет ответ, даже не зная вопроса! Полнота контекста – выполняем операции по постепенному увеличению контекста (аналогично процедуре описанной выше), только деление происходит на 20 частей.  Результат: parts 0/20 1/20 2/20 3/20 4/20 5/20 6/20 dev_acc 54.68 50.78 52.34 63.57 71.09 69.53 71.48 parts 7/20 8/20 9/20 10/20 11/20 12/20 13/20 dev_acc 70.70 73.04 70.70 71.87 76.17 73.82 77.34 parts 14/20 15/20 16/20 17/20 18/20 19/20 20/20 dev_acc 77.34 76.56 71.87 76.95 77.73 78.90 79.68 	Видна явная зависимость точности от полноты контекста. Действительно, всё логично – чем полнее контекст, тем больше информации для поиска ответа моделью  и тем выше точность.Таким образом можно смело говорить о том, что  полнота данных (контекстов и вопросов) напрямую влияет на качество предсказания модели.  По ходу решения задачи модель RuBioRoBERTa показала высокую точность, но я решил попробовать увеличить этот показатель с помощью аугментации данных. Аугментация (augmentation) – это приём создания дополнительных примеров из имеющихся. Обычно при аугментации применяют преобразования исходных объектов, которые не меняют их метки, но меняют (иногда существенно) сами объекты (в нашем случае исходные тексты).  В текущей задаче я решил применить обратный перевод – способ аугментации данных, при котором текст сначала переводят на промежуточный язык (в эксперименте это был английский), а потом обратно на исходный. Смысл такой процедуры в том что итоговый вариант перевода скорее всего будет немного отличаться от исходного, но сохранит общий смысл. Для этого воспользовался FSMT моделью машинного перевода от Фейсбука. В целом это позволило увеличить датасет вдвое. Вместо того, чтобы сразу обучаться на удвоенном датасете, я решил наращивать количество примеров постепенно и проверять точность на каждом этапе добавления данных. Так, аугментированный датасет был разбит на 12 частей и они инкрементально добавлялись к исходным данным: parts 1/12 2/12 3/12 4/12 5/12 6/12 dev_acc 77.73 80.47 77.73 80.08 75.78 78.51 parts 7/12 8/12 9/12 10/12 11/12 12/12 dev_acc 78.91 74.70 73.46 70.31 76.17 70.31 Судя по результатам можно сказать, что при добавлении небольшой части аугментированных данных удается повысить точность, но лишь на 0.4-0.8%. При дальнейшем наращивании данных точность вообще падает, это говорит о том, что в моём случае аугментация обратным переводом вносит лишь шум, который ухудшает итоговое качество модели. Наверное, эту проблему можно исправить изменив параметр, который отвечает за количество токенов исходного предложения, которые  могут появляться в перефразированном, но такое упражнение я оставлю заинтересованным читателям. Также, в качестве перспективного способа аугментации, можно рассмотреть варианты перевода похожих примеров с других языков.   В итоге, моделью RuBioRoBERTa в задаче RuMedDaNet мне удалось добиться качества 73.24% на закрытой тестовой части данных (хотя на dev метрика Accuracy вообще была 81.64%). Это на 5.08% лучше результат текущей чемпионской модели, но всё ещё хуже человека на 19.73%... Код итогового решения можно посмотреть в репозитории проекта. Стоит отметить, что мне удалось проверить лишь некоторые идеи. Совсем не тронутыми остались ещё две задачи (RuMedNLI и RuMedTest), другие мульти и русскоязычные модели/архитектуры (например, T5), поиск и добавление медицинских знаний/эвристик способных улучшить качество и т.д. В общем, я думаю в этой области всё ещё остаётся огромное пространство для исследований, а их потенциальная прикладная составляющая вызывает у меня ещё большее вдохновение. Не каждый день появляется доступная возможность внести свой вклад в современную медицину :) Команда проекта MedBench открыта к всестороннему сотрудничеству и с радостью готова помочь в подготовке и публикации новых задач искусственного интеллекта в медицине. Давайте развивать проект вместе! Стажёр Sber AI Lab