1. QSAR概述
1.1 什么是QSAR?
QSAR,全称是 Quantitative Structure-Activity Relationship,翻译过来叫定量构效关系。说白了,它就是研究"分子的结构长什么样"和"它有没有生物活性"之间到底存在什么定量关系。
我个人习惯把QSAR理解为一个映射函数:你给我一个分子的结构式,我就能告诉你它的活性大概是多少。数学上可以写成:
活性 = f(结构特征)
这个f,早期是线性回归,后来变成了SVM、随机森林,再到现在各种深度学习模型。但不管怎么变,核心假设没变——结构相似的分子,活性也相似。你想想看,要是这个假设不成立,那QSAR就根本没法学。
我在2018年跟一个药企合作时,对方老问我:"你们搞机器学习的,真能用计算机算出化合物有没有毒?"我当时的回答是:QSAR不是算命,它是在历史数据中找规律。你给它的数据质量越高,它预测得就越准。
1.2 QSAR的核心逻辑
要理解QSAR,你得先搞清楚它整个链条是怎么跑的。我从数据到应用,把核心逻辑拆成5步。下面这张图是我自己经常拿来培训新人的,你看完就能明白全貌。
1.3 QSAR的历史发展
QSAR不是近几年才冒出来的新东西。它从上世纪60年代走到今天,经历了几个关键阶段。我刚开始做这行时翻过不少老论文,发现很多今天觉得"理所当然"的思路,在当年都是巨大的突破。
| 时期 | 代表人物 / 方法 | 核心特点 |
|---|---|---|
| 1960s | Hansch 分析 | 用 logP 和 Hammett 常数做线性回归,开创了 QSAR 先河 |
| 1970–1980s | 3D-QSAR (CoMFA) | 引入分子三维结构,用分子场描述活性 |
| 1990s | 机器学习方法 (SVM, RF) | 从线性走向非线性,处理高维数据 |
| 2000s | 深度学习 (DNN, GNN) | 自动提取特征,端到端建模 |
| 2010s–至今 | 多任务学习 / 迁移学习 | 共享表示,多目标联合预测 |
记得有一次我拿 Hansch 的老论文跟团队分享,有人笑着说"这方法也太原始了吧"。但我说实话,Hansch 那个年代能用一张纸和一把计算器把 logP 和活性捋出关系,放在今天依然让人佩服。你想想看,那时候哪有什么RDKit、scikit-learn,全是手算。
1.4 QSAR在药物发现中的作用
QSAR在药物发现链条里到底扮演什么角色?我用一句话概括:它是从"盲目试错"到"理性设计"的加速器。没有QSAR之前,药物研发更多靠经验+运气;有了QSAR,你可以用模型先筛一遍,把明显不行的化合物提前淘汰。
具体来说,QSAR在以下几个环节特别能打:
- 先导化合物优化:告诉你改哪个基团能提高活性、降低毒性。
- 虚拟筛选:从百万级化合物库里快速挑出候选分子。
- ADMET预测:在合成之前预判吸收、分布、代谢、排泄和毒性。
- 脱靶效应排查:预测化合物会不会跟其他蛋白发生不良反应。
- 专利空间规避:设计结构不同但活性相似的化合物。
我在一个抗肿瘤项目里遇到过这种情况:合成团队花了3个月做了40个化合物,结果送到生物测试那边,只有2个有活性。后来我们用QSAR模型重新设计了分子库,第二轮合成时活性命中率提高了4倍。说白了,QSAR就是帮你在烧钱之前先动脑子。
1.5 常见应用场景
QSAR的应用早就超出了药物研发的范围。我列几个我亲眼见过或者参与过的场景:
- 小分子药物研发——这是QSAR的主战场,从靶点发现到临床前优化都在用。
- 环境毒理学——评估工业化学品对水生生物的毒性,不用做动物实验。
- 农药与农用化学品设计——提高杀虫/除草活性,同时降低对非靶标生物的伤害。
- 化妆品成分安全评估——欧盟REACH法规推动下,替代动物实验的刚需。
- 纳米材料活性预测——虽然起步晚,但nano-QSAR已经在快速成长。
你可能会问:这些场景的建模方法都一样吗?当然不是。药物分子的描述符偏重疏水性和氢键,环境毒理更关注代谢稳定性,农用化学品还得考虑光解半衰期。场景不同,数据不同,但QSAR的底层逻辑一致。
下面是一个用RDKit计算分子描述符的简单示例。我在项目初期做数据探索时,几乎每次都要跑一遍类似的代码:
from rdkit import Chem
from rdkit.Chem import Descriptors
# 输入一个分子的SMILES
smiles = 'CC(=O)OC1=CC=CC=C1C(=O)O'
mol = Chem.MolFromSmiles(smiles)
# 计算常用描述符
mw = Descriptors.MolWt(mol)
logp = Descriptors.MolLogP(mol)
hbd = Descriptors.NumHDonors(mol)
hba = Descriptors.NumHAcceptors(mol)
tpsa = Descriptors.TPSA(mol)
print(f'分子量: {mw:.2f}')
print(f'logP: {logp:.2f}')
print(f'氢键供体: {hbd}')
print(f'氢键受体: {hba}')
print(f'极性表面积: {tpsa:.2f} Ų')