15.3. Duygu Analizi: Evrişimli Sinir Ağlarının Kullanımı¶ Open the notebook in SageMaker Studio Lab
Section 6 içinde, bitişik pikseller gibi yerel özelliklere uygulanan iki boyutlu CNN’lerle iki boyutlu imge verilerini işlemek için mekanizmaları inceledik. Aslında bilgisayarla görme için tasarlanmış olsa da, CNN’ler doğal dil işleme için de yaygın olarak kullanılmaktadır. Basitçe söylemek gerekirse, herhangi bir metin dizisini tek boyutlu bir imge olarak düşünün. Bu şekilde, tek boyutlu CNN’ler metindeki \(n\) gramlar gibi yerel özellikleri işleyebilir.
Bu bölümde, tek metni temsil etmede bir CNN mimarisinin nasıl tasarlanacağını göstermek için textCNN modelini kullanacağız (Kim, 2014). Duygu analizi için GloVe ön eğitimi ile RNN mimarisi kullanan Fig. 15.2.1 ile karşılaştırıldığında, Fig. 15.3.1 mimarisindeki tek fark mimarinin seçiminde yatmaktadır.
Fig. 15.3.1 Bu bölüm, duygu analizi için önceden eğitilmiş GloVe’i CNN tabanlı bir mimariye besler.¶
from d2l import mxnet as d2l
from mxnet import gluon, init, np, npx
from mxnet.gluon import nn
npx.set_np()
batch_size = 64
train_iter, test_iter, vocab = d2l.load_data_imdb(batch_size)
import torch
from torch import nn
from d2l import torch as d2l
batch_size = 64
train_iter, test_iter, vocab = d2l.load_data_imdb(batch_size)
15.3.1. Tek Boyutlu Evrişimler¶
Modeli tanıtmadan önce, tek boyutlu bir evrişimin nasıl çalıştığını görelim. Bunun çapraz korelasyon işlemine dayanan iki boyutlu bir evrişimin sadece özel bir durumu olduğunu unutmayın.
Fig. 15.3.2 Tek boyutlu çapraz korelasyon işlemi. Gölgeli kısımlar, çıktı hesaplaması için kullanılan girdi ve çekirdek tensör elemanlarının yanı sıra ilk çıktı elemanıdır: \(0\times1+1\times2=2\).¶
Fig. 15.3.2 içinde gösterildiği gibi, tek boyutlu durumda, evrişim penceresi girdi tensör boyunca soldan sağa doğru kayar. Kayma sırasında, belirli bir konumdaki evrişim penceresinde bulunan girdi alt tensör (örn. Fig. 15.3.2 içindeki \(0\) ve \(1\)) ve çekirdek tensör (örneğin, Fig. 15.3.2 içindeki \(1\) ve \(2\)) eleman yönlü çarpılır. Bu çarpımların toplamı, çıktı tensörünün karşılık gelen pozisyonunda tek sayıl değeri (örneğin, Fig. 15.3.2 içindeki \(0\times1+1\times2=2\)) verir.
Aşağıdaki corr1d
işlevinde tek boyutlu çapraz korelasyon
uyguluyoruz. Bir girdi tensör X
ve bir çekirdek tensör K
göz
önüne alındığında, Y
çıktı tensörünü döndürür.
def corr1d(X, K):
w = K.shape[0]
Y = np.zeros((X.shape[0] - w + 1))
for i in range(Y.shape[0]):
Y[i] = (X[i: i + w] * K).sum()
return Y
def corr1d(X, K):
w = K.shape[0]
Y = torch.zeros((X.shape[0] - w + 1))
for i in range(Y.shape[0]):
Y[i] = (X[i: i + w] * K).sum()
return Y
Yukarıdaki tek boyutlu çapraz korelasyon uygulamasının çıktısını
doğrulamak için Fig. 15.3.2 içindeki girdi tensörü X
’i ve
çekirdek tensörü K
’yi oluşturabiliriz.
X, K = np.array([0, 1, 2, 3, 4, 5, 6]), np.array([1, 2])
corr1d(X, K)
array([ 2., 5., 8., 11., 14., 17.])
X, K = torch.tensor([0, 1, 2, 3, 4, 5, 6]), torch.tensor([1, 2])
corr1d(X, K)
tensor([ 2., 5., 8., 11., 14., 17.])
Birden çok kanallı tek boyutlu girdi için, evrişim çekirdeğinin aynı sayıda girdi kanalına sahip olması gerekir. Ardından, her kanal için, tek boyutlu çıktı tensörünü üretmek için tüm kanallar üzerindeki sonuçları toplayarak, girdinin tek boyutlu tensörü ve evrişim çekirdeğinin tek boyutlu tensörü üzerinde bir çapraz korelasyon işlemi gerçekleştirilir. Fig. 15.3.3 3 adet girdi kanalıyla tek boyutlu çapraz korelasyon işlemini gösterir.
Fig. 15.3.3 3 adet girdi kanalı ile tek boyutlu çapraz korelasyon işlemi. Gölgeli kısımlar, çıktı hesaplaması için kullanılan girdi ve çekirdek tensör elemanlarının yanı sıra ilk çıktı elemanıdır: \(0\times1+1\times2+1\times3+2\times4+2\times(-1)+3\times(-3)=2\).¶
Birden fazla girdi kanalı için tek boyutlu çapraz korelasyon işlemini uygulayabilir ve Fig. 15.3.3 içindeki sonuçları doğrulayabiliriz.
def corr1d_multi_in(X, K):
# İlk olarak, `X` ve `K`'nin 0. boyutu (kanal boyutu) boyunca yineleyin.
# Ardından, bunları toplayın
return sum(corr1d(x, k) for x, k in zip(X, K))
X = np.array([[0, 1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6, 7],
[2, 3, 4, 5, 6, 7, 8]])
K = np.array([[1, 2], [3, 4], [-1, -3]])
corr1d_multi_in(X, K)
array([ 2., 8., 14., 20., 26., 32.])
def corr1d_multi_in(X, K):
# İlk olarak, `X` ve `K`'nin 0. boyutu (kanal boyutu) boyunca yineleyin.
# Ardından, bunları toplayın
return sum(corr1d(x, k) for x, k in zip(X, K))
X = torch.tensor([[0, 1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6, 7],
[2, 3, 4, 5, 6, 7, 8]])
K = torch.tensor([[1, 2], [3, 4], [-1, -3]])
corr1d_multi_in(X, K)
tensor([ 2., 8., 14., 20., 26., 32.])
Çoklu girdi kanallı tek boyutlu çapraz korelasyonların, tek girdi kanallı iki boyutlu çapraz korelasyonlara eşdeğer olduğuna dikkat edin. Göstermek için, Fig. 15.3.3 içindeki çoklu girdi kanallı tek boyutlu çapraz korelasyonun eşdeğer bir formu, Fig. 15.3.4 içindeki tek girdi kanallı iki boyutlu çapraz korelasyondur; burada evrişim çekirdeğinin yüksekliği girdi tensörünküyle aynı olmalıdır.
Fig. 15.3.4 Tek bir girdi kanalı ile iki boyutlu çapraz korelasyon işlemi. Gölgeli kısımlar, çıktı hesaplaması için kullanılan girdi ve çekirdek tensör elemanlarının yanı sıra ilk çıktı elemanıdır: \(2\times(-1)+3\times(-3)+1\times3+2\times4+0\times1+1\times2=2\).¶
Fig. 15.3.2 ve Fig. 15.3.3 içindeki her iki çıktıda da yalnızca bir kanal vardır. Section 6.4.2 içinde açıklanan çoklu çıktı kanallarına sahip iki boyutlu evrişimlerle aynı şekilde, tek boyutlu evrişimler için de çoklu çıktılı kanallar belirtebiliriz.
15.3.2. Zaman Üzerinden Maksimum Ortaklama¶
Benzer şekilde, zaman adımları boyunca en önemli öznitelik olarak dizi temsillerinden en yüksek değeri çıkarmak için ortaklamayı kullanabiliriz. textCNN’de kullanılan zaman üzerinden maksimum ortaklama, tek boyutlu küresel maksimum ortaklama gibi çalışır (Collobert et al., 2011). Her kanalın farklı zaman adımlarında değerleri depoladığı çok kanallı bir girdi için, her kanaldaki çıktı o kanal için maksimum değerdir. Zaman üzerinden maksimum ortaklamanın, farklı kanallarda farklı sayıda zaman adımına izin verdiğini unutmayın.
15.3.3. TextCNN Modeli¶
Tek boyutlu evrişim ve zaman üzerinden maksimum ortaklama kullanarak, textCNN modeli girdi olarak önceden eğitilmiş bireysel belirteç temsillerini alır, sonra aşağı akış uygulama için dizi temsillerini elde eder ve dönüştürür.
\(d\) boyutlu vektörlerle temsil edilen \(n\) belirteci olan tek bir metin dizisi için girdi tensörünün kanal genişliği, yüksekliği ve sayısı sırasıyla \(n\), \(1\) ve \(d\)’dir. textCNN modeli, girdiyi çıktıya aşağıdaki gibi dönüştürür:
Birden çok tek boyutlu evrişim çekirdeğini tanımlar ve girdiler üzerinde ayrı olarak evrişim işlemlerini gerçekleştirir. Farklı genişliklere sahip evrişim çekirdekleri, farklı sayıdaki bitişik belirteçler arasındaki yerel öznitelikleri yakalayabilir.
Tüm çıktı kanallarında zaman üzerinden maksimum ortaklama gerçekleştirin ve ardından tüm skaler ortaklama çıktılarını bir vektör olarak bitiştirin.
Tam bağlı katmanı kullanarak bitiştirilmiş vektörü çıktı kategorilerine dönüştürün. Hattan düşürme, aşırı öğrenmeyi azaltmak için kullanılabilir.
Fig. 15.3.5 textCNN model mimarisi.¶
Fig. 15.3.5, textCNN’in model mimarisini somut bir örnekle göstermektedir. Girdi, her belirtecin 6 boyutlu vektörlerle temsil edildiği 11 belirteçli bir cümledir. Bu yüzden genişliği 11 olan 6 kanallı bir girdiye sahibiz. Sırasıyla 4 ve 5 çıktı kanalı ile 2 ve 4 genişliklerindeki iki tek boyutlu evrişim çekirdeğini tanımlayın. \(11-2+1=10\) genişliğinde 4 çıktı kanalı ve \(11-4+1=8\) genişliğinde 5 çıktı kanalı üretirler. Bu 9 kanalın farklı genişliklerine rağmen, zaman üzerinden maksimum ortaklama, bitiştirilmiş 9 boyutlu bir vektör verir ve bu da en sonunda ikili duygu tahminleri için 2 boyutlu bir çıktı vektörüne dönüştürülür.
15.3.3.1. Modeli Tanımlama¶
textCNN modelini aşağıdaki sınıfta uyguluyoruz. Section 15.2 içindeki çift yönlü RNN modeliyle karşılaştırıldığında, yinelemeli katmanları evrişimli katmanlarla değiştirmenin yanı sıra, iki gömme katmanı da kullanıyoruz: Biri eğitilebilir ağırlıklara ve diğeri sabit ağırlıklara sahiptir.
class TextCNN(nn.Block):
def __init__(self, vocab_size, embed_size, kernel_sizes, num_channels,
**kwargs):
super(TextCNN, self).__init__(**kwargs)
self.embedding = nn.Embedding(vocab_size, embed_size)
# Eğitilmeyecek gömme katmanı
self.constant_embedding = nn.Embedding(vocab_size, embed_size)
self.dropout = nn.Dropout(0.5)
self.decoder = nn.Dense(2)
# Zaman üzerinden maksimum ortaklama katmanının parametresi yoktur,
# bu nedenle bu örnek paylaşılabilir
self.pool = nn.GlobalMaxPool1D()
# Birden çok tek boyutlu evrişim katmanı oluşturun
self.convs = nn.Sequential()
for c, k in zip(num_channels, kernel_sizes):
self.convs.add(nn.Conv1D(c, k, activation='relu'))
def forward(self, inputs):
# Vektörler boyunca (iş boyutu, belirteç sayısı, belirteç vektör
# boyutu) şekilli iki gömme katmanı çıktısını bitiştirin
embeddings = np.concatenate((
self.embedding(inputs), self.constant_embedding(inputs)), axis=2)
# Tek boyutlu evrişimli katmanların girdi formatına göre, tensörü
# yeniden düzenleyin, böylece ikinci boyut kanalları depolar
embeddings = embeddings.transpose(0, 2, 1)
# Her bir tek boyutlu evrişim katmanı için, maksimum zaman üzerinden
# ortaklamadan sonra, (iş boyutu, kanal sayısı, 1) şekilli bir tensör
# elde edilir. Son boyutu kaldırın ve kanallar boyunca bitiştirin.
encoding = np.concatenate([
np.squeeze(self.pool(conv(embeddings)), axis=-1)
for conv in self.convs], axis=1)
outputs = self.decoder(self.dropout(encoding))
return outputs
class TextCNN(nn.Module):
def __init__(self, vocab_size, embed_size, kernel_sizes, num_channels,
**kwargs):
super(TextCNN, self).__init__(**kwargs)
self.embedding = nn.Embedding(vocab_size, embed_size)
# Eğitilmeyecek gömme katmanı
self.constant_embedding = nn.Embedding(vocab_size, embed_size)
self.dropout = nn.Dropout(0.5)
self.decoder = nn.Linear(sum(num_channels), 2)
# Zaman üzerinden maksimum ortaklama katmanının parametresi yoktur,
# bu nedenle bu örnek paylaşılabilir
self.pool = nn.AdaptiveAvgPool1d(1)
self.relu = nn.ReLU()
# Birden çok tek boyutlu evrişim katmanı oluşturun
self.convs = nn.ModuleList()
for c, k in zip(num_channels, kernel_sizes):
self.convs.append(nn.Conv1d(2 * embed_size, c, k))
def forward(self, inputs):
# Vektörler boyunca (iş boyutu, belirteç sayısı, belirteç vektör
# boyutu) şekilli iki gömme katmanı çıktısını bitiştirin
embeddings = torch.cat((
self.embedding(inputs), self.constant_embedding(inputs)), dim=2)
# Tek boyutlu evrişimli katmanların girdi formatına göre, tensörü
# yeniden düzenleyin, böylece ikinci boyut kanalları depolar
embeddings = embeddings.permute(0, 2, 1)
# Her bir tek boyutlu evrişim katmanı için, maksimum zaman üzerinden
# ortaklamadan sonra, (iş boyutu, kanal sayısı, 1) şekilli bir tensör
# elde edilir. Son boyutu kaldırın ve kanallar boyunca bitiştirin.
encoding = torch.cat([
torch.squeeze(self.relu(self.pool(conv(embeddings))), dim=-1)
for conv in self.convs], dim=1)
outputs = self.decoder(self.dropout(encoding))
return outputs
Bir textCNN örneği oluşturalım. 3, 4 ve 5 çekirdek genişliklerine sahip 3 adet evrişimli katmana sahiptir ve hepsinde 100 çıktı kanalı bulunur.
embed_size, kernel_sizes, nums_channels = 100, [3, 4, 5], [100, 100, 100]
devices = d2l.try_all_gpus()
net = TextCNN(len(vocab), embed_size, kernel_sizes, nums_channels)
net.initialize(init.Xavier(), ctx=devices)
embed_size, kernel_sizes, nums_channels = 100, [3, 4, 5], [100, 100, 100]
devices = d2l.try_all_gpus()
net = TextCNN(len(vocab), embed_size, kernel_sizes, nums_channels)
def init_weights(m):
if type(m) in (nn.Linear, nn.Conv1d):
nn.init.xavier_uniform_(m.weight)
net.apply(init_weights);
15.3.3.2. Önceden Eğitilmiş Sözcük Vektörlerini Yükleme¶
Section 15.2 ile aynı şekilde, önceden eğitilmiş 100
boyutlu GloVe gömme yerleştirmelerini, ilkletilmiş belirteç temsilleri
olarak yükleriz. Bu belirteç temsilleri (gömme ağırlıkları)
embedding
’da eğitilecek ve constant_embedding
’te
sabitlenecektir.
glove_embedding = d2l.TokenEmbedding('glove.6b.100d')
embeds = glove_embedding[vocab.idx_to_token]
net.embedding.weight.set_data(embeds)
net.constant_embedding.weight.set_data(embeds)
net.constant_embedding.collect_params().setattr('grad_req', 'null')
glove_embedding = d2l.TokenEmbedding('glove.6b.100d')
embeds = glove_embedding[vocab.idx_to_token]
net.embedding.weight.data.copy_(embeds)
net.constant_embedding.weight.data.copy_(embeds)
net.constant_embedding.weight.requires_grad = False
15.3.3.3. Model Eğitimi ve Değerlendirilmesi¶
Şimdi textCNN modelini duygu analizi için eğitebiliriz.
lr, num_epochs = 0.001, 5
trainer = gluon.Trainer(net.collect_params(), 'adam', {'learning_rate': lr})
loss = gluon.loss.SoftmaxCrossEntropyLoss()
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)
loss 0.088, train acc 0.969, test acc 0.863
3352.3 examples/sec on [gpu(0), gpu(1)]
lr, num_epochs = 0.001, 5
trainer = torch.optim.Adam(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss(reduction="none")
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)
loss 0.064, train acc 0.980, test acc 0.856
3380.2 examples/sec on [device(type='cuda', index=0), device(type='cuda', index=1)]
Aşağıda iki basit cümlede duygu tahmini için eğitilmiş model kullanıyoruz.
d2l.predict_sentiment(net, vocab, 'this movie is so great')
'positive'
d2l.predict_sentiment(net, vocab, 'this movie is so bad')
'negative'
d2l.predict_sentiment(net, vocab, 'this movie is so great')
'positive'
d2l.predict_sentiment(net, vocab, 'this movie is so bad')
'negative'
15.3.4. Özet¶
Tek boyutlu CNN’ler metinlerdeki \(n\)-gramlar gibi yerel öznitelikleri işleyebilir.
Çoklu girdi kanallı tek boyutlu çapraz korelasyonlar, tek girdi kanallı iki boyutlu çapraz korelasyonlara eşdeğerdir.
Zaman üzerinden maksimum ortaklama, farklı kanallarda farklı sayıda zaman adımına olanak tanır.
textCNN modeli, tek boyutlu evrişimli katmanları ve zaman üzerinden maksimum ortaklama katmanları kullanarak tek tek belirteç temsillerini aşağı akış uygulama çıktılarına dönüştürür.
15.3.5. Alıştırmalar¶
Hiper parametreleri ayarlayın ve duygu analizi için iki mimariyi, Section 15.2 içindeki ve bu bölümdeki, örneğin sınıflandırma doğruluğu ve hesaplama verimliliği gibi, karşılaştırın.
Section 15.2 alıştırmalarında tanıtılan yöntemleri kullanarak modelin sınıflandırma doğruluğunu daha da iyileştirebilir misiniz?
Girdi temsillerine konumsal kodlama ekleyin. Sınıflandırma doğruluğunu arttırıyor mu?