Terramind Tiny in Action¶
📥 Download GeospatialStudio-Walkthrough-Flooding.ipynb and try it out
Assume you are interested in mapping flooding, traditionally you might have either relied on on-the-ground mapping, or possibly for manual analysis of remote-sensing imagery (i.e. satellite or UAV). In order to scale up these efforts and operationalise, we need a way to automate the extraction of flood extent from satellite imagery. This is where we turn to AI models.

In this walkthrough we will assume that a model doesn't exist yet and we want to train a new model. We will then show how to drive the model to map impact.
We will walk through the following steps as part of this walkthrough:
- Upload and onboarding of data
- Configuring and submitting a tuning task
- Monitoring model training
- Testing and validation of the outputs
Pre-requisites¶
You will require access to a deployed instance of the Geospatial Studio. For more information on how you can deploy the studio (either locally or in a cluster), visit the GEOStudio docs: Geospatial Studio Docs
For more information about the Geospatial Studio SDK that is used in this notebook, and all the functions available through it, see the SDK docs page: Geospatial Studio SDK Docs
This walkthrough also requires you to have a direct download URL pointing to a zip file of the dataset you wish to use. We provide a sample dataset url (zip file) below to go through this notebook.
If you have your own dataset locally, you can find instructions on how to use the SDK to temporarily upload it to the cloud and create a download url link in the steps that follow.
Get sample training data to use for this walkthrough¶
This walkthrough used the following dataset to fine-tune the Terramind Tiny model: https://geospatial-studio-example-data.s3.us-east.cloud-object-storage.appdomain.cloud/sen1floods11_v1.1.4_zip.zip
Download and unzip the above archive and if you wish you can explore the data with QGIS (or any similar tool).
%load_ext autoreload
%autoreload 2
import os
import json
import uuid
import pandas as pd
import rasterio
from rasterio.plot import show
import matplotlib.pyplot as plt
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
from geostudio import Client
from geostudio import gswidgets
Connecting to the platform¶
First, we set up the connection to the platform backend. To do this we need the base url for the studio UI and an API key.
To get an API Key:
- Go to the Geospatial Studio UI page and navigate to the Manage your API keys link.
- This should pop-up a window where you can generate, access and delete your api keys. NB: every user is limited to a maximum of two activate api keys at any one time.
Store the API key and geostudio ui base url in a credentials file locally, for example in /User/bob/.geostudio_config_file. You can do this by:
echo "GEOSTUDIO_API_KEY=<paste_api_key_here>" > .geostudio_config_file
echo "BASE_STUDIO_UI_URL=<paste_ui_base_url_here>" >> .geostudio_config_file
Copy and paste the file path to this credentials file in call below.
#############################################################
# Initialize Geostudio client using a geostudio config file
#############################################################
gfm_client = Client(geostudio_config_file="../../.geostudio_config_file_staging")
Populate Studio¶
Assuming you have deployed the studio, you should have also gone through the first steps which include populating your GEOStudio instance with sample data for testing. If you did so, you should have a list of datasets that you can explore with the functio below
Instructions to deploy the studio and first steps: https://terrastackai.github.io/geospatial-studio
# explore sample datasets populated with your Studio deployment
gfm_client.list_datasets(output="df")
Data onboarding¶
In order to onboard your dataset to the Geospatial Studio, you need to have a direct download URL pointing to a zip file of the dataset. You can use this dataset url as an example to go through this notebook.
If you have the dataset locally, you can use Box, OneDrive or any other cloud storage you are used to, but in addition, to make this easier for you, there is a function which will upload your data to a temporary location in the cloud (with in Studio object storage) and provide you with a url which can be used to pass to the onboarding process. NB: the same upload function can be useful for pushing files for inferecnce or to processing pipelines.
If needed you can package a set of files for upload, you can use a command like:
zip -j flooding-dataset-upload.zip flooding-dataset-upload/*
NB: If you already have the data online you can skip this step.
object_name = "test-checkpoint.ckpt" #Must be a valid string, not a path
dataset = "flooding-dataset-upload.zip"
upload_links = gfm_client.get_fileshare_links(object_name)
upload_url = upload_links["upload_url"]
download_url = upload_links["download_url"]
!curl --progress-bar -O -X PUT -T "$dataset" "$upload_url"
Pre-scan the dataset¶
Pre-scan the dataset to check the accessibility of the dataset URL, check that you have matching data and label pairs, and check that you have specified the correct number of bands and their descriptions from the dataset.
# [Optional]
gfm_client.pre_scan_dataset({
"dataset_url": download_url,
"label_suffix": "_LabelHand.tif",
"training_data_suffixes":
["_S2Hand.tif", "_S1Hand.tif"]
})
Onboard the dataset to the dataset factory¶
Now we use the SDK to provide the information about the dataset, including name, suffixes etc. A more detailed description of the dataset details is provided in the UI walkthrough.
Note:
- Change the value of the
dataset_urlvariable below to the url of your zip file or thedownload_urllink you got from using the SDK upload_file function above - Change the values of
training_data_suffixandlabel_suffixto the suffixes of your training and label data files respectively if using a different dataset (aside from the one provided) - Change the
label_categories,bandsand descriptions to those that match your dataset
onboard_dataset_payload = {
"dataset_name": "Sentinel Flood Multimodal Test",
"data_sources": [
{
"bands": [
{
"index": "0",
"band_name": "B01",
"description": "",
"scaling_factor": "1"
},
{
"index": "1",
"band_name": "B02",
"RGB_band": "B",
"description": "",
"scaling_factor": "1"
},
{
"index": "2",
"band_name": "B03",
"RGB_band": "G",
"description": "",
"scaling_factor": "1"
},
{
"index": "3",
"band_name": "B04",
"RGB_band": "R",
"description": "",
"scaling_factor": "1"
},
{
"index": "4",
"band_name": "B05",
"description": "",
"scaling_factor": "1"
},
{
"index": "5",
"band_name": "B06",
"description": "",
"scaling_factor": "1"
},
{
"index": "6",
"band_name": "B07",
"description": "",
"scaling_factor": "1"
},
{
"index": "7",
"band_name": "B08",
"description": "",
"scaling_factor": "1"
},
{
"index": "8",
"band_name": "B8A",
"description": "",
"scaling_factor": "1"
},
{
"index": "9",
"band_name": "B09",
"description": "",
"scaling_factor": "1"
},
{
"index": "10",
"band_name": "B11",
"description": "",
"scaling_factor": "1"
},
{
"index": "11",
"band_name": "B12",
"description": "",
"scaling_factor": "1"
},
{
"index": "12",
"band_name": "CLD",
"description": "",
"scaling_factor": "1"
}
],
"connector": "sentinelhub",
"collection": "s2_l2a",
"modality_tag": "S2L2A",
"file_suffix": "_S2Hand.tif"
},
{
"bands": [
{"index": "0", "band_name": "VV", "description": ""},
{"index": "1", "band_name": "VH", "description": ""}
],
"connector": "sentinelhub",
"collection": "s1_grd",
"modality_tag": "S1GRD",
"align_dates": "true",
"file_suffix": "_S1Hand.tif",
"scaling_factor": [1, 1]
}
],
"label_categories": [
{"id": "0", "name": "No Floods", "description": "Flooding assets"},
{"id": "1", "name": "Floods", "description": "Flooding assets"}
],
"dataset_url": "https://geospatial-studio-example-data.s3.us-east.cloud-object-storage.appdomain.cloud/sen1floods11_v1.1.4_zip.zip",
"description": "Flood data from places",
"label_suffix": "_LabelHand.tif",
"purpose": "Segmentation"
}
# start onboarding process
onboard_response = gfm_client.onboard_dataset(data=onboard_dataset_payload)
display(json.dumps(onboard_response, indent=2))
Monitor you dataset onboarding task¶
You can use the UI to monitor the progress of your dataset onboarding submission. Access and log in to the UI of your deployed GEOStudio instance and click on the Dataset factory page.
# Once onboarding is complete, select your onboarded dataset to use for you fine-tuning task
selected_dataset = onboard_response["id"]
Fine-tuning submission¶
Once the data is onboarded, you are ready to setup your tuning task. In order to run a fine-tuning task, you need to select the following items:
- tuning task type/config template - what type of learning task are you attempting? segmentation, regression etc
- fine-tuning dataset - what dataset will you use to train the model for your particular application?
- base foundation model - which geospatial foundation model will you use as the starting point for your tuning task?
Below we walk you through how to use the Geospatial Studio SDK to see how we configure our task and submit it.
Tuning task¶
The tuning task tells the model what type of task it is (segmentation, regression etc), and exposes a range of optional hyperparameters which the user can set. These all have reasonable defaults, but it gives uses the possibility to configure the model training how they wish. Below, we will check what task templates are available to us from the sample data from the GEOStudio deployment
After that, we will create and upload new task templates to the platform. The templates are for Terratorch (the backend tuning library), and more details of Terratroch and configuration options can be found here: https://github.com/terrastackai/terratorch
# list tasks available from our deployment sample data
tasks = gfm_client.list_tune_templates(output="df")
display(tasks[['name','description', 'id','created_by','updated_at']])
Create the Terramind Tiny tuning task¶
create_terramind_template = {
"name": "terramind tiny Segmentation",
"description": "Terramind multimodal task for Segmantation",
"purpose": "Segmentation",
"model_params": {
"$uri": "https://ibm.com/watsonx.ai.geospatial.finetune.segmentation.json",
"type": "object",
"title": "Finetune",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"data": {
"type": "object",
"default": {
"batch_size": 4,
"constant_multiply": 1,
"workers_per_gpu": 2,
"check_stackability": False
},
"properties": {
"batch_size": {
"type": "int",
"default": 4,
"description": "Batch size",
"studio_name": "Batch size"
},
"constant_multiply": {
"type": "float",
"default": 1,
"description": "Constant Scale",
"studio_name": "Constant Scale"
},
"workers_per_gpu": {
"studio_name": "Workers per GPU",
"description": "Workers per GPU",
"type": "int",
"default": 2
},
"check_stackability": {
"studio_name": "Check Stackability",
"description": "Check Stackability",
"type": "bool",
"default": False
}
},
"studio_name": "Data loading"
},
"model": {
"type": "object",
"default": {
"decode_head": {
"channels": 256,
"num_convs": 4,
"decoder": "UNetDecoder",
"loss_decode": {
"type": "CrossEntropyLoss",
"avg_non_ignore": True
}
},
"frozen_backbone": False,
"tiled_inference_parameters": {
"h_crop": 224,
"h_stride": 208,
"w_crop": 224,
"w_stride": 208,
"average_patches": True
}
},
"properties": {
"decode_head": {
"type": "object",
"default": {
"channels": 256,
"num_convs": 4,
"decoder": "UperNetDecoder",
"loss_decode": {
"type": "CrossEntropyLoss",
"avg_non_ignore": True
}
},
"properties": {
"channels": {
"type": "int",
"default": 256,
"description": "Channels at each block of the decode head, except the final one",
"studio_name": "Channels"
},
"num_convs": {
"type": "int",
"default": 4,
"description": "Number of convolutional blocks in the head (except the final one)",
"studio_name": "Blocks"
},
"decoder": {
"enum": [
"UperNetDecoder",
"UNetDecoder"
],
"type": "string",
"default": "Fixed",
"description": "Decoder type",
"studio_name": "Decoder type"
},
"loss_decode": {
"type": "object",
"properties": {
"type": {
"enum": [
"CrossEntropyLoss"
],
"type": "string",
"default": "CrossEntropyLoss",
"description": "Type of loss function",
"studio_name": "Loss function"
},
"avg_non_ignore": {
"type": "bool",
"default": True,
"description": "The loss is only averaged over non-ignored targets (ignored targets are usually where labels are missing in the dataset) if this is True"
}
},
"description": "Loss function to be used",
"studio_name": "Loss"
}
},
"description": "Architecture of the decode head",
"studio_name": "Head"
},
"auxiliary_head": {
"type": "object",
"default": {},
"properties": {
"decoder": {
"type": "string",
"default": "FCNDecoder",
"description": "Decoder function to use",
"studio_name": "Decoder"
},
"channels": {
"type": "int",
"default": 256,
"description": "Channels at each block of the decode head, except the final one",
"studio_name": "Channels"
},
"num_convs": {
"type": "int",
"default": 2,
"description": "Number of convolutional blocks in the head (except the final one)",
"studio_name": "Blocks"
},
"in_index": {
"type": "int",
"default": -1,
"description": "Index of the input list to take. Defaults to -1",
"studio_name": "In index"
},
"dropout": {
"type": "int",
"default": 0,
"description": "Dropout value to apply. Defaults to 0",
"studio_name": "Dropout"
},
"loss_decode": {
"type": "object",
"properties": {
"type": {
"enum": [
"CrossEntropyLoss"
],
"type": "string",
"default": "CrossEntropyLoss",
"description": "Type of loss function",
"studio_name": "Loss function"
},
"loss_weight": {
"type": "float",
"default": 1,
"description": "Multiplicative weight of the loss of the auxiliary head in the loss. The loss is calculated as aux_head_weight * aux_head_loss + decode_head_loss",
"studio_name": "Loss weight"
},
"avg_non_ignore": {
"type": "bool",
"default": True,
"description": "The loss is only averaged over non-ignored targets (ignored targets are usually where labels are missing in the dataset) if this is True"
}
},
"description": "Loss function to be used",
"studio_name": "Loss"
}
},
"description": "Architecture of the auxiliary head"
},
"frozen_backbone": {
"type": "bool",
"default": False,
"description": "Freeze the weights of the backbone when set to True",
"studio_name": "Freeze backbone"
},
"tiled_inference_parameters": {
"type": "object",
"default": {
"h_crop": 224,
"h_stride": 208,
"w_crop": 224,
"w_stride": 208,
"average_patches": True
},
"properties": {
"h_crop": {
"type": "int",
"default": 224,
"description": "h_crop values for tilling images",
"studio_name": "h_crop"
},
"h_stride": {
"type": "int",
"default": 196,
"description": "h_stride values for tilling images",
"studio_name": "h_stride"
},
"w_crop": {
"type": "int",
"default": 224,
"description": "w_crop values for tilling images",
"studio_name": "w_crop"
},
"w_stride": {
"type": "int",
"default": 196,
"description": "w_stride values for tilling images",
"studio_name": "w_stride"
},
"average_patches": {
"type": "bool",
"default": False,
"description": "Whether to use average_patches",
"studio_name": "average_patches"
}
}
}
},
"description": "Model architecture definition",
"studio_name": "Architecture"
},
"runner": {
"type": "object",
"default": {
"max_epochs": 10,
"early_stopping_patience": 20,
"early_stopping_monitor": "val/loss"
},
"properties": {
"max_epochs": {
"type": "int",
"default": 10,
"description": "Training epochs",
"studio_name": "Training epochs"
},
"early_stopping_patience": {
"type": "int",
"default": 20,
"description": "Early stopping patience",
"studio_name": "Early stopping patience"
},
"early_stopping_monitor": {
"type": "string",
"default": "val/loss",
"description": "Monitoring value to determine early stopping",
"studio_name": "Early stopping monitor"
}
},
"studio_name": "Runner"
},
"lr_config": {
"type": "object",
"default": {
"policy": "Fixed"
},
"required": [
"policy"
],
"properties": {
"policy": {
"enum": [
"Fixed",
"CosineAnnealing"
],
"type": "string",
"default": "Fixed",
"description": "Policy type",
"studio_name": "Policy type"
},
"warmup_iters": {
"type": "int",
"default": 0,
"description": "LR warmup iterations. Valid for some policies",
"studio_name": "Learning rate warmup iterations"
},
"warmup_ratio": {
"type": "float",
"default": 1,
"description": "Initial lr at warmup will be learning_rate * warmup_ratio",
"studio_name": "LR warmup initialization ratio"
}
},
"description": "Learning rate policy",
"studio_name": "Learning rate policy"
},
"optimizer": {
"type": "object",
"default": {
"lr": 6e-05,
"type": "Adam"
},
"properties": {
"lr": {
"type": "float",
"default": 6e-05,
"description": "Learning rate",
"studio_name": "Learning rate"
},
"type": {
"enum": [
"Adam",
"SGD",
"AdamW",
"RMSProp"
],
"default": "Adam",
"description": "Optimizer to be used",
"studio_name": "Optimizer type"
},
"weight_decay": {
"type": "float",
"default": 0,
"description": "L2 weight regularization (weight decay)",
"studio_name": "L2 regularization weight"
}
},
"description": "Optimizer",
"studio_name": "Optimizer"
},
"dataset_id": {
"type": "string",
"description": "ID of dataset to use for this finetuning",
"studio_name": "Dataset"
},
"evaluation": {
"type": "object",
"default": {
"interval": 1
},
"properties": {
"interval": {
"type": "int",
"default": 1,
"description": "Frequency of epochs with which to perform validation",
"studio_name": "Epoch interval"
}
},
"studio_name": "Validation"
},
"backbone_model_id": {
"type": "string",
"description": "ID of the pretrained backbone"
}
},
"description": "A request sent to the finetuning service to start a finetune task for segmentation"
},
"extra_info": {
"runtime_image": "quay.io/geospatial-studio/terratorch:latest",
"model_category": "terramind"
},
"content": "IyDCqSBDb3B5cmlnaHQgSUJNIENvcnBvcmF0aW9uIDIwMjUKIyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMAoKCiMgbGlnaHRuaW5nLnB5dG9yY2g9PTIuMS4xCnNlZWRfZXZlcnl0aGluZzogNDIKdHJhaW5lcjoKICBhY2NlbGVyYXRvcjogYXV0bwogIHN0cmF0ZWd5OiBhdXRvCiAgZGV2aWNlczogYXV0bwogIG51bV9ub2RlczogMQogIHByZWNpc2lvbjogMTYtbWl4ZWQKICBsb2dnZXI6CiAgICBjbGFzc19wYXRoOiBsaWdodG5pbmcucHl0b3JjaC5sb2dnZXJzLm1sZmxvdy5NTEZsb3dMb2dnZXIKICAgIGluaXRfYXJnczoKICAgICAgZXhwZXJpbWVudF9uYW1lOiB7eyB0dW5lX2lkIH19IAogICAgICBydW5fbmFtZTogIlRyYWluIiAgICAKICAgICAgdHJhY2tpbmdfdXJpOiB7eyBtbGZsb3dfdHJhY2tpbmdfdXJsIH19CiAgICAgIHNhdmVfZGlyOiB7eyBtb3VudF9yb290ICsgJ3R1bmUtdGFza3MvJyArIHR1bmVfaWQgKyAnL21sZmxvdycgfX0KICAgICAgeyUgaWYgbWxmbG93X3RhZ3MgLSV9CiAgICAgIHRhZ3M6CiAgICAgICAgeyUgZm9yIGtleSwgdmFsdWUgaW4gbWxmbG93X3RhZ3MuaXRlbXMoKSAtJX0KICAgICAgICB7eyBrZXkgfX06IHt7IHZhbHVlIH19CiAgICAgICAgeyUgZW5kZm9yICV9CiAgICAgIHslLSBlbmRpZiAlfSAgICAgICAKICBjYWxsYmFja3M6CiAgICAtIGNsYXNzX3BhdGg6IFJpY2hQcm9ncmVzc0JhcgogICAgLSBjbGFzc19wYXRoOiBMZWFybmluZ1JhdGVNb25pdG9yCiAgICAgIGluaXRfYXJnczoKICAgICAgICBsb2dnaW5nX2ludGVydmFsOiBlcG9jaAogICAgIyAtLS0tIEVhcmx5IHN0b3AgaWYgLS0tLQogICAgeyUgaWYgcnVubmVyWyJlYXJseV9zdG9wcGluZ19wYXRpZW5jZSJdIC0lfQogICAgLSBjbGFzc19wYXRoOiBFYXJseVN0b3BwaW5nCiAgICAgIGluaXRfYXJnczoKICAgICAgICBtb25pdG9yOiB7eyBydW5uZXJbImVhcmx5X3N0b3BwaW5nX21vbml0b3IiXSB9fQogICAgICAgIHBhdGllbmNlOiB7eyBydW5uZXJbImVhcmx5X3N0b3BwaW5nX3BhdGllbmNlIl0gfX0KICAgIHslLSBlbmRpZiAlfQogICAgICMgLS0tLSBFYXJseSBzdG9wIGVuZGlmIC0tLS0KICAgIC0gY2xhc3NfcGF0aDogTW9kZWxDaGVja3BvaW50CiAgICAgIGluaXRfYXJnczoKICAgICAgICBkaXJwYXRoOiB7eyBtb3VudF9yb290ICsgJ3R1bmUtdGFza3MvJyArIHR1bmVfaWQgICsgJy8nIH19CiAgICAgICAgbW9kZTogbWluCiAgICAgICAgbW9uaXRvcjogdmFsL2xvc3MKICAgICAgICBmaWxlbmFtZToge3sgJ2Jlc3Qtc3RhdGVfZGljdC17ZXBvY2g6MDJkfScgfX0KICAgICAgICBzYXZlX3dlaWdodHNfb25seTogVHJ1ZQogIG1heF9lcG9jaHM6IHt7IHJ1bm5lclsibWF4X2Vwb2NocyJdIH19CiAgY2hlY2tfdmFsX2V2ZXJ5X25fZXBvY2g6IHt7IGV2YWx1YXRpb25bImludGVydmFsIl0gfX0KICBsb2dfZXZlcnlfbl9zdGVwczogNTAKICBlbmFibGVfY2hlY2twb2ludGluZzogdHJ1ZQogIGRlZmF1bHRfcm9vdF9kaXI6IHt7IG1vdW50X3Jvb3QgKyAndHVuZS10YXNrcy8nICsgdHVuZV9pZCB9fQpkYXRhOgogIHslIGlmIGltYWdlX21vZGFsaXRpZXN8bGVuZ3RoID09IDEgJX0gCiAgY2xhc3NfcGF0aDogR2VuZXJpY05vbkdlb1NlZ21lbnRhdGlvbkRhdGFNb2R1bGUKICBpbml0X2FyZ3M6CiAgICB7JSBpZiBkYXRhWyJjaGVja19zdGFja2FiaWxpdHkiXSAtJX0KICAgIGNoZWNrX3N0YWNrYWJpbGl0eToge3sgZGF0YVsiY2hlY2tfc3RhY2thYmlsaXR5Il0gfX0KICAgIHslIGVsc2UgJX0KICAgIGNoZWNrX3N0YWNrYWJpbGl0eTogZmFsc2UKICAgIHslIGVuZGlmIC0lfQogICAgYmF0Y2hfc2l6ZTogMgogICAgbnVtX3dvcmtlcnM6IDEKICAgIGRhdGFzZXRfYmFuZHM6ICAjIERhdGFzZXQgYmFuZHMKICAgICAge3sgYmFuZHMudmFsdWVzKCkgfCBsaXN0IHwgZmlyc3QgfCB0b195YW1sIHwgaW5kZW50KDYpIH19CiAgICByZ2JfaW5kaWNlczoKICAgICAge3sgcmdiX2JhbmRfaW5kaWNlcyB8IHRvX3lhbWwgfCBpbmRlbnQoNikgfX0KICAgIHRyYWluX2RhdGFfcm9vdDoge3sgZGF0YV9yb290IH19e3sgdHJhaW5fZGF0YV9kaXIudmFsdWVzKCkgfCBsaXN0IHwgZmlyc3QgfX0KICAgIHZhbF9kYXRhX3Jvb3Q6IHt7IGRhdGFfcm9vdCB9fXt7IHZhbF9kYXRhX2Rpci52YWx1ZXMoKSB8IGxpc3QgfCBmaXJzdCB9fQogICAgdGVzdF9kYXRhX3Jvb3Q6IHt7IGRhdGFfcm9vdCB9fXt7IHRlc3RfZGF0YV9kaXIudmFsdWVzKCkgfCBsaXN0IHwgZmlyc3QgfX0KICAgICMgbGFiZWxzIHJvb3RzCiAgICB0cmFpbl9sYWJlbF9kYXRhX3Jvb3Q6IHt7IGRhdGFfcm9vdCArIHRyYWluX2xhYmVsc19kaXIgfX0KICAgIHZhbF9sYWJlbF9kYXRhX3Jvb3Q6IHt7IGRhdGFfcm9vdCArIHZhbF9sYWJlbHNfZGlyIH19CiAgICB0ZXN0X2xhYmVsX2RhdGFfcm9vdDoge3sgZGF0YV9yb290ICsgdGVzdF9sYWJlbHNfZGlyIH19CiAgICB7JSBpZiB0cmFpbl9zcGxpdF9wYXRoIC0lfQogICAgdHJhaW5fc3BsaXQ6IHt7IGRhdGFfcm9vdCArIHRyYWluX3NwbGl0X3BhdGggfX0KICAgIHslIGVuZGlmIC0lfQogICAgeyUgaWYgdGVzdF9zcGxpdF9wYXRoIC0lfQogICAgdGVzdF9zcGxpdDoge3sgZGF0YV9yb290ICsgdGVzdF9zcGxpdF9wYXRoIH19CiAgICB7JSBlbmRpZiAtJX0KICAgIHslIGlmIHZhbF9zcGxpdF9wYXRoIC0lfQogICAgdmFsX3NwbGl0OiB7eyBkYXRhX3Jvb3QgKyB2YWxfc3BsaXRfcGF0aCB9fQogICAgeyUgZW5kaWYgLSV9CgogICAgeyUgaWYgaW1nX3N1ZmZpeCAtJX0KICAgIGltZ19ncmVwOiAge3sgaW1nX3N1ZmZpeC52YWx1ZXMoKSB8IGxpc3QgfCBmaXJzdCB8IHRvanNvbiB9fQogICAgeyUgZW5kaWYgLSV9CiAgICB7JSBpZiBzZWdfbWFwX3N1ZmZpeCAtJX0KICAgIGxhYmVsX2dyZXA6ICJ7eyBzZWdfbWFwX3N1ZmZpeCB9fSIKICAgIHslIGVuZGlmIC0lfQogICAgbWVhbnM6IAogICAgICB7eyBub3JtX21lYW5zLnZhbHVlcygpIHwgbGlzdCB8IGZpcnN0fCB0b195YW1sIHwgaW5kZW50KDYpIH19CiAgICBzdGRzOiAKICAgICAge3sgbm9ybV9zdGRzLnZhbHVlcygpIHwgbGlzdCB8IGZpcnN0IHwgdG9feWFtbCB8IGluZGVudCg2KSB9fQogICAgbnVtX2NsYXNzZXM6IHt7IGNsYXNzZXN8bGVuZ3RoIH19CiAgICB0cmFpbl90cmFuc2Zvcm06CiAgICAgIC0gY2xhc3NfcGF0aDogYWxidW1lbnRhdGlvbnMuRDQKICAgICAgLSBjbGFzc19wYXRoOiBUb1RlbnNvclYyCiAgICBub19kYXRhX3JlcGxhY2U6IDAKICAgIG5vX2xhYmVsX3JlcGxhY2U6IC0xCgoKICB7JSBlbHNlICV9CiAgY2xhc3NfcGF0aDogdGVycmF0b3JjaC5kYXRhbW9kdWxlcy5HZW5lcmljTXVsdGlNb2RhbERhdGFNb2R1bGUKICBpbml0X2FyZ3M6CiAgICB7JSBpZiBkYXRhWyJjaGVja19zdGFja2FiaWxpdHkiXSAtJX0KICAgIGNoZWNrX3N0YWNrYWJpbGl0eToge3sgZGF0YVsiY2hlY2tfc3RhY2thYmlsaXR5Il0gfX0KICAgIHslIGVsc2UgJX0KICAgIGNoZWNrX3N0YWNrYWJpbGl0eTogZmFsc2UKICAgIHslIGVuZGlmIC0lfQogICAgIyBDb25maWcgZm9yIG9ubHkgc2VnbWVudGF0aW9uLiBObyBuZWVkIHRvIGF1dG9tYXRlIHRoaXMuIAogICAgdGFzazogJ3NlZ21lbnRhdGlvbicKICAgICMgT3V0IG9mIGN1ZGEgZXJyb3IgZm9yIGFueXRoaW5nID4gMgogICAgIyBUb0RvOiBGaWd1cmUgb3V0IHdoeSBiYXRjaF9zaXplIHJlcGxhY2VtZW50IGlzIG5vdCB3b3JraW5nLgogICAgYmF0Y2hfc2l6ZTogMgogICAgeyUgaWYgbnVtX3dvcmtlcnMgLSV9CiAgICBudW1fd29ya2Vyczoge3sgbnVtX3dvcmtlcnMgfX0KICAgIHslIGVsc2UgLSV9CiAgICBudW1fd29ya2VyczogMgogICAgeyUgZW5kaWYgLSV9CiAgICBub19sYWJlbF9yZXBsYWNlOiB7eyBsYWJlbF9ub2RhdGEgfX0KICAgIG5vX2RhdGFfcmVwbGFjZToge3sgaW1hZ2Vfbm9kYXRhX3JlcGxhY2UgfX0KICAgIGRhdGFzZXRfYmFuZHM6CiAgICAgIHt7IGJhbmRzIHwgdG9feWFtbCB8IGluZGVudCg2KX19CiAgICBvdXRwdXRfYmFuZHM6CiAgICAgIHt7IG91dHB1dF9iYW5kcyB8IHRvX3lhbWwgIHwgaW5kZW50KDYpfX0KICAgIG1vZGFsaXRpZXM6CiAgICAgIHt7IGltYWdlX21vZGFsaXRpZXMgfCB0b195YW1sIHwgaW5kZW50KDYpIH19CiAgICByZ2JfbW9kYWxpdHk6IHt7IHJnYl9tb2RhbGl0eSB9fQogICAgcmdiX2luZGljZXM6IAogICAgICB7eyByZ2JfYmFuZF9pbmRpY2VzIHwgdG9feWFtbCB8IGluZGVudCg2KSB9fQogICAgdHJhaW5fZGF0YV9yb290OgogICAgICB7JSBmb3Iga2V5LCB2YWwgaW4gdHJhaW5fZGF0YV9kaXIuaXRlbXMoKSAtJX0KICAgICAgIHt7IGtleSB9fToge3sgZGF0YV9yb290IH19e3sgdmFsIH19CiAgICAgIHslIGVuZGZvciAlfQogICAgdHJhaW5fbGFiZWxfZGF0YV9yb290OiAge3sgZGF0YV9yb290ICsgIHRyYWluX2xhYmVsc19kaXIgfX0KICAgIHZhbF9kYXRhX3Jvb3Q6CiAgICAgIHslIGZvciBrZXksIHZhbCBpbiB2YWxfZGF0YV9kaXIuaXRlbXMoKSAtJX0KICAgICAgIHt7IGtleSB9fToge3sgZGF0YV9yb290IH19e3sgdmFsIH19CiAgICAgIHslIGVuZGZvciAlfQogICAgdmFsX2xhYmVsX2RhdGFfcm9vdDoge3sgZGF0YV9yb290ICsgIHRlc3RfbGFiZWxzX2RpciB9fQogICAgdGVzdF9kYXRhX3Jvb3Q6CiAgICAgIHslIGZvciBrZXksIHZhbCBpbiB0ZXN0X2RhdGFfZGlyLml0ZW1zKCkgLSV9CiAgICAgICB7eyBrZXkgfX06IHt7IGRhdGFfcm9vdCB9fXt7IHZhbCB9fQogICAgICB7JSBlbmRmb3IgJX0KICAgIHRlc3RfbGFiZWxfZGF0YV9yb290OiB7eyBkYXRhX3Jvb3QgKyB0ZXN0X2xhYmVsc19kaXIgfX0KICAgIHslIGlmIHRyYWluX3NwbGl0X3BhdGggLSV9CiAgICB0cmFpbl9zcGxpdDoge3sgIGRhdGFfcm9vdCArIHRyYWluX3NwbGl0X3BhdGggfX0KICAgIHslIGVuZGlmIC0lfQogICAgeyUgaWYgdGVzdF9zcGxpdF9wYXRoIC0lfQogICAgdGVzdF9zcGxpdDoge3sgIGRhdGFfcm9vdCArIHRlc3Rfc3BsaXRfcGF0aCB9fQogICAgeyUgZW5kaWYgLSV9CiAgICB7JSBpZiB2YWxfc3BsaXRfcGF0aCAtJX0KICAgIHZhbF9zcGxpdDoge3sgIGRhdGFfcm9vdCArIHZhbF9zcGxpdF9wYXRoIH19CiAgICB7JSBlbmRpZiAtJX0KICAgIHslIGlmIGltZ19zdWZmaXggLSV9CiAgICBpbWFnZV9ncmVwOiAKICAgICAge3sgaW1nX3N1ZmZpeCB8IHRvX3lhbWwgfCBpbmRlbnQoNikgfX0KICAgIHslIGVuZGlmIC0lfQogICAgeyUgaWYgc2VnX21hcF9zdWZmaXggLSV9CiAgICBsYWJlbF9ncmVwOiAie3sgc2VnX21hcF9zdWZmaXggfX0iCiAgICB7JSBlbmRpZiAtJX0KCiAgICBudW1fY2xhc3Nlczoge3sgY2xhc3Nlc3xsZW5ndGggfX0KICAgIHslIGlmIGRhdGFbImV4cGFuZF90ZW1wb3JhbF9kaW1lbnNpb24iXSBpcyBub3Qgbm9uZSAtJX0KICAgIGV4cGFuZF90ZW1wb3JhbF9kaW1lbnNpb246IGRhdGFbImV4cGFuZF90ZW1wb3JhbF9kaW1lbnNpb24iXQogICAgeyUgZW5kaWYgLSV9CiAgICB7JSBpZiBkYXRhWyJkcm9wX2xhc3QiXSBpcyBub3Qgbm9uZSAtJX0KICAgIGRyb3BfbGFzdDogZGF0YVsiZHJvcF9sYXN0Il0KICAgIHslIGVuZGlmIC0lfQoKICAgIG1lYW5zOiAKICAgICAge3sgbm9ybV9tZWFucyB8IHRvX3lhbWwgfCBpbmRlbnQoNikgfX0KICAgIHN0ZHM6IAogICAgICB7eyBub3JtX3N0ZHMgfCB0b195YW1sIHwgaW5kZW50KDYpIH19CiAgICB0cmFpbl90cmFuc2Zvcm06CiAgICAgIC0gY2xhc3NfcGF0aDogYWxidW1lbnRhdGlvbnMuRDQgICMgUmFuZG9tIGZsaXAgYW5kIHJvdGF0aW9ucwogICAgICAtIGNsYXNzX3BhdGg6IGFsYnVtZW50YXRpb25zLnB5dG9yY2gudHJhbnNmb3Jtcy5Ub1RlbnNvclYyCiAgeyUgZW5kaWYgJX0KbW9kZWw6CiAgY2xhc3NfcGF0aDogdGVycmF0b3JjaC50YXNrcy5TZW1hbnRpY1NlZ21lbnRhdGlvblRhc2sKICBpbml0X2FyZ3M6CiAgICBtb2RlbF9mYWN0b3J5OiBFbmNvZGVyRGVjb2RlckZhY3RvcnkKICAgIG1vZGVsX2FyZ3M6CiAgICAgIGJhY2tib25lOiB7eyBwcmV0cmFpbmVkX21vZGVsX25hbWUgfX0KICAgICAgIyAgdGVycmFtaW5kX3YxX2Jhc2UgICMgbGFyZ2UgdmVyc2lvbjogdGVycmFtaW5kX3YxX2xhcmdlCiAgICAgIGJhY2tib25lX3ByZXRyYWluZWQ6IHRydWUKICAgICAgeyUtIGlmIGltYWdlX21vZGFsaXRpZXMgJX0KICAgICAgYmFja2JvbmVfbW9kYWxpdGllczoKICAgICAgICB7eyBpbWFnZV9tb2RhbGl0aWVzIHwgdG9feWFtbCAgfCBpbmRlbnQoOCl9fQogICAgICB7JS0gZW5kaWYgJX0KICAgICAgeyUgaWYgIGltYWdlX21vZGFsaXRpZXN8bGVuZ3RoID4gMSAlfQogICAgICBiYWNrYm9uZV9tZXJnZV9tZXRob2Q6IG1lYW4KICAgICAgeyUgZW5kaWYgJX0KICAgICAgYmFja2JvbmVfYmFuZHM6IAogICAgICAgIHt7IG91dHB1dF9iYW5kcyB8IHRvX3lhbWwgfCBpbmRlbnQoOCkgfX0KICAgICAgbmVja3M6CiAgICAgICAgeyUtIGlmIHByZXRyYWluZWRfbW9kZWxfbmFtZSA9PSAidGVycmFtaW5kX3YxX3RpbnkiICV9CiAgICAgICAgLSBuYW1lOiBTZWxlY3RJbmRpY2VzCiAgICAgICAgICBpbmRpY2VzOiBbMiwgNSwgOCwgMTFdICAjIHRpbnkgdmVyc2lvbgogICAgICAgIHslLSBlbGlmIHByZXRyYWluZWRfbW9kZWxfbmFtZSA9PSAidGVycmFtaW5kX3YxX2Jhc2UiICV9CiAgICAgICAgLSBuYW1lOiBTZWxlY3RJbmRpY2VzCiAgICAgICAgICBpbmRpY2VzOiBbMiwgNSwgOCwgMTFdICAjIGJhc2UgdmVyc2lvbgogICAgICAgIHslLSBlbGlmIHByZXRyYWluZWRfbW9kZWxfbmFtZSA9PSAidGVycmFtaW5kX3YxX2xhcmdlIiAlfQogICAgICAgIC0gbmFtZTogU2VsZWN0SW5kaWNlcwogICAgICAgICAgaW5kaWNlczogWzUsIDExLCAxNywgMjNdICAjIGxhcmdlIHZlcnNpb24KICAgICAgICB7JSBlbmRpZiAlfQogICAgICAgIC0gbmFtZTogUmVzaGFwZVRva2Vuc1RvSW1hZ2UKICAgICAgICAgIHJlbW92ZV9jbHNfdG9rZW46IEZhbHNlCiAgICAgICAgLSBuYW1lOiBMZWFybmVkSW50ZXJwb2xhdGVUb1B5cmFtaWRhbAoKICAgICAgZGVjb2Rlcjoge3sgbW9kZWxbImRlY29kZV9oZWFkIl1bImRlY29kZXIiXSB9fQogICAgICAjIFVOZXREZWNvZGVyCiAgICAgIHslIGlmICBtb2RlbFsiZGVjb2RlX2hlYWQiXVsiZGVjb2RlciJdID09ICJVcGVyTmV0RGVjb2RlciIgLSV9CiAgICAgIGRlY29kZXJfY2hhbm5lbHM6IFs1MTIsIDI1NiwgMTI4LCA2NF0KICAgICAgeyUgZWxpZiAgbW9kZWxbImRlY29kZV9oZWFkIl1bImRlY29kZXIiXSA9PSAiVU5ldERlY29kZXIiIC0lfQogICAgICAjVE9ETyB1c2VyIHByb3ZpZGVkIGNoYW5uZWxzCiAgICAgIGRlY29kZXJfY2hhbm5lbHM6IFs1MTIsIDI1NiwgMTI4LCA2NF0KICAgICAgeyUgZWxzZSAlfQogICAgICBkZWNvZGVyX2NoYW5uZWxzOiB7eyBtb2RlbFsiZGVjb2RlX2hlYWQiXVsiY2hhbm5lbHMiXSB9fQogICAgICB7JSBlbmRpZiAtJX0KICAgICAgaGVhZF9kcm9wb3V0OiAwLjEKICAgICAgbnVtX2NsYXNzZXM6IHt7IGNsYXNzZXN8bGVuZ3RoIH19CiAgICBsb3NzOiB7eyBtb2RlbFsiZGVjb2RlX2hlYWQiXVsibG9zc19kZWNvZGUiXVsidHlwZSJdIH19CiAgICBwbG90X29uX3ZhbDoge3sgcnVubmVyWyJwbG90X29uX3ZhbCJdIH19CiAgICAjICBkaWNlCiAgICBpZ25vcmVfaW5kZXg6IHt7IGlnbm9yZV9pbmRleCB9fQogICAgZnJlZXplX2JhY2tib25lOiAge3sgbW9kZWxbImZyb3plbl9iYWNrYm9uZSJdIHwgbG93ZXIgfX0KICAgIGZyZWV6ZV9kZWNvZGVyOiBmYWxzZQogICAgeyUgaWYgIG1vZGVsWyJjbGFzc19uYW1lcyJdICV9CiAgICBjbGFzc19uYW1lczoge3sgbW9kZWxbImNsYXNzX25hbWVzIl0gfCB0b195YW1sIHwgaW5kZW50KDYpfX0KICAgIHslIGVuZGlmICV9CiAgICAjIC0tLS0gb3B0aW1pemVyIHN0YXJ0IC0tLS0KICAgIHslIGlmIG1vZGVsWyJvcHRpbWl6ZXIiXSAtJX0KICAgIG9wdGltaXplcjoge3sgbW9kZWxbIm9wdGltaXplciJdWyJ0eXBlIl0gfX0KICAgIGxyOiB7eyBtb2RlbFsib3B0aW1pemVyIl1bImxyIl0gfCBmbG9hdCB9fQogICAgeyUgZW5kaWYgLSV9CiAgICAjIC0tLS0gb3B0aW1pemVyIGVuZCAtLS0tCiAgICB7JSBpZiBtb2RlbFsidGlsZWRfaW5mZXJlbmNlX3BhcmFtZXRlcnMiXSAlfQogICAgdGlsZWRfaW5mZXJlbmNlX3BhcmFtZXRlcnM6IAogICAgICBoX2Nyb3A6IHt7IG1vZGVsWyJ0aWxlZF9pbmZlcmVuY2VfcGFyYW1ldGVycyJdWyJoX2Nyb3AiXSB8IGludH19CiAgICAgIGhfc3RyaWRlOiB7eyBtb2RlbFsidGlsZWRfaW5mZXJlbmNlX3BhcmFtZXRlcnMiXVsiaF9zdHJpZGUiXSB8IGludCB9fQogICAgICB3X2Nyb3A6IHt7IG1vZGVsWyJ0aWxlZF9pbmZlcmVuY2VfcGFyYW1ldGVycyJdWyJ3X2Nyb3AiXSB8IGludH19CiAgICAgIHdfc3RyaWRlOiB7eyBtb2RlbFsidGlsZWRfaW5mZXJlbmNlX3BhcmFtZXRlcnMiXVsid19zdHJpZGUiXSB8IGludCB9fQogICAgICBhdmVyYWdlX3BhdGNoZXM6IHt7IG1vZGVsWyJ0aWxlZF9pbmZlcmVuY2VfcGFyYW1ldGVycyJdWyJhdmVyYWdlX3BhdGNoZXMiXSB9fQogICAgeyUgZWxzZSAlfQogICAgIyBUb0RvOiBSZW1vdmUgdGhlIHRpbGVkX2luZmVyZW5jZSBpZiB1c2VyIG5vdCBwcm92aWRlZC4gCiAgICB0aWxlZF9pbmZlcmVuY2VfcGFyYW1ldGVyczogCiAgICAgIGhfY3JvcDogMjI0CiAgICAgICMgc3RyaWRlIGxvZ2ljID0gd291bGQgYmUgaF9jcm9wIC0gaF9jcm9wICogMC4xMjUKICAgICAgaF9zdHJpZGU6IDIwOAogICAgICB3X2Nyb3A6IDIyNAogICAgICAjIHN0cmlkZSBsb2dpYyA9IHdvdWxkIGJlIHdfY3JvcCAtIHdfY3JvcCAqIDAuMTI1CiAgICAgIHdfc3RyaWRlOiAyMDgKICAgICAgYXZlcmFnZV9wYXRjaGVzOiB0cnVlCiAgICB7JSBlbmRpZiAlfQoKb3B0aW1pemVyOgogIGNsYXNzX3BhdGg6IHRvcmNoLm9wdGltLkFkYW1XCiAgaW5pdF9hcmdzOgogICAgeyUgaWYgb3B0aW1pemVyWyJsciJdIC0lfQogICAgbHI6IHt7IG9wdGltaXplclsibHIiXSB8IGZsb2F0IH19CiAgICB7JSBlbHNlICV9CiAgICBscjogMi5lLTUKICAgIHslIGVuZGlmIC0lfQogICAgeyUtIGlmIG9wdGltaXplclsid2VpZ2h0X2RlY2F5Il0gLSV9CiAgICB3ZWlnaHRfZGVjYXk6IHt7IG9wdGltaXplclsid2VpZ2h0X2RlY2F5Il0gfX0KICAgIHslLSBlbHNlIC0lfQogICAgd2VpZ2h0X2RlY2F5OiAwLjA1CiAgICB7JSBlbmRpZiAlfQpscl9zY2hlZHVsZXI6CiAgY2xhc3NfcGF0aDogUmVkdWNlTFJPblBsYXRlYXUKICBpbml0X2FyZ3M6CiAgICBtb25pdG9yOiB2YWwvbG9zcwogICAgZmFjdG9yOiAwLjUKICAgIHBhdGllbmNlOiA1Cg=="
}
res = gfm_client.create_task(data=create_terramind_template)
# Select the task you created for your fine-tuning job
task_id = res["id"]
Review the created task¶
# Review the created task with the original template
tt = gfm_client.get_task_template("601cc248-135e-4ad3-96e1-0bb270ac0534", output='cell')
# Review the created task with the replaced template with the provided dataset
rendered_template = gfm_client.render_template(task_id = "601cc248-135e-4ad3-96e1-0bb270ac0534", dataset_id=selected_dataset, output="cell")
Configure the task parameters you want¶
# view the full meta-data and details of the selected task
task_meta = gfm_client.get_task(task_id=task_id)
task_meta
If you are happy with your choice, you can decide which (if any) hyperparameters you want to set (otherwise defaults will be used).
Here we can see the available parameters and their associated defaults. To update a parameter you can just set values in the dictionary (as shown below for max_epochs).
# show the default values for parameters
task_params = gfm_client.get_task_param_defaults(task_id)
task_params
# [Optional]Configure the parameters you want
task_params['runner']['max_epochs'] = '3'
# task_params['optimizer']['type'] = 'AdamW'
# task_params['data']['batch_size'] = 4
Base foundation model¶
The base model is the foundation model (encoder) which has been pre-trained and has the basic understanding of the data. More information can currently be found on the Terramind tiny model can be found on hugging face.
For this example, we will start by creating a base foundation model entry for terramind tiny in the GEOstudio
# list foundation models populated with your Studio deployment
base = gfm_client.list_base_models(output='df')
display(base[['name','description','id','updated_at']])
# Create terramind Tiny base model entry in the Studio
create_terramind_tiny_backbone = {
"name": "terramind_v1_tiny",
"description": "Terramind Multimodal tiny backbone base",
"checkpoint_filename": "terramind_v1_tiny/terramind_v1_tiny.pt",
"model_params": {
"backbone": "terramind_v1_tiny",
"model_category": "terramind"
}
}
base_model = gfm_client.create_base_model(data=create_terramind_tiny_backbone)
# select base foundation model you just created for your fine-tuning task
base_model_id = base_model["id"]
Submitting the tune¶
Now we pull these choices together into a payload which we then submit to the platform. This will then deploy the job in the backend and we will see below how we can monitor it. First, we populate the payload so we can check it, then we simply submit.
# create the tune payload
tune_payload = {
"name": "test-fine-tuning-tiny",
"description": "Segmentation",
"dataset_id": selected_dataset,
"base_model_id": base_model_id,
"tune_template_id": task_id,
}
print(json.dumps(tune_payload, indent=2))
# submit tune
submitted = gfm_client.submit_tune(
data = tune_payload,
output = 'json'
)
print(submitted)
Monitoring training¶
Once the tune has been submitted you can check its status and monitor tuning progress through the SDK. You can also access the training metrics and images in MLflow. The get_tune function will give you the meta-data of the tune, including the status.
# get metadata about the submitted tune
tune_id = submitted.get("tune_id")
tune_info = gfm_client.get_tune(tune_id, output='json')
tune_info
Once the model has started training, you will also be able to access the training metrics. The get_tune_metrics_df function returns a dataframe containing the up-to-date training metrics, which you are free to explore and analyse. In addition to that, you can simply plot the training and validation loss and multi-class accuracy using the plot_tune_metrics function.
# get training metrics
mdf = gfm_client.get_mlflow_metrics(tune_id)
mdf.head()
# plot some basic training metrics
# gfm_client.plot_tune_metrics(tune_id)
Once your model is finished training and you are happy with the metrics (and images in MLflow), you can run some inference in test mode through the inference service.
Testing your model¶
Example flood events
| Location | Date | Bounding box | Link |
|---|---|---|---|
| Maiduguri, Nigeria | 2024-09-12 | [13.146418, 11.799808, 13.215874, 11.871586] | https://www.aljazeera.com/features/2024/9/19/a-disaster-homes-lost-relatives-missing-in-floods-in-northeast-nigeria |
| Porto Alegre, Brazil | 2024-05-06 | [-51.33225, -30.08903, -51.19011, -29.97489] | https://www.reuters.com/pictures/stunning-images-show-extent-flooding-southern-brazil-2024-05-07/ |
| Ahero, Kenya | 2024-05-05 | [34.838652, -0.231379, 34.977847, -0.131439] | |
| Gloucester, UK | 2024-01-09 | [-2.311807, 51.855573, -2.17892, 51.952735] |
Try out the model for inference¶
Once your model has finished tuning, if you want to run inference as a test you can do by passing either a location (bbox) or a url to a sample tiff file. For this walkthrough, we will run inference with the bbox option. The steps to test the model are:
- Define the inference payload
- Try out the tune
# define the inference payload
inference_payload = {
"spatial_domain": {
"bbox": [
[
34.709244,
-0.307616,
35.121231,
-0.065918
]
]
},
"temporal_domain": [
"2024-04-30_2024-05-07"
],
"model_input_data_spec": [
{
"bands": [
{
"index": "0",
"band_name": "coastal",
"scaling_factor": "1",
"description": ""
},
{
"index": "1",
"band_name": "blue",
"scaling_factor": "1",
"RGB_band": "B",
"description": ""
},
{
"index": "2",
"band_name": "green",
"scaling_factor": "1",
"RGB_band": "G",
"description": ""
},
{
"index": "3",
"band_name": "red",
"scaling_factor": "1",
"RGB_band": "R",
"description": ""
},
{
"index": "4",
"band_name": "rededge1",
"scaling_factor": "1",
"description": ""
},
{
"index": "5",
"band_name": "rededge2",
"scaling_factor": "1",
"description": ""
},
{
"index": "6",
"band_name": "rededge3",
"scaling_factor": "1",
"description": ""
},
{
"index": "7",
"band_name": "nir",
"scaling_factor": "1",
"description": ""
},
{
"index": "8",
"band_name": "nir08",
"scaling_factor": "1",
"description": ""
},
{
"index": "9",
"band_name": "nir09",
"scaling_factor": "1",
"description": ""
},
{
"index": "10",
"band_name": "swir16",
"scaling_factor": "1",
"description": ""
},
{
"index": "11",
"band_name": "swir22",
"scaling_factor": "1",
"description": ""
},
{
"index": "12",
"band_name": "scl",
"scaling_factor": "1",
"description": ""
}
],
"connector": "sentinel_aws",
"collection": "sentinel-2-l2a",
"file_suffix": "S2Hand",
"modality_tag": "S2L2A"
},
{
"bands": [
{
"index": "0",
"band_name": "VV",
"scaling_factor": 1,
"description": ""
},
{
"index": "1",
"band_name": "VH",
"scaling_factor": 1,
"description": ""
}
],
"connector": "sentinelhub",
"collection": "s1_grd",
"file_suffix": "S1Hand",
"modality_tag": "S1GRD",
"align_dates": "true",
"scaling_factor": [
1,
1
]
}
],
"geoserver_push": [
{
"workspace": "geofm",
"layer_name": "input_rgb",
"display_name": "Input image (RGB)",
"filepath_key": "model_input_original_image_rgb",
"file_suffix": "",
"z_index": 0,
"visible_by_default": "True",
"geoserver_style": {
"rgb": [
{
"minValue": 0,
"maxValue": "2000",
"channel": 1,
"label": "RedChannel"
},
{
"minValue": 0,
"maxValue": "2000",
"channel": 2,
"label": "GreenChannel"
},
{
"minValue": 0,
"maxValue": "2000",
"channel": 3,
"label": "BlueChannel"
}
]
}
},
{
"workspace": "geofm",
"layer_name": "pred",
"display_name": "Model prediction",
"filepath_key": "model_output_image_masked",
"file_suffix": "",
"z_index": 1,
"visible_by_default": "True",
"geoserver_style": {
"segmentation": [
{
"color": "#808080",
"label": "No flood",
"opacity": "0",
"quantity": "0"
},
{
"color": "#FA4D56",
"label": "Flood",
"opacity": 1,
"quantity": "1"
},
{
"color": "#4589FF",
"label": "Permanent water",
"opacity": 1,
"quantity": 997
},
{
"color": "#FFFAFA",
"label": "Snow/ice",
"opacity": 1,
"quantity": 998
},
{
"color": "#CCCCCC",
"label": "Clouds",
"opacity": 1,
"quantity": 999
}
]
}
}
],
"model_display_name": "geofm-sandbox-models",
"fine_tuning_id": tune_id,
"description": "Ahero: Kisumu Inference",
"location": "Kamahawa /kasiwindi, Ahero ward, Kenya, "
}
Once you have registered the model, you can now run. You can then monitor it and visualise the outputs either through the SDK, or in the UI.
# Now submit the test inference request
inference_response = gfm_client.try_out_tune(tune_id=tune_id, data=inference_payload)
inference_response
Monitoring your inference task and Visualize results¶
Once submitted you can check on progress from the UI side. Log into the UI of your deployed GEOStudio instance and click on the Inference lab page. Click on the history icon on the top left and you can click on the inference task you submitted to see its progress as well as visualize results when complete
# get metadata about the inference task
gfm_client.get_inference(inference_response['id'])
Checking model outputs¶
You can check out the results visually in the Studio UI, or with the quick widget below. You can alternatively use the SDK to download selected files for further analysis see documentation.
Note:
For this walkthrough, you can check out the inference output and results visually in the Studio UI through the history tab of the inference page.
# view inference results
# gswidgets.inferenceViewer(gfm_client, inference_response['id'])