Практическая классификация текста с использованием Python и Keras

4 мая, 2020

Подписывайся на наш канал в Telegram, чтобы ежедневно совершенствоваться в Python - перейти

Перевод статьи

Оглавление

  • Выбор набора данных
  • Определение базовой модели
  • Учебник по (глубоким) нейронным сетям
    • Представляем Keras
    • Установка Кеras
    • Ваша первая модель Keras
  • Что такое вложение слова?
    • One-Hot Кодирование
    • Вложения слов
    • Слой встраивания Keras
    • Использование встроенных слов
  • Сверточные нейронные сети (CNN)
  • Оптимизация гиперпараметров
  • Вывод
  • Дальнейшее чтение

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

Чтение настроения из текста с помощью машинного обучения называется анализом настроения , и это один из известных вариантов использования в классификации текста. Это относится к очень активной области исследований обработки естественного языка (НЛП) . Другие распространенные случаи классификации текста включают обнаружение спама, автоматическую пометку запросов клиентов и категоризацию текста по определенным темам. Так как вы можете это сделать?

Выбор набора данных

Прежде чем мы начнем, давайте посмотрим, какие данные у нас есть. Загрузите набор данных из набора данных Sentiment Labeled Sentences из репозитория машинного обучения UCI.

Кстати, этот репозиторий является прекрасным источником для наборов данных машинного обучения, когда вы хотите опробовать некоторые алгоритмы. Этот набор данных включает помеченные отзывы от IMDb, Amazon и Yelp. Каждый отзыв отмечен 0 баллами за негативное настроение или 1 баллом за позитивное настроение.

Извлеките папку в dataпапку и продолжайте загружать данные с помощью Pandas :

import pandas as pd

filepath_dict = {'yelp':   'data/sentiment_analysis/yelp_labelled.txt',
                 'amazon': 'data/sentiment_analysis/amazon_cells_labelled.txt',
                 'imdb':   'data/sentiment_analysis/imdb_labelled.txt'}

df_list = []
for source, filepath in filepath_dict.items():
    df = pd.read_csv(filepath, names=['sentence', 'label'], sep='\t')
    df['source'] = source  # Add another column filled with the source name
    df_list.append(df)

df = pd.concat(df_list)
print(df.iloc[0])

Результат будет следующим:

sentence    Wow... Loved this place.
label                              1
source                          yelp
Name: 0, dtype: object

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

Один из способов сделать это – подсчитать частоту каждого слова в каждом предложении и связать этот счет со всем набором слов в наборе данных. Вы начинаете с того, что берете данные и создаете словарь из всех слов во всех предложениях. Коллекция текстов также называется корпусом в НЛП.

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

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

Давайте быстро проиллюстрируем это. Представьте, что у вас есть следующие два предложения:>>>

>>> sentences = ['John likes ice cream', 'John hates chocolate.']

Затем вы можете использовать CountVectorizerпредоставленную библиотеку scikit-learn для векторизации предложений. Он берет слова каждого предложения и создает словарь всех уникальных слов в предложениях. Этот словарь можно затем использовать для создания вектора признаков количества слов:>>>

>>> from sklearn.feature_extraction.text import CountVectorizer

>>> vectorizer = CountVectorizer(min_df=0, lowercase=False)
>>> vectorizer.fit(sentences)
>>> vectorizer.vocabulary_
{'John': 0, 'chocolate': 1, 'cream': 2, 'hates': 3, 'ice': 4, 'likes': 5}

Этот словарь также служит указателем каждого слова. Теперь вы можете взять каждое предложение и получить слова вхождения слов на основе предыдущего словаря. Словарь состоит из всех пяти слов в наших предложениях, каждое из которых представляет одно слово в словаре. Когда вы возьмете предыдущие два предложения и преобразуете их с помощью, CountVectorizerвы получите вектор, представляющий счетчик каждого слова в предложении:>>>

>>> vectorizer.transform(sentences).toarray()
array([[1, 0, 1, 0, 1, 1],
    [1, 1, 0, 1, 0, 0]])

Теперь вы можете увидеть результирующие векторы объектов для каждого предложения на основе предыдущего словаря. Например, если вы посмотрите на первый элемент, вы увидите, что оба вектора 1есть. Это означает, что оба предложения имеют одно вхождение John, которое находится на первом месте в словаре.

Это считается моделью мешков слов (BOW) , которая в НЛП является обычным способом создания векторов из текста. Каждый документ представлен в виде вектора. Теперь вы можете использовать эти векторы как векторы функций для модели машинного обучения. Это приводит нас к следующей части, определяющей базовую модель.

Определение базовой модели

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

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

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

Мы начнем с набора данных Yelp, который мы извлекаем из нашего объединенного набора данных. Оттуда мы берем предложения и метки. .valuesВозвращает массив NumPy вместо панды серии объекта , который в этом контексте легче работать с:>>>

>>> from sklearn.model_selection import train_test_split

>>> df_yelp = df[df['source'] == 'yelp']

>>> sentences = df_yelp['sentence'].values
>>> y = df_yelp['label'].values

>>> sentences_train, sentences_test, y_train, y_test = train_test_split(
...    sentences, y, test_size=0.25, random_state=1000)

Здесь мы снова будем использовать предыдущую модель BOW для векторизации предложений. Вы можете использовать снова CountVectorizerдля этой задачи. Поскольку у вас могут отсутствовать данные тестирования во время обучения, вы можете создать словарь, используя только данные обучения. Используя этот словарь, вы можете создать векторы функций для каждого предложения набора обучения и тестирования:>>>

>>> from sklearn.feature_extraction.text import CountVectorizer

>>> vectorizer = CountVectorizer()
>>> vectorizer.fit(sentences_train)

>>> X_train = vectorizer.transform(sentences_train)
>>> X_test  = vectorizer.transform(sentences_test)
>>> X_train
<750x1714 sparse matrix of type '<class 'numpy.int64'>'
    with 7368 stored elements in Compressed Sparse Row format>

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

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

Примечание: здесь есть много дополнительных параметров, к CountVectorizer()которым мы воздерживаемся , например, добавление нграмм , потому что сначала цель – создать простую базовую модель. Сам шаблон токена по умолчанию имеет значение token_pattern=’(?u)\b\w\w+\b’, которое является шаблоном регулярных выражений , который говорит: «слово – это 2 или более символов слова Unicode, окруженных границами слова».

Модель классификации, которую мы собираемся использовать, – это логистическая регрессия, которая представляет собой простую, но мощную линейную модель, которая математически фактически представляет собой форму регрессии между 0 и 1 на основе вектора входных признаков. Задав предельное значение (по умолчанию 0,5), регрессионная модель используется для классификации. Вы можете снова использовать библиотеку scikit-learn, которая предоставляет LogisticRegressionклассификатор:>>>

>>> from sklearn.linear_model import LogisticRegression

>>> classifier = LogisticRegression()
>>> classifier.fit(X_train, y_train)
>>> score = classifier.score(X_test, y_test)

>>> print("Accuracy:", score)
Accuracy: 0.796

Вы можете видеть, что логистическая регрессия достигла впечатляющих 79,6%, но давайте посмотрим, как эта модель работает на других наборах данных, которые мы имеем. В этом сценарии мы выполняем и оцениваем весь процесс для каждого набора данных:

for source in df['source'].unique():
    df_source = df[df['source'] == source]
    sentences = df_source['sentence'].values
    y = df_source['label'].values

    sentences_train, sentences_test, y_train, y_test = train_test_split(
        sentences, y, test_size=0.25, random_state=1000)

    vectorizer = CountVectorizer()
    vectorizer.fit(sentences_train)
    X_train = vectorizer.transform(sentences_train)
    X_test  = vectorizer.transform(sentences_test)

    classifier = LogisticRegression()
    classifier.fit(X_train, y_train)
    score = classifier.score(X_test, y_test)
    print('Accuracy for {} data: {:.4f}'.format(source, score))

Вот результат:

Accuracy for yelp data: 0.7960
Accuracy for amazon data: 0.7960
Accuracy for imdb data: 0.7487

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

Учебник по (глубоким) нейронным сетям

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

Проще говоря, все исследователи ИИ согласились, что они не согласны друг с другом, когда ИИ превысит показатели человеческого уровня. Согласно этой статье у нас еще должно быть время.

Так что, возможно, вам уже любопытно, как работают нейронные сети. Если вы уже знакомы с нейронными сетями, не стесняйтесь переходить к частям, связанным с Keras. Кроме того, есть замечательная книга глубокого обучения Иана Гудфеллоу, которую я очень рекомендую, если вы хотите углубиться в математику. Вы можете прочитать всю книгу онлайн бесплатно. В этом разделе вы получите обзор нейронных сетей и их внутренней работы, а позже вы увидите, как использовать нейронные сети с выдающейся библиотекой Keras.

В этой статье вам не нужно беспокоиться об уникальности, но (глубокие) нейронные сети играют решающую роль в последних разработках ИИ. Все началось с известной статьи Джеффри Хинтона и его команды в 2012 году , которая превзошла все предыдущие модели в знаменитой ImageNet Challenge .

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

С тех пор нейронные сети перешли в несколько областей, включая классификацию, регрессию и даже генеративные модели. Наиболее распространенные области включают компьютерное зрение, распознавание голоса и обработку естественного языка (NLP).

Нейронные сети, или иногда называемые искусственной нейронной сетью (ANN) или нейронной сетью с прямой связью, являются вычислительными сетями, которые были смутно вдохновлены нейронными сетями человеческого мозга. Они состоят из нейронов (также называемых узлами), которые связаны, как показано на графике ниже.

Вы начинаете с наличия слоя входных нейронов, в который вы вводите векторы объектов, а затем значения передаются в скрытый слой. При каждом соединении вы передаете значение вперед, в то время как значение умножается на вес и смещение добавляется к значению. Это происходит при каждом соединении, и в конце вы получаете выходной слой с одним или несколькими выходными узлами.

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

структура нейронной сети
Модель нейронной сети

Вы можете иметь столько скрытых слоев, сколько пожелаете. Фактически, нейронная сеть с более чем одним скрытым слоем считается глубокой нейронной сетью. Не волнуйтесь: я не буду вдаваться в математические глубины, касающиеся нейронных сетей. Но если вы хотите получить интуитивно понятное визуальное представление о математике, вы можете просмотреть плейлист YouTube Гранта Сандерсона. Формула от одного слоя к следующему – это короткое уравнение:

формула нейронной сети
Формула нейронной сети

Давайте медленно распаковать то, что здесь происходит. Видите ли, мы имеем дело только с двумя слоями. Слой с узлами aслужит входом для слоя с узлами o. Чтобы вычислить значения для каждого выходного узла, мы должны умножить каждый входной узел на вес wи добавить смещение b.

Все они должны быть затем суммированы и переданы функции f. Эта функция считается функцией активации, и существуют различные функции, которые можно использовать в зависимости от уровня или проблемы. Как правило, обычно используется выпрямленная линейная единица (ReLU) для скрытых слоев, сигмовидная функция для выходного слоя в задаче двоичной классификации или функция softmax для выходного слоя для задач классификации нескольких классов.

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

Это делается с помощью методов оптимизации (также называемых оптимизатором), таких как градиентный спуск , чтобы уменьшить погрешность между вычисленным и желаемым выходом (также называемым целевым выходом). Ошибка определяется функцией потерь , потери которой мы хотим минимизировать с помощью оптимизатора. Весь процесс слишком обширен, чтобы охватить его здесь, но я снова вернусь к плейлисту Гранта Сандерсона и книге Глубокого обучения Яна Гудфеллоу, которую я упоминал ранее.

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

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

Представляем Keras

Keras – это API глубокого обучения и нейронных сетей от Франсуа Шоле, который может работать поверх Tensorflow (Google), Theano или CNTK (Microsoft). Процитирую замечательную книгу Франсуа Шоле « Глубокое обучение с Python» :

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

Это отличный способ начать экспериментировать с нейронными сетями без необходимости реализовывать каждый слой самостоятельно. Например, Tensorflow – отличная библиотека для машинного обучения, но вам нужно реализовать много стандартного кода, чтобы модель работала.

Установка Кераса

Перед установкой Keras вам понадобится Tensorflow, Theano или CNTK. В этом уроке мы будем использовать Tensorflow, поэтому ознакомьтесь с их руководством по установке здесь , но не стесняйтесь использовать любую из фреймворков, которая лучше всего подходит для вас. Keras можно установить с помощью PyPI с помощью следующей команды:

$ pip install keras

Вы можете выбрать серверную часть, которую хотите иметь, открыв файл конфигурации Keras, который вы можете найти здесь:

$HOME/.keras/keras.json

Если вы пользователь Windows, вы должны заменить $HOMEна %USERPROFILE%. Файл конфигурации должен выглядеть следующим образом:

{
    "image_data_format": "channels_last",
    "epsilon": 1e-07,
    "floatx": "float32",
    "backend": "tensorflow"
}

Вы можете изменить backendполе там "theano""tensorflow"или "cntk", учитывая , что вы установили бэкенд на вашей машине. Для получения более подробной информации ознакомьтесь с документацией на бэкэндс Keras .

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

Ваша первая модель Keras

Теперь вы, наконец, готовы экспериментировать с Керасом. Keras поддерживает два основных типа моделей. У вас есть API последовательной модели, который вы увидите в этом учебном пособии, и функциональный API, который может выполнять все из последовательной модели, но его также можно использовать для расширенных моделей со сложной сетевой архитектурой.

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

Давайте посмотрим, сможем ли мы достичь некоторого улучшения нашей предыдущей модели логистической регрессии. Вы можете использовать X_trainи X_testмассивы , которые вы построили в предыдущем примере.

Перед тем, как построить нашу модель, нам нужно знать входное измерение векторов объектов. Это происходит только в первом слое, поскольку следующие слои могут делать автоматический вывод формы. Чтобы построить последовательную модель, вы можете добавлять слои один за другим в следующем порядке:>>>

>>> from keras.models import Sequential
>>> from keras import layers

>>> input_dim = X_train.shape[1]  # Number of features

>>> model = Sequential()
>>> model.add(layers.Dense(10, input_dim=input_dim, activation='relu'))
>>> model.add(layers.Dense(1, activation='sigmoid'))
Using TensorFlow backend.

Прежде чем вы сможете начать с обучения модели, вам необходимо настроить процесс обучения. Это делается с помощью .compile()метода. Этот метод определяет оптимизатор и функцию потерь.

Кроме того, вы можете добавить список показателей, которые впоследствии можно будет использовать для оценки, но они не влияют на обучение. В этом случае мы хотим использовать двоичную кросс-энтропию и оптимизатор Адама, который вы видели в упомянутом выше учебнике. Keras также включает удобную .summary()функцию для обзора модели и количества параметров, доступных для обучения:>>>

>>> model.compile(loss='binary_crossentropy', 
...               optimizer='adam', 
...               metrics=['accuracy'])
>>> model.summary()
_________________________________________________________________
Layer (type)                 Output Shape          Param #   
=================================================================
dense_1 (Dense)              (None, 10)            17150     
_________________________________________________________________
dense_2 (Dense)              (None, 1)             11        
=================================================================
Total params: 17,161
Trainable params: 17,161
Non-trainable params: 0
_________________________________________________________________

Вы можете заметить, что у нас есть 8575 параметров для первого слоя и еще 6 в следующем. Откуда они взялись?

Видите, у нас есть 1714 измерений для каждого вектора объектов, а затем у нас есть 5 узлов. Нам нужны весовые коэффициенты для каждого измерения объекта и каждого узла, который учитывает 1714 * 5 = 8570параметры, а затем у нас есть еще 5-кратное добавленное смещение для каждого узла, что дает нам 8575 параметров. В последнем узле у нас есть еще 5 весов и одно смещение, что приводит нас к 6 параметрам.

Ухоженная! Вы почти у цели. Теперь пришло время начать тренировки с этой .fit()функцией.

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

Другим параметром, который вы выбираете, является размер партии . Размер партии отвечает за то, сколько сэмплов мы хотим использовать за одну эпоху, что означает, сколько сэмплов используется за один проход вперед / назад. Это увеличивает скорость вычислений, так как для запуска требуется меньше эпох, но также требуется больше памяти, и модель может ухудшиться с увеличением размера пакета. Поскольку у нас есть небольшой тренировочный набор, мы можем оставить его для небольшого размера партии:>>>

>>> history = model.fit(X_train, y_train,
...                     epochs=100,
...                     verbose=False,
...                     validation_data=(X_test, y_test)
...                     batch_size=10)

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

Обратите внимание, что если вы перезапустите .fit()метод, вы начнете с вычисленных весов из предыдущего обучения. Убедитесь, что вы скомпилировали модель еще раз, прежде чем снова приступить к ее обучению. Теперь давайте оценим точность модели:>>>

>>> loss, accuracy = model.evaluate(X_train, y_train, verbose=False)
>>> print("Training Accuracy: {:.4f}".format(accuracy))
>>> loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
>>> print("Testing Accuracy:  {:.4f}".format(accuracy))
Training Accuracy: 1.0000
Testing Accuracy:  0.7960

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

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

import matplotlib.pyplot as plt
plt.style.use('ggplot')

def plot_history(history):
    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    x = range(1, len(acc) + 1)

    plt.figure(figsize=(12, 5))
    plt.subplot(1, 2, 1)
    plt.plot(x, acc, 'b', label='Training acc')
    plt.plot(x, val_acc, 'r', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.legend()
    plt.subplot(1, 2, 2)
    plt.plot(x, loss, 'b', label='Training loss')
    plt.plot(x, val_loss, 'r', label='Validation loss')
    plt.title('Training and validation loss')
    plt.legend()

Чтобы использовать эту функцию, просто вызовите plot_history()с собранной точностью и потерями внутри historyсловаря:>>>

>>> plot_history(history)
базовая модель точности потерь
Точность и потеря для базовой модели

Вы можете видеть, что мы тренировали нашу модель слишком долго, так как тренировочный набор достиг 100% точности. Хороший способ увидеть, когда модель начинает переоснащение, – это когда потери данных проверки снова начинают расти. Это хороший момент, чтобы остановить модель. Вы можете увидеть это около 20-40 эпох в этом тренинге.

Примечание. При обучении нейронных сетей следует использовать отдельный набор для тестирования и проверки. Обычно вы берете модель с наивысшей точностью проверки и затем тестируете модель с набором для тестирования.

Это гарантирует, что вы не надеваете модель. Использование набора проверки для выбора наилучшей модели – это форма утечки данных (или «мошенничество»), позволяющая выбрать результат, который дал лучший результат теста из сотен из них. Утечка данных происходит, когда в модели используется информация вне обучающего набора данных.

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

Что такое вложение слова?

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

  • Слова, представленные каждым словом как вектор
  • Символы, представленные каждым символом в виде вектора
  • N-граммы слов / символов, представленных в виде вектора (N-граммы – это перекрывающиеся группы из нескольких последовательных слов / символов в тексте)

В этом уроке вы увидите, как работать с представлением слов в качестве векторов, что является распространенным способом использования текста в нейронных сетях. Два возможных способа представления слова в виде вектора – однократное кодирование и встраивание слова.

One-Hot Кодирование

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

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

Допустим, у вас есть список городов, как в следующем примере:>>>

>>> cities = ['London', 'Berlin', 'Berlin', 'New York', 'London']
>>> cities
['London', 'Berlin', 'Berlin', 'New York', 'London']

Вы можете использовать scikit-learn и the LabelEncoderдля кодирования списка городов в категориальные целочисленные значения, как здесь:>>>

>>> from sklearn.preprocessing import LabelEncoder

>>> encoder = LabelEncoder()
>>> city_labels = encoder.fit_transform(cities)
>>> city_labels
array([1, 0, 0, 2, 1])

Используя это представление, вы можете использовать OneHotEncoderпредоставляемый scikit-learn для кодирования категориальных значений, которые мы получили ранее, в числовой массив с горячим кодированием. OneHotEncoderожидает, что каждое категориальное значение будет находиться в отдельной строке, поэтому вам нужно изменить форму массива, после чего вы можете применить кодировщик:>>>

>>> from sklearn.preprocessing import OneHotEncoder

>>> encoder = OneHotEncoder(sparse=False)
>>> city_labels = city_labels.reshape((5, 1))
>>> encoder.fit_transform(city_labels)
array([[0., 1., 0.],
       [1., 0., 0.],
       [1., 0., 0.],
       [0., 0., 1.],
       [0., 1., 0.]])

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

Вложения слов

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

Обратите внимание, что вложения слова не понимают текст как человек, а скорее отображают статистическую структуру языка, используемого в корпусе. Их цель состоит в том, чтобы отобразить смысловой смысл в геометрическое пространство. Это геометрическое пространство называется пространством вложения .

Это будет отображать семантически похожие слова близко в пространстве вложения, как числа или цвета. Если вложение хорошо отражает связь между словами, такие вещи, как векторная арифметика, должны стать возможными. Известным примером в этой области исследования является возможность сопоставления Король – Мужчина + Женщина = Королева .

Как вы можете получить такое вложение слова? У вас есть два варианта для этого. Одним из способов является обучение ваших встраиваний слов во время обучения вашей нейронной сети. Другой способ заключается в использовании предварительно обученных вложений слов, которые вы можете напрямую использовать в своей модели. Там у вас есть возможность либо оставить эти вложения слов без изменений во время обучения, либо вы также тренируете их.

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

Вы можете начать с использования Tokenizerслужебного класса, который может векторизовать текстовый корпус в список целых чисел. Каждое целое число соответствует значению в словаре, которое кодирует весь корпус, причем ключи в словаре сами являются словарными терминами. Вы можете добавить параметр num_words, который отвечает за установку размера словаря. Самые распространенные num_wordsслова будут сохранены. У меня есть данные тестирования и обучения, подготовленные из предыдущего примера:>>>

>>> from keras.preprocessing.text import Tokenizer

>>> tokenizer = Tokenizer(num_words=5000)
>>> tokenizer.fit_on_texts(sentences_train)

>>> X_train = tokenizer.texts_to_sequences(sentences_train)
>>> X_test = tokenizer.texts_to_sequences(sentences_test)

>>> vocab_size = len(tokenizer.word_index) + 1  # Adding 1 because of reserved 0 index

>>> print(sentences_train[2])
>>> print(X_train[2])
Of all the dishes, the salmon was the best, but all were great.
[11, 43, 1, 171, 1, 283, 3, 1, 47, 26, 43, 24, 22]

Индексирование упорядочено по наиболее распространенным словам в тексте, которые можно увидеть по слову, theимеющему индекс 1. Важно отметить, что индекс 0зарезервирован и не назначен ни одному слову. Этот нулевой индекс используется для дополнения, которое я сейчас представлю.

Неизвестные слова (слова, которых нет в словаре) обозначаются в керасе, word_count + 1поскольку они также могут содержать некоторую информацию. Вы можете увидеть индекс каждого слова, взглянув на word_indexсловарь Tokenizerобъекта:>>>

>>> for word in ['the', 'all', 'happy', 'sad']:
...     print('{}: {}'.format(word, tokenizer.word_index[word]))
the: 1
all: 43
happy: 320
sad: 450

Примечание: обратите пристальное внимание на разницу между этой техникой и тем, X_trainчто было создано scikit-learn’s CountVectorizer.

При помощи CountVectorizerмы сложили векторы количества слов, и каждый вектор был одинаковой длины (размер общего словаря корпуса). С помощью Tokenizer результирующие векторы равны длине каждого текста, а числа не обозначают число, а скорее соответствуют значениям слова из словаря tokenizer.word_index.

Одна проблема, которая у нас есть, состоит в том, что каждая текстовая последовательность в большинстве случаев имеет разную длину слов. Чтобы противостоять этому, вы можете использовать, pad_sequence()который просто дополняет последовательность слов нулями. По умолчанию он добавляет нули, но мы хотим добавить их. Как правило, не имеет значения, добавляете ли вы нули или дополняете их.

Кроме того, вы хотели бы добавить maxlenпараметр, чтобы указать, как долго должны быть последовательности. Это сокращает последовательности, которые превышают это число. В следующем коде вы можете увидеть, как дополнять последовательности с помощью Keras:>>>

>>> from keras.preprocessing.sequence import pad_sequences

>>> maxlen = 100

>>> X_train = pad_sequences(X_train, padding='post', maxlen=maxlen)
>>> X_test = pad_sequences(X_test, padding='post', maxlen=maxlen)

>>> print(X_train[0, :])
[  1  10   3 282 739  25   8 208  30  64 459 230  13   1 124   5 231   8
  58   5  67   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
   0   0   0   0   0   0   0   0   0   0]

Первые значения представляют индекс в словаре, как вы узнали из предыдущих примеров. Вы также можете видеть, что результирующий вектор объектов содержит в основном нули, поскольку у вас есть довольно короткое предложение. В следующей части вы узнаете, как работать с вложениями слов в Keras.

Слой встраивания Keras

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

  • input_dim: размер словарного запаса
  • output_dim: размер плотного вектора
  • input_length: длина последовательности

Со Embeddingслоем у нас теперь есть пара вариантов. Одним из способов было бы взять вывод встроенного слоя и подключить его к Denseслою. Чтобы сделать это, вы должны добавить Flattenслой между ними, который подготавливает последовательный ввод для Denseслоя:

from keras.models import Sequential
from keras import layers

embedding_dim = 50

model = Sequential()
model.add(layers.Embedding(input_dim=vocab_size, 
                           output_dim=embedding_dim, 
                           input_length=maxlen))
model.add(layers.Flatten())
model.add(layers.Dense(10, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])
model.summary()

Результат будет следующим:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_8 (Embedding)      (None, 100, 50)           87350     
_________________________________________________________________
flatten_3 (Flatten)          (None, 5000)              0         
_________________________________________________________________
dense_13 (Dense)             (None, 10)                50010     
_________________________________________________________________
dense_14 (Dense)             (None, 1)                 11        
=================================================================
Total params: 137,371
Trainable params: 137,371
Non-trainable params: 0
_________________________________________________________________

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

history = model.fit(X_train, y_train,
                    epochs=20,
                    verbose=False,
                    validation_data=(X_test, y_test),
                    batch_size=10)
loss, accuracy = model.evaluate(X_train, y_train, verbose=False)
print("Training Accuracy: {:.4f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
print("Testing Accuracy:  {:.4f}".format(accuracy))
plot_history(history)

Результат будет следующим:

Training Accuracy: 0.5100
Testing Accuracy:  0.4600
потеря точности первой модели
Точность и потеря для первой модели

Как правило, это не очень надежный способ работы с последовательными данными, как вы можете видеть в производительности. При работе с последовательными данными вы хотите сосредоточиться на методах, которые обращаются к локальной и последовательной информации, а не к абсолютной позиционной информации.

Другим способом работы с вложениями является использованием MaxPooling1DAveragePooling1Dили GlobalMaxPooling1DGlobalAveragePooling1Dслоя после того, как вложения. Вы можете думать о пулах как способ уменьшить выборку (способ уменьшить размер) входящих векторов объектов.

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

Глобальное максимальное / среднее объединение занимает максимальное / среднее значение для всех функций, тогда как в другом случае вы должны определить размер пула. У Keras снова есть собственный слой, который вы можете добавить в последовательную модель:

from keras.models import Sequential
from keras import layers

embedding_dim = 50

model = Sequential()
model.add(layers.Embedding(input_dim=vocab_size, 
                           output_dim=embedding_dim, 
                           input_length=maxlen))
model.add(layers.GlobalMaxPool1D())
model.add(layers.Dense(10, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])
model.summary()

Результат будет следующим:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_9 (Embedding)      (None, 100, 50)           87350     
_________________________________________________________________
global_max_pooling1d_5 (Glob (None, 50)                0         
_________________________________________________________________
dense_15 (Dense)             (None, 10)                510       
_________________________________________________________________
dense_16 (Dense)             (None, 1)                 11        
=================================================================
Total params: 87,871
Trainable params: 87,871
Non-trainable params: 0
_________________________________________________________________

Порядок обучения не меняется:

history = model.fit(X_train, y_train,
                    epochs=50,
                    verbose=False,
                    validation_data=(X_test, y_test),
                    batch_size=10)
loss, accuracy = model.evaluate(X_train, y_train, verbose=False)
print("Training Accuracy: {:.4f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
print("Testing Accuracy:  {:.4f}".format(accuracy))
plot_history(history)

Результат будет следующим:

Training Accuracy: 1.0000
Testing Accuracy:  0.8050
макс пулинг
Точность и потеря для максимальной модели пула

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

Использование встроенных слов

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

Альтернатива состоит в том, чтобы использовать предварительно вычисленное пространство внедрения, которое использует намного больший корпус. Можно заранее вычислить вложение слов, просто обучив их на большом корпусе текста. Среди наиболее популярных методов – Word2Vec, разработанный Google, и GloVe (Global Векторы для представления слов), разработанный Stanford NLP Group.

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

В этом руководстве вы узнаете, как работать с вложениями слов GloVe из Stanford NLP Group, поскольку их размер более управляем, чем вложения слов Word2Vec, предоставляемые Google. Идите и загрузите отсюда 6B (обучено 6 миллиардам слов) встраивания слов (822 MB).

Вы можете найти другие вложения слов также на главной странице GloVe . Вы можете найти предварительно подготовленные вложения Word2Vec от Google здесь . Если вы хотите тренировать свои собственные вложения слов, вы можете сделать это эффективно с пакетом gensim Python, который использует Word2Vec для расчета. Подробнее о том, как это сделать здесь .

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

Это большой файл с 400000 строками, каждая строка которого представляет слово, за которым следует вектор в виде потока с плавающей точкой. Например, вот первые 50 символов первой строки:

$ head -n 1 data/glove_word_embeddings/glove.6B.50d.txt | cut -c-50
    the 0.418 0.24968 -0.41242 0.1217 0.34527 -0.04445

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

import numpy as np

def create_embedding_matrix(filepath, word_index, embedding_dim):
    vocab_size = len(word_index) + 1  # Adding again 1 because of reserved 0 index
    embedding_matrix = np.zeros((vocab_size, embedding_dim))

    with open(filepath) as f:
        for line in f:
            word, *vector = line.split()
            if word in word_index:
                idx = word_index[word] 
                embedding_matrix[idx] = np.array(
                    vector, dtype=np.float32)[:embedding_dim]

    return embedding_matrix

Вы можете использовать эту функцию сейчас, чтобы получить матрицу встраивания:>>>

>>> embedding_dim = 50
>>> embedding_matrix = create_embedding_matrix(
...     'data/glove_word_embeddings/glove.6B.50d.txt',
...     tokenizer.word_index, embedding_dim)

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

Во-первых, давайте кратко рассмотрим, сколько векторов внедрения отличны от нуля:>>>

>>> nonzero_elements = np.count_nonzero(np.count_nonzero(embedding_matrix, axis=1))
>>> nonzero_elements / vocab_size
0.9507727532913566

Это означает, что 95,1% словарного запаса покрыто предварительно обученной моделью, которая является хорошим охватом нашего словарного запаса. Давайте посмотрим на производительность при использовании GlobalMaxPool1Dслоя:

model = Sequential()
model.add(layers.Embedding(vocab_size, embedding_dim, 
                           weights=[embedding_matrix], 
                           input_length=maxlen, 
                           trainable=False))
model.add(layers.GlobalMaxPool1D())
model.add(layers.Dense(10, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])
model.summary()

Результат будет следующим:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_10 (Embedding)     (None, 100, 50)           87350     
_________________________________________________________________
global_max_pooling1d_6 (Glob (None, 50)                0         
_________________________________________________________________
dense_17 (Dense)             (None, 10)                510       
_________________________________________________________________
dense_18 (Dense)             (None, 1)                 11        
=================================================================
Total params: 87,871
Trainable params: 521
Non-trainable params: 87,350
_________________________________________________________________
history = model.fit(X_train, y_train,
                    epochs=50,
                    verbose=False,
                    validation_data=(X_test, y_test),
                    batch_size=10)
loss, accuracy = model.evaluate(X_train, y_train, verbose=False)
print("Training Accuracy: {:.4f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
print("Testing Accuracy:  {:.4f}".format(accuracy))
plot_history(history)

Результат будет следующим:

Training Accuracy: 0.7500
Testing Accuracy:  0.6950
потеря точности вложения нетренированных
Точность и потеря для нетренированных вложений в слова

Поскольку вложения слова дополнительно не обучаются, ожидается, что они будут ниже. Но давайте теперь посмотрим, как это работает, если мы позволим обучить внедрению с помощью trainable=True:

model = Sequential()
model.add(layers.Embedding(vocab_size, embedding_dim, 
                           weights=[embedding_matrix], 
                           input_length=maxlen, 
                           trainable=True))
model.add(layers.GlobalMaxPool1D())
model.add(layers.Dense(10, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])
model.summary()

Результат будет следующим:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_11 (Embedding)     (None, 100, 50)           87350     
_________________________________________________________________
global_max_pooling1d_7 (Glob (None, 50)                0         
_________________________________________________________________
dense_19 (Dense)             (None, 10)                510       
_________________________________________________________________
dense_20 (Dense)             (None, 1)                 11        
=================================================================
Total params: 87,871
Trainable params: 87,871
Non-trainable params: 0
_________________________________________________________________
history = model.fit(X_train, y_train,
                    epochs=50,
                    verbose=False,
                    validation_data=(X_test, y_test),
                    batch_size=10)
loss, accuracy = model.evaluate(X_train, y_train, verbose=False)
print("Training Accuracy: {:.4f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
print("Testing Accuracy:  {:.4f}".format(accuracy))
plot_history(history)

Результат будет следующим:

Training Accuracy: 1.0000
Testing Accuracy:  0.8250
тренировка встраивания точности потери
Точность и потеря для предварительно обученных вложений в слова

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

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

Сверточные нейронные сети (CNN)

Сверточные нейронные сети или также называются коннетами являются одним из самых интересных разработок в области машинного обучения в последние годы.

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

Если это просто еще одна нейронная сеть, что отличает ее от того, что вы изучили ранее?

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

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

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

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

одномерная свертка
1D Convolution ( Источник изображения )

Теперь давайте посмотрим, как вы можете использовать эту сеть в Керасе. Keras снова предлагает различные сверточные слои, которые вы можете использовать для этой задачи. Слой, который вам нужен, это Conv1Dслой. Этот слой снова имеет различные параметры для выбора. На данный момент вас интересуют количество фильтров, размер ядра и функция активации. Вы можете добавить этот слой между Embeddingслоем и GlobalMaxPool1Dслоем:

embedding_dim = 100

model = Sequential()
model.add(layers.Embedding(vocab_size, embedding_dim, input_length=maxlen))
model.add(layers.Conv1D(128, 5, activation='relu'))
model.add(layers.GlobalMaxPooling1D())
model.add(layers.Dense(10, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])
model.summary()

Результат будет следующим:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_13 (Embedding)     (None, 100, 100)          174700    
_________________________________________________________________
conv1d_2 (Conv1D)            (None, 96, 128)           64128     
_________________________________________________________________
global_max_pooling1d_9 (Glob (None, 128)               0         
_________________________________________________________________
dense_23 (Dense)             (None, 10)                1290      
_________________________________________________________________
dense_24 (Dense)             (None, 1)                 11        
=================================================================
Total params: 240,129
Trainable params: 240,129
Non-trainable params: 0
_________________________________________________________________
history = model.fit(X_train, y_train,
                    epochs=10,
                    verbose=False,
                    validation_data=(X_test, y_test),
                    batch_size=10)
loss, accuracy = model.evaluate(X_train, y_train, verbose=False)
print("Training Accuracy: {:.4f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
print("Testing Accuracy:  {:.4f}".format(accuracy))
plot_history(history)

Результат будет следующим:

Training Accuracy: 1.0000
Testing Accuracy:  0.7700
модель свертки точности потерь
Точность и потеря для сверточной нейронной сети

Вы можете видеть, что точность 80%, кажется, является трудным препятствием, чтобы преодолеть с этим набором данных, и CNN, возможно, не хорошо оборудован. Причиной такого плато может быть то, что:

  • Недостаточно тренировочных образцов
  • Ваши данные плохо обобщаются
  • Отсутствует фокус на настройке гиперпараметров

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

Оптимизация гиперпараметров

Одним из важных шагов глубокого обучения и работы с нейронными сетями является оптимизация гиперпараметров .

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

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

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

Чтобы применить случайный поиск с Keras, вам нужно использовать KerasClassifier, который служит оболочкой для API scikit-learn . С помощью этой обертки вы можете использовать различные инструменты, доступные в scikit-learn, такие как перекрестная проверка . Класс, который вам нужен, это RandomizedSearchCV, который реализует случайный поиск с перекрестной проверкой. Перекрестная проверка – это способ проверки модели и получения всего набора данных и разделения его на несколько наборов данных тестирования и обучения.

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

Первый шаг для того KerasClassifier, чтобы иметь функцию, которая создает модель Keras. Мы будем использовать предыдущую модель, но мы позволим установить различные параметры для оптимизации гиперпараметра:

def create_model(num_filters, kernel_size, vocab_size, embedding_dim, maxlen):
    model = Sequential()
    model.add(layers.Embedding(vocab_size, embedding_dim, input_length=maxlen))
    model.add(layers.Conv1D(num_filters, kernel_size, activation='relu'))
    model.add(layers.GlobalMaxPooling1D())
    model.add(layers.Dense(10, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))
    model.compile(optimizer='adam',
                  loss='binary_crossentropy',
                  metrics=['accuracy'])
    return model

Далее вы хотите определить сетку параметров, которую вы хотите использовать в обучении. Он состоит из словаря с именами каждого параметра, как в предыдущей функции. Количество пробелов в сетке – это то 3 * 3 * 1 * 1 * 1, где каждое из этих чисел является числом различных вариантов для данного параметра.

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

param_grid = dict(num_filters=[32, 64, 128],
                  kernel_size=[3, 5, 7],
                  vocab_size=[5000], 
                  embedding_dim=[50],
                  maxlen=[100])

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

Полученный экземпляр и сетка параметров затем используются в качестве оценщика в RandomSearchCVклассе. Кроме того, вы можете выбрать количество сгибов в перекрестной проверке k-складок, как в данном случае 4. Вы уже видели большую часть кода в этом фрагменте ранее в наших предыдущих примерах. Помимо RandomSearchCVи KerasClassifier, я добавил небольшой блок кода, обрабатывающий оценку:

from keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import RandomizedSearchCV

# Main settings
epochs = 20
embedding_dim = 50
maxlen = 100
output_file = 'data/output.txt'

# Run grid search for each source (yelp, amazon, imdb)
for source, frame in df.groupby('source'):
    print('Running grid search for data set :', source)
    sentences = df['sentence'].values
    y = df['label'].values

    # Train-test split
    sentences_train, sentences_test, y_train, y_test = train_test_split(
        sentences, y, test_size=0.25, random_state=1000)

    # Tokenize words
    tokenizer = Tokenizer(num_words=5000)
    tokenizer.fit_on_texts(sentences_train)
    X_train = tokenizer.texts_to_sequences(sentences_train)
    X_test = tokenizer.texts_to_sequences(sentences_test)

    # Adding 1 because of reserved 0 index
    vocab_size = len(tokenizer.word_index) + 1

    # Pad sequences with zeros
    X_train = pad_sequences(X_train, padding='post', maxlen=maxlen)
    X_test = pad_sequences(X_test, padding='post', maxlen=maxlen)

    # Parameter grid for grid search
    param_grid = dict(num_filters=[32, 64, 128],
                      kernel_size=[3, 5, 7],
                      vocab_size=[vocab_size],
                      embedding_dim=[embedding_dim],
                      maxlen=[maxlen])
    model = KerasClassifier(build_fn=create_model,
                            epochs=epochs, batch_size=10,
                            verbose=False)
    grid = RandomizedSearchCV(estimator=model, param_distributions=param_grid,
                              cv=4, verbose=1, n_iter=5)
    grid_result = grid.fit(X_train, y_train)

    # Evaluate testing set
    test_accuracy = grid.score(X_test, y_test)

    # Save and evaluate results
    prompt = input(f'finished {source}; write to file and proceed? [y/n]')
    if prompt.lower() not in {'y', 'true', 'yes'}:
        break
    with open(output_file, 'a') as f:
        s = ('Running {} data set\nBest Accuracy : '
             '{:.4f}\n{}\nTest Accuracy : {:.4f}\n\n')
        output_string = s.format(
            source,
            grid_result.best_score_,
            grid_result.best_params_,
            test_accuracy)
        print(output_string)
        f.write(output_string)

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

Running amazon data set
Best Accuracy : 0.8122
{'vocab_size': 4603, 'num_filters': 64, 'maxlen': 100, 'kernel_size': 5, 'embedding_dim': 50}
Test Accuracy : 0.8457

Running imdb data set
Best Accuracy : 0.8161
{'vocab_size': 4603, 'num_filters': 128, 'maxlen': 100, 'kernel_size': 5, 'embedding_dim': 50}
Test Accuracy : 0.8210

Running yelp data set
Best Accuracy : 0.8127
{'vocab_size': 4603, 'num_filters': 64, 'maxlen': 100, 'kernel_size': 7, 'embedding_dim': 50}
Test Accuracy : 0.8384

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

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

Вывод

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

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

Одной большой темой, которую мы не затронули здесь, на другой раз, были периодические нейронные сети , в частности LSTM и GRU . Это другие мощные и популярные инструменты для работы с последовательными данными, такими как текст или временные ряды. Другие интересные разработки в настоящее время относятся к нейронным сетям, которые привлекают внимание, которые находятся в стадии активного исследования и, по-видимому, являются многообещающим следующим шагом, поскольку LSTM, как правило, тяжелы в вычислениях.

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

  • Обнаружение спама в электронных письмах
  • Автоматическая пометка текстов
  • Категоризация новостных статей с заранее заданными темами

Вы можете использовать эти знания и модели, которые вы изучили в продвинутом проекте, как в этом руководстве, чтобы использовать анализ настроений в непрерывном потоке данных Twitter с Kibana и Elasticsearch. Вы также можете объединить анализ настроений или классификацию текста с распознаванием речи, как в этом удобном руководстве, используя библиотеку SpeechRecognition в Python.


Совершенствуй знания каждый день у нас в Телеграм-каналах

Вопросы, реклама — VK | Telegram