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
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
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
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
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
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]])
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)