| @@ -16,17 +16,14 @@ from zoopt import Dimension, Objective, Parameter, Opt | |||
| from ..utils.utils import confidence_dist, flatten, reform_idx, hamming_dist | |||
| class AbducerBase(abc.ABC): | |||
| def __init__(self, kb, dist_func='confidence', zoopt=False, multiple_predictions=False, cache=True): | |||
| def __init__(self, kb, dist_func='hamming', zoopt=False, multiple_predictions=False): | |||
| self.kb = kb | |||
| assert dist_func == 'hamming' or dist_func == 'confidence' | |||
| self.dist_func = dist_func | |||
| self.zoopt = zoopt | |||
| self.multiple_predictions = multiple_predictions | |||
| self.cache = cache | |||
| if self.cache: | |||
| self.cache_min_address_num = {} | |||
| self.cache_candidates = {} | |||
| if dist_func == 'confidence': | |||
| self.mapping = dict(zip(self.kb.pseudo_label_list, list(range(len(self.kb.pseudo_label_list))))) | |||
| def _get_cost_list(self, pred_res, pred_res_prob, candidates): | |||
| if self.dist_func == 'hamming': | |||
| @@ -40,9 +37,7 @@ class AbducerBase(abc.ABC): | |||
| if self.multiple_predictions: | |||
| pred_res_prob = flatten(pred_res_prob) | |||
| candidates = [flatten(c) for c in candidates] | |||
| mapping = dict(zip(self.kb.pseudo_label_list, list(range(len(self.kb.pseudo_label_list))))) | |||
| candidates = [list(map(lambda x: mapping[x], c)) for c in candidates] | |||
| candidates = [list(map(lambda x: self.mapping[x], c)) for c in candidates] | |||
| return confidence_dist(pred_res_prob, candidates) | |||
| def _get_one_candidate(self, pred_res, pred_res_prob, candidates): | |||
| @@ -53,69 +48,43 @@ class AbducerBase(abc.ABC): | |||
| else: | |||
| cost_list = self._get_cost_list(pred_res, pred_res_prob, candidates) | |||
| min_address_num = np.min(cost_list) | |||
| idxs = np.where(cost_list == min_address_num)[0] | |||
| candidate = [candidates[idx] for idx in idxs][0] | |||
| candidate = candidates[np.argmin(cost_list)] | |||
| return candidate | |||
| # for zoopt | |||
| def _zoopt_score_multiple(self, pred_res, key, solution): | |||
| all_address_flag = reform_idx(solution, pred_res) | |||
| score = 0 | |||
| for idx in range(len(pred_res)): | |||
| address_idx = [i for i, flag in enumerate(all_address_flag[idx]) if flag != 0] | |||
| candidate = self.address_by_idx([pred_res[idx]], key[idx], address_idx) | |||
| if len(candidate) > 0: | |||
| score += 1 | |||
| return score | |||
| def _zoopt_address_score(self, pred_res, key, sol): | |||
| def _zoopt_address_score_single(self, sol_x, pred_res, pred_res_prob, key): | |||
| address_idx = np.where(sol_x != 0)[0] | |||
| candidates = self.address_by_idx(pred_res, key, address_idx) | |||
| if len(candidates) > 0: | |||
| return np.min(self._get_cost_list(pred_res, pred_res_prob, candidates)) | |||
| else: | |||
| return len(pred_res) | |||
| def _zoopt_address_score(self, pred_res, pred_res_prob, key, sol): | |||
| if not self.multiple_predictions: | |||
| address_idx = [idx for idx, i in enumerate(sol.get_x()) if i != 0] | |||
| candidates = self.address_by_idx(pred_res, key, address_idx) | |||
| return 1 if len(candidates) > 0 else 0 | |||
| return self._zoopt_address_score_single(sol.get_x(), pred_res, pred_res_prob, key) | |||
| else: | |||
| return self._zoopt_score_multiple(pred_res, key, sol.get_x()) | |||
| all_address_flag = reform_idx(sol.get_x(), pred_res) | |||
| score = 0 | |||
| for idx in range(len(pred_res)): | |||
| score += self._zoopt_address_score_single(all_address_flag[idx], pred_res[idx], pred_res_prob[idx], key) | |||
| return score | |||
| def _constrain_address_num(self, solution, max_address_num): | |||
| x = solution.get_x() | |||
| return max_address_num - x.sum() | |||
| def zoopt_get_solution(self, pred_res, key, max_address_num): | |||
| def zoopt_get_solution(self, pred_res, pred_res_prob, key, max_address_num): | |||
| length = len(flatten(pred_res)) | |||
| dimension = Dimension(size=length, regs=[[0, 1]] * length, tys=[False] * length) | |||
| objective = Objective( | |||
| lambda sol: -self._zoopt_address_score(pred_res, key, sol), | |||
| lambda sol: self._zoopt_address_score(pred_res, pred_res_prob, key, sol), | |||
| dim=dimension, | |||
| constraint=lambda sol: self._constrain_address_num(sol, max_address_num), | |||
| ) | |||
| parameter = Parameter(budget=100, intermediate_result=False, autoset=True) | |||
| solution = Opt.min(objective, parameter).get_x() | |||
| return solution | |||
| def _get_cache(self, data, max_address_num, require_more_address): | |||
| pred_res, pred_res_prob, key = data | |||
| if self.multiple_predictions: | |||
| pred_res = flatten(pred_res) | |||
| key = tuple(key) | |||
| if (tuple(pred_res), key) in self.cache_min_address_num: | |||
| address_num = min(max_address_num, self.cache_min_address_num[(tuple(pred_res), key)] + require_more_address) | |||
| if (tuple(pred_res), key, address_num) in self.cache_candidates: | |||
| candidates = self.cache_candidates[(tuple(pred_res), key, address_num)] | |||
| if self.zoopt: | |||
| return candidates[0] | |||
| else: | |||
| return self._get_one_candidate(pred_res, pred_res_prob, candidates) | |||
| return None | |||
| def _set_cache(self, pred_res, key, min_address_num, address_num, candidates): | |||
| if self.multiple_predictions: | |||
| pred_res = flatten(pred_res) | |||
| key = tuple(key) | |||
| self.cache_min_address_num[(tuple(pred_res), key)] = min_address_num | |||
| self.cache_candidates[(tuple(pred_res), key, address_num)] = candidates | |||
| def address_by_idx(self, pred_res, key, address_idx): | |||
| return self.kb.address_by_idx(pred_res, key, address_idx, self.multiple_predictions) | |||
| @@ -124,37 +93,26 @@ class AbducerBase(abc.ABC): | |||
| if max_address_num == -1: | |||
| max_address_num = len(flatten(pred_res)) | |||
| if self.cache: | |||
| candidate = self._get_cache(data, max_address_num, require_more_address) | |||
| if candidate is not None: | |||
| return candidate | |||
| if self.zoopt: | |||
| solution = self.zoopt_get_solution(pred_res, key, max_address_num) | |||
| address_idx = [idx for idx, i in enumerate(solution) if i != 0] | |||
| solution = self.zoopt_get_solution(pred_res, pred_res_prob, key, max_address_num) | |||
| address_idx = np.where(solution != 0)[0] | |||
| candidates = self.address_by_idx(pred_res, key, address_idx) | |||
| address_num = int(solution.sum()) | |||
| min_address_num = address_num | |||
| else: | |||
| candidates, min_address_num, address_num = self.kb.abduce_candidates( | |||
| candidates = self.kb.abduce_candidates( | |||
| pred_res, key, max_address_num, require_more_address, self.multiple_predictions | |||
| ) | |||
| candidate = self._get_one_candidate(pred_res, pred_res_prob, candidates) | |||
| if self.cache: | |||
| self._set_cache(pred_res, key, min_address_num, address_num, candidates) | |||
| return candidate | |||
| def abduce_rules(self, pred_res): | |||
| return self.kb.abduce_rules(pred_res) | |||
| def batch_abduce(self, Z, Y, max_address_num=-1, require_more_address=0): | |||
| # if self.multiple_predictions: | |||
| return self.abduce((Z['cls'], Z['prob'], Y), max_address_num, require_more_address) | |||
| # else: | |||
| # return [self.abduce((z, prob, y), max_address_num, require_more_address) for z, prob, y in zip(Z['cls'], Z['prob'], Y)] | |||
| if self.multiple_predictions: | |||
| return self.abduce((Z['cls'], Z['prob'], Y), max_address_num, require_more_address) | |||
| else: | |||
| return [self.abduce((z, prob, y), max_address_num, require_more_address) for z, prob, y in zip(Z['cls'], Z['prob'], Y)] | |||
| def __call__(self, Z, Y, max_address_num=-1, require_more_address=0): | |||
| return self.batch_abduce(Z, Y, max_address_num, require_more_address) | |||
| @@ -273,11 +231,11 @@ if __name__ == '__main__': | |||
| print(kb.consist_rule([1, '+', 1, '=', 1, 0], rules), kb.consist_rule([1, '+', 1, '=', 1, 1], rules)) | |||
| print() | |||
| res = abd.abduce((consist_exs, None, [None] * len(consist_exs))) | |||
| res = abd.abduce((consist_exs, [None] * len(consist_exs), [None] * len(consist_exs))) | |||
| print(res) | |||
| res = abd.abduce((inconsist_exs, None, [None] * len(inconsist_exs))) | |||
| res = abd.abduce((inconsist_exs, [None] * len(consist_exs), [None] * len(inconsist_exs))) | |||
| print(res) | |||
| print() | |||
| abduced_rules = abd.abduce_rules(consist_exs) | |||
| print(abduced_rules) | |||
| print(abduced_rules) | |||
| @@ -17,10 +17,11 @@ import numpy as np | |||
| from collections import defaultdict | |||
| from itertools import product, combinations | |||
| from ..utils.utils import flatten, reform_idx, hamming_dist, check_equal | |||
| from ..utils.utils import flatten, reform_idx, hamming_dist, check_equal, to_hashable, hashable_to_list | |||
| from multiprocessing import Pool | |||
| from functools import lru_cache | |||
| import pyswip | |||
| class KBBase(ABC): | |||
| @@ -84,7 +85,7 @@ class KBBase(ABC): | |||
| if self.GKB_flag: | |||
| return self._abduce_by_GKB(pred_res, key, max_address_num, require_more_address, multiple_predictions) | |||
| else: | |||
| return self._abduce_by_search(pred_res, key, max_address_num, require_more_address, multiple_predictions) | |||
| return self._abduce_by_search(to_hashable(pred_res), to_hashable(key), max_address_num, require_more_address, multiple_predictions) | |||
| @abstractmethod | |||
| def _find_candidate_GKB(self, pred_res, key): | |||
| @@ -92,33 +93,32 @@ class KBBase(ABC): | |||
| def _abduce_by_GKB(self, pred_res, key, max_address_num, require_more_address, multiple_predictions): | |||
| if self.base == {}: | |||
| return [], 0, 0 | |||
| return [] | |||
| if not multiple_predictions: | |||
| if len(pred_res) not in self.len_list: | |||
| return [], 0, 0 | |||
| return [] | |||
| all_candidates = self._find_candidate_GKB(pred_res, key) | |||
| if len(all_candidates) == 0: | |||
| return [], 0, 0 | |||
| return [] | |||
| else: | |||
| cost_list = hamming_dist(pred_res, all_candidates) | |||
| min_address_num = np.min(cost_list) | |||
| address_num = min(max_address_num, min_address_num + require_more_address) | |||
| idxs = np.where(cost_list <= address_num)[0] | |||
| candidates = [all_candidates[idx] for idx in idxs] | |||
| return candidates, min_address_num, address_num | |||
| return candidates | |||
| else: | |||
| min_address_num = 0 | |||
| all_candidates_save = [] | |||
| cost_list_save = [] | |||
| for p_res, k in zip(pred_res, key): | |||
| if len(p_res) not in self.len_list: | |||
| return [], 0, 0 | |||
| return [] | |||
| all_candidates = self._find_candidate_GKB(p_res, k) | |||
| if len(all_candidates) == 0: | |||
| return [], 0, 0 | |||
| return [] | |||
| else: | |||
| all_candidates_save.append(all_candidates) | |||
| cost_list = hamming_dist(p_res, all_candidates) | |||
| @@ -126,18 +126,15 @@ class KBBase(ABC): | |||
| cost_list_save.append(cost_list) | |||
| multiple_all_candidates = [flatten(c) for c in product(*all_candidates_save)] | |||
| assert len(multiple_all_candidates[0]) == len(flatten(pred_res)) | |||
| multiple_cost_list = np.array([sum(cost) for cost in product(*cost_list_save)]) | |||
| assert len(multiple_all_candidates) == len(multiple_cost_list) | |||
| address_num = min(max_address_num, min_address_num + require_more_address) | |||
| idxs = np.where(multiple_cost_list <= address_num)[0] | |||
| candidates = [reform_idx(multiple_all_candidates[idx], pred_res) for idx in idxs] | |||
| return candidates, min_address_num, address_num | |||
| return candidates | |||
| def address_by_idx(self, pred_res, key, address_idx, multiple_predictions=False): | |||
| candidates = [] | |||
| abduce_c = product(self.pseudo_label_list, repeat=len(address_idx)) | |||
| if multiple_predictions: | |||
| save_pred_res = pred_res | |||
| pred_res = flatten(pred_res) | |||
| @@ -146,10 +143,8 @@ class KBBase(ABC): | |||
| candidate = pred_res.copy() | |||
| for i, idx in enumerate(address_idx): | |||
| candidate[idx] = c[i] | |||
| if multiple_predictions: | |||
| candidate = reform_idx(candidate, save_pred_res) | |||
| if check_equal(self._logic_forward(candidate, multiple_predictions), key, self.max_err): | |||
| candidates.append(candidate) | |||
| return candidates | |||
| @@ -166,9 +161,12 @@ class KBBase(ABC): | |||
| new_candidates += candidates | |||
| return new_candidates | |||
| @lru_cache(maxsize=100) | |||
| def _abduce_by_search(self, pred_res, key, max_address_num, require_more_address, multiple_predictions): | |||
| pred_res = hashable_to_list(pred_res) | |||
| key = hashable_to_list(key) | |||
| candidates = [] | |||
| for address_num in range(len(flatten(pred_res)) + 1): | |||
| if address_num == 0: | |||
| if check_equal(self._logic_forward(pred_res, multiple_predictions), key, self.max_err): | |||
| @@ -176,21 +174,18 @@ class KBBase(ABC): | |||
| else: | |||
| new_candidates = self._address(address_num, pred_res, key, multiple_predictions) | |||
| candidates += new_candidates | |||
| if len(candidates) > 0: | |||
| min_address_num = address_num | |||
| break | |||
| if address_num >= max_address_num: | |||
| return [], 0, 0 | |||
| return [] | |||
| for address_num in range(min_address_num + 1, min_address_num + require_more_address + 1): | |||
| if address_num > max_address_num: | |||
| return candidates, min_address_num, address_num - 1 | |||
| return candidates | |||
| new_candidates = self._address(address_num, pred_res, key, multiple_predictions) | |||
| candidates += new_candidates | |||
| return candidates, min_address_num, address_num | |||
| return candidates | |||
| def _dict_len(self, dic): | |||
| if not self.GKB_flag: | |||
| @@ -276,12 +271,16 @@ class prolog_KB(KBBase): | |||
| candidates.append(candidate) | |||
| return candidates | |||
| class HED_prolog_KB(prolog_KB): | |||
| def __init__(self, pseudo_label_list, pl_file): | |||
| super().__init__(pseudo_label_list, pl_file) | |||
| def consist_rule(self, exs, rules): | |||
| rules = str(rules).replace("\'","") | |||
| return len(list(self.prolog.query("eval_inst_feature(%s, %s)." % (exs, rules)))) != 0 | |||
| def abduce_rules(self, pred_res): | |||
| # print(pred_res) | |||
| prolog_result = list(self.prolog.query("consistent_inst_feature(%s, X)." % pred_res)) | |||
| if len(prolog_result) == 0: | |||
| return None | |||
| @@ -339,26 +338,11 @@ class HWF_KB(RegKB): | |||
| def logic_forward(self, formula): | |||
| if not self._valid_candidate(formula): | |||
| return np.inf | |||
| mapping = { | |||
| '1': '1', | |||
| '2': '2', | |||
| '3': '3', | |||
| '4': '4', | |||
| '5': '5', | |||
| '6': '6', | |||
| '7': '7', | |||
| '8': '8', | |||
| '9': '9', | |||
| '+': '+', | |||
| '-': '-', | |||
| 'times': '*', | |||
| 'div': '/', | |||
| } | |||
| mapping = {str(i): str(i) for i in range(1, 10)} | |||
| mapping.update({'+': '+', '-': '-', 'times': '*', 'div': '/'}) | |||
| formula = [mapping[f] for f in formula] | |||
| return eval(''.join(formula)) | |||
| import time | |||
| if __name__ == "__main__": | |||
| pass | |||
| @@ -150,7 +150,7 @@ def abduce_and_train(model, abducer, mapping, train_X_true, select_num): | |||
| for m in mappings: | |||
| pred_res = mapping_res(original_pred_res, m) | |||
| max_abduce_num = 20 | |||
| solution = abducer.zoopt_get_solution(pred_res, [None] * len(pred_res), max_abduce_num) | |||
| solution = abducer.zoopt_get_solution(pred_res, [None] * len(pred_res), [None] * len(pred_res), max_abduce_num) | |||
| all_address_flag = reform_idx(solution, pred_res) | |||
| consistent_idx_tmp = [] | |||
| @@ -291,7 +291,7 @@ def train_with_rule(model, abducer, train_data, val_data, select_num=10, min_len | |||
| INFO('consist_rule_acc is %f, %f\n' %(true_consist_rule_acc, false_consist_rule_acc)) | |||
| # decide next course or restart | |||
| if true_consist_rule_acc > 0.9 and false_consist_rule_acc < 0.1: | |||
| if true_consist_rule_acc > 0.95 and false_consist_rule_acc < 0.1: | |||
| torch.save(model.cls_list[0].model.state_dict(), "./weights/weights_%d.pth" % equation_len) | |||
| break | |||
| else: | |||
| @@ -22,10 +22,10 @@ def reform_idx(flatten_pred_res, save_pred_res): | |||
| def hamming_dist(A, B): | |||
| B = np.array(B) | |||
| A = np.expand_dims(A, axis=0).repeat(axis=0, repeats=(len(B))) | |||
| return np.sum(A != B, axis=1) | |||
| A = np.array(A, dtype='<U') | |||
| B = np.array(B, dtype='<U') | |||
| A = np.expand_dims(A, axis = 0).repeat(axis=0, repeats=(len(B))) | |||
| return np.sum(A != B, axis = 1) | |||
| def confidence_dist(A, B): | |||
| B = np.array(B) | |||
| @@ -38,7 +38,6 @@ def confidence_dist(A, B): | |||
| cols = np.expand_dims(cols, axis=0).repeat(axis=0, repeats=len(B)) | |||
| return 1 - np.prod(A[rows, cols, B], axis=1) | |||
| def block_sample(X, Z, Y, sample_num, epoch_idx): | |||
| part_num = len(X) // sample_num | |||
| if part_num == 0: | |||
| @@ -51,7 +50,6 @@ def block_sample(X, Z, Y, sample_num, epoch_idx): | |||
| return X, Z, Y | |||
| def gen_mappings(chars, symbs): | |||
| n_char = len(chars) | |||
| n_symbs = len(symbs) | |||
| @@ -112,3 +110,18 @@ def reduce_dimension(data): | |||
| for equation in equations | |||
| ] | |||
| data[truth_value][equation_len] = reduced_equations | |||
| def to_hashable(l): | |||
| if type(l) is not list: | |||
| return l | |||
| if type(l[0]) is not list: | |||
| return tuple(l) | |||
| return tuple(tuple(sublist) for sublist in l) | |||
| def hashable_to_list(t): | |||
| if type(t) is not tuple: | |||
| return t | |||
| if type(t[0]) is not tuple: | |||
| return list(t) | |||
| return [list(subtuple) for subtuple in t] | |||