Table of Contents

Multi-objective optimization

Real problems often trade off competing goals — a model that is both accurate and small, both fast and cheap. A multi-objective study optimizes several objectives at once and returns the set of best trade-offs (the Pareto front) rather than a single winner. This recipe mirrors Optuna's tutorial, which minimizes a model's size while maximizing its accuracy.

This is the how-to. For the concepts — dominance, the Pareto front, constrained MO, choosing a sampler — see the Multi-objective Optimization guide.

Define the objective

A multi-objective objective returns an IReadOnlyList<double>, one value per objective, in the order of the directions you declare:

using OptunaSharp;

IReadOnlyList<double> Objective(Trial trial)
{
    long  nLayers = trial.SuggestInt("n_layers", 1, 3);
    double lr     = trial.SuggestFloat("lr", 1e-5, 1e-1, log: true);

    var model = BuildAndTrain(nLayers, lr);   // your model here
    double size     = model.ParameterCount;   // minimize this…
    double accuracy = model.ValidationAccuracy; // …and maximize this
    return new[] { size, accuracy };
}

Run it

Pass directions (plural) instead of direction. Here we minimize size and maximize accuracy:

var study = Optuna.CreateStudy(
    directions: new[] { StudyDirection.Minimize, StudyDirection.Maximize });

study.Optimize(Objective, nTrials: 30, timeout: 300);

Console.WriteLine($"Number of finished trials: {study.Trials.Count}");

The default sampler for a multi-objective study is NSGAIISampler. For sample-efficient runs (nTrials below ~1000) the Samplers guide recommends TPESampler or GPSampler, both of which also support constrained multi-objective optimization.

Read the Pareto front

Study.BestTrials returns the FrozenTrials on the Pareto front — the non-dominated trade-offs. Each trial's Values holds its objective vector, so you can pick the one that suits you (say, the highest accuracy):

using System.Linq;

Console.WriteLine($"Trials on the Pareto front: {study.BestTrials.Count}");

FrozenTrial best = study.BestTrials.MaxBy(t => t.Values![1]);   // Values[1] = accuracy
Console.WriteLine($"  number: {best.Number}");
Console.WriteLine($"  params: {string.Join(", ", best.Params)}");
Console.WriteLine($"  values: {string.Join(", ", best.Values!)}");

Visualize

The visualization layer has a Pareto-front plot. Compute the data with the Core builder, or render it directly with the Plotly.NET satellite:

using OptunaSharp.Visualization;                 // Core data layer
ParetoFrontInfo info = ParetoFront.GetInfo(study, targetNames: new[] { "size", "accuracy" });

using OptunaSharp.Visualization.Plotly;          // OptunaSharp.Visualization.Plotly package
var fig = PlotlyVisualization.PlotParetoFront(study, targetNames: new[] { "size", "accuracy" });

Relationship to Optuna

Optuna (Python) OptunaSharp
optuna.create_study(directions=["minimize", "maximize"]) Optuna.CreateStudy(directions: new[] { StudyDirection.Minimize, StudyDirection.Maximize })
objective returns a tuple (flops, accuracy) objective returns IReadOnlyList<double>
study.best_trials study.BestTrials
max(study.best_trials, key=lambda t: t.values[1]) study.BestTrials.MaxBy(t => t.Values![1])
optuna.visualization.plot_pareto_front(study, …) PlotlyVisualization.PlotParetoFront(study, …)

See also

Multi-objective Optimization guide · Samplers · Visualization · Re-use the best trial · the API reference.