...
 
Commits (2)
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
TORCH_HOME: "$CI_PROJECT_DIR/.cache/torch"
SECML_HOME_DIR: "$CI_PROJECT_DIR/secml-data"
TOX_WORKDIR: "$CI_PROJECT_DIR/.tox"
TOX_TESTENV_PASSENV: "PIP_CACHE_DIR SECML_HOME_DIR"
TOX_TESTENV_PASSENV: "PIP_CACHE_DIR TORCH_HOME SECML_HOME_DIR"
TOX_USEDEVELOP: "False"
stages:
......@@ -15,13 +16,9 @@ stages:
.test-cache:
cache: &test-cache
paths:
- "$CI_PROJECT_DIR/.cache/pip"
- "$CI_PROJECT_DIR/secml-data"
.test-cache-py2:
cache: &test-cache-py2
key: "test-cache-py2"
<<: *test-cache
- $PIP_CACHE_DIR
- $TORCH_HOME
- $SECML_HOME_DIR
.test-cache-py35:
cache: &test-cache-py35
......@@ -128,12 +125,6 @@ package:docs:
variables:
- $SKIP_TESTS
test:py2:min:
extends: .test
image: python:2
script: tox -e py2-min
cache: *test-cache-py2
test:py35:latest:
extends: .test
image: python:3.5
......@@ -159,6 +150,25 @@ test:py37:min:
cache: *test-cache-py37
.test:notebooks:
extends: .test
needs: ["package:docs"] # Use [] after gitlab 12.6
only:
changes:
- src/secml/**/*
- tutorials/**/*
- .gitlab-ci.yml
- requirements.txt
- setup.py
- tox.ini
test:notebooks:py35:latest:
extends: .test:notebooks
image: python:3.5
script: tox -e latest-notebooks
cache: *test-cache-py35
.test:install:
stage: test
needs: ["package"]
......@@ -206,14 +216,7 @@ test:py37:min:
after_script:
- python -c "import secml"
- python -c "from secml.ml.classifiers import CClassifierPyTorch"
- python -c "from secml.ml.classifiers import CModelCleverhans"
test:install:whl:py2:
extends: .test:install:whl
image: python:2
cache:
<<: *test-cache-py2
policy: pull
- python -c "from secml.adv.attacks.evasion import CAttackEvasionCleverhans"
test:install:whl:py35:
extends: .test:install:whl
......@@ -236,13 +239,6 @@ test:install:whl:py37:
<<: *test-cache-py37
policy: pull
test:install:whl:extras:py2:
extends: .test:install:whl:extras
image: python:2
cache:
<<: *test-cache-py2
policy: pull
test:install:whl:extras:py35:
extends: .test:install:whl:extras
image: python:3.5
......
## v0.11 (02/12/2019)
- #653 Added new `secml.ml.model_zoo` package, which provides a zoo of pre-trained SecML models. The list of available models will be greatly expanded in the future. See https://secml.gitlab.io/secml.ml.model_zoo.html for more details.
- #629 Greatly improved the performance of the `grad_f_x` method for `CClassifier` and `CPreProcess` classes, especially when nested via `preprocess` attribute.
- #613 Support for Python 2.7 is dropped. Python version 3.5, 3.6, or 3.7 is now required.
### Requirements (2 changes)
- #633 The following dependencies are now required: `numpy >= 1.17`, `scipy >= 1.3.1`, `scikit-learn >= 0.21` `matplotlib = 3`.
- #622 Removed dependency on `six` library.
### Added (5 changes)
- #539 Added new core interface to get and set the state of an object instance: `set_state`, `get_state`, `save_state`, `load_state`. The state of an object is a simple human-readable Python dictionary object which stores the data necessary to restore an instance to a specific status. Please not that to guarantee the exact match between the original object instance and the restored one, the standard save/load interface should be used.
- #647 Added new function `core.attr_utils.get_protected` which returns a protected attribute from a class (if exists).
- #629 `CClassifier` and `CPreProcess` classes now provide a `gradient` method, which computes the gradient by doing a forward and a backward pass on the classifier or preprocessor function chain, accepting an optional pre-multiplier `w`.
- #539 Added new accessible attributes to multiple classes: `CNormalizerMinMax .m .q`; `CReducerLDA .lda`; `CClassifierKNN .tr`; `CClassifierRidge .tr`; `CClassifierSGD .tr`; `CClassifierPyTorch .trained`.
- #640 Added `random_state` parameter to `CClassifierDecisionTree`.
### Improved (6 changes)
- #631 Data objects are now stored using protocol 4 by `pickle_utils.save`. This protocol adds support for very large objects, pickling more kinds of objects, and some data format optimizations.
- #639 Objective function parameter (`objective_function`) in `CAttackEvasionCleverhans` is now correctly populated for the following attacks: `CarliniWagnerL2`, `FastGradientMethod`, `ProjectedGradientDescent`, `LBFGS`, `MomentumIterativeMethod`, `MadryEtAl`, `BasicIterativeMethod`.
- #638 The sequence of modifications to the attack point (`x_seq` parameter) is now correctly populated in `CAttackEvasionCleverhans`.
- #595 A pre-trained classifier can now be passed to `CClassifierRejectThreshold` to avoid running fit twice.
- #627 Slight improvement of `CKernel.gradient()` method performance by removing unnecessary calls.
- #630 Sparse data can now be used in `CKernelHistIntersect`.
### Changed (2 changes)
- #616 Renamed `CModelCleverhans` to `_CModelCleverhans` as this class is not supposed to be explicitly used.
- #111 Default value of the parameter `tol` changed from `-inf` to `None` in `CClassifierSGD`. This change should not alter the classifier behavior when using the default parameters.
### Fixed (8 changes)
- #611 Fixed `CDataloaderMNIST` crashing depending on the desired number of samples and digits to load.
- #652 Number of gradient computations returned by `CAttackEvasionCleverhans.grad_eval` is now accurate.
- #650 Fixed `CAttackEvasionCleverhans.f_eval` wrongly returns the number of gradient evaluations.
- #637 Fixed checks on `y_taget` in `CAttackEvasionCleverhans` which compared the 0 label to untargeted case (`y_true = None`).
- #648 Function `core.attr_utils.is_public` now correctly return False for properties.
- #649 Fixed wrong use of `core.attr_utils.is_public` in `CCreator` and `CDatasetHeader`.
- #655 Fixed `CClassifierRejectThreshold.n_classes` not taking into account the rejected class (label -1).
- #636 Fixed a `TypeError` raised by `CFigure.clabel()` when using matplotlib 3.
### Removed & Deprecated (4 changes)
- #628 Method `is_linear` of `CClassifier` and `CNormalizer` subclasses is now deprecated.
- #641 Parameter `random_seed` of `CClassifierLogistic` is now deprecated. Use `random_state` instead.
- #603 Removed deprecated class `CNormalizerMeanSTD`.
- #603 Removed deprecated parameter `batch_size` from `CKernel` and subclasses.
### Documentation (4 changes)
- #625 Reorganized notebooks tutorials into different categories: *Machine Learning*, *Adversarial Machine Learning*, and *Explainable Machine Learning*.
- #615 Added a tutorial notebook on the use of Cleverhans library wrapper.
- #607 Settings module `secml.settings` is now correctly displayed in the docs.
- #626 Added missing reference to `CPlotMetric` class in docs.
## v0.10 (29/10/2019)
- #535 Added new package `secml.explanation`, which provides different methods for explaining machine learning models. See documentation and examples for more information.
- #584 **[beta]** Added `CModelCleverhans` and `CAttackEvasionCleverhans` to support adversarial attacks from [CleverHans](https://github.com/tensorflow/cleverhans), a Python library to benchmark vulnerability of machine learning systems to adversarial examples.
- #584 **[beta]** Added `CAttackEvasionCleverhans` to support adversarial attacks from [CleverHans](https://github.com/tensorflow/cleverhans), a Python library to benchmark vulnerability of machine learning systems to adversarial examples.
### Requirements (1 change)
- #580 PyTorch version `1.3` is now supported.
......@@ -36,7 +87,7 @@
- #575 Parameter `batch_size` of `CKernel` is now deprecated.
- #597 Removed unused parameter `gamma` from `CKernelChebyshevDistance`.
- #596 Removed `CKernelHamming`.
- #602 Renamed `CNormalizerMeanSTD` to `CNormalizerMeanStd`. The old class has been deprecated and will be removed in a future vearsion.
- #602 Renamed `CNormalizerMeanSTD` to `CNormalizerMeanStd`. The old class has been deprecated and will be removed in a future version.
### Documentation (5 changes)
- #538 Added a notebook tutorial on the use of Explainable ML methods provided by the `secml.explanation` package.
......
......@@ -4,4 +4,5 @@ include update/*
include requirements.txt
include src/secml/VERSION
include src/secml/VERSION_REV
include src/secml/secml.conf
\ No newline at end of file
include src/secml/secml.conf
include src/secml/ml/model_zoo/models_dict.json
\ No newline at end of file
......@@ -31,7 +31,7 @@ Please see our [ROADMAP](https://secml.gitlab.io/roadmap.html) for an overview
of the future development directions.
[![Status Alpha](https://img.shields.io/badge/status-alpha-yellow.svg)](.)
[![Python 2.7 | 3.5 | 3.6 | 3.7](https://img.shields.io/badge/python-2.7%20%7C%203.5%20%7C%203.6%20%7C%203.7-brightgreen.svg)](.)
[![Python 3.5 | 3.6 | 3.7](https://img.shields.io/badge/python-3.5%20%7C%203.6%20%7C%203.7-brightgreen.svg)](.)
[![Platform Linux | MacOS ](https://img.shields.io/badge/platform-linux%20%7C%20macos-lightgrey.svg)](.)
[![Apache License 2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
......@@ -47,16 +47,15 @@ procedure.
### Operating System requirements
SecML can run under Python 2.7 and Python >= 3.5 with no configuration steps
required, as all its dependencies are available as wheel packages for the main
macOS versions and Linux distributions.
SecML can run under Python >= 3.5 with no additional configuration steps
required, as all its dependencies are available as wheel packages for
the primary macOS versions and Linux distributions.
However, to support additional advanced features more packages can be necessary
depending on the Operating System used:
- Linux (Ubuntu >= 16.04 or equivalent dist)
- `python-tk` (Python 2.7), `python3-tk` (Python >= 3.5), for running
MatplotLib Tk-based backends;
- `python3-tk`, for running MatplotLib Tk-based backends;
- NVIDIA<sup>®</sup> CUDA<sup>®</sup> Toolkit for running `tf-gpu`
[extra component](#extra-components).
See the [TensorFlow Guide](https://www.tensorflow.org/install/gpu).
......
......@@ -3,11 +3,12 @@
.. toctree::
:hidden:
:caption: User Guide
:titlesonly:
:maxdepth: 2
:numbered:
:glob:
tutorials/*
tutorials.ml
tutorials.adv
tutorials/10-Explanation
.. toctree::
:hidden:
......
......@@ -13,12 +13,12 @@ fig.sp.bar(X, Y1, facecolor='#9999ff', edgecolor='white')
fig.sp.bar(X, -Y2, facecolor='#ff9999', edgecolor='white')
for x, y in zip(X, Y1):
fig.sp.text(x + 0.4, y + 0.05, '%.2f' % y, ha='center', va='bottom')
fig.sp.text(x, y, '%.2f' % y, ha='center', va='bottom')
for x, y in zip(X, Y2):
fig.sp.text(x + 0.4, -y - 0.05, '%.2f' % y, ha='center', va='top')
fig.sp.text(x, -y - 0.02, '%.2f' % y, ha='center', va='top')
fig.sp.xlim(-.5, n)
fig.sp.xlim(-.5, n-.5)
fig.sp.xticks(())
fig.sp.ylim(-1.25, 1.25)
fig.sp.yticks(())
......
......@@ -5,6 +5,7 @@ from secml.figure import CFigure
def f(x, y):
return (1 - x / 2 + x ** 5 + y ** 3) * (-x ** 2 - y ** 2).exp()
fig = CFigure()
x_linspace = CArray.linspace(-3, 3, 256)
......@@ -12,7 +13,7 @@ y_linspace = CArray.linspace(-3, 3, 256)
X, Y = CArray.meshgrid((x_linspace, y_linspace))
C = fig.sp.contour(X, Y, f(X, Y), linewidth=.5, cmap='hot')
C = fig.sp.contour(X, Y, f(X, Y), linewidths=.5, cmap='hot')
fig.sp.clabel(C, inline=1, fontsize=10)
fig.sp.xticks(())
......
......@@ -9,7 +9,7 @@ sigma = 15 # standard deviation of distribution
x = mu + sigma * CArray.randn((10000,))
num_bins = 50
# the histogram of the data
n, bins, patches = fig.sp.hist(x, num_bins, normed=1, facecolor='green', alpha=0.5)
n, bins, patches = fig.sp.hist(x, num_bins, density=1, facecolor='green', alpha=0.5)
# add a 'best fit' line
y = bins.normpdf(mu, sigma)
fig.sp.plot(bins, y, 'r--')
......
......@@ -12,7 +12,7 @@ fig.sp.plot(X, S)
fig.sp.xticks(CArray([-pi, -pi / 2, 0, pi / 2, pi]))
fig.sp.xticklabels(CArray(["- pi", "-pi/2", "0", "pi/2", "pi"]))
fig.sp.tick_params(direction='out', length=6, width=2, colors='r', right='off')
fig.sp.tick_params(direction='out', length=6, width=2, colors='r', right=False)
fig.sp.yticks(CArray([-1, 0, +1]))
fig.show()
......@@ -33,7 +33,7 @@ CAttackEvasionPGDLS
CAttackEvasionCleverhans
------------------------
.. automodule:: secml.adv.attacks.evasion.c_attack_evasion_cleverhans
.. automodule:: secml.adv.attacks.evasion.cleverhans.c_attack_evasion_cleverhans
:members:
:undoc-members:
:show-inheritance:
......
......@@ -44,6 +44,10 @@ by calling the active subplot via :meth:`.CFigure.sp`
:members:
:undoc-members:
.. autoclass:: secml.figure._plots.c_plot_metric.CPlotMetric
:members:
:undoc-members:
.. autoclass:: secml.figure._plots.c_plot_sec_eval.CPlotSecEval
:members:
:undoc-members:
......
......@@ -109,15 +109,6 @@ CClassifierPyTorch
:undoc-members:
:show-inheritance:
CModelCleverhans
----------------
.. automodule:: secml.ml.classifiers.tf.c_model_cleverhans
:members:
:undoc-members:
:show-inheritance:
clf\_utils
----------
......
secml.ml.model_zoo
==================
.. automodule:: secml.ml.model_zoo
:members:
:undoc-members:
:show-inheritance:
load_model
----------
.. automodule:: secml.ml.model_zoo.load_model
:members:
:undoc-members:
:show-inheritance:
......@@ -14,3 +14,4 @@ secml.ml
secml.ml.kernel
secml.ml.peval
secml.ml.stats
secml.ml.model_zoo
Adversarial Machine Learning
============================
.. toctree::
:titlesonly:
tutorials/03-Evasion
tutorials/04-Transferability
tutorials/05-Poisoning
tutorials/06-MNIST_dataset
tutorials/07-NeuralNetworks-MNIST
tutorials/08-ImageNet
tutorials/09-Cleverhans
\ No newline at end of file
Machine Learning
================
.. toctree::
:titlesonly:
tutorials/01-Training
tutorials/02-NeuralNetworks
\ No newline at end of file
{
"path": "../../../tutorials/02-Evasion.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/02-NeuralNetworks.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/03-Evasion.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/03-Transferability.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/04-Poisoning.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/04-Transferability.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/05-MNIST_dataset.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/05-Poisoning.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/06-MNIST_dataset.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/06-NeuralNetworks.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/07-ImageNet.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/07-NeuralNetworks-MNIST.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/08-Explanation.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/08-ImageNet.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/09-Cleverhans.ipynb"
}
\ No newline at end of file
{
"path": "../../../tutorials/10-Explanation.ipynb"
}
\ No newline at end of file
numpy >= 1.15
scipy >= 1.2
matplotlib ~= 2.2.0
scikit-learn >= 0.20
numpy >= 1.17
scipy >= 1.3.1
matplotlib ~= 3.0.0
scikit-learn >= 0.21
joblib
Pillow
requests
python-dateutil
# For python 2/3 compatibility
six
\ No newline at end of file
python-dateutil
\ No newline at end of file
from setuptools import setup, find_packages
from pkg_resources import parse_version
import os
from io import open # TODO: REMOVE AFTER TRANSITIONING TO PYTHON 3
import subprocess
here = os.path.abspath(os.path.dirname(__file__))
......@@ -142,8 +141,6 @@ Intended Audience :: Science/Research
Intended Audience :: Developers
License :: OSI Approved
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
......@@ -177,13 +174,13 @@ setup(
"*.tests", "*.tests.*", "tests.*", "tests"]),
package_dir={'': 'src'},
include_package_data=True,
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4',
python_requires='>=3.5.*, <4',
install_requires=REQ_PKGS,
extras_require={
'pytorch': ["torch>=1.1", "torchvision>=0.2.2"],
'cleverhans': ["tensorflow>=1.14,<2", "cleverhans"],
'tf-gpu': ["tensorflow-gpu>=1.14,<2"],
'unittests': ['pytest>=4.2,<5.1', 'pytest-cov>=2.6.1']
'unittests': ['pytest>=5,<5.1', 'pytest-cov>=2.8', 'jupyter', 'nbval']
},
zip_safe=False
)
0.10
\ No newline at end of file
0.11
\ No newline at end of file
import os
from io import open # TODO: REMOVE AFTER TRANSITIONING TO PYTHON 3
import sys
import subprocess
from pkg_resources import parse_version
......@@ -20,12 +19,6 @@ __all__ = ['_NoValue', '__version__', 'global_filterwarnings']
_here = os.path.abspath(os.path.dirname(__file__))
if sys.version_info < (3, 0):
_logger.warn("DEPRECATION: Python 2.7 is deprecated, please use "
"Python >= 3.5. Support for Python 2.7 will be dropped "
"in a future release without advanced notice.")
def _read(*path_parts):
with open(os.path.join(_here, *path_parts), 'r', encoding='ascii') as fp:
return fp.read().strip()
......@@ -106,17 +99,9 @@ except:
# The following are global filters for warnings
def global_filterwarnings():
import warnings
# TODO: REMOVE WHEN SCIPY MIN VERSION WILL BE 1.3
warnings.filterwarnings(
"ignore", category=PendingDeprecationWarning,
message="the matrix subclass is not the recommended way to represent "
"matrices or deal with linear algebra*"
)
# Warnings related to data-type size changed. # TODO: fixed in numpy 1.16.1
# Warnings related to data-type size changed. # TODO: fixed in numpy ??
warnings.filterwarnings(
"ignore", category=RuntimeWarning, message="numpy.dtype size changed*"
)
......@@ -124,29 +109,30 @@ def global_filterwarnings():
"ignore", category=RuntimeWarning, message="numpy.ufunc size changed*"
)
# TODO: REMOVE AFTER SWITCHING TO PYTHON 3
# TODO: check after upgrading to tensorflow 2
warnings.filterwarnings(
"ignore", category=DeprecationWarning,
message="The SafeConfigParser class has been renamed to "
"ConfigParser in Python 3.2.*"
)
message="Using or importing the ABCs from 'collections' instead of "
"from 'collections.abc' is deprecated*")
# TODO: fixed in scipy 1.3.1
# TODO: check after upgrading to tensorflow 2
warnings.filterwarnings(
"ignore", category=FutureWarning,
message="future versions will not create a writeable array "
"from broadcast_array*"
)
# TODO: fixed in scipy 1.3.1
"ignore", category=PendingDeprecationWarning,
message="the imp module is deprecated in favour of importlib*")
warnings.filterwarnings(
"ignore", category=DeprecationWarning,
message="Numpy has detected that you (may be)*") # same as before
message="the imp module is deprecated in favour of importlib*")
# TODO: check after upgrading to matplotlib 3
# TODO: check after upgrading to tensorflow 2
warnings.filterwarnings(
"ignore", category=DeprecationWarning,
message="Using or importing the ABCs from 'collections' instead of "
"from 'collections.abc' is deprecated*")
"ignore", category=FutureWarning, message="Passing (type, 1)*")
# TODO: check after cleverhans fix this (post 3.0.1)
try: # For some reason we are not able to filter tf warnings
import tensorflow as tf
tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR)
except ImportError:
pass
# Call the filterwarnings method to make it active project-wide
......
......@@ -24,7 +24,7 @@ if '_is_loaded' in globals():
_is_loaded = True
class _NoValueType(object):
class _NoValueType:
"""Special keyword value.
The instance of this class may be used as the default value assigned to a
......
......@@ -7,8 +7,6 @@
"""
from abc import ABCMeta, abstractmethod
import six
from six.moves import range
from secml.core import CCreator
from secml.core.type_utils import is_int
......@@ -18,8 +16,7 @@ from secml.ml.classifiers import CClassifier
from secml.data import CDataset
@six.add_metaclass(ABCMeta)
class CAttack(CCreator):
class CAttack(CCreator, metaclass=ABCMeta):
"""Interface class for evasion and poisoning attacks.
Parameters
......
......@@ -7,4 +7,4 @@ try:
except ImportError:
pass # cleverhans is an extra component
else:
from .c_attack_evasion_cleverhans import CAttackEvasionCleverhans
from .cleverhans.c_attack_evasion_cleverhans import CAttackEvasionCleverhans
......@@ -8,16 +8,13 @@
"""
from abc import ABCMeta, abstractmethod
import six
from six.moves import range
from secml.adv.attacks import CAttack
from secml.array import CArray
from secml.data import CDataset
@six.add_metaclass(ABCMeta)
class CAttackEvasion(CAttack):
class CAttackEvasion(CAttack, metaclass=ABCMeta):
"""Interface for Evasion attacks.
Parameters
......@@ -73,7 +70,7 @@ class CAttackEvasion(CAttack):
adv_ds : CDataset
Dataset of manipulated samples.
f_obj : float
Average value of the objective function computed on each data point.
Mean value of the objective function computed on each data point.
"""
x = CArray(x).atleast_2d()
......
......@@ -7,8 +7,6 @@
.. moduleauthor:: Marco Melis <marco.melis@unica.it>
"""
from six.moves import range
from secml.adv.attacks import CAttack
from secml.adv.attacks.evasion import CAttackEvasion
from secml.optim.optimizers import COptimizer
......
"""
.. module:: CAttackEvasionCleverhansLossesMixin
:synopsis: Mixin class that defines loss functions
for supported Cleverhans attacks.
.. moduleauthor:: Maura Pintor <maura.pintor@unica.it>
"""
import numpy as np
from secml.array import CArray
from secml.ml.classifiers.loss import CLossCrossEntropy
class CAttackEvasionCleverhansLossesMixin(object):
"""Mixin class for defining losses of several supported
Cleverhans attacks."""
def _objective_function_cw(self, x):
l2dist = ((self._x0 - x) ** 2).sum(axis=1).ravel()
z_labels, z_predicted = self.classifier.predict(
x, return_decision_function=True)
y_target = CArray.zeros(shape=(1, self._n_classes),
dtype=np.float32)
# destination point label
if self.y_target is not None:
y_target[0, self.y_target] = 1
else: # indiscriminate attack
y_target[0, self._y0] = 1
z_target = (z_predicted * y_target).sum(axis=1).ravel()
z_other = ((z_predicted * (1 - y_target) +
(z_predicted.min(axis=1) - 1) * y_target)).max(axis=1)
z_other = z_other.ravel()
# The following differs from the exact definition given in Carlini
# and Wagner (2016). There (page 9, left column, last equation),
# the maximum is taken over Z_other - Z_ target (or Z_target - Z_other
# respectively) and -confidence. However, it doesn't seem that that
# would have the desired effect (loss term is <= 0 if and only if
# the difference between the logit of the target and any other class
# differs by at least confidence). Hence the rearrangement here.
c_weight = self._clvrh_attack.initial_const
self.confidence = self._clvrh_attack.confidence
if self.y_target is not None:
# if targeted, optimize for making the target class most likely
loss = CArray.maximum(z_other - z_target + self.confidence,
CArray.zeros(x.shape[0]))
else:
# if untargeted, optimize for making any other class most likely
loss = CArray.maximum(z_target - z_other + self.confidence,
CArray.zeros(x.shape[0]))
return c_weight * loss + l2dist
def _objective_function_cross_entropy(self, x):
preds, scores = self.classifier.predict(
x, return_decision_function=True)
if self.y_target is None:
target = self._y0
else:
target = CArray(self.y_target)
loss = CLossCrossEntropy()
f_obj = loss.loss(y_true=target, score=scores)
return f_obj if self.y_target is not None else -f_obj
......@@ -8,8 +8,6 @@
"""
import warnings
from abc import ABCMeta, abstractmethod
import six
from six.moves import range
from secml.adv.attacks import CAttack
from secml.optim.optimizers import COptimizer
......@@ -21,8 +19,7 @@ from secml.optim.constraints import CConstraint
from secml.optim.function import CFunction
@six.add_metaclass(ABCMeta)
class CAttackPoisoning(CAttack):
class CAttackPoisoning(CAttack, metaclass=ABCMeta):
"""Interface for poisoning attacks.
Parameters
......
......@@ -113,7 +113,7 @@ class CAttackPoisoningSVM(CAttackPoisoning):
# enforce storing dual variables in SVM
if not self._surrogate_classifier.store_dual_vars and \
self._surrogate_classifier.is_linear():
self._surrogate_classifier.is_kernel_linear():
raise ValueError(
"please retrain the classifier with `store_dual_vars=True`")
......
......@@ -7,7 +7,6 @@
"""
import time
from six.moves import range
from secml.core import CCreator
from secml.array import CArray
......
......@@ -6,7 +6,7 @@
"""
from copy import deepcopy
from six.moves import range
import numpy as np
import scipy.sparse as scs
......@@ -916,12 +916,12 @@ class CArray(_CArrayInterface):
"""
if is_scalar(other) or is_bool(other):
return self.__class__(self._data.__div__(other))
return self.__class__(self._data.__truediv__(other))
elif isinstance(other, CArray):
# dense vs sparse not supported (sparse vs dense IS supported)
if self.isdense is True and other.issparse is True:
other = other.todense()
return self.__class__(self._data.__div__(other._data))
return self.__class__(self._data.__truediv__(other._data))
elif is_ndarray(other) or is_scsarray(other):
raise TypeError("unsupported operand type(s) for /: "
"'{:}' and '{:}'".format(type(self).__name__,
......@@ -949,22 +949,6 @@ class CArray(_CArrayInterface):
else:
return NotImplemented
def __div__(self, other): # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
"""Element-wise division. True division will be performed.
See .__truediv__() for more informations.
"""
return self.__truediv__(other)
def __rdiv__(self, other): # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
"""Element-wise (inverse) division. True division will be performed.
See .__rtruediv__() for more informations.
"""
return self.__rtruediv__(other)
def __floordiv__(self, other):
"""Element-wise floor division (// operator).
......@@ -1316,8 +1300,6 @@ class CArray(_CArrayInterface):
"""Manage 'and' and 'or' operators."""
return bool(self._data)
__nonzero__ = __bool__ # Compatibility with python < 3
def __iter__(self):
"""Yields array elements in raster-scan order.
......
......@@ -5,15 +5,13 @@
.. moduleauthor:: Marco Melis <marco.melis@unica.it>
"""
from abc import ABCMeta, abstractmethod, abstractproperty
import six
from abc import ABCMeta, abstractmethod
from copy import deepcopy
from secml.core.type_utils import to_builtin
@six.add_metaclass(ABCMeta)
class _CArrayInterface(object):
class _CArrayInterface(metaclass=ABCMeta):
"""Interface for array classes.
For extensive definition of each method, see `secml.array.CArray`.
......@@ -24,42 +22,50 @@ class _CArrayInterface(object):
# # # # # # PROPERTIES # # # # # #
# -------------------------------#
@abstractproperty
@property
@abstractmethod
def shape(self):
"""Shape of stored data, tuple of ints."""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def size(self):
"""Size (number of elements) of array."""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def ndim(self):
"""Number of array dimensions."""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def dtype(self):
"""Data-type of stored data."""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def nnz(self):
"""Number of non-zero values in the array."""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def nnz_indices(self):
"""Index of non-zero array elements."""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def nnz_data(self):
"""Return non-zero array elements."""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def T(self):
"""Transposed array data."""
raise NotImplementedError
......@@ -148,16 +154,6 @@ class _CArrayInterface(object):
"""Element-wise (inverse) true division."""
raise NotImplementedError
@abstractmethod
def __div__(self, other):
"""Element-wise division. True division will be performed."""
raise NotImplementedError
@abstractmethod
def __rdiv__(self, other):
"""Element-wise (inverse) division. True division will be performed."""
raise NotImplementedError
@abstractmethod
def __floordiv__(self, other):
"""Element-wise floor division (// operator)."""
......@@ -223,8 +219,6 @@ class _CArrayInterface(object):
"""Manage 'and' and 'or' operators."""
raise NotImplementedError
__nonzero__ = __bool__ # Compatibility with python < 3
@abstractmethod
def __iter__(self):
"""Yields array elements in raster-scan order."""
......
......@@ -5,8 +5,6 @@
.. moduleauthor:: Marco Melis <marco.melis@unica.it>
"""
from __future__ import print_function, division
from six.moves import range, map
import numpy as np
import numpy.matlib
from numpy.linalg import inv, pinv
......@@ -595,22 +593,6 @@ class CDense(_CArrayInterface):
else:
return NotImplemented
def __div__(self, other): # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
"""Element-wise division.
See .__truediv__() for more informations.
"""
return self.__truediv__(other)
def __rdiv__(self, other): # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
"""Element-wise (inverse) division.
See .__rtruediv__() for more informations.
"""
return self.__rtruediv__(other)
def __floordiv__(self, other):
"""Element-wise floor division.
......@@ -853,8 +835,6 @@ class CDense(_CArrayInterface):
"""Manage 'and' and 'or' operators."""
return bool(self._data)
__nonzero__ = __bool__ # Compatibility with python < 3
def __iter__(self):
"""Yields array elements in raster-scan order."""
# The following can be simplified by ravelling the array first
......
......@@ -6,11 +6,6 @@
.. moduleauthor:: Ambra Demontis <ambra.demontis@unica.it>
"""
from __future__ import division
import six
from six.moves import range, map
from io import open # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
import scipy.sparse as scs
from scipy.sparse.linalg import inv, norm
import numpy as np
......@@ -22,7 +17,6 @@ from secml.array.c_dense import CDense
from secml.core.type_utils import is_ndarray, is_list_of_lists, \
is_list, is_slice, is_scalar, is_intlike, is_int, is_bool
from secml.core.constants import inf
import copy
def _expand_nnz_bool(array, nnz_val):
......@@ -588,14 +582,14 @@ class CSparse(_CArrayInterface):
"""
if is_scalar(other) or is_bool(other):
return self.__class__(self._data.__div__(other))
return self.__class__(self._data.__truediv__(other))
elif isinstance(other, CSparse): # Sparse / Sparse = Dense
# Scipy does not support broadcast natively
other = self._broadcast_other(other)
return CDense(self._data.__div__(other.tocsr()))
return CDense(self._data.__truediv__(other.tocsr()))
elif isinstance(other, CDense): # Sparse / Dense = Dense
# Compatible shapes, call built-in div
return CDense(self._data.__div__(other.tondarray()))
return CDense(self._data.__truediv__(other.tondarray()))
else:
return NotImplemented
......@@ -604,22 +598,6 @@ class CSparse(_CArrayInterface):
raise NotImplementedError(
"dividing a scalar by a sparse array is not supported")
def __div__(self, other): # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
"""Element-wise division.
See .__truediv__() for more informations.
"""
return self.__truediv__(other)
def __rdiv__(self, other): # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
"""Element-wise (inverse) division.
See .__rtruediv__() for more informations.
"""
return self.__rtruediv__(other)
def __floordiv__(self, other):
"""Element-wise floor division (integral part of the quotient).
......@@ -879,8 +857,6 @@ class CSparse(_CArrayInterface):
"""Manage 'and' and 'or' operators."""
return bool(self._data)
__nonzero__ = __bool__ # Compatibility with python < 3
def __iter__(self):
"""Yields array elements in raster-scan order."""
for row_id in range(self.shape[0]):
......@@ -966,8 +942,7 @@ class CSparse(_CArrayInterface):
data_cndarray.save(fhandle)
indices_cndarray.save(fhandle)
indptr_cndarray.save(fhandle)
fhandle.write(six.text_type(self.shape[0]) + " " +
six.text_type(self.shape[1]))
fhandle.write(str(self.shape[0]) + " " + str(self.shape[1]))
@classmethod
def load(cls, datafile, dtype=float):
......
......@@ -8,7 +8,8 @@
from secml import _NoValue
from secml.core.type_utils import is_str
__all__ = ['as_public', 'as_protected', 'has_protected',
__all__ = ['as_public',
'as_protected', 'has_protected', 'get_protected',
'as_private', 'has_private', 'get_private',
'has_property', 'get_property', 'has_getter', 'has_setter',
'add_readonly', 'add_readwrite',
......@@ -80,6 +81,25 @@ def has_protected(obj, attr):
return hasattr(obj, as_protected(attr))
def get_protected(obj_class, attr, default=_NoValue):
"""Return the protected attribute of class.
Parameters
----------
obj_class : class
Target class (usually extracted using obj.__class__).
attr : str
Name of the attribute to return.
default : any, optional
Value that is returned when the named attribute is not found.
"""
if default is not _NoValue: # Pass default to getattr
return getattr(obj_class, as_protected(attr), default)
else: # Standard getattr (error will be raise if attr is not found)
return getattr(obj_class, as_protected(attr))
def as_private(obj_class, attr):
"""Return the PRIVATE name associated with input attribute.
......@@ -259,13 +279,15 @@ def is_public(obj, attr):
Parameters
----------
obj : object
Any class instance. --> NOT USED
Any class instance.
attr : str
Name of the attribute to check.
"""
_check_is_attr_name(attr)
return True if not attr.startswith('_') else False
# Exclude properties to only return actual public attributes
return True if not attr.startswith('_') and \
not has_property(obj, attr) else False
def is_readonly(obj, attr):
......@@ -282,7 +304,8 @@ def is_readonly(obj, attr):
"""
_check_is_attr_name(attr)
return True if not is_public(obj, attr) and has_getter(obj, attr) and \
return True if not is_public(obj, attr) and \
has_getter(obj, attr) and \
not has_setter(obj, attr) else False
......@@ -341,7 +364,8 @@ def is_readable(obj, attr):
"""
_check_is_attr_name(attr)
return True if is_public(obj, attr) or is_readwrite(obj, attr) or \
return True if is_public(obj, attr) or \
is_readwrite(obj, attr) or \
is_readonly(obj, attr) else False
......@@ -354,6 +378,8 @@ def is_writable(obj, attr):
Parameters
----------
obj : object
Any class instance.
attr : str
Name of the attribute to check.
......@@ -369,7 +395,7 @@ def extract_attr(obj, mode):
keys having a name compatible with specified mode.
The following modalities are available:
* 'pub' -> PUBLIC (no '_' in the prefix)
* 'pub' -> PUBLIC (standard attribute, no '_' in the prefix)
* 'rw' -> READ/WRITE (a getter/setter is associated with it)
* 'r' -> READ ONLY (a getter is associated with it)
* 'pro' -> PROTECTED ('_' as the prefix and no getter/setter associated)
......@@ -409,7 +435,7 @@ def extract_attr(obj, mode):
# Parsing the modes string
check_list = parse_modes(mode)
# Yelding only the required attributes
# Yielding only the required attributes
for attr in obj.__dict__:
if any(e(obj, attr) for e in check_list):
yield attr
This diff is collapsed.
......@@ -4,7 +4,7 @@ import functools
__all__ = ["deprecated"]
class deprecated(object):
class deprecated:
"""Decorator to mark a function or class as deprecated.
Issue a warning when the function is called/the class is instantiated and
......
......@@ -5,7 +5,6 @@
.. moduleauthor:: Marco Melis <marco.melis@unica.it>
"""
import six
import numpy as np
from scipy.sparse import issparse
......@@ -21,8 +20,7 @@ def is_bool(x):
def is_int(x):
# TODO: REMOVE integer_types AFTER TRANSITIONING TO PY3
if isinstance(x, six.integer_types) and not isinstance(x, bool): # bool is a subclass of int
if isinstance(x, int) and not isinstance(x, bool): # bool is a subclass of int
return True
elif isinstance(x, np.integer):
return True
......@@ -271,9 +269,8 @@ def is_slice(x):
return isinstance(x, slice)
def is_str(x): # text unicode strings (unicode AND bytes in Py2)
# TODO: REMOVE string_types AFTER TRANSITIONING TO PY3
if isinstance(x, six.string_types):
def is_str(x): # text unicode strings
if isinstance(x, str):
return True
elif isinstance(x, (np.str_, np.unicode_)):
return True
......
......@@ -5,8 +5,6 @@
.. moduleauthor:: Marco Melis <marco.melis@unica.it>
"""
from six.moves import range
from secml.core import CCreator
from secml.array import CArray
from secml.data import CDatasetHeader
......
......@@ -6,7 +6,7 @@
"""
from secml.core import CCreator
from secml.core.attr_utils import is_public
from secml.core.attr_utils import is_writable
from secml.core.type_utils import is_list
from secml.array import CArray
......@@ -87,8 +87,8 @@ class CDatasetHeader(CCreator):
super(CDatasetHeader, self).__setattr__(key, value)
# Make sure that input public attributes are consistent
if is_public(self, key):
# Make sure that input writable attributes are consistent
if is_writable(self, key):
self._validate_params()
def _validate_params(self):
......
......@@ -7,14 +7,12 @@
"""
from abc import ABCMeta, abstractmethod
import six
from secml.array import CArray
from secml.core import CCreator
@six.add_metaclass(ABCMeta)
class CDataLoader(CCreator):
class CDataLoader(CCreator, metaclass=ABCMeta):
"""Interface for Dataset loaders."""
__super__ = 'CDataLoader'
......
......@@ -6,12 +6,11 @@
"""
import tarfile
from six.moves import cPickle
from six.moves import range
from io import open # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
from multiprocessing import Lock
from abc import ABCMeta, abstractmethod, abstractproperty
import six
import pickle
from abc import ABCMeta, abstractmethod
import numpy as np
from secml.data.loader import CDataLoader
......@@ -31,8 +30,7 @@ CIFAR10_PATH = fm.join(CIFAR_PATH, 'cifar-10-batches-py')
CIFAR100_PATH = fm.join(CIFAR_PATH, 'cifar-100-python')
@six.add_metaclass(ABCMeta)
class CDataLoaderCIFAR(CDataLoader):
class CDataLoaderCIFAR(CDataLoader, metaclass=ABCMeta):
"""Loads the CIFAR tiny images datasets.
Available at: https://www.cs.toronto.edu/~kriz/cifar.html
......@@ -57,7 +55,8 @@ class CDataLoaderCIFAR(CDataLoader):
# Downloaded datafile seems valid, extract only
self._get_data(self.data_url, CIFAR_PATH, extract_only=True)
@abstractproperty
@property
@abstractmethod
def data_url(self):
"""URL of the datafile. Specific for each dataset type.
......@@ -69,7 +68,8 @@ class CDataLoaderCIFAR(CDataLoader):
"""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def data_md5(self):
"""MD5 digest of the datafile. Specific for each dataset type.
......@@ -81,7 +81,8 @@ class CDataLoaderCIFAR(CDataLoader):
"""
raise NotImplementedError
@abstractproperty
@property
@abstractmethod
def data_path(self):
"""URL of the data directory. Specific for each dataset type.
......@@ -167,10 +168,7 @@ class CDataLoaderCIFAR(CDataLoader):
labels = None
for batch in batches_list:
with open(batch, 'rb') as bf:
try: # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
mydict = cPickle.load(bf, encoding='bytes')
except TypeError:
mydict = cPickle.load(bf)
mydict = pickle.load(bf, encoding='bytes')
# The labels have different names in the two datasets
new_data = np.array(mydict[b'data'], dtype='uint8')
......@@ -237,10 +235,7 @@ class CDataLoaderCIFAR(CDataLoader):
# Load the class-names from the pickled file.
with open(meta_file_url, 'rb') as mf:
try: # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
raw = cPickle.load(mf, encoding='bytes')[class_names_key]
except TypeError:
raw = cPickle.load(mf)[class_names_key]
raw = pickle.load(mf, encoding='bytes')[class_names_key]
# Convert from binary strings.
names = {i: x.decode('utf-8') for i, x in enumerate(raw)}
......
......@@ -8,9 +8,9 @@
"""
from multiprocessing import Lock
import zipfile
import six
import os
from fnmatch import fnmatch
from abc import ABCMeta, abstractmethod
from PIL import Image
......@@ -39,8 +39,7 @@ ICUBWORLD28_PATH = fm.join(ICUBWORLD_PATH, 'iCubWorld28')
# TODO: iCubWorld Transformations
@six.add_metaclass(ABCMeta)
class CDataLoaderICubWorld(CDataLoader):
class CDataLoaderICubWorld(CDataLoader, metaclass=ABCMeta):
"""Interface for loaders of iCubWorld datasets.
iCubWorld is a set of computer vision datasets for robotic applications,
......
......@@ -5,9 +5,6 @@
.. moduleauthor:: Marco Melis <marco.melis@unica.it>
"""
from __future__ import division
from six.moves import range
from io import open # TODO: REMOVE AFTER TRANSITION TO PYTHON 3
import gzip
import struct
from array import array
......@@ -132,9 +129,15 @@ class CDataLoaderMNIST(CDataLoader):
digits = tuple(digits)
# Number of samples per class
num_samples_class = size
if num_samples is not None:
num_samples_class = int(num_samples / len(digits))
div = len(digits)
n_samples_class = [
int(num_samples / div) + (1 if x < num_samples % div else 0)
for x in range(div)]
n_samples_class = {
e: n_samples_class[e_i] for e_i, e in enumerate(digits)}
else: # No constraint on the number of samples
n_samples_class = {e: size for e in digits}
# Counter of already taken sample for a class
count_samples_class = {e: 0 for e in digits}
......@@ -142,18 +145,21 @@ class CDataLoaderMNIST(CDataLoader):
# Extract the indices of samples to load
ind = []