Download code/pmps/src/direct_match_loss.py from WaitXian/liqi: direct link, hf CLI and curl.
- Browser
- Download file 9.14 kB
-
https://huggingface.co/WaitXian/liqi/resolve/main/code/pmps/src/direct_match_loss.py
- Command line
-
hf download hf://WaitXian/liqi/code/pmps/src/direct_match_loss.py
-
curl -L -o direct_match_loss.py https://huggingface.co/WaitXian/liqi/resolve/main/code/pmps/src/direct_match_loss.py
9.14 kB
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| class DirectMatchSupervisor(nn.Module): | |
| """Direct soft matching supervisor without prototypes. | |
| Ablation variant of PMPSLatentSupervisor: removes the prototype layer | |
| and Sinkhorn-Knopp, directly matching latent and CoT embeddings | |
| in the projected space via cosine similarity. | |
| The matching structure (soft many-to-many, bidirectional, PSA-compatible) | |
| is preserved. The loss changes from cross-entropy on prototype codes to | |
| cosine similarity cross-reconstruction in embedding space. | |
| Interface-compatible with PMPSLatentSupervisor for drop-in replacement. | |
| """ | |
| def __init__( | |
| self, | |
| hidden_dim, | |
| feat_dim=128, | |
| hidden_mlp=512, | |
| match_temperature=0.3, | |
| use_projector=True, | |
| position_bias_strength=0.0, | |
| ): | |
| super().__init__() | |
| self.match_temperature = match_temperature | |
| self.position_bias_strength = position_bias_strength | |
| self.use_projector = use_projector | |
| if use_projector: | |
| self.projector = self._build_projector(hidden_dim, hidden_mlp, feat_dim) | |
| self.feat_dim = feat_dim | |
| else: | |
| self.projector = nn.Identity() | |
| self.feat_dim = hidden_dim | |
| def _build_projector(input_dim, hidden_dim, output_dim): | |
| return nn.Sequential( | |
| nn.Linear(input_dim, hidden_dim), | |
| nn.BatchNorm1d(hidden_dim), | |
| nn.ReLU(inplace=True), | |
| nn.Linear(hidden_dim, output_dim), | |
| ) | |
| def normalize_prototypes(self): | |
| """No-op: no prototypes in direct matching.""" | |
| pass | |
| def _project(self, hidden_states): | |
| """Project (B, N, D) -> L2-normalised (B, N, feat_dim).""" | |
| b, n, d = hidden_states.shape | |
| flat = hidden_states.reshape(b * n, d) | |
| emb = self.projector(flat) | |
| emb = F.normalize(emb, dim=1, p=2) | |
| return emb.reshape(b, n, -1) | |
| def _compute_position_bias(N_c, N_l, strength, cot_mask=None, device=None): | |
| """Gaussian positional bias (identical to PMPSLatentSupervisor).""" | |
| if strength <= 0: | |
| return 0.0 | |
| lat_pos = torch.arange(N_l, device=device, dtype=torch.float32) | |
| lat_pos_norm = lat_pos / max(N_l - 1, 1) | |
| if cot_mask is not None and cot_mask.dim() == 2: | |
| B = cot_mask.size(0) | |
| bias = torch.zeros(B, N_c, N_l, device=device) | |
| for b in range(B): | |
| n_valid = cot_mask[b].sum().long().item() | |
| if n_valid <= 1: | |
| continue | |
| cot_pos = torch.arange(n_valid, device=device, dtype=torch.float32) | |
| cot_pos_norm = cot_pos / max(n_valid - 1, 1) | |
| delta = cot_pos_norm.unsqueeze(1) - lat_pos_norm.unsqueeze(0) | |
| bias[b, :n_valid, :] = -strength * delta.pow(2) | |
| return bias | |
| else: | |
| cot_pos = torch.arange(N_c, device=device, dtype=torch.float32) | |
| cot_pos_norm = cot_pos / max(N_c - 1, 1) | |
| delta = cot_pos_norm.unsqueeze(1) - lat_pos_norm.unsqueeze(0) | |
| bias = -strength * delta.pow(2) | |
| return bias.unsqueeze(0) | |
| def forward(self, latent_hidden, cot_hidden, latent_mask=None, cot_mask=None, | |
| position_bias_strength_override=None): | |
| """Compute direct soft matching loss between latent and CoT embeddings. | |
| Same interface and return format as PMPSLatentSupervisor.forward(). | |
| Matching pipeline: | |
| 1. Project both sides through shared g(·) → L2-normalised z | |
| 2. Compute cosine similarity matrix z_cot · z_lat^T (stop-grad) | |
| 3. Apply PSA positional bias | |
| 4. Softmax → bidirectional matching weights A (no gradient) | |
| 5. Cross-reconstruct: z_hat = A · z (gradient through z) | |
| 6. Loss: 1 - cosine_similarity(z_target, z_hat) | |
| Returns | |
| ------- | |
| loss_c2l : scalar tensor (used for backprop, consistent with PMPS) | |
| log_dict : diagnostic stats | |
| """ | |
| assert latent_hidden.dim() == 3 and cot_hidden.dim() == 3 | |
| assert latent_hidden.size(0) == cot_hidden.size(0) | |
| assert latent_hidden.size(-1) == cot_hidden.size(-1) | |
| B, N_l, _ = latent_hidden.shape | |
| _, N_c, _ = cot_hidden.shape | |
| device = latent_hidden.device | |
| if latent_mask is None: | |
| latent_mask = torch.ones(B, N_l, device=device, dtype=latent_hidden.dtype) | |
| if cot_mask is None: | |
| cot_mask = torch.ones(B, N_c, device=device, dtype=cot_hidden.dtype) | |
| # 1. Project to shared space (gradient flows through projector) | |
| z_lat = self._project(latent_hidden) # (B, N_l, d) | |
| z_cot = self._project(cot_hidden) # (B, N_c, d) | |
| # 2. Compute matching weights (stop-gradient, analogous to Sinkhorn codes) | |
| with torch.no_grad(): | |
| match_logits_c2l = torch.bmm( | |
| z_cot.detach(), z_lat.detach().transpose(1, 2) | |
| ) / self.match_temperature # (B, N_c, N_l) | |
| # PSA positional bias | |
| effective_strength = ( | |
| position_bias_strength_override | |
| if position_bias_strength_override is not None | |
| else self.position_bias_strength | |
| ) | |
| if isinstance(effective_strength, (int, float)) and effective_strength > 0: | |
| pos_bias = self._compute_position_bias( | |
| N_c, N_l, effective_strength, | |
| cot_mask=cot_mask, device=device, | |
| ) | |
| match_logits_c2l = match_logits_c2l + pos_bias | |
| match_logits_l2c = match_logits_c2l.transpose(1, 2) # (B, N_l, N_c) | |
| # Mask out invalid positions | |
| very_neg = torch.finfo(match_logits_c2l.dtype).min / 4 | |
| match_logits_c2l = match_logits_c2l.masked_fill( | |
| ~latent_mask.bool().unsqueeze(1), very_neg | |
| ) | |
| match_logits_l2c = match_logits_l2c.masked_fill( | |
| ~cot_mask.bool().unsqueeze(1), very_neg | |
| ) | |
| A_c2l = F.softmax(match_logits_c2l, dim=-1) # (B, N_c, N_l) | |
| A_l2c = F.softmax(match_logits_l2c, dim=-1) # (B, N_l, N_c) | |
| # 3. Cross-reconstruction (gradient flows through z_lat / z_cot → projector) | |
| z_lat_mix = torch.bmm(A_c2l, z_lat) # (B, N_c, d) | |
| z_cot_mix = torch.bmm(A_l2c, z_cot) # (B, N_l, d) | |
| # 4. Loss: 1 - cosine_similarity (stop-gradient on targets) | |
| cos_c2l = F.cosine_similarity(z_cot.detach(), z_lat_mix, dim=-1) # (B, N_c) | |
| cos_l2c = F.cosine_similarity(z_lat.detach(), z_cot_mix, dim=-1) # (B, N_l) | |
| loss_c2l = ((1 - cos_c2l) * cot_mask).sum() / cot_mask.sum().clamp(min=1) | |
| loss_l2c = ((1 - cos_l2c) * latent_mask).sum() / latent_mask.sum().clamp(min=1) | |
| loss = 0.5 * (loss_c2l + loss_l2c) | |
| # 5. Diagnostics (same format as PMPSLatentSupervisor) | |
| with torch.no_grad(): | |
| # Match entropy | |
| A_c2l_valid = A_c2l[cot_mask.bool()] | |
| match_entropy = -( | |
| A_c2l_valid * A_c2l_valid.clamp(min=1e-8).log() | |
| ).sum(-1).mean() | |
| # Match order correlation (Pearson between CoT position and | |
| # attended latent position) | |
| lat_indices = torch.arange(N_l, device=device, dtype=torch.float32) | |
| attended_lat_pos = ( | |
| A_c2l * lat_indices.view(1, 1, -1) | |
| ).sum(dim=-1) # (B, N_c) | |
| cot_indices = torch.arange(N_c, device=device, dtype=torch.float32) | |
| corr_list = [] | |
| for b in range(B): | |
| valid = cot_mask[b].bool() | |
| x = cot_indices[valid] | |
| y = attended_lat_pos[b, valid] | |
| if len(x) > 2: | |
| vx = x - x.mean() | |
| vy = y - y.mean() | |
| denom = ( | |
| vx.pow(2).sum().sqrt() * vy.pow(2).sum().sqrt() | |
| ).clamp(min=1e-8) | |
| corr = (vx * vy).sum() / denom | |
| if not torch.isnan(corr): | |
| corr_list.append(corr) | |
| match_order_corr = ( | |
| torch.stack(corr_list).mean() | |
| if corr_list | |
| else torch.tensor(0.0, device=device) | |
| ) | |
| avg_cos_c2l = (cos_c2l * cot_mask).sum() / cot_mask.sum().clamp(min=1) | |
| avg_cos_l2c = (cos_l2c * latent_mask).sum() / latent_mask.sum().clamp(min=1) | |
| log_dict = { | |
| "pmps_loss": loss.detach(), | |
| "pmps_loss_c2l": loss_c2l.detach(), | |
| "pmps_loss_l2c": loss_l2c.detach(), | |
| "code_entropy_latent": torch.tensor(0.0, device=device), | |
| "code_entropy_cot": torch.tensor(0.0, device=device), | |
| "match_entropy_c2l": match_entropy, | |
| "match_order_correlation": match_order_corr, | |
| "avg_cosine_c2l": avg_cos_c2l, | |
| "avg_cosine_l2c": avg_cos_l2c, | |
| } | |
| return loss, log_dict | |