Supervised Learning Tutorial¶
In this example we will create a restaurant search bot, by training a neural net on example conversations.
A user can contact the bot with something close to “I want a mexican restaurant!” and the bot will ask more details until it is ready to suggest a restaurant.
This assumes you already know what the
Action classes do.
If you don’t, it’s a good idea to read the basic tutorial first.
The training conversations come from the bAbI dialog task . However, the messages in these dialogues are machine generated, so we will augment this dataset with real user messages from the DSTC dataset. Lucky for us, this dataset is also in the restaurant domain.
the babi dataset is machine-generated, and there are a LOT of dialogues in there. There are 1000 stories in the training set, but you don’t need that many to build a useful bot. How much data you need depends on the number of actions you define, and the number of edge cases you want to support. But a few dozen stories is a good place to start.
Here’s an example conversation snippet:
## story_07715946 * _greet - action_ask_howcanhelp * _inform[location=rome,price=cheap] - action_on_it - action_ask_cuisine * _inform[cuisine=spanish] - action_ask_numpeople * _inform[people=six] - action_ack_dosearch ...
You can read about the Rasa data format here : Training Data Format.
It may be worth browsing through
data/babi_task5_trn_rasa_with_slots.md to get a sense of how these work.
We can also visualize that training data to generate a graph which is similar to a flow chart:
The chart shows the incoming user intents and entities and the action the bot is supposed to execute based on the stories from the training data. As you can see, flow charts get complicated quite quickly. Nevertheless, they can be a helpful tool in debugging a bot. More information can be found in Visualization of story training data.
Training your bot¶
We can go directly from data to bot with only a few steps:
- train a Rasa NLU model to extract intents and entities. Read more about that in the NLU docs.
- train a dialogue policy which will learn to choose the correct actions
- set up an agent which has both model 1 and model 2 working together to go directly from user input to action
We will go through these steps one by one.
1. Train NLU model¶
train_nlu.py program looks like this:
def train_babi_nlu(): training_data = load_data('examples/babi/data/franken_data.json') trainer = Trainer(RasaNLUConfig("examples/babi/data/config_nlu.json")) trainer.train(training_data) model_directory = trainer.persist('examples/babi/models/nlu/', model_name=model_name) return model_directory
You can learn all about Rasa NLU starting from the
What you need to know though is that
a dictionary with the intent and entities from a user message.
This step takes approximately 18 seconds on a 2014 MacBook Pro.
2. Train Dialogue Policy¶
Now our bot needs to learn what to do in response to these messages.
We do this by training the Rasa Core model. From
def train_babi_dm(): training_data_file = 'examples/babi/data/babi_task5_trn_rasa_with_slots.md' model_path = 'examples/babi/models/policy/current' agent = Agent("examples/restaurant_domain.yml", policies=[MemoizationPolicy(), RestaurantPolicy()]) agent.train( training_data_file, max_history=3, epochs=100, batch_size=50, augmentation_factor=50, validation_split=0.2 ) agent.persist(model_path)
This creates a
policy object. What you need to know is that
which action the bot should take next.
Here we’ll quickly explain the Domain and Policy objects, feel free to skip this if you don’t care, or read Plumbing - How it all fits together for more info.
Let’s start with
slots: cuisine: type: text people: type: text location: type: text price: type: text info: type: text matches: type: list intents: - greet - affirm - deny - inform - thankyou - request_info entities: - location - info - people - price - cuisine templates: utter_greet: - "hey there!" utter_goodbye: - "goodbye :(" utter_default: - "default message" utter_ack_dosearch: - "ok let me see what I can find" utter_ack_findalternatives: - "ok let me see what else there is" utter_ack_makereservation: - "ok making a reservation" utter_ask_cuisine: - "what kind of cuisine would you like?" utter_ask_helpmore: - "is there anything more that I can help with?" utter_ask_howcanhelp: - "how can I help you?" utter_ask_location: - "in which city?" utter_ask_moreupdates: - "anything else you'd like to modify?" utter_ask_numpeople: - "for how many people?" utter_ask_price: - "in which price range?" utter_on_it: - "I'm on it" actions: - utter_greet - utter_goodbye - utter_default - utter_ack_dosearch - utter_ack_findalternatives - utter_ack_makereservation - utter_ask_cuisine - utter_ask_helpmore - utter_ask_howcanhelp - utter_ask_location - utter_ask_moreupdates - utter_ask_numpeople - utter_ask_price - utter_on_it - examples.restaurant_example.ActionSearchRestaurants - examples.restaurant_example.ActionSuggest
Domain has clearly defined
slots (in our case criterion for target restaurant) and
(what the user can send). It also requires
templates to have text to use to respond given a certain
Each of these
actions must either be named after an utterance (dropping the
or must be a module path to an action. Here is the code for one the two custom actions:
from rasa_core.actions import Action class ActionSearchRestaurants(Action): def name(self): return 'search_restaurants' def run(self, dispatcher, tracker, domain): dispatcher.utter_message("here's what I found") return 
name method is to match up actions to utterances, and the
run command is run whenever the action is called. This
may involve api calls or internal bot dynamics.
class RestaurantPolicy(KerasPolicy): def _build_model(self, num_features, num_actions, max_history_len): """Build a keras model and return a compiled model. :param max_history_len: The maximum number of historical turns used to decide on next action""" from keras.layers import LSTM, Activation, Masking, Dense from keras.models import Sequential n_hidden = 32 # size of hidden layer in LSTM # Build Model batch_shape = (None, max_history_len, num_features) model = Sequential() model.add(Masking(-1, batch_input_shape=batch_shape)) model.add(LSTM(n_hidden, batch_input_shape=batch_shape)) model.add(Dense(input_dim=n_hidden, output_dim=num_actions)) model.add(Activation('softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) logger.debug(model.summary()) return model
This policy builds an LSTM in Keras which will then be taken by the trainer and trained. The parameters
n_hidden may be altered dependent on the task complexity and the amount of data one has.
important as it is the amount of story steps the network has access to to make a classification.
Now we can simply run
python train_dm.py to get our trained policy.
This step takes roughly 12 minutes on a 2014 MacBook Pro
Using your bot¶
Now we have a trained NLU and DM model which can be merged together to make a bot. This is done using an
def run_babi(serve_forever=True): agent = Agent.load("examples/babi/models/policy/current", interpreter=RasaNLUInterpreter(nlu_model_path)) if serve_forever: agent.handle_channel(ConsoleInputChannel()) return agent
We put the NLU model into an
Interpreter and then put that into an
You now have a working bot! It will recommend you the same place (papi’s pizza place) no matter what preferences you give, but at least its trying!