| name | tinyml-workflow-agent |
| description | Guides users through end-to-end Tiny ML model creation, training, compilation, and device deployment. Use when user mentions creating AI solutions, AI models, training for embedded devices, deploying to MCUs, or any tiny ML workflow. Covers config creation, model training, compilation, and Code Composer Studio (TI CCStudio IDE) flashing. Always confirm tinyml-tensorlab installation path before proceeding. |
Tiny ML Tensorlab Skill
Overview
Build, train, compile, and deploy ML models to embedded MCUs using Tiny ML tensorlab.
CRITICAL: Script and Path Locations
runner.py and SCRIPTS_DIR are in THIS SKILL directory, NOT in tinyml-tensorlab.
When setting up:
SCRIPTS_DIR=<path-to-this-skill>/scripts
$SCRIPTS_DIR/runner.py
Do NOT use:
~/tinyml-tensorlab/scripts/ (wrong — this doesn't exist)
~/tinyml-tensorlab/tinyml-modelmaker/scripts/ (wrong)
Use:
~/.claude/plugins/marketplaces/<plugin_name>/skills/<skill_name>/scripts/ (correct — this is the skill)
Workflow (13 steps):
- Session setup — load
.env, activate venv, check for updates. If not set up, run /tinyml-agent-skills:setup first.
- Requirements — task type, device, data type, channel count
- Common section — task_type + target_device → save to WORK_DIR
- Dataset validation — validate format, get effective path
- Dataset section — generate YAML → save to WORK_DIR
- Feature extraction and Data Processing transforms
- Step 6A: Analyze dataset for statistical insights
- Step 6B: Get recommendations → generate YAML → save to WORK_DIR
- Model selection — analyze dataset size → rank models
- Training section — model name + hyperparams → save to WORK_DIR
- Testing section — testing config → save to WORK_DIR
- Compilation section — preset selection → save to WORK_DIR
- Assemble config — combine all sections → write config.yaml
- Run training — execute run_tinyml_modelzoo.sh
- Deploy to device — create CCS project → flash
IMPORTANT: After EACH step which generates a section of the config file, pause and show the user the config file (created thus far) and proceed only with user's approval of the config.
Reference guides (read on demand, not upfront):
references/config_creation_guide.md — task types, devices, YAML rules
references/example_running_guide.md — run commands, monitoring, troubleshooting
references/device_deployment_guide.md — CCS project, flashing, validation
Session Setup (do this before Step 1) NEVER SKIP THIS
1. Discover SCRIPTS_DIR
Find the tinyml-workflow-agent runner (works on all platforms — search under ~/.claude):
find ~/.claude -name "runner.py" 2>/dev/null | grep "tinyml-workflow-agent" | head -1
Strip /runner.py from the result to get SCRIPTS_DIR.
If not found, ask the user where the tinyml-agent-skills plugin is installed.
2. Load environment
Check if .env exists at ~/.tinyml-agent-skills/.env (same location on all platforms — Linux, macOS, Windows).
If it exists, load and export these variables:
| Variable | Description |
|---|
IS_REPO_SETUP | Indicates if tinyml-tensorlab is setup |
TINYML_BASE_PATH | Root of tinyml-tensorlab checkout |
TINYML_TENSORLAB_DOCS_PATH | Path to documentation |
Inform the user:
".env loaded from: ~/.tinyml-agent-skills/.env"
"Session variables ready: IS_REPO_SETUP, TINYML_BASE_PATH, TINYML_TENSORLAB_DOCS_PATH"
If .env is missing or any variable is unset: stop and tell the user:
"Run /tinyml-agent-skills:setup first. It will create ~/.tinyml-agent-skills/.env with your configuration."
Do not proceed until setup is done.
2. Activate virtual environment
Check if a venv exists first:
ls "$TINYML_BASE_PATH/tinyml-modelmaker/venv/bin/activate" 2>/dev/null && echo "venv found" || echo "no venv"
- venv found — activate it:
source "$TINYML_BASE_PATH/tinyml-modelmaker/venv/bin/activate"
- no venv — user may have installed packages globally or via pyenv/conda. Do NOT force a venv. Proceed with current Python environment. If packages are still not found, ask the user regarding the same. And if you are told the packages are not yet installed, direct them to run
/tinyml-agent-skills:setup before running the tinyml-workflow-agent workflow.
3. Check for updates
python3 $SCRIPTS_DIR/runner.py get_update_status '{}'
initialized: false → tell user to run /tinyml-agent-skills:setup first. Stop.
mode: "auto" → run python3 $SCRIPTS_DIR/runner.py check_updates '{}'
update_available: true → show message from check_updates output and ask: "Update now?"
- Yes:
python3 $SCRIPTS_DIR/runner.py do_update '{"tinyml_base_path": "$TINYML_BASE_PATH"}'
- No: proceed with current version
update_available: false → proceed normally
mode: "pinned" → proceed normally
Always create config files in the /examples directory within tinyml-tensorlab/tinyml-modelzoo.
Session state — track these throughout all steps. .env variables are pre-loaded from setup. Remaining variables set progressively; do NOT write them to .env.
| Variable | Source | Description |
|---|
SCRIPTS_DIR | discovered | Absolute path to scripts/ folder |
IS_REPO_SETUP | ~/.tinyml-agent-skills/.env | tinyml-tensorlab setup flag |
TINYML_BASE_PATH | ~/.tinyml-agent-skills/.env | Root of tinyml-tensorlab checkout |
TINYML_TENSORLAB_DOCS_PATH | ~/.tinyml-agent-skills/.env | Path to documentation |
TASK_NAME | Step 2 | Slug used for config subdirectory |
WORK_DIR | Step 2 | Temp dir for intermediate section YAMLs |
CONFIG_DIR | Step 11 | Permanent dir where config.yaml is saved |
TASK_TYPE | Step 2 | e.g., motor_fault |
TARGET_DEVICE | Step 2 | e.g., F28P55 |
TARGET_MODULE | Step 3 | timeseries or vision |
VARIABLES | Step 2/6 | Number of sensor channels |
DATA_PATH | Step 2 | User's raw dataset path |
EFFECTIVE_DATA_PATH | Step 4 | Path after any auto-reorganization |
QUANTIZATION_MODE | Step 8C | 0/1/2 — drives compilation preset |
NAS_ENABLED | Step 8D | true/false |
Error Handling Rule
After every runner.py call: check success in the JSON output.
"success": true → proceed to next step
"success": false → read errors array, fix the issue, re-run the same step
Never skip to the next step while success is false. The errors field always describes what went wrong and what to fix.
Step 1: Session setup
See "Session Setup" above. Do not proceed until:
check_installation returns success: true
IS_REPO_SETUP is set to true or 1
- All four setup variables (
SCRIPTS_DIR, IS_REPO_SETUP, TINYML_BASE_PATH, TINYML_TENSORLAB_DOCS_PATH) are set and saved to .env
- Virtual environment is activated
- [MANDATORY — every session, no exceptions] Step 3 ("Check for updates") run and completed — update mode checked, acted accordingly
Step 2: Understand requirements
Ask the user these questions (all answers needed before proceeding):
-
Task type — "What kind of ML task? Classification, anomaly detection, regression, or forecasting?"
- Use
list_supported_values to show exact valid strings:
python3 $SCRIPTS_DIR/runner.py list_supported_values '{"parameter_type": "task_type"}'
-
Target device — "Which MCU are you targeting?"
python3 $SCRIPTS_DIR/runner.py list_supported_values '{"parameter_type": "target_device"}'
-
Data — "Where is your dataset? (local path or URL)" and "How many sensor channels/variables does it have?"
-
Task name — pick a name for the config directory, e.g., motor_fault_demo
Set session variables from answers:
TASK_TYPE=<answer>
TARGET_DEVICE=<answer>
VARIABLES=<answer>
DATA_PATH=<answer>
TASK_NAME=<answer>
WORK_DIR=$(mktemp -d -t tinyml_${TASK_NAME}_XXXXXX)
echo "Work dir: $WORK_DIR"
Task-type disambiguation example:
User: "motor fault detection"
→ Ask: "Classify fault vs healthy (motor_fault / generic_timeseries_classification)
or detect anomalies (generic_timeseries_anomalydetection)?"
Step 3: Generate common section
python3 $SCRIPTS_DIR/runner.py generate_common_section_yaml \
"{\"task_type\": \"$TASK_TYPE\", \"target_device\": \"$TARGET_DEVICE\"}" \
--save-yaml $WORK_DIR/common.yaml
- On
success: true: note inferred_module (set as TARGET_MODULE)
- On
success: false: show errors, call list_supported_values for the invalid parameter, correct and retry
python3 $SCRIPTS_DIR/runner.py generate_common_section_yaml \
"{\"task_type\": \"$TASK_TYPE\", \"target_device\": \"$TARGET_DEVICE\", \"run_name\": \"{date-time}/{model_name}\"}" \
--save-yaml $WORK_DIR/common.yaml
Step 4: Validate dataset format
This step checks the dataset directory structure and auto-fixes it if possible.
It returns effective_input_data_path — which may differ from DATA_PATH if data was reorganized.
python3 $SCRIPTS_DIR/runner.py validate_dataset_section \
"{\"enable\": true, \"dataset_name\": \"my_dataset\", \"input_data_path\": \"$DATA_PATH\", \"task_type\": \"$TASK_TYPE\"}"
Read the output carefully:
Stage 1: Format validation
If success: true:
- Set
EFFECTIVE_DATA_PATH to effective_input_data_path from output (use this, not DATA_PATH, from now on):
EFFECTIVE_DATA_PATH=<value from effective_input_data_path>
If success: false:
- Create a copy of the user-given dataset.
- Manually re-organize the copy into the required structure per
$SCRIPTS_DIR/constants.py → EXPECTED_STRUCTURES[{task_family}].
- Set
EFFECTIVE_DATA_PATH to the reorganized copy's path and re-run validate_dataset_section.
Step 5: Generate dataset section
Always ask user for the below params:
- Split type:
amongst_files or within_files
IMPORTANT: If after formatting, each class has only ONE csv(/txt/npy or any supported format) file, then split_type of within_files MUST be used
- Split factor: e.g.
[0.6, 0.3, 0.1] -> THIS IS JUST AN EXAMPLE
IMPORTANT: If user does not know or does not specify, then create WITHOUT the above two params. Inform user that they will be picked from params.py (default values).
python3 $SCRIPTS_DIR/runner.py generate_dataset_section_yaml \
"{\"enable\": true, \"dataset_name\": \"my_dataset\", \"input_data_path\": \"$EFFECTIVE_DATA_PATH\"}" \
--save-yaml $WORK_DIR/dataset.yaml
With optional split params:
python3 $SCRIPTS_DIR/runner.py generate_dataset_section_yaml \
"{\"enable\": true, \"dataset_name\": \"my_dataset\", \"input_data_path\": \"$EFFECTIVE_DATA_PATH\", \"split_type\": \"USER_GIVEN_SPLIT_TYPE\" \"split_factor\": {USER_GIVEN_SPLIT_FACTOR}}" \
--save-yaml $WORK_DIR/dataset.yaml
Step 6A: Analyze dataset for statistical insights
Run:
python3 $SCRIPTS_DIR/runner.py analyse_dataset \
"{\"formatted_dataset_path\": \"$EFFECTIVE_DATA_PATH\", \"task_family\": \"{classification/anomalydetection/regression/forecasting}\"}"
Use the output for downstream tasks - particularly for the feature extraction presets/transforms, data processing presets/transforms, and model selection recommendations. To persist the results for the rest of the session, follow the below steps:
-
Create a temporary file titled .tmp_dataset_stats.json within the temporary $WORK_DIR.
-
Store the following JSON in it's exact format in the above file:
{
"result":{
"dataset_bucket": <tiny|small|medium|large>,
"dataset_size": <total_num_samples>
"task_type": $TASK_TYPE,
"min_sample_or_seq_length":
if task_family is classification add the below key:
"data_distribution": {
class_{cls1}:<total_num_of_entries_in_cls1>,
class_{cls2}:<total_num_of_entries_in_cls2>,
...
},
if task_family is anomalydetection add the below key:
"data_distribution": {
normal:<total_num_of_normal_entries>,
anomalous:<total_num_of_anomalous_entries>
},
if task_family is regression add the below key:
"data_distribution": {
datafile1:<total_num_of_entries_in_datafile1>,
datafile2:<total_num_of_entries_in_datafile2>
},
if task_family is forecasting add the below key:
"data_distribution": {
sequence1:<total_num_of_entries_in_sequence1>,
sequence2:<total_num_of_entries_in_sequence2>
},
"formatted_dataset_path": <path to formatted dataset (step 3 or step 2 if dataset was already in correct format)>
}
}
Step 6B: Generate feature extraction and Data Processing transforms
Part A — get recommendations (required):
Follow the below flowchart first to intelligently select values for the mentioned params:
Param Selection FlowChart:
Is the pattern of the data in frequency content?
|-- Yes --> Use FFT-based preset --> set prefer_fft to true
| |-- Need full spectrum? --> set need_full_spectrum to true
| |-- Reduce features? --> Binning to be used --> need_full_spectrum set to false
|-- No --> Use RAW preset --> set prefer_fft to false
|-- Need temporal context? --> Multi-frame --> set need_temporal_ctx to true
|-- Single snapshot? --> 1Frame --> set need_temporal_ctx to false
Based on the above flowchart, run the following command:
python3 $SCRIPTS_DIR/runner.py get_data_proc_feat_ext_recommendations \
"{\"task_type\": \"$TASK_TYPE\", \"prefer_fft\": <based-on-above-analysis>, \"need_full_spectrum\":"<based-on-above-analysis>, \"need_temporal_ctx\":"<based-on-above-analysis>\", \"min_sample_or_seq_length\":{`min_sample_or_seq_length` from step-6}, variables\": $VARIABLES}"
IMPORTANT - NEVER SKIP THIS CHECK
Before returning the fetched recommendations to the user, go through the dataset analysis in $WORK_DIR/.tmp_dataset_stats.json completely. Based on these statistics ensure the recommended presets/transforms are suited for the dataset.
For example:
If for a classification task, the min_sample_or_seq_length is 70, then having a frame_size > 70 in the preset or data transform would be too high and would cause that sample file to be skipped, thus causing data loss. This would be unacceptable.
You must therefore suggest presets or transforms having frame/window size <=min_sample_or_seq_length. #NAME min_sample_or_seq_length could be causing confusion for agent
Also, understand what each recommended transform does - go through references/FE_and_Data_Processing_Transforms/FE_transforms.md and references/Data_processing_transforms.md. For presets, consult $TINYML_BASE_PATH/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/constants.py (source of truth for feature extraction and data processing presets).
Try understanding whether your recommendations actually will be useful for the dataset you are working with and if so, why. Give CLEAR point-by-point reasoning to the user regarding why your recommended transforms or presets are valid and useful.
Show user the complete, structured output of get_data_proc_feat_ext_recommendations - summarize the same as well while showing it to the user.
If user asks about a specific transform or preset:
python3 $SCRIPTS_DIR/runner.py get_transform_context \
'{"transform_or_preset": "<name>"}'
Part B — validate the chosen config:
python3 $SCRIPTS_DIR/runner.py validate_feat_ext_data_shape \
"{\"data_path\": \"$EFFECTIVE_DATA_PATH\", \"task_type\": \"$TASK_TYPE\", \"variables\": $VARIABLES, \"data_proc_transforms\": [\"<chosen transforms>\"], \"feature_extraction_name\": \"<chosen preset>\", \"frame_size\": <value if SimpleWindow>}"
Check errors — if frame_size > min_sequence_length or required transforms missing, fix before proceeding.
Part C — generate YAML:
python3 $SCRIPTS_DIR/runner.py generate_feat_ext_section_yaml \
"{\"task_type\": \"$TASK_TYPE\", \"variables\": $VARIABLES, \"data_proc_transforms\": [\"<chosen transforms>\"], \"feature_extraction_name\": \"<chosen preset>\", \"frame_size\": <value if SimpleWindow>}" \
--save-yaml $WORK_DIR/feat_ext.yaml
Note: only pass frame_size if SimpleWindow is in data_proc_transforms. Only pass sampling_rate/new_sr if DownSample is used.
Step 7: Select model
Part A — analyze dataset size:
Refer $WORK_DIR/.tmp_dataset_stats.json to get insight on statistical information about the dataset. Basis this, proceed to Part B (Model recommendation).
Part B — get model recommendations:
python3 $SCRIPTS_DIR/runner.py select_model_for_task \
"{\"task_type\": \"$TASK_TYPE\", \"target_device\": \"$TARGET_DEVICE\", \"target_module\": \"$TARGET_MODULE\", \"variables\": $VARIABLES, \"dataset_size_bucket\": \"<from Part A>\", \"modelzoo_path\": \"$TINYML_BASE_PATH/tinyml-modelzoo/examples\"}"
Part C — fetch & display all available models:
Fetch all available models filtered by $TASK_TYPE and number of input variables ($VARIABLES):
python3 $SCRIPTS_DIR/runner.py list_available_models \
"{\"task_type\": \"$TASK_TYPE\", \"variables\": $VARIABLES, \"modelzoo_path\": \"$TINYML_BASE_PATH/tinyml-modelzoo/tinyml_modelzoo/models/\", \"model_descriptions_path\": \"$TINYML_BASE_PATH/tinyml-modelzoo/tinyml_modelzoo/model_descriptions/\"}"
Display results as a TABLE with columns:
| Model Name | Param Count | Complexity | Merits | Demerits | Ideal Use Case |
For each model, include:
- Merits: Speed, accuracy, memory efficiency, special capabilities (e.g., "Fast inference on NPU", "Best accuracy")
- Demerits: Trade-offs (e.g., "Lower accuracy than larger models", "Requires quantization")
- Ideal Use Case: When/where to use (e.g., "Tight memory constraints", "Real-time inference requirement")
Part D — provide your recommendation:
Based on dataset size, ranked scores, and device constraints, give CLEAR, POINT-BY-POINT recommendation format:
"I recommend [MODEL_NAME] because:
- Point 1: [reason specific to this dataset size]
- Point 2: [reason specific to this device]
- Point 3: [reason specific to performance needs]
Alternatives:
- [ALT_MODEL] if you prioritize [property]
- [ALT_MODEL2] if you need [property]"
Inform user: "You can accept this recommendation or select any model from the table above."
Store user's choice as MODEL_NAME.
Step 8: Generate training section
DO NOT SKIP ANY OF THE BELOW PARTS. ENSURE STEP 8 IS FULLY DONE, EVERY PART, EVERY SINGLE TIME
Part A — get recommendations
python3 $SCRIPTS_DIR/runner.py get_training_recommendations \
"{\"target_device\": \"$TARGET_DEVICE\", \"dataset_size_bucket\": \"<from Step 7A>\", \"num_gpus\": <0 or N>}"
Read and relay to user:
quantization.reason — explains the recommended mode for their device
nas.reason — explains whether NAS is viable and why
Part B — ask user about basic training params
Ask:
- How many training epochs?
- Batch size?
- Number of GPUs? (store as
NUM_GPUS)
- Custom learning rate?
Inform the user that all are optional, user can press Enter to use system defaults (taken from the corresponding modules' params.py).
HIGHLY IMPORTANT
For number of GPUs, if user does not specify or does not know, run the following simple python script:
import torch
print(torch.cuda.device_count())
Use the result from the above script to set NUM_GPUS.
Part C — ask user about quantization
Present all three modes clearly, state the recommendation, and ask the user to choose:
Quantization reduces model size and enables hardware acceleration.
Modes:
0 — Float32. No compression. Largest model, slowest inference. NPU will NOT be used.
1 — Standard PyTorch quantization. 4× smaller, works on all devices.
2 — TI NPU-optimized. Required for NPU hardware acceleration. [RECOMMENDED for NPU devices]
The recommendation for YOUR device ($TARGET_DEVICE): mode <quantization.recommended_mode>
Reason: <quantization.reason>
When you select mode 1 or 2, Automatic Mixed Precision (AMP) is enabled by default:
<autoquant_explanation>
Ask:
- "Which quantization mode do you want? (0 / 1 / 2)" → store as
QUANTIZATION_MODE
That's it. No need to ask about PTQ/QAT or bit widths — AMP handles per-layer precision automatically.
Quantization mode will also determine the compilation preset in Step 10 — setting it correctly here matters for end-to-end correctness.
Part D — generate YAML
Since AMP is default and handles bit widths automatically, just specify the quantization mode:
python3 $SCRIPTS_DIR/runner.py generate_training_section_yaml \
"{\"enable\": true, \"model_name\": \"$MODEL_NAME\", \"quantization\": $QUANTIZATION_MODE}" \
--save-yaml $WORK_DIR/training.yaml
Show user the generated YAML so they can confirm the training config before proceeding.
Note: If user explicitly wants to disable AMP and use uniform quantization instead, they can set quantization_method and bit widths manually, but this is not recommended and rarely needed.
Step 9: Generate testing section
For most users, defaults are correct — just run:
python3 $SCRIPTS_DIR/runner.py generate_testing_section_yaml \
'{"enable": true}' \
--save-yaml $WORK_DIR/testing.yaml
Ask only if user has non-default needs:
- Skip training and test an existing model? → add
"skip_train": true, "model_path": "<path>"
- Run on actual connected device? → add
"device_inference": true
- Use separate test dataset? → add
"test_data": "<path>"
Step 10: Generate compilation section
Part A — get preset recommendation (pass QUANTIZATION_MODE from Step 8C):
python3 $SCRIPTS_DIR/runner.py get_compilation_preset_recommendations \
"{\"task_type\": \"$TASK_TYPE\", \"target_device\": \"$TARGET_DEVICE\", \"quantization_mode\": $QUANTIZATION_MODE}"
The preset recommendation is driven by QUANTIZATION_MODE:
quantization_mode: 2 + NPU device → default_preset (NPU will be used) or compress_npu_layer_data (tight memory)
quantization_mode: 0 or 1 + NPU device → forced_soft_npu_preset (NPU requires mode 2 — forces CPU path)
- Non-NPU device →
default_preset
Show user recommended_preset and recommendation_reason. Ask if they want to use it or choose differently.
Available presets: default_preset, forced_soft_npu_preset, compress_npu_layer_data
Part B — generate YAML:
python3 $SCRIPTS_DIR/runner.py generate_compilation_section_yaml \
"{\"enable\": true, \"compile_preset_name\": \"<chosen preset>\", \"target_device\": \"$TARGET_DEVICE\"}" \
--save-yaml $WORK_DIR/compilation.yaml
Step 11: Assemble config.yaml
All section YAML files should now exist in $WORK_DIR. Verify before assembling:
ls -la $WORK_DIR/
Assemble (using default path under tinyml-modelzoo/examples/):
python3 $SCRIPTS_DIR/runner.py generate_complete_config_file \
"{\"task_name\": \"$TASK_NAME\", \"tinyml_base_path\": \"$TINYML_BASE_PATH\", \"common_yaml_file\": \"$WORK_DIR/common.yaml\", \"dataset_yaml_file\": \"$WORK_DIR/dataset.yaml\", \"feature_extraction_yaml_file\": \"$WORK_DIR/feat_ext.yaml\", \"training_yaml_file\": \"$WORK_DIR/training.yaml\", \"testing_yaml_file\": \"$WORK_DIR/testing.yaml\", \"compilation_yaml_file\": \"$WORK_DIR/compilation.yaml\"}"
Assemble (using user-specified output directory):
python3 $SCRIPTS_DIR/runner.py generate_complete_config_file \
"{\"task_name\": \"$TASK_NAME\", \"output_dir\": \"$CONFIG_DIR\", \"common_yaml_file\": \"$WORK_DIR/common.yaml\", \"dataset_yaml_file\": \"$WORK_DIR/dataset.yaml\", \"feature_extraction_yaml_file\": \"$WORK_DIR/feat_ext.yaml\", \"training_yaml_file\": \"$WORK_DIR/training.yaml\", \"testing_yaml_file\": \"$WORK_DIR/testing.yaml\", \"compilation_yaml_file\": \"$WORK_DIR/compilation.yaml\"}"
Before assembling, ask user: "Where should the final config.yaml be saved?"
Default is $TINYML_BASE_PATH/tinyml-modelzoo/examples/$TASK_NAME/config.yaml.
If user provides a custom path, pass it as "output_dir" instead of "tinyml_base_path".
CONFIG_DIR=<user choice or $TINYML_BASE_PATH/tinyml-modelzoo/examples/$TASK_NAME>
On success: config saved permanently to $CONFIG_DIR/config.yaml.
Show user the full config: cat $CONFIG_DIR/config.yaml
If any *_yaml_file is missing from WORK_DIR: go back to the relevant step and re-generate it.
Step 12: Run training & compilation
SHOW USER FULL CONFIG BEFORE ASKING USER TO ALLOW TRAINING
Ask user: "Ready to start training? This will take several minutes."
Reference references/example_running_guide.md for monitoring tips.
Stream logs live (recommended for long runs):
bash $TINYML_BASE_PATH/tinyml-modelzoo/run_with_log_stream.sh \
$CONFIG_DIR/config.yaml
Streams live updates. User sees training progress in real-time instead of waiting until end.
When complete, read the logs and summarize for the user:
- Training accuracy/loss:
{run_path}/training/base/run.log
- Compiled model size and latency:
{run_path}/compilation/run.log
From the logs, find and record:
RUN_ID — the run identifier (timestamp-based directory name)
MODEL_ID — the model artifact identifier
QUANTIZATION — whether quantization was applied (true/false)
In case of any errors, analyse and think what could have been done differently in the config to prevent the errors. Then, suggest those modifications to the user, explain in detail the cause of the error and ask the user if your suggested modifications should be applied or if the user has anything they would like to try.
Then proceed to implement the changes (either the ones you recommended or the ones the user gave you, as per what the user chose to do) and re-run training + compilation.
DO THIS UNTIL TRAINING HAPPENS CLEANLY WITHOUT ISSUE.
COMMON ISSUES TO LOOK OUT FOR:
-
Feature Extraction preset failed due to size issues. Dataset's class samples may have too few entries for presets. If no presets can fit it, then think of using raw feature extraction transforms. Consult $TINYML_BASE_PATH/tinyml-modelmaker/tinyml_modelmaker/ai_modules/timeseries/constants.py for all available transforms and presets. See if any of them or any combination of them could be of use for your usecase and accordingly select them.
-
Due to the above issue, many times files having less number of samples (less than what the presets may expect) may be skipped. As a result the metrics you get may be skewed and show very high values. Do not get confused and report this to the user as the final metrics. Stop the training, go back to the config, and refer point 1 to fix the issue. Once fixed, then train again.
Step 12B: Display compiled model memory footprint (MANDATORY)
AFTER training completes, ALWAYS extract and display memory usage to user.
Find and display FLASH (RO Mem) and SRAM (RW Mem) from compilation logs:
grep -E "FLASH|SRAM|RO Mem|RW Mem" "$TINYML_BASE_PATH/tinyml-modelmaker/data/projects/$TASK_TYPE/run/$RUN_ID/$MODEL_ID/compilation/run.log"
Display to user in this format:
═══════════════════════════════════════════════════════════════
MODEL MEMORY FOOTPRINT
═══════════════════════════════════════════════════════════════
Device: $TARGET_DEVICE
FLASH (RO Memory): <SIZE> bytes
SRAM (RW Memory): <SIZE> bytes
═══════════════════════════════════════════════════════════════
User MUST verify this fits on their device before deployment.
Get device specs from references/deployment_sdk_reference.md or:
- C2000 devices: check device datasheet (typically 256KB–1MB FLASH, 32KB–128KB SRAM)
- MSPM0 devices: check SDK documentation
- AM26x: check MCU+ SDK docs
If model doesn't fit:
- Stop. Do NOT proceed to deployment.
- Reduce model size: enable quantization mode 2, use "Memory" NAS optimization, or select smaller model
- Re-run training (Step 12)
- Check memory again before deployment
Step 13: Deploy to device (if user asks)
Read full guide first: references/device_deployment_guide.md
Collect from Step 12 logs:
RUN_ID=<timestamp dir, e.g., 20240515_143022>
MODEL_ID=<model_name from config, e.g., CLS_4k_NPU>
QUANTIZATION=<true or false>
CCS_INSTALL_PATH=<user-provided, e.g., /opt/ti/ccs1260>
Four scripted steps:
Step 13A: Verify artifacts exist
python3 $SCRIPTS_DIR/runner.py find_run_artifacts \
"{\"tinyml_base_path\": \"$TINYML_BASE_PATH\", \"task_type\": \"$TASK_TYPE\", \"run_id\": \"$RUN_ID\", \"model_id\": \"$MODEL_ID\", \"quantization\": $QUANTIZATION}"
Check success: true before proceeding. If false, training/compilation incomplete.
Step 13B: Create CCS project
B1. Check SDK installed:
python3 $SCRIPTS_DIR/runner.py check_sdk_installation \
"{\"target_device\": \"$TARGET_DEVICE\"}"
Store returned ai_examples_path as $AI_EXAMPLES_PATH.
B2. Create project (auto-copies artifacts + golden vectors):
python3 $SCRIPTS_DIR/runner.py create_ccs_project '{
"project_name": "<name>",
"device_type": "<e.g., f28p55x>",
"target_device": "$TARGET_DEVICE",
"run_id": "$RUN_ID",
"task_type": "$TASK_TYPE",
"quantization": $QUANTIZATION,
"model_id": "$MODEL_ID",
"tinyml_base_path": "$TINYML_BASE_PATH",
"ccs_templates_path": "$AI_EXAMPLES_PATH"
}'
Store returned project_path as $CCS_PROJECT_PATH.
B3. VERIFY project structure + artifact authenticity (MANDATORY before Step C):
CHECKPOINT: Do NOT proceed to Step 13C until verification passes.
Part 1: Check file existence
List files in project:
ls -la "$CCS_PROJECT_PATH/"
ls -la "$CCS_PROJECT_PATH/{device_type}/"
ls -la "$CCS_PROJECT_PATH/{device_type}/artifacts/"
Verify these files exist. All REQUIRED:
$CCS_PROJECT_PATH/application_main.c
$CCS_PROJECT_PATH/{device_type}/artifacts/mod.a
$CCS_PROJECT_PATH/{device_type}/artifacts/tvmgen_default.h
$CCS_PROJECT_PATH/{device_type}/CCS/{device_type}_{project_name}.projectspec
$CCS_PROJECT_PATH/{device_type}/test_vector.c
$CCS_PROJECT_PATH/{device_type}/user_input_config.h
If any file missing:
→ Go to manual fallback below. Do NOT skip this. Missing files = build/flash will fail.
Part 2: Verify artifact timestamps
The create_ccs_project command also validates that copied artifacts and golden vectors have matching creation timestamps with their sources in ModelMaker. This prevents:
- Files being silently copied from template (stale/wrong artifacts)
- Copy failures falling back to cached template files
- Model mismatch between what you trained and what gets deployed
Check the create_ccs_project response for:
"timestamp_validation_passed": true — Safe to proceed. Artifacts are from your actual run.
"timestamp_validation_passed": false — CRITICAL WARNING. Artifacts or golden vectors don't match source timestamps.
If timestamp validation FAILS:
-
Do NOT proceed to Step 13C. The project may contain template files, not your trained model.
-
Manually verify artifacts by comparing mtimes:
stat "$TINYML_BASE_PATH/tinyml-modelmaker/data/projects/$TASK_TYPE/run/$RUN_ID/$MODEL_ID/compilation/artifacts/mod.a"
stat "$CCS_PROJECT_PATH/{device_type}/artifacts/mod.a"
If mtimes differ by more than 0.5 seconds, files are from template (not your actual run).
-
Manual recovery:
- Delete the project:
rm -rf "$CCS_PROJECT_PATH"
- Re-run
create_ccs_project and verify timestamp validation passes
- If still fails, check that source artifacts exist and training completed successfully
B4. Manual fallback (if auto-copy failed):
If after checking above, any files are missing:
-
Verify artifact source paths exist:
ls "$TINYML_BASE_PATH/tinyml-modelmaker/data/projects/$TASK_TYPE/run/$RUN_ID/$MODEL_ID/compilation/artifacts/"
ls "$TINYML_BASE_PATH/tinyml-modelmaker/data/projects/$TASK_TYPE/run/$RUN_ID/$MODEL_ID/training/quantization/golden_vectors/"
-
Copy missing files manually:
mkdir -p "$CCS_PROJECT_PATH/{device_type}/artifacts"
cp "$TINYML_BASE_PATH/tinyml-modelmaker/data/projects/$TASK_TYPE/run/$RUN_ID/$MODEL_ID/compilation/artifacts/"* \
"$CCS_PROJECT_PATH/{device_type}/artifacts/"
cp "$TINYML_BASE_PATH/tinyml-modelmaker/data/projects/$TASK_TYPE/run/$RUN_ID/$MODEL_ID/training/quantization/golden_vectors/test_vector.c" \
"$CCS_PROJECT_PATH/{device_type}/"
cp "$TINYML_BASE_PATH/tinyml-modelmaker/data/projects/$TASK_TYPE/run/$RUN_ID/$MODEL_ID/training/quantization/golden_vectors/user_input_config.h" \
"$CCS_PROJECT_PATH/{device_type}/"
-
Re-verify all files now exist:
ls "$CCS_PROJECT_PATH/{device_type}/artifacts/mod.a"
ls "$CCS_PROJECT_PATH/{device_type}/test_vector.c"
ls "$CCS_PROJECT_PATH/{device_type}/user_input_config.h"
-
Only after re-verification passes, proceed to Step 13C.
Step 13C: Build project
python3 $SCRIPTS_DIR/runner.py build_ccs_project \
"{\"ccs_project_path\": \"$CCS_PROJECT_PATH\", \"ccs_install_path\": \"$CCS_INSTALL_PATH\"}"
If fails: build manually in CCS (Project → Build Project, Ctrl+B).
Step 13D: Flash to device
Connect device via USB/JTAG, then:
python3 $SCRIPTS_DIR/runner.py flash_ccs_project \
"{\"ccs_project_path\": \"$CCS_PROJECT_PATH\", \"ccs_install_path\": \"$CCS_INSTALL_PATH\"}"
If fails: flash manually in CCS (Run → Flash Project).
After flashing: In CCS Debug perspective, set breakpoint after inference, check test_result == 1 in Watch window (1 = pass, 0 = fail).
See references/device_deployment_guide.md for troubleshooting, device/SDK mappings, and full walkthrough.