Skip to content

如何为 Diffusers 🧨 贡献

我们 ❤️ 来自开源社区的贡献!每个人都是受欢迎的,所有类型的参与——不仅仅是代码——都受到重视和赞赏。回答问题、帮助他人、联系交流、改进文档等,对社区来说都是非常有价值的,所以如果你愿意的话,不要害怕,积极参与吧!

鼓励每个人从在我们的公共 Discord 频道中说 👋 开始。我们讨论最新的扩散模型趋势、提问、展示个人项目、互相帮助贡献,或者只是闲聊 ☕。

Join us on Discord

无论你选择哪种方式贡献,我们都努力成为开放、欢迎和友善的社区的一部分。请阅读我们的行为准则,并在互动中尊重它。我们还建议你熟悉指导我们项目的伦理准则,并要求你遵守透明和负责任的原则。

我们非常重视社区的反馈,所以如果你认为你有宝贵的反馈可以帮助改进库,请不要害怕说出来——每条消息、评论、问题和拉取请求(PR)都会被阅读和考虑。

概述

你可以通过多种方式贡献,从回答问题到为核心库添加新的扩散模型。

以下,我们概述了不同的贡献方式,并按难度升序排列。所有这些贡献对社区都是有价值的。

如前所述,所有贡献对社区都是有价值的。 以下,我们将更详细地解释每种贡献方式。

对于所有贡献 4 - 9,你需要打开一个 PR。如何操作的详细说明请参阅打开拉取请求

1. 在 Diffusers 讨论论坛或 Diffusers Discord 上提问和回答问题

任何与 Diffusers 库相关的问题或评论都可以在讨论论坛Discord上提出。这些问题和评论包括但不限于:

  • 分享训练或推理实验的报告
  • 个人项目的展示
  • 非官方训练示例的问题
  • 项目提案
  • 一般反馈
  • 论文摘要
  • 询问基于 Diffusers 库的个人项目帮助
  • 一般问题
  • 关于扩散模型的伦理问题
  • ...

在论坛或 Discord 上提出的问题或评论积极鼓励社区公开分享知识,可能会在未来帮助到有相同问题的初学者。请提出你可能有的任何问题。 同样,通过回答这些问题,你对社区的帮助是巨大的,因为这样你是在公开记录知识,供所有人学习。

请注意,你投入的提问或回答问题的努力越多,公开记录的知识质量就越高。同样,提出和回答得好的问题会创建一个高质量的知识库,供所有人访问,而提出和回答得不好的问题会降低公共知识库的整体质量。 简而言之,高质量的问题或回答是精确简洁相关易于理解易于访问格式良好/提出得当的。更多详细信息,请参阅如何写一个好的问题部分。

关于频道的注意事项论坛被搜索引擎(如 Google)索引得更好。帖子按受欢迎程度而不是时间顺序排列。因此,查找我们之前发布的问题和答案更容易。 此外,论坛上发布的问题和答案可以轻松链接。 相比之下,Discord 采用聊天格式,鼓励快速来回交流。 虽然在 Discord 上提问可能会更快得到回答,但你的问题会随着时间的推移而不再可见。此外,在 Discord 上查找之前发布的信息要困难得多。因此,我们强烈建议使用论坛来提出高质量的问题和答案,以创建持久的知识库。如果 Discord 上的讨论产生了非常有趣的答案和结论,我们建议将结果发布到论坛,以便未来读者更容易获取信息。

2. 在 GitHub Issues 选项卡上打开新问题

由于用户通知我们遇到的问题,🧨 Diffusers 库才变得稳健和可靠。因此,感谢你报告问题。

请记住,GitHub 问题仅用于与 Diffusers 库直接相关的技术问题、错误报告、功能请求或库设计反馈。

简而言之,这意味着与Diffusers 库代码(包括文档)无关的一切不应在 GitHub 上提问,而应提问在论坛Discord上。

打开新问题时请考虑以下指南

  • 确保你已经搜索过是否有类似的问题(使用 GitHub Issues 选项卡下的搜索栏)。
  • 请不要在一个(相关)问题上报告新问题。如果另一个问题高度相关,请仍然打开一个新问题并链接到相关问题。
  • 确保你的问题用英语书写。如果你不擅长英语,请使用诸如DeepL等优秀的免费在线翻译服务将你的母语翻译成英语。
  • 检查你的问题是否可以通过更新到最新版本的 Diffusers 来解决。在发布问题之前,请确保 python -c "import diffusers; print(diffusers.__version__)" 的版本高于或等于最新版本的 Diffusers。
  • 请记住,你投入的打开新问题的努力越多,你的回答质量就越高,Diffusers 问题的整体质量也会越好。

新问题通常包括以下内容。

2.1. 可复现的最小错误报告

错误报告应始终包含可复现的代码片段,并尽可能简洁。 具体来说:

  • 尽可能缩小错误范围,不要直接粘贴整个代码文件
  • 格式化你的代码。
  • 除了 Diffusers 依赖的外部库外,不要包含其他外部库。
  • 始终提供所有必要的环境信息;为此,你可以在 shell 中运行:diffusers-cli env 并将显示的信息复制粘贴到问题中。
  • 解释问题。如果读者不知道问题是什么以及为什么是问题,(他/她)无法解决它。
  • 始终确保读者可以尽可能少地努力复现你的问题。如果由于缺少库或未定义的变量而无法运行你的代码片段,读者无法帮助你。确保你的可复现代码片段尽可能简洁,并可以复制粘贴到简单的 Python shell 中。
  • 如果复现问题需要模型和/或数据集,请确保读者可以访问该模型或数据集。你可以将模型或数据集上传到Hub以方便下载。尽量保持模型和数据集尽可能小,以使复现问题尽可能简单。

更多详细信息,请参阅如何写一个好的问题部分。

你可以在这里打开错误报告。

2.2. 功能请求

世界级的功能请求应解决以下几点:

  1. 首先说明动机:
  • 是否与库中的问题/挫败感相关?如果是,请解释原因。提供一个演示问题的代码片段最佳。
  • 是否与你的项目需求相关?我们很乐意听到关于它的信息!
  • 是否是你自己开发的,认为可以惠及社区?太棒了!告诉我们它解决了你的什么问题。
  1. 写一段完整的段落描述该功能;
  2. 提供一个代码片段,演示其未来用途;
  3. 如果与论文相关,请附上链接;
  4. 附上任何你认为可能有帮助的其他信息(绘图、截图等)。

你可以在这里打开功能请求。

2.3. 反馈

关于库设计的反馈,无论是好的还是不好的,都极大地帮助核心维护者构建用户友好的库。要了解当前设计哲学背后的哲学,请参阅这里。如果你觉得某个设计选择不符合当前设计哲学,请解释原因以及如何改进。如果某个设计选择过于遵循设计哲学,从而限制了使用场景,请解释原因以及如何改进。 如果某个设计选择对你非常有用,请留下备注,因为这对未来的决策非常有帮助。

你可以在这里打开反馈问题。

2.4. 技术问题

技术问题主要涉及库中某些代码为何以某种方式编写,或某部分代码的作用。请确保链接到相关代码,并提供详细信息,说明为什么这部分代码难以理解。

你可以在这里打开技术问题。

2.5. 提议添加新模型、调度器或管道

如果扩散模型社区发布了你希望在 Diffusers 库中看到的新模型、管道或调度器,请提供以下信息:

  • 扩散管道、模型或调度器的简要描述及论文或公开发布的链接。
  • 任何开源实现的链接。
  • 如果可用,模型权重的链接。

如果你愿意自己贡献模型,请告知我们,以便我们更好地指导你。此外,不要忘记通过 GitHub 账号标签标记组件(模型、调度器、管道等)的原作者,如果可以找到的话。

你可以在这里打开模型/管道/调度器请求。

3. 在 GitHub Issues 选项卡上回答问题

在 GitHub 上回答问题可能需要一些 Diffusers 的技术知识,但我们鼓励每个人尝试,即使你不确定你的答案是否完全正确。 提供高质量问题答案的一些技巧:

  • 尽可能简洁和精简。
  • 保持主题。回答问题应仅涉及问题本身。
  • 提供链接到代码、论文或其他支持或鼓励你观点的来源。
  • 用代码回答。如果简单的代码片段是问题的答案或展示了如何解决问题,请提供一个完全可复现的代码片段。

此外,许多问题往往是离题的、其他问题的重复或无关的。如果你能回答这些问题,鼓励问题作者更精确地提问,提供重复问题的链接或将其重定向到论坛Discord,这对维护者非常有帮助。

如果你已经验证了错误报告是正确的并且需要在源代码中进行修正,请参阅以下部分。

对于以下所有贡献,你需要打开一个 PR。如何操作的详细说明请参阅打开拉取请求部分。

4. 修复“Good first issue”

Good first issues 标记为Good first issue标签。通常,问题已经解释了潜在解决方案的外观,因此更容易修复。 如果问题尚未关闭且你希望尝试修复此问题,你可以留言“我想尝试这个问题。”。通常有三种情况:

  • a.) 问题描述已经提出了修复方案。在这种情况下,如果解决方案对你有道理,你可以打开一个 PR 或草稿 PR 来修复它。
  • b.) 问题描述没有提出修复方案。在这种情况下,你可以询问建议的修复方案可能是什么样的,Diffusers 团队的某个人应该会很快回答。如果你有好的修复思路,可以直接打开一个 PR。
  • c.) 已经有一个开放的 PR 修复了问题,但问题尚未关闭。如果 PR 已经停滞,你可以打开一个新的 PR 并链接到停滞的 PR。PR 通常会停滞,如果原贡献者突然没有时间继续,这种情况在开源项目中非常常见。在这种情况下,社区会非常高兴你重新尝试并利用现有 PR 的知识。如果已经有 PR 且是活跃的,你可以通过提供建议、审查 PR 或询问是否可以贡献来帮助作者。

5. 贡献文档

一个好的库总是有好的文档!官方文档通常是新用户接触库的第一个点,因此贡献文档是一项非常有价值的贡献

贡献文档可以有多种形式:

  • 修正拼写或语法错误。
  • 修正文档字符串的格式错误。如果你发现官方文档显示异常或链接已损坏,我们非常高兴你花时间修正它。
  • 修正文档字符串输入或输出张量的形状或维度。
  • 澄清难以理解或不正确的文档。
  • 更新过时的代码示例。
  • 将文档翻译成其他语言。

官方 Diffusers 文档页面上显示的任何内容都是官方文档的一部分,可以在相应的文档源中进行修正或调整。

请参阅此页面了解如何在本地验证文档更改。

6. 贡献社区管道

TIP

阅读社区管道指南,了解 GitHub 和 Hugging Face Hub 社区管道之间的区别。如果你对为什么我们有社区管道感兴趣,可以查看 GitHub Issue #841(基本上,我们无法维护所有可能的扩散模型推理方式,但也不想阻止社区构建它们)。

贡献社区管道是与社区分享你的创造力和工作的绝佳方式。它让你可以在 [DiffusionPipeline] 的基础上构建,使任何人都可以通过设置 custom_pipeline 参数来加载和使用它。本节将引导你创建一个简单的管道,其中 UNet 只进行一次前向传递并调用一次调度器(“单步”管道)。

  1. 为你的社区管道创建一个 one_step_unet.py 文件。该文件可以包含用户安装的任何包。确保你只有一个继承自 [DiffusionPipeline] 的管道类,以从 Hub 加载模型权重和调度器配置。在 __init__ 函数中添加 UNet 和调度器。

    你还应添加 register_modules 函数,以确保你的管道及其组件可以使用 [~DiffusionPipeline.save_pretrained] 保存。

py
from diffusers import DiffusionPipeline
import torch

class UnetSchedulerOneForwardPipeline(DiffusionPipeline):
    def __init__(self, unet, scheduler):
        super().__init__()

        self.register_modules(unet=unet, scheduler=scheduler)
  1. 在前向传递中(我们建议定义为 __call__),你可以添加任何你想要的功能。对于“一步”管道,创建一个随机图像,并通过设置 timestep=1 来调用一次 UNet 和调度器。
py
  from diffusers import DiffusionPipeline
  import torch

  class UnetSchedulerOneForwardPipeline(DiffusionPipeline):
      def __init__(self, unet, scheduler):
          super().__init__()

          self.register_modules(unet=unet, scheduler=scheduler)

      def __call__(self):
          image = torch.randn(
              (1, self.unet.config.in_channels, self.unet.config.sample_size, self.unet.config.sample_size),
          )
          timestep = 1

          model_output = self.unet(image, timestep).sample
          scheduler_output = self.scheduler.step(model_output, timestep, image).prev_sample

          return scheduler_output

现在你可以通过传递一个 UNet 和调度器来运行管道,或者如果管道结构相同,也可以加载预训练权重。

py
from diffusers import DDPMScheduler, UNet2DModel

scheduler = DDPMScheduler()
unet = UNet2DModel()

pipeline = UnetSchedulerOneForwardPipeline(unet=unet, scheduler=scheduler)
output = pipeline()
# load pretrained weights
pipeline = UnetSchedulerOneForwardPipeline.from_pretrained("google/ddpm-cifar10-32", use_safetensors=True)
output = pipeline()

你可以将你的管道共享为 GitHub 社区管道或 Hub 社区管道。

7. 贡献训练示例

Diffusers 示例是一组训练脚本,位于 examples 目录中。

我们支持两种类型的训练示例:

  • 官方训练示例
  • 研究训练示例

研究训练示例位于 examples/research_projects,而官方训练示例包括 examples 目录下的所有文件夹,但不包括 research_projectscommunity 文件夹。官方训练示例由 Diffusers 的核心维护者维护,而研究训练示例由社区维护。这是因为在 6. 贡献社区管道 中提到的官方管道与社区管道之间的相同原因:核心维护者无法维护所有可能的扩散模型训练方法。如果 Diffusers 核心维护者和社区认为某种训练范式过于实验性或不够流行,相应的训练代码应放在 research_projects 文件夹中,并由作者维护。

官方训练示例和研究训练示例都包含一个目录,该目录包含一个或多个训练脚本、一个 requirements.txt 文件和一个 README.md 文件。为了使用户能够使用这些训练示例,需要克隆仓库:

bash
git clone https://github.com/huggingface/diffusers

以及安装所有训练所需的额外依赖:

bash
cd diffusers
pip install -r examples/<your-example-folder>/requirements.txt

因此,在添加示例时,requirements.txt 文件应定义你的训练示例所需的所有 pip 依赖项,以便安装所有这些依赖项后,用户可以运行示例的训练脚本。例如,参见 DreamBooth requirements.txt 文件

Diffusers 库的训练示例应遵循以下理念:

  • 运行示例所需的所有代码应包含在一个 Python 文件中。
  • 应能够通过命令行使用 python 运行示例。
py
# Copied from diffusers.pipelines.stable_diffusion.pipeline_output.StableDiffusionPipelineOutput with Stable->Alt
class AltDiffusionPipelineOutput(BaseOutput):
    """
    Output class for Alt Diffusion pipelines.

    Args:
        images (`List[PIL.Image.Image]` or `np.ndarray`)
            List of denoised PIL images of length `batch_size` or NumPy array of shape `(batch_size, height, width,
            num_channels)`.
        nsfw_content_detected (`List[bool]`)
            List indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content or
            `None` if safety checking could not be performed.
    """

要了解更多,请阅读这篇~Don't~ Repeat Yourself*博客文章的相应部分。

如何撰写一个好的问题

你撰写的问题越好,问题被快速解决的几率就越高。

  1. 确保你使用了正确的问题模板。你可以选择 Bug ReportFeature RequestFeedback about API DesignNew model/pipeline/scheduler additionForum 或空白问题。在打开新的问题时,请确保选择正确的模板。
  2. 精确:给你的问题一个合适的标题。尽量用最简单的方式描述你的问题。你在提交问题时越精确,理解问题并解决它所需的时间就越少。确保为一个单独的问题打开一个单独的问题,而不是多个问题。如果你发现了多个问题,可以分别打开多个问题。如果你的问题是一个 bug,尽量精确描述这个 bug 是什么——不要只是写“diffusers 中的错误”。
  3. 可复现性:没有可复现的代码片段 == 没有解决方案。如果你遇到一个 bug,维护者必须能够复现它。确保你包含一个可以复制粘贴到 Python 解释器中复现问题的代码片段。确保你的代码片段可以运行,即没有缺少的导入或图片链接等。你的问题应包含一个错误信息一个可以复制粘贴且无需任何修改即可复现相同错误信息的代码片段。如果你的问题使用了本地模型权重或本地数据,而读者无法访问这些数据,问题将无法解决。如果你不能分享你的数据或模型,可以尝试创建一个虚拟模型或虚拟数据。
  4. 简洁:尽量帮助读者尽快理解问题,保持内容尽可能简洁。删除所有与问题无关的代码/信息。如果你发现了一个 bug,尽量创建一个最简单的代码示例来展示你的问题,不要在发现 bug 后立即把整个工作流程都粘贴到问题中。例如,如果你在训练模型时某个阶段出现了错误,应首先尝试理解哪部分训练代码导致了错误,并尝试用几行代码复现它。尽量使用虚拟数据而不是完整数据集。
  5. 添加链接。如果你引用了某个命名、方法或模型,请确保提供一个链接,以便读者更好地理解你的意思。如果你引用了某个特定的 PR 或问题,请确保在你的问题中链接它。不要假设读者知道你在说什么。你添加的链接越多,问题越好。
  6. 格式化。确保你的问题格式良好,将代码格式化为 Python 代码语法,错误信息格式化为普通代码语法。更多格式化信息请参阅官方 GitHub 格式化文档
  7. 不要把问题看作一个需要解决的工单,而是看作一个写得很好的百科全书条目。每个添加的问题都是对公开知识的贡献。通过添加一个写得好的问题,你不仅使维护者更容易解决你的问题,还帮助整个社区更好地理解库的某个方面。

如何撰写一个好的 PR

  1. 做一个变色龙。理解现有的设计模式和语法,确保你的代码添加无缝融入现有代码库。与现有设计模式或用户界面显著不同的拉取请求将不会被合并。
  2. 保持专注。一个拉取请求应解决一个且仅一个问题。确保不要陷入“在添加时顺便修复另一个问题”的陷阱。同时解决多个不相关问题的拉取请求更难审查。
  3. 如果有帮助,尝试添加一个代码片段,展示你的添加如何使用。
  4. 你的拉取请求标题应总结其贡献。
  5. 如果你的拉取请求解决了某个问题,请在拉取请求描述中提及问题编号,以确保它们被链接(并且查看问题的人知道你在处理它);
  6. 为了表示正在进行的工作,请在标题前加上 [WIP]。这有助于避免重复工作,并将其与准备合并的 PR 区分开来;
  7. 尽量按照如何撰写一个好的问题中解释的方式撰写和格式化你的文本。
  8. 确保现有测试通过;
  9. 添加高覆盖率的测试。没有质量测试 = 没有合并。
  • 如果你添加了新的 @slow 测试,请确保它们通过 RUN_SLOW=1 python -m pytest tests/test_my_new_model.py。 CircleCI 不运行慢测试,但 GitHub Actions 每晚都会运行!
  1. 所有公共方法必须有信息丰富的 docstrings,这些 docstrings 能很好地与 markdown 配合使用。请参阅pipeline_latent_diffusion.py中的示例。
  2. 由于仓库迅速增长,确保不添加会显著增加仓库大小的文件非常重要。这包括图片、视频和其他非文本文件。我们更倾向于使用 hf.co 托管的 dataset,如 hf-internal-testinghuggingface/documentation-images 来放置这些文件。 如果是外部贡献,可以将图片添加到你的 PR 中,并请求 Hugging Face 成员将你的图片迁移到这个数据集中。

如何打开一个 PR

在编写代码之前,我们强烈建议你搜索现有的 PR 或 问题,以确保没有人已经在处理相同的问题。如果你不确定,最好先打开一个问题以获取一些反馈。

你需要具备基本的 git 技能才能为 🧨 Diffusers 贡献代码。git 不是最容易使用的工具,但它有最好的 手册。在 shell 中输入 git --help 并享受。如果你更喜欢书籍,Pro Git 是一个非常好的参考。

按照以下步骤开始贡献(支持的 Python 版本):

  1. 通过点击仓库页面上的 'Fork' 按钮来 fork 仓库。这会在你的 GitHub 用户账户下创建代码的副本。

  2. 将你的 fork 克隆到本地磁盘,并将基础仓库添加为远程仓库:

bash
 $ git clone git@github.com:<your GitHub handle>/diffusers.git
 $ cd diffusers
 $ git remote add upstream https://github.com/huggingface/diffusers.git
  1. 创建一个新分支来保存你的开发更改:
bash
 $ git checkout -b a-descriptive-name-for-my-changes

不要main 分支上工作。

  1. 通过在虚拟环境中运行以下命令来设置开发环境:
bash
 $ pip install -e ".[dev]"

如果你已经克隆了仓库,你可能需要执行 git pull 以获取库中的最新更改。

  1. 在你的分支上开发功能。

在开发功能时,你应该确保测试套件通过。你应该运行受你更改影响的测试,如下所示:

bash
 $ pytest tests/<TEST_TO_RUN>.py

在运行测试之前,请确保你安装了测试所需的依赖项。你可以使用以下命令来安装:

bash
 $ pip install -e ".[test]"

你也可以使用以下命令运行完整的测试套件,但由于 Diffusers 已经大幅增长,现在需要一台性能强大的机器才能在合理的时间内得出结果。以下是该命令:

bash
 $ make test

🧨 Diffusers 依赖于 blackisort 来格式化其源代码,以保持一致性。在你进行更改后,可以通过以下命令一次性应用自动样式修正和代码验证:

bash
 $ make style

🧨 Diffusers 还使用 ruff 和一些自定义脚本来检查编码错误。质量控制在 CI 中运行,但你也可以运行相同的检查:

bash
 $ make quality

一旦你对所做的更改满意后,使用 git add 添加更改的文件,并使用 git commit 提交更改以记录你在本地的更改:

bash
 $ git add modified_file.py
 $ git commit -m "A descriptive message about your changes."

定期将你的代码副本与原始仓库同步是一个好主意。这样你可以快速应对变更:

bash
 $ git pull upstream main

使用以下命令将更改推送到你的账户:

bash
git push origin main
bash
 $ git push -u origin a-descriptive-name-for-my-changes
  1. 一旦你满意了,前往你在 GitHub 上的 fork 仓库页面。点击 'Pull request' 将你的更改发送给项目维护者进行审查。

  2. 如果维护者要求你进行更改,这是正常的。即使是核心贡献者也会遇到这种情况!为了让所有人都能看到 Pull request 中的更改,请在你的本地分支中进行工作,并将更改推送到你的 fork 仓库。这些更改会自动出现在 Pull request 中。

测试

包含了一个广泛的测试套件来测试库的行为和多个示例。库的测试文件位于 tests 文件夹 中。

我们喜欢 pytestpytest-xdist,因为它们运行得更快。从仓库的根目录,以下是使用 pytest 运行库测试的方法:

bash
$ python -m pytest -n auto --dist=loadfile -s -v ./tests/

事实上,make test 就是这样实现的!

你可以指定一个较小的测试集,以便只测试你正在开发的功能。

默认情况下,慢速测试会被跳过。将 RUN_SLOW 环境变量设置为 yes 以运行这些测试。这将下载数吉字节的模型——确保你有足够的磁盘空间和良好的互联网连接,或者有足够的耐心!

bash
$ RUN_SLOW=yes python -m pytest -n auto --dist=loadfile -s -v ./tests/

unittest 完全支持,以下是使用它运行测试的方法:

bash
$ python -m unittest discover -s tests -t . -v
$ python -m unittest discover -s examples -t examples -v

同步分叉的主分支与上游(HuggingFace)主分支

为了避免向上游仓库发送请求,这会在每个上游 PR 中添加引用注释并发送不必要的通知给参与这些 PR 的开发者, 在同步分叉仓库的主分支时,请遵循以下步骤:

  1. 尽可能避免使用分叉仓库中的分支和 PR 来同步上游。相反,直接合并到分叉的主分支。
  2. 如果必须使用 PR,请在检出你的分支后按照以下步骤操作:
bash
$ git checkout -b your-branch-for-syncing
$ git pull --squash --no-commit upstream main
$ git commit -m '<your message without GitHub references>'
$ git push --set-upstream origin your-branch-for-syncing

风格指南

对于文档字符串,🧨 Diffusers 遵循 Google 风格