We propose a new way of building Protocol on top of Plan using PlaceHolder.
Example of protocol
@sy.protocol(alice=RoleWorker())
def foo(x):
y = x - 1
y_ptr = y.send(alice)
z_ptr = y_ptr.abs()
return z_ptr
Standard workflow:
# build constructs a tape of logs for each RoleWorker
foo.build(th.Tensor([-1]))
print(foo.plans)
#>out {'local': Plan1, 'alice': Plan2}
foo_deployed = foo.deploy(local=james, alice=bob)
res = foo_deployed.run(th.tensor([1.5]))
print(res)
#>out ptr to bob with remote value=0.5
# redeploy: basically copy and instantiate the Roles
foo_deployed2 = foo.deploy(local=andrew, alice=dan)
protocol.build will create a tape of log on each Role involved (these Role are like a worker that exist just for the build)(note that a local Role is created implicitly in addition to alice). Each tape will become a plan. Each Plan will be composed of a list of Operation or ObjectMessage exclusively. More precisely sending a tensor A -> B will add a ObjectMessage in tape of A. Calling now get()from A on a remote pointer to B will add a ObjectMessage on tape B to get the tensor back. (same strategy for move). During the build, ie when the tracing is on, ie when the tapes are recording, the owner of Placeholders (which is one of the Role worker involved) used in the Operation as arguments is used to determine in which tape goes the operation, and defines the owner of the return placeholders.
Role which should have the same function with workers as the placeholder for tensorstensor.send: it should in particular record the origin and target Roles.protocol.build, from the trace with multiple tapestensor.send corresponds to a remote instantiation of a placeholder.@sy.protocol(
alice=RoleWorker(state=(nn.Linear(2, 3), )),
bob=RoleWorker(state=(nn.Linear(3, 2), ))
)
def foo(x, bias):
"""
The arguments are not necessarily local, here `bias` is a pointer to alice
"""
x = (x - x.mean())/x.std()
x = x.send(alice)
fc1, = alice.state.read()
x = fc1(x) # computation is done on alice
x += bias
x = x.move(bob)
fc2, = bob.state.read()
x = fc2(x) # computation is done on bob
x = x.get()
return F.log_softmax(x, dim=0) # computation is done on the "local worker"
This configuration is not well defined
alice, bob = RoleWorker(), RoleWorker()
class Net(sy.Protocol):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(2, 3)
self.fc2 = nn.Linear(3, 2).send(alice)
self.fc3 = nn.Linear(2, 1).send(bob)
def forward(self, x):
x = F.relu(self.fc1(x))
x = x.send(alice)
x = F.relu(self.fc2(x))
x = x.move(bob)
x = self.fc3(x)
return F.log_softmax(x, dim=0)
@sy.protocol(alice=RoleWorker())
def foo(x):
y =x.send(alice)
z = y.abs()
noise, = alice.torch.randint(0, 10, (1,))
return z + noise
Here, we could create the tensor "remotely" by using worker.torch.<gen_func>: the Operation logged would be in the tape of Alice
torch.ones), but maybe we should handle correctly the new syntax worker.torch.ones [this is not the top priority right now]This is a neat idea! I can definitely see it working, particularly with the worker.torch syntax.
Definitely interesting - but there are a few features missing from the prototype above.
# Secure aggregation protocol
worker1 = sy.VirtualWorker(hook=hook, id="worker1")
worker2 = sy.VirtualWorker(hook=hook, id="worker2")
worker3 = sy.VirtualWorker(hook=hook, id="worker3")
worker1_model = worker1.Promise.Model()
worker2_model = worker2.Promise.Model()
worker3_model = worker3.Promise.Model()
worker1_shares = worker1_model.share(worker1, worker2, worker3)
worker2_shares = worker2_model.share(worker1, worker2, worker3)
worker3_shares = worker3_model.share(worker1, worker2, worker3)
new_model_shares = (worker1_shares + worker2_shares + worker3_shares) / 3
new_model_shares["worker1"].declare_output()
new_model_shares["worker2"].declare_output()
new_model_shares["worker3"].declare_output()
secure_aggregation_protocol = sy.Protocol(worker1, worker2, worker3)
To be clear - I like the interface you propose as an option. I think that yours is an interesting STATIC graph API whereas the one I proposed is a DYNAMIC graph API which can be converted into a static graph. We should support both.
I extended my example to answer some of your questions, I agree this is STATIC for the moment, but it's quite easy to turn to DYNAMIC: activate the tracing mode, do whatever you want, close the trace and pickup the tapes to build a protocol.
From a conversation with @Jasopaum, we're planning to start working on this and have a preliminary checklist of steps forward to get us started:
Can't wait to see progress on this one!
On Mon, Mar 2, 2020 at 5:12 PM Karl Higley notifications@github.com wrote:
From a conversation with @Jasopaum https://github.com/Jasopaum, we're
planning to start working on this and have a preliminary checklist of steps
forward to get us started:
- Extract ComputationAction from OperationMessage
- Create Action base class
- Create CommunicationAction class
- Create a Role class (possibly w/ a worker inside?)
- Create a Protocol class (w/ building and tracing like Plans)
- Partition the trace logs into separate roles
- Send Roles to workers
- Add the ability to execute Actions from Protocol Roles on workers
- Create an Execution abstraction to capture state (inputs, outputs,
intermediate results, etc.)- Add protocol id and execution id fields to message classes
- Update worker's message dispatch to take ids into account
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/OpenMined/PySyft/issues/3008?email_source=notifications&email_token=ABBAZEQYFG6FAYZUCLH26TTRFPSHBA5CNFSM4KP3P7M2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENQEH7Q#issuecomment-593511422,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ABBAZEXSLIPMUHZHWTUYXTTRFPSHBANCNFSM4KP3P7MQ
.
@iamtrask See #3132.
This issue has been marked stale because it has been open 30 days with no activity. Leave a comment or remove the stale label to unmark it. Otherwise, this will be closed in 7 days.
Most helpful comment
From a conversation with @Jasopaum, we're planning to start working on this and have a preliminary checklist of steps forward to get us started: