code: def cross_validation(
      ctx: Context,
      role: Role,
      cv_data: cpn.dataframe_input(roles=[GUEST, HOST]),
      learning_rate_scheduler: cpn.parameter(
          type=params.lr_scheduler_param(),
          default=params.LRSchedulerParam(
              method="linear", scheduler_params={"start_factor": 1.0}
          ),
          desc="learning rate scheduler, "
          "select method from {'step', 'linear', 'constant'}"
          "for list of configurable arguments, "
          "refer to torch.optim.lr_scheduler",
      ),
      epochs: cpn.parameter(
          type=params.conint(gt=0), default=20, desc="max iteration num"
      ),
      batch_size: cpn.parameter(
          type=params.conint(ge=10),
          default=None,
          desc="batch size, None means full batch, otherwise should be no less than 10, default None",
      ),
      optimizer: cpn.parameter(
          type=params.optimizer_param(),
          default=params.OptimizerParam(
              method="sgd",
              penalty="l2",
              alpha=1.0,
              optimizer_params={"lr": 1e-2, "weight_decay": 0},
          ),
      ),
      tol: cpn.parameter(type=params.confloat(ge=0), default=1e-4),
      early_stop: cpn.parameter(
          type=params.string_choice(["weight_diff", "diff", "abs"]),
          default="diff",
          desc="early stopping criterion, choose from {weight_diff, diff, abs, val_metrics}",
      ),
      init_param: cpn.parameter(
          type=params.init_param(),
          default=params.InitParam(
              method="random_uniform",
              fit_intercept=True,
              random_state=None,
          ),
          desc="Model param init setting.",
      ),
      threshold: cpn.parameter(
          type=params.confloat(ge=0.0, le=1.0),
          default=0.5,
          desc="predict threshold for binary data",
      ),
      he_param: cpn.parameter(
          type=params.he_param(),
          default=params.HEParam(kind="paillier", key_length=1024),
          desc="homomorphic encryption param",
      ),
      floating_point_precision: cpn.parameter(
          type=params.conint(ge=0),
          default=23,
          desc="floating point precision, ",
      ),
      cv_param: cpn.parameter(
          type=params.cv_param(),
          default=params.CVParam(
              n_splits=5, shuffle=False, random_state=None
          ),
          desc="cross validation param",
      ),
      metrics: cpn.parameter(
          type=params.metrics_param(), default=["auc"]
      ),
      output_cv_data: cpn.parameter(
          type=bool,
          default=True,
          desc="whether output prediction result per cv fold",
      ),
      cv_output_datas: cpn.dataframe_outputs(
          roles=[GUEST, HOST], optional=True
      ),
  ):
      optimizer = optimizer.dict()
      learning_rate_scheduler = learning_rate_scheduler.dict()
      init_param = init_param.dict()
      ctx.cipher.set_phe(ctx.device, he_param.dict())
  
      if role.is_arbiter:
          i = 0
          for fold_ctx, _ in ctx.on_cross_validations.ctxs_zip(
              zip(range(cv_param.n_splits))
          ):
              logger.info(f"enter fold {i}")
              module = CoordinatedLRModuleArbiter(
                  epochs=epochs,
                  early_stop=early_stop,
                  tol=tol,
                  batch_size=batch_size,
                  optimizer_param=optimizer,
                  learning_rate_param=learning_rate_scheduler,
              )
              module.fit(fold_ctx)
              i += 1
          return
  
      from fate.arch.dataframe import KFold
  
      kf = KFold(
          ctx,
          role=role,
          n_splits=cv_param.n_splits,
          shuffle=cv_param.shuffle,
          random_state=cv_param.random_state,
      )
      i = 0
      for fold_ctx, (
          train_data,
          validate_data,
      ) in ctx.on_cross_validations.ctxs_zip(kf.split(cv_data.read())):
          logger.info(f"enter fold {i}")
          if role.is_guest:
              module = CoordinatedLRModuleGuest(
                  epochs=epochs,
                  batch_size=batch_size,
                  optimizer_param=optimizer,
                  learning_rate_param=learning_rate_scheduler,
                  init_param=init_param,
                  threshold=threshold,
                  floating_point_precision=floating_point_precision,
              )
              module.fit(fold_ctx, train_data, validate_data)
              if output_cv_data:
                  sub_ctx = fold_ctx.sub_ctx("predict_train")
                  predict_df = module.predict(sub_ctx, train_data)
                  """train_predict_result = transform_to_predict_result(
                      train_data, predict_score, module.labels, threshold=module.threshold, is_ovr=module.ovr,
                      data_type="train"
                  )"""
                  train_predict_result = tools.add_dataset_type(
                      predict_df, consts.TRAIN_SET
                  )
                  sub_ctx = fold_ctx.sub_ctx("predict_validate")
                  predict_df = module.predict(sub_ctx, validate_data)
                  """validate_predict_result = transform_to_predict_result(
                      validate_data, predict_score, module.labels, threshold=module.threshold, is_ovr=module.ovr,
                      data_type="predict"
                  )"""
                  validate_predict_result = tools.add_dataset_type(
                      predict_df, consts.VALIDATE_SET
                  )
                  predict_result = DataFrame.vstack(
                      [train_predict_result, validate_predict_result]
                  )
                  next(cv_output_datas).write(df=predict_result)
  
              # evaluation = evaluate(predicted)
          elif role.is_host:
              module = CoordinatedLRModuleHost(
                  epochs=epochs,
                  batch_size=batch_size,
                  optimizer_param=optimizer,
                  learning_rate_param=learning_rate_scheduler,
                  init_param=init_param,
                  floating_point_precision=floating_point_precision,
              )
              module.fit(fold_ctx, train_data, validate_data)
              if output_cv_data:
                  sub_ctx = fold_ctx.sub_ctx("predict_train")
                  module.predict(sub_ctx, train_data)
                  sub_ctx = fold_ctx.sub_ctx("predict_validate")
                  module.predict(sub_ctx, validate_data)
          i += 1
  
file_path: FATE/python/fate/components/components/coordinated_lr.py
imports: import logging
  from fate.arch import Context
  from fate.arch.dataframe import DataFrame
  from fate.components.components.utils import consts, tools
  from fate.components.core import ARBITER, GUEST, HOST, Role, cpn, params
  from fate.ml.glm import CoordinatedLRModuleGuest, CoordinatedLRModuleHost, CoordinatedLRModuleArbiter
label_desc: Use image node_id 4 for calling a global function with example usage: cross_validation(ctx, role, cv_data, learning_rate_scheduler, epochs, batch_size, optimizer, tol, early_stop, init_param, threshold, he_param, floating_point_precision, cv_param, metrics, output_cv_data, cv_output_datas) without return types