Browse Source

abduction with exhastive search

pull/3/head
troyyyyy 3 years ago
parent
commit
bb89a659b3
5 changed files with 35160 additions and 132 deletions
  1. +54
    -59
      abducer/abducer_base.py
  2. +69
    -73
      abducer/kb.py
  3. +37
    -0
      datasets/mnist_add/get_mnist_add.py
  4. +5000
    -0
      datasets/mnist_add/test_data.txt
  5. +30000
    -0
      datasets/mnist_add/train_data.txt

+ 54
- 59
abducer/abducer_base.py View File

@@ -11,58 +11,60 @@
#================================================================#

import abc
from abducer.kb import ClsKB, RegKB
#from kb import ClsKB, RegKB
from kb import add_KB
import numpy as np

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)

def confidence_dist(A, B):
B = np.array(B)

#print(A)
A = np.clip(A, 1e-9, 1)
A = np.expand_dims(A, axis=0)
A = A.repeat(axis=0, repeats=(len(B)))
rows = np.array(range(len(B)))
rows = np.expand_dims(rows, axis = 1).repeat(axis = 1, repeats = len(B[0]))
cols = np.array(range(len(B[0])))
cols = np.expand_dims(cols, axis = 0).repeat(axis = 0, repeats = len(B))
return 1 - np.prod(A[rows, cols, B], axis = 1)
return np.sum(np.array(A) != np.array(B))

class AbducerBase(abc.ABC):
def __init__(self, kb, dist_func = "hamming", pred_res_parse = None):
def __init__(self, kb, dist_func = "hamming", pred_res_parse = None, cache = True):
self.kb = kb
if dist_func == "hamming":
dist_func = hamming_dist
elif dist_func == "confidence":
dist_func = confidence_dist
self.dist_func = dist_func
self.dist_func = hamming_dist
if pred_res_parse is None:
pred_res_parse = lambda x : x["cls"]
self.pred_res_parse = pred_res_parse
self.cache = cache
self.cache_min_address_num = {}
self.cache_candidates = {}

def abduce(self, data, max_address_num, require_more_address, length = -1):
def abduce(self, data, max_address_num = 3, require_more_address = 0, length = -1):
pred_res, ans = data

if length == -1:
length = len(pred_res)

candidates = self.kb.get_candidates(ans, length)
pred_res = np.array(pred_res)

cost_list = self.dist_func(pred_res, candidates)
address_num = np.min(cost_list)
threshold = min(address_num + require_more_address, max_address_num)
idxs = np.where(cost_list <= address_num+require_more_address)[0]

#return [candidates[idx] for idx in idxs], address_num
if len(idxs) > 1:
return None
return [candidates[idx] for idx in idxs][0]
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):
print('cached')
return self.cache_candidates[(tuple(pred_res), ans, address_num)]
candidates, min_address_num, address_num = self.kb.get_abduce_candidates(pred_res, ans, length, self.dist_func, max_address_num, require_more_address)
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

return candidates
# candidates = self.kb.get_candidates(ans, length)
# cost_list = self.dist_func(pred_res, candidates)
# address_num = np.min(cost_list)
# # threshold = min(address_num + require_more_address, max_address_num)
# idxs = np.where(cost_list <= address_num + require_more_address)[0]

# return [candidates[idx] for idx in idxs], address_num
# if len(idxs) > 1:
# return None
# return [candidates[idx] for idx in idxs]

def batch_abduce(self, Y, C, max_address_num = 3, require_more_address = 0):
return [
@@ -71,34 +73,27 @@ class AbducerBase(abc.ABC):
]

def __call__(self, Y, C, max_address_num = 3, require_more_address = 0):
return batch_abduce(Y, C, max_address_num, require_more_address)
return self.batch_abduce(Y, C, max_address_num, require_more_address)



if __name__ == "__main__":
#["1+1", "0+1", "1+0", "2+0"]
X = [[1,3,1], [0,3,1], [1,2,0], [3,2,0]]
Y = [2, 1, 1, 2]
kb = RegKB(X, Y)
pseudo_label_list = list(range(10))
kb = add_KB(pseudo_label_list)
abd = AbducerBase(kb)
res = abd.abduce(([0,2,0], None), 1, 0)
res = abd.abduce(([1, 1, 1], 4), max_address_num = 2, require_more_address = 0)
print(res)
res = abd.abduce(([0, 2, 0], 0.99), 1, 0)
res = abd.abduce(([1, 1, 1], 4), max_address_num = 2, require_more_address = 1)
print(res)

A = np.array([[0.5, 0.25, 0.25, 0], [0.3, 0.3, 0.3, 0.1], [0.1, 0.2, 0.3, 0.4]])
B = [[1, 2, 3], [0, 1, 3]]
res = confidence_dist(A, B)
res = abd.abduce(([1, 1, 1], 4), max_address_num = 1, require_more_address = 1)
print(res)

A = np.array([[0.5, 0.25, 0.25, 0], [0.3, 1.0, 0.3, 0.1], [0.1, 0.2, 0.3, 1.0]])
B = [[0, 1, 3]]
res = confidence_dist(A, B)
print()
print('Test cache')
res = abd.abduce(([1, 1, 1], 4), max_address_num = 2, require_more_address = 0)
print(res)

kb_str = ['10010001011', '00010001100', '00111101011', '11101000011', '11110011001', '11111010001', '10001010010', '11100100001', '10001001100', '11011010001', '00110000100', '11000000111', '01110111111', '11000101100', '10101011010', '00000110110', '11111110010', '11100101100', '10111001111', '10000101100', '01001011101', '01001110000', '01110001110', '01010010001', '10000100010', '01001011011', '11111111100', '01011101101', '00101110101', '11101001101', '10010110000', '10000000011']
X = [[int(c) for c in s] for s in kb_str]
kb = RegKB(X, len(X) * [None])

abd = AbducerBase(kb)
res = abd.abduce(((1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1), None), 1, 0)
res = abd.abduce(([1, 1, 1], 4), max_address_num = 20, require_more_address = 1)
print(res)
# res = abd.abduce(([0, 2, 0], 0.99), 1, 0)
# print(res)


+ 69
- 73
abducer/kb.py View File

@@ -10,57 +10,104 @@
#
#================================================================#

import abc
from abc import ABC, abstractmethod
import bisect
import copy
import numpy as np

from collections import defaultdict

class KBBase(abc.ABC):
def __init__(self, X = None, Y = None):
from itertools import product

class KBBase(ABC):
def __init__(self):
pass

def get_candidates(self, key = None, length = None):
@abstractmethod
def get_candidates(self):
pass

@abstractmethod
def get_all_candidates(self):
pass

@abstractmethod
def logic_forward(self, X):
pass
def _length(self, length):
if length is None:
length = list(self.base.keys())
if type(length) is int:
length = [length]
return length
def __len__(self):
pass

class ClsKB(KBBase):
def __init__(self, X, Y = None):
class add_KB(KBBase):
def __init__(self, pseudo_label_list, max_len = 5):
super().__init__()
self.pseudo_label_list = pseudo_label_list
self.base = {}
if X is None:
return

if Y is None:
Y = [None] * len(X)
X = self.get_X(self.pseudo_label_list, max_len)
Y = self.get_Y(X, self.logic_forward)

for x, y in zip(X, Y):
self.base.setdefault(len(x), defaultdict(list))[y].append(np.array(x))
def logic_forward(self, nums):
return sum(nums)
def get_X(self, pseudo_label_list, max_len):
res = []
assert(max_len >= 2)
for len in range(2, max_len + 1):
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]

def get_candidates(self, key, length = None):
if key is None:
return self.get_all_candidates()

length = self._length(length)

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()], [])
def get_abduce_candidates(self, pred_res, key, length, dist_func, max_address_num, require_more_address):
if key is None:
return self.get_all_candidates()
candidates = []
all_candidates = list(product(self.pseudo_label_list, repeat = len(pred_res)))
for address_num in range(length + 1):
if(address_num > max_address_num):
print('No candidates found')
return None, None, None
for c in all_candidates:
if(dist_func(c, pred_res) == address_num):
if(self.logic_forward(c) == key):
candidates.append(c)
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):
return candidates, min_address_num, address_num - 1
for c in all_candidates:
if(dist_func(c, pred_res) == address_num):
if(self.logic_forward(c) == key):
candidates.append(c)

return candidates, min_address_num, address_num

def _dict_len(self, dic):
return sum(len(c) for c in dic.values())
@@ -68,70 +115,19 @@ class ClsKB(KBBase):
def __len__(self):
return sum(self._dict_len(v) for v in self.base.values())

class RegKB(KBBase):
def __init__(self, X, Y = None):
super().__init__()
tmp_dict = {}
for x, y in zip(X, Y):
tmp_dict.setdefault(len(x), defaultdict(list))[y].append(np.array(x))

self.base = {}
for l in tmp_dict.keys():
data = sorted(list(zip(tmp_dict[l].keys(), tmp_dict[l].values())))
X = [x for y, x in data]
Y = [y for y, x in data]
self.base[l] = (X, Y)
def get_candidates(self, key, length = None):
if key is None:
return self.get_all_candidates()

length = self._length(length)

min_err = 999999
candidates = []
for l in length:
X, Y = self.base[l]

idx = bisect.bisect_left(Y, key)
begin = max(0, idx - 1)
end = min(idx + 2, len(X))

for idx in range(begin, end):
err = abs(Y[idx] - key)
if abs(err - min_err) < 1e-9:
candidates.extend(X[idx])
elif err < min_err:
candidates = copy.deepcopy(X[idx])
min_err = err
return candidates

def get_all_candidates(self):
return sum([sum(D[0], []) for D in self.base.values()], [])

def __len__(self):
return sum([sum(len(x) for x in D[0]) for D in self.base.values()])

if __name__ == "__main__":
X = ["1+1", "0+1", "1+0", "2+0", "1+0+1"]
Y = [2, 1, 1, 2, 2]
kb = ClsKB(X, Y)
print(len(kb))
res = kb.get_candidates(2, 5)
print(res)
res = kb.get_candidates(2, 3)
print(res)
res = kb.get_candidates(None)
print(res)

X = ["1+1", "0+1", "1+0", "2+0", "1+0.5", "0.75+0.75"]
Y = [2, 1, 1, 2, 1.5, 1.5]
kb = RegKB(X, Y)
print(len(kb))
res = kb.get_candidates(1.6)
pseudo_label_list = list(range(10))
kb = add_KB(pseudo_label_list, max_len = 5)
print('len(kb):', len(kb))
print()
res = kb.get_candidates(0)
print(res)
res = kb.get_candidates(1.6, length = 9)
print()
res = kb.get_candidates(18, length = 2)
print(res)
res = kb.get_candidates(None)
print()
res = kb.get_candidates(7, length = 3)
print(res)


+ 37
- 0
datasets/mnist_add/get_mnist_add.py View File

@@ -0,0 +1,37 @@
import torch
import torchvision
from torch.utils.data import Dataset
from torchvision.transforms import transforms

class MNIST_Addition(Dataset):
def __init__(self, dataset, examples):
self.data = list()
self.dataset = dataset
with open(examples) as f:
for line in f:
line = line.strip().split(' ')
self.data.append(tuple([int(i) for i in line]))

def __len__(self):
return len(self.data)

def __getitem__(self, index):
i1, i2, l = self.data[index]
return self.dataset[i1][0], self.dataset[i2][0], l

def get_mnist_add():
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081, ))])
train_dataset = MNIST_Addition(torchvision.datasets.MNIST(root='./', train=True, download=True, transform=transform), './train_data.txt')
test_loader = torch.utils.data.DataLoader(torchvision.datasets.MNIST('./', train=False, transform=transform), batch_size=1000, shuffle=True)
X = []
Y = []
for i1, i2, l in train_dataset:
X.append([i1, i2])
Y.append(l)
return X, Y, test_loader

if __name__ == "__main__":
X, Y, test_loader = get_mnist_add()
print(len(X), len(Y))
print(X[0][0].shape, X[0][1].shape, Y[0])

+ 5000
- 0
datasets/mnist_add/test_data.txt
File diff suppressed because it is too large
View File


+ 30000
- 0
datasets/mnist_add/train_data.txt
File diff suppressed because it is too large
View File


Loading…
Cancel
Save