Как простые NLP модели видят слова? | NLP | Пишем свой TF-IDF

Как модели видят наш текст?

Когда начинаешь погружаться в сферу NLP, сразу задумываешься, как модели представляют себе наш текст/наши слова? Ведь не логично бы звучало, если модель обрабатывала наши слова, как обычную последовательность букв. Это было бы не удобно и не понятно(как проводить операции со словами?).

Есть разные методы преобразования слов. Один из самых известных для не самых сложных моделей: TF-IDF.

Как работает TF-IDF?

TF-IDF(Term Frequency-Inverse Document Frequency) — это метод, который преобразует слова в числовые векторы, что делает их более понятными для моделей машинного обучения.

Причем числовые векторы здесь содержат TF-IDF значения, а не просто любые числа.

Значения TF-IDF пытаются показать насколько важно слово для нас в этом документе (наборе текстовых данных).

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

Вот формулы подсчета TF-IDF:

TF(t, d) = \frac{n_t}{\sum_{k} n_k}

t — наше слово
d — наш документ

В формуле мы делим число вхождений нашего слова в данный документ на общее число слов в документе.

TF(Term Frequency) — обозначает частоту нашего слова в документе.

IDF(t, D) = log(\frac{|D|}{|\{ d_i\in D | t \in d_i \}|})

t — наше слово
D — корпус документов (список всех текстовых данных)

В формуле мы берем логарифм от деления общего числа всех документов (длина D) на число документов из корпуса D, которые содержат наше слово.

Логарифм используется для сглаживания значений.

IDF(Inverse Document Frequency) — обозначает обратную частоту, с которой наше слово встречается в документах корпуса.

Умножая TF и IDF, получаем формулу TF-IDF:

TF\_IDF(t, d, D) = TF(t, d) * IDF(t, D)

Большой вес в TF-IDF получат слова с высокой частотой в пределах конкретного документа и с низкой частотой употреблений в других документах.

План написания своего TF-IDF.

Для закрепления и лучшего понимания напишем TF-IDF сами.

План:

  • договоримся в каком формате будут приходить текстовые данные

  • посчитаем TF-IDF

  • вернем нужную информацию

Данные будем получать в формате списка документов (в нашем случае список предложений).

Возвращать будет матрицу значений TF-IDF для каждого слова/документ.

Код

Сначала импортируем нужные библиотеки.

import numpy as np
import pandas as pd

Получим данные, приведем их в нужный формат — каждый новый документ (у нас предложение) будет с новой строки, поэтому разделим их по »\n», получим список.
(Текст взяли из случайной статьи на Википедиа.)

with open('data.txt', 'r') as file:
    content = file.read()
    print(content)

data = content.split("\\n")

Сейчас самое сложное — напишем функцию tf-idf.

Разобьём ее на четыре части:

#1
vocab = [] # cоздаем список всех слов.
for text in texts: # проходимся по каждому документу(преложению)
  words = text.split() # разбиваем его на слова
  for word in words: # проходимся по каждому слову
    if not word in vocab: # если такого слова еще нет в нашем списке
      vocab.append(word) # добавлем новое слово

Так мы заполняем список уникальными словами.

#2

# наш словарь TF в формате: "слово": [tf_в_документе1, tf_в_документе2, tf_в_документеN]
tf_dict = {}
for word in vocab: # проходимся по каждому слову из списка всех слов
  tf_dict_this_word = [] # список всех tf(для каждого документа ) для данного слова
  for text in texts: # проходимся по всем документам
    if word in text: # если слово в документе
      count_word = text.split().count(word) # считаем сколько его в этом документе
      
      # считаем tf для данного документа и добавляем в список всех tf для этого слова
      # tf = кол-во слова в документе / длина документа 
      tf_dict_this_word.append(count_word/len(text.split()))
    else:
      tf_dict_this_word.append(0) # если слова в этом документе нет - добавляем 0
  tf_dict[word] = tf_dict_this_word # добавлем новую запись в наш словарь в нужно формате

У нас уже есть таблица TF для всех слов ко всем документам.

#3
idf_dict = {} # словарь IDF в формате: "слово" : его_idf
for word in vocab: # проходимся по всем словам

  # считаем во скольки документах есть данное слово
  count_word = sum(1 for text in texts if word in text.split())

  # считаем idf и записывем в словарь
  # idf = log( кол-во всех документов / кол-во документов содержащих наше слово )
  idf_dict[word] = np.log(len(texts) / count_word)

IDF посчитали, осталось перемножить и вернуть.

#4

# словарь посчитанных tf-idf( "слово" : [tf-idf_в_док1, tf-idf_в_док2, tf-idf_в_докN] )
tf_idf = {}
for word in vocab: # проходимся по каждому слову

  # по элементно умножаем idf слова на список tf для этого слова
  # tf-idf = tf * idf
  tf_idf[word] = np.array(tf_dict[word])*idf_dict[word]

Собираем в одну функцию и возвращаем:

def tf_idf(texts: list):

    #vocab
    vocab = []
    for text in texts:
        words = text.split()
        for word in words:
            if not word in vocab:
                vocab.append(word)

    #tf
    tf_dict = {}
    for word in vocab:
        tf_dict_this_word = []
        for text in texts:
            if word in text:
                count_word = text.split().count(word)
                tf_dict_this_word.append(count_word/len(text.split()))
            else:
                tf_dict_this_word.append(0)
        tf_dict[word] = tf_dict_this_word

    #idf
    idf_dict = {}
    for word in vocab:
        count_word = sum(1 for text in texts if word in text.split())
        idf_dict[word] = np.log(len(texts) / count_word)

    #tf-idf
    tf_idf = {}
    for word in vocab:
        tf_idf[word] = np.array(tf_dict[word])*idf_dict[word]

    return tf_idf

Пример использования

(Взяли текст из этой статьи)

Наши текстовые данные(data.txt):

Произведения Альтова исполняли Геннадий Хазанов («Геракл», «Вобла», «Хор в посольстве», «Волки и овцы», «Плавки»), Клара Новикова («Кармен»), Ефим Шифрин («Кающаяся Мария Магдалина», «Покушение», «Блуждающая грудь», «Золушка», «Оазис», «Сексонфу», «Бычара», «Личный пример»), Владимир Винокур («Кувырок судьбы»).
Кроме того, свои произведения исполняет и автор.
Семён Альтов выделяется среди прочих выступающих писателей-юмористов своеобразной исполнительской манерой — Альтов читает свои монологи с непроницаемым и даже мрачным выражением лица, однообразным низким голосом со своеобразным акцентом, даже не улыбаясь.
Манера произношения Альтова пародируется многими эстрадными артистами (Братья Пономаренко, Игорь Христенко и т. д.).

my_tf_idf = tf_idf(data) # используем нашу функцию, передавая наши данные

# для визуала создаем DataFrame и транспонируем его(для красоты, опять же) 
tfidf_table = pd.DataFrame(my_tf_idf).T

print(tfidf_table) # смотрим результат

Видим такую таблицу значений TF-IDF:

cddb997f92477c060acdddd6ca4396f4.png

Ура, у нас получилось — мы написали свой TF-IDF.

Спасибо♥

Ресурсы

Код на Google Colab
Википедиа
Статья, которую мы использовали
TF_IDF
Github
Kaggle
Я на Github
Я на Kaggle
Мой Датасет на Kaggle

© Habrahabr.ru