Определение цели с помощью Microsoft Solution Foundation
Цель программы: интеграция. Я реализую адаптивный квадратурный (иначе числовое интегрирование) алгоритм для больших размеров (до 100). Идея состоит в том, чтобы случайным образом разделить объем на более мелкие участки путем оценки точек с использованием плотности выборки, пропорциональной оценке ошибки в этой точке. Вначале я "прожигал" однородную выборку, затем случайным образом выбирал точки в соответствии с гауссовым распределением по расчетной ошибке. Аналогично моделируемому отжигу, я "понижаю температуру" и уменьшаю стандартное отклонение моего гауссова с течением времени, так что точки с низкой ошибкой изначально имеют хороший шанс выбора, но в дальнейшем выбираются с неуклонно убывающим вероятность. Это позволяет программе наткнуться на пики, которые могут быть пропущены из-за несовершенства функции ошибок. (Мой алгоритм по духу похож на интеграцию Маркова-Цепи Монте-Карло.)
Функциональные характеристики. Интегрируемая функция - это оценка страхового полиса для нескольких зданий в результате стихийного бедствия. Функции политики не являются гладкими: существуют франшизы, максимумы, уровни (например, нулевая выплата до 1 миллиона долларов, 100% от 1-2 миллионов долларов, затем нулевая выплата свыше 2 миллионов долларов) и другие нечетные условия политики. Это вводит нелинейное поведение и функции, которые не имеют производных в многочисленных плоскостях. На вершине функции политики находится функция ущерба, которая зависит от типа здания и силы урагана и определенно не имеет колоколообразной формы.
Контекст проблемы: Функция ошибки. Сложность заключается в выборе хорошей функции ошибок. Для каждой точки я записываю меры, которые кажутся полезными для этого: величину функции, насколько она изменилась в результате предыдущего измерения (прокси для первой производной), объем области, которую занимает точка (большие объемы могут лучше скрыть ошибку) и геометрический фактор, связанный с формой региона. Моя функция ошибок будет представлять собой линейную комбинацию этих мер, где каждому показателю присваивается свой вес. (Если я получу плохие результаты, я буду рассматривать нелинейные функции.) Чтобы помочь мне в этих усилиях, я решил провести оптимизацию по широкому диапазону возможных значений для каждого веса, следовательно, Microsoft Solution Foundation.
Что оптимизировать: ошибка рейтинга. Мои меры нормализованы, от нуля до единицы. Эти значения ошибок постепенно пересматриваются по мере того, как интеграция переходит к отражению последних средних значений функций, изменений и т. Д. В результате я не пытаюсь создать функцию, которая выдает фактические значения ошибок, а вместо этого возвращает число, которое сортирует так же, как истинная ошибка, т. е. если все выбранные точки отсортированы по этому оценочному значению ошибки, они должны получить ранг, аналогичный рангу, который они получили бы, если бы они были отсортированы по истинной ошибке.
Не все очки равны. Мне очень важно, если область точек с истинной ошибкой #1 ранжируется #1000 (или наоборот), но очень мало, если точка #500 ранжируется #1000. Моя мера успеха состоит в том, чтобы МИНИМИЗИРОВАТЬ сумму следующих по многим регионам в некоторой точке в процессе выполнения алгоритма:
ABS (Log2 (trueErrorRank) - Log2 (оценочный ErrorRank))
Для Log2 я использую функцию, которая возвращает наибольшую степень двух меньше или равна числу. Из этого определения приходят полезные результаты. Обмен № 1 и № 2 стоит очко, но обмен № 2 и № 3 ничего не стоит. Это имеет эффект расслоения точек на мощность двух диапазонов. Точки, которые меняются в пределах диапазона, не добавляют к функции.
Как я оцениваю. Я создал класс под названием Rank, который делает это:
Занимает все регионы по ошибке один раз.
Для каждого отдельного набора параметризованных весов вычисляется пробная (оценочная) ошибка для этого региона.
Сортирует регионы по этой пробной ошибке.
Вычисляет пробный рейтинг для каждого региона.
Суммирует абсолютную разницу журналов двух рангов и называет это значением параметризации, следовательно значение должно быть минимизировано.
Код C#. Сделав все это, мне просто нужен способ настроить Microsoft Solver Foundation, чтобы найти для меня лучшие параметры. Синтаксис меня озадачил. Вот мой код C#, который у меня есть. В нем вы увидите комментарии по трем выявленным мною проблемам. Может быть, вы можете заметить еще больше! Есть идеи, как сделать эту работу?
public void Optimize()
{
// Get the parameters from the GUI and figures out the low and high values for each weight.
ParseParameters();
// Computes the true rank for each region according to true error.
var myRanker = new Rank(ErrorData, false);
// Obtain Microsoft Solver Foundation's core solver object.
var solver = SolverContext.GetContext();
var model = solver.CreateModel();
// Create a delegate that can extract the current value of each solver parameter
// and stuff it in to a double array so we can later use it to call LinearTrial.
Func<Model, double[]> marshalWeights = (Model m) =>
{
var i = 0;
var weights = new double[myRanker.ParameterCount];
foreach (var d in m.Decisions)
{
weights[i] = d.ToDouble();
i++;
}
return weights;
};
// Make a solver decision for each GUI defined parameter.
// Parameters is a Dictionary whose Key is the parameter name, and whose
// value is a Tuple of two doubles, the low and high values for the range.
// All are Real numbers constrained to fall between a defined low and high value.
foreach (var pair in Parameters)
{
// PROBLEM 1! Should I be using Decisions or Parameters here?
var decision = new Decision(Domain.RealRange(ToRational(pair.Value.Item1), ToRational(pair.Value.Item2)), pair.Key);
model.AddDecision(decision);
}
// PROBLEM 2! This calls myRanker.LinearTrial immediately,
// before the Decisions have values. Also, it does not return a Term.
// I want to pass it in a lambda to be evaluated by the solver for each attempted set
// of decision values.
model.AddGoal("Goal", GoalKind.Minimize,
myRanker.LinearTrial(marshalWeights(model), false)
);
// PROBLEM 3! Should I use a directive, like SimplexDirective? What type of solver is best?
var solution = solver.Solve();
var report = solution.GetReport();
foreach (var d in model.Decisions)
{
Debug.WriteLine("Decision " + d.Name + ": " + d.ToDouble());
}
Debug.WriteLine(report);
// Enable/disable buttons.
UpdateButtons();
}
ОБНОВЛЕНИЕ: я решил искать другую библиотеку как запасной вариант и нашел DotNumerics ( http://dotnumerics.com/). Их решатель Nelder-Mead Simplex было легко назвать:
Simplex simplex = new Simplex()
{
MaxFunEvaluations = 20000,
Tolerance = 0.001
};
int numVariables = Parameters.Count();
OptBoundVariable[] variables = new OptBoundVariable[numVariables];
//Constrained Minimization on the intervals specified by the user, initial Guess = 1;
foreach (var x in Parameters.Select((parameter, index) => new { parameter, index }))
{
variables[x.index] = new OptBoundVariable(x.parameter.Key, 1, x.parameter.Value.Item1, x.parameter.Value.Item2);
}
double[] minimum = simplex.ComputeMin(ObjectiveFunction, variables);
Debug.WriteLine("Simplex Method. Constrained Minimization.");
for (int i = 0; i < minimum.Length; i++)
Debug.WriteLine(Parameters[i].Key + " = " + minimum[i].ToString());
Все, что мне было нужно, это реализовать ObjectiveFunction как метод, принимающий двойной массив:
private double ObjectiveFunction(double[] weights)
{
return Ranker.LinearTrial(weights, false);
}
Я не пробовал это на реальных данных, но я создал симуляцию в Excel для настройки тестовых данных и оценки. Результаты, полученные из их алгоритма, не были идеальными, но дали очень хорошее решение.
1 ответ
Вот мое резюме TL;DR: он не знает, как минимизировать возвращаемое значение LinearTrial
, который принимает массив двойников. Каждое значение в этом массиве имеет свое собственное минимальное / максимальное значение, и он моделирует это, используя Decisions
,
Если это правильно, кажется, вы могли бы просто сделать следующее:
double[] minimums = Parameters.Select(p => p.Value.Item1).ToArray();
double[] maximums = Parameters.Select(p => p.Value.Item2).ToArray();
// Some initial values, here it's a quick and dirty average
double[] initials = Parameters.Select(p => (p.Item1 + p.Item2)/2.0).ToArray();
var solution = NelderMeadSolver.Solve(
x => myRanker.LinearTrial(x, false), initials, minimums, maximums);
// Make sure you check solution.Result to ensure that it found a solution.
// For this, I'll assume it did.
// Value 0 is the minimized value of LinearTrial
int i = 1;
foreach (var param in Parameters)
{
Console.WriteLine("{0}: {1}", param.Key, solution.GetValue(i));
i++;
}
NelderMeadSolver является новым в MSF 3.0. Solve
Статический метод "находит минимальное значение указанной функции" в соответствии с документацией в сборке MSF (несмотря на то, что документация MSDN пуста и показывает неверную сигнатуру функции).
Отказ от ответственности: я не эксперт MSF, но вышесказанное сработало для меня и моей целевой функции теста (сумма весов).