docs: protected void initializeTraining(){
          if(!initializedTraining) {
              if(trainingConfig == null) {
                  throw new ND4JIllegalStateException("Please specify a training config with setTrainingConfig");
              }
              //First: infer the variables to be optimized if required
              if(trainingConfig.getTrainableParams() == null || trainingConfig.getTrainableParams().size() == 0) {
                  //Variable is trainable if it's not the output of some function
                  //TODO also - should be floating point type
                  List<String> trainVarList = new ArrayList<>();
                  for(Variable var : variables.values()){
                      SDVariable v = var.getVariable();
                      String n = v.getVarName();
                      if(variables.get(n).getOutputOfOp() == null &&       //Is a leaf (not the output of a function)
                              !isPlaceHolder(n) &&                                //and not a placeholder
                              !variables.get(n).getVariable().isConstant() &&     //and not a constant
                              (trainingConfig.getDataSetFeatureMapping() == null || !trainingConfig.getDataSetFeatureMapping().contains(n))   &&  //and not an input (this really should be a placeholder, but we can't guarantee that...)
                              (trainingConfig.getDataSetLabelMapping() == null || !trainingConfig.getDataSetLabelMapping().contains(n))   &&      //and not a label (this really should be a placeholder, but we can't guarantee that...)
                              (trainingConfig.getDataSetFeatureMaskMapping() == null || !trainingConfig.getDataSetFeatureMaskMapping().contains(n))   &&  //and not a feature mask (this really should be a placeholder, but we can't guarantee that...)
                              (trainingConfig.getDataSetLabelMaskMapping() == null || !trainingConfig.getDataSetLabelMaskMapping().contains(n))){  //and not a label input (this really should be a placeholder, but we can't guarantee that...)
                          trainVarList.add(n);
                      }
                  }
  
                  trainingConfig.setTrainableParams(trainVarList);
                  log.info("Inferred trainable variables: {}", trainVarList);
              }
  
              //Allocate updater state
              long numTrainableParams = 0;
              DataType dt = null;             //TODO support mixed precision variables - https://github.com/deeplearning4j/deeplearning4j/issues/6992
              for(String s : trainingConfig.getTrainableParams()) {
                  SDVariable v = variables.get(s).getVariable();
                  Preconditions.checkState(v != null, "No variable found for trainable parameter name \"%s\"", s);
  
                  INDArray arr = v.getArr();
                  Preconditions.checkState(arr != null, "No array found for trainable parameter \"%s\"", s);
                  numTrainableParams += arr.length();
                  if(dt == null)
                      dt = arr.dataType();
              }
  
              long updaterStateSize = trainingConfig.getUpdater().stateSize(numTrainableParams);
  
              if(updaterStateSize > 0) {
                  try(MemoryWorkspace ws = Nd4j.getMemoryManager().scopeOutOfWorkspaces()) {
                      updaterState = Nd4j.createUninitialized(dt, 1, updaterStateSize);
                  }
              }
  
              long viewSoFar = 0;
              updaterViews = new HashMap<>();
              updaterMap = new HashMap<>();
              for(String s : trainingConfig.getTrainableParams()) {
                  long thisSize = trainingConfig.getUpdater().stateSize(variables.get(s).getVariable().getArr().length());
                  INDArray view = (updaterStateSize == 0 || thisSize == 0 ? null :
                          updaterState.get(NDArrayIndex.interval(0, 1), NDArrayIndex.interval(viewSoFar, viewSoFar + thisSize)));
  
                  updaterViews.put(s, view);
                  updaterMap.put(s, trainingConfig.getUpdater().instantiate(view, true));
                  viewSoFar += thisSize;
              }
  
              initializedTraining = true;
          }
      }