【阅读总结】Variant Effect Predictor: EVE 深度生成模型预测致病突变

省流:

本系列旨在整理Debora课题组一系列基于深度生成模型预测致病突变的工作,包括EVE、Tranception、TranceptEVE、EVEscape和popEVE,讨论数据来源与处理、模型架构与训练、性能测试与实例。

ModelPublishYearAvailable简述
EVENature2021https://github.com/OATML-Markslab/EVE https://evemodel.org/VAE模型,输入MSA,输出突变的进化分数(野生型ELBO减突变型ELBO)
TranceptionICML2022https://github.com/OATML-Markslab/Tranception
TranceptEVENIPS2022Tranception+EVE,weighted average
StructSeqNIPS2023TranceptEVE+ESM-IF1
EVEscapeNature2023https://github.com/OATML-Markslab/EVEscapeEVE+Biophysical
popEVEmedRxiv2024https://github.com/debbiemarkslab/popEVE https://pop.evemodel.org/EVE+ESM-1v

EVE

数据

训练数据

3,851 genes in ClinVar (the April 2021 release) 存在有一个错义突变被标记为Pathogenic:

→ 每个基因对应到Uniprot/Swissprot中记录的单一蛋白

→ 每条蛋白序列通过jackhmmer在UniRef100 (April 20th 2020) 中搜索五次,构建不同MSA

→ MSA的构建与选择的原则:

  1. retrieve sequences that align to at least 50% of the target protein sequence, and columns with at least 70% residue occupancy.

  2. using 0.3 bits per residue as a reference; prioritize coverage L c o v L_{cov} Lcov ≥ 0.8 L L L & 100, 000≥ N N N≥10 L L L; 达不到的话尝试 L c o v L_{cov} Lcov≥0.7 L L L & N N N≤200, 000; 依然达不到的剩余3%不做限制。

    L L L: length of the target protein sequence; N N N: total number of sequences

    未发现模型表现与alignment depth N / L c o v N/ L_{cov} N/Lcov之间存在强相关。

最终获得3,219 clinically relevant proteins with corresponding evolutionary training data

*MSA构建集成在Debora组之前的EVcouplings软件中,GitHub或者网站可用(2024.08.14查看网站,处于维护状态暂不可用)

模型

以下代码分析来自https://github.com/OATML-Markslab/EVE,ProteinGym-proteingym-baselines-EVE中代码基本一致。

EVE使用的模型架构为Variational Autoencoder (VAE)。简单来说,VAE学习样本高斯分布的均值和方差(先验分布),采样获得 p ( x ∣ z ) p(x|z) p(xz) z z z:潜在变量)。VAE的优化目标为 log ⁡ P θ ( x ) \log P_\theta(x) logPθ(x) 的下界Evidence Lower Bound (ELBO),推导过程可见https://zhuanlan.zhihu.com/p/678864844,ELBO由重构损失(Reconstruction Loss)和KL散度(KL Divergence)组成:

ELBO ⁡ ( θ , ϕ ) = E z ∼ q ϕ ( z ∣ x ) [ log ⁡ p θ ( x ∣ z ) ] − K L [ q ϕ ( z ∣ x ) ∣ ∣ p ( z ) ] \operatorname{ELBO}(\theta, \phi)=\mathbb{E}_{z \sim q_\phi(z \mid x)}\left[\log p_\theta(x \mid z)\right]-K L\left[q_\phi(z \mid x)|| p(z)\right] ELBO(θ,ϕ)=Ezqϕ(zx)[logpθ(xz)]KL[qϕ(zx)∣∣p(z)]

Encoder/Decoder

具体代码实现中,EVE采用对称尺寸的encoder(2, 000 - 1, 000 - 300)和 decoder(300 - 1, 000 - 2, 000),用nn.init.constant_()函数初始化所有线性层bias为0.1。

对于encoder,经过三层线性层后分别映射到大小为50的潜在空间,获得z_meanz_log_var。通过sampler函数从z_meanz_log_var采样获得zz输入decoder。

def sampler(self, mean, log_var):
    # torch.randn_like:创建与输入相同形状的新张量,该张量填充了均值为0、方差为1的正态分布随机数。
    eps = torch.randn_like(mean).to(self.device)
    z = torch.exp(0.5*log_var) * eps + mean
    return z

对于decoder,区别于standard VAE decoder的三层线性层相连,采用Bayesian Neural Network:对于第i层,用sampler函数来reparametrize对x进行线性操作的layer_i_weightlayer_i_bias

经过三层处理后,再一次采样获得W_outb_out用于最后一次线性操作。W_out做一次加权,权重为使用one-dimensional convolution捕获的对20种氨基酸的偏好性:依然用sampler函数,meanlog_var来自两个nn.Conv1d(40, 20, kernel_size=1,stride=1,bias=False).weights

# 用加权W_out和b_out对x进行最后一次线性操作
x = F.linear(x, weight=W_out, bias=b_out)

# 温度缩放,mean初始化为1,log_var初始化为-10
if self.include_temperature_scaler:
    temperature_scaler = self.sampler(self.temperature_scaler_mean,self.temperature_scaler_log_var)
    x = torch.log(1.0+torch.exp(temperature_scaler)) * x

x = x.view(batch_size, self.seq_len, self.alphabet_size)
# 得到重构的x
x_recon_log = F.log_softmax(x, dim=-1) 

代码详见./EVE/VAE_encoder.py./EVE/VAE_model.py,超参数设置详见./EVE/default_model_params.json

*include_sparsityDeepSequence (Nature Methods, 2018, Fig.5) 中假设蛋白质序列存在上位性,一个group内不同氨基酸与hidden units之间的权重,在不同groups中共享。EVE发现这一操作会使得模型在一些蛋白上表现显著下降,因此不再使用。

Loss function

损失函数为负ELBO:

def loss_function(self, x_recon_log, x, mu, log_var, kl_latent_scale, kl_global_params_scale, annealing_warm_up, training_step, Neff):
    BCE = F.binary_cross_entropy_with_logits(x_recon_log, x, reduction='sum') / x.shape[0]
    
    KLD_latent = (-0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp())) / x.shape[0]
    if self.decoder.bayesian_decoder:
        # 对于Bayesian decoder,计算weights的KL散度
        # Neff:有效样本数,根据MSA的汉明距离进行加权
        KLD_decoder_params_normalized = self.KLD_global_parameters() / Neff
    else:
        KLD_decoder_params_normalized = 0.0
    # 默认参数中annealing_warm_up取0,warm_up_scale为1
    warm_up_scale = self.annealing_factor(annealing_warm_up,training_step)
    
    neg_ELBO = BCE + warm_up_scale * (kl_latent_scale * KLD_latent + kl_global_params_scale * KLD_decoder_params_normalized)
    
    return neg_ELBO, BCE, KLD_latent, KLD_decoder_params_normalized

BCE(Binary Cross Entropy)是二元交叉熵损失,即ELBO中的Reconstruction Loss

KLD_latent 是标准的VAE中的KL散度,计算潜在变量的后验分布 q ( z ∣ x ) q(z \mid x) q(zx)和先验分布 p ( z ) p(z) p(z)之间的散度。KL散度公式:

K L D = − 1 2 ∑ i = 1 d ( 1 + log ⁡ ( σ i 2 ) − μ i 2 − σ i 2 ) KLD=-\frac{1}{2} \sum_{i=1}^d\left(1+\log \left(\sigma_i^2\right)-\mu_i^2-\sigma_i^2\right) KLD=21i=1d(1+log(σi2)μi2σi2)

kl_latent_scalekl_global_params_scale 用于分别控制潜在变量和解码器参数的KL散度对总损失的贡献,默认参数中两项均设置为1。

Prediction

一个蛋白质变体的 E s E_s Es(evolutionary index)定义为野生型与突变型的ELBO之差:
E s = − log ⁡ p ( s ∣ θ p ) p ( w ∣ θ p ) ∼ E L B O ( w ) − E L B O ( s ) E_s=-\log \frac{p\left(\mathbf{s} \mid \theta_{\mathbf{p}}\right)}{p\left(\mathbf{w} \mid \theta_{\mathbf{p}}\right)} \sim E L B O(\mathbf{w})-E L B O(\mathbf{s}) Es=logp(wθp)p(sθp)ELBO(w)ELBO(s)

注意此处ELBO计算与损失函数不同,不引入scale系数。

def all_likelihood_components(self, x):
    mu, log_var = self.encoder(x)
    z = self.sample_latent(mu, log_var)
    recon_x_log = self.decoder(z)

    recon_x_log = recon_x_log.view(-1,self.alphabet_size*self.seq_len)
    x = x.view(-1,self.alphabet_size*self.seq_len)
    
    BCE_batch_tensor = torch.sum(F.binary_cross_entropy_with_logits(recon_x_log, x, reduction='none'),dim=1)
    KLD_batch_tensor = (-0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp(),dim=1))
    
    ELBO_batch_tensor = -(BCE_batch_tensor + KLD_batch_tensor)
    
    return ELBO_batch_tensor, BCE_batch_tensor, KLD_batch_tensor

对于每个变体,采样20k,取多个ELBO_batch_tensor的平均值,然后与野生型相减计算 E s E_s Es

Gaussian Mixture Model (GMM)

观察到evolutionary index对致病标签有较强的相关性,因此,尝试用a Gaussian Mixture model with two components来拟合evolutionary index的分布,区分致病突变和良性突变。

GMM是一种概率模型,用来表示数据集中的多种分布。它假设数据由若干个高斯分布(也称为分量)混合而成。每个高斯分布对应于数据集中的一个子群体。GMM with two-component可以理解为这个模型由两个高斯分布的加权平均构成。每个高斯分布由mean和covariance matrix描述,并且每个分量都有一个权重表示该分量在整个混合分布中所占的比例。

对于给定的蛋白变体 s \mathbf{s} s

p ( X s = 1 ∣ E s ) = α ∗ p ( X s = 1 ∣ E s , θ p ) + ( 1 − α ) ∗ p ( X s = 1 ∣ E s , θ o ) p\left(\mathbf{X}_{\mathbf{s}}=1 \mid \mathbf{E}_{\mathbf{s}}\right)=\alpha * p\left(\mathbf{X}_{\mathbf{s}}=1 \mid \mathbf{E}_{\mathbf{s}}, \theta_p\right)+(1-\alpha) * p\left(\mathbf{X}_{\mathbf{s}}=1 \mid \mathbf{E}_{\mathbf{s}}, \theta_o\right) p(Xs=1Es)=αp(Xs=1Es,θp)+(1α)p(Xs=1Es,θo)

X s X_s Xs代表是否致病(1 if pathogenic,0 if benign); α \alpha α 代表权重,grid search确定为0.3; θ o \theta_o θo θ p \theta_p θp 分别是overarching GMM and proteinspecific GMM的协方差矩阵; p ( X s = 1 ∣ E s ) p\left(\mathbf{X}_{\mathbf{s}}=1 \mid \mathbf{E}_{\mathbf{s}}\right) p(Xs=1Es) 即EVE score,量化给定 s \mathbf{s} s是否致病。

可以借助sklearn来实现:

from sklearn import mixture

main_GMM = mixture.GaussianMixture(n_components=2, covariance_type='full',max_iter=1000,n_init=30,tol=1e-4)
main_GMM.fit(X_train)

不确定性用predictive entropy (PE) 来衡量:

P E = − log ⁡ p ( X s = 1 ∣ E s ) p ( X s = 1 ∣ E s ) − log ⁡ p ( X s = 0 ∣ E s ) p ( X s = 0 ∣ E s ) P E=-\log p\left(\mathbf{X}_{\mathbf{s}}=1 \mid \mathbf{E}_{\mathbf{s}}\right) p\left(\mathbf{X}_{\mathbf{s}}=1 \mid \mathbf{E}_{\mathbf{s}}\right)-\log p\left(\mathbf{X}_{\mathbf{s}}=0 \mid \mathbf{E}_{\mathbf{s}}\right) p\left(\mathbf{X}_{\mathbf{s}}=0 \mid \mathbf{E}_{\mathbf{s}}\right) PE=logp(Xs=1Es)p(Xs=1Es)logp(Xs=0Es)p(Xs=0Es)

本工作将PE最高的 25% 的变体标记为不确定,准确度可达 ∼ 90%。在实践中,具体基因的阈值可能根据precision/recall需求做调整。本部分代码见train_GMM_and_compute_EVE_scores.py

性能

  1. Fig.2:在3,219 proteins上评估致病性预测,以及至少3、5和10个良性标签和至少3、5和10个致病标签的蛋白子集。

    *用于比较的23种模型结果来自dbNSFP,排除了使用人口频率数据来训练的模型,因为该数据常常用于变体分类;有监督训练的模型无法获知具体的训练集/验证集划分,因此存在偏差。

  2. case study:与TP53、PTEN19、MSH2、BRCA1四种蛋白的deep mutational scans (DMS) 数据计算AUC

`std::variant`是C++17引入的一个类模板,它代表了一个可以存储多种类型值的类型安全的联合体。`std::variant`中的每一类型称为一个“可能类型”。当创建一个`std::variant`实例时,你需要列出所有可能存储的类型,而该实例会存储其中的一种类型。 在你提到的`std::variant<std::monostate, bool>`中,`std::variant`可以存储两种类型的值:`std::monostate`和`bool`。`std::monostate`是`std::variant`特有的一个类型,用来表示variant不持有任何值的状态。它通常用于表示variant未初始化的情况。 `std::variant`提供了一系列操作来检查当前存储的类型、获取当前存储的值以及修改存储的值。使用`std::variant`可以替代C++98的`union`,但是更加安全,因为不需要担心类型混淆和越界问题。 `std::variant`的使用示例可能如下: ```cpp #include <variant> #include <iostream> int main() { std::variant<std::monostate, bool> v; std::cout << std::holds_alternative<std::monostate>(v) << std::endl; // 输出:1,因为variant默认不持有任何值 v = true; if (std::holds_alternative<bool>(v)) { std::cout << std::get<bool>(v) << std::endl; // 输出:1 } return 0; } ``` 在上面的代码中,我们首先创建了一个`std::variant`实例`v`,默认情况下它不持有任何值,即持有`std::monostate`。然后我们给`v`赋予了一个`bool`类型的值`true`,并检查当前存储的类型和获取存储的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值