快速开始

Note

在运行下述示例之前,需要您已经安装了PyTorch-NPU环境,有关环境安装,请参考 安装指南

一般来说,要在代码中使用NPU进行训练推理,需要做以下更改:

  1. 导入torch_npu扩展包 import torch_npu

  2. 将模型,以及模型输入上传到NPU上

1device= torch.device("npu")
2model = model.to(device)
3input = input.to(device)

下面的实例演示了如何使用NPU进行训练和推理任务:

1. 单卡训练

以下代码使用了cifar10数据集在NPU上训练模型(截取自 PyTorch tutorials),请关注高亮的内容。

  1"""
  2Training an image classifier
  3----------------------------
  4
  5We will do the following steps in order:
  6
  71. Load and normalize the CIFAR10 training and test datasets using
  8``torchvision``
  91. Define a Convolutional Neural Network
 102. Define a loss function
 113. Train the network on the training data
 124. Test the network on the test data
 13
 145. Load and normalize CIFAR10
 15^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 16
 17Using ``torchvision``, it’s extremely easy to load CIFAR10.
 18"""
 19import torch
 20# 引入torch-npu包
 21import torch_npu
 22
 23# 定义device
 24device = torch.device('npu:0' if torch.npu.is_available() else 'cpu')
 25print(device)
 26
 27import torchvision
 28import torchvision.transforms as transforms
 29
 30########################################################################
 31# The output of torchvision datasets are PILImage images of range [0, 1].
 32# We transform them to Tensors of normalized range [-1, 1].
 33transform = transforms.Compose(
 34    [transforms.ToTensor(),
 35    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
 36
 37batch_size = 4
 38
 39trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
 40                                        download=True, transform=transform)
 41trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size,
 42                                        shuffle=True, num_workers=2)
 43
 44testset = torchvision.datasets.CIFAR10(root='./data', train=False,
 45                                    download=True, transform=transform)
 46testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,
 47                                        shuffle=False, num_workers=2)
 48
 49classes = ('plane', 'car', 'bird', 'cat',
 50        'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
 51
 52########################################################################
 53# 2. Define a Convolutional Neural Network
 54# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 55# Copy the neural network from the Neural Networks section before and modify it to
 56# take 3-channel images (instead of 1-channel images as it was defined).
 57import torch.nn as nn
 58import torch.nn.functional as F
 59
 60
 61class Net(nn.Module):
 62    def __init__(self):
 63        super().__init__()
 64        self.conv1 = nn.Conv2d(3, 6, 5)
 65        self.pool = nn.MaxPool2d(2, 2)
 66        self.conv2 = nn.Conv2d(6, 16, 5)
 67        self.fc1 = nn.Linear(16 * 5 * 5, 120)
 68        self.fc2 = nn.Linear(120, 84)
 69        self.fc3 = nn.Linear(84, 10)
 70
 71    def forward(self, x):
 72        x = self.pool(F.relu(self.conv1(x)))
 73        x = self.pool(F.relu(self.conv2(x)))
 74        x = torch.flatten(x, 1) # flatten all dimensions except batch
 75        x = F.relu(self.fc1(x))
 76        x = F.relu(self.fc2(x))
 77        x = self.fc3(x)
 78        return x
 79
 80net = Net()
 81
 82# 将模型加载到NPU上
 83net.to(device)
 84
 85########################################################################
 86# 3. Define a Loss function and optimizer
 87# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 88# Let's use a Classification Cross-Entropy loss and SGD with momentum.
 89import torch.optim as optim
 90
 91criterion = nn.CrossEntropyLoss()
 92optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
 93
 94########################################################################
 95# 4. Train the network
 96# ^^^^^^^^^^^^^^^^^^^^
 97#
 98# This is when things start to get interesting.
 99# We simply have to loop over our data iterator, and feed the inputs to the
100# network and optimize.
101
102for epoch in range(2):  # loop over the dataset multiple times
103
104    running_loss = 0.0
105    for i, data in enumerate(trainloader, 0):
106        # get the inputs; data is a list of [inputs, labels]
107        # 将input数据发送到NPU上
108        inputs, labels = data[0].to(device), data[1].to(device)
109
110        # zero the parameter gradients
111        optimizer.zero_grad()
112
113        # forward + backward + optimize
114        outputs = net(inputs)
115        loss = criterion(outputs, labels)
116        loss.backward()
117        optimizer.step()
118
119        # print statistics
120        running_loss += loss.item()
121        if i % 2000 == 1999:    # print every 2000 mini-batches
122            print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
123            running_loss = 0.0
124
125print('Finished Training')
126
127########################################################################
128# 5. Test the network on the test data
129# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
130#
131# We have trained the network for 2 passes over the training dataset.
132# But we need to check if the network has learnt anything at all.
133#
134# We will check this by predicting the class label that the neural network
135# outputs, and checking it against the ground-truth. If the prediction is
136# correct, we add the sample to the list of correct predictions.
137#
138# Let us look at how the network performs on the whole dataset.
139correct = 0
140total = 0
141# since we're not training, we don't need to calculate the gradients for our outputs
142with torch.no_grad():
143    for data in testloader:
144        # 将input数据发送到NPU上
145        images, labels = data[0].to(device), data[1].to(device)
146        # calculate outputs by running images through the network
147        outputs = net(images)
148        # the class with the highest energy is what we choose as prediction
149        _, predicted = torch.max(outputs.data, 1)
150        total += labels.size(0)
151        correct += (predicted == labels).sum().item()
152
153print(f'Accuracy of the network on the 10000 test images: {100 * correct // total} %')
154########################################################################
155# That looks way better than chance, which is 10% accuracy (randomly picking
156# a class out of 10 classes).
157# Seems like the network learnt something.
158#
159# Hmmm, what are the classes that performed well, and the classes that did
160# not perform well:
161
162# prepare to count predictions for each class
163correct_pred = {classname: 0 for classname in classes}
164total_pred = {classname: 0 for classname in classes}
165
166# again no gradients needed
167with torch.no_grad():
168    for data in testloader:
169        # 将input数据发送到NPU上
170        images, labels = data[0].to(device), data[1].to(device)
171        outputs = net(images)
172        _, predictions = torch.max(outputs, 1)
173        # collect the correct predictions for each class
174        for label, prediction in zip(labels, predictions):
175            if label == prediction:
176                correct_pred[classes[label]] += 1
177            total_pred[classes[label]] += 1
178
179
180# print accuracy for each class
181for classname, correct_count in correct_pred.items():
182    accuracy = 100 * float(correct_count) / total_pred[classname]
183    print(f'Accuracy for class: {classname:5s} is {accuracy:.1f} %')

2. 使用DeepSpeed多卡并行训练

以下代码使用了cifar10数据集,使用DeepSpeed训练模型在多张NPU卡上进行模型训练(来自 DeepSpeed Examples),使用最新 Release 的 DeepSpeed 则代码无需修改,即可自动检测NPU并进行训练。

  1import argparse
  2import os
  3
  4import deepspeed
  5import torch
  6import torch.nn as nn
  7import torch.nn.functional as F
  8import torchvision
  9from torchvision import transforms
 10from deepspeed.accelerator import get_accelerator
 11from deepspeed.moe.utils import split_params_into_different_moe_groups_for_optimizer
 12
 13
 14def add_argument():
 15    parser = argparse.ArgumentParser(description="CIFAR")
 16
 17    # For train.
 18    parser.add_argument(
 19        "-e",
 20        "--epochs",
 21        default=30,
 22        type=int,
 23        help="number of total epochs (default: 30)",
 24    )
 25    parser.add_argument(
 26        "--local_rank",
 27        type=int,
 28        default=-1,
 29        help="local rank passed from distributed launcher",
 30    )
 31    parser.add_argument(
 32        "--log-interval",
 33        type=int,
 34        default=2000,
 35        help="output logging information at a given interval",
 36    )
 37
 38    # For mixed precision training.
 39    parser.add_argument(
 40        "--dtype",
 41        default="fp16",
 42        type=str,
 43        choices=["bf16", "fp16", "fp32"],
 44        help="Datatype used for training",
 45    )
 46
 47    # For ZeRO Optimization.
 48    parser.add_argument(
 49        "--stage",
 50        default=0,
 51        type=int,
 52        choices=[0, 1, 2, 3],
 53        help="Datatype used for training",
 54    )
 55
 56    # For MoE (Mixture of Experts).
 57    parser.add_argument(
 58        "--moe",
 59        default=False,
 60        action="store_true",
 61        help="use deepspeed mixture of experts (moe)",
 62    )
 63    parser.add_argument(
 64        "--ep-world-size", default=1, type=int, help="(moe) expert parallel world size"
 65    )
 66    parser.add_argument(
 67        "--num-experts",
 68        type=int,
 69        nargs="+",
 70        default=[
 71            1,
 72        ],
 73        help="number of experts list, MoE related.",
 74    )
 75    parser.add_argument(
 76        "--mlp-type",
 77        type=str,
 78        default="standard",
 79        help="Only applicable when num-experts > 1, accepts [standard, residual]",
 80    )
 81    parser.add_argument(
 82        "--top-k", default=1, type=int, help="(moe) gating top 1 and 2 supported"
 83    )
 84    parser.add_argument(
 85        "--min-capacity",
 86        default=0,
 87        type=int,
 88        help="(moe) minimum capacity of an expert regardless of the capacity_factor",
 89    )
 90    parser.add_argument(
 91        "--noisy-gate-policy",
 92        default=None,
 93        type=str,
 94        help="(moe) noisy gating (only supported with top-1). Valid values are None, RSample, and Jitter",
 95    )
 96    parser.add_argument(
 97        "--moe-param-group",
 98        default=False,
 99        action="store_true",
100        help="(moe) create separate moe param groups, required when using ZeRO w. MoE",
101    )
102
103    # Include DeepSpeed configuration arguments.
104    parser = deepspeed.add_config_arguments(parser)
105
106    args = parser.parse_args()
107
108    return args
109
110
111def create_moe_param_groups(model):
112    """Create separate parameter groups for each expert."""
113    parameters = {"params": [p for p in model.parameters()], "name": "parameters"}
114    return split_params_into_different_moe_groups_for_optimizer(parameters)
115
116
117def get_ds_config(args):
118    """Get the DeepSpeed configuration dictionary."""
119    ds_config = {
120        "train_batch_size": 16,
121        "steps_per_print": 2000,
122        "optimizer": {
123            "type": "Adam",
124            "params": {
125                "lr": 0.001,
126                "betas": [0.8, 0.999],
127                "eps": 1e-8,
128                "weight_decay": 3e-7,
129            },
130        },
131        "scheduler": {
132            "type": "WarmupLR",
133            "params": {
134                "warmup_min_lr": 0,
135                "warmup_max_lr": 0.001,
136                "warmup_num_steps": 1000,
137            },
138        },
139        "gradient_clipping": 1.0,
140        "prescale_gradients": False,
141        "bf16": {"enabled": args.dtype == "bf16"},
142        "fp16": {
143            "enabled": args.dtype == "fp16",
144            "fp16_master_weights_and_grads": False,
145            "loss_scale": 0,
146            "loss_scale_window": 500,
147            "hysteresis": 2,
148            "min_loss_scale": 1,
149            "initial_scale_power": 15,
150        },
151        "wall_clock_breakdown": False,
152        "zero_optimization": {
153            "stage": args.stage,
154            "allgather_partitions": True,
155            "reduce_scatter": True,
156            "allgather_bucket_size": 50000000,
157            "reduce_bucket_size": 50000000,
158            "overlap_comm": True,
159            "contiguous_gradients": True,
160            "cpu_offload": False,
161        },
162    }
163    return ds_config
164
165
166class Net(nn.Module):
167    def __init__(self, args):
168        super(Net, self).__init__()
169        self.conv1 = nn.Conv2d(3, 6, 5)
170        self.pool = nn.MaxPool2d(2, 2)
171        self.conv2 = nn.Conv2d(6, 16, 5)
172        self.fc1 = nn.Linear(16 * 5 * 5, 120)
173        self.fc2 = nn.Linear(120, 84)
174        self.moe = args.moe
175        if self.moe:
176            fc3 = nn.Linear(84, 84)
177            self.moe_layer_list = []
178            for n_e in args.num_experts:
179                # Create moe layers based on the number of experts.
180                self.moe_layer_list.append(
181                    deepspeed.moe.layer.MoE(
182                        hidden_size=84,
183                        expert=fc3,
184                        num_experts=n_e,
185                        ep_size=args.ep_world_size,
186                        use_residual=args.mlp_type == "residual",
187                        k=args.top_k,
188                        min_capacity=args.min_capacity,
189                        noisy_gate_policy=args.noisy_gate_policy,
190                    )
191                )
192            self.moe_layer_list = nn.ModuleList(self.moe_layer_list)
193            self.fc4 = nn.Linear(84, 10)
194        else:
195            self.fc3 = nn.Linear(84, 10)
196
197    def forward(self, x):
198        x = self.pool(F.relu(self.conv1(x)))
199        x = self.pool(F.relu(self.conv2(x)))
200        x = x.view(-1, 16 * 5 * 5)
201        x = F.relu(self.fc1(x))
202        x = F.relu(self.fc2(x))
203        if self.moe:
204            for layer in self.moe_layer_list:
205                x, _, _ = layer(x)
206            x = self.fc4(x)
207        else:
208            x = self.fc3(x)
209        return x
210
211
212def test(model_engine, testset, local_device, target_dtype, test_batch_size=4):
213    """Test the network on the test data.
214
215    Args:
216        model_engine (deepspeed.runtime.engine.DeepSpeedEngine): the DeepSpeed engine.
217        testset (torch.utils.data.Dataset): the test dataset.
218        local_device (str): the local device name.
219        target_dtype (torch.dtype): the target datatype for the test data.
220        test_batch_size (int): the test batch size.
221
222    """
223    # The 10 classes for CIFAR10.
224    classes = (
225        "plane",
226        "car",
227        "bird",
228        "cat",
229        "deer",
230        "dog",
231        "frog",
232        "horse",
233        "ship",
234        "truck",
235    )
236
237    # Define the test dataloader.
238    testloader = torch.utils.data.DataLoader(
239        testset, batch_size=test_batch_size, shuffle=False, num_workers=0
240    )
241
242    # For total accuracy.
243    correct, total = 0, 0
244    # For accuracy per class.
245    class_correct = list(0.0 for i in range(10))
246    class_total = list(0.0 for i in range(10))
247
248    # Start testing.
249    model_engine.eval()
250    with torch.no_grad():
251        for data in testloader:
252            images, labels = data
253            if target_dtype != None:
254                images = images.to(target_dtype)
255            outputs = model_engine(images.to(local_device))
256            _, predicted = torch.max(outputs.data, 1)
257            # Count the total accuracy.
258            total += labels.size(0)
259            correct += (predicted == labels.to(local_device)).sum().item()
260
261            # Count the accuracy per class.
262            batch_correct = (predicted == labels.to(local_device)).squeeze()
263            for i in range(test_batch_size):
264                label = labels[i]
265                class_correct[label] += batch_correct[i].item()
266                class_total[label] += 1
267
268    if model_engine.local_rank == 0:
269        print(
270            f"Accuracy of the network on the {total} test images: {100 * correct / total : .0f} %"
271        )
272
273        # For all classes, print the accuracy.
274        for i in range(10):
275            print(
276                f"Accuracy of {classes[i] : >5s} : {100 * class_correct[i] / class_total[i] : 2.0f} %"
277            )
278
279
280def main(args):
281    # Initialize DeepSpeed distributed backend.
282    deepspeed.init_distributed()
283    _local_rank = int(os.environ.get("LOCAL_RANK"))
284    get_accelerator().set_device(_local_rank)
285
286    ########################################################################
287    # Step1. Data Preparation.
288    #
289    # The output of torchvision datasets are PILImage images of range [0, 1].
290    # We transform them to Tensors of normalized range [-1, 1].
291    #
292    # Note:
293    #     If running on Windows and you get a BrokenPipeError, try setting
294    #     the num_worker of torch.utils.data.DataLoader() to 0.
295    ########################################################################
296    transform = transforms.Compose(
297        [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
298    )
299
300    if torch.distributed.get_rank() != 0:
301        # Might be downloading cifar data, let rank 0 download first.
302        torch.distributed.barrier()
303
304    # Load or download cifar data.
305    trainset = torchvision.datasets.CIFAR10(
306        root="./data", train=True, download=True, transform=transform
307    )
308    testset = torchvision.datasets.CIFAR10(
309        root="./data", train=False, download=True, transform=transform
310    )
311
312    if torch.distributed.get_rank() == 0:
313        # Cifar data is downloaded, indicate other ranks can proceed.
314        torch.distributed.barrier()
315
316    ########################################################################
317    # Step 2. Define the network with DeepSpeed.
318    #
319    # First, we define a Convolution Neural Network.
320    # Then, we define the DeepSpeed configuration dictionary and use it to
321    # initialize the DeepSpeed engine.
322    ########################################################################
323    net = Net(args)
324
325    # Get list of parameters that require gradients.
326    parameters = filter(lambda p: p.requires_grad, net.parameters())
327
328    # If using MoE, create separate param groups for each expert.
329    if args.moe_param_group:
330        parameters = create_moe_param_groups(net)
331
332    # Initialize DeepSpeed to use the following features.
333    #   1) Distributed model.
334    #   2) Distributed data loader.
335    #   3) DeepSpeed optimizer.
336    ds_config = get_ds_config(args)
337    model_engine, optimizer, trainloader, __ = deepspeed.initialize(
338        args=args,
339        model=net,
340        model_parameters=parameters,
341        training_data=trainset,
342        config=ds_config,
343    )
344
345    # Get the local device name (str) and local rank (int).
346    local_device = get_accelerator().device_name(model_engine.local_rank)
347    local_rank = model_engine.local_rank
348
349    # For float32, target_dtype will be None so no datatype conversion needed.
350    target_dtype = None
351    if model_engine.bfloat16_enabled():
352        target_dtype = torch.bfloat16
353    elif model_engine.fp16_enabled():
354        target_dtype = torch.half
355
356    # Define the Classification Cross-Entropy loss function.
357    criterion = nn.CrossEntropyLoss()
358
359    ########################################################################
360    # Step 3. Train the network.
361    #
362    # This is when things start to get interesting.
363    # We simply have to loop over our data iterator, and feed the inputs to the
364    # network and optimize. (DeepSpeed handles the distributed details for us!)
365    ########################################################################
366
367    for epoch in range(args.epochs):  # loop over the dataset multiple times
368        running_loss = 0.0
369        for i, data in enumerate(trainloader):
370            # Get the inputs. ``data`` is a list of [inputs, labels].
371            inputs, labels = data[0].to(local_device), data[1].to(local_device)
372
373            # Try to convert to target_dtype if needed.
374            if target_dtype != None:
375                inputs = inputs.to(target_dtype)
376
377            outputs = model_engine(inputs)
378            loss = criterion(outputs, labels)
379
380            model_engine.backward(loss)
381            model_engine.step()
382
383            # Print statistics
384            running_loss += loss.item()
385            if local_rank == 0 and i % args.log_interval == (
386                args.log_interval - 1
387            ):  # Print every log_interval mini-batches.
388                print(
389                    f"[{epoch + 1 : d}, {i + 1 : 5d}] loss: {running_loss / args.log_interval : .3f}"
390                )
391                running_loss = 0.0
392    print("Finished Training")
393
394    ########################################################################
395    # Step 4. Test the network on the test data.
396    ########################################################################
397    test(model_engine, testset, local_device, target_dtype)
398
399
400if __name__ == "__main__":
401    args = add_argument()
402    main(args)

3. 使用Transformers进行模型微调

以下代码使用了Transformers对LLM进行微调(来自 transformers examples),使用最新 Release 的 Transformers 和 Accelerator 则代码无需修改,即可自动检测NPU并进行微调。

1python run_clm.py \
2    --model_name_or_path openai-community/gpt2 \
3    --train_file path_to_train_file \
4    --validation_file path_to_validation_file \
5    --per_device_train_batch_size 8 \
6    --per_device_eval_batch_size 8 \
7    --do_train \
8    --do_eval \
9    --output_dir /tmp/test-clm

4. 使用Diffusers进行模型微调

以下代码使用了Diffusers对文生图模型进行微调(来自 diffusers examples),使用最新 Release 的 Diffusers 则代码无需修改,即可自动检测NPU并进行微调。

   1#!/usr/bin/env python
   2# coding=utf-8
   3# Copyright 2025 The HuggingFace Inc. team. All rights reserved.
   4#
   5# Licensed under the Apache License, Version 2.0 (the "License");
   6# you may not use this file except in compliance with the License.
   7# You may obtain a copy of the License at
   8#
   9#     http://www.apache.org/licenses/LICENSE-2.0
  10#
  11# Unless required by applicable law or agreed to in writing, software
  12# distributed under the License is distributed on an "AS IS" BASIS,
  13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14# See the License for the specific language governing permissions and
  15# limitations under the License.
  16
  17import argparse
  18import logging
  19import math
  20import os
  21import random
  22import shutil
  23from contextlib import nullcontext
  24from pathlib import Path
  25
  26import accelerate
  27import datasets
  28import numpy as np
  29import torch
  30import torch.nn.functional as F
  31import torch.utils.checkpoint
  32import transformers
  33from accelerate import Accelerator
  34from accelerate.logging import get_logger
  35from accelerate.state import AcceleratorState
  36from accelerate.utils import ProjectConfiguration, set_seed
  37from datasets import load_dataset
  38from huggingface_hub import create_repo, upload_folder
  39from packaging import version
  40from torchvision import transforms
  41from tqdm.auto import tqdm
  42from transformers import CLIPTextModel, CLIPTokenizer
  43from transformers.utils import ContextManagers
  44
  45import diffusers
  46from diffusers import AutoencoderKL, DDPMScheduler, StableDiffusionPipeline, UNet2DConditionModel
  47from diffusers.optimization import get_scheduler
  48from diffusers.training_utils import EMAModel, compute_dream_and_update_latents, compute_snr
  49from diffusers.utils import check_min_version, is_wandb_available, make_image_grid
  50from diffusers.utils.hub_utils import load_or_create_model_card, populate_model_card
  51from diffusers.utils.import_utils import is_xformers_available
  52from diffusers.utils.torch_utils import is_compiled_module
  53
  54
  55if is_wandb_available():
  56    import wandb
  57
  58
  59# Will error if the minimal version of diffusers is not installed. Remove at your own risks.
  60check_min_version("0.42.0.dev0")
  61
  62logger = get_logger(__name__, log_level="INFO")
  63
  64DATASET_NAME_MAPPING = {
  65    "lambdalabs/naruto-blip-captions": ("image", "text"),
  66}
  67
  68
  69def save_model_card(
  70    args,
  71    repo_id: str,
  72    images: list = None,
  73    repo_folder: str = None,
  74):
  75    img_str = ""
  76    if len(images) > 0:
  77        image_grid = make_image_grid(images, 1, len(args.validation_prompts))
  78        image_grid.save(os.path.join(repo_folder, "val_imgs_grid.png"))
  79        img_str += "![val_imgs_grid](./val_imgs_grid.png)\n"
  80
  81    model_description = f"""
  82# Text-to-image finetuning - {repo_id}
  83
  84This pipeline was finetuned from **{args.pretrained_model_name_or_path}** on the **{args.dataset_name}** dataset. Below are some example images generated with the finetuned pipeline using the following prompts: {args.validation_prompts}: \n
  85{img_str}
  86
  87## Pipeline usage
  88
  89You can use the pipeline like so:
  90
  91```python
  92from diffusers import DiffusionPipeline
  93import torch
  94
  95pipeline = DiffusionPipeline.from_pretrained("{repo_id}", torch_dtype=torch.float16)
  96prompt = "{args.validation_prompts[0]}"
  97image = pipeline(prompt).images[0]
  98image.save("my_image.png")
  99```
 100
 101## Training info
 102
 103These are the key hyperparameters used during training:
 104
 105* Epochs: {args.num_train_epochs}
 106* Learning rate: {args.learning_rate}
 107* Batch size: {args.train_batch_size}
 108* Gradient accumulation steps: {args.gradient_accumulation_steps}
 109* Image resolution: {args.resolution}
 110* Mixed-precision: {args.mixed_precision}
 111
 112"""
 113    wandb_info = ""
 114    if is_wandb_available():
 115        wandb_run_url = None
 116        if wandb.run is not None:
 117            wandb_run_url = wandb.run.url
 118
 119    if wandb_run_url is not None:
 120        wandb_info = f"""
 121More information on all the CLI arguments and the environment are available on your [`wandb` run page]({wandb_run_url}).
 122"""
 123
 124    model_description += wandb_info
 125
 126    model_card = load_or_create_model_card(
 127        repo_id_or_path=repo_id,
 128        from_training=True,
 129        license="creativeml-openrail-m",
 130        base_model=args.pretrained_model_name_or_path,
 131        model_description=model_description,
 132        inference=True,
 133    )
 134
 135    tags = ["stable-diffusion", "stable-diffusion-diffusers", "text-to-image", "diffusers", "diffusers-training"]
 136    model_card = populate_model_card(model_card, tags=tags)
 137
 138    model_card.save(os.path.join(repo_folder, "README.md"))
 139
 140
 141def log_validation(vae, text_encoder, tokenizer, unet, args, accelerator, weight_dtype, epoch):
 142    logger.info("Running validation... ")
 143
 144    pipeline = StableDiffusionPipeline.from_pretrained(
 145        args.pretrained_model_name_or_path,
 146        vae=accelerator.unwrap_model(vae),
 147        text_encoder=accelerator.unwrap_model(text_encoder),
 148        tokenizer=tokenizer,
 149        unet=accelerator.unwrap_model(unet),
 150        safety_checker=None,
 151        revision=args.revision,
 152        variant=args.variant,
 153        torch_dtype=weight_dtype,
 154    )
 155    pipeline = pipeline.to(accelerator.device)
 156    pipeline.set_progress_bar_config(disable=True)
 157
 158    if args.enable_xformers_memory_efficient_attention:
 159        pipeline.enable_xformers_memory_efficient_attention()
 160
 161    if args.seed is None:
 162        generator = None
 163    else:
 164        generator = torch.Generator(device=accelerator.device).manual_seed(args.seed)
 165
 166    images = []
 167    for i in range(len(args.validation_prompts)):
 168        if torch.backends.mps.is_available():
 169            autocast_ctx = nullcontext()
 170        else:
 171            autocast_ctx = torch.autocast(accelerator.device.type)
 172
 173        with autocast_ctx:
 174            image = pipeline(args.validation_prompts[i], num_inference_steps=20, generator=generator).images[0]
 175
 176        images.append(image)
 177
 178    for tracker in accelerator.trackers:
 179        if tracker.name == "tensorboard":
 180            np_images = np.stack([np.asarray(img) for img in images])
 181            tracker.writer.add_images("validation", np_images, epoch, dataformats="NHWC")
 182        elif tracker.name == "wandb":
 183            tracker.log(
 184                {
 185                    "validation": [
 186                        wandb.Image(image, caption=f"{i}: {args.validation_prompts[i]}")
 187                        for i, image in enumerate(images)
 188                    ]
 189                }
 190            )
 191        else:
 192            logger.warning(f"image logging not implemented for {tracker.name}")
 193
 194    del pipeline
 195    torch.cuda.empty_cache()
 196
 197    return images
 198
 199
 200def parse_args():
 201    parser = argparse.ArgumentParser(description="Simple example of a training script.")
 202    parser.add_argument(
 203        "--input_perturbation", type=float, default=0, help="The scale of input perturbation. Recommended 0.1."
 204    )
 205    parser.add_argument(
 206        "--pretrained_model_name_or_path",
 207        type=str,
 208        default=None,
 209        required=True,
 210        help="Path to pretrained model or model identifier from huggingface.co/models.",
 211    )
 212    parser.add_argument(
 213        "--revision",
 214        type=str,
 215        default=None,
 216        required=False,
 217        help="Revision of pretrained model identifier from huggingface.co/models.",
 218    )
 219    parser.add_argument(
 220        "--variant",
 221        type=str,
 222        default=None,
 223        help="Variant of the model files of the pretrained model identifier from huggingface.co/models, 'e.g.' fp16",
 224    )
 225    parser.add_argument(
 226        "--dataset_name",
 227        type=str,
 228        default=None,
 229        help=(
 230            "The name of the Dataset (from the HuggingFace hub) to train on (could be your own, possibly private,"
 231            " dataset). It can also be a path pointing to a local copy of a dataset in your filesystem,"
 232            " or to a folder containing files that 🤗 Datasets can understand."
 233        ),
 234    )
 235    parser.add_argument(
 236        "--dataset_config_name",
 237        type=str,
 238        default=None,
 239        help="The config of the Dataset, leave as None if there's only one config.",
 240    )
 241    parser.add_argument(
 242        "--train_data_dir",
 243        type=str,
 244        default=None,
 245        help=(
 246            "A folder containing the training data. Folder contents must follow the structure described in"
 247            " https://huggingface.co/docs/datasets/image_dataset#imagefolder. In particular, a `metadata.jsonl` file"
 248            " must exist to provide the captions for the images. Ignored if `dataset_name` is specified."
 249        ),
 250    )
 251    parser.add_argument(
 252        "--image_column", type=str, default="image", help="The column of the dataset containing an image."
 253    )
 254    parser.add_argument(
 255        "--caption_column",
 256        type=str,
 257        default="text",
 258        help="The column of the dataset containing a caption or a list of captions.",
 259    )
 260    parser.add_argument(
 261        "--max_train_samples",
 262        type=int,
 263        default=None,
 264        help=(
 265            "For debugging purposes or quicker training, truncate the number of training examples to this "
 266            "value if set."
 267        ),
 268    )
 269    parser.add_argument(
 270        "--validation_prompts",
 271        type=str,
 272        default=None,
 273        nargs="+",
 274        help=("A set of prompts evaluated every `--validation_epochs` and logged to `--report_to`."),
 275    )
 276    parser.add_argument(
 277        "--output_dir",
 278        type=str,
 279        default="sd-model-finetuned",
 280        help="The output directory where the model predictions and checkpoints will be written.",
 281    )
 282    parser.add_argument(
 283        "--cache_dir",
 284        type=str,
 285        default=None,
 286        help="The directory where the downloaded models and datasets will be stored.",
 287    )
 288    parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.")
 289    parser.add_argument(
 290        "--resolution",
 291        type=int,
 292        default=512,
 293        help=(
 294            "The resolution for input images, all the images in the train/validation dataset will be resized to this"
 295            " resolution"
 296        ),
 297    )
 298    parser.add_argument(
 299        "--center_crop",
 300        default=False,
 301        action="store_true",
 302        help=(
 303            "Whether to center crop the input images to the resolution. If not set, the images will be randomly"
 304            " cropped. The images will be resized to the resolution first before cropping."
 305        ),
 306    )
 307    parser.add_argument(
 308        "--random_flip",
 309        action="store_true",
 310        help="whether to randomly flip images horizontally",
 311    )
 312    parser.add_argument(
 313        "--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader."
 314    )
 315    parser.add_argument("--num_train_epochs", type=int, default=100)
 316    parser.add_argument(
 317        "--max_train_steps",
 318        type=int,
 319        default=None,
 320        help="Total number of training steps to perform.  If provided, overrides num_train_epochs.",
 321    )
 322    parser.add_argument(
 323        "--gradient_accumulation_steps",
 324        type=int,
 325        default=1,
 326        help="Number of updates steps to accumulate before performing a backward/update pass.",
 327    )
 328    parser.add_argument(
 329        "--gradient_checkpointing",
 330        action="store_true",
 331        help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.",
 332    )
 333    parser.add_argument(
 334        "--learning_rate",
 335        type=float,
 336        default=1e-4,
 337        help="Initial learning rate (after the potential warmup period) to use.",
 338    )
 339    parser.add_argument(
 340        "--scale_lr",
 341        action="store_true",
 342        default=False,
 343        help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.",
 344    )
 345    parser.add_argument(
 346        "--lr_scheduler",
 347        type=str,
 348        default="constant",
 349        help=(
 350            'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",'
 351            ' "constant", "constant_with_warmup"]'
 352        ),
 353    )
 354    parser.add_argument(
 355        "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler."
 356    )
 357    parser.add_argument(
 358        "--snr_gamma",
 359        type=float,
 360        default=None,
 361        help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. "
 362        "More details here: https://huggingface.co/papers/2303.09556.",
 363    )
 364    parser.add_argument(
 365        "--dream_training",
 366        action="store_true",
 367        help=(
 368            "Use the DREAM training method, which makes training more efficient and accurate at the "
 369            "expense of doing an extra forward pass. See: https://huggingface.co/papers/2312.00210"
 370        ),
 371    )
 372    parser.add_argument(
 373        "--dream_detail_preservation",
 374        type=float,
 375        default=1.0,
 376        help="Dream detail preservation factor p (should be greater than 0; default=1.0, as suggested in the paper)",
 377    )
 378    parser.add_argument(
 379        "--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes."
 380    )
 381    parser.add_argument(
 382        "--allow_tf32",
 383        action="store_true",
 384        help=(
 385            "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see"
 386            " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices"
 387        ),
 388    )
 389    parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.")
 390    parser.add_argument("--offload_ema", action="store_true", help="Offload EMA model to CPU during training step.")
 391    parser.add_argument("--foreach_ema", action="store_true", help="Use faster foreach implementation of EMAModel.")
 392    parser.add_argument(
 393        "--non_ema_revision",
 394        type=str,
 395        default=None,
 396        required=False,
 397        help=(
 398            "Revision of pretrained non-ema model identifier. Must be a branch, tag or git identifier of the local or"
 399            " remote repository specified with --pretrained_model_name_or_path."
 400        ),
 401    )
 402    parser.add_argument(
 403        "--dataloader_num_workers",
 404        type=int,
 405        default=0,
 406        help=(
 407            "Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process."
 408        ),
 409    )
 410    parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.")
 411    parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.")
 412    parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.")
 413    parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer")
 414    parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.")
 415    parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.")
 416    parser.add_argument("--hub_token", type=str, default=None, help="The token to use to push to the Model Hub.")
 417    parser.add_argument(
 418        "--prediction_type",
 419        type=str,
 420        default=None,
 421        help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediction_type` is chosen.",
 422    )
 423    parser.add_argument(
 424        "--hub_model_id",
 425        type=str,
 426        default=None,
 427        help="The name of the repository to keep in sync with the local `output_dir`.",
 428    )
 429    parser.add_argument(
 430        "--logging_dir",
 431        type=str,
 432        default="logs",
 433        help=(
 434            "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to"
 435            " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***."
 436        ),
 437    )
 438    parser.add_argument(
 439        "--mixed_precision",
 440        type=str,
 441        default=None,
 442        choices=["no", "fp16", "bf16"],
 443        help=(
 444            "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >="
 445            " 1.10.and an Nvidia Ampere GPU.  Default to the value of accelerate config of the current system or the"
 446            " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config."
 447        ),
 448    )
 449    parser.add_argument(
 450        "--report_to",
 451        type=str,
 452        default="tensorboard",
 453        help=(
 454            'The integration to report the results and logs to. Supported platforms are `"tensorboard"`'
 455            ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.'
 456        ),
 457    )
 458    parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank")
 459    parser.add_argument(
 460        "--checkpointing_steps",
 461        type=int,
 462        default=500,
 463        help=(
 464            "Save a checkpoint of the training state every X updates. These checkpoints are only suitable for resuming"
 465            " training using `--resume_from_checkpoint`."
 466        ),
 467    )
 468    parser.add_argument(
 469        "--checkpoints_total_limit",
 470        type=int,
 471        default=None,
 472        help=("Max number of checkpoints to store."),
 473    )
 474    parser.add_argument(
 475        "--resume_from_checkpoint",
 476        type=str,
 477        default=None,
 478        help=(
 479            "Whether training should be resumed from a previous checkpoint. Use a path saved by"
 480            ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.'
 481        ),
 482    )
 483    parser.add_argument(
 484        "--enable_xformers_memory_efficient_attention", action="store_true", help="Whether or not to use xformers."
 485    )
 486    parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.")
 487    parser.add_argument(
 488        "--validation_epochs",
 489        type=int,
 490        default=5,
 491        help="Run validation every X epochs.",
 492    )
 493    parser.add_argument(
 494        "--tracker_project_name",
 495        type=str,
 496        default="text2image-fine-tune",
 497        help=(
 498            "The `project_name` argument passed to Accelerator.init_trackers for"
 499            " more information see https://huggingface.co/docs/accelerate/v0.17.0/en/package_reference/accelerator#accelerate.Accelerator"
 500        ),
 501    )
 502    parser.add_argument(
 503        "--image_interpolation_mode",
 504        type=str,
 505        default="lanczos",
 506        choices=[
 507            f.lower() for f in dir(transforms.InterpolationMode) if not f.startswith("__") and not f.endswith("__")
 508        ],
 509        help="The image interpolation method to use for resizing images.",
 510    )
 511
 512    args = parser.parse_args()
 513    env_local_rank = int(os.environ.get("LOCAL_RANK", -1))
 514    if env_local_rank != -1 and env_local_rank != args.local_rank:
 515        args.local_rank = env_local_rank
 516
 517    # Sanity checks
 518    if args.dataset_name is None and args.train_data_dir is None:
 519        raise ValueError("Need either a dataset name or a training folder.")
 520
 521    # default to using the same revision for the non-ema model if not specified
 522    if args.non_ema_revision is None:
 523        args.non_ema_revision = args.revision
 524
 525    return args
 526
 527
 528def main():
 529    args = parse_args()
 530
 531    if args.report_to == "wandb" and args.hub_token is not None:
 532        raise ValueError(
 533            "You cannot use both --report_to=wandb and --hub_token due to a security risk of exposing your token."
 534            " Please use `hf auth login` to authenticate with the Hub."
 535        )
 536
 537    logging_dir = os.path.join(args.output_dir, args.logging_dir)
 538
 539    accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir)
 540
 541    accelerator = Accelerator(
 542        gradient_accumulation_steps=args.gradient_accumulation_steps,
 543        mixed_precision=args.mixed_precision,
 544        log_with=args.report_to,
 545        project_config=accelerator_project_config,
 546    )
 547
 548    # Disable AMP for MPS.
 549    if torch.backends.mps.is_available():
 550        accelerator.native_amp = False
 551
 552    # Make one log on every process with the configuration for debugging.
 553    logging.basicConfig(
 554        format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
 555        datefmt="%m/%d/%Y %H:%M:%S",
 556        level=logging.INFO,
 557    )
 558    logger.info(accelerator.state, main_process_only=False)
 559    if accelerator.is_local_main_process:
 560        datasets.utils.logging.set_verbosity_warning()
 561        transformers.utils.logging.set_verbosity_warning()
 562        diffusers.utils.logging.set_verbosity_info()
 563    else:
 564        datasets.utils.logging.set_verbosity_error()
 565        transformers.utils.logging.set_verbosity_error()
 566        diffusers.utils.logging.set_verbosity_error()
 567
 568    # If passed along, set the training seed now.
 569    if args.seed is not None:
 570        set_seed(args.seed)
 571
 572    # Handle the repository creation
 573    if accelerator.is_main_process:
 574        if args.output_dir is not None:
 575            os.makedirs(args.output_dir, exist_ok=True)
 576
 577        if args.push_to_hub:
 578            repo_id = create_repo(
 579                repo_id=args.hub_model_id or Path(args.output_dir).name, exist_ok=True, token=args.hub_token
 580            ).repo_id
 581
 582    # Load scheduler, tokenizer and models.
 583    noise_scheduler = DDPMScheduler.from_pretrained(args.pretrained_model_name_or_path, subfolder="scheduler")
 584    tokenizer = CLIPTokenizer.from_pretrained(
 585        args.pretrained_model_name_or_path, subfolder="tokenizer", revision=args.revision
 586    )
 587
 588    def deepspeed_zero_init_disabled_context_manager():
 589        """
 590        returns either a context list that includes one that will disable zero.Init or an empty context list
 591        """
 592        deepspeed_plugin = AcceleratorState().deepspeed_plugin if accelerate.state.is_initialized() else None
 593        if deepspeed_plugin is None:
 594            return []
 595
 596        return [deepspeed_plugin.zero3_init_context_manager(enable=False)]
 597
 598    # Currently Accelerate doesn't know how to handle multiple models under Deepspeed ZeRO stage 3.
 599    # For this to work properly all models must be run through `accelerate.prepare`. But accelerate
 600    # will try to assign the same optimizer with the same weights to all models during
 601    # `deepspeed.initialize`, which of course doesn't work.
 602    #
 603    # For now the following workaround will partially support Deepspeed ZeRO-3, by excluding the 2
 604    # frozen models from being partitioned during `zero.Init` which gets called during
 605    # `from_pretrained` So CLIPTextModel and AutoencoderKL will not enjoy the parameter sharding
 606    # across multiple gpus and only UNet2DConditionModel will get ZeRO sharded.
 607    with ContextManagers(deepspeed_zero_init_disabled_context_manager()):
 608        text_encoder = CLIPTextModel.from_pretrained(
 609            args.pretrained_model_name_or_path, subfolder="text_encoder", revision=args.revision, variant=args.variant
 610        )
 611        vae = AutoencoderKL.from_pretrained(
 612            args.pretrained_model_name_or_path, subfolder="vae", revision=args.revision, variant=args.variant
 613        )
 614
 615    unet = UNet2DConditionModel.from_pretrained(
 616        args.pretrained_model_name_or_path, subfolder="unet", revision=args.non_ema_revision
 617    )
 618
 619    # Freeze vae and text_encoder and set unet to trainable
 620    vae.requires_grad_(False)
 621    text_encoder.requires_grad_(False)
 622    unet.train()
 623
 624    # Create EMA for the unet.
 625    if args.use_ema:
 626        ema_unet = UNet2DConditionModel.from_pretrained(
 627            args.pretrained_model_name_or_path, subfolder="unet", revision=args.revision, variant=args.variant
 628        )
 629        ema_unet = EMAModel(
 630            ema_unet.parameters(),
 631            model_cls=UNet2DConditionModel,
 632            model_config=ema_unet.config,
 633            foreach=args.foreach_ema,
 634        )
 635
 636    if args.enable_xformers_memory_efficient_attention:
 637        if is_xformers_available():
 638            import xformers
 639
 640            xformers_version = version.parse(xformers.__version__)
 641            if xformers_version == version.parse("0.0.16"):
 642                logger.warning(
 643                    "xFormers 0.0.16 cannot be used for training in some GPUs. If you observe problems during training, please update xFormers to at least 0.0.17. See https://huggingface.co/docs/diffusers/main/en/optimization/xformers for more details."
 644                )
 645            unet.enable_xformers_memory_efficient_attention()
 646        else:
 647            raise ValueError("xformers is not available. Make sure it is installed correctly")
 648
 649    # `accelerate` 0.16.0 will have better support for customized saving
 650    if version.parse(accelerate.__version__) >= version.parse("0.16.0"):
 651        # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format
 652        def save_model_hook(models, weights, output_dir):
 653            if accelerator.is_main_process:
 654                if args.use_ema:
 655                    ema_unet.save_pretrained(os.path.join(output_dir, "unet_ema"))
 656
 657                for i, model in enumerate(models):
 658                    model.save_pretrained(os.path.join(output_dir, "unet"))
 659
 660                    # make sure to pop weight so that corresponding model is not saved again
 661                    weights.pop()
 662
 663        def load_model_hook(models, input_dir):
 664            if args.use_ema:
 665                load_model = EMAModel.from_pretrained(
 666                    os.path.join(input_dir, "unet_ema"), UNet2DConditionModel, foreach=args.foreach_ema
 667                )
 668                ema_unet.load_state_dict(load_model.state_dict())
 669                if args.offload_ema:
 670                    ema_unet.pin_memory()
 671                else:
 672                    ema_unet.to(accelerator.device)
 673                del load_model
 674
 675            for _ in range(len(models)):
 676                # pop models so that they are not loaded again
 677                model = models.pop()
 678
 679                # load diffusers style into model
 680                load_model = UNet2DConditionModel.from_pretrained(input_dir, subfolder="unet")
 681                model.register_to_config(**load_model.config)
 682
 683                model.load_state_dict(load_model.state_dict())
 684                del load_model
 685
 686        accelerator.register_save_state_pre_hook(save_model_hook)
 687        accelerator.register_load_state_pre_hook(load_model_hook)
 688
 689    if args.gradient_checkpointing:
 690        unet.enable_gradient_checkpointing()
 691
 692    # Enable TF32 for faster training on Ampere GPUs,
 693    # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices
 694    if args.allow_tf32:
 695        torch.backends.cuda.matmul.allow_tf32 = True
 696
 697    if args.scale_lr:
 698        args.learning_rate = (
 699            args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes
 700        )
 701
 702    # Initialize the optimizer
 703    if args.use_8bit_adam:
 704        try:
 705            import bitsandbytes as bnb
 706        except ImportError:
 707            raise ImportError(
 708                "Please install bitsandbytes to use 8-bit Adam. You can do so by running `pip install bitsandbytes`"
 709            )
 710
 711        optimizer_cls = bnb.optim.AdamW8bit
 712    else:
 713        optimizer_cls = torch.optim.AdamW
 714
 715    optimizer = optimizer_cls(
 716        unet.parameters(),
 717        lr=args.learning_rate,
 718        betas=(args.adam_beta1, args.adam_beta2),
 719        weight_decay=args.adam_weight_decay,
 720        eps=args.adam_epsilon,
 721    )
 722
 723    # Get the datasets: you can either provide your own training and evaluation files (see below)
 724    # or specify a Dataset from the hub (the dataset will be downloaded automatically from the datasets Hub).
 725
 726    # In distributed training, the load_dataset function guarantees that only one local process can concurrently
 727    # download the dataset.
 728    if args.dataset_name is not None:
 729        # Downloading and loading a dataset from the hub.
 730        dataset = load_dataset(
 731            args.dataset_name,
 732            args.dataset_config_name,
 733            cache_dir=args.cache_dir,
 734            data_dir=args.train_data_dir,
 735        )
 736    else:
 737        data_files = {}
 738        if args.train_data_dir is not None:
 739            data_files["train"] = os.path.join(args.train_data_dir, "**")
 740        dataset = load_dataset(
 741            "imagefolder",
 742            data_files=data_files,
 743            cache_dir=args.cache_dir,
 744        )
 745        # See more about loading custom images at
 746        # https://huggingface.co/docs/datasets/v2.4.0/en/image_load#imagefolder
 747
 748    # Preprocessing the datasets.
 749    # We need to tokenize inputs and targets.
 750    column_names = dataset["train"].column_names
 751
 752    # 6. Get the column names for input/target.
 753    dataset_columns = DATASET_NAME_MAPPING.get(args.dataset_name, None)
 754    if args.image_column is None:
 755        image_column = dataset_columns[0] if dataset_columns is not None else column_names[0]
 756    else:
 757        image_column = args.image_column
 758        if image_column not in column_names:
 759            raise ValueError(
 760                f"--image_column' value '{args.image_column}' needs to be one of: {', '.join(column_names)}"
 761            )
 762    if args.caption_column is None:
 763        caption_column = dataset_columns[1] if dataset_columns is not None else column_names[1]
 764    else:
 765        caption_column = args.caption_column
 766        if caption_column not in column_names:
 767            raise ValueError(
 768                f"--caption_column' value '{args.caption_column}' needs to be one of: {', '.join(column_names)}"
 769            )
 770
 771    # Preprocessing the datasets.
 772    # We need to tokenize input captions and transform the images.
 773    def tokenize_captions(examples, is_train=True):
 774        captions = []
 775        for caption in examples[caption_column]:
 776            if isinstance(caption, str):
 777                captions.append(caption)
 778            elif isinstance(caption, (list, np.ndarray)):
 779                # take a random caption if there are multiple
 780                captions.append(random.choice(caption) if is_train else caption[0])
 781            else:
 782                raise ValueError(
 783                    f"Caption column `{caption_column}` should contain either strings or lists of strings."
 784                )
 785        inputs = tokenizer(
 786            captions, max_length=tokenizer.model_max_length, padding="max_length", truncation=True, return_tensors="pt"
 787        )
 788        return inputs.input_ids
 789
 790    # Get the specified interpolation method from the args
 791    interpolation = getattr(transforms.InterpolationMode, args.image_interpolation_mode.upper(), None)
 792
 793    # Raise an error if the interpolation method is invalid
 794    if interpolation is None:
 795        raise ValueError(f"Unsupported interpolation mode {args.image_interpolation_mode}.")
 796
 797    # Data preprocessing transformations
 798    train_transforms = transforms.Compose(
 799        [
 800            transforms.Resize(args.resolution, interpolation=interpolation),  # Use dynamic interpolation method
 801            transforms.CenterCrop(args.resolution) if args.center_crop else transforms.RandomCrop(args.resolution),
 802            transforms.RandomHorizontalFlip() if args.random_flip else transforms.Lambda(lambda x: x),
 803            transforms.ToTensor(),
 804            transforms.Normalize([0.5], [0.5]),
 805        ]
 806    )
 807
 808    def preprocess_train(examples):
 809        images = [image.convert("RGB") for image in examples[image_column]]
 810        examples["pixel_values"] = [train_transforms(image) for image in images]
 811        examples["input_ids"] = tokenize_captions(examples)
 812        return examples
 813
 814    with accelerator.main_process_first():
 815        if args.max_train_samples is not None:
 816            dataset["train"] = dataset["train"].shuffle(seed=args.seed).select(range(args.max_train_samples))
 817        # Set the training transforms
 818        train_dataset = dataset["train"].with_transform(preprocess_train)
 819
 820    def collate_fn(examples):
 821        pixel_values = torch.stack([example["pixel_values"] for example in examples])
 822        pixel_values = pixel_values.to(memory_format=torch.contiguous_format).float()
 823        input_ids = torch.stack([example["input_ids"] for example in examples])
 824        return {"pixel_values": pixel_values, "input_ids": input_ids}
 825
 826    # DataLoaders creation:
 827    train_dataloader = torch.utils.data.DataLoader(
 828        train_dataset,
 829        shuffle=True,
 830        collate_fn=collate_fn,
 831        batch_size=args.train_batch_size,
 832        num_workers=args.dataloader_num_workers,
 833    )
 834
 835    # Scheduler and math around the number of training steps.
 836    # Check the PR https://github.com/huggingface/diffusers/pull/8312 for detailed explanation.
 837    num_warmup_steps_for_scheduler = args.lr_warmup_steps * accelerator.num_processes
 838    if args.max_train_steps is None:
 839        len_train_dataloader_after_sharding = math.ceil(len(train_dataloader) / accelerator.num_processes)
 840        num_update_steps_per_epoch = math.ceil(len_train_dataloader_after_sharding / args.gradient_accumulation_steps)
 841        num_training_steps_for_scheduler = (
 842            args.num_train_epochs * num_update_steps_per_epoch * accelerator.num_processes
 843        )
 844    else:
 845        num_training_steps_for_scheduler = args.max_train_steps * accelerator.num_processes
 846
 847    lr_scheduler = get_scheduler(
 848        args.lr_scheduler,
 849        optimizer=optimizer,
 850        num_warmup_steps=num_warmup_steps_for_scheduler,
 851        num_training_steps=num_training_steps_for_scheduler,
 852    )
 853
 854    # Prepare everything with our `accelerator`.
 855    unet, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
 856        unet, optimizer, train_dataloader, lr_scheduler
 857    )
 858
 859    if args.use_ema:
 860        if args.offload_ema:
 861            ema_unet.pin_memory()
 862        else:
 863            ema_unet.to(accelerator.device)
 864
 865    # For mixed precision training we cast all non-trainable weights (vae, non-lora text_encoder and non-lora unet) to half-precision
 866    # as these weights are only used for inference, keeping weights in full precision is not required.
 867    weight_dtype = torch.float32
 868    if accelerator.mixed_precision == "fp16":
 869        weight_dtype = torch.float16
 870        args.mixed_precision = accelerator.mixed_precision
 871    elif accelerator.mixed_precision == "bf16":
 872        weight_dtype = torch.bfloat16
 873        args.mixed_precision = accelerator.mixed_precision
 874
 875    # Move text_encode and vae to gpu and cast to weight_dtype
 876    text_encoder.to(accelerator.device, dtype=weight_dtype)
 877    vae.to(accelerator.device, dtype=weight_dtype)
 878
 879    # We need to recalculate our total training steps as the size of the training dataloader may have changed.
 880    num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
 881    if args.max_train_steps is None:
 882        args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
 883        if num_training_steps_for_scheduler != args.max_train_steps * accelerator.num_processes:
 884            logger.warning(
 885                f"The length of the 'train_dataloader' after 'accelerator.prepare' ({len(train_dataloader)}) does not match "
 886                f"the expected length ({len_train_dataloader_after_sharding}) when the learning rate scheduler was created. "
 887                f"This inconsistency may result in the learning rate scheduler not functioning properly."
 888            )
 889    # Afterwards we recalculate our number of training epochs
 890    args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
 891
 892    # We need to initialize the trackers we use, and also store our configuration.
 893    # The trackers initializes automatically on the main process.
 894    if accelerator.is_main_process:
 895        tracker_config = dict(vars(args))
 896        tracker_config.pop("validation_prompts")
 897        accelerator.init_trackers(args.tracker_project_name, tracker_config)
 898
 899    # Function for unwrapping if model was compiled with `torch.compile`.
 900    def unwrap_model(model):
 901        model = accelerator.unwrap_model(model)
 902        model = model._orig_mod if is_compiled_module(model) else model
 903        return model
 904
 905    # Train!
 906    total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps
 907
 908    logger.info("***** Running training *****")
 909    logger.info(f"  Num examples = {len(train_dataset)}")
 910    logger.info(f"  Num Epochs = {args.num_train_epochs}")
 911    logger.info(f"  Instantaneous batch size per device = {args.train_batch_size}")
 912    logger.info(f"  Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}")
 913    logger.info(f"  Gradient Accumulation steps = {args.gradient_accumulation_steps}")
 914    logger.info(f"  Total optimization steps = {args.max_train_steps}")
 915    global_step = 0
 916    first_epoch = 0
 917
 918    # Potentially load in the weights and states from a previous save
 919    if args.resume_from_checkpoint:
 920        if args.resume_from_checkpoint != "latest":
 921            path = os.path.basename(args.resume_from_checkpoint)
 922        else:
 923            # Get the most recent checkpoint
 924            dirs = os.listdir(args.output_dir)
 925            dirs = [d for d in dirs if d.startswith("checkpoint")]
 926            dirs = sorted(dirs, key=lambda x: int(x.split("-")[1]))
 927            path = dirs[-1] if len(dirs) > 0 else None
 928
 929        if path is None:
 930            accelerator.print(
 931                f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run."
 932            )
 933            args.resume_from_checkpoint = None
 934            initial_global_step = 0
 935        else:
 936            accelerator.print(f"Resuming from checkpoint {path}")
 937            accelerator.load_state(os.path.join(args.output_dir, path))
 938            global_step = int(path.split("-")[1])
 939
 940            initial_global_step = global_step
 941            first_epoch = global_step // num_update_steps_per_epoch
 942
 943    else:
 944        initial_global_step = 0
 945
 946    progress_bar = tqdm(
 947        range(0, args.max_train_steps),
 948        initial=initial_global_step,
 949        desc="Steps",
 950        # Only show the progress bar once on each machine.
 951        disable=not accelerator.is_local_main_process,
 952    )
 953
 954    for epoch in range(first_epoch, args.num_train_epochs):
 955        train_loss = 0.0
 956        for step, batch in enumerate(train_dataloader):
 957            with accelerator.accumulate(unet):
 958                # Convert images to latent space
 959                latents = vae.encode(batch["pixel_values"].to(weight_dtype)).latent_dist.sample()
 960                latents = latents * vae.config.scaling_factor
 961
 962                # Sample noise that we'll add to the latents
 963                noise = torch.randn_like(latents)
 964                if args.noise_offset:
 965                    # https://www.crosslabs.org//blog/diffusion-with-offset-noise
 966                    noise += args.noise_offset * torch.randn(
 967                        (latents.shape[0], latents.shape[1], 1, 1), device=latents.device
 968                    )
 969                if args.input_perturbation:
 970                    new_noise = noise + args.input_perturbation * torch.randn_like(noise)
 971                bsz = latents.shape[0]
 972                # Sample a random timestep for each image
 973                timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=latents.device)
 974                timesteps = timesteps.long()
 975
 976                # Add noise to the latents according to the noise magnitude at each timestep
 977                # (this is the forward diffusion process)
 978                if args.input_perturbation:
 979                    noisy_latents = noise_scheduler.add_noise(latents, new_noise, timesteps)
 980                else:
 981                    noisy_latents = noise_scheduler.add_noise(latents, noise, timesteps)
 982
 983                # Get the text embedding for conditioning
 984                encoder_hidden_states = text_encoder(batch["input_ids"], return_dict=False)[0]
 985
 986                # Get the target for loss depending on the prediction type
 987                if args.prediction_type is not None:
 988                    # set prediction_type of scheduler if defined
 989                    noise_scheduler.register_to_config(prediction_type=args.prediction_type)
 990
 991                if noise_scheduler.config.prediction_type == "epsilon":
 992                    target = noise
 993                elif noise_scheduler.config.prediction_type == "v_prediction":
 994                    target = noise_scheduler.get_velocity(latents, noise, timesteps)
 995                else:
 996                    raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}")
 997
 998                if args.dream_training:
 999                    noisy_latents, target = compute_dream_and_update_latents(
1000                        unet,
1001                        noise_scheduler,
1002                        timesteps,
1003                        noise,
1004                        noisy_latents,
1005                        target,
1006                        encoder_hidden_states,
1007                        args.dream_detail_preservation,
1008                    )
1009
1010                # Predict the noise residual and compute loss
1011                model_pred = unet(noisy_latents, timesteps, encoder_hidden_states, return_dict=False)[0]
1012
1013                if args.snr_gamma is None:
1014                    loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean")
1015                else:
1016                    # Compute loss-weights as per Section 3.4 of https://huggingface.co/papers/2303.09556.
1017                    # Since we predict the noise instead of x_0, the original formulation is slightly changed.
1018                    # This is discussed in Section 4.2 of the same paper.
1019                    snr = compute_snr(noise_scheduler, timesteps)
1020                    mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min(
1021                        dim=1
1022                    )[0]
1023                    if noise_scheduler.config.prediction_type == "epsilon":
1024                        mse_loss_weights = mse_loss_weights / snr
1025                    elif noise_scheduler.config.prediction_type == "v_prediction":
1026                        mse_loss_weights = mse_loss_weights / (snr + 1)
1027
1028                    loss = F.mse_loss(model_pred.float(), target.float(), reduction="none")
1029                    loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights
1030                    loss = loss.mean()
1031
1032                # Gather the losses across all processes for logging (if we use distributed training).
1033                avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean()
1034                train_loss += avg_loss.item() / args.gradient_accumulation_steps
1035
1036                # Backpropagate
1037                accelerator.backward(loss)
1038                if accelerator.sync_gradients:
1039                    accelerator.clip_grad_norm_(unet.parameters(), args.max_grad_norm)
1040                optimizer.step()
1041                lr_scheduler.step()
1042                optimizer.zero_grad()
1043
1044            # Checks if the accelerator has performed an optimization step behind the scenes
1045            if accelerator.sync_gradients:
1046                if args.use_ema:
1047                    if args.offload_ema:
1048                        ema_unet.to(device="cuda", non_blocking=True)
1049                    ema_unet.step(unet.parameters())
1050                    if args.offload_ema:
1051                        ema_unet.to(device="cpu", non_blocking=True)
1052                progress_bar.update(1)
1053                global_step += 1
1054                accelerator.log({"train_loss": train_loss}, step=global_step)
1055                train_loss = 0.0
1056
1057                if global_step % args.checkpointing_steps == 0:
1058                    if accelerator.is_main_process:
1059                        # _before_ saving state, check if this save would set us over the `checkpoints_total_limit`
1060                        if args.checkpoints_total_limit is not None:
1061                            checkpoints = os.listdir(args.output_dir)
1062                            checkpoints = [d for d in checkpoints if d.startswith("checkpoint")]
1063                            checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1]))
1064
1065                            # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints
1066                            if len(checkpoints) >= args.checkpoints_total_limit:
1067                                num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1
1068                                removing_checkpoints = checkpoints[0:num_to_remove]
1069
1070                                logger.info(
1071                                    f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints"
1072                                )
1073                                logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}")
1074
1075                                for removing_checkpoint in removing_checkpoints:
1076                                    removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint)
1077                                    shutil.rmtree(removing_checkpoint)
1078
1079                        save_path = os.path.join(args.output_dir, f"checkpoint-{global_step}")
1080                        accelerator.save_state(save_path)
1081                        logger.info(f"Saved state to {save_path}")
1082
1083            logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]}
1084            progress_bar.set_postfix(**logs)
1085
1086            if global_step >= args.max_train_steps:
1087                break
1088
1089        if accelerator.is_main_process:
1090            if args.validation_prompts is not None and epoch % args.validation_epochs == 0:
1091                if args.use_ema:
1092                    # Store the UNet parameters temporarily and load the EMA parameters to perform inference.
1093                    ema_unet.store(unet.parameters())
1094                    ema_unet.copy_to(unet.parameters())
1095                log_validation(
1096                    vae,
1097                    text_encoder,
1098                    tokenizer,
1099                    unet,
1100                    args,
1101                    accelerator,
1102                    weight_dtype,
1103                    global_step,
1104                )
1105                if args.use_ema:
1106                    # Switch back to the original UNet parameters.
1107                    ema_unet.restore(unet.parameters())
1108
1109    # Create the pipeline using the trained modules and save it.
1110    accelerator.wait_for_everyone()
1111    if accelerator.is_main_process:
1112        unet = unwrap_model(unet)
1113        if args.use_ema:
1114            ema_unet.copy_to(unet.parameters())
1115
1116        pipeline = StableDiffusionPipeline.from_pretrained(
1117            args.pretrained_model_name_or_path,
1118            text_encoder=text_encoder,
1119            vae=vae,
1120            unet=unet,
1121            revision=args.revision,
1122            variant=args.variant,
1123        )
1124        pipeline.save_pretrained(args.output_dir)
1125
1126        # Run a final round of inference.
1127        images = []
1128        if args.validation_prompts is not None:
1129            logger.info("Running inference for collecting generated images...")
1130            pipeline = pipeline.to(accelerator.device)
1131            pipeline.torch_dtype = weight_dtype
1132            pipeline.set_progress_bar_config(disable=True)
1133
1134            if args.enable_xformers_memory_efficient_attention:
1135                pipeline.enable_xformers_memory_efficient_attention()
1136
1137            if args.seed is None:
1138                generator = None
1139            else:
1140                generator = torch.Generator(device=accelerator.device).manual_seed(args.seed)
1141
1142            for i in range(len(args.validation_prompts)):
1143                with torch.autocast("cuda"):
1144                    image = pipeline(args.validation_prompts[i], num_inference_steps=20, generator=generator).images[0]
1145                images.append(image)
1146
1147        if args.push_to_hub:
1148            save_model_card(args, repo_id, images, repo_folder=args.output_dir)
1149            upload_folder(
1150                repo_id=repo_id,
1151                folder_path=args.output_dir,
1152                commit_message="End of training",
1153                ignore_patterns=["step_*", "epoch_*"],
1154            )
1155
1156    accelerator.end_training()
1157
1158
1159if __name__ == "__main__":
1160    main()
 1export MODEL_NAME="CompVis/stable-diffusion-v1-4"
 2export DATASET_NAME="lambdalabs/naruto-blip-captions"
 3
 4accelerate launch --mixed_precision="fp16"  train_text_to_image.py \
 5--pretrained_model_name_or_path=$MODEL_NAME \
 6--dataset_name=$DATASET_NAME \
 7--use_ema \
 8--resolution=512 --center_crop --random_flip \
 9--train_batch_size=1 \
10--gradient_accumulation_steps=4 \
11--gradient_checkpointing \
12--max_train_steps=15000 \
13--learning_rate=1e-05 \
14--max_grad_norm=1 \
15--lr_scheduler="constant" --lr_warmup_steps=0 \
16--output_dir="sd-pokemon-model"