From cae32a2ee2004dfd74aad8c4439d4ec3939478b9 Mon Sep 17 00:00:00 2001 From: Rupak <63628559+rupakdas18@users.noreply.github.com> Date: Fri, 7 May 2021 17:28:12 -0500 Subject: [PATCH] Add files via upload --- EXPERIMENT.sh | 11 ++ Experimental Result.PNG | Bin 0 -> 53802 bytes INSTALL.sh | 18 ++ twitter_2017_taskA.py | 367 +++++++++++++++++++++++++++++++++++++++ twitter_2017_taskB.py | 373 ++++++++++++++++++++++++++++++++++++++++ twitter_2017_taskC.py | 366 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 1135 insertions(+) create mode 100644 EXPERIMENT.sh create mode 100644 Experimental Result.PNG create mode 100644 INSTALL.sh create mode 100644 twitter_2017_taskA.py create mode 100644 twitter_2017_taskB.py create mode 100644 twitter_2017_taskC.py diff --git a/EXPERIMENT.sh b/EXPERIMENT.sh new file mode 100644 index 0000000..de32e1c --- /dev/null +++ b/EXPERIMENT.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + + +python tweeter_2007_taskA.py +python tweeter_2017_taskB.py +python tweeter_2017_taskC.py + + +python baseline_taskA.py +python baseline_taskB.py +python baseline_taskC.py diff --git a/Experimental Result.PNG b/Experimental Result.PNG new file mode 100644 index 0000000000000000000000000000000000000000..8d2f9d835c334c902611d091a1cbc2462c50dcf4 GIT binary patch literal 53802 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 literal 0 HcmV?d00001 diff --git a/INSTALL.sh b/INSTALL.sh new file mode 100644 index 0000000..f371327 --- /dev/null +++ b/INSTALL.sh @@ -0,0 +1,18 @@ +#!/bin/bash + + +echo "****************** Installing packages ******************" + + +pip3 install matplotlib +pip3 install random2 +pip3 install numpy +pip3 install pandas +pip3 install torch +pip3 install tqdm +pip3 install torch==1.8.1+cu111 torchvision==0.9.1+cu111 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html +pip3 install transformers +pip3 install python-time +pip3 install matplotlib +pip3 install seaborn +pip3 install scikit-learn diff --git a/twitter_2017_taskA.py b/twitter_2017_taskA.py new file mode 100644 index 0000000..6b6a731 --- /dev/null +++ b/twitter_2017_taskA.py @@ -0,0 +1,367 @@ +#Install packages +import random +import torch +import numpy as np +import pandas as pd +from tqdm import tqdm +from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler +from transformers import BertTokenizer + +#Device Selection +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + +# Load the BERT tokenizer. +print('Loading BERT tokenizer...') +tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True) + + +# Load Dataset +train_data_df = pd.read_csv("data/twitter-2016train-A.txt", delimiter='\t', header=None, names=['id', 'label', 'tweet']) +val_data_df = pd.read_csv("data/twitter-2016devtest-A.txt", delimiter='\t', header=None, names=['id', 'label', 'tweet']) + + +train_tweet = train_data_df.tweet.values +y_train = train_data_df.label.values + +val_tweet = val_data_df.tweet.values +y_val = val_data_df.label.values + + +#Convert string classes into numeric classes +train_labels=[] +val_labels=[] +label_dict = {'negative':0, 'neutral':1, 'positive':2} + +for label in y_train: + train_labels.append(label_dict[label]) + +for label in y_val: + val_labels.append(label_dict[label]) + + +#Print length of Print and validation data +print(len(train_labels)) +print(len(val_labels)) + + +#Data Processing +def processdata(tweets,labels): + input_ids = [] + attention_masks = [] + for tweet in tweets: + encoded_dict = tokenizer.encode_plus( + tweet, # Sentence to encode. + add_special_tokens = True, # Add '[CLS]' and '[SEP]' + max_length = 64, # Pad & truncate all sentences. + pad_to_max_length = True, + return_attention_mask = True, # Construct attn. masks. + return_tensors = 'pt', # Return pytorch tensors. + ) + input_ids.append(encoded_dict['input_ids']) + attention_masks.append(encoded_dict['attention_mask']) + + input_ids = torch.cat(input_ids, dim=0) + attention_masks = torch.cat(attention_masks, dim=0) + labels = torch.tensor(labels) + return input_ids,attention_masks,labels + +#Process train and validation data +train_input_ids,train_attention_masks,train_labels = processdata(train_tweet,train_labels) +val_input_ids,val_attention_masks,val_labels = processdata(val_tweet,val_labels) + +# Convert into Tensordata +train_dataset = TensorDataset(train_input_ids, train_attention_masks, train_labels) +val_dataset = TensorDataset(val_input_ids, val_attention_masks, val_labels) + +# Create dataloader + +from torch.utils.data import DataLoader, RandomSampler, SequentialSampler +batch_size = 32 + +# Create the DataLoaders for our training and validation sets. +train_dataloader = DataLoader( + train_dataset, # The training samples. + sampler = RandomSampler(train_dataset), # Select batches randomly + batch_size = batch_size # Trains with this batch size. + ) + +# For validation the order doesn't matter, so we'll just read them sequentially. +validation_dataloader = DataLoader( + val_dataset, # The validation samples. + sampler = SequentialSampler(val_dataset), # Pull out batches sequentially. + batch_size = batch_size # Evaluate with this batch size. + ) + + +# Load BERT Model +from transformers import BertForSequenceClassification, AdamW, BertConfig + +# Load BertForSequenceClassification, the pretrained BERT model with a single +# linear classification layer on top. +model = BertForSequenceClassification.from_pretrained( + "bert-base-uncased", + num_labels = 3, # The number of output labels = 3 + + output_attentions = False, # Whether the model returns attentions weights. + output_hidden_states = False, # Whether the model returns all hidden-states. +) + +# Tell pytorch to run this model on the GPU. +model.cuda() + + +# Define Optimizer +optimizer = AdamW(model.parameters(), + lr = 2e-5, # args.learning_rate - default is 5e-5, our notebook had 2e-5 + eps = 1e-8 # args.adam_epsilon - default is 1e-8. + ) + +# Epchos and Scheduler +from transformers import get_linear_schedule_with_warmup + +# Number of training epochs. The BERT authors recommend between 2 and 4. +EPOCHS = 4 + +# Total number of training steps is [number of batches] x [number of epochs]. +# (Note that this is not the same as the number of training samples). +total_steps = len(train_dataloader) * EPOCHS + +# Create the learning rate scheduler. +scheduler = get_linear_schedule_with_warmup(optimizer, + num_warmup_steps = 0, # Default value in run_glue.py + num_training_steps = total_steps) + +# Accuracy Function +def accuracy(y_pred, y_test): + acc = (torch.log_softmax(y_pred, dim=1).argmax(dim=1) == y_test).sum().float() / float(y_test.size(0)) + return acc + + +# Time +import time +import datetime + +def format_time(elapsed): + ''' + Takes a time in seconds and returns a string hh:mm:ss + ''' + # Round to the nearest second. + elapsed_rounded = int(round((elapsed))) + + # Format as hh:mm:ss + return str(datetime.timedelta(seconds=elapsed_rounded)) + +# Train the Model +training_stats=[] +def train(model, train_loader, val_loader, optimizer,scheduler): + total_step = len(train_loader) + + for epoch in range(EPOCHS): + # Measure how long the training epoch takes. + train_start = time.time() + model.train() + + # Reset the total loss and accuracy for this epoch. + total_train_loss = 0 + total_train_acc = 0 + for batch_idx, (pair_token_ids, mask_ids, y) in enumerate(train_loader): + + # Unpack this training batch from our dataloader. + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + labels = y.to(device) + + #clear any previously calculated gradients before performing a backward pass + optimizer.zero_grad() + + #Get the loss and prediction + loss, prediction = model(pair_token_ids, + token_type_ids=None, + attention_mask=mask_ids, + labels=labels).values() + + acc = accuracy(prediction, labels) + + # Accumulate the training loss and accuracy over all of the batches so that we can + # calculate the average loss at the end + total_train_loss += loss.item() + total_train_acc += acc.item() + + # Perform a backward pass to calculate the gradients. + loss.backward() + + # Clip the norm of the gradients to 1.0. + # This is to help prevent the "exploding gradients" problem. + torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) + + # Update parameters and take a step using the computed gradient. + optimizer.step() + + # Update the learning rate. + scheduler.step() + + # Calculate the average accuracy and loss over all of the batches. + train_acc = total_train_acc/len(train_loader) + train_loss = total_train_loss/len(train_loader) + train_end = time.time() + + # Put the model in evaluation mode + model.eval() + + total_val_acc = 0 + total_val_loss = 0 + val_start = time.time() + with torch.no_grad(): + for batch_idx, (pair_token_ids, mask_ids, y) in enumerate(val_loader): + + #clear any previously calculated gradients before performing a backward pass + optimizer.zero_grad() + + # Unpack this validation batch from our dataloader. + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + labels = y.to(device) + + #Get the loss and prediction + loss, prediction = model(pair_token_ids, + token_type_ids=None, + attention_mask=mask_ids, + labels=labels).values() + + # Calculate the accuracy for this batch + acc = accuracy(prediction, labels) + + # Accumulate the validation loss and Accuracy + total_val_loss += loss.item() + total_val_acc += acc.item() + + # Calculate the average accuracy and loss over all of the batches. + val_acc = total_val_acc/len(val_loader) + val_loss = total_val_loss/len(val_loader) + + #end = time.time() + val_end = time.time() + hours, rem = divmod(val_end-train_start, 3600) + minutes, seconds = divmod(rem, 60) + + print(f'Epoch {epoch+1}: train_loss: {train_loss:.4f} train_acc: {train_acc:.4f} | val_loss: {val_loss:.4f} val_acc: {val_acc:.4f}') + print("{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds)) + + training_stats.append( + { + 'epoch': epoch + 1, + 'Training Loss': train_loss, + 'Valid. Loss': val_loss, + 'Valid. Accur.': val_acc, + 'Training Time': train_end-train_start, + 'Validation Time': val_end-val_start + } + ) + +train(model, train_dataloader, validation_dataloader, optimizer,scheduler) + + +# Training and validation loss visualization +import matplotlib.pyplot as plt +import seaborn as sns + +# Use plot styling from seaborn. +sns.set(style='darkgrid') + +# Increase the plot size and font size. +sns.set(font_scale=1.5) +plt.rcParams["figure.figsize"] = (12,6) + +# Plot the learning curve. +plt.plot(df_stats['Training Loss'], 'b-o', label="Training") +plt.plot(df_stats['Valid. Loss'], 'g-o', label="Validation") + +# Label the plot. +plt.title("Training & Validation Loss") +plt.xlabel("Epoch") +plt.ylabel("Loss") +plt.legend() +plt.xticks([1, 2, 3, 4]) + +plt.show() + + + +# Load the Test dataset into a pandas dataframe. +df = pd.read_csv("data/twitter-2016test-A.txt", delimiter='\t', header=None, names=['label', 'tweet','id']) + +# Report the number of sentences. +print('Number of test sentences: {:,}\n'.format(df.shape[0])) + +# Create sentence and label lists + +test_tweet = df.tweet.values +y_test = df.label.values + +input_ids = [] +attention_masks = [] + +labels=[] + +for label in y_test: + labels.append(label_dict[label]) + +# Process test data +input_ids,attention_masks,labels = processdata(test_tweet,labels) + +# Set the batch size. +batch_size = 32 + +# Create the DataLoader. +prediction_data = TensorDataset(input_ids, attention_masks, labels) +prediction_sampler = SequentialSampler(prediction_data) +prediction_dataloader = DataLoader(prediction_data, sampler=prediction_sampler, batch_size=batch_size) + + +# Test the accuracy +from sklearn import metrics +from sklearn.metrics import precision_recall_fscore_support +from sklearn.metrics import precision_score +from sklearn.metrics import recall_score + +def test(model,prediction_dataloader): + + total_test_acc = 0 + total_F1_Score = 0 + total_precision = 0 + total_recall = 0 + + for batch_idx, (pair_token_ids, mask_ids,y) in enumerate(prediction_dataloader): + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + labels = y.to(device) + + loss, prediction = model(pair_token_ids, + token_type_ids=None, + attention_mask=mask_ids, + labels=labels).values() + + + acc = accuracy(prediction, labels) + + f1 = metrics.f1_score(labels.cpu(), torch.argmax(prediction, -1).cpu(), labels=[0, 1, 2], average='macro') + precision = precision_score(labels.cpu(), torch.argmax(prediction, -1).cpu(),labels=[0, 1, 2], average='macro') + recall = recall_score(labels.cpu(), torch.argmax(prediction, -1).cpu(),labels=[0, 1, 2], average='macro') + + total_test_acc += acc.item() + total_F1_Score += f1 + total_precision += precision + total_recall += recall + + test_acc = total_test_acc/len(prediction_dataloader) + test_f1 = total_F1_Score/len(prediction_dataloader) + test_precision = total_precision/len(prediction_dataloader) + test_recall = total_recall/len(prediction_dataloader) + + + print(f'test_acc: {test_acc:.4f}') + print(f'f1 Score: {test_f1:.4f}') + print(f'precision: {test_precision:.4f}') + print(f'recall: {test_recall:.4f}') + +test(model,prediction_dataloader) diff --git a/twitter_2017_taskB.py b/twitter_2017_taskB.py new file mode 100644 index 0000000..b1947d2 --- /dev/null +++ b/twitter_2017_taskB.py @@ -0,0 +1,373 @@ +# Import libraries + +import random +import torch +import numpy as np +import pandas as pd +from tqdm import tqdm +from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler +from transformers import BertTokenizer + +# Device selection +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + +# Load the BERT tokenizer. +print('Loading BERT tokenizer...') +tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True) + + +# Load dataset +train_data_df = pd.read_csv("data/twitter-2016train-BD.txt", delimiter='\t', header=None, names=['id','topic','label', 'tweet']) +val_data_df = pd.read_csv("data/twitter-2016devtest-BD.txt", delimiter='\t', header=None, names=['id','topic','label', 'tweet']) + +train_tweet = train_data_df.tweet.values +train_topic = train_data_df.topic.values +y_train = train_data_df.label.values + +val_tweet = val_data_df.tweet.values +val_topic = val_data_df.topic.values +y_val = val_data_df.label.values + + +# Convert string classes into numeric classes +train_labels=[] +val_labels=[] +label_dict = {'negative':0,'positive':1} + +for label in y_train: + train_labels.append(label_dict[label]) + +for label in y_val: + val_labels.append(label_dict[label]) + + +# Print length of train and validation data +print(len(train_labels)) +print(len(val_labels)) + + +# Data Processing +def processdata(topics,tweets,labels): + input_ids = [] + attention_masks = [] + token_type_ids = [] + for topic, tweet in zip(topics,tweets): + encoded_dict = tokenizer.encode_plus(topic, + tweet, # Sentence to encode. + add_special_tokens = True, # Add '[CLS]' and '[SEP]' + max_length = 64, # Pad & truncate all sentences. + pad_to_max_length = True, + return_attention_mask = True, # Construct attn. masks. + return_tensors = 'pt', # Return pytorch tensors. + return_token_type_ids = True, + ) + input_ids.append(encoded_dict['input_ids']) + attention_masks.append(encoded_dict['attention_mask']) + token_type_ids.append(encoded_dict['token_type_ids']) + + input_ids = torch.cat(input_ids, dim=0) + attention_masks = torch.cat(attention_masks, dim=0) + token_type_ids = torch.cat(token_type_ids,dim=0) + labels = torch.tensor(labels) + return input_ids,attention_masks,token_type_ids,labels + + +train_input_ids,train_attention_masks,train_token_type_ids,train_labels = processdata(train_topic,train_tweet,train_labels) +val_input_ids,val_attention_masks,val_token_type_ids,val_labels = processdata(val_topic,val_tweet,val_labels) + + +# Convert into TensorData +train_dataset = TensorDataset(train_input_ids, train_attention_masks,train_token_type_ids,train_labels) +val_dataset = TensorDataset(val_input_ids, val_attention_masks,val_token_type_ids,val_labels) + + +# Create dataLoader +from torch.utils.data import DataLoader, RandomSampler, SequentialSampler +batch_size = 32 + +# Create the DataLoaders for our training and validation sets. +train_dataloader = DataLoader( + train_dataset, # The training samples. + sampler = RandomSampler(train_dataset), # Select batches randomly + batch_size = batch_size # Trains with this batch size. + ) + +# For validation the order doesn't matter, so we'll just read them sequentially. +validation_dataloader = DataLoader( + val_dataset, # The validation samples. + sampler = SequentialSampler(val_dataset), # Pull out batches sequentially. + batch_size = batch_size # Evaluate with this batch size. + ) + +# Create DataLoader +from transformers import BertForSequenceClassification, AdamW, BertConfig + +# Load BertForSequenceClassification, the pretrained BERT model with a single +# linear classification layer on top. +model = BertForSequenceClassification.from_pretrained( + "bert-base-uncased", + num_labels = 2, # The number of output labels = 2 + + output_attentions = False, # Whether the model returns attentions weights. + output_hidden_states = False, # Whether the model returns all hidden-states. +) + +# Tell pytorch to run this model on the GPU. +model.cuda() + + +# Optimizer design +optimizer = AdamW(model.parameters(), + lr = 2e-5, # args.learning_rate - default is 5e-5, our notebook had 2e-5 + eps = 1e-8 # args.adam_epsilon - default is 1e-8. + ) + +# Epochs and scheduler design +from transformers import get_linear_schedule_with_warmup + +EPOCHS = 4 + +# Total number of training steps is [number of batches] x [number of epochs]. +# (Note that this is not the same as the number of training samples). +total_steps = len(train_dataloader) * EPOCHS + +# Create the learning rate scheduler. +scheduler = get_linear_schedule_with_warmup(optimizer, + num_warmup_steps = 0, # Default value in run_glue.py + num_training_steps = total_steps) + +# Accuracy Function +def accuracy(y_pred, y_test): + acc = (torch.log_softmax(y_pred, dim=1).argmax(dim=1) == y_test).sum().float() / float(y_test.size(0)) + return acc + + +import time +import datetime + +def format_time(elapsed): + ''' + Takes a time in seconds and returns a string hh:mm:ss + ''' + # Round to the nearest second. + elapsed_rounded = int(round((elapsed))) + + # Format as hh:mm:ss + return str(datetime.timedelta(seconds=elapsed_rounded)) + + +# Train model +training_stats=[] +def train(model, train_loader, val_loader, optimizer,scheduler): + total_step = len(train_loader) + + for epoch in range(EPOCHS): + # Measure how long the training epoch takes. + train_start = time.time() + model.train() + + # Reset the total loss and accuracy for this epoch. + total_train_loss = 0 + total_train_acc = 0 + for batch_idx, (pair_token_ids, mask_ids,seg_ids, y) in enumerate(train_loader): + + # Unpack this training batch from our dataloader. + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + seg_ids = seg_ids.to(device) + labels = y.to(device) + + #clear any previously calculated gradients before performing a backward pass + optimizer.zero_grad() + + #Get the loss and prediction + loss, prediction = model(pair_token_ids, + token_type_ids=seg_ids, + attention_mask=mask_ids, + labels=labels).values() + + acc = accuracy(prediction, labels) + + # Accumulate the training loss and accuracy over all of the batches so that we can + # calculate the average loss at the end + total_train_loss += loss.item() + total_train_acc += acc.item() + + # Perform a backward pass to calculate the gradients. + loss.backward() + + # Clip the norm of the gradients to 1.0. + # This is to help prevent the "exploding gradients" problem. + torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) + + # Update parameters and take a step using the computed gradient. + optimizer.step() + + # Update the learning rate. + scheduler.step() + + # Calculate the average accuracy and loss over all of the batches. + train_acc = total_train_acc/len(train_loader) + train_loss = total_train_loss/len(train_loader) + train_end = time.time() + + # Put the model in evaluation mode + model.eval() + + total_val_acc = 0 + total_val_loss = 0 + val_start = time.time() + with torch.no_grad(): + for batch_idx, (pair_token_ids, mask_ids,seg_ids,y) in enumerate(val_loader): + + #clear any previously calculated gradients before performing a backward pass + optimizer.zero_grad() + + # Unpack this validation batch from our dataloader. + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + seg_ids = seg_ids.to(device) + labels = y.to(device) + + #Get the loss and prediction + loss, prediction = model(pair_token_ids, + token_type_ids=seg_ids, + attention_mask=mask_ids, + labels=labels).values() + + # Calculate the accuracy for this batch + acc = accuracy(prediction, labels) + + # Accumulate the validation loss and Accuracy + total_val_loss += loss.item() + total_val_acc += acc.item() + + # Calculate the average accuracy and loss over all of the batches. + val_acc = total_val_acc/len(val_loader) + val_loss = total_val_loss/len(val_loader) + + #end = time.time() + val_end = time.time() + hours, rem = divmod(val_end-train_start, 3600) + minutes, seconds = divmod(rem, 60) + + print(f'Epoch {epoch+1}: train_loss: {train_loss:.4f} train_acc: {train_acc:.4f} | val_loss: {val_loss:.4f} val_acc: {val_acc:.4f}') + print("{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds)) + + training_stats.append( + { + 'epoch': epoch + 1, + 'Training Loss': train_loss, + 'Valid. Loss': val_loss, + 'Valid. Accur.': val_acc, + 'Training Time': train_end-train_start, + 'Validation Time': val_end-val_start + } + ) + +train(model, train_dataloader, validation_dataloader, optimizer,scheduler) + + +# Training and validation loss visualization +import matplotlib.pyplot as plt +import seaborn as sns + +# Use plot styling from seaborn. +sns.set(style='darkgrid') + +# Increase the plot size and font size. +sns.set(font_scale=1.5) +plt.rcParams["figure.figsize"] = (12,6) + +# Plot the learning curve. +plt.plot(df_stats['Training Loss'], 'b-o', label="Training") +plt.plot(df_stats['Valid. Loss'], 'g-o', label="Validation") + +# Label the plot. +plt.title("Training & Validation Loss") +plt.xlabel("Epoch") +plt.ylabel("Loss") +plt.legend() +plt.xticks([1, 2, 3, 4]) + +plt.show() + + +# Load test data +# Load the dataset into a pandas dataframe. +df = pd.read_csv("data/twitter-2016test-BD.txt", delimiter='\t', header=None, names=['topic','label','tweet','id']) + +# Report the number of sentences. +print('Number of test sentences: {:,}\n'.format(df.shape[0])) + +# Create sentence and label lists + +test_tweet = df.tweet.values +test_topic = df.topic.values +y_test = df.label.values + +test_labels=[] +label_dict = {'negative':0,'positive':1} + +for label in y_test: + test_labels.append(label_dict[label]) + +input_ids,attention_masks,token_type_ids,labels = processdata(test_topic,test_tweet,test_labels) + +# Set the batch size. +batch_size = 32 + +# Create the DataLoader. +prediction_data = TensorDataset(input_ids, attention_masks,token_type_ids,labels) +prediction_sampler = SequentialSampler(prediction_data) +prediction_dataloader = DataLoader(prediction_data, sampler=prediction_sampler, batch_size=batch_size) + + +#Find the Accuracy +from sklearn import metrics +from sklearn.metrics import precision_recall_fscore_support +from sklearn.metrics import precision_score +from sklearn.metrics import recall_score + +def test(model,prediction_dataloader): + + total_test_acc = 0 + total_F1_Score = 0 + total_precision = 0 + total_recall = 0 + + for batch_idx, (pair_token_ids, mask_ids,token_type_ids,y) in enumerate(prediction_dataloader): + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + labels = y.to(device) + token_type_ids=token_type_ids.to(device) + + loss, prediction = model(pair_token_ids, + token_type_ids=token_type_ids, + attention_mask=mask_ids, + labels=labels).values() + + + acc = accuracy(prediction, labels) + + f1 = metrics.f1_score(labels.cpu(), torch.argmax(prediction, -1).cpu(), average='binary') + precision = precision_score(labels.cpu(), torch.argmax(prediction, -1).cpu(), average="binary") + recall = recall_score(labels.cpu(), torch.argmax(prediction, -1).cpu(), average="binary") + + total_test_acc += acc.item() + total_F1_Score += f1 + total_precision += precision + total_recall += recall + + test_acc = total_test_acc/len(prediction_dataloader) + test_f1 = total_F1_Score/len(prediction_dataloader) + test_precision = total_precision/len(prediction_dataloader) + test_recall = total_recall/len(prediction_dataloader) + + + print(f'test_acc: {test_acc:.4f}') + print(f'f1 Score: {test_f1:.4f}') + print(f'precision: {test_precision:.4f}') + print(f'recall: {test_recall:.4f}') + +test(model,prediction_dataloader) diff --git a/twitter_2017_taskC.py b/twitter_2017_taskC.py new file mode 100644 index 0000000..f177481 --- /dev/null +++ b/twitter_2017_taskC.py @@ -0,0 +1,366 @@ +# Import libraries + +import random +import torch +import numpy as np +import pandas as pd +from tqdm import tqdm +from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler +from transformers import BertTokenizer + + +# Select device +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + +# Load the BERT tokenizer. +print('Loading BERT tokenizer...') +tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True) + +# Load Test and Validation data +train_data_df = pd.read_csv("data/twitter-2016train-CE.txt", delimiter='\t', header=None, names=['id','topic','label', 'tweet']) +val_data_df = pd.read_csv("data/twitter-2016devtest-CE.txt", delimiter='\t', header=None, names=['id','topic','label', 'tweet']) + +train_tweet = train_data_df.tweet.values +train_topic = train_data_df.topic.values +y_train = train_data_df.label.values + +val_tweet = val_data_df.tweet.values +val_topic = val_data_df.topic.values +y_val = val_data_df.label.values + +# Convert string classes into numeric classee +train_labels=[] +val_labels=[] +label_dict = {-2:0,-1:1,0:2,1:3,2:4} + +for label in y_train: + train_labels.append(label_dict[label]) + +for label in y_val: + val_labels.append(label_dict[label]) + +# Train and validation data size +print(len(train_tweet)) +print(len(val_tweet)) + +# Data Processing +def processdata(topics,tweets,labels): + input_ids = [] + attention_masks = [] + token_type_ids = [] + for topic, tweet in zip(topics,tweets): + encoded_dict = tokenizer.encode_plus(topic, + tweet, # Sentence to encode. + add_special_tokens = True, # Add '[CLS]' and '[SEP]' + max_length = 64, # Pad & truncate all sentences. + pad_to_max_length = True, + return_attention_mask = True, # Construct attn. masks. + return_tensors = 'pt', # Return pytorch tensors. + return_token_type_ids = True, + ) + input_ids.append(encoded_dict['input_ids']) + attention_masks.append(encoded_dict['attention_mask']) + token_type_ids.append(encoded_dict['token_type_ids']) + + input_ids = torch.cat(input_ids, dim=0) + attention_masks = torch.cat(attention_masks, dim=0) + token_type_ids = torch.cat(token_type_ids,dim=0) + labels = torch.tensor(labels) + return input_ids,attention_masks,token_type_ids,labels + +train_input_ids,train_attention_masks,train_token_type_ids,train_labels = processdata(train_topic,train_tweet,train_labels) +val_input_ids,val_attention_masks,val_token_type_ids,val_labels = processdata(val_topic,val_tweet,val_labels) + + +# Convert to TensorData +train_dataset = TensorDataset(train_input_ids, train_attention_masks,train_token_type_ids,train_labels) +val_dataset = TensorDataset(val_input_ids, val_attention_masks,val_token_type_ids,val_labels) + +# Create DataLoader +from torch.utils.data import DataLoader, RandomSampler, SequentialSampler +batch_size = 32 + +# Create the DataLoaders for our training and validation sets. +train_dataloader = DataLoader( + train_dataset, # The training samples. + sampler = RandomSampler(train_dataset), # Select batches randomly + batch_size = batch_size # Trains with this batch size. + ) + +# For validation the order doesn't matter, so we'll just read them sequentially. +validation_dataloader = DataLoader( + val_dataset, # The validation samples. + sampler = SequentialSampler(val_dataset), # Pull out batches sequentially. + batch_size = batch_size # Evaluate with this batch size. + ) + + +# Load BERT Model +from transformers import BertForSequenceClassification, AdamW, BertConfig + +# Load BertForSequenceClassification, the pretrained BERT model with a single +# linear classification layer on top. +model = BertForSequenceClassification.from_pretrained( + "bert-base-uncased", + num_labels = 5, # The number of output labels = 5 + + output_attentions = False, # Whether the model returns attentions weights. + output_hidden_states = False, # Whether the model returns all hidden-states. +) + +# Tell pytorch to run this model on the GPU. +model.cuda() + + + +# Define Optimizer +optimizer = AdamW(model.parameters(), + lr = 2e-5, # args.learning_rate - default is 5e-5, our notebook had 2e-5 + eps = 1e-8 # args.adam_epsilon - default is 1e-8. + ) + +# Epochs and Scheduler +from transformers import get_linear_schedule_with_warmup + +# Number of training epochs. The BERT authors recommend between 2 and 4. +EPOCHS = 4 + +# Total number of training steps is [number of batches] x [number of epochs]. +# (Note that this is not the same as the number of training samples). +total_steps = len(train_dataloader) * EPOCHS + +# Create the learning rate scheduler. +scheduler = get_linear_schedule_with_warmup(optimizer, + num_warmup_steps = 0, # Default value in run_glue.py + num_training_steps = total_steps) + + +# Accuracy function +def accuracy(y_pred, y_test): + acc = (torch.log_softmax(y_pred, dim=1).argmax(dim=1) == y_test).sum().float() / float(y_test.size(0)) + return acc + + +import time +import datetime + +def format_time(elapsed): + ''' + Takes a time in seconds and returns a string hh:mm:ss + ''' + # Round to the nearest second. + elapsed_rounded = int(round((elapsed))) + + # Format as hh:mm:ss + return str(datetime.timedelta(seconds=elapsed_rounded)) + + +# Train Model +training_stats=[] +def train(model, train_loader, val_loader, optimizer,scheduler): + total_step = len(train_loader) + + for epoch in range(EPOCHS): + # Measure how long the training epoch takes. + train_start = time.time() + model.train() + + # Reset the total loss and accuracy for this epoch. + total_train_loss = 0 + total_train_acc = 0 + for batch_idx, (pair_token_ids, mask_ids,seg_ids, y) in enumerate(train_loader): + + # Unpack this training batch from our dataloader. + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + seg_ids = seg_ids.to(device) + labels = y.to(device) + + #clear any previously calculated gradients before performing a backward pass + optimizer.zero_grad() + + #Get the loss and prediction + loss, prediction = model(pair_token_ids, + token_type_ids=seg_ids, + attention_mask=mask_ids, + labels=labels).values() + + acc = accuracy(prediction, labels) + + # Accumulate the training loss and accuracy over all of the batches so that we can + # calculate the average loss at the end + total_train_loss += loss.item() + total_train_acc += acc.item() + + # Perform a backward pass to calculate the gradients. + loss.backward() + + # Clip the norm of the gradients to 1.0. + # This is to help prevent the "exploding gradients" problem. + torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) + + # Update parameters and take a step using the computed gradient. + optimizer.step() + + # Update the learning rate. + scheduler.step() + + # Calculate the average accuracy and loss over all of the batches. + train_acc = total_train_acc/len(train_loader) + train_loss = total_train_loss/len(train_loader) + train_end = time.time() + + # Put the model in evaluation mode + model.eval() + + total_val_acc = 0 + total_val_loss = 0 + val_start = time.time() + with torch.no_grad(): + for batch_idx, (pair_token_ids, mask_ids,seg_ids,y) in enumerate(val_loader): + + #clear any previously calculated gradients before performing a backward pass + optimizer.zero_grad() + + # Unpack this validation batch from our dataloader. + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + seg_ids = seg_ids.to(device) + labels = y.to(device) + + #Get the loss and prediction + loss, prediction = model(pair_token_ids, + token_type_ids=seg_ids, + attention_mask=mask_ids, + labels=labels).values() + + # Calculate the accuracy for this batch + acc = accuracy(prediction, labels) + + # Accumulate the validation loss and Accuracy + total_val_loss += loss.item() + total_val_acc += acc.item() + + # Calculate the average accuracy and loss over all of the batches. + val_acc = total_val_acc/len(val_loader) + val_loss = total_val_loss/len(val_loader) + + #end = time.time() + val_end = time.time() + hours, rem = divmod(val_end-train_start, 3600) + minutes, seconds = divmod(rem, 60) + + print(f'Epoch {epoch+1}: train_loss: {train_loss:.4f} train_acc: {train_acc:.4f} | val_loss: {val_loss:.4f} val_acc: {val_acc:.4f}') + print("{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds)) + + training_stats.append( + { + 'epoch': epoch + 1, + 'Training Loss': train_loss, + 'Valid. Loss': val_loss, + 'Valid. Accur.': val_acc, + 'Training Time': train_end-train_start, + 'Validation Time': val_end-val_start + } + ) + +train(model, train_dataloader, validation_dataloader, optimizer,scheduler) + +# Visualize the training and validation loss +import matplotlib.pyplot as plt +import seaborn as sns + +# Use plot styling from seaborn. +sns.set(style='darkgrid') + +# Increase the plot size and font size. +sns.set(font_scale=1.5) +plt.rcParams["figure.figsize"] = (12,6) + +# Plot the learning curve. +plt.plot(df_stats['Training Loss'], 'b-o', label="Training") +plt.plot(df_stats['Valid. Loss'], 'g-o', label="Validation") + +# Label the plot. +plt.title("Training & Validation Loss") +plt.xlabel("Epoch") +plt.ylabel("Loss") +plt.legend() +plt.xticks([1, 2, 3, 4]) + +plt.show() + + +# Load Test data +# Load the dataset into a pandas dataframe. +df = pd.read_csv("data/twitter-2016test-CE.txt", delimiter='\t', header=None, names=['id','topic','label','tweet']) + +# Report the number of sentences. +print('Number of test sentences: {:,}\n'.format(df.shape[0])) + +# Create sentence and label lists + +y_test = df.label.values +test_topic = df.topic.values +test_tweet = df.tweet.values + + +labels=[] +for label in y_test: + labels.append(label_dict[label]) + +input_ids,attention_masks,token_type_ids,labels = processdata(test_topic,test_tweet,labels) + +# Create the DataLoader. +prediction_data = TensorDataset(input_ids, attention_masks,token_type_ids,labels) +prediction_sampler = SequentialSampler(prediction_data) +prediction_dataloader = DataLoader(prediction_data, sampler=prediction_sampler, batch_size=batch_size) + +from sklearn import metrics +from sklearn.metrics import precision_recall_fscore_support +from sklearn.metrics import precision_score +from sklearn.metrics import recall_score + +#Test data preposessing +def test(model,prediction_dataloader): + + total_test_acc = 0 + total_F1_Score = 0 + total_precision = 0 + total_recall = 0 + + for batch_idx, (pair_token_ids, mask_ids,token_type_ids,y) in enumerate(prediction_dataloader): + pair_token_ids = pair_token_ids.to(device) + mask_ids = mask_ids.to(device) + labels = y.to(device) + token_type_ids=token_type_ids.to(device) + + loss, prediction = model(pair_token_ids, + token_type_ids=token_type_ids, + attention_mask=mask_ids, + labels=labels).values() + + + acc = accuracy(prediction, labels) + + f1 = metrics.f1_score(labels.cpu(), torch.argmax(prediction, -1).cpu(), labels=[0, 1, 2, 3, 4], average='weighted') + precision = precision_score(labels.cpu(), torch.argmax(prediction, -1).cpu(),labels=[0, 1, 2, 3, 4], average='weighted') + recall = recall_score(labels.cpu(), torch.argmax(prediction, -1).cpu(),labels=[0, 1, 2, 3, 4], average='weighted') + + total_test_acc += acc.item() + total_F1_Score += f1 + total_precision += precision + total_recall += recall + + test_acc = total_test_acc/len(prediction_dataloader) + test_f1 = total_F1_Score/len(prediction_dataloader) + test_precision = total_precision/len(prediction_dataloader) + test_recall = total_recall/len(prediction_dataloader) + + + print(f'test_acc: {test_acc:.4f}') + print(f'f1 Score: {test_f1:.4f}') + print(f'precision: {test_precision:.4f}') + print(f'recall: {test_recall:.4f}') + +test(model,prediction_dataloader)