@epfml/discojs 3.0.1-p20241107104659.0 → 3.0.1-p20241203151748.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/client.js +2 -0
- package/dist/client/federated/federated_client.js +2 -2
- package/dist/default_tasks/index.d.ts +1 -0
- package/dist/default_tasks/index.js +1 -0
- package/dist/default_tasks/tinder_dog.d.ts +2 -0
- package/dist/default_tasks/tinder_dog.js +72 -0
- package/dist/task/task_handler.js +5 -1
- package/dist/training/disco.js +6 -3
- package/package.json +1 -1
package/dist/client/client.js
CHANGED
|
@@ -149,6 +149,8 @@ export class Client extends EventEmitter {
|
|
|
149
149
|
}
|
|
150
150
|
url.pathname += `tasks/${this.task.id}/model.json`;
|
|
151
151
|
const response = await fetch(url);
|
|
152
|
+
if (!response.ok)
|
|
153
|
+
throw new Error(`fetch: HTTP status ${response.status}`);
|
|
152
154
|
const encoded = new Uint8Array(await response.arrayBuffer());
|
|
153
155
|
return await serialization.model.decode(encoded);
|
|
154
156
|
}
|
|
@@ -2,7 +2,7 @@ import createDebug from "debug";
|
|
|
2
2
|
import { serialization } from "../../index.js";
|
|
3
3
|
import { Client, shortenId } from "../client.js";
|
|
4
4
|
import { type } from "../messages.js";
|
|
5
|
-
import { waitMessage,
|
|
5
|
+
import { waitMessage, WebSocketServer, } from "../event_connection.js";
|
|
6
6
|
import * as messages from "./messages.js";
|
|
7
7
|
const debug = createDebug("discojs:client:federated");
|
|
8
8
|
/**
|
|
@@ -53,7 +53,7 @@ export class FederatedClient extends Client {
|
|
|
53
53
|
type: type.ClientConnected,
|
|
54
54
|
};
|
|
55
55
|
this.server.send(msg);
|
|
56
|
-
const { id, waitForMoreParticipants, payload, round, nbOfParticipants } = await
|
|
56
|
+
const { id, waitForMoreParticipants, payload, round, nbOfParticipants } = await waitMessage(this.server, type.NewFederatedNodeInfo);
|
|
57
57
|
// This should come right after receiving the message to make sure
|
|
58
58
|
// we don't miss a subsequent message from the server
|
|
59
59
|
// We check if the server is telling us to wait for more participants
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
import { models } from '../index.js';
|
|
3
|
+
export const tinderDog = {
|
|
4
|
+
getTask() {
|
|
5
|
+
return {
|
|
6
|
+
id: 'tinder_dog',
|
|
7
|
+
displayInformation: {
|
|
8
|
+
taskTitle: 'GDHF 2024 | TinderDog',
|
|
9
|
+
summary: {
|
|
10
|
+
preview: 'Which dog is the cutest....or not?',
|
|
11
|
+
overview: "Binary classification model for dog cuteness."
|
|
12
|
+
},
|
|
13
|
+
model: 'The model is a simple Convolutional Neural Network composed of two convolutional layers with ReLU activations and max pooling layers, followed by a fully connected output layer. The data preprocessing reshapes images into 64x64 pixels and normalizes values between 0 and 1',
|
|
14
|
+
dataFormatInformation: 'Accepted image formats are .png .jpg and .jpeg.',
|
|
15
|
+
dataExampleText: '',
|
|
16
|
+
dataExampleImage: 'https://storage.googleapis.com/deai-313515.appspot.com/tinder_dog_preview.png',
|
|
17
|
+
sampleDatasetLink: 'https://storage.googleapis.com/deai-313515.appspot.com/tinder_dog.zip',
|
|
18
|
+
sampleDatasetInstructions: 'Opening the link should start downloading a zip file which you can unzip. To connect the data, pick one of the data splits (the folder 0 for example) and use the CSV option below to select the file named "labels.csv". You can now connect the images located in the same folder.'
|
|
19
|
+
},
|
|
20
|
+
trainingInformation: {
|
|
21
|
+
epochs: 10,
|
|
22
|
+
roundDuration: 2,
|
|
23
|
+
validationSplit: 0, // nicer plot for GDHF demo
|
|
24
|
+
batchSize: 10,
|
|
25
|
+
dataType: 'image',
|
|
26
|
+
IMAGE_H: 64,
|
|
27
|
+
IMAGE_W: 64,
|
|
28
|
+
LABEL_LIST: ['Cute dogs', 'Less cute dogs'],
|
|
29
|
+
scheme: 'federated',
|
|
30
|
+
aggregationStrategy: 'mean',
|
|
31
|
+
minNbOfParticipants: 3,
|
|
32
|
+
tensorBackend: 'tfjs'
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
async getModel() {
|
|
37
|
+
const seed = 42; // set a seed to ensure reproducibility during GDHF demo
|
|
38
|
+
const imageHeight = this.getTask().trainingInformation.IMAGE_H;
|
|
39
|
+
const imageWidth = this.getTask().trainingInformation.IMAGE_W;
|
|
40
|
+
const imageChannels = 3;
|
|
41
|
+
const model = tf.sequential();
|
|
42
|
+
model.add(tf.layers.conv2d({
|
|
43
|
+
inputShape: [imageHeight, imageWidth, imageChannels],
|
|
44
|
+
kernelSize: 5,
|
|
45
|
+
filters: 8,
|
|
46
|
+
activation: 'relu',
|
|
47
|
+
kernelInitializer: tf.initializers.heNormal({ seed })
|
|
48
|
+
}));
|
|
49
|
+
model.add(tf.layers.conv2d({
|
|
50
|
+
kernelSize: 5, filters: 16, activation: 'relu',
|
|
51
|
+
kernelInitializer: tf.initializers.heNormal({ seed })
|
|
52
|
+
}));
|
|
53
|
+
model.add(tf.layers.maxPooling2d({ poolSize: 2, strides: 2 }));
|
|
54
|
+
model.add(tf.layers.dropout({ rate: 0.25, seed }));
|
|
55
|
+
model.add(tf.layers.flatten());
|
|
56
|
+
model.add(tf.layers.dense({
|
|
57
|
+
units: 32, activation: 'relu',
|
|
58
|
+
kernelInitializer: tf.initializers.heNormal({ seed })
|
|
59
|
+
}));
|
|
60
|
+
model.add(tf.layers.dropout({ rate: 0.25, seed }));
|
|
61
|
+
model.add(tf.layers.dense({
|
|
62
|
+
units: 2, activation: 'softmax',
|
|
63
|
+
kernelInitializer: tf.initializers.heNormal({ seed })
|
|
64
|
+
}));
|
|
65
|
+
model.compile({
|
|
66
|
+
optimizer: tf.train.adam(0.0005),
|
|
67
|
+
loss: 'categoricalCrossentropy',
|
|
68
|
+
metrics: ['accuracy']
|
|
69
|
+
});
|
|
70
|
+
return Promise.resolve(new models.TFJS('image', model));
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -9,7 +9,7 @@ function urlToTasks(base) {
|
|
|
9
9
|
return ret;
|
|
10
10
|
}
|
|
11
11
|
export async function pushTask(base, task, model) {
|
|
12
|
-
await fetch(urlToTasks(base), {
|
|
12
|
+
const response = await fetch(urlToTasks(base), {
|
|
13
13
|
method: "POST",
|
|
14
14
|
body: JSON.stringify({
|
|
15
15
|
task,
|
|
@@ -17,9 +17,13 @@ export async function pushTask(base, task, model) {
|
|
|
17
17
|
weights: await serialization.weights.encode(model.weights),
|
|
18
18
|
}),
|
|
19
19
|
});
|
|
20
|
+
if (!response.ok)
|
|
21
|
+
throw new Error(`fetch: HTTP status ${response.status}`);
|
|
20
22
|
}
|
|
21
23
|
export async function fetchTasks(base) {
|
|
22
24
|
const response = await fetch(urlToTasks(base));
|
|
25
|
+
if (!response.ok)
|
|
26
|
+
throw new Error(`fetch: HTTP status ${response.status}`);
|
|
23
27
|
const tasks = await response.json();
|
|
24
28
|
if (!Array.isArray(tasks)) {
|
|
25
29
|
throw new Error("Expected to receive an array of Tasks when fetching tasks");
|
package/dist/training/disco.js
CHANGED
|
@@ -134,10 +134,13 @@ export class Disco extends EventEmitter {
|
|
|
134
134
|
}
|
|
135
135
|
async #preprocessSplitAndBatch(dataset) {
|
|
136
136
|
const { batchSize, validationSplit } = this.#task.trainingInformation;
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
let preprocessed = await processing.preprocess(this.#task, dataset);
|
|
138
|
+
preprocessed = (this.#preprocessOnce
|
|
139
139
|
? new Dataset(await arrayFromAsync(preprocessed))
|
|
140
|
-
: preprocessed)
|
|
140
|
+
: preprocessed);
|
|
141
|
+
if (validationSplit === 0)
|
|
142
|
+
return [preprocessed.batch(batchSize).cached(), undefined];
|
|
143
|
+
const [training, validation] = preprocessed.split(validationSplit);
|
|
141
144
|
return [
|
|
142
145
|
training.batch(batchSize).cached(),
|
|
143
146
|
validation.batch(batchSize).cached(),
|