docs: def cloud_train(train_dataset,
                  eval_dataset,
                  analysis_dir,
                  output_dir,
                  features,
                  model_type,
                  max_steps,
                  num_epochs,
                  train_batch_size,
                  eval_batch_size,
                  min_eval_frequency,
                  top_n,
                  layer_sizes,
                  learning_rate,
                  epsilon,
                  job_name,
                  job_name_prefix,
                  config):
    """Train model using CloudML.
  
    See local_train() for a description of the args.
    Args:
      config: A CloudTrainingConfig object.
      job_name: Training job name. A default will be picked if None.
    """
    import google.datalab.ml as ml
  
    if len(train_dataset.input_files) != 1 or len(eval_dataset.input_files) != 1:
      raise ValueError('CsvDataSets must be built with a file pattern, not list '
                       'of files.')
  
    if file_io.file_exists(output_dir):
      raise ValueError('output_dir already exist. Use a new output path.')
  
    if isinstance(features, dict):
      # Make a features file.
      if not file_io.file_exists(output_dir):
        file_io.recursive_create_dir(output_dir)
      features_file = os.path.join(output_dir, 'features_file.json')
      file_io.write_string_to_file(
          features_file,
          json.dumps(features))
    else:
      features_file = features
  
    if not isinstance(config, ml.CloudTrainingConfig):
      raise ValueError('cloud should be an instance of '
                       'google.datalab.ml.CloudTrainingConfig for cloud training.')
  
    _assert_gcs_files([output_dir, train_dataset.input_files[0], eval_dataset.input_files[0],
                       features_file, analysis_dir])
  
    args = ['--train-data-paths=%s' % train_dataset.input_files[0],
            '--eval-data-paths=%s' % eval_dataset.input_files[0],
            '--preprocess-output-dir=%s' % analysis_dir,
            '--transforms-file=%s' % features_file,
            '--model-type=%s' % model_type,
            '--max-steps=%s' % str(max_steps),
            '--train-batch-size=%s' % str(train_batch_size),
            '--eval-batch-size=%s' % str(eval_batch_size),
            '--min-eval-frequency=%s' % str(min_eval_frequency),
            '--learning-rate=%s' % str(learning_rate),
            '--epsilon=%s' % str(epsilon)]
    if num_epochs:
      args.append('--num-epochs=%s' % str(num_epochs))
    if top_n:
      args.append('--top-n=%s' % str(top_n))
    if layer_sizes:
      for i in range(len(layer_sizes)):
        args.append('--layer-size%s=%s' % (i + 1, str(layer_sizes[i])))
  
    job_request = {
      'package_uris': [_package_to_staging(output_dir), _TF_GS_URL, _PROTOBUF_GS_URL],
      'python_module': 'mltoolbox._structured_data.trainer.task',
      'job_dir': output_dir,
      'args': args
    }
    job_request.update(dict(config._asdict()))
  
    if not job_name:
      job_name = job_name_prefix or 'structured_data_train'
      job_name += '_' + datetime.datetime.now().strftime('%y%m%d_%H%M%S')
    job = ml.Job.submit_training(job_request, job_name)
    print('Job request send. View status of job at')
    print('https://console.developers.google.com/ml/jobs?project=%s' %
          _default_project())
  
    return job