Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
10/04/17 22:00:52 (7 years ago)
Author:
gkronber
Message:

#2796 worked on MCTS symb reg

Location:
branches/MCTS-SymbReg-2796
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/MCTS-SymbReg-2796

    • Property svn:ignore set to
      TestResults
  • branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Automaton.cs

    r14185 r15403  
    9696    }
    9797
    98     // reverse notation ops
    99     // Expr -> c 0 Term { '+' Term } '+' '*' c '+' 'exit'
     98    // reverse notation
     99    // Expr -> 0 Term { '+' Term } '+' 'exit'
    100100    // Term -> c Fact { '*' Fact } '*'
    101101    // Fact -> VarFact | ExpFact | LogFact | InvFact
     
    120120      actionStrings = new List<string>[nStates, nStates];
    121121
    122       // Expr -> c 0 Term { '+' Term } '+' '*' c '+' 'exit'
     122      // Expr -> 0 Term { '+' Term } '+' 'exit'
    123123      AddTransition(StateExpr, StateTermStart, () => {
    124124        codeGenerator.Reset();
    125         codeGenerator.Emit1(OpCodes.LoadParamN);
    126125        codeGenerator.Emit1(OpCodes.LoadConst0);
    127126        constraintHandler.Reset();
    128       }, "c 0, Reset");
     127      }, "0, Reset");
    129128      AddTransition(StateTermEnd, StateExprEnd, () => {
    130129        codeGenerator.Emit1(OpCodes.Add);
    131         codeGenerator.Emit1(OpCodes.Mul);
    132         codeGenerator.Emit1(OpCodes.LoadParamN);
    133         codeGenerator.Emit1(OpCodes.Add);
    134130        codeGenerator.Emit1(OpCodes.Exit);
    135       }, "+*c+ exit");
     131      }, "+ exit");
    136132      if (allowMultipleTerms)
    137133        AddTransition(StateTermEnd, StateTermStart, () => {
     
    353349    private readonly int[] followStatesBuf = new int[1000];
    354350    public void FollowStates(int state, out int[] buf, out int nElements) {
    355       // for loop instead of where iterator
    356351      var fs = followStates[state];
    357352      int j = 0;
  • branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ConstraintHandler.cs

    r14185 r15403  
    2929  // This class restricts the set of allowed transitions of the automaton to prevent exploration of duplicate expressions.
    3030  // It would be possible to implement this class in such a way that the search never visits a duplicate expression. However,
    31   // it seems very intricate to detect this robustly and in all cases while generating an expression because
    32   // some for of lookahead is necessary.
    33   // Instead the constraint handler only catches the obvious duplicates directly, but does not guarantee that the search always produces a valid expression.
     31  // it seems very intricate to detect this robustly while generating an expression because
     32  // some form of lookahead is necessary.
     33  // Instead, the constraint handler only catches the obvious duplicates directly, but does not guarantee that the search always produces a valid expression.
    3434  // The ratio of the number of unsuccessful searches, that need backtracking should be tracked in the MCTS alg (MctsSymbolicRegressionStatic)
    3535
  • branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExpressionEvaluator.cs

    r14185 r15403  
    161161                var minFx = fxc.Min() - consts[curParamIdx]; // stack[topOfStack] is f(x) + c
    162162
    163                 var delta = 1.0 - minFx - consts[curParamIdx];
    164                 // adjust c so that minFx + c = 1 ... log(minFx + c) = 0
     163                // adjust c so that minFx + c = e and therefore log(minFx + c) = log(e) = 1
     164                // this initialization works in combination with the gradient check (instead of initializing such that log(minFx + c) = 0
     165                var delta = Math.E - minFx - consts[curParamIdx];
    165166                consts[curParamIdx] += delta;
    166167
  • branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionAlgorithm.cs

    r15360 r15403  
    2222using System;
    2323using System.Linq;
    24 using System.Runtime.CompilerServices;
    2524using System.Threading;
    2625using HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression.Policies;
     
    3635
    3736namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
    38   [Item("MCTS Symbolic Regression", "Monte carlo tree search for symbolic regression. Useful mainly as a base learner in gradient boosting.")]
     37  // TODO: support pause (persisting/cloning the state)
     38  [Item("MCTS Symbolic Regression", "Monte carlo tree search for symbolic regression.")]
    3939  [StorableClass]
    4040  [Creatable(CreatableAttribute.Categories.DataAnalysisRegression, Priority = 250)]
     
    5353    private const string CreateSolutionParameterName = "CreateSolution";
    5454    private const string PunishmentFactorParameterName = "PunishmentFactor";
    55 
    56     private const string VariableProductFactorName = "product(xi)";
    57     private const string ExpFactorName = "exp(c * product(xi))";
    58     private const string LogFactorName = "log(c + sum(c*product(xi))";
    59     private const string InvFactorName = "1 / (1 + sum(c*product(xi))";
    60     private const string FactorSumsName = "sum of multiple terms";
     55    private const string CollectParetoOptimalSolutionsParameterName = "CollectParetoOptimalSolutions";
     56    private const string LambdaParameterName = "Lambda";
     57
     58    private const string VariableProductFactorName = "x * y * ...";
     59    private const string ExpFactorName = "exp(c * x * y ...)";
     60    private const string LogFactorName = "log(c + c1 x + c2 x + ...)";
     61    private const string InvFactorName = "1 / (1 + c1 x + c2 x + ...)";
     62    private const string FactorSumsName = "t1(x) + t2(x) + ... ";
    6163    #endregion
    6264
     
    9496    public IFixedValueParameter<BoolValue> CreateSolutionParameter {
    9597      get { return (IFixedValueParameter<BoolValue>)Parameters[CreateSolutionParameterName]; }
     98    }
     99    public IFixedValueParameter<BoolValue> CollectParetoOptimalSolutionsParameter {
     100      get { return (IFixedValueParameter<BoolValue>)Parameters[CollectParetoOptimalSolutionsParameterName]; }
     101    }
     102    public IFixedValueParameter<DoubleValue> LambdaParameter {
     103      get { return (IFixedValueParameter<DoubleValue>)Parameters[LambdaParameterName]; }
    96104    }
    97105    #endregion
     
    136144      get { return CreateSolutionParameter.Value.Value; }
    137145      set { CreateSolutionParameter.Value.Value = value; }
     146    }
     147    public bool CollectParetoOptimalSolutions {
     148      get { return CollectParetoOptimalSolutionsParameter.Value.Value; }
     149      set { CollectParetoOptimalSolutionsParameter.Value.Value = value; }
     150    }
     151    public double Lambda {
     152      get { return LambdaParameter.Value.Value; }
     153      set { LambdaParameter.Value.Value = value; }
    138154    }
    139155    #endregion
     
    177193      Parameters.Add(new FixedValueParameter<IntValue>(ConstantOptimizationIterationsParameterName,
    178194        "Number of iterations for constant optimization. A small number of iterations should be sufficient for most models. " +
    179         "Set to 0 to disable constants optimization.", new IntValue(10)));
     195        "Set to 0 to let the algorithm stop automatically when it converges. Set to -1 to disable constants optimization.", new IntValue(10)));
    180196      Parameters.Add(new FixedValueParameter<BoolValue>(ScaleVariablesParameterName,
    181         "Set to true to scale all input variables to the range [0..1]", new BoolValue(false)));
     197        "Set to true to all input variables to the range [0..1]", new BoolValue(true)));
    182198      Parameters[ScaleVariablesParameterName].Hidden = true;
    183199      Parameters.Add(new FixedValueParameter<DoubleValue>(PunishmentFactorParameterName, "Estimations of models can be bounded. The estimation limits are calculated in the following way (lb = mean(y) - punishmentFactor*range(y), ub = mean(y) + punishmentFactor*range(y))", new DoubleValue(10)));
     
    187203      Parameters[UpdateIntervalParameterName].Hidden = true;
    188204      Parameters.Add(new FixedValueParameter<BoolValue>(CreateSolutionParameterName,
    189         "Flag that indicates if a solution should be produced at the end of the run", new BoolValue(true)));
     205        "Optionally produce a solution at the end of the run", new BoolValue(true)));
    190206      Parameters[CreateSolutionParameterName].Hidden = true;
     207
     208      Parameters.Add(new FixedValueParameter<BoolValue>(CollectParetoOptimalSolutionsParameterName,
     209        "Optionally collect a set of Pareto-optimal solutions minimizing error and complexity.", new BoolValue(false)));
     210      Parameters[CollectParetoOptimalSolutionsParameterName].Hidden = true;
     211
     212      Parameters.Add(new FixedValueParameter<DoubleValue>(LambdaParameterName,
     213        "Lambda is the factor for the regularization term in the objective function (Obj = (y - f(x,p))² + lambda * |p|²)", new DoubleValue(0.0)));
    191214    }
    192215
     
    195218    }
    196219
     220    // TODO: support pause and restart
    197221    protected override void Run(CancellationToken cancellationToken) {
    198222      // Set up the algorithm
    199223      if (SetSeedRandomly) Seed = new System.Random().Next();
     224      var collectPareto = CollectParetoOptimalSolutions;
    200225
    201226      // Set up the results display
     
    229254      var gradEvals = new IntValue();
    230255      Results.Add(new Result("Gradient evaluations", gradEvals));
    231       var paretoBestModelsResult = new Result("ParetoBestModels", typeof(ItemList<ISymbolicRegressionSolution>));
    232       Results.Add(paretoBestModelsResult);
    233 
     256
     257      Result paretoBestModelsResult = new Result("ParetoBestModels", typeof(ItemList<ISymbolicRegressionSolution>));
     258      if (collectPareto) {
     259        Results.Add(paretoBestModelsResult);
     260      }
    234261
    235262      // same as in SymbolicRegressionSingleObjectiveProblem
     
    246273      var problemData = (IRegressionProblemData)Problem.ProblemData.Clone();
    247274      if (!AllowedFactors.CheckedItems.Any()) throw new ArgumentException("At least on type of factor must be allowed");
    248       var state = MctsSymbolicRegressionStatic.CreateState(problemData, (uint)Seed, MaxVariableReferences, ScaleVariables, ConstantOptimizationIterations,
    249         Policy,
     275      var state = MctsSymbolicRegressionStatic.CreateState(problemData, (uint)Seed, MaxVariableReferences, ScaleVariables,
     276        ConstantOptimizationIterations, Lambda,
     277        Policy, collectPareto,
    250278        lowerLimit, upperLimit,
    251279        allowProdOfVars: AllowedFactors.CheckedItems.Any(s => s.Value.Value == VariableProductFactorName),
     
    261289      double curBestQ = 0.0;
    262290      int n = 0;
     291
     292      // cancelled before we acutally started
     293      cancellationToken.ThrowIfCancellationRequested();
     294
    263295      // Loop until iteration limit reached or canceled.
    264       for (int i = 0; i < Iterations && !state.Done; i++) {
    265         cancellationToken.ThrowIfCancellationRequested();
    266 
     296      for (int i = 0; i < Iterations && !state.Done && !cancellationToken.IsCancellationRequested; i++) {
    267297        var q = MctsSymbolicRegressionStatic.MakeStep(state);
    268298        sumQ += q; // sum of qs in the last updateinterval iterations
     
    286316          totalRollouts.Value = state.TotalRollouts;
    287317
    288           paretoBestModelsResult.Value = new ItemList<ISymbolicRegressionSolution>(state.ParetoBestModels);
     318          if (collectPareto) {
     319            paretoBestModelsResult.Value = new ItemList<ISymbolicRegressionSolution>(state.ParetoBestModels);
     320          }
    289321
    290322          table.Rows["Best quality"].Values.Add(bestQuality.Value);
     
    296328      }
    297329
    298       // final results
     330      // final results (assumes that at least one iteration was calculated)
    299331      if (n > 0) {
    300332        if (bestQ > bestQuality.Value) {
  • branches/MCTS-SymbReg-2796/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionStatic.cs

    r15360 r15403  
    3535namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
    3636  public static class MctsSymbolicRegressionStatic {
    37     // TODO: SGD with adagrad instead of lbfgs?
    38     // TODO: check Taylor expansion capabilities (ln(x), sqrt(x), exp(x)) in combination with GBT
    39     // TODO: optimize for 3 targets concurrently (y, 1/y, exp(y), and log(y))? Would simplify the number of possible expressions again
     37    // OBJECTIVES:
     38    // 1) solve toy problems without numeric constants (to show that structure search is effective / efficient)
     39    //    - e.g. Keijzer, Nguyen ... where no numeric constants are involved
     40    //    - assumptions:
     41    //      - we don't know the necessary operations or functions -> all available functions could be necessary
     42    //      - but we do not need to tune numeric constants -> no scaling of input variables x!
     43    // 2) Solve toy problems with numeric constants to make the algorithm invariant concerning variable scale.
     44    //    This is important for real world applications.
     45    //    - e.g. Korns or Vladislavleva problems where numeric constants are involved
     46    //    - assumptions:
     47    //      - any numeric constant is possible (a-priori we might assume that small abs. constants are more likely)
     48    //      - standardization of variables is possible (or might be necessary) as we adjust numeric parameters of the expression anyway
     49    //      - to simplify the problem we can restrict the set of functions e.g. we assume which functions are necessary for the problem instance
     50    //        -> several steps: (a) polyinomials, (b) rational polynomials, (c) exponential or logarithmic functions, rational functions with exponential and logarithmic parts
     51    // 3) efficiency and effectiveness for real-world problems
     52    //    - e.g. Tower problem
     53    //    - (1) and (2) combined, structure search must be effective in combination with numeric optimization of constants
     54    //   
     55
     56    // TODO NEXT: check if transformation of y is correct and works
     57    // TODO: transform target y to zero-mean and remove linear scaling parameters
     58    // TODO: include offset for variables as parameter
     59    // TODO: why does LM optimization converge so slowly with exp(x), log(x), and 1/x allowed?
     60    // TODO: support e(-x) and possibly (1/-x)
     61    // TODO: is it OK to initialize all constants to 1?
    4062    #region static API
    4163
     
    7698      private readonly double[] scalingFactor;
    7799      private readonly double[] scalingOffset;
     100      private readonly double yStdDev; // for scaling parameters (e.g. stopping condition for LM)
    78101      private readonly int constOptIterations;
     102      private readonly double lambda; // weight of penalty term for regularization
    79103      private readonly double lowerEstimationLimit, upperEstimationLimit;
     104      private readonly bool collectParetoOptimalModels;
    80105      private readonly List<ISymbolicRegressionSolution> paretoBestModels = new List<ISymbolicRegressionSolution>();
    81106      private readonly List<double[]> paretoFront = new List<double[]>(); // matching the models
     
    99124      private readonly double[][] gradBuf;
    100125
    101       public State(IRegressionProblemData problemData, uint randSeed, int maxVariables, bool scaleVariables, int constOptIterations,
     126      public State(IRegressionProblemData problemData, uint randSeed, int maxVariables, bool scaleVariables,
     127        int constOptIterations, double lambda,
    102128        IPolicy treePolicy = null,
     129        bool collectParetoOptimalModels = false,
    103130        double lowerEstimationLimit = double.MinValue, double upperEstimationLimit = double.MaxValue,
    104131        bool allowProdOfVars = true,
     
    108135        bool allowMultipleTerms = false) {
    109136
     137        if (lambda < 0) throw new ArgumentException("Lambda must be larger or equal zero", "lambda");
     138
    110139        this.problemData = problemData;
    111140        this.constOptIterations = constOptIterations;
     141        this.lambda = lambda;
    112142        this.evalFun = this.Eval;
    113143        this.lowerEstimationLimit = lowerEstimationLimit;
    114144        this.upperEstimationLimit = upperEstimationLimit;
     145        this.collectParetoOptimalModels = collectParetoOptimalModels;
    115146
    116147        random = new MersenneTwister(randSeed);
     
    128159        this.x = x;
    129160        this.y = y;
     161        this.yStdDev = HeuristicLab.Common.EnumerableStatisticExtensions.StandardDeviation(y);
    130162        this.testX = testX;
    131163        this.testY = testY;
     
    181213          var t = new SymbolicExpressionTree(treeGen.Exec(bestCode, bestConsts, bestNParams, scalingFactor, scalingOffset));
    182214          var model = new SymbolicRegressionModel(problemData.TargetVariable, t, interpreter, lowerEstimationLimit, upperEstimationLimit);
    183 
    184           // model has already been scaled linearly in Eval
     215          model.Scale(problemData); // apply linear scaling
    185216          return model;
    186217        }
     
    212243          Array.Copy(optConsts, bestConsts, bestNParams);
    213244        }
    214         // multi-objective best
    215         var complexity = // SymbolicDataAnalysisModelComplexityCalculator.CalculateComplexity() TODO
    216           Array.FindIndex(code, (opc)=>opc==(byte)OpCodes.Exit);
    217         UpdateParetoFront(q, complexity, code, optConsts, nParams, scalingFactor, scalingOffset);
    218 
     245        if (collectParetoOptimalModels) {
     246          // multi-objective best
     247          var complexity = // SymbolicDataAnalysisModelComplexityCalculator.CalculateComplexity() TODO: implement Kommenda's tree complexity directly in the evaluator
     248            Array.FindIndex(code, (opc) => opc == (byte)OpCodes.Exit);  // use length of expression as surrogate for complexity
     249          UpdateParetoFront(q, complexity, code, optConsts, nParams, scalingFactor, scalingOffset);
     250        }
    219251        return q;
    220252      }
     253
     254      private void Eval(byte[] code, int nParams, out double rsq, out double[] optConsts) {
     255        // we make a first pass to determine a valid starting configuration for all constants
     256        // constant c in log(c + f(x)) is adjusted to guarantee that x is positive (see expression evaluator)
     257        // scale and offset are set to optimal starting configuration
     258        // assumes scale is the first param and offset is the last param
     259
     260        // reset constants
     261        Array.Copy(ones, constsBuf, nParams);
     262        evaluator.Exec(code, x, constsBuf, predBuf, adjustOffsetForLogAndExp: true);
     263        funcEvaluations++;
     264
     265        if (nParams == 0 || constOptIterations < 0) {
     266          // if we don't need to optimize parameters then we are done
     267          // changing scale and offset does not influence r²
     268          rsq = RSq(y, predBuf);
     269          optConsts = constsBuf;
     270        } else {
     271          // optimize constants using the starting point calculated above
     272          OptimizeConstsLm(code, constsBuf, nParams, 0.0, nIters: constOptIterations);
     273
     274          evaluator.Exec(code, x, constsBuf, predBuf);
     275          funcEvaluations++;
     276
     277          rsq = RSq(y, predBuf);
     278          optConsts = constsBuf;
     279        }
     280      }
     281
     282
     283
     284      #region helpers
     285      private static double RSq(IEnumerable<double> x, IEnumerable<double> y) {
     286        OnlineCalculatorError error;
     287        double r = OnlinePearsonsRCalculator.Calculate(x, y, out error);
     288        return error == OnlineCalculatorError.None ? r * r : 0.0;
     289      }
     290
     291
     292      private void OptimizeConstsLm(byte[] code, double[] consts, int nParams, double epsF = 0.0, int nIters = 100) {
     293        double[] optConsts = new double[nParams]; // allocate a smaller buffer for constants opt (TODO perf?)
     294        Array.Copy(consts, optConsts, nParams);
     295
     296        // direct usage of LM is recommended in alglib manual for better performance than the lsfit interface (which uses lm internally).
     297        alglib.minlmstate state;
     298        alglib.minlmreport rep = null;
     299        alglib.minlmcreatevj(y.Length + 1, optConsts, out state); // +1 for penalty term
     300        // Using the change of the gradient as stopping criterion is recommended in alglib manual.
     301        // However, the most recent version of alglib (as of Oct 2017) only supports epsX as stopping criterion
     302        alglib.minlmsetcond(state, epsg: 1E-6 * yStdDev, epsf: epsF, epsx: 0.0, maxits: nIters);
     303        // alglib.minlmsetgradientcheck(state, 1E-5);
     304        alglib.minlmoptimize(state, Func, FuncAndJacobian, null, code);
     305        alglib.minlmresults(state, out optConsts, out rep);
     306        funcEvaluations += rep.nfunc;
     307        gradEvaluations += rep.njac * nParams;
     308
     309        if (rep.terminationtype < 0) throw new ArgumentException("lm failed: termination type = " + rep.terminationtype);
     310
     311        // only use optimized constants if successful
     312        if (rep.terminationtype >= 0) {
     313          Array.Copy(optConsts, consts, optConsts.Length);
     314        }
     315      }
     316
     317      private void Func(double[] arg, double[] fi, object obj) {
     318        var code = (byte[])obj;
     319        int n = predBuf.Length;
     320        evaluator.Exec(code, x, arg, predBuf); // gradients are nParams x vLen
     321        for (int r = 0; r < n; r++) {
     322          var res = predBuf[r] - y[r];
     323          fi[r] = res;
     324        }
     325
     326        var penaltyIdx = fi.Length - 1;
     327        fi[penaltyIdx] = 0.0;
     328        // calc length of parameter vector for regularization
     329        var aa = 0.0;
     330        for (int i = 0; i < arg.Length; i++) {
     331          aa += arg[i] * arg[i];
     332        }
     333        if (lambda > 0 && aa > 0) {
     334          // scale lambda using stdDev(y) to make the parameter independent of the scale of y
     335          // scale lambda using n to make parameter independent of the number of training points
     336          // take the root because LM squares the result
     337          fi[penaltyIdx] = Math.Sqrt(n * lambda / yStdDev * aa);
     338        }
     339      }
     340
     341      private void FuncAndJacobian(double[] arg, double[] fi, double[,] jac, object obj) {
     342        int n = predBuf.Length;
     343        int nParams = arg.Length;
     344        var code = (byte[])obj;
     345        evaluator.ExecGradient(code, x, arg, predBuf, gradBuf); // gradients are nParams x vLen
     346        for (int r = 0; r < n; r++) {
     347          var res = predBuf[r] - y[r];
     348          fi[r] = res;
     349
     350          for (int k = 0; k < nParams; k++) {
     351            jac[r, k] = gradBuf[k][r];
     352          }
     353        }
     354        // calc length of parameter vector for regularization
     355        double aa = 0.0;
     356        for (int i = 0; i < arg.Length; i++) {
     357          aa += arg[i] * arg[i];
     358        }
     359
     360        var penaltyIdx = fi.Length - 1;
     361        if (lambda > 0 && aa > 0) {
     362          fi[penaltyIdx] = 0.0;
     363          // scale lambda using stdDev(y) to make the parameter independent of the scale of y
     364          // scale lambda using n to make parameter independent of the number of training points
     365          // take the root because alglib LM squares the result
     366          fi[penaltyIdx] = Math.Sqrt(n * lambda / yStdDev * aa);
     367
     368          for (int i = 0; i < arg.Length; i++) {
     369            jac[penaltyIdx, i] = 0.5 / fi[penaltyIdx] * 2 * n * lambda / yStdDev * arg[i];
     370          }
     371        } else {
     372          fi[penaltyIdx] = 0.0;
     373          for (int i = 0; i < arg.Length; i++) {
     374            jac[penaltyIdx, i] = 0.0;
     375          }
     376        }
     377      }
     378
    221379
    222380      private void UpdateParetoFront(double q, int complexity, byte[] code, double[] param, int nParam,
     
    233391          }
    234392        }
    235         if(isNonDominated) {
     393        if (isNonDominated) {
    236394          paretoFront.Add(cur);
    237395
     
    242400          var t = new SymbolicExpressionTree(treeGen.Exec(code, param, nParam, scalingFactor, scalingOffset));
    243401          var model = new SymbolicRegressionModel(problemData.TargetVariable, t, interpreter, lowerEstimationLimit, upperEstimationLimit);
     402          model.Scale(problemData); // apply linear scaling
    244403
    245404          var sol = model.CreateRegressionSolution(this.problemData);
    246405          sol.Name = string.Format("{0:N5} {1}", q, complexity);
    247          
     406
    248407          paretoBestModels.Add(sol);
    249408        }
    250         for(int i=paretoFront.Count-2;i>=0;i--) {
     409        for (int i = paretoFront.Count - 2; i >= 0; i--) {
    251410          var @ref = paretoFront[i];
    252411          var domRes = DominationCalculator<int>.Dominates(cur, @ref, max, true);
    253           if(domRes == DominationResult.Dominates) {
     412          if (domRes == DominationResult.Dominates) {
    254413            paretoFront.RemoveAt(i);
    255414            paretoBestModels.RemoveAt(i);
     
    257416        }
    258417      }
    259 
    260       private void Eval(byte[] code, int nParams, out double rsq, out double[] optConsts) {
    261         // we make a first pass to determine a valid starting configuration for all constants
    262         // constant c in log(c + f(x)) is adjusted to guarantee that x is positive (see expression evaluator)
    263         // scale and offset are set to optimal starting configuration
    264         // assumes scale is the first param and offset is the last param
    265         double alpha;
    266         double beta;
    267 
    268         // reset constants
    269         Array.Copy(ones, constsBuf, nParams);
    270         evaluator.Exec(code, x, constsBuf, predBuf, adjustOffsetForLogAndExp: true);
    271         funcEvaluations++;
    272 
    273         // calc opt scaling (alpha*f(x) + beta)
    274         OnlineCalculatorError error;
    275         OnlineLinearScalingParameterCalculator.Calculate(predBuf, y, out alpha, out beta, out error);
    276         if (error == OnlineCalculatorError.None) {
    277           constsBuf[0] *= beta;
    278           constsBuf[nParams - 1] = constsBuf[nParams - 1] * beta + alpha;
    279         }
    280         if (nParams <= 2 || constOptIterations <= 0) {
    281           // if we don't need to optimize parameters then we are done
    282           // changing scale and offset does not influence r²
    283           rsq = RSq(y, predBuf);
    284           optConsts = constsBuf;
    285         } else {
    286           // optimize constants using the starting point calculated above
    287           OptimizeConstsLm(code, constsBuf, nParams, 0.0, nIters: constOptIterations);
    288 
    289           evaluator.Exec(code, x, constsBuf, predBuf);
    290           funcEvaluations++;
    291 
    292           rsq = RSq(y, predBuf);
    293           optConsts = constsBuf;
    294         }
    295       }
    296 
    297 
    298 
    299       #region helpers
    300       private static double RSq(IEnumerable<double> x, IEnumerable<double> y) {
    301         OnlineCalculatorError error;
    302         double r = OnlinePearsonsRCalculator.Calculate(x, y, out error);
    303         return error == OnlineCalculatorError.None ? r * r : 0.0;
    304       }
    305 
    306 
    307       private void OptimizeConstsLm(byte[] code, double[] consts, int nParams, double epsF = 0.0, int nIters = 100) {
    308         double[] optConsts = new double[nParams]; // allocate a smaller buffer for constants opt (TODO perf?)
    309         Array.Copy(consts, optConsts, nParams);
    310 
    311         alglib.minlmstate state;
    312         alglib.minlmreport rep = null;
    313         alglib.minlmcreatevj(y.Length, optConsts, out state);
    314         alglib.minlmsetcond(state, 0.0, epsF, 0.0, nIters);
    315         //alglib.minlmsetgradientcheck(state, 0.000001);
    316         alglib.minlmoptimize(state, Func, FuncAndJacobian, null, code);
    317         alglib.minlmresults(state, out optConsts, out rep);
    318         funcEvaluations += rep.nfunc;
    319         gradEvaluations += rep.njac * nParams;
    320 
    321         if (rep.terminationtype < 0) throw new ArgumentException("lm failed: termination type = " + rep.terminationtype);
    322 
    323         // only use optimized constants if successful
    324         if (rep.terminationtype >= 0) {
    325           Array.Copy(optConsts, consts, optConsts.Length);
    326         }
    327       }
    328 
    329       private void Func(double[] arg, double[] fi, object obj) {
    330         var code = (byte[])obj;
    331         evaluator.Exec(code, x, arg, predBuf); // gradients are nParams x vLen
    332         for (int r = 0; r < predBuf.Length; r++) {
    333           var res = predBuf[r] - y[r];
    334           fi[r] = res;
    335         }
    336       }
    337       private void FuncAndJacobian(double[] arg, double[] fi, double[,] jac, object obj) {
    338         int nParams = arg.Length;
    339         var code = (byte[])obj;
    340         evaluator.ExecGradient(code, x, arg, predBuf, gradBuf); // gradients are nParams x vLen
    341         for (int r = 0; r < predBuf.Length; r++) {
    342           var res = predBuf[r] - y[r];
    343           fi[r] = res;
    344 
    345           for (int k = 0; k < nParams; k++) {
    346             jac[r, k] = gradBuf[k][r];
    347           }
    348         }
    349       }
    350418      #endregion
    351419    }
    352420
     421
     422    /// <summary>
     423    /// Static method to initialize a state for the algorithm
     424    /// </summary>
     425    /// <param name="problemData">The problem data</param>
     426    /// <param name="randSeed">Random seed.</param>
     427    /// <param name="maxVariables">Maximum number of variable references that are allowed in the expression.</param>
     428    /// <param name="scaleVariables">Optionally scale input variables to the interval [0..1] (recommended)</param>
     429    /// <param name="constOptIterations">Maximum number of iterations for constants optimization (Levenberg-Marquardt)</param>
     430    /// <param name="lambda">Penalty factor for regularization (0..inf.), small penalty disabled regularization.</param>
     431    /// <param name="policy">Tree search policy (random, ucb, eps-greedy, ...)</param>
     432    /// <param name="collectParameterOptimalModels">Optionally collect all Pareto-optimal solutions having minimal length and error.</param>
     433    /// <param name="lowerEstimationLimit">Optionally limit the result of the expression to this lower value.</param>
     434    /// <param name="upperEstimationLimit">Optionally limit the result of the expression to this upper value.</param>
     435    /// <param name="allowProdOfVars">Allow products of expressions.</param>
     436    /// <param name="allowExp">Allow expressions with exponentials.</param>
     437    /// <param name="allowLog">Allow expressions with logarithms</param>
     438    /// <param name="allowInv">Allow expressions with 1/x</param>
     439    /// <param name="allowMultipleTerms">Allow expressions which are sums of multiple terms.</param>
     440    /// <returns></returns>
     441
    353442    public static IState CreateState(IRegressionProblemData problemData, uint randSeed, int maxVariables = 3,
    354       bool scaleVariables = true, int constOptIterations = 0,
     443      bool scaleVariables = true, int constOptIterations = -1, double lambda = 0.0,
    355444      IPolicy policy = null,
     445      bool collectParameterOptimalModels = false,
    356446      double lowerEstimationLimit = double.MinValue, double upperEstimationLimit = double.MaxValue,
    357447      bool allowProdOfVars = true,
     
    361451      bool allowMultipleTerms = false
    362452      ) {
    363       return new State(problemData, randSeed, maxVariables, scaleVariables, constOptIterations,
    364         policy,
     453      return new State(problemData, randSeed, maxVariables, scaleVariables, constOptIterations, lambda,
     454        policy, collectParameterOptimalModels,
    365455        lowerEstimationLimit, upperEstimationLimit,
    366456        allowProdOfVars, allowExp, allowLog, allowInv, allowMultipleTerms);
     
    481571      var i = 0;
    482572      if (scaleVariables) {
    483         scalingFactor = new double[xs.Length];
    484         scalingOffset = new double[xs.Length];
     573        scalingFactor = new double[xs.Length + 1];
     574        scalingOffset = new double[xs.Length + 1];
    485575      } else {
    486576        scalingFactor = null;
     
    502592      }
    503593
     594      if (scaleVariables) {
     595        // transform target variable to zero-mean
     596        scalingFactor[i] = 1.0;
     597        scalingOffset[i] = -problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows).Average();
     598      }
     599
    504600      GenerateData(problemData, rows, scalingFactor, scalingOffset, out xs, out y);
    505601    }
     
    518614      }
    519615
    520       y = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows).ToArray();
     616      {
     617        var sf = scalingFactor == null ? 1.0 : scalingFactor[i];
     618        var offset = scalingFactor == null ? 0.0 : scalingOffset[i];
     619        y = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows).Select(yi => yi * sf + offset).ToArray();
     620      }
    521621    }
    522622  }
Note: See TracChangeset for help on using the changeset viewer.