Бой НЛП: автоматическое создание оригинальных текстов песен

искусственный интеллект TensorFlow задняя часть
Бой НЛП: автоматическое создание оригинальных текстов песен

"Это седьмой день моего участия в ноябрьском испытании обновлений, ознакомьтесь с подробностями события:Вызов последнего обновления 2021 г.".

1. Предпосылки

У меня два увлечения, одно — традиционная культура, другое — высокие технологии.

Традиционная культура, мне нравится поэзия Тан и поэзия Сун, перо и тушь Danqing, высокие технологии. Я занимаюсь передовым ИТ-программированием, и мне нравится изучать искусственный интеллект.

Я хотел бы соединить два, старый и новый, я не знаю, какие искры столкнутся.

2. Результаты

Путем экспериментов, используя рекуррентную нейронную сеть в сочетании с генерацией текста, я, наконец, применил магический трюк: дайте начало, и он автоматически сгенерирует Song Ci. И это новое слово, безусловно, оригинально.

начало генерировать
морось Морось Сянгуйчунь. Яркая луна здесь, и сон разбит. Дождавшись холодного занавеса, возвращаемся. Вороны кричат.
ветер Ветер ломается, и внешний вид бессонный и безветренный. Люди мечтают о прелести рододендронов. Не заставляй людей выходить за дверь. робкое морозное утро.
высотный Высотные огни, девять улиц и луна. Сегодня вечером я вышел из здания, и когда я путешествовал, там было пусто. Но мыть. С видом на пять цветов мира.
морской бриз Морской бриз дует сегодня ночью там, где предпочитает Фэнлоу. чудесно. Полумесяц разбит. Положите сердце на зеленый холм и развалитесь.
сегодня вечером Кто и слезы высохли сегодня вечером. Ветер Каору достаточно слабый. Как затянувшаяся печаль. Как чистая волна, как нефритовый человек. стыдно видеть.

Меня, немного изучавшего поэзию, вполне устраивает текст, порожденный словом "многоэтажка" выше.

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

Высотное здание находится на высоком месте, и текст за ним также отражает характеристики «высокого».«Многоэтажное здание, смотрящее на улицу» — художественная концепция, «многоэтажное здание, смотрящее в ночь». это еще одна художественная концепция, и, наконец, есть «взгляд вниз на пять цветов». Слово «вниз» также отражает снисходительность, и весь текст вращается вокруг темы «высокого». Поразительнй!

Давайте проанализируем, как реализуется генерация Song Ci.

3. Реализация

3.1 Подготовка данных

Я нашел набор данных Song Ci, который представляет собой файл в формате csv, содержащий 20 000 Song Ci.

Первый столбец документа — это имя токена, второй столбец — автор, а третий столбец — текст. Среди них текст был разбит на слова.

Чтобы узнать о причастиях, см.Очки знаний НЛП: сегментация китайских слов.

2021-11-20_135645.png

3.2 Чтение данных

Сначала импортируйте пакеты, задействованные во всем проекте.

import csv
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
import numpy as np
from tensorflow.python.keras.engine.sequential import Sequential
from tensorflow.keras import layers
from tensorflow.keras.optimizers import Adam

Вот как загрузить данные из файла набора данных.

def load_data(num = 1000):
      
    # 读取csv文件。表头:0 题目| 1 作者| 2 内容
    csv_reader = csv.reader(open("./ci.csv",encoding="gbk"))
    # 以一首词为单位存储
    ci_list = []
    for row in csv_reader:
        # 取每一行,找到词内容那一列
        ci_list.append(row[2])
        # 超过最大数量退出循环,用多少取多少
        if len(ci_list) > num:break 
    return ci_list

Чтобы узнать больше о том, как загружать наборы данных csv, вы можете просмотретьОчки знаний НЛП: чтение в формате CSV.

Затем сериализуйте данные.

def get_train_data():
      
    # 加载数据作为语料库["春花 秋月","一江 春水 向东 流"]
    corpus = load_data()
    # 定义分词器
    tokenizer = Tokenizer()
    # 分词器适配文本,将语料库拆分词语并加索引{"春花":1,"秋月":2,"一江":3}
    tokenizer.fit_on_texts(corpus)

    # 定义输入序列
    input_sequences = []
    # 从语料库取出每一条
    for line in corpus:
        # 序列成数字 "一江 春水 向东 流" ->[3,4,5,6]
        token_list = tokenizer.texts_to_sequences([line])[0]
        # 截取字符[3,4,5,6]变成[3,4],[3,4,5],[3,4,5,6]
        for i in range(1, len(token_list)):
            n_gram_sequence = token_list[:i+1]
            input_sequences.append(n_gram_sequence)
    # 找到语料库中最大长度的一项
    max_sequence_len = max([len(x) for x in input_sequences])
    # 填充序列每一项到最大长度,采用前面补0的方式
    input_sequences = np.array(pad_sequences(input_sequences, maxlen=max_sequence_len, padding='pre'))

    return tokenizer, input_sequences, max_sequence_len

Для того, почему и как сериализовать текст, вы можете перейти к пункту знанийTokenizer,texts_to_sequences,pad_sequencesСмотрите подробные инструкции.

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

Например, предложение «Смотреть на гору — это не гора, а смотреть на гору — это гора», которое было преобразовано в несколько предложений:

看山 不是
看山 不是 山 
看山 不是 山 ,
看山 不是 山 , 看山
看山 不是 山 , 看山 又是
看山 不是 山 , 看山 又是 山

Цель этого состоит в том, чтобы сообщить нейронной сети, что если ему предшествует «посмотри на гору», за ним следует слово «нет». Когда фронт становится «Смотреть на гору, не на гору, а смотреть на гору», тогда «Смотреть на гору» становится «снова».

Спинка «глядя на гору» не фиксируется, а определяется на основе комплексного осмысления ряда слов перед ней.

Разделите одно предложение на несколько предложений. Это специальная обработка, которую выполняет следующий код:

for i in range(1, len(token_list)):
    n_gram_sequence = token_list[:i+1]
    input_sequences.append(n_gram_sequence)

3.3 Построение модели

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

def create_model(vocab_size, embedding_dim, max_length):
    
    # 构建序列模型
    model = Sequential()
    # 添加嵌入层
    model.add(layers.Embedding(vocab_size, embedding_dim, input_length = max_length))
    # 添加长短时记忆层
    model.add(layers.Bidirectional(layers.LSTM(512)))
    # 添加softmax分类
    model.add(layers.Dense(vocab_size, activation='softmax'))
    # adam优化器
    adam = Adam(lr=0.01)
    # 配置训练参数
    model.compile(loss='categorical_crossentropy',optimizer=adam, metrics=['accuracy'])

    return model

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

3.4 Провести обучение

Тренировочный код выглядит следующим образом:

# 分词器,输入序列,最大序列长度
tokenizer, input_sequences, max_sequence_len = get_train_data()
# 得出有多少个词,然后+1,1是统一长度用的填充词
total_words = len(tokenizer.word_index) + 1

# 从语料库序列中拆分出输入和输出,输入是前面几个词,输出是最后一个词
xs = input_sequences[:,:-1]
labels = input_sequences[:,-1]

# 结果转为独热编码
ys = tf.keras.utils.to_categorical(labels, num_classes=total_words)
# 创建模型
model = create_model(total_words, 256, max_sequence_len-1)
# 进行训练
model.fit(xs, ys, epochs= 15, verbose=1)

# 保存训练的模型
model_json = model.to_json()
with open('./save/model.json', 'w') as file:
    file.write(model_json)
# 保存训练的权重
model.save_weights('./save/model.h5')

Предположим, мы получили обучающую последовательность input_sequences:

[0, 0, 1, 2]
[0, 0, 3, 4]
[0, 3, 4, 5]
[3, 4, 5, 6]

Соответствующий текст:

[0,    0,    春花,  秋月]
[0,    0,    一江,  春水]
[0,    一江, 春水,  向东]
[一江, 春水, 向东,    流]

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

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

Следующий код принимает ввод и вывод из input_sequences:

xs = input_sequences[:,:-1] 
labels = input_sequences[:,-1]
введите xs выходные метки
[0, 0, весенний цветок] [Осенняя луна]
[0, 0, одна река] [Родниковая вода]
[0, одна река, родниковая вода] [Восток]
[Одна река, родниковая вода, на восток] [поток ]

Поскольку функция активации в модели используетactivation='softmax', поэтому этот вывод проходит черезtf.keras.utils.to_categoricalПреобразовано в горячее кодирование.

Если у вас есть вопросы о горячем кодировании, вы можете проверить«Знание: горячее кодирование».

На этом этапе необходимо выделить несколько концепций:

  • Максимальная длина текстовой последовательностиmax_sequence_lenэто[一江, 春水, 向东, 流]длина, здесь значение равно 4. Основная функция состоит в том, чтобы определить фиксированную длину обучения, заполнить 0, когда длина недостаточна, и обрезать, когда она превышается.

Зачем тебе это, ты можешьнажмите сюда, чтобы узнать больше.

  • длина входной последовательностиinput_lengthэто[0, 一江, 春水]Длина, фиксированная на 3, составляет отmax_sequence_lenВырезать, не нужно последнее слово. Основная роль - входная.

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

3.5 Делайте прогнозы

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

Код предсказания выглядит следующим образом:

def predict(seed_text, next_words = 20):

    # 分词器,输入序列,最大序列长度
    tokenizer, input_sequences, max_sequence_len = get_train_data()

    # 读取训练的模型结果
    with open('./save/model.json', 'r') as file:
        model_json_from = file.read()
    model = tf.keras.models.model_from_json(model_json_from)
    model.load_weights('./save/model.h5')

    # 假如要预测后面next_words=20个词,那么需要循环20词,每次预测一个
    for _ in range(next_words):
        # 将这个词序列化 如传来“高楼”,则从词库中找高楼的索引为50,序列成[50]
        token_list = tokenizer.texts_to_sequences([seed_text])[0]
        # 填充序列每一项到最大长度,采用前面补0的方式[0,0……50]
        token_list = pad_sequences([token_list], maxlen= max_sequence_len-1, padding='pre')
        # 预测下一个词,预测出来的是索引
        predicted = model.predict_classes(token_list, verbose = 0)
        # 定义一个输出存储输出的数值
        output_word = ''
        # 找到预测的索引是哪一个词,比如55是“灯火”
        for word, index in tokenizer.word_index.items():
            if index == predicted:
                output_word = word
                break
        # 输入+输出,作为下一次预测:高楼 灯火
        seed_text = seed_text + " " + output_word

    print(seed_text)
    # 替换空格返回
    return seed_text.replace(' ' , '')

# 预测数据
print(predict('细雨',next_words = 22))
# 细雨仙桂春。明月此,梦断在愁何。等闲帘寒,归。正在栖鸦啼来。

Для чтения приведенных выше данных модели, те, кто заинтересован, могут увидеть очки знаний:Чтение модели восстановления файлов json, h5.

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

предсказать Б
предсказать C
предсказать D
Предсказать следующие N слов
вступительное слово А
AB
ABC
ABCD

Во-первых, согласно начальным словам, поmodel.predict_classes(token_list)Прогнозируется следующее слово, а затем начальное слово и предсказанное слово используются в качестве входных данных для продолжения прогнозирования следующего слова. И так далее, как змея, медленно вытягивая длинное предложение из вступительного слова. Каждое слово в предложении имеет смысловой контекст.

Это логика реализации генератора Song Ci, надеюсь она вам поможет.