独立成分分析 Lab

在此 notebook 中,我们将使用独立成分分析方法从三个观察结果中提取信号,每个观察结果都包含不同的原始混音信号。这个问题与 ICA 视频中解释的问题一样。

数据集

首先看看手头的数据集。我们有三个 WAVE 文件,正如我们之前提到的,每个文件都是混音形式。如果你之前没有在 python 中处理过音频文件,没关系,它们实际上就是浮点数列表。

首先加载第一个音频文件 ICA_mix_1.wav [点击即可聆听该文件]:


In [ ]:
import numpy as np
import wave

# Read the wave file
mix_1_wave = wave.open('ICA_mix_1.wav','r')

我们看看该 wave 文件的参数,详细了解该文件


In [ ]:
mix_1_wave.getparams()

该文件只有一个声道(因此是单声道)。帧率是 44100,表示每秒声音由 44100 个整数组成(因为文件是常见的 PCM 16 位格式,所以是整数)。该文件总共有 264515 个整数/帧,因此时长为:


In [ ]:
264515/44100

我们从该 wave 文件中提取帧,这些帧将属于我们将运行 ICA 的数据集:


In [ ]:
# Extract Raw Audio from Wav File
signal_1_raw = mix_1_wave.readframes(-1)
signal_1 = np.fromstring(signal_1_raw, 'Int16')

signal_1 现在是一个整数列表,表示第一个文件中包含的声音。


In [ ]:
'length: ', len(signal_1) , 'first 100 elements: ',signal_1[:100]

如果将此数组绘制成线形图,我们将获得熟悉的波形:


In [ ]:
import matplotlib.pyplot as plt

fs = mix_1_wave.getframerate()
timing = np.linspace(0, len(signal_1)/fs, num=len(signal_1))


plt.figure(figsize=(12,2))
plt.title('Recording 1')
plt.plot(timing,signal_1, c="#3ABFE7")
plt.ylim(-35000, 35000)
plt.show()

现在我们可以按照相同的方式加载另外两个 wave 文件 ICA_mix_2.wavICA_mix_3.wav


In [ ]:
mix_2_wave = wave.open('ICA_mix_2.wav','r')

#Extract Raw Audio from Wav File
signal_raw_2 = mix_2_wave.readframes(-1)
signal_2 = np.fromstring(signal_raw_2, 'Int16')


mix_3_wave = wave.open('ICA_mix_3.wav','r')

#Extract Raw Audio from Wav File
signal_raw_3 = mix_3_wave.readframes(-1)
signal_3 = np.fromstring(signal_raw_3, 'Int16')


plt.figure(figsize=(12,2))
plt.title('Recording 2')
plt.plot(timing,signal_2, c="#3ABFE7")
plt.ylim(-35000, 35000)
plt.show()


plt.figure(figsize=(12,2))
plt.title('Recording 3')
plt.plot(timing,signal_3, c="#3ABFE7")
plt.ylim(-35000, 35000)
plt.show()

读取所有三个文件后,可以通过 zip 运算创建数据集。

  • 通过将 signal_1、signal_2 和 signal_3 组合成一个列表创建数据集 X

In [ ]:
X = list(zip(signal_1, signal_2, signal_3))

# Let's peak at what X looks like
X[:10]

现在准备运行 ICA 以尝试获取原始信号。

  • 导入 sklearn 的 FastICA 模块
  • 初始化 FastICA,查看三个成分
  • 使用 fit_transform 对数据集 X 运行 FastICA 算法

In [ ]:
# TODO: Import FastICA

# TODO: Initialize FastICA with n_components=3

# TODO: Run the FastICA algorithm using fit_transform on dataset X

In [ ]:
ica_result.shape

我们将其拆分为单独的信号并查看这些信号


In [ ]:
result_signal_1 = ica_result[:,0]
result_signal_2 = ica_result[:,1]
result_signal_3 = ica_result[:,2]

我们对信号进行绘制,查看波浪线的形状


In [ ]:
# Plot Independent Component #1
plt.figure(figsize=(12,2))
plt.title('Independent Component #1')
plt.plot(result_signal_1, c="#df8efd")
plt.ylim(-0.010, 0.010)
plt.show()

# Plot Independent Component #2
plt.figure(figsize=(12,2))
plt.title('Independent Component #2')
plt.plot(result_signal_2, c="#87de72")
plt.ylim(-0.010, 0.010)
plt.show()

# Plot Independent Component #3
plt.figure(figsize=(12,2))
plt.title('Independent Component #3')
plt.plot(result_signal_3, c="#f65e97")
plt.ylim(-0.010, 0.010)
plt.show()

某些波浪线看起来像音乐波形吗?

确认结果的最佳方式是聆听生成的文件。另存为 wave 文件并进行验证。在此之前,我们需要:

  • 将它们转换为整数(以便另存为 PCM 16 位 Wave 文件),否则只有某些媒体播放器能够播放它们
  • 将值映射到 int16 音频的相应范围内。该范围在 -32768 到 +32767 之间。基本的映射方法是乘以 32767。
  • 音量有点低,我们可以乘以某个值(例如 100)来提高音量

In [ ]:
from scipy.io import wavfile

# Convert to int, map the appropriate range, and increase the volume a little bit
result_signal_1_int = np.int16(result_signal_1*32767*100)
result_signal_2_int = np.int16(result_signal_2*32767*100)
result_signal_3_int = np.int16(result_signal_3*32767*100)


# Write wave files
wavfile.write("result_signal_1.wav", fs, result_signal_1_int)
wavfile.write("result_signal_2.wav", fs, result_signal_2_int)
wavfile.write("result_signal_3.wav", fs, result_signal_3_int)

现在生成的文件包括:[注意:确保先调低音响设备的音量,以防某些问题导致文件听起来像静态的]

音乐:

  • 钢琴 - The Carnival of the Animals - XIII. The Swan(钢琴曲独奏版)。演奏者:Markus Staab
  • 大提琴 - Cello Suite no. 3 in C, BWV 1009 - I. Prelude。演奏者:European Archive