From 3154d859522f2ec4281d43e4884044dfd011761a Mon Sep 17 00:00:00 2001 From: troyyyyy Date: Thu, 21 Dec 2023 15:09:19 +0800 Subject: [PATCH] [ENH] add hed example --- docs/Examples/HED.rst | 299 +++++- docs/Examples/HWF.rst | 2 +- docs/Examples/MNISTAdd.rst | 2 +- docs/Examples/ZOO.rst | 2 + docs/img/hed_dataset1.png | Bin 0 -> 3498 bytes docs/img/hed_dataset2.png | Bin 0 -> 11762 bytes docs/img/hed_dataset3.png | Bin 0 -> 4193 bytes docs/img/hed_dataset4.png | Bin 0 -> 9756 bytes docs/index.rst | 1 + examples/hed/bridge.py | 4 +- examples/hed/datasets/README.md | 4 - examples/hed/datasets/__init__.py | 4 +- examples/hed/datasets/equation_generator.py | 173 ++++ examples/hed/datasets/get_dataset.py | 74 +- examples/hed/hed.ipynb | 976 +++++--------------- examples/hed/reasoning/reasoning.py | 1 - examples/hed/requirements.txt | 3 +- examples/hwf/README.md | 6 +- examples/hwf/datasets/get_dataset.py | 4 +- examples/hwf/hwf.ipynb | 4 +- examples/hwf/main.py | 6 +- examples/mnist_add/README.md | 11 +- examples/mnist_add/main.py | 10 +- examples/mnist_add/mnist_add.ipynb | 22 +- examples/zoo/get_dataset.py | 29 + examples/zoo/kb.py | 80 ++ examples/zoo/requirements.txt | 4 + examples/zoo/zoo.ipynb | 370 ++++++++ examples/zoo/zoo_example.ipynb | 292 ------ tests/conftest.py | 2 +- tests/test_reasoning.py | 2 +- 31 files changed, 1286 insertions(+), 1101 deletions(-) create mode 100644 docs/Examples/ZOO.rst create mode 100644 docs/img/hed_dataset1.png create mode 100644 docs/img/hed_dataset2.png create mode 100644 docs/img/hed_dataset3.png create mode 100644 docs/img/hed_dataset4.png delete mode 100644 examples/hed/datasets/README.md create mode 100644 examples/hed/datasets/equation_generator.py create mode 100644 examples/zoo/get_dataset.py create mode 100644 examples/zoo/kb.py create mode 100644 examples/zoo/requirements.txt create mode 100644 examples/zoo/zoo.ipynb delete mode 100644 examples/zoo/zoo_example.ipynb 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 0000000000000000000000000000000000000000..f0d6be311fe87bf0d2fc45a3d61e1c8366f586f6 GIT binary patch literal 3498 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 literal 0 HcmV?d00001 diff --git a/docs/img/hed_dataset2.png b/docs/img/hed_dataset2.png new file mode 100644 index 0000000000000000000000000000000000000000..f8e203a9d2da51e3f24a96c5e6285f7c66d799fd GIT binary patch literal 11762 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 b^#gv9rUgfJv;I?m4IwM3B=JYgIOzWYgh7~B literal 0 HcmV?d00001 diff --git a/docs/img/hed_dataset3.png b/docs/img/hed_dataset3.png new file mode 100644 index 0000000000000000000000000000000000000000..5ed16c2b7e6478315e659a95d7c8a9784f9710f9 GIT binary patch literal 4193 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 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": "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", + "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]