分子性质AI预测挑战赛
#AI夏令营 #Datewhale #夏令营
分子性质预测挑战赛是讯飞开放平台组织的一场算法挑战赛,旨在以先进的人工智能算法预测分子的降解性能。赛事时间为2024年6月9日-2024年8月9日
赛事背景
在当今科技日新月异的时代,人工智能(AI)技术正以前所未有的深度和广度渗透到科研领域,特别是在化学及药物研发中展现出了巨大潜力。精准预测分子性质有助于高效筛选出具有优异性能的候选药物。以PROTACs为例,它是一种三元复合物由目标蛋白配体、linker、E3连接酶配体组成,靶向降解目标蛋白质。本次大赛聚焦于运用先进的人工智能算法预测其降解效能,旨在激发参赛者创新思维,推动AI技术与化学生物学的深度融合,进一步提升药物研发效率与成功率,为人类健康事业贡献智慧力量。通过此次大赛,我们期待见证并孵化出更多精准、高效的分子性质预测模型,共同开启药物发现的新纪元。
赛事任务
选手根据提供的demo数据集,可以基于demo数据集进行数据增强、自行搜集数据等方式扩充数据集,并自行划分数据。运用深度学习、强化学习或更加优秀人工智能的方法预测PROTACs的降解能力,若DC50>100nM且Dmax<80% ,则视为降解能力较差(demo数据集中Label=0);若DC50<=100nM或Dmax>=80%,则视为降解能力好(demo数据集中Label=1)。
赛事数据
Task1 跑通baseline
# 1. 导入需要用到的相关库
# 导入 pandas 库,用于数据处理和分析
import pandas as pd
# 导入 numpy 库,用于科学计算和多维数组操作
import numpy as np
# 从 lightgbm 模块中导入 LGBMClassifier 类
from lightgbm import LGBMClassifier
# 2. 读取训练集和测试集
# 使用 read_excel() 函数从文件中读取训练集数据,文件名为 'traindata-new.xlsx'
train = pd.read_excel('./data/data280993/traindata-new.xlsx')
# 使用 read_excel() 函数从文件中读取测试集数据,文件名为 'testdata-new.xlsx'
test = pd.read_excel('./data/data280993/testdata-new.xlsx')
# 3 特征工程
# 3.1 test数据不包含 DC50 (nM) 和 Dmax (%),将train数据中的DC50 (nM) 和 Dmax (%)删除
train = train.drop(['DC50 (nM)', 'Dmax (%)'], axis=1)
# 3.2 将object类型的数据进行目标编码处理
for col in train.columns[2:]:
if train[col].dtype == object or test[col].dtype == object:
train[col] = train[col].isnull()
test[col] = test[col].isnull()
# 4. 加载决策树模型进行训练
model = LGBMClassifier(verbosity=-1)
model.fit(train.iloc[:, 2:].values, train['Label'])
pred = model.predict(test.iloc[:, 1:].values, )
# 5. 保存结果文件到本地
pd.DataFrame(
{
'uuid': test['uuid'],
'Label': pred
}
).to_csv('submit.csv', index=None)
导入库:首先,导入需要用到的库,包括 pandas(用于数据处理和分析)和 LGBMClassifier(决策树分类器)等。
读取数据:代码通过使用 pd.read_excel 函数从文件中读取训练集和测试集数据,并将其存储在 traindata-new.xlsx 和 testdata-new.xlsx 两个数据框中。
特征工程:
- test数据不包含 DC50 (nM) 和 Dmax (%),将train数据中的 DC50 (nM) 和 Dmax (%)删除。
LGB树模型训练和预测: - 创建了一个 LGBMClassifier 的实例,即LGB分类器。
- 使用 fit 函数对训练集中的特征和目标进行拟合,训练了决策树模型。
- 对测试集使用已训练的模型进行预测,得到预测结果。
- 将预测结果和相应的 uuid 组成一个DataFrame,并将其保存到 submit.csv 文件中。
在完成基础代码的跑通后,便可以将生成的csv文件提交到赛事网站上获取评分,完成整个流程的跑通,接下来便是算法的优化以获取更高的分数了。
Task2 优化算法
在解决机器学习的问题过程中,我们通常会遵循以下流程:
问题分析---->数据扩充---->数据清洗---->提取特征---->模型训练---->模型验证---->结果输出
而我们算法模型的优化,重点便是在提取特征上。我们的算法越能充分的提取特征,那么模型的训练效果将会越好。
导入模块
import numpy as np
import pandas as pd
from catboost import CatBoostClassifier
from sklearn.model_selection import StratifiedKFold, KFold, GroupKFold
from sklearn.metrics import f1_score
from rdkit import Chem
from rdkit.Chem import Descriptors
from sklearn.feature_extraction.text import TfidfVectorizer
import tqdm, sys, os, gc, re, argparse, warnings
warnings.filterwarnings('ignore')
数据预处理
train = pd.read_excel('./dataset-new/traindata-new.xlsx')
test = pd.read_excel('./dataset-new/testdata-new.xlsx')
# test数据不包含 DC50 (nM) 和 Dmax (%)
train = train.drop(['DC50 (nM)', 'Dmax (%)'], axis=1)
# 定义了一个空列表drop_cols,用于存储在测试数据集中非空值小于10个的列名。
drop_cols = []
for f in test.columns:
if test[f].notnull().sum() < 10:
drop_cols.append(f)
# 使用drop方法从训练集和测试集中删除了这些列,以避免在后续的分析或建模中使用这些包含大量缺失值的列
train = train.drop(drop_cols, axis=1)
test = test.drop(drop_cols, axis=1)
# 使用pd.concat将清洗后的训练集和测试集合并成一个名为data的DataFrame,便于进行统一的特征工程处理
data = pd.concat([train, test], axis=0, ignore_index=True)
cols = data.columns[2:]
在数据预处理部分,因为test数据不包含DC50(nM)和Dmax(%),因此需要将train中的这两列删掉,这样做的目的是为了方便将test和train整合到一起,扩充数据量以利于更好的提取特征。而后由于数据中很多空值,为了减少噪声的影响,将非空值太少的数据剔除掉,以防止对模型训练的结果产生影响。
提取特征
# 将SMILES转换为分子对象列表,并转换为SMILES字符串列表
data['smiles_list'] = data['Smiles'].apply(lambda x:[Chem.MolToSmiles(mol, isomericSmiles=True) for mol in [Chem.MolFromSmiles(x)]])
data['smiles_list'] = data['smiles_list'].map(lambda x: ' '.join(x))
# 使用TfidfVectorizer计算TF-IDF
tfidf = TfidfVectorizer(max_df = 0.9, min_df = 1, sublinear_tf = True)
res = tfidf.fit_transform(data['smiles_list'])
# 将结果转为dataframe格式
tfidf_df = pd.DataFrame(res.toarray())
tfidf_df.columns = [f'smiles_tfidf_{i}' for i in range(tfidf_df.shape[1])]
# 按列合并到data数据
data = pd.concat([data, tfidf_df], axis=1)
# 自然数编码
def label_encode(series):
unique = list(series.unique())
return series.map(dict(zip(
unique, range(series.nunique())
)))
for col in cols:
if data[col].dtype == 'object':
data[col] = label_encode(data[col])
train = data[data.Label.notnull()].reset_index(drop=True)
test = data[data.Label.isnull()].reset_index(drop=True)
# 特征筛选
features = [f for f in train.columns if f not in ['uuid','Label','smiles_list']]
# 构建训练集和测试集
x_train = train[features]
x_test = test[features]
# 训练集标签
y_train = train['Label'].astype(int)
在提取特征部分,采用了SMILES转换将字符串转换回列表的形式,以统一所有的SMILES的格式,而后采用TF-IDF方法对这些字符串创建tF-IDF特征矩阵后转换为DataFrame便于与院士数据集结合。由于数据中包括浮点型,整型,对象等多种类型,故采用了自然数编码将对象类型的特征转换为整数编码。最后,从合并的数据集中分离出训练集和测试集,并在其中筛选出特征列,并从训练集提取标签列。
模型训练与预测
def cv_model(clf, train_x, train_y, test_x, clf_name, seed=2022):
kf = KFold(n_splits=5, shuffle=True, random_state=seed)
train = np.zeros(train_x.shape[0])
test = np.zeros(test_x.shape[0])
cv_scores = []
# 100, 1 2 3 4 5
# 1 2 3 4 5
# 1 2 3 5。 4
# 1
for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
print('************************************ {} {}************************************'.format(str(i+1), str(seed)))
trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
params = {'learning_rate': 0.1, 'depth': 6, 'l2_leaf_reg': 10, 'bootstrap_type':'Bernoulli','random_seed':seed,
'od_type': 'Iter', 'od_wait': 100, 'allow_writing_files': False, 'task_type':'CPU'}
model = clf(iterations=20000, **params, eval_metric='AUC')
model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
metric_period=100,
cat_features=[],
use_best_model=True,
verbose=1)
val_pred = model.predict_proba(val_x)[:,1]
test_pred = model.predict_proba(test_x)[:,1]
train[valid_index] = val_pred
test += test_pred / kf.n_splits
cv_scores.append(f1_score(val_y, np.where(val_pred>0.5, 1, 0)))
print(cv_scores)
print("%s_score_list:" % clf_name, cv_scores)
print("%s_score_mean:" % clf_name, np.mean(cv_scores))
print("%s_score_std:" % clf_name, np.std(cv_scores))
return train, test
cat_train, cat_test = cv_model(CatBoostClassifier, x_train, y_train, x_test, "cat")
pd.DataFrame(
{
'uuid': test['uuid'],
'Label': np.where(cat_test>0.5, 1, 0)
}
).to_csv('submit.csv', index=None)
在模型训练与预测中,则采用k折交叉验证的思想,将数据混合后按照4:1的比例分为训练集和验证集,进行模型的训练,并循环5次确保所有的数据都成为过验证集,每次训练完后计算F1分数,取平均值为整个模型训练的效果。分类器则采用CatBoost分类器,使用AUC作为评估指标。
在这些算法优化之后,基本的模型便搭建了起来,此时我们可以微调参数来进行效果的优化,但是这种优化往往不是很明显,想要得到大幅度的优化,还需要在提取特征部分采用效果更好的方法。