Skip to content

Commit

Permalink
fixes (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmcohen committed Feb 11, 2019
1 parent e748e70 commit bdfa228
Show file tree
Hide file tree
Showing 20 changed files with 1,067 additions and 46 deletions.
3 changes: 2 additions & 1 deletion .gitignore
@@ -1,3 +1,4 @@
.idea
*~

models
.DS_Store
14 changes: 8 additions & 6 deletions README.md
@@ -1,9 +1,9 @@
# Certified Adversarial Robustness via Randomized Smoothing

This repository contains code and trained models for the paper <a href="#">Certified Adversarial Robustness via Randomized Smoothing</a> by [Jeremy Cohen](http://cs.cmu.edu/~jeremiac), Elan Rosenfeld, and [Zico Kolter](http://zicokolter.com).
This repository contains code and trained models for the paper [Certified Adversarial Robustness via Randomized Smoothing](https://arxiv.org/abs/1902.02918) by [Jeremy Cohen](http://cs.cmu.edu/~jeremiac), Elan Rosenfeld, and [Zico Kolter](http://zicokolter.com).

Randomized smoothing is a **provable** adversarial defense in L2 norm which **scales to ImageNet.**
It's also SOTA on smaller datasets like CIFAR-10 and SVHN where alternative approaches are viable.
It's also SOTA on the smaller datasets like CIFAR-10 and SVHN where other provable L2-robust classifiers are viable.

## How does it work?

Expand Down Expand Up @@ -47,7 +47,7 @@ This is because it's not possible to exactly compute the probability distributio
For the same reason, it's not possible to exactly compute the radius in which _g_ is provably robust.

Instead, we give Monte Carlo algorithms for both
1. **prediction**: evaluating _g_(x); and
1. **prediction**: evaluating _g_(x)
2. **certification**: computing the L2 radius in which _g_ is robust around _x_

which are guaranteed to return a correct answer with arbitrarily high probability.
Expand Down Expand Up @@ -123,10 +123,12 @@ The best &sigma; for each radius is denoted with an asterisk.
The contents of this repository are as follows:

* [code/](code) contains the code for our experiments.
* [models/](models) is empty, but if you'd like to run our code, you need to download our models from [here](https://drive.google.com/file/d/1h_TpbXm5haY5f-l4--IKylmdz6tvPoR4/view?usp=sharing).
* [data/](data) contains the raw data from our experiments.
* [analysis/](analysis) contains the plots and tables, based on the contents of [data](/data), that are shown in our paper.

If you'd like to run our code, you need to download our models from [here](https://drive.google.com/file/d/1h_TpbXm5haY5f-l4--IKylmdz6tvPoR4/view?usp=sharing)
and then move the directory `models` into the root directory of this repo.

### Smoothed classifiers

Randomized smoothing is implemented in the `Smooth` class in [core.py](code/core.py).
Expand Down Expand Up @@ -204,7 +206,7 @@ Finally, we note that [this file](experiments.MD) describes exactly how to repro
2. Install the dependencies:
```
conda create -n smoothing
source activate smoothing
conda activate smoothing
# below is for linux, with CUDA 10; see https://pytorch.org/ for the correct command for your system
conda install pytorch torchvision cudatoolkit=10.0 -c pytorch
conda install scipy pandas statmodels matplotlib seaborn
Expand All @@ -220,6 +222,6 @@ on the CIFAR test set.
```
model="models/cifar10/resnet110/noise_0.25/checkpoint.pth.tar"
output="???"
python certify.py cifar10 $model 0.25 $output --skip 20 --batch 400
python code/certify.py cifar10 $model 0.25 $output --skip 20 --batch 400
```
where `???` is your desired output file.
Binary file removed analysis/.DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion analysis/latex/vary_noise_imagenet
@@ -1,5 +1,5 @@
& $r = 0.5$& $r = 1.0$& $r = 1.5$& $r = 2.0$& $r = 2.5$& $r = 3.0$\\
\midrule
$\sigma = 0.25$ & \textbf{0.49} & 0.00 & 0.00 & 0.00 & 0.00 & 0.00\\
$\sigma = 0.50 & 0.46 & \textbf{0.37} & \textbf{0.29} & 0.00 & 0.00 & 0.00\\
$\sigma = 0.50$ & 0.46 & \textbf{0.37} & \textbf{0.29} & 0.00 & 0.00 & 0.00\\
$\sigma = 1.00$ & 0.38 & 0.33 & 0.26 & \textbf{0.19} & \textbf{0.15} & \textbf{0.12}\\
1 change: 1 addition & 0 deletions analysis/markdown/vary_noise_cifar10
@@ -1,5 +1,6 @@
| | r = 0.25 |r = 0.5 |r = 0.75 |r = 1.0 |r = 1.25 |r = 1.5 |
| --- | --- | --- | --- | --- | --- | --- |
<b> &sigma; = 0.12 </b>| 0.59 |0.00 |0.00 |0.00 |0.00 |0.00 |
<b> &sigma; = 0.25 </b>| 0.60<b>*</b> |0.43<b>*</b> |0.27 |0.00 |0.00 |0.00 |
<b> &sigma; = 0.50 </b>| 0.55 |0.41 |0.32<b>*</b> |0.23<b>*</b> |0.15 |0.09 |
<b> &sigma; = 1.00 </b>| 0.39 |0.34 |0.28 |0.22 |0.17<b>*</b> |0.14<b>*</b> |
Binary file removed analysis/plots/.DS_Store
Binary file not shown.
Binary file modified analysis/plots/high_prob.pdf
Binary file not shown.
Binary file modified analysis/plots/high_prob.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified analysis/plots/vary_noise_cifar10.pdf
Binary file not shown.
Binary file modified analysis/plots/vary_noise_imagenet.pdf
Binary file not shown.
Binary file modified analysis/plots/vary_train_noise_cifar_050.pdf
Binary file not shown.
Binary file modified analysis/plots/vary_train_noise_cifar_050.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified analysis/plots/vary_train_noise_imagenet_050.pdf
Binary file not shown.
58 changes: 29 additions & 29 deletions code/analyze.py
Expand Up @@ -153,57 +153,57 @@ def markdown_table_certified_accuracy(outfile: str, radius_start: float, radius_
if __name__ == "__main__":
latex_table_certified_accuracy(
"analysis/latex/vary_noise_cifar10", 0.25, 1.5, 0.25, [
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.12/test/sigma_0.12"), "$\sigma = 0.12$"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.25/test/sigma_0.25"), "$\sigma = 0.25$"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.50/test/sigma_0.50"), "$\sigma = 0.50$"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_1.00/test/sigma_1.00"), "$\sigma = 1.00$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.12/test/sigma_0.12"), "$\sigma = 0.12$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.25/test/sigma_0.25"), "$\sigma = 0.25$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.50/test/sigma_0.50"), "$\sigma = 0.50$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_1.00/test/sigma_1.00"), "$\sigma = 1.00$"),
])
markdown_table_certified_accuracy(
"analysis/markdown/vary_noise_cifar10", 0.25, 1.5, 0.25, [
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.12/test/sigma_0.12"), "&sigma; = 0.12"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.25/test/sigma_0.25"), "&sigma; = 0.25"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.50/test/sigma_0.50"), "&sigma; = 0.50"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_1.00/test/sigma_1.00"), "&sigma; = 1.00"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.12/test/sigma_0.12"), "&sigma; = 0.12"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.25/test/sigma_0.25"), "&sigma; = 0.25"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.50/test/sigma_0.50"), "&sigma; = 0.50"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_1.00/test/sigma_1.00"), "&sigma; = 1.00"),
])
latex_table_certified_accuracy(
"analysis/latex/vary_noise_imagenet", 0.5, 3.0, 0.5, [
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.25/test/sigma_0.25"), "$\sigma = 0.25$"),
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "$\sigma = 0.50$"),
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_1.00/test/sigma_1.00"), "$\sigma = 1.00$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.25/test/sigma_0.25"), "$\sigma = 0.25$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "$\sigma = 0.50$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_1.00/test/sigma_1.00"), "$\sigma = 1.00$"),
])
markdown_table_certified_accuracy(
"analysis/markdown/vary_noise_imagenet", 0.5, 3.0, 0.5, [
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.25/test/sigma_0.25"), "&sigma; = 0.25"),
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "&sigma; = 0.50"),
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_1.00/test/sigma_1.00"), "&sigma; = 1.00"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.25/test/sigma_0.25"), "&sigma; = 0.25"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "&sigma; = 0.50"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_1.00/test/sigma_1.00"), "&sigma; = 1.00"),
])
plot_certified_accuracy(
"analysis/plots/vary_noise_cifar10", "CIFAR-10, vary $\sigma$", 1.5, [
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.12/test/sigma_0.12"), "$\sigma = 0.12$"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.25/test/sigma_0.25"), "$\sigma = 0.25$"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_0.50/test/sigma_0.50"), "$\sigma = 0.50$"),
Line(ApproximateAccuracy("data/cifar10/resnet110/noise_1.00/test/sigma_1.00"), "$\sigma = 1.00$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.12/test/sigma_0.12"), "$\sigma = 0.12$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.25/test/sigma_0.25"), "$\sigma = 0.25$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.50/test/sigma_0.50"), "$\sigma = 0.50$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_1.00/test/sigma_1.00"), "$\sigma = 1.00$"),
])
plot_certified_accuracy(
"analysis/plots/vary_train_noise_cifar_050", "CIFAR-10, vary train noise, $\sigma=0.5$", 1.5, [
Line(ApproximateAccuracy("data/cifar10/resnet20/noise_0.25/test/sigma_0.50"), "train $\sigma = 0.25$"),
Line(ApproximateAccuracy("data/cifar10/resnet20/noise_0.50/test/sigma_0.50"), "train $\sigma = 0.50$"),
Line(ApproximateAccuracy("data/cifar10/resnet20/noise_1.00/test/sigma_0.50"), "train $\sigma = 1.00$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.25/test/sigma_0.50"), "train $\sigma = 0.25$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_0.50/test/sigma_0.50"), "train $\sigma = 0.50$"),
Line(ApproximateAccuracy("data/certify/cifar10/resnet110/noise_1.00/test/sigma_0.50"), "train $\sigma = 1.00$"),
])
plot_certified_accuracy(
"analysis/plots/vary_train_noise_imagenet_050", "ImageNet, vary train noise, $\sigma=0.5$", 1.5, [
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.25/test/sigma_0.50"), "train $\sigma = 0.25$"),
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "train $\sigma = 0.50$"),
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_1.00/test/sigma_0.50"), "train $\sigma = 1.00$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.25/test/sigma_0.50"), "train $\sigma = 0.25$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "train $\sigma = 0.50$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_1.00/test/sigma_0.50"), "train $\sigma = 1.00$"),
])
plot_certified_accuracy(
"analysis/plots/vary_noise_imagenet", "ImageNet, vary $\sigma$", 4, [
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.25/test/sigma_0.25"), "$\sigma = 0.25$"),
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "$\sigma = 0.50$"),
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_1.00/test/sigma_1.00"), "$\sigma = 1.00$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.25/test/sigma_0.25"), "$\sigma = 0.25$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "$\sigma = 0.50$"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_1.00/test/sigma_1.00"), "$\sigma = 1.00$"),
])
plot_certified_accuracy(
"analysis/plots/high_prob", "Approximate vs. High-Probability", 2.0, [
Line(ApproximateAccuracy("data/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "Approximate"),
Line(HighProbAccuracy("data/imagenet/resnet50/noise_0.50/test/sigma_0.50", 0.001, 0.05), "High-Prob"),
Line(ApproximateAccuracy("data/certify/imagenet/resnet50/noise_0.50/test/sigma_0.50"), "Approximate"),
Line(HighProbAccuracy("data/certify/imagenet/resnet50/noise_0.50/test/sigma_0.50", 0.001, 0.001), "High-Prob"),
])
33 changes: 24 additions & 9 deletions code/core.py
Expand Up @@ -35,22 +35,28 @@ def certify(self, x: torch.tensor, n0: int, n: int, alpha: float, batch_size: in
in the case of abstention, the class will be ABSTAIN and the radius 0.
"""
self.base_classifier.eval()
# draw samples of f(x+ epsilon)
counts_selection = self._sample_noise(x, n0, batch_size)
cA = counts_selection.argmax().item()
# use these samples to take a guess at the top class
cAHat = counts_selection.argmax().item()
# draw more samples of f(x + epsilon)
counts_estimation = self._sample_noise(x, n, batch_size)
nA = counts_estimation[cA].item()

pA = self._clopper_lower(nA, n, alpha)
if pA < 0.5:
# use these samples to estimate a lower bound on pA
nA = counts_estimation[cAHat].item()
pABar = self._lower_confidence_bound(nA, n, alpha)
if pABar < 0.5:
return Smooth.ABSTAIN, 0.0
else:
radius = self.sigma * norm.ppf(pA)
return cA, radius
radius = self.sigma * norm.ppf(pABar)
return cAHat, radius

def predict(self, x: torch.tensor, n: int, alpha: float, batch_size: int) -> int:
""" Monte Carlo algorithm for evaluating the prediction of g at x. With probability at least 1 - alpha, the
class returned by this method will equal g(x).
This function uses the hypothesis test described in https://arxiv.org/abs/1610.03944
for identifying the top category of a multinomial distribution.
:param x: the input [channel x height x width]
:param n: the number of Monte Carlo samples to use
:param alpha: the failure probability
Expand Down Expand Up @@ -84,7 +90,7 @@ def _sample_noise(self, x: torch.tensor, num: int, batch_size) -> np.ndarray:
batch = x.repeat((this_batch_size, 1, 1, 1))
noise = torch.randn_like(batch, device='cuda') * self.sigma
predictions = self.base_classifier(batch + noise).argmax(1)
counts += self.count_arr(predictions.cpu().numpy(), self.num_classes)
counts += self._count_arr(predictions.cpu().numpy(), self.num_classes)
return counts

def _count_arr(self, arr: np.ndarray, length: int) -> np.ndarray:
Expand All @@ -93,5 +99,14 @@ def _count_arr(self, arr: np.ndarray, length: int) -> np.ndarray:
counts[idx] += 1
return counts

def _clopper_lower(self, NA: int, N: int, alpha: float) -> float:
def _lower_confidence_bound(self, NA: int, N: int, alpha: float) -> float:
""" Returns a (1 - alpha) lower confidence bound on a bernoulli proportion.
This function uses the Clopper-Pearson method.
:param NA: the number of "successes"
:param N: the number of total draws
:param alpha: the confidence level
:return: a lower bound on the binomial proportion which holds true w.p at least (1 - alpha) over the samples
"""
return proportion_confint(NA, N, alpha=2 * alpha, method="beta")[0]

0 comments on commit bdfa228

Please sign in to comment.