Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2583c237f2 | ||
|
|
c83958c457 | ||
|
|
a0b2d99bc7 | ||
|
|
ba145c4722 | ||
|
|
c21dbfe1e5 | ||
|
|
fc38b5c998 | ||
|
|
d26fcf6fc8 | ||
|
|
3d8827f132 | ||
|
|
62bef2e275 | ||
|
|
fecdfe01df | ||
|
|
c5610837e4 | ||
|
|
21ac7cf0cb | ||
|
|
4930d862d5 | ||
|
|
e8b727f914 | ||
|
|
389aedfd17 | ||
|
|
99824b2ee5 | ||
|
|
4bb226aabb |
@@ -0,0 +1,27 @@
|
||||
name: Publish to Comfy registry
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- "pyproject.toml"
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
publish-node:
|
||||
name: Publish Custom Node to registry
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'Reithan' }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Publish Custom Node
|
||||
uses: Comfy-Org/publish-node-action@v1
|
||||
with:
|
||||
personal_access_token: ${{ secrets.COMFY_REGISTRY_KEY }}
|
||||
+27
-195
@@ -13,6 +13,8 @@ class PredictionType(Enum):
|
||||
_RAW_TO_ENUM = {
|
||||
"eps": PredictionType.EPS,
|
||||
"epsilon": PredictionType.EPS,
|
||||
"flux": PredictionType.EPS,
|
||||
"chroma": PredictionType.EPS,
|
||||
"v": PredictionType.V,
|
||||
"v_prediction": PredictionType.V,
|
||||
"x0": PredictionType.X0,
|
||||
@@ -23,9 +25,9 @@ class NRS:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": { "model": ("MODEL",),
|
||||
"skew": ("FLOAT", {"default": 4.0, "min": -30.0, "max": 30.0, "step": 0.01}),
|
||||
"stretch": ("FLOAT", {"default": 2.0, "min": -30.0, "max": 30.0, "step": 0.01}),
|
||||
"squash": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"skew": ("FLOAT", {"default": 2.00, "min": -30.0, "max": 30.0, "step": 0.01}),
|
||||
"stretch": ("FLOAT", {"default": 5.00, "min": -30.0, "max": 30.0, "step": 0.01}),
|
||||
"squash": ("FLOAT", {"default": 0.75, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
}}
|
||||
RETURN_TYPES = ("MODEL",)
|
||||
FUNCTION = "patch"
|
||||
@@ -161,7 +163,7 @@ class NRS:
|
||||
sigma = sigma.view(sigma.shape[:1] + (1,) * (cond.ndim - 1))
|
||||
sig_root = (sigma ** 2 + 1).sqrt()
|
||||
|
||||
nrs_cond, nrs_uncond = None, None
|
||||
x_div, nrs_cond, nrs_uncond = None, None, None
|
||||
match self.__OPERATION_SPACE:
|
||||
case PredictionType.V:
|
||||
x_div, nrs_cond, nrs_uncond = self._convert_to_v_space(x_orig, sig_root, sigma, cond, uncond)
|
||||
@@ -174,201 +176,31 @@ class NRS:
|
||||
case _:
|
||||
raise RuntimeError("NRS.nrs: Invalid PredictionType used.")
|
||||
|
||||
x_final = None
|
||||
match "v0.6.0":
|
||||
case "v1":
|
||||
# displace cond by rejection of uncond on cond
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c = (u_dot_c / c_dot_c) * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
displaced = (nrs_cond - skew * u_rej_c)
|
||||
logging.debug(f"NRS.nrs: displaced")
|
||||
def _dot(a, b):
|
||||
return (a*b).sum(dim=1, keepdim=True) # [B,C,W,H] => [B,1,W,H]
|
||||
|
||||
# squash displaced vector towards len(cond) based on squash scale
|
||||
d_len_sq = torch.sum(displaced * displaced, dim=-1, keepdim=True)
|
||||
squash_scale = (1 - squash) + squash * ((c_dot_c/d_len_sq) ** 0.5)
|
||||
squashed = displaced * squash_scale
|
||||
logging.debug(f"NRS.nrs: squashed")
|
||||
def _nrm2(v):
|
||||
return _dot(v, v)
|
||||
|
||||
# stretch turned vector towards cond based on stretch scale
|
||||
sq_dot_c = torch.sum(squashed * nrs_cond, dim=-1, keepdim=True)
|
||||
sq_on_c = (sq_dot_c / c_dot_c) * nrs_cond
|
||||
x_final = squashed + sq_on_c * stretch
|
||||
logging.debug(f"NRS.nrs: final")
|
||||
case "v2":
|
||||
# displace cond by rejection of uncond on cond
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c_mag = (u_dot_c / c_dot_c)
|
||||
u_on_c = u_on_c_mag * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
displaced = nrs_cond + stretch * (nrs_cond - torch.clamp(u_dot_c / c_dot_c, min=0, max=1) * nrs_cond) - skew * u_rej_c
|
||||
logging.debug(f"NRS.nrs: displaced & stretched")
|
||||
eps = torch.finfo(nrs_cond.dtype).eps
|
||||
c_dot_c = _nrm2(nrs_cond) + eps # [B,1,W,H]
|
||||
u_dot_c = _dot(nrs_uncond, nrs_cond) # [B,1,W,H]
|
||||
u_on_c = (u_dot_c / c_dot_c) * nrs_cond # [B,1,W,H] * [B,C,H,W]
|
||||
|
||||
# Amplify Cond based on length compared to projection of uncond
|
||||
proj_diff = nrs_cond - u_on_c
|
||||
stretched = nrs_cond + (stretch * proj_diff)
|
||||
|
||||
# squash displaced vector towards len(cond) based on squash scale
|
||||
d_len_sq = torch.sum(displaced * displaced, dim=-1, keepdim=True)
|
||||
squash_scale = (1 - squash) + squash * ((c_dot_c/d_len_sq) ** 0.5)
|
||||
x_final = displaced * squash_scale
|
||||
logging.debug(f"NRS.nrs: final")
|
||||
case "v3":
|
||||
# displace cond by rejection of uncond on cond
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c_mag = (u_dot_c / c_dot_c)
|
||||
u_on_c = u_on_c_mag * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
displaced = (nrs_cond - skew * u_rej_c)
|
||||
logging.debug(f"NRS.nrs: displaced")
|
||||
# Skew/Steer Conf based on rejection of uncond on cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
skewed = stretched - (skew * u_rej_c)
|
||||
|
||||
# squash displaced vector towards len(cond) based on squash scale
|
||||
d_len_sq = torch.sum(displaced * displaced, dim=-1, keepdim=True)
|
||||
squash_scale = (1 - squash) + squash * ((c_dot_c/d_len_sq) ** 0.5)
|
||||
# Squash final length back down to original length of cond
|
||||
cond_len = nrs_cond.norm(dim=1, keepdim=True)
|
||||
nrs_len = skewed.norm(dim=1, keepdim=True) + eps
|
||||
|
||||
# stretch vector towards 2*len(cond) - len(u_on_c)
|
||||
c_len = c_dot_c ** 0.5
|
||||
stretch_scale = (1 - stretch) + stretch * (2 * c_len - u_on_c_mag)/c_len
|
||||
|
||||
x_final = displaced * squash_scale * stretch_scale
|
||||
logging.debug(f"NRS.nrs: final")
|
||||
case "v4":
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c_mag = (u_dot_c / c_dot_c)
|
||||
u_on_c = u_on_c_mag * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
rej_dor_rej = torch.sum(u_rej_c * u_rej_c, dim=-1, keepdim=True)
|
||||
x_final = (nrs_cond - squash * u_rej_c + stretch * nrs_cond * ((rej_dor_rej/c_dot_c) ** 0.5))
|
||||
logging.debug(f"NRS.nrs: displaced")
|
||||
case "v0.4.1":
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c_mag = (u_dot_c / c_dot_c)
|
||||
u_on_c = u_on_c_mag * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
rej_dor_rej = torch.sum(u_rej_c * u_rej_c, dim=-1, keepdim=True)
|
||||
stretched = nrs_cond + stretch * nrs_cond * ((rej_dor_rej/c_dot_c) ** 0.5)
|
||||
skewed = stretched - skew * u_rej_c
|
||||
sk_dot_sk = torch.sum(skewed * skewed, dim=-1, keepdim=True)
|
||||
squash_scale = (1 - squash) + squash * ((c_dot_c/sk_dot_sk) ** 0.5)
|
||||
x_final = skewed * squash_scale
|
||||
logging.debug(f"NRS.nrs: displaced")
|
||||
case "v0.4.2":
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c_mag = (u_dot_c / c_dot_c)
|
||||
u_on_c = u_on_c_mag * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
proj_len = torch.sum(u_on_c * u_on_c, dim=-1, keepdim=True) ** 0.5
|
||||
cond_len = c_dot_c ** 0.5
|
||||
stretched = nrs_cond * (1 + stretch * torch.abs(cond_len - proj_len) / cond_len)
|
||||
skewed = stretched - skew * u_rej_c
|
||||
sk_dot_sk = torch.sum(skewed * skewed, dim=-1, keepdim=True)
|
||||
squash_scale = (1 - squash) + squash * cond_len / (sk_dot_sk ** 0.5)
|
||||
x_final = skewed * squash_scale
|
||||
logging.debug(f"NRS.nrs: displaced")
|
||||
case "v0.4.3":
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c_mag = (u_dot_c / c_dot_c)
|
||||
u_on_c = u_on_c_mag * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
proj_len = torch.sum(u_on_c * u_on_c, dim=-1, keepdim=True) ** 0.5
|
||||
cond_len = c_dot_c ** 0.5
|
||||
stretched = nrs_cond * (1 + stretch * (cond_len - proj_len) / cond_len)
|
||||
skewed = stretched - skew * u_rej_c
|
||||
sk_dot_sk = torch.sum(skewed * skewed, dim=-1, keepdim=True)
|
||||
squash_scale = (1 - squash) + squash * cond_len / (sk_dot_sk ** 0.5)
|
||||
x_final = skewed * squash_scale
|
||||
logging.debug(f"NRS.nrs: displaced")
|
||||
case "v0.4.4":
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c_mag = (u_dot_c / c_dot_c)
|
||||
u_on_c = u_on_c_mag * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
cond_len = c_dot_c ** 0.5
|
||||
proj_diff = nrs_cond - u_on_c
|
||||
proj_diff_len = torch.sum(proj_diff * proj_diff, dim=-1, keepdim=True) ** 0.5
|
||||
stretched = nrs_cond * (1 + stretch * proj_diff_len / cond_len)
|
||||
skewed = stretched - skew * u_rej_c
|
||||
sk_dot_sk = torch.sum(skewed * skewed, dim=-1, keepdim=True)
|
||||
squash_scale = (1 - squash) + squash * cond_len / (sk_dot_sk ** 0.5)
|
||||
x_final = skewed * squash_scale
|
||||
logging.debug(f"NRS.nrs: displaced")
|
||||
case "v0.4.5":
|
||||
u_dot_c = torch.sum(nrs_uncond * nrs_cond, dim=-1, keepdim=True)
|
||||
c_dot_c = torch.sum(nrs_cond * nrs_cond, dim=-1, keepdim=True)
|
||||
u_on_c_mag = (u_dot_c / c_dot_c)
|
||||
u_on_c = u_on_c_mag * nrs_cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
cond_len = c_dot_c ** 0.5
|
||||
proj_diff = nrs_cond - u_on_c
|
||||
|
||||
# Amplify Cond based on length compared to projection of uncond
|
||||
stretched = nrs_cond + (stretch * proj_diff)
|
||||
|
||||
# Skew/Steer Conf based on rejection of uncond on cond
|
||||
skewed = stretched - skew * u_rej_c
|
||||
|
||||
# Squash final length back down to original length of cond
|
||||
sk_dot_sk = torch.sum(skewed * skewed, dim=-1, keepdim=True)
|
||||
squash_scale = (1 - squash) + squash * cond_len / (sk_dot_sk ** 0.5)
|
||||
x_final = skewed * squash_scale
|
||||
case "v0.5.0":
|
||||
def _dot(a, b):
|
||||
return (a*b).flatten(2).sum(dim=2, keepdim=True) # [B,C,W,H] => [B,C,1]
|
||||
|
||||
def _nrm2(v):
|
||||
return _dot(v, v)
|
||||
|
||||
eps = torch.finfo(nrs_cond.dtype).eps
|
||||
c_dot_c = _nrm2(nrs_cond) + eps # [B,1]
|
||||
u_dot_c = _dot(nrs_uncond, nrs_cond) # [B,1]
|
||||
|
||||
u_on_c = (u_dot_c / c_dot_c).unsqueeze(-1) * nrs_cond # [B,1,1,1] * [B,C,H,W]
|
||||
|
||||
# Amplify Cond based on length compared to projection of uncond
|
||||
proj_diff = nrs_cond - u_on_c
|
||||
stretched = nrs_cond + (stretch * proj_diff)
|
||||
|
||||
# Skew/Steer Conf based on rejection of uncond on cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
skewed = stretched - (skew * u_rej_c)
|
||||
|
||||
# Squash final length back down to original length of cond
|
||||
cond_len = torch.sqrt(c_dot_c) # [B,1]
|
||||
nrs_len = torch.sqrt(_nrm2(skewed)) + eps # [B,1]
|
||||
|
||||
squash_scale = (1 - squash) + (squash * (cond_len / nrs_len))
|
||||
x_final = skewed * squash_scale.unsqueeze(-1)
|
||||
case "v0.6.0":
|
||||
def _dot(a, b):
|
||||
return (a*b).sum(dim=1, keepdim=True) # [B,C,W,H] => [B,1,W,H]
|
||||
|
||||
def _nrm2(v):
|
||||
return _dot(v, v)
|
||||
|
||||
eps = torch.finfo(nrs_cond.dtype).eps
|
||||
c_dot_c = _nrm2(nrs_cond) + eps # [B,1]
|
||||
u_dot_c = _dot(nrs_uncond, nrs_cond) # [B,1]
|
||||
|
||||
u_on_c = (u_dot_c / c_dot_c) * nrs_cond # [B,1,1,1] * [B,C,H,W]
|
||||
|
||||
# Amplify Cond based on length compared to projection of uncond
|
||||
proj_diff = nrs_cond - u_on_c
|
||||
stretched = nrs_cond + (stretch * proj_diff)
|
||||
|
||||
# Skew/Steer Conf based on rejection of uncond on cond
|
||||
u_rej_c = nrs_uncond - u_on_c
|
||||
skewed = stretched - (skew * u_rej_c)
|
||||
|
||||
# Squash final length back down to original length of cond
|
||||
cond_len = cond.norm(dim=1, keepdim=True)
|
||||
nrs_len = skewed.norm(dim=1, keepdim=True)
|
||||
|
||||
squash_scale = (1 - squash) + (squash * (cond_len / nrs_len))
|
||||
x_final = skewed * squash_scale
|
||||
squash_scale = (1 - squash) + (squash * (cond_len / nrs_len))
|
||||
x_final = skewed * squash_scale
|
||||
|
||||
match self.__OPERATION_SPACE:
|
||||
case PredictionType.V:
|
||||
@@ -388,4 +220,4 @@ class NRS:
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"NRS": NRS,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
[](https://github.com/Reithan/negative_rejection_steering/actions/workflows/github-code-scanning/codeql)
|
||||
[](https://registry.comfy.org/nodes/negative_rejection_steering)
|
||||
|
||||
# Negative Rejection Steering
|
||||
NRS seeks to replace the 'naive' linear interpolation of Classifier Free Guidance with a more nuanced and composable steering of the generation process with better mathematical basis.
|
||||
|
||||
@@ -54,11 +57,11 @@ NRS seeks to replace the 'naive' linear interpolation of Classifier Free Guidanc
|
||||
#### Stretch
|
||||
**Stretch** changes the intensity of generated elements that align more with your positive prompt than the negative. This 'hits the gas' on any elements that are more strongly aligned with your positive prompt than your negative, and 'hit the brakes' on the opposite.
|
||||
#### Squash
|
||||
**Squash** is the speed limit. At 0.0 Squash, each diffusion step receives the full intensity you set from Skew and Stretch, while 1.0 Squash ensures each step has only the original step size output by the model. This setting has no effect unless you have a non-zero Skew value. Squash will 'soften' the effects of Skew and Stretch as it's raised, but the 'removed' Skew and Stretch intensity is replaced by enhanced micro-detailing and 'burn'. Squash should generally be left low and used as a 'finishing' step after dialing in a decent Skew and Stretch value.
|
||||
**Squash** is the speed limit. At 0.0 Squash, each diffusion step receives the full intensity you set from Skew and Stretch, while 1.0 Squash ensures each step has only the original step size output by the model. This setting will only remove intensity unless you have a non-zero Skew value. Squash will 'soften' the effects of Skew and Stretch as it's raised, but the 'removed' Skew and Stretch intensity is replaced by enhanced micro-detailing and 'burn'. Squash should generally be left low and used as a 'finishing' step after dialing in a decent Skew and Stretch value.
|
||||
|
||||
## Beginner How-To
|
||||
1. Set Skew to your normal CFG Scale setting and Stretch to 1/2 your normal CFG Scale. Set Squash to 0.0.<br>
|
||||
*Alternatively, try starting at 1/1/0.0 to get a baseline.*
|
||||
1. Set Skew to 1/2 of your normal CFG Scale setting and Stretch to your full normal CFG Scale. Set Squash to 0.0.<br>
|
||||
*Alternatively, try starting with the default of 2/5/0.75, or at 1/1/1 to get a baseline.*
|
||||
2. Test some outputs. Results should be similar in quality to CFG.
|
||||
3. Adjust Skew to change the intensity of your outputs adherence to your positive and negative prompts. This primarily effects composition of the output.
|
||||
4. Adjust Stretch to intensify your positive prompt's aspects and colors where they differ from the negative prompt. This primarily effects color and texture.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[project]
|
||||
name = "negative_rejection_steering"
|
||||
description = "NRS seeks to replace the 'naive' linear interpolation of Classifier Free Guidance with a more nuanced and composable steering of the generation process with better mathematical basis."
|
||||
authors = [{name = "Bryan O'Malley", email = "bo122081@hotmail.com"}]
|
||||
version = "0.7.3"
|
||||
license = {file = "LICENSE"}
|
||||
readme = "README.md"
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/Reithan/negative_rejection_steering"
|
||||
# Used by Comfy Registry https://comfyregistry.org
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "reithan"
|
||||
DisplayName = "Negative Rejection Steering"
|
||||
Icon = "https://raw.githubusercontent.com/Reithan/negative_rejection_steering/main/icon.png"
|
||||
@@ -11,9 +11,9 @@ class NRSScript(scripts.Script):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.enabled = False
|
||||
self.skew = 4.0
|
||||
self.stretch = 2.0
|
||||
self.squash = 0.0
|
||||
self.skew = 2.00
|
||||
self.stretch = 5.00
|
||||
self.squash = 0.75
|
||||
|
||||
sorting_priority = 5
|
||||
|
||||
|
||||
Reference in New Issue
Block a user