将TensorFlow自动编码器与音乐配合使用

将TensorFlow自动编码器与音乐配合使用

自动编码器是深度学习的一个令人惊奇的部分,有许多令人敬畏的用途。它们允许我们做任何事情,从数据压缩到重构输入。

为什么这很重要?

创建一个能够“清理”或重建信号的神经网络的能力是非常有用的。从在线流媒体压缩音乐到通信信号进行去噪,这都有其用途。

例如,假设您想创建一个流媒体音乐服务,它可以在不使用大量带宽的情况下提供无损的音乐。实际上,您可以使用编码器压缩服务器上的音乐,将压缩的音频流到客户端,然后使用解码器在客户端解压音频。您需要一个更复杂的深度学习模型来处理这样的事情,但是它显示了这种技术的强大。

autoencoder是一种神经网络,它利用无监督学习来学习如何表示给定的数据集。

将TensorFlow自动编码器与音乐配合使用

自动编码器由编码和解码机制组成。编码器负责减少(或压缩)我们的输入数据。解码器负责获取缩减(或压缩)的数据并将其重建为输入的最接近的可能表示。

我深度学习模型使用的数据集是ÁngelFaraldo,你可以在这里得到它(https://zenodo.org/record/1101082#.W5v6BBRlBhE)。对于这种情况,我只需要audio.zip文件(大约2.1 GB)。

下载zip文件后,我继续将其解压缩到一个文件夹中。

由于音频文件是MP3格式,因此需要将它们转换为WAV文件,以便更容易使用。为此,我使用了以下Python代码片段:

from pydub import AudioSegment
from glob import iglob
 
DATA_FILES_MP3 = 'audio'
DATA_FILES_WAV = 'audio_wav'
 
def convert_mp3_to_wav():
 index = 0
 for file in iglob(DATA_FILES_MP3 + '/*.mp3'):
 mp3_to_wav = AudioSegment.from_mp3(file)
 mp3_to_wav.export(DATA_FILES_WAV + '/' + 
 str(index) + '.wav', format='wav')
 index += 1

我并不担心维护音频文件的名称。除非您想要使用特定歌曲,否则在此处命名任何特定的WAV文件并不是非常重要。

一旦我将所有MP3文件转换为WAV文件,我就必须弄清楚如何加载WAV数据。将数据放入神经网络可以使用的格式是最困难的部分之一。

为了帮助我处理WAV文件,使用TensorFlow的audio_ops库。我选择使用audio_ops将WAV文件解码为一个样本数组,然后可以对其进行处理和批处理。

数据集中的WAV文件包含两个通道(左和右 - 立体声)。我发现将每个通道放在自己的数组中使数据更容易使用。

WAV文件包含一组样本,这些样本表示某个时刻的信号幅度。根据我的经验,这些数据对于网络来说非常难以学习。因此,我对每个通道进行了离散傅立叶变换,以便将音频信号分解为神经网络可以学习的组件。通过这样做,网络的训练大大改善。

完成所有操作后,我的Python批处理方法如下:

from scipy.fftpack import rfft, irfft
from tensorflow.contrib.framework.python.ops import audio_ops
 
'''
curr_batch - The current batch of the training data we are looking at.
 
songs_per_batch - How songs we want to load in per batch
 
sess - Our TensorFlow session object
'''
 
def get_next_batch(curr_batch, songs_per_batch, sess):
 wav_arr_ch1 = []
 wav_arr_ch2 = []
 if (curr_batch) >= (len(file_arr)):
 curr_batch = 0
 
 start_position = curr_batch * songs_per_batch
 end_position = start_position + songs_per_batch
 for idx in range(start_position, end_position):
 audio_binary = tf.read_file(file_arr[idx])
 wav_decoder = audio_ops.decode_wav(
 audio_binary, desired_channels=2)
 
 sample_rate, audio = sess.run(
 [wav_decoder.sample_rate, 
 wav_decoder.audio])
 audio = np.array(audio)
 
 # We want to ensure that every song we look at has the same
 # number of samples!
 if len(audio[:, 0]) != 5292000: 
 continue
 
 wav_arr_ch1.append(rfft(audio[:,0]))
 wav_arr_ch2.append(rfft(audio[:,1]))
 print("Returning File: " + file_arr[idx])
 
return wav_arr_ch1, wav_arr_ch2, sample_rate

每次提取新批次时,我都没有创建新的TensorFlow会话,而是发现将会话对象传递给方法更容易。

通过处理和准备数据,让我们进入网络架构以及如何提供数据。

在这种情况下,我使用深度自动编码器来处理信号数据。这意味着自动编码器由具有不同数量节点的多个层组成。确定每层的正确节点数可能是一项繁琐的工作。我发现数据中有一个最佳点,太多的神经元会导致明显的噪音而太少会阻止网络学习。我发现最小的层应该是输入大小的四分之一左右。

旁注:重要的是要知道网络的大小将受到你需要使用多少内存的限制(在我的情况下,16GB)。

经过几天调整我的神经网络后,我最终得到了以下超参数:

inputs = 12348
hidden_1_size = 8400
hidden_2_size = 3440
hidden_3_size = 2800
 
batch_size = 50
 
lr = 0.0001
l2 = 0.0001

我使用ReLU作为激活函数,使用Adam Optimizer作为我的优化器。

我获取了每批音频文件,并reshape数据以适应网络。我通过获取一首歌曲的两个声道并将它们分成大小为12348(输入层的大小)的子数组来完成此操作。然后我把第二个通道数组放在第一个通道后面。创建的数组如下:[ch1_samples_1,ch1_samples_2,ch1_samples _ .. N,ch2_samples_1,ch2_samples_2,ch2_samples_ ... N]。这是有效的,因为当定义占位符时,它的形状为[?,12348],我们的数据shape为[num_sample_arrs,12348]。

总的来说,Python处理看起来像这样:

def next_batch(c_batch, batch_size, sess):
 ch1_arr = []
 ch2_arr = []
 wav_arr_ch1, wav_arr_ch2, sample_rate =
 process_data.get_next_batch(c_batch, batch_size, sess)
 
 for sub_arr in wav_arr_ch1:
 batch_size_ch1 = math.floor(len(sub_arr)/inputs)
 sub_arr = sub_arr[:(batch_size_ch1*inputs)]
 ch1_arr.append(np.array(sub_arr).reshape(
 batch_size_ch1, inputs))
 
 for sub_arr in wav_arr_ch2:
 batch_size_ch2 = math.floor(len(sub_arr)/inputs)
 sub_arr = sub_arr[:(batch_size_ch2*inputs)]
 ch2_arr.append(np.array(sub_arr).reshape(
 batch_size_ch2, inputs))
 
 # Carry through sample_rate for reconstructing the WAV file
 return np.array(ch1_arr), np.array(ch2_arr), sample_rate

利用shaped arrays,数据可以准备好输入网络。我这样做是通过定义我正在使用的批次数量,以及我想要的epochs数。由于我没有运行强大的装备,我只在10,000个epochs内训练了50首歌曲。然后我会每1000个epochs运行一首测试歌曲并绘制结果图。我的测试歌曲是网络从未见过的(它不是训练集的一部分)。

提供网络数据的逻辑如下:

epochs = 10000
batches = 50
 
with tf.Session() as sess:
 init.run()
 ch1_song, ch2_song, sample_rate = next_batch(0, batch_size, sess)
 for epoch in range(epochs):
 epoch_loss = []
 print("Epoch: " + str(epoch))
 for i in range(batches):
 total_songs = np.hstack([ch1_song, ch2_song])
 batch_loss = []
 
 for j in range(len(total_songs)):
 x_batch = total_songs[j]
 
 _, l = sess.run([training_op, loss], 
 feed_dict = {X:x_batch})
 
 batch_loss.append(l)
 print("Song loss: " + str(l))
 print("Curr Epoch: " + str(epoch) + 
 " Curr Batch: " + str(i) + "/"+ str(batches))
 
 print("Batch Loss: " + str(np.mean(batch_loss)))
 epoch_loss.append(np.mean(batch_loss))
 
 print("Epoch Avg Loss: " + str(np.mean(epoch_loss)))
 
 ... Then feed in test songs to be graphed and converted back to WAV files ...

让自动编码器训练四天后,我停止了训练。

有10,000个epochs,我每1000个epochs的平均批量损失如下:

Epoch: 1000 Avg Loss: 45610.113

Epoch: 2000 Avg Loss: 16638.918

Epoch: 3000 Avg Loss: 14414.36

Epoch: 4000 Avg Loss: 12988.543

Epoch: 5000 Avg Loss: 12236.773

Epoch: 6000 Avg Loss: 11735.502

Epoch: 7000 Avg Loss: 11429.168

Epoch: 8000 Avg Loss: 11221.186

Epoch: 9000 Avg Loss: 11068.95

Epoch: 10000 Avg Loss: 10956.46

还不错!如果我让它训练的时间更长,结果会好得多。

让我们来看看原始波形和频谱图与重建的歌曲。

下面是歌曲在训练中的波形的GIF动画。蓝色是原始波形,橙色是重建波形。

将TensorFlow自动编码器与音乐配合使用

每1000个epochs的数据波形

我们可以肯定地看到神经网络能够很好地重建原始波形。这里仍有一些损失,但可以通过更多训练来纠正。

我们来看一下音频信号的频谱图。下面是网络训练过程中频谱图的另一个动画GIF。在它下面是原始未触动的音频的频谱图。

将TensorFlow自动编码器与音乐配合使用

每1000个测试歌曲epochs的频谱图

将TensorFlow自动编码器与音乐配合使用

测试歌曲的原始频谱图

我们可以看到神经网络肯定能够理解和映射音频的许多特征。

相关推荐