[PYTHON] Selbstgesteuertes Lied durch tiefes Lernen (gestapelte LSTM-Ausgabe) [DW Tag 6]

0. Grob gesagt

1. Was ist LSTM?

Siehe unten.

2. Was ist gestapeltes LSTM?

Ein neuronales Netz mit mehreren LSTM-Schichten. Es wird erwartet, dass lange Korrelationen und kurze Korrelationen in jeder Schicht durch Herstellen der Schichten gelernt werden können. Übrigens gibt es auch ein Netzwerk namens Grid LSTM, das LSMTs in vertikaler und horizontaler Richtung verbindet, um sie mehrdimensional zu machen. Es scheint, dass die Zeichenvorhersageaufgabe von Wikipedia und die chinesische Übersetzungsaufgabe eine gute Leistung erbringen.

3. Kettencode

Ich habe ein neuronales Netz hergestellt, wie in der folgenden Abbildung gezeigt.

Die Eingangsschicht und die Ausgangsschicht sind Ein-Hot-Vektoren. Die vier Zwischenschichten (orange) sind die LSTM-Schichten.

class rnn(Chain):
  state = {}

  def __init__(self, n_vocab, n_units):
    print n_vocab, n_units
    super(rnn, self).__init__(
      l_embed = L.EmbedID(n_vocab, n_units),
      l1_x = L.Linear(n_units, 4 * n_units),
      l1_h = L.Linear(n_units, 4 * n_units),
      l2_x = L.Linear(n_units, 4 * n_units),
      l2_h = L.Linear(n_units, 4 * n_units),
      l3_x=L.Linear(n_units, 4 * n_units),
      l3_h=L.Linear(n_units, 4 * n_units),
      l4_x=L.Linear(n_units, 4 * n_units),
      l4_h=L.Linear(n_units, 4 * n_units),
      l_umembed = L.Linear(n_units, n_vocab)
    )

  def forward(self, x, t, train=True, dropout_ratio=0.5):
    h0 = self.l_embed(x)
    c1, h1 = F.lstm(
      self.state['c1'],
      F.dropout( self.l1_x(h0), ratio=dropout_ratio, train=train ) + self.l1_h(self.state['h1'])
    )
    c2, h2 = F.lstm(
      self.state['c2'],
      F.dropout( self.l2_x(h1), ratio=dropout_ratio, train=train ) + self.l2_h(self.state['h2'])
    )
    c3, h3 = F.lstm(
      self.state['c3'],
      F.dropout( self.l3_x(h2), ratio=dropout_ratio, train=train ) + self.l3_h(self.state['h3'])
    )
    c4, h4 = F.lstm(
      self.state['c4'],
      F.dropout( self.l4_x(h3), ratio=dropout_ratio, train=train ) + self.l4_h(self.state['h4'])
    )
    y = self.l_umembed(h4)
    self.state = {'c1': c1, 'h1': h1, 'c2': c2, 'h2': h2, 'c3': c3, 'h3': h3, 'c4': c4, 'h4': h4}
    if train:
      return F.softmax_cross_entropy(y, t), F.accuracy(y, t)
    else:
      return F.softmax(y), y.data

  def initialize_state(self, n_units, batchsize=1, train=True):
    for name in ('c1', 'h1', 'c2', 'h2', 'c3', 'h3', 'c4', 'h4'):
      self.state[name] = Variable(np.zeros((batchsize, n_units), dtype=np.float32), volatile=not train)

4. Experimentieren (Ich habe versucht, die Leistung der automatischen Musikgenerierung zu verbessern)

Ich möchte die Leistung des selbstgesteuerten Songs im vorherigen Artikel (RNN + LSTM [DW Tag 1] verbessern. Letztes Mal waren es 2-Schicht-LSTMs, aber ich habe sie durch die oben genannten 4-Schicht-LSTMs ersetzt und sie neu gelernt.

Trainingsdaten

Ich habe die gleichen Midi-Daten wie beim letzten Mal verwendet. MIDI muss jedoch in das Textdatenformat konvertiert werden. Der Code, der in Text konvertiert werden soll, ist ebenfalls unten aufgeführt. Im folgenden Code können Sie nur eine Spur extrahieren und mit `python midi2text.py --midi foo.midi``` in Text konvertieren. 0_80_40_00 0_90_4f_64 0_90_43_64 120_80_4f_00 ・ ・ `` `1 oder 2 Bytes nach der Deltazeit, Statusbyte, Statusbyte, verkettet mit einem Unterstrich (" Chunk "genannt) werden durch Leerzeichen getrennt. Textdaten werden generiert. Diese Textdaten wurden vom oben genannten LSTM als Trainingsdaten trainiert.

Lernkurve

Songgenerierung

Nach dem Lernen wurde eine Reihe von Blöcken mit demselben Netzwerk generiert. Die generierte Chunk-Sequenz wurde unter Verwendung des später beschriebenen Codes in eine Midi-Datei umgewandelt.

Dies geschah → Wiedergabe (Achtung! Der Ton wird sofort wiedergegeben)

Impressionen

Das Lied war ziemlich gut. Es scheint jedoch, dass der Einfluss des zugrunde liegenden Midi ziemlich stark ist (verbleibend). Ich hatte auch erwartet, dass der Song von Stacked LSTM strukturiert wird, aber er war eintöniger als ich es mir vorgestellt hatte. Um den Song in Bewegung zu setzen, ist es besser, zum Zeitpunkt der Ausgabe der Serie absichtlich unregelmäßiges Rauschen (Ton, der die bis dahin fortgesetzte Serie überträgt) hinzuzufügen, anstatt alles automatisch zu generieren. Ich denke du kannst es bekommen. Unter dem Gesichtspunkt der Komposition gibt es neben der Klangauswahl noch andere Probleme. Das Anpassen von Tönen, das Hinzufügen von Effekten und Sitzungen mit mehreren Instrumenten sind ebenfalls Probleme.

Code

--midi → text (`` `delta time_status byte_status byte gefolgt vom tatsächlichen Daten``` Format)

!/usr/bin/env python
 -*- coding: utf-8 -*-

import sys
import os
import struct
from binascii import *
from types import *
reload(sys)
sys.setdefaultencoding('utf-8')

def is_eq_0x2f(b):
  return int(b2a_hex(b), 16) == int('2f', 16)

def is_gte_0x80(b):
  return int(b2a_hex(b), 16) >= int('80', 16)

def is_eq_0xff(b):
  return int(b2a_hex(b), 16) == int('ff', 16)

def is_eq_0xf0(b):
  return int(b2a_hex(b), 16) == int('f0', 16)

def is_eq_0xf7(b):
  return int(b2a_hex(b), 16) == int('f7', 16)

def is_eq_0x8n(b):
  return int(b2a_hex(b), 16) >= int('80', 16) and int(b2a_hex(b), 16) <= int('8f', 16)

def is_eq_0x9n(b):
  return int(b2a_hex(b), 16) >= int('90', 16) and int(b2a_hex(b), 16) <= int('9f', 16)

def is_eq_0xan(b): # An: 3byte
  return int(b2a_hex(b), 16) >= int('a0', 16) and int(b2a_hex(b), 16) <= int('af', 16)

def is_eq_0xbn(b): # Bn: 3byte
  return int(b2a_hex(b), 16) >= int('b0', 16) and int(b2a_hex(b), 16) <= int('bf', 16)

def is_eq_0xcn(b): # Cn: 2byte
  return int(b2a_hex(b), 16) >= int('c0', 16) and int(b2a_hex(b), 16) <= int('cf', 16)

def is_eq_0xdn(b): # Dn: 2byte
  return int(b2a_hex(b), 16) >= int('d0', 16) and int(b2a_hex(b), 16) <= int('df', 16)

def is_eq_0xen(b): # En: 3byte
  return int(b2a_hex(b), 16) >= int('e0', 16) and int(b2a_hex(b), 16) <= int('ef', 16)

def is_eq_0xfn(b):
  return int(b2a_hex(b), 16) >= int('f0', 16) and int(b2a_hex(b), 16) <= int('ff', 16)

def mutable_lengths_to_int(bs):
  length = 0
  for i, b in enumerate(bs):
    if is_gte_0x80(b):
      length += ( int(b2a_hex(b), 16) - int('80', 16) ) * pow(int('80', 16), len(bs) - i - 1)
    else:
      length += int(b2a_hex(b), 16)
  return length

def int_to_mutable_lengths(length):
  length = int(length)
  bs = []
  append_flag = False
  for i in range(3, -1, -1):
    a = length / pow(int('80', 16), i)
    length -= a * pow(int('80', 16), i)
    if a > 0:
      append_flag = True
    if append_flag:
      if i > 0:
        bs.append(hex(a + int('80', 16))[2:].zfill(2))
      else:
        bs.append(hex(a)[2:].zfill(2))
  return bs if len(bs) > 0 else ['00']

def read_midi(path_to_midi):
  midi = open(path_to_midi, 'rb')
  data = {'header': [], 'tracks': []}
  track = {'header': [], 'chunks': []}
  chunk = {'delta': [], 'status': [], 'meta': [], 'length': [], 'body': []}
  current_status = None

  """
  Load data.header
  """
  bs = midi.read(14)
  data['header'] = [b for b in bs]

  while 1:
    """
    Load data.tracks[0].header
    """
    if len(track['header']) == 0:
      bs = midi.read(8)
      if bs == '':
        break
      track['header'] = [b for b in bs]

    """
    Load data.tracks[0].chunks[0]
    """
    # delta time
    # ----------
    b = midi.read(1)
    while 1:
      chunk['delta'].append(b)
      if is_gte_0x80(b):
        b = midi.read(1)
      else:
        break

    # status
    # ------
    b = midi.read(1)
    if is_gte_0x80(b):
      chunk['status'].append(b)
      current_status = b
    else:
      midi.seek(-1, os.SEEK_CUR)
      chunk['status'].append(current_status)

    # meta and length
    # ---------------
    if is_eq_0xff(current_status): # meta event
      b = midi.read(1)
      chunk['meta'].append(b)
      b = midi.read(1)
      while 1:
        chunk['length'].append(b)
        if is_gte_0x80(b):
          b = midi.read(1)
        else:
          break
      length = mutable_lengths_to_int(chunk['length'])
    elif is_eq_0xf0(current_status) or is_eq_0xf7(current_status): # sysex event
      b = midi.read(1)
      while 1:
        chunk['length'].append(b)
        if is_gte_0x80(b):
          b = midi.read(1)
        else:
          break
      length = mutable_lengths_to_int(chunk['length'])
    else: # midi event
      if is_eq_0xcn(current_status) or is_eq_0xdn(current_status):
        length = 1
      else:
        length = 2

    # body
    # ----
    for i in range(0, length):
      b = midi.read(1)
      chunk['body'].append(b)

    track['chunks'].append(chunk)


    if is_eq_0xff(chunk['status'][0]) and is_eq_0x2f(chunk['meta'][0]):
      data['tracks'].append(track)
      track = {'header': [], 'chunks': []}
    chunk = {'delta': [], 'status': [], 'meta': [], 'length': [], 'body': []}

  return data

def write_text(tracks):
  midi = open('out.txt', 'w')
  for track in tracks:
    for chunks in track:
      midi.write('{} '.format(chunks))

if __name__ == '__main__':
  from argparse import ArgumentParser
  parser = ArgumentParser(description='audio RNN')
  parser.add_argument('--midi', type=unicode, default='', help='path to the MIDI file')
  args = parser.parse_args()
  
  data = read_midi(args.midi)

  # extract midi track
 track_list = [1] # ← Tracknummer, die Sie extrahieren möchten

  tracks = []
  for n in track_list:
    raw_data = []
    chunks = data['tracks'][n]['chunks']
    for i in range(0, len(chunks)):
      chunk = chunks[i]
      if is_eq_0xff(chunk['status'][0]) or \
         is_eq_0xf0(chunk['status'][0]) or \
         is_eq_0xf7(chunk['status'][0]) :
        continue
      raw_data.append('_'.join(
        [str(mutable_lengths_to_int(chunk['delta']))] +
        [str(b2a_hex(chunk['status'][0]))] +
        [str(b2a_hex(body)) for body in chunk['body']]
      ))
    tracks.append(raw_data)

  write_text(tracks)

--Text (delta time_status byte_status byte gefolgt von den tatsächlichen Daten`` format) → midi

!/usr/bin/env python
 -*- coding: utf-8 -*-

import sys
import os
import struct
from binascii import *
from types import *
reload(sys)
sys.setdefaultencoding('utf-8')

def int_to_mutable_lengths(length):
  length = int(length)
  bs = []
  append_flag = False
  for i in range(3, -1, -1):
    a = length / pow(int('80', 16), i)
    length -= a * pow(int('80', 16), i)
    if a > 0:
      append_flag = True
    if append_flag:
      if i > 0:
        bs.append(hex(a + int('80', 16))[2:].zfill(2))
      else:
        bs.append(hex(a)[2:].zfill(2))
  return bs if len(bs) > 0 else ['00']

def write_midi(tracks):
  print len(tracks)
  midi = open('out.midi', 'wb')

  """
  MIDI Header
  """
  header_bary = bytearray([])
  header_bary.extend([0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00])
  header_bary.extend([int(hex(len(tracks))[2:].zfill(4)[i:i+2], 16) for i in range(0, 4, 2)])
  header_bary.extend([0x01, 0xe0])
  midi.write(header_bary)

  for track in tracks:
    track_bary = bytearray([])
    for chunk in track:
      # It is assumed that each chunk consists of just 4 elements
      if len(chunk.split('_')) != 4:
        continue
      int_delta, status, data1, data2 = chunk.split('_')

      if status[0] == '8' or status[0] == '9' or status[0] == 'a' or status[0] == 'b' or status[0] == 'e': # 3byte
        delta = int_to_mutable_lengths(int_delta)
        track_bary.extend([int(d, 16) for d in delta])
        track_bary.extend([int(status, 16)]) 
        track_bary.extend([int(data1, 16)])  
        track_bary.extend([int(data2, 16)])  
      elif status[0] == 'c' or status[0] == 'd':
        delta = int_to_mutable_lengths(int_delta)
        track_bary.extend([int(d, 16) for d in delta])
        track_bary.extend([int(status, 16)]) 
        track_bary.extend([int(data1, 16)])  
      else:
        print status[0]

    """
    Track header
    """
    header_bary = bytearray([])
    header_bary.extend([0x4d, 0x54, 0x72, 0x6b])
    header_bary.extend([int(hex(len(track_bary)+4)[2:].zfill(8)[i:i+2], 16) for i in range(0, 8, 2)])
    midi.write(header_bary)

    """
    Track body
    """
    print len(track_bary)
    midi.write(track_bary)

    """
    Track footer
    """
    footer_bary = bytearray([])
    footer_bary.extend([0x00, 0xff, 0x2f, 0x00])
    midi.write(footer_bary)

if __name__ == '__main__':

 # ↓ Ordnen Sie das Format der "Delta-Zeit_Statusbyte_aktuellen Daten nach dem Statusbyte" durch Leerzeichen getrennt an
 # Funktioniert nicht gut, wenn der Ausführungsstatus enthalten ist. .. ..
  txt = '0_80_40_00 0_90_4f_64 0_90_43_64 120_80_4f_00 0_80_43_00 0_90_51_64 0_90_45_64 480_80_51_00 0_80_45_00 0_90_4c_64 0_90_44_64 120_80_4c_00 0_80_44_00 0_90_4f_64 0_90_43_64 60_80_4f_00 0_80_43_00 0_90_4d_64 0_90_41_64 120_80_4d_00'
  tracks = [txt.split(' ')]
  write_midi(tracks)

Verknüpfung

Tiefes Lernen gründlich studieren [DW Tag 0]

Recommended Posts

Selbstgesteuertes Lied durch tiefes Lernen (gestapelte LSTM-Ausgabe) [DW Tag 6]
Studiere gründlich Deep Learning [DW Tag 0]
Deep Learning / LSTM Scratch Code
<Kurs> Tiefes Lernen: Tag 1 NN
Themen> Deep Learning: Tag 3 RNN
[Deep Learning] Bildklassifizierung mit Faltungsnetz [DW Tag 4]
Deep Learning durch Implementierung 1 gelernt (Return Edition)
Einführung in Deep Learning ~ Dropout Edition ~
Lernprotokoll: Tag 1
Erster Tag des Python-Studiums
Studiere gründlich Deep Learning [DW Tag 0]
[Rabbit Challenge (E-Qualifikation)] Deep Learning (Tag 2)
Deep Learning 2 durch Implementierung gelernt (Bildklassifizierung)
Tiefes Lernen von Grund auf neu (Vorwärtsausbreitung)
[Rabbit Challenge (E-Qualifikation)] Deep Learning (Tag 3)
<Kurs> Deep Learning Day4 Stärkung des Lernens / Tensorflusses
Produzieren Sie wunderschöne Seekühe durch tiefes Lernen
Objekterkennung durch tiefes Lernen, Keras tief zu verstehen
[Rabbit Challenge (E-Qualifikation)] Deep Learning (Tag 4)
Lernaufzeichnung (2. Tag) Scraping von #BeautifulSoup