2.3 · 机器学习基础与网络药理初步结合
理解机器学习的基本概念(监督/无监督、分类/回归),掌握 scikit-learn 的完整建模流程(数据准备 → 划分 → 建模 → 评价),能用随机森林完成一次"化合物-靶点"分类或特征重要性分析,输出 ROC 曲线与特征重要性图。
0. 小白导读:机器学习到底是什么?
机器学习听起来很高深,用大白话说:
- 传统编程 = 你告诉计算机"规则",计算机照着执行。比如"如果温度 > 37.5℃,就判定发烧"。
- 机器学习 = 你不告诉计算机规则,而是给它大量"例子",让它自己从例子中总结出规则。比如给它一万个"化合物 + 是否有效"的例子,它自己学会"什么样的化合物更可能有效"。
一个贯穿本章的比喻——教小朋友认猫:
-
训练数据 = 给小朋友看一万张"猫/不是猫"的图片,并告诉他答案(标签)。
-
特征 = 图片里的"耳朵尖不尖、有没有胡须、体型大小"等可观察的属性。
-
模型 = 小朋友自己总结出的"判断规则"。
-
测试 = 拿没看过的图片考他,看他认对多少。
-
过拟合 = 小朋友把训练时某只特定猫的"花斑"背下来了,换一只猫就认不出——"死记硬背"而非"学会规律"。
机器学习要回答的核心问题:能不能让计算机从数据中自动学到规律,并用它预测新数据?
为什么学它? 网络药理学找到靶点后,机器学习可以进一步"预测":某个新化合物是不是某个靶点的潜在配体、有没有活性、有没有毒性。这是中药新药研发的前沿方向,也是你研究方向的加分项。
一句话总结:机器学习 = 给计算机喂"例子",让它自己总结规律,再用规律预测新数据。
💡 前置依赖:本章代码基于模块一的 Python / NumPy / Pandas / matplotlib 知识,建议先学完模块一再来。
1. 机器学习基本概念
1.1 监督学习 vs 无监督学习
| 类型 | 英文 | 有没有"标准答案" | 典型任务 | 比喻 |
|---|---|---|---|---|
| 监督学习 | Supervised Learning | 有(数据带标签) | 分类、回归 | 教小朋友认猫(告诉他每张图是不是猫) |
| 无监督学习 | Unsupervised Learning | 没有(数据无标签) | 聚类、降维 | 让小朋友自己把一堆照片分成几堆 |
- 监督学习:数据有"答案"(标签),模型学习"特征 → 标签"的映射。本章重点。
- 无监督学习:数据没有"答案",模型自己发现数据的内在结构(如把相似化合物聚成一类)。
网络药理学里,监督学习用于"预测化合物活性/毒性/是否靶点",无监督学习用于"化合物聚类、分子多样性分析"。
1.2 机器学习三要素
数据(Data)+ 模型(Model)+ 评价(Evaluation)
- 数据:特征矩阵 X + 标签 y。
- 模型:算法(随机森林、SVM、KNN……)。
- 评价:怎么判断模型好不好(准确率、AUC……)。
比喻:数据是"教材",模型是"学生",评价是"考试"。没有好教材(数据),再聪明的学生(模型)也学不好。
2. 分类与回归任务
| 任务 | 英文 | 预测的是什么 | 例子 | 评价指标 |
|---|---|---|---|---|
| 分类 | Classification | 离散的类别 | 化合物"有活性/无活性" | 准确率、精确率、召回率、AUC |
| 回归 | Regression | 连续的数值 | 预测化合物的 IC50 值 | R²、均方误差 MSE |
- 分类:输出是"类别"(0/1、猫/狗、有效/无效)。
- 回归:输出是"数值"(如结合能 -9.2、IC50 = 12.5 μM)。
网络药理学最常用的是二分类:预测"化合物-靶点"是否相互作用(1=是,0=否)。
3. 数据准备
3.1 特征矩阵与标签
机器学习的数据通常整理成一张表(DataFrame):
| 化合物 | 分子量 | LogP | 氢键供体 | 氢键受体 | 活性标签 |
|---|---|---|---|---|---|
| 化合物A | 294.3 | 3.2 | 1 | 4 | 1(有活性) |
| 化合物B | 358.4 | 4.1 | 0 | 6 | 0(无活性) |
| ... | ... | ... | ... | ... | ... |
- 特征(X):分子量、LogP、氢键供体/受体等"分子描述符"(也叫分子指纹)。
- 标签(y):活性标签(1/0)。
3.2 特征来源
- 分子描述符:用 RDKit(化学信息学工具包)计算,如分子量、LogP、TPSA、氢键数等。
- 分子指纹:把分子结构编码成 0/1 向量(如 Morgan 指纹、MACCS 指纹)。
用 RDKit 计算分子描述符示例:
from rdkit import Chem from rdkit.Chem import Descriptors, Draw # 从 SMILES 创建分子 mol = Chem.MolFromSmiles("CC(=O)Oc1ccccc1C(=O)O") # 阿司匹林 # 计算常用描述符 mw = Descriptors.MolWt(mol) # 分子量 logp = Descriptors.MolLogP(mol) # 脂水分配系数 hbd = Descriptors.NumHDonors(mol) # 氢键供体数 hba = Descriptors.NumHAcceptors(mol) # 氢键受体数 print(f"分子量={mw:.1f}, LogP={logp:.1f}, HBD={hbd}, HBA={hba}")
RDKit 是化学信息学的"瑞士军刀",pip install rdkit 即可安装。它能把 SMILES 变成模型能吃的数值特征。
3.3 数据清洗
- 处理缺失值(fillna / dropna,见模块一 03 章)。
- 特征标准化(StandardScaler):让不同量纲的特征(分子量 300 vs LogP 3)处于同一尺度。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_scaled = scaler.fit_transform(X)
4. 训练集/测试集划分与交叉验证
4.1 为什么要划分?
模型在"见过的数据"上表现好是应该的,关键是在没见过的数据上表现好不好(泛化能力)。所以要把数据分成:
- 训练集(train):用来"学习"(约 70-80%)。
- 测试集(test):用来"考试"(约 20-30%),模型从没见过。
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y )
stratify=y 让训练/测试集中正负样本比例一致(分层抽样);random_state=42 固定随机种子,保证结果可复现。
4.2 交叉验证(Cross-Validation)
单次划分有随机性,更稳妥的做法是 K 折交叉验证:把数据分成 K 份(通常 5 或 10),轮流用 K-1 份训练、1 份验证,重复 K 次,取平均。
数据分成 5 份: [1][2][3][4][5] 第1轮:训练[2][3][4][5] 验证[1] 第2轮:训练[1][3][4][5] 验证[2] ... 第5轮:训练[1][2][3][4] 验证[5] 最终:5 次验证结果取平均
from sklearn.model_selection import cross_val_score from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier(random_state=42) scores = cross_val_score(model, X, y, cv=5) print(f"5折交叉验证平均准确率: {scores.mean():.3f}")
比喻:单次划分 = 一次期末考试;交叉验证 = 五次不同试卷的期末考试取平均,更能反映真实水平。
5. 三大经典模型:随机森林、SVM、KNN

5.1 随机森林(Random Forest)
- 原理:训练很多棵"决策树",每棵树独立投票,少数服从多数。
- 特点:抗过拟合、能输出特征重要性、对数据要求低,药物靶点预测中最常用。
- 比喻:一群"专家"各自判断,综合意见比单个专家更可靠。
from sklearn.ensemble import RandomForestClassifier rf = RandomForestClassifier(n_estimators=100, random_state=42) rf.fit(X_train, y_train)
5.2 支持向量机(SVM)
- 原理:找一个"分界线"(超平面),把两类数据分得最开。
- 特点:小样本高维数据表现好(分子描述符正是高维数据),SwissTargetPrediction 底层就是 SVM 集成。
- 比喻:在两类点之间画一条"最宽的分隔带"。
from sklearn.svm import SVC svm = SVC(kernel="rbf", probability=True, random_state=42) svm.fit(X_train, y_train)
5.3 K 近邻(KNN)
- 原理:看新样本最近的 K 个邻居,少数服从多数定类别。
- 特点:最简单直观,常作为"基线模型"对比。
- 比喻:"物以类聚"——看你的邻居是什么,你就是什么。
from sklearn.neighbors import KNeighborsClassifier knn = KNeighborsClassifier(n_neighbors=5) knn.fit(X_train, y_train)
5.4 三个模型对比
| 模型 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 随机森林 | 多棵决策树投票 | 抗过拟合、有特征重要性 | 训练慢(树多时) | 药物活性/毒性预测(首选) |
| SVM | 找最优分界线 | 小样本高维强 | 大数据慢、难解释 | 分子描述符分类 |
| KNN | 看最近邻居 | 简单、无需训练 | 慢、对特征尺度敏感 | 基线对比 |
入门策略:三个模型都跑一遍,用交叉验证比一比,选表现最好的。论文里常同时报多个模型的结果。
6. 模型评价指标
6.1 混淆矩阵
分类结果可以分成四类:
| 预测为"有活性"(1) | 预测为"无活性"(0) | |
|---|---|---|
| 实际"有活性"(1) | TP(真阳性) | FN(假阴性) |
| 实际"无活性"(0) | FP(假阳性) | TN(真阴性) |
6.2 核心指标
| 指标 | 公式 | 通俗解释 |
|---|---|---|
| 准确率 Accuracy | (TP+TN)/(总数) | 全部预测中猜对的比例 |
| 精确率 Precision | TP/(TP+FP) | 预测为"有活性"的里面,真有多少是活性 |
| 召回率 Recall | TP/(TP+FN) | 真正有活性的里面,抓到了多少 |
| F1 | 精确率与召回率的调和平均 | 综合两者 |
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score y_pred = rf.predict(X_test) print("准确率:", accuracy_score(y_test, y_pred)) print("精确率:", precision_score(y_test, y_pred)) print("召回率:", recall_score(y_test, y_pred)) print("F1:", f1_score(y_test, y_pred))
6.3 ROC 曲线与 AUC
- ROC 曲线:横轴假阳性率、纵轴真阳性率,画出一条曲线。
- AUC:ROC 曲线下方的面积,越接近 1 越好(0.5 等于瞎猜,1 是完美)。
| AUC | 模型水平 |
|---|---|
| 0.5 | 等于随机猜测 |
| 0.7-0.8 | 可用 |
| 0.8-0.9 | 良好 |
| > 0.9 | 优秀 |
from sklearn.metrics import roc_curve, auc import matplotlib.pyplot as plt # 预测概率 y_prob = rf.predict_proba(X_test)[:, 1] # 计算 ROC fpr, tpr, _ = roc_curve(y_test, y_prob) roc_auc = auc(fpr, tpr) # 画 ROC 曲线 plt.figure(figsize=(6, 5)) plt.plot(fpr, tpr, label=f"Random Forest (AUC = {roc_auc:.3f})") plt.plot([0, 1], [0, 1], "k--", label="Random (AUC = 0.5)") plt.xlabel("False Positive Rate") plt.ylabel("True Positive Rate") plt.title("ROC Curve") plt.legend() plt.savefig("roc_curve.png", dpi=300) plt.show()
论文标准写法:报告"随机森林模型在测试集上的 AUC 为 0.87,准确率 0.82,精确率 0.85,召回率 0.78",并附 ROC 曲线图。
7. 特征重要性
随机森林的一个巨大优势是能告诉我们:哪些特征对预测贡献最大?
import pandas as pd # 特征重要性 importance = rf.feature_importances_ feat_names = X.columns if hasattr(X, "columns") else [f"f{i}" for i in range(X.shape[1])] # 排序并画图 imp_df = pd.DataFrame({"feature": feat_names, "importance": importance}) imp_df = imp_df.sort_values("importance", ascending=False) plt.figure(figsize=(8, 5)) plt.barh(imp_df["feature"][:10], imp_df["importance"][:10]) plt.xlabel("Feature Importance") plt.title("Top 10 Important Features") plt.gca().invert_yaxis() plt.savefig("feature_importance.png", dpi=300) plt.show()
比喻:特征重要性 = "哪些特征对认猫最管用"。如果"胡须长度"重要性最高,说明它是判断猫的关键依据。在网络药理中,如果"LogP"重要性最高,说明脂溶性对活性影响最大。
8. 与网络药理结合案例
8.1 应用场景
| 场景 | 输入特征 | 预测目标 | 意义 |
|---|---|---|---|
| 化合物-靶点预测 | 分子描述符 + 靶点特征 | 是否相互作用(1/0) | 筛选潜在活性成分 |
| 活性预测 | 分子描述符 | 活性类别/IC50 | 预测新成分药效 |
| 毒性预测 | 分子描述符 | 是否肝毒性(1/0) | 安全性初筛 |
| ADMET 预测 | 分子描述符 | 吸收/代谢性质 | 成药性评估 |
8.2 完整代码案例:化合物活性二分类
import numpy as np import pandas as pd from sklearn.model_selection import train_test_split, cross_val_score from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, roc_curve, auc import matplotlib.pyplot as plt # ========== 1. 模拟数据(真实项目替换为 RDKit 计算的描述符表) ========== rng = np.random.default_rng(42) n = 200 X = pd.DataFrame({ "MolWt": rng.uniform(150, 500, n), # 分子量 "LogP": rng.uniform(-2, 6, n), # 脂水分配系数 "HBD": rng.integers(0, 6, n), # 氢键供体 "HBA": rng.integers(0, 12, n), # 氢键受体 "TPSA": rng.uniform(20, 120, n), # 极性表面积 }) y = ((X["MolWt"] > 250) & (X["LogP"] > 1) & (X["HBD"] <= 3)).astype(int) # ========== 2. 划分训练/测试集 ========== X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # ========== 3. 训练随机森林 ========== rf = RandomForestClassifier(n_estimators=100, random_state=42) rf.fit(X_train, y_train) # ========== 4. 交叉验证 ========== scores = cross_val_score(rf, X, y, cv=5) print(f"5折交叉验证准确率: {scores.mean():.3f} ± {scores.std():.3f}") # ========== 5. 测试集评价 ========== y_pred = rf.predict(X_test) print(f"测试集准确率: {accuracy_score(y_test, y_pred):.3f}") # ========== 6. ROC 曲线 ========== y_prob = rf.predict_proba(X_test)[:, 1] fpr, tpr, _ = roc_curve(y_test, y_prob) roc_auc = auc(fpr, tpr) plt.figure(figsize=(6, 5)) plt.plot(fpr, tpr, label=f"Random Forest (AUC = {roc_auc:.3f})") plt.plot([0, 1], [0, 1], "k--") plt.xlabel("False Positive Rate") plt.ylabel("True Positive Rate") plt.title("ROC Curve") plt.legend() plt.savefig("roc_curve.png", dpi=300) # ========== 7. 特征重要性 ========== imp = pd.Series(rf.feature_importances_, index=X.columns).sort_values(ascending=False) print("特征重要性:\n", imp)
真实项目替换点:把第 1 步的模拟数据换成"用 RDKit 从化合物 SMILES 计算出的真实描述符表",把标签换成真实的活性数据(如 ChEMBL 数据库的活性数据),即可用于真实研究。
8.3 数据来源建议
- ChEMBL(https://www.chembl.org/):药物活性数据库,可下载"化合物-靶点-活性"数据。
- PubChem BioAssay:生物活性测定数据。
- 自己课题组数据:实验测得的活性数据。
9. 常见坑
| 坑 | 表现 | 解决办法 |
|---|---|---|
| 数据泄露 | 测试集表现虚高 | 特征标准化必须在划分训练/测试集之后做(只 fit 训练集) |
| 类别不平衡 | 正样本太少,准确率虚高 | 用 stratify 分层抽样,或报告精确率/召回率/AUC 而非只看准确率 |
| 忘记固定随机种子 | 结果每次不一样 | 统一 random_state=42 |
| 过拟合 | 训练集 99%、测试集 60% | 用交叉验证评估、增加数据、调参(n_estimators、max_depth) |
| 特征尺度差异大 | SVM/KNN 表现差 | 用 StandardScaler 标准化(随机森林不受影响) |
| 把测试集当训练集反复调参 | 测试集"被记住" | 调参用交叉验证,测试集只碰一次 |
10. 练习与交付物
练习(必须亲手做)
- 数据准备:用 RDKit 从 20 个中药成分的 SMILES 计算分子描述符,构造特征矩阵。
- 建模:用随机森林训练一个"活性分类"模型(可用模拟标签)。
- 对比:分别用随机森林、SVM、KNN 建模,用 5 折交叉验证比较准确率。
- 评价:计算准确率、精确率、召回率、F1、AUC,画出 ROC 曲线。
- 特征重要性:输出特征重要性排序,画 Top 10 柱状图。
交付物(验收标准)
使用公开数据完成一次随机森林分类或特征重要性排序,提交:
-
代码(带注释,可复现)
-
ROC 曲线图(含 AUC 值)
-
特征重要性图(Top 10)
-
简要结果解读(模型表现 + 关键特征的意义)
11. 速查表
工具速查
| 用途 | 工具 | 说明 |
|---|---|---|
| 机器学习 | scikit-learn | 当前版本 1.9.x |
| 分子描述符 | RDKit | pip install rdkit |
| 数据操作 | pandas | 模块一已学 |
| 绘图 | matplotlib / seaborn | 模块一已学 |
| 活性数据 | ChEMBL | https://www.chembl.org/ |
代码模板速查
# 划分 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y) # 交叉验证 from sklearn.model_selection import cross_val_score scores = cross_val_score(model, X, y, cv=5) # 随机森林 from sklearn.ensemble import RandomForestClassifier rf = RandomForestClassifier(n_estimators=100, random_state=42).fit(X_train, y_train) # 评价 from sklearn.metrics import accuracy_score, roc_curve, auc
关键概念速查
| 概念 | 一句话 |
|---|---|
| 监督学习 | 数据带标签,学"特征→标签"映射 |
| 无监督学习 | 数据无标签,自己发现结构 |
| 分类 vs 回归 | 预测类别 vs 预测数值 |
| 交叉验证 | K 份轮流训练/验证取平均 |
| 过拟合 | 死记硬背训练数据,新数据表现差 |
| AUC | ROC 曲线下面积,越接近 1 越好 |
| 特征重要性 | 哪些特征对预测贡献最大 |
下一章:模块三 · 实验药剂与纳米制剂