当前位置: 首页 > news >正文

Datawhale AI春训营 世界科学智能大赛--合成生物赛道:蛋白质固有无序区域预测 小白经验总结

一、报名大赛

二、跑通baseline

在魔塔社区创建实例,根据教程完成速通第一个分数~

Datawhale-学用 AI,从此开始

三、优化实例(这里是我的学习优化过程)

1.先将官方给的的模型训练实例了解一遍(敲一敲代码)

训练模型nn_baseline

导入必要的库

import argparse  # 解析命令行参数
import math      # 数学运算(如log)
import pickle    # 加载.pkl数据文件import torch     # PyTorch深度学习框架
import torch.nn as nn  # 神经网络模块
import torch.nn.functional as F  # 激活函数等from tqdm import tqdm  # 进度条显示
from omegaconf import OmegaConf  # 配置文件管理
from sklearn.metrics import f1_score  # 评估指标
from torch.utils.data import Dataset, DataLoader  # 数据加载
from torch.nn import TransformerEncoderLayer, TransformerEncoder  # Transformer模块

定义常量氨基酸类型序列

restypes = [  # 20种标准氨基酸'A', 'R', 'N', 'D', 'C', 'Q', 'E', 'G', 'H', 'I','L', 'K', 'M', 'F', 'P', 'S', 'T', 'W', 'Y', 'V'
]
unsure_restype = 'X'  # 不确定的氨基酸类型
unknown_restype = 'U'  # 未知氨基酸类型

处理数据集

#数据集处理
def make_dataset(data_config,train_rate=0.7,valid_rate=0.2):data_path = data_config.data_path  #从配置获取数据路径with open(data_path,'rb') as f:  #加载.pkl文件data = pickle.load(f)  #反序列化数据total_number = len(data)  #数据总量train_sep = int(total_number * train_rate)  #训练集结束索引valid_sep = int(total_number * (train_rate + valid_rate))  #训练集结束索引train_data_dicts = data[:train_sep]  #训练集valid_data_dicts = data[train_sep:valid_sep]  #训练集test_data_dicts = data[valid_sep:]  #测试集train_dataset = DisProtDataset(train_data_dicts)valid_dataset = DisProtDataset(valid_data_dicts)test_dataset = DisProtDataset(test_data_dicts)return train_dataset,valid_dataset,test_dataset#处理数据格式
class DisProtDataset(Dataset):def __init__(self,dict_data):sequences = [d['sequence'] for d in dict_data]  #提取所有序列labels = [d['label'] for d in dict_data]  #提取所有标签assert len(sequences) == len(labels)self.sequences = sequencesself.labels = labelsself.residue_mapping = {'X':20}  #未知氨基酸'X'映射为索引20self.residue_mapping.update(dict(zip(restypes,range(len(restypes)))))def __len__(self):return len(self.sequences)  #返回数据集样本数def __getitem__(self, idx):#将序列转为one-hot编码sequence = torch.zeros(len(self.sequences[idx]),len(self.residue_mapping))for i,c in enumerate(self.sequences[idx]):if c not in restypes:  #非标准氨基酸替换为‘X’c = 'X'sequence[i][self.residue_mapping[c]] = 1  #one-hot赋值#标签转为Tensorlabel = torch.tensor([int(c) for c in self.labels[idx]],dtype=torch.long)return sequence,label

添加模型组件

class DisProtModel(nn.Module):  #主模型def __init__(self,model_config):self.d_model = model_config.d_model  # 特征维度self.n_head = model_config.n_head  # 注意力头数self.n_layer = model_config.n_layer  # Transformer层数#输入层self.input_layer = nn.Linear(model_config.i_dim,self.d_model)  #将输入维度 i_dim投影到 d_modelself.position_embed = PositionalEncoding(self.d_model,max_len=20000)  #添加位置信息,支持最长20000的序列self.input_norm = nn.LayerNorm(self.d_model)  #对特征维度归一化,稳定训练self.dropout_in = nn.Dropout(p=0.1)  #防止过拟合,丢弃率10%#Transformer编码器encoder_layer = TransformerEncoderLayer(d_model=self.d_model,nhead=self.n_head,activation='gelu',  #GELU激活batch_first=True  #输入格式为 (batch, seq, dim))self.transformer = TransformerEncoder(encoder_layer,num_layers=self.n_layer)#输出层self.output_layer = nn.Sequential(nn.Linear(self.d_model,self.d_model),nn.GELU(),nn.Dropout(p=0.1),nn.Linear(self.d_model,model_config.o_dim)  #输出维度,通常为2)#前向传播def forward(self,x):x = self.input_layer(x)  #线性变换x = self.position_embed(x)  #位置编码x = self.input_norm(x)  #层归一化x = self.dropout_in(x)  #Dropoutx = self.transformer(x)  #Transformer编码x = self.output_layer(x)  #分类头return x

评估指标

def metric_fn(pred,gt):pred = pred.detach().cpu()  #脱离计算图,转到CPUgt = gt.detach().cpu()pred_labels = torch.argmax(pred, dim=-1).view(-1)  # 取预测类别gt_labels = gt.view(-1)  # 展平真实标签score = f1_score(y_true=gt_labels, y_pred=pred_labels, average='micro')  # 计算F1return score

主要训练逻辑

if __name__ == '__main__':device = 'cuda' if torch.cuda.is_available() else 'cpu'  # 自动选择设备# 解析命令行参数(如--config_path)parser = argparse.ArgumentParser('IDRs prediction')parser.add_argument('--config_path', default='./config.yaml')args = parser.parse_args()config = OmegaConf.load(args.config_path)  # 加载配置文件#数据准备train_dataset, valid_dataset, test_dataset = make_dataset(config.data)train_dataloader = DataLoader(dataset=train_dataset, **config.train.dataloader)valid_dataloader = DataLoader(dataset=valid_dataset, batch_size=1, shuffle=False)#模型初始化model = DisProtModel(config.model)model = model.to(device)#优化器与损失函数optimizer = torch.optim.AdamW(model.parameters(),  #比Adam更优的权重衰减处理lr=config.train.optimizer.lr,weight_decay=config.train.optimizer.weight_decay)loss_fn = nn.CrossEntropyLoss()#初始验证model.eval()metric = 0.with torch.no_grad():for sequence, label in valid_dataloader:sequence = sequence.to(device)label = label.to(device)pred = model(sequence)metric += metric_fn(pred, label)print("init f1_score:", metric / len(valid_dataloader))#训练循环for epoch in range(config.train.epochs):progress_bar = tqdm(train_dataloader, desc=f"epoch:{epoch:03d}")model.train()total_loss = 0.for sequence, label in progress_bar:sequence = sequence.to(device)label = label.to(device)pred = model(sequence)loss = loss_fn(pred.permute(0, 2, 1), label)  # 调整维度progress_bar.set_postfix(loss=loss.item())total_loss += loss.item()optimizer.zero_grad()loss.backward()optimizer.step()avg_loss = total_loss / len(train_dataloader)#验证阶段model.eval()metric = 0.with torch.no_grad():for sequence, label in valid_dataloader:sequence = sequence.to(device)label = label.to(device)pred = model(sequence)metric += metric_fn(pred, label)print(f"avg_training_loss: {avg_loss}, f1_score: {metric / len(valid_dataloader)}")

注意:在这里想要跑通,一定要把数据集WSAA_data_public.pkl文件和config.yaml文件放在合适的目录下。

在本地跑通的效果

2.了解可以优化的方向

(1)词向量

词向量(Word Embedding)是自然语言处理(NLP)中的一种重要技术,用于将词汇映射到低维连续向量空间,使得语义和语法相似的词在向量空间中距离相近。

方法:词向量+机器学习

①训练词向量

使用gensim 库的 Word2Vec 模型对氨基酸序列进行词向量训练。

将每个蛋白质序列转换为由空格分隔的字符串( ' '.join(x["sequence"]) ),形成句子列表。

vector_size=100 :词向量的维度为 100。

min_count=1 :至少出现一次的单词才会被考虑。

训练完成后,model_w2v 包含了每个氨基酸的词向量表示。

datas = pickle.load(open("WSAA_data_public.pkl", "rb"))model_w2v = gensim.models.Word2Vec(sentences=[' '.join(x["sequence"]) for x in datas], vector_size=100, min_count=1)

②编码词向量

对于序列中的每个氨基酸,提取其上下文窗口内的词向量,并计算平均值作为特征。

sequence[max(0, idx-2): min(len(sequence), idx+2)]:获取当前氨基酸及其前后两个氨基酸的窗口。

model_w2v.wv[...]:获取窗口内氨基酸的词向量。

.mean(0) :对窗口内的词向量取平均值,得到一个固定维度的特征向量

将特征向量添加到data_x,将对应的标签添加到data_y

data_x = []
data_y = []
for data in datas:sequence = list(data["sequence"])for idx, (_, y) in enumerate(zip(sequence, data['label'])):data_x.append(model_w2v.wv[sequence[max(0, idx-2): min(len(sequence), idx+2)]].mean(0))data_y.append(y)

③训练贝叶斯模型

使用GaussianNB (高斯朴素贝叶斯)分类器对提取的特征进行训练。

model.fit(data_x, data_y) :将特征和标签传入模型进行训练。

model = GaussianNB()
model.fit(data_x, data_y)dump((model, model_w2v), "model.pkl")

(2)BERT模型

BERT(Bidirectional Encoder Representations from Transformers)是由 Google 在 2018 年提出的一种预训练语言模型。BERT 的核心特点是其双向性,即它能够同时考虑上下文的左右信息,从而生成更准确的语言表示。这种双向性使得 BERT 在理解词语的多义性和上下文关系方面表现出色。

方法:BERT实体识别

①数据预处理

序列编码:将蛋白质的氨基酸序列转换为BERT可以处理的格式。每个氨基酸可以用单字母表示(如A、C、D等)。由于BERT是基于字符的模型,可以直接将氨基酸序列作为输入。

标签处理:将每个氨基酸位置的无序区域标签(0或1)作为目标标签。例如, 1 表示该位置属于无序区域,0 表示不属于无序区域。

分词:虽然蛋白质序列是连续的氨基酸序列,但可以将其视为一个“句子”,每个氨基酸视为一个“词”。BERT的输入格式通常是一个序列,因此可以直接将整个氨基酸序列作为输入。

②加载bert模型

微调(Fine-tuning):将预训练的BERT模型用于特定任务(如蛋白质固有无序区域预测)。在微调阶段,BERT模型的权重会根据蛋白质序列数据进行调整,以更好地适应任务需求。

输出层设计:在BERT的输出层添加一个分类层,用于预测每个氨基酸位置是否属于无序区域。具体来说,BERT的输出是一个序列的隐藏状态,每个位置的隐藏状态可以通过一个全连接层(Dense Layer)和激活函数(如Sigmoid)来预测二分类标签(0或1)。

③模型微调

损失函数:使用二分类交叉熵损失函数(Binary Cross-Entropy Loss)来训练模型。该损失函数适用于二分类问题,能够衡量模型预测值与真实标签之间的差异。

优化器:使用Adam优化器,这是一种常用的优化器,适用于深度学习任务。

(3)GPT大模型

GPT(Generative Pre-trained Transformer)是由 OpenAI 开发的一系列自然语言处理模型,基于 Transformer 架构的解码器部分,通过大规模语料库的预训练,学习语言的统计规律,并能够生成连贯、自然的文本。

GPT 模型采用自回归(Autoregressive)的方式生成文本,即在给定前面的文本基础上逐步预测并生成下一个词。其核心架构是 Transformer 的解码器部分,利用多头自注意力机制捕捉句子中单词之间的关系,并通过前馈神经网络进行非线性变换。与传统的循环神经网络(RNN)不同,Transformer 能够在一个时间步中并行计算整个输入序列,大大加快了训练和推理速度。

GPT 使用单向 Transformer 解码器,通过自回归语言模型训练,预测句子中的下一个词。这种单向训练方式使得 GPT 在生成连贯文本时表现强大,但对上下文的理解相对较弱。

方法:如微调类似GPT的Qwen

①数据预处理

指令微调使模型能够更准确地识别和理解用户指令的意图。

LLM 的微调一般指指令微调过程。所谓指令微调,是说我们使用的微调数据形如:

{"instruction": "回答以下用户问题,仅输出答案。","input": "1+1等于几?","output": "2"
}

其中, instruction 是用户指令,告知模型其需要完成的任务; input 是用户输入,是完成用户指令所必须的输入内容; output 是模型应该给出的输出。

②加载Qwen模型

模型以半精度形式加载,如果你的显卡比较新的话,可以用 torch.bfolat 形式加载。对于自定义的模型一定要指定 trust_remote_code 参数为 True 。

tokenizer = AutoTokenizer.from_pretrained('/root/autodl-tmp/qwen/Qwen2.5-7B-Instruct/', use_fast=False, trust_remote_code=True)model = AutoModelForCausalLM.from_pretrained('/root/autodl-tmp/qwen/Qwen2.5-7B-Instruct/', device_map="auto",torch_dtype=torch.bfloat16)

③模型微调

使用一种用于大模型微调的技术LoRA(Low-Rank Adaptation)。、

task_type :模型类型

target_modules :需要训练的模型层的名字,主要就是 attention 部分的层,不同的模型对应的层的名字不同,可以传入数组,也可以字符串,也可以正则表达式。

config = LoraConfig(task_type=TaskType.CAUSAL_LM,target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],inference_mode=False, # 训练模式r=8, # Lora 秩lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理lora_dropout=0.1# Dropout 比例
)

3.尝试一个优化方向

在这里我选择了BERT模型进行优化源代码,不过由于竞赛的保密性,在这里暂时不放上代码了,那就期待一下赛事结束未完待续。。。

相关文章:

  • (附邀请码)探秘扣子空间:排盘优美,魅力无限
  • C#学习第17天:序列化和反序列化
  • 数据结构0基础学习堆
  • PHP获取大文件行数
  • 实现Azure Function安全地请求企业内部API返回数据
  • springAi---智能客服
  • Python语法系列博客 · 第4期[特殊字符] 函数的定义与使用:构建可复用的模块
  • 机器学习误差图绘
  • SEO长尾关键词优化实战
  • JAVAEE(网络原理—UDP报头结构)
  • 一个 CTO 的深度思考
  • Vue+Notification 自定义消息通知组件 支持数据分页 实时更新
  • pycharm中怎么解决系统cuda版本高于pytorch可以支持的版本的问题?
  • 【Linux篇】探索进程间通信:如何使用匿名管道构建高效的进程池
  • 洛谷P3373线段树详解【模板】
  • 如何优雅地为 Axios 配置失败重试与最大尝试次数
  • 掌握 MySQL:从命令行操作到数据类型与字段管理
  • Windows上安装FFmpeg的详细指南
  • 树莓派超全系列教程文档--(33)树莓派启动选项
  • Git 中修改某个特定的commit提交内容
  • 2025欧亚经济合作发展论坛在沪举办
  • 广州远洋宾馆负一层配电房发生火情:明火已扑灭,无人员伤亡
  • 31年前失踪的男孩与家人在重庆一派出所团聚:人像比对后DNA鉴定成功
  • 万斯偕印裔妻子访问印度,4天行程能否推进美印贸易谈判?
  • 泽连斯基:乌英法美将在伦敦讨论停火事宜
  • 中远海运:坚决反对美方对中国海事物流及造船业301调查的歧视性决定