code: def test_gbm_model_save_reload_api(tmpdir, csv_filename, tmp_path):
      torch.manual_seed(1)
      random.seed(1)
      np.random.seed(1)
  
      input_features = [
          binary_feature(),
          number_feature(),
          category_feature(
              encoder={"type": "passthrough", "vocab_size": 3}
          ),
      ]
      output_features = [
          category_feature(
              decoder={"vocab_size": 3}, output_feature=True
          )
      ]
  
      # Generate test data
      data_csv_path = generate_data(
          input_features, output_features, csv_filename
      )
  
      #############
      # Train tree model
      #############
      config = {
          "model_type": "gbm",
          "input_features": input_features,
          "output_features": output_features,
          # Disable feature filtering to avoid having no features due to small test dataset,
          # see https://stackoverflow.com/a/66405983/5222402
          TRAINER: {"num_boost_round": 2, "feature_pre_filter": False},
      }
  
      data_df = read_csv(data_csv_path)
      splitter = get_splitter("random")
      training_set, validation_set, test_set = splitter.split(
          data_df, LocalTestBackend()
      )
  
      # create sub-directory to store results
      results_dir = tmp_path / "results"
      results_dir.mkdir()
  
      # perform initial model training
      backend = LocalTestBackend()
      ludwig_model1 = LudwigModel(config, backend=backend)
      _, _, output_dir = ludwig_model1.train(
          training_set=training_set,
          validation_set=validation_set,
          test_set=test_set,
          output_directory="results",  # results_dir
      )
  
      preds_1, _ = ludwig_model1.predict(dataset=validation_set)
  
      def check_model_equal(ludwig_model2):
          # Compare model predictions
          preds_2, _ = ludwig_model2.predict(dataset=validation_set)
          assert set(preds_1.keys()) == set(preds_2.keys())
          for key in preds_1:
              assert preds_1[key].dtype == preds_2[key].dtype, key
              assert np.all(
                  a == b for a, b in zip(preds_1[key], preds_2[key])
              ), key
  
          # Compare model weights
          for if_name in ludwig_model1.model.input_features:
              if1 = ludwig_model1.model.input_features.get(if_name)
              if2 = ludwig_model2.model.input_features.get(if_name)
              for if1_w, if2_w in zip(
                  if1.encoder_obj.parameters(),
                  if2.encoder_obj.parameters(),
              ):
                  assert torch.allclose(if1_w, if2_w)
  
          tree1 = ludwig_model1.model
          tree2 = ludwig_model2.model
  
          with tree1.compile():
              tree1_params = tree1.compiled_model.parameters()
  
          with tree2.compile():
              tree2_params = tree2.compiled_model.parameters()
  
          for t1_w, t2_w in zip(tree1_params, tree2_params):
              assert torch.allclose(t1_w, t2_w)
  
          for of_name in ludwig_model1.model.output_features:
              of1 = ludwig_model1.model.output_features.get(of_name)
              of2 = ludwig_model2.model.output_features.get(of_name)
              for of1_w, of2_w in zip(
                  of1.decoder_obj.parameters(),
                  of2.decoder_obj.parameters(),
              ):
                  assert torch.allclose(of1_w, of2_w)
  
      # Test saving and loading the model explicitly
      ludwig_model1.save(tmpdir)
      ludwig_model_loaded = LudwigModel.load(tmpdir, backend=backend)
      check_model_equal(ludwig_model_loaded)
  
      # Test loading the model from the experiment directory
      ludwig_model_exp = LudwigModel.load(
          os.path.join(output_dir, "model"), backend=backend
      )
      check_model_equal(ludwig_model_exp)
  
file_path: ludwig/tests/integration_tests/test_model_save_and_load.py
imports: import os
  import os.path
  import random
  import numpy
  import pandas
  import pytest
  import torch
  from ludwig.api import LudwigModel
  from ludwig.constants import BATCH_SIZE, ENCODER, LOSS, NAME, PREPROCESSING, TRAINER, TRAINING, TYPE
  from ludwig.data.split import get_splitter
  from ludwig.modules.loss_modules import MSELoss
  from ludwig.schema.features.loss.loss import MSELossConfig
  from ludwig.utils.data_utils import read_csv
  from tests.integration_tests.utils import audio_feature, bag_feature, binary_feature, category_feature, date_feature, generate_data, h3_feature, image_feature, LocalTestBackend, number_feature, sequence_feature, set_feature, text_feature, timeseries_feature, vector_feature
label_desc: Use image node_id 2 for calling a global function with example usage: test_gbm_model_save_reload_api(tmpdir, csv_filename, tmp_path) without return types