Лабораторная работа №1. Выделение ключевых слов

Дано

  1. Текст сказки Ганса Христиана Андерсена “Дюймовочка” на русском языке (assets/Дюймовочка.txt).

  2. Список стоп-слов (assets/stop_words.txt).

  3. Словарь с Inverse Document Frequency значениями (assets/IDF.json).

  4. Словарь с корпусными частотами (assets/corpus_frequencies.json).

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

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

  1. Создайте форк репозитория.

  2. Установите необходимые инструменты для работы.

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

  4. Закоммитьте изменения и создайте Pull request.

Для этого реализуйте функции в модуле 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. Очистить и токенизировать текст

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

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

  • знаков препинания,

  • букв верхнего регистра,

  • лишних пробелов.

Например, строка 'Hey! How are you?' должна быть токенизирована следующим образом: ['hey', 'how', 'are', 'you'].

Если на вход подаются некорректные значения, возвращается None. Некорректным значением считается значение не того типа, который ожидается.

Интерфейс:

def clean_and_tokenize(text: str) -> list[str]:
   pass

Шаг 2. Удалить стоп-слова

Important

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

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

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

Продемонстрируйте выделяемые токены из текста про Дюймовочку без стоп-слов в файле start.py. Текст сохранен в переменную target_text.

Интерфейс:

def remove_stop_words(tokens: list[str], stop_words: list[str]) -> list[str]:
   pass

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

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

Функция принимает список токенов. Возвращает словарь вида {токен: количество вхождений}. Так, из последовательности токенов ['hey', 'how', 'are', 'you'] должен получиться следующий словарь частот:

{'hey': 1,
 'how': 1,
 'are': 1,
 'you': 1}

Интерфейс:

def calculate_frequencies(tokens: list[str]) -> dict[str, int]:
   pass

Шаг 4. Получить список первых N по популярности слов

Important

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

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

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

Интерфейс:

def get_top_n(frequencies: dict[str, int | float], top: int) -> list[str]:
    pass

Шаг 5. Рассчитать значения Term Frequency для каждого из слов в тексте

Term Frequency - это отношение числа вхождений некоторого слова к общему числу слов документа. Таким образом оценивается важность слова в пределах отдельного документа.

Формула:

\[tf(t, d) = \frac{n_t}{N_d}\]

Где:

  • \(n_t\) — количество вхождений слова \(t\) в документ \(d\);

  • \(N_d\) — общее количество слов в документе \(d\).

Реализуйте функцию lab_1_keywords_tfidf.main.calculate_tf(). Она должны будет рассчитывать Term Frequency для токенов. Функция принимает на вход частотный словарь. Возвращаемым значением функции должен быть словарь, в котором ключами являются токены, а значениями - соответствующие им значения TF.

Интерфейс:

def calculate_tf(frequencies: dict[str, int]) -> dict[str, float]:
    pass

Шаг 6. Рассчитать значения TF-IDF для каждого из слов в тексте

TF-IDF — статистическая мера, используемая для оценки важности слова в контексте документа, являющегося частью коллекции документов или корпуса. Она состоит из двух множителей — Term Frequency (TF) и Inverse Document Frequency (IDF).

Формула:

\[TFIDF(t, d, D) = TF(t, d) \times IDF(t, D)\]

Множитель Inverse Document Frequency — это инверсия частоты, с которой некоторое слово встречается в документах коллекции:

\[idf(t, D) = \log \left(\frac{|D|}{|\{d_i \in D : t \in d_i\}| + 1}\right)\]

Здесь:

  • числитель (\(|D|\)) — общее количество документов в коллекции;

  • знаменатель (\(|\{d_i \in D : t \in d_i\}|\)) — количество таких документов, в которых встречается слово \(t\).

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

Note

Если слово не найдено в словаре IDF, используйте формулу выше, подставив 0 вместо \(|\{d_i \in D : t \in d_i\}|\) и 47 вместо \(|D|\) (именно столько текстов содержится в коллекции сказок Г. Х. Андерсена).

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

Функция принимает на вход два словаря: словарь со значениями TF и словарь со значениями IDF. Возвращает словарь формата {слово: значение TF-IDF}. Если на вход подаются некорректные значения, возвращается None.

Интерфейс:

def calculate_tfidf(term_freq: dict[str, float], idf: dict[str, float]) -> dict[str, float]:
    pass

Шаг 7. Продемонстрировать топ-10 ключевых слов согласно TF-IDF

Important

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

Выведите 10 наиболее важных слов согласно значению TF-IDF с помощью функции print. Напомним, чем выше значение TF-IDF, тем более важным считается слово.

В этом задании обязательно использовать функцию lab_1_keywords_tfidf.main.get_top_n().

Шаг 8. Рассчитать ожидаемую частотность слов

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

Формулировка гипотез:

  • \(H_0:\) частотность слова \(t\) в документе \(d\) статистически значимо превышает его частотность в коллекции \(D\)

  • \(H_1:\) частотность слова \(t\) в документе \(d\) не превышает его частотность в коллекции \(D\)

Для вычисления ожидаемой частотности необходимо найти следующие значения:

d

D

t

j

k

~t

l

m

где:

  • \(j\) — количество вхождений слова \(t\) в документ \(d\)

  • \(k\) — количество вхождений слова \(t\) во все документы коллекции \(D\)

  • \(l\) — количество вхождений всех слов, кроме \(t\), в документ \(d\)

  • \(m\) — количество вхождений всех слов, кроме \(t\), в коллекцию документов \(D\)

Ожидаемая частотность слова \(t\) в документе \(d\) рассчитывается по формуле:

\[Expected = \frac{(j + k) \times (j + l)}{j + k + l + m}\]

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

Интерфейс:

def calculate_expected_frequency(doc_freqs: dict[str, int], corpus_freqs: dict[str, int]) -> dict[str, float]:
    pass

Частотный словарь для коллекции сказок Г. Х. Андерсена загружен в переменной corpus_freqs в файле start.py.

Шаг 9. Рассчитать значения хи-квадрат

Для оценки того, насколько наблюдаемая частотность превышает ожидаемую, используется статистический критерий \(\chi^2\). Формула для каждого слова:

\[\chi^2 = \frac{(observed - expected)^2}{expected}\]

где:

  • observed — наблюдаемое количество вхождений слова в документе

  • expected — ожидаемая частотность слова (рассчитанная в Шаге 8)

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

Интерфейс:

def calculate_chi_values(expected: dict[str, float], observed: dict[str, int]) -> dict[str, float]:
    pass

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

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

Статистически значимыми словами считаются те, для которых значение \(\chi^2\) превышает критическое значение. Выбор критического значения зависит от желаемого уровня значимости (вероятность ошибочно назвать неключевое слово ключевым):

CRITERION = {0.05: 3.842, 0.01: 6.635, 0.001: 10.828}

Реализуйте функцию lab_1_keywords_tfidf.main.extract_significant_words(). Функция принимает словарь значений \(\chi^2\) и уровень значимости alpha. Возвращает словарь, содержащий только статистически значимые слова. Если на вход подаются некорректные значения, возвращается None.

Интерфейс:

def extract_significant_words(chi_values: dict[str, float], alpha: float) -> dict[str, float]:
    pass

Шаг 11. Вывести топ-10 ключевых слов согласно \(\chi^2\)

Important

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

Чем выше значение \(\chi^2\) для токена, тем более значимым считается слово. Выведите 10 самых важных слов с точки зрения статистики. Ранжирование токенов выполняется с помощью функции lab_1_keywords_tfidf.main.get_top_n().

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