Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add normalisation to confidence scores #4902

Merged
merged 41 commits into from
Jan 23, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9f05641
add normalisation to confidence scores"
akelad Nov 28, 2019
d0a50b7
add configuration parameter for normalization
akelad Dec 4, 2019
5d7df4d
fix label confidence not being updated
erohmensing Dec 12, 2019
4466052
make number to normalize configurable, report that num
erohmensing Dec 12, 2019
606e578
add normalization for embedding policy
erohmensing Dec 13, 2019
792eeb1
remove debug logging
erohmensing Dec 13, 2019
13cb22a
Merge branch 'master' into add_softmax_normalisation
erohmensing Dec 17, 2019
4996ca2
persist policy loss type
erohmensing Dec 17, 2019
efa399f
add response selector, update default, rename parameter
erohmensing Dec 17, 2019
93ac058
clean up code
erohmensing Dec 17, 2019
0d13ae4
changelog and docs
erohmensing Dec 17, 2019
0965009
fix policy tests
erohmensing Dec 17, 2019
4885ae5
clean up numpy
erohmensing Dec 17, 2019
71e6a68
migration/changelog
erohmensing Dec 17, 2019
a3fa955
Merge branch 'master' into add_softmax_normalisation
erohmensing Dec 17, 2019
c98b7b9
Merge branch 'master' into add_softmax_normalisation
erohmensing Dec 18, 2019
73f932e
tests for embedding policy normalization
erohmensing Dec 18, 2019
292ee54
truncate output as decided
erohmensing Dec 18, 2019
7a9879c
tests for embeddingintentclassifier normalization
erohmensing Dec 18, 2019
f55eaa1
use random seed for stable test results
erohmensing Dec 18, 2019
0f8dbc0
move normalization method to utils
erohmensing Dec 18, 2019
2087fc8
truncate labels
erohmensing Dec 18, 2019
3528a1c
Merge branch 'master' into add_softmax_normalisation
erohmensing Dec 18, 2019
36e6857
Merge branch 'master' into add_softmax_normalisation
erohmensing Jan 15, 2020
1c35df9
move migration content
erohmensing Jan 15, 2020
5da58cb
move normalization to calculate_message_sim
erohmensing Jan 15, 2020
7d1911a
make normalization method more general
erohmensing Jan 15, 2020
d6395cd
add test for margin loss type not undergoing normalization
erohmensing Jan 15, 2020
1ac4bea
update changelog
erohmensing Jan 15, 2020
90d6192
use hardcoded default ranking length in test
erohmensing Jan 15, 2020
4271286
use old method for truncating
erohmensing Jan 15, 2020
a0fc186
handle some edge cases
erohmensing Jan 15, 2020
fecb858
explicitly do not call normalization if ranking length <1
erohmensing Jan 15, 2020
bb9cd0c
use correct attribute
erohmensing Jan 15, 2020
1103fb0
Merge branch 'master' into add_softmax_normalisation
Ghostvv Jan 23, 2020
7d933ee
Update rasa/nlu/classifiers/embedding_intent_classifier.py
Ghostvv Jan 23, 2020
ad2beab
Update rasa/core/policies/embedding_policy.py
Ghostvv Jan 23, 2020
d76438f
Update rasa/utils/train_utils.py
Ghostvv Jan 23, 2020
1741307
black
Ghostvv Jan 23, 2020
f57624a
don't mutate an argument
Ghostvv Jan 23, 2020
97662f1
add norm test
Ghostvv Jan 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions rasa/core/policies/embedding_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ class EmbeddingPolicy(Policy):
"similarity_type": "auto", # string 'auto' or 'cosine' or 'inner'
# the type of the loss function
"loss_type": "softmax", # string 'softmax' or 'margin'
# number of top actions to normalize scores for softmax loss_type
# set to 0 to turn off normalization
"normalize_top_num_actions": 0,
erohmensing marked this conversation as resolved.
Show resolved Hide resolved
# how similar the algorithm should try
# to make embedding vectors for correct labels
"mu_pos": 0.8, # should be 0.0 < ... < 1.0 for 'cosine'
Expand Down Expand Up @@ -192,6 +195,7 @@ def _load_embedding_params(self, config: Dict[Text, Any]) -> None:
self.similarity_type = "inner"
elif self.loss_type == "margin":
self.similarity_type = "cosine"
self.normalize_top_num_actions = config["normalize_top_num_actions"]

self.mu_pos = config["mu_pos"]
self.mu_neg = config["mu_neg"]
Expand Down Expand Up @@ -556,8 +560,17 @@ def predict_action_probabilities(
tf_feed_dict = self.tf_feed_dict_for_prediction(tracker, domain)

confidence = self.session.run(self.pred_confidence, feed_dict=tf_feed_dict)
confidence = confidence[0, -1, :].tolist()
erohmensing marked this conversation as resolved.
Show resolved Hide resolved

return confidence[0, -1, :].tolist()
# normalise scores if turned on
if self.loss_type == "softmax" and self.normalize_top_num_actions > 0:
erohmensing marked this conversation as resolved.
Show resolved Hide resolved
ranked = sorted(confidence, reverse=True)
for i, value in enumerate(confidence):
if value < ranked[self.normalize_top_num_actions - 1]:
confidence[i] = 0.0
confidence = confidence / np.sum(confidence)
erohmensing marked this conversation as resolved.
Show resolved Hide resolved

return confidence

def persist(self, path: Text) -> None:
"""Persists the policy to a storage."""
Expand All @@ -572,7 +585,10 @@ def persist(self, path: Text) -> None:

self.featurizer.persist(path)

meta = {"priority": self.priority}
meta = {
"priority": self.priority,
"normalize_top_num_actions": self.normalize_top_num_actions,
}

meta_file = os.path.join(path, "embedding_policy.json")
rasa.utils.io.dump_obj_as_json_to_file(meta_file, meta)
Expand Down Expand Up @@ -654,7 +670,7 @@ def load(cls, path: Text) -> "EmbeddingPolicy":

return cls(
featurizer=featurizer,
priority=meta["priority"],
priority=meta.pop("priority"),
graph=graph,
session=session,
user_placeholder=a_in,
Expand All @@ -666,4 +682,5 @@ def load(cls, path: Text) -> "EmbeddingPolicy":
bot_embed=bot_embed,
all_bot_embed=all_bot_embed,
attention_weights=attention_weights,
**meta,
)
2 changes: 1 addition & 1 deletion rasa/nlu/classifiers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# How many labels are at max put into the output
# ranking, everything else will be cut off
LABEL_RANKING_LENGTH = 10
DEFAULT_LABEL_RANKING_LENGTH = 10
erohmensing marked this conversation as resolved.
Show resolved Hide resolved
26 changes: 19 additions & 7 deletions rasa/nlu/classifiers/embedding_intent_classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Any, Dict, List, Optional, Text, Tuple
import warnings

from rasa.nlu.classifiers import LABEL_RANKING_LENGTH
from rasa.nlu.classifiers import DEFAULT_LABEL_RANKING_LENGTH
from rasa.nlu.components import Component
from rasa.utils import train_utils
from rasa.nlu.constants import (
Expand Down Expand Up @@ -82,6 +82,9 @@ class EmbeddingIntentClassifier(Component):
"similarity_type": "auto", # string 'auto' or 'cosine' or 'inner'
# the type of the loss function
"loss_type": "softmax", # string 'softmax' or 'margin'
# number of top intents to normalize scores for softmax loss_type
# set to 0 to turn off normalization
"normalize_top_num_intents": 0,
erohmensing marked this conversation as resolved.
Show resolved Hide resolved
# how similar the algorithm should try
# to make embedding vectors for correct labels
"mu_pos": 0.8, # should be 0.0 < ... < 1.0 for 'cosine'
Expand Down Expand Up @@ -153,7 +156,8 @@ def __init__(
self._is_training = None

# config migration warning
def _check_old_config_variables(self, config: Dict[Text, Any]) -> None:
@staticmethod
def _check_old_config_variables(config: Dict[Text, Any]) -> None:

removed_tokenization_params = [
"intent_tokenization_flag",
Expand Down Expand Up @@ -206,6 +210,7 @@ def _load_embedding_params(self, config: Dict[Text, Any]) -> None:
elif self.loss_type == "margin":
self.similarity_type = "cosine"

self.normalize_top_num_intents = config["normalize_top_num_intents"]
self.mu_pos = config["mu_pos"]
self.mu_neg = config["mu_neg"]
self.use_max_sim_neg = config["use_max_sim_neg"]
Expand Down Expand Up @@ -617,17 +622,24 @@ def predict_label(self, message):

# if X contains all zeros do not predict some label
if X.any() and label_ids.size > 0:
label = {
"name": self.inverted_label_dict[label_ids[0]],
"confidence": message_sim[0],
}

# normalise scores if turned on
if self.loss_type == "softmax" and self.normalize_top_num_intents > 0:
label_ids = label_ids[: self.normalize_top_num_intents]
erohmensing marked this conversation as resolved.
Show resolved Hide resolved
message_sim = message_sim[: self.normalize_top_num_intents]
message_sim = message_sim / np.sum(message_sim)
else:
label_ids = label_ids[:DEFAULT_LABEL_RANKING_LENGTH]
Ghostvv marked this conversation as resolved.
Show resolved Hide resolved
message_sim = message_sim[:DEFAULT_LABEL_RANKING_LENGTH]
erohmensing marked this conversation as resolved.
Show resolved Hide resolved
ranking = list(zip(list(label_ids), message_sim))
ranking = ranking[:LABEL_RANKING_LENGTH]
label_ranking = [
{"name": self.inverted_label_dict[label_idx], "confidence": score}
for label_idx, score in ranking
]
label = {
"name": self.inverted_label_dict[label_ids[0]],
"confidence": message_sim[0],
}
return label, label_ranking

def process(self, message: "Message", **kwargs: Any) -> None:
Expand Down
4 changes: 2 additions & 2 deletions rasa/nlu/classifiers/sklearn_intent_classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Any, Dict, List, Optional, Text, Tuple

from rasa.nlu import utils
from rasa.nlu.classifiers import LABEL_RANKING_LENGTH
from rasa.nlu.classifiers import DEFAULT_LABEL_RANKING_LENGTH
from rasa.nlu.components import Component
from rasa.nlu.config import RasaNLUModelConfig
from rasa.nlu.model import Metadata
Expand Down Expand Up @@ -155,7 +155,7 @@ def process(self, message: Message, **kwargs: Any) -> None:

if intents.size > 0 and probabilities.size > 0:
ranking = list(zip(list(intents), list(probabilities)))[
:LABEL_RANKING_LENGTH
:DEFAULT_LABEL_RANKING_LENGTH
]

intent = {"name": intents[0], "confidence": probabilities[0]}
Expand Down