Added validation loss
This commit is contained in:
@@ -199,6 +199,8 @@ export default function SimpleJob({
|
||||
|
||||
const showGPUSelect = !isMac();
|
||||
|
||||
const validationConfig = jobConfig.config.process[0].train.validation_config;
|
||||
|
||||
let numDatasetCols = 4;
|
||||
let numSampleTopCols = 4;
|
||||
let datasetStyleClass = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6';
|
||||
@@ -834,6 +836,136 @@ export default function SimpleJob({
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<div>
|
||||
<Card
|
||||
title="Validation"
|
||||
toggled={!!validationConfig}
|
||||
onToggle={value => {
|
||||
if (value) {
|
||||
setJobConfig(
|
||||
{
|
||||
validation_items: [{ image_path: '', prompt: '' }],
|
||||
resolution: 512,
|
||||
validate_every_n_steps: 10,
|
||||
validation_sigmas: [1.0, 0.75, 0.5, 0.25],
|
||||
},
|
||||
'config.process[0].train.validation_config',
|
||||
);
|
||||
} else {
|
||||
setJobConfig(undefined, 'config.process[0].train.validation_config');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{validationConfig && (
|
||||
<>
|
||||
<p className="text-sm text-gray-400 mb-4">
|
||||
Validation runs a stable loss check on a fixed set of images. Each image is encoded once at startup
|
||||
and predicted at the selected sigmas with fixed seeds, so the result is always deterministic and
|
||||
comparable across the run. The average loss is logged as val/loss every time validation runs. The
|
||||
images need to match the concept of your dataset, but{' '}
|
||||
<span className="font-bold text-gray-300">do not include the validation images in the dataset</span>.
|
||||
They must be images containing the concept you want to train, but not an image trained on.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<NumberInput
|
||||
label="Validate Every"
|
||||
value={validationConfig.validate_every_n_steps}
|
||||
onChange={value =>
|
||||
setJobConfig(value, 'config.process[0].train.validation_config.validate_every_n_steps')
|
||||
}
|
||||
placeholder="eg. 10"
|
||||
min={1}
|
||||
required
|
||||
/>
|
||||
<NumberInput
|
||||
label="Validation Resolution"
|
||||
value={validationConfig.resolution}
|
||||
onChange={value => setJobConfig(value, 'config.process[0].train.validation_config.resolution')}
|
||||
placeholder="eg. 512"
|
||||
min={64}
|
||||
required
|
||||
/>
|
||||
<SelectInput
|
||||
label="Validation Sigmas"
|
||||
value={(validationConfig.validation_sigmas ?? [1.0, 0.75, 0.5, 0.25]).join(', ')}
|
||||
onChange={value =>
|
||||
setJobConfig(
|
||||
value.split(',').map((v: string) => parseFloat(v)),
|
||||
'config.process[0].train.validation_config.validation_sigmas',
|
||||
)
|
||||
}
|
||||
options={[
|
||||
{ value: '0.5', label: '0.5' },
|
||||
{ value: '1, 0.5', label: '1.0, 0.5' },
|
||||
{ value: '1, 0.66, 0.33', label: '1.0, 0.66, 0.33' },
|
||||
{ value: '1, 0.75, 0.5, 0.25', label: '1.0, 0.75, 0.5, 0.25' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label className="block text-xs text-gray-300 mb-2">
|
||||
Validation Images ({validationConfig.validation_items.length})
|
||||
</label>
|
||||
{validationConfig.validation_items.map((item, i) => (
|
||||
<div key={i} className="rounded-lg pl-4 pr-1 py-3 mb-4 bg-gray-950">
|
||||
<div className="flex items-center space-x-4">
|
||||
<SampleControlImage
|
||||
instruction="Add Image"
|
||||
src={item.image_path === '' ? null : item.image_path}
|
||||
onNewImageSelected={imagePath => {
|
||||
setJobConfig(
|
||||
imagePath ?? '',
|
||||
`config.process[0].train.validation_config.validation_items[${i}].image_path`,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<TextInput
|
||||
label="Prompt"
|
||||
value={item.prompt}
|
||||
onChange={value =>
|
||||
setJobConfig(
|
||||
value,
|
||||
`config.process[0].train.validation_config.validation_items[${i}].prompt`,
|
||||
)
|
||||
}
|
||||
placeholder="Enter prompt"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setJobConfig(
|
||||
validationConfig.validation_items.filter((_, index) => index !== i),
|
||||
'config.process[0].train.validation_config.validation_items',
|
||||
)
|
||||
}
|
||||
className="rounded-full p-1 text-sm"
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setJobConfig(
|
||||
[...validationConfig.validation_items, { image_path: '', prompt: '' }],
|
||||
'config.process[0].train.validation_config.validation_items',
|
||||
)
|
||||
}
|
||||
className="w-full px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg transition-colors"
|
||||
>
|
||||
Add Validation Image
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
<div>
|
||||
<Card title="Advanced" collapsible>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
|
||||
@@ -7,9 +7,52 @@ interface CardProps {
|
||||
children?: React.ReactNode;
|
||||
collapsible?: boolean;
|
||||
defaultOpen?: boolean;
|
||||
// when provided, the card is opened/closed by a toggle switch on the right instead of a chevron
|
||||
toggled?: boolean;
|
||||
onToggle?: (value: boolean) => void;
|
||||
}
|
||||
|
||||
const Card: React.FC<CardProps> = ({ title, children, collapsible, defaultOpen }) => {
|
||||
const Card: React.FC<CardProps> = ({ title, children, collapsible, defaultOpen, toggled, onToggle }) => {
|
||||
if (onToggle) {
|
||||
return (
|
||||
<section className="space-y-2 px-4 pb-2 pt-2 bg-gray-900 rounded-lg">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1">
|
||||
{title && (
|
||||
<h2 className={classNames('text-lg font-semibold uppercase text-gray-500', toggled ? 'mb-2' : 'mb-0')}>
|
||||
{title}
|
||||
</h2>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={toggled}
|
||||
onClick={() => onToggle(!toggled)}
|
||||
className={classNames(
|
||||
'relative ml-2 inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2',
|
||||
toggled ? 'bg-blue-500' : 'bg-gray-600',
|
||||
'hover:bg-opacity-80',
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Toggle {title}</span>
|
||||
<span
|
||||
className={classNames(
|
||||
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
|
||||
toggled ? 'translate-x-5' : 'translate-x-0',
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{toggled && (
|
||||
<>
|
||||
{children ?? null}
|
||||
<div className="pt-2"></div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
if (collapsible) {
|
||||
return (
|
||||
<Disclosure as="section" className="space-y-2 px-4 pb-2 pt-2 bg-gray-900 rounded-lg" defaultOpen={defaultOpen}>
|
||||
|
||||
@@ -198,18 +198,26 @@ export const NumberInput = (props: NumberInputProps) => {
|
||||
|
||||
const numValue = Number(rawValue);
|
||||
|
||||
// Only apply constraints and call onChange when we have a valid number
|
||||
// don't clamp to min/max while typing, it mangles partial input (typing 1024 with
|
||||
// min 64 becomes 64024). Clamping happens on blur.
|
||||
if (!isNaN(numValue)) {
|
||||
let constrainedValue = numValue;
|
||||
|
||||
// Apply min/max constraints if they exist
|
||||
if (min !== undefined && constrainedValue < min) {
|
||||
constrainedValue = min;
|
||||
}
|
||||
if (max !== undefined && constrainedValue > max) {
|
||||
constrainedValue = max;
|
||||
}
|
||||
|
||||
onChange(numValue);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
const numValue = Number(inputValue);
|
||||
if (inputValue === '' || isNaN(numValue)) {
|
||||
return;
|
||||
}
|
||||
let constrainedValue = numValue;
|
||||
if (min !== undefined && constrainedValue < min) {
|
||||
constrainedValue = min;
|
||||
}
|
||||
if (max !== undefined && constrainedValue > max) {
|
||||
constrainedValue = max;
|
||||
}
|
||||
if (constrainedValue !== numValue) {
|
||||
setInputValue(constrainedValue);
|
||||
onChange(constrainedValue);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -117,6 +117,18 @@ export interface EMAConfig {
|
||||
ema_decay: number;
|
||||
}
|
||||
|
||||
export interface ValidationItem {
|
||||
image_path: string;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
export interface ValidationConfig {
|
||||
validation_items: ValidationItem[];
|
||||
resolution: number;
|
||||
validate_every_n_steps: number;
|
||||
validation_sigmas?: number[];
|
||||
}
|
||||
|
||||
export interface TrainConfig {
|
||||
batch_size: number;
|
||||
bypass_guidance_embedding?: boolean;
|
||||
@@ -151,6 +163,7 @@ export interface TrainConfig {
|
||||
differential_guidance_scale?: number;
|
||||
audio_loss_multiplier?: number;
|
||||
max_loss?: number | null;
|
||||
validation_config?: ValidationConfig;
|
||||
}
|
||||
|
||||
export interface QuantizeKwargsConfig {
|
||||
|
||||
Reference in New Issue
Block a user