code: def train_procedure(
      ctx: Context,
      role: Role,
      train_data: DataframeReader,
      validate_data: DataframeReader,
      runner_module: str,
      runner_class: str,
      runner_conf: dict,
      source: str,
      train_data_output: DataframeWriter,
      train_model_output: ModelDirectoryWriter,
      train_model_input: ModelDirectoryReader,
      is_hetero=False,
  ):
      if train_model_input is not None:
          model_conf = train_model_input.get_metadata()
          (
              runner_conf,
              source,
              runner_class,
              runner_module,
          ) = prepared_saved_conf(
              model_conf,
              runner_class,
              runner_module,
              runner_conf,
              source,
          )
          saved_model_path = str(train_model_input.get_directory())
      else:
          saved_model_path = None
  
      runner: NNRunner = prepare_runner_class(
          runner_module, runner_class, runner_conf, source
      )
      prepare_context_and_role(runner, ctx, role, consts.TRAIN)
  
      output_dir = str(train_model_output.get_directory())
      train_data_, validate_data_ = get_input_data(
          consts.TRAIN, [train_data, validate_data]
      )
  
      runner.train(
          train_data_, validate_data_, output_dir, saved_model_path
      )
  
      logger.info("Predicting Train & Validate Data")
      train_pred = runner.predict(train_data_, saved_model_path)
      validate_pred = None
      if validate_data_ is not None:
          validate_pred = runner.predict(validate_data_)
  
      logger.info("predicting done")
      if train_pred is not None:
          assert isinstance(
              train_pred, DataFrame
          ), "train predict result should be a DataFrame"
          add_dataset_type(train_pred, consts.TRAIN_SET)
  
          if validate_pred is not None:
              assert isinstance(
                  validate_pred, DataFrame
              ), "validate predict result should be a DataFrame"
              add_dataset_type(validate_pred, consts.VALIDATE_SET)
              output_df = DataFrame.vstack([train_pred, validate_pred])
          else:
              output_df = train_pred
          logger.info("write result dataframe")
          train_data_output.write(output_df)
      else:
          if is_hetero and role.is_host:
              pass
          else:
              logger.warning(
                  "train_pred is None, It seems that the runner is not able to predict. Failed to output data"
              )
  
      output_conf = get_model_output_conf(
          runner_module, runner_class, runner_conf, source
      )
      train_model_output.write_metadata(output_conf)
  
file_path: FATE/python/fate/components/components/nn/component_utils.py
imports: from fate.components.components.nn.loader import Loader
  from fate.components.components.nn.nn_runner import NNRunner
  from fate.components.components.utils import consts
  from fate.arch.dataframe import DataFrame
  from fate.components.components.utils.tools import add_dataset_type
  from fate.arch import Context
  from fate.components.core import ARBITER, GUEST, HOST, Role, cpn
  import logging
  from fate.components.core.component_desc.artifacts.data._dataframe import DataframeReader, DataframeWriter
  from fate.components.core.component_desc.artifacts.data._directory import DataDirectoryReader
  from fate.components.core.component_desc.artifacts.model._directory import ModelDirectoryReader, ModelDirectoryWriter
label_desc: Use image node_id 7 for calling a global function with example usage: train_procedure(ctx, role, train_data, validate_data, runner_module, runner_class, runner_conf, source, train_data_output, train_model_output, train_model_input, is_hetero) without return types