From bc8a72c21dad1289b307e1ee49787cc3762fa15f Mon Sep 17 00:00:00 2001 From: Tony-HYX <605698554@qq.com> Date: Mon, 21 Nov 2022 14:47:42 +0800 Subject: [PATCH 1/9] update readme --- README => readme.md | 9 +++++++++ 1 file changed, 9 insertions(+) rename README => readme.md (66%) diff --git a/README b/readme.md similarity index 66% rename from README rename to readme.md index 0aa2a11..1a9ebf1 100644 --- a/README +++ b/readme.md @@ -22,3 +22,12 @@ python share_example.py ## NOTICE They can only be used for academic purpose. For other purposes, please contact with LAMDA Group(www.lamda.nju.edu.cn). +## To do list + +- Improve speed and accuracy +- Add comparison with DeepProbLog, NGS,... (Accuracy and Speed) +- Add Inference/Abduction example with FOL engine (e.g., Prolog) +- Add zoopt optimization +- Rearrange structure and make it a python package +- Documents + From 0242e44c09f7e46532bc87c362a5141c36fb8ff7 Mon Sep 17 00:00:00 2001 From: troyyyyy <49091847+troyyyyy@users.noreply.github.com> Date: Mon, 21 Nov 2022 14:56:35 +0800 Subject: [PATCH 2/9] Update kb.py --- abducer/kb.py | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/abducer/kb.py b/abducer/kb.py index 98ea9bc..c206f62 100644 --- a/abducer/kb.py +++ b/abducer/kb.py @@ -19,7 +19,7 @@ from collections import defaultdict from itertools import product class KBBase(ABC): - def __init__(self, GKB_flag = False): + def __init__(self): pass @abstractmethod @@ -31,7 +31,11 @@ class KBBase(ABC): pass @abstractmethod - def logic_forward(self, X): + def logic_forward(self): + pass + + @abstractmethod + def valid_candidate(self): pass def _length(self, length): @@ -48,27 +52,34 @@ class KBBase(ABC): class ClsKB(KBBase): def __init__(self, GKB_flag = False, pseudo_label_list = None, len_list = None): super().__init__() + self.GKB_flag = GKB_flag self.pseudo_label_list = pseudo_label_list self.base = {} self.len_list = len_list if GKB_flag: - X = self.get_X(self.pseudo_label_list, self.len_list) - Y = self.get_Y(X, self.logic_forward) + X, Y = self.get_GKB(self.pseudo_label_list, self.len_list) for x, y in zip(X, Y): self.base.setdefault(len(x), defaultdict(list))[y].append(np.array(x)) - def get_X(self, pseudo_label_list, len_list): - res = [] + def get_GKB(self, pseudo_label_list, len_list): + all_X = [] for len in len_list: - res += list(product(pseudo_label_list, repeat = len)) - return res - - def get_Y(self, X, logic_forward): - return [logic_forward(nums) for nums in X] + all_X += list(product(pseudo_label_list, repeat = len)) + + X = [] + Y = [] + for x in all_X: + if self.valid_candidate(x): + X.append(x) + Y.append(self.logic_forward(x)) + return X, Y + + def valid_candidate(self): + pass def logic_forward(self): - return None + pass def get_candidates(self, key, length = None): if(self.base == {}): @@ -92,11 +103,13 @@ class ClsKB(KBBase): return sum(self._dict_len(v) for v in self.base.values()) - class add_KB(ClsKB): def __init__(self, GKB_flag = False, len_list = [2]): self.pseudo_label_list = list(range(10)) super().__init__(GKB_flag, self.pseudo_label_list, len_list) + + def valid_candidate(self, x): + return True def logic_forward(self, nums): return sum(nums) @@ -107,7 +120,7 @@ class hwf_KB(ClsKB): self.pseudo_label_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/'] super().__init__(GKB_flag, self.pseudo_label_list, len_list) - def valid_formula(self, formula): + def valid_candidate(self, formula): if(len(formula) % 2 == 0): return False for i in range(len(formula)): @@ -118,7 +131,7 @@ class hwf_KB(ClsKB): return True def logic_forward(self, formula): - if(self.valid_formula(formula) == False): + if(self.valid_candidate(formula) == False): return np.inf try: return round(eval(''.join(formula)), 2) @@ -140,8 +153,12 @@ class RegKB(KBBase): Y = [y for y, x in data] self.base[l] = (X, Y) + def valid_candidate(self): + pass + def logic_forward(self): - return None + pass + def get_candidates(self, key, length = None): if key is None: From d544ccd3c12fefcbaebf0cee867409c48a290348 Mon Sep 17 00:00:00 2001 From: troyyyyy <49091847+troyyyyy@users.noreply.github.com> Date: Mon, 21 Nov 2022 14:56:54 +0800 Subject: [PATCH 3/9] Update abducer_base.py --- abducer/abducer_base.py | 55 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/abducer/abducer_base.py b/abducer/abducer_base.py index bbf00fa..6de9987 100644 --- a/abducer/abducer_base.py +++ b/abducer/abducer_base.py @@ -11,16 +11,12 @@ #================================================================# import abc -# from kb import add_KB, hwf_KB -from abducer.kb import add_KB, hwf_KB +from kb import add_KB, hwf_KB +# from abducer.kb import add_KB, hwf_KB import numpy as np from itertools import product, combinations - - - - class AbducerBase(abc.ABC): def __init__(self, kb, dist_func = 'confidence', cache = True): self.kb = kb @@ -57,10 +53,15 @@ class AbducerBase(abc.ABC): return self.confidence_dist(pred_res_prob, candidates) def get_min_cost_candidate(self, pred_res, pred_res_prob, candidates): - 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] - return [candidates[idx] for idx in idxs][0] + if(len(candidates) == 0): + return [] + elif(len(candidates) == 1): + return candidates[0] + 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] + return [candidates[idx] for idx in idxs][0] def abduce(self, data, max_address_num = -1, require_more_address = 0): pred_res, pred_res_prob, ans = data @@ -75,13 +76,16 @@ class AbducerBase(abc.ABC): candidate = self.get_min_cost_candidate(pred_res, pred_res_prob, candidates) return candidate - if(self.kb.base != {}): + if self.kb.GKB_flag: all_candidates = self.kb.get_candidates(ans, len(pred_res)) - cost_list = self.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] + if(len(all_candidates) == 0): + return [] + else: + cost_list = self.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] else: candidates, min_address_num, address_num = self.get_abduce_candidates(pred_res, ans, max_address_num, require_more_address) @@ -111,8 +115,7 @@ class AbducerBase(abc.ABC): candidates = [] for address_num in range(len(pred_res) + 1): if(address_num > max_address_num): - print('No candidates found') - return None, None, None + return [], None, None if(address_num == 0): if(abs(self.kb.logic_forward(pred_res) - key) <= 1e-3): @@ -146,24 +149,26 @@ class AbducerBase(abc.ABC): if __name__ == '__main__': - kb = add_KB() + kb = add_KB(GKB_flag = True) abd = AbducerBase(kb, 'hamming') - res = abd.abduce(([1, 1, 1], None, 4), max_address_num = 2, require_more_address = 0) + res = abd.abduce(([1, 1], None, 4), max_address_num = 2, require_more_address = 0) print(res) - res = abd.abduce(([1, 1, 1], None, 4), max_address_num = 2, require_more_address = 1) + res = abd.abduce(([1, 1], None, 4), max_address_num = 2, require_more_address = 1) print(res) - res = abd.abduce(([1, 1, 1], None, 4), max_address_num = 1, require_more_address = 1) + res = abd.abduce(([1, 1], None, 4), max_address_num = 1, require_more_address = 1) print(res) - res = abd.abduce(([1, 1, 1], None, 4), max_address_num = 2, require_more_address = 0) + res = abd.abduce(([1, 1], None, 4), max_address_num = 2, require_more_address = 0) print(res) - res = abd.abduce(([1, 1, 1], None, 5), max_address_num = 2, require_more_address = 1) + res = abd.abduce(([1, 1], None, 5), max_address_num = 2, require_more_address = 1) print(res) print() kb = hwf_KB() - abd = AbducerBase(kb) + abd = AbducerBase(kb, 'hamming') res = abd.abduce((['5', '+', '2'], None, 3), max_address_num = 2, require_more_address = 0) print(res) + res = abd.abduce((['5', '+', '2'], None, 3.09), max_address_num = 2, require_more_address = 0) + print(res) res = abd.abduce((['5', '+', '2'], None, 1.67), max_address_num = 3, require_more_address = 0) print(res) res = abd.abduce((['5', '+', '3'], None, 0.33), max_address_num = 3, require_more_address = 3) From 4a1de111f96d8876f6bd43a69a7658da14cb9221 Mon Sep 17 00:00:00 2001 From: troyyyyy <49091847+troyyyyy@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:00:58 +0800 Subject: [PATCH 4/9] Update kb.py --- abducer/kb.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/abducer/kb.py b/abducer/kb.py index c206f62..499d9bb 100644 --- a/abducer/kb.py +++ b/abducer/kb.py @@ -60,7 +60,7 @@ class ClsKB(KBBase): if GKB_flag: X, Y = self.get_GKB(self.pseudo_label_list, self.len_list) for x, y in zip(X, Y): - self.base.setdefault(len(x), defaultdict(list))[y].append(np.array(x)) + self.base.setdefault(len(x), defaultdict(list))[y].append(x) def get_GKB(self, pseudo_label_list, len_list): all_X = [] @@ -104,9 +104,10 @@ class ClsKB(KBBase): class add_KB(ClsKB): - def __init__(self, GKB_flag = False, len_list = [2]): - self.pseudo_label_list = list(range(10)) - super().__init__(GKB_flag, self.pseudo_label_list, len_list) + def __init__(self, GKB_flag = False, \ + pseudo_label_list = list(range(10)), \ + len_list = [2]): + super().__init__(GKB_flag, pseudo_label_list, len_list) def valid_candidate(self, x): return True @@ -116,9 +117,10 @@ class add_KB(ClsKB): class hwf_KB(ClsKB): - def __init__(self, GKB_flag = False, len_list = [1, 3, 5, 7]): - self.pseudo_label_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/'] - super().__init__(GKB_flag, self.pseudo_label_list, len_list) + def __init__(self, GKB_flag = False, \ + pseudo_label_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/'], \ + len_list = [1, 3, 5, 7]): + super().__init__(GKB_flag, pseudo_label_list, len_list) def valid_candidate(self, formula): if(len(formula) % 2 == 0): From 6c76700be1a4cf98bd32ebad90883da57fcc1b63 Mon Sep 17 00:00:00 2001 From: troyyyyy <49091847+troyyyyy@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:03:10 +0800 Subject: [PATCH 5/9] Update abducer_base.py --- abducer/abducer_base.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/abducer/abducer_base.py b/abducer/abducer_base.py index 6de9987..a0af59d 100644 --- a/abducer/abducer_base.py +++ b/abducer/abducer_base.py @@ -155,10 +155,6 @@ if __name__ == '__main__': print(res) res = abd.abduce(([1, 1], None, 4), max_address_num = 2, require_more_address = 1) print(res) - res = abd.abduce(([1, 1], None, 4), max_address_num = 1, require_more_address = 1) - print(res) - res = abd.abduce(([1, 1], None, 4), max_address_num = 2, require_more_address = 0) - print(res) res = abd.abduce(([1, 1], None, 5), max_address_num = 2, require_more_address = 1) print(res) print() From 15231012669e0fffe8e684e01aa9bf92d2834e7c Mon Sep 17 00:00:00 2001 From: Tony-HYX <605698554@qq.com> Date: Mon, 21 Nov 2022 15:23:26 +0800 Subject: [PATCH 6/9] update basic model --- models/basic_model.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/models/basic_model.py b/models/basic_model.py index 86fbea5..182e1e5 100644 --- a/models/basic_model.py +++ b/models/basic_model.py @@ -131,21 +131,19 @@ class BasicModel(): model.train() - loss_value = 0 - for _, data in enumerate(data_loader): - X = data[0].to(device) - Y = data[1].to(device) - pred_Y = model(X) - - loss = criterion(pred_Y, Y) + total_loss, total_num = 0.0, 0 + for data, target in data_loader: + data, target = data.to(device), target.to(device) + out = model(data) + loss = criterion(out, target) optimizer.zero_grad() loss.backward() optimizer.step() - loss_value += loss.item() + total_loss += loss.item() * data.size(0) - return loss_value + return total_loss / total_num def _predict(self, data_loader): model = self.model @@ -155,9 +153,9 @@ class BasicModel(): with torch.no_grad(): results = [] - for _, data in enumerate(data_loader): - X = data[0].to(device) - pred_Y = model(X) + for data, _ in data_loader: + data = data.to(device) + pred_Y = model(data) results.append(pred_Y) return torch.cat(results, axis=0) From 41be49c68ec1c7eb414fc3839815b61305acbaf4 Mon Sep 17 00:00:00 2001 From: troyyyyy <49091847+troyyyyy@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:55:58 +0800 Subject: [PATCH 7/9] Update abducer_base.py --- abducer/abducer_base.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/abducer/abducer_base.py b/abducer/abducer_base.py index a0af59d..b26c88a 100644 --- a/abducer/abducer_base.py +++ b/abducer/abducer_base.py @@ -47,15 +47,15 @@ class AbducerBase(abc.ABC): def get_cost_list(self, pred_res, pred_res_prob, candidates): - if(self.dist_func == 'hamming'): + if self.dist_func == 'hamming': return self.hamming_dist(pred_res, candidates) - elif(self.dist_func == 'confidence'): + elif self.dist_func == 'confidence': return self.confidence_dist(pred_res_prob, candidates) def get_min_cost_candidate(self, pred_res, pred_res_prob, candidates): - if(len(candidates) == 0): + if len(candidates) == 0: return [] - elif(len(candidates) == 1): + elif len(candidates) == 1: return candidates[0] else: cost_list = self.get_cost_list(pred_res, pred_res_prob, candidates) @@ -68,9 +68,9 @@ class AbducerBase(abc.ABC): if max_address_num == -1: max_address_num = len(pred_res) - if(self.cache and (tuple(pred_res), ans) in self.cache_min_address_num): + if self.cache and (tuple(pred_res), ans) in self.cache_min_address_num: address_num = min(max_address_num, self.cache_min_address_num[(tuple(pred_res), ans)] + require_more_address) - if((tuple(pred_res), ans, address_num) in self.cache_candidates): + if (tuple(pred_res), ans, address_num) in self.cache_candidates: # print('cached') candidates = self.cache_candidates[(tuple(pred_res), ans, address_num)] candidate = self.get_min_cost_candidate(pred_res, pred_res_prob, candidates) @@ -78,7 +78,7 @@ class AbducerBase(abc.ABC): if self.kb.GKB_flag: all_candidates = self.kb.get_candidates(ans, len(pred_res)) - if(len(all_candidates) == 0): + if len(all_candidates) == 0: return [] else: cost_list = self.hamming_dist(pred_res, all_candidates) @@ -90,7 +90,7 @@ class AbducerBase(abc.ABC): else: candidates, min_address_num, address_num = self.get_abduce_candidates(pred_res, ans, max_address_num, require_more_address) - if(self.cache): + if self.cache: self.cache_min_address_num[(tuple(pred_res), ans)] = min_address_num self.cache_candidates[(tuple(pred_res), ans, address_num)] = candidates @@ -104,9 +104,9 @@ class AbducerBase(abc.ABC): for address_idx in address_idx_list: for c in all_address_candidate: pred_res_array = np.array(pred_res) - if(np.count_nonzero(np.array(c) != pred_res_array[np.array(address_idx)]) == address_num): + if np.count_nonzero(np.array(c) != pred_res_array[np.array(address_idx)]) == address_num: pred_res_array[np.array(address_idx)] = c - if(self.kb.logic_forward(pred_res_array) == key): + if self.kb.logic_forward(pred_res_array) == key: new_candidates.append(pred_res_array) return new_candidates, address_num @@ -114,22 +114,22 @@ class AbducerBase(abc.ABC): candidates = [] for address_num in range(len(pred_res) + 1): - if(address_num > max_address_num): + if address_num > max_address_num: return [], None, None - if(address_num == 0): - if(abs(self.kb.logic_forward(pred_res) - key) <= 1e-3): + if address_num == 0: + if abs(self.kb.logic_forward(pred_res) - key) <= 1e-3: candidates.append(pred_res) else: new_candidates, address_num = self.address(address_num, pred_res, key) candidates += new_candidates - if(len(candidates) > 0): + if len(candidates) > 0: min_address_num = address_num break for address_num in range(min_address_num + 1, min_address_num + require_more_address + 1): - if(address_num > max_address_num): + if address_num > max_address_num: return candidates, min_address_num, address_num - 1 new_candidates, address_num = self.address(address_num, pred_res, key) candidates += new_candidates From 6925860422ea8f5134c9389dca78af4ad3edaa2a Mon Sep 17 00:00:00 2001 From: troyyyyy <49091847+troyyyyy@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:56:09 +0800 Subject: [PATCH 8/9] Update kb.py --- abducer/kb.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/abducer/kb.py b/abducer/kb.py index 499d9bb..7fa4b3f 100644 --- a/abducer/kb.py +++ b/abducer/kb.py @@ -54,10 +54,10 @@ class ClsKB(KBBase): super().__init__() self.GKB_flag = GKB_flag self.pseudo_label_list = pseudo_label_list - self.base = {} self.len_list = len_list if GKB_flag: + self.base = {} X, Y = self.get_GKB(self.pseudo_label_list, self.len_list) for x, y in zip(X, Y): self.base.setdefault(len(x), defaultdict(list))[y].append(x) @@ -82,7 +82,7 @@ class ClsKB(KBBase): pass def get_candidates(self, key, length = None): - if(self.base == {}): + if not self.GKB_flag or self.base == {}: return [] if key is None: @@ -94,13 +94,22 @@ class ClsKB(KBBase): return sum([self.base[l][key] for l in length], []) def get_all_candidates(self): - return sum([sum(v.values(), []) for v in self.base.values()], []) + if not self.GKB_flag or self.base == {}: + return [] + else: + return sum([sum(v.values(), []) for v in self.base.values()], []) def _dict_len(self, dic): - return sum(len(c) for c in dic.values()) + if not self.GKB_flag: + return 0 + else: + return sum(len(c) for c in dic.values()) def __len__(self): - return sum(self._dict_len(v) for v in self.base.values()) + if not self.GKB_flag: + return 0 + else: + return sum(self._dict_len(v) for v in self.base.values()) class add_KB(ClsKB): @@ -123,17 +132,17 @@ class hwf_KB(ClsKB): super().__init__(GKB_flag, pseudo_label_list, len_list) def valid_candidate(self, formula): - if(len(formula) % 2 == 0): + if len(formula) % 2 == 0: return False for i in range(len(formula)): - if(i % 2 == 0 and formula[i] not in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']): + if i % 2 == 0 and formula[i] not in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: return False - if(i % 2 != 0 and formula[i] not in ['+', '-', '*', '/']): + if i % 2 != 0 and formula[i] not in ['+', '-', '*', '/']: return False return True def logic_forward(self, formula): - if(self.valid_candidate(formula) == False): + if not self.valid_candidate(formula): return np.inf try: return round(eval(''.join(formula)), 2) From 5c6cbd45c74c2099ca5fbc7dc9c88a49bdf19077 Mon Sep 17 00:00:00 2001 From: troyyyyy <49091847+troyyyyy@users.noreply.github.com> Date: Mon, 21 Nov 2022 16:50:11 +0800 Subject: [PATCH 9/9] Update kb.py --- abducer/kb.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/abducer/kb.py b/abducer/kb.py index 7fa4b3f..8c3f49a 100644 --- a/abducer/kb.py +++ b/abducer/kb.py @@ -127,7 +127,7 @@ class add_KB(ClsKB): class hwf_KB(ClsKB): def __init__(self, GKB_flag = False, \ - pseudo_label_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/'], \ + pseudo_label_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', 'times', 'div'], \ len_list = [1, 3, 5, 7]): super().__init__(GKB_flag, pseudo_label_list, len_list) @@ -137,7 +137,7 @@ class hwf_KB(ClsKB): for i in range(len(formula)): if i % 2 == 0 and formula[i] not in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: return False - if i % 2 != 0 and formula[i] not in ['+', '-', '*', '/']: + if i % 2 != 0 and formula[i] not in ['+', '-', 'times', 'div']: return False return True @@ -145,6 +145,8 @@ class hwf_KB(ClsKB): if not self.valid_candidate(formula): return np.inf try: + mapping = {'0':'0', '1':'1', '2':'2', '3':'3', '4':'4', '5':'5', '6':'6', '7':'7', '8':'8', '9':'9', '+':'+', '-':'-', 'times':'*', 'div':'/'} + formula = [mapping[f] for f in formula] return round(eval(''.join(formula)), 2) except ZeroDivisionError: return np.inf