컴퓨터공학/인공지능

[인공지능] 8장. 시계열 데이터와 순환 신경망3 - LSTM 편곡

NIMHO 2023. 5. 14. 18:50
728x90

복습하기 위해 학부 수업 내용을 필기한 내용입니다.
이해를 제대로 하지 못하고 정리한 경우 틀린 내용이 있을 수 있습니다.
그러한 부분에 대해서는 알려주시면 정말 감사하겠습니다.

Time-Series Data and Recurrent Neural Networks

 

8.4 편곡하는 인공지능

인공지능의 창작 능력

- 딥드림으로 생성한 그림

- 마젠타 프로젝트의 음악 창작 활동

- 인공지능 소설 등

 

원시적인 수준에서 새로운 곡을 생성

- 앞 소설을 보고 다음 음표를 LSTM으로 예측하는 방식의 단순한 편곡을 한다.

- 높은 수준은 생성 모델(generative model)을 사용해야 한다.

 

8.4.1 ABC 악보 표기

가장 간단한 음악 표기법인 ABC

ABC 표기를 연주하는 코드이다.

import music21

# "작은 별" 악보를 ABC 표기로 표현
little_star="tinynotation: 4/4 c4 c4 g4 g4 a4 a4 g2 f4 f4 e4 e4 d4 d4 c2 g4 g4 f4 f4 e4 e4 d2 g4 g4 f4 f4 e4 e4 d2 c4 c4 g4 g4 a4 a4 g2 f4 f4 e4 e4 d4 d4 c2"
music21.converter.parse(little_star).show('mid') # 스피커로 연주를 들려줌

 

8.4.2 LSTM으로 편곡

악보를 학습하는 LSTM 신경망 코드이다.

- ABC 표기 데이터는 2 채널(계이름과 박자)이고, 분류 문제이다.

import music21

# "작은 별" 악보를 ABC 표기로 표현
little_star="tinynotation: 4/4 c4 c4 g4 g4 a4 a4 g2 f4 f4 e4 e4 d4 d4 c2 g4 g4 f4 f4 e4 e4 d2 g4 g4 f4 f4 e4 e4 d2 c4 c4 g4 g4 a4 a4 g2 f4 f4 e4 e4 d4 d4 c2"
music21.converter.parse(little_star).show('mid') # 스피커로 연주를 들려줌

import numpy as np

# 계이름과 숫자를 상호 변환하는 표(딕셔너리 자료구조를 사용함)
note2num={'c':1,'d':2,'e':3,'f':4,'g':5,'a':6,'b':7}
num2note={1:'c',2:'d',3:'e',4:'f',5:'g',6:'a',7:'b'}

# ABC 표기를 시계열 데이터로 변환
def abc2timeseries(s):
    notes=s.split(' ')[2:]
    seq=[]
    for i in notes:
        seq.append([note2num[i[0]],int(i[1])])
    return seq

# 시계열 데이터를 ABC 표기로 변환
def timeseries2abc(t):
    s='tinynotation: 4/4'
    for i in t:
        s=s+' '+num2note[i[0]]+str(i[1])
    return s

# 원핫 코드로 변환하는 표
onehot=[[1,2],[2,2],[3,2],[4,2],[5,2],[6,2],[7,2],[1,4],[2,4],[3,4],[4,4],[5,4],[6,4],[7,4],[1,8],[2,8],[3,8],[4,8],[5,8],[6,8],[7,8]]

# 레이블을 원핫 코드로 변환
def to_onehot(l):
    t=[]
    for i in range(len(l)):
        a=np.zeros(len(onehot))
        a[onehot.index(list(l[i]))]=1.0
        t.append(a)
    return np.array(t)

# 시계열 데이터를 훈련 집합으로 자름
def seq2dataset(seq,window,horizon):
    X=[]; Y=[]
    for i in range(len(seq)-(window+horizon)+1):
        x=seq[i:(i+window)]
        y=(seq[i+window+horizon-1])
        X.append(x); Y.append(y)
    return np.array(X), np.array(Y)

w=8 # 윈도우 크기
h=1 # 수평선 계수

seq=abc2timeseries(little_star)
X,Y=seq2dataset(seq,w,h)
print(X.shape,Y.shape)
print(X[0],Y[0])

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM
import tensorflow as tf

# 훈련 집합 구축
split=int(len(X)*1.0) # 100%를 훈련집합으로 사용
x_train=X[0:split]; y_train=Y[0:split]
y_train=to_onehot(y_train)

# LSTM 모델 설계와 학습
model=Sequential()
model.add(LSTM(units=128,activation='relu',input_shape=x_train[0].shape))
model.add(Dense(y_train.shape[1],activation='softmax'))
model.compile(loss='categorical_crossentropy',optimizer='Adam',metrics=['accuracy'])
model.fit(x_train,y_train,epochs=200,batch_size=1,verbose=2)

# 학습된 모델로 편곡을 하는 함수(first_measure: 첫 소절, duration: 생성될 곡의 길이)
def arranging_music(model,first_measure,duration):
    music=first_measure
    for i in range(duration):
        p=model.predict(np.float32(np.expand_dims(music[-w:],axis=0)))
        music=np.append(music,[onehot[np.argmax(p)]],axis=0)
    return timeseries2abc(music)

new_song=arranging_music(model,x_train[0],50)

print(new_song)
music21.converter.parse(new_song).show('mid')
728x90

 

변형된 첫 소절로 편곡

- 똑같은 곡을 생성해 내는 것은 무의미하다.

- 원래 곡의 패턴을 유지하면서 원래 곡과 다른 곡을 생성해야 한다.

- 아래와 같이 첫 소절을 원래 곡과 다르게 주고 arranging_music 함수를 호출해야 한다.

 

8.4.3 여러 곡을 이용한 학습

아래 코드는 여러 곡을 학습하는 코드이다.

- '작은 별', '봄나들이', '나비야 나비야'의 세 곡을 결합한 데이터로 모델링하고 편곡했다.

 

위 코드와 유사하지만, 곡을 한곡이 아닌 세곡을 넣어준다.
세 곡을 시계열로 변환하고 결합해 준 후 학습하면 된다.

나머지 부분은 원래 코드와 동일하다.
그렇기에 코드는 나중에 다시 와서 넣어야겠다.

728x90