#!/usr/bin/env python # coding: utf-8 # # RNN Text Classification: Predict the sentiment of IMDB movie reviews # In[1]: from pathlib import Path import pandas as pd import torch import torch.nn.functional as F import torch.nn as nn import torch.optim as optim from google_drive_downloader import GoogleDriveDownloader as gdd from sklearn.feature_extraction.text import CountVectorizer from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence from torch.utils.data import DataLoader, Dataset from tqdm import tqdm, tqdm_notebook # In order to perform deep learning on a GPU (so that everything runs super quick!), CUDA has to be installed and configured. Fortunately, Google Colab already has this set up, but if you want to try this on your own GPU, you can [install CUDA from here](https://developer.nvidia.com/cuda-downloads). Make sure you also [install cuDNN](https://developer.nvidia.com/cudnn) for optimized performance. # In[2]: device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') device # ## Download the training data # # This is a dataset of positive and negative IMDB reviews. We can download the data from a public Google Drive folder. # In[3]: DATA_PATH = 'data/imdb_reviews.csv' if not Path(DATA_PATH).is_file(): gdd.download_file_from_google_drive( file_id='1zfM5E6HvKIe7f3rEt1V2gBpw5QOSSKQz', dest_path=DATA_PATH, ) # ## Preprocess the text # In[4]: class Sequences(Dataset): def __init__(self, path, max_seq_len): self.max_seq_len = max_seq_len df = pd.read_csv(path) vectorizer = CountVectorizer(stop_words='english', min_df=0.015) vectorizer.fit(df.review.tolist()) self.token2idx = vectorizer.vocabulary_ self.token2idx[''] = max(self.token2idx.values()) + 1 tokenizer = vectorizer.build_analyzer() self.encode = lambda x: [self.token2idx[token] for token in tokenizer(x) if token in self.token2idx] self.pad = lambda x: x + (max_seq_len - len(x)) * [self.token2idx['']] sequences = [self.encode(sequence)[:max_seq_len] for sequence in df.review.tolist()] sequences, self.labels = zip(*[(sequence, label) for sequence, label in zip(sequences, df.label.tolist()) if sequence]) self.sequences = [self.pad(sequence) for sequence in sequences] def __getitem__(self, i): assert len(self.sequences[i]) == self.max_seq_len return self.sequences[i], self.labels[i] def __len__(self): return len(self.sequences) # In[5]: dataset = Sequences(DATA_PATH, max_seq_len=128) # In[6]: len(dataset.token2idx) # In[7]: def collate(batch): inputs = torch.LongTensor([item[0] for item in batch]) target = torch.FloatTensor([item[1] for item in batch]) return inputs, target batch_size = 2048 train_loader = DataLoader(dataset, batch_size=batch_size, collate_fn=collate) # ## GRU # # ![](images/gru_equations.png) # # ![](images/gru_diagram.png) # In[12]: class RNN(nn.Module): def __init__( self, vocab_size, batch_size, embedding_dimension=100, hidden_size=128, n_layers=1, device='cpu', ): super(RNN, self).__init__() self.n_layers = n_layers self.hidden_size = hidden_size self.device = device self.batch_size = batch_size self.encoder = nn.Embedding(vocab_size, embedding_dimension) self.rnn = nn.GRU( embedding_dimension, hidden_size, num_layers=n_layers, batch_first=True, ) self.decoder = nn.Linear(hidden_size, 1) def init_hidden(self): return torch.randn(self.n_layers, self.batch_size, self.hidden_size).to(self.device) def forward(self, inputs): # Avoid breaking if the last batch has a different size batch_size = inputs.size(0) if batch_size != self.batch_size: self.batch_size = batch_size encoded = self.encoder(inputs) output, hidden = self.rnn(encoded, self.init_hidden()) output = self.decoder(output[:, :, -1]).squeeze() return output # In[13]: model = RNN( hidden_size=128, vocab_size=len(dataset.token2idx), device=device, batch_size=batch_size, ) model = model.to(device) model # ## Train the model # # ![](images/rnn_training_diagram.png) # In[14]: criterion = nn.BCEWithLogitsLoss() optimizer = optim.Adam([p for p in model.parameters() if p.requires_grad], lr=0.001) # In[15]: model.train() train_losses = [] for epoch in range(10): progress_bar = tqdm_notebook(train_loader, leave=False) losses = [] total = 0 for inputs, target in progress_bar: inputs, target = inputs.to(device), target.to(device ) model.zero_grad() output = model(inputs) loss = criterion(output, target) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), 3) optimizer.step() progress_bar.set_description(f'Loss: {loss.item():.3f}') losses.append(loss.item()) total += 1 epoch_loss = sum(losses) / total train_losses.append(epoch_loss) tqdm.write(f'Epoch #{epoch + 1}\tTrain Loss: {epoch_loss:.3f}') # ## Analyzing reviews for "Cool Cat Saves the Kids" # # ![](https://m.media-amazon.com/images/M/MV5BNzE1OTY3OTk5M15BMl5BanBnXkFtZTgwODE0Mjc1NDE@._V1_UY268_CR11,0,182,268_AL_.jpg) # In[27]: def predict_sentiment(text): model.eval() with torch.no_grad(): test_vector = torch.LongTensor([dataset.pad(dataset.encode(text))]).to(device) output = model(test_vector) prediction = torch.sigmoid(output).item() if prediction > 0.5: print(f'{prediction:0.3}: Positive sentiment') else: print(f'{prediction:0.3}: Negative sentiment') # In[28]: test_text = """ This poor excuse for a movie is terrible. It has been 'so good it's bad' for a while, and the high ratings are a good form of sarcasm, I have to admit. But now it has to stop. Technically inept, spoon-feeding mundane messages with the artistic weight of an eighties' commercial, hypocritical to say the least, it deserves to fall into oblivion. Mr. Derek, I hope you realize you are like that weird friend that everybody know is lame, but out of kindness and Christian duty is treated like he's cool or something. That works if you are a good decent human being, not if you are a horrible arrogant bully like you are. Yes, Mr. 'Daddy' Derek will end on the history books of the internet for being a delusional sour old man who thinks to be a good example for kids, but actually has a poster of Kim Jong-Un in his closet. Destroy this movie if you all have a conscience, as I hope IHE and all other youtube channel force-closed by Derek out of SPITE would destroy him in the courts.This poor excuse for a movie is terrible. It has been 'so good it's bad' for a while, and the high ratings are a good form of sarcasm, I have to admit. But now it has to stop. Technically inept, spoon-feeding mundane messages with the artistic weight of an eighties' commercial, hypocritical to say the least, it deserves to fall into oblivion. Mr. Derek, I hope you realize you are like that weird friend that everybody know is lame, but out of kindness and Christian duty is treated like he's cool or something. That works if you are a good decent human being, not if you are a horrible arrogant bully like you are. Yes, Mr. 'Daddy' Derek will end on the history books of the internet for being a delusional sour old man who thinks to be a good example for kids, but actually has a poster of Kim Jong-Un in his closet. Destroy this movie if you all have a conscience, as I hope IHE and all other youtube channel force-closed by Derek out of SPITE would destroy him in the courts. """ predict_sentiment(test_text) # In[29]: test_text = """ Cool Cat Saves The Kids is a symbolic masterpiece directed by Derek Savage that is not only satirical in the way it makes fun of the media and politics, but in the way in questions as how we humans live life and how society tells us to live life. Before I get into those details, I wanna talk about the special effects in this film. They are ASTONISHING, and it shocks me that Cool Cat Saves The Kids got snubbed by the Oscars for Best Special Effects. This film makes 2001 look like garbage, and the directing in this film makes Stanley Kubrick look like the worst director ever. You know what other film did that? Birdemic: Shock and Terror. Both of these films are masterpieces, but if I had to choose my favorite out of the 2, I would have to go with Cool Cat Saves The Kids. It is now my 10th favorite film of all time. Now, lets get into the symbolism: So you might be asking yourself, Why is Cool Cat Orange? Well, I can easily explain. Orange is a color. Orange is also a fruit, and its a very good fruit. You know what else is good? Good behavior. What behavior does Cool Cat have? He has good behavior. This cannot be a coincidence, since cool cat has good behavior in the film. Now, why is Butch The Bully fat? Well, fat means your wide. You wanna know who was wide? Hitler. Nuff said this cannot be a coincidence. Why does Erik Estrada suspect Butch The Bully to be a bully? Well look at it this way. What color of a shirt was Butchy wearing when he walks into the area? I don't know, its looks like dark purple/dark blue. Why rhymes with dark? Mark. Mark is that guy from the Room. The Room is the best movie of all time. What is the opposite of best? Worst. This is how Erik knew Butch was a bully. and finally, how come Vivica A. Fox isn't having a successful career after making Kill Bill. I actually can't answer that question. Well thanks for reading my review. """ predict_sentiment(test_text) # In[30]: test_text = """ Don't let any bullies out there try and shape your judgment on this gem of a title. Some people really don't have anything better to do, except trash a great movie with annoying 1-star votes and spread lies on the Internet about how "dumb" Cool Cat is. I wouldn't be surprised to learn if much of the unwarranted negativity hurled at this movie is coming from people who haven't even watched this movie for themselves in the first place. Those people are no worse than the Butch the Bully, the film's repulsive antagonist. As it just so happens, one of the main points of "Cool Cat Saves the Kids" is in addressing the attitudes of mean naysayers who try to demean others who strive to bring good attitudes and fun vibes into people's lives. The message to be learned here is that if one is friendly and good to others, the world is friendly and good to one in return, and that is cool. Conversely, if one is miserable and leaving 1-star votes on IMDb, one is alone and doesn't have any friends at all. Ain't that the truth? The world has uncovered a great, new, young filmmaking talent in "Cool Cat" creator Derek Savage, and I sure hope that this is only the first of many amazing films and stories that the world has yet to appreciate. If you are a cool person who likes to have lots of fun, I guarantee that this is a movie with charm that will uplift your spirits and reaffirm your positive attitudes towards life. """ predict_sentiment(test_text) # In[ ]: