Based on good NSFW Lora/LoKr, I've experimented with reduced size/weight: dropping the DIT/UNET layer part because the model already know how to do NSFW. keeping only the text encoder/fusion layers from the NSFW lora.
This technique can reduce Krea2 lora weight (reduce ANY lora/lokr weight)
exemple with this lora 218mo => 13.5mo
exemple with LoKr 1.46go => 40mo or even 20mo in some case
I simply use the safetensors python lib and keep only the txtfusion.layerwise_blocks and txtfusion.refiner layers from NSFW lora. Dropping the other layers as the model know how to draw nsfw stuff ;).
I wanted to experiment with the Krea2 model and understand two things:
If the model was trainned on NSFW (I think it was or it couldn't draw NSFW without UNET/DIT layer part)
Where is the NSFW influence in the model: txtfusion.layerwise_blocks seems to have 80% of the NSFW decision making and txtfusion.refiner 20% ish. txtfusion.projector is a small vector and I think it should not be touched too much (it merge the 12 big vector from qwen3vl) but doesn't directly influence NSFW.
Detail and code at the end.
detail of the token handling before DIT as I think I understand it (maybe wrong):
'''
Frozen Qwen3-VL-4B (Text Encoder)
(hidden dim ~2560)
|
| Select 12 specific layers
| [2,5,8,11,14,17,20,23,26,29,32,35]
v
12 x (Seq_len, 2560) hidden states
|
| Stack / Concat along layer dim
v
[12, Seq_len, 2560] ← multilayer input
|
+---------------------------------------------------+
| txtfusion.layerwise_blocks |
| (2 blocks, cross-layer attn) |
+---------------------------------------------------+
|
Block 0:
- prenorm (RMSNorm scale [2560])
- attn: wq/wk/wv/wo/gate [2560,2560] + qknorm
- mlp: down[2560→6912] / gate[6912,2560] / up[6912,2560]
- postnorm (RMSNorm scale [2560])
|
Block 1: (same structure as Block 0)
|
v
Fused layer-wise features [Seq_len, 2560]
|
+---------------------------------------------------+
| txtfusion.projector |
| Linear(12 → 1) weight [1, 12] |
+---------------------------------------------------+
|
v
Single fused representation per token
(or pooled/processed) [Seq_len, 2560]
|
+---------------------------------------------------+
| txtfusion.refiner_blocks |
| (2 bidirectional transformer blocks)|
+---------------------------------------------------+
|
Refiner Block 0:
- prenorm (RMSNorm [2560])
- attn: wq/wk/wv/wo/gate [2560,2560] + qknorm
- mlp: down/gate/up (same as above)
- postnorm
|
Refiner Block 1: (identical structure)
|
v
Refined bidirectional text features [Seq_len, 2560]
|
+---------------------------------------------------+
| txtmlp |
| (final text MLP / projection) |
+---------------------------------------------------+
|
txtmlp.0.scale [2560] ← residual / scaling
|
txtmlp.1: Linear [2560 → 6144] + bias
|
txtmlp.3: Linear [6144 → 6144] + bias
|
v
Final conditioned text embeddings
(ready for cross-attention into DiT)
|
↓
Injected into first DiT / unet_blocks
(via cross-attention in the main transformer)Code for the layer stripping:
from safetensors import safe_open
from safetensors.torch import save_file
from pathlib import Path
def strip_layers(input_file, output_file, remove_prefixes):
tensors = {}
with safe_open(input_file, framework="pt", device="cpu") as f:
metadata = f.metadata()
for key in f.keys():
if any(key.startswith(prefix) for prefix in remove_prefixes):
print(f"Removing {key}")
continue
tensors[key] = f.get_tensor(key)
save_file(tensors, output_file, metadata=metadata)
inputFile = "i:/ComfyUI_windows_portable/ComfyUI/models/loras/krea2/KNPV3_1.safetensors"
stripLayer = [
# linked to the DIT/UNET
'blocks', 'first', 'last_linear', 'last.linear'
# liked to unet/DIT time MLP and timestep projection
'tmlp_', 'tproj_1',
# keep commented for NSFW
#'txtfusion.layerwise_blocks', 'txtfusion_layerwise_blocks', # <--- full censor
#'txtfusion.projector', 'txtfusion_projector', # <-- zero impact
#'txtfusion.refiner', 'txtfusion_refiner', # <-- tiny impact ==> full censor
]
prefix = 'diffusion_model.'
stripLayer = [prefix+x for x in stripLayer]
print('layer to strip: ', stripLayer)
outPutFile = Path(inputFile).name
strip_layers(
inputFile,
outPutFile,
stripLayer
)Description
Light VRAM Lora with only the text encode part. based on "Mystic XXX" lora
FAQ
Comments (22)
Problem I have with Krea is it always gives pubic hair, even with negatives of it. My guess, as you stated it must have been trained on SOME nsfw to be able to work how it does, but I think they purposefully didn't tag pubic hair so as it's a sort of censor in itself. Like, on some images there is this black smudging that can mess up the pussy, and I believe the model is confused cause pubic hair wasn't tagged, so now it thinks it needs to always add 'hair' to that area which can cause issues. I've even seen the pubic hair persist with strong loras on too. If you've trained a lora, this the same technique people use to make sure the character is 'sfw' by burning it with more repeats and not tagging all the clothing so that whatever isn't tagged becomes part of that character whether you prompt for it or not.
Yes! the model has some NSFW knowledge but not extensive. It need a lora for specific explicit part. The technique allow to get NSFW with a reduced size Lora/LoKR and keep the original "drawing" knowledge of the model, influencing only the "prompt part"
Very nice experiment. You did say it's based on Mystic Lora, but is it v2 or v1 ?
It's the last version the v2 ;)
Please share the code.
I've updated the model details, code at the end! :) some lora have different layer names due to different lora tool. so it may not always works but it's easy to fix if you know python or ask and LLM ;)
Can you do with SNOFS krea 2 lora? it has 1.4gb 😭
I can and it will be down to 40mo, but the author doesn't allow merge :( so I don't think I can do it on SNOFS :(
@Puppet_Master Ask him, wouldn't hurt to try.
Just tried both the original MysticXXX with the trimmed version both together.
They work perfectly fine together, and one doesn't replace the other.
Might as well let him know that too, and actually show him.
@fronyax @Intosica got SNOFS greenlight so it's up ;)
@Puppet_Master Noice, thank you very much!
@Puppet_Master freaking awesome
shout out to both of you! 🦄
SNOFS version?
merge is not authorised for SNOFS, so I'll see if the author allow it. but you have the python script/code at the end on my lora page/details if you want to do it locally :)
got SNOFS greenlight so it's up ;)
@Puppet_Master tyvm
https://civitai.red/models/2725430/krea-2-nsfw-v4?modelVersionId=3085473
The lora you made is amazing! Is Krea 2 NSFW V4 also possible?
Great experiment.
I did A/B tests with the full Realism Engine and the stripped version loses much of the anatomical details that is Realism Engine's best quality. But the stripped version also does not have character bleed like Realism Engine. It's kinda a strange place to be in terms of usability.
I keep the prompt change part but dropped all UNET layer so fro "drawing" the Krea2 model only use it's own thing not the lora
That's a good thing really since I only want to see anatomical details when i need to see them. I'm hoping the lighter version of snofs stops drawing an anus during anal sex because it ruins the illusion. Krea seems to know quite a lot of porn with just the by pass filter lora active at high strength, though prompting for it is tricky. Body part swallowing the penis seems to work better than inserted, it don't know if its the filter or that it does not know what vaginal and anal concepts are but it sure has been trained on them. It just doesn't know how to use those parts of the dataset and so you have to get creative with your prompts which is part of the fun.




