[{"data":1,"prerenderedAt":1453},["ShallowReactive",2],{"article-ai\u002Famino-hydro-classifier":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"tags":11,"body":15,"_type":1447,"_id":1448,"_source":1449,"_file":1450,"_stem":1451,"_extension":1452},"\u002Farticles\u002Fai\u002Famino-hydro-classifier","ai",false,"","AminoHydro Classifier：用 SMILES 和 MLP 判断氨基酸亲疏水性","基于 RDKit 提取 Morgan 指纹，使用 MLP 神经网络对氨基酸进行亲疏水性二分类的入门项目完整笔记","2026-05-05",[12,13,14],"深度学习","人工智能","生物信息学",{"type":16,"children":17,"toc":1433},"root",[18,26,39,44,64,69,75,80,129,142,147,152,158,163,168,201,206,219,224,247,252,258,263,268,273,302,314,330,342,361,367,372,377,400,405,410,451,456,469,500,505,511,516,521,535,540,554,559,564,585,597,699,712,717,730,758,763,872,892,897,902,907,992,997,1002,1007,1012,1017,1031,1036,1070,1075,1098,1103,1126,1139,1210,1223,1228,1233,1247,1252,1295,1300,1305,1310,1315,1320,1325,1330,1335,1340,1345,1383,1388,1393,1398,1412,1417,1422,1427],{"type":19,"tag":20,"props":21,"children":22},"element","p",{},[23],{"type":24,"value":25},"text","最近我做了一个很小但很完整的 AI 制药入门项目：输入一个氨基酸的 SMILES 字符串，程序会判断它更偏向亲水还是疏水。",{"type":19,"tag":20,"props":27,"children":28},{},[29,31,37],{"type":24,"value":30},"这个项目的名字叫 ",{"type":19,"tag":32,"props":33,"children":34},"strong",{},[35],{"type":24,"value":36},"AminoHydro Classifier",{"type":24,"value":38},"。它不追求复杂模型，也不假装自己已经能替代真正的药物性质预测系统。它更像一个“最小可运行样例”：把计算化学里的分子表示、RDKit 特征提取、机器学习分类器训练和模型预测串成一条完整链路。",{"type":19,"tag":20,"props":40,"children":41},{},[42],{"type":24,"value":43},"核心流程可以概括为：",{"type":19,"tag":45,"props":46,"children":49},"pre",{"className":47,"code":48,"language":24,"meta":7,"style":7},"language-text shiki shiki-themes github-dark","SMILES 字符串 -> RDKit 解析 -> Mol 对象 -> Morgan 指纹 -> MLP 神经网络 -> 亲水 \u002F 疏水\n",[50],{"type":19,"tag":51,"props":52,"children":53},"code",{"__ignoreMap":7},[54],{"type":19,"tag":55,"props":56,"children":59},"span",{"class":57,"line":58},"line",1,[60],{"type":19,"tag":55,"props":61,"children":62},{},[63],{"type":24,"value":48},{"type":19,"tag":20,"props":65,"children":66},{},[67],{"type":24,"value":68},"这条链路虽然短，但已经包含了很多 AI 制药项目里反复出现的基础概念。",{"type":19,"tag":70,"props":71,"children":73},"h2",{"id":72},"为什么从氨基酸开始",[74],{"type":24,"value":72},{"type":19,"tag":20,"props":76,"children":77},{},[78],{"type":24,"value":79},"氨基酸是蛋白质的基本组成单元。标准蛋白质编码氨基酸有 20 种，它们可以通过不同排列组合形成大量蛋白质。每个氨基酸都有共同的主链结构：",{"type":19,"tag":81,"props":82,"children":83},"ul",{},[84,96,107,118],{"type":19,"tag":85,"props":86,"children":87},"li",{},[88,90],{"type":24,"value":89},"氨基：",{"type":19,"tag":51,"props":91,"children":93},{"className":92},[],[94],{"type":24,"value":95},"-NH2",{"type":19,"tag":85,"props":97,"children":98},{},[99,101],{"type":24,"value":100},"羧基：",{"type":19,"tag":51,"props":102,"children":104},{"className":103},[],[105],{"type":24,"value":106},"-COOH",{"type":19,"tag":85,"props":108,"children":109},{},[110,112],{"type":24,"value":111},"一个氢原子：",{"type":19,"tag":51,"props":113,"children":115},{"className":114},[],[116],{"type":24,"value":117},"H",{"type":19,"tag":85,"props":119,"children":120},{},[121,123],{"type":24,"value":122},"一个侧链基团：",{"type":19,"tag":51,"props":124,"children":126},{"className":125},[],[127],{"type":24,"value":128},"R group",{"type":19,"tag":20,"props":130,"children":131},{},[132,134,140],{"type":24,"value":133},"真正决定不同氨基酸性质的，主要是侧链 ",{"type":19,"tag":51,"props":135,"children":137},{"className":136},[],[138],{"type":24,"value":139},"R",{"type":24,"value":141},"。有的侧链偏非极性，更容易和脂溶性环境相处，通常被认为更疏水；有的侧链带有羟基、羧基、氨基等极性或可电离基团，更容易和水形成相互作用，通常被认为更亲水。",{"type":19,"tag":20,"props":143,"children":144},{},[145],{"type":24,"value":146},"亲疏水性在药物设计里非常重要。比如，一个分子如果过于亲水，可能难以穿过脂质膜；如果过于疏水，又可能带来溶解度差、非特异性结合强等问题。血脑屏障 BBB 也是一个典型例子：一般来说，更合适的脂溶性有助于分子跨越屏障，但真实情况还会受到分子量、极性表面积、氢键供受体、转运蛋白等多种因素影响。",{"type":19,"tag":20,"props":148,"children":149},{},[150],{"type":24,"value":151},"所以，氨基酸亲疏水性是一个很适合作为入门任务的性质：概念直观，数据量小，容易验证，也能自然引出分子机器学习的基本流程。",{"type":19,"tag":70,"props":153,"children":155},{"id":154},"smiles把分子写成字符串",[156],{"type":24,"value":157},"SMILES：把分子写成字符串",{"type":19,"tag":20,"props":159,"children":160},{},[161],{"type":24,"value":162},"计算机不能直接理解“亮氨酸”“赖氨酸”这样的中文名称，也不能直接理解课本上的二维结构图。我们需要一种机器可读的表示方法。",{"type":19,"tag":20,"props":164,"children":165},{},[166],{"type":24,"value":167},"SMILES 就是其中一种常见格式。它可以用一串文本描述分子的原子、键、环、支链等结构信息。例如：",{"type":19,"tag":45,"props":169,"children":171},{"className":47,"code":170,"language":24,"meta":7,"style":7},"亮氨酸 Leu: CC(C)CC(N)C(=O)O\n丝氨酸 Ser: NC(CO)C(=O)O\n赖氨酸 Lys: NCCCCC(N)C(=O)O\n",[172],{"type":19,"tag":51,"props":173,"children":174},{"__ignoreMap":7},[175,183,192],{"type":19,"tag":55,"props":176,"children":177},{"class":57,"line":58},[178],{"type":19,"tag":55,"props":179,"children":180},{},[181],{"type":24,"value":182},"亮氨酸 Leu: CC(C)CC(N)C(=O)O\n",{"type":19,"tag":55,"props":184,"children":186},{"class":57,"line":185},2,[187],{"type":19,"tag":55,"props":188,"children":189},{},[190],{"type":24,"value":191},"丝氨酸 Ser: NC(CO)C(=O)O\n",{"type":19,"tag":55,"props":193,"children":195},{"class":57,"line":194},3,[196],{"type":19,"tag":55,"props":197,"children":198},{},[199],{"type":24,"value":200},"赖氨酸 Lys: NCCCCC(N)C(=O)O\n",{"type":19,"tag":20,"props":202,"children":203},{},[204],{"type":24,"value":205},"这些字符串看起来像化学版的“密码”，但对 RDKit 这样的化学信息学工具来说，它们是可以被解析的结构描述。",{"type":19,"tag":20,"props":207,"children":208},{},[209,211,217],{"type":24,"value":210},"项目的数据集在 ",{"type":19,"tag":51,"props":212,"children":214},{"className":213},[],[215],{"type":24,"value":216},"data\u002Famino_acids.csv",{"type":24,"value":218},"，一共包含 20 个蛋白质编码氨基酸，每条数据包括名称、缩写、三字母代码、SMILES 字符串和亲疏水标签。",{"type":19,"tag":20,"props":220,"children":221},{},[222],{"type":24,"value":223},"标签约定是：",{"type":19,"tag":45,"props":225,"children":227},{"className":47,"code":226,"language":24,"meta":7,"style":7},"1 = 疏水\n0 = 亲水\n",[228],{"type":19,"tag":51,"props":229,"children":230},{"__ignoreMap":7},[231,239],{"type":19,"tag":55,"props":232,"children":233},{"class":57,"line":58},[234],{"type":19,"tag":55,"props":235,"children":236},{},[237],{"type":24,"value":238},"1 = 疏水\n",{"type":19,"tag":55,"props":240,"children":241},{"class":57,"line":185},[242],{"type":19,"tag":55,"props":243,"children":244},{},[245],{"type":24,"value":246},"0 = 亲水\n",{"type":19,"tag":20,"props":248,"children":249},{},[250],{"type":24,"value":251},"当前数据集中有 9 个疏水氨基酸、11 个亲水氨基酸。",{"type":19,"tag":70,"props":253,"children":255},{"id":254},"rdkit-负责什么",[256],{"type":24,"value":257},"RDKit 负责什么",{"type":19,"tag":20,"props":259,"children":260},{},[261],{"type":24,"value":262},"一个很容易混淆的问题是：RDKit 是不是分类器？",{"type":19,"tag":20,"props":264,"children":265},{},[266],{"type":24,"value":267},"答案是：不是。",{"type":19,"tag":20,"props":269,"children":270},{},[271],{"type":24,"value":272},"RDKit 更像是一个“分子工具箱”。它不会自动告诉我们一个分子是亲水还是疏水，但它能做很多机器学习前必需的事情，比如：",{"type":19,"tag":81,"props":274,"children":275},{},[276,281,286,297],{"type":19,"tag":85,"props":277,"children":278},{},[279],{"type":24,"value":280},"读取 SMILES 字符串",{"type":19,"tag":85,"props":282,"children":283},{},[284],{"type":24,"value":285},"检查分子是否合法",{"type":19,"tag":85,"props":287,"children":288},{},[289,291],{"type":24,"value":290},"构建内部的分子对象 ",{"type":19,"tag":51,"props":292,"children":294},{"className":293},[],[295],{"type":24,"value":296},"Mol",{"type":19,"tag":85,"props":298,"children":299},{},[300],{"type":24,"value":301},"计算分子指纹、描述符、子结构等特征",{"type":19,"tag":20,"props":303,"children":304},{},[305,307,312],{"type":24,"value":306},"在这个项目里，RDKit 的第一步工作是把 SMILES 转成 ",{"type":19,"tag":51,"props":308,"children":310},{"className":309},[],[311],{"type":24,"value":296},{"type":24,"value":313}," 对象：",{"type":19,"tag":45,"props":315,"children":319},{"className":316,"code":317,"language":318,"meta":7,"style":7},"language-python shiki shiki-themes github-dark","mol = Chem.MolFromSmiles(smiles)\n","python",[320],{"type":19,"tag":51,"props":321,"children":322},{"__ignoreMap":7},[323],{"type":19,"tag":55,"props":324,"children":325},{"class":57,"line":58},[326],{"type":19,"tag":55,"props":327,"children":328},{},[329],{"type":24,"value":317},{"type":19,"tag":20,"props":331,"children":332},{},[333,335,340],{"type":24,"value":334},"可以把 ",{"type":19,"tag":51,"props":336,"children":338},{"className":337},[],[339],{"type":24,"value":296},{"type":24,"value":341}," 理解成 RDKit 在程序内部使用的分子结构。它比字符串更结构化，知道哪些原子连在一起，哪些是支链，哪些是环结构。",{"type":19,"tag":20,"props":343,"children":344},{},[345,347,352,354,359],{"type":24,"value":346},"但是机器学习模型仍然不能直接吃 ",{"type":19,"tag":51,"props":348,"children":350},{"className":349},[],[351],{"type":24,"value":296},{"type":24,"value":353}," 对象。下一步，我们还要把 ",{"type":19,"tag":51,"props":355,"children":357},{"className":356},[],[358],{"type":24,"value":296},{"type":24,"value":360}," 转成数值向量。",{"type":19,"tag":70,"props":362,"children":364},{"id":363},"morgan-指纹把分子变成-2048-位向量",[365],{"type":24,"value":366},"Morgan 指纹：把分子变成 2048 位向量",{"type":19,"tag":20,"props":368,"children":369},{},[370],{"type":24,"value":371},"这个项目使用 Morgan fingerprint，也就是常说的 Morgan 指纹。它和 ECFP 类指纹思想接近，会围绕每个原子观察一定半径内的局部化学环境，然后把这些结构信息编码到一个固定长度的二进制向量里。",{"type":19,"tag":20,"props":373,"children":374},{},[375],{"type":24,"value":376},"项目里默认使用：",{"type":19,"tag":45,"props":378,"children":380},{"className":47,"code":379,"language":24,"meta":7,"style":7},"radius = 2\nn_bits = 2048\n",[381],{"type":19,"tag":51,"props":382,"children":383},{"__ignoreMap":7},[384,392],{"type":19,"tag":55,"props":385,"children":386},{"class":57,"line":58},[387],{"type":19,"tag":55,"props":388,"children":389},{},[390],{"type":24,"value":391},"radius = 2\n",{"type":19,"tag":55,"props":393,"children":394},{"class":57,"line":185},[395],{"type":19,"tag":55,"props":396,"children":397},{},[398],{"type":24,"value":399},"n_bits = 2048\n",{"type":19,"tag":20,"props":401,"children":402},{},[403],{"type":24,"value":404},"也就是说，每个氨基酸最终都会变成一个长度为 2048 的 0\u002F1 向量。",{"type":19,"tag":20,"props":406,"children":407},{},[408],{"type":24,"value":409},"可以这样理解：",{"type":19,"tag":45,"props":411,"children":413},{"className":47,"code":412,"language":24,"meta":7,"style":7},"SMILES: CC(C)CC(N)C(=O)O\n\nMorgan 指纹:\n[0, 1, 0, 0, 1, 0, ..., 1, 0]\n",[414],{"type":19,"tag":51,"props":415,"children":416},{"__ignoreMap":7},[417,425,434,442],{"type":19,"tag":55,"props":418,"children":419},{"class":57,"line":58},[420],{"type":19,"tag":55,"props":421,"children":422},{},[423],{"type":24,"value":424},"SMILES: CC(C)CC(N)C(=O)O\n",{"type":19,"tag":55,"props":426,"children":427},{"class":57,"line":185},[428],{"type":19,"tag":55,"props":429,"children":431},{"emptyLinePlaceholder":430},true,[432],{"type":24,"value":433},"\n",{"type":19,"tag":55,"props":435,"children":436},{"class":57,"line":194},[437],{"type":19,"tag":55,"props":438,"children":439},{},[440],{"type":24,"value":441},"Morgan 指纹:\n",{"type":19,"tag":55,"props":443,"children":445},{"class":57,"line":444},4,[446],{"type":19,"tag":55,"props":447,"children":448},{},[449],{"type":24,"value":450},"[0, 1, 0, 0, 1, 0, ..., 1, 0]\n",{"type":19,"tag":20,"props":452,"children":453},{},[454],{"type":24,"value":455},"这个向量不是给人看的，而是给模型看的。它把“分子结构”转换成了“机器学习特征”。",{"type":19,"tag":20,"props":457,"children":458},{},[459,461,467],{"type":24,"value":460},"项目中的对应代码在 ",{"type":19,"tag":51,"props":462,"children":464},{"className":463},[],[465],{"type":24,"value":466},"src\u002Ffeatures.py",{"type":24,"value":468},"：",{"type":19,"tag":45,"props":470,"children":472},{"className":316,"code":471,"language":318,"meta":7,"style":7},"generator = rdFingerprintGenerator.GetMorganGenerator(radius=radius, fpSize=n_bits)\nfp = generator.GetFingerprint(mol)\nreturn np.array(fp, dtype=int)\n",[473],{"type":19,"tag":51,"props":474,"children":475},{"__ignoreMap":7},[476,484,492],{"type":19,"tag":55,"props":477,"children":478},{"class":57,"line":58},[479],{"type":19,"tag":55,"props":480,"children":481},{},[482],{"type":24,"value":483},"generator = rdFingerprintGenerator.GetMorganGenerator(radius=radius, fpSize=n_bits)\n",{"type":19,"tag":55,"props":485,"children":486},{"class":57,"line":185},[487],{"type":19,"tag":55,"props":488,"children":489},{},[490],{"type":24,"value":491},"fp = generator.GetFingerprint(mol)\n",{"type":19,"tag":55,"props":493,"children":494},{"class":57,"line":194},[495],{"type":19,"tag":55,"props":496,"children":497},{},[498],{"type":24,"value":499},"return np.array(fp, dtype=int)\n",{"type":19,"tag":20,"props":501,"children":502},{},[503],{"type":24,"value":504},"这里有一个关键点：Morgan 指纹不是神经网络，也不是预测模型。它只是特征工程的一部分。真正做分类的是后面的 MLP。",{"type":19,"tag":70,"props":506,"children":508},{"id":507},"mlp最经典的前馈神经网络",[509],{"type":24,"value":510},"MLP：最经典的前馈神经网络",{"type":19,"tag":20,"props":512,"children":513},{},[514],{"type":24,"value":515},"MLP，全称 Multi-Layer Perceptron，多层感知机。它是最经典的前馈神经网络之一。",{"type":19,"tag":20,"props":517,"children":518},{},[519],{"type":24,"value":520},"它的基本结构可以简单理解为：",{"type":19,"tag":45,"props":522,"children":524},{"className":47,"code":523,"language":24,"meta":7,"style":7},"输入层 -> 隐藏层 -> 激活函数 -> 隐藏层 -> 输出层\n",[525],{"type":19,"tag":51,"props":526,"children":527},{"__ignoreMap":7},[528],{"type":19,"tag":55,"props":529,"children":530},{"class":57,"line":58},[531],{"type":19,"tag":55,"props":532,"children":533},{},[534],{"type":24,"value":523},{"type":19,"tag":20,"props":536,"children":537},{},[538],{"type":24,"value":539},"每一层都在做类似这样的事情：",{"type":19,"tag":45,"props":541,"children":543},{"className":47,"code":542,"language":24,"meta":7,"style":7},"加权求和 -> 加上偏置 -> 经过激活函数 -> 传给下一层\n",[544],{"type":19,"tag":51,"props":545,"children":546},{"__ignoreMap":7},[547],{"type":19,"tag":55,"props":548,"children":549},{"class":57,"line":58},[550],{"type":19,"tag":55,"props":551,"children":552},{},[553],{"type":24,"value":542},{"type":19,"tag":20,"props":555,"children":556},{},[557],{"type":24,"value":558},"如果没有激活函数，多层线性变换叠在一起本质上仍然只是线性模型。加入 ReLU、Sigmoid、Tanh 这样的非线性激活函数后，模型才有能力学习更复杂的非线性关系。",{"type":19,"tag":20,"props":560,"children":561},{},[562],{"type":24,"value":563},"在这个项目里，MLP 的输入是 2048 维 Morgan 指纹，输出是一个二分类结果：",{"type":19,"tag":45,"props":565,"children":567},{"className":47,"code":566,"language":24,"meta":7,"style":7},"0 = 亲水\n1 = 疏水\n",[568],{"type":19,"tag":51,"props":569,"children":570},{"__ignoreMap":7},[571,578],{"type":19,"tag":55,"props":572,"children":573},{"class":57,"line":58},[574],{"type":19,"tag":55,"props":575,"children":576},{},[577],{"type":24,"value":246},{"type":19,"tag":55,"props":579,"children":580},{"class":57,"line":185},[581],{"type":19,"tag":55,"props":582,"children":583},{},[584],{"type":24,"value":238},{"type":19,"tag":20,"props":586,"children":587},{},[588,590,596],{"type":24,"value":589},"模型定义在 ",{"type":19,"tag":51,"props":591,"children":593},{"className":592},[],[594],{"type":24,"value":595},"src\u002Fmodel.py",{"type":24,"value":468},{"type":19,"tag":45,"props":598,"children":600},{"className":316,"code":599,"language":318,"meta":7,"style":7},"model = MLPClassifier(\n    hidden_layer_sizes=(128, 64),\n    activation=\"relu\",\n    solver=\"adam\",\n    alpha=0.001,\n    max_iter=1000,\n    random_state=42,\n    early_stopping=True,\n    validation_fraction=0.2,\n    n_iter_no_change=20,\n)\n",[601],{"type":19,"tag":51,"props":602,"children":603},{"__ignoreMap":7},[604,612,620,628,636,645,654,663,672,681,690],{"type":19,"tag":55,"props":605,"children":606},{"class":57,"line":58},[607],{"type":19,"tag":55,"props":608,"children":609},{},[610],{"type":24,"value":611},"model = MLPClassifier(\n",{"type":19,"tag":55,"props":613,"children":614},{"class":57,"line":185},[615],{"type":19,"tag":55,"props":616,"children":617},{},[618],{"type":24,"value":619},"    hidden_layer_sizes=(128, 64),\n",{"type":19,"tag":55,"props":621,"children":622},{"class":57,"line":194},[623],{"type":19,"tag":55,"props":624,"children":625},{},[626],{"type":24,"value":627},"    activation=\"relu\",\n",{"type":19,"tag":55,"props":629,"children":630},{"class":57,"line":444},[631],{"type":19,"tag":55,"props":632,"children":633},{},[634],{"type":24,"value":635},"    solver=\"adam\",\n",{"type":19,"tag":55,"props":637,"children":639},{"class":57,"line":638},5,[640],{"type":19,"tag":55,"props":641,"children":642},{},[643],{"type":24,"value":644},"    alpha=0.001,\n",{"type":19,"tag":55,"props":646,"children":648},{"class":57,"line":647},6,[649],{"type":19,"tag":55,"props":650,"children":651},{},[652],{"type":24,"value":653},"    max_iter=1000,\n",{"type":19,"tag":55,"props":655,"children":657},{"class":57,"line":656},7,[658],{"type":19,"tag":55,"props":659,"children":660},{},[661],{"type":24,"value":662},"    random_state=42,\n",{"type":19,"tag":55,"props":664,"children":666},{"class":57,"line":665},8,[667],{"type":19,"tag":55,"props":668,"children":669},{},[670],{"type":24,"value":671},"    early_stopping=True,\n",{"type":19,"tag":55,"props":673,"children":675},{"class":57,"line":674},9,[676],{"type":19,"tag":55,"props":677,"children":678},{},[679],{"type":24,"value":680},"    validation_fraction=0.2,\n",{"type":19,"tag":55,"props":682,"children":684},{"class":57,"line":683},10,[685],{"type":19,"tag":55,"props":686,"children":687},{},[688],{"type":24,"value":689},"    n_iter_no_change=20,\n",{"type":19,"tag":55,"props":691,"children":693},{"class":57,"line":692},11,[694],{"type":19,"tag":55,"props":695,"children":696},{},[697],{"type":24,"value":698},")\n",{"type":19,"tag":20,"props":700,"children":701},{},[702,704,710],{"type":24,"value":703},"这里用了两层隐藏层，分别有 128 和 64 个神经元。",{"type":19,"tag":51,"props":705,"children":707},{"className":706},[],[708],{"type":24,"value":709},"early_stopping=True",{"type":24,"value":711}," 表示如果验证集效果长时间没有提升，训练会提前停止，避免在小数据集上过度训练。",{"type":19,"tag":70,"props":713,"children":715},{"id":714},"训练流程",[716],{"type":24,"value":714},{"type":19,"tag":20,"props":718,"children":719},{},[720,722,728],{"type":24,"value":721},"训练入口在 ",{"type":19,"tag":51,"props":723,"children":725},{"className":724},[],[726],{"type":24,"value":727},"main.py",{"type":24,"value":729},"，运行：",{"type":19,"tag":45,"props":731,"children":735},{"className":732,"code":733,"language":734,"meta":7,"style":7},"language-bash shiki shiki-themes github-dark","python main.py train\n","bash",[736],{"type":19,"tag":51,"props":737,"children":738},{"__ignoreMap":7},[739],{"type":19,"tag":55,"props":740,"children":741},{"class":57,"line":58},[742,747,753],{"type":19,"tag":55,"props":743,"children":745},{"style":744},"--shiki-default:#B392F0",[746],{"type":24,"value":318},{"type":19,"tag":55,"props":748,"children":750},{"style":749},"--shiki-default:#9ECBFF",[751],{"type":24,"value":752}," main.py",{"type":19,"tag":55,"props":754,"children":755},{"style":749},[756],{"type":24,"value":757}," train\n",{"type":19,"tag":20,"props":759,"children":760},{},[761],{"type":24,"value":762},"完整训练流程如下：",{"type":19,"tag":764,"props":765,"children":766},"ol",{},[767,779,791,803,823,836,848,853],{"type":19,"tag":85,"props":768,"children":769},{},[770,772,777],{"type":24,"value":771},"从 ",{"type":19,"tag":51,"props":773,"children":775},{"className":774},[],[776],{"type":24,"value":216},{"type":24,"value":778}," 读取 20 个氨基酸。",{"type":19,"tag":85,"props":780,"children":781},{},[782,784,789],{"type":24,"value":783},"每个 SMILES 先通过 RDKit 转成 ",{"type":19,"tag":51,"props":785,"children":787},{"className":786},[],[788],{"type":24,"value":296},{"type":24,"value":790},"。",{"type":19,"tag":85,"props":792,"children":793},{},[794,796,801],{"type":24,"value":795},"每个 ",{"type":19,"tag":51,"props":797,"children":799},{"className":798},[],[800],{"type":24,"value":296},{"type":24,"value":802}," 再转成 2048 维 Morgan 指纹。",{"type":19,"tag":85,"props":804,"children":805},{},[806,808,814,816,822],{"type":24,"value":807},"得到特征矩阵 ",{"type":19,"tag":51,"props":809,"children":811},{"className":810},[],[812],{"type":24,"value":813},"X",{"type":24,"value":815}," 和标签数组 ",{"type":19,"tag":51,"props":817,"children":819},{"className":818},[],[820],{"type":24,"value":821},"y",{"type":24,"value":790},{"type":19,"tag":85,"props":824,"children":825},{},[826,828,834],{"type":24,"value":827},"使用 ",{"type":19,"tag":51,"props":829,"children":831},{"className":830},[],[832],{"type":24,"value":833},"train_test_split(..., stratify=y)",{"type":24,"value":835}," 切分训练集和测试集。",{"type":19,"tag":85,"props":837,"children":838},{},[839,841,847],{"type":24,"value":840},"训练 ",{"type":19,"tag":51,"props":842,"children":844},{"className":843},[],[845],{"type":24,"value":846},"MLPClassifier",{"type":24,"value":790},{"type":19,"tag":85,"props":849,"children":850},{},[851],{"type":24,"value":852},"打印准确率、分类报告和混淆矩阵。",{"type":19,"tag":85,"props":854,"children":855},{},[856,857,863,865,871],{"type":24,"value":827},{"type":19,"tag":51,"props":858,"children":860},{"className":859},[],[861],{"type":24,"value":862},"joblib",{"type":24,"value":864}," 保存模型到 ",{"type":19,"tag":51,"props":866,"children":868},{"className":867},[],[869],{"type":24,"value":870},"models\u002Fmlp_classifier.pkl",{"type":24,"value":790},{"type":19,"tag":20,"props":873,"children":874},{},[875,877,883,885,890],{"type":24,"value":876},"其中 ",{"type":19,"tag":51,"props":878,"children":880},{"className":879},[],[881],{"type":24,"value":882},"stratify=y",{"type":24,"value":884}," 是一个小但重要的细节。因为数据集很小，只有 20 条。如果随机切分时某一类样本在测试集里太少，评估结果会非常不稳定。",{"type":19,"tag":51,"props":886,"children":888},{"className":887},[],[889],{"type":24,"value":882},{"type":24,"value":891}," 可以尽量保证训练集和测试集中的亲水\u002F疏水比例接近原始数据。",{"type":19,"tag":20,"props":893,"children":894},{},[895],{"type":24,"value":896},"训练时，项目还做了 5 折交叉验证。交叉验证会重复多次切分训练集和验证集，比单次切分更能反映模型在小数据集上的波动。",{"type":19,"tag":70,"props":898,"children":900},{"id":899},"实验结果",[901],{"type":24,"value":899},{"type":19,"tag":20,"props":903,"children":904},{},[905],{"type":24,"value":906},"在当前数据和默认参数下，我运行了一次训练，得到结果如下：",{"type":19,"tag":45,"props":908,"children":910},{"className":47,"code":909,"language":24,"meta":7,"style":7},"数据集大小: 20 个氨基酸\n疏水性氨基酸: 9 个\n亲水性氨基酸: 11 个\n\n5 折交叉验证准确率: 0.5000 (+\u002F- 0.1581)\n测试集准确率: 0.6000\n\n混淆矩阵:\n[[2 1]\n [1 1]]\n",[911],{"type":19,"tag":51,"props":912,"children":913},{"__ignoreMap":7},[914,922,930,938,945,953,961,968,976,984],{"type":19,"tag":55,"props":915,"children":916},{"class":57,"line":58},[917],{"type":19,"tag":55,"props":918,"children":919},{},[920],{"type":24,"value":921},"数据集大小: 20 个氨基酸\n",{"type":19,"tag":55,"props":923,"children":924},{"class":57,"line":185},[925],{"type":19,"tag":55,"props":926,"children":927},{},[928],{"type":24,"value":929},"疏水性氨基酸: 9 个\n",{"type":19,"tag":55,"props":931,"children":932},{"class":57,"line":194},[933],{"type":19,"tag":55,"props":934,"children":935},{},[936],{"type":24,"value":937},"亲水性氨基酸: 11 个\n",{"type":19,"tag":55,"props":939,"children":940},{"class":57,"line":444},[941],{"type":19,"tag":55,"props":942,"children":943},{"emptyLinePlaceholder":430},[944],{"type":24,"value":433},{"type":19,"tag":55,"props":946,"children":947},{"class":57,"line":638},[948],{"type":19,"tag":55,"props":949,"children":950},{},[951],{"type":24,"value":952},"5 折交叉验证准确率: 0.5000 (+\u002F- 0.1581)\n",{"type":19,"tag":55,"props":954,"children":955},{"class":57,"line":647},[956],{"type":19,"tag":55,"props":957,"children":958},{},[959],{"type":24,"value":960},"测试集准确率: 0.6000\n",{"type":19,"tag":55,"props":962,"children":963},{"class":57,"line":656},[964],{"type":19,"tag":55,"props":965,"children":966},{"emptyLinePlaceholder":430},[967],{"type":24,"value":433},{"type":19,"tag":55,"props":969,"children":970},{"class":57,"line":665},[971],{"type":19,"tag":55,"props":972,"children":973},{},[974],{"type":24,"value":975},"混淆矩阵:\n",{"type":19,"tag":55,"props":977,"children":978},{"class":57,"line":674},[979],{"type":19,"tag":55,"props":980,"children":981},{},[982],{"type":24,"value":983},"[[2 1]\n",{"type":19,"tag":55,"props":985,"children":986},{"class":57,"line":683},[987],{"type":19,"tag":55,"props":988,"children":989},{},[990],{"type":24,"value":991}," [1 1]]\n",{"type":19,"tag":20,"props":993,"children":994},{},[995],{"type":24,"value":996},"这个结果并不惊艳，甚至可以说很朴素。但这正是这个项目值得写出来的地方：数据只有 20 条，模型还使用了 2048 维稀疏指纹，这在机器学习里是典型的“小样本、高维特征”场景。",{"type":19,"tag":20,"props":998,"children":999},{},[1000],{"type":24,"value":1001},"在这种情况下，模型很容易受到训练\u002F测试划分影响。一次测试集准确率 0.60，并不能说明模型已经学到了稳定、可泛化的化学规律。它更多说明：这条技术链路可以跑通，模型能基于分子指纹做出一个可解释的二分类输出。",{"type":19,"tag":20,"props":1003,"children":1004},{},[1005],{"type":24,"value":1006},"如果要做成更严肃的性质预测模型，需要更多数据、更稳健的评估方式，以及更仔细的特征和模型选择。",{"type":19,"tag":70,"props":1008,"children":1010},{"id":1009},"预测流程",[1011],{"type":24,"value":1009},{"type":19,"tag":20,"props":1013,"children":1014},{},[1015],{"type":24,"value":1016},"训练完成后，模型会保存为：",{"type":19,"tag":45,"props":1018,"children":1020},{"className":47,"code":1019,"language":24,"meta":7,"style":7},"models\u002Fmlp_classifier.pkl\n",[1021],{"type":19,"tag":51,"props":1022,"children":1023},{"__ignoreMap":7},[1024],{"type":19,"tag":55,"props":1025,"children":1026},{"class":57,"line":58},[1027],{"type":19,"tag":55,"props":1028,"children":1029},{},[1030],{"type":24,"value":1019},{"type":19,"tag":20,"props":1032,"children":1033},{},[1034],{"type":24,"value":1035},"预测时的流程和训练时保持一致：",{"type":19,"tag":764,"props":1037,"children":1038},{},[1039,1044,1049,1060,1065],{"type":19,"tag":85,"props":1040,"children":1041},{},[1042],{"type":24,"value":1043},"读取已经保存的模型。",{"type":19,"tag":85,"props":1045,"children":1046},{},[1047],{"type":24,"value":1048},"输入一个新的 SMILES 字符串。",{"type":19,"tag":85,"props":1050,"children":1051},{},[1052,1054,1059],{"type":24,"value":1053},"用 RDKit 解析成 ",{"type":19,"tag":51,"props":1055,"children":1057},{"className":1056},[],[1058],{"type":24,"value":296},{"type":24,"value":790},{"type":19,"tag":85,"props":1061,"children":1062},{},[1063],{"type":24,"value":1064},"转成同样参数的 Morgan 指纹。",{"type":19,"tag":85,"props":1066,"children":1067},{},[1068],{"type":24,"value":1069},"输入 MLP，得到类别和概率。",{"type":19,"tag":20,"props":1071,"children":1072},{},[1073],{"type":24,"value":1074},"运行演示：",{"type":19,"tag":45,"props":1076,"children":1078},{"className":732,"code":1077,"language":734,"meta":7,"style":7},"python main.py demo\n",[1079],{"type":19,"tag":51,"props":1080,"children":1081},{"__ignoreMap":7},[1082],{"type":19,"tag":55,"props":1083,"children":1084},{"class":57,"line":58},[1085,1089,1093],{"type":19,"tag":55,"props":1086,"children":1087},{"style":744},[1088],{"type":24,"value":318},{"type":19,"tag":55,"props":1090,"children":1091},{"style":749},[1092],{"type":24,"value":752},{"type":19,"tag":55,"props":1094,"children":1095},{"style":749},[1096],{"type":24,"value":1097}," demo\n",{"type":19,"tag":20,"props":1099,"children":1100},{},[1101],{"type":24,"value":1102},"也可以进入交互模式：",{"type":19,"tag":45,"props":1104,"children":1106},{"className":732,"code":1105,"language":734,"meta":7,"style":7},"python main.py interactive\n",[1107],{"type":19,"tag":51,"props":1108,"children":1109},{"__ignoreMap":7},[1110],{"type":19,"tag":55,"props":1111,"children":1112},{"class":57,"line":58},[1113,1117,1121],{"type":19,"tag":55,"props":1114,"children":1115},{"style":744},[1116],{"type":24,"value":318},{"type":19,"tag":55,"props":1118,"children":1119},{"style":749},[1120],{"type":24,"value":752},{"type":19,"tag":55,"props":1122,"children":1123},{"style":749},[1124],{"type":24,"value":1125}," interactive\n",{"type":19,"tag":20,"props":1127,"children":1128},{},[1129,1131,1137],{"type":24,"value":1130},"预测函数在 ",{"type":19,"tag":51,"props":1132,"children":1134},{"className":1133},[],[1135],{"type":24,"value":1136},"src\u002Fpredict.py",{"type":24,"value":1138},"，返回结果包括：",{"type":19,"tag":45,"props":1140,"children":1142},{"className":316,"code":1141,"language":318,"meta":7,"style":7},"{\n    \"smiles\": smiles,\n    \"prediction\": 0 or 1,\n    \"label\": \"亲水\" or \"疏水\",\n    \"confidence\": confidence,\n    \"hydrophobic_prob\": hydrophobic_prob,\n    \"hydrophilic_prob\": hydrophilic_prob,\n}\n",[1143],{"type":19,"tag":51,"props":1144,"children":1145},{"__ignoreMap":7},[1146,1154,1162,1170,1178,1186,1194,1202],{"type":19,"tag":55,"props":1147,"children":1148},{"class":57,"line":58},[1149],{"type":19,"tag":55,"props":1150,"children":1151},{},[1152],{"type":24,"value":1153},"{\n",{"type":19,"tag":55,"props":1155,"children":1156},{"class":57,"line":185},[1157],{"type":19,"tag":55,"props":1158,"children":1159},{},[1160],{"type":24,"value":1161},"    \"smiles\": smiles,\n",{"type":19,"tag":55,"props":1163,"children":1164},{"class":57,"line":194},[1165],{"type":19,"tag":55,"props":1166,"children":1167},{},[1168],{"type":24,"value":1169},"    \"prediction\": 0 or 1,\n",{"type":19,"tag":55,"props":1171,"children":1172},{"class":57,"line":444},[1173],{"type":19,"tag":55,"props":1174,"children":1175},{},[1176],{"type":24,"value":1177},"    \"label\": \"亲水\" or \"疏水\",\n",{"type":19,"tag":55,"props":1179,"children":1180},{"class":57,"line":638},[1181],{"type":19,"tag":55,"props":1182,"children":1183},{},[1184],{"type":24,"value":1185},"    \"confidence\": confidence,\n",{"type":19,"tag":55,"props":1187,"children":1188},{"class":57,"line":647},[1189],{"type":19,"tag":55,"props":1190,"children":1191},{},[1192],{"type":24,"value":1193},"    \"hydrophobic_prob\": hydrophobic_prob,\n",{"type":19,"tag":55,"props":1195,"children":1196},{"class":57,"line":656},[1197],{"type":19,"tag":55,"props":1198,"children":1199},{},[1200],{"type":24,"value":1201},"    \"hydrophilic_prob\": hydrophilic_prob,\n",{"type":19,"tag":55,"props":1203,"children":1204},{"class":57,"line":665},[1205],{"type":19,"tag":55,"props":1206,"children":1207},{},[1208],{"type":24,"value":1209},"}\n",{"type":19,"tag":20,"props":1211,"children":1212},{},[1213,1215,1221],{"type":24,"value":1214},"这里的 ",{"type":19,"tag":51,"props":1216,"children":1218},{"className":1217},[],[1219],{"type":24,"value":1220},"confidence",{"type":24,"value":1222}," 本质上是模型对预测类别的概率估计。它可以作为参考，但在这么小的数据集上，不能把它理解成严格可靠的化学置信度。",{"type":19,"tag":70,"props":1224,"children":1226},{"id":1225},"这个项目真正学到什么",[1227],{"type":24,"value":1225},{"type":19,"tag":20,"props":1229,"children":1230},{},[1231],{"type":24,"value":1232},"我觉得这个项目的价值不在于“用 MLP 精准预测氨基酸亲疏水性”，而在于把 AI 制药中一个常见问题拆成了几个清楚的层次：",{"type":19,"tag":45,"props":1234,"children":1236},{"className":47,"code":1235,"language":24,"meta":7,"style":7},"化学对象 -> 机器可读表示 -> 数值特征 -> 机器学习模型 -> 性质预测\n",[1237],{"type":19,"tag":51,"props":1238,"children":1239},{"__ignoreMap":7},[1240],{"type":19,"tag":55,"props":1241,"children":1242},{"class":57,"line":58},[1243],{"type":19,"tag":55,"props":1244,"children":1245},{},[1246],{"type":24,"value":1235},{"type":19,"tag":20,"props":1248,"children":1249},{},[1250],{"type":24,"value":1251},"更具体地说，它帮助我理解了：",{"type":19,"tag":81,"props":1253,"children":1254},{},[1255,1260,1265,1275,1280,1285,1290],{"type":19,"tag":85,"props":1256,"children":1257},{},[1258],{"type":24,"value":1259},"SMILES 是分子的文本表示，不是模型特征本身。",{"type":19,"tag":85,"props":1261,"children":1262},{},[1263],{"type":24,"value":1264},"RDKit 是化学信息学工具箱，不是神经网络分类器。",{"type":19,"tag":85,"props":1266,"children":1267},{},[1268,1273],{"type":19,"tag":51,"props":1269,"children":1271},{"className":1270},[],[1272],{"type":24,"value":296},{"type":24,"value":1274}," 是 RDKit 内部的结构化分子对象。",{"type":19,"tag":85,"props":1276,"children":1277},{},[1278],{"type":24,"value":1279},"Morgan 指纹把分子局部结构编码成固定长度向量。",{"type":19,"tag":85,"props":1281,"children":1282},{},[1283],{"type":24,"value":1284},"MLP 接收的是数值向量，而不是原始化学名称或 SMILES。",{"type":19,"tag":85,"props":1286,"children":1287},{},[1288],{"type":24,"value":1289},"亲疏水标签是人为定义的数据监督信号，模型是在学习这个标签规则。",{"type":19,"tag":85,"props":1291,"children":1292},{},[1293],{"type":24,"value":1294},"小数据集上的准确率很容易波动，不能过度解读。",{"type":19,"tag":20,"props":1296,"children":1297},{},[1298],{"type":24,"value":1299},"如果把这个项目看成一个“分子机器学习 Hello World”，它已经足够完整。",{"type":19,"tag":70,"props":1301,"children":1303},{"id":1302},"局限性",[1304],{"type":24,"value":1302},{"type":19,"tag":20,"props":1306,"children":1307},{},[1308],{"type":24,"value":1309},"这个项目也有明显局限。",{"type":19,"tag":20,"props":1311,"children":1312},{},[1313],{"type":24,"value":1314},"第一，数据量太小。20 个样本无法支撑复杂神经网络稳定学习，尤其输入还是 2048 维指纹。",{"type":19,"tag":20,"props":1316,"children":1317},{},[1318],{"type":24,"value":1319},"第二，亲疏水标签本身是简化的。真实分子的亲疏水性通常会用 logP、logD、溶解度、极性表面积等更连续、更细致的指标描述，而不是简单二分类。",{"type":19,"tag":20,"props":1321,"children":1322},{},[1323],{"type":24,"value":1324},"第三，氨基酸在不同 pH 条件下会有不同电荷状态。当前 SMILES 和标签没有显式考虑环境 pH、电离状态、两性离子形式等因素。",{"type":19,"tag":20,"props":1326,"children":1327},{},[1328],{"type":24,"value":1329},"第四，Morgan 指纹虽然经典，但它会压缩分子信息，也可能发生哈希碰撞。对于更复杂任务，可以尝试更多分子描述符、图神经网络或预训练分子模型。",{"type":19,"tag":20,"props":1331,"children":1332},{},[1333],{"type":24,"value":1334},"第五，模型评估还很初级。当前的交叉验证和测试集评估适合教学演示，但不适合宣称真实性能。",{"type":19,"tag":70,"props":1336,"children":1338},{"id":1337},"可以怎么继续优化",[1339],{"type":24,"value":1337},{"type":19,"tag":20,"props":1341,"children":1342},{},[1343],{"type":24,"value":1344},"如果继续扩展这个项目，我会优先考虑这些方向：",{"type":19,"tag":764,"props":1346,"children":1347},{},[1348,1353,1358,1363,1368,1373,1378],{"type":19,"tag":85,"props":1349,"children":1350},{},[1351],{"type":24,"value":1352},"增加数据量，引入更多天然或人工氨基酸及其性质数据。",{"type":19,"tag":85,"props":1354,"children":1355},{},[1356],{"type":24,"value":1357},"把二分类标签换成连续性质，例如 logP 或实验溶解度。",{"type":19,"tag":85,"props":1359,"children":1360},{},[1361],{"type":24,"value":1362},"加入更多 RDKit 分子描述符，例如分子量、TPSA、氢键供体\u002F受体数量、可旋转键数量。",{"type":19,"tag":85,"props":1364,"children":1365},{},[1366],{"type":24,"value":1367},"对比不同模型，例如 Logistic Regression、Random Forest、SVM、XGBoost。",{"type":19,"tag":85,"props":1369,"children":1370},{},[1371],{"type":24,"value":1372},"使用留一法交叉验证，适配 20 条样本这种极小数据集。",{"type":19,"tag":85,"props":1374,"children":1375},{},[1376],{"type":24,"value":1377},"做一个简单 Web 页面，让用户输入 SMILES 后实时看到预测结果。",{"type":19,"tag":85,"props":1379,"children":1380},{},[1381],{"type":24,"value":1382},"增加非法 SMILES 检查和可视化分子结构展示。",{"type":19,"tag":20,"props":1384,"children":1385},{},[1386],{"type":24,"value":1387},"这些扩展会让项目从“跑通流程”逐渐接近一个可展示、可解释、可交互的小型 AI 制药工具。",{"type":19,"tag":70,"props":1389,"children":1391},{"id":1390},"总结",[1392],{"type":24,"value":1390},{"type":19,"tag":20,"props":1394,"children":1395},{},[1396],{"type":24,"value":1397},"AminoHydro Classifier 是一个很小的项目，但它覆盖了分子机器学习中最基础也最重要的一条链路：",{"type":19,"tag":45,"props":1399,"children":1401},{"className":47,"code":1400,"language":24,"meta":7,"style":7},"分子字符串 -> 化学结构 -> 分子指纹 -> 神经网络 -> 性质预测\n",[1402],{"type":19,"tag":51,"props":1403,"children":1404},{"__ignoreMap":7},[1405],{"type":19,"tag":55,"props":1406,"children":1407},{"class":57,"line":58},[1408],{"type":19,"tag":55,"props":1409,"children":1410},{},[1411],{"type":24,"value":1400},{"type":19,"tag":20,"props":1413,"children":1414},{},[1415],{"type":24,"value":1416},"通过它，我更清楚地理解了 RDKit 和机器学习模型之间的分工：RDKit 负责把化学结构变成可计算特征，MLP 负责从特征中学习分类边界。",{"type":19,"tag":20,"props":1418,"children":1419},{},[1420],{"type":24,"value":1421},"这个项目目前不适合作为严肃的药物性质预测模型，但非常适合作为 AI 制药入门练习。它足够小，小到每一步都能看懂；又足够完整，完整到可以从数据、训练、评估一直走到预测。",{"type":19,"tag":20,"props":1423,"children":1424},{},[1425],{"type":24,"value":1426},"对我来说，这正是学习 AI 制药很好的起点：不是一上来追逐巨大的模型，而是先把一条最小的分子机器学习流水线亲手跑通。",{"type":19,"tag":1428,"props":1429,"children":1430},"style",{},[1431],{"type":24,"value":1432},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":185,"depth":185,"links":1434},[1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446],{"id":72,"depth":185,"text":72},{"id":154,"depth":185,"text":157},{"id":254,"depth":185,"text":257},{"id":363,"depth":185,"text":366},{"id":507,"depth":185,"text":510},{"id":714,"depth":185,"text":714},{"id":899,"depth":185,"text":899},{"id":1009,"depth":185,"text":1009},{"id":1225,"depth":185,"text":1225},{"id":1302,"depth":185,"text":1302},{"id":1337,"depth":185,"text":1337},{"id":1390,"depth":185,"text":1390},"markdown","content:articles:ai:amino-hydro-classifier.md","content","articles\u002Fai\u002Famino-hydro-classifier.md","articles\u002Fai\u002Famino-hydro-classifier","md",1779811689337]