Download code/src/enhancements/hyperbolic_head.py from savastakan/hitit-cuneiform-ocr: direct link, hf CLI and curl.
- Browser
- Download file 2.23 kB
-
https://huggingface.co/savastakan/hitit-cuneiform-ocr/resolve/main/code/src/enhancements/hyperbolic_head.py
- Command line
-
hf download hf://savastakan/hitit-cuneiform-ocr/code/src/enhancements/hyperbolic_head.py
-
curl -L -o hyperbolic_head.py https://huggingface.co/savastakan/hitit-cuneiform-ocr/resolve/main/code/src/enhancements/hyperbolic_head.py
2.23 kB
| #!/usr/bin/env python3 | |
| """#9 Hyperbolic Poincaré Ball Embeddings for ABZ hierarchy. | |
| Reference: Nickel & Kiela 2017 (arXiv:1705.08039); | |
| Hyperbolic Image Embeddings (Khrulkov 2020 CVPR). | |
| Poincaré ball doğal olarak hiyerarşik yapıları embed eder. | |
| ABZ numaralandırması tree-like → hyperbolic space ideal. | |
| """ | |
| import torch | |
| import torch.nn as nn | |
| def exponential_map(v, c=1.0): | |
| """Exp map at origin: Euclidean → Poincaré ball.""" | |
| v_norm = v.norm(dim=-1, keepdim=True).clamp(min=1e-8) | |
| return torch.tanh(v_norm * c**0.5) * v / (v_norm * c**0.5) | |
| def poincare_distance(x, y, c=1.0): | |
| """Poincaré ball distance (batched).""" | |
| x = x.clamp(max=1-1e-5, min=-(1-1e-5)) | |
| y = y.clamp(max=1-1e-5, min=-(1-1e-5)) | |
| diff = x - y | |
| num = 2 * (diff * diff).sum(-1) | |
| denom = (1 - c * (x*x).sum(-1)) * (1 - c * (y*y).sum(-1)) + 1e-8 | |
| return torch.acosh(torch.clamp(1 + num / denom, min=1+1e-7)) / c**0.5 | |
| class HyperbolicClassifier(nn.Module): | |
| """Maps features → Poincaré ball → distance to class prototypes.""" | |
| def __init__(self, in_dim=1024, n_classes=198, hyperbolic_dim=128, c=1.0): | |
| super().__init__() | |
| self.project = nn.Linear(in_dim, hyperbolic_dim) | |
| # Class prototypes in Euclidean; mapped to hyperbolic during forward | |
| self.class_protos = nn.Parameter(torch.randn(n_classes, hyperbolic_dim) * 0.01) | |
| self.c = c | |
| def forward(self, features): | |
| euc = self.project(features) # (B, H) | |
| hyp = exponential_map(euc, self.c) # Poincaré ball | |
| # Prototypes also in hyperbolic | |
| protos_hyp = exponential_map(self.class_protos, self.c) | |
| # Distance → negative logits (smaller dist = higher logit) | |
| dists = poincare_distance( | |
| hyp.unsqueeze(1), # (B, 1, H) | |
| protos_hyp.unsqueeze(0), # (1, C, H) | |
| self.c | |
| ) # (B, C) | |
| return -dists # negative distance = logits | |
| if __name__ == '__main__': | |
| torch.manual_seed(0) | |
| hc = HyperbolicClassifier(in_dim=1024, n_classes=198, hyperbolic_dim=128) | |
| feats = torch.randn(8, 1024) | |
| logits = hc(feats) | |
| print(f"Hyperbolic logits: {logits.shape}, range: [{logits.min().item():.3f}, {logits.max().item():.3f}]") | |