예제 #1
0
    def _get_restaurant_goal(self):
        cnt_slot = self.ind_slot_dist
        cnt_slot_value = self.ind_slot_value_dist

        while True:
            # domain_goal = defaultdict(lambda: {})
            # domain_goal = {'info': {}, 'reqt': []}
            domain_goal = {'info': {}}
            # inform
            if 'info' in cnt_slot:
                for slot in cnt_slot['info']:
                    if random.random() < cnt_slot['info'][slot]:
                        domain_goal['info'][slot] = nomial_sample(
                            cnt_slot_value['info'][slot])

                if 'name' in domain_goal['info'] and len(
                        domain_goal['info']) > 1:
                    if random.random() < cnt_slot['info']['name']:
                        domain_goal['info'] = {
                            'name': domain_goal['info']['name']
                        }
                    else:
                        del domain_goal['info']['name']

                if domain_goal['info'] == {}:
                    continue
            # request
            if 'reqt' in cnt_slot:
                reqt = [
                    slot for slot in cnt_slot['reqt']
                    if random.random() < cnt_slot['reqt'][slot]
                    and slot not in domain_goal['info']
                ]
                if len(reqt) > 0:
                    domain_goal['reqt'] = reqt

            # fail_info
            if 'info' in domain_goal and len(
                    query('restaurant', domain_goal['info'].items())) == 0:
                num_trial = 0
                while num_trial < 100:
                    adjusted_info = self._adjust_info(domain_goal['info'])
                    if len(query('restaurant', adjusted_info.items())) > 0:
                        domain_goal['info'] = adjusted_info

                        break
                    num_trial += 1

                if num_trial >= 100:
                    continue

            # at least there is one request and book
            if 'reqt' in domain_goal:
                break

        return domain_goal
예제 #2
0
 def action_devectorize(self, action_vec):
     """
     recover an action
     Args:
         action_vec (np.array):
             Dialog act vector
     Returns:
         action (tuple):
             Dialog act
     """
     act_array = []
     for i, idx in enumerate(action_vec):
         if idx == 1:
             act_array.append(self.vec2act[i])
     action = deflat_da(act_array)
     entities = {}
     for domint in action:
         domain, intent = domint.split('-')
         if domain not in entities and domain.lower() not in ['general', 'booking']:
             constraint = []
             for k, v in self.state[domain.lower()]['semi'].items():
                 if k in mapping[domain.lower()]:
                     constraint.append((mapping[domain.lower()][k], v))
             entities[domain] = query(domain.lower(), constraint)
     action = lexicalize_da(action, entities, self.state, self.requestable)
     return action
예제 #3
0
    def pointer(self, turn):
        pointer_vector = np.zeros(6 * len(self.db_domains))
        for domain in self.db_domains:
            constraint = []
            for k, v in turn[domain.lower()]['semi'].items():
                if k in mapping[domain.lower()]:
                    constraint.append((mapping[domain.lower()][k], v))
            entities = query(domain.lower(), constraint)
            pointer_vector = self.one_hot_vector(len(entities), domain, pointer_vector)

        return pointer_vector
예제 #4
0
    def get_user_goal(self, seed=None):
        if seed is not None:
            random.seed(seed)
            np.random.seed(seed)
        domain_ordering = ()
        while len(domain_ordering) <= 0:
            domain_ordering = nomial_sample(self.domain_ordering_dist)
        # domain_ordering = ('restaurant',)

        user_goal = {
            dom: self._get_domain_goal(dom)
            for dom in domain_ordering
        }
        assert len(user_goal.keys()) > 0

        # using taxi to communte between places, removing destination and departure.
        if 'taxi' in domain_ordering:
            places = [
                dom for dom in domain_ordering[:domain_ordering.index('taxi')]
                if 'address' in self.ind_slot_dist[dom]['reqt'].keys()
            ]
            if len(places) >= 1:
                del user_goal['taxi']['info']['destination']
                user_goal[places[-1]]['reqt'] = list(
                    set(user_goal[places[-1]].get('reqt',
                                                  [])).union({'address'}))
                if places[-1] == 'restaurant' and 'book' in user_goal[
                        'restaurant']:
                    user_goal['taxi']['info']['arriveBy'] = user_goal[
                        'restaurant']['book']['time']
                    if 'leaveAt' in user_goal['taxi']['info']:
                        del user_goal['taxi']['info']['leaveAt']
            if len(places) >= 2:
                del user_goal['taxi']['info']['departure']
                user_goal[places[-2]]['reqt'] = list(
                    set(user_goal[places[-2]].get('reqt',
                                                  [])).union({'address'}))

        # match area of attraction and restaurant
        if 'restaurant' in domain_ordering and \
                'attraction' in domain_ordering and \
                'fail_info' not in user_goal['restaurant'] and \
                domain_ordering.index('restaurant') > domain_ordering.index('attraction') and \
                'area' in user_goal['restaurant']['info'] and 'area' in user_goal['attraction']['info']:
            adjusted_restaurant_goal = deepcopy(
                user_goal['restaurant']['info'])
            adjusted_restaurant_goal['area'] = user_goal['attraction']['info'][
                'area']
            if len(query('restaurant', adjusted_restaurant_goal.items())
                   ) > 0 and random.random() < 0.5:
                user_goal['restaurant']['info']['area'] = user_goal[
                    'attraction']['info']['area']

        # match day and people of restaurant and hotel
        if 'restaurant' in domain_ordering and 'hotel' in domain_ordering and \
                'book' in user_goal['restaurant'] and 'book' in user_goal['hotel']:
            if random.random() < 0.5:
                user_goal['restaurant']['book']['people'] = user_goal['hotel'][
                    'book']['people']
                if 'fail_book' in user_goal['restaurant']:
                    user_goal['restaurant']['fail_book']['people'] = user_goal[
                        'hotel']['book']['people']
            if random.random() < 1.0:
                user_goal['restaurant']['book']['day'] = user_goal['hotel'][
                    'book']['day']
                if 'fail_book' in user_goal['restaurant']:
                    user_goal['restaurant']['fail_book']['day'] = user_goal[
                        'hotel']['book']['day']
                    if user_goal['restaurant']['book']['day'] == user_goal['restaurant']['fail_book']['day'] and \
                            user_goal['restaurant']['book']['time'] == user_goal['restaurant']['fail_book']['time'] and \
                            user_goal['restaurant']['book']['people'] == user_goal['restaurant']['fail_book']['people']:
                        del user_goal['restaurant']['fail_book']

        # match day and people of hotel and train
        if 'hotel' in domain_ordering and 'train' in domain_ordering and \
                'book' in user_goal['hotel'] and 'info' in user_goal['train']:
            if user_goal['train']['info']['destination'] == 'cambridge' and \
                'day' in user_goal['hotel']['book']:
                user_goal['train']['info']['day'] = user_goal['hotel']['book'][
                    'day']
            elif user_goal['train']['info']['departure'] == 'cambridge' and \
                'day' in user_goal['hotel']['book'] and 'stay' in user_goal['hotel']['book']:
                user_goal['train']['info']['day'] = days[
                    (days.index(user_goal['hotel']['book']['day']) +
                     int(user_goal['hotel']['book']['stay'])) % 7]
            # In case, we have no query results with adjusted train goal, we simply drop the train goal.
            if len(query('train', user_goal['train']['info'].items())) == 0:
                del user_goal['train']
                domain_ordering = tuple(list(domain_ordering).remove('train'))

        user_goal['domain_ordering'] = domain_ordering

        return user_goal
예제 #5
0
    def _get_domain_goal(self, domain):
        cnt_slot = self.ind_slot_dist[domain]
        cnt_slot_value = self.ind_slot_value_dist[domain]
        pro_book = self.book_dist[domain]

        while True:
            # domain_goal = defaultdict(lambda: {})
            # domain_goal = {'info': {}, 'fail_info': {}, 'reqt': {}, 'book': {}, 'fail_book': {}}
            domain_goal = {'info': {}}
            # inform
            if 'info' in cnt_slot:
                for slot in cnt_slot['info']:
                    if random.random(
                    ) < cnt_slot['info'][slot] + pro_correction['info']:
                        domain_goal['info'][slot] = nomial_sample(
                            cnt_slot_value['info'][slot])

                if domain in ['hotel', 'restaurant', 'attraction'
                              ] and 'name' in domain_goal['info'] and len(
                                  domain_goal['info']) > 1:
                    if random.random() < cnt_slot['info']['name']:
                        domain_goal['info'] = {
                            'name': domain_goal['info']['name']
                        }
                    else:
                        del domain_goal['info']['name']

                if domain in ['taxi', 'train'] and 'arriveBy' in domain_goal[
                        'info'] and 'leaveAt' in domain_goal['info']:
                    if random.random() < (cnt_slot['info']['leaveAt'] /
                                          (cnt_slot['info']['arriveBy'] +
                                           cnt_slot['info']['leaveAt'])):
                        del domain_goal['info']['arriveBy']
                    else:
                        del domain_goal['info']['leaveAt']

                if domain in ['taxi', 'train'] and 'arriveBy' not in domain_goal['info'] and 'leaveAt' not in \
                        domain_goal['info']:
                    if random.random() < (cnt_slot['info']['arriveBy'] /
                                          (cnt_slot['info']['arriveBy'] +
                                           cnt_slot['info']['leaveAt'])):
                        domain_goal['info']['arriveBy'] = nomial_sample(
                            cnt_slot_value['info']['arriveBy'])
                    else:
                        domain_goal['info']['leaveAt'] = nomial_sample(
                            cnt_slot_value['info']['leaveAt'])

                if domain in ['taxi', 'train'
                              ] and 'departure' not in domain_goal['info']:
                    domain_goal['info']['departure'] = nomial_sample(
                        cnt_slot_value['info']['departure'])

                if domain in ['taxi', 'train'
                              ] and 'destination' not in domain_goal['info']:
                    domain_goal['info']['destination'] = nomial_sample(
                        cnt_slot_value['info']['destination'])

                if domain in ['taxi', 'train'] and \
                        'departure' in domain_goal['info'] and \
                        'destination' in domain_goal['info'] and \
                        domain_goal['info']['departure'] == domain_goal['info']['destination']:
                    if random.random() < (cnt_slot['info']['departure'] /
                                          (cnt_slot['info']['departure'] +
                                           cnt_slot['info']['destination'])):
                        domain_goal['info']['departure'] = nomial_sample(
                            cnt_slot_value['info']['departure'])
                    else:
                        domain_goal['info']['destination'] = nomial_sample(
                            cnt_slot_value['info']['destination'])
                if domain_goal['info'] == {}:
                    continue
            # request
            if 'reqt' in cnt_slot:
                reqt = [
                    slot for slot in cnt_slot['reqt']
                    if random.random() < cnt_slot['reqt'][slot] +
                    pro_correction['reqt'] and slot not in domain_goal['info']
                ]
                if len(reqt) > 0:
                    domain_goal['reqt'] = reqt

            # book
            if 'book' in cnt_slot and random.random(
            ) < pro_book + pro_correction['book']:
                if 'book' not in domain_goal:
                    domain_goal['book'] = {}

                for slot in cnt_slot['book']:
                    if random.random(
                    ) < cnt_slot['book'][slot] + pro_correction['book']:
                        domain_goal['book'][slot] = nomial_sample(
                            cnt_slot_value['book'][slot])

                # makes sure that there are all necessary slots for booking
                if domain == 'restaurant' and 'time' not in domain_goal['book']:
                    domain_goal['book']['time'] = nomial_sample(
                        cnt_slot_value['book']['time'])

                if domain == 'hotel' and 'stay' not in domain_goal['book']:
                    domain_goal['book']['stay'] = nomial_sample(
                        cnt_slot_value['book']['stay'])

                if domain in ['hotel', 'restaurant'
                              ] and 'day' not in domain_goal['book']:
                    domain_goal['book']['day'] = nomial_sample(
                        cnt_slot_value['book']['day'])

                if domain in ['hotel', 'restaurant'
                              ] and 'people' not in domain_goal['book']:
                    domain_goal['book']['people'] = nomial_sample(
                        cnt_slot_value['book']['people'])

                if domain == 'train' and len(domain_goal['book']) <= 0:
                    domain_goal['book']['people'] = nomial_sample(
                        cnt_slot_value['book']['people'])

            # fail_book
            if 'book' in domain_goal and random.random() < 0.5:
                if domain == 'hotel':
                    domain_goal['fail_book'] = deepcopy(domain_goal['book'])
                    if 'stay' in domain_goal['book'] and random.random() < 0.5:
                        # increase hotel-stay
                        domain_goal['fail_book']['stay'] = str(
                            int(domain_goal['book']['stay']) + 1)
                    elif 'day' in domain_goal['book']:
                        # push back hotel-day by a day
                        domain_goal['fail_book']['day'] = days[
                            (days.index(domain_goal['book']['day']) - 1) % 7]

                elif domain == 'restaurant':
                    domain_goal['fail_book'] = deepcopy(domain_goal['book'])
                    if 'time' in domain_goal['book'] and random.random() < 0.5:
                        hour, minute = domain_goal['book']['time'].split(':')
                        domain_goal['fail_book']['time'] = str(
                            (int(hour) + 1) % 24) + ':' + minute
                    elif 'day' in domain_goal['book']:
                        if random.random() < 0.5:
                            domain_goal['fail_book']['day'] = days[
                                (days.index(domain_goal['book']['day']) - 1) %
                                7]
                        else:
                            domain_goal['fail_book']['day'] = days[
                                (days.index(domain_goal['book']['day']) + 1) %
                                7]

            # fail_info
            if 'info' in domain_goal and len(
                    query(domain, domain_goal['info'].items())) == 0:
                num_trial = 0
                while num_trial < 100:
                    adjusted_info = self._adjust_info(domain,
                                                      domain_goal['info'])
                    if len(query(domain, adjusted_info.items())) > 0:
                        if domain == 'train':
                            domain_goal['info'] = adjusted_info
                        else:
                            domain_goal['fail_info'] = domain_goal['info']
                            domain_goal['info'] = adjusted_info

                        break
                    num_trial += 1

                if num_trial >= 100:
                    continue

            # at least there is one request and book
            if 'reqt' in domain_goal or 'book' in domain_goal:
                break

        return domain_goal
예제 #6
0
    def _update_train(self, user_act, user_action, state, DA):
        trans = {
            'day': 'Day',
            'destination': 'Destination',
            'departure': 'Departure'
        }
        constraints = []
        for time in ['leaveAt', 'arriveBy']:
            if state['belief_state']['train']['semi'][time] != "":
                constraints.append(
                    [time, state['belief_state']['train']['semi'][time]])

        if len(constraints) == 0:
            p = random.random()
            if 'Train-Request' not in DA:
                DA['Train-Request'] = []
            if p < 0.33:
                DA['Train-Request'].append(['Leave', '?'])
            elif p < 0.66:
                DA['Train-Request'].append(['Arrive', '?'])
            else:
                DA['Train-Request'].append(['Leave', '?'])
                DA['Train-Request'].append(['Arrive', '?'])

        if 'Train-Request' not in DA:
            DA['Train-Request'] = []
        for prop in ['day', 'destination', 'departure']:
            if state['belief_state']['train']['semi'][prop] == "":
                slot = REF_USR_DA['Train'].get(prop, prop)
                DA["Train-Request"].append([slot, '?'])
            else:
                constraints.append(
                    [prop, state['belief_state']['train']['semi'][prop]])

        kb_result = query('train', constraints)
        self.kb_result['Train'] = deepcopy(kb_result)

        # print(constraints)
        # print(len(kb_result))
        if user_act == 'Train-Request':
            del (DA['Train-Request'])
            if 'Train-Inform' not in DA:
                DA['Train-Inform'] = []
            for slot in user_action[user_act]:
                # Train_DA_MAP = {'Duration': "Time", 'Price': 'Ticket', 'TrainID': 'Id'}
                # slot[0] = Train_DA_MAP.get(slot[0], slot[0])
                slot_name = REF_SYS_DA['Train'].get(slot[0], slot[0])
                try:
                    DA['Train-Inform'].append(
                        [slot[0], kb_result[0][slot_name]])
                except:
                    pass
            return
        if len(kb_result) == 0:
            if 'Train-NoOffer' not in DA:
                DA['Train-NoOffer'] = []
            for prop in constraints:
                DA['Train-NoOffer'].append(
                    [REF_USR_DA['Train'].get(prop[0], prop[0]), prop[1]])
            if 'Train-Request' in DA:
                del DA['Train-Request']
        elif len(kb_result) >= 1:
            if len(constraints) < 4:
                return
            if 'Train-Request' in DA:
                del DA['Train-Request']
            if 'Train-OfferBook' not in DA:
                DA['Train-OfferBook'] = []
            for prop in constraints:
                DA['Train-OfferBook'].append(
                    [REF_USR_DA['Train'].get(prop[0], prop[0]), prop[1]])
예제 #7
0
    def _update_DA(self, user_act, user_action, state, DA):
        """ Answer user's utterance about any domain other than taxi or train. """

        domain, intent_type = user_act.split('-')

        constraints = []
        for slot in state['belief_state'][domain.lower()]['semi']:
            if state['belief_state'][domain.lower()]['semi'][slot] != "":
                constraints.append([
                    slot, state['belief_state'][domain.lower()]['semi'][slot]
                ])

        kb_result = query(domain.lower(), constraints)
        self.kb_result[domain] = deepcopy(kb_result)

        # print("\tConstraint: " + "{}".format(constraints))
        # print("\tCandidate Count: " + "{}".format(len(kb_result)))
        # if len(kb_result) > 0:
        #     print("Candidate: " + "{}".format(kb_result[0]))

        # print(state['user_action'])
        # Respond to user's request
        if intent_type == 'Request':
            if self.recommend_flag > 1:
                self.recommend_flag = -1
                self.choice = ""
            elif self.recommend_flag == 1:
                self.recommend_flag == 0
            if (domain + "-Inform") not in DA:
                DA[domain + "-Inform"] = []
            for slot in user_action[user_act]:
                if len(kb_result) > 0:
                    kb_slot_name = REF_SYS_DA[domain].get(slot[0], slot[0])
                    if kb_slot_name in kb_result[0]:
                        DA[domain + "-Inform"].append(
                            [slot[0], kb_result[0][kb_slot_name]])
                    else:
                        DA[domain + "-Inform"].append([slot[0], "unknown"])
                        # DA[domain + "-Inform"].append([slot_name, state['kb_results_dict'][0][slot[0].lower()]])

        else:
            # There's no result matching user's constraint
            # if len(state['kb_results_dict']) == 0:
            if len(kb_result) == 0:
                if (domain + "-NoOffer") not in DA:
                    DA[domain + "-NoOffer"] = []

                for slot in state['belief_state'][domain.lower()]['semi']:
                    if state['belief_state'][domain.lower()]['semi'][slot] != "" and \
                                    state['belief_state'][domain.lower()]['semi'][slot] != "do n't care":
                        slot_name = REF_USR_DA[domain].get(slot, slot)
                        DA[domain + "-NoOffer"].append([
                            slot_name,
                            state['belief_state'][domain.lower()]['semi'][slot]
                        ])

                p = random.random()

                # Ask user if he wants to change constraint
                if p < 0.3:
                    req_num = min(
                        random.randint(0, 999999) %
                        len(DA[domain + "-NoOffer"]) + 1, 3)
                    if domain + "-Request" not in DA:
                        DA[domain + "-Request"] = []
                    for i in range(req_num):
                        slot_name = REF_USR_DA[domain].get(
                            DA[domain + "-NoOffer"][i][0],
                            DA[domain + "-NoOffer"][i][0])
                        DA[domain + "-Request"].append([slot_name, "?"])

            # There's exactly one result matching user's constraint
            # elif len(state['kb_results_dict']) == 1:
            elif len(kb_result) == 1:

                # Inform user about this result
                if (domain + "-Inform") not in DA:
                    DA[domain + "-Inform"] = []
                props = []
                for prop in state['belief_state'][domain.lower()]['semi']:
                    props.append(prop)
                property_num = len(props)
                if property_num > 0:
                    info_num = random.randint(0, 999999) % property_num + 1
                    random.shuffle(props)
                    for i in range(info_num):
                        slot_name = REF_USR_DA[domain].get(props[i], props[i])
                        # DA[domain + "-Inform"].append([slot_name, state['kb_results_dict'][0][props[i]]])
                        DA[domain + "-Inform"].append(
                            [slot_name, kb_result[0][props[i]]])

            # There are multiple resultes matching user's constraint
            else:
                p = random.random()

                # Recommend a choice from kb_list
                if True:  # p < 0.3:
                    if (domain + "-Inform") not in DA:
                        DA[domain + "-Inform"] = []
                    if (domain + "-Recommend") not in DA:
                        DA[domain + "-Recommend"] = []
                    DA[domain + "-Inform"].append(
                        ["Choice", str(len(kb_result))])
                    idx = random.randint(0, 999999) % len(kb_result)
                    # idx = 0
                    choice = kb_result[idx]
                    if domain in [
                            "Hotel", "Attraction", "Police", "Restaurant"
                    ]:
                        DA[domain + "-Recommend"].append(
                            ['Name', choice['name']])
                    self.recommend_flag = 0
                    self.candidate = choice
                    props = []
                    for prop in choice:
                        props.append([prop, choice[prop]])
                    prop_num = min(random.randint(0, 999999) % 3, len(props))
                    # prop_num = min(2, len(props))
                    random.shuffle(props)
                    for i in range(prop_num):
                        slot = props[i][0]
                        string = REF_USR_DA[domain].get(slot, slot)
                        if string in INFORMABLE_SLOTS:
                            DA[domain + "-Recommend"].append(
                                [string, str(props[i][1])])

                # Ask user to choose a candidate.
                elif p < 0.5:
                    prop_values = []
                    props = []
                    # for prop in state['kb_results_dict'][0]:
                    for prop in kb_result[0]:
                        # for candidate in state['kb_results_dict']:
                        for candidate in kb_result:
                            if prop not in candidate:
                                continue
                            if candidate[prop] not in prop_values:
                                prop_values.append(candidate[prop])
                        if len(prop_values) > 1:
                            props.append([prop, prop_values])
                        prop_values = []
                    random.shuffle(props)
                    idx = 0
                    while idx < len(props):
                        if props[idx][0] not in SELECTABLE_SLOTS[domain]:
                            props.pop(idx)
                            idx -= 1
                        idx += 1
                    if domain + "-Select" not in DA:
                        DA[domain + "-Select"] = []
                    for i in range(min(len(props[0][1]), 5)):
                        prop_value = REF_USR_DA[domain].get(
                            props[0][0], props[0][0])
                        DA[domain + "-Select"].append(
                            [prop_value, props[0][1][i]])

                # Ask user for more constraint
                else:
                    reqs = []
                    for prop in state['belief_state'][domain.lower()]['semi']:
                        if state['belief_state'][
                                domain.lower()]['semi'][prop] == "":
                            prop_value = REF_USR_DA[domain].get(prop, prop)
                            reqs.append([prop_value, "?"])
                    i = 0
                    while i < len(reqs):
                        if reqs[i][0] not in REQUESTABLE_SLOTS:
                            reqs.pop(i)
                            i -= 1
                        i += 1
                    random.shuffle(reqs)
                    if len(reqs) == 0:
                        return
                    req_num = min(random.randint(0, 999999) % len(reqs) + 1, 2)
                    if (domain + "-Request") not in DA:
                        DA[domain + "-Request"] = []
                    for i in range(req_num):
                        req = reqs[i]
                        req[0] = REF_USR_DA[domain].get(req[0], req[0])
                        DA[domain + "-Request"].append(req)