diff --git a/docs/Examples/HED.rst b/docs/Examples/HED.rst index fcdeb24..cf17f80 100644 --- a/docs/Examples/HED.rst +++ b/docs/Examples/HED.rst @@ -1,5 +1,298 @@ -Handwritten Equation Deciphering (HED) -====================================== +Handwritten Equation Decipherment (HED) +======================================= -.. contents:: Table of Contents +Below shows an implementation of `Handwritten Equation +Decipherment `__. +In this task, the handwritten equations are given, which consist of +sequential pictures of characters. The equations are generated with +unknown operation rules from images of symbols (‘0’, ‘1’, ‘+’ and ‘=’), +and each equation is associated with a label indicating whether the +equation is correct (i.e., positive) or not (i.e., negative). Also, we +are given a knowledge base which involves the structure of the equations +and a recursive definition of bit-wise operations. The task is to learn +from a training set of above mentioned equations and then to predict +labels of unseen equations. +Intuitively, we first use a machine learning model (learning part) to +obtain the pseudo-labels (‘0’, ‘1’, ‘+’ and ‘=’) for the observed +pictures. We then use the knowledge base (reasoning part) to perform +abductive reasoning so as to yield ground hypotheses as possible +explanations to the observed facts, suggesting some pseudo-labels to be +revised. This process enables us to further update the machine learning +model. + +.. code:: ipython3 + + # Import necessary libraries and modules + import os.path as osp + import torch + import torch.nn as nn + import matplotlib.pyplot as plt + from examples.hed.datasets import get_dataset, split_equation + from examples.models.nn import SymbolNet + from abl.learning import ABLModel, BasicNN + from examples.hed.reasoning import HedKB, HedReasoner + from abl.evaluation import ReasoningMetric, SymbolMetric + from abl.utils import ABLLogger, print_log + from examples.hed.bridge import HedBridge + +Working with Data +----------------- + +First, we get the datasets of handwritten equations: + +.. code:: ipython3 + + total_train_data = get_dataset(train=True) + train_data, val_data = split_equation(total_train_data, 3, 1) + test_data = get_dataset(train=False) + +The dataset are shown below: + +.. code:: ipython3 + + true_train_equation = train_data[1] + false_train_equation = train_data[0] + print(f"Equations in the dataset is organized by equation length, " + + f"from {min(train_data[0].keys())} to {max(train_data[0].keys())}") + print() + + true_train_equation_with_length_5 = true_train_equation[5] + false_train_equation_with_length_5 = false_train_equation[5] + print(f"For each euqation length, there are {len(true_train_equation_with_length_5)} " + + f"true equation and {len(false_train_equation_with_length_5)} false equation " + + f"in the training set") + + true_val_equation = val_data[1] + false_val_equation = val_data[0] + true_val_equation_with_length_5 = true_val_equation[5] + false_val_equation_with_length_5 = false_val_equation[5] + print(f"For each euqation length, there are {len(true_val_equation_with_length_5)} " + + f"true equation and {len(false_val_equation_with_length_5)} false equation " + + f"in the validation set") + + true_test_equation = test_data[1] + false_test_equation = test_data[0] + true_test_equation_with_length_5 = true_test_equation[5] + false_test_equation_with_length_5 = false_test_equation[5] + print(f"For each euqation length, there are {len(true_test_equation_with_length_5)} " + + f"true equation and {len(false_test_equation_with_length_5)} false equation " + + f"in the test set") + + +Out: + .. code:: none + :class: code-out + + Equations in the dataset is organized by equation length, from 5 to 26 + + For each euqation length, there are 225 true equation and 225 false equation in the training set + For each euqation length, there are 75 true equation and 75 false equation in the validation set + For each euqation length, there are 300 true equation and 300 false equation in the test set + + +As illustrations, we show four equations in the training dataset: + +.. code:: ipython3 + + true_train_equation_with_length_5 = true_train_equation[5] + true_train_equation_with_length_8 = true_train_equation[8] + print(f"First true equation with length 5 in the training dataset:") + for i, x in enumerate(true_train_equation_with_length_5[0]): + plt.subplot(1, 5, i+1) + plt.axis('off') + plt.imshow(x.transpose(1, 2, 0)) + plt.show() + print(f"First true equation with length 8 in the training dataset:") + for i, x in enumerate(true_train_equation_with_length_8[0]): + plt.subplot(1, 8, i+1) + plt.axis('off') + plt.imshow(x.transpose(1, 2, 0)) + plt.show() + + false_train_equation_with_length_5 = false_train_equation[5] + false_train_equation_with_length_8 = false_train_equation[8] + print(f"First false equation with length 5 in the training dataset:") + for i, x in enumerate(false_train_equation_with_length_5[0]): + plt.subplot(1, 5, i+1) + plt.axis('off') + plt.imshow(x.transpose(1, 2, 0)) + plt.show() + print(f"First false equation with length 8 in the training dataset:") + for i, x in enumerate(false_train_equation_with_length_8[0]): + plt.subplot(1, 8, i+1) + plt.axis('off') + plt.imshow(x.transpose(1, 2, 0)) + plt.show() + + +Out: + .. code:: none + :class: code-out + + First true equation with length 5 in the training dataset: + + .. image:: ../img/hed_dataset1.png + :width: 300px + + +Out: + .. code:: none + :class: code-out + + First true equation with length 8 in the training dataset: + + .. image:: ../img/hed_dataset2.png + :width: 480px + + +Out: + .. code:: none + :class: code-out + + First false equation with length 5 in the training dataset: + + .. image:: ../img/hed_dataset3.png + :width: 300px + + +Out: + .. code:: none + :class: code-out + + First false equation with length 8 in the training dataset: + + .. image:: ../img/hed_dataset4.png + :width: 480px + + +Building the Learning Part +-------------------------- + +To build the learning part, we need to first build a machine learning +base model. We use SymbolNet, and encapsulate it within a ``BasicNN`` +object to create the base model. ``BasicNN`` is a class that +encapsulates a PyTorch model, transforming it into a base model with an +sklearn-style interface. + +.. code:: ipython3 + + # class of symbol may be one of ['0', '1', '+', '='], total of 4 classes + cls = SymbolNet(num_classes=4) + loss_fn = nn.CrossEntropyLoss() + optimizer = torch.optim.RMSprop(cls.parameters(), lr=0.001, weight_decay=1e-4) + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + + base_model = BasicNN( + cls, + loss_fn, + optimizer, + device, + batch_size=32, + num_epochs=1, + stop_loss=None, + ) + +However, the base model built above deals with instance-level data +(i.e., individual images), and can not directly deal with example-level +data (i.e., a list of images comprising the equation). Therefore, we +wrap the base model into ``ABLModel``, which enables the learning part +to train, test, and predict on example-level data. + +.. code:: ipython3 + + model = ABLModel(base_model) + +Building the Reasoning Part +--------------------------- + +In the reasoning part, we first build a knowledge base. As mentioned +before, the knowledge base in this task involves the structure of the +equations and a recursive definition of bit-wise operations. The +knowledge base is already defined in ``HedKB``, which is derived from +``PrologKB``, and is built upon Prolog file ``reasoning/BK.pl`` and +``reasoning/learn_add.pl``. + +Specifically, the knowledge about the structure of equations (in +``reasoning/BK.pl``) is a set of DCG (definite clause grammar) rules +recursively define that a digit is a sequence of ‘0’ and ‘1’, and +equations share the structure of X+Y=Z, though the length of X, Y and Z +can be varied. The knowledge about bit-wise operations (in +``reasoning/learn_add.pl``) is a recursive logic program, which +reversely calculates X+Y, i.e., it operates on X and Y digit-by-digit +and from the last digit to the first. + +Note: Please notice that, the specific rules for calculating the +operations are undefined in the knowledge base, i.e., results of ‘0+0’, +‘0+1’ and ‘1+1’ could be ‘0’, ‘1’, ‘00’, ‘01’ or even ‘10’. The missing +calculation rules are required to be learned from the data. Therefore, +``HedKB`` incorporates methods for abducing rules from data. Users +interested can refer to the specific implementation of ``HedKB`` in +``reasoning/reasoning.py`` + +.. code:: ipython3 + + kb = HedKB() + +Then, we create a reasoner. Due to the indeterminism of abductive +reasoning, there could be multiple candidates compatible to the +knowledge base. When this happens, reasoner can minimize inconsistencies +between the knowledge base and pseudo-labels predicted by the learning +part, and then return only one candidate that has the highest +consistency. + +In this task, we create the reasoner by instantiating the class +``HedReasoner``, which is a reasoner derived from ``Reasoner`` and +tailored specifically for this task. ``HedReasoner`` leverages `ZOOpt +library `__ for acceleration, and has +designed a specific strategy to better harness ZOOpt’s capabilities. +Additionally, methods for abducing rules from data have been +incorporated. Users interested can refer to the specific implementation +of ``HedReasoner`` in ``reasoning/reasoning.py``. + +.. code:: ipython3 + + reasoner = HedReasoner(kb, dist_func="hamming", use_zoopt=True, max_revision=10) + +Building Evaluation Metrics +--------------------------- + +Next, we set up evaluation metrics. These metrics will be used to +evaluate the model performance during training and testing. +Specifically, we use ``SymbolMetric`` and ``ReasoningMetric``, which are +used to evaluate the accuracy of the machine learning model’s +predictions and the accuracy of the final reasoning results, +respectively. + +.. code:: ipython3 + + # Set up metrics + metric_list = [SymbolMetric(prefix="hed"), ReasoningMetric(kb=kb, prefix="hed")] + +Bridge Learning and Reasoning +----------------------------- + +Now, the last step is to bridge the learning and reasoning part. We +proceed this step by creating an instance of ``HedBridge``, which is +derived from ``SimpleBridge`` and tailored specific for this task. + +.. code:: ipython3 + + bridge = HedBridge(model, reasoner, metric_list) + +Perform training and testing. + +**[TODO]** give a detailed introduction about training in HedBridge. + +.. code:: ipython3 + + # Build logger + print_log("Abductive Learning on the HED example.", logger="current") + + # Retrieve the directory of the Log file and define the directory for saving the model weights. + log_dir = ABLLogger.get_current_instance().log_dir + weights_dir = osp.join(log_dir, "weights") + + bridge.pretrain("./weights") + bridge.train(train_data, val_data) + bridge.test(test_data) diff --git a/docs/Examples/HWF.rst b/docs/Examples/HWF.rst index 8bd403c..88f1238 100644 --- a/docs/Examples/HWF.rst +++ b/docs/Examples/HWF.rst @@ -2,7 +2,7 @@ Handwritten Formula (HWF) ========================= Below shows an implementation of `Handwritten -Formula `__. In this task. In this +Formula `__. In this task, handwritten images of decimal formulas and their computed results are given, alongwith a domain knowledge base containing information on how to compute the decimal formula. The task is to recognize the symbols diff --git a/docs/Examples/MNISTAdd.rst b/docs/Examples/MNISTAdd.rst index 7b83de7..12b6ee7 100644 --- a/docs/Examples/MNISTAdd.rst +++ b/docs/Examples/MNISTAdd.rst @@ -140,7 +140,7 @@ model with an sklearn-style interface. cls = LeNet5(num_classes=10) loss_fn = nn.CrossEntropyLoss() - optimizer = torch.optim.Adam(cls.parameters(), lr=0.001) + optimizer = torch.optim.RMSprop(cls.parameters(), lr=0.001, alpha=0.9) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") base_model = BasicNN( diff --git a/docs/Examples/ZOO.rst b/docs/Examples/ZOO.rst new file mode 100644 index 0000000..0cf1976 --- /dev/null +++ b/docs/Examples/ZOO.rst @@ -0,0 +1,2 @@ +ZOO +=== \ No newline at end of file diff --git a/docs/img/hed_dataset1.png b/docs/img/hed_dataset1.png new file mode 100644 index 0000000..f0d6be3 Binary files /dev/null and b/docs/img/hed_dataset1.png differ diff --git a/docs/img/hed_dataset2.png b/docs/img/hed_dataset2.png new file mode 100644 index 0000000..f8e203a Binary files /dev/null and b/docs/img/hed_dataset2.png differ diff --git a/docs/img/hed_dataset3.png b/docs/img/hed_dataset3.png new file mode 100644 index 0000000..5ed16c2 Binary files /dev/null and b/docs/img/hed_dataset3.png differ diff --git a/docs/img/hed_dataset4.png b/docs/img/hed_dataset4.png new file mode 100644 index 0000000..a4f74b4 Binary files /dev/null and b/docs/img/hed_dataset4.png differ diff --git a/docs/index.rst b/docs/index.rst index 9207975..4c43008 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,6 +26,7 @@ Examples/MNISTAdd Examples/HWF Examples/HED + Examples/ZOO .. toctree:: :maxdepth: 1 diff --git a/examples/hed/bridge.py b/examples/hed/bridge.py index 3d6d9c9..255f267 100644 --- a/examples/hed/bridge.py +++ b/examples/hed/bridge.py @@ -10,12 +10,12 @@ from abl.learning import ABLModel, BasicNN from abl.reasoning import Reasoner from abl.structures import ListData from abl.utils import print_log -from examples.hed.datasets.get_dataset import get_pretrain_data +from examples.hed.datasets import get_pretrain_data from examples.hed.utils import InfiniteSampler, gen_mappings from examples.models.nn import SymbolNetAutoencoder -class HEDBridge(SimpleBridge): +class HedBridge(SimpleBridge): def __init__( self, model: ABLModel, diff --git a/examples/hed/datasets/README.md b/examples/hed/datasets/README.md deleted file mode 100644 index f48c97d..0000000 --- a/examples/hed/datasets/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Download the Handwritten Equation Decipherment dataset from [NJU Box](https://box.nju.edu.cn/f/391c2d48c32b436cb833/) to this folder and unzip it: -``` -unzip HED.zip -``` diff --git a/examples/hed/datasets/__init__.py b/examples/hed/datasets/__init__.py index 3423195..ad88c85 100644 --- a/examples/hed/datasets/__init__.py +++ b/examples/hed/datasets/__init__.py @@ -1,4 +1,4 @@ -from .get_dataset import get_dataset, split_equation +from .get_dataset import get_dataset, get_pretrain_data, split_equation -__all__ = ["get_dataset", "split_equation"] \ No newline at end of file +__all__ = ["get_dataset", "get_pretrain_data", "split_equation"] \ No newline at end of file diff --git a/examples/hed/datasets/equation_generator.py b/examples/hed/datasets/equation_generator.py new file mode 100644 index 0000000..0fb2a2f --- /dev/null +++ b/examples/hed/datasets/equation_generator.py @@ -0,0 +1,173 @@ +import os +import itertools +import random +import numpy as np +from PIL import Image +import pickle + +def get_sign_path_list(data_dir, sign_names): + sign_num = len(sign_names) + index_dict = dict(zip(sign_names, list(range(sign_num)))) + ret = [[] for _ in range(sign_num)] + for path in os.listdir(data_dir): + if (path in sign_names): + index = index_dict[path] + sign_path = os.path.join(data_dir, path) + for p in os.listdir(sign_path): + ret[index].append(os.path.join(sign_path, p)) + return ret + +def split_pool_by_rate(pools, rate, seed = None): + if seed is not None: + random.seed(seed) + ret1 = [] + ret2 = [] + for pool in pools: + random.shuffle(pool) + num = int(len(pool) * rate) + ret1.append(pool[:num]) + ret2.append(pool[num:]) + return ret1, ret2 + +def int_to_system_form(num, system_num): + if num == 0: + return "0" + ret = "" + while (num > 0): + ret += str(num % system_num) + num //= system_num + return ret[::-1] + +def generator_equations(left_opt_len, right_opt_len, res_opt_len, system_num, label, generate_type): + expr_len = left_opt_len + right_opt_len + num_list = "".join([str(i) for i in range(system_num)]) + ret = [] + if generate_type == "all": + candidates = itertools.product(num_list, repeat = expr_len) + else: + candidates = [''.join(random.sample(['0', '1'] * expr_len, expr_len))] + random.shuffle(candidates) + for nums in candidates: + left_num = "".join(nums[:left_opt_len]) + right_num = "".join(nums[left_opt_len:]) + left_value = int(left_num, system_num) + right_value = int(right_num, system_num) + result_value = left_value + right_value + if (label == 'negative'): + result_value += random.randint(-result_value, result_value) + if (left_value + right_value == result_value): + continue + result_num = int_to_system_form(result_value, system_num) + #leading zeros + if (res_opt_len != len(result_num)): + continue + if ((left_opt_len > 1 and left_num[0] == '0') or (right_opt_len > 1 and right_num[0] == '0')): + continue + + #add leading zeros + if (res_opt_len < len(result_num)): + continue + while (len(result_num) < res_opt_len): + result_num = '0' + result_num + #continue + ret.append(left_num + '+' + right_num + '=' + result_num) # current only consider '+' and '=' + #print(ret[-1]) + return ret + +def generator_equation_by_len(equation_len, system_num = 2, label = 0, require_num = 1): + generate_type = "one" + ret = [] + equation_sign_num = 2 # '+' and '=' + while len(ret) < require_num: + left_opt_len = random.randint(1, equation_len - 1 - equation_sign_num) + right_opt_len = random.randint(1, equation_len - left_opt_len - equation_sign_num) + res_opt_len = equation_len - left_opt_len - right_opt_len - equation_sign_num + ret.extend(generator_equations(left_opt_len, right_opt_len, res_opt_len, system_num, label, generate_type)) + return ret + +def generator_equations_by_len(equation_len, system_num = 2, label = 0, repeat_times = 1, keep = 1, generate_type = "all"): + ret = [] + equation_sign_num = 2 # '+' and '=' + for left_opt_len in range(1, equation_len - (2 + equation_sign_num) + 1): + for right_opt_len in range(1, equation_len - left_opt_len - (1 + equation_sign_num) + 1): + res_opt_len = equation_len - left_opt_len - right_opt_len - equation_sign_num + for i in range(repeat_times): #generate more equations + if random.random() > keep ** (equation_len): + continue + ret.extend(generator_equations(left_opt_len, right_opt_len, res_opt_len, system_num, label, generate_type)) + return ret + +def generator_equations_by_max_len(max_equation_len, system_num = 2, label = 0, repeat_times = 1, keep = 1, generate_type = "all", num_per_len = None): + ret = [] + equation_sign_num = 2 # '+' and '=' + for equation_len in range(3 + equation_sign_num, max_equation_len + 1): + if (num_per_len is None): + ret.extend(generator_equations_by_len(equation_len, system_num, label, repeat_times, keep, generate_type)) + else: + ret.extend(generator_equation_by_len(equation_len, system_num, label, require_num = num_per_len)) + return ret + +def generator_equation_images(image_pools, equations, signs, shape, seed, is_color): + if (seed is not None): + random.seed(seed) + ret = [] + sign_num = len(signs) + sign_index_dict = dict(zip(signs, list(range(sign_num)))) + for equation in equations: + data = [] + for sign in equation: + index = sign_index_dict[sign] + pick = random.randint(0, len(image_pools[index]) - 1) + if is_color: + image = Image.open(image_pools[index][pick]).convert('RGB').resize(shape) + else: + image = Image.open(image_pools[index][pick]).convert('I').resize(shape) + image_array = np.array(image) + image_array = (image_array-127)*(1./128) + data.append(image_array) + ret.append(np.array(data)) + return ret + +def get_equation_std_data(data_dir, sign_dir_lists, sign_output_lists, shape = (28, 28), train_max_equation_len = 10, test_max_equation_len = 10, system_num = 2, tmp_file_prev = +None, seed = None, train_num_per_len = 10, test_num_per_len = 10, is_color = False): + tmp_file = "" + if (tmp_file_prev is not None): + tmp_file = "%s_train_len_%d_test_len_%d_sys_%d_.pk" % (tmp_file_prev, train_max_equation_len, test_max_equation_len, system_num) + if (os.path.exists(tmp_file)): + return pickle.load(open(tmp_file, "rb")) + + image_pools = get_sign_path_list(data_dir, sign_dir_lists) + train_pool, test_pool = split_pool_by_rate(image_pools, 0.8, seed) + + ret = {} + for label in ["positive", "negative"]: + print("Generating equations.") + train_equations = generator_equations_by_max_len(train_max_equation_len, system_num, label, num_per_len = train_num_per_len) + test_equations = generator_equations_by_max_len(test_max_equation_len, system_num, label, num_per_len = test_num_per_len) + print(train_equations) + print(test_equations) + print("Generated equations.") + print("Generating equation image data.") + ret["train:%s" % (label)] = generator_equation_images(train_pool, train_equations, sign_output_lists, shape, seed, is_color) + ret["test:%s" % (label)] = generator_equation_images(test_pool, test_equations, sign_output_lists, shape, seed, is_color) + print("Generated equation image data.") + + if (tmp_file_prev is not None): + pickle.dump(ret, open(tmp_file, "wb")) + return ret + +if __name__ == "__main__": + data_dirs = ["./dataset/hed/mnist_images", "./dataset/hed/random_images"] #, "../dataset/cifar10_images"] + tmp_file_prevs = ["mnist_equation_data", "random_equation_data"] #, "cifar10_equation_data"] + for data_dir, tmp_file_prev in zip(data_dirs, tmp_file_prevs): + data = get_equation_std_data(data_dir = data_dir,\ + sign_dir_lists = ['0', '1', '10', '11'],\ + sign_output_lists = ['0', '1', '+', '='],\ + shape = (28, 28),\ + train_max_equation_len = 26, \ + test_max_equation_len = 26, \ + system_num = 2, \ + tmp_file_prev = tmp_file_prev, \ + train_num_per_len = 300, \ + test_num_per_len = 300, \ + is_color = False) diff --git a/examples/hed/datasets/get_dataset.py b/examples/hed/datasets/get_dataset.py index 39e1934..fb80f65 100644 --- a/examples/hed/datasets/get_dataset.py +++ b/examples/hed/datasets/get_dataset.py @@ -2,6 +2,8 @@ import os import os.path as osp import pickle import random +import gdown +import zipfile from collections import defaultdict import cv2 @@ -10,29 +12,16 @@ from torchvision.transforms import transforms CURRENT_DIR = os.path.abspath(os.path.dirname(__file__)) - -def get_data(img_dataset, train): - X, Y = [], [] - if train: - positive = img_dataset["train:positive"] - negative = img_dataset["train:negative"] - else: - positive = img_dataset["test:positive"] - negative = img_dataset["test:negative"] - - for equation in positive: - equation = equation.astype(np.float32) - img_list = np.vsplit(equation, equation.shape[0]) - X.append(img_list) - Y.append(1) - - for equation in negative: - equation = equation.astype(np.float32) - img_list = np.vsplit(equation, equation.shape[0]) - X.append(img_list) - Y.append(0) - - return X, None, Y +def download_and_unzip(url, zip_file_name): + try: + gdown.download(url, zip_file_name) + with zipfile.ZipFile(zip_file_name, 'r') as zip_ref: + zip_ref.extractall(CURRENT_DIR) + os.remove(zip_file_name) + except Exception as e: + if os.path.exists(zip_file_name): + os.remove(zip_file_name) + raise Exception(f"An error occurred during download or unzip: {e}. Instead, you can download the dataset from {url} and unzip it in 'examples/hed/datasets' folder") def get_pretrain_data(labels, image_size=(28, 28, 1)): @@ -82,6 +71,19 @@ def split_equation(equations_by_len, prop_train, prop_val): def get_dataset(dataset="mnist", train=True): + data_dir = CURRENT_DIR + '/mnist_images' + + if not os.path.exists(data_dir): + print("Dataset not exist, downloading it...") + url = 'https://drive.google.com/u/0/uc?id=1XoJDjO3cNUdytqVgXUKOBe9dOcUBobom&export=download' + download_and_unzip(url, os.path.join(CURRENT_DIR, "HED.zip")) + print("Download and extraction complete.") + + if train: + file = os.path.join(data_dir, "expr_train.json") + else: + file = os.path.join(data_dir, "expr_test.json") + if dataset == "mnist": file = osp.join(CURRENT_DIR, "mnist_equation_data_train_len_26_test_len_26_sys_2_.pk") elif dataset == "random": @@ -91,11 +93,27 @@ def get_dataset(dataset="mnist", train=True): with open(file, "rb") as f: img_dataset = pickle.load(f) - X, _, Y = get_data(img_dataset, train) - equations_by_len = divide_equations_by_len(X, Y) + + X, Y = [], [] + if train: + positive = img_dataset["train:positive"] + negative = img_dataset["train:negative"] + else: + positive = img_dataset["test:positive"] + negative = img_dataset["test:negative"] - return equations_by_len + for equation in positive: + equation = equation.astype(np.float32) + img_list = np.vsplit(equation, equation.shape[0]) + X.append(img_list) + Y.append(1) + for equation in negative: + equation = equation.astype(np.float32) + img_list = np.vsplit(equation, equation.shape[0]) + X.append(img_list) + Y.append(0) + + equations_by_len = divide_equations_by_len(X, Y) + return equations_by_len -if __name__ == "__main__": - get_hed() diff --git a/examples/hed/hed.ipynb b/examples/hed/hed.ipynb index 4ade93d..b593a89 100644 --- a/examples/hed/hed.ipynb +++ b/examples/hed/hed.ipynb @@ -6,19 +6,9 @@ "source": [ "# Handwritten Equation Decipherment (HED)\n", "\n", - "This notebook shows an implementation of [Handwritten Equation Decipherment](https://proceedings.neurips.cc/paper_files/paper/2019/file/9c19a2aa1d84e04b0bd4bc888792bd1e-Paper.pdf). As shown below, the handwritten equations consist of sequential pictures of characters. The equations are generated with unknown operation rules from images of symbols ('0', '1', '+' and '='), and each equation is associated with a label indicating whether the equation is correct (i.e., positive) or not (i.e., negative). An agent is required to learn from a training set of such equations and then to predict labels of unseen equations. Note that the operation rules governing the label assignment of labels, \"xnor\" in this example, are unknown, and the sizes of equations can be different." - ] - }, - { - "attachments": { - "image.png": { - "image/png": "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" - } - }, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "![image.png](attachment:image.png)" + "This notebook shows an implementation of [Handwritten Equation Decipherment](https://proceedings.neurips.cc/paper_files/paper/2019/file/9c19a2aa1d84e04b0bd4bc888792bd1e-Paper.pdf). In this task, the handwritten equations are given, which consist of sequential pictures of characters. The equations are generated with unknown operation rules from images of symbols ('0', '1', '+' and '='), and each equation is associated with a label indicating whether the equation is correct (i.e., positive) or not (i.e., negative). Also, we are given a knowledge base which involves the structure of the equations and a recursive definition of bit-wise operations. The task is to learn from a training set of above mentioned equations and then to predict labels of unseen equations. \n", + "\n", + "Intuitively, we first use a machine learning model (learning part) to obtain the pseudo-labels ('0', '1', '+' and '=') for the observed pictures. We then use the knowledge base (reasoning part) to perform abductive reasoning so as to yield ground hypotheses as possible explanations to the observed facts, suggesting some pseudo-labels to be revised. This process enables us to further update the machine learning model." ] }, { @@ -31,13 +21,14 @@ "import os.path as osp\n", "import torch\n", "import torch.nn as nn\n", + "import matplotlib.pyplot as plt\n", "from examples.hed.datasets import get_dataset, split_equation\n", "from examples.models.nn import SymbolNet\n", "from abl.learning import ABLModel, BasicNN\n", "from examples.hed.reasoning import HedKB, HedReasoner\n", "from abl.evaluation import ReasoningMetric, SymbolMetric\n", "from abl.utils import ABLLogger, print_log\n", - "from examples.hed.bridge import HEDBridge" + "from examples.hed.bridge import HedBridge" ] }, { @@ -47,6 +38,13 @@ "## Working with Data" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we get the datasets of handwritten equations:" + ] + }, { "cell_type": "code", "execution_count": 2, @@ -59,34 +57,199 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "## Building the Learning Part" + "The dataset are shown below:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Equations in the dataset is organized by equation length, from 5 to 26\n", + "\n", + "For each euqation length, there are 225 true equation and 225 false equation in the training set\n", + "For each euqation length, there are 75 true equation and 75 false equation in the validation set\n", + "For each euqation length, there are 300 true equation and 300 false equation in the test set\n" + ] + } + ], "source": [ - "# Build necessary components for BasicNN\n", - "cls = SymbolNet(num_classes=4)\n", - "loss_fn = nn.CrossEntropyLoss()\n", - "optimizer = torch.optim.RMSprop(cls.parameters(), lr=0.001, weight_decay=1e-4)\n", - "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")" + "true_train_equation = train_data[1]\n", + "false_train_equation = train_data[0]\n", + "print(f\"Equations in the dataset is organized by equation length, \" +\n", + " f\"from {min(train_data[0].keys())} to {max(train_data[0].keys())}\")\n", + "print()\n", + "\n", + "true_train_equation_with_length_5 = true_train_equation[5]\n", + "false_train_equation_with_length_5 = false_train_equation[5]\n", + "print(f\"For each euqation length, there are {len(true_train_equation_with_length_5)} \" +\n", + " f\"true equation and {len(false_train_equation_with_length_5)} false equation \" +\n", + " f\"in the training set\")\n", + "\n", + "true_val_equation = val_data[1]\n", + "false_val_equation = val_data[0]\n", + "true_val_equation_with_length_5 = true_val_equation[5]\n", + "false_val_equation_with_length_5 = false_val_equation[5]\n", + "print(f\"For each euqation length, there are {len(true_val_equation_with_length_5)} \" +\n", + " f\"true equation and {len(false_val_equation_with_length_5)} false equation \" +\n", + " f\"in the validation set\")\n", + "\n", + "true_test_equation = test_data[1]\n", + "false_test_equation = test_data[0]\n", + "true_test_equation_with_length_5 = true_test_equation[5]\n", + "false_test_equation_with_length_5 = false_test_equation[5]\n", + "print(f\"For each euqation length, there are {len(true_test_equation_with_length_5)} \" +\n", + " f\"true equation and {len(false_test_equation_with_length_5)} false equation \" +\n", + " f\"in the test set\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As illustrations, we show four equations in the training dataset:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First true equation with length 5 in the training dataset:\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First true equation with length 8 in the training dataset:\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First false equation with length 5 in the training dataset:\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First false equation with length 8 in the training dataset:\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgQAAABICAYAAACJB+2oAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAliUlEQVR4nO3deZBcx33Y8W/3O+bNzM7sMXsDi8XiPikC4AWaokSRkkKZsixLSmSV40N2XBXJ5UoUO5VKqSpRVSopx0lVXHFJTipxbFmSJVmyTN0UJUoiJZMiSJAgQQJLXAtgd7Hn7OxcOzPvve78MbMACOJa7DGzi/6wUAR237zt2Zl579fdv+6f0FprDMMwDMO4rcl6N8AwDMMwjPozAYFhGIZhGCYgMAzDMAzDBASGYRiGYWACAsMwDMMwMAGBYRiGYRiYgMAwDMMwDExAYBiGYRgGYN/sge+WH1nOdtTFk+rvbuq42/m5AzwztHlJfqYjQrqtMl/IHOALX3mY5tOK1h+eYuQ3tvIbv/sE/e4UG50plJaEiCX5mdfy9o2nbuq42/m1v52fO8DhsxuWsSX1sb//3E0ddzu/9rfzczcjBIZhGIZhmIDAMAzDMIwFTBkYhmEYy08KjYVGorGEKTVjrBwTEBiGYTQQB4UrFM1S4IjqIK4yNeiMFWACAsMwjEWKiBBXqEWdY37+djyMklUeR0obmA1iOCJcfANrPrNuyU5lrEEmIDAMw1ikhFQkpIW1iNUxEeEgERwut/BSsZ8vHr4XK22jXM1SLbr5zN6lOY+xNpmAoAHY3V0EGzqZ2dlEdpPAmwZ3VtPxzAWC00P1bp5hrAhh29U//esJ2uKkd8eoNAv8OMgAkkOK2LiP8/SraL+y7O1plv4Nj7FqN+q/zb6Nl2Y3UAxcAi2RtzD378oAKTRHx3qYy3i0HHaJphWq9kOEBjSLCw4+uYjHGmueCQgaQLi+g7GDCdoeG+H53Y/zp+nNfPfCHgoTnURMQGDcJoTrImJRsntSZDda7PjgIB/oeJn3xs4xHko+9vLHmX69hc3PR1YkIGiRN16E5QiJRPLlMwfIv5zCKQikD/oWbtraAgTExzWpWUXyxfOoyWl0GILSS/Ocv7D4UxhrV8MFBHLPDnLbm0nvtJhbF4AA4QvW/RjiZ/OIY2dQxWK9m7nkhAatBaFW7PJGmGmP83Ssm0i9G2bUnZVMMvXB3ZTaBaWUxpsS9P39MHo2RzgzU+/mLZqIRJCRCJlHdzGzQ6J359jSOcWHO19ghzuOJyw6rJDf2vILvuwcwL9rK+7ILOEbN7e51K2655lP3LjtQoPQRF5oovNUiD2nkL6q9uQXqhZEWMUAWQrQxRIiFqV4cDOlVotCj0BLWGSqglFnVmsrdLQx8WAn03eFdP7cou2VDOLsKGFmtq5ta7iAoLAlydhBwYce/kf+pOtlACbCAg8U/oh2N0HLkAdrMCAAUFqg0Gx1prESgzzl3V/vJhn1Ji1EoomphyrsGRjhn3Uf4m8v3EPl2U4crWENBAQyEkEkE0zcJXjPOw/zu+1Pc4drAaCo/r9ZWHyydRApFJ/f/igtVgvOCQHLmH3f8U3vhsfMjwQ0n8xhXUijyxVQIdR69bfEsUEICAJEc5KJfTal9T5v3ztI0i6TDSIovca2kBFXDKms4VUVItnE3IYWso8UOPPg5xlwfg+nmKQ5nQMTEFRZLc3Q3sbUbpv99w3yUOIYoVYMBUWO++00nYXkiRx6rlTvphrGihC2jf/g25gecHnf7sM82nqEHc4UhxIDvNTeh1WIVy+kq/ziWfyl7YwdtNlzz2k+mnqOPtuHWiBwpT4nTWanRiiX9qck6KXLwL9S4nTh5g6UAlkoV2/kzU2EiQhD749TaQ+qEcNCX575e6MGbM3ebafZGJ/mHclBPFmhpNwFnrBxiQO7yexMMHUnOP0FyhMxnBnJ5r+ZJBw8We/mLbtQK7xkmUJPgmTsxgHocmuYgEDE4/hdzcytD/iNrmfZ7kyjiDIUNHOk2E9sQmGNTBFWln/ucMWJ6lCgJatjgaEWlLSD2ZPk9iZsm9nNLplt8MG2F3jQqwBRetxZno9LlOfUu4mLIwQIyeyAQ/d9o3y0+3kORkLg2hfGlJUn0lugNJ5ASIFexuFza3ZuAQdLVDxKkIpSSrnsfOA0v9b1IkV165N+vrZxRMDWyBgJWaJDli8mMa4JQlDsizN1J3zo4ef4T50v8heZTTw5tZO573cjB+vdwGVWGxFuipYptCTQDfB5bpiAYG5nD+f+icOBPSe5OzJBQtqUtc/vPf37tByK0HPkAuFMBh0E9W7qksv3x/AfyPJwV/UT8H+mH+CJszvpGr9xlrOxNlkdHdDRSvqBMu/YfoJN9iwQJa/K5EIPq6IRvrqlqepGYe3YQuZtKbIH5/jMwA/Y7U4A0es+ps/O8tFtL/JXs/eDWN5h85n/sZDfbogUAZ5dpNn2+Wj382x0phZVpCtEYqHwRICFpqgtpNaoZS78tRKs1lboTDF2n8XH3/MUu6IjvFiGPz/6DsTxJjZNjLN8Yz+NZWdqjJ9tSRAkvbrXEmiYgKDSYhMZyHFn8zDtVvWiUNQVoqcjdD2fRY9PocvlOrdyiQmBsB1KLZK71p1jpzcKwIl8J8WRJqyimR65LQkB7S2U1ifZvH6cx9qOEJeCoq5wNrA4N9eKVVIIP1y9AYEQ+Kk4mS2SLb2T3O9N4onq5UihCLVmPKzgI2iT4AkLR1g0S8EDTYN8tXUfMuqhtFq2TsJ/237zlREvZ6GJSR+HpRm+8Gu3CX8N5Q2IeIxyT5Kgt8zvtLzI6SDGKb+TYCRG6rRGFBYwOrPKrfcyNLcWCKNNJiC4nVmdHVR2rmdmt+ZT3U/SZflAlEwpip2TyEqwei/4xi0RjouMepz4nXa23n2Wf7vh+2xxshz3m3i2sJW/fPwREmeg88VT6Fx+VeYPyFgM2ZFiZH+Mu375KI+ljpCQLrJ2OTzt+5zw2/mjr/0W3qRgz4eO8XDbMT6cOENCutwVyXPfurMce3QPydMFOPTqsrTTE7cWaFi1T61f98t74yrc0cvZD8CjO1+lWbr8NL+TH4ztpOsX0Pr0GcKpdL2buGIOxIfIr4vwUnw/9Z40aIyAQFqEjiDuVWiyqr3ioq6QDkNEAMIPQa29tTbCdam02KhkwBZH44jqfGPMqRB6Gm2ZC8rtRnjVjHvZX+D31z/NLjdHRFic91O8lO2j7XVN4swcajq9OqfPpIVsbaG8qYPCOs17215jhzuOrF0KFYrD5T5+nt1K21FN00iZ04+k2JVoQWmNLSyapUNXJMtLbYLopLtsFzFrEeG4upWNCG4j5RaLvo3j7I6P4AiLs6U2hqda2DheIbgwVu/mLa9QYfmKMJAUdQVLKJqsMo0wAFT3gEBEIsiWZrIbJf9l23fY4U4CHt8r9PJ0djvJIYU4N4Zaa9MFAJYkdATCVkRFNXPYEpJ/v/E7vNC5ia8/9x6anq9zG40VpbdsIDvQxIH1b/BQdBJPuEyGZf7X0IOcP9XBrufGUGMTqFUYDIhIBKuzg6mH+uj7Fyf4QNsJ3hs7hycurSgo6YD/8K2P0PuMIvXCOXAdpv0ooZaEaBQatCI0ve9Vrdgh+cymJ9nuTAARnjm3mabnYrjjk2s+d0BNTeP4Ptb5zXwj388zmW0cz3TiFurf6a37p0oIgXAcVAS2OlO01Vo0HTYxWmzGLmn03Fx1t6616LL1t1YtSapDFtkUmUDVPVwzVloYr+aUtLlFYqI6jO5rmM7HcGYtdDa3KjfmEo6L1dZKcXcPs5sFH+x8iftjJ0hIF0dYqPn/tMablDQdT6NmMlDx39LbXt2plLc3YdtYySRBHDY5U7TUVlbNzXo0jYaI/G2SO6A0QkNYGxZolPGk+t9ypATbIoxoNjnOxXnE2SBGuhTDKilUae0n1833fABGwwSvz61D+ubCd7uptLoUuwUpN3/xaz6C4lSMxJSAsP69iIUSjou1vofMXd30/OEpPtB2ksfiwzjX2GvAm9aoU0PoIEC2tqxsY41lZXW0M7d3PcW+gF4rvJhIGjvlkvjuywSlNTgSfAXRFEf3tlNJhdwdHcKTPj3eLD+Jd1DvHSbqHxDME1wMBgAUglALrDV8T9TRCMUOiRev7q0w3/N5vbSe52YGcIqr7+JfLxYaeZU9XS8meGlrUUvAltv89r35XoviRp8N7jQAeVVmLIzjTtjExpYvo345CS9CeWOKXJ/FR9tOsD96hlhtikxdkYkfohEhF5+nDgKy03Feau2j1Po8zSvY7sgVZYctoQm1qF6bEA2RJzBfRMlC4whF2ABtui7bxo9b4IZ4wuJ8oDgfeDg5VuXI1y2xLLRrg62Ii4CSckj78YbYkrpxAoIrhFoSKrkqs6hvVqUrwey9JR7uO/Omr//d8H7GXuxm01jWDI7eJE8EV61ON7+RSzp0qCDx9dV7pfVmtacIe9qYfbDE5+79Em9zp1FEeN33+FFuN70/84kdOU9QWH0XTZlq5fwjEewds3w48RptVoSbnq2cK5F6zuHozADpfpuuFXz5Oi7rjVTLGgt8NAWlKGlJ+RojHCvNQRGXikRtyrGRF6PqiEMlIbGiZaLC5auze/jKif2khtfolPBVCNtGuRbCUTRLwZFCH88Mb6IjV/9gv2EDghczG5g4nWJLdu1OFyhXEk8UaXPfvEXq6HQzLadBZucaMsHmVrOvXRQOYAn1pkkzLarndEWIi6Ii4EYpt/OjAb62KKgIvyht4WfpzUih31R6NlDVUrQPtJ2kz0nTYWdxG/C3qlJJcgNxOtqm2OSkiUkLX4d8Z/YAT13YRku6jCoUWdat+ZaaEFgtLQSdzYQDc+zrvkBMWlTrAwpm1BzjoeS1SjfH5tYxXkkyUWoiNnnp9dFaY5VBVi71yOUKjfT84bnH3vTvQEvaIwV2x0fY6E6yo7bx0GL3B5gfubpyxOHy3v+VJBpLaHLKYTRs5nBxIy9n11ff/9f5fH553aKaumTmn6mvLYJAIm617sNqJATaFghL4wjJWClJfjpGd8kEBNf06rEN9H9P4ZybpP6/puURRiT9rTNsiEy/6evWqShdTw6jJqev8cj6cUSIJ27thuqgiEmLiPSpdbiqBMRkGU/4xGSAoyXhlcVOrnG+WS2ZDJP8vxP30fTVJFqClpclavoaLeEv/2kH7+4b5H0tR0jKxpunLPYnmTgg+dWu02xz4oRaMa3m+PKrdxF73SM1OkSQy9W7mQsiXBe1sZfMjiY+cccTPBAfxBP2xRv6cGDzZGEXnz9xL5WjzTQNQ2xSkTwycukzrxRWRWNVqjfd+cdaS7Tpz/Wc/uz2i3+XIVhlzfFNFk/du5Vf3vwaj3T/gpyqkFO3HmAqoKIlPtXA9WLQI3R1GgyNd5WxZEeAJwSjgcezhS18/tlfYv0PBKEjrp+MfPCWm7qkNLdxcqglCR2JZQXEhMuZTIrokIudK6zAu/r66h4QCNtGx6Mo+ypvDs2a3H/gRqyKQM9m0Q1St8ERIUUV4Y1KN0cL6/j+yZ0AWAtM8BBCE3ECMpNNdJzTeOkQEXGJjyr+5LlHsSIhbsRHL2AeVClBULFwT0aJjZdBgL5sw3erHKKlYKoYYa5WFEY2UJEIu7uLYGMXk3fYpO6Y4EB8CIBDZc2LpR1Ej3m0HQvQhZsstNNAZNRj4kCS2a2w3Rul2yoDERQaieCZ4jb+7+v3I44m6Dwa4k1XsDMl9NUCH13tSc8/diWWHcYvXJqCEkojyyEIjxknwTfG7uap/m0EShKGt9YWITRCwLs2vMHH2p5DCoWFxhMhIYJvZPdxptjOobE+ymUHy1JveqyUmrliBJVxaXndIjaaR9kSbcvq5+Amgmqj/pTm1splL4O6BwS4DkHSQ0Ua5DfSAGSFutfFvpwnQjLa4sVcPz8c3MHmzyqE1qjIwt8+Wri0BwF2vgiBQkcjtLw+S9P5SHUJplzYXl1aCLQlsAt5rOn8W74v5spgWwTFTvKBS6jlojacWWrBxi5G395E0/2T/MXOL9Jvh4Ta49vZO/nRhW30PFvCOTRIuAoTrkQ8zvT9Pm/bfJ797lQtd6BKoXlichdNTzaROpKD51+tff0qrni5Vqpn6Z2evKwNGoIQ54JN4jUXFYsQxr1FFSDTsjp0/K1fv5PH3vUynvSxUCSkD1ry9TN3MjvUwsbvBLjTc1cvZqUUspJHzhYhk8W2bbBtsORbSwobxg3UPyDoTDFxV5zYuuoN8EI4x/kghjtl4V3Iotf4MpT53upKzYsumUXM+S1VB11LQW59hIn7Q4SyEZXYWxb0igAQcGD7Se5MDOOKkFIDJBZaHR3429dx4ZdidL17mPf3vEKX5XOkkuRUpZMvPXM/rUclbecuEFb81ZVcKwR2fx/lgXY2bZjgwdQJvMsKEV0I5zhc7ub4SDf9pyrYU7mGnBY8/q960QKwNFZBErsgiI8pmo9mQF72Pl7oa1O7UVulAFH2aX+2lU/M/l71hAKUXf1r8qSkc0YRGcsj5iogJdoSb97BVIB2LHQsglQJits6yPfaZAfAb1MQCFM1tcGVKg5OsbYjb53VPSDwU3Fm9/oc7KoW9hkNohya24Q3LbAuTK3KntFCzc+Hzm9MtCpiA7nwRs4PYS7FyigtBFoICusEn37nN0nZeWLircGjqg0te8K/mIhY0nV/20NHK5P7Yuh7Zvnuzq/XltxG+Vquj+9N7Gb9DzWx775IEKyyYAAQlkWlL8XsQIQPdx3jvU2vERGXfuejQZTvzexFnvfwBs+isjfIjajT5+Hzv/JZHBHSIiscKm3gz06+i6lX2mk+fkXv+xZ64lqAKPvI6SydT+bofOoqPfogBK3RFR9hW4i4B1i8KZ4VAmUL8By0ZZHe4ZDd7fO79z7Db7a8wGToXvF+/zcLbquxvPyKjVfQ4Nc/LK7vlVEIwqhFLFVkXTQDVJePtVhFQhfwIgirkQZ4l8f8fGg4n0HeYE+4pC1cEXJP4gzRXT5PfGonWoNlLSzHQUpNPFJhYjJJ29PNNI0GxF4YIruvk9FHfexISMTzb/r+JwRsaL1At5PBE/41pgKqUfd8MLASyWg3Y64viXgkzQf7X8O+bPnas5lNHDvVy5a0j16FwQBUixe98RGX/p2jPNT0Oh2WwrosIPhS+j6e/+t9rB8so2Yy182VEVGPqTsF7rZZWmSFG5VHXkrzxY18LVlnz/CR/sO82raOQ1v6a3P4t1aeXNRWwhSPt9L78ySyorDK6k3JsABCa9AgyyGVFpfhRyzCloBEqoAAQiURte6/UgKlJAPtQzyUnOCO6DkKStZyEup/o7mWg00nyG+N8PO2u1fwlW0sfs4lPh5WpzfrrO5dpdCVdCQKdLpZoDqE7gm/mi3u2Gt+Hmw+q7iRM259beGIgB2RUfZ65/l46me3dB5PhAzYHp/PruNPh34NZ84iWiyS75F86p4fsjUyRr89c3HjlxtRWtSWfTX2pkNXU0rZ/MttT3PAG8KqbdITasWZbBvesIudy6BWYTCAEIh4jPvvGuQ/r/827dLFER5wqazxi1N99HztJDqXv+FmNMJxYGOBB/tOEVvhl/jSplaSNqvIY02v8oHEKzi9i3tdrNoKgd9u/gijoxuxCxqnqGuJgG893i5riu2S+w4e42DLKT7QdAwLKF2lGX5tQzcfSbk2lNBIXaorlxfucqaItxzmJ4l76tSiOqjlPc3P5ciihTdVQjfAjrz1CwiEQNgOc+0Wnx74AVudKSDCWJDglWIfkRmNHh1HzdX/l2Rc4msLtcB111IoXBRp5fFyuY3vTO4lPqoRCsoHd1Ds1cRqSwGL2q7tAnfzP2M1BQMyHkf0ryPfK9nsjtMmK4Ta5pmSzc8KO8g+3cXG72UQZy/Uu6kLJy1K7ztAZqvN+1serwUDl0Y/RoMyj+f3MDrcRjI3hrpefpAQWJ0dhOs72NEzwYPJQWKyei6JqMtIj49Eac2tjQtcIrWmJDS/2fssT//zNOXQvu5eBoGySDglHm19lRZZJKeqx6prvO8b+fOgcwWaT80xOekxo0rEpWCXm6uOCN8mSps6GH7I4UDfaQCcWYlzeoww99ak6JVW3xECKQii8I7oNLFa6d+ijjBZSWDPgVqFS61uBwu94ITawhKaknZ4udDPmZkU0Uy1uEdug0vQGhCvBQSrsbe/EMKLUO5JUGnRpGQRT1RHh46Xe/jxxDaazyjUy6/Xu5kLJwTSdchstsnu9NnkTlzMG5gf/Uorl3+c2YyddqrTBDdav9+coJzy2BafZpM7gYN1Mfk2RIKuDauvgEsjeUv03tSw3xvmXbHh6y6gtC4+X01aVUcrqoHJ6vyM6FIJeyqPXYgyHQo6LE2rjNIAeb4rptJiIwcKbG8aB8AqQTiVrk4R1lndpwyMtc9C0yIrjAQtfPXYfpzBGL3PnWf2rl7CX53h0Z6zbHXH13wwAKB7Ohn6ZYfuXeP02yGR2nTBXw0dpPLNDrqOZRoky2FhrM0b8de1IN+V5tPbfsJedwaIodCUtc9oGPLlmQc4/vfb6X0juGH1UuG6TD7QSWYb/EFykK22jyUcAkJKOiDjR3FzGmuucefHb6SkLaZvulhVdRoA3rqj4WqiCkXk+VHiw5381cz9PJx4jYeit9EosBBUmiSbO6cYiEze+PgVZgICY0VIwNc2YcalaQbU+CRC93Kga5g74ueJiYAiEDbCCoBlIhyXoMXD68/xttQoMeng65DxsJpoOfBGBTmdXZUBgUrEKKVcdnec4X3xk7RZkYsjA75WnA+SDOa6aD0REDufu25+hPQ8RHOS/HqB3lCkz5kmVtufoqh8TgQOZ/NtuHmFnPNX5e8Lqjf2RqmHsGJUiCoWcfOawWwXu6PDwG0SEAiBsCzCCGxsStNm5d9S3Kve1u7V12hMtkY51R6gFjBTiZJT3tINxTYoGYuhtw8wvSfKp3Z9nzu9c0gsHi9087kz7yD5gkfkpWOrdplt0BKh2C7p8WZplu6bKpeOhfC50Yd49fUN7Dp0FnW9TbeEoPSOPcxuctj5yAk+1HWYjXYFaoVhn5rr5o+f+HWaj1v0/Ow4ehUWezLAmwk5cno9m5sm+fXEeL2bsyKE6yKTSQq9gn/X9UMAxkOQDTTIZQKCBjE/N9rIqw2WxHwmtVXtGQVq4UmKq5FwHUpdMUopwe7ICB1WhRklOJQfYOR0O71jISqXu+FQeqOqJG1KKUGrXXxTIiFUE0UHpzqJTFqozCzqymxqaSEcG6u1BZ2Ik9nqkt2sONh2mv2R88QuO99Y0EzipEXzkE+YnlmVyzINEKGGskVZLWxn0tVMRj3obMNPKnqsKOeCOU75rcgKDVO0zAQEdVYtYnJpY6LbbADxtiESCcbucQh3FtjklJgOJY8XdvL4L/az88/SMD1DGDRQV2EhhGBqr03PO4e5P37iLd8+5Xcgf9pC9wkffZXnaHe2ozpbOffeNgp7Snx831P8SvJleq2QmHCwLlt6fKzQy/pvjaLGJlbnskwDAOUKZNwnbtd/7f1K0et7GHlvG4ktaQC+MnuAr5zeT2I4vOrnoh5MQNBgwgaJFI2lIyIRdCJGuSOkpzVHREjKWjJcacXOWjA+ueqX14ZRTU8sW1s+Wi1vrNDkVZnJIImb1VglhdXdBZasFjSLuQRNLvkOh7mUJL+rzL6B8zzYdJzdjotC4+uQ0aDMeBjlG7MHeOLETramz9xw/4KVshKFslZzEuG1hI4gGq/QbM3Vuykrx5YEUYg51Zv/WCVJdjpOa7FxrvkmIKiz6uY6sjpVoNXanzK4zQjbxurtptDfTP+OMd7ZeQIHi2kV5bXZHpycqNbrWKVTBfOUA+2RfK009vw6ecWgH2Gw2I1T0ISeJP1gH8UOSW5LSHx9jvdvfIE742c5EBkhJsATspZAWF1iWNYBX5y9i++M7CbxnxJsOzdFMJut63O93HyJ4uVwMa9GrL2goJKQ3Nt7ll3RkXo3pW4GZ7uInnZxM42zvN4EBA1kPhgotWvEvt3IXBFRqqBqpZB1+fYZXlsrtNIwV8IqK6RUeLXtbn1tky17WD7oUFWPW8VEALN+9C0b7CRkhe2xMb57l0D6NkGTgmSZ9T1p7mgb5cHEIJucNBvsN29cW9YBR33BiUoff3PsHvSZOG0jI6jp9IrlDTji2j23+SCgrC1Ktc20lrok8/x23J4IcYRaU4m3VkVzrtDKdHMT0DiVXVfSbNnDS2usYqVh1hqYgKAB9d4xxumgm8SZJLEpReJYGpGeRU2nG2auybhJKiQYG8ftaqNyWS8vE8aYzDbhFUH7C6sJ0YjsguBMNkWmIwpUUGgkki2OzUDyFB/76H+/eKwUAqeWLVPND7i0TZ1CUdIBORXy52Pv49BwPxs+a+GePEtwYfzGmxktoYS8/s+SQDrwOO+nKKgIJV1NkFvsLoq+tpFC0edMk5Ql2uwsjrhGaehVykuHnDzRw0vJfn47OVrv5tTFVKaJ/jcqyHSuYV7b+gYESuPmNH+b3cL+6BD73LWfbX45Jxfw+lAvXV4Omocufv3h7kF+tB+Ge1vJzLj055vx/ADSmbq11Vika/VqV/fAQJXWRKc0Z892MLKhFbxLy8gkEoQiJpw3f61mfh22QjEZlpkKHf5s/D28MtlD7rUU0TGBMzpWrYi4wvk1z5XWXfN7rghxRMAXxw9yeGQ95VkPUZLVyoyL7cir6jlSG2foS87wq50vkbLzlJS76I27Ni6yaUtGV/+oWq7J7SBIRJjb4LMpVuC1SoCaieBOzqIbJB8G6hkQaI0OfKJTIZ9940Ee60+xr/OlujWnHtzxHM2HOngu0Q99l77+6fajfLr9KEM7ihz32/n0Gx+nKx1HjNprojdprD3NpyooO8Ibd/fALa4rP+knOTy3kUP/sJeeZ+foeuU44cwM9cqu+ObUvmt+L2pVM+QP/XwH634cEB+cRI2OgWUhrFtfK6S1Bt8Hx2HqI3t4bSCFelCyIZ4m40cXvUT3o1sW9fAlF66x3IjrKbW7vH3vMTrcPE8WduGNWnDqPKqBpoLrPmUgK4piMUImiNW7KSsvPUvq9QTne5P86dZdPNA0yMFIyKsVn1N+B/9z6Nc4N5pi48kK1tgM4XXKxBoNSgis5iR+c5Su6DhdTnW+dDJIUp6Ikcyujd6RN5KlVTbzxPBOdkZHeXfsHK3Su+5j3vArPDc3wPem9nD0Qg/lqShOxmLdyz7uuTRqrr4Z6C99e1f1L7pamE6E1b8jqFZjldD1Rkj0fI6gPYHqbUE78i1ljG/W/L3RyVaQ5YDmM2ViEzZn0ps54W1emg1s7luCcxi3xCopTmQ6KCUcmuwyMgRd8Rtq75H6BgRaI31FmHfIVG6/atjh5DTOs1lSXXfyhe13o7YJDkZe4/nSAD9Jb2fqR71seMXHO3yaYGq63s01boGwLERLM5UWh77YDL32DJYQjPtJvFELL7M2ckL0mfN4k2nOntzKVxJ3s7d/hNYbdGZfLffypZF7GPnHdaz7aYXIcHU/BjWbI2iAkbD+/z1YneoJQ3TFv+pSR+G44DqU37mL2QGHMEK1UE9tSHwhtAUIiI67RLKK5p8PYY2N070UT2ben/zrpTybsQB2KeT8VBIpNP2xNCJovPyhuo8QuMNpun/cw7N6C093P83pcme9m7RytEJVfFqOTCPCNr7e8S6+lHoX7iw4eU3v8QLOhQwq3zjLUoyF0WGIzswSO9/E1356Hz/eupUn7vhr7oid4+/3Zcnkk/TGYtVVJKs4YVRVfGS+wIbvh4y9PMDHuj6FukFJWycP3pRm/fkykTOT6GwePTfXOD2mig+OjWhtQbUnyexoAkBcviJEgBaCiV8K6Ru4QIs3hyuDW1om6FohEs0r471MZaIUOzfhpQfQ1qXRg7XCna0QOx/n/I7WejdlxVhzAWI8xmgxxeOZJlrHG290sO4BgbowTtszAfl1/fzi4Bam/ThlZbEC+33Un9agQ8JjJ2g6Bk1XOWT13iIMALQmzOaxLkzR+XySqUo7ub2KdfYM7+w/yQ/f2FfduCgMYRUHBKgQVQpxv38IF1joZb4Rn7nWGiEtVHOc/IYYEwdDECCCy+7OtZGARw+8wic7fkyXVU2gvJWiNRHhIBH8Q0cLLxX7+SL3YqVtlKsXn6jYYKxChehEjKlivN5NWTFyzicyLVF5SThtE51uvHd9/QOCig/pGdY/meRbow8hQpChJnVkvG7JRIaxpFSIyszS9twYiTPNfPDkHyNCiOQ0A0N5VL7QOL1i46Jzf7AX5UKlVSE7S3zijmew0JQu239/fm+AfbEhANIKctzaunJZC4vW2ZpE0xxt9xTIhR6WUEu4+dEfLdF5FkdOzNB2LMKJg8284VewGmvkfFmIc2P0fV+CJdC2xB6ebrhAuO4BwXw5TI4cI3nk0pfN5XHtsa6z0ctap8tlgtNDiNPQ/uwV36tPk4wbkPdkiFghm5NZdjdf4JMtx4DqpkkXj6nVWUiHIUVtUdIWxUWO7zfLMh3WHPsjGRxR2/VxjdVtUPkCzkgae3odr5Z7kY2TaL9swpkZmJm5+O9GCwagEQIC47ZgCZAokGtv+NNYm/7j7m9jCYUnfGKyzHDoX+Wo6o3a19ai9wiY5yNRWlAJ9ZIH0aklPdutU4Ui+D5bvhDjc09/hN7XRhryBnm7MQGBsSIKSpIJY1CWyPnrqgkMjAb29ugFQq3xAV9DboVK9SotLm1TvLYGBi6p5ZzwynG8Vxqzt3w7MgGBsawSskJOufzXsffy/MgGNnwPomM5cB1Cx0QERuOaDC+tm1yq3r9hNDITEBjLyqp1cUYLzRRno3iTJeRMHiGEGSEwGtqVhZoMY60TWq+xbBXDMAzDMBbMhMCGYRiGYZiAwDAMwzAMExAYhmEYhoEJCAzDMAzDwAQEhmEYhmFgAgLDMAzDMDABgWEYhmEYmIDAMAzDMAxMQGAYhmEYBvD/AQE8MsqzGcUvAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "true_train_equation_with_length_5 = true_train_equation[5]\n", + "true_train_equation_with_length_8 = true_train_equation[8]\n", + "print(f\"First true equation with length 5 in the training dataset:\")\n", + "for i, x in enumerate(true_train_equation_with_length_5[0]):\n", + " plt.subplot(1, 5, i+1)\n", + " plt.axis('off') \n", + " plt.imshow(x.transpose(1, 2, 0))\n", + "plt.show()\n", + "print(f\"First true equation with length 8 in the training dataset:\")\n", + "for i, x in enumerate(true_train_equation_with_length_8[0]):\n", + " plt.subplot(1, 8, i+1)\n", + " plt.axis('off') \n", + " plt.imshow(x.transpose(1, 2, 0))\n", + "plt.show()\n", + "\n", + "false_train_equation_with_length_5 = false_train_equation[5]\n", + "false_train_equation_with_length_8 = false_train_equation[8]\n", + "print(f\"First false equation with length 5 in the training dataset:\")\n", + "for i, x in enumerate(false_train_equation_with_length_5[0]):\n", + " plt.subplot(1, 5, i+1)\n", + " plt.axis('off') \n", + " plt.imshow(x.transpose(1, 2, 0))\n", + "plt.show()\n", + "print(f\"First false equation with length 8 in the training dataset:\")\n", + "for i, x in enumerate(false_train_equation_with_length_8[0]):\n", + " plt.subplot(1, 8, i+1)\n", + " plt.axis('off') \n", + " plt.imshow(x.transpose(1, 2, 0))\n", + "plt.show()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Building the Learning Part" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To build the learning part, we need to first build a machine learning base model. We use SymbolNet, and encapsulate it within a `BasicNN` object to create the base model. `BasicNN` is a class that encapsulates a PyTorch model, transforming it into a base model with an sklearn-style interface. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, "outputs": [], "source": [ - "# Build BasicNN\n", - "# The function of BasicNN is to wrap NN models into the form of an sklearn estimator\n", + "# class of symbol may be one of ['0', '1', '+', '='], total of 4 classes\n", + "cls = SymbolNet(num_classes=4)\n", + "loss_fn = nn.CrossEntropyLoss()\n", + "optimizer = torch.optim.RMSprop(cls.parameters(), lr=0.001, weight_decay=1e-4)\n", + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "\n", "base_model = BasicNN(\n", " cls,\n", " loss_fn,\n", @@ -98,9 +261,16 @@ ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, the base model built above deals with instance-level data (i.e., individual images), and can not directly deal with example-level data (i.e., a list of images comprising the equation). Therefore, we wrap the base model into `ABLModel`, which enables the learning part to train, test, and predict on example-level data." + ] + }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -115,13 +285,41 @@ "## Building the Reasoning Part" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the reasoning part, we first build a knowledge base. As mentioned before, the knowledge base in this task involves the structure of the equations and a recursive definition of bit-wise operations. The knowledge base is already defined in `HedKB`, which is derived from `PrologKB`, and is built upon Prolog file `reasoning/BK.pl` and `reasoning/learn_add.pl`.\n", + "\n", + "Specifically, the knowledge about the structure of equations (in `reasoning/BK.pl`) is a set of DCG (definite clause grammar) rules recursively define that a digit is a sequence of '0' and '1', and equations share the structure of X+Y=Z, though the length of X, Y and Z can be varied. The knowledge about bit-wise operations (in `reasoning/learn_add.pl`) is a recursive logic program, which reversely calculates X+Y, i.e., it operates on X and Y digit-by-digit and from the last digit to the first.\n", + "\n", + "Note: Please notice that, the specific rules for calculating the operations are undefined in the knowledge base, i.e., results of '0+0', '0+1' and '1+1' could be '0', '1', '00', '01' or even '10'. The missing calculation rules are required to be learned from the data. Therefore, `HedKB` incorporates methods for abducing rules from data. Users interested can refer to the specific implementation of `HedKB` in `reasoning/reasoning.py`" + ] + }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "kb = HedKB()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then, we create a reasoner. Due to the indeterminism of abductive reasoning, there could be multiple candidates compatible to the knowledge base. When this happens, reasoner can minimize inconsistencies between the knowledge base and pseudo-labels predicted by the learning part, and then return only one candidate that has the highest consistency. \n", + "\n", + "In this task, we create the reasoner by instantiating the class `HedReasoner`, which is a reasoner derived from `Reasoner` and tailored specifically for this task. `HedReasoner` leverages [ZOOpt library](https://github.com/polixir/ZOOpt) for acceleration, and has designed a specific strategy to better harness ZOOpt’s capabilities. Additionally, methods for abducing rules from data have been incorporated. Users interested can refer to the specific implementation of `HedReasoner` in `reasoning/reasoning.py`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ - "kb = HedKB()\n", "reasoner = HedReasoner(kb, dist_func=\"hamming\", use_zoopt=True, max_revision=10)" ] }, @@ -133,9 +331,16 @@ "## Building Evaluation Metrics" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we set up evaluation metrics. These metrics will be used to evaluate the model performance during training and testing. Specifically, we use `SymbolMetric` and `ReasoningMetric`, which are used to evaluate the accuracy of the machine learning model’s predictions and the accuracy of the final reasoning results, respectively." + ] + }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -148,16 +353,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Bridging Learning and Logic Reasoning" + "## Bridge Learning and Reasoning\n", + "\n", + "Now, the last step is to bridge the learning and reasoning part. We proceed this step by creating an instance of `HedBridge`, which is derived from `SimpleBridge` and tailored specific for this task." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ - "bridge = HEDBridge(model, reasoner, metric_list)" + "bridge = HedBridge(model, reasoner, metric_list)" ] }, { @@ -165,703 +372,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Perform traing and testing." + "Perform training and testing.\n", + "\n", + "**[TODO]** give a detailed introduction about training in HedBridge." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "12/21 11:23:55 - abl - INFO - Abductive Learning on the HED example.\n", - "12/21 11:23:55 - abl - INFO - Loads checkpoint by local backend from path: ./weights/pretrain_weights.pth\n", - "12/21 11:23:55 - abl - INFO - ============== equation_len: 5-6 ================\n", - "12/21 11:23:55 - abl - INFO - Equation Len(train) [5] Segment Index [1]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [-1.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [-1.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [-1.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [-1.0, 8.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [-1.0, 7.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [-1.0, 8.0]\n", - "[zoopt] x: array([1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 1., 1., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [-4.0, 6.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0.])\n", - "[zoopt] value: [-4.0, 8.0]\n", - "12/21 11:24:12 - abl - INFO - model loss: 0.53343\n", - "12/21 11:24:12 - abl - INFO - Start machine learning model validation\n", - "12/21 11:24:12 - abl - INFO - mean loss: 0.055, accuray: 0.952\n", - "12/21 11:24:12 - abl - INFO - Revisible ratio is 0.400, Character accuracy is 0.952\n", - "12/21 11:24:12 - abl - INFO - Equation Len(train) [5] Segment Index [2]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-1.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,\n", - " 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-2.0, 8.0]\n", - "[zoopt] x: array([0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-9.0, 5.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-3.0, 8.0]\n", - "12/21 11:24:29 - abl - INFO - model loss: 0.33173\n", - "12/21 11:24:29 - abl - INFO - Start machine learning model validation\n", - "12/21 11:24:29 - abl - INFO - mean loss: 0.027, accuray: 1.000\n", - "12/21 11:24:29 - abl - INFO - Revisible ratio is 0.900, Character accuracy is 1.000\n", - "12/21 11:24:29 - abl - INFO - Equation Len(train) [5] Segment Index [3]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-1.0, 8.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-1.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-1.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-2.0, 7.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 1., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 1., 0., 0., 0., 0., 0., 0.,\n", - " 1., 0.])\n", - "[zoopt] value: [-1.0, 3.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-10.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 1., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-3.0, 5.0]\n", - "12/21 11:24:45 - abl - INFO - model loss: 0.06279\n", - "12/21 11:24:45 - abl - INFO - Start machine learning model validation\n", - "12/21 11:24:45 - abl - INFO - mean loss: 0.022, accuray: 0.981\n", - "12/21 11:24:45 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 0.981\n", - "12/21 11:24:45 - abl - INFO - Equation Len(train) [5] Segment Index [4]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [-1.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [-2.0, 7.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [-10.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "12/21 11:25:00 - abl - INFO - model loss: 0.00694\n", - "12/21 11:25:00 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:00 - abl - INFO - mean loss: 0.001, accuray: 1.000\n", - "12/21 11:25:00 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:00 - abl - INFO - Equation Len(train) [5] Segment Index [5]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,\n", - " 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,\n", - " 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-2.0, 4.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-1.0, 9.0]\n", - "[zoopt] x: array([0., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-1.0, 8.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "12/21 11:25:19 - abl - INFO - model loss: 0.00063\n", - "12/21 11:25:19 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:19 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:19 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:19 - abl - INFO - Equation Len(train) [5] Segment Index [6]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "12/21 11:25:36 - abl - INFO - model loss: 0.00105\n", - "12/21 11:25:36 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:36 - abl - INFO - mean loss: 0.001, accuray: 1.000\n", - "12/21 11:25:36 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:36 - abl - INFO - Equation Len(train) [5] Segment Index [7]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1.,\n", - " 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [-2.0, 5.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [-1.0, 8.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0.])\n", - "[zoopt] value: [0.0, 10.0]\n", - "12/21 11:25:51 - abl - INFO - model loss: 0.00027\n", - "12/21 11:25:51 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:51 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:51 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:51 - abl - INFO - Now checking if we can go to next course\n", - "12/21 11:25:51 - abl - INFO - Learned rules from data: ['my_op([1], [1], [1, 0])', 'my_op([0], [1], [1])', 'my_op([1], [0], [1])', 'my_op([0], [0], [0])']\n", - "12/21 11:25:51 - abl - INFO - True consistent ratio is 1.000, False inconsistent ratio is 1.000\n", - "12/21 11:25:51 - abl - INFO - Checkpoints will be saved to ./weights/eq_len_5.pth\n", - "12/21 11:25:51 - abl - INFO - ============== equation_len: 6-7 ================\n", - "12/21 11:25:51 - abl - INFO - Equation Len(train) [6] Segment Index [1]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:51 - abl - INFO - model loss: 0.00029\n", - "12/21 11:25:51 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:51 - abl - INFO - mean loss: 0.001, accuray: 1.000\n", - "12/21 11:25:51 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:51 - abl - INFO - Equation Len(train) [6] Segment Index [2]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:52 - abl - INFO - model loss: 0.00022\n", - "12/21 11:25:52 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:52 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:52 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:52 - abl - INFO - Equation Len(train) [6] Segment Index [3]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:52 - abl - INFO - model loss: 0.00026\n", - "12/21 11:25:52 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:52 - abl - INFO - mean loss: 0.001, accuray: 1.000\n", - "12/21 11:25:52 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:52 - abl - INFO - Equation Len(train) [6] Segment Index [4]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:53 - abl - INFO - model loss: 0.00016\n", - "12/21 11:25:53 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:53 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:53 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:53 - abl - INFO - Equation Len(train) [6] Segment Index [5]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:53 - abl - INFO - model loss: 0.00188\n", - "12/21 11:25:53 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:53 - abl - INFO - mean loss: 0.001, accuray: 1.000\n", - "12/21 11:25:53 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:53 - abl - INFO - Now checking if we can go to next course\n", - "12/21 11:25:53 - abl - INFO - Learned rules from data: ['my_op([1], [1], [1, 0])', 'my_op([1], [0], [1])', 'my_op([0], [1], [1])', 'my_op([0], [0], [0])']\n", - "12/21 11:25:53 - abl - INFO - True consistent ratio is 0.913, False inconsistent ratio is 1.000\n", - "12/21 11:25:53 - abl - INFO - Loads checkpoint by local backend from path: ./weights/eq_len_5.pth\n", - "12/21 11:25:53 - abl - INFO - Reload Model and retrain\n", - "12/21 11:25:53 - abl - INFO - Equation Len(train) [6] Segment Index [6]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:54 - abl - INFO - model loss: 0.00037\n", - "12/21 11:25:54 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:54 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:54 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:54 - abl - INFO - Equation Len(train) [6] Segment Index [7]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:54 - abl - INFO - model loss: 0.00026\n", - "12/21 11:25:54 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:54 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:54 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:54 - abl - INFO - Equation Len(train) [6] Segment Index [8]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:54 - abl - INFO - model loss: 0.00017\n", - "12/21 11:25:54 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:54 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:54 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:54 - abl - INFO - Equation Len(train) [6] Segment Index [9]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:55 - abl - INFO - model loss: 0.00019\n", - "12/21 11:25:55 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:55 - abl - INFO - mean loss: 0.127, accuray: 0.969\n", - "12/21 11:25:55 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 0.969\n", - "12/21 11:25:55 - abl - INFO - Equation Len(train) [6] Segment Index [10]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.])\n", - "[zoopt] value: [-8.0, 8.0]\n", - "12/21 11:25:55 - abl - INFO - model loss: 0.00018\n", - "12/21 11:25:55 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:55 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:55 - abl - INFO - Revisible ratio is 0.800, Character accuracy is 1.000\n", - "12/21 11:25:55 - abl - INFO - Equation Len(train) [6] Segment Index [11]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:56 - abl - INFO - model loss: 0.00123\n", - "12/21 11:25:56 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:56 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:56 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:56 - abl - INFO - Equation Len(train) [6] Segment Index [12]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:56 - abl - INFO - model loss: 0.00015\n", - "12/21 11:25:56 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:56 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:56 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:56 - abl - INFO - Equation Len(train) [6] Segment Index [13]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:56 - abl - INFO - model loss: 0.00013\n", - "12/21 11:25:56 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:56 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:56 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:56 - abl - INFO - Equation Len(train) [6] Segment Index [14]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:56 - abl - INFO - model loss: 0.00031\n", - "12/21 11:25:56 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:56 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:56 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:56 - abl - INFO - Equation Len(train) [6] Segment Index [15]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:57 - abl - INFO - model loss: 0.00012\n", - "12/21 11:25:57 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:57 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:57 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:57 - abl - INFO - Now checking if we can go to next course\n", - "12/21 11:25:57 - abl - INFO - Learned rules from data: ['my_op([0], [1], [1])', 'my_op([1], [1], [1, 0])', 'my_op([0], [0], [0])', 'my_op([1], [0], [1])']\n", - "12/21 11:25:57 - abl - INFO - True consistent ratio is 1.000, False inconsistent ratio is 1.000\n", - "12/21 11:25:57 - abl - INFO - Checkpoints will be saved to ./weights/eq_len_6.pth\n", - "12/21 11:25:57 - abl - INFO - ============== equation_len: 7-8 ================\n", - "12/21 11:25:57 - abl - INFO - Equation Len(train) [7] Segment Index [1]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:57 - abl - INFO - model loss: 0.00037\n", - "12/21 11:25:57 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:57 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:57 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:57 - abl - INFO - Equation Len(train) [7] Segment Index [2]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:58 - abl - INFO - model loss: 0.00004\n", - "12/21 11:25:58 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:58 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:58 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:58 - abl - INFO - Equation Len(train) [7] Segment Index [3]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:58 - abl - INFO - model loss: 0.00006\n", - "12/21 11:25:58 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:58 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:58 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:58 - abl - INFO - Equation Len(train) [7] Segment Index [4]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:58 - abl - INFO - model loss: 0.00004\n", - "12/21 11:25:58 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:58 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:58 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:58 - abl - INFO - Equation Len(train) [7] Segment Index [5]\n", - "[zoopt] x: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", - " 0., 0., 0., 0., 0., 0., 0.])\n", - "[zoopt] value: [-10.0, 10.0]\n", - "12/21 11:25:58 - abl - INFO - model loss: 0.00216\n", - "12/21 11:25:58 - abl - INFO - Start machine learning model validation\n", - "12/21 11:25:58 - abl - INFO - mean loss: 0.000, accuray: 1.000\n", - "12/21 11:25:58 - abl - INFO - Revisible ratio is 1.000, Character accuracy is 1.000\n", - "12/21 11:25:58 - abl - INFO - Now checking if we can go to next course\n", - "12/21 11:25:59 - abl - INFO - Learned rules from data: ['my_op([0], [1], [1])', 'my_op([1], [1], [1, 0])', 'my_op([0], [0], [0])', 'my_op([1], [0], [1])']\n", - "12/21 11:25:59 - abl - INFO - True consistent ratio is 1.000, False inconsistent ratio is 0.993\n", - "12/21 11:25:59 - abl - INFO - Checkpoints will be saved to ./weights/eq_len_7.pth\n" - ] - } - ], + "outputs": [], "source": [ "# Build logger\n", "print_log(\"Abductive Learning on the HED example.\", logger=\"current\")\n", @@ -871,15 +391,9 @@ "weights_dir = osp.join(log_dir, \"weights\")\n", "\n", "bridge.pretrain(\"./weights\")\n", - "bridge.train(train_data, val_data)" + "bridge.train(train_data, val_data)\n", + "bridge.test(test_data)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/examples/hed/reasoning/reasoning.py b/examples/hed/reasoning/reasoning.py index f85b967..3d6013f 100644 --- a/examples/hed/reasoning/reasoning.py +++ b/examples/hed/reasoning/reasoning.py @@ -1,7 +1,6 @@ import os import numpy as np import math -from zoopt import Dimension, Objective, Opt, Parameter from abl.reasoning import PrologKB, Reasoner from abl.utils import reform_list diff --git a/examples/hed/requirements.txt b/examples/hed/requirements.txt index 1710e0d..11aaa3a 100644 --- a/examples/hed/requirements.txt +++ b/examples/hed/requirements.txt @@ -1 +1,2 @@ -abl \ No newline at end of file +abl +gdown \ No newline at end of file diff --git a/examples/hwf/README.md b/examples/hwf/README.md index c10e94f..443c374 100644 --- a/examples/hwf/README.md +++ b/examples/hwf/README.md @@ -26,11 +26,9 @@ optional arguments: --no-cuda disables CUDA training --epochs EPOCHS number of epochs in each learning loop iteration (default : 1) - --lr LR base learning rate (default : 0.001) - --weight-decay WEIGHT_DECAY - weight decay value (default : 0.03) + --lr LR base model learning rate (default : 0.001) --batch-size BATCH_SIZE - batch size (default : 32) + base model batch size (default : 32) --loops LOOPS number of loop iterations (default : 5) --segment_size SEGMENT_SIZE segment size (default : 1/3) diff --git a/examples/hwf/datasets/get_dataset.py b/examples/hwf/datasets/get_dataset.py index c258c6d..6c79d0f 100644 --- a/examples/hwf/datasets/get_dataset.py +++ b/examples/hwf/datasets/get_dataset.py @@ -13,13 +13,13 @@ img_transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize( def download_and_unzip(url, zip_file_name): try: gdown.download(url, zip_file_name) - with zipfile.pseudo_labelipFile(zip_file_name, 'r') as zip_ref: + with zipfile.ZipFile(zip_file_name, 'r') as zip_ref: zip_ref.extractall(CURRENT_DIR) os.remove(zip_file_name) except Exception as e: if os.path.exists(zip_file_name): os.remove(zip_file_name) - raise Exception(f"An error occurred during download or unzip: {e}. Instead, you can download the dataset from {url} and unzip it in './datasets' folder") + raise Exception(f"An error occurred during download or unzip: {e}. Instead, you can download the dataset from {url} and unzip it in 'examples/hwf/datasets' folder") def get_dataset(train=True, get_pseudo_label=False): data_dir = CURRENT_DIR + '/data' diff --git a/examples/hwf/hwf.ipynb b/examples/hwf/hwf.ipynb index 6ddd79d..6cdd31f 100644 --- a/examples/hwf/hwf.ipynb +++ b/examples/hwf/hwf.ipynb @@ -6,7 +6,7 @@ "source": [ "# Handwritten Formula (HWF)\n", "\n", - "This notebook shows an implementation of [Handwritten Formula](https://arxiv.org/abs/2006.06649). In this task. In this task, handwritten images of decimal formulas and their computed results are given, alongwith a domain knowledge base containing information on how to compute the decimal formula. The task is to recognize the symbols (which can be digits or operators '+', '-', '×', '÷') of handwritten images and accurately determine their results.\n", + "This notebook shows an implementation of [Handwritten Formula](https://arxiv.org/abs/2006.06649). In this task, handwritten images of decimal formulas and their computed results are given, alongwith a domain knowledge base containing information on how to compute the decimal formula. The task is to recognize the symbols (which can be digits or operators '+', '-', '×', '÷') of handwritten images and accurately determine their results.\n", "\n", "Intuitively, we first use a machine learning model (learning part) to convert the input images to symbols (we call them pseudo-labels), and then use the knowledge base (reasoning part) to calculate the results of these symbols. Since we do not have ground-truth of the symbols, in Abductive Learning, the reasoning part will leverage domain knowledge and revise the initial symbols yielded by the learning part through abductive reasoning. This process enables us to further update the machine learning model." ] @@ -214,7 +214,7 @@ "# class of symbol may be one of ['0', '1', ..., '9', '+', '-', '*', '/'], total of 14 classes\n", "cls = SymbolNet(num_classes=14, image_size=(45, 45, 1))\n", "loss_fn = nn.CrossEntropyLoss()\n", - "optimizer = torch.optim.Adam(cls.parameters(), lr=0.001, betas=(0.9, 0.99))\n", + "optimizer = torch.optim.Adam(cls.parameters(), lr=0.001)\n", "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", "\n", "base_model = BasicNN(\n", diff --git a/examples/hwf/main.py b/examples/hwf/main.py index 6954a6b..75248e4 100644 --- a/examples/hwf/main.py +++ b/examples/hwf/main.py @@ -68,11 +68,9 @@ def main(): parser.add_argument('--epochs', type=int, default=3, help='number of epochs in each learning loop iteration (default : 3)') parser.add_argument('--lr', type=float, default=1e-3, - help='base learning rate (default : 0.001)') - parser.add_argument('--weight-decay', type=int, default=3e-2, - help='weight decay value (default : 0.03)') + help='base model learning rate (default : 0.001)') parser.add_argument('--batch-size', type=int, default=128, - help='batch size (default : 128)') + help='base model batch size (default : 128)') parser.add_argument('--loops', type=int, default=5, help='number of loop iterations (default : 5)') parser.add_argument('--segment_size', type=int or float, default=1000, diff --git a/examples/mnist_add/README.md b/examples/mnist_add/README.md index 51bdaad..c115c75 100644 --- a/examples/mnist_add/README.md +++ b/examples/mnist_add/README.md @@ -12,8 +12,8 @@ python main.py ## Usage ```bash -usage: main.py [-h] [--no-cuda] [--epochs EPOCHS] [--lr LR] - [--weight-decay WEIGHT_DECAY] [--batch-size BATCH_SIZE] +usage: main.py [-h] [--no-cuda] [--epochs EPOCHS] [--lr LR] + [--alpha ALPHA] [--batch-size BATCH_SIZE] [--loops LOOPS] [--segment_size SEGMENT_SIZE] [--save_interval SAVE_INTERVAL] [--max-revision MAX_REVISION] [--require-more-revision REQUIRE_MORE_REVISION] @@ -26,11 +26,10 @@ optional arguments: --no-cuda disables CUDA training --epochs EPOCHS number of epochs in each learning loop iteration (default : 1) - --lr LR base learning rate (default : 0.001) - --weight-decay WEIGHT_DECAY - weight decay value (default : 0.03) + --lr LR base model learning rate (default : 0.001) + --alpha ALPHA alpha in RMSprop (default : 0.9) --batch-size BATCH_SIZE - batch size (default : 32) + base model batch size (default : 32) --loops LOOPS number of loop iterations (default : 5) --segment_size SEGMENT_SIZE segment size (default : 1/3) diff --git a/examples/mnist_add/main.py b/examples/mnist_add/main.py index 72f10fe..873dae2 100644 --- a/examples/mnist_add/main.py +++ b/examples/mnist_add/main.py @@ -34,11 +34,11 @@ def main(): parser.add_argument('--epochs', type=int, default=1, help='number of epochs in each learning loop iteration (default : 1)') parser.add_argument('--lr', type=float, default=1e-3, - help='base learning rate (default : 0.001)') - parser.add_argument('--weight-decay', type=int, default=3e-2, - help='weight decay value (default : 0.03)') + help='base model learning rate (default : 0.001)') + parser.add_argument('--alpha', type=float, default=0.9, + help='alpha in RMSprop (default : 0.9)') parser.add_argument('--batch-size', type=int, default=32, - help='batch size (default : 32)') + help='base model batch size (default : 32)') parser.add_argument('--loops', type=int, default=5, help='number of loop iterations (default : 5)') parser.add_argument('--segment_size', type=int or float, default=1/3, @@ -65,7 +65,7 @@ def main(): # Build necessary components for BasicNN cls = LeNet5(num_classes=10) loss_fn = nn.CrossEntropyLoss() - optimizer = torch.optim.Adam(cls.parameters(), lr=args.lr) + optimizer = torch.optim.RMSprop(cls.parameters(), lr=args.lr, alpha=args.alpha) use_cuda = not args.no_cuda and torch.cuda.is_available() device = torch.device("cuda" if use_cuda else "cpu") diff --git a/examples/mnist_add/mnist_add.ipynb b/examples/mnist_add/mnist_add.ipynb index a69ab22..31ed3af 100644 --- a/examples/mnist_add/mnist_add.ipynb +++ b/examples/mnist_add/mnist_add.ipynb @@ -80,11 +80,6 @@ } ], "source": [ - "def describe_structure(lst):\n", - " if not isinstance(lst, list):\n", - " return type(lst).__name__ \n", - " return [describe_structure(item) for item in lst]\n", - "\n", "print(f\"Both train_data and test_data consist of 3 components: X, gt_pseudo_label, Y\")\n", "print()\n", "train_X, train_gt_pseudo_label, train_Y = train_data\n", @@ -357,7 +352,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -390,7 +385,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -402,14 +397,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Bridge Learning and Reasoning\n", + "## Bridging Learning and Reasoning\n", "\n", "Now, the last step is to bridge the learning and reasoning part. We proceed this step by creating an instance of `SimpleBridge`." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -437,6 +432,13 @@ "bridge.train(train_data, loops=5, segment_size=1/3, save_interval=1, save_dir=weights_dir)\n", "bridge.test(test_data)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -455,7 +457,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.18" + "version": "3.8.13" }, "orig_nbformat": 4, "vscode": { diff --git a/examples/zoo/get_dataset.py b/examples/zoo/get_dataset.py new file mode 100644 index 0000000..e7dd3db --- /dev/null +++ b/examples/zoo/get_dataset.py @@ -0,0 +1,29 @@ +import numpy as np +import openml + +# Function to load and preprocess the dataset +def load_and_preprocess_dataset(dataset_id): + dataset = openml.datasets.get_dataset(dataset_id, download_data=True, download_qualities=False, download_features_meta_data=False) + X, y, _, attribute_names = dataset.get_data(target=dataset.default_target_attribute) + # Convert data types + for col in X.select_dtypes(include='bool').columns: + X[col] = X[col].astype(int) + y = y.cat.codes.astype(int) + X, y = X.to_numpy(), y.to_numpy() + return X, y + +# Function to split data (one shot) +def split_dataset(X, y, test_size = 0.3): + # For every class: 1 : (1-test_size)*(len-1) : test_size*(len-1) + label_indices, unlabel_indices, test_indices = [], [], [] + for class_label in np.unique(y): + idxs = np.where(y == class_label)[0] + np.random.shuffle(idxs) + n_train_unlabel = int((1-test_size)*(len(idxs)-1)) + label_indices.append(idxs[0]) + unlabel_indices.extend(idxs[1:1+n_train_unlabel]) + test_indices.extend(idxs[1+n_train_unlabel:]) + X_label, y_label = X[label_indices], y[label_indices] + X_unlabel, y_unlabel = X[unlabel_indices], y[unlabel_indices] + X_test, y_test = X[test_indices], y[test_indices] + return X_label, y_label, X_unlabel, y_unlabel, X_test, y_test \ No newline at end of file diff --git a/examples/zoo/kb.py b/examples/zoo/kb.py new file mode 100644 index 0000000..0954ec7 --- /dev/null +++ b/examples/zoo/kb.py @@ -0,0 +1,80 @@ +from z3 import Solver, Int, If, Not, Implies, Sum, sat +import openml +from abl.reasoning import KBBase + +class ZooKB(KBBase): + def __init__(self): + super().__init__(pseudo_label_list=list(range(7)), use_cache=False) + + self.solver = Solver() + + # Load information of Zoo dataset + dataset = openml.datasets.get_dataset(dataset_id = 62, download_data=False, download_qualities=False, download_features_meta_data=False) + X, y, categorical_indicator, attribute_names = dataset.get_data(target=dataset.default_target_attribute) + self.attribute_names = attribute_names + self.target_names = y.cat.categories.tolist() + print("Attribute names are: ", self.attribute_names) + print("Target names are: ", self.target_names) + # self.attribute_names = ["hair", "feathers", "eggs", "milk", "airborne", "aquatic", "predator", "toothed", "backbone", "breathes", "venomous", "fins", "legs", "tail", "domestic", "catsize"] + # self.target_names = ["mammal", "bird", "reptile", "fish", "amphibian", "insect", "invertebrate"] + + # Define variables + for name in self.attribute_names+self.target_names: + exec(f"globals()['{name}'] = Int('{name}')") ## or use dict to create var and modify rules + # Define rules + rules = [ + Implies(milk == 1, mammal == 1), + Implies(mammal == 1, milk == 1), + Implies(mammal == 1, backbone == 1), + Implies(mammal == 1, breathes == 1), + Implies(feathers == 1, bird == 1), + Implies(bird == 1, feathers == 1), + Implies(bird == 1, eggs == 1), + Implies(bird == 1, backbone == 1), + Implies(bird == 1, breathes == 1), + Implies(bird == 1, legs == 2), + Implies(bird == 1, tail == 1), + Implies(reptile == 1, backbone == 1), + Implies(reptile == 1, breathes == 1), + Implies(reptile == 1, tail == 1), + Implies(fish == 1, aquatic == 1), + Implies(fish == 1, toothed == 1), + Implies(fish == 1, backbone == 1), + Implies(fish == 1, Not(breathes == 1)), + Implies(fish == 1, fins == 1), + Implies(fish == 1, legs == 0), + Implies(fish == 1, tail == 1), + Implies(amphibian == 1, eggs == 1), + Implies(amphibian == 1, aquatic == 1), + Implies(amphibian == 1, backbone == 1), + Implies(amphibian == 1, breathes == 1), + Implies(amphibian == 1, legs == 4), + Implies(insect == 1, eggs == 1), + Implies(insect == 1, Not(backbone == 1)), + Implies(insect == 1, legs == 6), + Implies(invertebrate == 1, Not(backbone == 1)) + ] + # Define weights and sum of violated weights + self.weights = {rule: 1 for rule in rules} + self.total_violation_weight = Sum([If(Not(rule), self.weights[rule], 0) for rule in self.weights]) + + def logic_forward(self, pseudo_label, data_point): + attribute_names, target_names = self.attribute_names, self.target_names + solver = self.solver + total_violation_weight = self.total_violation_weight + pseudo_label, data_point = pseudo_label[0], data_point[0] + + self.solver.reset() + for name, value in zip(attribute_names, data_point): + solver.add(eval(f"{name} == {value}")) + for cate, name in zip(self.pseudo_label_list,target_names): + value = 1 if (cate == pseudo_label) else 0 + solver.add(eval(f"{name} == {value}")) + + if solver.check() == sat: + model = solver.model() + total_weight = model.evaluate(total_violation_weight) + return total_weight.as_long() + else: + # No solution found + return 1e10 diff --git a/examples/zoo/requirements.txt b/examples/zoo/requirements.txt new file mode 100644 index 0000000..2f73c5b --- /dev/null +++ b/examples/zoo/requirements.txt @@ -0,0 +1,4 @@ +abl +z3-solver +openml +scikit-learn \ No newline at end of file diff --git a/examples/zoo/zoo.ipynb b/examples/zoo/zoo.ipynb new file mode 100644 index 0000000..2fa570d --- /dev/null +++ b/examples/zoo/zoo.ipynb @@ -0,0 +1,370 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ZOO\n", + "\n", + "This notebook shows an implementation of [MNIST Addition](https://arxiv.org/abs/1805.10872). In this task, pairs of MNIST handwritten images and their sums are given, alongwith a domain knowledge base containing information on how to perform addition operations. The task is to recognize the digits of handwritten images and accurately determine their sum.\n", + "\n", + "Intuitively, we first use a machine learning model (learning part) to convert the input images to digits (we call them pseudo-labels), and then use the knowledge base (reasoning part) to calculate the sum of these digits. Since we do not have ground-truth of the digits, in Abductive Learning, the reasoning part will leverage domain knowledge and revise the initial digits yielded by the learning part through abductive reasoning. This process enables us to further update the machine learning model." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Import necessary libraries and modules\n", + "import os.path as osp\n", + "import numpy as np\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from examples.zoo.get_dataset import load_and_preprocess_dataset, split_dataset\n", + "from abl.learning import ABLModel\n", + "from examples.zoo.kb import ZooKB\n", + "from abl.reasoning import Reasoner\n", + "from abl.evaluation import ReasoningMetric, SymbolMetric\n", + "from abl.utils import ABLLogger, print_log, confidence_dist\n", + "from abl.bridge import SimpleBridge" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with Data\n", + "\n", + "First, we get the training and testing datasets:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Load and preprocess the Zoo dataset\n", + "X, y = load_and_preprocess_dataset(dataset_id=62)\n", + "\n", + "# Split data into labeled/unlabeled/test data\n", + "X_label, y_label, X_unlabel, y_unlabel, X_test, y_test = split_dataset(X, y, test_size=0.3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`train_data` and `test_data` share identical structures: tuples with three components: X (list where each element is a list of two images), gt_pseudo_label (list where each element is a list of two digits, i.e., pseudo-labels) and Y (list where each element is the sum of the two digits). The length and structures of datasets are illustrated as follows.\n", + "\n", + "Note: ``gt_pseudo_label`` is only used to evaluate the performance of the learning part but not to train the model." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of X and y: (101, 16) (101,)\n", + "First five elements of X:\n", + "[[True False False True False False True True True True False False 4\n", + " False False True]\n", + " [True False False True False False False True True True False False 4\n", + " True False True]\n", + " [False False True False False True True True True False False True 0\n", + " True False False]\n", + " [True False False True False False True True True True False False 4\n", + " False False True]\n", + " [True False False True False False True True True True False False 4\n", + " True False True]]\n", + "First five elements of y:\n", + "[0 0 3 0 0]\n" + ] + } + ], + "source": [ + "print(\"Shape of X and y:\", X.shape, y.shape)\n", + "print(\"First five elements of X:\")\n", + "print(X[:5])\n", + "print(\"First five elements of y:\")\n", + "print(y[:5])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Transform tabluar data to the format required by ABL-Package, which is a tuple of (X, gt_pseudo_label, Y)\n", + "\n", + "For tabular data in abl, each example contains a single instance (a row from the dataset).\n", + "\n", + "For these tabular data samples, the reasoning results are expected to be 0, indicating no rules are violated." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def transform_tab_data(X, y):\n", + " return ([[x] for x in X], [[y_item] for y_item in y], [0] * len(y))\n", + "label_data = transform_tab_data(X_label, y_label)\n", + "test_data = transform_tab_data(X_test, y_test)\n", + "train_data = transform_tab_data(X_unlabel, y_unlabel)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Building the Learning Part" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To build the learning part, we need to first build a machine learning base model. We use a [Random Forest](https://en.wikipedia.org/wiki/Random_forest) as the base model" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
RandomForestClassifier()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "RandomForestClassifier()" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "base_model = RandomForestClassifier()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, the base model built above deals with instance-level data, and can not directly deal with example-level data. Therefore, we wrap the base model into `ABLModel`, which enables the learning part to train, test, and predict on example-level data." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "model = ABLModel(base_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Building the Reasoning Part" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the reasoning part, we first build a knowledge base which contain information on how to perform addition operations. We build it by creating a subclass of `KBBase`. In the derived subclass, we initialize the `pseudo_label_list` parameter specifying list of possible pseudo-labels, and override the `logic_forward` function defining how to perform (deductive) reasoning." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Attribute names are: ['hair', 'feathers', 'eggs', 'milk', 'airborne', 'aquatic', 'predator', 'toothed', 'backbone', 'breathes', 'venomous', 'fins', 'legs', 'tail', 'domestic', 'catsize']\n", + "Target names are: ['mammal', 'bird', 'reptile', 'fish', 'amphibian', 'insect', 'invertebrate']\n" + ] + } + ], + "source": [ + "kb = ZooKB()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The knowledge base can perform logical reasoning (both deductive reasoning and abductive reasoning). Below is an example of performing (deductive) reasoning, and users can refer to [Documentation]() for details of abductive reasoning." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reasoning result of pseudo-label example [1, 2] is 3.\n" + ] + } + ], + "source": [ + "pseudo_label = [0]\n", + "data_point = [np.array([1,0,0,1,0,0,1,1,1,1,0,0,4,0,0,1,1])]\n", + "print(kb.logic_forward(pseudo_label, data_point))\n", + "for x, y_item in zip(X, y):\n", + " print(x,y_item)\n", + " print(kb.logic_forward([y_item], [x]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note: In addition to building a knowledge base based on `KBBase`, we can also establish a knowledge base with a ground KB using `GroundKB`, or a knowledge base implemented based on Prolog files using `PrologKB`. The corresponding code for these implementations can be found in the `main.py` file. Those interested are encouraged to examine it for further insights." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then, we create a reasoner by instantiating the class ``Reasoner``. Due to the indeterminism of abductive reasoning, there could be multiple candidates compatible to the knowledge base. When this happens, reasoner can minimize inconsistencies between the knowledge base and pseudo-labels predicted by the learning part, and then return only one candidate that has the highest consistency." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def consitency(data_example, candidates, candidate_idxs, reasoning_results):\n", + " pred_prob = data_example.pred_prob\n", + " model_scores = confidence_dist(pred_prob, candidate_idxs)\n", + " rule_scores = np.array(reasoning_results)\n", + " scores = model_scores + rule_scores\n", + " return scores\n", + "\n", + "reasoner = Reasoner(kb, dist_func=consitency)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Building Evaluation Metrics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we set up evaluation metrics. These metrics will be used to evaluate the model performance during training and testing. Specifically, we use `SymbolMetric` and `ReasoningMetric`, which are used to evaluate the accuracy of the machine learning model’s predictions and the accuracy of the final reasoning results, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "metric_list = [SymbolMetric(prefix=\"zoo\"), ReasoningMetric(kb=kb, prefix=\"zoo\")]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bridging Learning and Reasoning\n", + "\n", + "Now, the last step is to bridge the learning and reasoning part. We proceed this step by creating an instance of `SimpleBridge`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "bridge = SimpleBridge(model, reasoner, metric_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Perform training and testing by invoking the `train` and `test` methods of `SimpleBridge`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Build logger\n", + "print_log(\"Abductive Learning on the ZOO example.\", logger=\"current\")\n", + "log_dir = ABLLogger.get_current_instance().log_dir\n", + "weights_dir = osp.join(log_dir, \"weights\")\n", + "\n", + "# Pre-train the machine learning model\n", + "base_model.fit(X_label, y_label)\n", + "\n", + "# Test the initial model\n", + "print(\"------- Test the initial model -----------\")\n", + "bridge.test(test_data)\n", + "print(\"------- Use ABL to train the model -----------\")\n", + "# Use ABL to train the model\n", + "bridge.train(train_data=train_data, label_data=label_data, loops=3, segment_size=len(X_unlabel), save_dir=weights_dir)\n", + "print(\"------- Test the final model -----------\")\n", + "# Test the final model\n", + "bridge.test(test_data)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "abl", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "9c8d454494e49869a4ee4046edcac9a39ff683f7d38abf0769f648402670238e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/zoo/zoo_example.ipynb b/examples/zoo/zoo_example.ipynb deleted file mode 100644 index 7dafc30..0000000 --- a/examples/zoo/zoo_example.ipynb +++ /dev/null @@ -1,292 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os.path as osp\n", - "\n", - "import numpy as np\n", - "from sklearn.ensemble import RandomForestClassifier\n", - "from sklearn.metrics import accuracy_score\n", - "from z3 import Solver, Int, If, Not, Implies, Sum, sat\n", - "import openml\n", - "\n", - "from abl.learning import ABLModel\n", - "from abl.reasoning import KBBase, Reasoner\n", - "from abl.evaluation import ReasoningMetric, SymbolMetric\n", - "from abl.bridge import SimpleBridge\n", - "from abl.utils.utils import confidence_dist\n", - "from abl.utils import ABLLogger, print_log" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Build logger\n", - "print_log(\"Abductive Learning on the Zoo example.\", logger=\"current\")\n", - "\n", - "# Retrieve the directory of the Log file and define the directory for saving the model weights.\n", - "log_dir = ABLLogger.get_current_instance().log_dir\n", - "weights_dir = osp.join(log_dir, \"weights\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Learning Part" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rf = RandomForestClassifier()\n", - "model = ABLModel(rf)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Logic Part" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "class ZooKB(KBBase):\n", - " def __init__(self):\n", - " super().__init__(pseudo_label_list=list(range(7)), use_cache=False)\n", - " \n", - " # Use z3 solver \n", - " self.solver = Solver()\n", - "\n", - " # Load information of Zoo dataset\n", - " dataset = openml.datasets.get_dataset(dataset_id = 62, download_data=False, download_qualities=False, download_features_meta_data=False)\n", - " X, y, categorical_indicator, attribute_names = dataset.get_data(target=dataset.default_target_attribute)\n", - " self.attribute_names = attribute_names\n", - " self.target_names = y.cat.categories.tolist()\n", - " \n", - " # Define variables\n", - " for name in self.attribute_names+self.target_names:\n", - " exec(f\"globals()['{name}'] = Int('{name}')\") ## or use dict to create var and modify rules\n", - " # Define rules\n", - " rules = [\n", - " Implies(milk == 1, mammal == 1),\n", - " Implies(mammal == 1, milk == 1),\n", - " Implies(mammal == 1, backbone == 1),\n", - " Implies(mammal == 1, breathes == 1),\n", - " Implies(feathers == 1, bird == 1),\n", - " Implies(bird == 1, feathers == 1),\n", - " Implies(bird == 1, eggs == 1),\n", - " Implies(bird == 1, backbone == 1),\n", - " Implies(bird == 1, breathes == 1),\n", - " Implies(bird == 1, legs == 2),\n", - " Implies(bird == 1, tail == 1),\n", - " Implies(reptile == 1, backbone == 1),\n", - " Implies(reptile == 1, breathes == 1),\n", - " Implies(reptile == 1, tail == 1),\n", - " Implies(fish == 1, aquatic == 1),\n", - " Implies(fish == 1, toothed == 1),\n", - " Implies(fish == 1, backbone == 1),\n", - " Implies(fish == 1, Not(breathes == 1)),\n", - " Implies(fish == 1, fins == 1),\n", - " Implies(fish == 1, legs == 0),\n", - " Implies(fish == 1, tail == 1),\n", - " Implies(amphibian == 1, eggs == 1),\n", - " Implies(amphibian == 1, aquatic == 1),\n", - " Implies(amphibian == 1, backbone == 1),\n", - " Implies(amphibian == 1, breathes == 1),\n", - " Implies(amphibian == 1, legs == 4),\n", - " Implies(insect == 1, eggs == 1),\n", - " Implies(insect == 1, Not(backbone == 1)),\n", - " Implies(insect == 1, legs == 6),\n", - " Implies(invertebrate == 1, Not(backbone == 1))\n", - " ]\n", - " # Define weights and sum of violated weights\n", - " self.weights = {rule: 1 for rule in rules}\n", - " self.total_violation_weight = Sum([If(Not(rule), self.weights[rule], 0) for rule in self.weights])\n", - " \n", - " def logic_forward(self, pseudo_label, data_point):\n", - " attribute_names, target_names = self.attribute_names, self.target_names\n", - " solver = self.solver\n", - " total_violation_weight = self.total_violation_weight\n", - " pseudo_label, data_point = pseudo_label[0], data_point[0]\n", - " \n", - " self.solver.reset()\n", - " for name, value in zip(attribute_names, data_point):\n", - " solver.add(eval(f\"{name} == {value}\"))\n", - " for cate, name in zip(self.pseudo_label_list,target_names):\n", - " value = 1 if (cate == pseudo_label) else 0\n", - " solver.add(eval(f\"{name} == {value}\"))\n", - " \n", - " if solver.check() == sat:\n", - " model = solver.model()\n", - " total_weight = model.evaluate(total_violation_weight)\n", - " return total_weight.as_long()\n", - " else:\n", - " # No solution found\n", - " return 1e10\n", - " \n", - "def consitency(data_example, candidates, candidate_idxs, reasoning_results):\n", - " pred_prob = data_example.pred_prob\n", - " model_scores = confidence_dist(pred_prob, candidate_idxs)\n", - " rule_scores = np.array(reasoning_results)\n", - " scores = model_scores + rule_scores\n", - " return scores\n", - "\n", - "kb = ZooKB()\n", - "reasoner = Reasoner(kb, dist_func=consitency)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Datasets and Evaluation Metrics" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Function to load and preprocess the dataset\n", - "def load_and_preprocess_dataset(dataset_id):\n", - " dataset = openml.datasets.get_dataset(dataset_id, download_data=True, download_qualities=False, download_features_meta_data=False)\n", - " X, y, _, attribute_names = dataset.get_data(target=dataset.default_target_attribute)\n", - " # Convert data types\n", - " for col in X.select_dtypes(include='bool').columns:\n", - " X[col] = X[col].astype(int)\n", - " y = y.cat.codes.astype(int)\n", - " X, y = X.to_numpy(), y.to_numpy()\n", - " return X, y\n", - "\n", - "# Function to split data (one shot)\n", - "def split_dataset(X, y, test_size = 0.3):\n", - " # For every class: 1 : (1-test_size)*(len-1) : test_size*(len-1)\n", - " label_indices, unlabel_indices, test_indices = [], [], []\n", - " for class_label in np.unique(y):\n", - " idxs = np.where(y == class_label)[0]\n", - " np.random.shuffle(idxs)\n", - " n_train_unlabel = int((1-test_size)*(len(idxs)-1))\n", - " label_indices.append(idxs[0])\n", - " unlabel_indices.extend(idxs[1:1+n_train_unlabel])\n", - " test_indices.extend(idxs[1+n_train_unlabel:])\n", - " X_label, y_label = X[label_indices], y[label_indices]\n", - " X_unlabel, y_unlabel = X[unlabel_indices], y[unlabel_indices]\n", - " X_test, y_test = X[test_indices], y[test_indices]\n", - " return X_label, y_label, X_unlabel, y_unlabel, X_test, y_test\n", - "\n", - "# Load and preprocess the Zoo dataset\n", - "X, y = load_and_preprocess_dataset(dataset_id=62)\n", - "\n", - "# Split data into labeled/unlabeled/test data\n", - "X_label, y_label, X_unlabel, y_unlabel, X_test, y_test = split_dataset(X, y, test_size=0.3)\n", - "\n", - "# Transform tabluar data to the format required by ABL, which is a tuple of (X, ground truth of X, reasoning results)\n", - "# For tabular data in abl, each example contains a single instance (a row from the dataset).\n", - "# For these tabular data examples, the reasoning results are expected to be 0, indicating no rules are violated.\n", - "def transform_tab_data(X, y):\n", - " return ([[x] for x in X], [[y_item] for y_item in y], [0] * len(y))\n", - "label_data = transform_tab_data(X_label, y_label)\n", - "test_data = transform_tab_data(X_test, y_test)\n", - "train_data = transform_tab_data(X_unlabel, y_unlabel)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Set up metrics\n", - "metric_list = [SymbolMetric(prefix=\"zoo\"), ReasoningMetric(kb=kb, prefix=\"zoo\")]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Bridge Machine Learning and Logic Reasoning" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "bridge = SimpleBridge(model, reasoner, metric_list)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Train and Test" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Pre-train the machine learning model\n", - "rf.fit(X_label, y_label)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Test the initial model\n", - "print(\"------- Test the initial model -----------\")\n", - "bridge.test(test_data)\n", - "print(\"------- Use ABL to train the model -----------\")\n", - "# Use ABL to train the model\n", - "bridge.train(train_data=train_data, label_data=label_data, loops=3, segment_size=len(X_unlabel), save_dir=weights_dir)\n", - "print(\"------- Test the final model -----------\")\n", - "# Test the final model\n", - "bridge.test(test_data)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "abl", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.13" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tests/conftest.py b/tests/conftest.py index ec3ceba..67c8024 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -215,7 +215,7 @@ def kb_hwf2(): def kb_hed(): kb = HedKB( pseudo_label_list=[1, 0, "+", "="], - pl_file="examples/hed/datasets/learn_add.pl", + pl_file="examples/hed/reasoning/learn_add.pl", ) return kb diff --git a/tests/test_reasoning.py b/tests/test_reasoning.py index 71e4bfd..744b10d 100644 --- a/tests/test_reasoning.py +++ b/tests/test_reasoning.py @@ -57,7 +57,7 @@ class TestPrologKB(object): def test_init_pl2(self, kb_hed): assert kb_hed.pseudo_label_list == [1, 0, "+", "="] - assert kb_hed.pl_file == "examples/hed/datasets/learn_add.pl" + assert kb_hed.pl_file == "examples/hed/reasoning/learn_add.pl" def test_prolog_file_not_exist(self): pseudo_label_list = [1, 2]