В этом посте я собираюсь показать, как приступить к разработке моделей машинного обучения для предсказания молекулярных свойств. Это должен быть очень минимальный пример. Мы рассмотрим другие методы улучшения этой модели в следующих постах. Мы собираемся предсказать липофильность малых молекул. Набор данных можно скачать с сайта MoleculeNet.

Прогнозирование липофильности — это регрессионная задача, и мы собираемся использовать алгоритм Random Forest, реализованный в пакете scikit-learn. Нам также потребуется создать молекулярные признаки для каждой молекулы в наборе данных. Мы будем использовать mordred и rdkit для создания этих функций. Если у вас еще нет этих пакетов, используйте pip для их установки.

pip install mordred
pip install rdkit-pypi

Импортировать необходимые пакеты

Ниже приведен полный список импорта, который нам понадобится.

import numpy as np
import pandas as pd

import mordred
from rdkit import Chem
from mordred import Calculator, descriptors
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_squared_error
import matplotlib.pyplot as plt

Загрузите данные и создайте молекулярные функции

Первым шагом является загрузка данных. Перейдите на этот веб-сайт MoleculeNet и выполните поиск липофильности, чтобы найти ссылку для загрузки набора данных. После того, как вы нажмете на ссылку, файл csv будет загружен.

Прочитайте данные.

lipo_data = pd.read_csv("/path/to/data/Lipophilicity.csv")

Фрейм данных содержит столбец ID (CMPD_CHEMBLID), столбец для экспериментальных значений липофильности (exp) и столбец для строк SMILES молекул (smiles). >)

Теперь давайте создадим молекулярные объекты с помощью mordred. Сначала нам нужно создать объект класса Calculator mordred, как показано ниже.

calc = Calculator(descriptors, ignore_3D=True)

ignore_id=True означает, что мы будем создавать только 2-мерные объекты. В классе калькулятора есть метод pandas, который представит нам молекулярные функции в виде кадра данных pandas. Входными данными для этого метода является список объектов rdkit mol наших молекул.
Итак, давайте создадим эти вот эти объекты mol.

mols = [Chem.MolFromSmiles(i) for i in lipo_data.smiles]

Хорошо, теперь мы создаем молекулярные функции.

mol_features = calc.pandas(mols)

Иногда определенные характеристики определенных молекул не рассчитываются должным образом. Такие функции содержат неверные данные, которые мы не можем использовать для обучения. Быстрый способ избавиться от столбцов, содержащих недопустимые данные, — выполнить некоторые вычисления с использованием этих данных. Я собираюсь масштабировать функции, используя StandardScaler sklearn. Затем я собираюсь найти, какие столбцы содержат недопустимые значения. Затем я откажусь от этих столбцов.

# standard scale
tmp = sc.fit_transform(mol_features.astype(float))
# find the columns that contain NaN values
nan_cols = np.where(np.isnan(tmp))[1]
nan_col_headers = mol_features.columns[nan_cols]
# drop the columns that contain NaN values
data = mol_features.drop(nan_col_headers, axis=1)

Хорошо! Теперь у нас есть чистый набор молекулярных признаков. Давайте добавим целевое свойство, значения липофильности в этот фрейм данных.

data.loc[:, 'exp'] = lipo.exp.values

Обучение, проверка и тестовые данные

Теперь нам нужно разделить набор данных, чтобы сформировать обучающие, проверочные и тестовые данные. Несмотря на то, что я не собираюсь использовать набор проверки в этом примере, набор данных проверки необходим для определения достоверности нашей модели для невидимого набора данных.

Мы будем использовать 80% данных в качестве данных для обучения и 10% данных для проверки и тестирования.

train, test = train_test_split(data, test_size=0.2, random_state=42)
test, val = train_test_split(test, test_size=0.5, random_state=42)

Теперь давайте разделим данные функций и целевые данные.

# features
x_train = train.drop(['exp'], axis=1) # train features
x_val = val.drop(['exp'], axis=1) # validation features
x_test = test.drop(['exp'], axis=1) # test features
# targets
y_train = train.exp.values # train targets
y_val = val.exp.values # validation targets
y_test = test.exp.values # test targets

При желании можно масштабировать функции. Я собираюсь масштабировать функции так, чтобы каждая функция имела среднее значение, равное нулю, и стандартное отклонение, равное единице. StandardScaler sklearn может это сделать.

sc = StandardScaler()
x_train = sc.fit_transform(x_train.values)
x_val = sc.transform(x_val.values)
x_test = sc.transform(x_test.values)

Обратите внимание, что я использовал только данные поезда, чтобы найти параметры масштабирования для данных поезда, проверки и тестирования.

Определить модель и обучить

Теперь мы готовы обучить модель. Давайте определим модель, как показано ниже.

rf = RandomForestRegressor(n_estimators=100, random_state=42)

И приступайте к тренировкам.

rf.fit(X=x_train, y=y_train)

Вот и все! Теперь мы можем найти производительность модели на тестовых данных.

Делать предсказания

Мы можем использовать метод прогнозирования случайного леса scikit-learn, чтобы найти прогнозы модели.

test_pred = rf.predict(x_test)
r2 = r2_score(y_pred = test_pred, y_true = y_test)
rmse = mean_squared_error(y_pred=test_pred, y_true=y_test)**.5
print(f'r2 score =  {r2}')
print(f'rmse = {rmse}')

Я получил оценку r2 0,66 и RMSE 0,70. Не так уж и плохо для очень простой модели. Вы можете получить разные результаты в зависимости от случайных состояний, которые вы используете для разделения поезда/теста и для случайных лесов.

Давайте также построим график истинной и предсказанной липофильности.

plt.plot(y_test, test_pred, 'o');
plt.plot([-1, 5], [-1,5], 'k--')
plt.xticks(fontsize=16, fontweight='bold')
plt.yticks(fontsize=16, fontweight='bold')
plt.xlabel('True', fontsize=16, fontweight='bold');
plt.ylabel('Predicted', fontsize=16, fontweight='bold');
plt.tight_layout();
plt.savefig('lipo_test.png')

Полный код можно найти в моем репозитории на GitHub.