Datawhale AI夏令营——siRNA药物药效预测学习笔记

一、赛事介绍

1.赛道

第二届世界科学智能大赛生命科学赛道:siRNA药物药效预测

2.任务

预测某类药物基因(siRNA)对某类疾病基因(mRNA)的治疗效果(利用化学修饰后的siRNA序列来预测RNA干扰(RNAi)机制下对靶基因(target gene)的沉默效率)。

3.数据

官方提供数据压缩包解压后(可以在终端输入unzip+文件)

_MACOSX:自动生成缓存文件

baseline.py:官方基础代码

sample_submission.csv:为初赛leaderboard submission测试集,最后一栏空白,需要填充空白处的预测结果

train_data.csv:训练集

vocab.csv:相关变量的解释

训练集如图:

数据表头内容解释:

4.评测

将公开文献中的siRNA序列数据随机打乱后,提供选手85%数据训练评估,保留15%数据用于leaderboard评分。根据以上指标最终得分基于计算公式:

score = 50% × (1 − MAE / 100) + 50% × F1 × (1 − Range-MAE / 100)

二、代码学习

本次使用modelscopep平台GPU环境跑通datawhale提供的baseline代码。

task1(baseline)

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

2. 创建基因组的分词器类

作用:将基因组的序列分割成固定长度。

过程:1.输入序列转化为大写;2.长序列分割成多个短序列(基因片段)。

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分词,确保每个分词长度等于ngram
            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

 3. 创建基因组的词汇表

作用:将基因组的特定片段(在模型中比较难处理的片段)与索引相关联,方便在后续数据分析中高效存储、检索和分析。

过程:1.统计每个基因片段的出现频率;2.按高到低的顺序排序基因片段;3.选取出现频率>=min_fred的基因片段加入词汇表(用于选取高频、特殊、影响大的基因片段,删除低频、普通、影响小的基因片段);4.保留max_vocab个基因片段(词汇表和算力有限)。

class GenomicVocab:
    def __init__(self, itos):
        # 初始化词汇表,itos是一个词汇表列表
        self.itos = itos
        # 创建从词汇到索引的映射
        self.stoi = {v: k for k, v in enumerate(self.itos)}
        
    @classmethod
    # tokens:所有token的列表,max_vocab:词汇表的最大容量,min_freq:词汇在被包含到词汇表中的最低频率
    def create(cls, tokens, max_vocab, min_freq):
        # 统计每个token出现的频率
        freq = Counter(tokens)
        # 按照频率从高到低排序,并选择出现频率大于等于min_freq的token,最多保留max_vocab个token,然后在词汇表中添加一个特殊的<pad>token,用于填充序列
        itos = ['<pad>'] + [o for o, c in freq.most_common(max_vocab - 1) if c >= min_freq]
        # 返回包含词汇表的类实例
        return cls(itos)

 4. siRNA数据集类

作用:将序列数据转化为模型可处理数据。

过程:1.文本处理;2.分词;3.词汇编码;4.序列填充或截断;5.转化为张量(将文本序列转化多维数组);6.数据加载与批处理。

class SiRNADataset(Dataset):
    def __init__(self, df, columns, vocab, tokenizer, max_len, is_test=False):
        # 初始化数据集
        self.df = df  # 包含数据的Pandas数据框
        self.columns = columns  # 包含序列的列名列表
        self.vocab = vocab  # 词汇表对象,用于将token转换为索引
        self.tokenizer = tokenizer  # 分词器对象,用于将序列分割为token
        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)

 5. siRNA Model

作用:创建一个基于GRU的神经网络模型,用于处理siRNA序列。

GRU模型是RNN(循环神经网络)的一种变体,通过引入更新门和重置门两个控制门结构,有效解决传统RNN在处理长序列数据时容易出现的梯度问题,并且在多个领域展现出了优越的性能。

h_{t}:模型输出

h_{t-1}:过去的信息

\tilde{h}_{t}:当前的信息

GUR模型介绍参考以下网站:

https://pytorch.org/docs/stable/generated/torch.nn.GRU.html

class SiRNAModel(nn.Module):
    def __init__(self, vocab_size, embed_dim=200, hidden_dim=256, n_layers=3, dropout=0.5): # vocab_size:词汇表大小,用于嵌入层;embed_dim:嵌入维度,嵌入层将词汇映射为 embed_dim 维向量;hidden_dim:隐藏层维度,GRU的隐藏状态维度;n_layers:GRU的层数;dropout:Dropout层的丢弃率,用于防止过拟合。

        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()

 6. 评估指标计算函数

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

def calculate_metrics(y_true, y_pred, threshold=30):
    # 计算实际值和预测值之间的平均绝对误差
    mae = np.mean(np.abs(y_true - y_pred))

    # 将实际值和预测值转换为二进制分类
    y_true_binary = (y_true < threshold).astype(int) # 如果实际值小于阈值,设为1,否则设为0
    y_pred_binary = (y_pred < threshold).astype(int) # 如果预测值小于阈值,设为1,否则设为0

    # 创建掩码
    mask = (y_pred >= 0) & (y_pred <= threshold) # 筛选预测值在0和阈值之间的样本
    range_mae = mean_absolute_error(y_true[mask], y_pred[mask]) if mask.sum() > 0 else 100 # 计算这些样本的平均绝对误差,如果没有符合条件的样本,设为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(在神经网络学习训练集每个样本一次)后评估模型的性能,保存最佳模型。

过程:1.将数据输入模型得到预测值;2.用损失函数计算预测值和真实值之间的差异;3.根据损失函数的梯度反向传播到模型每一层,计算每个参数的梯度;4.根据计算出来的梯度更新模型参数。

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  # 返回最佳模型

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)

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)

task2(基于lightgbm的baseline)

import pandas as pd
df_original = pd.read_csv("train_data.csv")
n_original = df_original.shape[0] # 获取df_original的行数
df_submit = pd.read_csv("sample_submission.csv")
df = pd.concat([df_original, df_submit], axis=0).reset_index(drop=True) # 合并df_original和df_submit方便后续数据预处理

特征工程

1.根据相关知识进行具备重要特征列优先类别的提取。

def siRNA_feat_builder(s: pd.Series, anti: bool = False): # s:一个pandas Series对象,其中每个元素代表一个siRNA序列;anti:一个布尔值,默认为 False。如果为True,则表示正在处理的是反义siRNA序列;如果为False,则表示处理的是正义siRNA序列。
    name = "anti" if anti else "sense"
    # 将输入的Series s转换为一个DataFrame df,以便能够添加额外的列作为特征
    df = s.to_frame() 
    # 序列长度
    df[f"feat_siRNA_{name}_seq_len"] = s.str.len() 
    for pos in [0, -1]:
        for c in list("AUGC"):
            # 遍历序列的开始(pos == 0)和结束(pos == -1)位置,并检查这些位置上的核苷酸是否为A、U、G、C中的一个
            df[f"feat_siRNA_{name}_seq_{c}_{'front' if pos == 0 else 'back'}"] = (
                s.str[pos] == c
            )
    # 是否已某一对碱基开头和某一对碱基结尾
    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
    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占整体长度的比例
    df[f"feat_siRNA_{name}_seq_pattern_GC_frac"] = (
        s.str.contains("G") + s.str.contains("C")
    ) / s.str.len()
    return df.iloc[:, 1:]

2.对剩余列进行优化和预处理 

df_publication_id = pd.get_dummies(df.publication_id)
df_publication_id.columns = [
    f"feat_publication_id_{c}" for c in df_publication_id.columns
]
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
]
df_gene_target_ncbi_id = pd.get_dummies(df.gene_target_ncbi_id)
df_gene_target_ncbi_id.columns = [
    f"feat_gene_target_ncbi_id_{c}" for c in df_gene_target_ncbi_id.columns
]
df_gene_target_species = pd.get_dummies(df.gene_target_species)
df_gene_target_species.columns = [
    f"feat_gene_target_species_{c}" for c in df_gene_target_species.columns
]
siRNA_duplex_id_values = df.siRNA_duplex_id.str.split("-|\.").str[1].astype("int")
siRNA_duplex_id_values = (siRNA_duplex_id_values - siRNA_duplex_id_values.min()) / (
    siRNA_duplex_id_values.max() - siRNA_duplex_id_values.min()
)
df_siRNA_duplex_id = pd.DataFrame(siRNA_duplex_id_values)
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
]
df_cell_line_donor["feat_cell_line_donor_hepatocytes"] = (
    (df.cell_line_donor.str.contains("Hepatocytes")).fillna(False).astype("int")
)
df_cell_line_donor["feat_cell_line_donor_cells"] = (
    df.cell_line_donor.str.contains("Cells").fillna(False).astype("int")
)
df_siRNA_concentration = df.siRNA_concentration.to_frame()
df_Transfection_method = pd.get_dummies(df.Transfection_method)
df_Transfection_method.columns = [
    f"feat_Transfection_method_{c}" for c in df_Transfection_method.columns
]
df_Duration_after_transfection_h = pd.get_dummies(df.Duration_after_transfection_h)
df_Duration_after_transfection_h.columns = [
    f"feat_Duration_after_transfection_h_{c}"
    for c in df_Duration_after_transfection_h.columns
]
# 将所有处理完的列归并为特征
feats = pd.concat(
    [
        df_publication_id,
        df_gene_target_symbol_name,
        df_gene_target_ncbi_id,
        df_gene_target_species,
        df_siRNA_duplex_id,
        df_cell_line_donor,
        df_siRNA_concentration,
        df_Transfection_method,
        df_Duration_after_transfection_h,
        siRNA_feat_builder(df.siRNA_sense_seq, False),
        siRNA_feat_builder(df.siRNA_antisense_seq, True),
        df.iloc[:, -1].to_frame(),
    ],
    axis=1,
)

创建lightgbm模型

lightgbm模型介绍参考以下网站:

Parameters Tuning — LightGBM 4.0.0 documentation

https://datawhalechina.github.io/ML-FTTI/01_tree_ensemble/04_gbdt.html

https://lightgbm.readthedocs.io/en/stable/Parameters.html

import lightgbm as lgb # 梯度提升决策树库,用于分类、回归问题
from sklearn.model_selection import train_test_split # 提供模型选择和评估工具,包括数据集的分割、网格搜索等

X_train, X_test, y_train, y_test = train_test_split(
    # 数据切片,用于特征集
    feats.iloc[:n_original, :-1],
    # 选择目标变量
    feats.iloc[:n_original, -1],
    # 根据赛题要求进行数据划分
    test_size=0.2,
    random_state=42,
)
# 创建一个LightGBM的Dataset对象,用于训练数据
# X_train是特征数据,y_train是对应的目标变量
train_data = lgb.Dataset(X_train, label=y_train)
# X_test是测试数据的特征,y_test是对应的目标变量
test_data = lgb.Dataset(X_test, label=y_test, reference=train_data)


# 定义一个回调函数来打印验证集的结果,该函数将在LightGBM的每次迭代后被调用
def print_validation_result(env):
    # 从env.evaluation_result_list中获取最后一次迭代的结果,这个列表包含了每次迭代时验证集上的评估结果
    result = env.evaluation_result_list[-1]
    print(f"[{env.iteration}] {result[1]}'s {result[0]}: {result[2]}")

# 设置LightGBM的训练参数
params = {
    "boosting_type": "gbdt", # 使用梯度提升决策树作为基学习器
    "objective": "regression", # 设置学习任务为回归
    "metric": "root_mean_squared_error", # 使用均方根误差作为评估指标
    "max_depth": 7, # 设置树的最大深度为7
    "learning_rate": 0.02, # 设置学习率为0.02
    "verbose": 0, # 设置LightGBM的打印信息级别为0,即不打印训练信息
}
gbm = lgb.train(
    params,
    train_data,
    num_boost_round=15000, # num_boost_round=15000指定了最大迭代次数为15000
    valid_sets=[test_data], # 指定验证集为之前创建的test_data
    callbacks=[print_validation_result], # 指定回调函数列表,包含之前定义的打印验证集结果的函数
)

# 对给定的最后一列特征集进行预测
y_pred = gbm.predict(feats.iloc[n_original:, :-1])
# 将结果填充并生成文件
df_submit["mRNA_remaining_pct"] = y_pred
df_submit.to_csv("submission.csv", index=False)

三、思考与总结

1.问题的处理思路

       如处理生物问题,首先要分清当前是一个分类、回归或者生成问题,然后确定模型选择,接着考虑如何把数据处理到模型中,再从处理数据的过程中结合生物学知识构造特征,最后再用模型训练这些特征和预测结果,不断优化得到最优结果。

2.特征构造的数据分析

       特征构造不受问题影响,即特征构造在不同模型是通用的,关键在于如何构造特征。

       如先把训练集的一个数据列提取出来,然后分析该数据是数值型变量或者是分类变量。

       1.数据是数值型变量,那么需将其均值、方差、偏度分布等可视化。

       2.数据是类别型变量,那么需进行类别的统计、不同类占比的比较(不同类别样本数量)如果是高样本类别,其价值可能相对低样本类别较高,全样本一类对预测影响不大,其价值相对较小;并且根据问题相关知识进行优先类别的提取(如传统siRNA设计原则中,对于序列而言,AA+(Nn)UU最好,那么就可以优先提取该类);当数据集中某些连续变量的分布非常不均时,可以采用分箱的办法(离散化)处理。

3.模型处理

       1.模型构造过程:首先要关注从原始的特征到输入模型之前经历的过程,然后思考输入模型后到模型输出过程,最后考虑模型输出与训练集上的预测值之间的损失。

       2.调参方向性问题:调参需考虑其对结果的作用,尤其是过拟合和欠拟合,如lightgbm模型中树越深、叶子结点过多可能会过拟合。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值