> ## Documentation Index
> Fetch the complete documentation index at: https://docs.krea.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Train a Custom Style

> Train custom image styles from your own datasets and generate consistent outputs with guidance on curation, tagging, and iteration best practices.

export const HeroHeader = ({image, video, title, description}) => {
  return <div className="relative aspect-[2/1] h-[30vh] w-full rounded-lg overflow-hidden mb-8">
      {}
      {video && <video autoPlay muted loop playsInline className="absolute top-0 left-0 w-full h-full object-cover hidden md:block m-0" style={{
    zIndex: 1,
    objectPosition: "20% 20%"
  }}>
          <source src={video} type="video/webm" />
        </video>}

      {}
      <img src={image} alt="" className={`absolute top-0 left-0 m-0 w-full h-full object-cover ${video ? "md:hidden" : "block"}`} style={{
    zIndex: 1,
    objectPosition: "20% 20%"
  }} />

      {}
      <div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/30 to-black/10 flex flex-col justify-end p-8 dark:hidden" style={{
    zIndex: 2
  }}>
        <h1 className="text-4xl font-bold text-white m-0 drop-shadow-[0_2px_4px_rgba(0,0,0,0.5)]">
          {title}
        </h1>
        <p className="text-lg text-white/95 mt-2 drop-shadow-[0_1px_2px_rgba(0,0,0,0.5)]">
          {description}
        </p>
      </div>

      {}
      <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-black/20 hidden dark:flex flex-col justify-end p-8" style={{
    zIndex: 2
  }}>
        <h1 className="text-4xl font-bold text-white m-0 drop-shadow-[0_2px_4px_rgba(0,0,0,0.5)]">
          {title}
        </h1>
        <p className="text-lg text-white/95 mt-2 drop-shadow-[0_1px_2px_rgba(0,0,0,0.5)]">
          {description}
        </p>
      </div>
    </div>;
};

<HeroHeader image="https://s.krea.ai/docs-lora.webp" title="Train a Custom Style (LoRA)" description="Train a custom style using your own images to generate AI art with consistent visual aesthetics." />

## Overview

LoRA (Low-Rank Adaptation) is a fine-tuning technique that teaches an AI model a new visual style using a small set of example images. It works by injecting new "style weights" into an existing model, making training both fast and efficient.

This guide walks through the complete workflow:

<Steps>
  <Step title="Prepare Training Images" icon="images">
    Curate and upload high-quality images that represent your desired style
  </Step>

  <Step title="Submit Training Job" icon="play">
    Send a POST request to `/styles/train` with your image URLs and configuration
  </Step>

  <Step title="Monitor Progress" icon="clock">
    Track your training job status using the returned `job_id`
  </Step>

  <Step title="Generate with Your Style" icon="sparkles">
    Apply your trained style to image generation
  </Step>
</Steps>

***

## Prepare Training Images

### Curating Your Dataset

The quality of your training images directly impacts results. Different training types have different requirements:

| Type          | Use Case                                 | Tips                                    |
| ------------- | ---------------------------------------- | --------------------------------------- |
| **Style**     | Artistic styles, visual aesthetics       | Consistent style across varied subjects |
| **Character** | Personal likeness, consistent characters | Varied poses, expressions, lighting     |
| **Object**    | Specific items, products                 | Multiple angles, consistent object      |

### How Many Images?

Quality matters far more than quantity. A small set of excellent images will outperform a large set of mediocre ones.

| Dataset Size     | Guidance                                               |
| ---------------- | ------------------------------------------------------ |
| **5 images**     | Minimum viable. Can work for simple, consistent styles |
| **10-30 images** | Recommended. Best balance of quality and coverage      |
| **50+ images**   | Diminishing returns unless style has high variation    |

<Warning>
  **Quality > Quantity**

  15 high-quality images will produce better results than 50 low-quality ones. Prioritize:

  * High resolution (1024x1024 minimum)
  * Consistent style across all images
  * No watermarks, text overlays, or compression artifacts
  * Varied subjects while maintaining style coherence
</Warning>

<Info>
  **Example Datasets**

  * **Character training**: Photos of a person with varied poses, expressions, and lighting conditions. Avoid including other people in the images.
  * **Style training**: A collection of artwork in a consistent style. For example, [The Metropolitan Museum of Art Ukiyo-E Dataset](https://www.kaggle.com/datasets/kengoichiki/the-metropolitan-museum-of-art-ukiyoe-dataset) provides Japanese woodblock prints ideal for training an artistic style.
</Info>

### Upload Images

Before training, upload your images to get hosted URLs. Use the `/assets` endpoint:

<CodeGroup>
  ```python Python theme={null}
  import requests
  import os
  from dotenv import load_dotenv
  import mimetypes

  load_dotenv()

  API_BASE = "https://api.krea.ai"
  API_TOKEN = os.getenv("API_TOKEN")

  image_dir = "training_images"
  uploaded_urls = []

  for filename in os.listdir(image_dir):
      if filename.lower().endswith(('.jpg', '.jpeg', '.png', '.webp')):
          filepath = os.path.join(image_dir, filename)
          mime_type, _ = mimetypes.guess_type(filepath)

          with open(filepath, 'rb') as f:
              response = requests.post(
                  f"{API_BASE}/assets",
                  headers={"Authorization": f"Bearer {API_TOKEN}"},
                  files={"file": (filename, f, mime_type)},
                  data={"description": f"Training image: {filename}"}
              )

          if response.ok:
              data = response.json()
              uploaded_urls.append(data["image_url"])
              print(f"Uploaded: {filename}")
          else:
              print(f"Failed: {filename}")

  print(f"\nUploaded {len(uploaded_urls)} images")
  ```

  ```bash cURL theme={null}
  # Upload a single image
  curl -X POST https://api.krea.ai/assets \
    -H "Authorization: Bearer YOUR_API_TOKEN" \
    -F "file=@/path/to/image.jpg" \
    -F "description=Training image"

  # Response: {"id": "...", "image_url": "https://..."}
  ```
</CodeGroup>

<Tip>
  Save the returned `image_url` values—you'll pass these to the training endpoint.
</Tip>

***

## Train Your Style

### Basic Training Example

Submit your image URLs to start training:

<CodeGroup>
  ```python Python theme={null}
  import requests
  import os
  from dotenv import load_dotenv

  load_dotenv()

  API_BASE = "https://api.krea.ai"
  API_TOKEN = os.getenv("API_TOKEN")

  # Training URLs from the upload step
  urls = [
      "https://krea.ai/assets/img1.jpg",
      "https://krea.ai/assets/img2.jpg",
      "https://krea.ai/assets/img3.jpg",
      # ... more images
  ]

  response = requests.post(
      f"{API_BASE}/styles/train",
      headers={
          "Authorization": f"Bearer {API_TOKEN}",
          "Content-Type": "application/json"
      },
      json={
          "name": "Ukiyo-E Style",
          "urls": urls,
          "model": "flux_dev",
          "type": "Style",
          "max_train_steps": 500
      }
  )

  response.raise_for_status()
  job = response.json()
  print(f"Training started! Job ID: {job['job_id']}")
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.krea.ai/styles/train \
    -H "Authorization: Bearer YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Ukiyo-E Style",
      "urls": [
        "https://krea.ai/assets/img1.jpg",
        "https://krea.ai/assets/img2.jpg",
        "https://krea.ai/assets/img3.jpg"
      ],
      "model": "flux_dev",
      "type": "Style",
      "max_train_steps": 500
    }'
  ```
</CodeGroup>

### Training Types

The `type` parameter sets intelligent defaults optimized for your use case:

| Type        | Best For                                 |
| ----------- | ---------------------------------------- |
| `Style`     | Artistic styles, visual aesthetics       |
| `Character` | Personal likeness, consistent characters |
| `Object`    | Specific items, products                 |
| `Default`   | Generic training                         |

### Parameters

#### Required Parameters

<ParamField path="name" type="string" required>
  A descriptive name for your custom style.

  **Example:** `"Ukiyo-E Style"`, `"Product Photos"`
</ParamField>

<ParamField path="urls" type="array" required>
  Array of image URLs to train on. Include more images for better results.
</ParamField>

#### Optional Parameters

<ParamField path="model" type="string" default="flux_dev">
  Base model for training:

  **Image models:**

  * `flux_dev` - High quality, versatile
  * `flux_schnell` - BFL's realtime model
  * `qwen` - Alibaba's model
  * `z-image` - Alibaba's efficient image model
  * `wan22` - Image generation only

  **Video models:**

  * `wan` - Alibaba's video model
</ParamField>

<ParamField path="type" type="string" default="Default">
  Training category: `Style`, `Object`, `Character`, or `Default`
</ParamField>

<ParamField path="trigger_word" type="string">
  Custom word to activate this style in prompts. When not specified, uses the style name.

  <Tip>Choose unique trigger words that won't appear in typical prompts. Use underscores for multi-word triggers: `ukiyo_style`</Tip>
</ParamField>

<Accordion title="Advanced Parameters" icon="sliders">
  <ParamField path="learning_rate" type="number">
    Controls training intensity. Higher values train faster but may overfit.

    **Recommended range:** 0.0001 - 0.001
  </ParamField>

  <ParamField path="max_train_steps" type="integer">
    Maximum training iterations. Range: 1-2000
  </ParamField>

  <ParamField path="batch_size" type="integer">
    Images processed simultaneously. Larger batches = faster training but more memory.
  </ParamField>
</Accordion>

### Tuning Advanced Parameters

Start with defaults set by the `type` field—they work well for most cases. Only adjust these if you're seeing specific issues:

<AccordionGroup>
  <Accordion title="Learning Rate" icon="gauge">
    Controls how aggressively the model adapts to your training images.

    | Value                     | When to Use                                        |
    | ------------------------- | -------------------------------------------------- |
    | **0.0001** (lower)        | Overfitting issues, complex styles, small datasets |
    | **0.0003** (default)      | Most use cases                                     |
    | **0.0005-0.001** (higher) | Faster training                                    |

    **Signs you need to adjust:**

    * Outputs look identical to training images → lower the rate
    * Style influence is weak after training → raise the rate slightly
  </Accordion>

  <Accordion title="Training Steps" icon="shoe-prints">
    How long the model trains on your images.

    | Dataset Size | Recommended Steps |
    | ------------ | ----------------- |
    | 5-10 images  | 300-500 steps     |
    | 15-30 images | 500-800 steps     |
    | 50+ images   | 800-1500 steps    |

    **Signs you need to adjust:**

    * Outputs are too rigid, ignoring prompts → reduce steps
    * Style influence is weak → increase steps
    * Generated images look exactly like training data → reduce steps (overfitting)
  </Accordion>
</AccordionGroup>

<Tip>
  **Iterative Tuning**

  If your first training attempt doesn't produce the results you want:

  1. Start by adjusting `max_train_steps` (most common fix)
  2. Then try `learning_rate` if steps alone don't help
</Tip>

### Response Format

```json theme={null}
{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued",
  "created_at": "2024-01-15T10:30:00Z"
}
```

***

## Monitor Training Progress

Training typically takes 5-15 minutes. Poll the Jobs API to check status:

<CodeGroup>
  ```python Python theme={null}
  import requests
  import time
  import os
  from dotenv import load_dotenv

  load_dotenv()

  API_BASE = "https://api.krea.ai"
  API_TOKEN = os.getenv("API_TOKEN")
  job_id = "your-job-id"

  while True:
      response = requests.get(
          f"{API_BASE}/jobs/{job_id}",
          headers={"Authorization": f"Bearer {API_TOKEN}"}
      )
      job = response.json()
      status = job["status"]

      print(f"Status: {status}")

      if status == "completed":
          style_id = job["result"]["style_id"]
          print(f"Training complete! Style ID: {style_id}")
          break
      elif status in ["failed", "cancelled"]:
          print(f"Training {status}")
          break

      time.sleep(30)
  ```

  ```bash cURL theme={null}
  # Check job status (repeat until completed)
  curl -X GET https://api.krea.ai/jobs/YOUR_JOB_ID \
    -H "Authorization: Bearer YOUR_API_TOKEN"
  ```
</CodeGroup>

<Accordion title="Job Status Values" icon="diagram-project">
  Training jobs progress through these states:

  1. **queued** - Waiting in queue
  2. **processing** - Active training
  3. **completed** - Training finished successfully
  4. **failed** - Training encountered an error
  5. **cancelled** - Job manually cancelled
</Accordion>

***

## Use Your Trained Style

Once training completes, apply your style to image generation using the `styles` parameter:

<Warning>
  **Style ownership between the app and API**

  The API and the Krea web app operate as separate user identities within your workspace. Styles are private to the user who created them, so:

  * **App-trained styles** are not accessible via the API unless shared
  * **API-trained styles** are not accessible in the app unless shared

  To share a style with your workspace (works in both directions):

  ```bash theme={null}
  curl -X POST https://api.krea.ai/styles/YOUR_STYLE_ID/share/workspace \
    -H "Authorization: Bearer YOUR_API_TOKEN"
  ```
</Warning>

<CodeGroup>
  ```python Python theme={null}
  import requests
  import time
  import os
  from dotenv import load_dotenv

  load_dotenv()

  API_BASE = "https://api.krea.ai"
  API_TOKEN = os.getenv("API_TOKEN")
  STYLE_ID = "w29t6pvy0"

  response = requests.post(
      f"{API_BASE}/generate/image/krea/krea-2/medium",
      headers={
          "Authorization": f"Bearer {API_TOKEN}",
          "Content-Type": "application/json"
      },
      json={
          "prompt": "An abstract, colorful, surreal composition of symmetry and balance. Swirling colors, imagery of trees and coalescing patterns converge. The lantern of light and death. It is as if the world was at once dark, and yet again lit.",
          "aspect_ratio": "1:1",
          "resolution": "1K",
          "styles": [
              {
                  "id": STYLE_ID,
                  "strength": 0.95
              }
          ]
      }
  )

  job = response.json()
  job_id = job["job_id"]
  print(f"Generation started! Job ID: {job_id}")

  # Poll for completion
  while True:
      check = requests.get(
          f"{API_BASE}/jobs/{job_id}",
          headers={"Authorization": f"Bearer {API_TOKEN}"}
      )
      status_data = check.json()

      if status_data["status"] == "completed":
          image_url = status_data["result"]["urls"][0]
          print(f"Image ready: {image_url}")
          break
      elif status_data["status"] == "failed":
          print("Generation failed")
          break

      time.sleep(2)
  ```

  ```bash cURL theme={null}
  # Generate with style
  curl -X POST https://api.krea.ai/generate/image/krea/krea-2/medium \
    -H "Authorization: Bearer YOUR_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "prompt": "An abstract, colorful, surreal composition of symmetry and balance. Swirling colors, imagery of trees and coalescing patterns converge. The lantern of light and death. It is as if the world was at once dark, and yet again lit.",
      "aspect_ratio": "1:1",
      "resolution": "1K",
      "styles": [
        {
          "id": "w29t6pvy0",
          "strength": 0.95
        }
      ]
    }'

  # Then poll /jobs/{job_id} for the result
  ```
</CodeGroup>

### Style Strength

The `strength` parameter (0.0-1.0) controls how strongly your style is applied:

| Strength | Effect                                                    |
| -------- | --------------------------------------------------------- |
| 0.5-0.7  | Subtle influence, maintains prompt flexibility            |
| 0.8-0.9  | Strong style application, recommended starting point      |
| 0.95-1.0 | Maximum style adherence, may reduce prompt responsiveness |

<Tip>
  Start with `0.8` strength and adjust based on results. Lower values give more creative freedom; higher values enforce stricter style adherence.
</Tip>

### Combining Multiple Styles

Apply multiple styles by adding them to the `styles` array:

```python theme={null}
"styles": [
    {"id": "style-id-1", "strength": 0.6},
    {"id": "style-id-2", "strength": 0.4}
]
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Image Selection" icon="images">
    * Use as many high-quality images as you have for optimal results
    * Ensure consistent style across all training images
    * Include variety in subjects while maintaining style coherence
    * Avoid watermarks, text overlays, or artifacts
    * Use images at least 1024x1024 resolution
  </Accordion>

  <Accordion title="Training Configuration" icon="sliders">
    * Start with default parameters using the `type` field
    * For styles: 500-1000 steps is usually sufficient
    * Lower learning rates (0.0001-0.0003) prevent overfitting
    * Increase steps if style isn't strong enough
    * Decrease steps if output is too rigid
  </Accordion>

  <Accordion title="Trigger Words" icon="wand-magic-sparkles">
    * Use the same trigger word if you plan on combining multiple styles
    * Trigger words are automatically injected into the prompt if you include the style
    * Avoid common words that appear in typical prompts
    * Use underscores for multi-word triggers: `my_custom_style`
  </Accordion>
</AccordionGroup>
