@myvillage/cli 1.72.0 → 1.74.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.
@@ -6,19 +6,23 @@
6
6
  * Studio shells out to that same command. This is the single training engine;
7
7
  * the Studio is just a front-door that streams the progress it emits.
8
8
  *
9
- * v1 modalities (zero downloaded backbones):
9
+ * v1 modalities:
10
10
  * - tabular: numeric feature vectors → standardized → dense head
11
11
  * - text: hashing vectorizer (pure JS) → dense head
12
- * image/audio need a bundled vision/audio backbone and land in a later slice.
12
+ * - image: bundled MobileNetV3-Small backbone → 1024-d embedding → dense head
13
+ * audio needs a bundled audio backbone and lands in a later slice.
13
14
  *
14
15
  * Transfer-learning-style split: featurize once, then train a small head. The
15
16
  * featurization params travel in the model record so predict() needs no data.
17
+ * (For image, the backbone itself is bundled — see image-backbone.js — so the
18
+ * embedding can always be recomputed at predict time from the same weights.)
16
19
  */
17
20
  import * as tf from '@tensorflow/tfjs';
18
- import { readRows } from './dataset-workspace.js';
21
+ import { readRows, getAssetPath } from './dataset-workspace.js';
22
+ import { embedImage, imageFeaturization } from './image-backbone.js';
19
23
 
20
24
  const TEXT_DIM = 1024;
21
- export const TRAINABLE_MODALITIES = ['tabular', 'text'];
25
+ export const TRAINABLE_MODALITIES = ['tabular', 'text', 'image'];
22
26
 
23
27
  // ── Featurization ──────────────────────────────────────
24
28
 
@@ -54,8 +58,22 @@ function toNumericVector(features) {
54
58
  throw new Error('Tabular features must be a numeric vector.');
55
59
  }
56
60
 
57
- /** Build the feature matrix + the featurization params to persist. */
58
- function featurize(rows, modality) {
61
+ /** Resolve an image row's features to an absolute file path on disk. */
62
+ function imagePathOf(features, slug) {
63
+ // Rows store an AssetRef {kind,path}; path is relative to the dataset assets/.
64
+ const rel = typeof features === 'string' ? features : features?.path;
65
+ if (!rel) throw new Error('Image rows must reference an image asset.');
66
+ return getAssetPath(slug, rel);
67
+ }
68
+
69
+ /** Build the feature matrix + the featurization params to persist. Async to
70
+ * allow the image backbone to embed each example off disk. */
71
+ async function featurize(rows, modality, slug) {
72
+ if (modality === 'image') {
73
+ const X = [];
74
+ for (const r of rows) X.push(await embedImage(imagePathOf(r.features, slug)));
75
+ return { X, featurization: imageFeaturization() };
76
+ }
59
77
  if (modality === 'text') {
60
78
  return { X: rows.map((r) => vectorizeText(r.features)), featurization: { modality, dim: TEXT_DIM } };
61
79
  }
@@ -75,8 +93,15 @@ function featurize(rows, modality) {
75
93
  return { X, featurization: { modality, dim, mean, std } };
76
94
  }
77
95
 
78
- /** Featurize a single input the same way (for predict). */
79
- export function featurizeOne(input, featurization) {
96
+ /** Featurize a single input the same way (for predict). Async because the
97
+ * image path runs the backbone; text/tabular resolve synchronously. */
98
+ export async function featurizeOne(input, featurization) {
99
+ if (featurization.modality === 'image') {
100
+ // input is an absolute image file path (CLI --image / model_predict tool).
101
+ const path = typeof input === 'string' ? input : input?.path;
102
+ if (!path) throw new Error('Image prediction needs an image file path.');
103
+ return embedImage(path);
104
+ }
80
105
  if (featurization.modality === 'text') return vectorizeText(input);
81
106
  const v = toNumericVector(input);
82
107
  return v.map((x, j) => (x - featurization.mean[j]) / (featurization.std[j] || 1));
@@ -119,7 +144,7 @@ export async function trainModel(slug, manifest, { epochs = 40, onProgress } = {
119
144
  if (rows.length < 4) throw new Error('Need at least 4 examples to train (more is better).');
120
145
 
121
146
  const task = manifest.taskType;
122
- const { X, featurization } = featurize(rows, manifest.modality);
147
+ const { X, featurization } = await featurize(rows, manifest.modality, slug);
123
148
  const inputDim = X[0].length;
124
149
 
125
150
  // Build targets: one-hot rows (classification) or standardized scalars (regression)
@@ -226,10 +251,12 @@ function computeMetrics(model, xs, ys, task, labels, targetMean, targetStd) {
226
251
  });
227
252
  }
228
253
 
229
- /** Run a prediction with an already-loaded model + its record. */
230
- export function predictWithModel(model, record, input) {
254
+ /** Run a prediction with an already-loaded model + its record. Async because
255
+ * image featurization runs the backbone; the head forward pass stays in a
256
+ * tidy() so intermediate tensors are freed. */
257
+ export async function predictWithModel(model, record, input) {
258
+ const feats = await featurizeOne(input, record.featurization);
231
259
  return tf.tidy(() => {
232
- const feats = featurizeOne(input, record.featurization);
233
260
  const out = model.predict(tf.tensor2d([feats]));
234
261
  if (record.taskType === 'classification') {
235
262
  const scores = Array.from(out.dataSync());
@@ -0,0 +1,13 @@
1
+ # MobileNetV3-Small (TF.js GraphModel) — vendored backbone
2
+
3
+ Bundled feature-extractor for the Studio "Lab" pillar image classification.
4
+ **Never downloaded at runtime** — shipped inside `@myvillage/cli` so the Studio
5
+ stays "the only thing you install" (see ed-platform docs/architecture/studio-models-pillar.md §0b, §13).
6
+
7
+ - **Model:** MobileNetV3-Small, 1.0 depth, 224×224, ImageNet feature-vector head removed
8
+ - **Input:** `[-1, 224, 224, 3]` float32, pixels normalized to `[0, 1]`
9
+ - **Output:** `[-1, 1024]` embedding (fed to the trained dense head)
10
+ - **Format:** TensorFlow.js `graph-model` (model.json + 2 weight shards, ~5.85 MB)
11
+ - **Source:** Google MobileNetV3 TfJs feature-vector (`small-100-224`), Kaggle Models
12
+ (`google/mobilenet-v3`), converted by TensorFlow.js Converter v3.0.0
13
+ - **License:** Apache-2.0 (TensorFlow Hub / Google)