Updated refiner logic + removed obsolete functionality

This commit is contained in:
aimfordeb
2026-04-20 19:44:18 +03:00
committed by GitHub
parent add1369bf6
commit a9c49d752f
+135 -423
View File
@@ -43,6 +43,7 @@ def serializable_to_tensor(d: dict) -> torch.Tensor:
if torch.cuda.is_available():
tensor = tensor.cuda()
return tensor
class FunPackGemmaEmbeddingRefiner:
_tokenizer = None
@@ -82,13 +83,6 @@ class FunPackGemmaEmbeddingRefiner:
"default": "",
"placeholder": "Positive prompt"
}),
"negative_prompt": ("STRING", {
"multiline": True,
"default": "",
"placeholder": "Negative prompt (optional)"
}),
"negative_conditioning": ("CONDITIONING",),
"latent": ("LATENT",),
"reset_session": ("BOOLEAN", {"default": False, "label": "Reset Session (clears ALL history)"}),
"unlimited_history": ("BOOLEAN", {
"default": False,
@@ -107,8 +101,8 @@ class FunPackGemmaEmbeddingRefiner:
}
}
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT", "STRING", "STRING", "STRING")
RETURN_NAMES = ("modified_positive", "modified_negative", "modified_latent", "status", "feedback_question", "training_info")
RETURN_TYPES = ("CONDITIONING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("modified_positive", "status", "feedback_question", "training_info")
FUNCTION = "refine"
CATEGORY = "FunPack/Refinement"
@@ -117,7 +111,7 @@ class FunPackGemmaEmbeddingRefiner:
return float("nan")
def _get_scheduler_factors(self, mode, rating, reward, similarity, iter_num, total_iters,
token_importance, word_groups, full_token_ids, global_adaptive, device):
word_importance, word_groups, global_adaptive, device):
if mode == "original":
base_lr = 0.18 / (1 + 0.08 * (iter_num ** 0.5))
confidence = max(0.2, min(1.0, (rating - 3.0) / 5.0))
@@ -133,17 +127,17 @@ class FunPackGemmaEmbeddingRefiner:
d_coef = 1.0 if mode == "accurate" else 1.8
adaptive_lr = global_adaptive.get("prodigy_lr_base", 1.0)
token_lr_mult = {}
for tid in full_token_ids:
tid_str = str(tid)
if tid_str not in token_importance:
word_lr_mult = {}
for _, _, full_word, _ in word_groups:
wkey = full_word.lower()
if wkey not in word_importance:
continue
g = abs(reward) + 1e-8
if tid_str not in prodigy_d:
prodigy_d[tid_str] = g
if wkey not in prodigy_d:
prodigy_d[wkey] = g
else:
prodigy_d[tid_str] = 0.9 * prodigy_d[tid_str] + 0.1 * g
token_lr_mult[tid_str] = adaptive_lr / (prodigy_d[tid_str] ** 0.5 + 1e-8) * d_coef
prodigy_d[wkey] = 0.9 * prodigy_d[wkey] + 0.1 * g
word_lr_mult[wkey] = adaptive_lr / (prodigy_d[wkey] ** 0.5 + 1e-8) * d_coef
current_step = global_adaptive.setdefault("current_step", 0)
current_step = min(current_step + 1, 500)
@@ -166,20 +160,20 @@ class FunPackGemmaEmbeddingRefiner:
confidence = min(1.0, confidence * 1.3)
exploration_mult = max(0.6, 1.3 - 0.7 * progress)
return lr_scale, confidence, exploration_mult, token_lr_mult
return lr_scale, confidence, exploration_mult, word_lr_mult
def _get_top_tokens(self, token_dict, tokenizer, top_k=10):
if not token_dict or not tokenizer:
return "N/A"
sorted_tokens = sorted(token_dict.items(), key=lambda x: x[1], reverse=True)[:top_k]
top_list = []
for tid_str, score in sorted_tokens:
for key, score in sorted_tokens:
try:
token_text = tokenizer.decode([int(tid_str)], skip_special_tokens=True).strip()
if token_text:
top_list.append(f"{token_text}({score:.2f})")
text = key if isinstance(key, str) else tokenizer.decode([int(key)], skip_special_tokens=True).strip()
if text:
top_list.append(f"{text}({score:.2f})")
except:
top_list.append(f"ID{tid_str}({score:.2f})")
top_list.append(f"{key}({score:.2f})")
return ", ".join(top_list) if top_list else "None"
def _is_valuable_token(self, token_text):
@@ -199,8 +193,7 @@ class FunPackGemmaEmbeddingRefiner:
return True
def refine(self, positive_conditioning, rating: int, refinement_key: str, scheduler_mode: str = "original",
positive_prompt: str = "", negative_prompt: str = "",
negative_conditioning=None, latent=None,
positive_prompt: str = "",
reset_session: bool = False, unlimited_history: bool = False,
seed: int = 0,
feedback_enabled: bool = False, feedback_rating: int = 3):
@@ -217,7 +210,7 @@ class FunPackGemmaEmbeddingRefiner:
json_file = os.path.join(refinements_dir, f"refine_{safe_key}.json")
if not positive_conditioning or not isinstance(positive_conditioning, list) or len(positive_conditioning) == 0:
return (positive_conditioning, negative_conditioning, latent, "ERROR: Empty positive CONDITIONING input", "", "ERROR: No positive conditioning")
return (positive_conditioning, "ERROR: Empty positive CONDITIONING input", "", "ERROR: No positive conditioning")
item = positive_conditioning[0]
if isinstance(item, (list, tuple)) and len(item) >= 2:
@@ -228,32 +221,44 @@ class FunPackGemmaEmbeddingRefiner:
positive_meta = {"pooled_output": None}
if not isinstance(raw_positive, torch.Tensor):
return (positive_conditioning, negative_conditioning, latent, "ERROR: No positive embedding tensor found", "", "ERROR: Invalid embedding")
return (positive_conditioning, "ERROR: No positive embedding tensor found", "", "ERROR: Invalid embedding")
prompt_key = positive_prompt
if negative_prompt:
prompt_key += f" |||NEG||| {negative_prompt}"
if reset_session or not os.path.exists(json_file):
data = {
"refinement_key": refinement_key,
"global_adaptive": {
"token_importance": {}, "infusion_tokens": {}, "token_library": {},
"exploration_base": 0.08, "momentum": None, "avg_reward_ema": 0.0,
"good_ratio": 0.0, "dynamic_sim_threshold": 0.82,
"last_feedback_tid": None, "last_feedback_word": None, "feedbacked_words": [],
"scheduler_mode": scheduler_mode, "prodigy_d": {}, "prodigy_lr_base": 1.0,
"warmup_steps": 8, "total_steps_estimate": 150, "current_step": 0,
"word_importance": {},
"infusion_tokens": {},
"token_library": {},
"exploration_base": 0.08,
"momentum": None,
"avg_reward_ema": 0.0,
"good_ratio": 0.0,
"dynamic_sim_threshold": 0.82,
"last_feedback_word": None,
"feedbacked_words": [],
"scheduler_mode": scheduler_mode,
"prodigy_d": {},
"prodigy_lr_base": 1.0,
"warmup_steps": 8,
"total_steps_estimate": 150,
"current_step": 0,
},
"prompt_histories": {
prompt_key: {"reference_embeds": tensor_to_serializable(raw_positive), "history": [], "last_rating": rating}
prompt_key: {
"reference_embeds": tensor_to_serializable(raw_positive),
"history": [],
"last_rating": rating
}
},
"last_prompt_key": prompt_key,
"pending_feedback": None
}
with open(json_file, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
return (positive_conditioning, negative_conditioning, latent, "✓ New session started – Reference saved", "", "✓ New session started. Reference embedding saved.")
return (positive_conditioning, "✓ New session started – Reference saved", "", "✓ New session started. Reference embedding saved.")
with open(json_file, "r", encoding="utf-8") as f:
data = json.load(f)
@@ -262,7 +267,7 @@ class FunPackGemmaEmbeddingRefiner:
prompt_histories = data.get("prompt_histories", {})
tokenizer = self._get_tokenizer()
# ====================== FEEDBACK STATE MACHINE ======================
# ====================== FEEDBACK STATE MACHINE (WORD-LEVEL) ======================
feedback_question_output = ""
pending = data.get("pending_feedback")
if not feedback_enabled:
@@ -273,23 +278,16 @@ class FunPackGemmaEmbeddingRefiner:
feedback_question_output = "Feedback disabled. Queue cleared."
else:
if pending is not None:
token_id = pending["token_id"]
tid_str = str(token_id)
token_importance = global_adaptive["token_importance"]
old_imp = token_importance.get(tid_str, 1.0)
full_word = pending["full_word"].lower()
word_importance = global_adaptive["word_importance"]
old_imp = word_importance.get(full_word, 1.0)
if feedback_rating == 6:
target_imp = 0.65
else:
target_imp = 0.4 + (feedback_rating / 5.0) * 2.1
new_imp = 0.65 * old_imp + 0.35 * target_imp
token_importance[tid_str] = max(0.3, min(2.5, new_imp))
token_library = global_adaptive.setdefault("token_library", {})
if tid_str in token_library:
if feedback_rating >= 4 and feedback_rating != 6:
token_library[tid_str]["high_rated_count"] += 2
elif feedback_rating <= 2:
token_library[tid_str]["low_rated_count"] += 2
word_importance[full_word] = max(0.3, min(2.8, new_imp))
data["pending_feedback"] = None
with open(json_file, "w", encoding="utf-8") as f:
@@ -303,7 +301,11 @@ class FunPackGemmaEmbeddingRefiner:
is_new_prompt = False
else:
is_new_prompt = True
active = {"reference_embeds": tensor_to_serializable(raw_positive), "history": [], "last_rating": rating}
active = {
"reference_embeds": tensor_to_serializable(raw_positive),
"history": [],
"last_rating": rating
}
prompt_histories[prompt_key] = active
old_reference = serializable_to_tensor(active["reference_embeds"])
@@ -312,71 +314,28 @@ class FunPackGemmaEmbeddingRefiner:
full_token_ids = tokenizer.encode(positive_prompt, add_special_tokens=True) if tokenizer and positive_prompt else []
# ====================== IMPROVED WORD GROUPING (FIXED) ======================
word_groups = []
if tokenizer and positive_prompt and full_token_ids:
try:
# Get raw tokens with Gemma's ▁ word-boundary marker
token_texts = tokenizer.convert_ids_to_tokens(full_token_ids)
i = 0
while i < len(token_texts):
start_idx = i
word_token_ids = [full_token_ids[i]]
# Build the human-readable word
word_str = token_texts[i].replace("▁", "")
i += 1
while i < len(token_texts) and not token_texts[i].startswith("▁"):
word_token_ids.append(full_token_ids[i])
word_str += token_texts[i].replace("▁", "")
i += 1
# Final clean decoded word for feedback
full_word = tokenizer.decode(word_token_ids, skip_special_tokens=True).strip()
if not full_word:
full_word = word_str.strip()
word_groups.append((start_idx, i, full_word, word_token_ids))
except Exception:
# Ultra-safe fallback
word_groups = [(i, i+1, tokenizer.decode([tid]).strip(), [tid]) for i, tid in enumerate(full_token_ids)]
# ====================== CROSS-PROMPT TRANSFER ======================
if is_new_prompt and tokenizer and full_token_ids:
current_set = set(full_token_ids)
token_importance = global_adaptive["token_importance"]
for other_key, other in prompt_histories.items():
if other_key == prompt_key:
# ====================== WORD GROUPING FROM PROMPT ======================
word_groups = [] # (start, end, full_word, word_token_list)
seen = set()
if tokenizer and positive_prompt:
raw_words = [w.strip() for w in positive_prompt.split() if w.strip()]
for word in raw_words:
clean_word = word
lower = clean_word.lower()
if lower in seen or len(clean_word) < 3 or not self._is_valuable_token(clean_word):
continue
other_tokens = set()
for entry in other.get("history", []):
if "prompt" in entry and entry["prompt"]:
try:
other_tokens.update(tokenizer.encode(entry["prompt"], add_special_tokens=True)[:60])
except:
pass
if not other_tokens:
continue
intersection = len(current_set & other_tokens)
union = len(current_set | other_tokens)
sim_weight = intersection / union if union > 0 else 0.0
if sim_weight < 0.3:
continue
for entry in other.get("history", []):
if "prompt" not in entry or not entry["prompt"]:
continue
try:
old_tokens = tokenizer.encode(entry["prompt"], add_special_tokens=True)[:60]
matching = current_set & set(old_tokens)
r = entry.get("rating", 5)
adj = 1.25 if r >= 8 else 0.75 if r <= 3 else None
if adj is not None:
for tid in matching:
tid_str = str(tid)
if tid_str in token_importance:
token_importance[tid_str] = max(0.3, min(2.5, token_importance[tid_str] * (1.0 + (adj - 1.0) * sim_weight)))
except:
pass
seen.add(lower)
word_token_list = tokenizer.encode(clean_word, add_special_tokens=False)
found = False
for start in range(len(full_token_ids) - len(word_token_list) + 1):
if full_token_ids[start:start + len(word_token_list)] == word_token_list:
word_groups.append((start, start + len(word_token_list), clean_word, word_token_list))
found = True
break
if not found and word_token_list:
word_groups.append((0, 1, clean_word, [word_token_list[0]]))
# ====================== SCHEDULER SETUP ======================
global_adaptive["scheduler_mode"] = scheduler_mode
@@ -388,86 +347,34 @@ class FunPackGemmaEmbeddingRefiner:
cur_mean = cur_positive.mean(dim=1)
similarity = F.cosine_similarity(ref_mean, cur_mean, dim=-1).mean().item()
reward = (rating - 5.5) / 4.5
last_rating = active.get("last_rating", 5)
last_reward = (last_rating - 5.5) / 4.5
lr_scale, confidence, exploration_mult, token_lr_mult = self._get_scheduler_factors(
word_importance = global_adaptive.setdefault("word_importance", {})
lr_scale, confidence, exploration_mult, word_lr_mult = self._get_scheduler_factors(
scheduler_mode, rating, reward, similarity, iter_num, total_iters,
global_adaptive["token_importance"], word_groups, full_token_ids, global_adaptive, device
word_importance, word_groups, global_adaptive, device
)
expl = global_adaptive["exploration_base"] * exploration_mult
# ====================== TOKEN LIBRARY UPDATE ======================
token_library = global_adaptive.setdefault("token_library", {})
token_importance = global_adaptive["token_importance"]
infusion_tokens = global_adaptive["infusion_tokens"]
# ====================== WORD IMPORTANCE UPDATE ======================
for start, end, full_word, word_token_list in word_groups:
if not self._is_valuable_token(full_word):
continue
wkey = full_word.lower()
if wkey not in word_importance:
word_importance[wkey] = 1.0
if tokenizer and full_token_ids:
valuable_token_ids = [tid for tid in full_token_ids if self._is_valuable_token(tokenizer.decode([tid]))]
norm_factor = len(valuable_token_ids) if valuable_token_ids else 1
for tid in full_token_ids:
tid_str = str(tid)
if tid_str not in token_library:
token_library[tid_str] = {
"fqc": 0,
"unique_prompts": 0,
"high_rated_count": 0,
"low_rated_count": 0,
"good_pairs": {},
"contrib_score": 0.0,
"redundancy_score": 0.0,
"protected_until": 0
}
lib_entry = token_library[tid_str]
lib_entry["fqc"] += 1
if is_new_prompt:
lib_entry["unique_prompts"] += 1
is_new = True
base_lr = 0.22 / (1 + 0.07 * (len(history) ** 0.5))
group_delta = reward * base_lr * lr_scale * confidence * (1.8 if is_new else 1.0)
if rating >= 8:
for i, tid1 in enumerate(full_token_ids):
tid1_str = str(tid1)
for tid2 in full_token_ids[i+1:]:
tid2_str = str(tid2)
if tid1_str in token_library and tid2_str in token_library:
if tid2_str not in token_library[tid1_str]["good_pairs"]:
token_library[tid1_str]["good_pairs"][tid2_str] = 0
if tid1_str not in token_library[tid2_str]["good_pairs"]:
token_library[tid2_str]["good_pairs"][tid1_str] = 0
token_library[tid1_str]["good_pairs"][tid2_str] += 1
token_library[tid2_str]["good_pairs"][tid1_str] += 1
if tid1_str in token_library:
token_library[tid1_str]["high_rated_count"] += 1
elif rating <= 4:
for tid in full_token_ids:
tid_str = str(tid)
if tid_str in token_library:
token_library[tid_str]["low_rated_count"] += 1
if wkey in word_lr_mult:
group_delta *= word_lr_mult[wkey]
# Safe contextual updates
for start, end, full_word, word_token_list in word_groups:
if not self._is_valuable_token(full_word):
continue
tid_str = str(word_token_list[0])
entry = token_library[tid_str]
entry["contrib_score"] = 0.85 * entry.get("contrib_score", 0.0) + 0.15 * 0.6
if len(word_groups) > 4:
entry["redundancy_score"] = min(1.0, entry.get("redundancy_score", 0.0) + 0.07)
if rating >= 8 and entry.get("contrib_score", 0) > 0.3:
entry["protected_until"] = max(entry.get("protected_until", 0), iter_num + 8)
word_importance[wkey] = max(0.35, min(2.8, word_importance[wkey] + group_delta))
for tid_str, entry in token_library.items():
total_rated = entry["high_rated_count"] + entry["low_rated_count"]
if total_rated == 0:
continue
appears_in_many = entry["unique_prompts"] >= 5
balanced = entry["high_rated_count"] > 0 and entry["low_rated_count"] > 0
mostly_high = entry["high_rated_count"] > entry["low_rated_count"] * 2
entry["is_general"] = appears_in_many and balanced
entry["is_specific"] = not entry["is_general"] and mostly_high and entry["unique_prompts"] <= 8
entry["infusable"] = (entry["is_general"] or entry["is_specific"]) and entry["high_rated_count"] >= 2
# ====================== CORE REFINEMENT ======================
# ====================== CORE REFINEMENT (POSITIVE ONLY) ======================
reference = cur_positive.clone()
momentum = global_adaptive.get("momentum")
if momentum is None or not isinstance(momentum, dict):
@@ -493,91 +400,11 @@ class FunPackGemmaEmbeddingRefiner:
sim_threshold = global_adaptive["dynamic_sim_threshold"]
is_close = (not is_new_prompt) and (similarity >= sim_threshold)
new_token_mask = None
infusion_active = False
infusion_weights = None
if tokenizer and positive_prompt:
try:
embed_seq_len = cur_positive.shape[1] if cur_positive.dim() == 3 else cur_positive.shape[0] if cur_positive.dim() == 2 else 1
prev_token_ids = tokenizer.encode(active.get("last_positive_prompt", ""), add_special_tokens=True) if "last_positive_prompt" in active else []
new_token_mask = torch.ones(embed_seq_len, device=device)
compare_len = min(len(full_token_ids), len(prev_token_ids), embed_seq_len)
for i in range(compare_len):
if full_token_ids[i] != prev_token_ids[i]:
new_token_mask[i] = 0.0
# Word-level importance update with scheduler
for start, end, full_word, word_token_list in word_groups:
if not self._is_valuable_token(full_word):
continue
group_key = str(word_token_list[0])
if group_key not in token_importance:
token_importance[group_key] = 1.0
is_new = any(tid not in prev_token_ids for tid in word_token_list)
update_strength = 1.8 if is_new else 1.0
# Missing token protection
missing_penalty = 1.0
if group_key in token_importance and group_key not in [str(t) for t in full_token_ids]:
if token_library.get(group_key, {}).get("contrib_score", 0) > 0.35:
missing_penalty = 0.78
else:
missing_penalty = 0.55
fqc = token_library.get(group_key, {}).get("fqc", 1)
base_lr = 0.22 / (1 + 0.07 * (fqc ** 0.5))
group_delta = reward * base_lr * lr_scale * confidence * update_strength * missing_penalty
for tid in word_token_list:
tid_str = str(tid)
if tid_str in token_lr_mult:
group_delta *= token_lr_mult[tid_str]
token_importance[tid_str] = max(0.35, min(2.8, token_importance[tid_str] + group_delta))
# Infusion logic (kept from original)
if random.random() < 0.18 and token_library:
infusion_weights = torch.ones(embed_seq_len, device=device)
infusion_any = False
for i, tid in enumerate(full_token_ids[:embed_seq_len]):
tid_str = str(tid)
if tid_str not in token_library or not token_library[tid_str].get("infusable", False):
continue
token_text = tokenizer.decode([int(tid)], skip_special_tokens=True).strip()
if not self._is_valuable_token(token_text):
continue
entry = token_library[tid_str]
if not entry.get("good_pairs"):
continue
pair_scores = [entry["good_pairs"].get(str(ct), 0) for ct in full_token_ids[:embed_seq_len]]
pair_scores = [s for s in pair_scores if s > 0]
if pair_scores:
avg_pair = sum(pair_scores) / len(pair_scores)
if avg_pair > 1.2 or entry.get("is_general", False):
if tid_str not in infusion_tokens:
infusion_tokens[tid_str] = 0.0
infusion_tokens[tid_str] = min(3.0, infusion_tokens[tid_str] + 0.6)
infusion_weights[i] += infusion_tokens[tid_str] * 0.15
infusion_any = True
if infusion_any:
infusion_active = True
except Exception as e:
print(f"[FunPackGemmaEmbeddingRefiner] Token comparison failed: {e}")
new_token_mask = None
infusion_weights = None
# Delta calculation
# Simple new_delta
if is_close and history:
last_entry = history[-1]
mod_data = last_entry.get("modified_embeds")
if mod_data is None:
prev_modified = torch.zeros_like(old_reference)
else:
prev_modified = serializable_to_tensor(mod_data).to(device)
if list(prev_modified.shape) != list(old_reference.shape):
prev_modified = torch.zeros_like(old_reference)
prev_modified = serializable_to_tensor(mod_data).to(device) if mod_data else torch.zeros_like(old_reference)
prev_delta = prev_modified - old_reference
multiplier = max(0.05, 1.0 + reward * 1.45)
if rating == last_rating and rating >= 8:
@@ -588,96 +415,28 @@ class FunPackGemmaEmbeddingRefiner:
good_deltas = []
for entry in history:
if entry.get("rating", 0) >= 7:
mod_data = entry.get("modified_embeds")
if mod_data is None:
continue
mod = serializable_to_tensor(mod_data).to(device)
mod = serializable_to_tensor(entry.get("modified_embeds")).to(device)
if list(mod.shape) == list(old_reference.shape):
good_deltas.append(mod - old_reference)
if good_deltas:
new_delta = torch.stack(good_deltas).mean(dim=0) * (0.7 + reward * 0.4)
else:
if is_new_prompt and isinstance(momentum, torch.Tensor) and list(momentum.shape) == list(reference.shape):
seed_strength = min(0.3, abs(avg_reward_ema) * 0.4)
new_delta = momentum * seed_strength + torch.randn_like(reference) * expl * 0.3
else:
new_delta = torch.randn_like(reference) * expl * 0.45
new_delta = torch.randn_like(reference) * expl * 0.45
# Apply importance, new token boost, infusion
if token_importance and cur_positive.dim() > 1:
# Apply WORD-LEVEL importance
if word_importance and cur_positive.dim() > 1:
seq_len = cur_positive.shape[1] if cur_positive.dim() == 3 else cur_positive.shape[0]
importance_tensor = torch.ones(seq_len, device=device)
for i, tid in enumerate(full_token_ids[:seq_len]):
tid_str = str(tid)
if tid_str in token_importance:
importance_tensor[i] = token_importance[tid_str]
for start, end, full_word, _ in word_groups:
imp = word_importance.get(full_word.lower(), 1.0)
importance_tensor[start:end] = imp
new_delta = new_delta * importance_tensor.unsqueeze(-1)
if new_token_mask is not None:
boost_factor = 1.0 + 1.8 * (1.0 - new_token_mask).unsqueeze(-1)
new_delta = new_delta * boost_factor
if infusion_active and infusion_weights is not None:
new_delta = new_delta * infusion_weights.unsqueeze(-1)
new_positive = reference + new_delta
new_positive = torch.clamp(new_positive, min=-60.0, max=60.0)
norm_factor = reference.norm(dim=-1, keepdim=True) + 1e-8
new_positive = new_positive / (new_positive.norm(dim=-1, keepdim=True) + 1e-8) * norm_factor
# ====================== NEGATIVE REFINEMENT ======================
new_negative = None
negative_meta = {"pooled_output": None}
if negative_conditioning is not None:
neg_item = negative_conditioning[0]
if isinstance(neg_item, (list, tuple)) and len(neg_item) >= 2:
raw_negative = neg_item[0]
negative_meta = neg_item[1] if isinstance(neg_item[1], dict) else {"pooled_output": None}
else:
raw_negative = neg_item if isinstance(neg_item, torch.Tensor) else None
if isinstance(raw_negative, torch.Tensor):
raw_negative = raw_negative.to(device)
neg_strength = 0.15 if rating <= 4 else 0.05
neg_delta = torch.randn_like(raw_negative) * neg_strength * (5.5 - rating) / 4.5
new_negative = raw_negative + neg_delta
new_negative = torch.clamp(new_negative, min=-60.0, max=60.0)
norm_factor_neg = raw_negative.norm(dim=-1, keepdim=True) + 1e-8
new_negative = new_negative / (new_negative.norm(dim=-1, keepdim=True) + 1e-8) * norm_factor_neg
# ====================== LATENT REFINEMENT ======================
modified_latent = latent
latent_info = " | Latent unchanged"
if latent is not None and "samples" in latent:
samples = latent["samples"].to(device) if latent["samples"].device != device else latent["samples"]
current_shape = list(samples.shape)
good_latents = []
bad_latents = []
for entry in history:
if "latent_samples" in entry:
mod_data = entry["latent_samples"]
if mod_data is not None:
mod = serializable_to_tensor(mod_data).to(device)
if list(mod.shape) == current_shape:
if entry.get("rating", 5) >= 8:
good_latents.append(mod)
elif entry.get("rating", 5) <= 3:
bad_latents.append(mod)
if good_latents or bad_latents:
latent_strength = max(0.0, avg_reward_ema * 0.35)
latent_delta = torch.zeros_like(samples)
if good_latents:
mean_good = torch.stack(good_latents).mean(dim=0)
latent_delta += (mean_good - samples) * latent_strength
if bad_latents:
mean_bad = torch.stack(bad_latents).mean(dim=0)
latent_delta -= (mean_bad - samples) * (latent_strength * 0.65)
mask = torch.abs(samples) < 0.05
latent_delta = latent_delta * (~mask).float()
if torch.abs(latent_delta).max() > 1e-4:
new_samples = torch.clamp(samples + latent_delta, min=-12.0, max=12.0)
modified_latent = {"samples": new_samples.cpu(), "noise_mask": latent.get("noise_mask")}
latent_info = f" | Latent ±{len(good_latents)}g/{len(bad_latents)}b"
# Update momentum
momentum = 0.75 * momentum + 0.25 * (new_delta * reward)
global_adaptive["momentum"] = tensor_to_serializable(momentum)
@@ -696,8 +455,6 @@ class FunPackGemmaEmbeddingRefiner:
"similarity": round(similarity, 4),
"prompt": positive_prompt[:180]
}
if latent is not None and "samples" in latent:
history_entry["latent_samples"] = tensor_to_serializable(latent["samples"])
history.append(history_entry)
if not unlimited_history and len(history) > 200:
@@ -715,107 +472,63 @@ class FunPackGemmaEmbeddingRefiner:
data["global_adaptive"] = global_adaptive
# ====================== INTELLIGENT FEEDBACK QUESTION ======================
if feedback_enabled and data.get("pending_feedback") is None and tokenizer and word_groups:
if feedback_enabled and data.get("pending_feedback") is None and word_groups:
feedbacked = set(global_adaptive.get("feedbacked_words", []))
last_word = global_adaptive.get("last_feedback_word")
new_unasked = []
for start, end, full_word, word_token_list in word_groups:
if not self._is_valuable_token(full_word):
candidates = []
for _, _, full_word, word_token_list in word_groups:
if not self._is_valuable_token(full_word) or full_word.lower() in feedbacked:
continue
if full_word.lower() in feedbacked or full_word == last_word:
continue
group_key = str(word_token_list[0])
imp = token_importance.get(group_key, 1.0)
freq = token_library.get(group_key, {}).get("fqc", 1)
score = freq * (1.0 - abs(imp - 1.0))
new_unasked.append((full_word, group_key, score, word_token_list[0]))
if new_unasked:
chosen = max(new_unasked, key=lambda x: x[2])
else:
candidates = []
for start, end, full_word, word_token_list in word_groups:
if not self._is_valuable_token(full_word) or full_word == last_word:
continue
group_key = str(word_token_list[0])
imp = token_importance.get(group_key, 1.0)
freq = token_library.get(group_key, {}).get("fqc", 1)
score = freq * (1.0 - abs(imp - 1.0))
rating_bonus = 1.5 if rating >= 8 or rating <= 3 else 1.0
candidates.append((full_word, group_key, score * rating_bonus, word_token_list[0]))
if candidates:
chosen = max(candidates, key=lambda x: x[2])
else:
chosen = None
if chosen:
full_word, group_key, _, best_tid = chosen
wkey = full_word.lower()
imp = word_importance.get(wkey, 1.0)
score = 1.0 - abs(imp - 1.0)
candidates.append((full_word, wkey, score, word_token_list))
if candidates:
chosen = max(candidates, key=lambda x: x[2])
full_word, wkey, _, word_token_list = chosen
data["pending_feedback"] = {
"token_id": int(best_tid),
"token_text": full_word,
"full_word": full_word,
"word_token_list": word_token_list,
"prompt_key": prompt_key,
"iteration": iter_num
}
global_adaptive["last_feedback_tid"] = int(best_tid)
global_adaptive["last_feedback_word"] = full_word
if full_word.lower() not in feedbacked:
global_adaptive.setdefault("feedbacked_words", []).append(full_word)
global_adaptive.setdefault("feedbacked_words", []).append(full_word)
feedback_question_output = f"Word: '{full_word}' – How well represented? (1=absent, 5=perfect, 6=overrepresented)"
else:
feedback_question_output = "Feedback enabled but no suitable word found in current prompt."
# ====================== TRAINING INFO ======================
current_top = self._get_top_tokens(token_importance, tokenizer, 12)
top_infusion = self._get_top_tokens(infusion_tokens, tokenizer, 8)
current_top = self._get_top_tokens(word_importance, tokenizer, 12)
top_contrib = sorted(
[(k, v.get("contrib_score", 0.0)) for k, v in token_library.items() if v.get("contrib_score", 0.0) > 0.1],
key=lambda x: x[1], reverse=True
)[:10]
top_contrib_str = ", ".join([
f"{tokenizer.decode([int(k)], skip_special_tokens=True).strip()}({v:.2f})"
for k, v in top_contrib
]) or "None"
normalized_reward = max(0.0, min(1.0, (avg_reward_ema + 1.0) / 2.0))
stability_factor = max(0.0, 1.0 - similarity)
raw_loss = (1.0 - normalized_reward) * 0.7 + stability_factor * 0.3
learning_loss = max(0.02, raw_loss * (1.0 - min(0.95, good_ratio * 0.8)))
training_info = (
f"Scheduler: {scheduler_mode.upper()} | Global Step: {global_adaptive['current_step']} | "
f"Scheduler: {scheduler_mode.upper()} | Step: {global_adaptive['current_step']} | "
f"EMA Reward: {avg_reward_ema:+.3f} | Confidence: {confidence:.2f} | LR Scale: {lr_scale:.3f}\n"
f"Exploration: {expl:.3f} | Similarity: {similarity:.4f} | Raw Reward: {reward:+.3f} | Good Ratio: {good_ratio:.1%}\n"
f"Top Importance Tokens: {current_top}\n"
f"Top Contributors (by delta impact): {top_contrib_str}\n"
f"Protected: {sum(1 for v in token_library.values() if v.get('protected_until', 0) > iter_num)} | "
f"Library: {len(token_library)} tokens | Infusion active: {infusion_active}"
f"Exploration: {expl:.3f} | Similarity: {similarity:.4f} | Good Ratio: {good_ratio:.1%}\n"
f"**Learning Loss: {learning_loss:.4f}** (lower is better)\n"
f"Top Word Importance: {current_top}\n"
f"Word Library Size: {len(word_importance)}"
)
if scheduler_mode == "accurate":
training_info += "\n[Accurate] Conservative Prodigy + Cosine decay - slow & stable"
training_info += "\n[Accurate] Conservative • Prodigy + Cosine"
elif scheduler_mode == "aggressive":
training_info += "\n[Aggressive] Fast early boosts - quick style locking"
training_info += "\n[Aggressive] Fast style locking"
# ====================== STATUS ======================
iter_num = len(history)
total_iterations = sum(len(p.get("history", [])) for p in prompt_histories.values())
unique_prompts = len(prompt_histories)
infusion_count = len([v for v in infusion_tokens.values() if v > 0.5])
trend = "↑" if reward > last_reward else "↓" if reward < last_reward else "→"
health = "🚀 Strong convergence" if avg_reward_ema > 0.6 else "✅ Learning well" if avg_reward_ema > 0.3 else "⚠️ Still exploring" if avg_reward_ema > -0.2 else "🔄 Heavy correction"
status = (f"Iter {iter_num} | Total iters {total_iterations} | "
f"History {len(history)} | Unique prompts {unique_prompts} | "
f"Infusion tokens {infusion_count}\n"
status = (f"Iter {iter_num} | Total iters {total_iters} | "
f"History {len(history)} | Unique prompts {len(prompt_histories)}\n"
f"Rating {rating}/10 {trend} | Sim {similarity:.3f} | Reward {reward:+.2f} | "
f"EMA {avg_reward_ema:+.2f} | Good ratio {good_ratio:.0%} | Expl {expl:.3f} | {health}\n"
f"Current focus: {current_top}\n"
f"Top infusion: {top_infusion}\n"
f"Library: {len(token_library)} tokens")
if is_new_prompt:
status += "\n✓ New prompt pair → token overlap transfer applied"
if new_token_mask is not None and (1.0 - new_token_mask).sum() > 0:
status += "\nNew tokens boosted"
if infusion_active:
status += "\n🔀 Token infusion active (delta scaled)"
if rating <= 4:
status += "\nLow rating: importance down-weighted + negative strengthened"
status += latent_info
f"Current focus: {current_top}")
# ====================== FINAL SAVE ======================
with open(json_file, "w", encoding="utf-8") as f:
@@ -823,9 +536,8 @@ class FunPackGemmaEmbeddingRefiner:
# ====================== RETURN ======================
modified_positive = [(new_positive, positive_meta)]
modified_negative = [(new_negative, negative_meta)] if new_negative is not None else negative_conditioning
return (modified_positive, modified_negative, modified_latent, status, feedback_question_output, training_info)
return (modified_positive, status, feedback_question_output, training_info)
# Constants from StoryMem
IMAGE_FACTOR = 28