@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.
- package/package.json +4 -1
- package/src/agent-runtime/builtin-tools-registry.js +6 -3
- package/src/agent-runtime/builtin-tools.js +22 -4
- package/src/commands/dataset.js +70 -3
- package/src/commands/model.js +9 -3
- package/src/index.js +26 -1
- package/src/utils/dataset-workspace.js +140 -1
- package/src/utils/image-backbone.js +144 -0
- package/src/utils/model-trainer.js +39 -12
- package/vendor/backbones/mobilenet-v3-small/SOURCE.md +13 -0
- package/vendor/backbones/mobilenet-v3-small/group1-shard1of2.bin +0 -0
- package/vendor/backbones/mobilenet-v3-small/group1-shard2of2.bin +0 -0
- package/vendor/backbones/mobilenet-v3-small/model.json +1 -0
|
@@ -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
|
|
9
|
+
* v1 modalities:
|
|
10
10
|
* - tabular: numeric feature vectors → standardized → dense head
|
|
11
11
|
* - text: hashing vectorizer (pure JS) → dense head
|
|
12
|
-
* image
|
|
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
|
-
/**
|
|
58
|
-
function
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
Binary file
|
|
Binary file
|