Disciplines · Integrations

Civitai LoRA Training Guide

The Civitai training integration supports:

16sections4 minread

On this page

This guide covers training custom LoRAs using the Civitai training API for both image and video generation models.

Overview#

The Civitai training integration supports:

  • Image LoRAs: Character, style, and object training on SD1.5/SDXL/Flux
  • Video LoRAs: Motion training on Wan and Hunyuan video models
  • Training Presets: Pre-configured hyperparameters for common use cases
  • Progress Monitoring: Real-time loss tracking and sample generation
  • Cost Estimation: Accurate training cost predictions

Quick Start#

Basic Training Example#

typescript
import {
  LoraTrainingProvider,
  createLoraTrainingProvider,
} from '@oshun/civitai-training';

const training = createLoraTrainingProvider(process.env.CIVITAI_API_KEY);

// Train a character LoRA
const job = await training.trainCharacterLoRA(
  'my-character', // name
  images, // dataset images
  'mycharacter', // trigger word
  { epochs: 15 } // optional customization
);

console.log('Training started:', job.id);
console.log('Estimated cost:', job.cost, 'Buzz');

Dataset Preparation#

Image Dataset#

typescript
import type { DatasetImage, DatasetConfig } from '@oshun/civitai-training';

// Prepare images
const images: DatasetImage[] = [
  {
    image: 'https://example.com/image1.jpg', // URL or base64
    caption: 'mycharacter, woman, portrait, looking at camera',
    repeats: 5, // higher repeats for important images
  },
  {
    image: 'https://example.com/image2.jpg',
    caption: 'mycharacter, woman, full body, standing',
    repeats: 3,
  },
  // Add 10-50 images for best results
];

// Dataset configuration
const dataset: DatasetConfig = {
  name: 'my-character-dataset',
  images,
  triggerWord: 'mycharacter',
  classWord: 'woman', // for regularization
  captionMode: 'caption', // 'tag', 'caption', or 'auto'
  autoCaption: false, // use AI captioning
  resolution: 1024, // training resolution
  bucketResolutionSteps: 64, // aspect ratio bucketing
  enableCropping: true,
  flipProbability: 0.5, // horizontal flip augmentation
};

Video Dataset#

typescript
import type { DatasetVideo } from '@oshun/civitai-training';

const videos: DatasetVideo[] = [
  {
    video: 'https://example.com/clip1.mp4',
    caption: 'smooth camera pan, cinematic motion',
    startTime: 0,
    endTime: 4, // seconds
  },
  {
    video: 'https://example.com/clip2.mp4',
    caption: 'zooming in, dynamic movement',
  },
  // Add 10+ clips for motion LoRAs
];

Dataset Guidelines#

Training Type Min Images Recommended Notes
Character 10 20-50 Varied poses, angles, lighting
Style 20 50-100 Consistent style across subjects
Object 15 30-60 Multiple angles and contexts
Motion (Video) 10 clips 30+ clips 2-5 second clips

Caption Modes#

Mode Description Best For
tag Comma-separated tags Anime, stylized content
caption Natural language descriptions Realistic, detailed content
auto AI-generated captions Large datasets

Training Presets#

Available Presets#

Preset Base Model Use Case Rank Epochs
character-sdxl SDXL Character/person training 128 15
style-sdxl SDXL Art style training 32 10
object-sdxl SDXL Object/product training 64 20
character-flux Flux Dev Character on Flux 64 10
style-flux Flux Dev Style on Flux 16 8
motion-wan Wan 2.1 Video motion training 64 20
character-hunyuan Hunyuan Video Character for video 64 15
fast-test SDXL Quick testing 8 3

Using Presets#

typescript
// Train with preset
const job = await training.startTrainingWithPreset(
  'character-sdxl', // preset name
  dataset, // dataset config
  'my-character-lora', // training name
  {
    // Optional: override specific settings
    epochs: 20,
    learningRate: {
      unetLR: 0.00005,
    },
  }
);

Preset Configurations#

typescript
// Character SDXL preset configuration
const characterSDXL = {
  network: { rank: 128, alpha: 64, dropout: 0.05 },
  learningRate: {
    unetLR: 0.00007,
    textEncoderLR: 0.000035,
    scheduler: 'cosine_with_warmup',
    warmup: 0.1,
  },
  optimizer: 'AdamW8bit',
  batchSize: 2,
  epochs: 15,
  mixedPrecision: 'bf16',
  gradientCheckpointing: true,
  xformers: true,
};

// Style SDXL preset configuration
const styleSDXL = {
  network: { rank: 32, alpha: 16, dropout: 0.1 },
  learningRate: {
    unetLR: 0.0001,
    textEncoderLR: 0.00005,
    scheduler: 'cosine',
    warmup: 0.05,
  },
  optimizer: 'AdamW8bit',
  batchSize: 4,
  epochs: 10,
  mixedPrecision: 'bf16',
};

Custom Training Configuration#

Full Configuration Example#

typescript
import type {
  TrainingConfig,
  TrainingHyperparams,
} from '@oshun/civitai-training';

const hyperparams: TrainingHyperparams = {
  network: {
    rank: 64, // Network dimension (4-256)
    alpha: 32, // Scaling factor (usually rank or rank/2)
    dropout: 0.05, // Dropout for regularization
    convRank: 32, // For LoCon (optional)
    convAlpha: 16, // For LoCon (optional)
  },
  learningRate: {
    unetLR: 0.0001, // U-Net learning rate
    textEncoderLR: 0.00005, // Text encoder learning rate
    scheduler: 'cosine_with_warmup',
    warmup: 0.1, // 10% warmup steps
    minLR: 0.00001, // Minimum LR at end
    numCycles: 1, // For cosine_with_restarts
  },
  optimizer: 'AdamW8bit', // Memory-efficient optimizer
  batchSize: 2,
  epochs: 15,
  maxSteps: undefined, // Override epochs with fixed steps
  gradientAccumulationSteps: 1,
  mixedPrecision: 'bf16', // bf16 for newer GPUs, fp16 otherwise
  gradientCheckpointing: true,
  xformers: true,
  seed: 42,
  cacheLatents: true,
  noiseOffset: 0.1, // For better blacks/whites
  priorPreservationWeight: 1.0, // For DreamBooth
};

const config: TrainingConfig = {
  name: 'my-custom-lora',
  baseModel: 'sdxl',
  trainingType: 'lora',
  dataset,
  hyperparams,
  outputPath: '/trained-loras/',
  callbackUrl: 'https://api.myapp.com/webhooks/training',
  saveEveryNEpochs: 5,
  sampleEveryNEpochs: 5,
  samplePrompts: [
    'mycharacter, portrait, high quality',
    'mycharacter, full body, standing',
  ],
};

const job = await training.startTraining(config);

Network Parameters#

Parameter Range Description
rank 4-256 Network dimension (higher = more capacity)
alpha 1-rank Scaling factor (common: rank/2 or rank)
dropout 0-0.3 Regularization (0.05-0.1 recommended)
convRank 4-64 Convolutional rank for LoCon

Learning Rate Guidelines#

Base Model U-Net LR Text Encoder LR
SD 1.5 1e-4 - 5e-4 5e-5 - 2e-4
SDXL 5e-5 - 1e-4 2.5e-5 - 5e-5
Flux 5e-5 - 2e-4 2.5e-5 - 1e-4

Schedulers#

Scheduler Description Use Case
constant Fixed learning rate Short training
constant_with_warmup Warmup then constant General purpose
linear Linear decay Long training
cosine Cosine decay Most recommended
cosine_with_restarts Periodic restarts Very long training
polynomial Polynomial decay Fine-tuning

Optimizers#

Optimizer VRAM Speed Description
AdamW High Fast Standard optimizer
AdamW8bit Low Fast Memory-efficient (recommended)
SGD Low Medium Simple, stable
Lion Medium Fast Newer, efficient
Prodigy Medium Fast Adaptive LR
DAdaptAdam Medium Medium Self-tuning LR

Supported Base Models#

Image Models#

Model ID Name Min VRAM Notes
sd15 SD 1.5 6GB Fast, broad compatibility
sd21 SD 2.1 8GB Improved quality
sdxl SDXL 1.0 12GB High quality, recommended
flux-dev Flux Dev 24GB Best quality
flux-schnell Flux Schnell 20GB Faster Flux
sd35-medium SD 3.5 Medium 16GB Latest SD
sd35-large SD 3.5 Large 24GB Best SD 3.5
pony-v6 Pony V6 12GB Anime/stylized

Video Models#

Model ID Name Min VRAM Notes
hunyuan-video Hunyuan Video 24GB Character LoRAs
wan-2.1 Wan 2.1 24GB Motion LoRAs
wan-2.2 Wan 2.2 24GB Improved Wan

Training Helpers#

Character LoRA#

typescript
// Quick character training
const job = await training.trainCharacterLoRA('john-doe', images, 'johndoe', {
  epochs: 20,
  network: { rank: 128 },
});

Style LoRA#

typescript
// Style training with lower rank
const job = await training.trainStyleLoRA(
  'watercolor-style',
  images,
  'watercolor',
  {
    network: { rank: 32 },
  }
);

Motion LoRA#

typescript
// Video motion training
const job = await training.trainMotionLoRA(
  'smooth-pan',
  videoClips,
  'smoothpan',
  {
    epochs: 25,
  }
);

Hunyuan Character LoRA#

typescript
// Character for Hunyuan video
const job = await training.trainHunyuanLoRA(
  'my-character-hunyuan',
  images,
  'mychar'
);

Progress Monitoring#

Job Status#

typescript
// Get job by ID
const job = training.getJob(jobId);

console.log('Status:', job.status);
console.log('Progress:', job.progress?.progress * 100, '%');
console.log('Current epoch:', job.progress?.currentEpoch);
console.log('Current step:', job.progress?.currentStep);
console.log('Loss:', job.progress?.loss.currentLoss);

Job Status Flow#

text
queued → preparing → training → completed
                             → failed
                             → cancelled

Event Handling#

typescript
// Listen for training events
training.on('training:started', ({ job }) => {
  console.log('Training started:', job.id);
});

training.on('training:progress', ({ jobId, progress }) => {
  console.log(`${jobId}: ${progress.progress * 100}%`);
  console.log(`Loss: ${progress.loss.currentLoss}`);
  console.log(`ETA: ${progress.estimatedTimeRemaining}s`);
});

training.on('training:sample', ({ jobId, sample }) => {
  console.log(`Sample generated at step ${sample.step}:`);
  console.log(`URL: ${sample.imageUrl}`);
});

training.on('training:checkpoint', ({ jobId, checkpoint }) => {
  console.log(`Checkpoint saved at epoch ${checkpoint.epoch}`);
  console.log(`Download: ${checkpoint.downloadUrl}`);
});

training.on('training:completed', ({ job }) => {
  console.log('Training completed!');
  console.log('LoRA URL:', job.loraUrl);
});

training.on('training:failed', ({ jobId, error }) => {
  console.error('Training failed:', error);
});

Loss Metrics#

typescript
const progress = job.progress;

// Loss tracking
console.log('Current loss:', progress.loss.currentLoss);
console.log('Average loss:', progress.loss.averageLoss);
console.log('Best loss:', progress.loss.bestLoss);
console.log('Best loss step:', progress.loss.bestLossStep);

// Loss history
for (const point of progress.loss.lossHistory) {
  console.log(`Step ${point.step}: ${point.loss}`);
}

Waiting for Completion#

typescript
// Wait with default timeout
const completedJob = await training.waitForTraining(jobId);
console.log('LoRA URL:', completedJob.loraUrl);

// Wait with custom timeout (2 hours)
const job = await training.waitForTraining(jobId, 7200000);

Cost Estimation#

Calculating Training Cost#

typescript
import {
  estimateTrainingCost,
  calculateTotalSteps,
} from '@oshun/civitai-training';

// Calculate total steps
const totalSteps = calculateTotalSteps(
  50, // imageCount
  5, // repeats per image
  2, // batchSize
  15 // epochs
);
// (50 * 5) / 2 * 15 = 1875 steps

// Estimate cost
const cost = estimateTrainingCost('sdxl', totalSteps);
console.log('Estimated cost:', cost, 'Buzz');

Cost Per 1000 Steps#

Base Model Cost/1000 Steps
SD 1.5 50 Buzz
SD 2.1 50 Buzz
SDXL 100 Buzz
Flux Dev 200 Buzz
Flux Schnell 150 Buzz
SD 3.5 Medium 120 Buzz
SD 3.5 Large 180 Buzz
Pony V6 100 Buzz
Hunyuan Video 300 Buzz
Wan 2.1 300 Buzz
Wan 2.2 350 Buzz

Time Estimation#

typescript
import { estimateTrainingTime } from '@oshun/civitai-training';

// Estimate time (default 0.5 steps/second)
const timeSeconds = estimateTrainingTime(1875);
console.log('Estimated time:', timeSeconds / 60, 'minutes');

// With custom speed
const timeWithSpeed = estimateTrainingTime(1875, 0.8);

Validation#

Dataset Validation#

typescript
import { validateDataset } from '@oshun/civitai-training';

const validation = validateDataset(dataset);

if (!validation.valid) {
  console.error('Dataset errors:', validation.errors);
  // [
  //   'Minimum 4 images required for effective training',
  //   'Dataset must contain at least one image or video'
  // ]
}

Hyperparameter Validation#

typescript
import { validateHyperparams } from '@oshun/civitai-training';

const validation = validateHyperparams(hyperparams);

if (!validation.valid) {
  console.error('Errors:', validation.errors);
}

if (validation.warnings.length > 0) {
  console.warn('Warnings:', validation.warnings);
  // [
  //   'Alpha is typically equal to or less than rank',
  //   'More than 50 epochs may lead to overfitting'
  // ]
}

Optimal Learning Rate#

typescript
import { calculateOptimalLR } from '@oshun/civitai-training';

// Calculate optimal LR based on rank
const optimalLR = calculateOptimalLR(
  128, // rank
  0.0001 // base LR
);
// Scales based on rank^-0.84

Job Management#

Cancelling Training#

typescript
await training.cancelJob(jobId);

Getting All Jobs#

typescript
const allJobs = training.getAllJobs();

for (const job of allJobs) {
  console.log(`${job.id}: ${job.status}`);
  if (job.progress) {
    console.log(`  Progress: ${job.progress.progress * 100}%`);
  }
}

Downloading Results#

typescript
// Download completed LoRA
const { url, filename } = await training.downloadLoRA(jobId);
console.log('Download URL:', url);
console.log('Filename:', filename);

Best Practices#

Dataset Preparation#

  1. Quality over quantity: 20 high-quality images > 100 poor ones
  2. Variety: Include different poses, angles, lighting, backgrounds
  3. Consistency: Keep style/subject consistent within dataset
  4. Resolution: Use images at or above training resolution
  5. Captions: Detailed captions improve quality significantly

Hyperparameter Tuning#

  1. Start with presets: Use presets as baseline
  2. Rank selection: Higher rank = more capacity but risk of overfitting
  3. Learning rate: Start low, increase if underfitting
  4. Epochs: Monitor loss, stop if it increases
  5. Batch size: Larger batches = more stable, but need more VRAM

Training Monitoring#

  1. Watch the loss curve: Should decrease smoothly
  2. Check samples: Visual quality is ultimate measure
  3. Look for overfitting: Loss goes up or samples look identical
  4. Save checkpoints: Keep intermediate checkpoints

Common Issues#

Issue Symptoms Solution
Overfitting Samples look identical to training images Reduce epochs, increase dropout
Underfitting LoRA has no effect Increase rank, LR, or epochs
Catastrophic forgetting Model loses base capabilities Lower LR, use regularization
Mode collapse Limited variety in outputs Increase dataset variety

Error Handling#

Error Codes#

Code Description
AUTH_FAILED Invalid API key
INSUFFICIENT_BUZZ Not enough Buzz balance
INVALID_DATASET Dataset validation failed
INVALID_CONFIG Invalid hyperparameters
TRAINING_FAILED Training failed on server
JOB_NOT_FOUND Job doesn't exist
TIMEOUT Training timed out

Error Handling Example#

typescript
import { LoraTrainingError } from '@oshun/civitai-training';

try {
  const job = await training.startTraining(config);
  const completed = await training.waitForTraining(job.id);
} catch (error) {
  if (error instanceof LoraTrainingError) {
    switch (error.code) {
      case 'INSUFFICIENT_BUZZ':
        console.error('Not enough Buzz. Please top up.');
        break;
      case 'INVALID_DATASET':
        console.error('Dataset issues:', error.details);
        break;
      case 'TRAINING_FAILED':
        console.error('Training failed:', error.message);
        break;
      default:
        console.error('Error:', error.message);
    }
  }
}

Lifecycle Management#

Shutdown#

typescript
// Graceful shutdown - cancels running jobs
await training.shutdown();

TypeScript Types#

typescript
import type {
  // Dataset Types
  DatasetImage,
  DatasetVideo,
  DatasetConfig,
  DatasetStats,

  // Training Configuration
  TrainingConfig,
  TrainingHyperparams,
  NetworkParams,
  LearningRateParams,

  // Base Models
  TrainingBaseModel,
  TrainingType,
  TrainingPreset,

  // Job Types
  TrainingJob,
  TrainingStatus,
  TrainingProgress,

  // Progress Types
  LossMetrics,
  TrainingSample,
  TrainingCheckpoint,

  // Configuration
  LoraTrainingConfig,
  OptimizerType,
  LRScheduler,
  CaptionMode,
} from '@oshun/civitai-training';