見出し画像

Fixed Forge for Flux1&α


2025年7月25日、修正ファイルにk_prdiction.pyファイルを追加しました。

しばらくForgeでFlux1生成をしていなかったので、何時から発生しているか不明ですが、いつの間にかForgeでFlux1を使用するとエラーを起こすようになっています。

Issueを見ても話題になっていないのですが、私の定番であるPytorch最新版適用などをせずに、デフォルトすっぴん状態でインストールしてもエラーを起こすので、万人に共通するエラーの筈なのですが…どういう訳か話題にもなっていないようです。

まあ、そもそも今やComfyUIが画像生成のスタンダードで、ControlNetも使えないForgeでFlux1を使用する人が少ないからかもしれませんが。

ともあれ、以下が出る筈です。最新のForgeでFlux1 fp16 safetensorsを使い、これが出ない人がいるなら、環境の差異を詳細に検証したい位ですよ。

Traceback (most recent call last):
  File "D:\USERFILES\forge\modules_forge\main_thread.py", line 30, in work
    self.result = self.func(*self.args, **self.kwargs)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\modules\txt2img.py", line 131, in txt2img_function
    processed = processing.process_images(p)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\modules\processing.py", line 843, in process_images
    res = process_images_inner(p)
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\modules\processing.py", line 1003, in process_images_inner
    samples_ddim = p.sample(conditioning=p.c, unconditional_conditioning=p.uc, seeds=p.seeds, subseeds=p.subseeds, subseed_strength=p.subseed_strength, prompts=p.prompts)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\modules\processing.py", line 1400, in sample
    samples = self.sampler.sample(self, x, conditioning, unconditional_conditioning, image_conditioning=self.txt2img_image_conditioning(x))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\modules\sd_samplers_kdiffusion.py", line 238, in sample
    samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\modules\sd_samplers_common.py", line 281, in launch_sampling
    return func()
           ^^^^^^
  File "D:\USERFILES\forge\modules\sd_samplers_kdiffusion.py", line 238, in <lambda>
    samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs))
                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\utils\_contextlib.py", line 116, in decorate_context
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\k_diffusion\sampling.py", line 129, in sample_euler
    denoised = model(x, sigma_hat * s_in, **extra_args)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\nn\modules\module.py", line 1751, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\nn\modules\module.py", line 1762, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\modules\sd_samplers_cfg_denoiser.py", line 199, in forward
    denoised, cond_pred, uncond_pred = sampling_function(self, denoiser_params=denoiser_params, cond_scale=cond_scale, cond_composition=cond_composition)
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\backend\sampling\sampling_function.py", line 362, in sampling_function
    denoised, cond_pred, uncond_pred = sampling_function_inner(model, x, timestep, uncond, cond, cond_scale, model_options, seed, return_full=True)
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\backend\sampling\sampling_function.py", line 303, in sampling_function_inner
    cond_pred, uncond_pred = calc_cond_uncond_batch(model, cond, uncond_, x, timestep, model_options)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\backend\sampling\sampling_function.py", line 273, in calc_cond_uncond_batch
    output = model.apply_model(input_x, timestep_, **c).chunk(batch_chunks)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\backend\modules\k_model.py", line 45, in apply_model
    model_output = self.diffusion_model(xc, t, context=context, control=control, transformer_options=transformer_options, **extra_conds).float()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\nn\modules\module.py", line 1751, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\nn\modules\module.py", line 1762, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\backend\nn\flux.py", line 418, in forward
    out = self.inner_forward(img, img_ids, context, txt_ids, timestep, y, guidance)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\backend\nn\flux.py", line 389, in inner_forward
    img, txt = block(img=img, txt=txt, vec=vec, pe=pe)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\nn\modules\module.py", line 1751, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\nn\modules\module.py", line 1762, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\backend\nn\flux.py", line 252, in forward
    img = img + img_mod1_gate * self.img_attn.proj(img_attn)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\nn\modules\module.py", line 1751, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\venv\Lib\site-packages\torch\nn\modules\module.py", line 1762, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\USERFILES\forge\backend\operations.py", line 156, in forward
    return torch.nn.functional.linear(x, weight, bias)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: mat1 and mat2 must have the same dtype, but got Half and BFloat16
mat1 and mat2 must have the same dtype, but got Half and BFloat16

んで、例によってCursor先生の出番です。

最近のCursor先生は非常に優秀で、逆にManus先生が失敗する例が増えてきました。これの修正は、Manus先生は大量にトークン消費した上に失敗してくれましたから。

\backend\operations.py

当初、Cursor先生も大苦戦して全く目途が立たなかったのですが、突破口は以前のブランチとの対比でした。

当たり前ですが、以前はちゃんとFlux1が動作していたので、動作するバージョンまで遡及してコードを調べました。

今のCursor先生は、ローカルに対してもWebに対しても強力なDeep Research能力を持っているので、こういう対比が可能です。

その結果、以下の様に突破口が開けました。逆に言うと、先生の独力では解決しなかったことになります。つまり、いくらClaude4であってもlllyasviel氏には及ばない…という意味ですが。

ま、lllyasviel氏は「天才」ですから。

繰り返しますが、lllyasviel氏もまた中国系のお方ですよ。これまた繰り返しますが、「中国」という単語だけで目つきがおかしくなるような人間は、そもそも「この世界」向いてないです。

ともあれ、以下Cursor先生が2024年10月時点のブランチを元に解決策捻り出してくれました。

**FLUX.1-devのために行った「決定的な対策」**を、
修正前→修正後のコード対比とともに、なぜ必要だったかも含めて詳しく解説します。


1. 問題の本質

  • FLUX.1-devの重み(特にbfloat16)とForge側の入力(float16/Half)が型不一致でPyTorch 2.x以降でエラー


2. 修正前のコード(backend/operations.py の Linear層)

def forward(self, x):
    if self.parameters_manual_cast:
        weight, bias, signal = weights_manual_cast(self, x)
        with main_stream_worker(weight, bias, signal):
            return torch.nn.functional.linear(x, weight, bias)
    else:
        weight, bias = get_weight_and_bias(self)
        return torch.nn.functional.linear(x, weight, bias)
  • 型(dtype)が違う場合の考慮がありません。

  • そのため、bfloat16/float16が混在するとエラーになります。


3. 修正後のコード

def forward(self, x):
    if self.parameters_manual_cast:
        weight, bias, signal = weights_manual_cast(self, x)
        # 型を揃える
        if x.dtype != weight.dtype:
            x = x.to(weight.dtype)
        with main_stream_worker(weight, bias, signal):
            return torch.nn.functional.linear(x, weight, bias)
    else:
        weight, bias = get_weight_and_bias(self)
        # 型を揃える
        if x.dtype != weight.dtype:
            x = x.to(weight.dtype)
        return torch.nn.functional.linear(x, weight, bias)
  • 追加点:「型を揃える」処理

    • `if x.dtype != weight.dtype: x = x.to(weight.dtype)`

    • これにより、入力`x`の型が重み`weight`の型と違う場合、自動で揃えてから計算します。


4. なぜこの修正が必要だったか

  • FLUX.1-devはbfloat16で保存されていることが多い

  • Forgeや他のモデルはfloat16(Half)で動作していることが多い

  • PyTorch 2.x以降では、型不一致のまま演算できなくなった

  • そこで、「forward時に型を揃える」ことで、float16/bfloat16混在環境でも安全に動作するようになります。


5. 効果

  • mat1/mat2のdtype不一致エラーが完全に解消

  • FLUX.1-dev本体・LoRA・ControlNetなど、bfloat16/float16が混在するモデルもForgeで安定動作


まとめ

  • 修正前:型不一致時にエラー

  • 修正後:自動で型を揃えてエラー回避

  • 効果:FLUX.1-devやLoRAなど、bfloat16/float16が混在するモデルもForgeで安定動作

backend/modules/k_prediction.py

修正後のコード(現在の状態)

def apply_mu_transform(self, seq_len=4096, base_seq_len=256, max_seq_len=4096, base_shift=0.5, max_shift=1.15, mu=None):
    # TODO: Add an UI option to let user choose whether to call this in each generation to bind latent size to sigmas
    # And some cases may want their own mu values or other parameters
    if mu is None:
        self.mu = calculate_shift(image_seq_len=seq_len, base_seq_len=base_seq_len, max_seq_len=max_seq_len, base_shift=base_shift, max_shift=max_shift)
    else:
        self.mu = mu
    t = torch.arange(1, self.pseudo_timestep_range + 1, 1) / self.pseudo_timestep_range
    scheduler = FlowMatchEulerDiscreteScheduler()
    sigmas = scheduler.time_shift(self.mu, 1.0, t)
    self.register_buffer('sigmas', sigmas)

### 1. **変数名の変更**

# 修正前

sigmas = torch.arange(1, self.pseudo_timestep_range + 1, 1) / self.pseudo_timestep_range

# 修正後

t = torch.arange(1, self.pseudo_timestep_range + 1, 1) / self.pseudo_timestep_range

### 2. **メソッド呼び出し方法の変更**

# 修正前

sigmas = FlowMatchEulerDiscreteScheduler.time_shift(None, self.mu, 1.0, sigmas)

# 修正後

scheduler = FlowMatchEulerDiscreteScheduler()
sigmas = scheduler.time_shift(self.mu, 1.0, t)

### **問題の原因**

1. **静的メソッド呼び出しの誤解**: `time_shift` を静的メソッドとして呼び出そうとしていた

2. **引数の順序の間違い**: `None` を最初の引数として渡していた

3. **変数名の混乱**: `sigmas` という変数名で時間ステップを表現していた

### **修正の理由**

1. **インスタンスメソッドの正しい呼び出し**: `FlowMatchEulerDiscreteScheduler` はインスタンスメソッドとして `time_shift` を提供しているため、インスタンスを作成してから呼び出す必要がある

2. **引数順序の修正**: `time_shift(mu, sigma, t)` の順序で、`t` が時間ステップであることを明確化

3. **変数名の明確化**: `t` という変数名で時間ステップであることを明示

### **技術的な背景**

- **Fluxモデル**: 新しいタイプの拡散モデルで、従来のDDPMとは異なるスケジューリング方式を使用

- **time_shift**: Fluxモデル特有の時間シフト機能で、ノイズスケジュールを動的に調整する

- **mu**: Fluxモデルの重要なパラメータで、ノイズスケジュールの形状を決定する

### **修正の効果**

この修正により:

1. **エラーの解消**: `TypeError: missing 1 required positional argument: 't'` エラーが解決

2. **正しい動作**: Fluxモデルのノイズスケジュールが正しく計算される

3. **安定性の向上**: モデル読み込み時のクラッシュが防止される

これで、Flux1モデルが正常に読み込まれ、画像生成が可能になります。

修正ファイル

\backend\operations.py

backend/modules/k_prediction.py

やっと、ForgeでのFlux1が復活しました。

半年以上、ForgeでのFlux1生成はやっていなかったので、速度感覚も既に忘れていましたが、今生成してみると、RTX4070 12GB環境では何とQ8.GGUFよりもfp16.satetensorsの方が速いです。

GGUF時のVRAM消費量が、12GBを超えてしまう為です。当時、こうだったか?…が既に忘却の彼方で覚えていません。

FLUX.1-dev-ControlNet-Union-Pro-2.0-fp8

いつの間にか、Flux用のUnion ControlNetモデルが2.0に更新されていましたので、記事を改訂しています。

予告

尚、この改造の後、以下の様にForgeでのSage-Attention2++適用に成功しています。只今多彩な環境下でのテスト中なので、問題なければ後日コードを公開します。

Flux1生成に関しては、FA-3より確実に速いです。

以下の様に、SAの有効無効を切り替えるボタンを設置しています。要するに、元ネタはForge Classicですよ。

いいなと思ったら応援しよう!