Keras LSTM Модель для генерации текста - PullRequest
0 голосов
/ 21 января 2019

Я новичок в Keras и пишу модели Neural Networks, и на самом деле я пытаюсь написать LSTM для генерации текста, но безуспешно.Что я делаю не так?

Я прочитал этот вопрос: здесь и другие статьи, но есть кое-что, чего мне не хватает, я не могу получить, извините, если я выгляжу глупым.

Цель

Моя цель - генерировать английские статьи фиксированной длины (1500 к настоящему времени).

Предположим, у меня есть набор данных из 20 000 записей в последовательностях (в основном, статьях) изразной длины, я установил фиксированную длину для всех статей (MAX_SEQUENCE_LENGTH=1500) и разложил их по токенам, получив матрицу (X, мои тренировочные данные), выглядящую так:

[[   0    0    0 ...   88  664  206]
 [   0    0    0 ...    1   93  140]
 [   0    0    0 ...    3  173 2283]
 ...
 [  50 2761    4 ...  167  148  156]
 [   0    0    0 ...   10   77  206]
 [   0    0    0 ...  167  148  156]]

с формой 20000x1500
вывод моего LSTM должен быть 1 x MAX_SEQUENCE_LENGTH массивом токенов.

Моя модель выглядит так:

def generator_model(sequence_input, embedded_sequences, output_shape):
    layer = LSTM(16,return_sequences = True)(embedded_sequences)
    layer = LSTM(32,return_sequences = True)(layer)
    layer = Flatten()(layer)
    output = Dense(output_shape, activation='softmax')(layer)
    generator = Model(sequence_input, output)
    return generator

с:
sequence_input = Input(batch_shape=(1, 1,1500), dtype='int32')
embedded_sequences = embedding_layer(sequence_input)
output_shape = MAX_SEQUENCE_LENGTH

LSTM должен тренироваться с model.fit() на тренировочном наборе 20k x MAX_SEQUENCE_LENGTH формы (X).

и получение массива токенов с 1 x MAX_SEQUENCE_LENGTH формой в качестве вывода, когда я вызываю model.predict(seed), с seed массивом случайных шумов.

компилируем, подбираем и прогнозируем

комментарии дляследующий раздел:
.generator.compile работает, модель приведена в edit разделе этого поста.
.generator.fit compile, epochs=1 param для целей тестирования, будет BATCH_NUM
.теперь у меня есть некоторые сомнения относительно y, который я даю generator.fit, к настоящему времени я даю матрицу 0 в качестве целевого вывода, если я сгенерирую ее с формой, отличной от X.shape[0], она выдаст ошибку,это означает, что для каждой записи в X должна быть метка.но если я дам ему матрицу 0 как target для model.fit, разве он не предскажет только массивы 0?
.ошибка дает всегда то же самое, несмотря на то, что я использую noise_generator() или noise_integer_generator(), я считаю, что это потому, что ему не нравится параметр y_shape, который я даю

embedding_layer = load_embeddings(word_index)
sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH,))
embedded_sequences = embedding_layer(sequence_input)
generator = generator_model(sequence_input, embedded_sequences, X.shape[1])
print(generator.summary())
generator.compile(loss='sparse_categorical_crossentropy', optimizer='adam')
Xnoise = generate_integer_noise(MAX_SEQUENCE_LENGTH)
y_shape = np.zeros((X.shape[0],), dtype=int)
generator.fit(X, y_shape, epochs=1)
acc = generator.predict(Xnoise, verbose=1)

Но на самом делеЯ получаю следующую ошибку

ValueError: Error when checking input: expected input_1 to have shape (1500,) but got array with shape (1,)

при вызове:

Xnoise = generate_noise(samples_number=MAX_SEQUENCE_LENGTH)
generator.predict(Xnoise, verbose=1)

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

Правильна ли моя модель для моих целей?или я написал что-то действительно глупое, чего не вижу?

Спасибо за помощь, которую вы можете мне оказать, я ценю это!

edit

История изменений:

v1.
###
- Changed model structure, now return_sequences = True and using shape instead of batch_shape
###
- Changed 
sequence_input = Input(batch_shape=(1,1,1500), dtype='int32')
to
sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH,))
###
- Changed the error the model is giving

v2.
###
- Changed generate_noise() code
###
- Added generate_integer_noise() code
###
- Added full sequence with the model compile, fit and predict
###
- Added model.fit summary under the model summary, in the tail of the post

generate_noise() код:

def generate_noise(samples_number, mean=0.5, stdev=0.1):
    noise = np.random.normal(mean, stdev, (samples_number, MAX_SEQUENCE_LENGTH))
    print(noise.shape)
    return noise

с печатью: (1500,)

generate_integer_noise() код:

def generate_integer_noise(samples_number):
    noise = []
    for _ in range(0, samples_number):
        noise.append(np.random.randint(1, MAX_NB_WORDS))
    Xnoise = np.asarray(noise)
    return Xnoise

моя функция load_embeddings() выглядит следующим образом:

def load_embeddings(word_index, embeddingsfile='Embeddings/glove.6B.%id.txt' %EMBEDDING_DIM):
    embeddings_index = {}
    f = open(embeddingsfile, 'r', encoding='utf8')
    for line in f:
        values = line.split(' ') #split the line by spaces
        word = values[0] #each line starts with the word
        coefs = np.asarray(values[1:], dtype='float32') #the rest of the line is the vector
        embeddings_index[word] = coefs #put into embedding dictionary
    f.close()

    print('Found %s word vectors.' % len(embeddings_index))

    embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))
    for word, i in word_index.items():
        embedding_vector = embeddings_index.get(word)
        if embedding_vector is not None:
            # words not found in embedding index will be all-zeros.
            embedding_matrix[i] = embedding_vector

    embedding_layer = Embedding(len(word_index) + 1,
                                EMBEDDING_DIM,
                                weights=[embedding_matrix],
                                input_length=MAX_SEQUENCE_LENGTH,
                                trainable=False)
    return embedding_layer

Сводка модели:

Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 1500)              0         
_________________________________________________________________
embedding_1 (Embedding)      (None, 1500, 300)         9751200   
_________________________________________________________________
lstm_1 (LSTM)                (None, 1500, 16)          20288     
_________________________________________________________________
lstm_2 (LSTM)                (None, 1500, 32)          6272      
_________________________________________________________________
flatten_1 (Flatten)          (None, 48000)             0         
_________________________________________________________________
dense_1 (Dense)              (None, 1500)              72001500  
=================================================================
Total params: 81,779,260
Trainable params: 72,028,060
Non-trainable params: 9,751,200
_________________________________________________________________


model.fit() Сводка (с использованием набора данных размером 999 для тестирования, вместо размера 20K):

999/999 [==============================] - 62s 62ms/step - loss: 0.5491 - categorical_accuracy: 0.9680

1 Ответ

0 голосов
/ 21 января 2019

Я переписал полный ответ, теперь он работает (по крайней мере, компилирует и запускает, ничего не говорит о конвергенции).

Во-первых, я не знаю, почему вы используете sparse_categorical_crossentropy вместо categorical_crossentropy?Это может быть важно.Я немного изменяю модель, чтобы она компилировалась и использовала категорийный кроссцентроп.Если вам нужен sparse, измените форму цели.

Кроме того, я изменяю аргумент batch_shape на shape, поскольку он позволяет использовать пакеты различной формы.С ним проще работать.

И последнее редактирование: вы должны изменить generate_noise, потому что слой Embedding ожидает числа от (0, max_features), а не нормально распределенные числа с плавающей точкой (см. Комментарий в функции).

РЕДАКТИРОВАТЬ
Обращаясь к последним комментариям, я удалил generate_noise и опубликовал измененную функцию generate_integer_noise:

from keras.layers import Input, Embedding, LSTM
from keras.models import Model
import numpy as np


def generate_integer_noise(samples_number):
    """
    samples_number is a number of samples, i.e. first dimension in (some, 1500)
    """
    return np.random.randint(1, MAX_NB_WORDS, size=(samples_number, MAX_SEQUENCE_LENGTH))

MAX_SEQUENCE_LENGTH = 1500
"""
Tou can use your definition of embedding layer, 
I post to make a reproducible example
"""
max_features, embed_dim = 10, 300
embedding_matrix = np.zeros((max_features, embed_dim))
output_shape = MAX_SEQUENCE_LENGTH

embedded_layer = Embedding(
    max_features,
    embed_dim,
    weights=[embedding_matrix],
    trainable=False
)


def generator_model(embedded_layer, output_shape):
    """
    embedded_layer: Embedding keras layer
    output_shape: shape of the target
    """
    sequence_input = Input(shape=(MAX_SEQUENCE_LENGTH, ))
    embedded_sequences = embedded_layer(sequence_input)   # Set trainable to the True if you wish to train

    layer = LSTM(32, return_sequences=True)(embedded_sequences)
    layer = LSTM(64, return_sequences=True)(layer)
    output = LSTM(output_shape)(layer)

    generator = Model(sequence_input, output)
    return generator


generator = generator_model(embedded_layer, output_shape)

noise = generate_integer_noise(32)

# generator.predict(noise)
generator.compile(loss='categorical_crossentropy', optimizer='adam')
generator.fit(noise, noise)
...