@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.
@@ -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, waitMessageWithTimeout, WebSocketServer, } from "../event_connection.js";
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 waitMessageWithTimeout(this.server, type.NewFederatedNodeInfo);
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
@@ -4,3 +4,4 @@ export { mnist } from './mnist.js';
4
4
  export { simpleFace } from './simple_face.js';
5
5
  export { titanic } from './titanic.js';
6
6
  export { wikitext } from './wikitext.js';
7
+ export { tinderDog } from './tinder_dog.js';
@@ -4,3 +4,4 @@ export { mnist } from './mnist.js';
4
4
  export { simpleFace } from './simple_face.js';
5
5
  export { titanic } from './titanic.js';
6
6
  export { wikitext } from './wikitext.js';
7
+ export { tinderDog } from './tinder_dog.js';
@@ -0,0 +1,2 @@
1
+ import type { TaskProvider } from '../index.js';
2
+ export declare const tinderDog: TaskProvider<'image'>;
@@ -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");
@@ -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
- const preprocessed = await processing.preprocess(this.#task, dataset);
138
- const [training, validation] = (this.#preprocessOnce
137
+ let preprocessed = await processing.preprocess(this.#task, dataset);
138
+ preprocessed = (this.#preprocessOnce
139
139
  ? new Dataset(await arrayFromAsync(preprocessed))
140
- : preprocessed).split(validationSplit);
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(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epfml/discojs",
3
- "version": "3.0.1-p20241107104659.0",
3
+ "version": "3.0.1-p20241203151748.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",