Beispiel #1
0
    def forward(self,
                dialog_graph,
                user_acts: List[UserAct] = None,
                beliefstate: BeliefState = None,
                sys_act: List[SysAct] = None,
                **kwargs) -> dict(beliefstate=BeliefState):
        """
            Function for updating the current dialog belief state (which tracks the system's
            knowledge about what has been said in the dialog) based on the user actions generated
            from the user's utterances

            Args:
                dialog_graph (DialogSystem): the graph to which the policy belongs
                belief_state (BeliefState): this should be None
                user_acts (list): a list of UserAct objects mapped from the user's last utterance
                sys_act (SysAct): this should be None

            Returns:
                (dict): a dictionary with the key "beliefstate" and the value the updated
                        BeliefState object

        """
        beliefstate.start_new_turn()

        if user_acts is None:
            # this check is required in case the BST is the first called module
            # e.g. usersimulation on semantic level:
            #   dialog acts as outputs -> no NLU
            return {'beliefstate': beliefstate}

        self._reset_informs(acts=user_acts, beliefstate=beliefstate)
        self._update_methods(beliefstate, user_acts)

        # TODO user acts should include probabilities and beliefstate should
        # update probabilities instead of always choosing 1.0

        # important to set these to zero since we don't want stale discourseAct
        for act in beliefstate['beliefs']['discourseAct']:
            beliefstate['beliefs']['discourseAct'][act] = 0.0
        beliefstate['beliefs']['discourseAct']['none'] = 1.0

        self.request_slots = {}

        self._handle_user_acts(beliefstate, user_acts, sys_act)

        beliefstate.update_num_dbmatches()
        return {'beliefstate': beliefstate}
Beispiel #2
0
    def forward(self,
                dialog_graph,
                user_utterance: str = "",
                user_acts: List[UserAct] = None,
                beliefstate: BeliefState = None,
                sys_act: List[SysAct] = None,
                **kwargs) -> dict(beliefstate=BeliefState):
        # initialize belief state
        if isinstance(beliefstate, type(None)):
            beliefstate = BeliefState(self.domain)
        else:
            beliefstate.start_new_turn()

        if user_acts is None:
            # this check is required in case the BST is the first called module
            # e.g. usersimulation on semantic level:
            #   dialog acts as outputs -> no NLU
            return {'beliefstate': beliefstate}

        # get all different action types in user inputs
        action_types = self._get_all_usr_action_types(user_acts)
        # update methods
        self._zero_all_scores(beliefstate['beliefs']['method'])
        if len(action_types) == 0:
            # no user actions
            beliefstate['beliefs']['method']['none'] = 1.0
        else:
            if UserActionType.RequestAlternatives in action_types:
                beliefstate['beliefs']['method']['byalternatives'] = 1.0
            elif UserActionType.Bye in action_types:
                beliefstate['beliefs']['method']['finished'] = 1.0
            elif UserActionType.Inform in action_types:
                # check if inform by primary key value or by constraints
                inform_primkeyval = \
                    [primkey_inform_act for primkey_inform_act in user_acts
                     if primkey_inform_act.type == UserActionType.Inform and
                        self.primary_key == primkey_inform_act.slot]
                if len(inform_primkeyval) > 0:
                    # inform by name
                    beliefstate['beliefs']['method']['byprimarykey'] = 1.0
                else:
                    # inform by constraints
                    beliefstate['beliefs']['method']['byconstraints'] = 1.0
            elif (UserActionType.Request in action_types or
                  UserActionType.Confirm in action_types) and \
                 not UserActionType.Inform in action_types and \
                 not UserActionType.Deny in action_types and \
                 not (beliefstate['system']['lastInformedPrimKeyVal'] == '**NONE**' or
                      beliefstate['system']['lastInformedPrimKeyVal'] == ''):
                beliefstate['beliefs']['method']['byprimarykey'] = 1.0
            else:
                beliefstate['beliefs']['method']['none'] = 1.0

        # important to set these to zero since we don't want stale discourseAct
        for act in beliefstate['beliefs']['discourseAct']:
            beliefstate['beliefs']['discourseAct'][act] = 0.0
        beliefstate['beliefs']['discourseAct']['none'] = 1.0

        for act_in in user_acts:
            if act_in.type is None:
                pass
            elif act_in.type is UserActionType.Bad:
                beliefstate['beliefs']['discourseAct']['none'] = 0.0
                beliefstate['beliefs']['discourseAct']['bad'] = 1.0
            elif act_in.type == UserActionType.Hello:
                beliefstate['beliefs']['discourseAct']['none'] = 0.0
                beliefstate['beliefs']['discourseAct']['hello'] = 1.0
            elif act_in.type == UserActionType.Thanks:
                beliefstate['beliefs']['discourseAct']['none'] = 0.0
                beliefstate['beliefs']['discourseAct']['thanks'] = 1.0
            # TODO adapt user actions to dstc action names
            elif act_in.type in [UserActionType.Bye]:
                # nothing to do here, but needed to cirumwent warning
                pass
            elif act.type == UserActionType.RequestAlternatives:
                pass
            elif act_in.type == UserActionType.Ack:
                beliefstate['beliefs']['discourseAct']['none'] = 0.0
                beliefstate['beliefs']['discourseAct']['ack'] = 1.0
            # else:
            #     # unknown Dialog Act
            #     # To be handled:
            #     self.logger.warning("user act not handled by BST: " + str(act_in))

        # track informables and requestables
        utterance = " ".join([
            tok.text for tok in nlp(user_utterance.strip().lower())
        ])  # tokenize
        # convert system utterance to text triples
        sys_utterance = []
        self._sysact_to_list(sys_act, sys_utterance)
        sys_utterance = " ".join(sys_utterance)

        # track informables
        for key in self.inf_trackers:
            output = self.inf_trackers[key].forward(
                sys_utterance,
                utterance,
                first_turn=(dialog_graph.num_turns == 0))
            output = output.squeeze()
            probabilities = F.softmax(output, dim=0)
            # print(probabilities)
            # top_value = self.data_mappings.get_informable_slot_value(key, probabilities.argmax(0).item())
            # top_prob = probabilities.max(0)[0].item()

            # copy beliefstate from network to complete beliefstate for policy
            for val in self.data_mappings.inf_values[key]:
                beliefstate['beliefs'][key][val] = probabilities[
                    self.data_mappings.get_informable_slot_value_index(
                        key, val)].item()

            # print("slot ", key)
            # print("    most probable:", top_value, " with prob", top_prob)

        for key in self.req_trackers:
            output = self.req_trackers[key].forward(
                sys_utterance,
                utterance,
                first_turn=(dialog_graph.num_turns == 0))
            output = output.squeeze()
            probabilities = F.softmax(output, dim=0)
            # print(probabilities)
            # top_value = bool(probabilities.argmax(0).item())
            # top_prob = probabilities.max(0)[0].item()
            # print("req slot ", key)
            # print("    most probable:", top_value, " with prob", top_prob)
            # copy beliefstate from network to complete beliefstate for policy
            beliefstate['beliefs']['requested'][key] = probabilities[1].item(
            )  # true

        return {'beliefstate': beliefstate}
Beispiel #3
0
class HandcraftedBST(Service):
    """
    A rule-based approach to belief state tracking.
    """
    def __init__(self, domain=None, logger=None):
        Service.__init__(self, domain=domain)
        self.logger = logger
        self.bs = BeliefState(domain)

    @PublishSubscribe(sub_topics=["user_acts"], pub_topics=["beliefstate"])
    def update_bst(self, user_acts: List[UserAct] = None) \
            -> dict(beliefstate=BeliefState):
        """
            Updates the current dialog belief state (which tracks the system's
            knowledge about what has been said in the dialog) based on the user actions generated
            from the user's utterances

            Args:
                user_acts (list): a list of UserAct objects mapped from the user's last utterance

            Returns:
                (dict): a dictionary with the key "beliefstate" and the value the updated
                        BeliefState object

        """
        # save last turn to memory
        self.bs.start_new_turn()
        if user_acts:
            self._reset_informs(user_acts)
            self._reset_requests()
            self.bs["user_acts"] = self._get_all_usr_action_types(user_acts)

            self._handle_user_acts(user_acts)

            num_entries, discriminable = self.bs.get_num_dbmatches()
            self.bs["num_matches"] = num_entries
            self.bs["discriminable"] = discriminable

        return {'beliefstate': self.bs}

    def dialog_start(self):
        """
            Restets the belief state so it is ready for a new dialog

            Returns:
                (dict): a dictionary with a single entry where the key is 'beliefstate'and
                        the value is a new BeliefState object
        """
        # initialize belief state
        self.bs = BeliefState(self.domain)

    def _reset_informs(self, acts: List[UserAct]):
        """
            If the user specifies a new value for a given slot, delete the old
            entry from the beliefstate
        """

        slots = {act.slot for act in acts if act.type == UserActionType.Inform}
        for slot in [s for s in self.bs['informs']]:
            if slot in slots:
                del self.bs['informs'][slot]

    def _reset_requests(self):
        """
            gets rid of requests from the previous turn
        """
        self.bs['requests'] = {}

    def _get_all_usr_action_types(
            self, user_acts: List[UserAct]) -> Set[UserActionType]:
        """ 
        Returns a set of all different UserActionTypes in user_acts.

        Args:
            user_acts (List[UserAct]): list of UserAct objects

        Returns:
            set of UserActionType objects
        """
        action_type_set = set()
        for act in user_acts:
            action_type_set.add(act.type)
        return action_type_set

    def _handle_user_acts(self, user_acts: List[UserAct]):
        """
            Updates the belief state based on the information contained in the user act(s)

            Args:
                user_acts (list[UserAct]): the list of user acts to use to update the belief state

        """

        # reset any offers if the user informs any new information
        if self.domain.get_primary_key() in self.bs['informs'] \
                and UserActionType.Inform in self.bs["user_acts"]:
            del self.bs['informs'][self.domain.get_primary_key()]

        # We choose to interpret switching as wanting to start a new dialog and do not support
        # resuming an old dialog
        elif UserActionType.SelectDomain in self.bs["user_acts"]:
            self.bs["informs"] = {}
            self.bs["requests"] = {}

        # Handle user acts
        for act in user_acts:
            if act.type == UserActionType.Request:
                self.bs['requests'][act.slot] = act.score
            elif act.type == UserActionType.Inform:
                # add informs and their scores to the beliefstate
                if act.slot in self.bs["informs"]:
                    self.bs['informs'][act.slot][act.value] = act.score
                else:
                    self.bs['informs'][act.slot] = {act.value: act.score}
            elif act.type == UserActionType.NegativeInform:
                # reset mentioned value to zero probability
                if act.slot in self.bs['informs']:
                    if act.value in self.bs['informs'][act.slot]:
                        del self.bs['informs'][act.slot][act.value]
            elif act.type == UserActionType.RequestAlternatives:
                # This way it is clear that the user is no longer asking about that one item
                if self.domain.get_primary_key() in self.bs['informs']:
                    del self.bs['informs'][self.domain.get_primary_key()]