Download src/simpleP2G_neigh.py from king159/SimpleP2G: direct link, hf CLI and curl.
- Browser
- Download file 4.69 kB
-
https://huggingface.co/king159/SimpleP2G/resolve/main/src/simpleP2G_neigh.py
- Command line
-
hf download hf://king159/SimpleP2G/src/simpleP2G_neigh.py
-
curl -L -o simpleP2G_neigh.py https://huggingface.co/king159/SimpleP2G/resolve/main/src/simpleP2G_neigh.py
4.69 kB
| """SimpleP2G-cross: target tokens and spatially positioned neighbor tokens.""" | |
| import torch | |
| from torch import nn | |
| from torch.nn import functional as F | |
| from torch.utils.checkpoint import checkpoint | |
| from src.simpleP2G import SimpleP2GDecoderLayer, SimpleP2GRMSNorm | |
| class SimpleP2GNeigh(nn.Module): | |
| """The target-plus-neighbors model used in the main comparison.""" | |
| def __init__(self, config): | |
| super().__init__() | |
| self.config = config | |
| self.decoder_depth = config.decoder_depth | |
| self.num_genes = config.num_genes | |
| self.hidden_size = config.hidden_size | |
| self.cond_size = config.cond_size | |
| self.num_cond_tokens = getattr(config, "num_cond_tokens", 4) | |
| self.num_neighbor_tokens = int(getattr(config, "num_neighbor_tokens", 24)) | |
| if str(getattr(config, "neighbor_position_mode", "relative")) != "relative": | |
| raise ValueError("SimpleP2G-cross requires relative neighbor positions") | |
| for key in ("neighbor_cond_size", "global_cond_size"): | |
| if int(getattr(config, key, self.cond_size)) != self.cond_size: | |
| raise ValueError("All image embeddings must use the same encoder dimension") | |
| if ( | |
| not getattr(config, "use_target_context", True) | |
| or not getattr(config, "use_neighbor_context", True) | |
| or getattr(config, "use_slide_context", False) | |
| or getattr(config, "use_local_summary", False) | |
| or getattr(config, "context_source_balance", 0.0) != 0 | |
| ): | |
| raise ValueError( | |
| "This model requires target + neighbor context; use the separate experiment entry point for other variants" | |
| ) | |
| self.gene_queries = nn.Embedding(self.num_genes, self.hidden_size) | |
| self.label_embed = nn.Sequential( | |
| nn.Linear(self.cond_size, self.cond_size), | |
| nn.SiLU(), | |
| nn.Linear(self.cond_size, self.hidden_size * self.num_cond_tokens), | |
| ) | |
| self.neighbor_proj = nn.Sequential( | |
| nn.Linear(self.cond_size, self.hidden_size), | |
| nn.SiLU(), | |
| nn.Linear(self.hidden_size, self.hidden_size), | |
| ) | |
| self.neighbor_pos_proj = nn.Sequential( | |
| nn.Linear(2, self.hidden_size), | |
| nn.SiLU(), | |
| nn.Linear(self.hidden_size, self.hidden_size), | |
| ) | |
| self.decoder_blocks = nn.ModuleList([SimpleP2GDecoderLayer(config) for _ in range(self.decoder_depth)]) | |
| self.final_norm = SimpleP2GRMSNorm(self.hidden_size) | |
| self.mean_head = nn.Linear(self.hidden_size, 1) | |
| if config.loss_name in ("gaussian_nll", "beta_nll"): | |
| self.log_var_head = nn.Linear(self.hidden_size, 1) | |
| self.gradient_checkpointing = False | |
| self.emb_dropout = nn.Dropout(getattr(config, "dropout", 0.0)) | |
| def forward(self, c, neighbor_emb=None, neighbor_mask=None, neighbor_pos=None, global_emb=None): | |
| """Predict from target (B,D), neighbors (B,K,D) and relative positions. | |
| global_emb is accepted for the shared slide-dataset interface; the | |
| main-comparison model does not consume a slide token. | |
| """ | |
| if neighbor_emb is None or neighbor_pos is None: | |
| raise ValueError("neighbor_emb and neighbor_pos are required") | |
| batch_size = c.shape[0] | |
| output = self.emb_dropout(self.gene_queries.weight.unsqueeze(0).expand(batch_size, -1, -1)) | |
| target = self.label_embed(c).view(batch_size, self.num_cond_tokens, self.hidden_size) | |
| neighbors = self.neighbor_proj(neighbor_emb) | |
| neighbors = neighbors + self.neighbor_pos_proj(neighbor_pos.to(dtype=neighbors.dtype)) | |
| condition = torch.cat((target, neighbors), dim=1) | |
| if neighbor_mask is None: | |
| neighbor_mask = torch.ones(neighbor_emb.shape[:2], device=c.device, dtype=torch.bool) | |
| target_mask = torch.ones(batch_size, self.num_cond_tokens, device=c.device, dtype=torch.bool) | |
| mask = torch.cat((target_mask, neighbor_mask.bool()), dim=1).unsqueeze(1).unsqueeze(1) | |
| for block in self.decoder_blocks: | |
| if self.gradient_checkpointing and self.training: | |
| output = checkpoint(block, output, condition, None, mask, use_reentrant=False) | |
| else: | |
| output = block(output, encoder_hidden_states=condition, cross_attention_mask=mask) | |
| output = self.final_norm(output) | |
| mean = F.softplus(self.mean_head(output).squeeze(-1)) | |
| log_var = ( | |
| self.log_var_head(output).squeeze(-1) | |
| if self.config.loss_name in ("gaussian_nll", "beta_nll") | |
| else torch.zeros_like(mean) | |
| ) | |
| return mean, log_var | |