Browse Source

Merge branch 'Dev' into main

main
troyyyyy 1 year ago
parent
commit
fba8530e5b
13 changed files with 154 additions and 568 deletions
  1. +0
    -141
      ablkit/data/data_converter.py
  2. +0
    -211
      ablkit/learning/model_converter.py
  3. +1
    -1
      docs/Examples/HWF.rst
  4. +42
    -15
      docs/Examples/MNISTAdd.rst
  5. +2
    -2
      docs/Intro/Basics.rst
  6. +5
    -2
      examples/hwf/README.md
  7. +8
    -3
      examples/hwf/hwf.ipynb
  8. +11
    -12
      examples/hwf/main.py
  9. +35
    -7
      examples/mnist_add/README.md
  10. +8
    -5
      examples/mnist_add/main.py
  11. +0
    -160
      examples/mnist_add/main_with_model_converter.py
  12. +41
    -8
      examples/mnist_add/mnist_add.ipynb
  13. +1
    -1
      pyproject.toml

+ 0
- 141
ablkit/data/data_converter.py View File

@@ -1,141 +0,0 @@
from typing import Any, Tuple

from ablkit.utils import tab_data_to_tuple
from .structures.list_data import ListData
from lambdaLearn.Base.TabularMixin import TabularMixin


class DataConverter:
"""
This class provides functionality to convert LambdaLearn data to ABLkit data.
"""

def __init__(self) -> None:
pass

def convert_lambdalearn_to_tuple(
self, dataset: TabularMixin, reasoning_result: Any
) -> Tuple[Tuple, Tuple, Tuple, Tuple]:
"""
Convert a lambdalearn dataset to a tuple of tuples (label_data, train_data, valid_data, test_data), # noqa: E501
each containing (data, label, reasoning_result).

Parameters
----------
dataset : TabularMixin
The LambdaLearn dataset to be converted.
reasoning_result : Any
The reasoning result of the dataset.
Returns
-------
Tuple[Tuple, Tuple, Tuple, Tuple]
A tuple of (label_data, train_data, valid_data, test_data), where each element is
a tuple of (data, label, reasoning_result).
"""

if not isinstance(dataset, TabularMixin):
raise NotImplementedError(
"Only support converting the datasets that are instances of TabularMixin. "
+ "Please refer to the documentation and manually convert the dataset into a tuple."
)

label_data = tab_data_to_tuple(
dataset.labeled_X, dataset.labeled_y, reasoning_result=reasoning_result
)
train_data = tab_data_to_tuple(
dataset.unlabeled_X, dataset.unlabeled_y, reasoning_result=reasoning_result
)
valid_data = tab_data_to_tuple(
dataset.valid_X, dataset.valid_y, reasoning_result=reasoning_result
)
test_data = tab_data_to_tuple(
dataset.test_X, dataset.test_y, reasoning_result=reasoning_result
)

return label_data, train_data, valid_data, test_data

def convert_lambdalearn_to_listdata(
self, dataset: TabularMixin, reasoning_result: Any
) -> Tuple[ListData, ListData, ListData, ListData]:
"""
Convert a lambdalearn dataset to a tuple of ListData
(label_data_examples, train_data_examples, valid_data_examples, test_data_examples).

Parameters
----------
dataset : TabularMixin
The LambdaLearn dataset to be converted.
reasoning_result : Any
The reasoning result of the dataset.
Returns
-------
Tuple[ListData, ListData, ListData, ListData]
A tuple of ListData (label_data_examples, train_data_examples, valid_data_examples, test_data_examples) # noqa: E501
"""

if not isinstance(dataset, TabularMixin):
raise NotImplementedError(
"Only support converting the datasets that are instances of TabularMixin. "
+ "Please refer to the documentation and manually convert the dataset "
+ "into a ListData."
)

label_data, train_data, valid_data, test_data = self.convert_lambdalearn_to_tuple(
dataset, reasoning_result
)

if label_data is not None:
X, gt_pseudo_label, Y = label_data
label_data_examples = ListData(X=X, gt_pseudo_label=gt_pseudo_label, Y=Y)
if train_data is not None:
X, gt_pseudo_label, Y = train_data
train_data_examples = ListData(X=X, gt_pseudo_label=gt_pseudo_label, Y=Y)
if valid_data is not None:
X, gt_pseudo_label, Y = valid_data
valid_data_examples = ListData(X=X, gt_pseudo_label=gt_pseudo_label, Y=Y)
if test_data is not None:
X, gt_pseudo_label, Y = test_data
test_data_examples = ListData(X=X, gt_pseudo_label=gt_pseudo_label, Y=Y)

return label_data_examples, train_data_examples, valid_data_examples, test_data_examples


if __name__ == "__main__":
from lambdaLearn.Dataset.Tabular.BreastCancer import BreastCancer

breast_dataset = BreastCancer(labeled_size=0.1, stratified=True, shuffle=True)
dataconverter = DataConverter()

label_data, train_data, valid_data, test_data = dataconverter.convert_lambdalearn_to_tuple(
breast_dataset, 0
)
print(
type(label_data).__name__,
type(train_data).__name__,
type(valid_data).__name__,
type(test_data).__name__,
)
print(len(label_data))
print(len(label_data[0]), len(label_data[1]), len(label_data[2]))
print(label_data[0][0], label_data[1][0], label_data[2][0])
print()

(
label_data_examples,
train_data_examples,
valid_data_examples,
test_data_examples,
) = dataconverter.convert_lambdalearn_to_listdata(breast_dataset, 0)
print(
type(label_data_examples).__name__,
type(train_data_examples).__name__,
type(valid_data_examples).__name__,
type(test_data_examples).__name__,
)
print(
len(label_data_examples.X),
len(label_data_examples.gt_pseudo_label),
len(label_data_examples.Y),
)
label_data_example = label_data_examples[0]
print(label_data_example.X, label_data_example.gt_pseudo_label, label_data_example.Y)

+ 0
- 211
ablkit/learning/model_converter.py View File

@@ -1,211 +0,0 @@
import torch
import copy
from typing import Any, Callable, List, Optional

from .abl_model import ABLModel
from .basic_nn import BasicNN
from lambdaLearn.Base.DeepModelMixin import DeepModelMixin


class ModelConverter:
"""
This class provides functionality to convert LambdaLearn models to ABLkit models.
"""

def __init__(self) -> None:
pass

def convert_lambdalearn_to_ablmodel(
self,
lambdalearn_model,
loss_fn: torch.nn.Module,
optimizer_dict: dict,
scheduler_dict: Optional[dict] = None,
device: Optional[torch.device] = None,
batch_size: int = 32,
num_epochs: int = 1,
stop_loss: Optional[float] = 0.0001,
num_workers: int = 0,
save_interval: Optional[int] = None,
save_dir: Optional[str] = None,
train_transform: Callable[..., Any] = None,
test_transform: Callable[..., Any] = None,
collate_fn: Callable[[List[Any]], Any] = None,
):
"""
Convert a lambdalearn model to an ABLModel. If the lambdalearn model is an instance of
DeepModelMixin, its network will be used as the model of BasicNN. Otherwise, the lambdalearn
model should implement ``fit`` and ``predict`` methods.

Parameters
----------
lambdalearn_model : Union[DeepModelMixin, Any]
The LambdaLearn model to be converted.
loss_fn : torch.nn.Module
The loss function used for training.
optimizer_dict : dict
The dict contains necessary parameters to construct a optimizer used for training.
The optimizer class is specified by the ``optimizer`` key.
scheduler_dict : dict, optional
The dict contains necessary parameters to construct a learning rate scheduler used
for training, which will be called at the end of each run of the ``fit`` method.
The scheduler class is specified by the ``scheduler`` key. It should implement the
``step`` method. Defaults to None.
device : torch.device, optional
The device on which the model will be trained or used for prediction,
Defaults to torch.device("cpu").
batch_size : int, optional
The batch size used for training. Defaults to 32.
num_epochs : int, optional
The number of epochs used for training. Defaults to 1.
stop_loss : float, optional
The loss value at which to stop training. Defaults to 0.0001.
num_workers : int
The number of workers used for loading data. Defaults to 0.
save_interval : int, optional
The model will be saved every ``save_interval`` epoch during training. Defaults to None.
save_dir : str, optional
The directory in which to save the model during training. Defaults to None.
train_transform : Callable[..., Any], optional
A function/transform that takes an object and returns a transformed version used
in the `fit` and `train_epoch` methods. Defaults to None.
test_transform : Callable[..., Any], optional
A function/transform that takes an object and returns a transformed version in the
`predict`, `predict_proba` and `score` methods. Defaults to None.
collate_fn : Callable[[List[T]], Any], optional
The function used to collate data. Defaults to None.

Returns
-------
ABLModel
The converted ABLModel instance.
"""
if isinstance(lambdalearn_model, DeepModelMixin):
base_model = self.convert_lambdalearn_to_basicnn(
lambdalearn_model,
loss_fn,
optimizer_dict,
scheduler_dict,
device,
batch_size,
num_epochs,
stop_loss,
num_workers,
save_interval,
save_dir,
train_transform,
test_transform,
collate_fn,
)
return ABLModel(base_model)

if not (hasattr(lambdalearn_model, "fit") and hasattr(lambdalearn_model, "predict")):
raise NotImplementedError(
"The lambdalearn_model should be an instance of DeepModelMixin, or implement "
+ "fit and predict methods."
)

return ABLModel(lambdalearn_model)

def convert_lambdalearn_to_basicnn(
self,
lambdalearn_model: DeepModelMixin,
loss_fn: torch.nn.Module,
optimizer_dict: dict,
scheduler_dict: Optional[dict] = None,
device: Optional[torch.device] = None,
batch_size: int = 32,
num_epochs: int = 1,
stop_loss: Optional[float] = 0.0001,
num_workers: int = 0,
save_interval: Optional[int] = None,
save_dir: Optional[str] = None,
train_transform: Callable[..., Any] = None,
test_transform: Callable[..., Any] = None,
collate_fn: Callable[[List[Any]], Any] = None,
):
"""
Convert a lambdalearn model to a BasicNN. If the lambdalearn model is an instance of
DeepModelMixin, its network will be used as the model of BasicNN.

Parameters
----------
lambdalearn_model : Union[DeepModelMixin, Any]
The LambdaLearn model to be converted.
loss_fn : torch.nn.Module
The loss function used for training.
optimizer_dict : dict
The dict contains necessary parameters to construct a optimizer used for training.
scheduler_dict : dict, optional
The dict contains necessary parameters to construct a learning rate scheduler used
for training, which will be called at the end of each run of the ``fit`` method.
The scheduler class is specified by the ``scheduler`` key. It should implement the
``step`` method. Defaults to None.
device : torch.device, optional
The device on which the model will be trained or used for prediction,
Defaults to torch.device("cpu").
batch_size : int, optional
The batch size used for training. Defaults to 32.
num_epochs : int, optional
The number of epochs used for training. Defaults to 1.
stop_loss : float, optional
The loss value at which to stop training. Defaults to 0.0001.
num_workers : int
The number of workers used for loading data. Defaults to 0.
save_interval : int, optional
The model will be saved every ``save_interval`` epoch during training. Defaults to None.
save_dir : str, optional
The directory in which to save the model during training. Defaults to None.
train_transform : Callable[..., Any], optional
A function/transform that takes an object and returns a transformed version used
in the `fit` and `train_epoch` methods. Defaults to None.
test_transform : Callable[..., Any], optional
A function/transform that takes an object and returns a transformed version in the
`predict`, `predict_proba` and `score` methods. Defaults to None.
collate_fn : Callable[[List[T]], Any], optional
The function used to collate data. Defaults to None.

Returns
-------
BasicNN
The converted BasicNN instance.
"""
if isinstance(lambdalearn_model, DeepModelMixin):
if not isinstance(lambdalearn_model.network, torch.nn.Module):
raise NotImplementedError(
"Expected lambdalearn_model.network to be a torch.nn.Module, "
+ f"but got {type(lambdalearn_model.network)}"
)
# Only use the network part and device of the lambdalearn model
network = copy.deepcopy(lambdalearn_model.network)
optimizer_class = optimizer_dict["optimizer"]
optimizer_dict.pop("optimizer")
optimizer = optimizer_class(network.parameters(), **optimizer_dict)
if scheduler_dict is not None:
scheduler_class = scheduler_dict["scheduler"]
scheduler_dict.pop("scheduler")
scheduler = scheduler_class(optimizer, **scheduler_dict)
else:
scheduler = None
device = lambdalearn_model.device if device is None else device
base_model = BasicNN(
model=network,
loss_fn=loss_fn,
optimizer=optimizer,
scheduler=scheduler,
device=device,
batch_size=batch_size,
num_epochs=num_epochs,
stop_loss=stop_loss,
num_workers=num_workers,
save_interval=save_interval,
save_dir=save_dir,
train_transform=train_transform,
test_transform=test_transform,
collate_fn=collate_fn,
)
return base_model
else:
raise NotImplementedError(
"The lambdalearn_model should be an instance of DeepModelMixin."
)

+ 1
- 1
docs/Examples/HWF.rst View File

@@ -192,7 +192,7 @@ sklearn-style interface.
num_epochs=3,
)

``BasicNN`` offers methods like ``predict`` and ``predict_prob``, which
``BasicNN`` offers methods like ``predict`` and ``predict_proba``, which
are used to predict the class index and the probabilities of each class
for images. As shown below:



+ 42
- 15
docs/Examples/MNISTAdd.rst View File

@@ -163,7 +163,7 @@ model with a sklearn-style interface.
num_epochs=1,
)

``BasicNN`` offers methods like ``predict`` and ``predict_prob``, which
``BasicNN`` offers methods like ``predict`` and ``predict_proba``, which
are used to predict the class index and the probabilities of each class
for images. As shown below:

@@ -381,17 +381,44 @@ We present the results of ABL as follows, which include the reasoning accuracy (
- `DeepProbLog <https://github.com/ML-KULeuven/deepproblog>`_: An extension of ProbLog by introducing neural predicates in Probabilistic Logic Programming;
- `DeepStochLog <https://github.com/ML-KULeuven/deepstochlog>`_: A neural-symbolic framework based on stochastic logic program.

.. table::
:class: centered

+--------------+----------+------------------------------+
| Method | Accuracy | Time to achieve the Acc. (s) |
+==============+==========+==============================+
| NeurASP | 0.964 | 354 |
+--------------+----------+------------------------------+
| DeepProbLog | 0.965 | 1965 |
+--------------+----------+------------------------------+
| DeepStochLog | 0.975 | 727 |
+--------------+----------+------------------------------+
| ABL | 0.980 | 42 |
+--------------+----------+------------------------------+
.. raw:: html

<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;margin-bottom:20px;}
.tg td, .tg th {border:1px solid #ddd;padding:8px 22px;text-align:center;}
.tg th {background-color:#f5f5f5;color:#333333;}
.tg tr:nth-child(even) {background-color:#f9f9f9;}
.tg tr:nth-child(odd) {background-color:#ffffff;}
</style>

<table class="tg" style="margin-left: auto; margin-right: auto;">
<thead>
<tr>
<th>Method</th>
<th>Accuracy</th>
<th>Time to achieve the Acc. (s)</th>
</tr>
</thead>
<tbody>
<tr>
<td>NeurASP</td>
<td>96.2</td>
<td>966</td>
</tr>
<tr>
<td>DeepProbLog</td>
<td>97.1</td>
<td>2045</td>
</tr>
<tr>
<td>DeepStochLog</td>
<td>97.5</td>
<td>257</td>
</tr>
<tr>
<td>ABL</td>
<td><span style="font-weight:bold">98.1</span></td>
<td><span style="font-weight:bold">47</span></td>
</tr>
</tbody>
</table>

+ 2
- 2
docs/Intro/Basics.rst View File

@@ -28,8 +28,8 @@ such as ``SymbolAccuracy`` and ``ReasoningMetric`` (both specialized metrics
inherited from the ``BaseMetric`` class), for evaluating performance from a
data perspective.

:blue-bold:`Learning` part focuses on the construction, training, and
prediction of machine learning models. The ``ABLModel`` class is the
:blue-bold:`Learning` part focuses on creating, training, and utilizing
machine learning models. The ``ABLModel`` class is the
central class that encapsulates the machine learning model. This class is
compatible with various frameworks, including those based on scikit-learn
or PyTorch neural networks constructed by the ``BasicNN`` class.


+ 5
- 2
examples/hwf/README.md View File

@@ -12,7 +12,8 @@ python main.py
## Usage

```bash
usage: main.py [-h] [--no-cuda] [--epochs EPOCHS] [--lr LR]
usage: main.py [-h] [--no-cuda] [--epochs EPOCHS]
[--label_smoothing LABEL_SMOOTHING] [--lr LR]
[--batch-size BATCH_SIZE]
[--loops LOOPS] [--segment_size SEGMENT_SIZE]
[--save_interval SAVE_INTERVAL] [--max-revision MAX_REVISION]
@@ -26,6 +27,8 @@ optional arguments:
--no-cuda disables CUDA training
--epochs EPOCHS number of epochs in each learning loop iteration
(default : 1)
--label_smoothing LABEL_SMOOTHING
label smoothing in cross entropy loss (default : 0.2)
--lr LR base model learning rate (default : 0.001)
--batch-size BATCH_SIZE
base model batch size (default : 32)
@@ -100,7 +103,7 @@ We present the results of ABL as follows, which include the reasoning accuracy (
<td><span style="font-weight:bold">89.7</span></td>
<td><span style="font-weight:bold">96.5</span></td>
<td><span style="font-weight:bold">97.2</span></td>
<td><span style="font-weight:bold">98.6</span></td>
<td><span style="font-weight:bold">99.2</span></td>
<td><span style="font-weight:bold">77.3</span></td>
</tr>
</tbody>


+ 8
- 3
examples/hwf/hwf.ipynb View File

@@ -166,7 +166,7 @@
"source": [
"# class of symbol may be one of ['1', ..., '9', '+', '-', '*', '/'], total of 13 classes\n",
"cls = SymbolNet(num_classes=13, image_size=(45, 45, 1))\n",
"loss_fn = nn.CrossEntropyLoss()\n",
"loss_fn = nn.CrossEntropyLoss(label_smoothing=0.2)\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",
@@ -184,7 +184,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"`BasicNN` offers methods like `predict` and `predict_prob`, which are used to predict the class index and the probabilities of each class for images. As shown below:"
"`BasicNN` offers methods like `predict` and `predict_proba`, which are used to predict the class index and the probabilities of each class for images. As shown below:"
]
},
{
@@ -503,13 +503,18 @@
" <td><span style=\"font-weight:bold\">89.7</span></td>\n",
" <td><span style=\"font-weight:bold\">96.5</span></td>\n",
" <td><span style=\"font-weight:bold\">97.2</span></td>\n",
" <td><span style=\"font-weight:bold\">98.6</span></td>\n",
" <td><span style=\"font-weight:bold\">99.2</span></td>\n",
" <td><span style=\"font-weight:bold\">77.3</span></td>\n",
" </tr>\n",
"</tbody>\n",
"</table>\n",
"<p style=\"font-size: 13px;\">* timeout: need more than 1 hour to execute</p>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {


+ 11
- 12
examples/hwf/main.py View File

@@ -77,6 +77,12 @@ def main():
default=3,
help="number of epochs in each learning loop iteration (default : 3)",
)
parser.add_argument(
"--label-smoothing",
type=float,
default=0.2,
help="label smoothing in cross entropy loss (default : 0.2)"
)
parser.add_argument(
"--lr", type=float, default=1e-3, help="base model learning rate (default : 0.001)"
)
@@ -84,17 +90,14 @@ def main():
"--batch-size", type=int, default=128, help="base model batch size (default : 128)"
)
parser.add_argument(
"--loops", type=int, default=5, help="number of loop iterations (default : 5)"
"--loops", type=int, default=3, help="number of loop iterations (default : 3)"
)
parser.add_argument(
"--segment_size", type=int, default=1000, help="segment size (default : 1000)"
)
parser.add_argument("--save_interval", type=int, default=1, help="save interval (default : 1)")
parser.add_argument(
"--max-revision",
type=int,
default=-1,
help="maximum revision in reasoner (default : -1)",
"--max-revision", type=int, default=-1, help="maximum revision in reasoner (default : -1)"
)
parser.add_argument(
"--require-more-revision",
@@ -128,19 +131,14 @@ def main():

# Build necessary components for BasicNN
cls = SymbolNet(num_classes=13, image_size=(45, 45, 1))
loss_fn = nn.CrossEntropyLoss()
loss_fn = nn.CrossEntropyLoss(label_smoothing=args.label_smoothing)
optimizer = torch.optim.Adam(cls.parameters(), lr=args.lr)
use_cuda = not args.no_cuda and torch.cuda.is_available()
device = torch.device("cuda" if use_cuda else "cpu")

# Build BasicNN
base_model = BasicNN(
cls,
loss_fn,
optimizer,
device=device,
batch_size=args.batch_size,
num_epochs=args.epochs,
cls, loss_fn, optimizer, device=device, batch_size=args.batch_size, num_epochs=args.epochs,
)

# Build ABLModel
@@ -175,6 +173,7 @@ def main():
# Train and Test
bridge.train(
train_data,
val_data=test_data,
loops=args.loops,
segment_size=args.segment_size,
save_interval=args.save_interval,


+ 35
- 7
examples/mnist_add/README.md View File

@@ -12,7 +12,8 @@ python main.py
## Usage

```bash
usage: main.py [-h] [--no-cuda] [--epochs EPOCHS] [--lr LR]
usage: main.py [-h] [--no-cuda] [--epochs EPOCHS]
[--label_smoothing LABEL_SMOOTHING] [--lr LR]
[--alpha ALPHA] [--batch-size BATCH_SIZE]
[--loops LOOPS] [--segment_size SEGMENT_SIZE]
[--save_interval SAVE_INTERVAL] [--max-revision MAX_REVISION]
@@ -26,6 +27,8 @@ optional arguments:
--no-cuda disables CUDA training
--epochs EPOCHS number of epochs in each learning loop iteration
(default : 1)
--label_smoothing LABEL_SMOOTHING
label smoothing in cross entropy loss (default : 0.2)
--lr LR base model learning rate (default : 0.001)
--alpha ALPHA alpha in RMSprop (default : 0.9)
--batch-size BATCH_SIZE
@@ -53,9 +56,34 @@ We present the results of ABL as follows, which include the reasoning accuracy (
- [**DeepProbLog**](https://github.com/ML-KULeuven/deepproblog): An extension of ProbLog by introducing neural predicates in Probabilistic Logic Programming;
- [**DeepStochLog**](https://github.com/ML-KULeuven/deepstochlog): A neural-symbolic framework based on stochastic logic program.

| Method | Accuracy | Time to achieve the Acc. (s) |
| :----------: | :------: | :--------------------------: |
| NeurASP | 0.964 | 354 |
| DeepProbLog | 0.965 | 1965 |
| DeepStochLog | 0.975 | 727 |
| ABL | 0.980 | 42 |
<table class="tg" style="margin-left: auto; margin-right: auto;">
<thead>
<tr>
<th>Method</th>
<th>Accuracy</th>
<th>Time to achieve the Acc. (s)</th>
</tr>
</thead>
<tbody>
<tr>
<td>NeurASP</td>
<td>96.2</td>
<td>966</td>
</tr>
<tr>
<td>DeepProbLog</td>
<td>97.1</td>
<td>2045</td>
</tr>
<tr>
<td>DeepStochLog</td>
<td>97.5</td>
<td>257</td>
</tr>
<tr>
<td>ABL</td>
<td><span style="font-weight:bold">98.1</span></td>
<td><span style="font-weight:bold">47</span></td>
</tr>
</tbody>
</table>

+ 8
- 5
examples/mnist_add/main.py View File

@@ -42,6 +42,12 @@ def main():
default=1,
help="number of epochs in each learning loop iteration (default : 1)",
)
parser.add_argument(
"--label-smoothing",
type=float,
default=0.2,
help="label smoothing in cross entropy loss (default : 0.2)",
)
parser.add_argument(
"--lr", type=float, default=3e-4, help="base model learning rate (default : 0.0003)"
)
@@ -57,10 +63,7 @@ def main():
)
parser.add_argument("--save_interval", type=int, default=1, help="save interval (default : 1)")
parser.add_argument(
"--max-revision",
type=int,
default=-1,
help="maximum revision in reasoner (default : -1)",
"--max-revision", type=int, default=-1, help="maximum revision in reasoner (default : -1)"
)
parser.add_argument(
"--require-more-revision",
@@ -91,7 +94,7 @@ def main():

# Build necessary components for BasicNN
cls = LeNet5(num_classes=10)
loss_fn = nn.CrossEntropyLoss(label_smoothing=0.2)
loss_fn = nn.CrossEntropyLoss(label_smoothing=args.label_smoothing)
optimizer = 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")


+ 0
- 160
examples/mnist_add/main_with_model_converter.py View File

@@ -1,160 +0,0 @@
import argparse
import os.path as osp

from torch import nn
from torch.optim import RMSprop, lr_scheduler

from lambdaLearn.Algorithm.AbductiveLearning.bridge import SimpleBridge
from lambdaLearn.Algorithm.AbductiveLearning.data.evaluation import ReasoningMetric, SymbolAccuracy
from lambdaLearn.Algorithm.AbductiveLearning.learning import ABLModel
from lambdaLearn.Algorithm.AbductiveLearning.learning.model_converter import ModelConverter
from lambdaLearn.Algorithm.AbductiveLearning.reasoning import GroundKB, KBBase, PrologKB, Reasoner
from lambdaLearn.Algorithm.AbductiveLearning.utils import ABLLogger, print_log
from lambdaLearn.Algorithm.SemiSupervised.Classification.FixMatch import FixMatch

from datasets import get_dataset
from models.nn import LeNet5


class AddKB(KBBase):
def __init__(self, pseudo_label_list=list(range(10))):
super().__init__(pseudo_label_list)

def logic_forward(self, nums):
return sum(nums)


class AddGroundKB(GroundKB):
def __init__(self, pseudo_label_list=list(range(10)), GKB_len_list=[2]):
super().__init__(pseudo_label_list, GKB_len_list)

def logic_forward(self, nums):
return sum(nums)


def main():
parser = argparse.ArgumentParser(description="MNIST Addition example")
parser.add_argument(
"--no-cuda", action="store_true", default=False, help="disables CUDA training"
)
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=3e-4, help="base model learning rate (default : 0.0003)"
)
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="base model batch size (default : 32)"
)
parser.add_argument(
"--loops", type=int, default=2, help="number of loop iterations (default : 2)"
)
parser.add_argument(
"--segment_size", type=int, default=0.01, help="segment size (default : 0.01)"
)
parser.add_argument("--save_interval", type=int, default=1, help="save interval (default : 1)")
parser.add_argument(
"--max-revision",
type=int,
default=-1,
help="maximum revision in reasoner (default : -1)",
)
parser.add_argument(
"--require-more-revision",
type=int,
default=0,
help="require more revision in reasoner (default : 0)",
)
kb_type = parser.add_mutually_exclusive_group()
kb_type.add_argument(
"--prolog", action="store_true", default=False, help="use PrologKB (default: False)"
)
kb_type.add_argument(
"--ground", action="store_true", default=False, help="use GroundKB (default: False)"
)

args = parser.parse_args()

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

# -- Working with Data ------------------------------
print_log("Working with Data.", logger="current")
train_data = get_dataset(train=True, get_pseudo_label=True)
test_data = get_dataset(train=False, get_pseudo_label=True)

# -- Building the Learning Part ---------------------
print_log("Building the Learning Part.", logger="current")

# Build necessary components for BasicNN
model = FixMatch(
network=LeNet5(),
threshold=0.95,
lambda_u=1.0,
mu=7,
T=0.5,
epoch=1,
num_it_epoch=2**20,
num_it_total=2**20,
device="cuda",
)

loss_fn = nn.CrossEntropyLoss(label_smoothing=0.2)
optimizer_dict = dict(optimizer=RMSprop, lr=0.0003, alpha=0.9)
scheduler_dict = dict(
scheduler=lr_scheduler.OneCycleLR, max_lr=0.0003, pct_start=0.15, total_steps=200
)

converter = ModelConverter()
base_model = converter.convert_lambdalearn_to_basicnn(
model, loss_fn=loss_fn, optimizer_dict=optimizer_dict, scheduler_dict=scheduler_dict
)

# Build ABLModel
model = ABLModel(base_model)

# -- Building the Reasoning Part --------------------
print_log("Building the Reasoning Part.", logger="current")

# Build knowledge base
if args.prolog:
kb = PrologKB(pseudo_label_list=list(range(10)), pl_file="add.pl")
elif args.ground:
kb = AddGroundKB()
else:
kb = AddKB()

# Create reasoner
reasoner = Reasoner(
kb, max_revision=args.max_revision, require_more_revision=args.require_more_revision
)

# -- Building Evaluation Metrics --------------------
print_log("Building Evaluation Metrics.", logger="current")
metric_list = [SymbolAccuracy(prefix="mnist_add"), ReasoningMetric(kb=kb, prefix="mnist_add")]

# -- Bridging Learning and Reasoning ----------------
print_log("Bridge Learning and Reasoning.", logger="current")
bridge = SimpleBridge(model, reasoner, metric_list)

# 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")

# Train and Test
bridge.train(
train_data,
loops=args.loops,
segment_size=args.segment_size,
save_interval=args.save_interval,
save_dir=weights_dir,
)
bridge.test(test_data)


if __name__ == "__main__":
main()

+ 41
- 8
examples/mnist_add/mnist_add.ipynb View File

@@ -208,7 +208,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"`BasicNN` offers methods like `predict` and `predict_prob`, which are used to predict the class index and the probabilities of each class for images. As shown below:"
"`BasicNN` offers methods like `predict` and `predict_proba`, which are used to predict the class index and the probabilities of each class for images. As shown below:"
]
},
{
@@ -479,12 +479,45 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"| Method | Accuracy | Time to achieve the Acc. (s) |\n",
"| :----------: | :------: | :--------------------------: |\n",
"| NeurASP | 0.964 | 354 |\n",
"| DeepProbLog | 0.965 | 1965 |\n",
"| DeepStochLog | 0.975 | 727 |\n",
"| ABL | 0.980 | 42 |"
"<style type=\"text/css\">\n",
".tg {border-collapse:collapse;border-spacing:0;margin-bottom:20px;}\n",
".tg td, .tg th {border:1px solid #ddd;padding:10px 15px;text-align:center;}\n",
".tg th {background-color:#f5f5f5;color:#333333;}\n",
".tg tr:nth-child(even) {background-color:#f9f9f9;}\n",
".tg tr:nth-child(odd) {background-color:#ffffff;}\n",
"</style>\n",
"\n",
"<table class=\"tg\" style=\"margin-left: auto; margin-right: auto;\">\n",
"<thead>\n",
"<tr>\n",
" <th>Method</th>\n",
" <th>Accuracy</th>\n",
" <th>Time to achieve the Acc. (s)</th>\n",
"</tr>\n",
"</thead>\n",
"<tbody>\n",
"<tr>\n",
" <td>NeurASP</td>\n",
" <td>96.2</td>\n",
" <td>966</td>\n",
"</tr>\n",
"<tr>\n",
" <td>DeepProbLog</td>\n",
" <td>97.1</td>\n",
" <td>2045</td>\n",
"</tr>\n",
"<tr>\n",
" <td>DeepStochLog</td>\n",
" <td>97.5</td>\n",
" <td>257</td>\n",
"</tr>\n",
"<tr>\n",
" <td>ABL</td>\n",
" <td><span style=\"font-weight:bold\">98.1</span></td>\n",
"<td><span style=\"font-weight:bold\">47</span></td>\n",
"</tr>\n",
"</tbody>\n",
"</table>\n"
]
}
],
@@ -504,7 +537,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.13"
"version": "3.8.18"
},
"orig_nbformat": 4,
"vscode": {


+ 1
- 1
pyproject.toml View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "ablkit"
version = "0.2.0"
version = "0.2.1"
authors = [
{ name="LAMDA 2024", email = "abductivelearning@gmail.com" },
]


Loading…
Cancel
Save