def log_validation(
    network, noise_scheduler, vae, text_encoder, tokenizer, unet, args, accelerator, weight_dtype, epoch, global_step, **kwargs
):
    """
    This function, `log_validation`, serves as a validation step during training.
    It generates ID photo templates using controlnet if `template_dir` exists, otherwise, it creates random templates based on validation prompts.
    The resulting images are saved in the validation folder and logged in either TensorBoard or WandB.

    Args:
        model_dir (str): Directory path of the model.
        vae: Variational Autoencoder model.
        text_encoder: Text encoder model.
        tokenizer: Tokenizer for text data.
        unet: UNet model.
        args: Command line arguments.
        accelerator: Training accelerator.
        weight_dtype: Data type for model weights.
        epoch (int): Current training epoch.
        global_step (int): Current global training step.
        **kwargs: Additional keyword arguments.

    Returns:
        None
    """
    # When template_dir doesn't exist, generate randomly based on validation prompts.
    text_encoder, vae, unet = load_models_from_stable_diffusion_checkpoint(False, args.pretrained_model_ckpt)

    pipeline = StableDiffusionInpaintPipeline(
        tokenizer=tokenizer,
        scheduler=noise_scheduler,
        unet=unet.to(accelerator.device, weight_dtype),
        text_encoder=text_encoder.to(accelerator.device, weight_dtype),
        vae=vae.to(accelerator.device, weight_dtype),
        safety_checker=None,
        feature_extractor=None,
    )
    pipeline = pipeline.to(accelerator.device)
    pipeline.safety_checker = None
    pipeline.scheduler = DPMSolverMultistepScheduler.from_config(pipeline.scheduler.config)
    pipeline.set_progress_bar_config(disable=True)

    network_module.merge_lora(pipeline, network.state_dict(), 1, "cuda", torch.float16)
    generator = torch.Generator(device=accelerator.device)

    if args.seed is not None:
        generator = generator.manual_seed(args.seed)

    # Predictions before the start
    images = []
    if args.template_dir is not None:
        # Iteratively generate ID photos
        jpgs = os.listdir(args.template_dir)
        for jpg, read_jpg, shape, read_mask in zip(jpgs, kwargs["input_images"], kwargs["input_images_shape"], kwargs["input_masks"]):
            image = pipeline(
                args.validation_prompt,
                image=read_jpg,
                mask_image=read_mask,
                strength=0.65,
                negative_prompt=args.neg_prompt,
                guidance_scale=args.guidance_scale,
                num_inference_steps=20,
                generator=generator,
                height=kwargs["new_size"][1],
                width=kwargs["new_size"][0],
            ).images[0]

            images.append(image)

            save_name = jpg.split(".")[0]
            if not os.path.exists(os.path.join(args.output_dir, "validation")):
                os.makedirs(os.path.join(args.output_dir, "validation"))
            image.save(os.path.join(args.output_dir, "validation", f"global_step_{save_name}_{global_step}_0.jpg"))

    else:
        # Random Generate
        for _ in range(args.num_validation_images):
            images.append(
                pipeline(
                    args.validation_prompt,
                    negative_prompt=args.neg_prompt,
                    guidance_scale=args.guidance_scale,
                    num_inference_steps=50,
                    generator=generator,
                    height=args.resolution,
                    width=args.resolution,
                ).images[0]
            )
        for index, image in enumerate(images):
            if not os.path.exists(os.path.join(args.output_dir, "validation")):
                os.makedirs(os.path.join(args.output_dir, "validation"))
            image.save(os.path.join(args.output_dir, "validation", f"global_step_{global_step}_" + str(index) + ".jpg"))

    # Wandb or tensorboard if we have
    for tracker in accelerator.trackers:
        if tracker.name == "tensorboard":
            for index, image in enumerate(images):
                tracker.writer.add_images("validation_" + str(index), np.asarray(image), epoch, dataformats="HWC")
        if tracker.name == "wandb":
            tracker.log({"validation": [wandb.Image(image, caption=f"{i}: {args.validation_prompt}") for i, image in enumerate(images)]})

    del pipeline
    torch.cuda.empty_cache()
    vae.to(accelerator.device, dtype=weight_dtype)

    This function, `log_validation`, serves as a validation step during training.
    It generates ID photo templates using controlnet if `template_dir` exists, otherwise, it creates random templates based on validation prompts.
    The resulting images are saved in the validation folder and logged in either TensorBoard or WandB.

    Args:
        model_dir (str): Directory path of the model.
        vae: Variational Autoencoder model.
        text_encoder: Text encoder model.
        tokenizer: Tokenizer for text data.
        unet: UNet model.
        args: Command line arguments.
        accelerator: Training accelerator.
        weight_dtype: Data type for model weights.
        epoch (int): Current training epoch.
        global_step (int): Current global training step.
        **kwargs: Additional keyword arguments.

    Returns:
        None
    
scripts/train_kohya/train_lora.py