Browse Source

[DOC] complete 'Qucik Start' and 'Installation'

pull/1/head
Gao Enhao 2 years ago
parent
commit
ab633210c1
6 changed files with 198 additions and 113 deletions
  1. +43
    -43
      docs/Brief-Introduction/Usage.rst
  2. +15
    -1
      docs/Overview/Installation.rst
  3. +79
    -1
      docs/Overview/Quick Start.rst
  4. +1
    -1
      docs/index.rst
  5. +22
    -13
      examples/mnist_add/datasets/get_mnist_add.py
  6. +38
    -54
      examples/mnist_add/mnist_add_example.ipynb

+ 43
- 43
docs/Brief-Introduction/Usage.rst View File

@@ -3,53 +3,16 @@ Use ABL-Package Step by Step

Using ABL-Package for your learning tasks contains five steps

- Build the reasoning part
- Build the machine learning part
- Build the reasoning part
- Build datasets and evaluation metrics
- Bridge the reasoning and machine learning parts
- Bridge the machine learning and reasoning 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``.
First, 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

@@ -94,6 +57,43 @@ In the MNIST Add example, the machine learning model looks like
)
model = ABLModel(base_model)

Build the reasoning part
~~~~~~~~~~~~~~~~~~~~~~~~

Next, we 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 datasets and evaluation metrics
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

@@ -117,11 +117,11 @@ In the case of MNIST Add example, the metric definition looks like

metric_list = [SymbolMetric(prefix="mnist_add"), SemanticsMetric(kb=kb, prefix="mnist_add")]

Bridge the reasoning and machine learning parts
Bridge the machine learning and reasoning 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:
We next need to bridge the machine learning and reasoning 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 ``model``, ``reasoner``, and ``metric_list`` as follows:

.. code:: python



+ 15
- 1
docs/Overview/Installation.rst View File

@@ -1,5 +1,19 @@
Installation
==================

.. contents:: Table of Contents
Case a: If you develop and run ``abl`` directly, install it from source:

.. code-block:: bash

git clone https://github.com/AbductiveLearning/ABL-Package.git
cd ABL-Package
pip install -v -e .
# "-v" means verbose, or more output
# "-e" means installing a project in editable mode,
# thus any local modifications made to the code will take effect without reinstallation.

Case b: If you use ``abl`` as a dependency or third-party package, install it with pip:

.. code-block:: bash

pip install abl

+ 79
- 1
docs/Overview/Quick Start.rst View File

@@ -1,5 +1,83 @@
Quick Start
==================

.. contents:: Table of Contents
We use the MNIST Add benchmark as a quick start example.

.. code:: python

import torch
import torch.nn as nn

from abl.bridge import SimpleBridge
from abl.evaluation import SemanticsMetric, SymbolMetric
from abl.learning import ABLModel, BasicNN
from abl.reasoning import KBBase, ReasonerBase
from abl.utils import print_log
from examples.mnist_add.datasets.get_mnist_add import get_mnist_add
from examples.models.nn import LeNet5

# Build logger
print_log("Abductive Learning on the MNIST Add example.", logger="current")

# Machine Learning Part
# Build necessary components for BasicNN
cls = LeNet5(num_classes=10)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(cls.parameters(), lr=0.001, betas=(0.9, 0.99))
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Build BasicNN
base_model = BasicNN(cls, criterion, optimizer, device)
# Build ABLModel
model = ABLModel(base_model)

# Logic Part
# Build knowledge base and reasoner
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")

# Datasets and Evaluation Metrics
# Get training and testing data
train_data = get_mnist_add(train=True, get_pseudo_label=True)
test_data = get_mnist_add(train=False, get_pseudo_label=True)
# Set up metrics
metric_list = [SymbolMetric(prefix="mnist_add"), SemanticsMetric(kb=kb, prefix="mnist_add")]

# Bridge Machine Learning and Logic Reasoning
bridge = SimpleBridge(model, reasoner, metric_list)

# Train and Test
bridge.train(train_data, loops=5, segment_size=10000)
bridge.test(test_data)


Training log would be similar to this:

.. code:: text

2023/11/29 23:14:17 - abl - INFO - Abductive Learning on the MNIST Add example.
2023/11/29 23:14:42 - abl - INFO - loop(train) [1/5] segment(train) [1/3] model loss is 1.86793
2023/11/29 23:14:44 - abl - INFO - loop(train) [1/5] segment(train) [2/3] model loss is 1.48877
2023/11/29 23:14:46 - abl - INFO - loop(train) [1/5] segment(train) [3/3] model loss is 1.26435
2023/11/29 23:14:46 - abl - INFO - Evaluation start: loop(val) [1]
2023/11/29 23:14:47 - abl - INFO - Evaluation ended, mnist_add/character_accuracy: 0.334 mnist_add/semantics_accuracy: 0.190
2023/11/29 23:14:49 - abl - INFO - loop(train) [2/5] segment(train) [1/3] model loss is 1.06395
2023/11/29 23:14:51 - abl - INFO - loop(train) [2/5] segment(train) [2/3] model loss is 0.78799
2023/11/29 23:14:53 - abl - INFO - loop(train) [2/5] segment(train) [3/3] model loss is 0.33641
2023/11/29 23:14:53 - abl - INFO - Evaluation start: loop(val) [2]
2023/11/29 23:14:54 - abl - INFO - Evaluation ended, mnist_add/character_accuracy: 0.963 mnist_add/semantics_accuracy: 0.926
...
2023/11/29 23:15:08 - abl - INFO - loop(train) [5/5] segment(train) [1/3] model loss is 0.04223
2023/11/29 23:15:10 - abl - INFO - loop(train) [5/5] segment(train) [2/3] model loss is 0.03444
2023/11/29 23:15:12 - abl - INFO - loop(train) [5/5] segment(train) [3/3] model loss is 0.03274
2023/11/29 23:15:12 - abl - INFO - Evaluation start: loop(val) [5]
2023/11/29 23:15:13 - abl - INFO - Evaluation ended, mnist_add/character_accuracy: 0.991 mnist_add/semantics_accuracy: 0.983
2023/11/29 23:15:13 - abl - INFO - Evaluation ended, mnist_add/character_accuracy: 0.985 mnist_add/semantics_accuracy: 0.970

+ 1
- 1
docs/index.rst View File

@@ -5,8 +5,8 @@
:caption: Overview

Overview/Abductive Learning
Overview/Quick Start
Overview/Installation
Overview/Quick Start

.. toctree::
:maxdepth: 1


+ 22
- 13
examples/mnist_add/datasets/get_mnist_add.py View File

@@ -1,6 +1,11 @@
import os

import torchvision
from torchvision.transforms import transforms

CURRENT_DIR = os.path.abspath(os.path.dirname(__file__))


def get_data(file, img_dataset, get_pseudo_label):
X = []
if get_pseudo_label:
@@ -8,32 +13,36 @@ def get_data(file, img_dataset, get_pseudo_label):
Y = []
with open(file) as f:
for line in f:
line = line.strip().split(' ')
line = line.strip().split(" ")
X.append([img_dataset[int(line[0])][0], img_dataset[int(line[1])][0]])
if get_pseudo_label:
Z.append([img_dataset[int(line[0])][1], img_dataset[int(line[1])][1]])
Y.append(int(line[2]))
if get_pseudo_label:
return X, Z, Y
else:
return X, None, Y

def get_mnist_add(train = True, get_pseudo_label = False):
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081, ))])
img_dataset = torchvision.datasets.MNIST(root='./datasets/', train=train, download=True, transform=transform)

def get_mnist_add(train=True, get_pseudo_label=False):
transform = transforms.Compose(
[transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
)
img_dataset = torchvision.datasets.MNIST(
root=CURRENT_DIR, train=train, download=True, transform=transform
)

if train:
file = './datasets/train_data.txt'
file = os.path.join(CURRENT_DIR, "train_data.txt")
else:
file = './datasets/test_data.txt'
file = os.path.join(CURRENT_DIR, "test_data.txt")
return get_data(file, img_dataset, get_pseudo_label)

if __name__ == "__main__":
train_X, train_Y = get_mnist_add(train = True)
test_X, test_Y = get_mnist_add(train = False)
train_X, train_Y = get_mnist_add(train=True)
test_X, test_Y = get_mnist_add(train=False)
print(len(train_X), len(test_X))
print(train_X[0][0].shape, train_X[0][1].shape, train_Y[0])

+ 38
- 54
examples/mnist_add/mnist_add_example.ipynb View File

@@ -8,18 +8,16 @@
"source": [
"import os.path as osp\n",
"\n",
"import torch.nn as nn\n",
"import torch\n",
"import torch.nn as nn\n",
"\n",
"from abl.reasoning import ReasonerBase, KBBase\n",
"\n",
"from abl.learning import BasicNN, ABLModel\n",
"from abl.bridge import SimpleBridge\n",
"from abl.evaluation import SymbolMetric, SemanticsMetric\n",
"from abl.evaluation import SemanticsMetric, SymbolMetric\n",
"from abl.learning import ABLModel, BasicNN\n",
"from abl.reasoning import KBBase, ReasonerBase\n",
"from abl.utils import ABLLogger, print_log\n",
"\n",
"from examples.models.nn import LeNet5\n",
"from examples.mnist_add.datasets.get_mnist_add import get_mnist_add"
"from examples.mnist_add.datasets.get_mnist_add import get_mnist_add\n",
"from examples.models.nn import LeNet5"
]
},
{
@@ -28,7 +26,7 @@
"metadata": {},
"outputs": [],
"source": [
"# Initialize logger\n",
"# Build logger\n",
"print_log(\"Abductive Learning on the MNIST Add example.\", logger=\"current\")\n",
"\n",
"# Retrieve the directory of the Log file and define the directory for saving the model weights.\n",
@@ -36,31 +34,6 @@
"weights_dir = osp.join(log_dir, \"weights\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Logic Part"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Initialize knowledge base and reasoner\n",
"class add_KB(KBBase):\n",
" def logic_forward(self, nums):\n",
" return sum(nums)\n",
"\n",
"kb = add_KB(pseudo_label_list=list(range(10)))\n",
"\n",
"# kb = prolog_KB(pseudo_label_list=list(range(10)), pl_file='datasets/mnist_add/add.pl')\n",
"reasoner = ReasonerBase(kb, dist_func=\"confidence\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
@@ -75,11 +48,11 @@
"metadata": {},
"outputs": [],
"source": [
"# Initialize necessary component for machine learning part\n",
"cls = LeNet5(num_classes=len(kb.pseudo_label_list))\n",
"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
"# Build necessary components for BasicNN\n",
"cls = LeNet5(num_classes=10)\n",
"criterion = nn.CrossEntropyLoss()\n",
"optimizer = torch.optim.Adam(cls.parameters(), lr=0.001, betas=(0.9, 0.99))"
"optimizer = torch.optim.Adam(cls.parameters(), lr=0.001, betas=(0.9, 0.99))\n",
"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")"
]
},
{
@@ -88,7 +61,7 @@
"metadata": {},
"outputs": [],
"source": [
"# Initialize BasicNN\n",
"# Build BasicNN\n",
"# The function of BasicNN is to wrap NN models into the form of an sklearn estimator\n",
"base_model = BasicNN(\n",
" cls,\n",
@@ -100,32 +73,23 @@
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Use ABL model to join two parts"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Initialize ABL model\n",
"# # Build ABLModel\n",
"# The main function of the ABL model is to serialize data and \n",
"# provide a unified interface for different machine learning models\n",
"model = ABLModel(base_model)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Metric"
"### Logic Part"
]
},
{
@@ -134,8 +98,18 @@
"metadata": {},
"outputs": [],
"source": [
"# Add metric\n",
"metric = [SymbolMetric(prefix=\"mnist_add\"), SemanticsMetric(kb=kb, prefix=\"mnist_add\")]"
"# Build knowledge base and reasoner\n",
"class AddKB(KBBase):\n",
" def __init__(self, pseudo_label_list):\n",
" super().__init__(pseudo_label_list)\n",
"\n",
" # Implement the deduction function\n",
" def logic_forward(self, nums):\n",
" return sum(nums)\n",
"\n",
"\n",
"kb = AddKB(pseudo_label_list=list(range(10)))\n",
"reasoner = ReasonerBase(kb, dist_func=\"confidence\")"
]
},
{
@@ -143,7 +117,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dataset"
"### Datasets and Evaluation Metrics"
]
},
{
@@ -157,6 +131,16 @@
"test_data = get_mnist_add(train=False, get_pseudo_label=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Set up metrics\n",
"metric_list = [SymbolMetric(prefix=\"mnist_add\"), SemanticsMetric(kb=kb, prefix=\"mnist_add\")]"
]
},
{
"attachments": {},
"cell_type": "markdown",
@@ -171,7 +155,7 @@
"metadata": {},
"outputs": [],
"source": [
"bridge = SimpleBridge(model, reasoner, metric)"
"bridge = SimpleBridge(model, reasoner, metric_list)"
]
},
{


Loading…
Cancel
Save