Я наткнулся на хорошо подготовленный набор данных, предоставленный Google, с 58 000 тщательно отобранных комментариев Reddit, помеченных одной или несколькими из 27 эмоций, например гнев, замешательство, любовь. Google использовал это для обучения модели BERT, в которой они имели переменный успех в обнаружении эмоций в зависимости от типа комментария. Я подумал, что это будет отличный пример, чтобы узнать, как настроить сверточные нейронные сети (CNN) и встраивание в проблему обработки естественного языка (NLP), и получил приличную точность для некоторых эмоций, но, конечно, не так хорошо, как BERT Google.

Это не учебник - существует множество отличных руководств по CNN и встраиванию. Вместо этого это скорее полный пример кода, посвященный классификации нескольких классов и нескольких меток, для которой было довольно сложно найти полные и бесплатные примеры.

Спойлер: Мой код хуже, чем Google, который также предоставляет свой код по указанной выше ссылке. Их хорошо смазанный раствор BERT набирает около 46% баллов F1, в то время как я получаю только 42%. Однако я не тратил слишком много времени на оптимизацию своей модели, поэтому с помощью некоторых оптимизаций легко могут быть доступны еще 2 или 3 процента.

Подробнее о наборе данных GoEmotions

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

Набор данных, наряду с некоторыми другими метаданными, предоставляет комментарий Reddit («текст») вместе с соответствующим набором меток эмоций:

Начальная интуиция

Это сложная область - разные пользователи по-разному отображают настроения, и многие из этих комментариев очень короткие и содержат два значения, например «Это восхитительно». Во-вторых, некоторые комментарии, например «В этом видео даже не показана обувь, в которой он был одет», полностью нейтральны и помечены как таковые.

Однако определенно есть слова и фразы, которые передают определенные эмоции. Некоторые из них, например любовь или волнение, вероятно, довольно легко обнаружить. Однако тонкие эмоции, такие как гордость или любопытство, в меньшей степени. Так что я рассчитываю добиться определенного успеха, но, конечно, не ожидаю, что добьюсь большего, чем Google. В конце концов, эти комментарии не являются ответами на вопрос «Как вы себя чувствуете?» - они вроде бы произвольно взяты из Reddit.

Код, объясненный

Код Python, получивший 42% балла F1 по набору данных, находится здесь. Как только вы получите набор данных от Google, вы можете запустить его прямо из коробки, просто изменив путь к наборам данных, при условии, что у вас установлены все зависимости. Однако обучение нейронной сети займет около 30 минут, чтобы тренироваться в 15 эпох, в зависимости от вашей вычислительной мощности.

Ниже я опишу каждый раздел, за исключением импорта.

Получение данных

Это стандартно. Мы получаем CSV-файлы данных (которые по замыслу Google разделены), объединяем их и выбираем вектор ввода X и двоичный вывод y. Затем мы разделяем данные на данные для обучения и тестирования. Набор данных очень большой - почти 20 000 комментариев. Следовательно, подойдут и многие другие расщепления train_test_split, но мы можем придерживаться стандартной практики.

После этого у нас теперь есть примерно 20 000 сопоставлений комментариев («Я действительно люблю Bitterballen!») С соответствующими помеченными эмоциями (восхищение, веселье, гнев, раздражение, одобрение, забота, замешательство, любопытство, желание, разочарование, неодобрение, отвращение и т. Д. смущение, волнение, страх, благодарность, горе, радость, любовь, нервозность, оптимизм, гордость, осознание, облегчение, раскаяние, печаль, удивление).

Токенизация данных

Чтобы обеспечить действительный ввод в нейронную сеть, нам нужно токенизировать каждое слово в каждой строке данных. Это означает, что вместо слова у нас будет сопоставление с гигантским словарем Token: Word, например предложение «Я очень люблю биттербаллен!» будет размечать 1: «я», 2: «правда», 3: «любовь», 4: «горький баллен»

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

Вместо того, чтобы теперь представлять каждое предложение в тексте, это будет вектор: [1, 2, 3, 4]. Второе предложение: «Bitterballen действительно люблю!» теперь будет векторизоваться в [4, 3, 2, 1], а предложение с новыми словами добавит новые значения [5, 6,…] в список токенов.

  • num_words - количество слов в словаре. Это было выбрано на основе результатов токенизатора. После первых 9000 наиболее часто встречающихся слов в наборе данных мы начинаем обнаруживать опечатки, странные сокращения и слова, которые используются только один раз. Они бесполезны для обучения сети, поэтому от них следует отказаться. В результате количество слов, охватываемых вложением слов (раздел ниже), увеличивается с 51% до 95%. Сейчас мы работаем с хорошим корпусом слов, которые хорошо известны по встраиванию слов, поэтому в основном используются часто используемые слова, но с 5% слов, которые могут быть специфичными для Reddit, поэтому отлично подходят для обучения сети.
  • Здесь также есть двухстрочный обходной путь при ручной настройке индекса слов токенизатора, решающий известную проблему с Tokenizer. Это не влияет на вывод модели, но значительно сокращает время обучения, поскольку вручную уменьшает размер токенизатора до 9000.
  • maxlen - максимальная длина вводимого текста из Reddit. Для нашего алгоритма нам нужно, чтобы каждое сообщение было списком токенов одинаковой длины. Например, если maxlen было 10, короткое сообщение всего из 5 слов было бы дополнено нулями: [1 52 3 10 3 0 0 0 0 0], тогда как длинное сообщение из 15 слов было бы ограничено 10. Здесь я пошли с 20 просто ради безопасности.

Вложения

Путем токенизации мы превращаем каждую строку в более математический вектор слов, например [1 52 3 10 3 0 0 0 0 0]. Поскольку это вектор, это то, что мы можем использовать в качестве входных данных для модели машинного обучения. Однако есть еще лучший способ представления данных.

Вложения - это большие предварительно обученные отображения связанных слов в n-мерном пространстве. Например, как показано ниже, если мы обучим модель словесных ассоциаций на большом количестве текстов, одно измерение связанных слов, которые она подберет, будет относиться к полу. В этом измерении вложение будет простым вектором, указывающим, что мужчина для женщины, как король для королевы. При использовании встраивания вы активируете обучаемую модель, чтобы быстро различать эти различия. Это помогает модели обучаться быстрее и эффективнее, устраняя путаницу, которую могут создать синонимы и более абстрактный текст. Ваша модель может быть более эффективно обучена тому, чтобы считать эти слова похожими на x% и давать аналогичные результаты. Для более интуитивного и подробного объяснения попробуйте Will’s.

Итак, представьте себе это изображение, но с размерами от 50 до 300. Пол, цвет кожи, настроение и так далее. Это то, что предоставляет Stanford’s Glove library, и это источник встраивания, используемый в этом примере. Google BERT также использует предварительно обученные вложения.

В приведенном ниже коде есть функция create_embedding_matrix для обработки файла внедрения в матрицу. Это бесстыдно получено из отличного туториала по классификации текста с помощью keras.

Здесь есть только два варианта объяснения:

  • Используемый пакет внедрения. Я пробовал Glove и Fasttext. Оба дали одинаковые результаты, но Glove был немного лучше, даже с меньшими размерами.
  • embedding_dim - это количество измерений словесных отношений, которые необходимо учитывать. У Glove есть варианты от 50 до 300. Использование 50 действительно ускорило тренировку, но 300 дало мне 2% улучшение результата F1 для меня.

Итак, в результате внедрения, а не входного массива, отображающего некоторые предложения в текст

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

где каждое значение dim_ является фактическим представлением значения этого слова в измерении, например Пол.

Нейронная сеть

Во-первых, мы добавляем слой внедрения, чтобы принять только что созданную матрицу внедрения. Trainable = True здесь, потому что 1. встраивание перчаток не обучено последним 5% слов в нашем корпусе, что может быть очень полезно для модели и 2. Могут быть различия в размерах встраивания при использовании языка Reddit, которые могут быть модифицируется в режиме реального времени во время тренировки.

Во-вторых, сверточный слой, чтобы превратить это в CNN. По сути, это ведет себя как скользящий слой из 3 слов, ищущих 256 различных фильтров. Другими словами, он обучает модель искать 256 различных «значений», постепенно перемещаясь по тексту. Хотя я пробовал разные значения для обоих, эта сумма работала хорошо, вероятно, потому что:

  • 256 фильтров близко к используемым 300 размерам
  • 3 слова - хорошее среднее количество слов, по которым можно различить эмоцию во входных данных

Слой исключения (здесь тоже можно было бы использовать регуляризатор). Я обнаружил, что сеть с таким большим набором данных переоснащалась сразу после одной эпохи. Чтобы предотвратить это, слой исключения просто деактивирует случайные 20% узлов в пакете, чтобы мы не слишком полагались на один узел в сети. Это снижает переоснащение, и большинство моделей теперь показывают свои лучшие результаты примерно через 10 эпох, а не через 1. Это работает рука об руку со скоростью обучения и количеством выбранных эпох.

Это стандартная практика в CNN, теперь количество входящих векторов признаков сокращается, и для каждого измерения выбираются только максимальные.

Опять же, стандартная практика - иметь еще один небольшой плотный слой ретрансляции перед выходным сигмовидным слоем для классификации. Наконец, модель сочетается с оптимизатором Адама и функцией потерь binary_crossentropy, которые являются вашим хлебом и маслом для нейронных сетей бинарной классификации, а также для классификации с несколькими метками и нескольких классов. Скорость обучения 0,0002 была выбрана методом проб и ошибок. Как упоминалось выше, вы можете поиграть с этим, количеством эпох и частотой выпадения, чтобы повлиять на скорость обучения, переобучения и общего конечного результата сети.

* Теперь мы добавляем несколько обратных вызовов, чтобы мы могли вернуться к лучшей модели и вскоре прекратить обучение, если нейронная сеть начнет ухудшаться с обучением. * Размер партии 100 также был найден методом проб и ошибок.

Выбор сигмовидного порога

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

Полученные результаты

После скромных 12 эпох сеть оптимизирует свою функцию потерь, и мы получаем оценку F1 почти 42%.

Используя вышеуказанный пороговый цикл, похоже, что при пороге 0,25 мы получаем оптимальную оценку F1:

Используя этот порог 0,25, мы можем определить оценку F1 для каждой отдельной эмоции:

Вот несколько интересных результатов при построении графика результатов эмоций в F1:

Как и ожидалось, некоторые эмоции (веселье, благодарность, любовь) гораздо легче предсказать, чем тонкие эмоции, такие как разочарование, осознание, облегчение.

Дальнейшее развитие этой модели

Как упоминалось в рецензии, есть несколько способов улучшить эту модель:

  • Использование поиска по сетке для настройки параметров (особенно скорости обучения, отсева, сверточных гиперпараметров)
  • Оптимизация сигмовидного порога для каждой эмоции, в отличие от вывода всей сети
  • Отображение дополнительной информации с помощью восклицательных знаков, заглавных слов и т. Д.
  • Скопируйте вставку кода Google на основе BERT :)

Первоначально опубликовано на https://rian-van-den-ander.github.io 26 июля 2020 г.