import marimo __generated_with = "0.10.17" app = marimo.App(width="medium") @app.cell(hide_code=True) def _(): def app_vars(): app = dict( author_name='Jesús Pérez', author_link='mailto:info@jesusperez.pro', name='PerfSPEC Learning 🍃', train_mode = 'LSTM', date="2025", version="1.0", repo='https://repo.jesusperez.pro/jesus/perfspec-learning', about='https://repo.jesusperez.pro/jesus/perfspec-learning/src/branch/main/about.md', intro='https://repo.jesusperez.pro/jesus/perfspec-learning/src/branch/main/intro.md', image='https://repo.jesusperez.pro/jesus/perfspec-learning/media/branch/main/imgs/perfSPEC-learning.png', ) return app return (app_vars,) @app.cell(hide_code=True) def title(app_vars): import marimo as mo notebook_name = 'lib_perfspec.py' _app = app_vars() mo.md( f""" {mo.hstack([ mo.md(f"{notebook_name} | vers: {_app['version']} {_app['date']} "), mo.md(f"[{_app['author_name']}]({_app['author_link']})"), ])} {mo.vstack(items=[ mo.md(f"
"), mo.md(f"# {_app['name']}"), mo.md(f'
'), ],justify='center',gap=0.)} It has been design as a [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) approach to put `thing` in only one place """ ) return mo, notebook_name @app.cell(hide_code=True) def perfspec_header(mo, perfspec): def header(notebook_name): return f""" {mo.hstack([ mo.md(f"{notebook_name} | vers: {perfspec['app']['version']} {perfspec['app']['date']} "), perfspec['app']['main_menu'], mo.md(f"[{perfspec['app']['author_name']}]({perfspec['app']['author_link']})"), ])} {mo.vstack(items=[ mo.md(f"
"), mo.md(f"# {perfspec['app']['name']}"), mo.md(f'
'), ],justify='center',gap=0.)} """ def intro(notebook_name): return f""" ## Intro [{notebook_name}]({perfspec['defaults']['models_dirpath'] / notebook_name}") __notebook__ is created for `trained model PREDICTIONS` with [{perfspec['app']['train_mode']}](https://en.wikipedia.org/wiki/Long_short-term_memory) using [Tensor Keras](https://www.tensorflow.org/guide/keras) Settings are defined in: [lib_prefspec.py]({mo.notebook_dir()} / lib_perfspec.py) Values can be overwritten by using **command-line** see current values below """ return header, intro @app.cell(hide_code=True) def perfspec_vars(app_vars, mo): from pathlib import Path perfspec = dict( defaults = dict( epochs=300, train_size=0.8, sequence_length=2, model_path="perfSPEC_model.keras", actions_path="actions-dataset-audit.txt", history_path="history.json", checkpoint_path="model_at_epoch_{epoch:02d}.keras", checkpoint_mode="epochs", # alternative "weights" pred_input="delete_apiservices", verbose=None, data_dirpath=mo.notebook_dir() / "../../data", models_dirpath=mo.notebook_dir() / "../../models", checkpoints_dirpath=mo.notebook_dir() / "../../models/checkpoints", ), app = app_vars(), ) if not Path(perfspec["defaults"]["models_dirpath"]).exists(): exit( f"Trained models path not found: {perfspec['defaults']['models_dirpath']}" ) if not Path(perfspec["defaults"]["data_dirpath"]).exists(): exit(f"Data path not found: {perfspec['defaults']['data_dirpath']}") if not Path(perfspec["defaults"]["checkpoints_dirpath"]).exists(): exit( f"Checkpoints path not found: {perfspec['defaults']['checkpoints_dirpath']}" ) perfspec["app"]['main_menu'] = mo.nav_menu( { "#main": "🍃 Main", f"{perfspec['app']['intro']}": f"{mo.icon('lucide:shield-check')} Intro", f"{perfspec['app']['about']}": f"{mo.icon('lucide:package')} About", f"{perfspec['app']['repo']}": f"{mo.icon('lucide:github')} Repo", } ) perfspec["settings"] = dict( # Number of epochs epochs=mo.cli_args().get("epochs") or perfspec["defaults"]["epochs"], checkpoint_mode = mo.cli_args().get("checkpoint_mode") or perfspec["defaults"]["checkpoint_mode"], # Number of train_size train_size=mo.cli_args().get("train_size") or perfspec["defaults"]["train_size"], sequence_length=mo.cli_args().get("sequence") or perfspec["defaults"]["sequence_length"], embedding_dim=50, # Dimensionality of embedding vectors input_length=mo.cli_args().get("sequence") or perfspec["defaults"]["sequence_length"], lstm_units_1=256, # Depth of the first LSTM layer lstm_units_2=128, # Depth of the second LSTM layer dropout_rate=0.2, # Dropout rate batch_size=16, # Adjusted for smaller datasets model_history_filepath=perfspec["defaults"]["models_dirpath"] / perfspec["defaults"]["history_path"], model_filepath=mo.cli_args().get("model_path") or perfspec["defaults"]["models_dirpath"] / perfspec["defaults"]["model_path"], checkpoint_filepath=mo.cli_args().get("checkpoint_path") or perfspec["defaults"]["checkpoints_dirpath"] / perfspec["defaults"]["checkpoint_path"], actions_filepath=mo.cli_args().get("actions_path") or perfspec["defaults"]["data_dirpath"] / perfspec["defaults"]["actions_path"], input_str=mo.cli_args().get("input") or perfspec["defaults"]["pred_input"], main_audit_log = perfspec['defaults']['data_dirpath'] / "main-audit-logs.log", raw_audit_log = perfspec['defaults']['data_dirpath'] / "raw-audit-logs.log", actions_distrib_filepath = perfspec['defaults']['data_dirpath'] / 'actions_distribution.pdf', actions_distrib_format = 'pdf' ) if mo.cli_args().get("verbose") == "": perfspec["settings"]["verbose"] = 1 else: perfspec["settings"]["verbose"] = ( mo.cli_args().get("verbose") or perfspec["defaults"]["verbose"] ) if not Path(perfspec["settings"]["actions_filepath"]).exists(): exit( f"Actions path not found: {perfspec['settings']['actions_filepath']}" ) mo.md( f""" # Vars settings 🍃 **perfspec** includes: `defaults` and `settings` to keep all vars inside one dictionary Main configurable variables some of them can be overload from **command-line** Be sur `defaults` path exist for: | variable | path | |:---------- | :---------------- | | data_dirpath | {perfspec["defaults"]["data_dirpath"]} | | checkpoints_dirpath | {perfspec["defaults"]["checkpoints_dirpath"]} | | actions_filepath | {perfspec["settings"]["actions_filepath"]} | These path can be changed by using full file path with **command-line** arguments """ ) return Path, perfspec @app.cell(hide_code=True) def perfspec_args(mo, perfspec): if mo.cli_args().get("version") == "": print (f"PerfSPEC Learning: {perfspec['defaults']['version']}") exit() if mo.cli_args().get("help") == "": print ("PerfSPEC Learning:\n") print (f" To predict sequences use:\n\t\t --input value,value1 (default: {perfspec['defaults']['pred_input']})") print (f" Epochs to train use:\n\t\t --epochs num-value (default: {perfspec['defaults']['epochs']})") print (f" For train size use:\n\t\t --train_size decimal-value (default: {perfspec['defaults']['train_size']}i)") print (f" For sequence size in train model use:\n\t\t --sequence num-value (default: {perfspec['defaults']['train_size']})") print (f" For checkpoint mode (epochs,weights) in train model use:\n\t\t --checkpoint_mode value (default: {perfspec['defaults']['checkpoint_mode']})") print (f" For model_filepath use:\n\t\t --model_path model_filepath (default: {perfspec['defaults']['model_path']})") print (f" For checkpoint_filepath use:\n\t\t --checkpoint_path checkpoint_filepath (default: {perfspec['defaults']['checkpoint_path']})") print (f" For actions_filepath use:\n\t\t --actions_path actions_filepath\n\t\t (default: {perfspec['settings']['actions_filepath']})") print (f" For verbose mode use:\n\t\t --verbose (default: {perfspec['defaults']['verbose']})") exit() mo.md( f""" # Arguments 🍃 Main arguments parsing some of values can be overload from **command-line** """ ) return @app.cell(hide_code=True) def perfspec_cli_ops(mo): def out_cli_ops(notebook_name): return( f"""

Command-Line options

Input value for prediction trained model

```python {mo.notebook_location()}/{notebook_name} -- --input `value, value-1` ```

Model filepath

```python {mo.notebook_location()}/{notebook_name} -- --model_path `model-filepath` ```

Checkpoints model filepath

```python {mo.notebook_location()}/{notebook_name} -- --checkpoint_path `checkpoint-filepath` ```

Actions filepath

```python {mo.notebook_location()}/{notebook_name} -- --actions_path `actions-filepath` ```

Sequence to train model

```python {mo.notebook_location()}/{notebook_name} -- ---sequence `num-value` ```

Epochs to train model

```python {mo.notebook_location()}/{notebook_name} -- ---epochs `num-value` ```

Train model size

```python {mo.notebook_location()}/{notebook_name} -- ---train_size `decimal-value` ```

Train model checkpoint modes

```python {mo.notebook_location()}/{notebook_name} -- ---checkpoint_mode `decimal-value` ```

Verbose

```python {mo.notebook_location()}/{notebook_name} -- --verbose ``` """ ) mo.md( f""" # Command-line options 🍃 """ ) return (out_cli_ops,) @app.cell(hide_code=True) def perfspec_out_settings(mo, perfspec): def out_settings(notebook_name): return( f"""

Defaults

| variable | value | descripción | |:---------- | :---- | :------------ | | models dir path | {perfspec['defaults']['models_dirpath']} | models path | | model file | **{perfspec['defaults']['model_path']}** | trained model filepath | | checkpoints model dir path | {perfspec['defaults']['checkpoints_dirpath']} | checkpoint models path | | checkpoint model file | **{perfspec['defaults']['checkpoint_path']}** | checkpoint trained model filepath | | data_path | {perfspec['defaults']['data_dirpath']} | data path | | actions file| **{perfspec['defaults']['actions_path']}** | actions filepath | | history file| **{perfspec['defaults']['history_path']}** | history filepath | | input | **{perfspec['defaults']['pred_input']}** | input value for prediction | | verbose | None | show detail info |

Train model settings

| variable | value | descripción | |:---------- | :---- | :------------ | | epochs | **{perfspec['defaults']['epochs']}** | or change in `comman-line`
with **--epochs** `num-value` | | train_size | **{perfspec['defaults']['train_size']}** | or change in `comman-line`
with **--train_size** `decimal-value` | | sequence_length | **{perfspec['defaults']['sequence_length']}** | Length of sequencs
or change in `comman-line`
with **--sequence** `num-value` | | checkpoint_mode | **{perfspec['defaults']['checkpoint_mode']}** | Checkpoint_mode (epochs | weights)
or change in `comman-line`
with **--checkpoint_mode** `num-value` | | input_length | {perfspec['defaults']['sequence_length']} | Length of input sequencs | | lstm_units_1 | {perfspec['settings']['lstm_units_1']} | Depth of the first LSTM layer | | lstm_units_2 | {perfspec['settings']['lstm_units_2']} | Depth of the second LSTM layer | | dropout_rate | {perfspec['settings']['dropout_rate']} | Dropout rate | | batch_size | {perfspec['settings']['batch_size']} | Adjusted for smaller datasets | """ ) mo.md( f""" # Default values 🍃 """ ) return (out_settings,) @app.cell(hide_code=True) def perfspec_load_actions(mo): def load_actions(actions_path, verbose): import numpy as np from pathlib import Path from tensorflow.keras.preprocessing.sequence import pad_sequences from sklearn.preprocessing import LabelEncoder actions = [] unique_actions = [] if Path(actions_path).exists(): if verbose != None: #perfspec['settings']['verbose'] != None and mo.cli_args().get("verbose") != None: print (f"Actions loaded from: {actions_path}") with open(actions_path, 'r') as file: for line in file: action = line.rstrip() actions.append(action) if action not in unique_actions: unique_actions.append(action) label_encoder = LabelEncoder() encoded_actions = label_encoder.fit_transform(actions) return (actions, unique_actions, label_encoder, encoded_actions) mo.md( f""" # Load actions and encoders 🍃 """ ) return (load_actions,) @app.cell(hide_code=True) def perfspec_input_sequence(mo, perfspec): def get_input_sequence(input_str, unique_actions): if input_str == "": input_str = perfspec['settings']['input_str'] else: input_sequence = [] for str in input_str.split(","): if str not in unique_actions: if perfspec['settings']['verbose'] != None or mo.cli_args().get("verbose") != None: print (f"Input not exist: {str}") else: input_sequence.append(str) return input_sequence mo.md( f""" # Input sequence 🍃 Control **input sequence** to be included in __actions data__ in the model """ ) return (get_input_sequence,) @app.cell(hide_code=True) def perfspec_load_model_from_path(mo, perfspec): def load_model_from_path(model_path, verbose): # Load the model from keras.models import load_model from train_perfspec import ( PrecisionMetric, RecallMetric, F1ScoreMetric ) # Set via options custom_objects = { # "PrecisionMetric": PrecisionMetric, # "RecallMetric": RecallMetric, # "F1ScoreMetric": F1ScoreMetric, } if model_path.exists(): model = load_model( model_path, custom_objects=custom_objects ) if verbose != None: print (f"Trained model loaded from: {model_path}") return model mo.md( f""" ## Load model 🍃 Load trained model from file **model_path** Default path: {perfspec['settings']['model_filepath']} """ ) return (load_model_from_path,) @app.cell(hide_code=True) def perfspec_predict(mo, perfspec): def predict_action(model, sequence_length, input_sequence, label_encoder, verbose): import numpy as np from sklearn.preprocessing import LabelEncoder from tensorflow.keras.utils import to_categorical from tensorflow.keras.preprocessing.sequence import pad_sequences predict_encoder = LabelEncoder() vocab_size = len(label_encoder.classes_) # Total number of unique actions # Encode the input sequence using label_encoder encoded_input = predict_encoder.fit_transform(input_sequence) for j in range(len(encoded_input) - sequence_length): encoded_input.append(encoded_input[j:j + sequence_length]) # Input sequence # Pad the input sequence to the expected sequence length encoded_input = pad_sequences([encoded_input], maxlen=sequence_length, padding='pre') encoded_input = np.expand_dims(encoded_input, axis=-1) predicted = {} _verbose = verbose if verbose != "-1" else None predicted['probabilities'] = model.predict(encoded_input, verbose=_verbose) predicted['index'] = np.argmax(predicted['probabilities']) #, axis=-1)[0] predicted['max'] = np.max(predicted['probabilities'], axis=-1)[0] predicted['max_value'] = "{:2.4f}".format(predicted['max']) if 'index' in predicted: predicted['action'] = label_encoder.inverse_transform(np.ravel(predicted['index'])) if verbose != "-1": if verbose != None or mo.cli_args().get("verbose") != None: print(f"Predicted next action: {predicted['action'][0]} with: {predicted['max_value']}") elif verbose == None and not mo.running_in_notebook(): print(f"{predicted['action'][0]}") return (encoded_input,predicted) mo.md( f""" # Predict action 🍃 Predict action from trained model Default value: **{perfspec['settings']['input_str']}** For **command-line** it can be provided via **--input** `value` (can use several comma separated) """ ) return (predict_action,) if __name__ == "__main__": app.run()