Skip to content

Commit a0ac8de

Browse files
committed
feat: constrain gating by inter-chain contacts
1 parent 1345928 commit a0ac8de

1 file changed

Lines changed: 48 additions & 1 deletion

File tree

profold2/model/head.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,9 @@ def __init__(
12971297
label_threshold=0.,
12981298
label_epsilon=0.,
12991299
pos_weight=None,
1300+
gij_guidance=0,
1301+
gij_cutoff=6,
1302+
gij_noise=0.15,
13001303
focal_loss=0.,
13011304
shard_size=2048
13021305
):
@@ -1348,6 +1351,12 @@ def __init__(
13481351
else:
13491352
self.pos_weight = None
13501353
self.alpha = alpha
1354+
1355+
# gating guided by inter-chain contact
1356+
self.gij_guidance = gij_guidance
1357+
self.gij_cutoff = gij_cutoff
1358+
self.gij_noise = gij_noise
1359+
13511360
self.focal_loss = focal_loss
13521361
self.shard_size = env('profold2_fitness_shard_size', defval=shard_size, dtype=int)
13531362
self.return_motifs = env('profold2_fitness_return_motifs', defval=True, dtype=bool)
@@ -1452,6 +1461,44 @@ def loss(self, value, batch):
14521461
num_class = self.mask.shape[0]
14531462
avg_error_motif = 0
14541463

1464+
avg_error_gij = 0
1465+
if self.gij_guidance > 0 and 'pseudo_beta' in batch:
1466+
positions = batch['pseudo_beta']
1467+
mask = batch['pseudo_beta_mask']
1468+
seq_color = batch["seq_color"]
1469+
assert positions.shape[-1] == 3
1470+
1471+
targets = F.sigmoid(self.gij_cutoff - torch.cdist(positions, positions))
1472+
targets = targets * mask[..., :, None] * mask[..., None, :] * (
1473+
seq_color[..., :, None] != seq_color[..., None, :]
1474+
) * (targets >= self.gij_noise)
1475+
targets = repeat(targets, "...->... t", t=self.task_num)
1476+
if 'variant_task_mask' in batch:
1477+
variant_task_mask = batch['variant_task_mask'][..., 0, :, :]
1478+
targets = targets * (
1479+
variant_task_mask[..., :, None, :] * variant_task_mask[..., None, :, :]
1480+
)
1481+
targets = torch.clamp(
1482+
1. - torch.exp(
1483+
torch.sum(torch.log(torch.clamp(1 - targets, min=1e-10, max=1.)), dim=-2)
1484+
), min=0
1485+
)
1486+
1487+
mask = mask[..., None] * torch.any(
1488+
seq_color[..., :, None] != seq_color[..., None, :], dim=-1, keepdim=True
1489+
)
1490+
if 'variant_task_mask' in batch:
1491+
mask = mask * batch['variant_task_mask'][..., 0, :, :]
1492+
1493+
logger.info('FitnessHead.gij.loss.targets: %s', torch.sum(targets >= 0.5, dim=-2))
1494+
logger.info('FitnessHead.gij.loss.masked: %s', torch.sum(mask, dim=-2))
1495+
1496+
errors = sigmoid_cross_entropy(gating, targets, gammar=self.focal_loss)
1497+
1498+
avg_error_gij = functional.masked_mean(value=errors, mask=mask, epsilon=1e-6)
1499+
logger.info('FitnessHead.gij.loss: %s', avg_error_gij)
1500+
avg_error_gij = self.gij_guidance * avg_error_gij
1501+
14551502
if 'variant' in batch:
14561503
variant_mask = batch['variant_mask']
14571504
variant_label = batch['variant_label']
@@ -1581,7 +1628,7 @@ def loss(self, value, batch):
15811628
value=errors, mask=variant_label_mask * self.task_weight
15821629
)
15831630
logger.debug('FitnessHead.loss: %s', avg_error_fitness)
1584-
return dict(loss=avg_error_motif + avg_error_fitness)
1631+
return dict(loss=avg_error_gij + avg_error_motif + avg_error_fitness)
15851632

15861633

15871634
class SequenceProfileHead(nn.Module):

0 commit comments

Comments
 (0)