docstr: Define hyperparameter search scaffold for building Experiments during optimization
  
  OptPros use this method to guide Experiment construction behind the scenes, which is why it
  looks just like :meth:`hyperparameter_hunter.experiments.BaseExperiment.__init__`.
  `forge_experiment` offers one major upgrade to standard Experiment initialization: it
  accepts hyperparameters not only as concrete values, but also as space choices -- using
  :class:`~hyperparameter_hunter.space.dimensions.Real`,
  :class:`~hyperparameter_hunter.space.dimensions.Integer`, and
  :class:`~hyperparameter_hunter.space.dimensions.Categorical`. This functionality applies to
  the `model_init_params`, `model_extra_params` and `feature_engineer` kwargs. Any Dimensions
  provided to `forge_experiment` are detected by the OptPro and used to define the
  hyperparameter search space to be optimized
  
  Parameters
  ----------
  model_initializer: Class, or functools.partial, or class instance
      Algorithm class used to initialize a model, such as XGBoost's `XGBRegressor`, or
      SKLearn's `KNeighborsClassifier`; although, there are hundreds of possibilities across
      many different ML libraries. `model_initializer` is expected to define at least `fit`
      and `predict` methods. `model_initializer` will be initialized with `model_init_params`,
      and its extra methods (`fit`, `predict`, etc.) will be invoked with parameters in
      `model_extra_params`
  model_init_params: Dict, or object (optional)
      Dictionary of arguments given to create an instance of `model_initializer`. Any kwargs
      that are considered valid by the `__init__` method of `model_initializer` are valid in
      `model_init_params`.
  
      In addition to providing concrete values, hyperparameters can be expressed as choices
      (dimensions to optimize) by using instances of
      :class:`~hyperparameter_hunter.space.dimensions.Real`,
      :class:`~hyperparameter_hunter.space.dimensions.Integer`, or
      :class:`~hyperparameter_hunter.space.dimensions.Categorical`. Furthermore,
      hyperparameter choices and concrete values can be used together in `model_init_params`.
  
      Using XGBoost's `XGBClassifier` to illustrate, the `model_init_params` kwarg of
      :class:`~hyperparameter_hunter.experiments.CVExperiment` is limited to using concrete
      values, such as ``dict(max_depth=10, learning_rate=0.1, booster="gbtree")``. This is
      still valid for :meth:`.forge_experiment`. However, :meth:`.forge_experiment` also
      allows `model_init_params` to consist entirely of space choices, such as
      ``dict(max_depth=Integer(2, 20), learning_rate=Real(0.001, 0.5),
      booster=Categorical(["gbtree", "dart"]))``, or as any combination of concrete values
      and choices, for instance, ``dict(max_depth=10, learning_rate=Real(0.001, 0.5),
      booster="gbtree")``.
  
      One of the key features that makes HyperparameterHunter so magical is that **ALL**
      hyperparameters in the signature of `model_initializer` (and their default values) are
      discovered -- whether or not they are explicitly given in `model_init_params`. Not only
      does this make Experiment result descriptions incredibly thorough, it also makes
      optimization smoother, more effective, and far less work for the user. For example, take
      LightGBM's `LGBMRegressor`, with `model_init_params`=`dict(learning_rate=0.2)`.
      HyperparameterHunter recognizes that this differs from the default of 0.1. It also
      recognizes that `LGBMRegressor` is actually initialized with more than a dozen other
      hyperparameters we didn't bother mentioning, and it records their values, too. So if we
      want to optimize `num_leaves` tomorrow, the OptPro doesn't start from scratch. It knows
      that we ran an Experiment that didn't explicitly mention `num_leaves`, but its default
      value was 31, and it uses this information to fuel optimization -- all without us having
      to manually keep track of tons of janky collections of hyperparameters. In fact, we
      really don't need to go out of our way at all. HyperparameterHunter just acts as our
      faithful lab assistant, keeping track of all the stuff we'd rather not worry about
  model_extra_params: Dict (optional)
      Dictionary of extra parameters for models' non-initialization methods (like `fit`,
      `predict`, `predict_proba`, etc.), and for neural networks. To specify parameters for
      an extra method, place them in a dict named for the extra method to which the
      parameters should be given. For example, to call `fit` with `early_stopping_rounds`=5,
      use `model_extra_params`=`dict(fit=dict(early_stopping_rounds=5))`.
  
      Declaring hyperparameter space choices works identically to `model_init_params`, meaning
      that in addition to concrete values, extra parameters can be given as instances of
      :class:`~hyperparameter_hunter.space.dimensions.Real`,
      :class:`~hyperparameter_hunter.space.dimensions.Integer`, or
      :class:`~hyperparameter_hunter.space.dimensions.Categorical`. To optimize over a space
      in which `early_stopping_rounds` is between 3 and 9, use
      `model_extra_params`=`dict(fit=dict(early_stopping_rounds=Real(3, 9)))`.
  
      For models whose `fit` methods have a kwarg like `eval_set` (such as XGBoost's), one can
      use the `DatasetSentinel` attributes of the current active
      :class:`~hyperparameter_hunter.environment.Environment`, documented under its
      "Attributes" section and under
      :attr:`~hyperparameter_hunter.environment.Environment.train_input`. An example using
      several DatasetSentinels can be found in HyperparameterHunter's
      [XGBoost Classification Example](https://github.com/HunterMcGushion/hyperparameter_hunter/blob/master/examples/xgboost_examples/classification.py)
  feature_engineer: `FeatureEngineer`, or list (optional)
      Feature engineering/transformation/pre-processing steps to apply to datasets defined in
      :class:`~hyperparameter_hunter.environment.Environment`. If list, will be used to
      initialize :class:`~hyperparameter_hunter.feature_engineering.FeatureEngineer`, and can
      contain any of the following values:
  
          1. :class:`~hyperparameter_hunter.feature_engineering.EngineerStep` instance
          2. Function input to :class:~hyperparameter_hunter.feature_engineering.EngineerStep`
          3. :class:`~hyperparameter_hunter.space.dimensions.Categorical`, with `categories`
             comprising a selection of the previous two values (optimization only)
  
      For important information on properly formatting `EngineerStep` functions, please see
      the documentation of :class:`~hyperparameter_hunter.feature_engineering.EngineerStep`.
  
      To search a space optionally including an `EngineerStep`, use the `optional` kwarg of
      :class:`~hyperparameter_hunter.space.dimensions.Categorical`. This functionality is
      illustrated in :class:`~hyperparameter_hunter.feature_engineering.FeatureEngineer`. If
      using a `FeatureEngineer` instance to optimize `feature_engineer`, this instance cannot
      be used with `CVExperiment` because Experiments can't handle space choices
  feature_selector: List of str, callable, or list of booleans (optional)
      Column names to include as input data for all provided DataFrames. If None,
      `feature_selector` is set to all columns in :attr:`train_dataset`, less
      :attr:`target_column`, and :attr:`id_column`. `feature_selector` is provided as the
      second argument for calls to `pandas.DataFrame.loc` when constructing datasets
  notes: String (optional)
      Additional information about the Experiment that will be saved with the Experiment's
      description result file. This serves no purpose other than to facilitate saving
      Experiment details in a more readable format
  do_raise_repeated: Boolean, default=False
      If True and this Experiment locates a previous Experiment's results with matching
      Environment and Hyperparameter Keys, a RepeatedExperimentError will be raised. Else, a
      warning will be logged
  
  Notes
  -----
  The `auto_start` kwarg is not available here because :meth:`._execute_experiment` sets it
  to False in order to check for duplicated keys before running the whole Experiment. This
  and `target_metric` being moved to :meth:`.__init__` are the most notable differences
  between calling :meth:`forge_experiment` and instantiating
  :class:`~hyperparameter_hunter.experiments.CVExperiment`
  
  A more accurate name for this method might be something like "build_experiment_forge", since
  `forge_experiment` itself does not actually execute any Experiments. However,
  `forge_experiment` sounds cooler and much less clunky
  
  See Also
  --------
  :class:`hyperparameter_hunter.experiments.BaseExperiment`
      One-off experimentation counterpart to an OptPro's :meth:`.forge_experiment`.
      Internally, OptPros feed the processed arguments from `forge_experiment` to initialize
      Experiments. This hand-off to Experiments takes place in :meth:`._execute_experiment`
docstr_tok: ["Define","hyperparameter","search","scaffold","for","building","Experiments","during","optimization","OptPros","use","this","method","to","guide","Experiment","construction","behind","the","scenes",",","which","is","why","it","looks","just","like",":","meth",":","`","hyperparameter_hunter.experiments.BaseExperiment.__init__","`",".","`","forge_experiment","`","offers","one","major","upgrade","to","standard","Experiment","initialization",":","it","accepts","hyperparameters","not","only","as","concrete","values",",","but","also","as","space","choices","--","using",":","class",":","`","~hyperparameter_hunter.space.dimensions.Real","`",",",":","class",":","`","~hyperparameter_hunter.space.dimensions.Integer","`",",","and",":","class",":","`","~hyperparameter_hunter.space.dimensions.Categorical","`",".","This","functionality","applies","to","the","`","model_init_params","`",",","`","model_extra_params","`","and","`","feature_engineer","`","kwargs",".","Any","Dimensions","provided","to","`","forge_experiment","`","are","detected","by","the","OptPro","and","used","to","define","the","hyperparameter","search","space","to","be","optimized","Parameters","--","--","--","--","--","model_initializer",":","Class",",","or","functools.partial",",","or","class","instance","Algorithm","class","used","to","initialize","a","model",",","such","as","XGBoost","'s","`","XGBRegressor","`",",","or","SKLearn","'s","`","KNeighborsClassifier","`",";","although",",","there","are","hundreds","of","possibilities","across","many","different","ML","libraries",".","`","model_initializer","`","is","expected","to","define","at","least","`","fit","`","and","`","predict","`","methods",".","`","model_initializer","`","will","be","initialized","with","`","model_init_params","`",",","and","its","extra","methods","(","`","fit","`",",","`","predict","`",",","etc",".",")","will","be","invoked","with","parameters","in","`","model_extra_params","`","model_init_params",":","Dict",",","or","object","(","optional",")","Dictionary","of","arguments","given","to","create","an","instance","of","`","model_initializer","`",".","Any","kwargs","that","are","considered","valid","by","the","`","__init__","`","method","of","`","model_initializer","`","are","valid","in","`","model_init_params","`",".","In","addition","to","providing","concrete","values",",","hyperparameters","can","be","expressed","as","choices","(","dimensions","to","optimize",")","by","using","instances","of",":","class",":","`","~hyperparameter_hunter.space.dimensions.Real","`",",",":","class",":","`","~hyperparameter_hunter.space.dimensions.Integer","`",",","or",":","class",":","`","~hyperparameter_hunter.space.dimensions.Categorical","`",".","Furthermore",",","hyperparameter","choices","and","concrete","values","can","be","used","together","in","`","model_init_params","`",".","Using","XGBoost","'s","`","XGBClassifier","`","to","illustrate",",","the","`","model_init_params","`","kwarg","of",":","class",":","`","~hyperparameter_hunter.experiments.CVExperiment","`","is","limited","to","using","concrete","values",",","such","as","``","dict","(","max_depth=10",",","learning_rate=0.1",",","booster=","''","gbtree","''",")","``",".","This","is","still","valid","for",":","meth",":","`",".forge_experiment","`",".","However",",",":","meth",":","`",".forge_experiment","`","also","allows","`","model_init_params","`","to","consist","entirely","of","space","choices",",","such","as","``","dict","(","max_depth=Integer","(","2",",","20",")",",","learning_rate=Real","(","0.001",",","0.5",")",",","booster=Categorical","(","[","``","gbtree","''",",","``","dart","''","]",")",")","``",",","or","as","any","combination","of","concrete","values","and","choices",",","for","instance",",","``","dict","(","max_depth=10",",","learning_rate=Real","(","0.001",",","0.5",")",",","booster=","''","gbtree","''",")","``",".","One","of","the","key","features","that","makes","HyperparameterHunter","so","magical","is","that","*","*","ALL","*","*","hyperparameters","in","the","signature","of","`","model_initializer","`","(","and","their","default","values",")","are","discovered","--","whether","or","not","they","are","explicitly","given","in","`","model_init_params","`",".","Not","only","does","this","make","Experiment","result","descriptions","incredibly","thorough",",","it","also","makes","optimization","smoother",",","more","effective",",","and","far","less","work","for","the","user",".","For","example",",","take","LightGBM","'s","`","LGBMRegressor","`",",","with","`","model_init_params","`","=","`","dict","(","learning_rate=0.2",")","`",".","HyperparameterHunter","recognizes","that","this","differs","from","the","default","of","0.1",".","It","also","recognizes","that","`","LGBMRegressor","`","is","actually","initialized","with","more","than","a","dozen","other","hyperparameters","we","did","n't","bother","mentioning",",","and","it","records","their","values",",","too",".","So","if","we","want","to","optimize","`","num_leaves","`","tomorrow",",","the","OptPro","does","n't","start","from","scratch",".","It","knows","that","we","ran","an","Experiment","that","did","n't","explicitly","mention","`","num_leaves","`",",","but","its","default","value","was","31",",","and","it","uses","this","information","to","fuel","optimization","--","all","without","us","having","to","manually","keep","track","of","tons","of","janky","collections","of","hyperparameters",".","In","fact",",","we","really","do","n't","need","to","go","out","of","our","way","at","all",".","HyperparameterHunter","just","acts","as","our","faithful","lab","assistant",",","keeping","track","of","all","the","stuff","we","'d","rather","not","worry","about","model_extra_params",":","Dict","(","optional",")","Dictionary","of","extra","parameters","for","models","'","non-initialization","methods","(","like","`","fit","`",",","`","predict","`",",","`","predict_proba","`",",","etc",".",")",",","and","for","neural","networks",".","To","specify","parameters","for","an","extra","method",",","place","them","in","a","dict","named","for","the","extra","method","to","which","the","parameters","should","be","given",".","For","example",",","to","call","`","fit","`","with","`","early_stopping_rounds","`","=5",",","use","`","model_extra_params","`","=","`","dict","(","fit=dict","(","early_stopping_rounds=5",")",")","`",".","Declaring","hyperparameter","space","choices","works","identically","to","`","model_init_params","`",",","meaning","that","in","addition","to","concrete","values",",","extra","parameters","can","be","given","as","instances","of",":","class",":","`","~hyperparameter_hunter.space.dimensions.Real","`",",",":","class",":","`","~hyperparameter_hunter.space.dimensions.Integer","`",",","or",":","class",":","`","~hyperparameter_hunter.space.dimensions.Categorical","`",".","To","optimize","over","a","space","in","which","`","early_stopping_rounds","`","is","between","3","and","9",",","use","`","model_extra_params","`","=","`","dict","(","fit=dict","(","early_stopping_rounds=Real","(","3",",","9",")",")",")","`",".","For","models","whose","`","fit","`","methods","have","a","kwarg","like","`","eval_set","`","(","such","as","XGBoost","'s",")",",","one","can","use","the","`","DatasetSentinel","`","attributes","of","the","current","active",":","class",":","`","~hyperparameter_hunter.environment.Environment","`",",","documented","under","its","``","Attributes","''","section","and","under",":","attr",":","`","~hyperparameter_hunter.environment.Environment.train_input","`",".","An","example","using","several","DatasetSentinels","can","be","found","in","HyperparameterHunter's","[","XGBoost","Classification","Example","]","(","https",":","\/\/github.com\/HunterMcGushion\/hyperparameter_hunter\/blob\/master\/examples\/xgboost_examples\/classification.py",")","feature_engineer",":","`","FeatureEngineer","`",",","or","list","(","optional",")","Feature","engineering\/transformation\/pre-processing","steps","to","apply","to","datasets","defined","in",":","class",":","`","~hyperparameter_hunter.environment.Environment","`",".","If","list",",","will","be","used","to","initialize",":","class",":","`","~hyperparameter_hunter.feature_engineering.FeatureEngineer","`",",","and","can","contain","any","of","the","following","values",":","1.",":","class",":","`","~hyperparameter_hunter.feature_engineering.EngineerStep","`","instance","2",".","Function","input","to",":","class",":","~hyperparameter_hunter.feature_engineering.EngineerStep","`","3.",":","class",":","`","~hyperparameter_hunter.space.dimensions.Categorical","`",",","with","`","categories","`","comprising","a","selection","of","the","previous","two","values","(","optimization","only",")","For","important","information","on","properly","formatting","`","EngineerStep","`","functions",",","please","see","the","documentation","of",":","class",":","`","~hyperparameter_hunter.feature_engineering.EngineerStep","`",".","To","search","a","space","optionally","including","an","`","EngineerStep","`",",","use","the","`","optional","`","kwarg","of",":","class",":","`","~hyperparameter_hunter.space.dimensions.Categorical","`",".","This","functionality","is","illustrated","in",":","class",":","`","~hyperparameter_hunter.feature_engineering.FeatureEngineer","`",".","If","using","a","`","FeatureEngineer","`","instance","to","optimize","`","feature_engineer","`",",","this","instance","can","not","be","used","with","`","CVExperiment","`","because","Experiments","ca","n't","handle","space","choices","feature_selector",":","List","of","str",",","callable",",","or","list","of","booleans","(","optional",")","Column","names","to","include","as","input","data","for","all","provided","DataFrames",".","If","None",",","`","feature_selector","`","is","set","to","all","columns","in",":","attr",":","`","train_dataset","`",",","less",":","attr",":","`","target_column","`",",","and",":","attr",":","`","id_column","`",".","`","feature_selector","`","is","provided","as","the","second","argument","for","calls","to","`","pandas.DataFrame.loc","`","when","constructing","datasets","notes",":","String","(","optional",")","Additional","information","about","the","Experiment","that","will","be","saved","with","the","Experiment's","description","result","file",".","This","serves","no","purpose","other","than","to","facilitate","saving","Experiment","details","in","a","more","readable","format","do_raise_repeated",":","Boolean",",","default=False","If","True","and","this","Experiment","locates","a","previous","Experiment","'s","results","with","matching","Environment","and","Hyperparameter","Keys",",","a","RepeatedExperimentError","will","be","raised",".","Else",",","a","warning","will","be","logged","Notes","--","--","-","The","`","auto_start","`","kwarg","is","not","available","here","because",":","meth",":","`","._execute_experiment","`","sets","it","to","False","in","order","to","check","for","duplicated","keys","before","running","the","whole","Experiment",".","This","and","`","target_metric","`","being","moved","to",":","meth",":","`",".__init__","`","are","the","most","notable","differences","between","calling",":","meth",":","`","forge_experiment","`","and","instantiating",":","class",":","`","~hyperparameter_hunter.experiments.CVExperiment","`","A","more","accurate","name","for","this","method","might","be","something","like","``","build_experiment_forge","''",",","since","`","forge_experiment","`","itself","does","not","actually","execute","any","Experiments",".","However",",","`","forge_experiment","`","sounds","cooler","and","much","less","clunky","See","Also","--","--","--","--",":","class",":","`","hyperparameter_hunter.experiments.BaseExperiment","`","One-off","experimentation","counterpart","to","an","OptPro","'s",":","meth",":","`",".forge_experiment","`",".","Internally",",","OptPros","feed","the","processed","arguments","from","`","forge_experiment","`","to","initialize","Experiments",".","This","hand-off","to","Experiments","takes","place","in",":","meth",":","`","._execute_experiment","`"]
code: def forge_experiment(
      self,
      model_initializer,
      model_init_params=None,
      model_extra_params=None,
      feature_engineer=None,
      feature_selector=None,
      notes=None,
      do_raise_repeated=True,
  ):
      """Define hyperparameter search scaffold for building Experiments during optimization
  
      OptPros use this method to guide Experiment construction behind the scenes, which is why it
      looks just like :meth:`hyperparameter_hunter.experiments.BaseExperiment.__init__`.
      `forge_experiment` offers one major upgrade to standard Experiment initialization: it
      accepts hyperparameters not only as concrete values, but also as space choices -- using
      :class:`~hyperparameter_hunter.space.dimensions.Real`,
      :class:`~hyperparameter_hunter.space.dimensions.Integer`, and
      :class:`~hyperparameter_hunter.space.dimensions.Categorical`. This functionality applies to
      the `model_init_params`, `model_extra_params` and `feature_engineer` kwargs. Any Dimensions
      provided to `forge_experiment` are detected by the OptPro and used to define the
      hyperparameter search space to be optimized
  
      Parameters
      ----------
      model_initializer: Class, or functools.partial, or class instance
          Algorithm class used to initialize a model, such as XGBoost's `XGBRegressor`, or
          SKLearn's `KNeighborsClassifier`; although, there are hundreds of possibilities across
          many different ML libraries. `model_initializer` is expected to define at least `fit`
          and `predict` methods. `model_initializer` will be initialized with `model_init_params`,
          and its extra methods (`fit`, `predict`, etc.) will be invoked with parameters in
          `model_extra_params`
      model_init_params: Dict, or object (optional)
          Dictionary of arguments given to create an instance of `model_initializer`. Any kwargs
          that are considered valid by the `__init__` method of `model_initializer` are valid in
          `model_init_params`.
  
          In addition to providing concrete values, hyperparameters can be expressed as choices
          (dimensions to optimize) by using instances of
          :class:`~hyperparameter_hunter.space.dimensions.Real`,
          :class:`~hyperparameter_hunter.space.dimensions.Integer`, or
          :class:`~hyperparameter_hunter.space.dimensions.Categorical`. Furthermore,
          hyperparameter choices and concrete values can be used together in `model_init_params`.
  
          Using XGBoost's `XGBClassifier` to illustrate, the `model_init_params` kwarg of
          :class:`~hyperparameter_hunter.experiments.CVExperiment` is limited to using concrete
          values, such as ``dict(max_depth=10, learning_rate=0.1, booster="gbtree")``. This is
          still valid for :meth:`.forge_experiment`. However, :meth:`.forge_experiment` also
          allows `model_init_params` to consist entirely of space choices, such as
          ``dict(max_depth=Integer(2, 20), learning_rate=Real(0.001, 0.5),
          booster=Categorical(["gbtree", "dart"]))``, or as any combination of concrete values
          and choices, for instance, ``dict(max_depth=10, learning_rate=Real(0.001, 0.5),
          booster="gbtree")``.
  
          One of the key features that makes HyperparameterHunter so magical is that **ALL**
          hyperparameters in the signature of `model_initializer` (and their default values) are
          discovered -- whether or not they are explicitly given in `model_init_params`. Not only
          does this make Experiment result descriptions incredibly thorough, it also makes
          optimization smoother, more effective, and far less work for the user. For example, take
          LightGBM's `LGBMRegressor`, with `model_init_params`=`dict(learning_rate=0.2)`.
          HyperparameterHunter recognizes that this differs from the default of 0.1. It also
          recognizes that `LGBMRegressor` is actually initialized with more than a dozen other
          hyperparameters we didn't bother mentioning, and it records their values, too. So if we
          want to optimize `num_leaves` tomorrow, the OptPro doesn't start from scratch. It knows
          that we ran an Experiment that didn't explicitly mention `num_leaves`, but its default
          value was 31, and it uses this information to fuel optimization -- all without us having
          to manually keep track of tons of janky collections of hyperparameters. In fact, we
          really don't need to go out of our way at all. HyperparameterHunter just acts as our
          faithful lab assistant, keeping track of all the stuff we'd rather not worry about
      model_extra_params: Dict (optional)
          Dictionary of extra parameters for models' non-initialization methods (like `fit`,
          `predict`, `predict_proba`, etc.), and for neural networks. To specify parameters for
          an extra method, place them in a dict named for the extra method to which the
          parameters should be given. For example, to call `fit` with `early_stopping_rounds`=5,
          use `model_extra_params`=`dict(fit=dict(early_stopping_rounds=5))`.
  
          Declaring hyperparameter space choices works identically to `model_init_params`, meaning
          that in addition to concrete values, extra parameters can be given as instances of
          :class:`~hyperparameter_hunter.space.dimensions.Real`,
          :class:`~hyperparameter_hunter.space.dimensions.Integer`, or
          :class:`~hyperparameter_hunter.space.dimensions.Categorical`. To optimize over a space
          in which `early_stopping_rounds` is between 3 and 9, use
          `model_extra_params`=`dict(fit=dict(early_stopping_rounds=Real(3, 9)))`.
  
          For models whose `fit` methods have a kwarg like `eval_set` (such as XGBoost's), one can
          use the `DatasetSentinel` attributes of the current active
          :class:`~hyperparameter_hunter.environment.Environment`, documented under its
          "Attributes" section and under
          :attr:`~hyperparameter_hunter.environment.Environment.train_input`. An example using
          several DatasetSentinels can be found in HyperparameterHunter's
          [XGBoost Classification Example](https://github.com/HunterMcGushion/hyperparameter_hunter/blob/master/examples/xgboost_examples/classification.py)
      feature_engineer: `FeatureEngineer`, or list (optional)
          Feature engineering/transformation/pre-processing steps to apply to datasets defined in
          :class:`~hyperparameter_hunter.environment.Environment`. If list, will be used to
          initialize :class:`~hyperparameter_hunter.feature_engineering.FeatureEngineer`, and can
          contain any of the following values:
  
              1. :class:`~hyperparameter_hunter.feature_engineering.EngineerStep` instance
              2. Function input to :class:~hyperparameter_hunter.feature_engineering.EngineerStep`
              3. :class:`~hyperparameter_hunter.space.dimensions.Categorical`, with `categories`
                 comprising a selection of the previous two values (optimization only)
  
          For important information on properly formatting `EngineerStep` functions, please see
          the documentation of :class:`~hyperparameter_hunter.feature_engineering.EngineerStep`.
  
          To search a space optionally including an `EngineerStep`, use the `optional` kwarg of
          :class:`~hyperparameter_hunter.space.dimensions.Categorical`. This functionality is
          illustrated in :class:`~hyperparameter_hunter.feature_engineering.FeatureEngineer`. If
          using a `FeatureEngineer` instance to optimize `feature_engineer`, this instance cannot
          be used with `CVExperiment` because Experiments can't handle space choices
      feature_selector: List of str, callable, or list of booleans (optional)
          Column names to include as input data for all provided DataFrames. If None,
          `feature_selector` is set to all columns in :attr:`train_dataset`, less
          :attr:`target_column`, and :attr:`id_column`. `feature_selector` is provided as the
          second argument for calls to `pandas.DataFrame.loc` when constructing datasets
      notes: String (optional)
          Additional information about the Experiment that will be saved with the Experiment's
          description result file. This serves no purpose other than to facilitate saving
          Experiment details in a more readable format
      do_raise_repeated: Boolean, default=False
          If True and this Experiment locates a previous Experiment's results with matching
          Environment and Hyperparameter Keys, a RepeatedExperimentError will be raised. Else, a
          warning will be logged
  
      Notes
      -----
      The `auto_start` kwarg is not available here because :meth:`._execute_experiment` sets it
      to False in order to check for duplicated keys before running the whole Experiment. This
      and `target_metric` being moved to :meth:`.__init__` are the most notable differences
      between calling :meth:`forge_experiment` and instantiating
      :class:`~hyperparameter_hunter.experiments.CVExperiment`
  
      A more accurate name for this method might be something like "build_experiment_forge", since
      `forge_experiment` itself does not actually execute any Experiments. However,
      `forge_experiment` sounds cooler and much less clunky
  
      See Also
      --------
      :class:`hyperparameter_hunter.experiments.BaseExperiment`
          One-off experimentation counterpart to an OptPro's :meth:`.forge_experiment`.
          Internally, OptPros feed the processed arguments from `forge_experiment` to initialize
          Experiments. This hand-off to Experiments takes place in :meth:`._execute_experiment`
      """
      self.model_initializer = model_initializer
      self.model_init_params = identify_algorithm_hyperparameters(
          self.model_initializer
      )
      model_init_params = (
          model_init_params if model_init_params is not None else {}
      )
      try:
          self.model_init_params.update(model_init_params)
      except TypeError:
          self.model_init_params.update(
              dict(build_fn=model_init_params)
          )
  
      self.model_extra_params = (
          model_extra_params if model_extra_params is not None else {}
      )
  
      self.feature_engineer = feature_engineer
      if not isinstance(self.feature_engineer, FeatureEngineer):
          self.feature_engineer = FeatureEngineer(self.feature_engineer)
  
      self.feature_selector = (
          feature_selector if feature_selector is not None else []
      )
  
      self.notes = notes
      self.do_raise_repeated = do_raise_repeated
  
      if self.do_raise_repeated is False:
          G.warn_(
              "WARNING: Setting `do_raise_repeated`=False allows duplicated Experiments"
          )
  
      self.algorithm_name, self.module_name = identify_algorithm(
          self.model_initializer
      )
      self._validate_guidelines()
  
      #################### Deal with Keras ####################
      if self.module_name == "keras":
          (
              reusable_build_fn,
              reusable_wrapper_params,
              dummy_layers,
              dummy_compile_params,
          ) = keras_prep_workflow(
              self.model_initializer,
              self.model_init_params["build_fn"],
              self.model_extra_params,
              self.source_script,
          )
          self.model_init_params = dict(build_fn=reusable_build_fn)
          self.model_extra_params = reusable_wrapper_params
          self.dummy_layers = dummy_layers
          self.dummy_compile_params = dummy_compile_params
          # FLAG: Deal with capitalization conflicts when comparing similar experiments: `optimizer`='Adam' vs 'adam'
  
      self.set_dimensions()
  
imports: from hyperparameter_hunter import __version__
  from hyperparameter_hunter.algorithm_handlers import identify_algorithm, identify_algorithm_hyperparameters
  from hyperparameter_hunter.experiments import CVExperiment
  from hyperparameter_hunter.feature_engineering import FeatureEngineer
  from hyperparameter_hunter.i_o.exceptions import EnvironmentInactiveError, EnvironmentInvalidError, RepeatedExperimentError, DeprecatedWarning
  from hyperparameter_hunter.i_o.reporting import OptimizationReporter
  from hyperparameter_hunter.i_o.result_reader import finder_selector
  from hyperparameter_hunter.compat.keras_helper import reinitialize_callbacks
  from hyperparameter_hunter.compat.keras_optimization_helper import keras_prep_workflow, link_choice_ids
  from hyperparameter_hunter.metrics import get_formatted_target_metric
  from hyperparameter_hunter.optimization.backends.skopt.engine import Optimizer, cook_estimator
  from hyperparameter_hunter.settings import G, TEMP_MODULES_DIR_PATH
  from hyperparameter_hunter.space.dimensions import RejectedOptional
  from hyperparameter_hunter.space.space_core import Space
  from hyperparameter_hunter.utils.boltons_utils import get_path
  from hyperparameter_hunter.utils.general_utils import deep_restricted_update, subdict
  from hyperparameter_hunter.utils.optimization_utils import get_choice_dimensions, dimension_subset
  from hyperparameter_hunter.utils.version_utils import Deprecated
  from abc import ABCMeta, abstractmethod
  from datetime import datetime
  from inspect import currentframe, getframeinfo
  from os import walk, remove, rmdir
  from os.path import abspath
  from typing import Any, Dict
  from warnings import warn
  from skopt.callbacks import check_callback
  from skopt.utils import eval_callbacks
label_desc: Use image node_id 2 for calling the BaseOptPro obj's underlying member method code with example usage: obj.forge_experiment(model_initializer, model_init_params, model_extra_params, feature_engineer, feature_selector, notes, do_raise_repeated) without return types