mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-09 11:28:14 +08:00
Fix bug when given inf objective value
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import json
|
||||
import math
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from optuna.distributions import BaseDistribution
|
||||
from optuna.study import StudySummary
|
||||
@@ -27,7 +29,7 @@ IntermediateValue = TypedDict(
|
||||
"IntermediateValue",
|
||||
{
|
||||
"step": int,
|
||||
"value": float,
|
||||
"value": Union[float, str],
|
||||
},
|
||||
)
|
||||
TrialParam = TypedDict(
|
||||
@@ -53,7 +55,10 @@ def serialize_attrs(attrs: Dict[str, Any]) -> List[Attribute]:
|
||||
|
||||
|
||||
def serialize_intermediate_values(values: Dict[int, float]) -> List[IntermediateValue]:
|
||||
return [{"step": step, "value": value} for step, value in values.items()]
|
||||
return [
|
||||
{"step": step, "value": "inf" if math.isinf(value) else value}
|
||||
for step, value in values.items()
|
||||
]
|
||||
|
||||
|
||||
def serialize_trial_params(params: Dict[str, Any]) -> List[TrialParam]:
|
||||
@@ -120,7 +125,7 @@ def serialize_frozen_trial(study_id: int, trial: FrozenTrial) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
if trial.values is not None:
|
||||
serialized["values"] = trial.values
|
||||
serialized["values"] = ["inf" if math.isinf(v) else v for v in trial.values]
|
||||
|
||||
if trial.datetime_start is not None:
|
||||
serialized["datetime_start"] = trial.datetime_start.isoformat()
|
||||
|
||||
@@ -7,7 +7,7 @@ interface TrialResponse {
|
||||
study_id: number
|
||||
number: number
|
||||
state: TrialState
|
||||
values?: number[]
|
||||
values?: (number | "inf")[]
|
||||
intermediate_values: TrialIntermediateValue[]
|
||||
datetime_start?: string
|
||||
datetime_complete?: string
|
||||
|
||||
@@ -64,15 +64,19 @@ export const Edf: FC<{
|
||||
)
|
||||
}
|
||||
|
||||
const filterFunc = (trial: Trial, objectiveId: number): boolean => {
|
||||
return trial.state !== "Complete" || trial.values![objectiveId] !== "inf"
|
||||
}
|
||||
|
||||
const plotEdf = (study: StudyDetail, objectiveId: number, mode: string) => {
|
||||
if (document.getElementById(plotDomId) === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const trials: Trial[] = study ? study.trials : []
|
||||
const completedTrials = trials.filter((t) => t.state === "Complete")
|
||||
const filteredTrials = trials.filter((t) => filterFunc(t, objectiveId))
|
||||
|
||||
if (completedTrials.length === 0) {
|
||||
if (filteredTrials.length === 0) {
|
||||
plotly.react(plotDomId, [])
|
||||
return
|
||||
}
|
||||
@@ -80,7 +84,7 @@ const plotEdf = (study: StudyDetail, objectiveId: number, mode: string) => {
|
||||
const target_name = "Objective Value"
|
||||
|
||||
const target = (t: Trial): number => {
|
||||
return t.values![objectiveId]
|
||||
return t.values![objectiveId] as number
|
||||
}
|
||||
|
||||
const layout: Partial<plotly.Layout> = {
|
||||
@@ -99,7 +103,7 @@ const plotEdf = (study: StudyDetail, objectiveId: number, mode: string) => {
|
||||
template: mode === "dark" ? plotlyDarkTemplate : {},
|
||||
}
|
||||
|
||||
const values = completedTrials.map((t) => target(t))
|
||||
const values = filteredTrials.map((t) => target(t))
|
||||
const numValues = values.length
|
||||
const minX = Math.min(...values)
|
||||
const maxX = Math.max(...values)
|
||||
|
||||
@@ -168,6 +168,18 @@ export const GraphHistory: FC<{
|
||||
)
|
||||
}
|
||||
|
||||
const filterFunc = (trial: Trial, objectiveId: number): boolean => {
|
||||
if (trial.state !== "Complete" && trial.state !== "Pruned") {
|
||||
return false
|
||||
}
|
||||
if (trial.values === undefined) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
trial.values.length > objectiveId && trial.values[objectiveId] !== "inf"
|
||||
)
|
||||
}
|
||||
|
||||
const plotHistory = (
|
||||
study: StudyDetail,
|
||||
objectiveId: number,
|
||||
@@ -198,11 +210,7 @@ const plotHistory = (
|
||||
template: mode === "dark" ? plotlyDarkTemplate : {},
|
||||
}
|
||||
|
||||
let filteredTrials = study.trials.filter(
|
||||
(t) =>
|
||||
t.state === "Complete" ||
|
||||
(t.state === "Pruned" && t.values && t.values.length > 0)
|
||||
)
|
||||
let filteredTrials = study.trials.filter((t) => filterFunc(t, objectiveId))
|
||||
if (filterCompleteTrial) {
|
||||
filteredTrials = filteredTrials.filter((t) => t.state !== "Complete")
|
||||
}
|
||||
@@ -215,22 +223,22 @@ const plotHistory = (
|
||||
}
|
||||
const trialsForLinePlot: Trial[] = []
|
||||
let currentBest: number | null = null
|
||||
filteredTrials.forEach((item) => {
|
||||
filteredTrials.forEach((t) => {
|
||||
if (currentBest === null) {
|
||||
currentBest = item.values![objectiveId]
|
||||
trialsForLinePlot.push(item)
|
||||
currentBest = t.values![objectiveId] as number
|
||||
trialsForLinePlot.push(t)
|
||||
} else if (
|
||||
study.directions[objectiveId] === "maximize" &&
|
||||
item.values![objectiveId] > currentBest
|
||||
t.values![objectiveId] > currentBest
|
||||
) {
|
||||
currentBest = item.values![objectiveId]
|
||||
trialsForLinePlot.push(item)
|
||||
currentBest = t.values![objectiveId] as number
|
||||
trialsForLinePlot.push(t)
|
||||
} else if (
|
||||
study.directions[objectiveId] === "minimize" &&
|
||||
item.values![objectiveId] < currentBest
|
||||
t.values![objectiveId] < currentBest
|
||||
) {
|
||||
currentBest = item.values![objectiveId]
|
||||
trialsForLinePlot.push(item)
|
||||
currentBest = t.values![objectiveId] as number
|
||||
trialsForLinePlot.push(t)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -245,14 +253,16 @@ const plotHistory = (
|
||||
const xForLinePlot = trialsForLinePlot.map(getAxisX)
|
||||
xForLinePlot.push(getAxisX(filteredTrials[filteredTrials.length - 1]))
|
||||
const yForLinePlot = trialsForLinePlot.map(
|
||||
(t: Trial): number => t.values![objectiveId]
|
||||
(t: Trial): number => t.values![objectiveId] as number
|
||||
)
|
||||
yForLinePlot.push(yForLinePlot[yForLinePlot.length - 1])
|
||||
|
||||
const plotData: Partial<plotly.PlotData>[] = [
|
||||
{
|
||||
x: filteredTrials.map(getAxisX),
|
||||
y: filteredTrials.map((t: Trial): number => t.values![objectiveId]),
|
||||
y: filteredTrials.map(
|
||||
(t: Trial): number => t.values![objectiveId] as number
|
||||
),
|
||||
mode: "markers",
|
||||
type: "scatter",
|
||||
},
|
||||
|
||||
@@ -54,9 +54,10 @@ const plotIntermediateValue = (trials: Trial[], mode: string) => {
|
||||
(t.state === "Pruned" && t.values && t.values.length > 0)
|
||||
)
|
||||
const plotData: Partial<plotly.PlotData>[] = filteredTrials.map((trial) => {
|
||||
const values = trial.intermediate_values.filter((iv) => iv.value !== "inf")
|
||||
return {
|
||||
x: trial.intermediate_values.map((iv) => iv.step),
|
||||
y: trial.intermediate_values.map((iv) => iv.value),
|
||||
x: values.map((iv) => iv.step),
|
||||
y: values.map((iv) => iv.value),
|
||||
mode: "lines+markers",
|
||||
type: "scatter",
|
||||
name: `trial #${trial.number}`,
|
||||
|
||||
@@ -65,6 +65,18 @@ export const GraphParallelCoordinate: FC<{
|
||||
)
|
||||
}
|
||||
|
||||
const filterFunc = (trial: Trial, objectiveId: number): boolean => {
|
||||
if (trial.state !== "Complete" && trial.state !== "Pruned") {
|
||||
return false
|
||||
}
|
||||
if (trial.values === undefined) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
trial.values.length > objectiveId && trial.values[objectiveId] !== "inf"
|
||||
)
|
||||
}
|
||||
|
||||
const plotCoordinate = (
|
||||
study: StudyDetail,
|
||||
objectiveId: number,
|
||||
@@ -89,11 +101,7 @@ const plotCoordinate = (
|
||||
return
|
||||
}
|
||||
|
||||
const filteredTrials = study.trials.filter(
|
||||
(t) =>
|
||||
t.state === "Complete" ||
|
||||
(t.state === "Pruned" && t.values && t.values.length > 0)
|
||||
)
|
||||
const filteredTrials = study.trials.filter((t) => filterFunc(t, objectiveId))
|
||||
|
||||
const maxLabelLength = 40
|
||||
const breakLength = maxLabelLength / 2
|
||||
@@ -115,7 +123,7 @@ const plotCoordinate = (
|
||||
|
||||
// Intersection param names
|
||||
const objectiveValues: number[] = filteredTrials.map(
|
||||
(t) => t.values![objectiveId]
|
||||
(t) => t.values![objectiveId] as number
|
||||
)
|
||||
const dimensions = [
|
||||
{
|
||||
|
||||
@@ -85,6 +85,10 @@ export const GraphParetoFront: FC<{
|
||||
)
|
||||
}
|
||||
|
||||
const filterFunc = (trial: Trial): boolean => {
|
||||
return trial.state !== "Complete" || trial.values!.every((v) => v !== "inf")
|
||||
}
|
||||
|
||||
const plotParetoFront = (
|
||||
study: StudyDetail,
|
||||
objectiveXId: number,
|
||||
@@ -106,18 +110,20 @@ const plotParetoFront = (
|
||||
}
|
||||
|
||||
const trials: Trial[] = study ? study.trials : []
|
||||
const completedTrials = trials.filter((t) => t.state === "Complete")
|
||||
const filteredTrials = trials.filter(filterFunc)
|
||||
|
||||
if (completedTrials.length === 0) {
|
||||
if (filteredTrials.length === 0) {
|
||||
plotly.react(plotDomId, [], layout)
|
||||
return
|
||||
}
|
||||
|
||||
const normalizedValues: number[][] = []
|
||||
completedTrials.forEach((t) => {
|
||||
filteredTrials.forEach((t) => {
|
||||
if (t.values && t.values.length === study.directions.length) {
|
||||
const trialValues = t.values.map((v: number, i: number) => {
|
||||
return study.directions[i] === "minimize" ? v : -v
|
||||
const trialValues = t.values.map((v, i) => {
|
||||
return study.directions[i] === "minimize"
|
||||
? (v as number)
|
||||
: (-v as number)
|
||||
})
|
||||
normalizedValues.push(trialValues)
|
||||
}
|
||||
@@ -144,11 +150,11 @@ const plotParetoFront = (
|
||||
const plotData: Partial<plotly.PlotData>[] = [
|
||||
{
|
||||
type: "scatter",
|
||||
x: completedTrials.map((t: Trial): number => {
|
||||
return t.values![objectiveXId]
|
||||
x: filteredTrials.map((t: Trial): number => {
|
||||
return t.values![objectiveXId] as number
|
||||
}),
|
||||
y: completedTrials.map((t: Trial): number => {
|
||||
return t.values![objectiveYId]
|
||||
y: filteredTrials.map((t: Trial): number => {
|
||||
return t.values![objectiveYId] as number
|
||||
}),
|
||||
mode: "markers",
|
||||
xaxis: "Objective X",
|
||||
@@ -156,7 +162,7 @@ const plotParetoFront = (
|
||||
marker: {
|
||||
color: pointColors,
|
||||
},
|
||||
text: completedTrials.map(
|
||||
text: filteredTrials.map(
|
||||
(t: Trial): string => `Trial (number=${t.number})`
|
||||
),
|
||||
hovertemplate: "%{text}<extra></extra>",
|
||||
|
||||
@@ -129,6 +129,25 @@ export const GraphSlice: FC<{
|
||||
)
|
||||
}
|
||||
|
||||
const filterFunc = (
|
||||
trial: Trial,
|
||||
objectiveId: number,
|
||||
selected: string | null
|
||||
): boolean => {
|
||||
if (trial.state !== "Complete" && trial.state !== "Pruned") {
|
||||
return false
|
||||
}
|
||||
if (trial.params.find((p) => p.name == selected) === undefined) {
|
||||
return false
|
||||
}
|
||||
if (trial.values === undefined) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
trial.values.length > objectiveId && trial.values[objectiveId] !== "inf"
|
||||
)
|
||||
}
|
||||
|
||||
const plotSlice = (
|
||||
trials: Trial[],
|
||||
objectiveId: number,
|
||||
@@ -164,11 +183,8 @@ const plotSlice = (
|
||||
template: mode === "dark" ? plotlyDarkTemplate : {},
|
||||
}
|
||||
|
||||
const filteredTrials = trials.filter(
|
||||
(t) =>
|
||||
(t.state === "Complete" ||
|
||||
(t.state === "Pruned" && t.values && t.values.length > 0)) &&
|
||||
t.params.find((p) => p.name == selected) !== undefined
|
||||
const filteredTrials = trials.filter((t) =>
|
||||
filterFunc(t, objectiveId, selected)
|
||||
)
|
||||
|
||||
if (filteredTrials.length === 0 || selected === null) {
|
||||
@@ -177,7 +193,7 @@ const plotSlice = (
|
||||
}
|
||||
|
||||
const objectiveValues: number[] = filteredTrials.map(
|
||||
(t) => t.values![objectiveId]
|
||||
(t) => t.values![objectiveId] as number
|
||||
)
|
||||
const valueStrings = filteredTrials.map((t) => {
|
||||
return t.params.find((p) => p.name == selected)!.value
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ type Distribution =
|
||||
|
||||
declare interface TrialIntermediateValue {
|
||||
step: number
|
||||
value: number
|
||||
value: number | "inf"
|
||||
}
|
||||
|
||||
declare interface TrialParam {
|
||||
@@ -50,7 +50,7 @@ declare interface Trial {
|
||||
study_id: number
|
||||
number: number
|
||||
state: TrialState
|
||||
values?: number[]
|
||||
values?: (number | "inf")[]
|
||||
intermediate_values: TrialIntermediateValue[]
|
||||
datetime_start?: Date
|
||||
datetime_complete?: Date
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import argparse
|
||||
import asyncio
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
@@ -84,6 +85,18 @@ def create_dummy_storage() -> optuna.storages.InMemoryStorage:
|
||||
|
||||
study.optimize(objective_single_dynamic, n_trials=50)
|
||||
|
||||
# Single objective study with 'inf' value
|
||||
study = optuna.create_study(study_name="single-inf", storage=storage)
|
||||
|
||||
def objective_single_inf(trial: optuna.Trial) -> float:
|
||||
x = trial.suggest_float("x", -10, 10)
|
||||
if x > 0:
|
||||
return math.inf
|
||||
else:
|
||||
return x ** 2
|
||||
|
||||
study.optimize(objective_single_inf, n_trials=50)
|
||||
|
||||
# Multi-objective study
|
||||
study = optuna.create_study(
|
||||
study_name="multi-objective",
|
||||
|
||||
Reference in New Issue
Block a user