Skip to content
This repository has been archived by the owner on Nov 8, 2018. It is now read-only.

ChatBot Documentation

Mayank Lunayach edited this page Aug 13, 2018 · 12 revisions

About

PC-Prep-Kit is a web app that aims to teach Peace Corps volunteers Malaria Prevention techniques before they go for remote missions. App has many interactive games to ensure that. It was realised that a ChatBot which would answer Malaria-related queries would be further fulfilling the purpose.

Bot Domain

The bot would be context specific. In our case, it would be everything related to Malaria. We may not care about questions other than malaria.

Setting up Local Environment

Requirements

Recommended Installation Pathway

  • Install Anaconda.

  • Install Tensorflow. The ML backend to train our models.

    conda install tensorflow==1.8

  • Install Pip. Package manager to manage Python libraries.

    conda install pip

  • Install Rasa_Core. The framework to build our dialogue model.

    pip install rasa_core

  • Install Rasa_NLU. The NLP backend used for Rasa_Core.

    pip install rasa_nlu

  • Install Tensorflow Embedding layer for intent classification.

    pip install rasa_nlu[tensorflow]

  • Install MNITIE backend for entity extraction.

    pip install git+https://github.com/mit-nlp/MITIE.git

    pip install rasa_nlu[mitie]

  • Download the MITIE models from here. We require this file,total_word_feature_extractor.dat. Save it somewhere and make a note of its path.

Getting Started

  • Fork PC-Prep-Kit repository and clone it locally.
  • Go to the directory, /ChatBot/ and copy the above downloaded file, i.e total_word_feature_extractor.dat here.
  • Open the terminal here, i.e. in /ChatBot/ and you can type make run. It will open the bot in the terminal. And, that's it. You can now start interacting with it.

To ease out the development process, we have a MakeFile in place that maps Make commands to Python commands.

List of available commands:

Command Description
make run Launches bot in the terminal. Only text Input/Output supported.
make train-nlu Trains the NLP model using the backend installed above. Uses dialogues stored in data/nlu_data/ directory. You can tweak the existing processing pipeline in nlu_model_config.yml.
make train-dialogue Trains the Dialogue model using custom functions created in /bot.py/
make train-dialogue-default Trains the Dialogue model using the default configuration.
make describe Exports the current design to the /graph.png to help visualise the current design.
make learn Starts the Reinforcement Learning mode. You can tweak the current training structure in the file, bot.py.
make server Starts serving the API on port 5005. To change the port number, add the flag p followed by desired port number under the make server command in Makefile.

Working

The framework is based on this research paper.

Broadly, we have two components.

Rasa_NLU

A framework to apply Natural Language Processing on the users' dialogues. The user can say a particular thing in many different ways. We'll end up responding in the same way or taking the same action. Example,

how do I prevent malaria

any way of preventing malaria?

So, to find the Intent of what the user is saying, we are using Rasa_NLU. Intent classification is not the alone aim here, we also want to extract information based on what the user is saying. Example

I am 18 years old.

I should be able to extract 18 from the statement in order to respond to query better. This is called Entity Extraction.

We are not doing this whole thing i.e Intent classification, Entity Extraction etc. in one go! Rather, we have a processing pipeline for that. That is, all these things happen step by step. The output of one component becomes the input of other and so on. We are free to choose all the components of our choice. All components have been listed in /nlu_model_config.yml. In simple terms, this acts as the config file for the command make train-nlu.

Current pipeline is like this,

language: "en"

pipeline:
- name: "nlp_mitie"
  model: "total_word_feature_extractor.dat"
- name: "tokenizer_mitie"
- name: "ner_mitie"
- name: "ner_synonyms"
- name: "intent_entity_featurizer_regex"
- name: "intent_featurizer_count_vectors"
- name: "intent_classifier_tensorflow_embedding"

For entity extraction, out of options from MITIE, crfsuite, spaCy and duckling. MNITIE worked best for our case. It performed well in extracting intent from dialogues that were not used for training.

For intent classification, Tensorflow embedding performed very well. Other alternatives like spaCy were giving the classification accuracy of 80% or below even for the exact training examples. This was not the case with Tensorflow embedding.

Also, MITIE may be used for intent classification. But training becomes very slow while using it.

Other options may also be tried. They are listed here.

Rasa_Core

After we extract the entity and classify the intent, bot shall decide what action it has to take or simply what responses it has to give. For this, we need to train the dialogue model.

But how do we add actual dialogues and responses to train the model?

We do this by writing compactly written dialogue set also called, stories. They are located in /data/stories.md. They are written in the markdown file in the following manner,

  • Each intent follows the symbol * and we just need to write the name of the intent.
  • What actions that are needed to be taken are written in an unordered list each followed preceded by symbol -
  • It can be then followed by either an intent or an action.

Example, one of the story blocks:

## Some Story Name
* medicines
    - utter_ask_gender
* Female
    - utter_ask_pregnant
* affirm
    - utter_ask_age
* safeMedicine{"age": "18"}
    - slot{"age": "18"}
    - action_suggest_pregnant

Here, medicines, Female, affirm, safeMedicine were intents.

utter_ask_gender, utter_ask_pregnant, utter_ask_age, action_suggest_pregnant were actions, and age was a slot

Training the Model: The model can be trained in two ways. Either by using the default parameters that come ready with the Rasa or defining your own custom model. We have defined a custom Keras model in /bot.py/. Currently it has the following structure.

Layer (type)                 Output Shape              Param #   
=================================================================
masking_1 (Masking)          (None, 5, 44)             0         
_________________________________________________________________
lstm_1 (LSTM)                (None, 32)                9856      
_________________________________________________________________
dense_1 (Dense)              (None, 22)                726       
_________________________________________________________________
activation_1 (Activation)    (None, 22)                0         
=================================================================
Total params: 10,582
Trainable params: 10,582
Non-trainable params: 0
________________________

You may go ahead and learn more about these layers.

Defining custom actions: In a complex application, we will not have only straight forward question-answering. Rather, we may sometimes need to respond to the user based on the conversation history. Example this sequence,

User: Suggest me medicines to prevent malaria
Bot: May I know your gender please?
User: female
Bot: Are you Pregnant? Please answer in yes or no
User: yes
Bot: May I know your age please

As you may see, what the bot asks the user is very much dependent on how the user is responding. In this case, pregnant. In our bot, we take care of these cases using Python functions. These functions uses Action class and are written in bot.py file. Example of one such function,

class ActionSuggestPregnant(Action):
    @classmethod
    def name(self):
        return 'action_suggest_pregnant'

    @classmethod
    def run(self, dispatcher, tracker, domain):
        medicines = ['Chloroquine', 'Doxycycline', 'Mefloquine']
        age = int(tracker.get_slot("age"))
        if age < 8:
            medicines.remove("Doxycycline")
        dispatcher.utter_message("You can take these medicines:\n" + ' '.join(medicines))
        return []

Here, we are simply suggesting a medicine based on the user's age. This function may look intimidating at the first look. Lets try to break it into components:

  • Action: It is Python class that comes with Rasa_Core and we had imported it using from rasa_core.actions import Action. This dictates the next action that the bot will take.
  • Name: It is a unique identifier of this action. We call the action method using this name, while defining our stories in stories.md.
  • Run: It executes the actual effect of an action.
  • Tracker: As the name suggests, it keeps the track of the conversion state. It is who which detects a new message.
  • Dispatcher allows us to send the message back to the user. In our case, we had sent "You can take these medicines...." message.

A broad view of how everything works, blank diagram

More detailed documentation about how everything fits, can be found here.