BranchOne / head.py
ndemirdemir's picture
Release BranchOne FP16 text and image decision model
f78d5a7 verified
Raw History Blame Contribute Delete
1.82 kB
"""Shared candidate scorer; checkpoint parameter names are preserved."""
from torch import nn
TARGETS = [
"q_proj",
"k_proj",
"v_proj",
"o_proj",
"gate_proj",
"up_proj",
"down_proj",
"in_proj_qkv",
"in_proj_z",
"in_proj_a",
"in_proj_b",
"out_proj",
]
class CandidateHead(nn.Module):
def __init__(self, hidden=1024, width=256, comparison=False):
super().__init__()
self.width = width
self.comparison = comparison
# Common layers are initialized in the same order for both variants.
self.norm = nn.LayerNorm(hidden)
self.project = nn.Linear(hidden, width)
self.ff_norm = nn.LayerNorm(width)
self.ff = nn.Sequential(
nn.Linear(width, width * 2), nn.GELU(), nn.Linear(width * 2, width)
)
self.final_norm = nn.LayerNorm(width)
self.scorer = nn.Linear(width, 1)
if comparison:
self.set_norm = nn.LayerNorm(width)
self.attention = nn.MultiheadAttention(
width, 4, dropout=0.0, batch_first=True
)
# Start as the independent head; learn interactions without perturbing its initial logits.
nn.init.zeros_(self.attention.out_proj.weight)
nn.init.zeros_(self.attention.out_proj.bias)
def forward(self, h, valid, return_features=False):
u = self.project(self.norm(h.float()))
if self.comparison:
q = self.set_norm(u)
delta, _ = self.attention(
q, q, q, key_padding_mask=~valid, need_weights=False
)
u = u + delta
u = u + self.ff(self.ff_norm(u))
u = self.final_norm(u)
z = self.scorer(u).squeeze(-1).masked_fill(~valid, -1e9)
return (z, u) if return_features else z