Quora - потрясающая платформа, где интернет-компании задают вопросы, на них отвечают, следят за ними и редактируют их. Это дает людям возможность учиться друг у друга и лучше понимать мир. Около 100 миллионов человек посещают Quora каждый месяц, поэтому неудивительно, что многие люди задают похожие вопросы. Не лучше для qurara просить своих последователей написать ответ на тот же вопрос. Так что будет лучше, если есть система, способная определять, что новый вопрос похож на вопросы, на которые уже были даны ответы.

Итак, наша постановка задачи состоит в том, чтобы предсказать, будут ли пара вопросов дублироваться или нет. Мы будем использовать различные методы машинного обучения, чтобы найти решение этой проблемы. Этот блог не является полным пошаговым руководством по коду, но я объясню различные подходы, которые я использовал для решения проблемы. Вы можете посмотреть мой код в моем репозитории Github.

Некоторые бизнес-ограничения

  • Цена ошибочной классификации может быть очень высокой. то есть, если пользователь задал конкретный вопрос, и если мы дадим другой ответ, то это нехорошо. Это повлияет на бизнес. Это самое важное ограничение.
  • Мы хотим, чтобы вероятность пары вопросов дублировалась, чтобы вы могли выбрать любой порог выбора. Поэтому в зависимости от варианта использования мы можем его изменить.
  • У нас нет требований к задержке.
  • Отчасти важна интерпретируемость. т.е. мы не хотим, чтобы пользователи знали, почему пара вопросов дублируется. Но если мы знаем, будет лучше.

Метрика производительности

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

Потеря журнала - это не что иное, как отрицательное значение журнала произведения вероятностей.

Исследовательский анализ данных

У нас есть около 404290 точек данных и 6 столбцов. 6 столбцов / функций:

  • id: уникальный идентификатор для пары вопросов.
  • qid1: id первого вопроса.
  • qid2: id второго вопроса
  • вопрос1: первый вопрос
  • вопрос2: второй вопрос
  • is_duplicate: дублируются ли оба.

Как и вначале, я проверил пропущенные значения. Я обнаружил, что в трех строках отсутствуют значения (в question1 и question2). Так что я отбросил эти строки. Затем я проверил наличие повторяющихся строк. Но таких ссор не было.

Анализ целевой переменной

Очевидно, что у нас несбалансированные данные, и количество повторяющихся вопросов превышает количество недубликатов.

Анализ по вопросам

Проанализировав ряд вопросов, я пришел к следующему наблюдению:

Total number of unique questions is 537929
Number of questions that repeated more than 1 time is 111778 which is 20.779322178205675%
The maximum number of times a question occured is 157

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

Теперь у нас есть широкое понимание данных. Затем я создал несколько основных функций перед предварительной обработкой данных.

Базовая разработка функций

Я создал следующие функции:

  • freq_qid1 = частота появления qid1. количество раз, когда встречается вопрос1.
  • freq_qid2 = частота появления qid2. количество раз, когда встречается вопрос1.
  • q1len = длина q1
  • q2len = длина q2
  • q1_n_words = Количество слов в вопросе 1
  • q2_n_words = Количество слов в вопросе 2
  • word_Common = (Количество общих уникальных слов в Вопросе 1 и Вопросе 2).
  • word_Total = (общее количество слов в вопросе 1 + общее количество слов в вопросе 2)
  • word_share = (word_common) / (word_Total)
  • freq_q1 + freq_q2 = сумма частот qid1 и qid2
  • freq_q1-freq_q2 = абсолютная разница частот qid1 и qid2

Анализ технических характеристик

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

Мы видим, что common_words не хватает информации, разделяющей классы. Графики истории word_common повторяющихся и неповторяющихся вопросов перекрываются. Невозможно получить много информации, так как большая часть PDF-файлов перекрывается.

Продвинутая разработка функций

Теперь мы создадим некоторые расширенные функции, используя имеющиеся у нас данные. Перед этим очистим наши текстовые данные. В рамках предварительной обработки текста я удалил игнорируемые слова, знаки препинания, специальные символы, такие как «₹», «$», «€» и т. Д., А также применил стемминг для большей обобщенности. Затем я разработал следующие функции.

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

  • cwc_min: отношение common_word_count к минимальной длине количества слов в Q1 и Q2
    cwc_min = common_word_count / (min (len (q1_words), len (q2_words))
  • cwc_max: отношение common_word_count к максимальной длине количества слов в Q1 и Q2
    cwc_max = common_word_count / (max (len (q1_words), len (q2_words))
  • csc_min: отношение common_stop_count к минимальной длине стоп-счетчиков Q1 и Q2
    csc_min = common_stop_count / (min (len (q1_stops), len (q2_stops))
  • csc_max: отношение common_stop_count к максимальной длине стоп-счетчика Q1 и Q2
    csc_max = common_stop_count / (max (len (q1_stops), len (q2_stops))
  • ctc_min: отношение common_token_count к минимальной длине количества токенов Q1 и Q2
    ctc_min = common_token_count / (min (len (q1_tokens), len (q2_tokens))
  • ctc_max: отношение common_token_count к максимальной длине количества токенов Q1 и Q2
    ctc_max = common_token_count / (max (len (q1_tokens), len (q2_tokens))
  • last_word_eq: проверьте, совпадает ли последнее слово обоих вопросов
    last_word_eq = int (q1_tokens [-1] == q2_tokens [-1])
  • first_word_eq: проверьте, совпадает ли первое слово обоих вопросов
    first_word_eq = int (q1_tokens [0] == q2_tokens [0])
  • abs_len_diff: абс. разница в длине
    abs_len_diff = abs (len (q1_tokens) - len (q2_tokens))
  • mean_len: средняя длина токена для обоих вопросов
    mean_len = (len (q1_tokens) + len (q2_tokens)) / 2
  • fuzz_ratio. А вот и самое интересное. Коэффициент нечеткости зависит от расстояния Левенштейна. Интуитивно говоря, если соответствующие правки, требуемые от одного сообщения, чтобы стать другим, велики, коэффициент нечеткости будет небольшим. т.е. коэффициент нечеткости будет одинаковым для большинства похожих слов.
eg: s1 = “mumbai is a great place”   s2 = "mumbai is a nice place"
fuzz ratio = 91
  • fuzz_partial_ratio: в некоторых случаях коэффициент нечеткости не может решить проблему.
fuzz.ratio("YANKEES", "NEW YORK YANKEES") ⇒ 60
fuzz.ratio("NEW YORK METS", "NEW YORK YANKEES") ⇒ 75

И s1, и s2 означают одно и то же. Но их коэффициент пуха может быть меньше. Таким образом, мы найдем соотношение для неполных предложений, и оно будет высоким. В таком случае он известен как частичный коэффициент нечеткости.

fuzz.partial_ratio("YANKEES", "NEW YORK YANKEES") ⇒ 60
  • token_sort_ratio: в некоторых других случаях даже частичное соотношение фаззинга не сработает.

Например:

fuzz.partial_ratio("MI vs RCB","RCB vs MI") ⇒ 72

На самом деле оба предложения имеют одинаковое значение. Но коэффициент пушистости дает невысокий результат. Таким образом, лучший подход - отсортировать токены, а затем применить коэффициент нечеткости.

fuzz.token_sort_ratio("MI vs RCB","RCB vs MI") ⇒ 100
  • token_set_ratio: существует еще один тип коэффициента нечеткости, который помогает даже в тех случаях, когда все вышеперечисленное не работает. Это коэффициент набора токенов.

Для этого мы должны сначала найти следующее:

t0 - ›найти слова пересечения sentance1 и sentance2 и отсортировать их.

t1- ›t0 + остальные токены в sendance1

t2- ›t0 + остальные токены в sendance2

tocken_set_ratio = max (fuzz_ratio (to, t1), fuzz_ratio (t1, t2), fuzz_ratio (t0, t2))

  • longest_substr_ratio: отношение длины самой длинной общей подстроки к минимальной длине количества токенов Q1 и Q2.
s1-> hai, today is a good day
s2-> No, today is a bad day

Здесь самая длинная общая подстрока - «today is a». Итак, у нас longest_substring_ratio = 3 / min (6,6) = 0,5
longest_substr_ratio = len (самая длинная общая подстрока) / (min (len (q1_tokens), len (q2_tokens))

Больше анализа данных

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

Чем крупнее слова, тем больше частота повторения в корпусе. Мы видим, что такие слова, как «Дональд Трамп», «рупия», лучше всего повторяются в случае повторяющихся предложений, в то время как такие слова, как «разница», «Индия», «использование» и т. Д., Чаще всего повторяются в недублирующих предложениях.

Парный график для «ctc_min», «cwc_min», «csc_min», «token_sort_ratio»

Из парного графика мы видим, что есть некоторая полезная информация во всех функциях, которые разделяют повторяющиеся и не повторяющиеся предложения. Из которых коэффициент сортировки токенов и ctc min работают лучше, чем другие.

Абсолютная разница в количестве слов между вопросами

Мы видим, что большинство вопросов различаются одним словом. Есть очень небольшое количество вопросов с огромной разницей.

ЦНЭ Визуализация

Затем я попытался визуализировать данные в более низком измерении. Я произвольно отобрал 5000 точек данных и использовал TSNE для визуализации более низкого измерения. Я использовал только те функции, которые мы недавно разработали, чтобы увидеть их влияние на анализ. Мы видим, что в некоторых регионах классы хорошо разделены. Таким образом, мы можем сказать, что теперь в нашей модели имеется много информации для проведения хорошей классификации.

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

Тренировочный тестовый сплит

Мы сделали сплит 70:30. то есть мы разместили 70% точек данных для обучения, а остальные 30% - для тестирования.

Векторизация текстовых данных

Перед созданием моделей с использованием данных мы должны векторизовать текстовые данные. Для этого мы использовали 2 подхода

  • Векторизация TFIDF
  • Взвешенная векторизация по TFIDF

Мы сохранили отдельные файлы после объединения этих векторов с созданными нами исходными объектами.

Модели машинного обучения

Давайте перейдем к самой интересной части этого блога - созданию моделей машинного обучения. Теперь у нас есть два фрейма данных для обучения - один с использованием tfidf, а другой - с взвешенными векторами перчаток tfidf.

Логистическая регрессия

Возможности TFIDF:

При обучении логистической регрессии данных TFIDF мы получаем потерю журнала около 0,43 для поезда и 0,53 для теста. Наши матрицы точности и отзывчивости выглядят следующим образом:

Каждый должен знать, как интерпретировать нормальную матрицу путаницы. Я не буду вдаваться в его объяснение. Давайте посмотрим, как мы можем интерпретировать матрицу точности. На приведенном выше рисунке вторая - это матрица точности.

Интуитивно точность означает из всех точек, предсказанных как положительные, сколько на самом деле положительных. Здесь мы видим, что из всех меток, прогнозируемых как класс 1, 24,6% относятся к классу 2, а остальные 75,4% относятся к классу 1. Аналогично, для всех точек, прогнозируемых как класс 2, 69,2% относятся к классу 2 и 30,02. % принадлежат к классу 1. Здесь точность для класса 1 составляет 0,754, а для класса 2 - 0,30.

Третья матрица, которая у нас есть, - это матрица отзыва. Интуитивно вспомнить означает, сколько из всех точек, принадлежащих классу, предсказано правильно. В матрице отзыва из всех меток, относящихся к классу 1, 86,5% были предсказаны как класс 1, а 13,5% - как класс 2. аналогично, Из всех исходных точек, принадлежащих к классу 2, 59,9% относятся к классу 2, а остальные - к классу 1.

ПЕРЧАТКА с весами TFIDF:

После настройки гиперпараметров мы получаем потерю журнала около 0,39 для теста и 0,38 для поезда.

Из обеих матриц отзыва мы видим, что у нас низкий показатель отзыва. Давайте посмотрим, как это работает с линейной SVM.

Линейный SVM

Возможности TFIDF:

Потеря бревна поезда: 0,3851

Потеря журнала испытаний: 0,3942

Матрица путаницы:

ПЕРЧАТКА с весами TFIDF:

Потеря бревна поезда: 0,3876

Потеря журнала испытаний: 0,395

Матрица путаницы:

Мы видим, что и для логистической регрессии, и для линейной SVM мы получаем низкую ценность отзыва. Но обе модели не особо переоснащены. Итак, я подумал о проблеме с высокой предвзятостью. Если мы выберем некоторые методы повышения, мы сможем преодолеть это и получить лучшее значение отзыва. С этой целью я опробовал XGBoost на функциях взвешенных перчаток tfidf и tfidf.

XGBOOST

Возможности TFIDF:

Потеря бревна поезда: 0,457

Потеря журнала испытаний: 0,516

Матрица путаницы:

Особенности TFIDF Weighted GLOVE:

Потеря бревна поезда: 0,183

Потеря журнала испытаний: 0,32

Матрица путаницы:

Мы видим, что с помощью Xgboost мы немного улучшили запоминание обоих классов. Из всех этих векторов xgboost на перчатках работает лучше.

Для полного ознакомления с кодом вы можете посетить мой репозиторий GitHub.

использованная литература

  • Прикладной курс