Лабораторная работа №2. Кодирование текста с помощью алгоритма BPE

Дано

  1. Текст на русском языке (assets/text.txt), который загружен и сохранен в переменную text в start.py.

  2. Секретные зашифрованные тексты.

  3. Обученный на большом корпусе текстов словарь токенов assets/vocab.json.

  4. Нейросетевая языковая модель assets/nmt_demo (пример обращения — assets/nmt_demo/demo.py).

В ходе выполнения лабораторной работы Вы научитесь предобрабатывать текст при помощи алгоритма Byte-Pair Encoding таким образом, чтобы суметь получить предсказания от нейросетевой языковой модели и оценить ее работу.

Терминология

В данной лабораторной работе мы будем оперировать следующими понятиями:

Идентификатор токена

Целочисленное значение, однозначно указывающее на определенный токен.

Словарь токенов (vocabulary)

Словарь, устанавливающий однозначное соответствие между токенами и идентификаторами токенов.

Слово

Последовательность символов между пробельными символами.

Токен

Часть, выделяемая в слове.

Частота слова

Количество вхождений слова в текст.

Описание алгоритма BPE (Byte-Pair Encoding)

Целью автоматической обработки естественного языка (Natural Language Processing, NLP) является формализация языка таким образом, чтобы сохранить его смысловое наполнение. Ключевую роль в такой обработке играет токенизация и кодирование текста. Благодаря этому становится возможным использование формальных языковых моделей, которые не способны работать с текстовыми данными в первоначальном виде, но широко используются для выявления скрытых закономерностей в числовых данных.

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

  1. Размер словаря (vocabulary), множества всех уникальных токенов, в таком случае получается достаточно большим.

  2. Даже при таком очень большом словаре нередко приходится сталкиваться с проблемой Out-of-Vocabulary.

Это не позволяет сделать обработку универсальной и масштабируемой на большое количество дискурсов.

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

Данный алгоритм был предложен в 1994 Филиппом Гейджем в статье “A New Algorithm for Data Compression”. Идея алгоритма сводится к замене самых частотных пар символов другим символом, при этом объем используемой памяти снижается в два раза.

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

Для лучшего понимания рассмотрим пример применения этого алгоритма. Допустим, нам дан следующий текст:

"It's far, farther, farthest and old, older, oldest"

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

[
    ('I', 't', "'", 's', '</s>'),
    ('f', 'a', 'r', ',', '</s>'),
    ('f', 'a', 'r', 't', 'h', 'e', 'r', ',', '</s>'),
    ('f', 'a', 'r', 't', 'h', 'e', 's', 't', '</s>'),
    ('a', 'n', 'd', '</s>'),
    ('o', 'l', 'd', ',', '</s>'),
    ('o', 'l', 'd', 'e', 'r', ',', '</s>'),
    ('o', 'l', 'd', 'e', 's', 't', '</s>')
]

Note

Каждое слово заканчивается специальным токеном '</s>', который обозначает конец слова.

Составим таблицу частотности пар токенов. Нас интересуют те токены, которые идут друг за другом:

{
    ('I', 't'): 1, ('t', "'"): 1, ("'", 's'): 1, ('s', '</s>'): 1,
    ('f', 'a'): 3, ('a', 'r'): 3, ('r', ','): 3, (',', '</s>'): 4,
    ('r', 't'): 2, ('t', 'h'): 2, ('h', 'e'): 2, ('e', 'r'): 2,
    ('e', 's'): 2, ('s', 't'): 2, ('t', '</s>'): 2,
    ('a', 'n'): 1, ('n', 'd'): 1, ('d', '</s>'): 1,
    ('o', 'l'): 3, ('l', 'd'): 3, ('d', ','): 1,
    ('d', 'e'): 2
}

Обратите внимание, что мы не рассматриваем пары токенов, которые встречаются на границе слов. Поэтому у нас нет пар, начинающихся с '</s>'.

Warning

Знаки препинания как часть слова

Обратите внимание, что в рамках данной лабораторной работы текст разделяется на слова исключительно по пробельным символам (методом .split()). Поэтому знаки препинания (например, запятые в "far," или "old,") не удаляются и технически становятся частью слова.

Алгоритм BPE воспринимает их как обычные символы, поэтому в процессе обучения могут возникать токены, содержащие пунктуацию (например, ',</s>'). В реальных задачах текст обычно проходит предварительную токенизацию или очистку, но здесь мы оставляем их для демонстрации базовой работы алгоритма.

Теперь нужно выбрать самую часто встречающуюся пару. В нашем примере это пара токенов (',', '</s>'). Давайте объединим ее в один токен:

[
    ('I', 't', "'", 's', '</s>'),
    ('f', 'a', 'r', ',</s>'),
    ('f', 'a', 'r', 't', 'h', 'e', 'r', ',</s>'),
    ('f', 'a', 'r', 't', 'h', 'e', 's', 't', '</s>'),
    ('a', 'n', 'd', '</s>'),
    ('o', 'l', 'd', ',</s>'),
    ('o', 'l', 'd', 'e', 'r', ',</s>'),
    ('o', 'l', 'd', 'e', 's', 't', '</s>')
]

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

Результатом работы алгоритма BPE становится словарь токенов (vocabulary) заданного размера (базовый алфавит символов + количество слияний num_merges). Для нашего примера итоговое разбиение слов на токены будет выглядеть следующим образом:

[
    ('I', 't', "'", 's', '</s>'),
    ('far', ',</s>'),
    ('far', 'th', 'er', ',</s>'),
    ('far', 'th', 'est', '</s>'),
    ('a', 'n', 'd', '</s>'),
    ('old', ',</s>'),
    ('old', 'er', ',</s>'),
    ('old', 'est', '</s>')
]

Предполагается, что таким образом удается выделить из текста значимые последовательности: это могут быть корни слов либо другие широко употребляемые морфемы. Так, в нашем примере нам удалось выделить два корня (far и old), а также суффиксы сравнительной и превосходной степени er и est.

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

Давайте пошагово рассмотрим реализацию такого алгоритма:

  1. Создать частотный словарь корпуса, который отражает количество вхождений каждого из слов.

  2. Токенизировать слова (на первой итерации — разделить слова на символы).

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

  4. Сформировать новый токен из самой часто встречающейся пары.

  5. Повторить шаги 2-4 до тех пор, пока не будет достигнуто желаемое количество токенов.

Что надо сделать

Шаг 0. Начать работу над лабораторной (вместе с преподавателем на практике)

  1. Измените файлы main.py и start.py.

  2. Закоммитьте изменения и создайте новый Pull Request.

Important

Код, выполняющий все требуемые действия, должен быть написан в функции main в модуле start.py.

Для этого реализуйте функции в модуле main.py и импортируйте их в start.py. Вызов функции в файле start.py:

if __name__ == '__main__':
    main()

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

Обратите внимание, что в файле settings.json необходимо выставить желаемую оценку: 4, 6, 8 или 10. Чем выше желаемая оценка, тем большее количество тестов запускается при проверке вашего Pull Request.

Important

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

Шаг 1. Токенизировать одно слово

Начнем с предобработки слов. Для этого реализуйте функцию lab_2_tokenize_by_bpe.main.prepare_word().

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

Например, строка "It's" должна быть обработана следующим образом: ('I', 't', "'", 's', '</s>'), где '</s>' — токен, который обозначает конец слова. В качестве токена, который обозначает начало слова, было передано None.

Шаг 2. Сформировать частотный словарь

Important

Выполнение Шага 2 соответствует 4 баллам.

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

Например, из текста "It's far, farther, farthest and old, older, oldest" должен получиться словарь следующего вида:

{
    ('I', 't', "'", 's', '</s>'): 1,
    ('f', 'a', 'r', ',', '</s>'): 1,
    ('f', 'a', 'r', 't', 'h', 'e', 'r', ',', '</s>'): 1,
    ('f', 'a', 'r', 't', 'h', 'e', 's', 't', '</s>'): 1,
    ('a', 'n', 'd', '</s>'): 1,
    ('o', 'l', 'd', ',', '</s>'): 1,
    ('o', 'l', 'd', 'e', 'r', ',', '</s>'): 1,
    ('o', 'l', 'd', 'e', 's', 't', '</s>'): 1
}

Реализуйте функцию lab_2_tokenize_by_bpe.main.collect_frequencies(). При этом токен начала слова может быть представлен None, а токен конца — нет. Функция обязательно должна вызывать функцию lab_2_tokenize_by_bpe.main.prepare_word().

Note

Зачем нужен None для границ слова?

В качестве самого слова (raw_word) всегда передается обычная строка. Значение None используется только для параметров начала (start_of_word) или конца (end_of_word) слова.

Это флаг: если параметр равен None, алгоритм понимает, что этот технический токен добавлять к слову не нужно. Например, в классическом BPE на Шаге 2 токен начала слова не используется, соответственно в start_of_word передается None.

Продемонстрируйте составление частотного словаря в функции main() модуля start.py, используя текст на русском языке (переменная text). В качестве токена конца слова используйте строку </s>. Токен начала слова не используйте, то есть передайте в его качестве None.

Шаг 3. Посчитать количество вхождений каждой из пар токенов

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

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

{
    ('I', 't'): 1, ('t', "'"): 1, ("'", 's'): 1, ('s', '</s>'): 1,
    ('f', 'a'): 3, ('a', 'r'): 3, ('r', ','): 3, (',', '</s>'): 4,
    ('r', 't'): 2, ('t', 'h'): 2, ('h', 'e'): 2, ('e', 'r'): 2,
    ('e', 's'): 2, ('s', 't'): 2, ('t', '</s>'): 2,
    ('a', 'n'): 1, ('n', 'd'): 1, ('d', '</s>'): 1,
    ('o', 'l'): 3, ('l', 'd'): 3, ('d', ','): 1,
    ('d', 'e'): 2
}

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

Реализуйте функцию lab_2_tokenize_by_bpe.main.count_tokens_pairs().

Шаг 4. Сформировать новый токен

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

Реализуйте функцию lab_2_tokenize_by_bpe.main.merge_tokens(). Она обновляет частотный словарь слов, заменяя в ключах пары токенов, из которых сформирован новый токен, на этот самый новый объединенный токен.

Так, если в частотном словаре слов из предыдущего шага объединить пару токенов (',', '</s>') в новый токен, то словарь должен измениться следующим образом:

{
    ('I', 't', "'", 's', '</s>'): 1,
    ('f', 'a', 'r', ',</s>'): 1,
    ('f', 'a', 'r', 't', 'h', 'e', 'r', ',</s>'): 1,
    ('f', 'a', 'r', 't', 'h', 'e', 's', 't', '</s>'): 1,
    ('a', 'n', 'd', '</s>'): 1,
    ('o', 'l', 'd', ',</s>'): 1,
    ('o', 'l', 'd', 'e', 'r', ',</s>'): 1,
    ('o', 'l', 'd', 'e', 's', 't', '</s>'): 1
}

Warning

Ошибочное появление пустого токена (‘’):

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

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

Шаг 5. Обучить токенизатор

Теперь у нас есть все компоненты для того, чтобы обучить наш токенизатор и сформировать необходимое количество новых токенов. Для этого реализуйте функцию lab_2_tokenize_by_bpe.main.train(), которая обязательно вызывает функции lab_2_tokenize_by_bpe.main.count_tokens_pairs() и lab_2_tokenize_by_bpe.main.merge_tokens().

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

Например, для пар {('a', 'b'): 3, ('b', 'cd'): 3, ('b', 'ca'): 3} нужно сформировать токен 'bca', так как этот токен длиннее токена 'ab' и лексикографически меньше токена 'bcd'.

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

Продемонстрируйте обучение токенизатора на материале текста из переменной text в модуле start.py. Используйте 100 слияний в качестве критерия остановки обучения.

Note

Что является результатом выполнения этапа на 6 баллов:

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

Note

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

Шаг 6. Присвоить токенам идентификатор

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

Note

При присваивании идентификатора не нужно учитывать повторяющиеся токены и символы.

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

Например, из набора токенов ['far', 'old', 'th', 'er', 'est', '</s>', 'a', 'n', 'd'] должен получиться следующий словарь:

{
    '<unk>': 0, '</s>': 1, 'est': 2, 'far': 3, 'old': 4, 'er': 5,
    'th': 6, '/': 7, '<': 8, '>': 9, 'a': 10, 'd': 11, 'e': 12,
    'f': 13, 'h': 14, 'l': 15, 'n': 16, 'o': 17, 'r': 18, 's': 19, 't': 20
}

Для этого реализуйте функцию lab_2_tokenize_by_bpe.main.get_vocabulary(). В возвращаемом словаре должны присутствовать специальные токены:

  • токен конца слова (при наличии);

  • токен начала слова (при наличии);

  • неизвестный токен.

Присвоение идентификатора данным токенам производится так же после сортировки вместе с остальными токенами.

Шаг 7. Декодировать текст

Important

Выполнение Шага 7 соответствует 6 баллам.

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

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

Так, при использовании словаря из предыдущего шага, последовательность [3, 6, 2, 1] (соответствующая токенам 'far', 'th', 'est' и '</s>') должна быть раскодирована как 'farthest '.

Создайте словарь вида <токен: числовой идентификатор>, используя '<unk>' в качестве токена, который обозначает неизвестную последовательность. Затем продемонстрируйте работу декодера, прочитав и декодировав секретное содержимое любого файла из папки assets/secrets в модуле start.py.

Note

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

Шаг 8. Закодировать одно слово

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

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

Например, мы токенизируем слово ('w', 'o', 'r', 'd', '</s>') и получаем следующий словарь токенов:

{'<unk>': 0, '</s>': 1, 'ord': 2, 'wo': 3, 'd': 4, 'o': 5, 'r': 6, 'w': 7}

При корректной реализации слово должно быть токенизировано следующим образом: [7, 2, 1]. Варианты [3, 6, 4, 1] и [7, 5, 6, 4, 1] являются неправильными: токен ord длиннее токенов wo и тем более w, o, r и d.

Note

Разбор жадного алгоритма сегментации слова:

Поиск токенов в слове происходит не слева направо по буквам, а по убыванию длины токенов в словаре.

  1. Перед началом работы функции все токены из предоставленного словаря сортируются строго по длине (от самых длинных к коротким).

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

  3. Если токен найден, он заменяет собой буквы слова.

  4. Программа переходит к следующим, более коротким токенам.

Почему выбирается “ord”, а не “wo”? Токен 'ord' имеет длину 3, а токен 'wo' длину 2. Так как мы ищем от самых длинных к коротким, токен 'ord' проверяется первым, находит совпадение в слове "word" и поглощает букву 'o'. После этого токен 'wo' применить уже невозможно. Верный результат: ['w', 'ord'].

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

Шаг 9. Загрузить словарь

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

На данном шаге мы научимся считывать такие словари из файлов с расширением .json. Для этого реализуйте функцию lab_2_tokenize_by_bpe.main.load_vocabulary().

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

Шаг 10. Закодировать текст

Important

Выполнение Шага 10 соответствует 8 баллам.

Чтобы формальная модель поняла наш запрос, нужно его закодировать. Для этого реализуйте функцию lab_2_tokenize_by_bpe.main.encode(). Она обязательно должна вызывать функции lab_2_tokenize_by_bpe.main.prepare_word() и lab_2_tokenize_by_bpe.main.tokenize_word().

Продемонстрируйте работу функций lab_2_tokenize_by_bpe.main.load_vocabulary() и lab_2_tokenize_by_bpe.main.encode() в функции main() модуля start.py, используя текст из файла assets/ru_raw.txt. Для этого загрузите специальный обученный словарь assets/vocab.json. При кодировании в качестве токена начала слова используйте строку \u2581, а в качестве неизвестного токена — <unk>. Токен конца слова использовать не нужно, то есть передайте в его качестве None. Чтобы убедиться, что файл удалось закодировать верным образом, сравните первые 10-20 токенов полученного результата с эталонной закодированной версией текста из файла assets/ru_encoded.txt.

Шаг 11. Выделить n-граммы

Теперь мы готовы перейти к тому, чтобы научиться оценивать качество перевода. Для сравнения полученных предсказаний с истинным ответом, нужно научиться рассчитывать метрику BLEU (bilingual evaluation understudy).

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

В течение следующих нескольких шагов мы реализуем подсчет метрики BLEU. Для начала нужно реализовать функцию для выделения n-грамм из последовательности lab_2_tokenize_by_bpe.main.collect_ngrams().

N-граммами называют такую подпоследовательность, которая включает в себя ровно n последовательных элементов. Например, из буквенной последовательности мыть можно выделить четыре униграммы (м, ы, т, ь), три биграммы (мы, ыт, ть), две триграммы (мыт, ыть) и, наконец, одну 4-грамму (мыть).

Important

Символьный или словесный BLEU?

В рамках данной лабораторной работы функции collect_ngrams и calculate_bleu работают на уровне отдельных символов (букв) (включая знаки препинания и пробелы).

Например, при вызове функции collect_ngrams с текстовой последовательностью (строкой) "мыть" и порядком слияния 3, мы работаем с символами и ожидаем получить результат в виде списка кортежей: [('м', 'ы', 'т'), ('ы', 'т', 'ь')].

Шаг 12. Вычислить метрику Precision

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

Иными словами, чем больше предсказанных n-грамм действительно присутствуют в истинной последовательности, тем выше значение Precision.

Для вычисления метрики Precision реализуйте функцию lab_2_tokenize_by_bpe.main.calculate_precision().

Рассмотрим пример. Пусть истинными значениями является [('м',), ('ы',), ('т',), ('ь',)], а предсказанными — [('м',), ('ы',), ('т',), ('ь',), ('c',), ('я',)]. Тогда совпадающими значениями будут следующие 4 элемента: [('м',), ('ы',), ('т',), ('ь',)]. Так как всего нам дано 6 предсказанных значений Precision, то доля совпадающих элементов, равна \(\frac{4}{6}=0.(6)\).

Шаг 13. Вычислить среднее геометрическое взвешенное

До расчета метрики BLEU нам остался всего один шаг, и это подсчет среднего геометрического взвешенного значения. Для этого реализуйте функцию lab_2_tokenize_by_bpe.main.calculate_geo_mean().

Рассчитывать среднее взвешенное значение мы будем для метрик Precision, которые посчитаны для n-грамм различного порядка, от 1 до \(order_{max}\), где \(order_{max}\) — максимальный порядок рассматриваемых n-грамм.

Рассчитать значение можно по следующей формуле:

\[Mean_{geometric} = \exp\left(\frac{1}{order_{max}} \times \sum_{i=1}^{order_{max}} \ln(Precision_i)\right)\]

Здесь \(Precision_{i}\) обозначает значение метрики Precision для n-грамм порядка \(i\), причем \(i\) принимает значения от 1 до \(order_{max}\).

Шаг 14. Вычислить метрику BLEU

Important

Выполнение Шага 14 соответствует 10 баллам.

Наконец, мы готовы рассчитать метрику BLEU, которая отражает близость предсказанного значения истинному. Для этого реализуйте функцию lab_2_tokenize_by_bpe.main.calculate_bleu(), которая обязательно вызывает функции lab_2_tokenize_by_bpe.main.collect_ngrams(), lab_2_tokenize_by_bpe.main.calculate_precision() и lab_2_tokenize_by_bpe.main.calculate_geo_mean().

Функция должна:

  1. Выделить n-граммы из предоставленных последовательностей всех порядков от 1 до обозначенного максимального.

  2. Посчитать Precision для выделенных n-грамм каждого порядка.

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

Продемонстрируйте оценку работы языковой модели Helsinki-NLP/opus-mt-ru-en в задаче машинного перевода в start.py. Информацию об этой языковой модели можно получить здесь. Пример работы с данной языковой моделью вы можете увидеть в assets/nmt_demo/demo.py.

Вам не нужно импортировать модель и самостоятельно генерировать предсказания — это уже сделано за вас.

Закодированные предсказания языковой модели (числовые ID токенов английского языка) находятся в файле assets/en_encoded.txt. Продемонстрируйте работу алгоритма машинного перевода в функции main() модуля start.py. Для этого декодируйте предсказания, используя реализованные вами функции, и сравните получившийся текст с эталонным человеческим переводом из файла assets/en_raw.txt. При декодировании токен конца слова следует передать как None. Сравнение следует производить по метрике BLEU. После декодирования и перед сравнением нужно очистить текст от токена начала слова (\u2581), заменив его на пробел.

Note

Зачем нужен токен начала слова (например, \u2581) ?

В продвинутых реализациях BPE пробелы между словами не удаляются, а заменяются специальным символом начала слова — \u2581.

Этот токен создается функцией prepare_word и приклеивается строго в начало каждого слова. Он выполняет две важнейшие задачи:

  1. Позволяет алгоритму BPE на этапе обучения склеивать маркер пробела с первыми буквами слова, если это сочетание встречается очень часто.

  2. Позволяет функции decode безошибочно восстанавливать исходный текст. При декодировании все технические токены начала слова \u2581 просто заменяются на обычные пробелы, возвращая тексту человеческий вид.

Note

Какова роль нейросетевой модели в этом задании?

В рамках этой задачи мы симулируем работу системы машинного перевода. Нейросетевая модель уже перевела текст за нас. Закодированные предсказания модели на английском языке находятся в файле en_encoded.txt. Текст в нём уже переведен, но представлен числовыми ID.

Ваша задача: загрузить готовый словарь vocab.json, декодировать числовой английский перевод в понятные слова и оценить качество работы нейросетевой модели с помощью метрики BLEU относительно идеального человеческого перевода из файла en_raw.txt.

Полезные ссылки