Skip to content

Commit c711dc7

Browse files
npapernotbenoitsteiner
authored andcommitted
added private learning with multiple teachers (tensorflow#331)
1 parent 54a48a1 commit c711dc7

File tree

8 files changed

+1636
-0
lines changed

8 files changed

+1636
-0
lines changed

privacy/README.md

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Learning private models with multiple teachers
2+
3+
This repository contains code to create a setup for learning privacy-preserving
4+
student models by transferring knowledge from an ensemble of teachers trained
5+
on disjoint subsets of the data for which privacy guarantees are to be provided.
6+
7+
Knowledge acquired by teachers is transferred to the student in a differentially
8+
private manner by noisily aggregating the teacher decisions before feeding them
9+
to the student during training.
10+
11+
A paper describing the approach is in preparation. A link will be added to this
12+
README when available.
13+
14+
## Dependencies
15+
16+
This model uses `TensorFlow` to perform numerical computations associated with
17+
machine learning models, as well as common Python libraries like: `numpy`,
18+
`scipy`, and `six`. Instructions to install these can be found in their
19+
respective documentations.
20+
21+
## How to run
22+
23+
This repository supports the MNIST, CIFAR10, and SVHN datasets. The following
24+
instructions are given for MNIST but can easily be adapted by replacing the
25+
flag `--dataset=mnist` by `--dataset=cifar10` or `--dataset=svhn`.
26+
There are 2 steps: teacher training and student training. Data will be
27+
automatically downloaded when you start the teacher training.
28+
29+
The following is a two-step process: first we train an ensemble of teacher
30+
models and second we train a student using predictions made by this ensemble.
31+
32+
**Training the teachers:** first run the `train_teachers.py` file with at least
33+
three flags specifying (1) the number of teachers, (2) the ID of the teacher
34+
you are training among these teachers, and (3) the dataset on which to train.
35+
For instance, to train teacher number 10 among an ensemble of 100 teachers for
36+
MNIST, you use the following command:
37+
38+
```
39+
python train_teachers.py --nb_teachers=100 --teacher_id=10 --dataset=mnist
40+
```
41+
42+
Other flags like `train_dir` and `data_dir` should optionally be set to
43+
respectively point to the directory where model checkpoints and temporary data
44+
(like the dataset) should be saved. The flag `max_steps` (default at 3000)
45+
controls the length of training. See `train_teachers.py` and `deep_cnn.py`
46+
to find available flags and their descriptions.
47+
48+
**Training the student:** once the teachers are all trained, e.g., teachers
49+
with IDs `0` to `99` are trained for `nb_teachers=100`, we are ready to train
50+
the student. The student is trained by labeling some of the test data with
51+
predictions from the teachers. The predictions are aggregated by counting the
52+
votes assigned to each class among the ensemble of teachers, adding Laplacian
53+
noise to these votes, and assigning the label with the maximum noisy vote count
54+
to the sample. This is detailed in function `noisy_max` in the file
55+
`aggregation.py`. To learn the student, use the following command:
56+
57+
```
58+
python train_student.py --nb_teachers=100 --dataset=mnist --stdnt_share=5000
59+
```
60+
61+
The flag `--stdnt_share=5000` indicates that the student should be able to
62+
use the first `5000` samples of the dataset's test subset as unlabeled
63+
training points (they will be labeled using the teacher predictions). The
64+
remaining samples are used for evaluation of the student's accuracy, which
65+
is displayed upon completion of training.
66+
67+
## Alternative deeper convolutional architecture
68+
69+
Note that a deeper convolutional model is available. Both the default and
70+
deeper models graphs are defined in `deep_cnn.py`, respectively by
71+
functions `inference` and `inference_deeper`. Use the flag `--deeper=true`
72+
to switch to that model when launching `train_teachers.py` and
73+
`train_student.py`.
74+
75+
## Contact
76+
77+
To ask questions, please email `[email protected]` or open an issue on
78+
the `tensorflow/models` issues tracker. Please assign issues to
79+
[(@npapernot)](https://github.com/npapernot).

privacy/aggregation.py

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Copyright 2016 Google Inc. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# ==============================================================================
15+
16+
17+
from __future__ import absolute_import
18+
from __future__ import division
19+
from __future__ import print_function
20+
21+
import numpy as np
22+
23+
24+
def labels_from_probs(probs):
25+
"""
26+
Helper function: computes argmax along last dimension of array to obtain
27+
labels (max prob or max logit value)
28+
:param probs: numpy array where probabilities or logits are on last dimension
29+
:return: array with same shape as input besides last dimension with shape 1
30+
now containing the labels
31+
"""
32+
# Compute last axis index
33+
last_axis = len(np.shape(probs)) - 1
34+
35+
# Label is argmax over last dimension
36+
labels = np.argmax(probs, axis=last_axis)
37+
38+
# Return as np.int32
39+
return np.asarray(labels, dtype=np.int32)
40+
41+
42+
def noisy_max(logits, lap_scale, return_clean_votes=False):
43+
"""
44+
This aggregation mechanism takes the softmax/logit output of several models
45+
resulting from inference on identical inputs and computes the noisy-max of
46+
the votes for candidate classes to select a label for each sample: it
47+
adds Laplacian noise to label counts and returns the most frequent label.
48+
:param logits: logits or probabilities for each sample
49+
:param lap_scale: scale of the Laplacian noise to be added to counts
50+
:param return_clean_votes: if set to True, also returns clean votes (without
51+
Laplacian noise). This can be used to perform the
52+
privacy analysis of this aggregation mechanism.
53+
:return: pair of result and (if clean_votes is set to True) the clean counts
54+
for each class per sample and the the original labels produced by
55+
the teachers.
56+
"""
57+
58+
# Compute labels from logits/probs and reshape array properly
59+
labels = labels_from_probs(logits)
60+
labels_shape = np.shape(labels)
61+
labels = labels.reshape((labels_shape[0], labels_shape[1]))
62+
63+
# Initialize array to hold final labels
64+
result = np.zeros(int(labels_shape[1]))
65+
66+
if return_clean_votes:
67+
# Initialize array to hold clean votes for each sample
68+
clean_votes = np.zeros((int(labels_shape[1]), 10))
69+
70+
# Parse each sample
71+
for i in xrange(int(labels_shape[1])):
72+
# Count number of votes assigned to each class
73+
label_counts = np.bincount(labels[:,i], minlength=10)
74+
75+
if return_clean_votes:
76+
# Store vote counts for export
77+
clean_votes[i] = label_counts
78+
79+
# Cast in float32 to prepare before addition of Laplacian noise
80+
label_counts = np.asarray(label_counts, dtype=np.float32)
81+
82+
# Sample independent Laplacian noise for each class
83+
for item in xrange(10):
84+
label_counts[item] += np.random.laplace(loc=0.0, scale=float(lap_scale))
85+
86+
# Result is the most frequent label
87+
result[i] = np.argmax(label_counts)
88+
89+
# Cast labels to np.int32 for compatibility with deep_cnn.py feed dictionaries
90+
result = np.asarray(result, dtype=np.int32)
91+
92+
if return_clean_votes:
93+
# Returns several array, which are later saved:
94+
# result: labels obtained from the noisy aggregation
95+
# clean_votes: the number of teacher votes assigned to each sample and class
96+
# labels: the labels assigned by teachers (before the noisy aggregation)
97+
return result, clean_votes, labels
98+
else:
99+
# Only return labels resulting from noisy aggregation
100+
return result
101+
102+
103+
def aggregation_most_frequent(logits):
104+
"""
105+
This aggregation mechanism takes the softmax/logit output of several models
106+
resulting from inference on identical inputs and computes the most frequent
107+
label. It is deterministic (no noise injection like noisy_max() above.
108+
:param logits: logits or probabilities for each sample
109+
:return:
110+
"""
111+
# Compute labels from logits/probs and reshape array properly
112+
labels = labels_from_probs(logits)
113+
labels_shape = np.shape(labels)
114+
labels = labels.reshape((labels_shape[0], labels_shape[1]))
115+
116+
# Initialize array to hold final labels
117+
result = np.zeros(int(labels_shape[1]))
118+
119+
# Parse each sample
120+
for i in xrange(int(labels_shape[1])):
121+
# Count number of votes assigned to each class
122+
label_counts = np.bincount(labels[:,i], minlength=10)
123+
124+
label_counts = np.asarray(label_counts, dtype=np.int32)
125+
126+
# Result is the most frequent label
127+
result[i] = np.argmax(label_counts)
128+
129+
return np.asarray(result, dtype=np.int32)
130+
131+

0 commit comments

Comments
 (0)