Вступление
Это 19-я статья в моей серии статей по Python для НЛП. Из последних нескольких статей мы изучали довольно продвинутые концепции НЛП, основанные на методах глубокого обучения. В предыдущей статье мы видели , как создать модель классификации текста, обученную с использованием нескольких входных данных различных типов данных. Мы разработали предиктор настроений текста, используя текстовые входные данные и метаинформацию.
В этой статье мы рассмотрим, как разработать модель классификации текста с несколькими выходами. Мы будем разрабатывать модель классификации текста, которая анализирует текстовый комментарий и предсказывает несколько меток, связанных с комментарием. Проблема классификации с несколькими метками на самом деле является подмножеством модели множественного вывода. В конце этой статьи вы сможете выполнить классификацию текста с несколькими метками для ваших данных.
Подход, описанный в этой статье, может быть расширен для выполнения общей классификации с несколькими метками. Например, вы можете решить задачу классификации, когда у вас есть изображение в качестве входных данных, и вы хотите предсказать категорию изображения и описание изображения.
На этом этапе важно объяснить разницу между проблемой классификации по нескольким классам и классификацией по нескольким меткам. В задаче многоклассовой классификации экземпляр или запись могут принадлежать одному и только одному из нескольких выходных классов. Например, в задаче анализа настроений, которую мы изучали в предыдущей статье, текстовый обзор может быть либо “хорошим”, либо “плохим”, либо “средним”. Он не мог быть одновременно и “хорошим”, и “средним”. С другой стороны, в задачах классификации с несколькими метками экземпляр может иметь несколько выходов одновременно. Например, в задаче классификации текста, которую мы собираемся решить в этой статье, комментарий может иметь несколько тегов. Эти теги включают в себя “токсичные”, “непристойные”, “оскорбительные” и т. Д.
Набор данных
Набор данных содержит комментарии из Страница обсуждения Википедии правки. Существует шесть выходных меток для каждого комментария: токсичный, severe_toxic, непристойный, угроза, оскорбление и identity_hate. Комментарий может принадлежать всем этим категориям или подмножеству этих категорий, что делает его проблемой классификации с несколькими метками.
Набор данных для этой статьи можно скачать по этой ссылке Kaggle . Мы будем использовать только файл “train.csv”, содержащий 160 000 записей.
Загрузите CSV – файл в свой локальный каталог. Я переименовал файл в “toxic_comments.csv”. Вы можете дать ему любое имя, но только обязательно используйте это имя в своем коде.
Теперь давайте импортируем необходимые библиотеки и загрузим набор данных в наше приложение. Следующий сценарий импортирует необходимые библиотеки:
from numpy import array from keras.preprocessing.text import one_hot from keras.preprocessing.sequence import pad_sequences from keras.models import Sequential from keras.layers.core import Activation, Dropout, Dense from keras.layers import Flatten, LSTM from keras.layers import GlobalMaxPooling1D from keras.models import Model from keras.layers.embeddings import Embedding from sklearn.model_selection import train_test_split from keras.preprocessing.text import Tokenizer from keras.layers import Input from keras.layers.merge import Concatenate import pandas as pd import numpy as np import re import matplotlib.pyplot as plt
Теперь давайте загрузим набор данных в память:
toxic_comments = pd.read_csv("/content/drive/My Drive/Colab Datasets/toxic_comments.csv")
Следующий сценарий отображает форму набора данных, а также печатает заголовок набора данных:
print(toxic_comments.shape) toxic_comments.head()
Выход:
(159571,8)
Набор данных содержит 159571 запись и 8 столбцов. Заголовок набора данных выглядит следующим образом:
Давайте удалим все записи, где любая строка содержит нулевое значение или пустую строку.
filter = toxic_comments["comment_text"] != "" toxic_comments = toxic_comments[filter] toxic_comments = toxic_comments.dropna()
Столбец comment_text
содержит текстовые комментарии. Давайте напечатаем случайный комментарий, а затем посмотрим метки для комментариев.
print(toxic_comments["comment_text"][168])
Выход:
You should be fired, you're a moronic wimp who is too lazy to do research. It makes me sick that people like you exist in this world.
Это явно ядовитый комментарий. Давайте посмотрим связанные метки с этим комментарием:
print("Toxic:" + str(toxic_comments["toxic"][168])) print("Severe_toxic:" + str(toxic_comments["severe_toxic"][168])) print("Obscene:" + str(toxic_comments["obscene"][168])) print("Threat:" + str(toxic_comments["threat"][168])) print("Insult:" + str(toxic_comments["insult"][168])) print("Identity_hate:" + str(toxic_comments["identity_hate"][168]))
Выход:
Toxic:1 Severe_toxic:0 Obscene:0 Threat:0 Insult:1 Identity_hate:0
Теперь давайте построим график количества комментариев для каждой метки. Для этого мы сначала отфильтруем все метки или выходные столбцы.
toxic_comments_labels = toxic_comments[["toxic", "severe_toxic", "obscene", "threat", "insult", "identity_hate"]] toxic_comments_labels.head()
Выход:
Используя фрейм данных toxic_comments_labels
, мы построим гистограммы, которые покажут общее количество комментариев для различных меток.
fig_size = plt.rcParams["figure.figsize"] fig_size[0] = 10 fig_size[1] = 8 plt.rcParams["figure.figsize"] = fig_size toxic_comments_labels.sum(axis=0).plot.bar()
Выход:
Вы можете видеть, что “токсичный” комментарий имеет самую высокую частоту встречаемости, за которым следуют “непристойный” и “оскорбительный” соответственно.
Мы успешно проанализировали наш набор данных, и в следующем разделе мы создадим модели классификации с несколькими метками, используя этот набор данных.
Создание Моделей Классификации Текста с Несколькими метками
Существует два способа создания моделей классификации с несколькими метками: использование одного плотного выходного слоя и использование нескольких плотных выходных слоев.
В первом подходе мы можем использовать один плотный слой с шестью выходами с сигмоидными функциями активации и бинарными функциями кросс-энтропийных потерь. Каждый нейрон в выходном плотном слое будет представлять одну из шести выходных меток. Сигмовидная функция активации возвращает значение от 0 до 1 для каждого нейрона. Если выходное значение какого-либо нейрона больше 0,5, предполагается, что комментарий принадлежит классу, представленному этим конкретным нейроном.
Во втором подходе мы создадим один плотный выходной слой для каждой метки. На выходе мы получим в общей сложности 6 плотных слоев. Каждый слой будет иметь свою сигмовидную функцию.
Модель классификации текста с несколькими метками и Одним Выходным слоем
В этом разделе мы создадим модель классификации текста с несколькими метками и одним выходным слоем. Как всегда, первым шагом в модели классификации текста является создание функции, ответственной за очистку текста.
def preprocess_text(sen): # Remove punctuations and numbers sentence = re.sub('[^a-zA-Z]', ' ', sen) # Single character removal sentence = re.sub(r"\s+[a-zA-Z]\s+", ' ', sentence) # Removing multiple spaces sentence = re.sub(r'\s+', ' ', sentence) return sentence
На следующем шаге мы создадим наш входной и выходной набор. Входными данными являются комментарии из столбца comment_text
. Мы очистим все комментарии и сохраним их в переменной X
. Метки или выходные данные уже были сохранены в фрейме данных toxic_comments_labels
. Мы будем использовать эти значения фрейма данных для хранения выходных данных в переменной y
. Посмотрите на следующий сценарий:
X = [] sentences = list(toxic_comments["comment_text"]) for sen in sentences: X.append(preprocess_text(sen)) y = toxic_comments_labels.values
Здесь нам не нужно выполнять какое-либо одногорячее кодирование, потому что наши выходные метки уже находятся в форме векторов с одним горячим кодированием.
На следующем этапе мы разделим наши данные на обучающие и тестовые наборы:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42)
Нам нужно преобразовать текстовые входные данные во встроенные векторы. Чтобы более подробно понять встраивание слов, пожалуйста, обратитесь к моей статье о встраивании слов .
tokenizer = Tokenizer(num_words=5000) tokenizer.fit_on_texts(X_train) X_train = tokenizer.texts_to_sequences(X_train) X_test = tokenizer.texts_to_sequences(X_test) vocab_size = len(tokenizer.word_index) + 1 maxlen = 200 X_train = pad_sequences(X_train, padding='post', maxlen=maxlen) X_test = pad_sequences(X_test, padding='post', maxlen=maxlen)
Мы будем использовать встраивания перчаток для преобразования текстовых входных данных в их числовые аналоги.
from numpy import array from numpy import asarray from numpy import zeros embeddings_dictionary = dict() glove_file = open('/content/drive/My Drive/Colab Datasets/glove.6B.100d.txt', encoding="utf8") for line in glove_file: records = line.split() word = records[0] vector_dimensions = asarray(records[1:], dtype='float32') embeddings_dictionary[word] = vector_dimensions glove_file.close() embedding_matrix = zeros((vocab_size, 100)) for word, index in tokenizer.word_index.items(): embedding_vector = embeddings_dictionary.get(word) if embedding_vector is not None: embedding_matrix[index] = embedding_vector
Следующий сценарий создает модель. Наша модель будет иметь один входной слой, один слой встраивания, один слой LSTM со 128 нейронами и один выходной слой с 6 нейронами, так как у нас есть 6 меток на выходе.
deep_inputs = Input(shape=(maxlen,)) embedding_layer = Embedding(vocab_size, 100, weights=[embedding_matrix], trainable=False)(deep_inputs) LSTM_Layer_1 = LSTM(128)(embedding_layer) dense_layer_1 = Dense(6, activation='sigmoid')(LSTM_Layer_1) model = Model(inputs=deep_inputs, outputs=dense_layer_1) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['acc'])
Давайте распечатаем сводку модели:
print(model.summary())
Выход:
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) (None, 200) 0 _________________________________________________________________ embedding_1 (Embedding) (None, 200, 100) 14824300 _________________________________________________________________ lstm_1 (LSTM) (None, 128) 117248 _________________________________________________________________ dense_1 (Dense) (None, 6) 774 ================================================================= Total params: 14,942,322 Trainable params: 118,022 Non-trainable params: 14,824,300
Следующий скрипт печатает архитектуру нашей нейронной сети:
from keras.utils import plot_model plot_model(model, to_file='model_plot4a.png', show_shapes=True, show_layer_names=True)
Выход:
Из рисунка выше видно, что выходной слой содержит только 1 плотный слой с 6 нейронами. Давайте теперь потренируем нашу модель:
history = model.fit(X_train, y_train, batch_size=128, epochs=5, verbose=1, validation_split=0.2)
Мы будем тренировать нашу модель в течение 5 эпох. Вы можете тренировать модель с большим количеством эпох и посмотреть, получите ли вы лучшие или худшие результаты.
Результат для всех 5 эпох выглядит следующим образом:
rain on 102124 samples, validate on 25532 samples Epoch 1/5 102124/102124 [==============================] - 245s 2ms/step - loss: 0.1437 - acc: 0.9634 - val_loss: 0.1361 - val_acc: 0.9631 Epoch 2/5 102124/102124 [==============================] - 245s 2ms/step - loss: 0.0763 - acc: 0.9753 - val_loss: 0.0621 - val_acc: 0.9788 Epoch 3/5 102124/102124 [==============================] - 243s 2ms/step - loss: 0.0588 - acc: 0.9800 - val_loss: 0.0578 - val_acc: 0.9802 Epoch 4/5 102124/102124 [==============================] - 246s 2ms/step - loss: 0.0559 - acc: 0.9807 - val_loss: 0.0571 - val_acc: 0.9801 Epoch 5/5 102124/102124 [==============================] - 245s 2ms/step - loss: 0.0528 - acc: 0.9813 - val_loss: 0.0554 - val_acc: 0.9807
Давайте теперь оценим нашу модель на тестовом наборе:
score = model.evaluate(X_test, y_test, verbose=1) print("Test Score:", score[0]) print("Test Accuracy:", score[1])
Выход:
31915/31915 [==============================] - 108s 3ms/step Test Score: 0.054090796736467786 Test Accuracy: 0.9810642735274182
Наша модель достигает точности около 98%, что довольно впечатляет.
Наконец, мы построим график значений потерь и точности для обучающих и тестовых наборов, чтобы увидеть, является ли наша модель переоснащенной.
import matplotlib.pyplot as plt plt.plot(history.history['acc']) plt.plot(history.history['val_acc']) plt.title('model accuracy') plt.ylabel('accuracy') plt.xlabel('epoch') plt.legend(['train','test'], loc='upper left') plt.show() plt.plot(history.history['loss']) plt.plot(history.history['val_loss']) plt.title('model loss') plt.ylabel('loss') plt.xlabel('epoch') plt.legend(['train','test'], loc='upper left') plt.show()
Выход:
Вы можете видеть, что модель не слишком подходит для набора валидации.
Модель классификации текста с несколькими метками и несколькими выходными слоями
В этом разделе мы создадим модель классификации текста с несколькими метками, где каждая выходная метка будет иметь выделенный выходной плотный слой. Давайте сначала определим нашу функцию предварительной обработки:
def preprocess_text(sen): # Remove punctuations and numbers sentence = re.sub('[^a-zA-Z]', ' ', sen) # Single character removal sentence = re.sub(r"\s+[a-zA-Z]\s+", ' ', sentence) # Removing multiple spaces sentence = re.sub(r'\s+', ' ', sentence) return sentence
Второй шаг-создание входных и выходных данных для модели. Входными данными для модели будут текстовые комментарии, а выходными-шесть меток. Следующий сценарий создает входной слой и объединенный выходной слой:
X = [] sentences = list(toxic_comments["comment_text"]) for sen in sentences: X.append(preprocess_text(sen)) y = toxic_comments[["toxic", "severe_toxic", "obscene", "threat", "insult", "identity_hate"]]
Давайте разделим данные на обучающие и тестовые наборы:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42)
Переменная y
содержит комбинированный вывод из 6 меток. Однако мы хотим создать отдельный выходной слой для каждой метки. Мы создадим 6 переменных, которые хранят отдельные метки из обучающих данных, и 6 переменных, которые хранят отдельные значения меток для тестовых данных.
Посмотрите на следующий сценарий:
# First output y1_train = y_train[["toxic"]].values y1_test = y_test[["toxic"]].values # Second output y2_train = y_train[["severe_toxic"]].values y2_test = y_test[["severe_toxic"]].values # Third output y3_train = y_train[["obscene"]].values y3_test = y_test[["obscene"]].values # Fourth output y4_train = y_train[["threat"]].values y4_test = y_test[["threat"]].values # Fifth output y5_train = y_train[["insult"]].values y5_test = y_test[["insult"]].values # Sixth output y6_train = y_train[["identity_hate"]].values y6_test = y_test[["identity_hate"]].values
Следующим шагом является преобразование текстовых входных данных во встроенные векторы. Это делает следующий сценарий:
tokenizer = Tokenizer(num_words=5000) tokenizer.fit_on_texts(X_train) X_train = tokenizer.texts_to_sequences(X_train) X_test = tokenizer.texts_to_sequences(X_test) vocab_size = len(tokenizer.word_index) + 1 maxlen = 200 X_train = pad_sequences(X_train, padding='post', maxlen=maxlen) X_test = pad_sequences(X_test, padding='post', maxlen=maxlen)
Здесь мы снова будем использовать перчатку слово вложения:
glove_file = open('/content/drive/My Drive/Colab Datasets/glove.6B.100d.txt', encoding="utf8") for line in glove_file: records = line.split() word = records[0] vector_dimensions = asarray(records[1:], dtype='float32') embeddings_dictionary[word] = vector_dimensions glove_file.close() embedding_matrix = zeros((vocab_size, 100)) for word, index in tokenizer.word_index.items(): embedding_vector = embeddings_dictionary.get(word) if embedding_vector is not None: embedding_matrix[index] = embedding_vector
Сейчас самое время создать нашу модель. Наша модель будет иметь один входной слой, один слой встраивания, за которым последует один слой LSTM со 128 нейронами. Выходные данные слоя LSTM будут использоваться в качестве входных данных для 6 плотных выходных слоев. Каждый выходной слой будет иметь 1 нейрон с сигмовидной активационной функцией. Каждый вывод будет предсказывать целочисленное значение от 1 до 0 для соответствующей метки.
Следующий скрипт создает нашу модель:
input_1 = Input(shape=(maxlen,)) embedding_layer = Embedding(vocab_size, 100, weights=[embedding_matrix], trainable=False)(input_1) LSTM_Layer1 = LSTM(128)(embedding_layer) output1 = Dense(1, activation='sigmoid')(LSTM_Layer1) output2 = Dense(1, activation='sigmoid')(LSTM_Layer1) output3 = Dense(1, activation='sigmoid')(LSTM_Layer1) output4 = Dense(1, activation='sigmoid')(LSTM_Layer1) output5 = Dense(1, activation='sigmoid')(LSTM_Layer1) output6 = Dense(1, activation='sigmoid')(LSTM_Layer1) model = Model(inputs=input_1, outputs=[output1, output2, output3, output4, output5, output6]) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['acc'])
Следующий сценарий печатает сводку модели:
print(model.summary())
Выход:
__________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_1 (InputLayer) (None, 200) 0 __________________________________________________________________________________________________ embedding_1 (Embedding) (None, 200, 100) 14824300 input_1[0][0] __________________________________________________________________________________________________ lstm_1 (LSTM) (None, 128) 117248 embedding_1[0][0] __________________________________________________________________________________________________ dense_1 (Dense) (None, 1) 129 lstm_1[0][0] __________________________________________________________________________________________________ dense_2 (Dense) (None, 1) 129 lstm_1[0][0] __________________________________________________________________________________________________ dense_3 (Dense) (None, 1) 129 lstm_1[0][0] __________________________________________________________________________________________________ dense_4 (Dense) (None, 1) 129 lstm_1[0][0] __________________________________________________________________________________________________ dense_5 (Dense) (None, 1) 129 lstm_1[0][0] __________________________________________________________________________________________________ dense_6 (Dense) (None, 1) 129 lstm_1[0][0] ================================================================================================== Total params: 14,942,322 Trainable params: 118,022 Non-trainable params: 14,824,300
И следующий скрипт печатает архитектуру нашей модели:
from keras.utils import plot_model plot_model(model, to_file='model_plot4b.png', show_shapes=True, show_layer_names=True)
Выход:
Вы можете видеть, что у нас есть 6 различных выходных слоев. Приведенный выше рисунок ясно объясняет разницу между моделью с одним входным слоем, которую мы создали в предыдущем разделе, и моделью с несколькими выходными слоями.
Давайте теперь потренируем нашу модель:
history = model.fit(x=X_train, y=[y1_train, y2_train, y3_train, y4_train, y5_train, y6_train], batch_size=8192, epochs=5, verbose=1, validation_split=0.2)
Я пытался запустить модель в течение пяти эпох, но она ужасно переоснащала набор валидации. Я увеличил размер партии, но все равно точность теста была не так хороша. Одна из возможных причин переоснащения заключается в том, что в данном случае мы имеем индивидуальный выходной слой для каждой метки, что увеличивает сложность нашей модели. Увеличение сложности модели часто приводит к переоснащению.
Результат для каждой эпохи показан ниже:
Выход:
Train on 102124 samples, validate on 25532 samples Epoch 1/5 102124/102124 [==============================] - 24s 239us/step - loss: 3.5116 - dense_1_loss: 0.6017 - dense_2_loss: 0.5806 - dense_3_loss: 0.6150 - dense_4_loss: 0.5585 - dense_5_loss: 0.5828 - dense_6_loss: 0.5730 - dense_1_acc: 0.9029 - dense_2_acc: 0.9842 - dense_3_acc: 0.9444 - dense_4_acc: 0.9934 - dense_5_acc: 0.9508 - dense_6_acc: 0.9870 - val_loss: 1.0369 - val_dense_1_loss: 0.3290 - val_dense_2_loss: 0.0983 - val_dense_3_loss: 0.2571 - val_dense_4_loss: 0.0595 - val_dense_5_loss: 0.1972 - val_dense_6_loss: 0.0959 - val_dense_1_acc: 0.9037 - val_dense_2_acc: 0.9901 - val_dense_3_acc: 0.9469 - val_dense_4_acc: 0.9966 - val_dense_5_acc: 0.9509 - val_dense_6_acc: 0.9901 Epoch 2/5 102124/102124 [==============================] - 20s 197us/step - loss: 0.9084 - dense_1_loss: 0.3324 - dense_2_loss: 0.0679 - dense_3_loss: 0.2172 - dense_4_loss: 0.0338 - dense_5_loss: 0.1983 - dense_6_loss: 0.0589 - dense_1_acc: 0.9043 - dense_2_acc: 0.9899 - dense_3_acc: 0.9474 - dense_4_acc: 0.9968 - dense_5_acc: 0.9510 - dense_6_acc: 0.9915 - val_loss: 0.8616 - val_dense_1_loss: 0.3164 - val_dense_2_loss: 0.0555 - val_dense_3_loss: 0.2127 - val_dense_4_loss: 0.0235 - val_dense_5_loss: 0.1981 - val_dense_6_loss: 0.0554 - val_dense_1_acc: 0.9038 - val_dense_2_acc: 0.9900 - val_dense_3_acc: 0.9469 - val_dense_4_acc: 0.9965 - val_dense_5_acc: 0.9509 - val_dense_6_acc: 0.9900 Epoch 3/5 102124/102124 [==============================] - 20s 199us/step - loss: 0.8513 - dense_1_loss: 0.3179 - dense_2_loss: 0.0566 - dense_3_loss: 0.2103 - dense_4_loss: 0.0216 - dense_5_loss: 0.1960 - dense_6_loss: 0.0490 - dense_1_acc: 0.9043 - dense_2_acc: 0.9899 - dense_3_acc: 0.9474 - dense_4_acc: 0.9968 - dense_5_acc: 0.9510 - dense_6_acc: 0.9915 - val_loss: 0.8552 - val_dense_1_loss: 0.3158 - val_dense_2_loss: 0.0566 - val_dense_3_loss: 0.2074 - val_dense_4_loss: 0.0225 - val_dense_5_loss: 0.1960 - val_dense_6_loss: 0.0568 - val_dense_1_acc: 0.9038 - val_dense_2_acc: 0.9900 - val_dense_3_acc: 0.9469 - val_dense_4_acc: 0.9965 - val_dense_5_acc: 0.9509 - val_dense_6_acc: 0.9900 Epoch 4/5 102124/102124 [==============================] - 20s 198us/step - loss: 0.8442 - dense_1_loss: 0.3153 - dense_2_loss: 0.0570 - dense_3_loss: 0.2061 - dense_4_loss: 0.0213 - dense_5_loss: 0.1952 - dense_6_loss: 0.0493 - dense_1_acc: 0.9043 - dense_2_acc: 0.9899 - dense_3_acc: 0.9474 - dense_4_acc: 0.9968 - dense_5_acc: 0.9510 - dense_6_acc: 0.9915 - val_loss: 0.8527 - val_dense_1_loss: 0.3156 - val_dense_2_loss: 0.0558 - val_dense_3_loss: 0.2074 - val_dense_4_loss: 0.0226 - val_dense_5_loss: 0.1951 - val_dense_6_loss: 0.0561 - val_dense_1_acc: 0.9038 - val_dense_2_acc: 0.9900 - val_dense_3_acc: 0.9469 - val_dense_4_acc: 0.9965 - val_dense_5_acc: 0.9509 - val_dense_6_acc: 0.9900 Epoch 5/5 102124/102124 [==============================] - 20s 197us/step - loss: 0.8410 - dense_1_loss: 0.3146 - dense_2_loss: 0.0561 - dense_3_loss: 0.2055 - dense_4_loss: 0.0213 - dense_5_loss: 0.1948 - dense_6_loss: 0.0486 - dense_1_acc: 0.9043 - dense_2_acc: 0.9899 - dense_3_acc: 0.9474 - dense_4_acc: 0.9968 - dense_5_acc: 0.9510 - dense_6_acc: 0.9915 - val_loss: 0.8501 - val_dense_1_loss: 0.3153 - val_dense_2_loss: 0.0553 - val_dense_3_loss: 0.2069 - val_dense_4_loss: 0.0226 - val_dense_5_loss: 0.1948 - val_dense_6_loss: 0.0553 - val_dense_1_acc: 0.9038 - val_dense_2_acc: 0.9900 - val_dense_3_acc: 0.9469 - val_dense_4_acc: 0.9965 - val_dense_5_acc: 0.9509 - val_dense_6_acc: 0.9900
Вы можете видеть, что для каждой эпохи у нас есть значения потерь, потерь значений, точности и точности значений для всех 6 плотных слоев на выходе.
Теперь давайте оценим производительность нашей модели на тестовом наборе:
score = model.evaluate(x=X_test, y=[y1_test, y2_test, y3_test, y4_test, y5_test, y6_test], verbose=1) print("Test Score:", score[0]) print("Test Accuracy:", score[1])
Выход:
31915/31915 [==============================] - 111s 3ms/step Test Score: 0.8471985269747015 Test Accuracy: 0.31425264998511726
Точность всего 31% достигается на тестовом наборе с помощью нескольких выходных слоев.
Следующий сценарий отображает значения потерь и точности для обучающих и проверочных наборов для первого плотного слоя.
import matplotlib.pyplot as plt plt.plot(history.history['dense_1_acc']) plt.plot(history.history['val_dense_1_acc']) plt.title('model accuracy') plt.ylabel('accuracy') plt.xlabel('epoch') plt.legend(['train','test'], loc='upper left') plt.show() plt.plot(history.history['dense_1_loss']) plt.plot(history.history['val_dense_1_loss']) plt.title('model loss') plt.ylabel('loss') plt.xlabel('epoch') plt.legend(['train','test'], loc='upper left') plt.show()
Выход:
Из выходных данных видно, что точность для тестового (валидационного) набора не сходится после первых эпох. Кроме того, разница между точностью обучения и валидации очень мала. Таким образом, модель начинает перестраиваться после первых эпох, и, следовательно, мы получаем плохую производительность на невидимом тестовом наборе.
Вывод
Классификация текста с несколькими метками является одной из наиболее распространенных проблем классификации текста. В этой статье мы изучили два подхода глубокого обучения для классификации текста с несколькими метками. В первом подходе мы использовали один плотный выходной слой с несколькими нейронами, где каждый нейрон представлял одну метку.
Во втором подходе мы создали отдельные плотные слои для каждой метки с одним нейроном. Результаты показывают, что в нашем случае один выходной слой с несколькими нейронами работает лучше, чем несколько выходных слоев.
В качестве следующего шага я бы посоветовал вам изменить функцию активации и разделение теста поезда, чтобы увидеть, сможете ли вы получить лучшие результаты, чем тот, который представлен в этой статье.