Export to pamguard
The PamgUard module that supports deep learning models expects a format that's slightly different from the current ketos format (.kt).
The main differences are:
- extra fields in the audio representation
- data type
- number of dimensions for the model input
- the shape of the input
- The model itself is expected to be in tensorflow's protobuf format.
Here is a draft function to save an instance of a ketos model to this format:
from tensorflow.saved_model import save as export_to_saved_model
from ketos.audio.audio_loader import audio_repres_dict
from zipfile import ZipFile
import warnings
import json
import shutil
import os
def export_to_pamguard(ketos_model, output_name, audio_repr_dict=audio_repres_dict, tmp_folder="tmp_export_folder", overwrite=False, expected_input_shape=None):
""" Export a ketos model to a PamGuard-compatible fortmat
Args:
ketos_model:
The ketos model to be exported. Usually created by one of the Interface classes found in ketos.neural_networks (e.g.: ResNetInterface)
output_name: str
The name of the exported model, usually with a '.ktpg" extension.
audio_repr_dict: dict
A dictionary with keys as the names of supportes audio representations and values as the classes that implement them.
A dictionary like this can be found at ketos.audio.audio_loader.audio_repres_dict, and is used as default.
tmp_folder: str
The name for a temporary folder created during the model conversion. It will be deleted upon sucessful execution.
If the folder already exists, a 'FileExistsError will be thrown ( unless 'overwite' is set to True).
overwrite: bool
If true and the folder specified in 'tmp_folder' exists, the folder will be overwritten.
expected_input_shape: None, list or tuple.
The input shape expected by the model. It can be represented by a tuple or list of four elements: [number of intances, width, height, number of channels). The number of instances and number of channels are commonly 1, and the width and height are usually the number of time and frequency bins ins a spectrogram, respectively. This, however, can vary with the model in question.
"""
if expected_input_shape is None:
warnings.warning("Input shape not specified. The model will be saved but this might cause problems if the importer expects an input shape")
if os.path.exists(tmp_folder):
if not overwrite:
raise FileExistsError("{} already exists. If you want to overwrite it set the 'overwrite' argument to True.".format(tmp_folder))
else:
shutil.rmtree(tmp_folder)
assert model.model.built, "The model must be built. Call model.run_on_instance() on a sample input"
audio_inputs = audio_repr_dict.keys()
for audio_input in audio_inputs:
audio_repr_dict[audio_input]['dtype'] = model.model.dtype
audio_repr_dict[audio_input]['input_ndims'] = model.model.layers[0].input_spec.min_ndim
audio_repr_dict[audio_input]['input_shape'] = expected_input_shape
os.makedirs(tmp_folder)
recipe_path = os.path.join(tmp_folder, 'recipe.json')
model.save_recipe_file(recipe_path)
model_path = os.path.join(tmp_folder, 'model')
export_to_saved_model(obj=model.model, export_dir=model_path)
with ZipFile(output_name, 'w') as zip:
zip.write(model_path, "model")
for root, dirs, files in os.walk(model_path):
renamed_root = root.replace(model_path, "model")
for d in dirs:
zip.write(os.path.join(root,d), os.path.join(renamed_root,d))
for f in files:
zip.write(os.path.join(root,f),os.path.join(renamed_root,f))
zip.write(recipe_path, "recipe.json")
if audio_repr_dict is not None:
audio_repr_path = os.path.join(tmp_folder, "audio_repr.json")
with open(audio_repr_path, 'w') as json_repr:
json.dump(audio_repr_dict, json_repr)
zip.write(audio_repr_path, "audio_repr.json")
shutil.rmtree(tmp_folder)
Here is an example using the function:
from ketos.neural_networks import load_model_file
from ketos.audio.audio_loader import audio_repres_dict
model, audio_repr = load_model_file("narw.kt", './tmp_folder', load_audio_repr=True)
spec_config = audio_repr[0]['spectrogram']
input_spec = audio_repres_dict[spec_config['type']].from_wav(path="input.wav", **spec_config)
input_spec = input_spec.data
model.run_on_instance(input_spec)
export_to_pamguard(ketos_model=model, output_name="narw.ktpg", audio_repr_dict=audio_repr[0], overwrite=True, expected_input_shape=(1,94,129,1))
And attached is an example of the model in the PamGuard-compatible format.
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information