Hoşgeldin Misafir

Duygu Analizi: Evrişimli Sinir Ağlarının Kullanımı

Mustafa73

20 Eyl 2023
423 Mesaj

Aktiflik

Seviye

Deneyim

TIM / GÖREV:
Evrişimli Sinir Ağları (CNN'ler), bilgisayarla görme için tasarlanmış olsalar da, doğal dil işleme (NLP) alanında da etkili bir şekilde kullanılmaktadır. Metinleri tek boyutlu imge olarak düşünerek, CNN'ler n-gramlar gibi yerel özellikleri yakalayabilir. Bu makalede, duygu analizi için önceden eğitilmiş GloVe kelime vektörlerini kullanarak CNN tabanlı bir modelin nasıl oluşturulacağı anlatılmaktadır.

Tek Boyutlu Evrişimler
CNN'lerde genellikle 2 boyutlu evrişimler kullanılır, ancak doğal dil işleme için 1 boyutlu evrişimler de yaygın olarak kullanılır. Tek boyutlu evrişimde, evrişim penceresi girdi tensörü boyunca kayar ve belirli bir konumdaki girdi alt tensörü ile çekirdek tensörü eleman yönlü çarpılır. Bu çarpımların toplamı, çıktı tensöründe bir değer oluşturur.

Aşağıdaki corr1d fonksiyonu, tek boyutlu çapraz korelasyon işlemini gerçekleştirir:

Python:
import torch

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

X, K = torch.tensor([0, 1, 2, 3, 4, 5, 6]), torch.tensor([1, 2])
print(corr1d(X, K))  # Output: tensor([ 2.,  5.,  8., 11., 14., 17.])


Bu örnek, tek boyutlu çapraz korelasyon işleminin nasıl çalıştığını göstermektedir.

Birden Çok Kanal
Birden fazla girdi kanalı için, her kanal üzerinde ayrı ayrı evrişim işlemleri yapılır ve sonuçlar toplanır:
Python:
def corr1d_multi_in(X, K):
    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]])
print(corr1d_multi_in(X, K))  # Output: tensor([ 2.,  8., 14., 20., 26., 32.])

Zaman Üzerinden Maksimum Ortaklama
Zaman adımları boyunca en yüksek değeri seçmek için maksimum ortaklama kullanılır. Bu işlem, tek boyutlu evrişimli katmanlardan gelen özellik haritalarını küçültmek için kullanılır.

TextCNN Modeli
TextCNN, metinleri duygu analizine tabi tutmak için tasarlanmış bir CNN modelidir. Model, çeşitli genişliklerdeki evrişim çekirdekleri kullanarak girdiler üzerinde evrişim işlemleri gerçekleştirir ve ardından maksimum ortaklama ile sonuçları birleştirir.

Modelin tanımı şu şekildedir:

Python:
import torch
from torch import nn
from d2l import torch as d2l

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)
        self.constant_embedding = nn.Embedding(vocab_size, embed_size)
        self.dropout = nn.Dropout(0.5)
        self.decoder = nn.Linear(sum(num_channels), 2)
        self.pool = nn.AdaptiveAvgPool1d(1)
        self.relu = nn.ReLU()
        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):
        embeddings = torch.cat((
            self.embedding(inputs), self.constant_embedding(inputs)), dim=2)
        embeddings = embeddings.permute(0, 2, 1)
        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

# Modelin oluşturulması
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);

# Önceden Eğitilmiş Sözcük Vektörlerini Yükleme
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


Model Eğitimi ve Değerlendirilmesi
Modeli eğitmek ve değerlendirmek için:

Python:
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)


Duygu Tahmini
Eğitilmiş model ile duygu tahmini yapılabilir:


Python:
d2l.predict_sentiment(net, vocab, 'this movie is so great')  # Output: 'positive'
d2l.predict_sentiment(net, vocab, 'this movie is so bad')    # Output: 'negative


Bu açıklama ve kodlar, duygu analizi için CNN'lerin nasıl kullanılacağını göstermektedir. Model, çeşitli genişliklerdeki evrişim çekirdekleri ile metinlerdeki yerel özellikleri yakalayıp, zaman üzerinden maksimum ortaklama ile bu özellikleri birleştirir.

Doğal Dil Çıkarımı ve Veri Kümesi

Doğal Dil Çıkarımı (NLI)

Doğal dil çıkarımı (Natural Language Inference, NLI), bir metin dizisinin (öncül) başka bir metin dizisinden (hipotez) çıkarılıp çıkarılamayacağını belirler. NLI, bir çift metin arasındaki mantıksal ilişkiyi sınıflandırır ve bu ilişkiler genellikle üç kategoriye ayrılır:

1. Gerekçe (Entailment): Hipotez, öncülden mantıksal olarak çıkarılabilir.
2. Çelişki (Contradiction): Hipotezin olumsuzlanması öncülden çıkarılabilir.
3. Tarafsızlık (Neutral): Hiçbiri geçerli değilse, yani ne gerekçe ne de çelişki durumu varsa.

Örnekler:

- Gerekçe: "İki kadın birbirlerine sarılıyor." → "İki kadın sevgi gösteriyor."
- Çelişki: "Bir adam kodlama örneğini çalıştırıyor." → "Adam uyuyor."
- Tarafsızlık: "Müzisyenler bizim için performans gösteriyor." → "Müzisyenler ünlüdür."

Stanford Doğal Dil Çıkarımı (SNLI) Veri Kümesi

SNLI veri kümesi, NLI problemlerini çözmek için kullanılan yaygın bir veri kümesidir. 500.000'den fazla etiketli İngilizce cümle çifti içerir. Bu veri kümesi, gerekçe, çelişki ve tarafsızlık kategorilerinde etiketlenmiş cümle çiftlerini barındırır.

Veri Kümesini İşleme ve Okuma

SNLI veri kümesini indirdikten sonra, metinleri ve etiketleri içeren cümle çiftlerini çıkarmak için bir işlev tanımlayabiliriz. İşlevin görevi, gereksiz karakterleri ve boşlukları temizlemek ve veriyi uygun bir formatta döndürmektir.

Python:
import os
import re
import torch
from torch.utils.data import Dataset, DataLoader

def read_snli(data_dir, is_train):
    def extract_text(s):
        s = re.sub('\\(', '', s)
        s = re.sub('\\)', '', s)
        s = re.sub('\\s{2,}', ' ', s)
        return s.strip()

    label_set = {'entailment': 0, 'contradiction': 1, 'neutral': 2}
    file_name = os.path.join(data_dir, 'snli_1.0_train.txt' if is_train else 'snli_1.0_test.txt')

    with open(file_name, 'r') as f:
        rows = [row.split('\t') for row in f.readlines()[1:]]
        premises = [extract_text(row[1]) for row in rows if row[0] in label_set]
        hypotheses = [extract_text(row[2]) for row in rows if row[0] in label_set]
        labels = [label_set[row[0]] for row in rows if row[0] in label_set]

    return premises, hypotheses, labels

train_data = read_snli(data_dir, is_train=True)
for x0, x1, y in zip(train_data[0][:3], train_data[1][:3], train_data[2][:3]):
    print('premise:', x0)
    print('hypothesis:', x1)
    print('label:', y)

Veri Kümesini Yüklemek İçin Sınıf Tanımlama

SNLI veri kümesini yüklemek için özel bir veri kümesi sınıfı tanımlayabiliriz. Bu sınıf, veri kümesini öncüller, hipotezler ve etiketler olarak yükler ve dizi uzunluğunu standart bir uzunluğa ayarlar.
Python:
class SNLIDataset(Dataset):
    def __init__(self, dataset, num_steps, vocab=None):
        self.num_steps = num_steps
        all_premise_tokens = [sentence.split() for sentence in dataset[0]]
        all_hypothesis_tokens = [sentence.split() for sentence in dataset[1]]

        if vocab is None:
            self.vocab = {word: idx for idx, word in enumerate(set([token for sentence in all_premise_tokens + all_hypothesis_tokens for token in sentence]))}
            self.vocab['<pad>'] = len(self.vocab)
        else:
            self.vocab = vocab

        self.premises = self._pad(all_premise_tokens)
        self.hypotheses = self._pad(all_hypothesis_tokens)
        self.labels = torch.tensor(dataset[2])

        print('read ' + str(len(self.premises)) + ' examples')

    def _pad(self, lines):
        return torch.tensor([self._truncate_pad(line) for line in lines])

    def _truncate_pad(self, line):
        tokens = [self.vocab[token] for token in line]
        if len(tokens) > self.num_steps:
            return tokens[:self.num_steps]
        else:
            return tokens + [self.vocab['<pad>']] * (self.num_steps - len(tokens))

    def __getitem__(self, idx):
        return (self.premises[idx], self.hypotheses[idx]), self.labels[idx]

    def __len__(self):
        return len(self.premises)

Veri Yükleyicileri Oluşturma

Veri kümesini yüklemek ve eğitim ile test veri yükleyicilerini oluşturmak için bir fonksiyon tanımlayabiliriz.

Python:
def load_data_snli(batch_size, num_steps=50):
    data_dir = 'path_to_snli_data'
    train_data = read_snli(data_dir, True)
    test_data = read_snli(data_dir, False)

    train_set = SNLIDataset(train_data, num_steps)
    test_set = SNLIDataset(test_data, num_steps, train_set.vocab)

    train_iter = DataLoader(train_set, batch_size, shuffle=True)
    test_iter = DataLoader(test_set, batch_size, shuffle=False)

    return train_iter, test_iter, train_set.vocab

train_iter, test_iter, vocab = load_data_snli(128, 50)
print(len(vocab))

İlk Mini-Batch'in Şeklini Yazdırma

Son olarak, eğitim veri yükleyicisinden ilk mini-batch'in şeklini yazdırabiliriz.

Python:
for X, Y in train_iter:
    print(X[0].shape)  # Premises
    print(X[1].shape)  # Hypotheses
    print(Y.shape)     # Labels
    break

Bu kod, SNLI veri kümesini indirip işleyerek eğitim ve test veri yükleyicileri oluşturur. Ayrıca, her bir mini-batch'in şeklini yazdırarak veri yükleyicisinin doğru çalışıp çalışmadığını kontrol eder.