| @@ -1,5 +1,168 @@ | |||
| Use ABL-Package Step by Step | |||
| ================== | |||
| ============================= | |||
| .. contents:: Table of Contents | |||
| Using ABL-Package for your learning tasks contains five steps | |||
| - Build the reasoning part | |||
| - Build the machine learning part | |||
| - Build datasets and evaluation metrics | |||
| - Bridge the reasoning and machine learning parts | |||
| - Use ``Bridge.train`` and ``Bridge.test`` to train and test | |||
| Build the reasoning part | |||
| ~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| In ABL-Package, the reasoning part is wrapped in the ``ReasonerBase`` class. In order to create an instance of this class, we first need to inherit the ``KBBase`` class to customize our knowledge base. Arguments of the ``__init__`` method of the knowledge base should at least contain ``pseudo_label_list`` which is a list of all pseudo labels. The ``logic_forward`` method of ``KBBase`` is an abstract method and we need to instantiate this method in our sub-class to give the ability of deduction to the knowledge base. In general, we can customize our knowledge base by | |||
| .. code:: python | |||
| class MyKB(KBBase): | |||
| def __init__(self, pseudo_label_list): | |||
| super().__init__(pseudo_label_list) | |||
| def logic_forward(self, *args, **kwargs): | |||
| # Deduction implementation... | |||
| return deduction_result | |||
| Aside from the knowledge base, the instantiation of the ``ReasonerBase`` also needs to set an extra argument called ``dist_func``, which is the consistency measure used to select the best candidate from all candidates. In general, we can instantiate our reasoner by | |||
| .. code:: python | |||
| kb = MyKB(pseudo_label_list) | |||
| reasoner = ReasonerBase(kb, dist_func="hamming") | |||
| In the MNIST Add example, the reasoner looks like | |||
| .. code:: python | |||
| class AddKB(KBBase): | |||
| def __init__(self, pseudo_label_list): | |||
| super().__init__(pseudo_label_list) | |||
| # Implement the deduction function | |||
| def logic_forward(self, nums): | |||
| return sum(nums) | |||
| kb = AddKB(pseudo_label_list=list(range(10))) | |||
| reasoner = ReasonerBase(kb, dist_func="confidence") | |||
| Build the machine learning part | |||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| Next, we build the machine learning part, which needs to be wrapped in the ``ABLModel`` class. We can use machine learning models from scikit-learn or based on PyTorch to create an instance of ``ABLModel``. | |||
| - for a scikit-learn model, we can directly use the model to create an instance of ``ABLModel``. For example, we can customize our machine learning model by | |||
| .. code:: python | |||
| # Load a scikit-learn model | |||
| base_model = sklearn.neighbors.KNeighborsClassifier(n_neighbors=3) | |||
| model = ABLModel(base_model) | |||
| - for a PyTorch-based neural network, we first need to encapsulate it within a ``BasicNN`` object and then use this object to instantiate an instance of ``ABLModel``. For example, we can customize our machine learning model by | |||
| .. code:: python | |||
| # Load a PyTorch-based neural network | |||
| cls = torchvision.models.resnet18(pretrained=True) | |||
| # criterion and optimizer are used for training | |||
| criterion = torch.nn.CrossEntropyLoss() | |||
| optimizer = torch.optim.Adam(cls.parameters()) | |||
| base_model = BasicNN(cls, criterion, optimizer) | |||
| model = ABLModel(base_model) | |||
| In the MNIST Add example, the machine learning model looks like | |||
| .. code:: python | |||
| cls = LeNet5(num_classes=len(kb.pseudo_label_list)) | |||
| device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") | |||
| criterion = torch.nn.CrossEntropyLoss() | |||
| optimizer = torch.optim.Adam(cls.parameters(), lr=0.001, betas=(0.9, 0.99)) | |||
| base_model = BasicNN( | |||
| cls, | |||
| criterion, | |||
| optimizer, | |||
| device=device, | |||
| batch_size=32, | |||
| num_epochs=1, | |||
| ) | |||
| model = ABLModel(base_model) | |||
| Build datasets and evaluation metrics | |||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| Next, we need to build datasets and evaluation metrics for training and validation. ABL-Package assumes data to be in the form of ``(X, gt_pseudo_label, Y)`` where ``X`` is the input of the machine learning model, ``Y`` is the ground truth of the reasoning result and ``gt_pseudo_label`` is the ground truth label of each element in ``X``. ``X`` should be of type ``List[List[Any]]``, ``Y`` should be of type ``List[Any]`` and ``gt_pseudo_label`` can be ``None`` or of the type ``List[List[Any]]``. | |||
| In the MNIST Add example, the data loading looks like | |||
| .. code:: python | |||
| # train_data and test_data are all tuples consist of X, gt_pseudo_label and Y. | |||
| train_data = get_mnist_add(train=True, get_pseudo_label=True) | |||
| test_data = get_mnist_add(train=False, get_pseudo_label=True) | |||
| To validate and test the model, we need to inherit from ``BaseMetric`` to define metrics and implement the ``process`` and ``compute_metrics`` methods where the process method accepts a batch of outputs. After processing this batch of data, we save the information to ``self.results`` property. The input results of ``compute_metrics`` is all the information saved in ``process``. Use these information to calculate and return a dict that holds the results of the evaluation metrics. | |||
| We provide two basic metrics, namely ``SymbolMetric`` and ``SemanticsMetric``, which are used to evaluate the accuracy of the machine learning model's predictions and the accuracy of the ``logic_forward`` results, respectively. | |||
| In the case of MNIST Add example, the metric definition looks like | |||
| .. code:: python | |||
| metric_list = [SymbolMetric(prefix="mnist_add"), SemanticsMetric(kb=kb, prefix="mnist_add")] | |||
| Bridge the reasoning and machine learning parts | |||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| We next need to bridge the reasoning and machine learning parts. In ABL-Package, the ``BaseBridge`` class gives necessary abstract interface definitions to bridge the two parts and ``SimpleBridge`` provides a basic implementation. | |||
| We build a bridge with previously defined ``reasoner``, ``model``, and ``metric_list`` as follows: | |||
| .. code:: python | |||
| bridge = SimpleBridge(model, reasoner, metric_list) | |||
| In the MNIST Add example, the bridge creation looks the same. | |||
| Use ``Bridge.train`` and ``Bridge.test`` to train and test | |||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| ``BaseBridge.train`` and ``BaseBridge.test`` trigger the training and testing processes, respectively. | |||
| The two methods take the previous prepared ``train_data`` and ``test_data`` as input. | |||
| .. code:: python | |||
| bridge.train(train_data) | |||
| bridge.test(test_data) | |||
| Aside from data, ``BaseBridge.train`` can also take some other training configs shown as follows: | |||
| .. code:: python | |||
| bridge.train( | |||
| # training data | |||
| train_data, | |||
| # number of Abductive Learning loops | |||
| loops=5, | |||
| # data will be divided into segments and each segment will be used to train the model iteratively | |||
| segment_size=10000, | |||
| # evaluate the model every eval_interval loops | |||
| eval_interval=1, | |||
| # save the model every save_interval loops | |||
| save_interval=1, | |||
| # directory to save the model | |||
| save_dir='./save_dir', | |||
| ) | |||
| In the MNIST Add example, the code to train and test looks like | |||
| .. code:: python | |||
| bridge.train(train_data, loops=5, segment_size=10000, save_interval=1, save_dir=weights_dir) | |||
| bridge.test(test_data) | |||