<jupyter_start><jupyter_text>MPAA rating prediction<jupyter_code>import pandas as pd
import numpy as np
import warnings
import re
import pickle
import seaborn as sns

%matplotlib inline
warnings.filterwarnings('ignore')<jupyter_output><empty_output><jupyter_text>Import dataset :: using the rotten tomatoes dataset<jupyter_code>dataset_df = pd.read_csv( './rtt_dataset/all_movie.csv' )
dataset_df = dataset_df[['Title', 'Rating', 'Description']]
dataset_df.columns = dataset_df.columns.str.strip()
dataset_df.head(4)
# finding unique mpaa rating in the dataset
dataset_df['Rating'].unique()
# clean mpaa ratings
mpaa_fix = {
    'PG-13 ' : 'PG-13',
    'R ' : 'R',
    'PG ': 'PG',
    'G ' : 'G',
    'NR ': 'NR',
    'NC17 ': 'NC17'
}
for i, rating in dataset_df['Rating'].iteritems():
    if rating in mpaa_fix.keys():
        fix_val = mpaa_fix.get(rating)
        dataset_df.iloc[i]['Rating'] = fix_val
# Clean the movie description
def clean_description( text_str ):
    text = re.sub('[^a-zA-Z]',  ' ', text_str)
    text = re.sub(r'\s+[a-zA-Z]\s+', ' ', text)
    text = re.sub(r'\s+', ' ', text)
    return text

cleaned_desc = []
for i, description in dataset_df['Description'].iteritems():
    cleaned_desc.append( clean_description( str(description) ) )

dataset_df['cleanedDescr'] = cleaned_desc
dataset_df.head(4)
dataset_df.info()
## drop G, NC-17
dataset_df = dataset_df[dataset_df.Rating != 'NC17']
pickle.dump(dataset_df, open('./data/dataset_df_cleaned_n_sample_exceptNC17BiD.pkl', 'wb'))<jupyter_output><empty_output><jupyter_text>Further clean the description  Training model<jupyter_code># training_df = pickle.load( open('./data/dataset_df_cleaned_n_sample_exceptNC17BiD_cleaned.pkl', 'rb') )
training_df = pickle.load( open('./data/dataset_df_cleaned_n_sample_exceptNC17BiD.pkl', 'rb') )
# fig = plt.figure(figsize=(8,6))
# dataset_df.groupby('Rating')['cleanedDescr'].count().plot.bar(ylim=0)
# plt.show()
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from keras.preprocessing.text import one_hot, Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.utils import to_categorical
from keras.models import Sequential, load_model
from keras.layers import Dense, Flatten, LSTM, Embedding, GlobalMaxPooling1D, Bidirectional, Dropout
from keras import regularizers
_y = training_df['Rating']
encoder = LabelEncoder()
Y = encoder.fit_transform(_y)
Y
le = encoder.fit(_y)
labels = list(le.classes_)
labels
X = training_df['cleanedDescr']
X
description_lengths = []
for i, description in training_df['cleanedDescr'].iteritems():
    description_lengths.append( len(description) )
max_description_length = np.mean( description_lengths )
max_description_length<jupyter_output><empty_output><jupyter_text>Train, test split<jupyter_code>X_train, X_test, Y_train, Y_test = train_test_split( X, Y, test_size=0.3, random_state=40 )
Y_train = to_categorical(Y_train)
Y_test  = to_categorical(Y_test)
tokenizer = Tokenizer(num_words=10000)
tokenizer.fit_on_texts(X_train)
X_train = tokenizer.texts_to_sequences(X_train)
X_test  = tokenizer.texts_to_sequences(X_test)
vocabulary_size = len(tokenizer.word_index) + 1
vocabulary_size
maxlen =  400 # Only concider first 400 words of each description

X_train = pad_sequences( X_train, maxlen=maxlen )
X_test  = pad_sequences( X_test, maxlen=maxlen )<jupyter_output><empty_output><jupyter_text>Use GloVe : Global Vectors for Word Representationhttps://nlp.stanford.edu/projects/glove/GloVe is an unsupervised learning algorithm for obtaining vector representations for words. Training is performed on aggregated global word-word co-occurrence statistics from a corpus, and the resulting representations showcase interesting linear substructures of the word vector space.<jupyter_code>glove_file = open('./glove.6B/glove.6B.100d.txt', encoding='utf8')
embedd_dictionary = dict()
for line in glove_file:
    records = line.split()
    word = records[0]
    vector_dims = np.asarray(records[1:], dtype='float32')
    embedd_dictionary[word] = vector_dims

glove_file.close()
embedd_matrix = np.zeros((vocabulary_size, 100))
for word, index in tokenizer.word_index.items():
    embedd_vector = embedd_dictionary.get(word)
    if embedd_vector is not None:
        embedd_matrix[index] = embedd_vector
embedd_matrix.shape
output_dim = embedd_matrix.shape[1]
output_dim<jupyter_output><empty_output><jupyter_text>Build the NN Model<jupyter_code>model = Sequential()
model.add(Embedding(input_dim=vocabulary_size, output_dim=output_dim, weights=[embedd_matrix], trainable=False))
model.add( Bidirectional(LSTM(units=256, recurrent_dropout=0)) )
model.add(Dropout(0.2))
model.add(Dense(256, activation='relu', kernel_regularizer=regularizers.l2(0.001)))
model.add(Dropout(0.2))
model.add(Dense(64))
model.add(Dropout(0.2))
model.add(Dense(5, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.summary()
import keras
callback = [
    keras.callbacks.EarlyStopping(patience=20, monitor='val_loss', verbose=2),
    keras.callbacks.ModelCheckpoint(filepath='./data/checkpoint/BDLSTM.{epoch:02d}-{val_loss:.2f}.h5', save_best_only=True ),
    keras.callbacks.TensorBoard(log_dir='./data/tf/logs'),
]
hist = model.fit(X_train, Y_train, batch_size=64, epochs=120, validation_data=(X_test, Y_test), verbose=1, callbacks=callback)
loss, acc = model.evaluate(X_test, Y_test, verbose=1)
print('loss: ', loss)
print('accuracy: ', acc)<jupyter_output>279/279 [==============================] - 6s 23ms/step - loss: 1.4838 - accuracy: 0.8085
loss:  1.4837886095046997
accuracy:  0.8085464239120483<jupyter_text>Save model and tokenizer<jupyter_code># save model
model.save('./data/BLSTM_model.h5')

# save word tokenizer
pickle.dump(tokenizer, open('./data/BLSTM_model_tokenizer.pkl', 'wb'))<jupyter_output><empty_output><jupyter_text>Model Architecture<jupyter_code># load the saved model
from keras.models import load_model
model = load_model("./data/BLSTM_model.h5")
from keras.utils.vis_utils import plot_model
plot_model(model, to_file='model_plot.png', show_shapes=True, show_layer_names=True)<jupyter_output><empty_output><jupyter_text>Metrics<jupyter_code>import matplotlib.pyplot as plt
plt.plot(hist.history['accuracy'])
plt.plot(hist.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('# Epochs')
plt.legend(['train', 'test'])
plt.show()
plt.plot(hist.history['loss'])
plt.plot(hist.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('# Epochs')
plt.legend(['train', 'test'])
plt.show()<jupyter_output><empty_output><jupyter_text>Label maps<jupyter_code>le = encoder.fit(_y)
labels = list(le.classes_)
labels
class_names = dict(zip(le.classes_, le.transform(le.classes_)))
class_names
Y_pred = model.predict(X_test)
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(Y_test.argmax(axis=1), Y_pred.argmax(axis=1) )
cmd = ConfusionMatrixDisplay(cm, display_labels=list(le.classes_))
cmd.plot()
from sklearn.metrics import classification_report

y_pred = model.predict(X_test, batch_size=64, verbose=1)
y_pred_bool = np.argmax(Y_pred, axis=1)

print(classification_report(Y_test.argmax(axis=1), y_pred_bool))<jupyter_output>140/140 [==============================] - 4s 25ms/step
              precision    recall  f1-score   support

           0       0.88      0.82      0.85       491
           1       0.84      0.80      0.82      2423
           2       0.73      0.80      0.77      1234
           3       0.78      0.74      0.76      1512
           4       0.82      0.85      0.84      3256

    accuracy                           0.81      8916
   macro avg       0.81      0.80      0.81      8916
weighted avg       0.81      0.81      0.81      8916<jupyter_text>---  Demonstration<jupyter_code>import pickle
from keras.models import Sequential, load_model
from keras.preprocessing.sequence import pad_sequences
import warnings
warnings.filterwarnings('ignore')
## Get tokenizer 
text_tokenizer = pickle.load(open('./data/BLSTM_model_tokenizer.pkl', 'rb'))
## Get saved model
saved_model = load_model("./data/BLSTM_model.h5")

## prepare class names
mpaa_labels = {0 : 'G', 1: 'NR', 2: 'PG', 3: 'PG-13', 4 : 'R'}

## max length allowed for texts
max_text_len= 420
import ipywidgets as widgets
from ipywidgets import interact, interact_manual
history = []
textarea = widgets.Textarea(
    value='',
    placeholder='Enter movie synopsis',
    description='Synopsis:',
    disabled=False,
    width=600
)
def make_prediction(b):
    val = textarea.value
    if val is not None:
        synopsisTKN = text_tokenizer.texts_to_sequences(val)
        #print("TOKENIZATION=", synopsisTKN)
        
        text_list = []
        for sub_list in synopsisTKN:
            for tm in sub_list:
                text_list.append(tm)
             
        # add padding
        synopsis = pad_sequences([text_list], maxlen=max_text_len)
        #print(synopsis)
        
        # make prediction
        pred = saved_model.predict_classes(synopsis)
        print(pred)
        _class_lable = mpaa_labels[pred[0]]
        
        record = {
            "label" : _class_lable,
            "synopsis" : val
        }
        
        print(record)
submit = widgets.Button(description='Submit')
submit.on_click(make_prediction)
widgets.VBox([textarea,submit])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13014566181127296, 'fraction_numerical': 0.02934346632890015, 'mean_word_length': 4.71816535908268, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 43, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '209175', 'license': 'MIT', 'n_tokens_mistral': 3484, 'n_tokens_neox': 3269, 'n_words': 631, 'path': 'models/classification/Keras_BLSTM_demo.ipynb', 'repo_name': 'ma-he-sh/MovieMetaPredict', 'size': '9474'}