# -*- coding: utf-8 -*-
"""IMDb Review Classification using CNN Tensorflow.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1yg3mzMIxBjCzOZkadeCA7lYFQ8q79dcq
"""

import tensorflow as tf
import numpy as np
import string
import re
from tensorflow.keras.layers import TextVectorization

"""#### Download Dataset"""

!curl -O https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
!tar -xf aclImdb_v1.tar.gz

"""#### Dataset overview

The `aclImdb` dir contains `train/` and `test/` dir. Each dir contains `pos` and `neg` dir. Each `pos` or `neg` dir contains multiple text file and each of them represents one sample (i.e., text only)
"""

!ls ac*/

!ls ac*/train/

!cat ac*/train/pos/11694_7.txt

# remove unwanted dir 
!rm -r ac*/train/unsup

"""#### Load dataset"""

train_ds = tf.keras.utils.text_dataset_from_directory(
    "aclImdb/train",
    validation_split=0.2,
    subset="training",
    seed=42,
)

# set seed to prevent train/val set overlap

val_ds = tf.keras.utils.text_dataset_from_directory(
    "aclImdb/train",
     validation_split=0.2,
    subset="validation",
    seed=42, 
)


test_ds = tf.keras.utils.text_dataset_from_directory(
    "aclImdb/test",    
)

print(f"Number of batches in train_ds: {tf.data.experimental.cardinality(train_ds)}")
print(f"Number of batches in val_ds: {tf.data.experimental.cardinality(val_ds)}")
print(f"Number of batches in test_ds: {tf.data.experimental.cardinality(test_ds)}")

"""#### Preview batches"""

for text, label in train_ds.take(1):
    print(f"Each batch contains {len(text)} samples")
    for i in range(3):
        print(f"Text: {text.numpy()[i]}")
        print(f"Label: {label.numpy()[i]}")

"""#### Basic text cleaning"""

MAX_SEQ_LEN = 256
EMBEDDING_SIZE = 300
MAX_VOCAB_SIZE = 10000

def clean_text(text):
    text = tf.strings.lower(text)
    text = tf.strings.regex_replace(text, "<br />", " ")
    text = tf.strings.regex_replace(text, f"[{string.punctuation}]", "")
    return text

vectorized_layer = tf.keras.layers.TextVectorization(
    max_tokens=MAX_VOCAB_SIZE,
    standardize=clean_text,
    output_mode="int",
    output_sequence_length=MAX_SEQ_LEN,
)

train_text = train_ds.map(lambda x, y: x)
# learn vocab from training set text
vectorized_layer.adapt(train_text)

vectorized_layer.vocabulary_size()

print(vectorized_layer("This is a demo sentence."))

train_ds = train_ds.cache().prefetch(tf.data.AUTOTUNE)
val_ds  = val_ds.cache().prefetch(tf.data.AUTOTUNE)
test_ds = test_ds.cache().prefetch(tf.data.AUTOTUNE)

"""#### Build Model"""

def build_model():
    input = tf.keras.Input(shape=(1,), dtype="string", name="input_str_layer")
    x = vectorized_layer(input)
    x = tf.keras.layers.Embedding(input_dim=MAX_VOCAB_SIZE, output_dim=EMBEDDING_SIZE)(x)
    x = tf.keras.layers.Conv1D(128, 3, activation="relu", padding="valid", strides=2)(x)
    x = tf.keras.layers.Conv1D(256, 6, activation="relu", padding="valid", strides=2)(x)
    x = tf.keras.layers.GlobalMaxPooling1D()(x)
    x = tf.keras.layers.Dense(256, activation="relu")(x)
    x = tf.keras.layers.Dropout(rate=0.5)(x)
    output = tf.keras.layers.Dense(1, activation="sigmoid")(x)
    
    model = tf.keras.Model(inputs=input, outputs=output)

    model.compile(
        loss=tf.keras.losses.BinaryCrossentropy(),
        optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
        metrics=[tf.keras.metrics.BinaryAccuracy()]
        )
    
    return model

model = build_model()

model.summary()

"""#### Training"""

model.fit(
    train_ds,
    batch_size=32,
    validation_data=val_ds,
    verbose=1,
    epochs=3,
)

"""#### Testing"""

model.evaluate(test_ds)

model.metrics_names

"""#### Inferencing"""

test_sentence = tf.constant(["Terrible movie. I hate this so much. No no. ugly so bad"])

predictions = model(test_sentence)

print(f"Text: {test_sentence}")
print(f"Predictions: {predictions} ({'Positive' if predictions >= 0.5 else 'Negative'})")

test_sentence = tf.constant(["BEST movie. I love this so much."])

predictions = model(test_sentence)

print(f"Text: {test_sentence}")
print(f"Predictions: {predictions} ({'Positive' if predictions >= 0.5 else 'Negative'})")