Дескрипторы заряда (признаки) для предсказания молекулярных свойств.
В этом посте я пытаюсь исследовать отпечатки дескрипторов заряда, как это можно назвать. Дэвид Винклер в 2009 году опубликовал статью об этом Towards Novel Universal Descriptors: Charge Fingerprints. Это выглядит очень интересно и может дать представление об электростатических свойствах молекул, которые играют важную роль в молекулярных взаимодействиях и сродстве к связыванию. Кодируя частичные заряды атомов, отпечатки пальцев могут помочь в сравнении сходств и различий между молекулами на основе их распределения заряда, а также могут помочь в прогнозировании активности соединений в биологических системах или их физико-химических свойств.
Вычисление отпечатков заряда обычно включает два основных этапа. Во-первых, парциальные заряды атомов в молекуле вычисляются с использованием модели заряда, такой как Gasteiger или MMFF94 в открытом вавилоне. Существует несколько способов вычисления. Еще одним пакетом квантовой химии является psi4, который можно использовать для расчета зарядов Малликена для каждого атома. Эти модели основаны на различных приближениях и эмпирических правилах, полученных из расчетов квантовой химии и экспериментальных данных. Такие методы, как метод выравнивания заряда Гастайгера, а также более современный метод выравнивания электроотрицательности (EEM), основанный на уравнении Сандерсона
. Другие методы, такие как полуэмпирические методы молекулярных орбиталей, DFT или методы ab initio, также могут использоваться для расчета зарядов атомов, если границы ячеек установлены соответствующим образом. Однако я не пытался воспроизвести бумагу, но попытался использовать идею для создания отпечатков пальцев.
После получения частичных зарядов их можно закодировать в отпечаток пальца, который обычно представляет собой двоичный вектор фиксированной длины. Обычный подход к созданию отпечатка пальца заключается в дискретизации значений заряда по ячейкам и назначении каждого атома определенной ячейке. Это приводит к разреженному двоичному вектору, где каждый элемент соответствует определенной комбинации атома и ячейки заряда. Наличие «1» в определенном положении в векторе указывает на то, что соответствующий атом имеет частичный заряд в пределах диапазона соответствующего бина. Сравнивая зарядовые отпечатки разных молекул, можно оценить их сходство с точки зрения электростатических свойств, что важно для различных задач химико-информатики, таких как виртуальный скрининг, поиск сходства и предсказание свойств.
Код ниже показывает, как вы можете сгенерировать эти отпечатки пальцев с помощью принудительного поля mmff94.
import numpy as np import openbabel as ob from openbabel import openbabel def tanimoto_similarity(fp1, fp2): common_bits = np.bitwise_and(fp1, fp2).sum() total_bits = np.bitwise_or(fp1, fp2).sum() return common_bits / total_bits def generate_charge_fingerprint(smiles, n_bits=2048, bin_min=-1.0, bin_max=1.0, nbins=32): # Initialize Open Babel objects ob_conversion = ob.OBConversion() ob_conversion.SetInFormat("smi") ob_mol = ob.OBMol() # Convert SMILES to Open Babel molecule ob_conversion.ReadString(ob_mol, smiles) ob_mol.AddHydrogens() ob_charge_model = ob.OBChargeModel.FindType("mmff94") ob_charge_model.ComputeCharges(ob_mol) charges = [ob_mol.GetAtom(i+1).GetPartialCharge() for i in range(ob_mol.NumAtoms())] # Initialize the fingerprint vector fingerprint = np.zeros(n_bits, dtype=np.uint8) # Create bins for the partial charges bins = np.linspace(bin_min, bin_max, nbins + 1) # Set the corresponding bits for each atom's partial charge for idx, charge in enumerate(charges): bin_index = np.digitize(charge, bins) - 1 bit_index = idx * nbins + bin_index if bit_index < n_bits: fingerprint[bit_index] = 1 return fingerprint
Добавление атомов водорода в молекулярную структуру перед расчетом дескрипторов заряда важно, поскольку атомы водорода играют важную роль в распределении зарядов внутри молекулы. Большинство молекулярных представлений, таких как SMILES или SDF, явно не включают атомы водорода, поскольку они часто опускаются для краткости и простоты. Однако атомы водорода участвуют в различных химических взаимодействиях, таких как образование водородных связей и протонирование/депротонирование, которые могут существенно влиять на распределение заряда молекулы и ее физико-химические свойства. При расчете дескрипторов заряда базовые модели заряда, такие как Gasteiger или MMFF94, нуждаются в точной информации о молекулярной структуре, чтобы обеспечить надежные оценки частичного заряда. Явно добавляя атомы водорода в молекулу, вы гарантируете, что модели заряда учитывают правильную среду связывания каждого атома, что приводит к более точным дескрипторам заряда.
Затем следующая часть довольно проста, когда вы получаете отпечатки пальцев и видите, имеют ли эти отпечатки смысл или нет, обучая модель. Я использовал здесь xgboost с 5-кратным CV. Набор данных, который меня интересовал, был herg, который я рассмотрел в сравнительном исследовании tdc. Однако я мало изучал другие наборы данных, но результаты с этим набором данных выглядят так, будто в этом дескрипторе что-то есть. Среднее значение AUC составило около ROC-AUC: 0,7929.
import numpy as np import xgboost as xgb from sklearn.model_selection import KFold from sklearn import metrics from sklearn.metrics import roc_auc_score, confusion_matrix from tdc.single_pred import Tox data = Tox(name = 'hERG') split = data.get_split() train,test = split['train'],split['test'] smiles_list = train['Drug'].tolist() y = train['Y'].values # Generate charge fingerprints for each molecule in the dataset fingerprints = np.array([generate_charge_fingerprint(smiles,n_bits=2048, nbins=32) for smiles in smiles_list]) # Generate charge fingerprints for each molecule in the dataset #fingerprints = np.array([generate_charge_fingerprint(smiles) for smiles in smiles_list]) # Cross-validation parameters n_splits = 5 kf = KFold(n_splits=n_splits, shuffle=True, random_state=123) params = { 'colsample_bynode': 0.8, 'learning_rate': 0.01, 'max_depth': 12, 'alpha':0.5, 'lambda':0.5, 'min_child_weight':1, 'num_parallel_tree': 100, 'objective': 'binary:logistic', 'subsample': 0.8, 'scale_pos_weight':1, 'tree_method':'hist', 'eval_metric':['auc','error'], 'n_jobs': -1, 'random_state': 123} # Perform cross-validation roc_auc_scores = [] for train_index, test_index in kf.split(fingerprints): X_train, X_test = fingerprints[train_index], fingerprints[test_index] y_train, y_test = y[train_index], y[test_index] dtrain = xgb.DMatrix(X_train, label=y_train) dtest = xgb.DMatrix(X_test, label=y_test) bst = xgb.train(params, dtrain, num_boost_round=100, early_stopping_rounds=10, evals=[(dtest, 'test')]) y_pred_proba = bst.predict(dtest) y_pred = y_pred_proba > 0.5 roc_auc = roc_auc_score(y_test, y_pred_proba) roc_auc_scores.append(roc_auc) # Calculate the average ROC-AUC score average_roc_auc = np.mean(roc_auc_scores) print(f"Average ROC-AUC: {average_roc_auc:.4f}") # Train the model on the full dataset dtrain_full = xgb.DMatrix(fingerprints, label=y) bst_full = xgb.train(params, dtrain_full, num_boost_round=100)
Результаты тестов, которые я тестировал, приведены ниже, похоже, что эти функции могут быть ценным способом их использования в моделях.
from sklearn.metrics import precision_score, recall_score, f1_score, accuracy_score test = split['test'] test_list = test['Drug'].tolist() y_test = test['Y'].values # # Generate charge fingerprints for each molecule in the dataset test_fp = np.array([generate_charge_fingerprint(smiles,n_bits=2048, nbins=32) for smiles in test_list]) test = xgb.DMatrix(test_fp, label=y_test) y_pred_proba = bst_full.predict(test) y_pred = y_pred_proba > 0.5 roc_auc = roc_auc_score(y_test, y_pred_proba) confusion_matrix(y_test, y_pred) print('Precision: %.3f' % precision_score(y_test, y_pred)) print('Recall: %.3f' % recall_score(y_test, y_pred)) print('Accuracy: %.3f' % accuracy_score(y_test, y_pred)) print('F1 Score: %.3f' % f1_score(y_test, y_pred))
Точность: 0,839 Отзыв: 0,959 Точность: 0,832 Оценка F1: 0,895
Зарядовые отпечатки могут быть очень ценными при моделировании различных молекулярных свойств и активности, поскольку они дают представление об электростатическом поведении соединений, которое является ключевым фактором во многих химических и биологических взаимодействиях. Включение информации о заряде в молекулярные модели позволяет лучше уловить нюансы молекулярного распознавания, связывания и реактивности, что приводит к более точным прогнозам и лучшему пониманию лежащих в основе молекулярных механизмов.
Пожалуйста, оставляйте комментарии, если вы считаете эту идею полезной и хотели бы узнать больше по этой теме.