Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 79 additions & 60 deletions AGENTS_DEV.md

Large diffs are not rendered by default.

16 changes: 10 additions & 6 deletions tensorbored/components/vz_line_chart2/symlog-scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,22 @@ import * as Plottable from 'plottable';
import {TfScale} from './tf-scale';

/**
* Symmetric log transformation: sign(x) * log10(|x| + 1)
* Symmetric log transformation: sign(x) * log10(|x|/c + 1)
* This handles zero and negative values gracefully.
* @param x Input value
* @param c Linear threshold: the region |x| < c is approximately linear (default 1)
*/
function symlog(x: number): number {
return Math.sign(x) * Math.log10(Math.abs(x) + 1);
function symlog(x: number, c: number = 1): number {
return Math.sign(x) * Math.log10(Math.abs(x) / c + 1);
}

/**
* Inverse of symmetric log: sign(y) * (10^|y| - 1)
* Inverse of symmetric log: sign(y) * c * (10^|y| - 1)
* @param y Transformed value
* @param c Linear threshold (must match the c used in symlog)
*/
function symexp(y: number): number {
return Math.sign(y) * (Math.pow(10, Math.abs(y)) - 1);
function symexp(y: number, c: number = 1): number {
return Math.sign(y) * c * (Math.pow(10, Math.abs(y)) - 1);
}

/**
Expand Down
14 changes: 14 additions & 0 deletions tensorbored/plugins/core/profile_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,12 @@ def create_profile(
tag_filter: str = "",
run_filter: str = "",
smoothing: float = 0.6,
symlog_linear_threshold: float = 1.0,
group_by: GroupByConfig | None = None,
y_axis_scale: AxisScale | None = None,
x_axis_scale: AxisScale | None = None,
tag_axis_scales: dict[str, TagAxisScale] | None = None,
tag_symlog_linear_thresholds: dict[str, float] | None = None,
) -> SerializedProfile:
"""Create a TensorBoard profile dictionary.

Expand All @@ -214,10 +216,14 @@ def create_profile(
tag_filter: Regex pattern to filter tags.
run_filter: Regex pattern to filter runs.
smoothing: Scalar smoothing value (0.0 to 0.999).
symlog_linear_threshold: Linear threshold for the symlog scale.
Controls the width of the linear region near zero. Default 1.0.
group_by: Run-grouping configuration.
y_axis_scale: Global Y-axis scale for scalar plots.
x_axis_scale: Global X-axis scale for scalar plots
(STEP/RELATIVE only).
tag_symlog_linear_thresholds: Per-tag symlog linear threshold
overrides. Example: ``{"train/loss": 10.0}``
tag_axis_scales: Per-tag axis scale overrides. Example::

{"train/loss": {"y": "log10"}}
Expand Down Expand Up @@ -289,6 +295,10 @@ def create_profile(
data["xAxisScale"] = x_axis_scale
if tag_axis_scales:
data["tagAxisScales"] = tag_axis_scales
if symlog_linear_threshold != 1.0:
data["symlogLinearThreshold"] = symlog_linear_threshold
if tag_symlog_linear_thresholds:
data["tagSymlogLinearThresholds"] = tag_symlog_linear_thresholds

return SerializedProfile(version=PROFILE_VERSION, data=data)

Expand Down Expand Up @@ -346,10 +356,12 @@ def set_default_profile(
tag_filter: str = "",
run_filter: str = "",
smoothing: float = 0.6,
symlog_linear_threshold: float = 1.0,
group_by: GroupByConfig | None = None,
y_axis_scale: AxisScale | None = None,
x_axis_scale: AxisScale | None = None,
tag_axis_scales: dict[str, TagAxisScale] | None = None,
tag_symlog_linear_thresholds: dict[str, float] | None = None,
) -> str:
"""Create and write a profile in one call.

Expand All @@ -371,10 +383,12 @@ def set_default_profile(
tag_filter=tag_filter,
run_filter=run_filter,
smoothing=smoothing,
symlog_linear_threshold=symlog_linear_threshold,
group_by=group_by,
y_axis_scale=y_axis_scale,
x_axis_scale=x_axis_scale,
tag_axis_scales=tag_axis_scales,
tag_symlog_linear_thresholds=tag_symlog_linear_thresholds,
)
return write_profile(logdir, profile)

Expand Down
12 changes: 12 additions & 0 deletions tensorbored/webapp/metrics/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ export const metricsScalarPartitionNonMonotonicXToggled = createAction(
'[Metrics] Metrics Setting Partition Non Monotonic X Toggled'
);

export const metricsChangeSymlogLinearThreshold = createAction(
'[Metrics] Metrics Setting Change Symlog Linear Threshold',
props<{symlogLinearThreshold: number}>()
);

export const metricsChangeImageBrightness = createAction(
'[Metrics] Metrics Setting Change Image Brightness',
props<{brightnessInMilli: number}>()
Expand Down Expand Up @@ -312,6 +317,11 @@ export const metricsTagXAxisScaleChanged = createAction(
props<{tag: string; scaleType: ScaleType}>()
);

export const metricsTagSymlogLinearThresholdChanged = createAction(
'[Metrics] Tag Symlog Linear Threshold Changed',
props<{tag: string; symlogLinearThreshold: number}>()
);

// TODO(jieweiwu): Delete after internal code is updated.
export const stepSelectorTimeSelectionChanged = timeSelectionChanged;

Expand Down Expand Up @@ -406,6 +416,8 @@ export const profileMetricsSettingsApplied = createAction(
string,
{yAxisScale: ScaleType; xAxisScale: ScaleType}
>;
symlogLinearThreshold?: number;
tagSymlogLinearThresholds?: Record<string, number>;
}>()
);

Expand Down
121 changes: 87 additions & 34 deletions tensorbored/webapp/metrics/effects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type StoredAxisScalesV1 = {
yAxisScale?: string;
xAxisScale?: string;
tagAxisScales?: Record<string, {y?: string; x?: string}>;
symlogLinearThreshold?: number;
tagSymlogLinearThresholds?: Record<string, number>;
};

type StoredSuperimposedCard = {
Expand Down Expand Up @@ -100,6 +102,8 @@ import {
getMetricsYAxisScale,
getMetricsXAxisScale,
getTagAxisScales,
getMetricsSymlogLinearThreshold,
getTagSymlogLinearThresholds,
getSuperimposedCardsWithMetadata,
} from '../store';
import {
Expand Down Expand Up @@ -713,50 +717,75 @@ export class MetricsEffects implements OnInitEffects {
actions.metricsChangeXAxisScale,
actions.metricsTagYAxisScaleChanged,
actions.metricsTagXAxisScaleChanged,
actions.metricsChangeSymlogLinearThreshold,
actions.metricsTagSymlogLinearThresholdChanged,
actions.profileMetricsSettingsApplied
),
debounceTime(200),
withLatestFrom(
this.store.select(getMetricsYAxisScale),
this.store.select(getMetricsXAxisScale),
this.store.select(getTagAxisScales)
this.store.select(getTagAxisScales),
this.store.select(getMetricsSymlogLinearThreshold),
this.store.select(getTagSymlogLinearThresholds)
),
tap(([, yScale, xScale, tagScales]) => {
const tagAxisScalesPayload: Record<string, {y?: string; x?: string}> =
{};
for (const [tag, scales] of Object.entries(tagScales)) {
const entry: {y?: string; x?: string} = {};
if (scales.yAxisScale !== ScaleType.LINEAR) {
entry.y = scaleTypeToName(scales.yAxisScale);
}
if (scales.xAxisScale !== ScaleType.LINEAR) {
entry.x = scaleTypeToName(scales.xAxisScale);
tap(
([
,
yScale,
xScale,
tagScales,
symlogThreshold,
tagSymlogThresholds,
]) => {
const tagAxisScalesPayload: Record<string, {y?: string; x?: string}> =
{};
for (const [tag, scales] of Object.entries(tagScales)) {
const entry: {y?: string; x?: string} = {};
if (scales.yAxisScale !== ScaleType.LINEAR) {
entry.y = scaleTypeToName(scales.yAxisScale);
}
if (scales.xAxisScale !== ScaleType.LINEAR) {
entry.x = scaleTypeToName(scales.xAxisScale);
}
if (entry.y || entry.x) {
tagAxisScalesPayload[tag] = entry;
}
}
if (entry.y || entry.x) {
tagAxisScalesPayload[tag] = entry;
const payload: StoredAxisScalesV1 = {
version: 1,
...(yScale !== ScaleType.LINEAR
? {yAxisScale: scaleTypeToName(yScale)}
: undefined),
...(xScale !== ScaleType.LINEAR
? {xAxisScale: scaleTypeToName(xScale)}
: undefined),
...(Object.keys(tagAxisScalesPayload).length > 0
? {tagAxisScales: tagAxisScalesPayload}
: undefined),
...(symlogThreshold !== 1
? {symlogLinearThreshold: symlogThreshold}
: undefined),
...(Object.keys(tagSymlogThresholds).length > 0
? {tagSymlogLinearThresholds: tagSymlogThresholds}
: undefined),
};
if (
payload.yAxisScale ||
payload.xAxisScale ||
payload.tagAxisScales ||
payload.symlogLinearThreshold ||
payload.tagSymlogLinearThresholds
) {
window.localStorage.setItem(
AXIS_SCALES_STORAGE_KEY,
JSON.stringify(payload)
);
} else {
window.localStorage.removeItem(AXIS_SCALES_STORAGE_KEY);
}
}
const payload: StoredAxisScalesV1 = {
version: 1,
...(yScale !== ScaleType.LINEAR
? {yAxisScale: scaleTypeToName(yScale)}
: undefined),
...(xScale !== ScaleType.LINEAR
? {xAxisScale: scaleTypeToName(xScale)}
: undefined),
...(Object.keys(tagAxisScalesPayload).length > 0
? {tagAxisScales: tagAxisScalesPayload}
: undefined),
};
if (payload.yAxisScale || payload.xAxisScale || payload.tagAxisScales) {
window.localStorage.setItem(
AXIS_SCALES_STORAGE_KEY,
JSON.stringify(payload)
);
} else {
window.localStorage.removeItem(AXIS_SCALES_STORAGE_KEY);
}
})
)
);

// Load axis scales from localStorage on navigation
Expand Down Expand Up @@ -804,6 +833,30 @@ export class MetricsEffects implements OnInitEffects {
}
}
}
if (
typeof parsed.symlogLinearThreshold === 'number' &&
parsed.symlogLinearThreshold > 0
) {
scaleActions.push(
actions.metricsChangeSymlogLinearThreshold({
symlogLinearThreshold: parsed.symlogLinearThreshold,
})
);
}
if (parsed.tagSymlogLinearThresholds) {
for (const [tag, threshold] of Object.entries(
parsed.tagSymlogLinearThresholds
)) {
if (typeof threshold === 'number' && threshold > 0) {
scaleActions.push(
actions.metricsTagSymlogLinearThresholdChanged({
tag,
symlogLinearThreshold: threshold,
})
);
}
}
}
return scaleActions;
} catch {
return [];
Expand Down
13 changes: 13 additions & 0 deletions tensorbored/webapp/metrics/metrics_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
getMetricsLinkedTimeEnabled,
getMetricsRangeSelectionEnabled,
getMetricsScalarSmoothing,
getMetricsSymlogLinearThreshold,
getMetricsStepSelectorEnabled,
getMetricsTooltipSort,
getMetricsSavingPinsEnabled,
Expand Down Expand Up @@ -86,6 +87,15 @@ export function getSmoothingSettingFactory() {
});
}

export function getSymlogLinearThresholdSettingFactory() {
return createSelector(
getMetricsSymlogLinearThreshold,
(symlogLinearThreshold) => {
return {symlogLinearThreshold};
}
);
}

export function getMetricsIgnoreOutliersSettingFactory() {
return createSelector(getMetricsIgnoreOutliers, (ignoreOutliers) => {
return {ignoreOutliers};
Expand Down Expand Up @@ -168,6 +178,9 @@ export function getRangeSelectionHeadersFactory() {
PersistentSettingsConfigModule.defineGlobalSetting(
getSmoothingSettingFactory
),
PersistentSettingsConfigModule.defineGlobalSetting(
getSymlogLinearThresholdSettingFactory
),
PersistentSettingsConfigModule.defineGlobalSetting(
getMetricsIgnoreOutliersSettingFactory
),
Expand Down
Loading