Last active
August 21, 2024 02:25
-
-
Save rikyperdana/b57f19dcb0923e0c74aec7c3edd37c6b to your computer and use it in GitHub Desktop.
DDI Lia RegCN
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# RegCN | |
import math # Untuk fungsi matematika dasar | |
import torch # Librari utama untuk komputasi tensor dalam PyTorch | |
import torch.nn as nn # Modul untuk membangun jaringan saraf | |
import torch.nn.functional as F # Fungsi aktivasi dan operasi umum dalam jaringan saraf | |
from transformers import BertModel # Model BERT dari pustaka Transformers, untuk embedding teks | |
# Modul DynamicLSTM yang didefinisikan secara terpisah, mungkin untuk menangani panjang urutan yang variabel | |
from layers.dynamic_rnn import DynamicLSTM | |
class GraphConvolution(nn.Module): # kelas yang mengimplementasikan lapisan konvolusi grafis | |
# Inisialisasi kelas dengan parameter input dan output, serta pilihan bias | |
def __init__(self, in_features, out_features, bias=True): | |
super(GraphConvolution, self).__init__() | |
self.in_features = in_features | |
self.out_features = out_features | |
self.weight = nn.Parameter(torch.FloatTensor(in_features, out_features)) | |
if bias: | |
self.bias = nn.Parameter(torch.FloatTensor(out_features)) | |
else: | |
self.register_parameter('bias', None) | |
# Fungsi yang melakukan operasi konvolusi grafis | |
def forward(self, text, adj): | |
text = text.to(torch.float32) # Tensor yang mewakili masukan teks | |
hidden = torch.matmul(text, self.weight) # Hasil perkalian text dengan weight untuk mendapatkan representasi tersembunyi | |
denom = torch.sum(adj, dim=2, keepdim=True) + 1 # Penjumlahan elemen dalam setiap baris dari adj (plus 1 untuk menghindari pembagian dengan nol) | |
# Hasil konvolusi grafis, dihitung dengan perkalian matriks adj dan hidden dan dibagi dengan denom | |
output = torch.matmul(adj, hidden) / denom # adj: Matriks adjacency yang menggambarkan koneksi antar kata | |
if self.bias is not None: # Bias ditambahkan ke output jika diperlukan | |
return output + self.bias | |
else: | |
return output | |
class REGCN(nn.Module): # adalah kelas utama model ReGCN | |
# Inisialisasi kelas dengan matriks embedding dan opsi konfigurasi (`opt`) | |
def __init__(self, embedding_matrix, opt): | |
super(REGCN, self).__init__() | |
self.opt = opt | |
self.usebert = self.opt.usebert # Flag untuk menentukan apakah model BERT digunakan | |
if self.usebert: | |
self.embed = BertModel.from_pretrained('./datasets/bert-base-uncased').requires_grad_(False) | |
self.embed.eval() # Inisialisasi model embedding (BERT atau Embedding biasa) | |
# Layer LSTM untuk memproses teks | |
self.text_lstm = DynamicLSTM(768, opt.hidden_dim, num_layers=1, batch_first=True, bidirectional=True) | |
else: | |
self.embed = nn.Embedding.from_pretrained(torch.tensor(embedding_matrix, dtype=torch.float)) | |
self.text_lstm = DynamicLSTM(opt.embed_dim, opt.hidden_dim, num_layers=1, batch_first=True, bidirectional=True) | |
# Layers konvolusi grafis yang digunakan untuk pemrosesan semantic, syntax, dan dependency | |
self.gc1 = GraphConvolution(2*opt.hidden_dim, 2*opt.hidden_dim) | |
self.gc2 = GraphConvolution(2*opt.hidden_dim, 2*opt.hidden_dim) | |
self.gc3 = GraphConvolution(2*opt.hidden_dim, 2*opt.hidden_dim) | |
self.gc4 = GraphConvolution(2*opt.hidden_dim, 2*opt.hidden_dim) | |
# Layers konvolusi grafis yang digunakan untuk pemrosesan semantic, syntax, dan dependency | |
self.semantic = nn.Sequential(*[GraphConvolution(2*opt.hidden_dim, 2*opt.hidden_dim) for _ in range(opt.gcn_layer)]) | |
self.syntax = nn.Sequential(*[GraphConvolution(2*opt.hidden_dim, 2*opt.hidden_dim) for _ in range(opt.gcn_layer)]) | |
self.dependency = nn.Sequential(*[GraphConvolution(2*opt.hidden_dim, 2*opt.hidden_dim) for _ in range(opt.gcn_layer)]) | |
# Layer fully-connected untuk klasifikasi sentimen | |
self.fc = nn.Linear(2*opt.hidden_dim, opt.polarities_dim) | |
self.dfc = nn.Linear(4*opt.hidden_dim, opt.polarities_dim) | |
self.text_embed_dropout = nn.Dropout(opt.dropout) # Layer dropout untuk regularisasi | |
# Parameter yang digunakan untuk cross-network | |
self.weight = nn.Parameter(torch.FloatTensor(4 * opt.hidden_dim, 4 * opt.hidden_dim)) | |
self.bias = nn.Parameter(torch.FloatTensor(4 * opt.hidden_dim)) | |
# Fungsi untuk menghitung bobot posisi berdasarkan lokasi aspek dalam teks | |
def position_weight(self, x, aspect_double_idx, text_len, aspect_len): | |
batch_size = x.shape[0] | |
seq_len = x.shape[1] | |
aspect_double_idx = aspect_double_idx.cpu().numpy() | |
text_len = text_len.cpu().numpy() | |
aspect_len = aspect_len.cpu().numpy() | |
weight = [[] for i in range(batch_size)] | |
for i in range(batch_size): | |
context_len = text_len[i] - aspect_len[i] | |
for j in range(aspect_double_idx[i,0]): | |
weight[i].append(1-(aspect_double_idx[i,0]-j)/context_len) | |
for j in range(aspect_double_idx[i,0], aspect_double_idx[i,1]+1): | |
weight[i].append(0) | |
for j in range(aspect_double_idx[i,1]+1, text_len[i]): | |
weight[i].append(1-(j-aspect_double_idx[i,1])/context_len) | |
for j in range(text_len[i], seq_len): | |
weight[i].append(0) | |
weight = torch.tensor(weight).unsqueeze(2).to(self.opt.device) | |
return weight*x | |
# Fungsi untuk melakukan cross-network antara representasi semantic dan syntax | |
def cross_network(self,f0,fn): | |
fn_weight = torch.matmul(fn,self.weight) | |
fl = f0*fn_weight + self.bias + f0 | |
x = fl[:,:,0:2*self.opt.hidden_dim] | |
y = fl[:,:,2*self.opt.hidden_dim:] | |
return x,y | |
# Fungsi untuk me-mask representasi teks di luar rentang aspek | |
def mask(self, x, aspect_double_idx): | |
batch_size, seq_len = x.shape[0], x.shape[1] | |
aspect_double_idx = aspect_double_idx.cpu().numpy() | |
mask = [[] for i in range(batch_size)] | |
for i in range(batch_size): | |
for j in range(aspect_double_idx[i,0]): | |
mask[i].append(0) | |
for j in range(aspect_double_idx[i,0], aspect_double_idx[i,1]+1): | |
mask[i].append(1) | |
for j in range(aspect_double_idx[i,1]+1, seq_len): | |
mask[i].append(0) | |
mask = torch.tensor(mask).unsqueeze(2).float().to(self.opt.device) | |
return mask*x | |
# Fungsi utama model yang mengambil masukan dan melakukan prediksi sentimen | |
def forward(self, inputs): | |
# inputs: Masukan model, termasuk indeks teks, indeks aspek, dan matriks adjacency | |
text_indices, aspect_indices, left_indices, pmi_adj, cos_adj, dep_adj = inputs | |
aspect_len = torch.sum(aspect_indices != 0, dim=-1) # Panjang aspek dan bagian kiri teks | |
left_len = torch.sum(left_indices != 0, dim=-1) | |
# Indeks awal dan akhir aspek | |
aspect_double_idx = torch.cat([left_len.unsqueeze(1), (left_len+aspect_len-1).unsqueeze(1)], dim=1) | |
if not self.usebert: | |
text_len = torch.sum(text_indices != 0, dim=-1) | |
text = self.embed(text_indices) # Hasil embedding teks | |
text_out = self.text_embed_dropout(text) | |
text_out, (_, _) = self.text_lstm(text_out, text_len) # Hasil dari layer LSTM | |
f0 = torch.cat([text_out, text_out], dim=2) # Representasi awal teks yang digabungkan dari text_out | |
else: | |
text_len = torch.sum(text_indices != 0, dim=-1) - 1 | |
text, pool = self.embed(text_indices) | |
pad0 = torch.zeros((text.shape[0], 1, text.shape[2])).to(self.opt.device) | |
text = torch.cat((text[:, 1:, :], pad0), dim=1) | |
text_out = self.text_embed_dropout(text) | |
text_out, (_, _) = self.text_lstm(text_out, text_len - 2) | |
f0 = torch.cat([text_out, text_out], dim=2) | |
num_layer = self.opt.num_layer | |
f_n = None | |
for i in range(num_layer): | |
if i == 0: | |
# Hasil konvolusi grafis menggunakan matriks adjacency PMI dan cosine | |
x_pmi_1 = F.relu(self.gc1(self.position_weight(text_out, aspect_double_idx, text_len, aspect_len), pmi_adj)) | |
x_pmi_2 = F.relu(self.gc2(self.position_weight(x_pmi_1, aspect_double_idx, text_len, aspect_len), pmi_adj)) | |
x_cos_1 = F.relu(self.gc3(self.position_weight(text_out, aspect_double_idx, text_len, aspect_len),cos_adj)) | |
x_cos_2 = F.relu(self.gc4(self.position_weight(x_cos_1, aspect_double_idx, text_len, aspect_len),cos_adj)) | |
x_s = torch.cat([(x_pmi_2) , (x_cos_2)],dim=2) | |
else: | |
x_d_pmi = F.relu(self.gc1(self.position_weight(x_pmi, aspect_double_idx, text_len, aspect_len), dep_adj)) | |
x_p_d = F.relu(self.gc2(self.position_weight(x_d_pmi, aspect_double_idx, text_len, aspect_len), dep_adj)) | |
x_d_cos = F.relu(self.gc3(self.position_weight(x_cos, aspect_double_idx, text_len, aspect_len), dep_adj)) | |
x_c_d = F.relu(self.gc4(self.position_weight(x_d_cos, aspect_double_idx, text_len, aspect_len), dep_adj)) | |
f_n = torch.cat([(0.3*x_pmi_2 + x_p_d) , (0.3*x_cos_2 + x_c_d)],dim=2) | |
# Hasil akhir dari layer konvolusi grafis yang telah di-mask dan di-attention | |
x = self.mask(f_n, aspect_double_idx) | |
alpha_mat = torch.matmul(x, f0.transpose(1, 2)) | |
alpha = F.softmax(alpha_mat.sum(1, keepdim=True), dim=2) | |
x = torch.matmul(alpha, f0).squeeze(1) #(batch,hidden_dim) | |
output = self.dfc(x) # Prediksi sentimen | |
return output ,text_indices, alpha |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment