メインコンテンツへスキップ
見出し画像

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ですよ。

    画像
     
     

    GJL

     
     
    Road bike&Cycling Road Race, Respecting Mr.Greg LeMond, & Anti Doping, Football, Rock&Pops, History.

    あなたへのおすすめ