Лабораторная работа №1. Определение языка текста на основе частотного словаря
Full API
Дано
Три текста на английском (
assets/texts/en.txt
), немецком (assets/texts/de.txt
) и неизвестном (assets/texts/unknown.txt
) языках.Языковые профили немецкого, английского, испанского, французского, итальянского, русского и турецкого языков (
assets/profiles
). Необходимо определить, на каком языке написан неизвестный текст, анализируя встречаемость букв в каждом из языков.
Что надо сделать
Шаг 0. Начать работу над лабораторной (вместе с преподавателем на практике)
Создайте форк репозитория.
Установите необходимые инструменты для работы.
Измените файлы
main.py
иstart.py
.Закоммитьте изменения и создайте Pull request.
Important
В файле start.py
вы должны написать код, определяющий
язык неизвестного текста.
Для этого реализуйте функции в модуле main.py
и импортируйте их в
start.py
. Весь код, выполняющий детектирование языка, должен быть
выполнен в функции main
в файле start.py
:
def main() -> None:
pass
Вызов функции в файле start.py
:
if __name__ == '__main__':
main()
В рамках данной лабораторной работы нельзя использовать модули collections, itertools, а также сторонние модули.
Обратите внимание, что желаемую оценку необходимо указать в файле
settings.json
в поле target_score
. Возможные значения: 0, 4, 6, 8, 10.
Чем большее значение выставлено, тем больше тестов будет запущено.
Шаг 1. Токенизировать текст
Important
Выполнение Шага 1 соответствует 4 баллам.
Реализуйте функцию lab_1_classify_by_unigrams.main.tokenize()
.
Например, строка 'Hey! How are you?'
должна быть токенизирована
следующим образом:
['h', 'e', 'y', 'h', 'o', 'w', 'a', 'r', 'e', 'y', 'o', 'u']
.
Продемонстрируйте выделяемые токены из текста на английском языке в
файле start.py
. Текст на английском языке сохранен в переменную
en_text
.
Шаг 2. Получить частотный словарь по заданному тексту
Реализуйте функцию
lab_1_classify_by_unigrams.main.calculate_frequencies()
.
Под относительной частотой подразумевается отношение количества вхождений токена к общему числу токенов.
Так, из последовательности токенов
['h', 'e', 'y', 'h', 'o', 'w', 'a', 'r', 'e', 'y', 'o', 'u']
должен получиться следующий словарь частот:
{'h': 0.16666666666666666,
'e': 0.16666666666666666,
'y': 0.16666666666666666,
'o': 0.16666666666666666,
'w': 0.08333333333333333,
'a': 0.08333333333333333,
'r': 0.08333333333333333,
'u': 0.08333333333333333}
Шаг 3. Создать профиль конкретного языка
Important
Выполнение Шагов 1-3 соответствует 6 баллам.
Профиль языка – это структура с информацией о конкретном языке. В настоящей лабораторной работе профиль языка состоит из названия языка и частотного словаря.
В дальнейших лабораторных работах вы будете работать с другими языковыми профилями. Пример языковых профилей вы можете найти в следующем проекте. Несмотря на то что данные профили содержат информацию о n-граммах, с которыми мы познакомимся позднее, структура этих профилей аналогична.
Пример языкового профиля, который требуется в настоящей лабораторной работе:
{
"name": "en",
"freq": {
"g": 0.8,
"t": 0.2
}
}
Здесь ключу "freq"
соответствует частотный словарь, ключу "name"
– название языка.
В данной лабораторной работе языковой профиль обязательно
представляет собой словарь, который содержит два ключа – "freq"
и
"name"
.
Для создания профиля языка реализуйте функцию
lab_1_classify_by_unigrams.main.create_language_profile()
.
Используйте функцию lab_1_classify_by_unigrams.main.tokenize()
для токенизации и функцию
lab_1_classify_by_unigrams.main.calculate_frequencies()
для получения частотного словаря.
Продемонстрируйте создание языкового профиля для английского языка в
файле start.py
.
Шаг 4. Рассчитать метрику MSE
В дальнейшем для определения близости двух языковых профилей нам
понадобится метрика среднеквадратичной ошибки (MSE
, Mean Squared
Error). Для начала
рассмотрим эту метрику безотносительно применения к задаче детекции
языка.
Значение MSE
рассчитывается по формуле
\(MSE = \frac{\sum (y_{i} - p_{i})^{2}}{n}\), где:
y
- истинное значение;p
- предсказанное значение;n
- количество значений.
Обратите внимание, что количество истинных значений y
и количество предсказанных значений p
совпадает и равно n
.
Таким образом, метрика MSE
- не что иное, как среднее квадратов
разности между истинными значениями и предсказанными значениями. Чем это
значение меньше, тем ближе предсказанные значения к истинным.
Для того чтобы рассчитать метрику MSE
,
реализуйте функцию lab_1_classify_by_unigrams.main.calculate_mse()
.
Шаг 5. Сравнить два языковых профиля
Чтобы сравнить языковые профили необходимо рассчитать значение метрики
MSE
, которая определяет различие между двумя языками.
Для этого нужно выделить все токены, встречающиеся в двух языковых профилях, а также сопоставить им частотность в каждом из языков. Иными словами, мы находим объединение множества токенов в первом языке со множеством токенов во втором языке. Далее, для каждого из токенов находим его встречаемость в каждом из множеств.
Для примера рассмотрим два языковых профиля:
profile_1 = {
'name': 'lang1',
'freq': {'a': 0.5, 'b': 0.5}
}
profile_2 = {
'name': 'lang2',
'freq': {'b': 0.5, 'c': 0.5}
}
В данных профилях встречаются следующие символы: a
, b
, c
.
При этом в профиле первого языка их встречаемость равна
[0.5, 0.5, 0]
, а в профиле второго языка - [0, 0.5, 0.5]
.
Приняв встречаемость символов в первом языке за истинные значения и
встречаемость символов во втором языке за предсказанные, мы можем
рассчитать разницу профилей по метрике MSE
. Ее значение будет равно
0.167
(с округлением до третьего знака).
Note
Что изменится, если сделать наоборот и принять за истинные значения встречаемость токенов во втором языке и за предсказанные - в первом? Почему?
Реализуйте функцию сравнения двух языковых профилей
lab_1_classify_by_unigrams.main.compare_profiles()
.
Для расчета метрики MSE
нужно обратиться к функции
lab_1_classify_by_unigrams.main.calculate_mse()
.
Шаг 6. Определить язык неизвестного текста
Important
Выполнение Шагов 1-6 соответствует 8 баллам.
Чтобы определить язык неизвестного текста,
реализуйте функцию lab_1_classify_by_unigrams.main.detect_language()
.
Она устанавливает язык текста на основе метрики MSE
и
возвращает название языка с ее наименьшим значением. Название языка
находится в языковом профиле. Если у двух языков одинаковое значение
метрики, отсортируйте названия языков в алфавитном порядке и возьмите
первое.
Для нахождения метрики MSE
нужно использовать функцию
lab_1_classify_by_unigrams.main.compare_profiles()
.
В файле start.py
определите, к какому языку ближе текст на
неизвестном языке: к английскому или к немецкому. Текст на немецком
языке сохранен в переменной de_text
. Текст на неизвестном языке
сохранен в переменной unknown_text
.
Шаг 7. Загрузить языковой профиль
Для определения языка может быть недостаточно двух языковых профилей. На самом деле в данной задаче может использоваться произвольное количество языковых профилей (например, 6).
Для дальнейшей работы нам потребуется возможность загружать языковой
профиль из файла с расширением json
. Узнать больше об этом типе
файлов можно здесь.
В папке assets
для вас сохранены несколько языковых
профилей. Для корректной работы необходимо сформировать путь к каждому
из них в формате assets/profiles/<filename>.json
. Например, путь к
испанскому языковому профилю должен выглядеть так:
assets/profiles/es.json
. Сохраните
список таких путей в переменную в файле start.py
.
Для чтения и сохранения такого типа файлов часто используется
стандартный модуль json
. Изучить его документацию можно по
ссылке.
Реализуйте функцию чтения языкового профиля из файла
lab_1_classify_by_unigrams.main.load_profile()
.
При этом функция должна только читать файл,
никакой дополнительной обработки не подразумевается.
Пример вызова функции:
language_profile = load_profile(filename)
Шаг 8. Обработать языковой профиль
Языковые профили могут выглядеть по-разному. Вы можете заметить, что
в папке assets
они имеют особый формат:
в них содержатся токены не только из одного символа, но и из нескольких,
а также присутствует дополнительный ключ n_words
.
На данном шаге Вам нужно привести языковой профиль к нашему единому формату.
Для этого реализуйте функцию
lab_1_classify_by_unigrams.main.preprocess_profile()
,
Напоминаем, что языковой профиль должен содержать
только два ключа: name
и freq
. По ключу freq
содержится
частотный словарь, ключами которого выступают униграммы в нижнем
регистре, а значениями - относительная встречаемость токена.
Например, у нас есть следующий необработанный языковой профиль:
profile_raw = {
'name': 'lang1',
'freq': {
'ab': 3,
'4c': 2,
'a': 1
'F': 2,
'c&': 1,
'abc': 7
},
'n_words': [3, 6, 7]
}
Для приведения языкового профиля к нужному формату нужно:
Выбрать униграммы, состоящие из букв, из представленного набора токенов (ключ
freq
).Привести их к нижнему регистру.
Посчитать их относительную частоту.
Поле n_words
содержит в себе список из трех
чисел, которые обозначают количество униграмм, биграмм и триграмм,
соответственно. Для подсчета относительной частоты токенов используйте
первое из трех чисел поля n_words
, при этом ключа n_words
в
обработанном профиле быть не должно. Ключ name
дополнительно обрабатывать не нужно.
Таким образом, для примера выше должен получится следующий обработанный языковой профиль:
profile_raw = {
'name': 'lang1',
'freq': {
'a': 0.33333333333,
'f': 0.66666666666,
},
}
Пример вызова функции:
processed_profile = preprocess_profile(profile_raw)
Шаг 9. Собрать языковые профили
Поскольку нам предстоит сравнить целый ряд языковых профилей, нужно загрузить и предобработать сразу несколько профилей.
Для этого реализуйте функцию
lab_1_classify_by_unigrams.main.collect_profiles()
,
которая должна вызывать lab_1_classify_by_unigrams.main.load_profile()
и lab_1_classify_by_unigrams.main.preprocess_profile()
.
Пример вызова функции:
collected_profiles = collect_profiles(paths_to_profiles)
Шаг 10. Определить язык неизвестного текста
Теперь мы готовы определить язык неизвестного текста, рассматривая сразу несколько возможных вариантов.
Для этого реализуйте функцию
lab_1_classify_by_unigrams.main.detect_language_advanced()
.
Она возвращает отсортированный список кортежей вида
[('lang1', score), ('lang2', score)]
, где первым элементом кортежа
выступает название языка, а вторым - значение MSE
. Длина списка
соответствует количеству переданных профилей известных языков.
Сортировка предполагается от меньшего значения метрики к большему. Языки
с совпадающим значением MSE
нужно упорядочить
лексикографически.
Note
Почему в данной задаче лучше сортировать от меньшего к большему, а не наоборот?
Шаг 11. Сформировать отчет
Important
Выполнение Шагов 1-11 соответствует 10 баллам.
Теперь, когда мы можем сравнить целый ряд языков, нужно сформировать понятный отчет.
Для этого реализуйте функцию
lab_1_classify_by_unigrams.main.print_report()
,
которая выводит отчет в следующей форме:
<lang1>: MSE <score>
<lang2>: MSE <score>
Обратите внимание, что score
необходимо округлить до пяти знаков
после запятой. Например, если был получен результат
[('en', 0.00013213), ('de', 0.00016231), ('fr', 0.00010123)]
, то при
вызове функции в консоль будет выведен следующий отчет:
en: MSE 0.00013
de: MSE 0.00016
fr: MSE 0.0001
Для округления можно использовать форматирование строки:
f'{score:.5f}'
.
Продемонстрируйте детекцию неизвестного языка путём сравнения с
языковыми профилями английского, французского, итальянского, русского,
испанского и турецкого языков в файле start.py
. Для вывода отчета в
консоль вызовите функцию print_report
.