从零入门 AI for Science(AI+药物) #Datawhale AI 夏令营 Task2

书接上回: 从零入门 AI for Science(AI+药物) #Datawhale AI 夏令营

Task2

前面了解了赛题,这个主要讲baseline代码,入门RNN和特征工程

解读官方baseline

set_random_seed

统一设置随机种子

def set_random_seed(seed):
    """
    设置随机种子,确保结果可复现。

    参数:
        seed (int): 随机种子值。

    返回:
        无
    """
    np.random.seed(seed)  # 设置NumPy的随机种子
    random.seed(seed)  # 设置Python内置的随机数生成器的种子
    torch.manual_seed(seed)  # 设置PyTorch的随机种子
    torch.cuda.manual_seed(seed)  # 设置CUDA的随机种子
    torch.cuda.manual_seed_all(seed)  # 设置所有CUDA设备的随机种子
    torch.backends.cudnn.deterministic = True  # 确保每次卷积算法选择都是确定的
    torch.backends.cudnn.benchmark = False  # 关闭CuDNN自动优化功能,确保结果可复现

这里做了这些操作

  1. 设置NumPy的随机种子
  2. 设置Python内置的随机数生成器的种子
  3. 设置PyTorch的随机种子
  4. 设置CUDA的随机种子
  5. 设置所有CUDA设备的随机种子
  6. 确保每次卷积算法选择是确定的
  7. 关闭CuDNN自动优化功能

就是把每一个自动优化或随机种子的选项都关掉了,然后确保结果不会因为自动优化或随机数而改变,因而可以复现结果。

SiRNADataset

class SiRNADataset(Dataset):
    def __init__(self, df, columns, vocab, tokenizer, max_len, is_test=False):
        """
        初始化SiRNADataset类
        
        参数:
            df (DataFrame): 包含数据的数据框
            columns (list): 包含序列的列名列表
            vocab (Vocab): 词汇表
            tokenizer (Tokenizer): 分词器
            max_len (int): 最大序列长度
            is_test (bool, optional): 是否是测试集,默认为False
        """
        self.df = df
        self.columns = columns
        self.vocab = vocab
        self.tokenizer = tokenizer
        self.max_len = max_len
        self.is_test = is_test

    def __len__(self):
        """
        返回数据集的长度
        """
        return len(self.df)

    def __getitem__(self, idx):
        """
        获取数据集中的第idx个样本
        
        参数:
            idx (int): 样本索引
            
        返回:
            seqs (list): 编码后的序列列表
            target (tensor): 目标值张量(仅在非测试集模式下)
        """
        row = self.df.iloc[idx]
        seqs = [self.tokenize_and_encode(row[col]) for col in self.columns]
        if self.is_test:
            return seqs
        else:
            target = torch.tensor(row['mRNA_remaining_pct'], dtype=torch.float)
            return seqs, target

    def tokenize_and_encode(self, seq):
        """
        对序列进行分词和编码
        
        参数:
            seq (str): 待处理的序列
            
        返回:
            encoded_seq (tensor): 编码后的序列张量
        """
        if ' ' in seq:
            tokens = seq.split()  # 如果序列中包含空格,则按空格分词
        else:
            tokens = self.tokenizer.tokenize(seq)  # 否则使用分词器进行分词
        encoded = [self.vocab.stoi.get(token, 0) for token in tokens]  # 将分词后的每个词编码为对应的索引
        padded = encoded + [0] * (self.max_len - len(encoded))  # 将序列补齐到最大长度
        return torch.tensor(padded[:self.max_len], dtype=torch.long)

定义了一个SiRNADataset类来创建一个自定义的PyTorch数据集对象

目的是将输入的数据框(df)中的序列数据分词、编码和填充,并返回编码后的序列和目标值

SiRNADataset类的方法

初始化方法:

接受数据并处理成对象属性

接收了下面这些数据并保存为对象的属性:

1. 接收数据框(df)
2. 包含序列的列名(columns)
3. 词汇表(vocab)
4. 分词器(tokenizer)
5. 最大序列长度(max_len)
6. 否为测试集(is_test)

__len__方法

返回数据框中的样本数量。

__getitem__方法

根据给定的索引(idx),获取数据集中的第idx个样本。

首先根据索引获取数据框中的一行数据,然后对每一列的序列数据进行分词和编码。

  • 如果是测试集模式(is_test为True),则返回编码后的序列。
  • 如果不是测试集模式,则将目标值转换为张量,并返回编码后的序列和目标值。
tokenize_and_encode方法

接收一个序列(seq,这个就是我们要处理的序列)作为输入,根据序列是否包含空格,选择不同的方式分词。

这里有两种分词方法:

  • 包含空格的序列,将其按空格进行分词; (这个就是对modified_siRNA_antisense_seq_list(modified_xxxx) 的数据,它本身已经根据空格分好了)
  • 常规序列,使用指定的分词器进行分词。

然后,将分词后的token转换为词汇表中对应的索引,未知的token使用索引0(代表 < p a d > <pad> <pad>)。最后将编码后的序列填充到最大长度,返回张量格式的序列。

SiRNAModel 类

class SiRNAModel(nn.Module):
    def __init__(self, vocab_size, embed_dim=200, hidden_dim=256, n_layers=3, dropout=0.5):
        """
        初始化SiRNA模型

        参数:
            vocab_size (int): 词汇表大小
            embed_dim (int): 嵌入维度 (默认值: 200)
            hidden_dim (int): 隐藏层维度 (默认值: 256)
            n_layers (int): GRU层的层数 (默认值: 3)
            dropout (float): Dropout层的丢弃率 (默认值: 0.5)
        """
        super(SiRNAModel, self).__init__()

        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)  # 初始化嵌入层
        self.gru = nn.GRU(embed_dim, hidden_dim, n_layers, bidirectional=True, batch_first=True, dropout=dropout)  # 初始化GRU层
        self.fc = nn.Linear(hidden_dim * 4, 1)  # 初始化全连接层
        self.dropout = nn.Dropout(dropout)  # 初始化Dropout层

    def forward(self, x):
        """
        前向传播函数

        参数:
            x (List[Tensor]): 输入序列列表

        返回:
            Tensor: 模型的输出张量
        """
        embedded = [self.embedding(seq) for seq in x]  # 将输入序列传入嵌入层
        outputs = []

        for embed in embedded:
            x, _ = self.gru(embed)  # 传入GRU层
            x = self.dropout(x[:, -1, :])  # 取最后一个隐藏状态,并进行dropout处理
            outputs.append(x)

        x = torch.cat(outputs, dim=1)  # 将所有序列的输出拼接起来
        x = self.fc(x)  # 传入全连接层
        return x.squeeze()  # 返回结果

这个类继承自nn.Module类,用来处理RNA序列。

  1. 先将输入序列列表x传入嵌入层
  2. 然后通过循环将每个序列的嵌入向量传入双向GRU层取最后一个隐藏状态并进行dropout处理
  3. 最后,将所有序列的输出拼接起来,并传入一个全连接层,输出一个标量结果

nn.Module类

nn.Module类是PyTorch中所有神经网络模型的基类,提供了一些基本的功能和方法,用于定义和管理神经网络模型的结构和参数

nn.Module类的作用有:
定义模型的结构

通过__init__方法中定义各个层和模块,可以将不同的层组合在一起,构建出模型的结构。

前向传播函数

通过forward方法中定义前向传播的过程,可以将输入数据在模型中传递,计算输出结果

参数管理

nn.Module类提供了一些方法,如parameters()named_parameters(),可以自动追踪模型中所有的可学习参数,可以方便地进行参数的访问和管理。

parameters()

parameters()方法返回一个迭代器,该迭代器会遍历模型中的所有可学习参数。

可学习参数是指那些需要在训练过程中进行优化调整的参数,例如神经网络中的权重和偏置项。

model = SiRNAModel(...)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
named_parameters()

named_parameters()方法返回一个迭代器,该迭代器会遍历模型中的所有可学习参数,并为每个参数附上一个名称。

这个方法在调试和模型分析时常见,可以方便地查看每个参数的名称和对应的数值。也可以利用这个方法来选择性地冻结或更新某些参数。

for name, param in model.named_parameters():
    if 'embedding' in name:
        param.requires_grad = False  # 冻结嵌入层的参数
模型保存和加载

nn.Module类提供了方法,如state_dict()load_state_dict(),可以方便地保存模型的状态和加载已保存的状态。

继承自nn.Module类的子类可以自由定义自己的网络结构,并且可以利用nn.Module提供的方法和功能来管理参数和实现前向传播过程。
还可以与优化器、损失函数、数据加载器等,进一步提升模型的训练和使用效果

state_dict()

state_dict()(状态字典)是一个Python字典对象,其中包含了模型的所有可学习参数的名称和对应的张量值。

state_dict()方法返回模型的状态字典,可以将其保存到文件中,以便在之后的时间点恢复模型的状态。

model = SiRNAModel(...)
torch.save(model.state_dict(), 'model.pth')
load_state_dict()

用于加载之前保存的模型的状态字典。可以将保存的状态字典加载到同一类别的模型对象中,以便恢复模型的参数。

model = SiRNAModel(...)
model.load_state_dict(torch.load('model.pth'))

通过state_dict()load_state_dict()方法,可以方便地保存和加载模型的参数状态,以便进行模型的训练和推理。这些方法在迁移学习、继续训练以及模型部署等场景中常见。

如何将序列转换成张量输入到模型里

关键代码是在forward()方法
方法: forward(self, x)
参数: x (List[Tensor]): 输入序列列表

发现输入的是这个x,x又是输入的序列

for inputs,target in train_loader:
  print("len(inputs):\n {0}".format(len(inputs)))
  print("inputs[0].shape:\n {0} ".format(inputs[0].shape))
  print("intputs[0][0]:\n {0} ".format(inputs[0][0]))
  print("traget.shape:\n {0}".format(target.shape))
  break

在这里插入图片描述
在处理siRNA序列数据时,我们首先注意到输入数据inputs包含两个元素,每个元素的尺寸为64×25。
这里的64表示批量处理的大小,而25代表每个序列的长度。通过观察inputs[0][0],我们可以了解到siRNA的反义链序列(siRNA_antisense_seq)在经过向量化处理后的表现。在这里,序列的前7位是非零值,这些非零值代表了序列编码中每个字符的唯一标识符。

在这个模型的嵌入层初始化时我们做了这样一个操作,
在这里插入图片描述
其中
vocab_size表示词汇表的大小
embed_dim表示嵌入向量的维度
padding_idx=0表示对应的填充符号的索引。

为了使RNN模型能够有效处理这些数据,需要保证每个输入样本的长度一致,在创建模型时采取了填充(padding)策略(上方引用)。
如果某个序列编码后的长度小于最大长度,我们会在其后补零,以确保所有序列在输入到RNN模型时具有统一的长度
这里把所有序列都被填充至25位,来满足模型的输入要求。

如何为siRNA序列分配唯一标识符

首先进行分词处理

对于未格式化的siRNA_antisense_seq等序列

使用GenomicTokenizer实现

siRNA_antisense_seq序列通过每3个核苷酸一组划分,使用GenomicTokenizer实现,其中ngram和stride均设为3。

例如序列"AGCCGAGAU",分词后得到[“AGC”, “CGA”, “GAU”]。

对于格式化的modified_siRNA_antisense_seq_list等序列

modified_siRNA_antisense_seq_list序列根据空格已分词。

基于数据集中所有token构建词汇表。

该词汇表映射token至唯一标识符,即索引。映射过程确保RNN模型接收数值形式输入,同时学习序列中不同token间关系。

使用GenomicVocab.create方法基于tokens创建基因词汇表。

创建基因词汇表代码

# 创建分词器
```python
tokenizer = GenomicTokenizer(ngram=3, stride=3)

# 创建词汇表
all_tokens = []  # 用于存储所有的tokens
for col in columns:  # 遍历每一列
    for seq in train_data[col]:  # 遍历每个序列
        if ' ' in seq:  # 如果序列中包含空格,则说明是修改过的序列
            all_tokens.extend(seq.split())  # 将序列按空格进行切分,并添加到all_tokens中
        else:  # 如果序列中不包含空格,则使用tokenizer对序列进行分词
            all_tokens.extend(tokenizer.tokenize(seq))  # 将分词后的结果添加到all_tokens中

vocab = GenomicVocab.create(all_tokens, max_vocab=10000, min_freq=1)  # 使用all_tokens创建基因词汇表,设定最大词汇量为10000,词频阈值为1
  1. 先创建一个GenomicTokenizer对象,用于对序列进行分词。
  2. 然后遍历数据集中的每个序列,如果序列中包含空格,则说明是修改过的序列,直接按空格切分并添加到all_tokens中;
  3. 如果序列中不包含空格,则使用分词器tokenizer对序列进行分词,并将结果添加到all_tokens中。
  4. 最后使用GenomicVocab.create方法基于all_tokens创建基因词汇表,设定最大词汇量为10000,词频阈值为1。
来获得序列的最大长度
# 用于计算训练数据中每列数据最大长度

# 首先使用嵌套的生成器表达式,遍历训练数据中的每一列
# 在内部生成器中,首先检查当前列的每个样本,判断是否包含空格
# 如果包含空格,则使用split()方法将字符串拆分成单词,并返回拆分后的单词个数
# 如果不包含空格,则使用tokenizer.tokenize()将字符串拆分成单词,并返回拆分后的单词个数
# 通过max函数将每列中的最大长度取出,并使用嵌套的生成器表达式再次计算所有列中的最大长度
max_len = max(
    max(
        len(seq.split()) if ' ' in seq else len(tokenizer.tokenize(seq))
        for seq in train_data[col]
    )
    for col in columns
)

SiRNADataset类

完成上面的操作之后,在loader获取样本的时候把token转为索引,即我们通过转换成SiRNADataset类的过程中,让数据转换成索引

class SiRNADataset(Dataset):
    def __init__(self, df, columns, vocab, tokenizer, max_len, is_test=False):
        """
        初始化SiRNADataset类
        
        参数:
        - df:包含数据的DataFrame
        - columns:包含序列的列名
        - vocab:词汇表
        - tokenizer:分词器
        - max_len:最大序列长度
        - is_test:指示是否是测试集
        
        """
        self.df = df  # 数据框
        self.columns = columns  # 包含序列的列名
        self.vocab = vocab  # 词汇表
        self.tokenizer = tokenizer  # 分词器
        self.max_len = max_len  # 最大序列长度
        self.is_test = is_test  # 指示是否是测试集

    def __len__(self):
        """
        获取数据集的长度
        
        返回值:
        - 数据集的长度
        
        """
        return len(self.df)

    def __getitem__(self, idx):
        """
        获取数据集中的第idx个样本
        
        参数:
        - idx:样本索引
        
        返回值:
        - 如果是测试集模式,返回编码后的序列
        - 如果是训练集模式,返回编码后的序列和对应的目标值
        
        """
        row = self.df.iloc[idx]  # 获取第idx行数据
        
        # 对每一列进行分词和编码
        seqs = [self.tokenize_and_encode(row[col]) for col in self.columns]
        
        if self.is_test:
            # 仅返回编码后的序列(测试集模式)
            return seqs
        else:
            # 获取目标值并转换为张量(训练集模式)
            target = torch.tensor(row['mRNA_remaining_pct'], dtype=torch.float)
            # 返回编码后的序列和目标值
            return seqs, target

    def tokenize_and_encode(self, seq):
        """
        对序列进行分词和编码
        
        参数:
        - seq:输入的序列
        
        返回值:
        - 编码后的序列
        
        """
        if ' ' in seq:
            # 修改过的序列,按空格分词
            tokens = seq.split()
        else:
            # 常规序列,使用分词器分词
            tokens = self.tokenizer.tokenize(seq)
        
        # 将token转换为索引,未知token使用0(<pad>)
        encoded = [self.vocab.stoi.get(token, 0) for token in tokens]
        # 将序列填充到最大长度
        padded = encoded + [0] * (self.max_len - len(encoded))
        # 返回张量格式的序列
        return torch.tensor(padded[:self.max_len], dtype=torch.long)

这个类继承自PyTorch的Dataset类,用于加载数据并将其传递给模型进行训练或预测。

__getitem__方法中,根据索引idx获取对应的数据行。然后针对每个包含序列的列,调用tokenize_and_encode方法对序列进行分词和编码。 如果是测试集模式,直接返回编码后的序列;如果是训练集模式,还需获取目标值并将其转换为张量。然后,tokenize_and_encode方法用于对序列进行分词和编码。对于常规序列,使用传入的分词器对其进行分词;对于修改过的序列,直接按空格进行分词。然后将分词后的token转换为词汇表中的索引,未知token使用索引0表示。最后将序列填充到最大长度,并返回张量格式的序列。

Dataset类

它是一个数据集的抽象接口,可以根据需要自定义数据集的读取和处理方式。

使用PyTorch进行训练和预测时,需要将数据加载到Dataset对象中,并通过DataLoader对象对数据进行批处理和数据加载
通过继承Dataset类,我们可以自定义数据集的处理逻辑,包括数据读取、数据预处理、数据转换等。

我们需要实现__len__和__getitem__方法,分别用于获取数据集的长度和获取指定索引位置的样本。

可以自定义Dataset类来灵活地处理不同类型的数据集,并将其传递给模型进行训练或预测。

关于训练的模型前面在SiRNAModel 类时讲过,就不再重述

我们首先进行索引嵌入处理,即将离散的符号(例如单词、字符或基因序列片段)转换成连续的向量形式。过程中涉及将高维的稀疏表示(如独热编码)转换为低维的密集向量,以使得语义相近的符号在向量空间中的相对位置更接近
转换后,嵌入向量的维度将从BatchSize * Length扩展为BatchSize * Length * EmbeddingSize,其中EmbeddingSize,也就是嵌入维度embed_dim,被设定为200。

RNN(递归神经网络)知识点

一种专门用于处理序列数据的神经网络模型。
与传统的前馈神经网络不同,RNN具有反馈连接,可以将前面的输出作为后续输入的一部分,使其具有记忆性。

RNN的基本结构是一个单元(cell)或节点,其中包含一个输入层、一个隐藏层和一个输出层。隐藏层中的神经元通过时间反馈连接,使得信息可以在不同时间步之间传递和共享。这种结构使得RNN能够处理任意长度的序列数据,并且能够捕捉到序列中的上下文信息。

RNN的架构示意图
在这里插入图片描述
RNN,即循环神经网络(Recurrent Neural Network),是一种适合于序列数据的深度学习模型。它与传统的前馈神经网络(如多层感知机)不同,RNN 能够处理序列中的动态特征,即能够捕捉时间序列中的动态依赖关系。

RNN的数学表达可以简化为以下形式:
h t = f ( W h h h t − 1 + W x h x t + b h )   \\ h_t = f(W_{hh} h_{t-1} + W_{xh} x_t + b_h) \ ht=f(Whhht1+Wxhxt+bh)    y t = f ( W h y h t + b y )   \ y_t = f(W_{hy} h_t + b_y) \  yt=f(Whyht+by) 

其中,
h t h_t ht是时间步 t t t的隐藏状态。
x t x_t xt是时间步 t t t的输入向量。
W h h W_{hh} Whh W x h W_{xh} Wxh分别是从上一个时间步的隐藏状态到当前隐藏状态、从当前时间步的输入到当前隐藏状态的权重矩阵。
b h b_h bh是隐藏层的偏置项。
W h y W_{hy} Why是从隐藏状态到输出的权重矩阵。
b y b_y by是输出层的偏置项。
f f f是激活函数。

RNN的训练过程

在RNN中,每个时间步都有一个输入和一个输出。输入可以是任意维度的向量,而输出通常是一个固定大小的向量或者是一个标量。RNN通过学习一组可学习的权重参数来对输入序列进行处理,并输出相应的预测结果。

RNN的训练过程通常是使用反向传播算法来优化模型的权重参数。 由于反向传播算法的梯度消失问题,在处理长序列时RNN往往会出现难以学习到长期依赖关系的情况。为了解决这个问题,一种常用的改进版本是长短期记忆网络(LSTM)和门控循环单元(GRU),它们能够更有效地捕捉和利用序列中的长期依赖关系。

RNN 的特点
  • 循环连接:RNN的每个神经元不仅与下一层的神经元相连,而且与同一层的下一个时间步的神经元相连,形成了一个循环结构。
  • 时间步:RNN在序列的每个时间步上都会进行计算,每个时间步的输出不仅依赖于当前的输入,还依赖于前一个时间步的输出。
  • 隐藏状态:RNN通过隐藏状态(hidden state)来传递之前时间步的信息。隐藏状态可以看作是网络对之前序列信息的总结。
  • 参数共享:在RNN中,同一网络参数在每个时间步上都会被重复使用,这简化了模型结构,但同时也带来了一些挑战,如梯度消失或梯度爆炸问题。
  • 长短期记忆(LSTM)和门控循环单元(GRU):这两种网络结构是对传统RNN的改进,它们通过引入门控机制来解决梯度消失问题,使得网络能够学习长期依赖关系。
  • 应用领域:RNN广泛应用于自然语言处理(NLP)、语音识别、时间序列预测等领域,特别是在需要处理序列数据和捕捉时间依赖性的任务中。
  • 训练挑战:RNN的训练可能面临梯度消失或梯度爆炸的问题,这使得训练过程可能不稳定。现代优化技术如梯度裁剪或使用更高级的优化器(如Adam)可以帮助缓解这些问题。
  • 变长序列处理:RNN能够处理不同长度的序列,但需要通过填充(padding)或截断来保证输入序列具有相同的长度。

数据的特征工程 (EDA)

在官方baseline中,得分较低可能是由于数据特征简单、序列特征构造粗糙以及数据量不足等原因。为了解决序列特征问题,可以将其转化为表格问题并进行特征工程。

基本操作

缺失值处理

检查数据是否存在缺失值,并根据具体情况决定如何处理缺失值,如删除、填充等。

异常值处理

检测和处理数据中的异常值,包括通过可视化和统计学方法识别异常值,并根据业务逻辑进行处理。

处理类别型变量

统计唯一值
df.gene_target_symbol_name.nunique()

计算DataFrame(df)中某一列(gene_target_symbol_name)中唯一值(unique value)的数量(nunique)。也就是统计该列中有多少不重复的值。

nunique()

nunique()函数是pandas库中的一个方法,用于计算一个序列(Series)或数据框(DataFrame)中唯一值的数量
语法如下:

Series.nunique(dropna=True)

# 或

DataFrame.nunique(axis=0, dropna=True)

参数:

  • dropna:是否排除缺失值,默认为True,即排除缺失值。
  • axis:对于数据框,可以指定按行(axis=0)或按列(axis=1)计算唯一值的数量。
data = pd.Series([1, 2, 3, 2, 1, 4, 5, 2, 3])
unique_count = data.nunique()
print(unique_count)  # 输出:5

df = pd.DataFrame({'A': [1, 2, 3], 'B': [1, 1, 2]})
unique_count_col = df.nunique(axis=0)
print(unique_count_col)

# 输出:
# A    3
# B    2
# dtype: int64

统计每个值的频率分布

df.gene_target_symbol_name.value_counts()

这段代码是用来计算DataFrame(df)中某一列(gene_target_symbol_name)中每个唯一值(unique value)出现的次数(count)。它会返回一个Series对象,其中索引是唯一值,值是对应唯一值的出现次数。通过这个可以快速了解该列中每个值的频率分布。

value_counts()

value_counts()函数是pandas库中的一个方法,用于计算一个序列(Series)中每个唯一值的数量。

语法如下:

Series.value_counts(normalize=False, sort=True, ascending=False, bins=None, dropna=True)

参数:

  • normalize:是否返回相对频率,默认为False,即返回唯一值的数量。
  • sort:是否按值进行排序,默认为True,即按值进行排序。
  • ascending:是否按升序排列,默认为False,即按降序排列。
  • bins:指定柱状图的箱数。
  • dropna:是否排除缺失值,默认为True,即排除缺失值。

示例:

data = pd.Series([1, 2, 3, 2, 1, 4, 5, 2, 3])
value_count = data.value_counts()
print(value_count)

# 输出:
# 2    3
# 1    2
# 3    2
# 5    1
# 4    1
# dtype: int64

以上value_counts()方法计算了序列data中每个唯一值出现的次数,按降序排列输出。

one-hot特征的构造

# 如果有40个类别,那么会产生40列,如果第i行属于第j个类别,那么第j列第i行就是1,否则为0

df_gene_target_symbol_name = pd.get_dummies(df.gene_target_symbol_name)
df_gene_target_symbol_name.columns = [
    f"feat_gene_target_symbol_name_{c}" for c in df_gene_target_symbol_name.columns
]

时间特征构造

有可能
没看出来,啃臭cv一份,很妙

在数据观察的时候发现,siRNA_duplex_id的编码方式很有意思,其格式为AD-1810676.1,我们猜测AD是某个类别,后面的.1是版本,当中的可能是按照一定顺序的序列号,因此可以构造如下特征

siRNA_duplex_id_values = df.siRNA_duplex_id.str.split("-|\.").str[1].astype("int")

这段代码是从siRNA_duplex_id列中提取出按照一定顺序的序列号作为新的特征siRNA_duplex_id_values。
siRNA_duplex_id的编码格式为"AD-1810676.1",其中"AD"表示某个类别,".1"表示版本号,而中间的数字则是按照顺序的序列号。(假定,大概率)
代码通过使用正则表达式分隔符"-“和”.",将siRNA_duplex_id拆分成多个部分,然后取第二部分(索引为1),并将其转换为整数类型。得到的siRNA_duplex_id_values列即为按照一定顺序的序列号特征。

上述对每一个siRNA_duplex_id的过程同下

(方便复制)

import re
import pandas as pd
import numpy as np

str = "AD-1810676.1"
# 使用正则表达式分割字符串
parts = re.split(r'[-.]', str)
# 将数字部分转换为NumPy数组,并转换为整数类型
numbers = np.array(parts[1], dtype=int)
print(numbers)

在这里插入图片描述

包含某些单词

对df中的cell_line_donor列构造特征

# 对cell_line_donor列进行独热编码
df_cell_line_donor = pd.get_dummies(df.cell_line_donor)
# 为独热编码后的列名添加前缀
df_cell_line_donor.columns = [
    f"feat_cell_line_donor_{c}" for c in df_cell_line_donor.columns
]

# 创建新的特征列feat_cell_line_donor_hepatocytes,值为cell_line_donor列是否包含"Hepatocytes"的布尔值转换为整数
df_cell_line_donor["feat_cell_line_donor_hepatocytes"] = (
    (df.cell_line_donor.str.contains("Hepatocytes")).fillna(False).astype("int")
)

# 创建新的特征列feat_cell_line_donor_cells,值为cell_line_donor列是否包含"Cells"的布尔值转换为整数
df_cell_line_donor["feat_cell_line_donor_cells"] = (
    df.cell_line_donor.str.contains("Cells").fillna(False).astype("int")
)

代码小结
  1. 使用 pd.get_dummies() 函数对 cell_line_donor 列进行独热编码, 编码后的列会根据不同的取值创建新的列。
  2. 使用列表推导式为 df_cell_line_donor 的列名添加前缀 “feat_cell_line_donor_”。
  3. 创建新的特征列 feat_cell_line_donor_hepatocytes根据 cell_line_donor 列是否包含 “Hepatocytes” ,将布尔值转换为整数(1 表示包含,0 表示不包含)。
  4. 创建新的特征列 feat_cell_line_donor_cells根据 cell_line_donor 列是否包含 “Cells” 来确定的,将布尔值转换为整数(1 表示包含,0 表示不包含)。

cell_line_donor 列转换为独热编码,并创建两个新的特征列,用于表示是否包含特定的关键词。

对碱基的模式进行特征构造

根据上一个task中的rna知识提取

在这里插入图片描述

def siRNA_feat_builder(s: pd.Series, anti: bool = False):
    """
    构建siRNA特征的函数

    参数:
    s: pd.Series -- 输入的siRNA序列
    anti: bool -- 是否构建反义链特征,默认为False

    返回:
    pd.DataFrame -- 构建的siRNA特征DataFrame
    """
    name = "anti" if anti else "sense"  # 根据 anti 的值确定特征名称前缀
    df = s.to_frame()  # 将输入的 Series 对象转换为 DataFrame 对象
    df[f"feat_siRNA_{name}_seq_len"] = s.str.len()  # 计算序列长度,并将其作为特征添加到 DataFrame 中

    # 遍历两个位置:第一个和最后一个
    for pos in [0, -1]:
        # 遍历碱基:A、U、G、C
        for c in list("AUGC"):
            # 判断序列的第一个或最后一个碱基是否与当前碱基相等,并将结果作为特征添加到 DataFrame 中
            df[f"feat_siRNA_{name}_seq_{c}_{'front' if pos == 0 else 'back'}"] = (
                s.str[pos] == c
            )

    # 判断序列是否以特定的模式开头和结尾,并将结果作为特征添加到 DataFrame 中
    df[f"feat_siRNA_{name}_seq_pattern_1"] = s.str.startswith("AA") & s.str.endswith(
        "UU"
    )
    df[f"feat_siRNA_{name}_seq_pattern_2"] = s.str.startswith("GA") & s.str.endswith(
        "UU"
    )
    df[f"feat_siRNA_{name}_seq_pattern_3"] = s.str.startswith("CA") & s.str.endswith(
        "UU"
    )
    df[f"feat_siRNA_{name}_seq_pattern_4"] = s.str.startswith("UA") & s.str.endswith(
        "UU"
    )
    df[f"feat_siRNA_{name}_seq_pattern_5"] = s.str.startswith("UU") & s.str.endswith(
        "AA"
    )
    df[f"feat_siRNA_{name}_seq_pattern_6"] = s.str.startswith("UU") & s.str.endswith(
        "GA"
    )
    df[f"feat_siRNA_{name}_seq_pattern_7"] = s.str.startswith("UU") & s.str.endswith(
        "CA"
    )
    df[f"feat_siRNA_{name}_seq_pattern_8"] = s.str.startswith("UU") & s.str.endswith(
        "UA"
    )

    # 判断序列的第二位和倒数第二位是否为 A,并将结果作为特征添加到 DataFrame 中
    df[f"feat_siRNA_{name}_seq_pattern_9"] = s.str[1] == "A"
    df[f"feat_siRNA_{name}_seq_pattern_10"] = s.str[-2] == "A"

    # 计算序列中的 GC 碱基占整体长度的比例,并将结果作为特征添加到 DataFrame 中
    df[f"feat_siRNA_{name}_seq_pattern_GC_frac"] = (
        s.str.count("G") + s.str.count("C")
    ) / s.str.len()

    return df.iloc[:, 1:]  # 返回除第一列外的所有列,即去掉序列本身的列
代码小结
  1. “feat_siRNA_{name}_seq_len”:siRNA序列的长度作为特征。
  2. siRNA序列的第一个和最后一个位置,在前端或后端:
    • “feat_siRNA_{name}seq{c}_{‘front’ if pos == 0 else ‘back’}”:判断序列的第一个或最后一个碱基是否与’A’, ‘U’, ‘G’, 'C’相等。
  3. siRNA序列的起始和结束:
    • “feat_siRNA_{name}seq_pattern_1",…,"feat_siRNA{name}_seq_pattern_8”:判断序列是否以特定的模式开头和结尾。
  4. siRNA序列的第二位和倒数第二位:
    • “feat_siRNA_{name}_seq_pattern_9”:判断序列的第二位是否为’A’。
    • “feat_siRNA_{name}_seq_pattern_10”:判断序列的倒数第二位是否为’A’。
  5. “feat_siRNA_{name}_seq_pattern_GC_frac”:计算序列中的GC碱基占整体长度的比例。

最后选择模型预测

这里是task2给出的lightgbm的代码来对特征值预测 引一份

train_data = lgb.Dataset(X_train, label=y_train)
test_data = lgb.Dataset(X_test, label=y_test, reference=train_data)

def print_validation_result(env):
    result = env.evaluation_result_list[-1]
    print(f"[{env.iteration}] {result[1]}'s {result[0]}: {result[2]}")

params = {
    "boosting_type": "gbdt",
    "objective": "regression",
    "metric": "root_mean_squared_error",
    "max_depth": 7,
    "learning_rate": 0.02,
    "verbose": 0,
}

gbm = lgb.train(
    params,
    train_data,
    num_boost_round=15000,
    valid_sets=[test_data],
    callbacks=[print_validation_result],
)

tips : 可以多模型融合,k折 ,调超参等方法涨点

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值