Однажды мне стало интересно, отличается ли британская и американская литература с точки зрения выбора слов, и если отличается, удастся ли мне обучить классификатор, который бы различал литературные тексты с точки зрения частоты использованных слов. Различать тексты, написанные на разных языках, довольно легко, мощность пересечения множества слов небольшая относительно множества слов в выборке. Классификация текста по категориям «наука», «христианство», «компьютерная графика», «атеизм», — всем известный hello world среди задач по работе с частотностью текста. Передо мной стояла более сложная задача, так как я сравнивала два диалекта одного языка, а тексты не имели общей смысловой направленности.
Самый долгий этап машинного обучения — это извлечение данных. Для обучающей выборки я использовала тексты с 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.
Код можно взять здесь, там же доступен уже обученный классификатор.
Автор: omruruch