Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
47 changes: 47 additions & 0 deletions federatedscope/caesar_v_fl/Paillier/abstract_paillier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# You can refer to pyphe for the detail implementation. (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is the same as federatedscope/vertical_fl/Paillier/abstract_paillier.py, maybe you can reuse it.

# https://github.com/data61/python-paillier/blob/master/phe/paillier.py)
# Or implement an effective version of Paillier (<Public-key cryptosystems
# based on composite degree residuosity classes>)

DEFAULT_KEYSIZE = 3072


def generate_paillier_keypair(n_length=DEFAULT_KEYSIZE):
"""Generate public key and private key used Paillier`.

Args:
n_length: key size in bits.

Returns:
tuple: The generated :class:`PaillierPublicKey` and
:class:`PaillierPrivateKey`
"""
n = p = q = None
public_key = PaillierPublicKey(n)
private_key = PaillierPrivateKey(public_key, p, q)

return public_key, private_key


class PaillierPublicKey(object):
"""Contains a public key and associated encryption methods.
"""
def __init__(self, n):
pass

def encrypt(self, value):
# We only provide an abstract implementation here

return value


class PaillierPrivateKey(object):
"""Contains a private key and associated decryption method.
"""
def __init__(self, public_key, p, q):
pass

def decrypt(self, encrypted_number):
# We only provide an abstract implementation here

return encrypted_number
13 changes: 13 additions & 0 deletions federatedscope/caesar_v_fl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
### Caesar Vertical Federated Learning

We provide an example for seCure lArge-scalE SlArse logistic Regression (caesar) vertical federated learning, you can run with:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the references here.

```bash
python3 ../main.py --cfg caesar_v_fl.yaml
```

You can specify customized configurations in `caesar_v_fl.yaml`, such as `data.type` and `federate.total_round_num`.
More details of the provided example can be found in [Tutorial](https://federatedscope.io/docs/cross-silo/).

Note that FederatedScope only provide an `abstract_paillier`, user can refer to [pyphe](https://github.com/data61/python-paillier/blob/master/phe/paillier.py) for the detail implementation, or adopt other homomorphic encryption algorithms.

More support for vertical federated learning is coming soon! We really appreciate any contributions to FederatedScope !
1 change: 1 addition & 0 deletions federatedscope/caesar_v_fl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from federatedscope.caesar_v_fl.Paillier.abstract_paillier import *
22 changes: 22 additions & 0 deletions federatedscope/caesar_v_fl/caesar_v_fl.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use_gpu: False
federate:
mode: standalone
client_num: 2
total_round_num: 30
model:
type: lr
use_bias: False
train:
optimizer:
lr: 0.05
data:
type: caesar_v_fl_data
batch_size: 50
caesar_vertical:
use: True
key_size: 256
trainer:
type: none
eval:
freq: 5
best_res_update_round_wise_key: test_loss
4 changes: 4 additions & 0 deletions federatedscope/caesar_v_fl/dataloader/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from federatedscope.caesar_v_fl.dataloader.dataloader \
import load_caesar_v_fl_data

__all__ = ['load_caesar_v_fl_data']
86 changes: 86 additions & 0 deletions federatedscope/caesar_v_fl/dataloader/dataloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import numpy as np


def load_caesar_v_fl_data(config=None, generate=False):
"""
To generate the synthetic data for vertical FL

Arguments:
config: configuration
generate (bool): whether to generate the synthetic data
:returns: The synthetic data, the modified config
:rtype: dict
"""

if generate:
# generate toy data for running a vertical FL example
INSTANCE_NUM = 1000
TRAIN_SPLIT = 0.9

total_dims = np.sum(config.caesar_vertical.dims)
theta = np.random.uniform(low=-1.0, high=1.0, size=(total_dims, 1))
x = np.random.choice([-1.0, 1.0, -2.0, 2.0, -3.0, 3.0],
size=(INSTANCE_NUM, total_dims))
y = np.asarray([
1.0 if x >= 0 else -1.0
for x in np.reshape(np.matmul(x, theta), -1)
])

def standardize(X):
m, n = X.shape
for j in range(n):
features = X[:, j]
meanVal = features.mean(axis=0)
std = features.std(axis=0)
if std != 0:
X[:, j] = (features - meanVal) / std
else:
X[:, j] = 0
return X

def normalize(X):
m, n = X.shape
for j in range(n):
features = X[:, j]
minVal = features.min(axis=0)
maxVal = features.max(axis=0)
diff = maxVal - minVal
if diff != 0:
X[:, j] = (features - minVal) / diff
else:
X[:, j] = 0
return X

x = standardize(x)
# x = normalize(x)

train_num = int(TRAIN_SPLIT * INSTANCE_NUM)
test_data = {'theta': theta, 'x': x[train_num:], 'y': y[train_num:]}
data = dict()

# For Server
data[0] = dict()
data[0]['train'] = None
data[0]['val'] = None
data[0]['test'] = test_data

# For Client #1
data[1] = dict()
data[1]['train'] = {
'x': x[:train_num, :config.caesar_vertical.dims[0]]
}
data[1]['val'] = None
data[1]['test'] = test_data

# For Client #2
data[2] = dict()
data[2]['train'] = {
'x': x[:train_num, config.caesar_vertical.dims[0]:],
'y': y[:train_num]
}
data[2]['val'] = None
data[2]['test'] = test_data

return data, config
else:
raise ValueError('You must provide the data file')
30 changes: 30 additions & 0 deletions federatedscope/caesar_v_fl/dataloader/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import numpy as np
import math


def batch_iter(data, batch_size, shuffled=True):
"""
A batch iteration

Arguments:
data(dict): data
batch_size (int): the batch size
shuffled (bool): whether to shuffle the data at the start of each epoch
:returns: sample index, batch of x, batch_of y
:rtype: int, ndarray, ndarray
"""

assert 'x' in data and 'y' in data
data_x = data['x']
data_y = data['y']
data_size = len(data_y)
num_batches_per_epoch = math.ceil(data_size / batch_size)

while True:
shuffled_index = np.random.permutation(
np.arange(data_size)) if shuffled else np.arange(data_size)
for batch in range(num_batches_per_epoch):
start_index = batch * batch_size
end_index = min(data_size, (batch + 1) * batch_size)
sample_index = shuffled_index[start_index:end_index]
yield sample_index, data_x[sample_index], data_y[sample_index]
4 changes: 4 additions & 0 deletions federatedscope/caesar_v_fl/worker/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from federatedscope.caesar_v_fl.worker.vertical_client import vFLClient
from federatedscope.caesar_v_fl.worker.vertical_server import vFLServer

__all__ = ['vFLServer', 'vFLClient']
Loading