Bir kokteyl partisindesiniz ve aynı anda birden çok kişi konuşuyor. Yani birden çok kaynaktan ses geliyor. $N$ kişi konuşuyor olsun. Siz de odanın değişik yerlerine $N$ tane mikrofon koyup kayıt almış olun. Bu kayıtların hepsi muhakkak ki kaynaklardan çıkan seslerin doğrusal bileşimlerinden oluşuyor olacaklar. Kaynakları görmüyorsunuz fakat bileşimlerini görüyorsunuz, ya da duyuyorsunuz. Kör kaynak problemi denilen bu problem için bir yöntemimiz var!

Bu yazıda bağımsız bileşenler analizi (ICA - independent component analysis) ile bu kaynakları nasıl elde edeceğimize bakalım.

Öncelikle basit sinyaller ile işe başlayalım:

 # -*- coding: utf-8 -*-
import numpy as np
import pylab as plt
from sklearn.decomposition import FastICA

# Örnek veriyi üretelim
np.random.seed(0)
nSamples = 1000
time = np.linspace(0, 10, nSamples)
s1 = np.sin(2 * time)  # Sinyal 1 : sinüsoid
s2 = np.sign(np.sin(3 * time))  # Sinyal 2 : kare dalga
S = np.c_[s1, s2]
S += 0.1 * np.random.normal(size=S.shape)  # Gürültü ekle
S /= S.std(axis=0)  # Düzgele

Şimdi sinyalleri karıştıralım ve sonuçları ICA ile ayrıştıralım:

A = np.array([[1, 1], [0.8, 2]])  # Karışım matrisi
X = np.dot(S, A.T)  # Gözlemleri oluştur
# ICA hesapla
ica = FastICA()
S_ = ica.fit(X).transform(X)  # Kaynakları tahmin et
A_ = ica.get_mixing_matrix()  # Karışım matrisini tahmin et
assert np.allclose(X, np.dot(S_, A_.T))

Ve sonuçlara bakalım:

plt.figure()

ax1 = plt.subplot(3, 1, 1)
plt.plot(S)
plt.title(u'Gerçek Kaynaklar')
plt.setp(ax1.get_xticklabels(), visible=False)
ax2 = plt.subplot(3, 1, 2, sharex=ax1)
plt.plot(X)
plt.title(u'Gözlemler (karışım sinyali)')
plt.setp(ax2.get_xticklabels(), visible=False)
plt.subplot(3, 1, 3, sharex=ax1)
plt.plot(S_)
plt.title(u'ICA tarafından bulunan kaynaklar')
plt.savefig('ica.png')

ica

Bulunan sinyaller doğru. Fakat sıraları ve ölçekleri bilinemiyor. Yeşil renkle çizilen kare dalganın ters yönde çıkması da bu ölçek katsayısı ile ilgili. Negatif bir sayıyla çarparsanız özgün halinde olduğu gibi ters çıkacaktır.

kemal_sunalŞimdi daha güzel bir örnek üstünde bakalım. Büyük usta Kemal Sunal'ın iki ses kaydını alalım:

Bunları önceden aynı boyuta getirmiştim.

Sonra onları karıştıralım ve yeni sinyaller elde edelim:

İki sinyalde de iki ses karışmış. İşimiz zor gibi. Haydi, ICA ile gözlemler üstünden kaynakları tahmin edelim:

Bunlar için alttaki kodu kullanabilirsiniz:

import numpy as np
from sklearn.decomposition import FastICA
from scipy.io.wavfile import read, write

Fs, s1 = read('kibarfeyzo.wav')
Fs, s2 = read('gunah.wav')

nSamples = len(s1)
assert len(s1) == len(s2)

s1 = np.float32(s1)
s2 = np.float32(s2)
S = np.c_[s1, s2]

A = np.array([[1, 0.8], [1, 2]])  # Karışım matrisi
X = np.dot(S, A)  # Gözlemleri üret

ica = FastICA()
S_ = ica.fit(X).transform(X)  # Kaynakları tahmin et

x1, x2 = X[:,0], X[:,1]
x1 = x1 / np.max(np.abs(x1)) * 255
x2 = x2 / np.max(np.abs(x2)) * 255
write('x1.wav', Fs, np.uint8(x1))
write('x2.wav', Fs, np.uint8(x2))

s1, s2 = S_[:,0], S_[:,1]
s1 = s1 / np.max(np.abs(s1)) * 255
s2 = s2 / np.max(np.abs(s2)) * 255
write('s1.wav', Fs, np.uint8(s1))
write('s2.wav', Fs, np.uint8(s2))