conda create -n tf20 python==3.7
conda activate tf20
pip install jupyter
pip install matplotlib
pip install scipy
pip install music21
pip install tensorflow==2.0.0-alpha0
import tensorflow as tf
print(tf.__version__)
2.0.0-alpha0
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM
import tensorflow.keras.utils as utils
import os
#http://web.mit.edu/music21/doc/usersGuide/usersGuide_08_installingMusicXML.html
import music21
seq = ['g8', 'e8', 'e4', 'f8', 'd8', 'd4', 'c8', 'd8', 'e8', 'f8', 'g8', 'g8', 'g4',
'g8', 'e8', 'e8', 'e8', 'f8', 'd8', 'd4', 'c8', 'e8', 'g8', 'g8', 'e8', 'e8', 'e4',
'd8', 'd8', 'd8', 'd8', 'd8', 'e8', 'f4', 'e8', 'e8', 'e8', 'e8', 'e8', 'f8', 'g4',
'g8', 'e8', 'e4', 'f8', 'd8', 'd4', 'c8', 'e8', 'g8', 'g8', 'e8', 'e8', 'e4']
print("length of seq: {0}".format(len(seq)))
length of seq: 54
note_seq = ""
for note in seq:
note_seq += note + " "
m = music21.converter.parse("2/4 " + note_seq, format='tinyNotation')
m.show("midi")
m.show()
code2idx = {'c4': 0, 'd4': 1, 'e4': 2, 'f4': 3, 'g4': 4, 'a4': 5, 'b4': 6,
'c8': 7, 'd8': 8, 'e8': 9, 'f8': 10, 'g8': 11, 'a8': 12, 'b8': 13}
idx2code = {0: 'c4', 1: 'd4', 2: 'e4', 3: 'f4', 4: 'g4', 5: 'a4', 6: 'b4',
7: 'c8', 8: 'd8', 9: 'e8', 10: 'f8', 11: 'g8', 12: 'a8', 13: 'b8'}
def seq2dataset(seq, window_size):
dataset = []
for i in range(len(seq) - window_size):
subset = seq[i: (i + window_size + 1)]
dataset.append([code2idx[item] for item in subset])
return np.array(dataset)
test_seq = ['c4', 'd4', 'e4', 'f4', 'g4', 'd8', 'b8']
dataset = seq2dataset(seq=test_seq, window_size=4)
print(dataset)
[[ 0 1 2 3 4] [ 1 2 3 4 8] [ 2 3 4 8 13]]
n_steps = 4
n_inputs = 1
dataset = seq2dataset(seq, window_size=n_steps)
print("dataset.shape: {0}".format(dataset.shape))
dataset.shape: (50, 5)
x_train = dataset[:, 0: n_steps]
y_train = dataset[:, n_steps]
print("x_train: {0}".format(x_train.shape))
print("y_train: {0}".format(y_train.shape))
print(x_train[0])
print(y_train[0])
x_train: (50, 4) y_train: (50,) [11 9 2 10] 8
max_idx_value = len(code2idx) - 1
print("max_idx_value: {0}".format(max_idx_value))
max_idx_value: 13
x_train = x_train / float(max_idx_value)
x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], n_inputs))
y_train = utils.to_categorical(y_train)
one_hot_vec_size = y_train.shape[1]
print("one hot encoding vector size is {0}".format(one_hot_vec_size))
print("After pre-processing")
print("x_train: {0}".format(x_train.shape))
print("y_train: {0}".format(y_train.shape))
one hot encoding vector size is 12 After pre-processing x_train: (50, 4, 1) y_train: (50, 12)
model = Sequential()
model.add(LSTM(
units=128,
kernel_initializer='glorot_normal',
bias_initializer='zero',
batch_input_shape=(1, n_steps, n_inputs),
stateful=True
))
model.add(Dense(
units=one_hot_vec_size,
kernel_initializer='glorot_normal',
bias_initializer='zero',
activation='softmax'
))
model.compile(
loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy']
)
class LossHistory(tf.keras.callbacks.Callback):
def init(self):
self.epoch = 0
self.losses = []
def on_epoch_end(self, batch, logs={}):
self.losses.append(logs.get('loss'))
if self.epoch % 100 == 0:
print("epoch: {0} - loss: {1:8.6f}".format(self.epoch, logs.get('loss')))
self.epoch += 1
num_epochs = 1500
history = LossHistory() # 손실 이력 객체 생성
history.init()
for epoch_idx in range(num_epochs + 1):
model.fit(
x=x_train,
y=y_train,
epochs=1,
batch_size=1,
verbose=0,
shuffle=False,
callbacks=[history]
)
if history.losses[-1] < 1e-5:
print("epoch: {0} - loss: {1:8.6f}".format(epoch_idx, history.losses[-1]))
model.reset_states()
break
model.reset_states()
epoch: 0 - loss: 2.421980 epoch: 100 - loss: 1.248247 epoch: 200 - loss: 0.175045 epoch: 300 - loss: 0.075572 epoch: 400 - loss: 0.000506 epoch: 500 - loss: 0.000030 epoch: 541 - loss: 0.000010
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(history.losses)
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train'], loc='upper right')
plt.show()
scores = model.evaluate(x_train, y_train, batch_size=1)
print("{0}: {1}".format(model.metrics_names[1], scores[1]*100))
model.reset_states()
50/50 [==============================] - 0s 2ms/sample - loss: 9.6750e-06 - accuracy: 1.0000 accuracy: 100.0
pred_count = 50 # 최대 예측 개수 정의
# 한 스텝 예측
seq_out = ['g8', 'e8', 'e4', 'f8']
pred_out = model.predict(x_train)
for i in range(pred_count):
idx = np.argmax(pred_out[i]) # one-hot 인코딩을 인덱스 값으로 변환
seq_out.append(idx2code[idx]) # seq_out는 최종 악보이므로 인덱스 값을 코드로 변환하여 저장
model.reset_states()
print("one step prediction : ", seq_out)
one step prediction : ['g8', 'e8', 'e4', 'f8', 'd8', 'd4', 'c8', 'd8', 'e8', 'f8', 'g8', 'g8', 'g4', 'g8', 'e8', 'e8', 'e8', 'f8', 'd8', 'd4', 'c8', 'e8', 'g8', 'g8', 'e8', 'e8', 'e4', 'd8', 'd8', 'd8', 'd8', 'd8', 'e8', 'f4', 'e8', 'e8', 'e8', 'e8', 'e8', 'f8', 'g4', 'g8', 'e8', 'e4', 'f8', 'd8', 'd4', 'c8', 'e8', 'g8', 'g8', 'e8', 'e8', 'e4']
seq_in = ['g8', 'c8', 'f4', 'e8']
seq_out = seq_in
seq_in = [code2idx[note] / float(max_idx_value) for note in seq_in] # 코드를 인덱스값으로 변환
for i in range(pred_count):
sample_in = np.array(seq_in)
sample_in = np.reshape(sample_in, (1, n_steps, n_inputs)) # 샘플 수, 타입스텝 수, 속성 수
pred_out = model.predict(sample_in)
idx = np.argmax(pred_out)
seq_out.append(idx2code[idx])
seq_in.append(idx / float(max_idx_value))
seq_in.pop(0)
model.reset_states()
print("full song prediction : ")
for note in seq_out:
print(note, end=" ")
full song prediction : g8 c8 f4 e8 d8 d4 c8 d8 e8 f8 g8 g8 g4 g8 e8 e8 e8 f8 d8 d4 c8 e8 g8 g8 e8 e8 e4 d8 d8 d8 d8 d8 e8 f4 e8 e8 e8 e8 e8 f8 g4 g8 e8 e4 f8 d8 d4 c8 e8 g8 g8 e8 e8 e4
#http://web.mit.edu/music21/doc/usersGuide/usersGuide_08_installingMusicXML.html
import music21
note_seq = ""
for note in seq_out:
note_seq += note + " "
conv_midi = music21.converter.subConverters.ConverterMidi()
m = music21.converter.parse("2/4 " + note_seq, format='tinyNotation')
m.show("midi")
m.show()
m.write("midi", fp="./new_music.mid")
'./new_music.mid'