Bohrium
robot
新建

空间站广场

论文
Notebooks
比赛
课程
Apps
我的主页
我的Notebooks
我的论文库
我的足迹

我的工作空间

任务
节点
文件
数据集
镜像
项目
数据库
公开
AI4S Cup学习赛:特征工程与可视化
AI4S Cup-Getting Started
AI4S Cup-Getting Started
hyb
发布于 2024-02-01
推荐镜像 :Basic Image:bohrium-notebook:2023-04-07
推荐机型 :c2_m4_cpu
赞 4
1
4
AI4S Cup学习赛:特征工程与可视化
特征工程基本流程
1. 数据收集和理解
2.数据处理:
3.特征提取
4. 特征选择
5.特征变换
6.模型优化

AI4S Cup学习赛:特征工程与可视化

©️ 特征工程是什么?
  是将原始数据转化成更好的表达问题本质的特征,来减少过拟合,并提高模型的性能、泛化能力。简单来说特征工程就是将复杂的原始数据转换为更具代表性的信息,以便机器学习算法更准确地进行预测和决策。
  eg:以生理性别判别为例,如果以身高为数据特征我们很难得出结果,如果以喉结为特征数据能判断大多数情况,但以染色体的种类为特征时,就能判定所有情况,没有误差。可见特征的选取对模型的性能表现十分重要。
©️ 快速开始:点击上方的 开始连接 按钮,选择 bohrium-notebook:2023-04-07镜像及任意CPU节点配置,稍等片刻即可运行。

代码
文本

特征工程基本流程

image.png

1. 数据收集和理解

  数据集包括训练集和测试集,每个数据集包含两列:分子的 SMILES 表达式和分类标签(1表示CNS药物,0表示非CNS药物)。SMILES(Simplified molecular input line entry system),是一种简化分子线性输入规范。

2.数据处理:

  • 对于缺失值: 删除、替换、插补;
  • 对于异常值的处理有:
    • 删除含有异常值的观测(样本少时直接删除会造成样本量不足,改变分布);
    • 当作缺失值(利用现有的信息,对其当缺失值填补);
    • 平均值修正(用前后两个观测值的均值修正该异常值)、不处理。
    • 在进行异常值处理时要先复习异常值出现的可能原因,再判断异常值是否应该舍弃。

3.特征提取

特征提取涉及将原始数据转化为机器学习算法可用的特征。SMILES是一种常见的分子表示方式,从SMILES中提取有关分子结构和性质的特征是一项关键任务。在这次赛题中可能包括:

  • 分子属性描述符(Molecular Property Descriptors):计算分子的物化性质,如分子质量、极性、溶解度等。
  • 分子指纹(Molecular Fingerprints):将分子结构表示为二进制或整数向量,以描述分子的拓扑结构和子结构。
  • 分子的三维结构信息:如果可用,可以提取分子的三维坐标信息,以描述其立体结构。 针对本题,它是QSAR的一个典型应用场景。QSAR(Quantitative Structure-Activity Relationship)是一种常用的计算化学方法,主要用于研究分子结构与生物活性之间的定量关系,通常用于设计、筛选和优化药物分子。 image.png   我们可以查询到QSAR早期时就提出,分子的生物活性主要与疏水效(logP)、立体效应等分子性质有关,我们可以把这些性质称作分子描述符,因为使用1D-QSAR特征比较有利于进行特征工程的说明,故在以下代码中仅采用1D-QSAR特征。
      而在本文开头的代码中则将三个维度的共11037个QSAR特征用随机森林做了特征选择,PCA降维,然后创建一个分类器,进行训练。
      我们可以使用RDKit库把分子结构转化为众多特征,先提取两个看看效果:
代码
文本
[2]
! pip install lightgbm numpy pandas rdkit scikit-learn #在终端用pip下载lightgbm库、numpy库、pandas库、rdkit库、scikit-learn库
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple

Requirement already satisfied: lightgbm in /opt/conda/lib/python3.8/site-packages (2.2.3)

Requirement already satisfied: numpy in /opt/conda/lib/python3.8/site-packages (1.22.4)

Requirement already satisfied: pandas in /opt/conda/lib/python3.8/site-packages (1.5.3)

Collecting rdkit

  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/6b/34/4141c233ca567d949c616154b99f632d35efe4f423aae1cc5b9a6b6e6621/rdkit-2023.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (30.5 MB)

     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 30.5/30.5 MB 21.6 MB/s eta 0:00:0000:0100:01

Requirement already satisfied: scikit-learn in /opt/conda/lib/python3.8/site-packages (1.0.2)

Requirement already satisfied: scipy in /opt/conda/lib/python3.8/site-packages (from lightgbm) (1.7.3)

Requirement already satisfied: python-dateutil>=2.8.1 in /opt/conda/lib/python3.8/site-packages (from pandas) (2.8.2)

Requirement already satisfied: pytz>=2020.1 in /opt/conda/lib/python3.8/site-packages (from pandas) (2022.7)

Requirement already satisfied: Pillow in /opt/conda/lib/python3.8/site-packages (from rdkit) (9.4.0)

Requirement already satisfied: joblib>=0.11 in /opt/conda/lib/python3.8/site-packages (from scikit-learn) (1.2.0)

Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/lib/python3.8/site-packages (from scikit-learn) (3.1.0)

Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.8/site-packages (from python-dateutil>=2.8.1->pandas) (1.16.0)

Installing collected packages: rdkit

Successfully installed rdkit-2023.9.1

WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

代码
文本
[3]
import os
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.metrics import fbeta_score

# 读取数据
DIR_PATH = "/data"
data = pd.read_csv(os.path.join(DIR_PATH, 'mol_train.csv'))
features = data['SMILES']
labels = data['TARGET']


# 定义计算1D-QSAR特征的函数
def calculate_1dqsar_repr(smiles):
mol = Chem.MolFromSmiles(smiles)
mol_weight = Descriptors.MolWt(mol) # 计算分子的分子量
log_p = Descriptors.MolLogP(mol) # 计算分子的LogP值

return [mol_weight, log_p]

# 初始化特征列表
all_features = []

# 计算1D-QSAR特征
for smiles in features:
features_repr = calculate_1dqsar_repr(smiles) # 调用计算函数获取特征
all_features.append(features_repr) # 将特征添加到特征列表

# 将特征数据和标签数据拆分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(all_features, labels, test_size=0.2, random_state=42)

# 采用逻辑回归模型
clf = LogisticRegression()

# 在训练集上拟合模型
clf.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = clf.predict(X_test)

# 计算f2-score
f2_score = fbeta_score(y_test, y_pred, beta=2)

print("F2-score: ", f2_score)

# 运行结果:F2-score:0.29126213592233013
F2-score:  0.29126213592233013
代码
文本
  • 结果并不理想,那我们尝试多加几个题目提示的特征,会发生什么变化呢?
代码
文本
[10]
import os
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.metrics import fbeta_score

# 读取数据
data = pd.read_csv(os.path.join(DIR_PATH, 'mol_train.csv'))
features = data['SMILES']
labels = data['TARGET']


# 定义计算1D-QSAR特征的函数
def calculate_1dqsar_repr(smiles):
mol = Chem.MolFromSmiles(smiles)
mol_weight = Descriptors.MolWt(mol) # 计算分子的分子量
log_p = Descriptors.MolLogP(mol) # 计算分子的LogP值
tpsa = Descriptors.TPSA(mol) # 计算分子的表面积极性
num_polar_hydrogens = Descriptors.NumHAcceptors(mol)#计算极性氢原子数量
# 计算N原子的数量
count_n = 0
for atom in mol.GetAtoms():
atomic_num = atom.GetAtomicNum()
if atomic_num == 7: # 7代表氮原子的原子序数
count_n += 1
return [mol_weight,log_p,count_n,num_polar_hydrogens,tpsa,]

# 初始化特征列表
all_features = []

# 计算1D-QSAR特征
for smiles in features:
features_repr = calculate_1dqsar_repr(smiles) # 调用计算函数获取特征
all_features.append(features_repr) # 将特征添加到特征列表

# 将特征数据和标签数据拆分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(all_features, labels, test_size=0.2, random_state=42)

# 采用逻辑回归模型
clf = LogisticRegression()

# 在训练集上拟合模型
clf.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = clf.predict(X_test)

# 计算f2-score
f2_score = fbeta_score(y_test, y_pred, beta=2)

print("F2-score: ", f2_score)


# 运行结果:F2-score:0.6651376146788991
F2-score:  0.6651376146788991
代码
文本
  • 效果显而易见,从0.2912多提升到了0.6651多,那说明是不是特征越多越好呢?接下来我们多加一些特征看看:
代码
文本
[11]
import os
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.metrics import fbeta_score

# 读取数据
data = pd.read_csv(os.path.join(DIR_PATH, 'mol_train.csv'))
features = data['SMILES']
labels = data['TARGET']


# 定义计算1D-QSAR特征的函数
def calculate_1dqsar_repr(smiles):
mol = Chem.MolFromSmiles(smiles)
mol_weight = Descriptors.MolWt(mol) # 计算分子的分子量
log_p = Descriptors.MolLogP(mol) # 计算分子的LogP值
num_h_donors = Descriptors.NumHDonors(mol) # 计算分子中的氢键供体数量
num_h_acceptors = Descriptors.NumHAcceptors(mol) # 计算分子中的氢键受体数量
tpsa = Descriptors.TPSA(mol) # 计算分子的表面积极性
num_rotatable_bonds = Descriptors.NumRotatableBonds(mol) # 计算分子中的可旋转键数量
num_aromatic_rings = Descriptors.NumAromaticRings(mol) # 计算分子中的芳香环数量
num_aliphatic_rings = Descriptors.NumAliphaticRings(mol) # 计算分子中的脂环数量
num_saturated_rings = Descriptors.NumSaturatedRings(mol) # 计算分子中的饱和环数量
num_heteroatoms = Descriptors.NumHeteroatoms(mol) # 计算分子中的杂原子数量
num_valence_electrons = Descriptors.NumValenceElectrons(mol) # 计算分子中的价电子数量
num_radical_electrons = Descriptors.NumRadicalElectrons(mol) # 计算分子中的自由基电子数量
num_polar_hydrogens = Descriptors.NumHAcceptors(mol)#计算极性氢原子数量
# 计算N原子的数量
count_n = 0
for atom in mol.GetAtoms():
atomic_num = atom.GetAtomicNum()
if atomic_num == 7: # 7代表氮原子的原子序数
count_n += 1
return [mol_weight, log_p, num_h_donors, num_h_acceptors, tpsa, num_rotatable_bonds, num_aromatic_rings, num_aliphatic_rings, num_saturated_rings, num_heteroatoms, num_valence_electrons, num_radical_electrons,count_n,num_polar_hydrogens]

# 初始化特征列表
all_features = []

# 计算1D-QSAR特征
for smiles in features:
features_repr = calculate_1dqsar_repr(smiles) # 调用计算函数获取特征
all_features.append(features_repr) # 将特征添加到特征列表

# 将特征数据和标签数据拆分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(all_features, labels, test_size=0.2, random_state=42)

# 采用逻辑回归模型
clf = LogisticRegression()

# 在训练集上拟合模型
clf.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = clf.predict(X_test)

# 计算f2-score
f2_score = fbeta_score(y_test, y_pred, beta=2)

print("F2-score: ", f2_score)


#运行结果:F2-score:0.7589285714285713
F2-score:  0.7589285714285713
/opt/conda/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:814: ConvergenceWarning: lbfgs failed to converge (status=1):

STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.



Increase the number of iterations (max_iter) or scale the data as shown in:

    https://scikit-learn.org/stable/modules/preprocessing.html

Please also refer to the documentation for alternative solver options:

    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression

  n_iter_i = _check_optimize_result(
代码
文本
  • 可见虽然分数有多提升,但大量特征的加入并不会让分数呈现较大提升。
    此时我们删掉两个特征看看结果又是怎么样的呢?
代码
文本
[12]
import os
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.metrics import fbeta_score

# 读取数据
data = pd.read_csv(os.path.join(DIR_PATH, 'mol_train.csv'))
features = data['SMILES']
labels = data['TARGET']


# 定义计算1D-QSAR特征的函数
def calculate_1dqsar_repr(smiles):
mol = Chem.MolFromSmiles(smiles)
mol_weight = Descriptors.MolWt(mol) # 计算分子的分子量
log_p = Descriptors.MolLogP(mol) # 计算分子的LogP值
#num_h_donors = Descriptors.NumHDonors(mol) # 计算分子中的氢键供体数量
#num_h_acceptors = Descriptors.NumHAcceptors(mol) # 计算分子中的氢键受体数量
tpsa = Descriptors.TPSA(mol) # 计算分子的表面积极性
num_rotatable_bonds = Descriptors.NumRotatableBonds(mol) # 计算分子中的可旋转键数量
num_aromatic_rings = Descriptors.NumAromaticRings(mol) # 计算分子中的芳香环数量
num_aliphatic_rings = Descriptors.NumAliphaticRings(mol) # 计算分子中的脂环数量
num_saturated_rings = Descriptors.NumSaturatedRings(mol) # 计算分子中的饱和环数量
num_heteroatoms = Descriptors.NumHeteroatoms(mol) # 计算分子中的杂原子数量
num_valence_electrons = Descriptors.NumValenceElectrons(mol) # 计算分子中的价电子数量
num_radical_electrons = Descriptors.NumRadicalElectrons(mol) # 计算分子中的自由基电子数量
num_polar_hydrogens = Descriptors.NumHAcceptors(mol)#计算极性氢原子数量
# 计算N原子的数量
count_n = 0
for atom in mol.GetAtoms():
atomic_num = atom.GetAtomicNum()
if atomic_num == 7: # 7代表氮原子的原子序数
count_n += 1
return [mol_weight, log_p, tpsa, num_rotatable_bonds, num_aromatic_rings, num_aliphatic_rings, num_saturated_rings, num_heteroatoms, num_valence_electrons, num_radical_electrons,count_n,num_polar_hydrogens]

# 初始化特征列表
all_features = []

# 计算1D-QSAR特征
for smiles in features:
features_repr = calculate_1dqsar_repr(smiles) # 调用计算函数获取特征
all_features.append(features_repr) # 将特征添加到特征列表

# 将特征数据和标签数据拆分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(all_features, labels, test_size=0.2, random_state=42)

# 采用逻辑回归模型
clf = LogisticRegression()

# 在训练集上拟合模型
clf.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = clf.predict(X_test)

# 计算f2-score
f2_score = fbeta_score(y_test, y_pred, beta=2)

print("F2-score: ", f2_score)


# 运行结果:F2-score:0.7657657657657657
F2-score:  0.7657657657657657
/opt/conda/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:814: ConvergenceWarning: lbfgs failed to converge (status=1):

STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.



Increase the number of iterations (max_iter) or scale the data as shown in:

    https://scikit-learn.org/stable/modules/preprocessing.html

Please also refer to the documentation for alternative solver options:

    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression

  n_iter_i = _check_optimize_result(
代码
文本
  • 得分反而会上升,这可能表明原始特征中的某些特征对机器学习模型并没有提供有用的信息,甚至引入噪音或冗余信息,从而对模型的性能产生负面影响。这时就体现出特征选择对机器学习的影响。
代码
文本

4. 特征选择

  基于以上结果,我们需要从原始特征集合中选择最具相关性或最具代表性的特征子集,以提高结果的准确性,这就是在进行特征选择。
  所以我们将模型换成随机森林,显示不同特征的重要性,通过它找到比较重要的特征:

代码
文本
[13]
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import fbeta_score
import numpy as np
import matplotlib.pyplot as plt
import os

# 读取数据
data = pd.read_csv(os.path.join(DIR_PATH, 'mol_train.csv'))
features = data['SMILES']
labels = data['TARGET']

# 定义计算1D-QSAR特征的函数
def calculate_1dqsar_repr(smiles):
mol = Chem.MolFromSmiles(smiles)
mol_weight = Descriptors.MolWt(mol) # 计算分子的分子量
log_p = Descriptors.MolLogP(mol) # 计算分子的LogP值
num_h_donors = Descriptors.NumHDonors(mol) # 计算分子中的氢键供体数量
num_h_acceptors = Descriptors.NumHAcceptors(mol) # 计算分子中的氢键受体数量
tpsa = Descriptors.TPSA(mol) # 计算分子的表面积极性
num_rotatable_bonds = Descriptors.NumRotatableBonds(mol) # 计算分子中的可旋转键数量
num_aromatic_rings = Descriptors.NumAromaticRings(mol) # 计算分子中的芳香环数量
num_aliphatic_rings = Descriptors.NumAliphaticRings(mol) # 计算分子中的脂环数量
num_saturated_rings = Descriptors.NumSaturatedRings(mol) # 计算分子中的饱和环数量
num_heteroatoms = Descriptors.NumHeteroatoms(mol) # 计算分子中的杂原子数量
num_valence_electrons = Descriptors.NumValenceElectrons(mol) # 计算分子中的价电子数量
num_radical_electrons = Descriptors.NumRadicalElectrons(mol) # 计算分子中的自由基电子数量
num_polar_hydrogens = Descriptors.NumHAcceptors(mol) # 计算极性氢原子数量
# 计算N原子的数量
count_n = 0
for atom in mol.GetAtoms():
atomic_num = atom.GetAtomicNum()
if atomic_num == 7: # 7代表氮原子的原子序数
count_n += 1

# 返回特征及其简写名称
feature_names = ['mw', 'log_p', 'nhd', 'nha', 'tpsa', 'nrb',
'nar', 'nalr', 'nsr', 'nh',
'nve', 'nre', 'count_n', 'nph']
features_repr = [mol_weight, log_p, num_h_donors, num_h_acceptors, tpsa, num_rotatable_bonds,
num_aromatic_rings, num_aliphatic_rings, num_saturated_rings, num_heteroatoms,
num_valence_electrons, num_radical_electrons, count_n, num_polar_hydrogens]
return feature_names, features_repr
# 初始化特征列表
all_features = []

# 计算1D-QSAR特征
for smiles in features:
feature_names, features_repr = calculate_1dqsar_repr(smiles) # 调用计算函数获取特征及其名称
all_features.append(features_repr) # 将特征添加到特征列表

# 将特征数据和标签数据拆分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(all_features, labels, test_size=0.2, random_state=42)

# 创建随机森林分类器
clf = RandomForestClassifier()

# 在训练集上拟合模型
clf.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = clf.predict(X_test)

# 提取特征重要性
importances = clf.feature_importances_

# 可视化特征重要性
X_train = np.array(X_train)
indices = np.argsort(importances)[::-1] # 特征重要性排序的索引

plt.figure()
plt.title("Feature Importance")
plt.bar(range(X_train.shape[1]), importances[indices], align="center")
plt.xticks(range(X_train.shape[1]), [feature_names[i] for i in indices])
plt.xlabel("Feature")
plt.ylabel("Importance")
plt.show()

# 计算f2-score
f2_score = fbeta_score(y_test, y_pred, beta=2)

print("F2-score: ", f2_score)
F2-score:  0.8222222222222222
代码
文本

  由以上图可知,tpsa特征明显高于其他特征,但这并不意味着其他特征可以随意删除。
  若按照此图将末尾的特征删去,分数反而会下降,特征之间可能存在复杂的隐性关系,即使单独的特征对目标变量的影响较小。有时候,特征之间的交互效应(组合效应)可能对模型性能产生重要影响。删除其中一个特征可能失去这种交互效应的信息。
  让我们来画个热力图去观察一下特征之间的相关关系。

代码
文本
[14]
import os
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
import seaborn as sns#记得先安装seaborn库 pip install seaborn
import matplotlib.pyplot as plt

# 读取数据
data_train = pd.read_csv(os.path.join(DIR_PATH, 'mol_train.csv'))
features_train = data_train['SMILES']

# 定义计算1D-QSAR特征的函数
def calculate_1dqsar_repr(smiles):
mol = Chem.MolFromSmiles(smiles)
mol_weight = Descriptors.MolWt(mol)
log_p = Descriptors.MolLogP(mol)
num_h_donors = Descriptors.NumHDonors(mol)
num_h_acceptors = Descriptors.NumHAcceptors(mol)
tpsa = Descriptors.TPSA(mol)
num_rotatable_bonds = Descriptors.NumRotatableBonds(mol)
num_aromatic_rings = Descriptors.NumAromaticRings(mol)
num_aliphatic_rings = Descriptors.NumAliphaticRings(mol)
num_saturated_rings = Descriptors.NumSaturatedRings(mol)
num_heteroatoms = Descriptors.NumHeteroatoms(mol)
num_valence_electrons = Descriptors.NumValenceElectrons(mol)
num_radical_electrons = Descriptors.NumRadicalElectrons(mol)
qed = Descriptors.qed(mol)
num_polar_hydrogens = Descriptors.NumHAcceptors(mol)
count_n = sum(1 for atom in mol.GetAtoms() if atom.GetAtomicNum() == 7)
return [mol_weight, log_p, num_h_donors, num_h_acceptors, tpsa, num_rotatable_bonds, num_aromatic_rings,
num_aliphatic_rings, num_saturated_rings, num_heteroatoms, num_valence_electrons, num_radical_electrons,
qed, count_n, num_polar_hydrogens]

# 初始化特征列表
all_features = []

# 计算1D-QSAR特征
for smiles in features_train:
features_repr = calculate_1dqsar_repr(smiles)
all_features.append(features_repr)

# 创建DataFrame
df_features = pd.DataFrame(all_features, columns=['MolWt', 'MolLogP', 'NumHDonors', 'NumHAcceptors', 'TPSA',
'NumRotatableBonds', 'NumAromaticRings', 'NumAliphaticRings',
'NumSaturatedRings', 'NumHeteroatoms', 'NumValenceElectrons',
'NumRadicalElectrons', 'QED', 'CountN', 'NumPolarHydrogens'])

# 计算特征之间的相关性
correlation_matrix = df_features.corr()

# 绘制热力图
plt.figure(figsize=(12, 10))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt=".2f", linewidths=0.5)
plt.title('Correlation Heatmap of 1D-QSAR Features')
plt.show()
代码
文本

  热力图是一种通过颜色变化来展示矩阵数据的图表。在特征相关性矩阵的热力图中,不同颜色代表了不同的相关性程度,通常是从低到高的渐变。这种图表有助于直观地发现特征之间的关系。
  在特征相关性矩阵中,每一行和每一列都代表一个特征,矩阵中的每个元素表示对应特征之间的相关性。相关性的值通常在 -1 到 1 之间,-1 表示负相关,1 表示正相关,0 表示无相关性。 具体来说:

  • 如果两个特征之间的相关性接近于 1,那么它们之间存在强正相关性。这意味着当一个特征增加时,另一个特征也增加。
  • 如果两个特征之间的相关性接近于 -1,那么它们之间存在强负相关性。这意味着当一个特征增加时,另一个特征减少。
  • 如果相关性接近于 0,表示两个特征之间基本上没有线性关系。

  相关性系数反映的是线性关系,因此它可能无法捕捉到非线性关系。如果特征之间存在复杂的非线性关系,相关性系数可能不足以完全描述它们之间的相互作用。在这种情况下,其他更复杂的方法,如基于树模型的特征重要性或非线性降维技术,可能更适合用于揭示特征之间的关系。

  对于本次赛题,其特征应该是存在较强的隐性关系,因为使用常用的特征选择方法——滤波法之后,成绩反而下降了。 滤波法是特征选择的一种方法,其基本思想是通过某种准则过滤掉对目标变量影响较小的特征,保留对目标变量影响较大的特征。这样可以在降低计算复杂度的同时,提高模型的泛化能力。其常用方法包含方差阈值,相关系数,卡方检验,互信息法。 滤波法的缺点是可能忽略了特征之间的复杂关系,因为它们只考虑了单个特征与目标变量的关系。

代码
文本
[15]
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Descriptors
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import VarianceThreshold
from sklearn.metrics import fbeta_score
from sklearn.linear_model import LogisticRegression
import os

# 读取数据
data = pd.read_csv(os.path.join(DIR_PATH, 'mol_train.csv'))
features = data['SMILES']
labels = data['TARGET']

# 定义计算1D-QSAR特征的函数
def calculate_1dqsar_repr(smiles):
mol = Chem.MolFromSmiles(smiles)
mol_weight = Descriptors.MolWt(mol) # 计算分子的分子量
log_p = Descriptors.MolLogP(mol) # 计算分子的LogP值
num_h_donors = Descriptors.NumHDonors(mol) # 计算分子中的氢键供体数量
num_h_acceptors = Descriptors.NumHAcceptors(mol) # 计算分子中的氢键受体数量
tpsa = Descriptors.TPSA(mol) # 计算分子的表面积极性
num_rotatable_bonds = Descriptors.NumRotatableBonds(mol) # 计算分子中的可旋转键数量
num_aromatic_rings = Descriptors.NumAromaticRings(mol) # 计算分子中的芳香环数量
num_aliphatic_rings = Descriptors.NumAliphaticRings(mol) # 计算分子中的脂环数量
num_saturated_rings = Descriptors.NumSaturatedRings(mol) # 计算分子中的饱和环数量
num_heteroatoms = Descriptors.NumHeteroatoms(mol) # 计算分子中的杂原子数量
num_valence_electrons = Descriptors.NumValenceElectrons(mol) # 计算分子中的价电子数量
num_radical_electrons = Descriptors.NumRadicalElectrons(mol) # 计算分子中的自由基电子数量
qed = Descriptors.qed(mol) # 计算分子的QED值
num_polar_hydrogens = Descriptors.NumHAcceptors(mol) # 计算极性氢原子数量
# 计算N原子的数量
count_n = 0
for atom in mol.GetAtoms():
atomic_num = atom.GetAtomicNum()
if atomic_num == 7: # 7代表氮原子的原子序数
count_n += 1
return [mol_weight, log_p, num_h_donors, num_h_acceptors, tpsa, num_rotatable_bonds, num_aromatic_rings, num_aliphatic_rings, num_saturated_rings, num_heteroatoms, num_valence_electrons, num_radical_electrons, qed, count_n, num_polar_hydrogens]

# 初始化特征列表
all_features = []

# 计算1D-QSAR特征
for smiles in features:
features_repr = calculate_1dqsar_repr(smiles) # 调用计算函数获取特征
all_features.append(features_repr) # 将特征添加到特征列表

# 将特征数据和标签数据拆分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(all_features, labels, test_size=0.2, random_state=42)

# 使用方差阈值进行特征选择
selector = VarianceThreshold(threshold=0.1)
X_train_filtered = selector.fit_transform(X_train)
X_test_filtered = selector.transform(X_test)

# 采用逻辑回归模型
clf = LogisticRegression()

# 在训练集上拟合模型
clf.fit(X_train_filtered, y_train)

# 在测试集上进行预测
y_pred = clf.predict(X_test_filtered)

# 计算f2-score
f2_score = fbeta_score(y_test, y_pred, beta=2)

print("F2-score: ", f2_score)

# 运行结果:F2-score: 0.7142857142857143
F2-score:  0.7142857142857143
/opt/conda/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:814: ConvergenceWarning: lbfgs failed to converge (status=1):

STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.



Increase the number of iterations (max_iter) or scale the data as shown in:

    https://scikit-learn.org/stable/modules/preprocessing.html

Please also refer to the documentation for alternative solver options:

    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression

  n_iter_i = _check_optimize_result(
代码
文本

除此之外,还有

  • 包裹法:包裹法使用预测模型的性能作为特征选择的标准。例如,我们可以使用前向选择或后向剔除的方法,逐步添加或删除特征,同时观察模型在验证集上的表现,从而选择最佳特征组合。此法可以考虑到特征间的相互关系,但计算成本比滤波法高。
  • 嵌入法:通过构建模型来选择特征,如使用带惩罚项的模型(例如Lasso和Ridge回归),或者基于树的模型(例如随机森林或梯度提升树)。这类模型可以在训练过程中自动进行特征选择。此法通常可以获得较好的性能,但解释性可能较差。

5.特征变换

  如果需要,可以对提取出的特征进行归一化、中心化或其他转换,以提高模型的稳定性和效率。主要方法有特征缩放、Box-Cox变换、主成分分析(Principal Component Analysis, PCA)等。
  组合特征:在某些情况下,原始特征的组合可能包含有关目标变量的更多信息。可以尝试组合现有的特征,例如使用四则运算或统计方法(如平均值、中位数等)。

6.模型优化

  不断地调整前面的特征工程操作以及模型参数,以优化模型在预测 CNS 药物方面的性能。可以尝试多种不同的模型、管理不平衡分类问题或利用集成方法改进模型性能。

代码
文本
AI4S Cup-Getting Started
AI4S Cup-Getting Started
已赞4
本文被以下合集收录
构建特征
霞猛猛
更新于 2024-04-22
1 篇0 人关注
推荐阅读
公开
AI4S Cup 酶功能与突变序列间的关系预测 工具介绍
AI4Snotebookproteinenzyme
AI4Snotebookproteinenzyme
zhangjun19
发布于 2024-03-28
1 赞5 转存文件2 评论
公开
AI4S Cup 电芯电化学阻抗赛题 Baseline
AI4SCUP-EIS
AI4SCUP-EIS
JiaweiMiao
发布于 2023-10-12
5 赞85 转存文件