Cara menghitung kernel Gaussian secara efektif di numpy [ditutup]

12

Saya memiliki array numpy dengan kolom m dan n baris, kolom menjadi dimensi dan baris datapoints.

Sekarang saya perlu menghitung nilai kernel untuk setiap kombinasi titik data.

Untuk kernel linear bisa saya lakukanK(xi,xj)=xi,xjdot(X,X.T)

Bagaimana saya bisa menghitung secara efektif semua nilai untuk Gaussian Kernel K(xi,xj)=expxixj22s2 dengan nilai s ?

Peter Smit
sumber
1
Nah jika Anda tidak terlalu peduli dengan faktor dua peningkatan perhitungan, Anda selalu dapat melakukan dan kemudian mana, tentu saja, adalah unsur th . Ini mungkin juga bukan yang paling stabil secara numerik. S=XXTK(xi,xj)=exp((Sii+Sjj2Sij)/s2)Sij(i,j)S
kardinal
2
(Bertahun-tahun kemudian) untuk array jarang, lihat sklearn.metrics.pairwise.pairwise_distances.html di scikit-learn.
denis

Jawaban:

26

Saya pikir masalah utama adalah untuk mendapatkan jarak berpasangan secara efisien. Setelah Anda memiliki sisanya adalah elemen bijaksana.

Untuk melakukan ini, Anda mungkin ingin menggunakan Scipy. Fungsi scipy.spatial.distance.pdistmelakukan apa yang Anda butuhkan, dan scipy.spatial.distance.squareformmungkin akan memudahkan hidup Anda.

Jadi jika Anda ingin matriks kernel yang Anda lakukan

from scipy.spatial.distance import pdist, squareform
  # this is an NxD matrix, where N is number of items and D its dimensionalites
X = loaddata() 
pairwise_dists = squareform(pdist(X, 'euclidean'))
K = scip.exp(-pairwise_dists ** 2 / s ** 2)

Dokumentasi dapat ditemukan di sini

bayerj
sumber
3
Tampaknya bagi saya bahwa jawaban bayerj memerlukan beberapa modifikasi kecil agar sesuai dengan formula, kalau-kalau ada orang lain yang membutuhkannya:K = scipy.exp(-pairwise_dists**2 / s**2)
chloe
Jika ada yang penasaran, algoritma yang digunakan pdistsangat sederhana: itu hanya loop C-diimplementasikan yang secara langsung menghitung jarak dengan cara yang jelas , looping dilakukan di sini ; tidak ada vektorisasi mewah atau apa pun di luar apa pun yang dapat dicapai kompilator secara otomatis.
Dougal
11

Sebagai tambahan kecil untuk jawaban bayerj, pdistfungsi scipy dapat secara langsung menghitung norma euclidean kuadrat dengan menyebutnya sebagai pdist(X, 'sqeuclidean'). Kode lengkap kemudian dapat ditulis lebih efisien sebagai

from scipy.spatial.distance import pdist, squareform
  # this is an NxD matrix, where N is number of items and D its dimensionalites
X = loaddata() 
pairwise_sq_dists = squareform(pdist(X, 'sqeuclidean'))
K = scip.exp(-pairwise_sq_dists / s**2)
tenedor
sumber
1
Atau hanya pairwise_sq_dists = cdist(X, X, 'sqeuclidean')yang memberikan hal yang sama.
user1721713
5

Anda juga dapat menulis formulir persegi dengan tangan:

import numpy as np
def vectorized_RBF_kernel(X, sigma):
    # % This is equivalent to computing the kernel on every pair of examples
    X2 = np.sum(np.multiply(X, X), 1) # sum colums of the matrix
    K0 = X2 + X2.T - 2 * X * X.T
    K = np.power(np.exp(-1.0 / sigma**2), K0)
    return K

PS tapi ini bekerja 30% lebih lambat

spetz911
sumber
Ini, yang merupakan metode yang disarankan oleh kardinal dalam komentar, dapat dipercepat sedikit dengan menggunakan operasi in-house. Ini bagaimana scikit-belajar melakukannya , dengan sebuah einsumpanggilan untuk Anda X2.
Dougal
4
def my_kernel(X,Y):
    K = np.zeros((X.shape[0],Y.shape[0]))
    for i,x in enumerate(X):
        for j,y in enumerate(Y):
            K[i,j] = np.exp(-1*np.linalg.norm(x-y)**2)
    return K

clf=SVR(kernel=my_kernel)

yang sama dengan

clf=SVR(kernel="rbf",gamma=1)

Anda dapat secara efektif menghitung RBF dari catatan kode di atas bahwa nilai gamma adalah 1, karena itu adalah konstanta yang Anda minta juga konstan yang sama.

John
sumber
Selamat datang di situs kami! Kami memiliki penekanan yang sedikit berbeda dengan Stack Overflow, di mana pada umumnya kami kurang fokus pada kode dan lebih banyak pada ide-ide yang mendasarinya, jadi mungkin ada baiknya memberi anotasi pada kode Anda atau memberikan ide singkat apa ide kunci itu, seperti beberapa jawaban lain telah dilakukan. Itu akan membantu menjelaskan bagaimana jawaban Anda berbeda dengan yang lain.
Silverfish
Ini akan jauh lebih lambat daripada jawaban lain karena menggunakan loop Python daripada vektorisasi.
Dougal
-1

Saya pikir ini akan membantu:

def GaussianKernel(v1, v2, sigma):
    return exp(-norm(v1-v2, 2)**2/(2.*sigma**2))
Inti
sumber
3
Selamat datang di situs @Kernel. Anda dapat menampilkan matematika dengan meletakkan ekspresi antara $ tanda dan menggunakan sintaks seperti LateX. Dan Anda dapat menampilkan kode (dengan penyorotan sintaks) dengan memberi indentasi baris dengan 4 spasi. Lihat bantuan pengeditan penurunan harga untuk panduan pemformatan, dan faq untuk pedoman yang lebih umum.
Antoine Vernet
1
Tidakkah ini hanya menggemakan apa yang ada dalam pertanyaan?
Whuber