Asking RNN + LSTM : What would Mozart write?(번역)

Asking RNN + LSTM : What would Mozart write?

http://www.wise.io/tech/asking-rnn-and-ltsm-what-would-mozart-write

 

 

AI를 넘어서는 진보는 Aritificial Creativity가 될 것이다.

나는 AC에 한동안 관심이 있었고 AC를 미술, 음악, 글쓰기 등에서 테스트하는 방법을 연구하는 학회에서 여러가지 주제들을 학습했다.

음악에서는, 흥미롭고, 도전할만 하고, 유니크한(기존의 작곡가들과 차별화 될 만한) 곡을 기계가 작곡한다면 매우 큰 성과가 될 것이다.

기계 작곡은 긴 역사를 가지고 있다.

 

LSTM RNN 에 대한 관심이 부활하면서 나는 이를 이용한 음악 생성에 흥미를 느꼈다. RNN을 사용한 음악 생성에는 여러시도가 있어왔지만, 이제는 큰 데이터를 사용한 복잡한 모델을 학습할 수 있는 시대에 왔다고 생각한다. Andrej Karpathy의 블로그에는 character level에서 셰익스피어나 폴 그래함의 에세이를 학습해서 그럴듯하게 흉내내는 텍스트를 생성해내는 방법을 제시하고 있다. 무언가를 표현하는 센스는 없지만, 구조자체를 배우는데에 있어서 LSTM RNN이 훌륭한 결과물을 보여주고 있다.

 

 

 

_Data Preparation for training

 

나는 딥러닝과 음악 양쪽다에 대단한 전문가가 아니기 때문에 사실 이 프로젝트에 확신은 없다.

 

Which music?

Karpathy의 코드를 기반으로 사용하기 위해서, 나는 이에 알맞는 데이터셋을 만들어야 했다. 특정 음악 장르와 작곡가를 묶는 것이 알맞아 보인다. 최소한의 악기를 사용하는 것도 알맞아 보인다. 따라서 나는 모짜르트와 베토벤의 피아노 소나타를 선정했다.

 

Which format?

음악을 표현하는 데에는 많은 방식이 있다. 음악을 다루는 최근의 라이브러리들이(muisc21같은) MusicXML을 사용한다. 또하나는 humdrum kern이다. 이 둘은 서로 전환이 가능하다. humdrum이 더 컴팩트해보인다.

 

http://kern.humdrum.org

여기에서 커다란 클래식 음악 데이터를 구할 수 있다.

 

 

 

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

Dload  Upload   Total   Spent    Left  Speed

100 22098    0 22098    0     0   3288      0 –:–:–  0:00:06 –:–:–  5133

 

이것을 music21을 사용해서 musicXML 포맷을 변경할 수 있다.

import music21

m = music21.converter.parse(“ana-music/corpus/mozart/sonata07-1.krn”)

m.show(“musicxml”)

 

musicXML의 첫 노트는 256 character 의 11 라인의 코드이다.

   <part id="P1">

<note default-x=”70.79″ default-y=”-50.00″>

<grace/>

<pitch>

<step>C</step>

<octave>4</octave>

</pitch>

<voice>1</voice>

<type>eighth</type>

<stem>up</stem>

<beam number=”1″>begin</beam>

</note>

</part>

 

반면, humdrum 포맷의 첫 노트는 14 charater이다.

.t(32cQ/LLLt.n

 

컴팩트함을 위해 나는 humdrum 포맷을 사용했다. ker.humdrum.org 사이트를 헤메는 대신, 나는 Github에서 좋은 데이터 셋을 찾아냈다. 이 프로젝트는 ana-music이라는 프로젝트였다. (Automatic analysis of classical music for generative composition) 

이 프로젝트는 32개의 베토벤 소나타(102개의 악장)와 17개의 모짜르트 소나타(51개 악장)를 컴파일 했다.

 

이 kern 포맷은 다음과 같은 메타데이터로 시작된다.

 

!!!COM: Mozart, Wolfgang Amadeus

!!!CDT: 1756/01/27/-1791/12/05/

!!!CNT: German

!!!OTL: Piano Sonata No. 7 in C major

!!!SCT1: K<sup>1</sup> 309

!!!SCT2: K<sup>6</sup> 284b

!!!OMV: Mvmt. 1

!!!OMD: Allegro con spirito

!!!ODT: 1777///

**kern    **kern    **dynam

*staff2    *staff1    *staff1/2

*>[A,A,B]    *>[A,A,B]    *>[A,A,B]

*>norep[A,B]    *>norep[A,B]    *>norep[A,B]

*>A    *>A    *>A

*clefF4    *clefG2    *clefG2

*k[]    *k[]    *k[]

*C:    *C:    *C:

*met(c)    *met(c)    *met(c)

*M4/4    *M4/4    *M4/4

*MM160    *MM160    *MM160

=1-    =1-    =1-

….

 

이 작곡가와 노래 자체에 대한 메타데이터(!!!로 시작되는 라인들) 이후에는 세개의 staff / voices와 반복 schedule(dc al code), 음악의 키, 템포, 등등이 정의된다.

첫번째 staff는 다음 라인으로 시작된다. :

=1-    =1-    =1-

 

첫번째 실험에서는 나는 !!!로 시작되는 라인만 지워버리고 나머지는 서문으로 남겨두었다. 하지만 트레이닝 데이터가 적기 때문에 나는 생성된 서문이 매우 부정확하다는 것을 알게 되었다. 따라서 메타데이터, 서문, 그리고 마디 수 정보도 모두 날려버렸다.

 

import glob

REP=“@n

composers = [“mozart”,“beethoven”]

for composer in composers:

comp_txt = open(composer + “.txt”,“w”)

ll = glob.glob(dir + “/ana-music/corpus/{composer}/*.krn”.format(composer=composer))

for song in ll:

lines = open(song,“r”).readlines()

out = []

found_first = False

for l in lines:

if l.startswith(“=”):

## new measure, replace the measure with the @ sign, not part of humdrum

out.append(REP)

found_first = True

continue

if not found_first:

## keep going until we find the end of the header and metadata

continue

if l.startswith(“!”):

## ignore comments

continue

out.append(l)

comp_txt.writelines(out)

comp_txt.close()

 

이 과정을 통해서 나는 mozart.txt와 beethoven.txt를 만들어서 학습할 준비를 마쳤다.

 

 

_Learning

 

학습을 위해서 나는 terminal.com 환경을 이용했다. (AWS 기반으로 돌아가는 유저친화적 소프트웨어 레이어로 GPU 연산 지원)

나는 Torch와 nngraph, optim을 설치하고 학습을 시작했다.

 

th train.lua -data_dir data/beethoven -rnn_size 128

-num_layers 3 -dropout 0.3

-eval_val_every 100

-checkpoint_dir cv/beethoven -gpuid -1

 

th train.lua -data_dir data/mozart -rnn_size 128 -num_layers 3

-dropout 0.05 -eval_val_every 100

-checkpoint_dir cv/mozart -gpuid -1

 

서문을 제거한 트레이닝 데이터에는 71가지의 character만 있었다. 학습이 잘 되는 것을 확인한 나는 더 빠른 머신을 도입했다. GPU연산을 도입하는 데에는 실패했지만 18시간에 걸친 CPU학습으로 다음과 같은 결과를 얻었다.

 

5159/5160 (epoch 29.994), train_loss = 0.47868490, grad/param norm = 4.2709

evaluating loss over split index 2

1/9…

2/9…

3/9…

4/9…

5/9…

6/9…

7/9…

8/9…

9/9…

saving checkpoint to cv/mozart/lm_lstm_epoch30.00_0.5468.t7



Note: I played only a little bit with dropout rates (for regularization) so there’s obviously a lot more to try here. 

 

 

 

Sampling with the model

 

학습된 모델이 생성되었으므로 이것을 이용해서 샘플을 생성해보자.

th sample.lua cv/beethoven/lm_lstm_epoch12.53_0.6175.t7

-temperature 0.8 -seed 1 -primetext “@”

-sample 1 -length 15000 -gpuid -1 > b5_0.8.txt

 

 

-primetext @ 는 마디를 시작하라는 뜻이다.

-length 15000 는 15000개의 character를 요청한다. (적당한 길이의 악보)

-temperature 0.8 는 보수적인 작업을 하도록 하는 변수이다.

 

생성된 모짜르트 샘플의 첫 두마디는 다음과 같다.

@

8r      16cc#LL        .

.       16ee   .

8r      16cc#  .

.       16ddJJ .

4r      16gg#LL        .

.       16aa   .

.       16dd   .

.       16ff#JJ)       .

.       16ddLL .

.       16gg’  .

.       16ee   .

.       16gg#JJ        .

*clefF4 *       *

@

8.GG#L 16bbLL .

.       16ee   .

8F#    16gg   .

.       16ee#JJ        .

8G     16ff#LL        .

.       16bb   .

8G#    16ff#  .

.       16gg#  .

8G#J   16ff#  .

.       16ccc#JJ       .

@

 

이것은 kern 포맷이 아니다. 서문이 없고, 마디의 넘버가 적혀있지 않다.

따라서 나는 서문을 더해서 파일을 잘 메꾸었다.

 

!!! m1a.krn – josh bloom – AC mozart

**kern  **kern  **dynam

*staff2 *staff1 *staff1/2

*>[A,A,B,B]     *>[A,A,B,B]     *>[A,A,B,B]

*>norep[A,B]    *>norep[A,B]    *>norep[A,B]

*>A     *>A     *>A

*clefF4 *clefG2 *clefG2

*k[]    *k[]    *k[]

*C:     *C:     *C:

*M4/4   *M4/4   *M4/4

*met(c) *met(c) *met(c)

*MM80  *MM80  *MM80

==   ==  ==

*-   *-  *-

EOF

 

또한 마디 넘버를 더했다.

f = open("m1a.krn","r").readlines()

r = []

bar = 1

for l in f:

if l.startswith(“@”):

if bar == 1:

r.append(“=1-t=1-t=1-n)

else:

r.append(“={bar}t={bar}t={bar}n.format(bar=bar))

bar += 1

else:

r.append(l)

open(“m1a-bar.krn”,“w”).writelines(r)

 

결과물을 확인할 차례이다.

from music21 import *

m1 = converter.parse(“m1a-bar.krn”)

 

이 결과물은 music21에 의해서 valid한 음악으로 인식되었다. 노트에 대해서는 어떠한 후처리도 하지 않았음에도 불구하고 말이다. 다만 때로는 music21로 파싱하는 파서에서 “Unknown dynamic tags”를 쉼표로 바꾸는 처리를 해야했다.

 

이제 악보를 살펴보자.

m1.show(“musicxml”)

 

 

 

 

_Conclusion

 

사실 음악은 별로 좋게 들리지 않는다.

어떤 면에서는 모짜르트의 프레이즈들 처럼 들린다. 쉼표와 빨라짐과 긴장감의 변화같은 것들이 있다.

하지만 코드 진행은 이상하고 멜로디는 귀에 남지 않는다. 하지만 랜덤하게 재생되는 음악보다는 훨씬 낫다.

 

이 시도는 음악적 조화를 배우도록 하는데에는 실패했다. 하지만 여러가지 다른 방향성을 시도해볼 수 있다.

 

a) increase the dataset size

b) combine composers and genres

c) play with the hyperparameters of the model (how many layers? how much dropout? etc.)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s