Back to Cosmos

**Importing Libraries**

code/artificial_intelligence/src/chatbot/Chatbot.ipynb

latest5.4 KB
Original Source

Importing Libraries

python
import nltk #for tokenization, stemming and vectorization of words
nltk.download('punkt') #for the first time
from nltk.stem.porter import PorterStemmer # for stemming
import json #for reading and manipulating training data
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import random #for random results

Creating Training Data

python
def tokenize(sentence):
  return nltk.word_tokenize(sentence)
python
stemmer = PorterStemmer()
def stem(word):
  return stemmer.stem(word.lower())
python
def bag_of_words(tokenized_sentence, all_words):
  """
  Argument:
  sentence : ["hello", "how", "are", "you"]
  all_words : ["hi", "hello", "I", "you", "thank", "cool"]

  Returns:
  bag = [0, 1, 0, 1, 0, 0] 
  """
  tokenized_sentence= [stem(w) for w in tokenized_sentence]

  bag = np.zeros(len(all_words), dtype = np.float32) 

  for idx, w in enumerate(all_words):
    if w in tokenized_sentence:
      bag[idx] = 1.0

  return bag
python
with open('intents.json', "r") as file:
  intents = json.load(file)

all_words = []
tags = []
data = []

for intent in intents['intents']:
  tag = intent['tag']
  tags.append(tag)
  for pattern in intent['patterns']:
    w = tokenize(pattern)
    all_words.extend(w)
    data.append((w, tag))

ignore_words = ['?', "!", ".", ","] #remove punctionations

all_words = [stem(word) for word in all_words if word not in ignore_words]
all_words = sorted(set(all_words))
tags = sorted(set(tags))
print("Words: ", all_words)
print("Tags: ", tags)

x_train = [] #bag of words
y_train = [] #tag numbers

for pattern_sentence, tag in data:
  bag = bag_of_words(pattern_sentence, all_words)
  x_train.append(bag)

  class_label = tags.index(tag) 
  y_train.append(class_label) 

x_train = np.array(x_train)
y_train = np.array(y_train)

print("Xtrain: ", x_train)
print("YTrain: ", y_train) 

Dataset class

python
class ChatDataset(Dataset):
  def __init__(self):
    self.n_samples = len(x_train)
    self.x_data = x_train
    self.y_data = y_train
  
  def __getitem__(self, index):
    return (self.x_data[index], self.y_data[index])
  
  def __len__(self):
    return self.n_samples

Hyper parameters

python
BATCH_SIZE = 8
INPUT_SIZE = len(all_words) #or len(x_train[0])
HIDDEN_SIZE = 8
OUTPUT_SIZE = len(tags) #no of classes

LEARNING_RATE = 0.001
EPOCHS = 1000

DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(DEVICE)

FILE = "data.pth"
python
dataset = ChatDataset()
train_loader = DataLoader(dataset = dataset, batch_size = BATCH_SIZE, shuffle=True, num_workers = 2)

Architecture and Model

python
class NNet(nn.Module):
  def __init__(self, input_size, hidden_size, num_classes):
    super(NNet, self).__init__()
    self.l1 = nn.Linear(input_size, hidden_size)
    self.l2 = nn.Linear(hidden_size, hidden_size)
    self.l3 = nn.Linear(hidden_size, num_classes)
    self.relu = nn.ReLU()
    
  def forward(self, x):
    out = self.l1(x)
    out = self.relu(out)
    out = self.l2(out)
    out = self.relu(out) 
    out = self.l3(out)
    #no activation and no softmax
    return out
python
model = NNet(INPUT_SIZE, HIDDEN_SIZE, OUTPUT_SIZE).to(DEVICE)

Loss function and optimization

python
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr = LEARNING_RATE)

Training

python
for epoch in range(EPOCHS):
  for words, labels in train_loader:
    words = words.to(DEVICE)
    labels = labels.to(DEVICE)

    #forward pass
    outputs = model(words)
    loss = criterion(outputs, labels)

    #backward pass
    optimizer.zero_grad() #empty the gradient before calculating gradient for every epoch
    loss.backward() #run back prop
    optimizer.step() #update parameters

  if(epoch + 1) % 100 == 0:
    print(f'Epoch {epoch+1}/{EPOCHS}, loss = {loss.item():.4f}')

model_data = {
    "model_state": model.state_dict(),
    "input_size": INPUT_SIZE,
    "output_size": OUTPUT_SIZE,
    "hidden_size": HIDDEN_SIZE,
    "all_words": all_words,
    "tags": tags,
}

print(f'Final loss = {loss.item():.4f}')
torch.save(model_data, FILE)
print(f"Training complete... file saved to {FILE}")

ChatBOT

python
final_model_data = torch.load(FILE) #loading model

model_state = final_model_data["model_state"]
all_words = final_model_data["all_words"]

best_model = NNet(INPUT_SIZE, HIDDEN_SIZE, OUTPUT_SIZE).to(DEVICE)
best_model.load_state_dict(model_state)
best_model.eval() #change to evalutation stage
python
BOT_NAME = "STARY"
print("Let's chat! type 'quit to exit")

while True:
  sentence = input("You: ")
  if(sentence == 'quit'):
    break
  
  #tokenize and bag of words, same as training
  sentence = tokenize(sentence)
  x = bag_of_words(sentence, all_words)
  x = x.reshape(1, x.shape[0])
  x= torch.from_numpy(x).to(DEVICE)

  output = best_model(x)
  _, predicted = torch.max(output, dim = 1)
  tag = tags[predicted.item()] #get tag of the sentence spoken by user
  
  #to get probabilities of outputs
  probs = torch.softmax(output, dim = 1)
  prob = probs[0][predicted.item()]
  if(prob.item() > 0.75): 
    #loop through all tags in intent to select a random sentence from responses of that specific tag
    for intent in intents["intents"]:
      if tag == intent["tag"]:
        print(f"{BOT_NAME}: {random.choice(intent['responses'])}")
  else:
    print(f"{BOT_NAME}: Sorry, I do not understand...")