Глубокое обучение Python для обработки текстовых данных

глубокое обучение Python

Это 6-й день моего участия в августовском испытании обновлений.Подробности о мероприятии:Испытание августовского обновления

Эта статья посвящена изучению «Глубокого обучения с помощью Python» (второе издание, Франсуа Шолле).Глава 6 Глубокое обучение для текста и последовательностей (Chapter 6. Deep learning for text and sequences) Примечания. Ты сможешьнажмите на эту ссылкуПрочтите оригинальный текст этой книги онлайн (на английском языке).

Обработка текстовых данных с помощью глубокого обучения

6.1 Working with text data

Для обработки текстовых данных нейронными сетями глубокого обучения, аналогично изображениям, данные также векторизуются: текст -> числовой тензор.

Для этого вы можете превратить каждое слово в вектор, вы можете превратить символ в вектор и вы можете превратить несколько последовательных слов или символов (называемыхN-grams) в вектор.

В любом случае, независимо от того, как он разделен, единица, на которую мы разделяем текст, называетсяtokens(разметка), процесс разбиения текста называетсяtokenization(Причастие).

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

Векторизация текста заключается в том, чтобы сначала сегментировать слова, затем сопоставить сгенерированные токены числовым векторам один за другим и, наконец, объединить соответствующие числовые векторы в тензор, выражающий исходный текст. Среди них более интересно, как установить связь между токеном и числовым вектором, Вот два метода для этого: горячее кодирование (one-hot encoding) и встраивание токена (встраивание метки), где встраивание токена обычно используется для слов, называемых встраиванием слов.

文本的向量化:从文本到token再到张量

n-граммы и набор слов

N-грамма — это набор из ≤N последовательных слов, которые можно извлечь из предложения. Например: «Кошка села на коврик».

Если разбить это предложение на 2 грамма, получится:

{"The", "The cat", "cat", "cat sat", "sat",
  "sat on", "on", "on the", "the", "the mat", "mat"}

Этот набор называется пакетом по 2 грамма.

Разложение на 3 грамма:

{"The", "The cat", "cat", "cat sat", "The cat sat",
  "sat", "sat on", "on", "cat sat on", "on the", "the",
  "sat on the", "the mat", "mat", "on the mat"}

Этот набор называется пакетом по 3 грамма.

Это называется «мешок», потому что это просто набор токенов без порядка и значения исходного текста. Метод сегментации слов, который делит текст на такие пакеты, называется «мешок слов».

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

однократное кодирование

One-hot является относительно простым и широко используемым. Он делает это, связывая каждый токен с уникальным целочисленным индексом, а затем преобразовывая целочисленный индекс i в двоичный вектор длины N (N — размер словаря), где только i-й элемент этого вектора равен 1, а остальные элементы равны 0 .

Вот пример горячего кодирования для двух версий игрушек:

# 单词级的 one-hot 编码

import numpy as np

samples = ['The cat sat on the mat.', 'The dog ate my homework.']

token_index = {}
for sample in samples:
    for word in sample.split():
        if word not in token_index:
            token_index[word] = len(token_index) + 1
            
# 对样本进行分词。只考虑每个样本前 max_length 个单词
max_length = 10

results = np.zeros(shape=(len(samples), 
                          max_length, 
                          max(token_index.values()) + 1))
for i, sample in enumerate(samples):
    for j, word in list(enumerate(sample.split()))[:max_length]:
        index = token_index.get(word)
        results[i, j, index] = 1.

print(results)
[[[0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 1. 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. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
  [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
  [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.]]]
# 字符级的 one-hot 编码

import string

samples = ['The cat sat on the mat.', 'The dog ate my homework.']

characters = string.printable    # 所有可打印的 ASCII 字符
token_index = dict(zip(range(1, len(characters) + 1), characters))

max_length = 50
results = np.zeros((len(samples), max_length, max(token_index.keys()) + 1))
for i, sample in enumerate(samples):
    for j, character in enumerate(sample):
        index = token_index.get(character)
        results[i, j, index] = 1.
        
print(results)
[[[1. 1. 1. ... 1. 1. 1.]
  [1. 1. 1. ... 1. 1. 1.]
  [1. 1. 1. ... 1. 1. 1.]
  ...
  [0. 0. 0. ... 0. 0. 0.]
  [0. 0. 0. ... 0. 0. 0.]
  [0. 0. 0. ... 0. 0. 0.]]

 [[1. 1. 1. ... 1. 1. 1.]
  [1. 1. 1. ... 1. 1. 1.]
  [1. 1. 1. ... 1. 1. 1.]
  ...
  [0. 0. 0. ... 0. 0. 0.]
  [0. 0. 0. ... 0. 0. 0.]
  [0. 0. 0. ... 0. 0. 0.]]]

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

from tensorflow.keras.preprocessing.text import Tokenizer

samples = ['The cat sat on the mat.', 'The dog ate my homework.']

tokenizer = Tokenizer(num_words=1000)    # 只考虑前 1000 个最常见的单词
tokenizer.fit_on_texts(samples)

sequences = tokenizer.texts_to_sequences(samples)    # 将字符串转换为整数索引组成的列表
print('sequences:', sequences)

one_hot_results = tokenizer.texts_to_matrix(samples, mode='binary')  # 直接得到 one-hot 二进制表示

word_index = tokenizer.word_index    # 单词索引,就是词表字典啦,用这个就可以还原数据

print(f'one_hot_results: shape={one_hot_results.shape}:\n', one_hot_results, )
print(f'Found {len(word_index)} unique tokens.', 'word_index:', word_index)
sequences: [[1, 2, 3, 4, 1, 5], [1, 6, 7, 8, 9]]
one_hot_results: shape=(2, 1000):
 [[0. 1. 1. ... 0. 0. 0.]
 [0. 1. 0. ... 0. 0. 0.]]
Found 9 unique tokens. word_index: {'the': 1, 'cat': 2, 'sat': 3, 'on': 4, 'mat': 5, 'dog': 6, 'ate': 7, 'my': 8, 'homework': 9}

Существует также более простой вариант этого одноразового кодирования, который называетсяone-hot hashing trick(метод горячего хэширования), идея этого метода заключается не в том, чтобы связать уникальный целочисленный индекс с каждым токеном, а в том, чтобы использовать хеш-функцию для прямого отображения текста в вектор фиксированной длины.

Этот метод может сэкономить накладные расходы памяти на поддержание индекса слов, а также может реализовать онлайн-кодирование (кодировать по одному, не затрагивая его и позже); но есть и некоторые недостатки: могут возникать коллизии хэшей, и закодированные данные не могут быть снижение.

# 使用散列技巧的单词级的 one-hot 编码,玩具版本

samples = ['The cat sat on the mat.', 'The dog ate my homework.']

dimensionality = 1000  # 将单词保存为长度为 1000 的向量,单词越多这个值就要越大,不然散列冲突可能会加大
max_length = 10

results = np.zeros((len(samples), max_length, dimensionality))
for i, sample in enumerate(samples):
    for j, word in list(enumerate(sample.split()))[:max_length]:
        index = abs(hash(word)) % dimensionality  # 将单词散列到 0~dimensionality 范围内的一个随机整数索引
        results[i, j, index] = 1.

print(results.shape)
print(results)
(2, 10, 1000)
[[[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.]]]

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

Из предыдущего примера также видно, что результирующий вектор, полученный этим жестким кодированием one-hot, очень разреженный и имеет относительно большую размерность.

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

one-hot编码与词嵌入的区别

Существует два способа использования встраивания слов:

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

Изучение встраивания слов с помощью слоев встраивания

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

词嵌入空间的简单示例

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

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

Поэтому очень трудно иметь такое идеальное пространство для встраивания слов, и его пока нет. Но с глубоким обучением мы все еще можем получить лучшее пространство для встраивания слов для конкретной проблемы. В Keras нам нужно только позволить модели узнать вес слоя Embedding, чтобы получить пространство для встраивания слова для текущей задачи:

from tensorflow.keras.layers import Embedding

embedding_layer = Embedding(1000, 64)  # Embedding(可能的token个数, 嵌入的维度)

Слой Embedding на самом деле является словарем, который сопоставляет целочисленный индекс, представляющий конкретное слово, с вектором слов.

Embedding 层

Входные данные для слоя Embedding имеют форму(samples, sequence_length)Двумерный целочисленный тензор . Один элемент в этом входном тензоре — это последовательность целых чисел, представляющая последовательность текста. Все последовательности во входных данных должны иметь одинаковую длину, более короткие последовательности должны быть дополнены нулями, а более длинные последовательности должны быть усечены.

Выходной вывод слоя встраивания в форме(samples, sequence_length, embedding_dimensionality)Трехмерный тензор с плавающей запятой. Затем этот вывод может быть обработан RNN или Conv1D для других целей.

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

Пример: обработка прогноза тональности обзора фильма IMDB с помощью слоя внедрения.

# 加载 IMDB 数据,准备用于 Embedding 层

from tensorflow.keras.datasets import imdb
from tensorflow.keras import preprocessing

max_features = 10000    # 作为特征的单词个数
maxlen = 20    # 在 maxlen 个特征单词后截断文本

(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)

# 将整数列表转换成形状为 (samples, maxlen) 的二维整数张量
x_train = preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = preprocessing.sequence.pad_sequences(x_test, maxlen=maxlen)
# 在 IMDB 数据上使用 Embedding 层和分类器

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Flatten, Dense

model = Sequential()
model.add(Embedding(10000, 8, input_length=maxlen))  # (samples, maxlen, 8)
model.add(Flatten())  # (samles, maxlen*8)
model.add(Dense(1, activation='sigmoid'))  # top classifier

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['acc'])
model.summary()

history = model.fit(x_train, y_train, 
                    epochs=10, 
                    batch_size=32, 
                    validation_split=0.2)

Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_2 (Embedding)      (None, 20, 8)             80000     
_________________________________________________________________
flatten_1 (Flatten)          (None, 160)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 161       
=================================================================
Total params: 80,161
Trainable params: 80,161
Non-trainable params: 0
_________________________________________________________________
Epoch 1/10
625/625 [==============================] - 1s 1ms/step - loss: 0.6686 - acc: 0.6145 - val_loss: 0.6152 - val_acc: 0.6952
...
Epoch 10/10
625/625 [==============================] - 1s 886us/step - loss: 0.3017 - acc: 0.8766 - val_loss: 0.5260 - val_acc: 0.7508

Здесь мы только расширяем последовательность встраивания слов и используем плотный слой для завершения классификации, что заставит модель обрабатывать каждое слово во входной последовательности отдельно, без учета взаимосвязи между словами и структурой предложения. Это заставляет модель думать, что «этот фильм — бомба» и «этот фильм — бомба» являются негативными отзывами. Чтобы выучить все предложение, вам нужно использовать RNN или Conv1D, которые будут представлены позже.

Используйте предварительно обученные вложения слов

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

Распространенные вложения слов обычно рассчитываются с использованием статистики частотности слов.Теперь у нас есть много вариантов на выбор, таких как word2vec, GloVe и т. д. Конкретные принципы более сложны, мы можем использовать их в первую очередь.

Мы попробуем GloVe в примерах ниже.

От необработанного текста к встраиванию слов

Мы пытаемся начать с необработанных данных IMDB (которые представляют собой кучу текста), обработать данные и выполнить встраивание слов.

Скачать необработанный текст данных IMDB

Исходный набор данных IMDB можно загрузить сmng.bz/0tIoСкачать (наконец-то перейти к тому, что от aws s3Да 3. Amazon AWS.com/text — привкус..., Ненаучный доступ в Интернет очень медленный).

Загруженный набор данных после распаковки выглядит так:

aclImdb
├── test
│   ├── neg
│   └── pos
└── train
    ├── neg
    └── pos

Под каждым каталогом neg/pos находится куча.txtфайлы, каждый с комментарием.

Ниже мы преобразуем комментарии поезда в список строк, по одному комментарию на строку, и записываем соответствующие метки (neg/pos) в список меток.

# 处理 IMDB 原始数据的标签

import os

imdb_dir = '/Volumes/WD/Files/dataset/aclImdb'
train_dir = os.path.join(imdb_dir, 'train')

texts = []
labels = []

for label_type in ['neg', 'pos']:
    dir_name = os.path.join(train_dir, label_type)
    for fname in os.listdir(dir_name):
        if fname.endswith('.txt'):
            with open(os.path.join(dir_name, fname)) as f:
                texts.append(f.read())
            labels.append(0 if label_type == 'neg' else 1)

Взгляните на данные:

print(labels[0], texts[0], sep=' --> ')
print(labels[-1], texts[-1], sep=' --> ')
print(len(texts), len(labels))

0 --> Working with one of the best Shakespeare sources, this film manages to be creditable to it's source, whilst still appealing to a wider audience. Branagh steals the film from under Fishburne's nose, and there's a talented cast on good form.

1 --> Enchanted April is a tone poem, an impressionist painting, a masterpiece of conveying a message with few words. It has been one of my 10 favorite films since it came out. I continue to wait, albeit less patiently, for the film to come out in DVD format. Apparently, I am not alone. If parent company Amazon's listings are correct, there are many people who want this title in DVD format. Many people want to go to Italy with this cast and this script. Many people want to keep a permanent copy of this film in their libraries. The cast is spectacular, the cinematography and direction impeccable. The film is a definite keeper. Many have already asked. Please add our names to the list.

Здесь тексты и этикетки длина 25000.

словесная сегментация данных

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

# 对 IMDB 原始数据的文本进行分词

import numpy as np

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

maxlen = 100  # 只看每条评论的前100个词
training_samples = 200
validation_samples = 10000
max_words = 10000

tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)
word_index = tokenizer.word_index
print(f'Found {len(word_index)} unique tokens.')

data = pad_sequences(sequences, maxlen=maxlen)

labels = np.asarray(labels)

print('Shape of data tensor:', data.shape)
print('Shape of label tensor:', labels.shape)

# 打乱数据
indices = np.arange(labels.shape[0])
np.random.shuffle(indices)
data = data[indices]
labels = labels[indices]

# 划分训练、验证集
x_train = data[:training_samples]
y_train = labels[:training_samples]
x_val = data[training_samples: training_samples + validation_samples]
y_val = labels[training_samples: training_samples + validation_samples]

вывод:

Found 88582 unique tokens.
Shape of data tensor: (25000, 100)
Shape of label tensor: (25000,)

Скачать вложения слов GloVe

Загрузите предварительно обученные вложения слов GloVe:NLP.Stanford.quote/data/glove. …

Запишите, чтобы извлечь его, сэкономив на обучении 40 000 токенов в виде простого текста.

Предварительно обработайте встраивание

Разобрать распакованный файл:

glove_dir = '/Volumes/WD/Files/glove.6B'

embeddings_index = {}

with open(os.path.join(glove_dir, 'glove.6B.100d.txt')) as f:
    for line in f:
        values = line.split()
        word = values[0]
        coefs = np.asarray(values[1:], dtype='float32')
        embeddings_index[word] = coefs

print(f'Found {len(embeddings_index)} word vectors.')
Found 400000 word vectors.

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

embedding_dim = 100

embedding_matrix = np.zeros((max_words, embedding_dim))
for word, i in word_index.items():
    if i < max_words:
        embedding_vector = embeddings_index.get(word)  # 有的就用 embeddings_index 里的词向量
        if embedding_vector is not None:               # 没有就用全零
            embedding_matrix[i] = embedding_vector
            
print(embedding_matrix)
[[ 0.          0.          0.         ...  0.          0.
   0.        ]
 [-0.038194   -0.24487001  0.72812003 ... -0.1459      0.82779998
   0.27061999]
 [-0.071953    0.23127     0.023731   ... -0.71894997  0.86894
   0.19539   ]
 ...
 [-0.44036001  0.31821999  0.10778    ... -1.29849994  0.11824
   0.64845002]
 [ 0.          0.          0.         ...  0.          0.
   0.        ]
 [-0.54539001 -0.31817999 -0.016281   ... -0.44865     0.067047
   0.17975999]]

Определите модель

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Flatten, Dense

model = Sequential()
model.add(Embedding(max_words, embedding_dim, input_length=maxlen))
model.add(Flatten())
model.add(Dense(32, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_3 (Embedding)      (None, 100, 100)          1000000   
_________________________________________________________________
flatten_2 (Flatten)          (None, 10000)             0         
_________________________________________________________________
dense_2 (Dense)              (None, 32)                320032    
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 33        
=================================================================
Total params: 1,320,065
Trainable params: 1,320,065
Non-trainable params: 0
_________________________________________________________________

Загрузите встраивание слова GloVe в модель

model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False

Обучение и оценка моделей

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy', 
              metrics=['acc'])
history = model.fit(x_train, y_train, 
                    epochs=10, 
                    batch_size=32, 
                    validation_data=(x_val, y_val))
model.save_weights('pre_trained_glove_model.h5')

Составьте график процесса обучения, чтобы увидеть:

import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'bo-', label='Training acc')
plt.plot(epochs, val_acc, 'rs-', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'bo-', label='Training loss')
plt.plot(epochs, val_loss, 'rs-', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

png

png

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

# 构建模型

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Flatten, Dense

model = Sequential()
model.add(Embedding(max_words, embedding_dim, input_length=maxlen))
model.add(Flatten())
model.add(Dense(32, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

model.summary()

# 不使用 GloVe 词嵌入

# 训练

model.compile(optimizer='rmsprop', 
              loss='binary_crossentropy', 
              metrics=['acc'])
history = model.fit(x_train, y_train, 
                    epochs=10, 
                    batch_size=32, 
                    validation_data=(x_val, y_val))
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_4 (Embedding)      (None, 100, 100)          1000000   
_________________________________________________________________
flatten_3 (Flatten)          (None, 10000)             0         
_________________________________________________________________
dense_4 (Dense)              (None, 32)                320032    
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 33        
=================================================================
Total params: 1,320,065
Trainable params: 1,320,065
Non-trainable params: 0
_________________________________________________________________

Сюжет тренировочного процесса:

png

png

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

Наконец, давайте посмотрим на результаты на тестовом наборе:

# 对测试集数据进行分词

test_dir = os.path.join(imdb_dir, 'test')

texts = []
labels = []

for label_type in ['neg', 'pos']:
    dir_name = os.path.join(test_dir, label_type)
    for fname in sorted(os.listdir(dir_name)):
        if fname.endswith('.txt'):
            with open(os.path.join(dir_name, fname)) as f:
                texts.append(f.read())
            labels.append(0 if label_type == 'neg' else 1)

sequences = tokenizer.texts_to_sequences(texts)
x_test = pad_sequences(sequences, maxlen=maxlen)
y_test = np.asarray(labels)

# 在测试集上评估模型

model.load_weights('pre_trained_glove_model.h5')
model.evaluate(x_test, y_test)

результат:

[1.1397335529327393, 0.512719988822937]

эммм, окончательный прогресс — потрясающие 50%+! По-прежнему сложно тренироваться с таким небольшим объемом данных.