๐ค Building an Intent-Based Chatbot Using TensorFlow and NLTK
Chatbots are everywhere—from customer support to virtual assistants—and building one from scratch is a fantastic way to dive into natural language processing and machine learning. In this post, I’ll walk you through how I built a simple intent-based chatbot using Python, TensorFlow, and NLTK.
๐ง What Is an Intent-Based Chatbot?
Unlike generative chatbots that try to generate human-like responses, intent-based chatbots classify user input into predefined categories (called intents) and respond accordingly. For example, if a user says “Hi,” the bot might classify it as a greeting
intent and reply with “Hello! How can I help you?”
๐ ️ Tools and Libraries Used
NLTK: For tokenization and lemmatization.
TensorFlow/Keras: To build and train the neural network.
JSON: To store intents and training patterns.
Pickle: For saving processed data.
NumPy: For numerical operations.
๐ Step 1: Preparing the Data
We start by loading the intents.json
file, which contains patterns and responses for each intent. Then we tokenize and lemmatize the words, and create a bag-of-words representation for each pattern.
from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
# Load intents
with open('intents.json', 'r') as file:
intents = json.load(file)
words = []
classes = []
documents = []
ignore_letters = ["?", "!", ".", ","]
# Tokenize and label patterns
for intent in intents['intents']:
for pattern in intent['patterns']:
word_list = nltk.word_tokenize(pattern)
words.extend(word_list)
documents.append((word_list, intent['tag']))
if intent['tag'] not in classes:
classes.append(intent['tag'])
# Lemmatize and clean words
words = [lemmatizer.lemmatize(word.lower()) for word in words if word not in ignore_letters]
words = sorted(set(words))
classes = sorted(set(classes))
๐งช Step 2: Creating Training Data
We convert each pattern into a binary vector (bag of words) and associate it with a one-hot encoded output vector representing the intent.
training = []
output_empty = [0] * len(classes)
for document in documents:
bag = []
word_patterns = [lemmatizer.lemmatize(word.lower()) for word in document[0]]
for word in words:
bag.append(1 if word in word_patterns else 0)
output_row = list(output_empty)
output_row[classes.index(document[1])] = 1
training.append([bag, output_row])
random.shuffle(training)
training = np.array(training, dtype=object)
train_x = list(training[:, 0])
train_y = list(training[:, 1])
๐ง Step 3: Building the Neural Network
We use a simple feedforward neural network with two hidden layers and dropout for regularization.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import SGD
model = Sequential()
model.add(Dense(128, input_shape=(len(train_x[0]),), activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(len(train_y[0]), activation='softmax'))
# Compile the model
sgd = SGD(learning_rate=0.01, weight_decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])
# Train the model
hist = model.fit(np.array(train_x), np.array(train_y), epochs=200, batch_size=5, verbose=1)
๐พ Step 4: Saving the Model
Once training is complete, we save the model and the processed data for later use.
model.save("chatbotmodel.h5", hist)
pickle.dump(words, open('words.pkl', 'wb'))
pickle.dump(classes, open('classes.pkl', 'wb'))
๐ Final Thoughts
This chatbot is a great starting point for anyone interested in NLP and machine learning. You can now build a response engine, connect it to a web interface, or even deploy it as a Telegram bot.
Want to take it further? Try adding:
Context handling (e.g., remembering previous user inputs)
More advanced NLP with spaCy or transformers
A GUI using Tkinter or a web app with Flask
Thanks for reading! If you found this helpful, feel free to share or drop a comment. Happy coding!
Would you like me to help you write a follow-up post on how to use this trained model to respond to user input? I can also help you design a chatbot interface or deploy it online.
intents.json
{"intents": [
{"tag": "greeting",
"patterns": ["Hi there", "How are you", "Is anyone there?","Hey","Hola", "Hello", "Good day"],
"responses": ["Hello, thanks for asking", "Good to see you again", "Hi there, how can I help?"],
"context": [""]
},
{"tag": "goodbye",
"patterns": ["Bye", "See you later", "Goodbye", "Nice chatting to you, bye", "Till next time"],
"responses": ["See you!", "Have a nice day", "Bye! Come back again soon."],
"context": [""]
},
{"tag": "thanks",
"patterns": ["Thanks", "Thank you", "That's helpful", "Awesome, thanks", "Thanks for helping me"],
"responses": ["Happy to help!", "Any time!", "My pleasure"],
"context": [""]
},
{"tag": "noanswer",
"patterns": [],
"responses": ["Sorry, can't understand you", "Please give me more info", "Not sure I understand"],
"context": [""]
},
{"tag": "options",
"patterns": ["How you could help me?", "What you can do?", "What help you provide?", "How you can be helpful?", "What support is offered"],
"responses": ["I can guide you through Adverse drug reaction list, Blood pressure tracking, Hospitals and Pharmacies", "Offering support for Adverse drug reaction, Blood pressure, Hospitals and Pharmacies"],
"context": [""]
},
{"tag": "adverse_drug",
"patterns": ["How to check Adverse drug reaction?", "Open adverse drugs module", "Give me a list of drugs causing adverse behavior", "List all drugs suitable for patient with adverse reaction", "Which drugs dont have adverse reaction?" ],
"responses": ["Navigating to Adverse drug reaction module"],
"context": [""]
},
{"tag": "blood_pressure",
"patterns": ["Open blood pressure module", "Task related to blood pressure", "Blood pressure data entry", "I want to log blood pressure results", "Blood pressure data management" ],
"responses": ["Navigating to Blood Pressure module"],
"context": [""]
},
{"tag": "blood_pressure_search",
"patterns": ["I want to search for blood pressure result history", "Blood pressure for patient", "Load patient blood pressure result", "Show blood pressure results for patient", "Find blood pressure results by ID" ],
"responses": ["Please provide Patient ID", "Patient ID?"],
"context": ["search_blood_pressure_by_patient_id"]
},
{"tag": "search_blood_pressure_by_patient_id",
"patterns": [],
"responses": ["Loading Blood pressure result for Patient"],
"context": [""]
},
{"tag": "pharmacy_search",
"patterns": ["Find me a pharmacy", "Find pharmacy", "List of pharmacies nearby", "Locate pharmacy", "Search pharmacy" ],
"responses": ["Please provide pharmacy name"],
"context": ["search_pharmacy_by_name"]
},
{"tag": "search_pharmacy_by_name",
"patterns": [],
"responses": ["Loading pharmacy details"],
"context": [""]
},
{"tag": "hospital_search",
"patterns": ["Lookup for hospital", "Searching for hospital to transfer patient", "I want to search hospital data", "Hospital lookup for patient", "Looking up hospital details" ],
"responses": ["Please provide hospital name or location"],
"context": ["search_hospital_by_params"]
},
{"tag": "search_hospital_by_params",
"patterns": [],
"responses": ["Please provide hospital type"],
"context": ["search_hospital_by_type"]
},
{"tag": "search_hospital_by_type",
"patterns": [],
"responses": ["Loading hospital details"],
"context": [""]
}
]
}
main chat.py
import random
import json
import pickle
import numpy as np
import nltk
from tensorflow.keras.models import load_model
from nltk.stem import WordNetLemmatizer
import os
lemmatizer = WordNetLemmatizer()
file_path = os.path.join(os.path.dirname(__file__), '', 'intents.json')
intents = json.loads(open(file_path).read())
words = pickle.load(open('words.pkl', 'rb'))
classes = pickle.load(open('classes.pkl', 'rb'))
model = load_model('chatbotmodel.h5')
def clean_up_sentences(sentence):
sentence_words = nltk.word_tokenize(sentence)
sentence_words = [lemmatizer.lemmatize(word)
for word in sentence_words]
return sentence_words
def bagw(sentence):
sentence_words = clean_up_sentences(sentence)
bag = [0]*len(words)
for w in sentence_words:
for i, word in enumerate(words):
if word == w:
bag[i] = 1
return np.array(bag)
def predict_class(sentence):
bow = bagw(sentence)
res = model.predict(np.array([bow]))[0]
ERROR_THRESHOLD = 0.25
results = [[i, r] for i, r in enumerate(res)
if r > ERROR_THRESHOLD]
results.sort(key=lambda x: x[1], reverse=True)
return_list = []
for r in results:
return_list.append({'intent': classes[r[0]],
'probability': str(r[1])})
return return_list
def get_response(intents_list, intents_json):
tag = intents_list[0]['intent']
list_of_intents = intents_json['intents']
result = ""
for i in list_of_intents:
if i['tag'] == tag:
result = random.choice(i['responses'])
break
return result
print("Chatbot is up!")
while True:
message = input("")
ints = predict_class(message)
res = get_response(ints, intents)
print(res)
import nltk
import json
import pickle
from nltk.stem import WordNetLemmatizer
import os
import numpy as np
import random
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import SGD
file_path = os.path.join(os.path.dirname(__file__), '', 'intents.json')
lemmatizer = WordNetLemmatizer()
with open(file_path, "r") as file:
intents = json.load(file)
words = []
classes = []
documents = []
ignore_letters = ["?", "!", ".", ","]
for intent in intents['intents']:
for pattern in intent['patterns']:
# separating words from patterns
word_list = nltk.word_tokenize(pattern)
words.extend(word_list) # and adding them to words list
# associating patterns with respective tags
documents.append(((word_list), intent['tag']))
# appending the tags to the class list
if intent['tag'] not in classes:
classes.append(intent['tag'])
# storing the root words or lemma
words = [lemmatizer.lemmatize(word)
for word in words if word not in ignore_letters]
words = sorted(set(words))
# saving the words and classes list to binary filesclea
pickle.dump(words, open('words.pkl', 'wb'))
pickle.dump(classes, open('classes.pkl', 'wb'))
training = []
output_empty = [0]*len(classes)
for document in documents:
bag = []
word_patterns = document[0]
word_patterns = [lemmatizer.lemmatize(
word.lower()) for word in word_patterns]
for word in words:
if word in word_patterns:
bag.append(1)
else:
bag.append(0)
# making a copy of the output_empty
output_row = list(output_empty)
output_row[classes.index(document[1])] = 1
training.append([bag, output_row])
random.shuffle(training)
desired_length = 9
padded_sequences = [seq + [0] * (desired_length - len(seq)) for seq in training]
training = np.array(training, dtype=object)
# splitting the data
train_x = list(training[:, 0])
train_y = list(training[:, 1])
# creating a Sequential machine learning model
model = Sequential()
model.add(Dense(128, input_shape=(len(train_x[0]), ),
activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(len(train_y[0]),
activation='softmax'))
# compiling the model
sgd = SGD(learning_rate=0.01, weight_decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy',
optimizer=sgd, metrics=['accuracy'])
hist = model.fit(np.array(train_x), np.array(train_y),
epochs=200, batch_size=5, verbose=1)
# saving the model
model.save("chatbotmodel.h5", hist)
# print statement to show the
# successful training of the Chatbot model
print("Yay!")
Comments
Post a Comment