- Оригинальный адрес:How to Generate Music using a LSTM Neural Network in Keras
- Оригинальный автор:Сигурдур Скули
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:HearFishle
- Корректор:xionglong58,JackEggie
вводить
Нейронные сети используются для улучшения всех аспектов нашей жизни. Они дают нам советы по покупкам,Создать документ на основе авторского стиляможно даже использоватьИзменение художественного стиля изображения. В последние годы многие учебные пособия были посвящены тому, как использовать нейронные сети для создания текста, но очень немногие учебные пособия научили вас создавать музыку. В этой статье мы расскажем, как использовать библиотеки Python и Keras для создания музыки с помощью рекуррентных нейронных сетей.
Для нетерпеливых в конце приведена ссылка на репозиторий Github для этого руководства.
задний план
Прежде чем перейти к конкретной реализации, необходимо уточнить некоторые технические термины.
Рекуррентная нейронная сеть (RNN)
Рекуррентные нейронные сети — это класс искусственных нейронных сетей, которые позволяют нам использовать информацию временных рядов. Их называют циклами, потому что они выполняют одну и ту же функцию для каждого элемента в последовательности данных. Результат каждого раза зависит от предыдущей операции. С другой стороны, традиционные нейронные сети имеют выходные данные, которые не зависят от предыдущих вычислений.
В этом уроке мы используемДолгосрочная кратковременная память (LSTM)Нейронные сети. Такие рекуррентные нейронные сети можно эффективно изучать с помощью градиентного спуска. Используя механизм ворот, LSTM могут идентифицировать и кодировать долгосрочные шаблоны. LSTM особенно полезны для тех случаев, когда требуется долговременная память информации, например, для сочинения музыки и текста.
Music21
Music21это набор инструментов Python, используемый в компьютерном музыковедении. Это позволяет нам обучать основам музыки, создавать музыкальные примеры и изучать музыку. Этот инструментарий предоставляет простой интерфейс для получения музыкальных ключей в файлах MIDI. Кроме того, мы также можем использовать его для создания нот и аккордов, чтобы легко создавать собственные MIDI-файлы.
В этом уроке мы будем использовать Music21 для извлечения содержимого нашего набора данных, получения выходных данных нейронной сети и преобразования их в музыкальные ноты.
Keras
Kerasпредставляет собой высокоуровневый интерфейс нейронной сети, который упрощает иTensorflowвзаимодействие. Он был разработан с акцентом на возможность быстрого экспериментирования.
В этом руководстве мы будем использовать библиотеку Keras для создания и обучения модели LSTM. Как только эта модель будет обучена, мы будем использовать ее для создания нот для нашей музыки.
тренироваться
В этом разделе мы объясним, как собирать данные для нашей модели, как организовать данные, чтобы их можно было использовать в модели LSTM, и какова структура нашей модели.
данные
существуетРепозиторий на гитхабеВ , мы используем фортепианную пьесу (шоу), а музыка состоит в основном из треков из Final Fantasy. Музыка из серии Final Fantasy была выбрана потому, что в ней много частей, и большая часть мелодий ясна и красива. И нам подойдет любой набор MIDI-файлов, состоящий из одного инструмента.
Первым шагом в реализации нейронной сети является изучение данных, которые мы хотим обработать.
Ниже мы видим отрывок из миди-файла, прочитанного Music21:
...
<music21.note.Note F>
<music21.chord.Chord A2 E3>
<music21.chord.Chord A2 E3>
<music21.note.Note E>
<music21.chord.Chord B-2 F3>
<music21.note.Note F>
<music21.note.Note G>
<music21.note.Note D>
<music21.chord.Chord B-2 F3>
<music21.note.Note F>
<music21.chord.Chord B-2 F3>
<music21.note.Note E>
<music21.chord.Chord B-2 F3>
<music21.note.Note D>
<music21.chord.Chord B-2 F3>
<music21.note.Note E>
<music21.chord.Chord A2 E3>
...
Эти данные делятся на два типа:Note(Примечание переводчика: набор заметок) иChord(Примечание переводчика: набор аккордов). Обратите внимание, объекты включаютподача,масштаби музыкальныйКомпенсировать
-
подачаОтносится к частоте звука или [A, B, C, D, E, F, G], чтобы указать, высокий он или низкий. где А — самый высокий, а G — самый низкий.
-
масштабОтносится к тому, какие высоты тона вы будете использовать на фортепиано.
-
КомпенсироватьОтносится к положению ноты в произведении.
Суть объекта аккорда — это контейнер, который одновременно воспроизводит группу нот.
Теперь мы видим, что для точного сочинения музыки наша нейронная сеть должна уметь предсказывать, какая нота или аккорд будет использована. Это означает, что наш прогнозируемый набор должен содержать каждую ноту и объект аккорда, с которыми мы столкнулись в нашем обучающем наборе. В тренировочном наборе на странице Github количество отдельных нот и аккордов составляет 352. Кажется, что это дает сети множество возможных прогнозов для вывода, но сеть LSTM может легко с этим справиться.
Далее мне нужно было подумать о том, куда положить заметки. Как замечает большинство людей при прослушивании музыки, интервалы между нотами обычно разные. Вы можете услышать быструю последовательность нот, за которой следует пустое место, где ноты не воспроизводятся.
Далее мы находим отрывок из другого миди-файла, прочитанного Music21, на этот раз мы просто добавляем смещение после него. Это позволяет нам видеть интервал между каждой нотой и аккордом.
...
<music21.note.Note B> 72.0
<music21.chord.Chord E3 A3> 72.0
<music21.note.Note A> 72.5
<music21.chord.Chord E3 A3> 72.5
<music21.note.Note E> 73.0
<music21.chord.Chord E3 A3> 73.0
<music21.chord.Chord E3 A3> 73.5
<music21.note.Note E-> 74.0
<music21.chord.Chord F3 A3> 74.0
<music21.chord.Chord F3 A3> 74.5
<music21.chord.Chord F3 A3> 75.0
<music21.chord.Chord F3 A3> 75.5
<music21.chord.Chord E3 A3> 76.0
<music21.chord.Chord E3 A3> 76.5
<music21.chord.Chord E3 A3> 77.0
<music21.chord.Chord E3 A3> 77.5
<music21.chord.Chord F3 A3> 78.0
<music21.chord.Chord F3 A3> 78.5
<music21.chord.Chord F3 A3> 79.0
...
Как показано в этом отрывке, большинство наборов данных в миди-файле имеют ноты, разделенные 0,5. Следовательно, мы можем упростить данные и модель, игнорируя смещения для разных выходных данных. Это не сильно влияет на музыкальную мелодию, создаваемую нейронной сетью. Таким образом, мы проигнорируем смещение в руководстве и сохраним наш список возможных выходных данных равным 352.
Подготовить данные
Теперь, когда мы изучили данные и решили, что хотим использовать ноты и аккорды в качестве функций для вывода и вывода сети, пришло время подготовить данные для сети.
Сначала мы загружаем данные в массив, как в следующем коде:
from music21 import converter, instrument, note, chord
notes = []
for file in glob.glob("midi_songs/*.mid"):
midi = converter.parse(file)
notes_to_parse = None
parts = instrument.partitionByInstrument(midi)
if parts: # 文件包含乐器
notes_to_parse = parts.parts[0].recurse()
else: # 文件有扁平结构的音符
notes_to_parse = midi.flat.notes
for element in notes_to_parse:
if isinstance(element, note.Note):
notes.append(str(element.pitch))
elif isinstance(element, chord.Chord):
notes.append('.'.join(str(n) for n in element.normalOrder))
использоватьconverter.parse(file)
мы начинаем загружать каждый файл в объект потока Music21. Используя этот объект потока, мы получаем список всех нот и аккордов в файле. Придерживайтесь нотации массива к высоте тона каждого объекта ноты, потому что использование нотации массива воссоздает наиболее важные части ноты. Кодируйте идентификатор каждого аккорда в отдельную строку, где каждая нота отделяется точкой. Эти коды позволяют нам легко декодировать вывод, сгенерированный сетью, в правильные ноты и аккорды.
Теперь, когда мы поместили все ноты и аккорды в список последовательностей, мы можем создать последовательность для использования в качестве входных данных для сети.
Рис. 1. Когда данные преобразуются из категориальных в числовые, данные преобразуются в целочисленный индекс, представляющий положение класса в наборе различных значений. Например, яблоко является первым явным значением, поэтому оно сопоставляется с 0. Таким образом, апельсины сопоставляются с 1 во втором, ананасы с 3 и так далее.
Во-первых, мы напишем функцию сопоставления для сопоставления символьных категориальных данных с целочисленными числовыми данными. Это сделано потому, что нейронные сети обрабатывают целочисленные числовые данные намного лучше, чем символьные категориальные данные. На рис. 1 показан пример преобразования категориальных значений в числовые.
Далее мы должны создать входные последовательности отдельно для сети и ее выходов. Выходная последовательность, соответствующая каждой входной последовательности, будет первой нотой или аккордом во входной последовательности списка нот после списка нот.
sequence_length = 100
# 得到所有的音高名称
pitchnames = sorted(set(item for item in notes))
# 创建一个音高到音符的映射字典
note_to_int = dict((note, number) for number, note in enumerate(pitchnames))
network_input = []
network_output = []
# 创建输入序列和与之对应的输出
for i in range(0, len(notes) - sequence_length, 1):
sequence_in = notes[i:i + sequence_length]
sequence_out = notes[i + sequence_length]
network_input.append([note_to_int[char] for char in sequence_in])
network_output.append(note_to_int[sequence_out])
n_patterns = len(network_input)
# 整理输入格式使之与 LSTM 兼容
network_input = numpy.reshape(network_input, (n_patterns, sequence_length, 1))
# 归一化输入
network_input = network_input / float(n_vocab)
network_output = np_utils.to_categorical(network_output)
В этом сводном примере кода мы устанавливаем длину каждой последовательности равной 100 нотам или аккордам. Это означает, что для предсказания следующей ноты в последовательности в сети уже есть 100 нот, которые помогут предсказать. Я настоятельно рекомендую использовать последовательности разной длины для обучения сети, а затем наблюдать за влиянием этих последовательностей разной длины на музыку, воспроизводимую сетью.
Последним шагом в подготовке данных для сети является нормализация входных ивыход с горячим кодированием.
Модель
Наконец, давайте разработаем архитектуру этой модели. В модели мы используем четыре разных типа слоев:
LSTM-слой— это слой рекуррентной нейронной сети, который принимает последовательность в качестве входных данных и возвращает другую последовательность (возвращает последовательность, которая оценивается как истинная) или матрицу.
выпадающий слой— это правило регуляризации, которое включает в себя установку части входных единиц на 0 при каждом обновлении во время обучения, чтобы предотвратить переоснащение. Это определяется параметрами, используемыми со слоем.
Плотный слойилиполносвязный слойпредставляет собой полносвязный слой нейронной сети, где каждый входной узел соединен с выходным узлом.
Слой активацииРешите, какую функцию активации в нейронной сети использовать для вычисления выходных узлов.
model = Sequential()
model.add(LSTM(
256,
input_shape=(network_input.shape[1], network_input.shape[2]),
return_sequences=True
))
model.add(Dropout(0.3))
model.add(LSTM(512, return_sequences=True))
model.add(Dropout(0.3))
model.add(LSTM(256))
model.add(Dense(256))
model.add(Dropout(0.3))
model.add(Dense(n_vocab))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
Теперь, когда у нас есть некоторая информация о разных слоях, добавим их в модель нейронной сети.
Для каждого слоя LSTM, Dense и Activation первым параметром является количество узлов, которые должны быть в слое. Для слоев Dropout первый параметр — это доля входных единиц во входной единице, которая должна быть отброшена во время обучения.
Для первого слоя мы должны предоставить уникальное имя, котороеinput_shapeпараметр. Этот параметр определяет формат данных в обучаемой сети.
Последний слой всегда должен содержать столько узлов, сколько мы выводим разных результатов. Это гарантирует, что выходные данные сети будут отображаться непосредственно в нашем классе.
Здесь мы будем использовать простую сеть с тремя слоями LSTM, тремя слоями Dropout, двумя плотными слоями и одним слоем активации. Я рекомендую настроить структуру сети и посмотреть, сможете ли вы улучшить качество прогнозов.
Чтобы рассчитать потери для каждой итерации, мы будем использовать [категориальную кросс-энтропию], (Пьетро.GitHub.IO/содружественно-в…), потому что каждый раз наш вывод принадлежит простому классу, а над ним работает более двух классов. Для оптимизации сети мы будем использовать оптимизатор RMSprop. Обычно для рекуррентных нейронных сетей это хороший выбор.
filepath = "weights-improvement-{epoch:02d}-{loss:.4f}-bigger.hdf5"
checkpoint = ModelCheckpoint(
filepath, monitor='loss',
verbose=0,
save_best_only=True,
mode='min'
)
callbacks_list = [checkpoint]
model.fit(network_input, network_output, epochs=200, batch_size=64, callbacks=callbacks_list)
Как только мы определились со структурой сети, пора приступать к обучению. Использование Керасаmodel.fit()
Функция для обучения сети. Первый аргумент — это список входных последовательностей, которые мы подготовили ранее, а второй аргумент — это список их соответствующих выходов. В этом уроке мы будем обучать сеть на 200 итераций, каждая партия распространяется через сеть, содержащую 60 ветвей.
Чтобы убедиться, что мы можем остановить обучение в любой момент, не теряя наших предыдущих усилий, мы будем использовать модельные контрольные точки. Это дает нам возможность сохранять веса узлов сети после каждой итерации в файл. Это позволяет нам остановить нейронную сеть, как только мы будем удовлетворены значением потери, не беспокоясь о потере веса. В противном случае нам придется ждать, пока сеть завершит все 200 итераций, чтобы сохранить веса в файл.
создавать музыку
Теперь, когда мы закончили обучение сети, пришло время насладиться сетью, на обучение которой мы потратили часы.
Чтобы иметь возможность создавать музыку с помощью нейронной сети, вы должны восстановить ее в исходное состояние. Короче говоря, мы снова будем использовать код в разделе обучения, чтобы подготовить данные и построить модель сети так же, как и раньше. Это делается не для переобучения сети, а для загрузки в модель весов из предыдущей сети.
model = Sequential()
model.add(LSTM(
512,
input_shape=(network_input.shape[1], network_input.shape[2]),
return_sequences=True
))
model.add(Dropout(0.3))
model.add(LSTM(512, return_sequences=True))
model.add(Dropout(0.3))
model.add(LSTM(512))
model.add(Dense(256))
model.add(Dropout(0.3))
model.add(Dense(n_vocab))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
# 给每一个音符赋予权重
model.load_weights('weights.hdf5')
Теперь мы можем использовать обученную модель, чтобы начать создавать заметки.
Поскольку у нас есть полный список последовательностей заметок, мы выберем любой индекс в списке в качестве отправной точки, что позволит нам повторно запускать код без каких-либо изменений и каждый раз возвращать разные результаты. Однако, если вы хотите контролировать начальную точку, просто замените случайную функцию аргументом командной строки.
Здесь мне также нужно написать функцию сопоставления для кодирования вывода сети. Эта функция сопоставляет числовые данные с категориальными данными (превращает целые числа в музыкальные ноты).
start = numpy.random.randint(0, len(network_input)-1)
int_to_note = dict((number, note) for number, note in enumerate(pitchnames))
pattern = network_input[start]
prediction_output = []
# 生成 500 个音符
for note_index in range(500):
prediction_input = numpy.reshape(pattern, (1, len(pattern), 1))
prediction_input = prediction_input / float(n_vocab)
prediction = model.predict(prediction_input, verbose=0)
index = numpy.argmax(prediction)
result = int_to_note[index]
prediction_output.append(result)
pattern.append(index)
pattern = pattern[1:len(pattern)]
Мы решили использовать Интернет для создания 500 нот, потому что это около двух минут музыки и дает Интернету достаточно места для создания мелодии. Чтобы сделать какую-либо заметку, мы должны отправить последовательность в сеть. Первая последовательность, которую мы отправляем, — это последовательность нот в начальной позиции. Для каждой последующей последовательности, которую мы используем в качестве входных данных, мы удаляем первую ноту последовательности и вставляем выходные данные предыдущей итерации в конец последовательности, как показано на рисунке 2.
Рисунок 2: Первый входной столбец — ABCDE. Выход, который мы полагаемся на то, что сеть может получить из потока, — это F. Для следующей итерации мы удаляем A из списка и добавляем к нему F. Затем повторите этот шаг.
Чтобы определить наиболее точный прогноз на выходе сети, мы извлекаем индекс с наибольшим значением. В выходной группе приемника индексXСтолбец может соответствовать следующей нотеX. Рисунок 3 помогает объяснить это.
Рисунок 3: Мы видим сопоставление сети с предсказаниями вывода класса. Как мы видим, следующее значение, скорее всего, D, поэтому мы выбираем D как наиболее вероятный набор шагов.
Затем мы собираем все выходы сети и складываем их в единый массив.
Теперь, когда у нас есть кодировка всех нот и аккордов в массиве, мы можем начать их декодирование и создать массив объектов нот и аккордов.
Сначала мы должны определить, является ли наш декодированный вывод нотой или аккордом.
если режимаккорды, мы должны разбить строку заметки на набор заметок. Затем мы перебираем строковое представление каждой заметки и создаем объект заметки для каждой заметки. Затем мы можем создать объект аккорда, содержащий каждую ноту.
Если выход представляет собойПримечание, мы создаем объект ноты, используя представление строки основного тона, содержащееся в паттерне.
В конце каждой итерации мы увеличиваем время смещения на 0,5 и добавляем объект ноты/аккорда в список.
offset = 0
output_notes = []
# 基于模型生成的值来创建音符与和弦
for pattern in prediction_output:
# 输出是和弦
if ('.' in pattern) or pattern.isdigit():
notes_in_chord = pattern.split('.')
notes = []
for current_note in notes_in_chord:
new_note = note.Note(int(current_note))
new_note.storedInstrument = instrument.Piano()
notes.append(new_note)
new_chord = chord.Chord(notes)
new_chord.offset = offset
output_notes.append(new_chord)
# 输出是音符
else:
new_note = note.Note(pattern)
new_note.offset = offset
new_note.storedInstrument = instrument.Piano()
output_notes.append(new_note)
# 增加每次迭代的偏移量使音符不会堆叠
offset += 0.5
После создания списка нот и аккордов в сети мы можем использовать этот список для создания объекта потока Music21, который использует этот список в качестве параметра. Наконец, чтобы создать MIDI-файл, содержащий сгенерированную сетью музыку, мы используем командуwriteФункция записывает поток в файл.
midi_stream = stream.Stream(output_notes)
midi_stream.write('midi', fp='test_output.mid')
результат
Настало время стать свидетелем чудес. На рис. 4 представлена страница музыкальных партитур, созданных с помощью нейронной сети LSTM. С первого взгляда видна его структура, что особенно заметно в предпоследних строках второй страницы.
Любой человек, обладающий музыкальными знаниями и умеющий читать ноты, может увидеть на этой странице странные ноты. Это результат неспособности сети создавать идеальные мелодии. В нашей текущей работе всегда будут какие-то неправильные ноты. Если мы хотим получить лучшие результаты, мы должны иметь большую сеть.
Рисунок 4: Музыка, созданная сетью LSTM
Результаты для этой относительно неглубокой сети по-прежнему впечатляют, что можно услышать из музыкального образца. Для тех, кому интересно, оценка на рисунке 4 представляет собой большой шаг вперед в создании музыки с помощью нейронных сетей.
i.soundcloud.com/player/?горячий воздух…
Будущая работа
Мы достигли этого замечательного результата с помощью простой сети LSTM и 352 шагов. Тем не менее, есть некоторые области, которые необходимо улучшить.
Во-первых, реализованные в настоящее время результаты не поддерживают несколько длин нот и смещения между нотами. Мы хотим добавить высоту тона, которая служит разной продолжительности, и тона, которые представляют время паузы для нот.
Чтобы получить удовлетворительные результаты за счет увеличения шага, мы также должны увеличить глубину сети LSTM, для чего требуется компьютер с более высокой производительностью. Моему собственному ноутбуку требуется около двух часов, чтобы обучить сеть.
Во-вторых, добавьте вступления и концовки к движению. Теперь в сети нет разрыва между двумя движениями, сеть не знает, где заканчивается одна глава и начинается другая. Это позволяет сети составлять главу от вступления до конца, а не резко завершать авторскую работу, как это происходит сейчас.
В-третьих, добавьте метод для обработки неизвестных заметок. Текущая ситуация такова, что если сеть встречает заметку, которую она не распознает, она возвращает статус ошибки. Возможное решение этого метода состоит в том, чтобы найти ноту или аккорд, наиболее похожий на неизвестную ноту.
Наконец, добавьте в набор данных больше инструментов (музыки). В настоящее время сеть поддерживает работу только с одним единственным инструментом. Было бы очень интересно, если бы его можно было распространить на весь оркестр.
Эпилог
В этом уроке мы продемонстрировали, как создать нейронную сеть LSTM для сочинения музыки. Возможно, результаты были далеки от идеальных, но тем не менее они были впечатляющими. И это показывает нам, что нейронные сети могут создавать музыку и могут использоваться, чтобы помочь людям создавать более сложные музыкальные композиции.
Ознакомьтесь с этим руководством в репозитории GitHub.
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из ИнтернетаНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.