🏷️sec_nadaraya-watson
Artık :numref:fig_qkv
çerçevesinde dikkat mekanizmalarının ana bileşenlerini biliyorsunuz. Yeniden özetlemek için sorgular (istemli işaretler) ve anahtarlar (istemsiz işaretler) arasındaki etkileşimler dikkat ortaklama ile sonuçlanır. Dikkat ortaklama, çıktıyı üretmek için seçici olarak değerleri (duyusal girdiler) bir araya getirir. Bu bölümde, dikkat mekanizmalarının pratikte nasıl çalıştığına dair üst düzey bir görünüm vermek için dikkat ortaklamasını daha ayrıntılı olarak anlatacağız. Özellikle, 1964 yılında önerilen Nadaraya-Watson çekirdek bağlanım modeli, makine öğrenmesini dikkat mekanizmaları ile göstermek için basit ama eksiksiz bir örnektir.
from d2l import mxnet as d2l
from mxnet import autograd, gluon, np, npx
from mxnet.gluon import nn
npx.set_np()
#@tab pytorch
from d2l import torch as d2l
import torch
from torch import nn
#@tab tensorflow
from d2l import tensorflow as d2l
import tensorflow as tf
tf.random.set_seed(seed=1322)
İşleri basit tutmak için, aşağıdaki regresyon problemini ele alalım:
Burada
burada
n_train = 50 # Eğitim örneklerinin adedi
x_train = np.sort(d2l.rand(n_train) * 5) # Eğitim girdileri
#@tab pytorch
n_train = 50 # Eğitim örneklerinin adedi
x_train, _ = torch.sort(d2l.rand(n_train) * 5) # Eğitim girdileri
#@tab tensorflow
n_train = 50
x_train = tf.sort(tf.random.uniform(shape=(n_train,), maxval=5))
def f(x):
return 2 * d2l.sin(x) + x**0.8
y_train = f(x_train) + d2l.normal(0.0, 0.5, (n_train,)) # Eğitim çıktıları
x_test = d2l.arange(0, 5, 0.1) # Test örnekleri
y_truth = f(x_test) # Test örneklerinin adedi gerçek referans değeri
n_test = len(x_test) # Test örneklerinin adedi
n_test
#@tab pytorch
def f(x):
return 2 * d2l.sin(x) + x**0.8
y_train = f(x_train) + d2l.normal(0.0, 0.5, (n_train,)) # Eğitim çıktıları
x_test = d2l.arange(0, 5, 0.1) # Test örnekleri
y_truth = f(x_test) # Test örneklerinin adedi gerçek referans değeri
n_test = len(x_test) # Test örneklerinin adedi
n_test
#@tab tensorflow
def f(x):
return 2 * d2l.sin(x) + x**0.8
y_train = f(x_train) + d2l.normal((n_train,), 0.0, 0.5) # Eğitim çıktıları
x_test = d2l.arange(0, 5, 0.1) # Test örnekleri
y_truth = f(x_test) # Test örneklerinin adedi gerçek referans değeri
n_test = len(x_test) # Test örneklerinin adedi
n_test
Aşağıdaki işlev, tüm eğitim örneklerini (dairelerle temsil edilmiş), gürültü terimi olmadan gerçek referans değer veri üretme işlevi 'f'yi ("Truth" ile etiketlenmiş) ve öğrenilen tahmin işlevini ("Pred" ile etiketlenmiş) çizer.
#@tab all
def plot_kernel_reg(y_hat):
d2l.plot(x_test, [y_truth, y_hat], 'x', 'y', legend=['Truth', 'Pred'],
xlim=[0, 5], ylim=[-1, 5])
d2l.plt.plot(x_train, y_train, 'o', alpha=0.5);
Bu regresyon problemi için belki de dünyanın “en aptalca” tahmin edicisiyle başlıyoruz: Ortalama ortaklama kullanarak tüm eğitim çıktıları üzerinde ortalama,
eq_avg-pooling
aşağıda çizilmiştir. Gördüğümüz gibi, bu tahminci gerçekten o kadar akıllı değil.
y_hat = y_train.mean().repeat(n_test)
plot_kernel_reg(y_hat)
#@tab pytorch
y_hat = torch.repeat_interleave(y_train.mean(), n_test)
plot_kernel_reg(y_hat)
#@tab tensorflow
y_hat = tf.repeat(tf.reduce_mean(y_train), repeats=n_test)
plot_kernel_reg(y_hat)
Açıkçası, ortalama ortaklama girdileri, Nadaraya.1964
ve Watson :cite:Watson.1964
tarafından daha iyi bir fikir önerildi:
eq_nadaraya-watson
burada eq_nadaraya-watson
içindeki tahmin ediciye Nadaraya-Watson çekirdek regresyonu denir. Burada çekirdeklerin ayrıntılarına dalmayacağız. :numref:fig_qkv
içindeki dikkat mekanizmalarının çerçevesini hatırlayın. Dikkat açısından bakıldığında, :eqref:eq_nadaraya-watson
denklemini dikkat ortaklamanın daha genelleştirilmiş bir formunda yeniden yazabiliriz:
eq_attn-pooling
burada eq_attn-pooling
ve :eqref:eq_avg-pooling
karşılaştırılırsa, buradaki dikkat havuzlama eq_attn-pooling
içindeki dikkat ağırlığı
Dikkat ortaklama sezgileri kazanmak için, sadece aşağıda tanımlanan bir Gauss çekirdeğini düşünün
Gauss çekirdeğini :eqref:eq_attn-pooling
ve :eqref:eq_nadaraya-watson
denklemlerine koyarsak
$$\begin{aligned} f(x) &=\sum_{i=1}^n \alpha(x, x_i) y_i\ &= \sum_{i=1}^n \frac{\exp\left(-\frac{1}{2}(x - x_i)^2\right)}{\sum_{j=1}^n \exp\left(-\frac{1}{2}(x - x_j)^2\right)} y_i \&= \sum_{i=1}^n \mathrm{softmax}\left(-\frac{1}{2}(x - x_i)^2\right) y_i. \end{aligned}$$
:eqlabel:eq_nadaraya-watson-gaussian
:eqref:eq_nadaraya-watson-gaussian
içinde, verilen
Nadaraya-Watson çekirdek regresyonu parametrik olmayan bir modeldir; bu nedenle :eqref:eq_nadaraya-watson-gaussian
, parametrik olmayan dikkat ortaklama örneğidir. Aşağıda, bu parametrik olmayan dikkat modeline dayanarak tahmini çiziyoruz. Tahmin edilen çizgi düzgün ve ortalama ortaklama tarafından üretilen gerçek referans değere daha yakındır.
# `X_repeat` şekli: (`n_test`, `n_train`), burada her satır aynı test
# girdilerini içerir (yani aynı sorgular)
X_repeat = d2l.reshape(x_test.repeat(n_train), (-1, n_train))
# `x_train`'in anahtarları içerdiğine dikkat edin. `attention_weights` şekli:
# (`n_test`, `n_train`)'dir, burada her satır, her sorguya verilen değerler
# (`y_train`) arasında atanacak dikkat ağırlıklarını içerir
attention_weights = npx.softmax(-(X_repeat - x_train)**2 / 2)
# Each element of `y_hat` is weighted average of values, where weights are
# attention weights
# `y_hat`'nin her bir öğesi, ağırlıkların dikkat ağırlıkları olduğu
# değerlerin ağırlıklı ortalamasıdır.
y_hat = d2l.matmul(attention_weights, y_train)
plot_kernel_reg(y_hat)
#@tab pytorch
# `X_repeat` şekli: (`n_test`, `n_train`), burada her satır aynı test
# girdilerini içerir (yani aynı sorgular)
X_repeat = d2l.reshape(x_test.repeat_interleave(n_train), (-1, n_train))
# `x_train`'in anahtarları içerdiğine dikkat edin. `attention_weights` şekli:
# (`n_test`, `n_train`)'dir, burada her satır, her sorguya verilen değerler
# (`y_train`) arasında atanacak dikkat ağırlıklarını içerir
attention_weights = nn.functional.softmax(-(X_repeat - x_train)**2 / 2, dim=1)
# `y_hat`'nin her bir öğesi, ağırlıkların dikkat ağırlıkları olduğu
# değerlerin ağırlıklı ortalamasıdır.
y_hat = d2l.matmul(attention_weights, y_train)
plot_kernel_reg(y_hat)
#@tab tensorflow
# `X_repeat` şekli: (`n_test`, `n_train`), burada her satır aynı test
# girdilerini içerir (yani aynı sorgular)
X_repeat = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_train, axis=0)
# `x_train`'in anahtarları içerdiğine dikkat edin. `attention_weights` şekli:
# (`n_test`, `n_train`)'dir, burada her satır, her sorguya verilen değerler
# (`y_train`) arasında atanacak dikkat ağırlıklarını içerir
attention_weights = tf.nn.softmax(-(X_repeat - tf.expand_dims(x_train, axis=1))**2/2, axis=1)
# `y_hat`'nin her bir öğesi, ağırlıkların dikkat ağırlıkları olduğu
# değerlerin ağırlıklı ortalamasıdır.
y_hat = tf.matmul(attention_weights, tf.expand_dims(y_train, axis=1))
plot_kernel_reg(y_hat)
Şimdi [dikkat ağırlıkları]na bir göz atalım. Burada test girdileri sorgulardır, eğitim girdileri ise anahtarlardır. Her iki girdi sıralandığından, sorgu-anahtar çifti ne kadar yakın olursa, dikkat ortaklamasında dikkat ağırlığı o kadar yüksek olur.
d2l.show_heatmaps(np.expand_dims(np.expand_dims(attention_weights, 0), 0),
xlabel='Sorted training inputs',
ylabel='Sorted testing inputs')
#@tab pytorch
d2l.show_heatmaps(attention_weights.unsqueeze(0).unsqueeze(0),
xlabel='Sorted training inputs',
ylabel='Sorted testing inputs')
#@tab tensorflow
d2l.show_heatmaps(tf.expand_dims(tf.expand_dims(attention_weights, axis=0), axis=0),
xlabel='Sorted training inputs',
ylabel='Sorted testing inputs')
Parametrik olmayan Nadaraya-Watson çekirdek regresyonu tutarlılık avantajından yararlanır: Yeterli veri verildiğinde bu model en uygun çözüme yakınlaşır. Bununla birlikte, öğrenilebilir parametreleri dikkat ortaklamasına kolayca tümleştirebiliriz.
Örnek olarak, :eqref:eq_nadaraya-watson-gaussian
denkleminden biraz farklı olarak, aşağıdaki gibi
$$\begin{aligned}f(x) &= \sum_{i=1}^n \alpha(x, x_i) y_i \&= \sum_{i=1}^n \frac{\exp\left(-\frac{1}{2}((x - x_i)w)^2\right)}{\sum_{j=1}^n \exp\left(-\frac{1}{2}((x - x_j)w)^2\right)} y_i \&= \sum_{i=1}^n \mathrm{softmax}\left(-\frac{1}{2}((x - x_i)w)^2\right) y_i.\end{aligned}$$
:eqlabel:eq_nadaraya-watson-gaussian-para
Bölümün geri kalanında, :eqref:eq_nadaraya-watson-gaussian-para
denklemindeki dikkat ortaklama parametresini öğrenerek bu modeli eğiteceğiz.
🏷️subsec_batch_dot
Minigruplar için dikkati daha verimli bir şekilde hesaplamak için, derin öğrenme çerçeveleri tarafından sağlanan toplu matris çarpma yardımcı programlarından yararlanabiliriz.
İlk minigrup
İlk minigrubun
X = d2l.ones((2, 1, 4))
Y = d2l.ones((2, 4, 6))
npx.batch_dot(X, Y).shape
#@tab pytorch
X = d2l.ones((2, 1, 4))
Y = d2l.ones((2, 4, 6))
torch.bmm(X, Y).shape
#@tab tensorflow
X = tf.ones((2, 1, 4))
Y = tf.ones((2, 4, 6))
tf.matmul(X, Y).shape
Dikkat mekanizmaları bağlamında, [bir minigruptaki değerlerin ağırlıklı ortalamalarını hesaplamak için minigrup matris çarpımını kullanabiliriz.]
weights = d2l.ones((2, 10)) * 0.1
values = d2l.reshape(d2l.arange(20), (2, 10))
npx.batch_dot(np.expand_dims(weights, 1), np.expand_dims(values, -1))
#@tab pytorch
weights = d2l.ones((2, 10)) * 0.1
values = d2l.reshape(d2l.arange(20.0), (2, 10))
torch.bmm(weights.unsqueeze(1), values.unsqueeze(-1))
#@tab tensorflow
weights = tf.ones((2, 10)) * 0.1
values = tf.reshape(tf.range(20.0), shape = (2, 10))
tf.matmul(tf.expand_dims(weights, axis=1), tf.expand_dims(values, axis=-1)).numpy()
Minigrup matris çarpımını kullanarak, aşağıda :eqref:eq_nadaraya-watson-gaussian-para
denklemindeki [parametrik dikkat ortaklama]yı temel alan Nadaraya-Watson çekirdek regresyonunun parametrik versiyonunu tanımlıyoruz.
class NWKernelRegression(nn.Block):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.w = self.params.get('w', shape=(1,))
def forward(self, queries, keys, values):
# `queries` ve `attention_weights` çıktısının şekli:
# (sorgu sayısı, anahtar/değer çifti sayısı)
queries = d2l.reshape(
queries.repeat(keys.shape[1]), (-1, keys.shape[1]))
self.attention_weights = npx.softmax(
-((queries - keys) * self.w.data())**2 / 2)
# `values` (değerler) şekli: (sorgu sayısı, anahtar/değer çifti sayısı)
return npx.batch_dot(np.expand_dims(self.attention_weights, 1),
np.expand_dims(values, -1)).reshape(-1)
#@tab pytorch
class NWKernelRegression(nn.Module):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.w = nn.Parameter(torch.rand((1,), requires_grad=True))
def forward(self, queries, keys, values):
# `queries` ve `attention_weights` çıktısının şekli:
# (sorgu sayısı, anahtar/değer çifti sayısı)
queries = d2l.reshape(
queries.repeat_interleave(keys.shape[1]), (-1, keys.shape[1]))
self.attention_weights = nn.functional.softmax(
-((queries - keys) * self.w)**2 / 2, dim=1)
# `values` (değerler) şekli: (sorgu sayısı, anahtar/değer çifti sayısı)
return torch.bmm(self.attention_weights.unsqueeze(1),
values.unsqueeze(-1)).reshape(-1)
#@tab tensorflow
class NWKernelRegression(tf.keras.layers.Layer):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.w = tf.Variable(initial_value=tf.random.uniform(shape=(1,)))
def call(self, queries, keys, values, **kwargs):
# For training queries are `x_train`. Keys are distance of taining data for each point. Values are `y_train`.
# Shape of the output `queries` and `attention_weights`: (no. of queries, no. of key-value pairs)
queries = tf.repeat(tf.expand_dims(queries, axis=1), repeats=keys.shape[1], axis=1)
self.attention_weights = tf.nn.softmax(-((queries - keys) * self.w)**2 /2, axis =1)
# Shape of `values`: (no. of queries, no. of key-value pairs)
return tf.squeeze(tf.matmul(tf.expand_dims(self.attention_weights, axis=1), tf.expand_dims(values, axis=-1)))
Aşağıda, dikkat modelini eğitmek için eğitim veri kümesini anahtar ve değerlere dönüştürürüz. Parametrik dikkat ortaklamada, herhangi bir eğitim girdisi çıktısını tahmin etmek için kendisi dışındaki tüm eğitim örneklerinden anahtar-değer çiftlerini alır.
# `X_tile`'in şekli: (`n_train`, `n_train`), burada her sütun aynı
# eğitim girdilerini içerir
X_tile = np.tile(x_train, (n_train, 1))
# `Y_tile`'in şekli: (`n_train`, `n_train`), burada her sütun aynı
# eğitim çıktılarını içerir
Y_tile = np.tile(y_train, (n_train, 1))
# `keys`'in şekli: (`n_train`, `n_train` - 1)
keys = d2l.reshape(X_tile[(1 - d2l.eye(n_train)).astype('bool')],
(n_train, -1))
# `values`'in şekli: (`n_train`, `n_train` - 1)
values = d2l.reshape(Y_tile[(1 - d2l.eye(n_train)).astype('bool')],
(n_train, -1))
#@tab pytorch
# `X_tile`'in şekli: (`n_train`, `n_train`), burada her sütun aynı
# eğitim girdilerini içerir
X_tile = x_train.repeat((n_train, 1))
# `Y_tile`'in şekli: (`n_train`, `n_train`), burada her sütun aynı
# eğitim çıktılarını içerir
Y_tile = y_train.repeat((n_train, 1))
# `keys`'in şekli: (`n_train`, `n_train` - 1)
keys = d2l.reshape(X_tile[(1 - d2l.eye(n_train)).type(torch.bool)],
(n_train, -1))
# `values`'in şekli: (`n_train`, `n_train` - 1)
values = d2l.reshape(Y_tile[(1 - d2l.eye(n_train)).type(torch.bool)],
(n_train, -1))
#@tab tensorflow
# `X_tile`'in şekli: (`n_train`, `n_train`), burada her sütun aynı
# eğitim girdilerini içerir
X_tile = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_train, axis=0)
# `Y_tile`'in şekli: (`n_train`, `n_train`), burada her sütun aynı
# eğitim çıktılarını içerir
Y_tile = tf.repeat(tf.expand_dims(y_train, axis=0), repeats=n_train, axis=0)
# `keys`'in şekli: (`n_train`, `n_train` - 1)
keys = tf.reshape(X_tile[tf.cast(1 - tf.eye(n_train), dtype=tf.bool)], shape=(n_train, -1))
# `values`'in şekli: (`n_train`, `n_train` - 1)
values = tf.reshape(Y_tile[tf.cast(1 - tf.eye(n_train), dtype=tf.bool)], shape=(n_train, -1))
Kare kayıp ve rasgele gradyan inişi kullanarak, [parametrik dikkat modelini eğitiriz].
net = NWKernelRegression()
net.initialize()
loss = gluon.loss.L2Loss()
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.5})
animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[1, 5])
for epoch in range(5):
with autograd.record():
l = loss(net(x_train, keys, values), y_train)
l.backward()
trainer.step(1)
print(f'epoch {epoch + 1}, loss {float(l.sum()):.6f}')
animator.add(epoch + 1, float(l.sum()))
#@tab pytorch
net = NWKernelRegression()
loss = nn.MSELoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=0.5)
animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[1, 5])
for epoch in range(5):
trainer.zero_grad()
l = loss(net(x_train, keys, values), y_train)
l.sum().backward()
trainer.step()
print(f'epoch {epoch + 1}, loss {float(l.sum()):.6f}')
animator.add(epoch + 1, float(l.sum()))
#@tab tensorflow
net = NWKernelRegression()
loss_object = tf.keras.losses.MeanSquaredError()
optimizer = tf.keras.optimizers.SGD(learning_rate=0.5)
animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[1, 5])
for epoch in range(5):
with tf.GradientTape() as t:
loss = loss_object(y_train, net(x_train, keys, values)) * len(y_train)
grads = t.gradient(loss, net.trainable_variables)
optimizer.apply_gradients(zip(grads, net.trainable_variables))
print(f'epoch {epoch + 1}, loss {float(loss):.6f}')
animator.add(epoch + 1, float(loss))
Parametrik dikkat modelini eğittikten sonra, [tahminini] çizebiliriz. Eğitim veri kümesini gürültüye oturtmaya çalışırken, tahmin edilen çizgi, daha önce çizilen parametrik olmayan karşılığından daha az pürüzsüzdür.
# `keys`'in şekli: (`n_test`, `n_train`), burada her sütun aynı eğitim
# girdilerini (yani aynı anahtarları) içerir
keys = np.tile(x_train, (n_test, 1))
# `value`'in şekli: (`n_test`, `n_train`)
values = np.tile(y_train, (n_test, 1))
y_hat = net(x_test, keys, values)
plot_kernel_reg(y_hat)
#@tab pytorch
# `keys`'in şekli: (`n_test`, `n_train`), burada her sütun aynı eğitim
# girdilerini (yani aynı anahtarları) içerir
keys = x_train.repeat((n_test, 1))
# `value`'in şekli: (`n_test`, `n_train`)
values = y_train.repeat((n_test, 1))
y_hat = net(x_test, keys, values).unsqueeze(1).detach()
plot_kernel_reg(y_hat)
#@tab tensorflow
# `keys`'in şekli: (`n_test`, `n_train`), burada her sütun aynı eğitim
# girdilerini (yani aynı anahtarları) içerir
keys = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_test, axis=0)
# `value`'in şekli: (`n_test`, `n_train`)
values = tf.repeat(tf.expand_dims(y_train, axis=0), repeats=n_test, axis=0)
y_hat = net(x_test, keys, values)
plot_kernel_reg(y_hat)
Parametrik olmayan dikkat ortaklama ile karşılaştırıldığında, öğrenilebilir ve parametrik ayarda [büyük dikkat ağırlıkları olan bölge daha keskinleşir].
d2l.show_heatmaps(np.expand_dims(np.expand_dims(net.attention_weights, 0), 0),
xlabel='Sorted training inputs',
ylabel='Sorted testing inputs')
#@tab pytorch
d2l.show_heatmaps(net.attention_weights.unsqueeze(0).unsqueeze(0),
xlabel='Sorted training inputs',
ylabel='Sorted testing inputs')
#@tab tensorflow
d2l.show_heatmaps(tf.expand_dims(tf.expand_dims(net.attention_weights, axis=0), axis=0),
xlabel='Sorted training inputs',
ylabel='Sorted testing inputs')
- Nadaraya-Watson çekirdek regresyonu, makine öğrenmesinin dikkat mekanizmaları ile bir örneğidir.
- Nadaraya-Watson çekirdek regresyonunun dikkat ortaklaması, eğitim çıktılarının ağırlıklı bir ortalamasıdır. Dikkat açısından bakıldığında, dikkat ağırlığı, bir sorgunun işlevine ve değerle eşleştirilmiş anahtara dayanan bir değere atanır.
- Dikkat ortaklama parametrik olabilir de olmayabilir de.
- Eğitim örneklerinin sayısını artırın. Parametrik olmayan Nadaraya-Watson çekirdek regresyonunu daha iyi öğrenebilir misin?
- Parametrik dikkat ortaklama deneyinde öğrendiğimiz
$w$ 'nin değeri nedir? Dikkat ağırlıklarını görselleştirirken ağırlıklı bölgeyi neden daha keskin hale getiriyor? - Daha iyi tahmin etmek için parametrik olmayan Nadaraya-Watson çekirdek regresyonuna nasıl hiper parametre ekleyebiliriz?
- Bu bölümün çekirdek regresyonu için başka bir parametrik dikkat ortaklama tasarlayın. Bu yeni modeli eğitin ve dikkat ağırlıklarını görselleştirin.
:begin_tab:mxnet
Tartışmalar
:end_tab:
:begin_tab:pytorch
Tartışmalar
:end_tab: