PIPELINE

  1. Imported the cleaned data.
  2. Balance the training set.
  3. Load the text input.
  4. Tokenize text input and build attention masks for BERT.
  5. Load image input.
  6. Build text/image dataloaders.
  7. Define the model class using the BERT and ResNext model.
  8. Train the model.
  9. Generate training graphs.

NOTE :

  • We combine a pretrained BERT and ResNext model and then combine their representations to classify the overall sentiment.
  • We change the input/output layers of the above models to match them with the dataset.
  • Additional fully connected layers are added after the fusion.
  • Most of the preprocessing (except text) is done in the previous notebook.
  • This notebook focuses on building the model and training it.

PRELIMINARY

In [ ]:
# from IPython.core.display import display, HTML
# display(HTML("<style>.container { width:98% !important; }</style>"))
In [0]:
'''GOOGLE COLAB STUFF'''

import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
n_gpu = torch.cuda.device_count()
torch.cuda.get_device_name(0)

from google.colab import drive
drive.mount('/content/drive', force_remount=False)
Out[0]:
'Tesla P100-PCIE-16GB'
In [0]:
'''SETTING UP THE DATASET'''

import os
working_dir = '/content/drive/My Drive/NNFL_eval'
os.chdir(working_dir)

# !cp data.zip /content/
# !unzip -d /content /content/data.zip
# assert(len(os.listdir('/content/data_7000_clean/data_7000')) == 7001)
In [0]:
os.chdir("/content/drive/My Drive/NNFL_eval/shared_dump")
In [0]:
!tar xvzf submit.tar.gz
./
./Inference notebooks/
./Inference notebooks/model-6.1_epochs=3_modelP
./Inference notebooks/modeloffensive(2).h5
./Inference notebooks/tokenizer.pickle
./Inference notebooks/Embedding.npy
./Inference notebooks/CNN_Sentiment.h5
./Inference notebooks/Submission.ipynb
./FinalReport.pdf
In [0]:
# !zip -r data.zip ./data_7000_clean  
In [ ]:
'''GET THE PRETRAINED BERT MODEL'''

!pip3 install pytorch-pretrained-bert pytorch-nlp
In [ ]:
'''IMPORTS'''

import torch
from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler
from keras.preprocessing.sequence import pad_sequences
from sklearn.model_selection import train_test_split
from pytorch_pretrained_bert import BertTokenizer, BertConfig
from pytorch_pretrained_bert import BertAdam, BertForSequenceClassification
from tqdm import tqdm, trange, tqdm_notebook
import pandas as pd
import io
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import f1_score
import os
import time

%matplotlib inline
In [0]:
train = pd.read_csv("final4.csv")
train
Out[0]:
fileName text Hilariousness Intentions Offensiveness isMotivational Sentiment
0 10_year_10-year-challenge_1547788782.jpeg The best of #10 YearChallenge! Completed in le... 0 0 0 1 4
1 10_year_10yearchallenge-5c75f8b946e0fb0001edc7... Sam Thorne @Strippin ( Follow Follow Saw every... 2 1 0 0 3
2 10_year_10-year-challenge-sweet-dee-edition-40... 10 Year Challenge - Sweet Dee Edition 2 2 2 1 3
3 10_year_10-year-challenge-with-no-filter-47-hi... 10 YEAR CHALLENGE WITH NO FILTER 47 Hilarious ... 3 3 2 0 2
4 10_year_10-years-challenge.jpg What if the "10 Year Challenge" was created by... 3 0 1 0 4
... ... ... ... ... ... ... ...
6994 zeck_y5txrpi0sehz.jpg That's a nice opinion you have there. Sure wou... 2 0 2 1 3
6995 zeck_YOU-WON-ZUCKERBERG-IS-GIVING-YOU-THE-MONE... YOU WON.ZUCKERBERG IS GIVING YOU THE MONEY! AN... 2 2 2 0 3
6996 zeck_zf5we.jpg USES TWITTER TO ASK THE INVENTOR OF FACEBOOK F... 0 1 0 0 4
6997 zeck_zuckerberg.jpeg THE MORE OF YOUR DATA I GATHER THE MORE I UNDE... 0 0 0 0 2
6998 zeck_zuckerberg-memes-fairly-odd-parents-1.jpg how can I delete Facebook without Mark Zuckerb... 0 1 0 0 2

6999 rows × 7 columns

In [0]:
'''BALANCING THE TRAINING SET'''

train_df, valid_df = train_test_split(train[:], test_size=0.1, random_state=0, shuffle=True)
label_class = 'isMotivational'

while len(train_df[train_df[label_class] == 1]) < len(train_df[train_df[label_class] == 0]):
    random = train_df[train_df[label_class] == 1].sample()
    train_df = train_df.append(random, ignore_index = True)

# while len(valid_df[valid_df[label_class] == 1]) < len(valid_df[valid_df[label_class] == 0]):
#     random = valid_df[valid_df[label_class] == 1].sample()
#     valid_df = valid_df.append(random, ignore_index = True)

train_df = train_df.sample(frac=1).reset_index(drop=True)
valid_df = valid_df.sample(frac=1).reset_index(drop=True)

_, train_class_counts = np.unique(train_df[label_class], return_counts=True)
_, valid_class_counts = np.unique(valid_df[label_class], return_counts=True)

print(train_class_counts, valid_class_counts)
[4085 4085] [445 255]
In [0]:
text_train = train_df.text
imageNames_train = train_df.fileName
labels_train = train_df[label_class]

text_valid = valid_df.text
imageNames_valid = valid_df.fileName
labels_valid = valid_df[label_class]

TEXT INPUT

  • Cleaning the raw text data.
  • Removing twitter feed data that has been wrongly picked up by OCR.
  • Tokenizing and building attention masks for BERT.
In [ ]:
import nltk
import spacy
import string
import re
nltk.download('stopwords')
nltk.download('punkt')
nltk.download('wordnet')
from nltk.corpus import stopwords 
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer 
from nltk.tokenize import word_tokenize 
In [ ]:
'''DEFINING CLEANING FUNCTIONS'''

def text_lowercase(text): 
    return text.lower() 

def remove_numbers(text): 
    result = re.sub(r'\d+', '', text) 
    return result 

def remove_punctuation(word_array):
    output_word_array = []
    
    for word in word_array:
        translator = str.maketrans('', '', string.punctuation) 
        clean_word = word.translate(translator)
        if(clean_word != ''): output_word_array.append(clean_word)
    
    return output_word_array


### CUSTOM word preprocessing functions
### Domain specific

# Handle website names.
# net, com, meme**, in, memecenter,  
# Remove only the words containg '.com', '.co', '.net' ?
# Remove all words containg '.' except when dot is last character.
# Do BEFORE removing punctuation.
def remove_word_containing_dot(text_arr):
    clean_text_arr = []
    
    for word in text_arr:
        if(word.find('.') == -1 or word.find('.') == len(word)-1):
            clean_text_arr.append(word)
    
    return clean_text_arr

# Handle twitter stuff.
# Remove connon words.
# Remove month names.
def remove_twitter(text_arr):
    banned_words = ["retweets", "likes", "k", "pm", "follow"]
    months = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"]
    
    conc_remove_words = banned_words + months
    # print(conc_remove_words)
    
    clean_text_arr = []
        
    for word in text_arr:
        if(word not in conc_remove_words):
            clean_text_arr.append(word)
    
    return clean_text_arr

# print(remove_word_containing_dot("Me a memee text. SnoopyMems.com. Here more meme text. AnotherWebsite.net".split()))
# print(remove_twitter("Kudus to @narendramodi ji 8:05 PM - 16 Jan 2019 from Mumbai, India".split()))
# TODO : Dont remove 2009, 2019 as they actually contain meme context while removing numbers.
In [ ]:
'''COMBINE ALL THE ABOVE FUNCTIONS'''

def clean_text(input_text):

    string = text_lowercase(input_text)
    string = remove_numbers(string)
    text_arr = string.split()
    
    text_arr = remove_word_containing_dot(text_arr)
    text_arr = remove_punctuation(text_arr)
    text_arr = remove_twitter(text_arr)
    string = ' '.join(x for x in text_arr) 
    return string

# print(clean_text("Hey, did you know that 4 the summer Break is coming? Amazing right !! It's only 5 more days !!"))
# print(clean_text("Me a memee text. SnoopyMems.com. Here more meme text. AnotherWebsite.net"))
# print(clean_text("Kudus to @narendramodi ji 8:05 PM - 16 Jan 2019 from Mumbai, India"))
# print(clean_text("Sam Thorne @Strippin ( Follow Follow Saw everyone posting these 2009 vs 2019 pics so here's mine 6:23 PM - 12 Jan 2019 O 636 Retweets 3 224 LIKES 65 636 3.2K "))
In [0]:
'''FINALLY CLEAN THE DATA'''

ctext_train = pd.DataFrame(np.zeros(len(text_train)), columns=["textArray"], )
ctext_train = ctext_train.astype(object)

for i,unclean_text in enumerate(text_train):
    ctext_train.iloc[i,0] = clean_text(unclean_text)
    
text_train = np.squeeze(ctext_train.values)

ctext_valid = pd.DataFrame(np.zeros(len(text_valid)), columns=["textArray"], )
ctext_valid = ctext_valid.astype(object)

for i,unclean_text in enumerate(text_valid):
    ctext_valid.iloc[i,0] = clean_text(unclean_text)
    
text_valid = np.squeeze(ctext_valid.values)

print(text_train.shape, text_valid.shape)
[nltk_data] Downloading package stopwords to /root/nltk_data...
[nltk_data]   Package stopwords is already up-to-date!
[nltk_data] Downloading package punkt to /root/nltk_data...
[nltk_data]   Package punkt is already up-to-date!
[nltk_data] Downloading package wordnet to /root/nltk_data...
[nltk_data]   Package wordnet is already up-to-date!
(8170,) (700,)
In [0]:
'''ADDING BERT TOKENS'''

sentences_train = text_train
sentences_train = ["[CLS] " + sentence + " [SEP]" for sentence in sentences_train]

sentences_valid = text_valid
sentences_valid = ["[CLS] " + sentence + " [SEP]" for sentence in sentences_valid]
In [0]:
'''TOKENIZING THE SENTENCES'''

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True)
# tokenizer = BertTokenizer.from_pretrained('bert-base-cased', do_lower_case=False)
MAX_LEN = 128

tokenized_texts_train = [tokenizer.tokenize(sent) for sent in sentences_train]
input_train = [tokenizer.convert_tokens_to_ids(x) for x in tokenized_texts_train]
input_train = pad_sequences(input_train, maxlen=MAX_LEN, dtype="long", truncating="post", padding="post")

tokenized_texts_valid = [tokenizer.tokenize(sent) for sent in sentences_valid]
input_valid = [tokenizer.convert_tokens_to_ids(x) for x in tokenized_texts_valid]
input_valid = pad_sequences(input_valid, maxlen=MAX_LEN, dtype="long", truncating="post", padding="post")
In [0]:
'''BUILDING ATTENTION MASKS'''

attention_masks_train = []

for seq in input_train:
    seq_mask = [float(i>0) for i in seq]
    attention_masks_train.append(seq_mask)
    
attention_masks_valid = []

for seq in input_valid:
    seq_mask = [float(i>0) for i in seq]
    attention_masks_valid.append(seq_mask)
In [0]:
'''CONVERTING TO TENSORS'''

train_text = torch.tensor(input_train)
train_labels = torch.tensor(labels_train)
train_masks = torch.tensor(attention_masks_train)

valid_text = torch.tensor(input_valid)
valid_labels = torch.tensor(labels_valid)
valid_masks = torch.tensor(attention_masks_valid)

IMAGE INPUT

In [0]:
from PIL import Image, ImageFile
import cv2
import torchvision.models as models
import torchvision.transforms as transforms
from sklearn.metrics import f1_score, accuracy_score
from sklearn.model_selection import train_test_split
ImageFile.LOAD_TRUNCATED_IMAGES = True
In [0]:
'''READING THE IMAGES'''

# Several images had a wrong image extention that made them unreadable.
# The try-except block reports these images for their rectfication.

# img_dir_root = './data_7000_clean/data_7000'
img_dir_root = '/content/data_7000_clean/data_7000'

def print_img(img_name, img_size=[18,10]):
    image_path = os.path.join(img_dir_root, img_name)

    try:
        img = Image.open(image_path)
        plt.figure(figsize=img_size)
        plt.imshow(img)
        plt.show()
    except:
        print("ERROR : ", img_name)

def read_img(img_name):
    try:
        image_path = os.path.join(img_dir_root, img_name)
        img = Image.open(image_path).convert('RGB')
        return img
    except:
        print("ERROR : ", img_name)

# for img_name in img_names[:10]:
#     print_img(img_name)

# img = read_img(img_names[6200])
# len(train)
In [0]:
np.array(read_img("misog_55best-memes-2017-1.jpg")).shape
Out[0]:
(320, 480, 3)

DATA LOADER

  • Building the dataloaders to load both the images and the text.
  • Seperate dataloaders are defined for images and text.
In [0]:
batch_size = 8
In [0]:
'''DEFINE: DATA LOADER TEXT'''

train_data = TensorDataset(train_text, train_masks, torch.Tensor(train_df.loc[:,label_class]))
train_sampler = SequentialSampler(train_data)
train_loader1 = DataLoader(train_data, sampler=train_sampler, batch_size=batch_size)

valid_data = TensorDataset(valid_text, valid_masks, torch.Tensor(valid_df.loc[:,label_class]))
valid_sampler = SequentialSampler(valid_data)
valid_loader1 = DataLoader(valid_data, sampler=valid_sampler, batch_size=batch_size)
In [0]:
'''DEFINE: DATA LOADER IMAGES'''

class ImageDataset():

    def __init__(self, type, train_df, valid_df, transform=None):
        self.type = type
        self.train_df = train_df
        self.valid_df = valid_df
        self.transform = transform

    def __getitem__(self, index):
        # t = time.time()

        if(self.type == 'train'): 
            img = read_img(train_df.fileName[index])
            target = train_df.loc[index,label_class]
        elif(self.type == 'valid'): 
            img = read_img(valid_df.fileName[index])
            target = valid_df.loc[index,label_class]

        # print(np.array(img).shape[0]*np.array(img).shape[1] / 100000)

        # print(time.time() - t)
        # t = time.time()

        if self.transform:
            img = self.transform(img) 

        # print(time.time() - t)
        return img, target

    def __len__(self):
        if(self.type == 'train'): return len(self.train_df)
        elif(self.type == 'valid'): return len(self.valid_df)
In [0]:
'''INSTANTIATE THE DATA-LOADERS'''

normalize = transforms.Normalize(
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225]
)

compose = transforms.Compose([
        transforms.Resize([256,256]),
        transforms.ToTensor(),
        normalize
])
        
train_dataset = ImageDataset('train', train_df, valid_df, compose)
valid_dataset = ImageDataset('valid', train_df, valid_df, compose)

train_loader2 = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=False)
valid_loader2 = torch.utils.data.DataLoader(dataset=valid_dataset, batch_size=batch_size, shuffle=False)
In [0]:
# assert(len(train_loader1) == len(train_loader2))
# assert(len(valid_loader1) == len(valid_loader2))
# for i,(x,y,z) in enumerate(train_loader1): print(z.shape)
# for i,(x,y,z) in enumerate(valid_loader1): print(i)
# for i,(x,y) in enumerate(train_loader2): print(i)
# for i,(x,y) in enumerate(valid_loader2): print(i)

THE MODEL

Building the model pipeline

In [0]:
import torch.nn as nn
In [ ]:
'''DEFINING THE MODEL CLASS'''

class SuperModel(nn.Module):
    def __init__(self):
        super(SuperModel, self).__init__()
        self.bert = BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=2)
        self.bert.classifier = nn.Linear(self.bert.classifier.in_features, 128)

        self.resnext = models.resnext50_32x4d(pretrained=True,progress=True)
        self.resnext.fc = nn.Linear(self.resnext.fc.in_features, 128)

        self.fc1 = nn.Linear(256, 32)
        self.fc2 = nn.Linear(32,2)
        self.relu = nn.ReLU()
        self.softmax = nn.Softmax()

    def forward(self, text, mask, image):
        text_rep = self.bert(text, token_type_ids=None, attention_mask=mask)
        image_rep = self.resnext(image)
        x = torch.cat((text_rep, image_rep),1)
        x = self.relu(x)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        x = self.softmax(x)
        return x
In [0]:
model = SuperModel().to(device)
In [0]:
'''DEFINING THE LOSS AND OPTIMIZERS'''

# counts = np.array(label_counts)
# weights = counts.sum() / counts
# print(weights)
# criterion = torch.nn.CrossEntropyLoss(torch.tensor(weights, dtype=torch.float32).to(device))
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.00001)

np.set_printoptions(suppress=True)
print(len(train_loader1), len(train_loader2))
print(len(valid_loader1), len(valid_loader2))
1022 1022
88 88
In [0]:
'''INITIALIZING SOME VARIABLES'''

epoch_count = 0
iterr_count = 0
best_yet_f1 = 0.7
iter_list = []
train_loss_list = []
train_acc_list = []
valid_loss_list = []
valid_acc_list = []
In [0]:
'''SETTING UP TRAINING CUSTOMIZERS'''

inspect_size = 80
num_epochs = 10
monitor_iter = True
monitor_train = True
monitor_train_preds = True
eval_length = 5
monitor_valid = True
monitor_valid_preds = True
save_bset = False
In [ ]:
'''TRAINING THE MODEL'''

time_start = time.time()

soft = torch.nn.Softmax(dim=1) 
train_labels = np.array([])
train_preds = np.array([])

len_train_batches = len(train_loader1)
len_valid_batches = len(valid_loader1)



for epoch in range(num_epochs):
    epoch_count += 1
    print("===============================================================================================================")
    # print("### Epoch : {:2d}/{:2d} ###".format(epoch_count, num_epochs))
    
    it_train_loader1 = iter(train_loader1)
    it_train_loader2 = iter(train_loader2)
    it_valid_loader1 = iter(valid_loader1)
    it_valid_loader2 = iter(valid_loader2)
    
    model.train()
    for iteration in tqdm_notebook(range(len_train_batches)):
        iterr_count += 1

        texts, masks, labels1 = next(it_train_loader1)
        images, labels2 = next(it_train_loader2)
        assert((labels1 == labels2).all())
        texts = texts.to(device)
        masks = masks.to(device)
        images = images.to(device)
        labels = labels1.to(device)

        optimizer.zero_grad()
        preds = model(texts, masks, images)
        loss = criterion(preds, labels.long())
        loss.backward()
        optimizer.step()
        
        if(monitor_train):
            preds = preds.detach().cpu().numpy()
            preds = np.argmax(preds, axis=1)
            labels = labels.cpu().numpy()
            train_preds = np.append(train_preds, preds)
            train_labels = np.append(train_labels, labels)

        # ---------------------------------------------------------------------------------------------
        ####################################### Validation #######################################
        if(iteration % inspect_size != 0): continue

        if(monitor_iter):
            print("[{:2d}/{:2d}] Iteration: {:3d} [{:3.0f}%]".format(epoch_count, num_epochs, iteration, (iteration/len_train_batches) * 100, iteration))
            print("------------------------")
            iter_list.append(iterr_count)
        
        if(save_bset): print(" - BValid f1: {:0.4f}".format(best_yet_f1))
            
        if(monitor_train):
            train_f1 = f1_score(train_labels, train_preds, average='micro')  
            train_acc = accuracy_score(train_labels, train_preds)
            train_loss = loss.item()
            train_loss_list.append(train_loss)
            train_acc_list.append(train_acc)
            print("[Train] Loss: {:0.4f} \t Acc: {:0.4f} \t f1: {:0.4f}".format(train_loss, train_acc, train_f1))
            if(not monitor_train_preds):
                train_labels = np.array([])
                train_preds = np.array([])  

        if(monitor_valid):
            model.eval()
            valid_labels = np.array([])
            valid_preds = np.array([])

            for iteration in range(len_valid_batches):
                with torch.no_grad():

                    texts, masks, labels1 = next(it_valid_loader1)
                    images, labels2 = next(it_valid_loader2)
                    assert(lables1 == labels2)
                    texts = texts.to(device)
                    masks = masks.to(device)
                    images = images.to(device)
                    labels = labels1.to(device)
                    preds = model(texts, masks, images)
                    loss = criterion(preds, labels)
                    loss = criterion(preds, labels)

                    preds = preds.detach().cpu().numpy()
                    preds = np.argmax(preds, axis=1)
                    labels = labels.to('cpu').numpy()

                    valid_labels = np.append(valid_labels, labels)
                    valid_preds = np.append(valid_preds, preds)

                    if(eval_length != None and iteration == eval_length): 
                        break 

            valid_f1 = f1_score(valid_labels, valid_preds, average='micro')  
            valid_acc = accuracy_score(valid_labels, valid_preds)
            valid_loss = loss.item()
            valid_loss_list.append(valid_loss)
            valid_acc_list.append(valid_acc)

            print("[Valid] Loss: {:0.4f} \t Acc: {:0.4f} \t f1: {:0.4f}".format(valid_loss, valid_acc, valid_f1))

            if(save_bset and valid_f1 > best_yet_f1):
                best_yet_f1 = valid_f1
                torch.save(model.state_dict(), "best_yet.pt")
                print("########### Saved best model ###########")
        
        if(monitor_train_preds):
            print("Train Target : ", train_labels[:24])
            print("Train Preds  : ", train_preds[:24])
            train_labels = np.array([])
            train_preds = np.array([])

        if(monitor_valid_preds):
            print("Valid Target : ", valid_labels[:24])
            print("Valid Preds  : ", valid_preds[:24])

        print("==========================================")

time_end = time.time()
print("Time taken : ", time_end-time_start)
In [0]:
 
In [0]:
 

MODEL ANALYSIS

In [0]:
'''GENERATING AVERAGE INSPECTION LISTS'''

os.makedirs('Graphs', exist_ok=True)

# Defining roll function
def make_roll(input_list, roll_size=5):
    output_list = []
    
    for i in range(len(input_list)):
        if i==0:
            output_list.append(input_list[0])
        elif i<roll_size:
            output_list.append(np.mean(input_list[:i+1]))
        else:
            output_list.append(np.mean(input_list[i-roll_size:i]))
    return output_list

# Generating roll lists
train_roll_loss_list = make_roll(train_loss_list, roll_size=5) 
train_roll_acc_list = make_roll(train_acc_list, roll_size=5)  

valid_roll_acc_list = make_roll(valid_acc_list, roll_size=5)  
valid_roll_loss_list = make_roll(valid_loss_list, roll_size=5)  
In [ ]:
'''PLOTTTING THE ACCURACY GRAPH'''

plt.figure(figsize=[15,10])

plt.plot(train_acc_list, '-', lw=1, c='salmon', label='Train Accuracy')
plt.plot(train_roll_acc_list, '-|r', lw=3, label='Train Accuracy [Avg]')

plt.plot(valid_acc_list, '-', lw=1, c='brown', label='Valid Accuracy')
plt.plot(valid_roll_acc_list, '-|k', lw=3, label='Valid Accuracy [Avg]')

plt.title('model-{} : ACCURACY vs ITERATIONS'.format(params['model_no']))

plt.xlabel('Number of Iterations')
plt.ylabel('Accuracy')

# plt.yticks(np.arange(0,101,10))

plt.grid(True, linestyle='-.',)
plt.tick_params(labelcolor='k')

plt.legend()
plt.savefig("Graphs/model-{}_epochs={}_acc.png".format(params['model_no'], params['num_epochs_trained']), dpi=100)
# plt.show()
In [ ]:
'''PLOTTTING THE LOSS GRAPH'''

plt.figure(figsize=[15,10])

plt.plot(train_loss_list, '-', lw=1, c='salmon', label='Train Loss')
plt.plot(train_roll_loss_list, '-|r', lw=3, label='Train Loss [Avg]')

plt.plot(valid_loss_list, '-', lw=1, c='brown', label='Valid Loss')
plt.plot(valid_roll_loss_list, '-|k', lw=3, label='Valid Loss [Avg]')

plt.title('model-{} : LOSS vs ITERATIONS'.format(params['model_no']))
plt.xlabel('Number of Iterations')
plt.ylabel('Loss')

plt.grid(True, linestyle='-.',)
plt.tick_params()
plt.legend()
plt.savefig("Graphs/model-{}_epochs={}_loss.png".format(params['model_no'], params['num_epochs_trained']), dpi=100)
# plt.show()
In [ ]:
from sklearn.metrics import confusion_matrix
confusion_matrix(valid_labels, valid_preds)
In [0]:
 

SAVED MODEL WEIGHTS AND GRAPHS

In [0]: