raystermomo's picture
Deploy romance chat to Hugging Face Spaces
6cd1906
Raw
History Blame Contribute Delete
44.2 kB
import os
import sys
import time
import tqdm
import glob
import copy
import logging
import argparse
import pandas as pd
import numpy as np
from scipy.stats import pearsonr, spearmanr
from sklearn.metrics import accuracy_score, balanced_accuracy_score
import torch
import torch.nn as nn
from torch.nn import DataParallel
from torch.utils.data import Dataset, DataLoader
import torch.nn.functional as F
from torch_geometric.nn import GraphConv,GATv2Conv,RGCNConv
from transformers import AutoTokenizer, AutoModel, get_linear_schedule_with_warmup
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1,2,3,4,5,6,7'
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
parser = argparse.ArgumentParser()
# folders
parser.add_argument('--data_folder', type=str, default='./data/monologue_split_orig', help='input data folder')
parser.add_argument('--model_folder', type=str, default='./model/exp_name', help='model folder')
parser.add_argument('--log_file', type=str, default='./log/exp_name.log', help='log file')
parser.add_argument('--train_filename', type=str, default='train.csv', help='train data file')
parser.add_argument('--valid_filename', type=str, default='valid.csv', help='valid data file')
parser.add_argument('--test_filename', type=str, default='test.csv', help='test data file')
# flags
parser.add_argument('--train_flag', type=int, default=1, help='whether to train the model')
parser.add_argument('--test_flag', type=int, default=1, help='whether to test the model')
parser.add_argument('--real_time_flag', type=int, default=1, help='whether to test the model in real time setting, which means using only first several turns of a dialogue')
#parser.add_argument('--data_split', type=str, default='split1', help='which data split to use')
parser.add_argument('--max_length', type=int, default=512, help='max length of input sequence')
parser.add_argument('--base_model_name', type=str, default='studio-ousia/luke-japanese-base', help='name of pretrained model in huggingface')
parser.add_argument('--use_dropout', type=int, default=0, help='whether to use dropout after base model')
parser.add_argument('--multilinear', type=int, default=1, help='whether to use multiple linear layers after base model 0 means single linear layer')
parser.add_argument('--hidden_size', type=int, default=16, help='hidden size of the first linear layer if multilinear is 1')
parser.add_argument('--critertion_type', type=str, default='mae', help='criterion type, choose from mae mse')
parser.add_argument('--multitask', type=str, default='11111', help='whether to use multitask learning, 1 means use, 0 means not use, the order is n e o a c')
parser.add_argument('--lr', type=float, default=1e-5)
parser.add_argument('--warmup_steps', type=int, default=150)
parser.add_argument('--max_epoch', type=int, default=20, help='max epoch for training')
parser.add_argument('--patience', type=int, default=3, help='patience for early stopping')
parser.add_argument('--batch_size', type=int, default=128, help='batch size for training')
parser.add_argument('--context', type=int, default=0, help='whether to use dialogue scenario. 0 means monologue, 1 means dialogue')
parser.add_argument('--context_model_type', type=str, default='linear', help='context model type, choose from linear gcn-nospk2pred-lastnode')
parser.add_argument('--head_num', type=int, default=3, help='head number of gcn')
parser.add_argument('--model_variant', type=str, default='hcgnn', help='model variant, choose from gcn gatv2 rgcn hcgnn22 hcgnn3rel')
parser.add_argument('--ensemble', type=int, default=0, help='whether to use ensemble model')
parser.add_argument('--ensemble_model_folder', type=str, default='/share03/song/person_rec/model/nocontext_ensemble', help='folder of ensemble models')
args = parser.parse_args()
# variables
input_data_folder = args.data_folder
model_folder = args.model_folder
log_file = args.log_file
train_filename = args.train_filename
valid_filename = args.valid_filename
test_filename = args.test_filename
train_file = os.path.join(input_data_folder, train_filename)
valid_file = os.path.join(input_data_folder, valid_filename)
test_file = os.path.join(input_data_folder, test_filename)
os.makedirs(model_folder, exist_ok=True)
os.makedirs(os.path.dirname(log_file), exist_ok=True)
logging.basicConfig(filename=log_file, level=logging.INFO,
format='%(asctime)s:%(levelname)s:%(message)s')
train_flag = args.train_flag
test_flag = args.test_flag
real_time_flag = args.real_time_flag
max_length = args.max_length
# settings for base model
base_model_name = args.base_model_name
dropout = args.use_dropout
multilinear = args.multilinear
hidden_size = args.hidden_size
# settings for training
critertion_type = args.critertion_type
multitask_str = args.multitask
multitask = [int(flag) for flag in multitask_str]
lr = args.lr
warmup_steps = args.warmup_steps
max_epoch = args.max_epoch
patience = args.patience
batch_size = args.batch_size
# settings for dialogue scenario model
context = args.context
context_str = 'context' if context == 1 else 'nocontext'
context_model_type = args.context_model_type
if ('gcn' in context_model_type):
max_length = 64
head_num = args.head_num
model_variant = args.model_variant
pad_length = -1
# settings for ensemble (not really working well)
ensemble = args.ensemble
ensemble_model_folder = args.ensemble_model_folder
# constants for trail normalization
min_label = 1
max_label = 7
# constants (not used)
#exp_folder = './'
#input_data_folder = os.path.join(exp_folder, 'data', f'{context_str}_data', data_split)
#name = f'{context_str}_{data_split}_contextmodeltype-{context_model_type}_headnum{head_num}_modelvariant-{model_variant}_1'
#name = f'{context_str}_{data_split}_{context_model_type}_{model_variant}'
#model_folder = os.path.join(exp_folder, 'model', name)
#log_file = os.path.join(exp_folder, 'log', f'{name}.log')
def gene_data_from_csv(file_path, tokenizer, max_sentence_num=0):
csv_data = pd.read_csv(file_path)
dict_data = []
for i in range(len(csv_data)):
data_sample = csv_data.iloc[i]
text = data_sample['dialogue']
text = text.replace('\n', '')
if (context == 0):
if (max_sentence_num > 0):
if (text.startswith('[CLS]')):
text = text[len('[CLS]'):]
if (text.startswith('[SEP]')):
text = text[len('[SEP]'):]
sentences = text.split('[SEP]')
sentences = sentences[:max_sentence_num]
text = '[CLS]' + '[SEP]'.join(sentences)
text = text.replace('[CLS]', tokenizer.cls_token)
text = text.replace('[SEP]', tokenizer.sep_token)
if (not text.startswith(tokenizer.cls_token)):
text = tokenizer.cls_token + text
else:
if (max_sentence_num > 0):
if (text.startswith('[CLS]')):
text = text[len('[CLS]'):]
if (text.startswith('[SPK1]')):
text = text[len('[SPK1]'):]
sentences = text.split('[SPK1]')
sentences = sentences[:max_sentence_num]
text = '[CLS]' + '[SPK1]'.join(sentences)
text = text.replace('[CLS]', tokenizer.cls_token)
if (not text.startswith(tokenizer.cls_token)):
text = tokenizer.cls_token + text
assert text.startswith(tokenizer.cls_token)
labels = [data_sample['n'], data_sample['e'], data_sample['o'], data_sample['a'], data_sample['c']]
normalized_labels = [(label - min_label) / (max_label - min_label) for label in labels]
dict_data.append({
'text': text,
'labels': normalized_labels,
})
return dict_data
def gene_context_data(data, pad_length, tokenizer):
context_data = []
for sample in data:
text = sample['text']
text = text.replace('[CLS]', tokenizer.cls_token)
text = text.replace('[SEP]', tokenizer.sep_token)
text = text.replace(tokenizer.cls_token, '').replace(tokenizer.sep_token, '')
text_tmp = text.replace('[SPK1]', tokenizer.sep_token).replace('[SPK2]', tokenizer.sep_token)
if text_tmp.startswith(tokenizer.sep_token):
text_tmp = text_tmp[len(tokenizer.sep_token):]
sentences = text_tmp.split(tokenizer.sep_token)
sentences_with_speaker = []
for i, sentence in enumerate(sentences):
if sentence == '':
continue
if sentence.startswith(tokenizer.cls_token):
sentence = sentence[len(tokenizer.cls_token):]
if i % 2 == 0:
s = '[SPK1]' + sentence
else:
s = '[SPK2]' + sentence
sentences_with_speaker.append(s)
if len(sentences_with_speaker) < pad_length:
sentences_with_speaker += ['<pad>' for _ in range(pad_length - len(sentences_with_speaker))]
else:
sentences_with_speaker = sentences_with_speaker[:pad_length]
context_data.append({
'text': sentences_with_speaker, # each sentence is <s>[SPK*]text
'labels': sample['labels'],
})
return context_data
class ConversationDataset(Dataset):
def __init__(self, data, tokenizer,max_length):
self.data = data
self.tokenizer = tokenizer
self.max_length=max_length
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
sample = self.data[idx]
labels = torch.tensor(sample['labels'], dtype=torch.float)
input_ids=[]
attention_mask=[]
length=0
for text_row in sample['text']:
if text_row !='<pad>':
length+=1
encoding = self.tokenizer(text_row, padding="max_length", max_length=self.max_length, truncation=True, return_tensors="pt", add_special_tokens=True)
input_ids.append(encoding['input_ids'].squeeze())
attention_mask.append(encoding['attention_mask'].squeeze())
return {
'input_ids': input_ids,
'attention_mask': attention_mask,
'labels': torch.tensor(labels),
'current_length':length
}
class MultiTaskDataset(Dataset):
def __init__(self, data, tokenizer, max_length):
self.data = data
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
sample = self.data[idx]
encoding = self.tokenizer(sample['text'], max_length=self.max_length, padding='max_length',
truncation=True, return_tensors='pt', add_special_tokens=False)
return {
'input_ids': encoding['input_ids'].squeeze(),
'attention_mask': encoding['attention_mask'].squeeze(),
'labels': torch.tensor(sample['labels'], dtype=torch.float)
}
class MultiTaskModel(nn.Module):
def __init__(self, base_model, dropout_flag, multilinear_flag):
super(MultiTaskModel, self).__init__()
self.base_model = base_model
self.dropout_flag = dropout_flag
self.multilinear_flag = multilinear_flag
self.dropout_layer = nn.Dropout(0.1)
if (self.multilinear_flag == 0):
self.task_heads = nn.ModuleList([
nn.Sequential(
nn.Linear(base_model.config.hidden_size, 1),
) for _ in range(5)
])
else:
self.task_heads = nn.ModuleList([
nn.Sequential(
nn.Linear(base_model.config.hidden_size, hidden_size),
nn.ReLU(),
nn.Linear(hidden_size, 1),
) for _ in range(5)
])
def forward(self, input_ids, attention_mask):
outputs = self.base_model(input_ids=input_ids, attention_mask=attention_mask)
pooled_output = outputs['pooler_output']
if self.dropout_flag == 1:
pooled_output = self.dropout_layer(pooled_output)
regression_outputs = []
for task_head in self.task_heads:
regression_outputs.append(task_head(pooled_output))
return torch.cat(regression_outputs, dim=1)
class GCN(nn.Module):
def __init__(self,input_dim, hidden_dim1, output_dim, relations, heads):
super(GCN, self).__init__()
self.relations=1
self.output_dim=output_dim
self.conv2 = nn.ModuleList([GraphConv(input_dim, hidden_dim1) for _ in range(self.relations)])
self.conv3 = nn.ModuleList([GraphConv(hidden_dim1, output_dim) for _ in range(self.relations)])
def forward(self, x, relationsedge_indices_relations):
relation_outputs=[]
for i, conv_layer in enumerate(self.conv2):
relation_output=(conv_layer(x, relationsedge_indices_relations[-1]))
relation_output=F.relu(relation_output)
relation_output=self.conv3[i](relation_output, relationsedge_indices_relations[-1])
relation_output=relation_output.reshape(-1,1,self.output_dim)
relation_outputs.append(relation_output)
x=torch.cat(relation_outputs, dim=1)
return x
class GAT(nn.Module):
def __init__(self,input_dim, hidden_dim1, output_dim, relations, heads):
super(GAT, self).__init__()
self.relations=1
self.heads=heads
self.output_dim=hidden_dim1*self.heads
self.conv1 = nn.ModuleList([GATv2Conv(input_dim, hidden_dim1,heads=self.heads) for _ in range(self.relations)])
def forward(self, x, relationsedge_indices_relations):
relation_outputs=[]
for i, conv_layer in enumerate(self.conv1):
relation_output=(conv_layer(x, relationsedge_indices_relations[-1]))
relation_output=relation_output.reshape(-1,1,self.output_dim)
relation_outputs.append(relation_output)
x=torch.cat(relation_outputs, dim=1)
return x
class RGCN (nn.Module):
def __init__(self,input_dim, hidden_dim1, output_dim, relations, heads):
super(RGCN, self).__init__()
self.relations=1
self.heads=heads
self.output_dim=output_dim
self.conv1 = nn.ModuleList([RGCNConv(input_dim, hidden_dim1,2) for _ in range(self.relations)])
self.conv2 = nn.ModuleList([GraphConv(hidden_dim1, output_dim) for _ in range(self.relations)])
def forward(self, x, relationsedge_indices_relations,edge_type):
relation_outputs=[]
for i, conv_layer in enumerate(self.conv1):
relation_output=(conv_layer(x, relationsedge_indices_relations[-1],edge_type))
relation_output=F.relu(relation_output)
relation_output=self.conv2[i](relation_output, relationsedge_indices_relations[-1])
relation_output=relation_output.reshape(-1,1,self.output_dim)
relation_outputs.append(relation_output)
x=torch.cat(relation_outputs, dim=1)
return x
class GATv2GCN22(nn.Module):
def __init__(self,input_dim, hidden_dim1, output_dim, relations, heads):
super(GATv2GCN22, self).__init__()
self.relations=relations
self.heads=heads
self.output_dim=output_dim
self.conv1 = nn.ModuleList([GATv2Conv(input_dim, hidden_dim1,heads=self.heads) for _ in range(self.relations)])
self.conv2 = nn.ModuleList([GATv2Conv(hidden_dim1*self.heads, hidden_dim1,heads=self.heads) for _ in range(self.relations)])
self.conv3 = nn.ModuleList([GraphConv(hidden_dim1*self.heads, hidden_dim1) for _ in range(self.relations)])
self.conv4 = nn.ModuleList([GraphConv(hidden_dim1, output_dim) for _ in range(self.relations)])
def forward(self, x, relationsedge_indices_relations):
relation_outputs=[]
for i, conv_layer in enumerate(self.conv1):
relation_output=(conv_layer(x, relationsedge_indices_relations[i]))
relation_output=F.relu(relation_output)
relation_output=self.conv2[i](relation_output, relationsedge_indices_relations[i])
relation_output=F.relu(relation_output)
relation_output=self.conv3[i](relation_output, relationsedge_indices_relations[i])
relation_output=F.relu(relation_output)
relation_output=self.conv4[i](relation_output, relationsedge_indices_relations[i])
relation_output=relation_output.reshape(-1,1,self.output_dim)
relation_outputs.append(relation_output)
x=torch.cat(relation_outputs, dim=1)
return x
class GATv2GCN(nn.Module):
"""
GATv2+GCN model with 4/6 relations (GAT->GCN)
"""
def __init__(self,input_dim, hidden_dim1, output_dim, relations, heads):
super(GATv2GCN, self).__init__()
self.relations=relations
self.heads=heads
self.output_dim=output_dim
self.conv1 = nn.ModuleList([GATv2Conv(input_dim, hidden_dim1,heads=self.heads) for _ in range(self.relations)])
self.conv2 = nn.ModuleList([GraphConv(hidden_dim1*self.heads, hidden_dim1) for _ in range(self.relations)])
self.conv3 = nn.ModuleList([GraphConv(hidden_dim1, output_dim) for _ in range(self.relations)])
def forward(self, x, relationsedge_indices_relations):
relation_outputs=[]
for i, conv_layer in enumerate(self.conv1):
relation_output=(conv_layer(x, relationsedge_indices_relations[i]))
relation_output=F.relu(relation_output)
relation_output=self.conv2[i](relation_output, relationsedge_indices_relations[i])
relation_output=F.relu(relation_output)
relation_output=self.conv3[i](relation_output, relationsedge_indices_relations[i])
relation_output=relation_output.reshape(-1,1,self.output_dim)
relation_outputs.append(relation_output)
x=torch.cat(relation_outputs, dim=1)
return x
class GATv2GCN3REL(nn.Module):
def __init__(self,input_dim, hidden_dim1, output_dim, relations, heads):
super(GATv2GCN3REL, self).__init__()
self.relations=relations
self.heads=heads
self.output_dim=output_dim
self.conv1 = nn.ModuleList([GATv2Conv(input_dim, hidden_dim1,heads=self.heads) for _ in range(self.relations)])
self.conv2 = nn.ModuleList([GraphConv(hidden_dim1*self.heads, hidden_dim1) for _ in range(self.relations)])
self.conv3 = nn.ModuleList([GraphConv(hidden_dim1, output_dim) for _ in range(self.relations)])
self.conv4 = RGCNConv(input_dim, hidden_dim1,2)
self.conv5 = GraphConv(hidden_dim1,output_dim)
def forward(self, x, relationsedge_indices_relations,edge_type):
relation_outputs=[]
for i, conv_layer in enumerate(self.conv1):
relation_output=(conv_layer(x, relationsedge_indices_relations[i]))
relation_output=F.relu(relation_output)
relation_output=self.conv2[i](relation_output, relationsedge_indices_relations[i])
relation_output=F.relu(relation_output)
relation_output=self.conv3[i](relation_output, relationsedge_indices_relations[i])
relation_output=relation_output.reshape(-1,1,self.output_dim)
relation_outputs.append(relation_output)
relation_output_rgcn=self.conv4(x, relationsedge_indices_relations[-1],edge_type)
relation_output_rgcn=self.conv5(relation_output_rgcn,relationsedge_indices_relations[-1])
relation_output_rgcn=relation_output_rgcn.reshape(-1,1,self.output_dim)
relation_outputs.append(relation_output_rgcn)
x=torch.cat(relation_outputs, dim=1)
return x
class ScaledDotProductAttention(nn.Module):
def __init__(self, dim: int):
super(ScaledDotProductAttention, self).__init__()
self.sqrt_dim = np.sqrt(dim)
def forward(self, query, key, value,mask=None):
score = torch.bmm(query, key.transpose(1, 2)) / self.sqrt_dim
if mask is not None:
score.masked_fill_(mask.view(score.size()), -float('Inf'))
attn = F.softmax(score, -1)
context = torch.bmm(attn, value)
return context, attn
def edge_perms(length):
"""
Method to construct the edges of a graph (a utterance) considering the all the past utterances.
return: list of tuples. tuple -> (vertice(int), neighbor(int))
"""
user_sys = set()
sys_user = set()
user_user = set()
sys_sys = set()
eff_array_user=[]
eff_array_sys=[]
for i in range(length):
if i%2==0:
eff_array_user.append(i)
else:
eff_array_sys.append(i)
for i in range (length):
if i%2==0:
for j in eff_array_user:
user_user.add((i,j))
for j in eff_array_sys:
user_sys.add((i,j))
else:
for j in eff_array_user:
sys_user.add((i,j))
for j in eff_array_sys:
sys_sys.add((i,j))
user_sys_user=user_sys.union(user_user)
sys_user_sys=sys_user.union(sys_sys)
return [user_user,user_sys, sys_sys, sys_user, sys_user_sys,user_sys_user]
def batch_graphify_hgcn(features, lengths, device):
node_features, edge_index1,edge_index2,edge_index3,edge_index4, edge_index5, edge_index6,edge_type= [], [], [],[],[],[],[],[]
batch_size = features.size(0)
length_sum = 0
for j in range(batch_size):
cur_len = lengths[j].item()
node_features.append(features[j, :cur_len, :])
perms = edge_perms(cur_len)
perms_rec0 = [(item[0] + length_sum, item[1] + length_sum) for item in perms[0]]
perms_rec1= [(item[0] + length_sum, item[1] + length_sum) for item in perms[1]]
perms_rec2 = [(item[0] + length_sum, item[1] + length_sum) for item in perms[2]]
perms_rec3 = [(item[0] + length_sum, item[1] + length_sum) for item in perms[3]]
perms_rec4= [(item[0] + length_sum, item[1] + length_sum) for item in perms[4]]
perms_rec5= [(item[0] + length_sum, item[1] + length_sum) for item in perms[5]]
length_sum += cur_len
for item, item_rec in zip(perms[0], perms_rec0):
edge_index1.append(torch.tensor([item_rec[0], item_rec[1]])) #user_user
edge_type.append(torch.tensor([0]))
for item, item_rec in zip(perms[1], perms_rec1):
edge_index2.append(torch.tensor([item_rec[0], item_rec[1]]))#user_sys
edge_type.append(torch.tensor([1]))
for item, item_rec in zip(perms[2], perms_rec2):
edge_index3.append(torch.tensor([item_rec[0], item_rec[1]])) #sys_sys
for item, item_rec in zip(perms[3], perms_rec3):
edge_index4.append(torch.tensor([item_rec[0], item_rec[1]])) #sys_user
for item, item_rec in zip(perms[4], perms_rec4):
edge_index5.append(torch.tensor([item_rec[0], item_rec[1]])) #sys_user_sys
for item, item_rec in zip(perms[5], perms_rec5):
edge_index6.append(torch.tensor([item_rec[0], item_rec[1]])) #user_sys_user
node_features = torch.cat(node_features, dim=0).to(device) # [E, D_g]
edge_index = [torch.stack(edge_index1).t().contiguous().to(device),torch.stack(edge_index2).t().contiguous().to(device),torch.stack(edge_index3).t().contiguous().to(device),torch.stack(edge_index4).t().contiguous().to(device), torch.stack(edge_index5).t().contiguous().to(device), torch.stack(edge_index6).t().contiguous().to(device)] # [2, E]
edge_type = torch.stack(edge_type).to(device)
edge_type=edge_type.squeeze(-1)
return node_features, edge_index,edge_type
class ContextModel(nn.Module):
def __init__(self, base_model, batch_size, max_conver_num,device, context_model_type, head_num, model_variant):
super(ContextModel, self).__init__()
self.base_model=base_model
self.max_conver_num = max_conver_num
self.device = device
self.heads=head_num
self.context_model_type = context_model_type
in_hidden_dim = base_model.config.hidden_size
gcn_hidden_dim1 = 512
gcn_out_dim = 256
if ('nospk2pred' in self.context_model_type):
self.relations=2
else:
self.relations=4
if (model_variant == 'gcn'):
self.gcn = GCN(input_dim=in_hidden_dim, hidden_dim1=gcn_hidden_dim1,output_dim=gcn_out_dim,relations=self.relations,heads=self.heads).to(device)
self.task_heads = nn.ModuleList([nn.Sequential(nn.Linear(gcn_out_dim, hidden_size), nn.ReLU(), nn.Linear(hidden_size, 1),) for _ in range(5)])
elif (model_variant == 'gatv2'):
self.gcn = GAT(input_dim=in_hidden_dim, hidden_dim1=gcn_hidden_dim1,output_dim=gcn_out_dim,relations=self.relations,heads=self.heads).to(device)
self.task_heads = nn.ModuleList([nn.Sequential(nn.Linear(self.heads*gcn_hidden_dim1, hidden_size), nn.ReLU(), nn.Linear(hidden_size, 1),) for _ in range(5)])
elif (model_variant == 'rgcn'):
self.gcn = RGCN(input_dim=in_hidden_dim, hidden_dim1=gcn_hidden_dim1,output_dim=gcn_out_dim,relations=self.relations,heads=self.heads).to(device)
self.task_heads = nn.ModuleList([nn.Sequential(nn.Linear(gcn_out_dim, hidden_size), nn.ReLU(), nn.Linear(hidden_size, 1),) for _ in range(5)])
elif (model_variant == 'hcgnn22'):
self.gcn = GATv2GCN22(input_dim=in_hidden_dim, hidden_dim1=gcn_hidden_dim1,output_dim=gcn_out_dim,relations=self.relations,heads=self.heads).to(device)
self.task_heads = nn.ModuleList([nn.Sequential(nn.Linear(self.relations*gcn_out_dim, hidden_size), nn.ReLU(), nn.Linear(hidden_size, 1),) for _ in range(5)])
elif (model_variant == 'hcgnn3rel'):
self.gcn = GATv2GCN3REL(input_dim=in_hidden_dim, hidden_dim1=gcn_hidden_dim1,output_dim=gcn_out_dim,relations=self.relations,heads=self.heads).to(device)
self.task_heads = nn.ModuleList([nn.Sequential(nn.Linear((self.relations+1)*gcn_out_dim, hidden_size), nn.ReLU(), nn.Linear(hidden_size, 1),) for _ in range(5)])
else:
self.gcn = GATv2GCN(input_dim=in_hidden_dim, hidden_dim1=gcn_hidden_dim1,output_dim=gcn_out_dim,relations=self.relations,heads=self.heads).to(device)
self.task_heads = nn.ModuleList([nn.Sequential(nn.Linear(self.relations*gcn_out_dim, hidden_size), nn.ReLU(), nn.Linear(hidden_size, 1),) for _ in range(5)])
self.attention=ScaledDotProductAttention(gcn_out_dim)
def forward(self, input_ids, attention_mask, current_length):
input_id = input_ids.reshape(-1, input_ids.shape[-1]) #torch.Size([96, 64])
attention_mask = attention_mask.reshape(-1, attention_mask.shape[-1])
batch_embeddings=self.base_model(input_ids=input_id, attention_mask=attention_mask).pooler_output #torch.Size([192, 768])
batch_embeddings = batch_embeddings.reshape(input_ids.shape[0], -1, batch_embeddings.shape[-1]) #torch.Size([4, 48, 768])
if model_variant == 'rgcn':
features, edge_index,edge_type = batch_graphify_hgcn(batch_embeddings, current_length, self.device)
gcn_features = self.gcn(features, edge_index,edge_type)
elif model_variant == 'hcgnn3rel':
features, edge_index,edge_type = batch_graphify_hgcn(batch_embeddings, current_length, self.device)
gcn_features = self.gcn(features, edge_index,edge_type)
gcn_features, attn = self.attention(gcn_features, gcn_features, gcn_features)
else:
features, edge_index, edge_type = batch_graphify_hgcn(batch_embeddings, current_length, self.device)
gcn_features = self.gcn(features, edge_index)
gcn_features, attn = self.attention(gcn_features, gcn_features, gcn_features)
gcn_features = gcn_features.reshape(gcn_features.shape[0], -1)
regression_outputs = []
for task_head in self.task_heads:
regression_outputs.append(task_head(gcn_features))
return torch.cat(regression_outputs, dim=1)
def valid(model, valid_dataloader, criterion):
model.eval()
epoch_loss = 0
with torch.no_grad():
for batch in valid_dataloader:
loss, outputs, labels = cal_loss(batch, model, criterion, context_model_type)
epoch_loss += loss.item()
return epoch_loss/len(valid_dataloader)
def cal_loss(batch, model, criterion, context_model_type):
if ('gcn' in context_model_type):
input_ids = torch.stack(batch['input_ids'],dim=0).to(device).permute(1,0,2) # shape: [batch_size, max_conver_num, max_length]
attention_mask = torch.stack(batch['attention_mask'],dim=0).to(device).permute(1,0,2)
current_length=batch['current_length'].to(device)
labels = batch['labels'].to(device) # [batch_size, 5]
dialogue_labels = labels.repeat_interleave(current_length, dim=0)
model_outputs = model(input_ids, attention_mask, current_length)
if 'lastnode' in context_model_type:
loss = 0
outputs = []
accumulate_num = 0
for i in range(len(current_length)):
accumulate_num += current_length[i]
outputs.append(model_outputs[accumulate_num-1])
loss += criterion(model_outputs[accumulate_num-1], dialogue_labels[accumulate_num-1])
outputs = torch.stack(outputs, dim=0).to(device) # torch.stack
else:
outputs = []
accumulate_num = 0
loss = 0
for i in range(len(current_length)):
dialogue_loss = 0
accumulate_result = torch.zeros_like(model_outputs[0])
for j in range(current_length[i]):
if (j % 2 == 0):
accumulate_result += model_outputs[accumulate_num+j]
sentence_loss = criterion(model_outputs[i], dialogue_labels[i])
dialogue_loss += sentence_loss
accumulate_result = accumulate_result / (current_length[i]/2)
#accumulate_result = accumulate_result / (current_length[i])
outputs.append(accumulate_result)
accumulate_num += current_length[i]
dialogue_loss /= (current_length[i]/2)
loss += dialogue_loss
outputs = torch.stack(outputs, dim=0).to(device) # torch.stack
else:
input_ids = batch['input_ids'].cuda()
attention_mask = batch['attention_mask'].cuda()
labels = batch['labels'].cuda()
outputs = model(input_ids=input_ids, attention_mask=attention_mask)
loss = criterion(outputs[:, [i for i, flag in enumerate(multitask) if flag == 1]], labels[:, [i for i, flag in enumerate(multitask) if flag == 1]])
return loss, outputs, labels
def train(model, train_dataloader, valid_dataloader, criterion, patience, max_epoch):
model.cuda()
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
if (warmup_steps > 0):
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=warmup_steps, num_training_steps=max_epoch*len(train_dataloader))
best_loss = float('inf')
best_epoch = -1
for epoch in range(max_epoch):
model.train()
epoch_loss = 0
for batch in tqdm.tqdm(train_dataloader):
loss, outputs, labels = cal_loss(batch, model, criterion, context_model_type)
epoch_loss += loss.item()
optimizer.zero_grad()
if (warmup_steps > 0):
scheduler.step()
loss.backward()
optimizer.step()
#torch.save(model.state_dict(), os.path.join(model_folder, f'model_{epoch}.pt'))
print('epoch: {}, train loss: {}'.format(epoch, epoch_loss/len(train_dataloader)))
logging.info('epoch: {}'.format(epoch))
logging.info('train loss: {}'.format(epoch_loss/len(train_dataloader)))
valid_loss = valid(model, valid_dataloader, criterion)
print('valid loss: {}'.format(valid_loss))
logging.info('valid loss: {}'.format(valid_loss))
if valid_loss < best_loss:
best_loss = valid_loss
best_epoch = epoch
torch.save(model.state_dict(), os.path.join(model_folder, 'model.pt'))
else:
if epoch - best_epoch >= patience:
break
def eval_real_time(model, criterion, label_medians, test_file, tokenizer, max_sent, context):
if ('gcn' in context_model_type):
tmp_test_data = gene_data_from_csv(test_file, tokenizer, max_sent)
tmp_test_data = gene_context_data(tmp_test_data, pad_length, tokenizer)
tmp_test_dataset = ConversationDataset(tmp_test_data, tokenizer, max_length)
tmp_test_dataloader= DataLoader(tmp_test_dataset, batch_size=batch_size, shuffle=False)
tmp_res = evaluation(model, tmp_test_dataloader, criterion, label_medians)
else:
tmp_test_data = gene_data_from_csv(test_file, tokenizer, max_sent)
tmp_test_dataset = MultiTaskDataset(tmp_test_data, tokenizer, max_length)
tmp_test_dataloader= DataLoader(tmp_test_dataset, batch_size=batch_size, shuffle=False)
tmp_res = evaluation(model, tmp_test_dataloader, criterion, label_medians)
return tmp_res
def evaluation(model, test_dataloader, criterion, label_medians, ensemble=False):
if ensemble:
model_list = model
for m in model_list:
m.eval()
else:
model.eval()
epoch_loss = 0
epoch_labels = []
epoch_preds = []
with torch.no_grad():
for batch in test_dataloader:
if ensemble:
output_list = []
for m in model_list:
batch_clone = copy.deepcopy(batch)
loss, outputs, labels = cal_loss(batch_clone, m, criterion, context_model_type)
output_list.append(outputs.clone())
outputs = torch.stack(output_list, dim=0)
outputs = torch.mean(outputs, dim=0)
else:
loss, outputs, labels = cal_loss(batch, model, criterion, context_model_type)
epoch_loss += loss.item()
epoch_labels.append(labels.cpu().numpy())
epoch_preds.append(outputs.cpu().numpy())
avg_loss = epoch_loss/len(test_dataloader)
labels = np.concatenate(epoch_labels)
preds = np.concatenate(epoch_preds)
# pearson correlation
pearson_corr = []
pearson_p_values = []
spearman_corr = []
spearman_p_values = []
for i in range(preds.shape[1]):
labels_i = labels[:, i]
preds_i = preds[:, i]
pearson_corr_i, pearson_p_value_i = pearsonr(labels_i, preds_i)
spearman_corr_i, spearman_p_value_i = spearmanr(labels_i, preds_i)
pearson_corr.append(pearson_corr_i)
pearson_p_values.append(pearson_p_value_i)
spearman_corr.append(spearman_corr_i)
spearman_p_values.append(spearman_p_value_i)
# acc for using 0.5 as the threshold
labels_binary_05 = np.array([[1 if label >= 0.5 else 0 for i, label in enumerate(sample)] for sample in labels])
preds_binary_05 = np.array([[1 if pred >= 0.5 else 0 for i, pred in enumerate(sample)] for sample in preds])
acc_list_05 = []
balanced_acc_list_05 = []
for i in range(preds_binary_05.shape[1]):
acc_list_05.append(accuracy_score(labels_binary_05[:, i], preds_binary_05[:, i]))
balanced_acc_list_05.append(balanced_accuracy_score(labels_binary_05[:, i], preds_binary_05[:, i]))
# acc
labels_binary = np.array([[1 if label >= label_medians[i] else 0 for i, label in enumerate(sample)] for sample in labels])
preds_binary = np.array([[1 if pred >= label_medians[i] else 0 for i, pred in enumerate(sample)] for sample in preds])
acc_list = []
balanced_acc_list = []
for i in range(preds_binary.shape[1]):
acc_list.append(accuracy_score(labels_binary[:, i], preds_binary[:, i]))
balanced_acc_list.append(balanced_accuracy_score(labels_binary[:, i], preds_binary[:, i]))
return {
'loss': avg_loss,
'pearson_corr_list': pearson_corr,
'pearson_p_values': pearson_p_values,
'spearman_corr_list': spearman_corr,
'spearman_p_values': spearman_p_values,
'acc_list_05': acc_list_05,
'balanced_acc_list_05': balanced_acc_list_05,
'acc_list': acc_list,
'balanced_acc_list': balanced_acc_list
}
def print_res(res, name='test'):
print (res)
logging.info(f'-----------{name}---------------------')
logging.info('{} loss: {:.3f}'.format(name, res['loss']))
logging.info('pearson correlation: {}'.format([round(corr, 3) for corr in res['pearson_corr_list']]))
logging.info('pearson p value: {}'.format([round(p_value, 3) for p_value in res['pearson_p_values']]))
logging.info('spearman correlation: {}'.format([round(corr, 3) for corr in res['spearman_corr_list']]))
logging.info('spearman p value: {}'.format([round(p_value, 3) for p_value in res['spearman_p_values']]))
logging.info('acc using 0.5 as threshold: {}'.format([round(acc, 3) for acc in res['acc_list_05']]))
logging.info('avg acc using 0.5 as threshold: {:.3f}'.format(np.mean(res['acc_list_05'])))
logging.info('balanced acc using 0.5 as threshold: {}'.format([round(balanced_acc, 3) for balanced_acc in res['balanced_acc_list_05']]))
logging.info('avg balanced acc using 0.5 as threshold: {:.3f}'.format(np.mean(res['balanced_acc_list_05'])))
logging.info('acc: {}'.format([round(acc, 3) for acc in res['acc_list']]))
logging.info('avg acc: {:.3f}'.format(np.mean(res['acc_list'])))
logging.info('balanced acc: {}'.format([round(balanced_acc, 3) for balanced_acc in res['balanced_acc_list']]))
logging.info('avg balanced acc: {:.3f}'.format(np.mean(res['balanced_acc_list'])))
def main():
global pad_length
logging.info('base model: {}'.format(base_model_name))
logging.info('batch size: {}'.format(batch_size))
logging.info('max length: {}'.format(max_length))
logging.info('max epoch: {}'.format(max_epoch))
logging.info('patience: {}'.format(patience))
logging.info('learning rate: {}'.format(lr))
logging.info('hidden size: {}'.format(hidden_size))
logging.info('multitask: {}'.format(multitask_str))
logging.info('dropout: {}'.format(dropout))
logging.info('multilinear: {}'.format(multilinear))
logging.info('warmup steps: {}'.format(warmup_steps))
logging.info('criterion type: {}'.format(critertion_type))
logging.info('context: {}'.format(context))
logging.info('context model type: {}'.format(context_model_type))
logging.info('loading data...')
tokenizer=AutoTokenizer.from_pretrained(base_model_name)
tokenizer.add_tokens(['[SPK1]','[SPK2]'])
train_data = gene_data_from_csv(train_file, tokenizer)
label_medians = np.median(np.array([sample['labels'] for sample in train_data]), axis=0)
valid_data = gene_data_from_csv(valid_file, tokenizer)
test_data = gene_data_from_csv(test_file, tokenizer)
if ('gcn' in context_model_type):
pad_length = 0
for sample in train_data:
sentences_num = sample['text'].count('[SPK')
pad_length = max(pad_length, sentences_num)
train_data = gene_context_data(train_data, pad_length, tokenizer)
valid_data = gene_context_data(valid_data, pad_length, tokenizer)
test_data = gene_context_data(test_data, pad_length, tokenizer)
train_dataset = ConversationDataset(train_data, tokenizer, max_length)
valid_dataset = ConversationDataset(valid_data, tokenizer, max_length)
test_dataset = ConversationDataset(test_data, tokenizer, max_length)
else:
train_dataset = MultiTaskDataset(train_data, tokenizer, max_length)
valid_dataset = MultiTaskDataset(valid_data, tokenizer, max_length)
test_dataset = MultiTaskDataset(test_data, tokenizer, max_length)
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
valid_dataloader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False)
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
logging.info('initializing model...')
base_model = AutoModel.from_pretrained(base_model_name)
base_model.resize_token_embeddings(len(tokenizer))
if ('gcn' in context_model_type):
model = ContextModel(base_model, batch_size, pad_length, device, context_model_type, head_num, model_variant)
else:
model = MultiTaskModel(base_model=base_model, dropout_flag=dropout, multilinear_flag=multilinear)
model = DataParallel(model)
model.to(device)
if (critertion_type=='mse'):
criterion = nn.MSELoss()
elif (critertion_type=='mae'):
criterion = nn.L1Loss()
if (train_flag==1):
logging.info('start training...')
train(model, train_dataloader, valid_dataloader, criterion, patience, max_epoch)
if (test_flag==1):
logging.info('start evaluation...')
model.load_state_dict(torch.load(os.path.join(model_folder, 'model.pt')))
if (real_time_flag == 1):
for max_sent in [2,3,4,5,10]:
tmp_res = eval_real_time(model, criterion, label_medians, test_file, tokenizer, max_sent, context)
print_res(tmp_res, name=f'test_max_sent{max_sent}')
valid_res = evaluation(model, valid_dataloader, criterion, label_medians)
res = evaluation(model, test_dataloader, criterion, label_medians)
print_res(valid_res, 'valid')
print_res(res, 'test')
def eval_ensemble():
global pad_length
logging.info('base model: {}'.format(base_model_name))
logging.info('batch size: {}'.format(batch_size))
logging.info('max length: {}'.format(max_length))
logging.info('max epoch: {}'.format(max_epoch))
logging.info('patience: {}'.format(patience))
logging.info('learning rate: {}'.format(lr))
logging.info('hidden size: {}'.format(hidden_size))
logging.info('multitask: {}'.format(multitask_str))
logging.info('dropout: {}'.format(dropout))
logging.info('multilinear: {}'.format(multilinear))
logging.info('warmup steps: {}'.format(warmup_steps))
logging.info('criterion type: {}'.format(critertion_type))
logging.info('context: {}'.format(context))
logging.info('context model type: {}'.format(context_model_type))
logging.info('loading data...')
tokenizer=AutoTokenizer.from_pretrained(base_model_name)
#tokenizer.add_tokens(['[SPK1]','[SPK2]'])
train_data = gene_data_from_csv(train_file, tokenizer)
label_medians = np.median(np.array([sample['labels'] for sample in train_data]), axis=0)
test_data = gene_data_from_csv(test_file, tokenizer)
test_dataset = MultiTaskDataset(test_data, tokenizer, max_length)
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
logging.info('initializing model...')
base_model = AutoModel.from_pretrained(base_model_name)
#base_model.resize_token_embeddings(len(tokenizer))
logging.info('start evaluation...')
model_files = glob.glob(os.path.join(ensemble_model_folder, '*.pt'))[:2]
# sort
model_files = sorted(model_files)
print (model_files)
model_num = len(model_files)
model_list = []
for i in range(model_num):
if ('gcn' in context_model_type):
model = ContextModel(base_model, batch_size, pad_length, device, context_model_type, head_num, model_variant)
else:
model = MultiTaskModel(base_model=base_model, dropout_flag=dropout, multilinear_flag=multilinear)
model = DataParallel(model)
model.to(device)
model_list.append(model)
if (critertion_type=='mse'):
criterion = nn.MSELoss()
elif (critertion_type=='mae'):
criterion = nn.L1Loss()
for model, model_name in zip(model_list, model_files):
try:
model.load_state_dict(torch.load(model_name))
print (model_name, 'loaded')
except:
print (model_name, 'failed')
res = evaluation(model_list, test_dataloader, criterion, label_medians, ensemble=True)
print_res(res, 'test')
if __name__ == '__main__':
if not ensemble:
main()
else:
# no training only evaluation
eval_ensemble()