Недавно я построил модель Word Prediction и использовал эту модель для создания текстов песен. Данные, на которых я обучал свою модель, содержали тексты песен Тейлор Свифт. Мой ИИ выглядит подавленным, а может, это просто горе. Вот каковы были результаты:
Теперь поговорим о том, как я это сделал - о подходе и инструментах. Давай сделаем это!
Чтение данных
Начнем с простого вопроса. Как читать данные. Это довольно просто
data = open('../filename.txt').read()
Ваши данные могут выглядеть примерно так
'He said the way my blue eyes shined\r\nPut those Georgia.......'
Обратите внимание на "\ r \ n"? Они представляют следующую строку или конец предложения. Иногда, в зависимости от данных и того, как вы их читаете, это может быть просто «\ r» или «\ n». Мы должны от них избавиться.
# Splitting the string into sentences, while converting whole data into lowercase. corpus = data.lower().split("\r\n") # To make sure no sentence appears twice in our corpus, we use set. Otherwise, it will make the model biased. corpus = list(set(corpus))
Теперь корпус - это список, содержащий каждое предложение ваших данных в качестве элемента. Как мы использовали set, у него нет повторяющихся значений.
Организация данных
Поскольку наша модель будет принимать только числа в качестве входных данных, нам нужно преобразовать наши слова в числа.
Токены: для этой цели мы будем использовать Tokenizer - очень полезный инструмент, поставляемый с Keras.
tokenizer = Tokenizer() tokenizer.fit_on_texts(corpus) total_words = len(tokenizer.word_index) + 1
Когда мы подбираем токенизатор корпуса, мы сохраняем данные, относящиеся к корпусу, в самом токенизаторе.
Если вы запустите tokenizer.word_index, он вернет примерно следующее:
{'you': 1, 'i': 2, 'and': 3, 'the': 4, 'to': 5, 'me': 6, 'a': 7, 'it': 8, 'in': 9, 'my': 10, 'your': 11,.......}
Это словарь, в котором ключи представляют все слова в корпусе, а значение каждого ключа является его индексом.
Общее количество слов равно len (tokenizer.word_index) + 1.
«+1» используется для обозначения неизвестного слова, которое не является частью нашего корпуса.
Последовательности ввода: Последовательность ввода - это числовое представление того, как расположены наши слова. Продолжайте читать, чтобы узнать, что это значит.
# create input sequences using list of tokens input_sequences = [] for line in corpus: token_list = tokenizer.texts_to_sequences([line])[0] for i in range(1, len(token_list)): n_gram_sequence = token_list[:i+1] input_sequences.append(n_gram_sequence)
Мы перепроектируем это, чтобы увидеть, как это работает.
Когда вы печатаете input_sequences, мы получим что-то вроде этого:
[[125, 45], [125, 45, 901], [125, 45, 901, 9], [125, 45, 901, 9, 10], [125, 45, 901, 9, 10, 36], [125, 45, 901, 9, 10, 36, 96], [125, 45, 901, 9, 10, 36, 96, 11], [125, 45, 901, 9, 10, 36, 96, 11, 902], ..... ......]
Это представление первого предложения в корпусе,
«у меня на спине еще остались шрамы от ваших ножей».
Теперь вы можете проверить, что в tokenizer.word_index индекс для still равен 125. То же самое верно для всех слов.
>>> tokenizer.word_index["still"] Out: 125
Для одного и того же предложения существуют разные входные последовательности, которые различаются по длине. Они просто представляют, как модель учится. Сначала мы кормим 2 слова и учим предсказывать 3-е слово. Затем мы вводим 3 слова и ожидаем, что он предскажет 4-е слово. То же самое верно для каждого предложения. Вот как работает наша модель.
Последовательности пэдов. В нашем корпусе каждое предложение имеет разную длину. Таким образом, входная последовательность также будет иметь разную длину. Такой ввод нельзя подавать в модель, потому что она неуравновешена. Следовательно, мы выполняем заполнение.
# pad sequences max_sequence_len = max([len(x) for x in input_sequences]) input_sequences = np.array(pad_sequences(input_sequences, maxlen = max_sequence_len, padding='pre'))
Сначала мы определяем максимальную длину последовательности. Затем мы заполняем каждую строку входной последовательности так, чтобы в каждой строке было равное количество элементов. Но как работает обивка?
Допустим, максимальная длина последовательности равна 10. Но ваша последовательность имеет длину только 4. Пример: [125, 45, 901, 9].
После заполнения эта последовательность станет: [0, 0, 0, 0, 0, 0, 125, 45, 901, 9].
Входная последовательность теперь будет выглядеть примерно так:
[[ 0, 0, 0, ..., 0, 125, 45], [ 0, 0, 0, ..., 125, 45, 901], [ 0, 0, 0, ..., 45, 901, 9], .......... ...........
Предикторы и метки. Проблема, которую мы решаем, - это проблема контролируемого обучения. Поэтому нам нужно будет снабдить модель некоторыми метками, чтобы она могла обобщить связь между словами, используемыми для предсказания, и предсказанным словом.
Итак, мы будем использовать нашу входную последовательность и использовать последнее слово всех последовательностей в качестве меток для всех предыдущих слов.
Проще говоря, в предложении «на моей спине еще остались шрамы от ваших ножей»:
все равно будет использоваться для прогнозирования получил
еще есть будет использоваться для прогнозирования шрамов
еще есть шрамы будет использоваться для прогнозирования в и так далее.
predictors, label = input_sequences[:,:-1],input_sequences[:,-1]
Построение модели
Честно говоря, сложная часть сделана. Теперь нам просто нужно построить модель RNN с помощью LSTM. Подробнее про LSTM здесь.
model = Sequential() model.add(Embedding(total_words, 50, input_length=max_sequence_len-1)) # Add an LSTM Layer model.add(Bidirectional(LSTM(150, return_sequences=True))) # A dropout layer for regularisation model.add(Dropout(0.2)) # Add another LSTM Layer model.add(LSTM(100)) model.add(Dense(total_words/2, activation='relu')) # In the last layer, the shape should be equal to the total number of words present in our corpus model.add(Dense(total_words, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics='accuracy') #(# Pick a loss function and an optimizer) print(model.summary())
Теперь мы подгоним модель к предикторам и меткам. Вы можете варьировать эпохи, чтобы увидеть изменение точности и то, где модель перекрывает данные.
history = model.fit(predictors, label, epochs= 100, verbose=1)
Создание текстов песен
Теперь мы создадим тексты песен, используя созданную нами модель.
def make_lyrics(seed_text, next_words): for _ in range(next_words): token_list = tokenizer.texts_to_sequences([seed_text])[0] token_list = pad_sequences([token_list], maxlen=max_sequence_len-1,padding='pre') predicted = model.predict_classes(token_list, verbose=0) output_word = "" for word, index in tokenizer.word_index.items(): if index == predicted: output_word = word break seed_text += " " + output_word print(seed_text)
Вышеупомянутая функция довольно проста для понимания.
Входными данными является «seed_text», который затем токенизируется и дополняется, чтобы сделать его равным максимальной длине последовательности.
Затем модель использует этот исходный текст для прогнозирования числа. Число представляет индекс предсказанного слова.
Следовательно, мы перебираем все элементы в словаре word_index, чтобы найти предсказанное слово и добавить его к начальному слову для завершения предложения. Но этот метод предсказывает только одно слово. Итак, мы помещаем все это в цикл for и запускаем его несколько раз. Количество итераций, которые вам нужно сделать, равно количеству слов, которые вы хотите предсказать.
Спасибо за прочтение. Надеюсь, вам понравилась моя статья, и вы нашли ее полезной. Если у вас есть какие-либо вопросы или предложения, не стесняйтесь записывать их в разделе комментариев. Вы также можете связаться со мной на LinekdIn здесь: Ishant Juyal.
Использованная литература:
- Набор данных: Kaggle.com/Taylor Swift Song Lyrics
- Видеоурок: https://youtu.be/Pe56OZ4aPds