Changeset 18011
- Timestamp:
- 07/15/21 23:04:44 (3 years ago)
- Location:
- branches/3087_Ceres_Integration/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4
- Files:
-
- 1 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/3087_Ceres_Integration/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression-3.4.csproj
r18009 r18011 136 136 <Compile Include="SingleObjective\ConstantOptimizationAnalyzer.cs" /> 137 137 <Compile Include="SingleObjective\Evaluators\NMSESingleObjectiveConstraintsEvaluator.cs" /> 138 <Compile Include="SingleObjective\Evaluators\ParameterOptimizationEvaluator.cs" /> 138 139 <Compile Include="SingleObjective\Evaluators\SymbolicRegressionMeanRelativeErrorEvaluator.cs" /> 139 140 <Compile Include="SingleObjective\ShapeConstrainedRegressionSingleObjectiveProblem.cs" /> -
branches/3087_Ceres_Integration/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/SymbolicRegressionConstantOptimizationEvaluator.cs
r18010 r18011 30 30 using HeuristicLab.Data; 31 31 using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; 32 using HeuristicLab.NativeInterpreter;33 32 using HeuristicLab.Optimization; 34 33 using HeuristicLab.Parameters; … … 212 211 bool updateConstantsInTree = true, Action<double[], double, object> iterationCallback = null, EvaluationsCounter counter = null) { 213 212 213 // Numeric constants in the tree become variables for parameter optimization. 214 // Variables in the tree become parameters (fixed values) for parameter optimization. 215 // For each parameter (variable in the original tree) we store the 216 // variable name, variable value (for factor vars) and lag as a DataForVariable object. 217 // A dictionary is used to find parameters 218 double[] initialConstants; 219 var parameters = new List<TreeToAutoDiffTermConverter.DataForVariable>(); 220 221 TreeToAutoDiffTermConverter.ParametricFunction func; 222 TreeToAutoDiffTermConverter.ParametricFunctionGradient func_grad; 223 if (!TreeToAutoDiffTermConverter.TryConvertToAutoDiff(tree, updateVariableWeights, applyLinearScaling, out parameters, out initialConstants, out func, out func_grad)) 224 throw new NotSupportedException("Could not optimize constants of symbolic expression tree due to not supported symbols used in the tree."); 225 if (parameters.Count == 0) return 0.0; // constant expressions always have a R² of 0.0 226 var parameterEntries = parameters.ToArray(); // order of entries must be the same for x 227 228 // extract inital constants 229 double[] c; 230 if (applyLinearScaling) { 231 c = new double[initialConstants.Length + 2]; 232 c[0] = 0.0; 233 c[1] = 1.0; 234 Array.Copy(initialConstants, 0, c, 2, initialConstants.Length); 235 } else { 236 c = (double[])initialConstants.Clone(); 237 } 238 214 239 double originalQuality = SymbolicRegressionSingleObjectivePearsonRSquaredEvaluator.Calculate(interpreter, tree, lowerEstimationLimit, upperEstimationLimit, problemData, rows, applyLinearScaling); 215 240 216 var nodesToOptimize = new HashSet<ISymbolicExpressionTreeNode>(); 217 var originalNodeValues = new Dictionary<ISymbolicExpressionTreeNode, double>(); 218 219 foreach (var node in tree.IterateNodesPrefix().OfType<SymbolicExpressionTreeTerminalNode>()) { 220 if (node is VariableTreeNode && !updateVariableWeights) { 221 continue; 241 if (counter == null) counter = new EvaluationsCounter(); 242 var rowEvaluationsCounter = new EvaluationsCounter(); 243 244 alglib.lsfitstate state; 245 alglib.lsfitreport rep; 246 int retVal; 247 248 IDataset ds = problemData.Dataset; 249 double[,] x = new double[rows.Count(), parameters.Count]; 250 int row = 0; 251 foreach (var r in rows) { 252 int col = 0; 253 foreach (var info in parameterEntries) { 254 if (ds.VariableHasType<double>(info.variableName)) { 255 x[row, col] = ds.GetDoubleValue(info.variableName, r + info.lag); 256 } else if (ds.VariableHasType<string>(info.variableName)) { 257 x[row, col] = ds.GetStringValue(info.variableName, r) == info.variableValue ? 1 : 0; 258 } else throw new InvalidProgramException("found a variable of unknown type"); 259 col++; 222 260 } 223 if (node is ConstantTreeNode && node.Parent.Symbol is Power && node.Parent.GetSubtree(1) == node) { 224 // do not optimize exponents 225 continue; 261 row++; 262 } 263 double[] y = ds.GetDoubleValues(problemData.TargetVariable, rows).ToArray(); 264 int n = x.GetLength(0); 265 int m = x.GetLength(1); 266 int k = c.Length; 267 268 alglib.ndimensional_pfunc function_cx_1_func = CreatePFunc(func); 269 alglib.ndimensional_pgrad function_cx_1_grad = CreatePGrad(func_grad); 270 alglib.ndimensional_rep xrep = (p, f, obj) => iterationCallback(p, f, obj); 271 272 try { 273 alglib.lsfitcreatefg(x, y, c, n, m, k, false, out state); 274 alglib.lsfitsetcond(state, 0.0, maxIterations); 275 alglib.lsfitsetxrep(state, iterationCallback != null); 276 alglib.lsfitfit(state, function_cx_1_func, function_cx_1_grad, xrep, rowEvaluationsCounter); 277 alglib.lsfitresults(state, out retVal, out c, out rep); 278 } catch (ArithmeticException) { 279 return originalQuality; 280 } catch (alglib.alglibexception) { 281 return originalQuality; 282 } 283 284 counter.FunctionEvaluations += rowEvaluationsCounter.FunctionEvaluations / n; 285 counter.GradientEvaluations += rowEvaluationsCounter.GradientEvaluations / n; 286 287 //retVal == -7 => constant optimization failed due to wrong gradient 288 // -8 => optimizer detected NAN / INF in the target 289 // function and/ or gradient 290 if (retVal != -7 && retVal != -8) { 291 if (applyLinearScaling) { 292 var tmp = new double[c.Length - 2]; 293 Array.Copy(c, 2, tmp, 0, tmp.Length); 294 UpdateConstants(tree, tmp, updateVariableWeights); 295 } else UpdateConstants(tree, c, updateVariableWeights); 296 } 297 var quality = SymbolicRegressionSingleObjectivePearsonRSquaredEvaluator.Calculate(interpreter, tree, lowerEstimationLimit, upperEstimationLimit, problemData, rows, applyLinearScaling); 298 299 if (!updateConstantsInTree) UpdateConstants(tree, initialConstants, updateVariableWeights); 300 301 if (originalQuality - quality > 0.001 || double.IsNaN(quality)) { 302 UpdateConstants(tree, initialConstants, updateVariableWeights); 303 return originalQuality; 304 } 305 return quality; 306 } 307 308 private static void UpdateConstants(ISymbolicExpressionTree tree, double[] constants, bool updateVariableWeights) { 309 int i = 0; 310 foreach (var node in tree.Root.IterateNodesPrefix().OfType<SymbolicExpressionTreeTerminalNode>()) { 311 ConstantTreeNode constantTreeNode = node as ConstantTreeNode; 312 VariableTreeNodeBase variableTreeNodeBase = node as VariableTreeNodeBase; 313 FactorVariableTreeNode factorVarTreeNode = node as FactorVariableTreeNode; 314 if (constantTreeNode != null) { 315 if (constantTreeNode.Parent.Symbol is Power 316 && constantTreeNode.Parent.GetSubtree(1) == constantTreeNode) continue; // exponents in powers are not optimizated (see TreeToAutoDiffTermConverter) 317 constantTreeNode.Value = constants[i++]; 318 } else if (updateVariableWeights && variableTreeNodeBase != null) 319 variableTreeNodeBase.Weight = constants[i++]; 320 else if (factorVarTreeNode != null) { 321 for (int j = 0; j < factorVarTreeNode.Weights.Length; j++) 322 factorVarTreeNode.Weights[j] = constants[i++]; 226 323 } 227 nodesToOptimize.Add(node); 228 if (node is ConstantTreeNode constant) { 229 originalNodeValues[node] = constant.Value; 230 } else if (node is VariableTreeNode variable) { 231 originalNodeValues[node] = variable.Weight; 232 } 233 } 234 235 var options = new SolverOptions { 236 Iterations = maxIterations 324 } 325 } 326 327 private static alglib.ndimensional_pfunc CreatePFunc(TreeToAutoDiffTermConverter.ParametricFunction func) { 328 return (double[] c, double[] x, ref double fx, object o) => { 329 fx = func(c, x); 330 var counter = (EvaluationsCounter)o; 331 counter.FunctionEvaluations++; 237 332 }; 238 var summary = new OptimizationSummary(); 239 var optimizedNodeValues = ParameterOptimizer.OptimizeTree(tree, problemData.Dataset, problemData.TrainingIndices, problemData.TargetVariable, nodesToOptimize, options, ref summary); 240 241 counter.FunctionEvaluations += summary.ResidualEvaluations; 242 counter.GradientEvaluations += summary.JacobianEvaluations; 243 244 // check if the fitting of the parameters was successful 245 UpdateNodeValues(optimizedNodeValues); 246 247 var quality = SymbolicRegressionSingleObjectivePearsonRSquaredEvaluator.Calculate(interpreter, tree, lowerEstimationLimit, upperEstimationLimit, problemData, rows, applyLinearScaling); 248 if (quality < originalQuality || !updateConstantsInTree) { 249 UpdateNodeValues(originalNodeValues); 250 } 251 return Math.Max(quality, originalQuality); 252 } 253 254 private static void UpdateNodeValues(IDictionary<ISymbolicExpressionTreeNode, double> values) { 255 foreach (var item in values) { 256 var node = item.Key; 257 if (node is ConstantTreeNode constant) { 258 constant.Value = item.Value; 259 } else if (node is VariableTreeNode variable) { 260 variable.Weight = item.Value; 261 } 262 } 263 } 264 333 } 334 335 private static alglib.ndimensional_pgrad CreatePGrad(TreeToAutoDiffTermConverter.ParametricFunctionGradient func_grad) { 336 return (double[] c, double[] x, ref double fx, double[] grad, object o) => { 337 var tuple = func_grad(c, x); 338 fx = tuple.Item2; 339 Array.Copy(tuple.Item1, grad, grad.Length); 340 var counter = (EvaluationsCounter)o; 341 counter.GradientEvaluations++; 342 }; 343 } 265 344 public static bool CanOptimizeConstants(ISymbolicExpressionTree tree) { 266 345 return TreeToAutoDiffTermConverter.IsCompatible(tree);
Note: See TracChangeset
for help on using the changeset viewer.