Syft.js
The official Syft worker for Web and Node, built in Javascript
Install / Use
/learn @OpenMined/Syft.jsREADME
Syft.js
Syft.js is the “web” part of the OpenMined's open-source ecosystem for federated learning, which currently spans across web, iOS, Android, and servers/IoT.
Syft.js has following core features:
- :hammer_and_wrench: Integration with PyGrid federated learning API.
- :gear: Training and inference of any PySyft model written in PyTorch or TensorFlow.
- :bust_in_silhouette: Allows all data to stay on the user's device.
- :lock: Support for secure multi-party computation and secure aggregation protocols using peer-to-peer WebRTC connections (in progress).
The library is built on top of TensorFlow.js.
There are a variety of additional privacy-preserving protections that may be applied, including differential privacy, muliti-party computation, and secure aggregation.
If you want to know how scalable federated systems are built, Towards Federated Learning at Scale is a fantastic introduction!
Installation
Note that syft.js needs Tensorflow.js library as peer dependency.
If you're using a package manager like NPM:
npm install --save @openmined/syft.js @tensorflow/tfjs-core
Or if Yarn is your cup of tea:
yarn add @openmined/syft.js @tensorflow/tfjs-core
If you're not using a package manager, you will be able to include Syft.js within a <script> tag.
In this case library classes will be available under syft global object.
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.2.5/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@openmined/syft.js@latest/dist/index.min.js"></script>
<script type="text/javascript">
// Create syft worker
const worker = syft.Syft({...});
...
</script>
Quick Start
As a developer, there are few steps to building your own secure federated learning system upon the OpenMined infrastructure:
- :robot: Develop ML model and training procedure (aka
Planin PySyft terminology) using PySyft. - :earth_americas: Host model and Plans on PyGrid, which will deal with all the federated learning components of your pipeline.
- :tada: Execute the training on the variety of end-user devices using the client library (syft.js, SwiftSyft, KotlinSyft, PySyft).
- :lock: Securely aggregate trained user models in PyGrid.
:notebook: The entire workflow and process is described in greater detail in the Web & Mobile Federated Learning project roadmap.
Syft.js provides minimalistic API to communicate with federated learning PyGrid endpoints and execute PySyft's Plans in a browser. The federated learning cycle implemented with syft.js would contain following steps:
- Register into training cycle on PyGrid.
- Download required model and Plans from PyGrid.
- Execute the Plan with given model parameters and local user's data (multiple times) to create better model.
- Submit difference between original and trained model parameters for aggregation.
These steps can be expressed in the following code:
import * as tf from '@tensorflow/tfjs-core';
import { Syft } from '@openmined/syft.js';
const gridUrl = 'ws://pygrid.myserver.com:5000';
const modelName = 'my-model';
const modelVersion = '1.0.0';
// if the model is protected with authentication token (optional)
const authToken = '...';
const worker = new Syft({ gridUrl, verbose: true });
const job = await worker.newJob({ modelName, modelVersion, authToken });
job.request();
job.on('accepted', async ({ model, clientConfig }) => {
const batchSize = clientConfig.batch_size;
const lr = clientConfig.lr;
// Load data.
const [data, target] = LOAD_DATA();
const batches = MAKE_BATCHES(data, target, batchSize);
// Load model parameters.
let modelParams = model.params.map((p) => p.clone());
// Main training loop.
for (let [dataBatch, targetBatch] of batches) {
// NOTE: this is just one possible example.
// Plan name (e.g. 'training_plan'), its input arguments and outputs depends on FL configuration and actual Plan implementation.
let updatedModelParams = await job.plans['training_plan'].execute(
job.worker,
dataBatch,
targetBatch,
batchSize,
lr,
...modelParams
);
// Use updated model params in the next iteration.
for (let i = 0; i < modelParams.length; i++) {
modelParams[i].dispose();
modelParams[i] = updatedModelParams[i];
}
}
// Calculate & send model diff.
const modelDiff = await model.createSerializedDiff(modelParams);
await job.report(modelDiff);
});
job.on('rejected', ({ timeout }) => {
// Handle the job rejection, e.g. re-try after timeout.
});
job.on('error', (err) => {
// Handle errors.
});
Model Training API
The Plan execution and Model training can be implemented easier
using training helper that will do training loop for you
(model, batch size, etc. are automatically taken from Job):
// Main training loop.
const training = job.train('training_plan', {
inputs: [/* ... */],
outputs: [/* ... */],
data,
target,
});
training.on('end', async () => {
// Calculate & send model diff.
const modelDiff = await model.createSerializedDiff(modelParams);
await job.report(modelDiff);
});
inputs and outputs need to be specified using PlanInputSpec and PlanOutputSpec
and need to match with Plan's arguments and outputs.
For example, if the Plan has following arguments and outputs:
loss, accuracy, modelParams1, modelParams2, modelParams3, modelParams4 =
plan(dataBatch, targetBatch, batchSize, lr, modelParams1, modelParams2, modelParams3, modelParams4)
Corresponding inputs, outputs in job.train will be:
const inputs = [
new PlanInputSpec(PlanInputSpec.TYPE_DATA),
new PlanInputSpec(PlanInputSpec.TYPE_TARGET),
new PlanInputSpec(PlanInputSpec.TYPE_BATCH_SIZE),
new PlanInputSpec(PlanInputSpec.TYPE_CLIENT_CONFIG_PARAM, 'lr'),
new PlanInputSpec(PlanInputSpec.TYPE_MODEL_PARAM, 'param1', 0),
new PlanInputSpec(PlanInputSpec.TYPE_MODEL_PARAM, 'param2', 1),
new PlanInputSpec(PlanInputSpec.TYPE_MODEL_PARAM, 'param3', 2),
new PlanInputSpec(PlanInputSpec.TYPE_MODEL_PARAM, 'param4', 3),
];
const outputs = [
new PlanOutputSpec(PlanOutputSpec.TYPE_LOSS),
new PlanOutputSpec(PlanOutputSpec.TYPE_METRIC, 'accuracy'),
new PlanOutputSpec(PlanOutputSpec.TYPE_MODEL_PARAM, 'param1', 0),
new PlanOutputSpec(PlanOutputSpec.TYPE_MODEL_PARAM, 'param2', 1),
new PlanOutputSpec(PlanOutputSpec.TYPE_MODEL_PARAM, 'param3', 2),
new PlanOutputSpec(PlanOutputSpec.TYPE_MODEL_PARAM, 'param4', 3),
];
Stop & Resume
PlanTrainer allows stopping and resuming the training using stop and resume methods:
// Main training loop.
const training = job.train('training_plan', {
inputs: [/* ... */],
outputs: [/* ... */],
data,
target,
});
training.on('start', () => {
// training is started!
});
training.on('stop', () => {
// training is stopped!
});
document.getElementById('stop-button').onclick = () => {
training.stop();
};
document.getElementById('resume-button').onclick = () => {
training.resume();
};
Checkpointing
stop method returns current training state as PlanTrainerCheckpoint object,
which can be serialized to JSON to restored from JSON later to continue the training:
const checkpoint = await training.stop();
const checkpointJson = await checkpoint.toJSON();
const checkpointJsonString = JSON.stringify(checkpointJson);
localStorage.setItem('checkpoint', checkpointJsonString);
// ... checkpoint can survive page reload ...
const checkpointJsonString = localStorage.getItem('checkpoint');
const checkpointJson = JSON.parse(checkpointJsonString);
const checkpoint = PlanTrainerCheckpoint.fromJSON(worker, checkpointJson);
// Main training loop.
const training = job.train('training_plan', {
// Pass checkpoint into train method to resume from it
// NOTE: checkpoint doesn't store Plan and training data, these still need to be supplied
checkpoint,
inputs: [/* ... */],
outputs: [/* ... */],
data,
target,
});
Checkpoint can be created directly from PlanTrainer object
using createCheckpoint method and applied back using applyCheckpoint:
const checkpoint = training.createCheckpoint();
// ...
training.applyCheckpoint(checkpoint);
training.resume();
