#ai夏令营#datawhale#夏令营
1. 赛事简介&baseline
预测算法类比赛:
分子性质AI预测挑战赛
baseline与初步优化:
【机器学习】Datawhale-AI夏令营分子性质AI预测挑战赛-基础
2. 进阶思路
2.1 数据字段介绍(涉及生物医药方面)
Ref: Task2:赛题深入解析
数据集包含了多个字段,如uuid(唯一标识符)、Label(降解能力的标签,0表示降解能力较差,1表示降解能力好)、Uniprot、Target、E3 ligase、PDB等,这些都是与PROTACs分子相关的信息。还包括了降解能力相关的数值指标,如DC50(半数降解浓度)、Dmax(最大降解效率)、IC50(半抑制浓度)、EC50(半有效浓度)等,以及与分子物理化学性质相关的字段,如Molecular Weight(分子量)、Exact Mass(精确质量)、XLogP3(预测的脂水分配系数)等。
rdkit 是一个化学信息学和机器学习软件,用于处理化学结构。
使用 rdkit 库处理SMILES字符串。将SMILES转换为分子对象,然后进行分子结构绘制:
from rdkit import Chem
from rdkit.Chem import AllChem, Draw
# SMILES字符串
smiles = "NC1=NC=NC2=C1C(C1=CC=C(OC3=CC=CC=C3)C=C1)=NN2[C@@H]1CCCN(C(=O)C2=CN(CCOCCOCCOCCCC3=CC=CC4=C3CN(C3CCC(=O)NC3=O)C4=O)N=N2)C1"
# 将SMILES转换为分子对象
mol = Chem.MolFromSmiles(smiles)
# 检查分子是否有效
if mol is None:
print("The SMILES string is invalid.")
else:
print("The molecule was parsed successfully.")
# 绘制分子结构
molDrawer = Draw.MolToMPL(mol)
molDrawer.show()
2.2 进阶baseline
Ref: Task3:进阶baseline详解
1️⃣ 导入库
导入Python中用于数据分析、机器学习建模和化学信息学所需的库。以下是每个库的简要说明:
- numpy: 提供强大的多维数组对象和相应的操作。
- pandas: 提供高性能、易用的数据结构和数据分析工具。
- catboost: 一个用于机器学习的库,特别是分类和回归任务。
- sklearn.model_selection: 包含模型选择的多种方法,如交叉验证。
- sklearn.metrics: 包含评估模型性能的多种指标。
- rdkit: 一个化学信息学和机器学习软件,用于处理化学结构。
- sklearn.feature_extraction.text: 提供将文本转换为特征向量的Tf-idf向量化器。
- tqdm: 用于在长循环中添加进度条的库。
- sys: 与Python解释器密切相关的模块,提供访问由解释器使用或维护的变量和函数。
- os: 提供与操作系统交互的功能。
- gc: 垃圾收集器接口,用于手动标记对象为可删除。
- re: 正则表达式库,用于字符串搜索和替换。
- argparse: 用于编写用户友好的命令行接口。
- warnings: 用于发出警告的库,这里用来忽略警告信息。
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')
2️⃣ 数据预处理
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:]
3️⃣ 特征工程
特征工程指的是把原始数据转变为模型训练数据的过程,目的是获取更好的训练数据特征。特征工程能使得模型的性能得到提升,有时甚至在简单的模型上也能取得不错的效果。
# 将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转换:使用RDKit库将数据集中的SMILES字符串转换回SMILES字符串的列表。这里看起来有些冗余,因为您已经拥有SMILES字符串,但可能您想确保所有SMILES都是以相同的方式(例如,考虑异构体信息)处理的。
- 字符串处理:将SMILES字符串列表转换为单个字符串,每个SMILES之间用空格分隔。
- TF-IDF计算:使用TfidfVectorizer从处理后的SMILES字符串创建TF-IDF特征矩阵。
- 转换为DataFrame:将TF-IDF矩阵转换为DataFrame,以便与原始数据集结合。
- 自然数编码:定义了一个函数label_encode,用于将分类特征(对象类型)转换为整数编码。
- 特征和标签准备:
- 对于所有的特征列(cols),如果它们的数据类型是对象(通常表示为字符串),则应用自然数编码。
- 从合并后的数据集中分离出训练集和测试集,其中训练集包含标签(Label),测试集不包含。
- 特征和标签的筛选:从训练集和测试集中筛选出特征列(不包括uuid、Label和smiles_list),并从训练集中提取标签列。
- 数据类型转换:将标签列Label转换为整数类型,以便于模型训练。
4️⃣ 模型训练与预测
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)
定义了一个名为 cv_model 的函数,用于使用交叉验证训练分类模型,并对测试集进行预测。以下是代码的详细步骤和说明:
- 函数定义:cv_model 函数接受分类器对象 clf,训练特征集 train_x 和标签集 train_y,测试特征集 test_x,分类器名称 clf_name,以及可选的随机种子 seed。
- 交叉验证设置:使用 KFold 进行5折交叉验证,shuffle=True 表示在分折前打乱数据。
- 初始化变量:创建两个数组 train 和 test 来存储交叉验证过程中的训练集预测和测试集预测。
- 循环执行交叉验证:
- 对每一折数据,使用训练索引 train_index 和验证索引 valid_index 分割训练集和验证集。
- 打印当前折数和随机种子。
- 设置 CatBoost 分类器的参数 params。
- 模型训练:使用 CatBoost 分类器训练模型,iterations=20000 表示最大迭代次数,eval_metric=‘AUC’ 表示使用 AUC 作为评估指标。
- 模型评估:
- 使用验证集 val_x 和 val_y 对模型进行评估,获取预测概率 val_pred。
- 使用测试集 test_x 获取测试集预测概率 test_pred。
- 保存结果:将验证集的预测结果存储在 train 数组中,将测试集的预测结果累加到 test 数组中,并计算当前折的 F1 分数。
- 输出结果:打印所有折的 F1 分数、平均值和标准差。
- 返回结果:返回训练集预测结果 train 和测试集预测结果 test。
- 模型应用:
- 使用 cv_model 函数训练 CatBoost 分类器,并将返回的测试集预测结果 cat_test 用于生成提交文件。
- 根据预测概率 cat_test 生成二元标签,概率大于0.5的预测为1,否则为0。
- 生成提交文件:创建一个包含 uuid 和预测标签 Label 的 DataFrame,并将其保存为 CSV 文件。
跑完Task3的代码之后:
分数提升0.02左右!
2.3 优化思路
Ref: Task4:持续尝试,上分!
- 提取更多特征:在数据挖掘比赛中,挖掘重要特征可以帮助我们提高预测精准度,然后将其转化为特征输入到模型。对于本次赛题可以从专业角度构建特征,除了Smiles特征外,还有很多特征可以提取有价值的信息,比如InChI是由一系列部分组成,提供了关于分子结构的详细信息。比如开头标识、分子式、连接表、氢原子计数、多可旋转键计数、立体化学信息、同分异构体信息、混合物或互变异构体信息、电荷和自旋多重度信息等。
- 尝试不同的模型:模型间存在很大的差异,预测结果也会不一样,比赛的过程就是不断的实验和试错的过程,通过不断的实验寻找最佳模型,同时帮助自身加强模型的理解能力。另外,模型融合也是一种提分方法。
3. 每日问答
Q1: 大家知道我们的Baseline是基于决策树的,那么大家知道决策树经典的属性划分方法有哪些?决策树在实际应用中有哪些优缺点?简单介绍即可。
Q2: 在机器学习中,过拟合现象是非常常见的。大家知道什么是过拟合吗?如何防止过拟合?请简单说明。
【参考解答】
Q1:
(1)基于信息增益判断:基于熵的概念,选择能够最大程度减少数据集熵的属性进行划分。常用于ID3算法; 基于信息增益率判断:对信息增益进行改进,考虑到属性取值较多的问题,选择信息增益率最高的属性进行划分。常用于C4.5算法;基于基尼指数判断: 选择基尼指数最小的属性进行划分。基尼指数衡量的是数据集的纯度。常用于CART(分类与回归树)算法。
(2)优点:计算复杂度不高,便于使用,高效,能够处理多种数据类型,可很容易地构造出易于理解的规则。
缺点:易过拟合,对噪声数据敏感,忽略数据集中属性之间的相关性,不稳定等。
Q2:
(1)过拟合是指模型过于紧密或精确地匹配特定数据集,泛化能力差,导致在新的、未见过的数据上表现不佳。
(2)防止过拟合的方法有很多。例如:数据增强:通过对训练数据进行变换,比如旋转、缩放、翻转等,增加数据的多样性,帮助模型学习到更一般的特征。正则化:添加正则项到损失函数中可以惩罚模型的复杂度,限制模型权重的大小,从而减少过拟合。提前停止:在机器学习模型学习数据中的噪音之前,提前停止暂停训练阶段。