#Datawhale AI夏令营 之 AI for Science(AI+药物)#深度学习

Task1:赛题解析&背景入门

个人赛前了解:        

RNA干扰(RNA interference,RNAi)是一种利用双链RNA(double-stranded RNA,ds RNA)依据碱基互补配对原则,实现转录后的基因沉默现象。植物、真菌、无脊椎动物和哺乳动物等真核生物都能够实现RNAi过程。在哺乳动物细胞中,ds RNA被剪切成较短的21-23nt的双链RNA,即小干RNA(small interfering RNA,siRNA),诱导靶标m RNA的降解。siRNA设计是将RNAi技术应用到研究基因功能与药物研发等领域的重要前提,也已经成为RNAi研究的一个热点。目前siRNA设计方法主要分为两类:基于统计规则的siRNA设计方法以及基于机器学习的siRNA设计方法。研究表明,基于机器学习的siRNA设计方法能够更准确地定量预测siRNA对靶标mRNA的沉默效率

赛题分析:

本比赛旨在使用基于机器学习的siRNA设计方法,预测siRNA对靶标mRNA的沉默效率,通过构建并优化模型,从而提升药物设计的效率和效果,我们要完成的任务就是预测某类药物基因对某类疾病基因的治疗效果

赛前思考:

本比赛应该使用哪些模型和方法来优化呢,有什么优缺点呢?

答:

  1. 传统机器学习模型
    • 线性回归(Linear Regression):适用于预测连续值,但可能无法捕捉siRNA序列的非线性关系。
    • 支持向量机(Support Vector Machines, SVM):可以有效处理高维数据,适用于分类和回归问题。
    • 随机森林(Random Forest):集成学习方法,可以处理大量的特征,并且对异常值不敏感。
    • 梯度提升决策树(Gradient Boosting Decision Tree, GBDT):也是集成学习方法,通过迭代优化来提高预测准确性。
  2. 深度学习模型
    • 卷积神经网络(Convolutional Neural Networks, CNN):适用于处理序列数据,可以捕捉局部特征。
    • Transformer模型:使用自注意力机制,能够处理长序列,并在NLP领域取得了显著成果,也被应用于生物信息学。
    在上一期的学习中我们了解到通常用于模型的优化的方法有:
  • 特征工程:包括序列的特征提取、编码以及化学修饰信息的整合。
  • 模型选择:根据问题的性质和数据的特点选择合适的模型。
  • 交叉验证:使用K折交叉验证等方法来评估模型的泛化能力。
  • 模型融合:结合多个模型的预测结果来提高整体的预测性能。

相关生物背景 :

RNA干扰(RNAi)机制

  • 背景:RNA干扰是一种自然的生物过程,通过它,细胞可以特异性地降解或抑制特定基因的mRNA,从而调控基因表达。
  • 机制:在RNAi过程中,双链RNA(dsRNA)被切割成21-23个核苷酸长的小片段,即siRNA。这些siRNA与RNA诱导沉默复合体(RISC)结合,并指导RISC识别并降解与siRNA互补的mRNA。

                                    

                                                 这里附一张dw提供的图帮助大家理解

siRNA药物的作用原理

  • 目标基因沉默:siRNA药物通过设计特定的siRNA序列,使其与疾病相关基因的mRNA互补,从而沉默这些基因,达到治疗目的。
  • 设计要素
    • 正义链(Sense Strand)反义链(Antisense Strand):siRNA由这两条链组成,反义链负责识别并结合目标mRNA。
    • 化学修饰:为了提高稳定性和减少免疫反应,siRNA的核苷酸可能会进行化学修饰。

siRNA修饰的目的

  • 提高稳定性:防止siRNA在生物体内被核酸酶降解,从而延长其作用时间。
  • 增强靶向性:确保siRNA能够特异性地结合并抑制靶基因的表达,减少脱靶效应。
  • 降低毒性:减少siRNA对细胞的毒性作用,提高其安全性。
  • 提高生物利用度:通过优化递送系统,提高siRNA在体内的吸收和分布效率。 

siRNA药物的应用

  • 疾病治疗:siRNA药物被用于治疗多种疾病,包括遗传性疾病、病毒性疾病和某些类型的癌症。
  • 药物开发挑战
    • 脱靶效应:siRNA可能非特异性地结合并沉默非目标基因,导致不良副作用。
    • 递送问题:如何将siRNA有效地递送到靶细胞是一个技术挑战。

siRNA药物研发的关键步骤

  • 序列设计:选择合适的siRNA序列以特异性地针对目标mRNA。
  • 化学修饰:对siRNA进行化学修饰以提高其稳定性和降低免疫原性。
  • 体外和体内实验:通过实验室测试来评估siRNA的沉默效率和安全性。
  • 临床前和临床试验:在动物和人类中进行试验,以验证siRNA药物的安全性和有效性。

siRNA药效预测的重要性

  • 优化设计:通过预测模型,可以快速筛选出高效的siRNA序列,减少实验成本和时间。
  • 降低风险:预测模型有助于识别可能引起不良反应的siRNA序列,降低临床失败的风险。

更多详情可见Datawhale

下面进入正文:

首先了解一下评分规则:

在这次比赛中,模型的评分由多个指标共同决定,以全面评估模型的性能。这些指标包括平均绝对误差(MAE)、区间内的平均绝对误差(Range MAE和和F1得分(F1 Score)。这些指标分别衡量模型在预测上的准确性和稳定性,以及在区间内的表现。最终的评分(Score)是综合这些指标的加权结果,具体代码下面有所讲解。

数据集介绍:

train_data.csv

train_data.csv的每行为一条训练记录,包含数据记录的id、siRNA裸序列、相应的siRNA修饰序列、目标mRNA序列、siRNA浓度、细胞系、转染方法等实验室条件以及对应的实验室测量的mRNA Remaining值等总计19个字段。其中mRNA Remaining值为我们模型的训练目标,其余18个字段的全部或部分可以作为模型的输入特征。Remaining值代表了经过siRNA的沉默之后,mRNA的剩余百分比(相对于对照组)。Remaining值越低,siRNA的沉默效率越好,药效就越好。Remaining值一般位于0-100的区间内,100表示完全没有沉默效果,0表示该mRNA被彻底沉默,但是由于实验室测量的误差,可能存在少量训练记录的mRNA Remaining值在这个范围之外,这是正常的数据。

vocab.csv

vocab.csv为经过归一化后的siRNA化学修饰缩写表

  • 第一列(Abbreviation):列出了各种修饰核苷酸的缩写。
  • 第二列(Chemical Name):展示了与第一列中缩写对应的完整化学名称。这些化学名称详细描述了核苷酸的结构,包括对其进行的化学修饰。这些修饰可能涉及改变核苷酸的糖部分、磷酸基团或碱基,以增强其性能,如提高稳定性或特定的生物活性。

进入代码部分:

### 1. 依赖库的导入
import os  # 文件操作
import torch  # 深度学习框架
import random  # 随机数生成
import numpy as np  # 数值计算
import pandas as pd  # 数据处理

import torch.nn as nn  # 神经网络模块
import torch.optim as optim  # 优化器模块

from tqdm import tqdm  # 进度条显示
from rich import print  # 美化打印输出
from collections import Counter  # 计数器工具

from torch.utils.data import Dataset, DataLoader  # 数据集和数据加载器
from sklearn.model_selection import train_test_split  # 数据集划分
from sklearn.metrics import precision_score, recall_score, mean_absolute_error  # 模型评估指标

# 这些库包括了文件操作、深度学习、数据处理、模型评估等必要的工具。

# 该函数确保了在使用NumPy、Python内置随机数生成器和PyTorch时,所有的随机数生成都是可控的和可复现的,有助于实验结果的一致性。
def set_random_seed(seed):
    # 设置NumPy的随机种子
    np.random.seed(seed)
    # 设置Python内置的随机数生成器的种子
    random.seed(seed)
    # 设置PyTorch的随机种子
    torch.manual_seed(seed)
    # 设置CUDA的随机种子
    torch.cuda.manual_seed(seed)
    # 设置所有CUDA设备的随机种子
    torch.cuda.manual_seed_all(seed)
    # 确保每次卷积算法选择都是确定的
    torch.backends.cudnn.deterministic = True
    # 关闭CuDNN自动优化功能,确保结果可复现
    torch.backends.cudnn.benchmark = False

上面就是一些基本的库,个人感觉值得一提的是以下几个

数据集和数据加载器

  • torch.utils.data.Dataset: PyTorch 的数据集抽象基类,用于定义数据集的加载和访问方式。
  • torch.utils.data.DataLoader: PyTorch 的数据加载器,用于批量加载数据集并进行预处理。

数据集划分:sklearn.model_selection.train_test_split: 用于将数据集划分为训练集和测试集。

模型评估指标

  • sklearn.metrics.precision_score: 计算精确度。
  • sklearn.metrics.recall_score: 计算召回率。
  • sklearn.metrics.mean_absolute_error: 计算平均绝对误差。

还有set_random_seed 函数,用于设置随机数生成器的种子,以确保随机数生成的可复现性和可控性。

### 2. 基因组分词器类
该类用于将基因组序列分割成固定长度的n-gram。

class GenomicTokenizer:
    def __init__(self, ngram=5, stride=2):
        # 初始化分词器,设置n-gram长度和步幅
        self.ngram = ngram
        self.stride = stride
        
    def tokenize(self, t):
        # 将输入序列转换为大写
        t = t.upper()
        
        if self.ngram == 1:
            # 如果n-gram长度为1,直接将序列转换为字符列表
            toks = list(t)
        else:
            # 否则,按照步幅对序列进行n-gram分词
            toks = [t[i:i+self.ngram] for i in range(0, len(t), self.stride) if len(t[i:i+self.ngram]) == self.ngram]
        
        # 如果最后一个分词长度小于n-gram,移除最后一个分词
        if len(toks[-1]) < self.ngram:
            toks = toks[:-1]
        
        # 返回分词结果
        return toks

类属性

  • ngram: n-gram 的长度,默认为 5。
  • stride: 分词的步幅,默认为 2。

类方法

  • __init__(self, ngram=5, stride=2): 初始化方法,设置 n-gram 长度和步幅。
  • tokenize(self, t): 分词方法,将输入序列分割成 n-gram 列表。主要步骤:
    1. 将输入序列转换为大写。
    2. 如果 n-gram 长度为 1,则直接将序列转换为字符列表。
    3. 否则,按照步幅对序列进行 n-gram 分词,并确保每个 n-gram 的长度等于 n-gram 长度。
    4. 如果最后一个 n-gram 长度小于 n-gram,则移除最后一个 n-gram。
    5. 返回分词结果列表。
    6. 如果还不理解怎么分词请看下图

               

### 3. 基因组词汇类

该类用于创建一个词汇表,用于将基因组片段映射为索引。

class GenomicVocab:
    def __init__(self, itos):
        # 初始化词汇表,itos是一个词汇表列表
        self.itos = itos
        # 创建从词汇到索引的映射
        self.stoi = {v: k for k, v in enumerate(self.itos)}
        
    @classmethod
    def create(cls, tokens, max_vocab, min_freq):
        # 创建词汇表类方法
        # 统计每个token出现的频率
        freq = Counter(tokens)
        # 选择出现频率大于等于min_freq的token,并且最多保留max_vocab个token
        itos = ['<pad>'] + [o for o, c in freq.most_common(max_vocab - 1) if c >= min_freq]
        # 返回包含词汇表的类实例
        return cls(itos)

类属性

  • itos: 一个列表,包含了词汇表中的所有单词或序列片段,索引从 0 开始。
  • stoi: 一个字典,将单词或序列片段映射到其在 itos 列表中的索引。

类方法

  • __init__(self, itos): 初始化方法,接收一个词汇表列表 itos 作为输入,并创建一个从词汇到索引的映射 stoi
  • create(cls, tokens, max_vocab, min_freq): 类方法,用于创建一个新的词汇表实例。主要步骤:
    1. 统计每个 token(单词或序列片段)出现的频率。
    2. 选择出现频率大于等于 min_freq 的 token,并且最多保留 max_vocab 个 token。
    3. 创建词汇表列表 itos,包含 <pad> 作为 padding 字符,然后是按频率排序的 token。
    4. 返回包含词汇表的类实例。

这里提出一个重要思路:如果一段基因片段在疾病中高频率出现,那么他很有可能是“坏人”,反之如果在药物基因序列中频繁出现,那么他可能是“好人”,日后都需要重点关注

 

### 4. siRNA数据集类

该类用于加载siRNA数据,并将序列数据转换为模型可以处理的格式。

class SiRNADataset(Dataset):
    def __init__(self, df, columns, vocab, tokenizer, max_len, is_test=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个样本
        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):
        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)

 

类属性

  • df: 一个 Pandas 数据框,包含了 siRNA 数据。
  • columns: 一个列表,包含了包含序列数据的列名。
  • vocab: 一个 GenomicVocab 实例,用于将序列片段映射为索引。
  • tokenizer: 一个 GenomicTokenizer 实例,用于将序列分割成 n-gram。
  • max_len: 一个整数,定义了序列的最大长度。
  • is_test: 一个布尔值,指示数据集是否为测试集。

类方法

  • __init__(self, df, columns, vocab, tokenizer, max_len, is_test=False): 初始化方法,接收数据框、列名、词汇表、分词器、最大序列长度和是否为测试集的参数。
  • __len__(self): 返回数据集的长度。
  • __getitem__(self, idx): 获取数据集中的第 idx 个样本。主要步骤:
    1. 从数据框中获取第 idx 行数据。
    2. 对数据框中的每一列进行分词和编码。
    3. 如果数据集是测试集,仅返回编码后的序列。
    4. 如果数据集不是测试集,获取目标值并将其转换为张量。
    5. 返回编码后的序列和目标值。
  • tokenize_and_encode(self, seq): 辅助方法,用于对序列进行分词和编码。主要步骤:
    1. 如果序列包含空格,按空格分词。
    2. 如果序列不包含空格,使用分词器进行分词。
    3. 将 token 转换为索引,未知 token 使用 0()。
    4. 将序列填充到最大长度。
    5. 返回张量格式的序列。
### 5. siRNA Model

这是一个基于GRU的神经网络模型,用于处理siRNA序列。

class SiRNAModel(nn.Module):
    def __init__(self, vocab_size, embed_dim=200, hidden_dim=256, n_layers=3, dropout=0.5):
        super(SiRNAModel, self).__init__()
        
        # 初始化嵌入层
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        # 初始化GRU层
        self.gru = nn.GRU(embed_dim, hidden_dim, n_layers, bidirectional=True, batch_first=True, dropout=dropout)
        # 初始化全连接层
        self.fc = nn.Linear(hidden_dim * 4, 1)  # hidden_dim * 4 因为GRU是双向的,有n_layers层
        # 初始化Dropout层
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x):
        # 将输入序列传入嵌入层
        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()

 

类属性

  • vocab_size: 一个整数,表示词汇表的大小。
  • embed_dim: 一个整数,表示嵌入层的维度。
  • hidden_dim: 一个整数,表示 GRU 层的隐藏状态维度。
  • n_layers: 一个整数,表示 GRU 层的层数。
  • dropout: 一个浮点数,表示 dropout 的比例。

类方法

  • __init__(self, vocab_size, embed_dim=200, hidden_dim=256, n_layers=3, dropout=0.5): 初始化方法,接收词汇表大小、嵌入维度、隐藏状态维度、层数和 dropout 比例的参数。
  • forward(self, x): 前向传播方法,接收一个张量 x 作为输入,其中 x 是一个列表,每个元素是一个序列的张量表示。主要步骤:
    1. 将输入序列传入嵌入层。
    2. 对每个嵌入的序列进行处理,通过 GRU 层,并将结果通过 dropout 层。
    3. 将所有序列的输出拼接起来。
    4. 传入全连接层。
    5. 返回结果。
    6. 同样附一张dw提供的参考图帮助大家理解
### 6. 评估指标计算函数

该函数用于计算模型的各项评估指标,包括精确度、召回率、F1值和评分。

def calculate_metrics(y_true, y_pred, threshold=30):
    # 计算平均绝对误差
    mae = np.mean(np.abs(y_true - y_pred))

    # 将实际值和预测值转换为二进制分类(低于阈值为1,高于或等于阈值为0)
    y_true_binary = (y_true < threshold).astype(int)
    y_pred_binary = (y_pred < threshold).astype(int)

    # 创建掩码,用于筛选预测值在0和阈值之间的样本
    mask = (y_pred >= 0) & (y_pred <= threshold)
    range_mae = mean_absolute_error(y_true[mask], y_pred[mask]) if mask.sum() > 0 else 100

    # 计算精确度、召回率和F1得分
    precision = precision_score(y_true_binary, y_pred_binary, average='binary')
    recall = recall_score(y_true_binary, y_pred_binary, average='binary')
    f1 = 2 * precision * recall / (precision + recall)

    # 计算综合评分
    score = (1 - mae / 100) * 0.5 + (1 - range_mae / 100) * f1 * 0.5

    return score


### 7. 模型评估函数

该函数用于在测试集上评估模型性能。

def evaluate_model(model, test_loader, device='cuda'):
    # 设置模型为评估模式
    model.eval()
    predictions = []
    targets = []
    
    # 禁用梯度计算
    with torch.no_grad():
        # 遍历测试数据加载器中的每个批次
        for inputs, target in test_loader:
            # 将输入数据移动到指定设备上
            inputs = [x.to(device) for x in inputs]
            # 获取模型的输出
            outputs = model(inputs)
            # 将预测结果从GPU移到CPU,并转换为numpy数组,添加到predictions列表中
            predictions.extend(outputs.cpu().numpy())
            # 将目标值转换为numpy数组,添加到targets列表中
            targets.extend(target.numpy())

    # 将预测结果和目标值转换为numpy数组
    y_pred = np.array(predictions)
    y_test = np.array(targets)
    
    # 计算评估指标
    score = calculate_metrics(y_test, y_pred)
    # 打印测试得分
    print(f"Test Score: {score:.4f}")

 这两部分就是上面提到的通过计算函数来评估模型性能的部分

### 8. 模型训练函数

函数用于训练模型,并在每个epoch后评估模型的性能,保存最佳模型。

def train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=50, device='cuda', output_dir: str=""):
    # 将模型移动到指定设备
    model.to(device)
    best_score = -float('inf')  # 初始化最佳得分
    best_model = None  # 初始化最佳模型

    for epoch in range(num_epochs):
        model.train()  # 设置模型为训练模式
        train_loss = 0  # 初始化训练损失
        for inputs, targets in tqdm(train_loader, desc=f'Epoch {epoch+1}/{num_epochs}'):
            inputs = [x.to(device) for x in inputs]  # 将输入移动到设备
            targets = targets.to(device)  # 将目标值移动到设备
            
            optimizer.zero_grad()  # 清空梯度
            outputs = model(inputs)  # 前向传播
            loss = criterion(outputs, targets)  # 计算损失
            loss.backward()  # 反向传播
            optimizer.step()  # 更新参数
            
            train_loss += loss.item()  # 累加训练损失
        
        model.eval()  # 设置模型为评估模式
        val_loss = 0  # 初始化验证损失
        val_preds = []
        val_targets = []

        with torch.no_grad():
            for inputs, targets in val_loader:
                inputs = [x.to(device) for x in inputs]  # 将输入移动到设备
                targets = targets.to(device)  # 将目标值移动到设备
                outputs = model(inputs)  # 前向传播
                loss = criterion(outputs, targets)  # 计算损失
                val_loss += loss.item()  # 累加验证损失
                val_preds.extend(outputs.cpu().numpy())  # 收集预测值
                val_targets.extend(targets.cpu().numpy())  # 收集目标值
        
        train_loss /= len(train_loader)  # 计算平均训练损失
        val_loss /= len(val_loader)  # 计算平均验证损失
        
        val_preds = np.array(val_preds)
        val_targets = np.array(val_targets)
        score = calculate_metrics(val_targets, val_preds)  # 计算验证集上的得分
        
        print(f'Epoch {epoch+1}/{num_epochs}')
        print(f'Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}')
        print(f'Learning Rate: {optimizer.param_groups[0]["lr"]:.6f}')
        print(f'Validation Score: {score:.4f}')

        if score > best_score:
            best_score = score  # 更新最佳得分
            best_model = model.state_dict().copy()  # 更新最佳模型
            torch.save(model.state_dict(), os.path.join(output_dir, "best.pt".format(epoch)))  # 保存最佳模型
            print(f'New best model found with score: {best_score:.4f}')

    return best_model  # 返回最佳模型

模型训练函数:用于训练模型,并在每个 epoch 后评估模型的性能,保存最佳模型。在深度学习模型训练中,这种方法非常常见,因为它允许模型在训练过程中不断优化,并在达到最佳性能时保存模型。

epoch:神经网络已经遍历(或者说“学习”)了训练集中的每一个样本一次

模型训练通常在一个或多个epoch中进行,每个epoch包含对整个训练集的完整遍历。在每个epoch开始时,将模型设置为训练模式

在每个epoch结束时,将模型设置为评估模式。然后,通过遍历val_loader中的输入和目标值来进行模型的验证。对于每个批次的数据,将其移动到设备上,执行前向传播、计算损失、收集预测值和目标值。累加验证损失。

最后,返回最佳模型

在每个epoch中,模型会对每个批次的数据进行以下操作:

  • 前向传播:将数据输入模型,得到预测值。
  • 计算损失:使用损失函数计算预测值与真实值之间的差异。
  • 反向传播:根据损失函数的梯度反向传播到模型的每一层,计算每个参数的梯度。
  • 参数更新:使用优化器根据计算出的梯度更新模型的参数。
### 9. 训练主程序

# 设置参数
bs = 64    # 批次大小
epochs = 50    # 训练的迭代次数
lr = 0.001    # 学习率
seed = 42    # 随机种子
output_dir = "output/models"    # 模型保存路径

# 选择设备
device = 'cuda' if torch.cuda.is_available() else 'cpu'

# 设置随机种子以确保结果可重复
set_random_seed(seed)

# 创建输出目录
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# 加载数据
train_data = pd.read_csv('train_data.csv')

# 指定需要处理的列
columns = ['siRNA_antisense_seq', 'modified_siRNA_antisense_seq_list']
# 删除包含空值的行
train_data.dropna(subset=columns + ['mRNA_remaining_pct'], inplace=True)
# 将数据分为训练集和验证集
train_data, val_data = train_test_split(train_data, test_size=0.1, random_state=42)

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

# 创建词汇表
all_tokens = []
for col in columns:
    for seq in train_data[col]:
        if ' ' in seq:  # 修改过的序列
            all_tokens.extend(seq.split())
        else:
            all_tokens.extend(tokenizer.tokenize(seq))
vocab = GenomicVocab.create(all_tokens, max_vocab=10000, min_freq=1)

# 找到最大序列长度
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)

# 创建数据集
train_dataset = SiRNADataset(train_data, columns, vocab, tokenizer, max_len)
val_dataset = SiRNADataset(val_data, columns, vocab, tokenizer, max_len)

# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=bs, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=bs)

# 初始化模型
model = SiRNAModel(len(vocab.itos))
criterion = nn.MSELoss()

# 初始化优化器
optimizer = optim.Adam(model.parameters(), lr=lr)

# 训练模型
best_model = train_model(model, train_loader, val_loader, criterion, optimizer, epochs, device, output_dir=output_dir)

 这一部分主要定义了一个名为 train_main 的训练主程序,用于训练一个基于 GRU 的 siRNA 序列预测模型。在这个程序中,我们定义了训练参数,加载数据,创建分词器和词汇表,构建数据集和数据加载器,初始化模型和优化器,并最终调用 train_model 函数来训练模型。

### 10. 测试程序

# 设置输出目录
output_dir = "result"
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# 加载测试数据
test_data = pd.read_csv('sample_submission.csv')
columns = ['siRNA_antisense_seq', 'modified_siRNA_antisense_seq_list']
test_data.dropna(subset=columns, inplace=True)

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

# 创建词汇表
all_tokens = []
for col in columns:
    for seq in test_data[col]:
        if ' ' in seq:  # 修改过的序列
            all_tokens.extend(seq.split())
        else:
            all_tokens.extend(tokenizer.tokenize(seq))
            
vocab = GenomicVocab.create(all_tokens, max_vocab=10000, min_freq=1)

# 找到最大序列长度
max_len = max(max(len(seq.split()) if ' ' in seq else len(tokenizer.tokenize(seq)) 
                    for seq in test_data[col]) for col in columns)

# 创建测试数据集
test_dataset = SiRNADataset(test_data, columns, vocab, tokenizer, max_len, is_test=True)

# 创建数据加载器
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)

# 初始化模型
model = SiRNAModel(len(vocab.itos))
model.load_state_dict(best_model)  # 加载最佳模型权重
model.to(device=device)
model.eval()  # 切换到评估模式,这对于某些模块如Dropout和BatchNorm是必需的

# 进行预测
preds = []
with torch.no_grad():
    for inputs in tqdm(test_loader):
        # import pdb;pdb.set_trace()
        inputs = [x.to(device) for x in inputs]
        outputs = model(inputs)
        preds.extend(outputs.cpu().numpy())

# 将预测结果添加到测试数据中
test_data["mRNA_remaining_pct"] = preds
df = pd.DataFrame(test_data)

# 保存预测结果
output_csv = os.path.join(output_dir, "submission.csv")
print(f"submission.csv 保存在 {output_csv}")
df.to_csv(output_csv, index=False)

生成预测结果并保存,之后可以去官网提交从而获取分数

总结

至此task1的部分也结束了hh,得到的分数为0.5038~

接下来我们可以考虑如何进行优化,例如RNN模型分析(对LSTM 的改进和对GRU 的改进),也可以加入特征构造等方法进行优化,本人纯小白,上文有借助检索解决疑惑和一些自己的理解,如有不对的地方还请各位大佬提出,看到后就会修改的hh,期待task2的学习qwq

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值