<jupyter_start><jupyter_text>Magenta魔改记-1：原始数据转换 前言本文主要讲述Magenta项目原始数据整合的过程，并介绍了读取MIDI和XML的函数。通过本文我们可以看到，在原始音乐数据整合的过程中，Magenta将不同格式的数据转换到了一个接近MusicXML的统一格式中统一存储。Magenta中有很多自动作曲模型，它们都使用不同格式的数据输入。在Magenta中，原始数据（MIDI,MusicXML等）先被转换成基于protocol buffers的NoteSequence，之后，根据模型的不同，再将NoteSequence转换成该模型需要的输入。Magenta支持MIDI（.mid/.midi）、MusicXML（.xml/.mxl）、ABC（，没有测试过）等格式的原始数据文件做训练数据。通过[convert_dir_to_note_sequences.py](https://github.com/tensorflow/magenta/blob/master/magenta/scripts/convert_dir_to_note_sequences.py)，这些原始数据被转换为NoteSequence，并以tfrecord格式储存。接下来我们分析在将convert_dir_to_note_sequences.py中如何将MIDI/MusicXML文件转换成NoteSequence。 Magenta version:1.1.1  魔改-1.0：从命令行输入参数：在Magenta的github中提供了如何将原始数据通过命令行转换为NoteSequence protocol buffers的方法：上述链接中提供的Linux命令行如下：```INPUT_DIRECTORY= TFRecord file that will contain NoteSequence protocol buffers.SEQUENCES_TFRECORD=/tmp/notesequences.tfrecordconvert_dir_to_note_sequences \  --input_dir=$INPUT_DIRECTORY \  --output_file=$SEQUENCES_TFRECORD \  --recursive```这一步的python命令行如下（摘自[convert_dir_to_note_sequences.py](https://github.com/tensorflow/magenta/blob/master/magenta/scripts/convert_dir_to_note_sequences.py)源代码注释）：```Example usage:  $ python magenta/scripts/convert_dir_to_note_sequences.py \    --input_dir=/path/to/input/dir \    --output_file=/path/to/tfrecord/file \    --log=INFO```那么下面介绍如何在代码中直接修改这一步预处理的参数。这一步运行的文件位置如下：[convert_dir_to_note_sequences.py](https://github.com/tensorflow/magenta/blob/master/magenta/scripts/convert_dir_to_note_sequences.py)打开源代码我们可以看到，程序一开始就定义了一系列tf.flag：```FLAGS = tf.app.flags.FLAGStf.app.flags.DEFINE_string('input_dir', None,                           'Directory containing files to convert.')输入路径tf.app.flags.DEFINE_string('output_file', None,                           'Path to output TFRecord file. Will be overwritten '                           'if it already exists.')输出路径tf.app.flags.DEFINE_bool('recursive', False,                         'Whether or not to recurse into subdirectories.')是否递归查找子路径的文件tf.app.flags.DEFINE_string('log', 'INFO',                           'The threshold for what messages will be logged '                           'DEBUG, INFO, WARN, ERROR, or FATAL.')显示消息的类型```tf.app.flags是Tensorflow中用于从命令行传递参数的模块，基于argparse。如果在运行时不输入参数，则会按程序中默认填写的参数运行。通过`python convert_dir_to_note_sequences.py –h `可以显示注释信息和参数及其详情。因此，我们在自定义参数时，既可以在命令行运行时输入：```python convert_dir_to_note_sequences.py --input_dir=XXX --output_file=YYY --recursive=True```同样，我们也可以把前面这几行当做超参数变量声明，直接在`convert_dir_to_note_sequences.py`中修改，然后运行这个文件。除了命令行之外，我们接下来介绍如何在python文件中直接修改参数以及如何在jupyter环境中修改参数并调试。  魔改-2.0：在jupyter notebook中调试：接下来，我们介绍如何在jupyter notebook中调试，并展现这个程序的详细原理以及文件储存的数据类型。程序源代码地址：https://github.com/tensorflow/magenta/blob/master/magenta/scripts/convert_dir_to_note_sequences.py在本程序中，大致的运行步骤为：1. 先检测输入路径（以及子路径）中所有符合要求的文件，生成文件路径列表。2. 再根据路径列表多线程地处理数据，转换为NoteSequence。3. 保存为.tfrecord文件。第一步对应`queue_conversions(root_dir, sub_dir, pool, recursive=False)`函数，在此不多展开。第二步对应`convert_midi(root_dir, sub_dir, full_file_path)`、`convert_musicxml(root_dir, sub_dir, full_file_path)`两个函数。顾名思义就是针对midi和xml文件的处理函数（一开始说的ABC数据处理函数未知）。它们的参数以及返回值可以在函数注释中找到详细的介绍。简单来说就是输入文件路径、文件所在文件夹路径、上一级路径，输出NoteSequence proto，一个在Magenta项目中用来表示音符序列的数据类型。第三步则对应`convert_directory(root_dir, output_file, num_threads,recursive=False)`，是总的函数。 首先我们可以把这个文件导入：<jupyter_code>import tensorflow as tf
import magenta as mgt
import magenta.scripts.convert_dir_to_note_sequences as cvrt<jupyter_output>WARNING: The TensorFlow contrib module will not be included in TensorFlow 2.0.
For more information, please see:
  * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md
  * https://github.com/tensorflow/addons
If you depend on functionality not listed there, please file an issue.<jupyter_text>导入之后我们就可以用查看子类的方式查看它的FLAGS参数：<jupyter_code>print(cvrt.FLAGS)
# 加这行是因为jupyter notebook对tf.app.flags.FLAGS有bug
# 见https://github.com/tensorflow/tensorflow/issues/17702
tf.app.flags.DEFINE_string('f', '', 'kernel')<jupyter_output><empty_output><jupyter_text>因此我们也可以用修改FLAGS子类参数的方法运行本程序： 首先给参数赋值：<jupyter_code>cvrt.FLAGS.input_dir = r'Dataset\raw\example-musicxml'
cvrt.FLAGS.output_file = r'Dataset\pre\example-musicxml.tfrecord'
cvrt.FLAGS.recursive = True
cvrt.FLAGS.log = 'INFO'<jupyter_output><empty_output><jupyter_text>接着，运行main函数：如果使用`convert_dir_to_note_sequences.py`文件中的运行方法，可以替换成`tf.app.run(cvrt.main)`，但是使用`tf.app.run`会使进程结束并抛出异常，这里我们先使用直接运行`main`函数的方法。在`convert_dir_to_note_sequences.py`中，main函数有一个不使用的占位参数`unused_argv`。<jupyter_code>unused_argv = ''
cvrt.main(unused_argv)<jupyter_output>INFO:tensorflow:Converting files in 'Dataset\raw\example-musicxml\'.
INFO:tensorflow:0 files converted.
INFO:tensorflow:Converted MusicXML file Dataset\raw\example-musicxml\bwv1.6.mxl.<jupyter_text>这样，我们就完成了第一步NoteSequences的创建。 如上所说，转换MIDI和MusicXML对应`convert_midi(root_dir, sub_dir, full_file_path)`、`convert_musicxml(root_dir, sub_dir, full_file_path)`两个函数。下面我们选取一个MusicXML文件和一个MIDI，分别来运行一下转换函数并看一下它们返回的结果。 MusicXML转换：<jupyter_code>full_file_path_xml = r'Dataset\raw\example-musicxml\bwv1.6.mxl'
root_dir_xml = r'Dataset\raw\example-musicxml'
sub_dir_xml = r'Dataset\raw\example-musicxml'
sequence_xml = cvrt.convert_musicxml(root_dir_xml, sub_dir_xml,
                                     full_file_path_xml)<jupyter_output>INFO:tensorflow:Converted MusicXML file Dataset\raw\example-musicxml\bwv1.6.mxl.<jupyter_text>查看转换结果sequence_xml的类型<jupyter_code>print(type(sequence_xml))<jupyter_output><class 'music_pb2.NoteSequence'><jupyter_text>我们可以看到sequence_xml是一个基于Google protobuf的数据类型。 接下来，我们查看sequence_xml的内容：<jupyter_code>print(str(sequence_xml)[:1000])<jupyter_output>id: "/id/musicxml/example-musicxml/b916b4d6787e8de96484206b4c617879add937ce"
filename: "Dataset\\raw\\example-musicxml\\bwv1.6.mxl"
collection_name: "example-musicxml"
ticks_per_quarter: 220
time_signatures {
  numerator: 4
  denominator: 4
}
time_signatures {
  time: 38.5
  numerator: 3
  denominator: 4
}
time_signatures {
  numerator: 1
  denominator: 4
}
time_signatures {
  time: 0.5
  numerator: 4
  denominator: 4
}
key_signatures {
  key: F
}
tempos {
  qpm: 120.0
}
notes {
  pitch: 65
  velocity: 64
  end_time: 0.5
  numerator: 1
  denominator: 4
  instrument: 7
  program: 1
  voice: 1
}
notes {
  pitch: 67
  velocity: 64
  start_time: 0.5
  end_time: 0.75
  numerator: 1
  denominator: 8
  instrument: 7
  program: 1
  voice: 1
}
notes {
  pitch: 60
  velocity: 64
  start_time: 0.75
  end_time: 1.0
  numerator: 1
  denominator: 8
  instrument: 7
  program: 1
  voice: 1
}
notes {
  pitch: 65
  velocity: 64
  start_time: 1.0
  end_time: 1.25
  numerator: 1
  denominator: 8
  instrum[...]<jupyter_text>从上面我们可以看到这里面包含了路径、id、以及xml中的内容。数据格式很像MusicXML与MIDI的结合，但将它们以类的形式结构化储存了。于是，我们也可以直接访问它的子类：<jupyter_code>print(sequence_xml.id)
print(sequence_xml.filename)
print(sequence_xml.source_info)
print(sequence_xml.total_time)
print(type(sequence_xml.notes))<jupyter_output><class 'google.protobuf.pyext._message.RepeatedCompositeContainer'><jupyter_text>`sequence_xml`的`notes`类里面就是最主要的内容了，`notes`记录了所有的音符。音符类当然也支持索引，我们可以看到每个音符由音高、音色、起始时间、终止时间等元素组成。<jupyter_code>print(sequence_xml.notes[0])
print(sequence_xml.notes[20])
print(sequence_xml.notes[30])<jupyter_output>pitch: 65
velocity: 64
end_time: 0.5
numerator: 1
denominator: 4
instrument: 7
program: 1
voice: 1

pitch: 70
velocity: 64
start_time: 5.0
end_time: 5.25
numerator: 1
denominator: 8
instrument: 7
program: 1
voice: 1

pitch: 71
velocity: 64
start_time: 7.0
end_time: 7.5
numerator: 1
denominator: 4
instrument: 7
program: 1
voice: 1<jupyter_text>接下来，我们查看MIDI文件的转换结果：<jupyter_code>full_file_path_midi = r'Dataset\raw\example-midi\Bwv0525 Sonate en trio n1.mid'
root_dir_midi = r'Dataset\raw\example-midii'
sub_dir_midi = r'Dataset\raw\example-midi'
sequence_midi = cvrt.convert_midi(root_dir_midi, sub_dir_midi,
                                  full_file_path_midi)
print(str(sequence_midi)[:1000])
print(sequence_midi.id)
print(sequence_midi.filename)
print(sequence_midi.source_info)
print(sequence_midi.total_time)
print(sequence_midi.notes[0])
print(sequence_midi.notes[20])
print(sequence_midi.notes[30])<jupyter_output>pitch: 70
velocity: 92
start_time: 6.4
end_time: 6.800000000000001

pitch: 72
velocity: 92
start_time: 12.8
end_time: 13.200000000000001

pitch: 74
velocity: 92
start_time: 16.0
end_time: 16.400000000000002
{'fraction_non_alphanumeric': 0.10868522072936661, 'fraction_numerical': 0.03166986564299424, 'mean_word_length': 6.820825515947467, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 35, 'https://': 7, 'lorem ipsum': 0, 'www.': 0, 'xml': 39}, 'pii_count': 0, 'substrings_counts': 3, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '583763', 'license': 'Apache-2.0', 'n_tokens_mistral': 4085, 'n_tokens_neox': 4042, 'n_words': 386, 'path': 'Magenta魔改记-1：原始数据转换.ipynb', 'repo_name': 'lukewys/Magenta-Modification', 'size': '8336'}