Skip to content
This repository has been archived by the owner on Aug 22, 2019. It is now read-only.

Commit

Permalink
Merge ef7d34b into 7451990
Browse files Browse the repository at this point in the history
  • Loading branch information
erohmensing committed Feb 21, 2019
2 parents 7451990 + ef7d34b commit 1e2b30a
Show file tree
Hide file tree
Showing 18 changed files with 243 additions and 109 deletions.
6 changes: 5 additions & 1 deletion docs/changelog.rst
@@ -1,6 +1,10 @@
:desc: Rasa Core Changelog

.. include:: ../CHANGELOG.rst
Added
-------
- Added `priority` property of policies to influence best policy in the case of equal confidence

Changed
-------
- Change payloads from "text" to "message" in files: server.yml, docs/connectors.rst, rasa_core/server.py, rasa_core/training/interactive.py, tests/test_interactive.py
- Change payloads from "text" to "message" in files: server.yml, docs/connectors.rst, rasa_core/server.py, rasa_core/training/interactive.py, tests/test_interactive.py
10 changes: 6 additions & 4 deletions rasa_core/actions/action.py
Expand Up @@ -10,7 +10,7 @@
from rasa_core.constants import (
DOCS_BASE_URL,
DEFAULT_REQUEST_TIMEOUT,
REQUESTED_SLOT, FALLBACK_SCORE, USER_INTENT_OUT_OF_SCOPE)
REQUESTED_SLOT, USER_INTENT_OUT_OF_SCOPE)
from rasa_core.events import (UserUtteranceReverted, UserUttered,
ActionExecuted, Event)
from rasa_core.utils import EndpointConfig
Expand Down Expand Up @@ -143,7 +143,8 @@ def __init__(self, name):
self._name = name

def run(self, dispatcher, tracker, domain):
"""Simple run implementation uttering a (hopefully defined) template."""
"""Simple run implementation uttering a (hopefully defined)
template."""

dispatcher.utter_template(self.name(),
tracker)
Expand Down Expand Up @@ -416,15 +417,16 @@ def run(self, dispatcher: 'Dispatcher', tracker: 'DialogueStateTracker',


def has_user_affirmed(tracker: 'DialogueStateTracker') -> bool:
return tracker.last_executed_action_has(ACTION_DEFAULT_ASK_AFFIRMATION_NAME)
return tracker.last_executed_action_has(
ACTION_DEFAULT_ASK_AFFIRMATION_NAME)


def _revert_affirmation_events(tracker: 'DialogueStateTracker') -> List[Event]:
revert_events = _revert_single_affirmation_events()

last_user_event = tracker.get_last_event_for(UserUttered)
last_user_event = copy.deepcopy(last_user_event)
last_user_event.parse_data['intent']['confidence'] = FALLBACK_SCORE
last_user_event.parse_data['intent']['confidence'] = 1.0

# User affirms the rephrased intent
rephrased_intent = tracker.last_executed_action_has(
Expand Down
6 changes: 0 additions & 6 deletions rasa_core/constants.py
Expand Up @@ -16,12 +16,6 @@

DEFAULT_REQUEST_TIMEOUT = 60 * 5 # 5 minutes

MEMO_SCORE = 1.1

FALLBACK_SCORE = 1.2

FORM_SCORE = 1.3

REQUESTED_SLOT = 'requested_slot'

# start of special user message section
Expand Down
15 changes: 13 additions & 2 deletions rasa_core/policies/embedding_policy.py
@@ -1,6 +1,7 @@
from collections import namedtuple
import copy
import io
import json
import logging
import os
import warnings
Expand Down Expand Up @@ -139,6 +140,7 @@ def _standard_featurizer(cls):
def __init__(
self,
featurizer: Optional[FullDialogueTrackerFeaturizer] = None,
priority: int = 1,
encoded_all_actions: Optional[np.ndarray] = None,
graph: Optional[tf.Graph] = None,
session: Optional[tf.Session] = None,
Expand Down Expand Up @@ -167,7 +169,7 @@ def __init__(
raise TypeError("Passed tracker featurizer of type {}, "
"should be FullDialogueTrackerFeaturizer."
"".format(type(featurizer).__name__))
super(EmbeddingPolicy, self).__init__(featurizer)
super(EmbeddingPolicy, self).__init__(featurizer, priority)

# flag if to use the same embeddings for user and bot
try:
Expand Down Expand Up @@ -351,7 +353,7 @@ def _create_tf_session_data(self,
data_X: np.ndarray,
data_Y: Optional[np.ndarray] = None
) -> SessionData:
"""Combine all tf session related data into a namedtuple"""
"""Combine all tf session related data into a named tuple"""

X, slots, previous_actions = \
self._create_X_slots_previous_actions(data_X)
Expand Down Expand Up @@ -1369,6 +1371,11 @@ def persist(self, path: Text) -> None:

self.featurizer.persist(path)

meta = {"priority": self.priority}

meta_file = os.path.join(path, 'embedding_policy.json')
utils.dump_obj_as_json_to_file(meta_file, meta)

file_name = 'tensorflow_embedding.ckpt'
checkpoint = os.path.join(path, file_name)
utils.create_dir_for_file(checkpoint)
Expand Down Expand Up @@ -1442,6 +1449,9 @@ def load(cls, path: Text) -> 'EmbeddingPolicy':
if not os.path.exists(checkpoint + '.meta'):
return cls(featurizer=featurizer)

meta_file = os.path.join(path, "embedding_policy.json")
meta = json.loads(utils.read_file(meta_file))

tf_config_file = os.path.join(
path, "{}.tf_config.pkl".format(file_name))

Expand Down Expand Up @@ -1486,6 +1496,7 @@ def load(cls, path: Text) -> 'EmbeddingPolicy':
encoded_all_actions = pickle.load(f)

return cls(featurizer=featurizer,
priority=meta["priority"],
encoded_all_actions=encoded_all_actions,
graph=graph,
session=sess,
Expand Down
37 changes: 30 additions & 7 deletions rasa_core/policies/ensemble.py
Expand Up @@ -41,6 +41,8 @@ def __init__(self,
else:
self.action_fingerprints = {}

self._check_priorities()

@staticmethod
def _training_events_from_trackers(training_trackers):
events_metadata = defaultdict(set)
Expand All @@ -55,6 +57,22 @@ def _training_events_from_trackers(training_trackers):

return events_metadata

def _check_priorities(self) -> None:
"""Checks for duplicate policy priorities within PolicyEnsemble."""

priority_dict = defaultdict(list)
for p in self.policies:
priority_dict[p.priority].append(type(p).__name__)

for k, v in priority_dict.items():
if len(v) > 1:
logger.warning(("Found policies {} with same priority {} "
"in PolicyEnsemble. When personalizing "
"priorities, be sure to give all policies "
"different priorities. More information: "
"https://rasa.com/docs/core/"
"policies/").format(v, k))

def train(self,
training_trackers: List[DialogueStateTracker],
domain: Domain, **kwargs: Any) -> None:
Expand Down Expand Up @@ -220,8 +238,8 @@ def from_dict(cls, dictionary: Dict[Text, Any]) -> List[Policy]:
policies = dictionary.get('policies') or dictionary.get('policy')
if policies is None:
raise InvalidPolicyConfig("You didn't define any policies. "
"Please define them under 'policies:' in "
"your policy configuration file.")
"Please define them under 'policies:' "
"in your policy configuration file.")
if len(policies) == 0:
raise InvalidPolicyConfig("The policy configuration file has to "
"include at least one policy.")
Expand Down Expand Up @@ -254,9 +272,9 @@ def from_dict(cls, dictionary: Dict[Text, Any]) -> List[Policy]:
policy_object = constr_func(**policy)
parsed_policies.append(policy_object)
except(ImportError, AttributeError):
raise InvalidPolicyConfig("Module for policy '{}' could not be "
"loaded. Please make sure the name "
"is a valid policy."
raise InvalidPolicyConfig("Module for policy '{}' could not "
"be loaded. Please make sure the "
"name is a valid policy."
"".format(policy_name))

return parsed_policies
Expand Down Expand Up @@ -315,17 +333,22 @@ def probabilities_using_best_policy(self,
result = None
max_confidence = -1
best_policy_name = None
best_policy_priority = -1

for i, p in enumerate(self.policies):
probabilities = p.predict_action_probabilities(tracker, domain)

if isinstance(tracker.events[-1], ActionExecutionRejected):
probabilities[domain.index_for_action(
tracker.events[-1].action_name)] = 0.0
confidence = np.max(probabilities)
if confidence > max_confidence:

if (confidence, p.priority) > (max_confidence,
best_policy_priority):
max_confidence = confidence
result = probabilities
best_policy_name = 'policy_{}_{}'.format(i, type(p).__name__)
best_policy_priority = p.priority

if (result.index(max_confidence) ==
domain.index_for_action(ACTION_LISTEN_NAME) and
Expand Down Expand Up @@ -355,7 +378,7 @@ def probabilities_using_best_policy(self,
fallback_idx,
type(fallback_policy).__name__)

# normalize probablilities
# normalize probabilities
if np.sum(result) != 0:
result = result / np.nansum(result)

Expand Down
12 changes: 5 additions & 7 deletions rasa_core/policies/fallback.py
Expand Up @@ -6,7 +6,6 @@
from rasa_core.actions.action import ACTION_LISTEN_NAME

from rasa_core import utils
from rasa_core.constants import FALLBACK_SCORE
from rasa_core.domain import Domain
from rasa_core.policies.policy import Policy
from rasa_core.trackers import DialogueStateTracker
Expand All @@ -26,6 +25,7 @@ def _standard_featurizer():
return None

def __init__(self,
priority: int = 3,
nlu_threshold: float = 0.3,
core_threshold: float = 0.3,
fallback_action_name: Text = "action_default_fallback"
Expand All @@ -42,8 +42,7 @@ def __init__(self,
predict fallback action with confidence 1.0.
fallback_action_name: name of the action to execute as a fallback
"""

super(FallbackPolicy, self).__init__()
super(FallbackPolicy, self).__init__(priority=priority)

self.nlu_threshold = nlu_threshold
self.core_threshold = core_threshold
Expand Down Expand Up @@ -72,7 +71,7 @@ def should_nlu_fallback(self,
return (nlu_confidence < self.nlu_threshold and
last_action_name == ACTION_LISTEN_NAME)

def fallback_scores(self, domain, fallback_score=FALLBACK_SCORE):
def fallback_scores(self, domain, fallback_score=1.0):
"""Prediction scores used if a fallback is necessary."""

result = [0.0] * domain.num_actions
Expand All @@ -99,15 +98,13 @@ def predict_action_probabilities(self,
if tracker.latest_action_name == self.fallback_action_name:
result = [0.0] * domain.num_actions
idx = domain.index_for_action(ACTION_LISTEN_NAME)
result[idx] = FALLBACK_SCORE
result[idx] = 1.0

elif self.should_nlu_fallback(nlu_confidence,
tracker.latest_action_name):
logger.debug("NLU confidence {} is lower "
"than NLU threshold {}. "
"".format(nlu_confidence, self.nlu_threshold))
# we set this to 1.2 to make sure fallback overrides
# the memoization policy
result = self.fallback_scores(domain)

else:
Expand All @@ -124,6 +121,7 @@ def persist(self, path: Text) -> None:

config_file = os.path.join(path, 'fallback_policy.json')
meta = {
"priority": self.priority,
"nlu_threshold": self.nlu_threshold,
"core_threshold": self.core_threshold,
"fallback_action_name": self.fallback_action_name
Expand Down
7 changes: 4 additions & 3 deletions rasa_core/policies/form_policy.py
Expand Up @@ -2,7 +2,6 @@
from typing import List, Optional, Dict, Text

from rasa_core.actions.action import ACTION_LISTEN_NAME
from rasa_core.constants import FORM_SCORE
from rasa_core.domain import PREV_PREFIX, ACTIVE_FORM_PREFIX, Domain
from rasa_core.events import FormValidation
from rasa_core.featurizers import TrackerFeaturizer
Expand All @@ -19,12 +18,14 @@ class FormPolicy(MemoizationPolicy):

def __init__(self,
featurizer: Optional[TrackerFeaturizer] = None,
priority: int = 4,
lookup: Optional[Dict] = None
) -> None:

# max history is set to 2 in order to capture
# previous meaningful action before action listen
super(FormPolicy, self).__init__(featurizer=featurizer,
priority=priority,
max_history=2,
lookup=lookup)

Expand Down Expand Up @@ -107,12 +108,12 @@ def predict_action_probabilities(self,
return result

idx = domain.index_for_action(tracker.active_form['name'])
result[idx] = FORM_SCORE
result[idx] = 1.0

elif tracker.latest_action_name == tracker.active_form.get('name'):
# predict action_listen after form action
idx = domain.index_for_action(ACTION_LISTEN_NAME)
result[idx] = FORM_SCORE
result[idx] = 1.0
else:
logger.debug("There is no active form")

Expand Down
7 changes: 5 additions & 2 deletions rasa_core/policies/keras_policy.py
Expand Up @@ -44,6 +44,7 @@ def _standard_featurizer(max_history=None):

def __init__(self,
featurizer: Optional[TrackerFeaturizer] = None,
priority: int = 1,
model: Optional[tf.keras.models.Sequential] = None,
graph: Optional[tf.Graph] = None,
session: Optional[tf.Session] = None,
Expand All @@ -53,7 +54,7 @@ def __init__(self,
) -> None:
if not featurizer:
featurizer = self._standard_featurizer(max_history)
super(KerasPolicy, self).__init__(featurizer)
super(KerasPolicy, self).__init__(featurizer, priority)

self._load_params(**kwargs)
self.model = model
Expand Down Expand Up @@ -236,7 +237,8 @@ def persist(self, path: Text) -> None:
if self.model:
self.featurizer.persist(path)

meta = {"model": "keras_model.h5",
meta = {"priority": self.priority,
"model": "keras_model.h5",
"epochs": self.current_epoch}

meta_file = os.path.join(path, 'keras_policy.json')
Expand Down Expand Up @@ -280,6 +282,7 @@ def load(cls, path: Text) -> 'KerasPolicy':
model = load_model(model_file)

return cls(featurizer=featurizer,
priority=meta["priority"],
model=model,
graph=graph,
session=session,
Expand Down

0 comments on commit 1e2b30a

Please sign in to comment.