>>1.00<<>>1.00<<Видео проигрыватель загружается.Воспроизвести видеоВоспроизвестиБез звукаТекущее время 0:00/Продолжительность 5:28Загрузка: 0.00%0:00Тип потока ОНЛАЙНSeek to live, currently behind liveОНЛАЙНОставшееся время -5:28 1xСкорость воспроизведения2x1.75x1.5x1.25x1x, выбрано0.75x0.5xГлавыГлавыОписанияОтключить описания, выбраноСубтитрынастройки субтитров, откроется диалог настройки субтитровСубтитры выкл., выбраноЗвуковая дорожкаPicture-in-PictureПолноэкранный режимThis is a modal window.Начало диалоговго окна. Кнопка Escape закроет или отменит окноТекстColorБелыйЧерныйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачностьПолупрозрачныйФонColorЧерныйБелыйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачностьПолупрозрачныйПрозрачныйОкноColorЧерныйБелыйКрасныйЗеленыйСинийЖелтыйПурпурныйГолубойTransparencyПрозрачныйПолупрозрачныйПрозрачностьРазмер шрифта50%75%100%125%150%175%200%300%400%Стиль края текстаНичегоПоднятыйПониженныйОдинаковыйТеньШрифтПропорциональный без засечекМоноширинный без засечекПропорциональный с засечкамиМоноширинный с засечкамиСлучайныйПисьменныйМалые прописныеСбросить сбросить все найстройки по умолчаниюГотовоЗакрыть модальное окноКонец диалогового окна.
Давайте предпримем первую попытку решить нашу задачу. Для этого нам необходимо описать нейросетевой модуль для pytorch. И особенность этого модуля будет заключаться в том, что он будет предсказывать метки частей речи токенов, используя информацию, содержащуюся только в самих токенах. Другими словами, эта модель никак не будет использовать контекст, в котором слово употребляется. Опять же, давайте начнём с метода "forward". Для начала, получим переменные, представляющие форму исходного тензора. На вход нам приходит трёхмерный тензор, размерности которого соответствуют [размеру батча, наибольшей длине предложения в токенах и наибольшей длине каждого токена в символах]. Далее мы схлопываем первое и второе измерения, чтобы получить двухмерный тензор. Другими словами, мы забываем о том, что токены у нас были как-то объединены в предложения (для этой модели нам совершенно неважно). Далее мы используем embedding-слой для того, чтобы для каждого символа получить вектора. Таким образом, мы снова получаем трёхмерный тензор, который соответствует [количеству токенов в батче на длину каждого токена на размер вектора для символа]. Затем мы транспонируем этот тензор для того, чтобы мы могли его подать в свёрточную нейросеть. В pytotch принята следующая конвенция о порядке размерностей: сначала идёт размер батча, затем идёт количество признаков для каждого элемента (в данном случае — это размер вектора), а затем идёт какое-то количество размерностей, соответствующих самим элементам, то есть дальше идут пространственные измерения. В текстах мы работаем с одномерными данными, то есть у нас, в данном случае это длина токена. После всех этих операций, переменная "char embeddings" содержит векторы для отдельных символов. Пока что, эти векторы не содержат информации о том, в каком контексте используется каждый символ — это просто какие-то априорные знания о том, что это вообще за символ. Вначале обучения эти вектора инициализируется случайными числами. Затем эти вектора мы передаём в свёрточный модуль, для того, чтобы учесть локальный контекст — то есть ту ситуацию, в которой каждый конкретный символ используется. Для этого мы применяем "backbone"-сетку, роль которой выполняет простенький ResNet, который мы определили чуть выше. В результате мы получаем трёхмерный тензор такой же размерности кладём его в переменную features, и эта переменная содержит векторы символов уже с учётом к их контекста. Тут нужно вспомнить, что тэги нам нужно предсказывать не для каждого символа, а для каждого токена. Поэтому нам нужно как-то агрегировать признаки символов в токене, чтобы получить вектор токена. Для этого мы используем pooling — в данном случае это max pooling. Как работает max pooling? Допустим, у нас есть некоторая матричка, строки в этой матричке представляют отдельные символы в токене, а столбцы — это признаки этих символов. И max pooling делает из этого один вектор — количество элементов в этом векторе соответствует количеству столбцов в исходной матрице, и каждый элемент получен взятием функции максимума из соответствующего столбца. В результате применения global пулинга мы получаем тензор, на этот раз — уже двумерный, каждая строчка этого тензора представляет отдельный токен. Ну и наконец, по каждому токену нам нужно принять решение касательно его метки. Для этого признаки токенов мы передаём в ещё один нейросетевой модуль, который называется out — это просто полносвязный блок. В результате применения этого блока мы получаем также двухмерный тензор, но у него уже размер строки не "embedding size", a "количество меток частей речи". Далее мы меняем форму этого тензора, преобразуем его в трёхмерный, то есть "вспоминаем" о том, что у нас есть предложения, и транспонируем для того, чтобы порядок измерений соответствовал порядку измерений в исходном тензоре "tokens". В чём же физический смысл того, что мы только что рассмотрели? Физический смысл того, что мы сейчас описали, заключается в том, чтобы рассмотреть все возможные N-граммы символов, которые встречаются в каждом токене, и по ним попробовать определить часть речи. Благодаря тому, что основная наша нейросеть (backbone) содержит "skip connections", N-граммы, которые учитываются этой нейросетью, по сути, имеют различную длину. Например, если мы используем размер ядра свёртки, равный 3, то первый блок учитывает трёхграммы, второй блок уже учитывает пятиграммы, а третий — семиграммы, соответственно. При этом, благодаря тому, что есть "skip connection", информация о трёхграммах не теряется, она пробрасывать до самого конца.

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