[Из песочницы] Как различать британскую и американскую литературу с помощью машинного обучения

Однажды мне стало интересно, отличается ли британская и американская литература с точки зрения выбора слов, и если отличается, удастся ли мне обучить классификатор, который бы различал литературные тексты с точки зрения частоты использованных слов. Различать тексты, написанные на разных языках, довольно легко, мощность пересечения множества слов небольшая относительно множества слов в выборке. Классификация текста по категориям «наука», «христианство», «компьютерная графика», «атеизм», — всем известный hello world среди задач по работе с частотностью текста. Передо мной стояла более сложная задача, так как я сравнивала два диалекта одного языка, а тексты не имели общей смысловой направленности.


image


Самый долгий этап машинного обучения — это извлечение данных. Для обучающей выборки я использовала тексты с Project Gutenberg, их можно свободно скачивать. Список американских и британских авторов я выкачала из википедии. Сложность заключалась в том, чтобы найти соответствие по имени автора. На сайте проекта реализован хороший поиск по именам, но парсить сайт запрещено, вместо этого предлагается использовать архив с метаданными. Это означало, что мне нужно решать нетривиальную задачу совпадения имен (Sir Arthur Ignatius Conan Doyle и Doyle, C. — одни и те же люди, а Doyle, M.E. уже нет) и делать это с очень высокой точностью. Вместо этого я, пожертвовав размером выборки в пользу точности и экономии своего времени, выбрала в качестве уникального идентификатора ссылку на википедию автора, которая была включена в некоторые файлы метаданных. Так я получила около 1600 британских текстов и 2500 американских и принялась обучать классификатор.


При всех операциях я использовала пакет sklearn. Первый шаг после сбора и анализа данных — это препроцессинг, для которого я взяла CountVectorizer. Он принимает на вход массив текстовых данных и возвращает вектор признаков. Далее требуется представить признаки в числовом формате, так как классификатор работает с числовыми данными. Для этого нужно вычислить tf-idf, term frequency — inverted document frequency, используя TfidfTransformer.


Короткий пример о том, как это делается и зачем:


Возьмем слово «the» и посчитаем количество вхождений этого слова в тексте А. Пусть у нас будет 100 вхождений, а общее количество слов в документе 1000,


tf("the”) = 100/1000 = 0.1

Далее возьмем слово «sepal» (чашелистик), которое встретилось 50 раз,


tf("sepal”) = 50/1000 = 0.05

Чтобы посчитать inverted document frequency для этих слов, нужно взять логарифм от отношения количества текстов, в которых есть хоть одно вхождение этого слова, к общему количеству текстов. Если всего текстов 10000, и в каждом есть слово «the»,


idf("the”) = log(10000/10000) = 0

tf-idf("the”) = idf("the”) * tf("the”) = 0 * 0.1 = 0

Слово «sepal» гораздо более редкое, и встретилось только в 5 текстах, поэтому


idf("sepal”) = log(10000/5) = 7.6

tf-idf("sepal”) = 7.6 * 0.05 = 0.38

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


Теперь, когда данные представлены как набор признаков, нужно обучить классификатор. Я работаю с текстом, который представлен как разреженные данные, поэтому оптимальным вариантом стал линейный классификатор, хорошо справляющийся с задачами классификации с большим количеством признаков. И CountVectorizer, и TF-IDFTransformer, и SGD я обучала с параметрами по умолчанию. Можно отдельно работать с каждым этапом, но удобнее использовать pipeline:


pipeline = Pipeline([
    ('vect', CountVectorizer()),
    ('tfidf', TfidfTransformer()),
    ('clf', SGDClassifier()),
])

Проанализировав график точности от размера выборки, я заметила сильные колебания точности даже на больших объемах выборки, что свидетельствовало о том, что классификатор очень сильно зависим от конкретной выборки, а значит мало эффективен, и требуются существенные улучшения. Получив список весов классификатора, я заметила часть проблемы: алгоритм переобучался на частых словах типа «of» и «he», которые по сути являются шумом. Эта проблема легко решается удалением подобных слов из признаков, и задается параметром CountVectorizer stop_words = 'english' или вашим собственным списком слов. Еще немного улучшило точность, удаление некоторых популярных общеупотребимых слов. Убрав стоп-слова, я получила улучшение точности до 0.85.


Далее я занялась настройкой параметров c помощью GridSearchCV. Этот способ выявляет лучшую комбинацию параметров для CountVectorizer, TfidfTransformer и SGDClassifier, поэтому это очень долгий процесс, у меня он считался около суток. В итоге получила такой pipeline:


pipeline = Pipeline([
    ('vect', CountVectorizer(stop_words = modifyStopWords(), ngram_range = (1, 1))),
    ('tfidf', TfidfTransformer(use_idf = True, norm = 'l2', smooth_idf = True)),
    ('clf', SGDClassifier(alpha=0.001, fit_intercept = True, n_iter = 10, penalty = 'l2', loss = 'epsilon_insensitive')),
    ])

Итоговая точность — 0.89.


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


Американский текст: dollars, new, york, girl, gray, american, carvel, color, city, ain, long, just, parlor, boston, honor, washington, home, labor, got, finally, maybe, hodder, forever, dorothy, dr


Британский текст: round, sir, lady, london, quite, mr, shall, lord, grey, dear, honour, having, philip, poor, pounds, scrooge, soames, things, sea, man, end, come, colour, illustration, english, learnt


Развлекаясь с классификатором, я получила самых «британских» авторов из числа американцев и самых «американских» британцев (изящный способ рассказать о том, как сильно может ошибаться мой классификатор):


Cамые «британские» американцы:


  • Бёрнетт, Фрэнсис Элиза (родилась в Англии, в возрасте 17 лет переехала в США, поэтому отношу ее к американцам)
  • Генри Джеймс (родился в США, в 33 года эмигрировал в Великобританию)
  • Уистер, Оуэн
  • Мэри Робертс Рейнхарт (как видим, называемая американской Агатой Кристи не без причины)
  • Уильям Мак-Фи (как и Бёрнетт, переехал в США в юном возрасте)

Cамые «американские» британцы:


  • Ридьярд Киплинг (прожил несколько лет в США, попал в список благодаря «American Notes»)
  • Энтони Троллоп (опять же виноваты американские топонимы в его «North America»)
  • Фредерик Марриет (возможно, одного бы названия «Narrative of the Travels and Adventures of Monsieur Violet in California, Sonara, and Western Texas» хватило бы, чтобы запутать классификатор)
  • Арнольд Беннетт (спасибо его «Your United States: Impressions of a first visit»)
  • Филлипс Оппенхейм

А также самых «британских» британцев и «американских» американцев (потому что классификатор все же хорош).


Aмериканцы:


  • Франсис Хопкинсон Смит
  • Гэмлин Гарленд
  • Джордж Эйд
  • Чарльз Дадли Уорнер
  • Марк Твен

Британцы:


  • Джордж Мередит
  • Сэмюэл Ричардсон
  • Джон Голсуорси
  • Гилберт Кит Честертон
  • Энтони Троллоп

На попытку сделать такой классификатор меня подтолкнул твит @TragicAllyHere:


I would love to be British. Drinking my leaf water and staring at a huge clock from my red phone booth, adding extra letters to wourds.

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

Комментарии (0)

© Habrahabr.ru