基于氨基酸序列和对应活性值的条件对抗生成网络(Cgan)

基于氨基酸序列和对应活性值的条件对抗生成网络(Cgan)

目的:根据氨基酸序列和某值(可以是任意的长度或者数值)进行训练,最后根据一个噪声和条件标签生成相关的氨基酸序列

数据准备工作

在蛋白质网站uniport根据 胶原蛋白某发挥作用的结构域氨基酸序列进行blast,操作获取到相关同源性序列并且进行收集,并且收集其对应的稳定性,具体数据格式如下图
这里是对数据的介绍,形式是 序列+稳定性的数值

代码工作开始

导入相关的库包
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt 
import torchvision
from torchvision import transforms
print(torch.__version__)
数据准备
import pandas as pd

data = pd.read_csv('../data/8E_double_dubplcate_.csv')
data = data.dropna()
data = data[data['A4']>0.65]
data
编写一个自定义的dataset类处理我们的序列数据
import torch
import torch.nn.functional as F
from torch.utils.data import Dataset

class MyDataset(Dataset):
    def __init__(self, sequences, labels,amino_acid_table):
        self.sequences = sequences
        self.labels = labels
        self.table = amino_acid_table

    def __len__(self):
        return len(self.sequences)
    
    def __getitem__(self, index):
        sequence = self.sequences[index]
        label = self.labels[index]

        # 将label转换为32位浮点型张量
        label = torch.tensor(label, dtype=torch.float32)
        
        # 将氨基酸序列进行One-hot编码
        encoded_sequence = torch.zeros(len(sequence), 20)  # 初始化编码后的序列
        
        # 根据氨基酸序列中的每个氨基酸位置,将对应位置设置为1
        for i, amino_acid in enumerate(sequence):
            amino_acid_index = self._amino_acid_to_index(amino_acid)
            encoded_sequence[i, amino_acid_index] = 1
        
        return encoded_sequence, label # 在此处label作为氨基酸的区分标记把
    
    def _amino_acid_to_index(self,amino_acid):
        amino_acids = self.table  # 20种常见氨基酸
        return amino_acids.index(amino_acid)

其中我们对于我输入进行来的氨基酸序列数据采用one-hot编码方式进行序列矩阵化,
在这里我们需要对输入的实际氨基酸序列 进行一个剪裁,因为我们必须确保每一条序列进来的长度是一样的,这里由于胶原蛋白β螺旋中的结构域氨基酸序列56个氨基酸长度的就能确定整个胶原蛋白的稳定性,所以这里我们采用截断操作,使得每个氨基酸序列最长等于56个氨基酸。(数据处理操作过程省略,)

对实际数据进行实例化
from torch.utils.data import DataLoader
import pandas as pd

import json

with open('../config/amino_acids_table.json', 'r') as f:
    content = f.read()
    table = json.loads(content)
    print('table idnex',table)
    amino_acids_table = table['amino_acids_table']
    print("amino_acids_table is same as training:",amino_acids_table)


sequences = data['seqs'].values
labels = data['A4']

# 打印训练集和测试集的大小
print("训练集大小:", sequences.shape, labels.shape)


# 创建自定义数据集实例
train_set = MyDataset(sequences, labels,amino_acids_table)

# 创建数据加载器
train_loader = DataLoader(train_set, batch_size=8, shuffle=True)

output:

后续我们需要根据amino_acids_table的氨基酸index进行氨基酸序列还原

table idnex {‘amino_acids_table’: ‘CTDLQGWMAFHKYPINRSVE’}
amino_acids_table is same as training: CTDLQGWMAFHKYPINRSVE
训练集大小: (4900,) (4900,)

定义生成器和判别器
# 定义生成器  
class Generator(nn.Module):
    def __init__(self, ):
        super().__init__()
        
        # seqs torch.Size([8, 56, 20])
        self.noise = nn.Linear(100,56*20)
        # a4 torch.Size([8])
        self.fc_a4 = nn.Linear(1,56*20)

        # learning netword
        self.rnn1 = nn.GRU(input_size=56 * 20  *2, hidden_size=512, num_layers=3, batch_first=True)
        self.rnn2 = nn.GRU(input_size=512, hidden_size=128, num_layers=3, batch_first=True)
        self.rnn3 = nn.GRU(input_size=128, hidden_size=512, num_layers=3, batch_first=True)
        self.fc_out = nn.Linear(512, 56 * 20)
        self.softmax = nn.Softmax(dim=2)

    def forward(self, seq, a4):
        # nosie
        x = torch.relu(self.noise(seq)) 
        # x = x.view(-1,56,20)
        # a4
        a4 = a4.unsqueeze(1)
        y = torch.relu(self.fc_a4(a4))
        # y = y.view(-1,56,20)

        # print("noise shape",x.shape)
        # print("a4 shape",y.shape)
        
        feature = torch.cat([x, y], dim=1)
        
        # print("after cat feature shape",feature.shape)
        
        out, _ = self.rnn1(feature)
        out, _ = self.rnn2(out)
        out, _ = self.rnn3(out)


        out = self.fc_out(out) # out shape == [8,1120]
        # print("out1  shape",out.shape ) 
        out = out.view(-1,56,20) # out shape == [8,56,29]
        # print('out after view shape',out.shape)
        out = self.softmax(out) # out shape == [8,56,20] 最后以为进行了softmax

        # print("out2  shape",out.shape )
        
        # print('out after view shape',out.shape)
        return out

# 定义判别器
class Discriminator(nn.Module):
    def __init__(self, seq_dim=56*2 ,a4_dim=1, hid_dim=128):
        super().__init__()


        self.label = nn.Linear(1,56*20)
        
        self.main = nn.Sequential(
                nn.Linear(56*20*2,512),
                nn.BatchNorm1d(512),  # 添加批量归一化层
                nn.LeakyReLU(),#leakyRelu() 再负值的是时候保留一定的梯度
                nn.Dropout(0.25),
                
                nn.Linear(512,1024),
                nn.BatchNorm1d(1024),  # 添加批量归一化层
                nn.LeakyReLU(),# leakyRelu() f(x),x>0 输出x  ,X<0,输出a*x,a是一个很小的斜率值,比如0.1
                nn.Dropout(0.25),

                nn.Linear(1024,512),
                nn.BatchNorm1d(512),  # 添加批量归一化层
                nn.LeakyReLU(),
                nn.Dropout(0.25),

                nn.Linear(512,1), # 输出到1,然后激活后 是 0-1之间的一个概率
                nn.Sigmoid()
            )
        # self.conv1 = nn.Conv1d(20, 64, kernel_size=3, stride=1)
        # self.conv2 = nn.Conv1d(64, 10, kernel_size=3, stride=1)
        
        # self.maxpool = nn.MaxPool1d(kernel_size=2, stride=1)
        
        # self.fc = nn.Linear(10 * 108, 1)
        # self.sigmoid = nn.Sigmoid()
      
        

    def forward(self, seq, a4):
        
        # seq 进来的是 torch.size([16,56,20])
        # a4
        a4 = a4.unsqueeze(1)
        y = torch.relu(self.label(a4))
        y = y.view(-1,56,20)

        # print("seq shape",seq.shape) # torch.Size([16, 56, 20])
        # print("a4 shape",y.shape) # torch.Size([16, 56, 20])
        
        feature = torch.cat([seq, y], dim=1) #  torch.Size([16, 112, 20])
        
        # print("feature shape before view ==>",feature.shape)
        feature = feature.view(-1,112*20)
        
    
        # Apply convolutional layers
        # feature = feature.permute(0,2,1)
        # x = self.conv1(feature)
        # x = self.conv2(x)
        
        out = self.main(feature)
        
        # print(out.shape)
        # out = np.argmax(out.detach().numpy(),axis=0) # 后续采用
        return out
损失函数的选择

在设定损失函数的同时我们要根据我们的需求进行选择,首先我们要求生成的序列跟原来指定稳定性的序列 关键节点需要一致,同时我们需要氨基酸序列尽可能多的多样化
即 确保生成的氨基酸序列确实有如实的稳定性的同时 且具有一定的多样性可能。所以选择wgans中的损失函数,其确保固定氨基酸之间的距离能够足够相似的同时整体结构不改变。

class Wgansloss(nn.Module):
    
    def __init__(self):
        super(Wgansloss, self).__init__()

    def forward(self, real_predictions, fake_predictions):
        # 自定义损失计算方式
        real_loss = -torch.mean(real_predictions)  # 最大化真实样本的平均预测值
        fake_loss = torch.mean(fake_predictions)  # 最小化生成样本的平均预测值
        loss = real_loss + fake_loss  # 真实样本的平均预测值减去生成样本的平均预测值
        return loss
intel One-API 组件中的 AI tooklist

在这里我们需要使用inter 公司架构的one-API架构,实验一次代码编写,可以在任意的基础架构上进行运行。

OneAPI 旨在提供一个适用于各类计算架构的统一编程模型和应用程序接口。也就是说,应用程序的开发者只需要开发一次代码,就可以让代码在跨平台的异构系统上执行,底层的硬件架构可以是CPU、GPU、FPGA、神经网络处理器,或者其他针对不同应用的硬件加速器等等。

这种演化的过程中不可避免的造成了软、硬件体系的蓬勃发展,市场上架构各异的硬件芯片,纷繁多样的语言、各式各样的商业模式都是其推动的直接产物,然而也正是这些多样化、多元化的环境使得跨架构的系统设计变得越来越复杂、愈来愈让人难以理解。

在这种应用环境下,开发者在集成多种架构芯片、系统阶段性升级或改造时将必须面对如下挑战:
在这里插入图片描述
而Intel oneAPI 正是为解决这些问题而诞生,简单来说它是一个跨行业、开放、标准统一、简化的编程模型,旨在促进社区和行业合作、简化跨多架构的开发过程、解决跨体系及供应商代码重用,为跨 CPU、GPU、FPGA、专用加速器的开发者提供统一的开发体验

intel oneAPI 赋能 AI

在 intel oneAPI架构下,有我们训练神经网络的所需要的集成工具库,AI-tooklist 其底层架构支持 AI训练中间件,pytorch、tensorflow、X-net、numpy、等等相关工具
在这里啊插入图片描述
在这里我们使用AI-tooklist进行pytorch深度学习框架的加速,同时构建我们自己one-API应用,以支持在任何底层架构上使用,在其他系统上我们可以基于任意的 XPUs进行运算和运行。
在这里插入图片描述

定义解码函数

解码函数将生成的数据矩阵重新根绝amino_acid_table 进行还原\

def generator_seq(model, test_input,label,table):
    # np.squeeze 把维度为1的值去掉
    prediction_seq = model(test_input,label).detach().cpu().numpy()# detach 截断梯度,然后再放到CPU上

    # 假设氨基酸字典为 amino_acids_dic
    # print(table)
    # 假设网络输出的概率矩阵为 output
    # print(prediction_seq)

    # 获取每个位点最大概率的氨基酸索引
    amino_acids_index = np.argmax(prediction_seq, axis=2)
    # print(amino_acids_index)
    # 根据氨基酸索引获取对应的氨基酸
    amino_acids_seq = []
    for row in amino_acids_index:
        seq = ''.join([amino_acids_dic[index] for index in row])  # 将氨基酸索引转换为氨基酸序列
        amino_acids_seq.append(seq)

    # 输出氨基酸序列
    # print(amino_acids_seq)
    
    return amino_acids_seq

    # 后续加载回归模型去预测一下生成标签的结果

生成器,判别器,损失函数 ,实例化,创建one API 接口应用,加速model 和 优化器计算

在此之前我们先要安装 intel_extension_for_pytorch
python -m pip install intel_extension_for_pytorch

# 运行下面的代码检验是否安装成功 pytorch 和 intel-one API
python -c "import torch; import intel_extension_for_pytorch as ipex; print(torch.__version__); print(ipex.__version__);"
import torch

import intel_extension_for_pytorch as ipex

generator = Generator().to(device=device)
discriminator = Discriminator().to(device=device)

opt_gen = torch.optim.RMSprop(generator.parameters(),lr=0.0005)

opt_disc = torch.optim.RMSprop(discriminator.parameters(),lr=0.0005)

criterion = Wgansloss()

# 这里是构建 oneAPI AI tooklist的过程,只用将模型和优化器通过接口即可,其余都按照正常的训练步骤
generator , opt_gen  = ipex.optimize(generator , optimizer=opt_gen )
discriminator , opt_disc = ipex.optimize(discriminator , optimizer=opt_disc )
训练代码

在经过400个epoch的时候,生成器的损失达到稳定

# 训练
num_epochs = 100
for epoch in range(num_epochs):
  d_epoch_loss = 0
  g_epoch_loss = 0
  count = len(train_loader)
  # disc_iter = 0 # 判别器的迭代次数
  for seq, a4 in train_loader:
 
    # print(seq.shape) # 编码完成的 氨基酸序列 形状 torch.Size([8, 56, 20])
    # print(a4.shape) #  每个氨基酸序列对应的A4值 形状 torch.Size([8])
    # break
    seq = seq.to(device=device)
    a4 = a4.to(device=device)
    # 随机正态噪音
    batch_size = seq.size(0)
    random_noise = torch.randn(batch_size,100,device=device)

     
    # A.训练判别器
    opt_disc.zero_grad()
    
    # 生成假样本
    fake_seq = generator(random_noise, a4).detach() 
    # print(fake_seq)
    # 计算真实样本判别器输出
    # real_pred = discriminator(seq, a4)
    # real_loss = criterion(real_pred, torch.ones_like(real_pred,device=device))
    # real_loss.backward()
    
    # # 计算假样本判别器输出
    # fake_pred = discriminator(fake_seq, a4) 
    # fake_loss = criterion(fake_pred, torch.zeros_like(fake_pred,device=device))
    # fake_loss.backward()

    # wgans 训练方法 判别器计算 真、假样本的计算输出
    real_pred = discriminator(seq, a4)
    # real_loss = criterion(real_pred, a4)
    # real_loss.backward()

    fake_pred = discriminator(fake_seq, a4)
    # fake_loss = criterion(fake_pred, a4) 
    # fake_loss.backward()
    
    # opt_disc.step()
    # Wasserstein 损失计算
    discriminator_loss = criterion(real_pred, fake_pred)
    # 损失backward(),梯度更新
    discriminator_loss.backward()
    opt_disc.step()
    # disc_iter+=1
    # 限制判别器参数的范围
    for p in discriminator.parameters():
        p.data.clamp_(-0.01, 0.01)

    # 记录损失函数
    disc_loss = discriminator_loss
    # 判别器每更新五次参数则 生成器更新一次参数
    # if disc_iter % 8 == 0:

    # B.  训练生成器
    opt_gen.zero_grad()
    # 生成假样本
    fake_seq = generator(random_noise, a4)
    
    # 计算假样本判别器输出
    fake_pred = discriminator(fake_seq, a4)
    # gen_loss = criterion(fake_pred, torch.ones_like(fake_pred,device=device)) 
    gen_loss = -torch.mean(fake_pred)
    gen_loss.backward()

    opt_gen.step()


    with torch.no_grad():
      d_epoch_loss +=disc_loss
      g_epoch_loss +=gen_loss

  print(f'Epoch [{epoch}/{num_epochs}]')
  # 生成新样本  
  # ...
  if epoch % 10 ==0: # 每25个epoch生成一次序列
    with torch.no_grad():
          # 每个epoch上 判别器的损失
          d_epoch_loss = d_epoch_loss / count 
          # 每个epoch上 生成器的损失
          g_epoch_loss = g_epoch_loss / count
          # 每个epoch结束后,都添加到list中
          print(f"d_epoch_loss:==>{d_epoch_loss}, and g_epoch_loss:==>{g_epoch_loss}")
          D_loss.append(d_epoch_loss.item())
          G_loss.append(g_epoch_loss.item())
          print("给定的a4值 ==》 氨基酸序列:",a4_seed) # 展示标签,每个epoch应该不变
          seq_gen_list = generator_seq(model=generator,test_input=noise_seed,label=a4_seed,table=amino_acids_dic)
          print(seq_gen_list) # 展示生成的序列
          generator_seq_history[epoch] = seq_gen_list  # 将 seq_gen_list 存储到 generator_seq_history 字典中
      # break


结果

在这里插入图片描述

不足之处:

可以根据损失函数进行丰富,可以多加几个损失函数 丰富模型的生成样式,同样可以变换模型结构去降低数据维度,同时加以大模型做氨基酸序列的嵌入。如果有相关的问题,可以联系我
邮箱 2397895834@qq.COM

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值