Skip to content

Commit 2a42ae7

Browse files
authored
Merge branch 'main' into fixes-issue-10872
2 parents f844f2b + 6e2a93d commit 2a42ae7

File tree

10 files changed

+412
-180
lines changed

10 files changed

+412
-180
lines changed

.github/workflows/pr_style_bot.yml

Lines changed: 40 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -9,160 +9,43 @@ permissions:
99
pull-requests: write
1010

1111
jobs:
12-
check-permissions:
13-
if: >
14-
contains(github.event.comment.body, '@bot /style') &&
15-
github.event.issue.pull_request != null
16-
runs-on: ubuntu-latest
17-
outputs:
18-
is_authorized: ${{ steps.check_user_permission.outputs.has_permission }}
19-
steps:
20-
- name: Check user permission
21-
id: check_user_permission
22-
uses: actions/github-script@v6
23-
with:
24-
script: |
25-
const comment_user = context.payload.comment.user.login;
26-
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
27-
owner: context.repo.owner,
28-
repo: context.repo.repo,
29-
username: comment_user
30-
});
31-
const authorized = permission.permission === 'admin';
32-
console.log(`User ${comment_user} has permission level: ${permission.permission}, authorized: ${authorized} (only admins allowed)`);
33-
core.setOutput('has_permission', authorized);
34-
35-
run-style-bot:
36-
needs: check-permissions
37-
if: needs.check-permissions.outputs.is_authorized == 'true'
38-
runs-on: ubuntu-latest
39-
steps:
40-
- name: Extract PR details
41-
id: pr_info
42-
uses: actions/github-script@v6
43-
with:
44-
script: |
45-
const prNumber = context.payload.issue.number;
46-
const { data: pr } = await github.rest.pulls.get({
47-
owner: context.repo.owner,
48-
repo: context.repo.repo,
49-
pull_number: prNumber
50-
});
51-
52-
// We capture both the branch ref and the "full_name" of the head repo
53-
// so that we can check out the correct repository & branch (including forks).
54-
core.setOutput("prNumber", prNumber);
55-
core.setOutput("headRef", pr.head.ref);
56-
core.setOutput("headRepoFullName", pr.head.repo.full_name);
57-
58-
- name: Check out PR branch
59-
uses: actions/checkout@v3
60-
env:
61-
HEADREPOFULLNAME: ${{ steps.pr_info.outputs.headRepoFullName }}
62-
HEADREF: ${{ steps.pr_info.outputs.headRef }}
63-
with:
64-
# Instead of checking out the base repo, use the contributor's repo name
65-
repository: ${{ env.HEADREPOFULLNAME }}
66-
ref: ${{ env.HEADREF }}
67-
# You may need fetch-depth: 0 for being able to push
68-
fetch-depth: 0
69-
token: ${{ secrets.GITHUB_TOKEN }}
70-
71-
- name: Debug
72-
env:
73-
HEADREPOFULLNAME: ${{ steps.pr_info.outputs.headRepoFullName }}
74-
HEADREF: ${{ steps.pr_info.outputs.headRef }}
75-
PRNUMBER: ${{ steps.pr_info.outputs.prNumber }}
76-
run: |
77-
echo "PR number: $PRNUMBER"
78-
echo "Head Ref: $HEADREF"
79-
echo "Head Repo Full Name: $HEADREPOFULLNAME"
80-
81-
- name: Set up Python
82-
uses: actions/setup-python@v4
83-
84-
- name: Install dependencies
85-
run: |
86-
pip install .[quality]
87-
88-
- name: Download necessary files from main branch of Diffusers
89-
run: |
90-
curl -o main_Makefile https://raw.githubusercontent.com/huggingface/diffusers/main/Makefile
91-
curl -o main_setup.py https://raw.githubusercontent.com/huggingface/diffusers/refs/heads/main/setup.py
92-
curl -o main_check_doc_toc.py https://raw.githubusercontent.com/huggingface/diffusers/refs/heads/main/utils/check_doc_toc.py
93-
94-
- name: Compare the files and raise error if needed
95-
run: |
96-
diff_failed=0
97-
98-
if ! diff -q main_Makefile Makefile; then
99-
echo "Error: The Makefile has changed. Please ensure it matches the main branch."
100-
diff_failed=1
101-
fi
102-
103-
if ! diff -q main_setup.py setup.py; then
104-
echo "Error: The setup.py has changed. Please ensure it matches the main branch."
105-
diff_failed=1
106-
fi
107-
108-
if ! diff -q main_check_doc_toc.py utils/check_doc_toc.py; then
109-
echo "Error: The utils/check_doc_toc.py has changed. Please ensure it matches the main branch."
110-
diff_failed=1
111-
fi
112-
113-
if [ $diff_failed -eq 1 ]; then
114-
echo "❌ Error happened as we detected changes in the files that should not be changed ❌"
115-
exit 1
116-
fi
117-
118-
echo "No changes in the files. Proceeding..."
119-
rm -rf main_Makefile main_setup.py main_check_doc_toc.py
120-
121-
- name: Run make style and make quality
122-
run: |
123-
make style && make quality
124-
125-
- name: Commit and push changes
126-
id: commit_and_push
127-
env:
128-
HEADREPOFULLNAME: ${{ steps.pr_info.outputs.headRepoFullName }}
129-
HEADREF: ${{ steps.pr_info.outputs.headRef }}
130-
PRNUMBER: ${{ steps.pr_info.outputs.prNumber }}
131-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
132-
run: |
133-
echo "HEADREPOFULLNAME: $HEADREPOFULLNAME, HEADREF: $HEADREF"
134-
# Configure git with the Actions bot user
135-
git config user.name "github-actions[bot]"
136-
git config user.email "github-actions[bot]@users.noreply.github.com"
137-
138-
# Make sure your 'origin' remote is set to the contributor's fork
139-
git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/$HEADREPOFULLNAME.git"
140-
141-
# If there are changes after running style/quality, commit them
142-
if [ -n "$(git status --porcelain)" ]; then
143-
git add .
144-
git commit -m "Apply style fixes"
145-
# Push to the original contributor's forked branch
146-
git push origin HEAD:$HEADREF
147-
echo "changes_pushed=true" >> $GITHUB_OUTPUT
148-
else
149-
echo "No changes to commit."
150-
echo "changes_pushed=false" >> $GITHUB_OUTPUT
151-
fi
152-
153-
- name: Comment on PR with workflow run link
154-
if: steps.commit_and_push.outputs.changes_pushed == 'true'
155-
uses: actions/github-script@v6
156-
with:
157-
script: |
158-
const prNumber = parseInt(process.env.prNumber, 10);
159-
const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
160-
161-
await github.rest.issues.createComment({
162-
owner: context.repo.owner,
163-
repo: context.repo.repo,
164-
issue_number: prNumber,
165-
body: `Style fixes have been applied. [View the workflow run here](${runUrl}).`
166-
});
167-
env:
168-
prNumber: ${{ steps.pr_info.outputs.prNumber }}
12+
style:
13+
uses: huggingface/huggingface_hub/.github/workflows/style-bot-action.yml@main
14+
with:
15+
python_quality_dependencies: "[quality]"
16+
pre_commit_script_name: "Download and Compare files from the main branch"
17+
pre_commit_script: |
18+
echo "Downloading the files from the main branch"
19+
20+
curl -o main_Makefile https://raw.githubusercontent.com/huggingface/diffusers/main/Makefile
21+
curl -o main_setup.py https://raw.githubusercontent.com/huggingface/diffusers/refs/heads/main/setup.py
22+
curl -o main_check_doc_toc.py https://raw.githubusercontent.com/huggingface/diffusers/refs/heads/main/utils/check_doc_toc.py
23+
24+
echo "Compare the files and raise error if needed"
25+
26+
diff_failed=0
27+
if ! diff -q main_Makefile Makefile; then
28+
echo "Error: The Makefile has changed. Please ensure it matches the main branch."
29+
diff_failed=1
30+
fi
31+
32+
if ! diff -q main_setup.py setup.py; then
33+
echo "Error: The setup.py has changed. Please ensure it matches the main branch."
34+
diff_failed=1
35+
fi
36+
37+
if ! diff -q main_check_doc_toc.py utils/check_doc_toc.py; then
38+
echo "Error: The utils/check_doc_toc.py has changed. Please ensure it matches the main branch."
39+
diff_failed=1
40+
fi
41+
42+
if [ $diff_failed -eq 1 ]; then
43+
echo "❌ Error happened as we detected changes in the files that should not be changed ❌"
44+
exit 1
45+
fi
46+
47+
echo "No changes in the files. Proceeding..."
48+
rm -rf main_Makefile main_setup.py main_check_doc_toc.py
49+
style_command: "make style && make quality"
50+
secrets:
51+
bot_token: ${{ secrets.GITHUB_TOKEN }}

examples/advanced_diffusion_training/train_dreambooth_lora_flux_advanced.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -880,9 +880,7 @@ def save_embeddings(self, file_path: str):
880880
idx_to_text_encoder_name = {0: "clip_l", 1: "t5"}
881881
for idx, text_encoder in enumerate(self.text_encoders):
882882
train_ids = self.train_ids if idx == 0 else self.train_ids_t5
883-
embeds = (
884-
text_encoder.text_model.embeddings.token_embedding if idx == 0 else text_encoder.encoder.embed_tokens
885-
)
883+
embeds = text_encoder.text_model.embeddings.token_embedding if idx == 0 else text_encoder.shared
886884
assert embeds.weight.data.shape[0] == len(self.tokenizers[idx]), "Tokenizers should be the same."
887885
new_token_embeddings = embeds.weight.data[train_ids]
888886

@@ -904,9 +902,7 @@ def device(self):
904902
@torch.no_grad()
905903
def retract_embeddings(self):
906904
for idx, text_encoder in enumerate(self.text_encoders):
907-
embeds = (
908-
text_encoder.text_model.embeddings.token_embedding if idx == 0 else text_encoder.encoder.embed_tokens
909-
)
905+
embeds = text_encoder.text_model.embeddings.token_embedding if idx == 0 else text_encoder.shared
910906
index_no_updates = self.embeddings_settings[f"index_no_updates_{idx}"]
911907
embeds.weight.data[index_no_updates] = (
912908
self.embeddings_settings[f"original_embeddings_{idx}"][index_no_updates]
@@ -1749,7 +1745,7 @@ def load_model_hook(models, input_dir):
17491745
if args.enable_t5_ti: # whether to do pivotal tuning/textual inversion for T5 as well
17501746
text_lora_parameters_two = []
17511747
for name, param in text_encoder_two.named_parameters():
1752-
if "token_embedding" in name:
1748+
if "shared" in name:
17531749
# ensure that dtype is float32, even if rest of the model that isn't trained is loaded in fp16
17541750
param.data = param.to(dtype=torch.float32)
17551751
param.requires_grad = True

examples/dreambooth/train_dreambooth_lora.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,17 +1119,22 @@ def compute_text_embeddings(prompt):
11191119
)
11201120

11211121
# Scheduler and math around the number of training steps.
1122-
overrode_max_train_steps = False
1123-
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
1122+
# Check the PR https://github.com/huggingface/diffusers/pull/8312 for detailed explanation.
1123+
num_warmup_steps_for_scheduler = args.lr_warmup_steps * accelerator.num_processes
11241124
if args.max_train_steps is None:
1125-
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
1126-
overrode_max_train_steps = True
1125+
len_train_dataloader_after_sharding = math.ceil(len(train_dataloader) / accelerator.num_processes)
1126+
num_update_steps_per_epoch = math.ceil(len_train_dataloader_after_sharding / args.gradient_accumulation_steps)
1127+
num_training_steps_for_scheduler = (
1128+
args.num_train_epochs * accelerator.num_processes * num_update_steps_per_epoch
1129+
)
1130+
else:
1131+
num_training_steps_for_scheduler = args.max_train_steps * accelerator.num_processes
11271132

11281133
lr_scheduler = get_scheduler(
11291134
args.lr_scheduler,
11301135
optimizer=optimizer,
1131-
num_warmup_steps=args.lr_warmup_steps * accelerator.num_processes,
1132-
num_training_steps=args.max_train_steps * accelerator.num_processes,
1136+
num_warmup_steps=num_warmup_steps_for_scheduler,
1137+
num_training_steps=num_training_steps_for_scheduler,
11331138
num_cycles=args.lr_num_cycles,
11341139
power=args.lr_power,
11351140
)
@@ -1146,8 +1151,15 @@ def compute_text_embeddings(prompt):
11461151

11471152
# We need to recalculate our total training steps as the size of the training dataloader may have changed.
11481153
num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps)
1149-
if overrode_max_train_steps:
1154+
if args.max_train_steps is None:
11501155
args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch
1156+
if num_training_steps_for_scheduler != args.max_train_steps:
1157+
logger.warning(
1158+
f"The length of the 'train_dataloader' after 'accelerator.prepare' ({len(train_dataloader)}) does not match "
1159+
f"the expected length ({len_train_dataloader_after_sharding}) when the learning rate scheduler was created. "
1160+
f"This inconsistency may result in the learning rate scheduler not functioning properly."
1161+
)
1162+
11511163
# Afterwards we recalculate our number of training epochs
11521164
args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch)
11531165

tests/pipelines/hunyuandit/test_hunyuan_dit.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# limitations under the License.
1515

1616
import gc
17+
import tempfile
1718
import unittest
1819

1920
import numpy as np
@@ -212,6 +213,99 @@ def test_fused_qkv_projections(self):
212213
def test_encode_prompt_works_in_isolation(self):
213214
pass
214215

216+
def test_save_load_optional_components(self):
217+
components = self.get_dummy_components()
218+
pipe = self.pipeline_class(**components)
219+
pipe.to(torch_device)
220+
pipe.set_progress_bar_config(disable=None)
221+
222+
inputs = self.get_dummy_inputs(torch_device)
223+
224+
prompt = inputs["prompt"]
225+
generator = inputs["generator"]
226+
num_inference_steps = inputs["num_inference_steps"]
227+
output_type = inputs["output_type"]
228+
229+
(
230+
prompt_embeds,
231+
negative_prompt_embeds,
232+
prompt_attention_mask,
233+
negative_prompt_attention_mask,
234+
) = pipe.encode_prompt(prompt, device=torch_device, dtype=torch.float32, text_encoder_index=0)
235+
236+
(
237+
prompt_embeds_2,
238+
negative_prompt_embeds_2,
239+
prompt_attention_mask_2,
240+
negative_prompt_attention_mask_2,
241+
) = pipe.encode_prompt(
242+
prompt,
243+
device=torch_device,
244+
dtype=torch.float32,
245+
text_encoder_index=1,
246+
)
247+
248+
# inputs with prompt converted to embeddings
249+
inputs = {
250+
"prompt_embeds": prompt_embeds,
251+
"prompt_attention_mask": prompt_attention_mask,
252+
"negative_prompt_embeds": negative_prompt_embeds,
253+
"negative_prompt_attention_mask": negative_prompt_attention_mask,
254+
"prompt_embeds_2": prompt_embeds_2,
255+
"prompt_attention_mask_2": prompt_attention_mask_2,
256+
"negative_prompt_embeds_2": negative_prompt_embeds_2,
257+
"negative_prompt_attention_mask_2": negative_prompt_attention_mask_2,
258+
"generator": generator,
259+
"num_inference_steps": num_inference_steps,
260+
"output_type": output_type,
261+
"use_resolution_binning": False,
262+
}
263+
264+
# set all optional components to None
265+
for optional_component in pipe._optional_components:
266+
setattr(pipe, optional_component, None)
267+
268+
output = pipe(**inputs)[0]
269+
270+
with tempfile.TemporaryDirectory() as tmpdir:
271+
pipe.save_pretrained(tmpdir)
272+
pipe_loaded = self.pipeline_class.from_pretrained(tmpdir)
273+
pipe_loaded.to(torch_device)
274+
pipe_loaded.set_progress_bar_config(disable=None)
275+
276+
for optional_component in pipe._optional_components:
277+
self.assertTrue(
278+
getattr(pipe_loaded, optional_component) is None,
279+
f"`{optional_component}` did not stay set to None after loading.",
280+
)
281+
282+
inputs = self.get_dummy_inputs(torch_device)
283+
284+
generator = inputs["generator"]
285+
num_inference_steps = inputs["num_inference_steps"]
286+
output_type = inputs["output_type"]
287+
288+
# inputs with prompt converted to embeddings
289+
inputs = {
290+
"prompt_embeds": prompt_embeds,
291+
"prompt_attention_mask": prompt_attention_mask,
292+
"negative_prompt_embeds": negative_prompt_embeds,
293+
"negative_prompt_attention_mask": negative_prompt_attention_mask,
294+
"prompt_embeds_2": prompt_embeds_2,
295+
"prompt_attention_mask_2": prompt_attention_mask_2,
296+
"negative_prompt_embeds_2": negative_prompt_embeds_2,
297+
"negative_prompt_attention_mask_2": negative_prompt_attention_mask_2,
298+
"generator": generator,
299+
"num_inference_steps": num_inference_steps,
300+
"output_type": output_type,
301+
"use_resolution_binning": False,
302+
}
303+
304+
output_loaded = pipe_loaded(**inputs)[0]
305+
306+
max_diff = np.abs(to_np(output) - to_np(output_loaded)).max()
307+
self.assertLess(max_diff, 1e-4)
308+
215309

216310
@slow
217311
@require_torch_accelerator

0 commit comments

Comments
 (0)