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

Monster ted #7262

Merged
merged 68 commits into from
Nov 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
8279a17
add diet to ted
Ghostvv Oct 20, 2020
3ef6892
merge e2e
Ghostvv Oct 21, 2020
e4f795a
reshape 4d tensors into 3d and back
Ghostvv Oct 22, 2020
0a97001
fix shapes in non eager mode
Ghostvv Oct 22, 2020
f904e46
make shape indices more general
Ghostvv Oct 22, 2020
dd576e2
fix add_length
tabergma Oct 22, 2020
45a2982
add todo
tabergma Oct 23, 2020
6e64bd7
sentence features are now also 4D
tabergma Oct 23, 2020
94c0fa9
sequence length is 4D
tabergma Oct 23, 2020
5263b60
convert 4d to 3 during padding
tabergma Oct 23, 2020
b5d479b
mask is 4d now
tabergma Oct 23, 2020
df2ccc3
bring mask in correct shape before transformer
tabergma Oct 23, 2020
9813014
keep also the orginial dialogue length
tabergma Oct 23, 2020
71c527f
update doc strings
tabergma Oct 23, 2020
f4c119a
use tf.scatter_nd to tranform 3d back to 4d
tabergma Oct 26, 2020
cbb1324
Merge branch '4d-3d-vectors' into diet-ted
tabergma Oct 27, 2020
886ab01
move tensor transformation to _encode_features_per_attribute
tabergma Oct 27, 2020
098e441
fix issues in _encode_features_per_attribute
tabergma Oct 27, 2020
94e0d81
use correct dialogue length
tabergma Oct 27, 2020
0326662
add comments
tabergma Oct 27, 2020
03cc881
clean up
tabergma Oct 28, 2020
788a3ee
Merge branch 'e2e' into diet-ted
tabergma Oct 28, 2020
2cb13f5
update constants
tabergma Oct 28, 2020
4d2b5a1
review comment
tabergma Oct 28, 2020
6525b8e
keep entity dict
tabergma Oct 28, 2020
f4aec12
create tag_ids for TED
tabergma Oct 28, 2020
d773f12
Merge branch 'e2e' into entities-in-ted
tabergma Nov 3, 2020
2fd1c52
clean up after merge
tabergma Nov 3, 2020
5fe3f30
Merge branch 'e2e' into entities-in-ted
tabergma Nov 5, 2020
62d8bab
add batch_loss_entities (not working)
tabergma Nov 5, 2020
e50f4eb
concatenate text and dialogue transformer output
tabergma Nov 5, 2020
2833ef5
get last dialogue before CRF
tabergma Nov 5, 2020
ff6f002
add predicting entities
tabergma Nov 5, 2020
5b46f40
clean up
tabergma Nov 5, 2020
906ff97
differentiate between max history tracker featurizer used or not
tabergma Nov 5, 2020
c0eaa70
add todo
tabergma Nov 5, 2020
9239bfa
add comments
tabergma Nov 5, 2020
76b41ee
use correct tag id mapping
tabergma Nov 5, 2020
58fc4ad
check if text exists
tabergma Nov 6, 2020
74be410
fix frozenset issues
tabergma Nov 6, 2020
90feabe
ignore actual entity value in MemoizationPolicy
tabergma Nov 6, 2020
6a5efc3
fix import
tabergma Nov 9, 2020
ccd93d1
fix some tests
tabergma Nov 9, 2020
8dff41c
Merge branch 'e2e' into entities-in-ted
tabergma Nov 9, 2020
f3e2b89
update after merge
tabergma Nov 10, 2020
adea49e
use python if instead of tf.cond
tabergma Nov 10, 2020
d3bd22d
we need to return a tensor in tf.cond instead of None
tabergma Nov 10, 2020
cd69de9
create entity tags for all texts
tabergma Nov 10, 2020
8e8af87
update batch loss entities (not yet working)
tabergma Nov 10, 2020
d1f7e97
input to entity loss
Ghostvv Nov 11, 2020
d9a5378
update entity prediction
Ghostvv Nov 12, 2020
c287d8c
fix randomness and shapes
Ghostvv Nov 12, 2020
f87c134
fix ffnn encoding layer name
Ghostvv Nov 12, 2020
bfc2571
add todo
Ghostvv Nov 12, 2020
53b2159
Update rasa/core/policies/ted_policy.py
Ghostvv Nov 12, 2020
05639b4
Update rasa/core/featurizers/single_state_featurizer.py
Ghostvv Nov 12, 2020
4e873f9
rename to entity_tag_id_mapping
Ghostvv Nov 12, 2020
563085b
add comment to last dial mask
Ghostvv Nov 12, 2020
4a97b0b
add comments to tf.cond
Ghostvv Nov 12, 2020
d2db715
add docstrings
Ghostvv Nov 12, 2020
b3b28d7
refactor number of dims check
Ghostvv Nov 12, 2020
779db7f
rename zero features to fake features
Ghostvv Nov 13, 2020
ee85c17
pre compute dialogue_indices
Ghostvv Nov 13, 2020
fc48d4a
create helper methods
Ghostvv Nov 13, 2020
419f90c
calculate number of units for text_transformer_output
Ghostvv Nov 13, 2020
229723a
add todo
Ghostvv Nov 13, 2020
76ca209
fix tests
Ghostvv Nov 13, 2020
ce4098e
use indices constant
Ghostvv Nov 13, 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
2 changes: 1 addition & 1 deletion examples/e2ebot/data/stories.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ stories:

- story: sad path (text to text)
steps:
- user: "Hello"
- user: "[Hello](bla)"
Ghostvv marked this conversation as resolved.
Show resolved Hide resolved
- bot: "Welcome to moodbot. How are you feeling today?"
- user: "Horrible"
- bot: "Oh no! Here is a kitten photo. Did it help?"
Expand Down
5 changes: 4 additions & 1 deletion examples/e2ebot/domain.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
version: "2.0"

# TODO create a bot that makes sense
actions:
- utter_greet
- utter_happy
Expand All @@ -9,3 +9,6 @@ actions:
intents:
- greet
- mood_great

entities:
- bla
74 changes: 72 additions & 2 deletions rasa/core/featurizers/single_state_featurizer.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import logging
import numpy as np
import scipy.sparse
from typing import List, Optional, Dict, Text, Set
from typing import List, Optional, Dict, Text, Set, Any
from collections import defaultdict

import rasa.shared.utils.io
from rasa.nlu.constants import TOKENS_NAMES
from rasa.shared.core.domain import SubState, State, Domain
from rasa.shared.nlu.interpreter import NaturalLanguageInterpreter
from rasa.shared.core.constants import PREVIOUS_ACTION, ACTIVE_LOOP, USER, SLOTS
Expand All @@ -16,9 +17,15 @@
ACTION_TEXT,
ACTION_NAME,
INTENT,
TEXT,
NO_ENTITY_TAG,
ENTITY_ATTRIBUTE_TYPE,
ENTITY_TAGS,
)
from rasa.shared.nlu.training_data.features import Features
from rasa.shared.nlu.training_data.message import Message
from rasa.utils.tensorflow.model_data_utils import TAG_ID_ORIGIN
from rasa.utils.tensorflow.constants import IDS

logger = logging.getLogger(__name__)

Expand All @@ -36,6 +43,23 @@ class SingleStateFeaturizer:
def __init__(self) -> None:
self._default_feature_states = {}
self.action_texts = []
self.entity_tag_id_mapping = {}

def get_entity_tag_ids(self) -> Dict[Text, int]:
"""Returns the tag to index mapping for entities.

Returns:
Tag to index mapping.
"""
if ENTITIES not in self._default_feature_states:
return {}

tag_ids = {
tag: idx + 1 # +1 to keep 0 for the NO_ENTITY_TAG
for tag, idx in self._default_feature_states[ENTITIES].items()
}
tag_ids[NO_ENTITY_TAG] = 0
return tag_ids

def prepare_from_domain(self, domain: Domain) -> None:
"""Gets necessary information for featurization from domain.
Expand All @@ -55,6 +79,7 @@ def convert_to_dict(feature_states: List[Text]) -> Dict[Text, int]:
self._default_feature_states[SLOTS] = convert_to_dict(domain.slot_states)
self._default_feature_states[ACTIVE_LOOP] = convert_to_dict(domain.form_names)
self.action_texts = domain.action_texts
self.entity_tag_id_mapping = self.get_entity_tag_ids()

def _state_features_for_attribute(
self, sub_state: SubState, attribute: Text
Expand Down Expand Up @@ -84,7 +109,7 @@ def _create_features(

features = np.zeros(len(self._default_feature_states[attribute]), np.float32)
for state_feature, value in state_features.items():
# check that the value is in default_feature_states to be able to assigh
# check that the value is in default_feature_states to be able to assign
# its value
if state_feature in self._default_feature_states[attribute]:
features[self._default_feature_states[attribute][state_feature]] = value
Expand Down Expand Up @@ -215,6 +240,51 @@ def encode_state(

return state_features

def encode_entities(
self, entity_data: Dict[Text, Any], interpreter: NaturalLanguageInterpreter
) -> Dict[Text, List["Features"]]:
"""Encode the given entity data with the help of the given interpreter.

Produce numeric entity tags for tokens.

Args:
entity_data: The dict containing the text and entity labels and locations
interpreter: The interpreter used to encode the state

Returns:
A dictionary of entity type to list of features.
"""
from rasa.nlu.test import determine_token_labels

# TODO
# The entity states used to create the tag-idx-mapping contains the
# entities and the concatenated entity and roles/groups. We do not
# distinguish between entities and roles/groups right now.
# TODO
# Should we support BILOU tagging?

if TEXT not in entity_data or len(self.entity_tag_id_mapping) < 2:
# we cannot build a classifier if there are less than 2 class
return {}

parsed_text = interpreter.featurize_message(Message({TEXT: entity_data[TEXT]}))
entities = entity_data.get(ENTITIES, [])

_tags = []
for token in parsed_text.get(TOKENS_NAMES[TEXT]):
_tag = determine_token_labels(
token, entities, attribute_key=ENTITY_ATTRIBUTE_TYPE
)
# TODO handle if tag is not in mapping
_tags.append(self.entity_tag_id_mapping[_tag])

# transpose to have seq_len x 1
return {
ENTITY_TAGS: [
Features(np.array([_tags]).T, IDS, ENTITY_TAGS, TAG_ID_ORIGIN,)
]
}

def _encode_action(
self, action: Text, interpreter: NaturalLanguageInterpreter
) -> Dict[Text, List["Features"]]:
Expand Down
113 changes: 88 additions & 25 deletions rasa/core/featurizers/tracker_featurizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import jsonpickle
import logging

from rasa.shared.nlu.constants import TEXT, INTENT
from rasa.shared.nlu.constants import TEXT, INTENT, ENTITIES
from rasa.shared.exceptions import RasaException
from tqdm import tqdm
from typing import Tuple, List, Optional, Dict, Text, Union
from typing import Tuple, List, Optional, Dict, Text, Union, Any
import numpy as np

from rasa.core.featurizers.single_state_featurizer import SingleStateFeaturizer
from rasa.shared.core.domain import State, Domain
from rasa.shared.core.events import ActionExecuted
from rasa.shared.core.events import ActionExecuted, UserUttered
from rasa.shared.core.trackers import (
DialogueStateTracker,
is_prev_action_listen_in_state,
Expand Down Expand Up @@ -91,6 +91,43 @@ def _convert_labels_to_ids(
]
)

def _create_entity_tags(
self,
trackers_as_entities: List[List[Dict[Text, Any]]],
interpreter: NaturalLanguageInterpreter,
) -> List[List[Dict[Text, List["Features"]]]]:
return [
[
self.state_featurizer.encode_entities(entity_data, interpreter)
for entity_data in trackers_entities
]
for trackers_entities in trackers_as_entities
]

@staticmethod
def _entity_data(event: UserUttered) -> Dict[Text, Any]:
if event.text:
return {TEXT: event.text, ENTITIES: event.entities}

# input is not textual, so add empty dict
return {}

def training_states_actions_and_entities(
Ghostvv marked this conversation as resolved.
Show resolved Hide resolved
self, trackers: List[DialogueStateTracker], domain: Domain
) -> Tuple[List[List[State]], List[List[Text]], List[List[Dict[Text, Any]]]]:
"""Transforms list of trackers to lists of states, actions and entity data.

Args:
trackers: The trackers to transform
domain: The domain

Returns:
A tuple of list of states, list of actions and list of entity data.
"""
raise NotImplementedError(
"Featurizer must have the capacity to encode trackers to feature vectors"
)

def training_states_and_actions(
self, trackers: List[DialogueStateTracker], domain: Domain
) -> Tuple[List[List[State]], List[List[Text]]]:
Expand All @@ -103,16 +140,23 @@ def training_states_and_actions(
Returns:
A tuple of list of states and list of actions.
"""
raise NotImplementedError(
"Featurizer must have the capacity to encode trackers to feature vectors"
)
(
trackers_as_states,
trackers_as_actions,
_,
) = self.training_states_actions_and_entities(trackers, domain)
return trackers_as_states, trackers_as_actions

def featurize_trackers(
self,
trackers: List[DialogueStateTracker],
domain: Domain,
interpreter: NaturalLanguageInterpreter,
) -> Tuple[List[List[Dict[Text, List["Features"]]]], np.ndarray]:
) -> Tuple[
List[List[Dict[Text, List["Features"]]]],
np.ndarray,
List[List[Dict[Text, List["Features"]]]],
]:
"""Featurize the training trackers.

Args:
Expand All @@ -137,14 +181,17 @@ def featurize_trackers(

self.state_featurizer.prepare_from_domain(domain)

trackers_as_states, trackers_as_actions = self.training_states_and_actions(
trackers, domain
)
(
trackers_as_states,
trackers_as_actions,
trackers_as_entities,
) = self.training_states_actions_and_entities(trackers, domain)

tracker_state_features = self._featurize_states(trackers_as_states, interpreter)
label_ids = self._convert_labels_to_ids(trackers_as_actions, domain)
entity_tags = self._create_entity_tags(trackers_as_entities, interpreter)

return tracker_state_features, label_ids
return tracker_state_features, label_ids, entity_tags

@staticmethod
def _choose_last_user_input(
Expand Down Expand Up @@ -252,23 +299,22 @@ class FullDialogueTrackerFeaturizer(TrackerFeaturizer):
Training data is padded up to the length of the longest dialogue with -1.
"""

def training_states_and_actions(
def training_states_actions_and_entities(
self, trackers: List[DialogueStateTracker], domain: Domain
) -> Tuple[List[List[State]], List[List[Text]]]:
"""Transforms list of trackers to lists of states and actions.

Training data is padded up to the length of the longest dialogue with -1.
) -> Tuple[List[List[State]], List[List[Text]], List[List[Dict[Text, Any]]]]:
"""Transforms list of trackers to lists of states, actions and entity data.

Args:
trackers: The trackers to transform
domain: The domain

Returns:
A tuple of list of states and list of actions.
A tuple of list of states, list of actions and list of entity data.
"""

trackers_as_states = []
trackers_as_actions = []
trackers_as_entities = []

logger.debug(
"Creating states and action examples from "
Expand All @@ -285,14 +331,20 @@ def training_states_and_actions(

delete_first_state = False
actions = []
entities = []
entity_data = {}
for event in tracker.applied_events():
if isinstance(event, UserUttered):
entity_data = self._entity_data(event)

if not isinstance(event, ActionExecuted):
continue

if not event.unpredictable:
# only actions which can be
# predicted at a stories start
actions.append(event.action_name or event.action_text)
entities.append(entity_data)
else:
# unpredictable actions can be
# only the first in the story
Expand All @@ -303,13 +355,17 @@ def training_states_and_actions(
)
delete_first_state = True

# reset entity_data for the the next turn
entity_data = {}

if delete_first_state:
states = states[1:]

trackers_as_states.append(states[:-1])
trackers_as_actions.append(actions)
trackers_as_entities.append(entities)

return trackers_as_states, trackers_as_actions
return trackers_as_states, trackers_as_actions, trackers_as_entities

def prediction_states(
self,
Expand Down Expand Up @@ -386,23 +442,22 @@ def _hash_example(
frozen_actions = (action,)
return hash((frozen_states, frozen_actions))

def training_states_and_actions(
def training_states_actions_and_entities(
self, trackers: List[DialogueStateTracker], domain: Domain
) -> Tuple[List[List[State]], List[List[Text]]]:
"""Transforms list of trackers to lists of states and actions.

Training data is padded up to the length of the longest dialogue with -1.
) -> Tuple[List[List[State]], List[List[Text]], List[List[Dict[Text, Any]]]]:
"""Transforms list of trackers to lists of states, actions and entity data.

Args:
trackers: The trackers to transform
domain: The domain

Returns:
A tuple of list of states and list of actions.
A tuple of list of states, list of actions and list of entity data.
"""

trackers_as_states = []
trackers_as_actions = []
trackers_as_entities = []

# from multiple states that create equal featurizations
# we only need to keep one.
Expand All @@ -422,7 +477,11 @@ def training_states_and_actions(
states = self._create_states(tracker, domain)

states_length_for_action = 0
entity_data = {}
for event in tracker.applied_events():
if isinstance(event, UserUttered):
entity_data = self._entity_data(event)

if not isinstance(event, ActionExecuted):
continue

Expand All @@ -448,15 +507,19 @@ def training_states_and_actions(
trackers_as_actions.append(
[event.action_name or event.action_text]
)
trackers_as_entities.append([entity_data])
else:
trackers_as_states.append(sliced_states)
trackers_as_actions.append([event.action_name or event.action_text])
trackers_as_entities.append([entity_data])

# reset entity_data for the the next turn
entity_data = {}
pbar.set_postfix({"# actions": "{:d}".format(len(trackers_as_actions))})

logger.debug("Created {} action examples.".format(len(trackers_as_actions)))

return trackers_as_states, trackers_as_actions
return trackers_as_states, trackers_as_actions, trackers_as_entities

def prediction_states(
self,
Expand Down
Loading