{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "3d5d52d1-4874-44b5-b532-ef03da47644a", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "from rdkit import Chem\n", "from rdkit.Chem import Descriptors, rdMolDescriptors, Crippen, Lipinski\n", "from tqdm import tqdm\n", "import warnings\n", "from sklearn.preprocessing import StandardScaler\n", "from sklearn.model_selection import train_test_split\n", "import random\n", "from concurrent.futures import ProcessPoolExecutor\n", "import multiprocessing\n", "\n", "def analyze_polymer_features_rdkit(smiles):\n", " mol = Chem.MolFromSmiles(smiles)\n", " if mol is None:\n", " return None\n", " \n", " features = {}\n", " \n", " # Basic molecular properties\n", " features['mol_weight'] = Descriptors.MolWt(mol)\n", " features['exact_mol_weight'] = Descriptors.ExactMolWt(mol)\n", " features['num_heavy_atoms'] = mol.GetNumHeavyAtoms()\n", " features['num_atoms'] = mol.GetNumAtoms()\n", " features['num_bonds'] = mol.GetNumBonds()\n", " \n", " # Hydrogen bonding features\n", " features['num_hbond_donors'] = Descriptors.NumHDonors(mol)\n", " features['num_hbond_acceptors'] = Descriptors.NumHAcceptors(mol)\n", " features['num_heteroatoms'] = Descriptors.NumHeteroatoms(mol)\n", " \n", " # Structural complexity\n", " features['num_rotatable_bonds'] = Descriptors.NumRotatableBonds(mol)\n", " features['num_saturated_rings'] = Descriptors.NumSaturatedRings(mol)\n", " features['num_aromatic_rings'] = Descriptors.NumAromaticRings(mol)\n", " features['num_aliphatic_rings'] = Descriptors.NumAliphaticRings(mol)\n", " features['ring_count'] = Descriptors.RingCount(mol)\n", " features['fraction_csp3'] = Descriptors.FractionCSP3(mol)\n", " \n", " # Surface area and polarity\n", " features['tpsa'] = Descriptors.TPSA(mol)\n", " features['polar_surface_area'] = rdMolDescriptors.CalcTPSA(mol)\n", " \n", " # Lipophilicity and solubility\n", " features['logp'] = Descriptors.MolLogP(mol)\n", " features['crippen_logp'] = Crippen.MolLogP(mol)\n", " features['crippen_mr'] = Crippen.MolMR(mol) # Molar refractivity\n", " \n", " # Flexibility and rigidity\n", " features['kappa1'] = Descriptors.Kappa1(mol) # Molecular shape index\n", " features['kappa2'] = Descriptors.Kappa2(mol)\n", " features['kappa3'] = Descriptors.Kappa3(mol)\n", " features['chi0v'] = Descriptors.Chi0v(mol) # Connectivity indices\n", " features['chi1v'] = Descriptors.Chi1v(mol)\n", " features['chi2v'] = Descriptors.Chi2v(mol)\n", " \n", " # Electronic properties\n", " features['balaban_j'] = Descriptors.BalabanJ(mol)\n", " features['bertz_ct'] = Descriptors.BertzCT(mol) # Complexity index\n", " \n", " # Polymer-specific features\n", " features['num_radical_electrons'] = Descriptors.NumRadicalElectrons(mol)\n", " features['num_valence_electrons'] = Descriptors.NumValenceElectrons(mol)\n", " \n", " # Atom type counts\n", " atom_counts = {}\n", " for atom in mol.GetAtoms():\n", " symbol = atom.GetSymbol()\n", " atom_counts[symbol] = atom_counts.get(symbol, 0) + 1\n", " \n", " # Add individual atom counts as features\n", " for element in ['C', 'N', 'O', 'S', 'P', 'F', 'Cl', 'Br', 'I']:\n", " features[f'count_{element}'] = atom_counts.get(element, 0)\n", " features[f'ratio_{element}'] = atom_counts.get(element, 0) / features['num_atoms'] if features['num_atoms'] > 0 else 0\n", " \n", " # Bond type analysis\n", " bond_types = {'SINGLE': 0, 'DOUBLE': 0, 'TRIPLE': 0, 'AROMATIC': 0}\n", " for bond in mol.GetBonds():\n", " bond_type = str(bond.GetBondType())\n", " if bond_type in bond_types:\n", " bond_types[bond_type] += 1\n", " \n", " for bond_type, count in bond_types.items():\n", " features[f'num_{bond_type.lower()}_bonds'] = count\n", " features[f'ratio_{bond_type.lower()}_bonds'] = count / features['num_bonds'] if features['num_bonds'] > 0 else 0\n", " \n", " # Hybridization analysis\n", " hybridization_counts = {'SP': 0, 'SP2': 0, 'SP3': 0, 'SP3D': 0, 'SP3D2': 0}\n", " for atom in mol.GetAtoms():\n", " hyb = str(atom.GetHybridization())\n", " if hyb in hybridization_counts:\n", " hybridization_counts[hyb] += 1\n", " \n", " for hyb_type, count in hybridization_counts.items():\n", " features[f'num_{hyb_type.lower()}_carbons'] = count\n", " features[f'ratio_{hyb_type.lower()}_carbons'] = count / features['num_atoms'] if features['num_atoms'] > 0 else 0\n", " \n", " # Formal charge analysis\n", " formal_charges = [atom.GetFormalCharge() for atom in mol.GetAtoms()]\n", " features['total_formal_charge'] = sum(formal_charges)\n", " features['abs_total_formal_charge'] = sum(abs(charge) for charge in formal_charges)\n", " features['max_formal_charge'] = max(formal_charges) if formal_charges else 0\n", " features['min_formal_charge'] = min(formal_charges) if formal_charges else 0\n", " \n", " # Aromaticity features\n", " aromatic_atoms = sum(1 for atom in mol.GetAtoms() if atom.GetIsAromatic())\n", " features['num_aromatic_atoms'] = aromatic_atoms\n", " features['aromatic_ratio'] = aromatic_atoms / features['num_atoms'] if features['num_atoms'] > 0 else 0\n", " \n", " # Ring size analysis\n", " ring_info = mol.GetRingInfo()\n", " ring_sizes = [len(ring) for ring in ring_info.AtomRings()]\n", " if ring_sizes:\n", " features['avg_ring_size'] = sum(ring_sizes) / len(ring_sizes)\n", " features['max_ring_size'] = max(ring_sizes)\n", " features['min_ring_size'] = min(ring_sizes)\n", " features['num_3_rings'] = sum(1 for size in ring_sizes if size == 3)\n", " features['num_4_rings'] = sum(1 for size in ring_sizes if size == 4)\n", " features['num_5_rings'] = sum(1 for size in ring_sizes if size == 5)\n", " features['num_6_rings'] = sum(1 for size in ring_sizes if size == 6)\n", " features['num_7_rings'] = sum(1 for size in ring_sizes if size == 7)\n", " features['num_large_rings'] = sum(1 for size in ring_sizes if size > 7)\n", " else:\n", " features.update({\n", " 'avg_ring_size': 0, 'max_ring_size': 0, 'min_ring_size': 0,\n", " 'num_3_rings': 0, 'num_4_rings': 0, 'num_5_rings': 0,\n", " 'num_6_rings': 0, 'num_7_rings': 0, 'num_large_rings': 0\n", " })\n", " \n", " # Polymer-specific structural features\n", " features['has_polymer_notation'] = '*' in smiles\n", " features['smiles_length'] = len(smiles)\n", " features['branch_count'] = smiles.count('(')\n", " features['branch_ratio'] = smiles.count('(') / len(smiles) if len(smiles) > 0 else 0\n", " \n", " return features\n", "\n", "def add_features(df, num_workers=None):\n", " \"\"\"\n", " Improved version using multiprocessing to calculate RDKit descriptors efficiently.\n", " \n", " Parameters:\n", " df: pandas DataFrame with 'Smiles' column\n", " num_workers: Number of worker processes (defaults to number of CPU cores)\n", " \"\"\"\n", " if num_workers is None:\n", " num_workers = multiprocessing.cpu_count()\n", " \n", " smiles_list = df['Smiles'].tolist()\n", " \n", " with ProcessPoolExecutor(max_workers=num_workers) as executor:\n", " # Use tqdm with executor.map for progress tracking\n", " features_list = list(tqdm(executor.map(analyze_polymer_features_rdkit, smiles_list), \n", " total=len(smiles_list), \n", " desc=\"Computing RDKit descriptors\"))\n", " \n", " # Convert results to DataFrame\n", " features_df = pd.DataFrame(features_list)\n", " \n", " # Concatenate with original DataFrame\n", " df_result = pd.concat([df, features_df], axis=1)\n", " \n", " return df_result\n", "\n", "def get_list_dif(l1, l2):\n", " return list(set(l1) - set(l2))\n", "\n", "# Usage example:\n", "# df_with_features = add_features(df, num_workers=4)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "155598af-79f3-4933-8b5c-1fd11f64b870", "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv('/home/jovyan/simson_training_bolgov/regression/PI_Tg_P308K_synth_db_chem.csv').drop(columns=['Unnamed: 0'], axis=1)" ] }, { "cell_type": "code", "execution_count": null, "id": "c69cc497-9fb6-4f74-96eb-257d7aa4a91a", "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv('/home/jovyan/simson_training_bolgov/kaggle_comp/train.csv')\n", "df['Smiles'] = df['SMILES']\n", "df" ] }, { "cell_type": "code", "execution_count": null, "id": "7b076c55-d6ef-4780-af97-5fccd5062661", "metadata": {}, "outputs": [], "source": [ "sample_df = df.iloc[:10_000]" ] }, { "cell_type": "code", "execution_count": null, "id": "96313883-c2ca-4eb8-9ec7-9aaca8dba077", "metadata": {}, "outputs": [], "source": [ "features_df = add_features(sample_df)" ] }, { "cell_type": "code", "execution_count": 2, "id": "41c7f85a-ea65-42e5-b315-ef304ba311c4", "metadata": {}, "outputs": [], "source": [ "selected_features = ['mol_weight', 'exact_mol_weight', 'num_heavy_atoms', 'num_atoms',\n", " 'num_bonds', 'num_hbond_donors', 'num_hbond_acceptors',\n", " 'num_heteroatoms', 'num_rotatable_bonds', 'num_saturated_rings',\n", " 'num_aromatic_rings', 'num_aliphatic_rings', 'ring_count',\n", " 'fraction_csp3', 'tpsa', 'polar_surface_area', 'logp', 'crippen_logp',\n", " 'crippen_mr', 'kappa1', 'kappa2', 'kappa3', 'chi0v', 'chi1v', 'chi2v',\n", " 'balaban_j', 'bertz_ct', 'num_radical_electrons',\n", " 'num_valence_electrons',\n", " 'count_O', 'ratio_O', 'count_S', 'ratio_S', 'count_P', 'ratio_P',\n", " 'count_F', 'ratio_F', 'count_Cl', 'ratio_Cl', 'count_Br', 'ratio_Br',\n", " 'count_I', 'ratio_I', 'num_single_bonds', 'ratio_single_bonds',\n", " 'num_double_bonds', 'ratio_double_bonds', 'num_triple_bonds',\n", " 'ratio_triple_bonds', 'num_aromatic_bonds', 'ratio_aromatic_bonds',\n", " 'num_sp_carbons', 'ratio_sp_carbons', 'num_sp2_carbons',\n", " 'ratio_sp2_carbons', 'num_sp3_carbons', 'ratio_sp3_carbons',\n", " 'num_sp3d_carbons', 'ratio_sp3d_carbons', 'num_sp3d2_carbons',\n", " 'ratio_sp3d2_carbons', 'total_formal_charge', 'abs_total_formal_charge',\n", " 'max_formal_charge', 'min_formal_charge', 'num_aromatic_atoms',\n", " 'aromatic_ratio', 'avg_ring_size', 'max_ring_size', 'min_ring_size',\n", " 'num_3_rings', 'num_4_rings', 'num_5_rings', 'num_6_rings',\n", " 'num_7_rings', 'num_large_rings', 'has_polymer_notation',\n", " 'branch_count', 'branch_ratio']" ] }, { "cell_type": "code", "execution_count": 4, "id": "fc31605d-cc21-4533-b04e-f8acdaef1a65", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['id', 'SMILES', 'Tg', 'FFV', 'Tc', 'Density', 'Rg', 'Smiles',\n", " 'mol_weight', 'exact_mol_weight', 'num_heavy_atoms', 'num_atoms',\n", " 'num_bonds', 'num_hbond_donors', 'num_hbond_acceptors',\n", " 'num_heteroatoms', 'num_rotatable_bonds', 'num_saturated_rings',\n", " 'num_aromatic_rings', 'num_aliphatic_rings', 'ring_count',\n", " 'fraction_csp3', 'tpsa', 'polar_surface_area', 'logp', 'crippen_logp',\n", " 'crippen_mr', 'kappa1', 'kappa2', 'kappa3', 'chi0v', 'chi1v', 'chi2v',\n", " 'balaban_j', 'bertz_ct', 'num_radical_electrons',\n", " 'num_valence_electrons', 'count_C', 'ratio_C', 'count_N', 'ratio_N',\n", " 'count_O', 'ratio_O', 'count_S', 'ratio_S', 'count_P', 'ratio_P',\n", " 'count_F', 'ratio_F', 'count_Cl', 'ratio_Cl', 'count_Br', 'ratio_Br',\n", " 'count_I', 'ratio_I', 'num_single_bonds', 'ratio_single_bonds',\n", " 'num_double_bonds', 'ratio_double_bonds', 'num_triple_bonds',\n", " 'ratio_triple_bonds', 'num_aromatic_bonds', 'ratio_aromatic_bonds',\n", " 'num_sp_carbons', 'ratio_sp_carbons', 'num_sp2_carbons',\n", " 'ratio_sp2_carbons', 'num_sp3_carbons', 'ratio_sp3_carbons',\n", " 'num_sp3d_carbons', 'ratio_sp3d_carbons', 'num_sp3d2_carbons',\n", " 'ratio_sp3d2_carbons', 'total_formal_charge', 'abs_total_formal_charge',\n", " 'max_formal_charge', 'min_formal_charge', 'num_aromatic_atoms',\n", " 'aromatic_ratio', 'avg_ring_size', 'max_ring_size', 'min_ring_size',\n", " 'num_3_rings', 'num_4_rings', 'num_5_rings', 'num_6_rings',\n", " 'num_7_rings', 'num_large_rings', 'has_polymer_notation',\n", " 'smiles_length', 'branch_count', 'branch_ratio'],\n", " dtype='object')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "scalers = []\n", "for col in selected_features:\n", " scaler = StandardScaler()\n", " features_df[col] = scaler.fit_transform(features_df[col].to_numpy().reshape(-1, 1)).flatten()\n", " scalers.append(scaler)\n", " \n", "features_df.columns" ] }, { "cell_type": "code", "execution_count": 3, "id": "f2f1a614-0ba7-4a01-9731-532afc1d14e0", "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'features_df' is not defined", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m 1\u001b[39m new_features = []\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m feature \u001b[38;5;129;01min\u001b[39;00m selected_features:\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m unique_list = \u001b[43mfeatures_df\u001b[49m[feature].unique()\n\u001b[32m 5\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(unique_list) > \u001b[32m300\u001b[39m:\n\u001b[32m 6\u001b[39m new_features.append(feature)\n", "\u001b[31mNameError\u001b[39m: name 'features_df' is not defined" ] } ], "source": [ "new_features = []\n", "\n", "for feature in selected_features:\n", " unique_list = features_df[feature].unique()\n", " if len(unique_list) > 300:\n", " new_features.append(feature)\n", "new_features.append('Smiles')\n", "print(new_features)\n", "len(new_features), len(selected_features)" ] }, { "cell_type": "code", "execution_count": null, "id": "28cbac75-8a9f-4292-aedb-11f33f5a6056", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "c065d950-7a63-4424-9923-1072d2e2268c", "metadata": {}, "outputs": [], "source": [ "features_df.to_csv('7k_w_descriptors.csv', index=False)" ] }, { "cell_type": "code", "execution_count": 4, "id": "069a9021-d440-4bf1-9882-a2af25f2e801", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idSMILESTgFFVTcDensityRgSmilesmol_weightexact_mol_weight...num_3_ringsnum_4_ringsnum_5_ringsnum_6_ringsnum_7_ringsnum_large_ringshas_polymer_notationsmiles_lengthbranch_countbranch_ratio
087817*CC(*)c1ccccc1C(=O)OCCCCCCNaN0.3746450.205667NaNNaN*CC(*)c1ccccc1C(=O)OCCCCCC-0.875755-0.875617...-0.048476-0.069289-0.626991-0.788904-0.051542-0.0479170.026-0.985221-0.813832
1106919*Nc1ccc([C@H](CCC)c2ccc(C3(c4ccc([C@@H](CCC)c5...NaN0.370410NaNNaNNaN*Nc1ccc([C@H](CCC)c2ccc(C3(c4ccc([C@@H](CCC)c5...0.6518760.651916...-0.048476-0.069289-0.6269910.736852-0.051542-0.0479170.0820.336345-0.286141
2388772*Oc1ccc(S(=O)(=O)c2ccc(Oc3ccc(C4(c5ccc(Oc6ccc(...NaN0.378860NaNNaNNaN*Oc1ccc(S(=O)(=O)c2ccc(Oc3ccc(C4(c5ccc(Oc6ccc(...2.3365732.336165...-0.048476-0.069289-0.6269912.644047-0.051542-0.0479170.01341.657910-0.109289
3519416*Nc1ccc(-c2c(-c3ccc(C)cc3)c(-c3ccc(C)cc3)c(N*)...NaN0.387324NaNNaNNaN*Nc1ccc(-c2c(-c3ccc(C)cc3)c(-c3ccc(C)cc3)c(N*)...0.4177160.417722...-0.048476-0.069289-0.6269911.118291-0.051542-0.0479170.0790.5566060.132247
4539187*Oc1ccc(OC(=O)c2cc(OCCCCCCCCCOCC3CCCN3c3ccc([N...NaN0.355470NaNNaNNaN*Oc1ccc(OC(=O)c2cc(OCCCCCCCCCOCC3CCCN3c3ccc([N...2.1780032.178499...-0.048476-0.0692891.5011490.355413-0.051542-0.0479170.01180.556606-0.830501
..................................................................
79682146592435*Oc1cc(CCCCCCCC)cc(OC(=O)c2cccc(C(*)=O)c2)c1NaN0.367498NaNNaNNaN*Oc1cc(CCCCCCCC)cc(OC(=O)c2cccc(C(*)=O)c2)c1-0.375261-0.375084...-0.048476-0.069289-0.626991-0.407465-0.051542-0.0479170.044-0.3244380.124891
79692146810552*C(=O)OCCN(CCOC(=O)c1ccc2c(c1)C(=O)N(c1cccc(N3...NaN0.353280NaNNaNNaN*C(=O)OCCN(CCOC(=O)c1ccc2c(c1)C(=O)N(c1cccc(N3...1.2842751.284737...-0.048476-0.0692891.5011490.736852-0.051542-0.0479170.01101.2173880.008668
79702147191531*c1cc(C(=O)NCCCCCCCC)cc(N2C(=O)c3ccc(-c4ccc5c(...NaN0.369411NaNNaNNaN*c1cc(C(=O)NCCCCCCCC)cc(N2C(=O)c3ccc(-c4ccc5c(...0.3295700.329823...-0.048476-0.0692891.501149-0.026026-0.051542-0.0479170.0730.3363450.021405
79712147435020*C=C(*)c1ccccc1C261.662355NaNNaNNaNNaN*C=C(*)c1ccccc1C-1.359802-1.359728...-0.048476-0.069289-0.626991-0.788904-0.051542-0.0479170.016-1.205481-1.182617
79722147438299*c1ccc(OCCCCCCCCCCCOC(=O)CCCCC(=O)OCCCCCCCCCCC...NaN0.374049NaNNaNNaN*c1ccc(OCCCCCCCCCCCOC(=O)CCCCC(=O)OCCCCCCCCCCC...1.1606671.160653...-0.048476-0.0692890.437079-0.407465-0.051542-0.0479170.072-0.324438-1.005054
\n", "

7973 rows × 92 columns

\n", "
" ], "text/plain": [ " id SMILES \\\n", "0 87817 *CC(*)c1ccccc1C(=O)OCCCCCC \n", "1 106919 *Nc1ccc([C@H](CCC)c2ccc(C3(c4ccc([C@@H](CCC)c5... \n", "2 388772 *Oc1ccc(S(=O)(=O)c2ccc(Oc3ccc(C4(c5ccc(Oc6ccc(... \n", "3 519416 *Nc1ccc(-c2c(-c3ccc(C)cc3)c(-c3ccc(C)cc3)c(N*)... \n", "4 539187 *Oc1ccc(OC(=O)c2cc(OCCCCCCCCCOCC3CCCN3c3ccc([N... \n", "... ... ... \n", "7968 2146592435 *Oc1cc(CCCCCCCC)cc(OC(=O)c2cccc(C(*)=O)c2)c1 \n", "7969 2146810552 *C(=O)OCCN(CCOC(=O)c1ccc2c(c1)C(=O)N(c1cccc(N3... \n", "7970 2147191531 *c1cc(C(=O)NCCCCCCCC)cc(N2C(=O)c3ccc(-c4ccc5c(... \n", "7971 2147435020 *C=C(*)c1ccccc1C \n", "7972 2147438299 *c1ccc(OCCCCCCCCCCCOC(=O)CCCCC(=O)OCCCCCCCCCCC... \n", "\n", " Tg FFV Tc Density Rg \\\n", "0 NaN 0.374645 0.205667 NaN NaN \n", "1 NaN 0.370410 NaN NaN NaN \n", "2 NaN 0.378860 NaN NaN NaN \n", "3 NaN 0.387324 NaN NaN NaN \n", "4 NaN 0.355470 NaN NaN NaN \n", "... ... ... ... ... .. \n", "7968 NaN 0.367498 NaN NaN NaN \n", "7969 NaN 0.353280 NaN NaN NaN \n", "7970 NaN 0.369411 NaN NaN NaN \n", "7971 261.662355 NaN NaN NaN NaN \n", "7972 NaN 0.374049 NaN NaN NaN \n", "\n", " Smiles mol_weight \\\n", "0 *CC(*)c1ccccc1C(=O)OCCCCCC -0.875755 \n", "1 *Nc1ccc([C@H](CCC)c2ccc(C3(c4ccc([C@@H](CCC)c5... 0.651876 \n", "2 *Oc1ccc(S(=O)(=O)c2ccc(Oc3ccc(C4(c5ccc(Oc6ccc(... 2.336573 \n", "3 *Nc1ccc(-c2c(-c3ccc(C)cc3)c(-c3ccc(C)cc3)c(N*)... 0.417716 \n", "4 *Oc1ccc(OC(=O)c2cc(OCCCCCCCCCOCC3CCCN3c3ccc([N... 2.178003 \n", "... ... ... \n", "7968 *Oc1cc(CCCCCCCC)cc(OC(=O)c2cccc(C(*)=O)c2)c1 -0.375261 \n", "7969 *C(=O)OCCN(CCOC(=O)c1ccc2c(c1)C(=O)N(c1cccc(N3... 1.284275 \n", "7970 *c1cc(C(=O)NCCCCCCCC)cc(N2C(=O)c3ccc(-c4ccc5c(... 0.329570 \n", "7971 *C=C(*)c1ccccc1C -1.359802 \n", "7972 *c1ccc(OCCCCCCCCCCCOC(=O)CCCCC(=O)OCCCCCCCCCCC... 1.160667 \n", "\n", " exact_mol_weight ... num_3_rings num_4_rings num_5_rings \\\n", "0 -0.875617 ... -0.048476 -0.069289 -0.626991 \n", "1 0.651916 ... -0.048476 -0.069289 -0.626991 \n", "2 2.336165 ... -0.048476 -0.069289 -0.626991 \n", "3 0.417722 ... -0.048476 -0.069289 -0.626991 \n", "4 2.178499 ... -0.048476 -0.069289 1.501149 \n", "... ... ... ... ... ... \n", "7968 -0.375084 ... -0.048476 -0.069289 -0.626991 \n", "7969 1.284737 ... -0.048476 -0.069289 1.501149 \n", "7970 0.329823 ... -0.048476 -0.069289 1.501149 \n", "7971 -1.359728 ... -0.048476 -0.069289 -0.626991 \n", "7972 1.160653 ... -0.048476 -0.069289 0.437079 \n", "\n", " num_6_rings num_7_rings num_large_rings has_polymer_notation \\\n", "0 -0.788904 -0.051542 -0.047917 0.0 \n", "1 0.736852 -0.051542 -0.047917 0.0 \n", "2 2.644047 -0.051542 -0.047917 0.0 \n", "3 1.118291 -0.051542 -0.047917 0.0 \n", "4 0.355413 -0.051542 -0.047917 0.0 \n", "... ... ... ... ... \n", "7968 -0.407465 -0.051542 -0.047917 0.0 \n", "7969 0.736852 -0.051542 -0.047917 0.0 \n", "7970 -0.026026 -0.051542 -0.047917 0.0 \n", "7971 -0.788904 -0.051542 -0.047917 0.0 \n", "7972 -0.407465 -0.051542 -0.047917 0.0 \n", "\n", " smiles_length branch_count branch_ratio \n", "0 26 -0.985221 -0.813832 \n", "1 82 0.336345 -0.286141 \n", "2 134 1.657910 -0.109289 \n", "3 79 0.556606 0.132247 \n", "4 118 0.556606 -0.830501 \n", "... ... ... ... \n", "7968 44 -0.324438 0.124891 \n", "7969 110 1.217388 0.008668 \n", "7970 73 0.336345 0.021405 \n", "7971 16 -1.205481 -1.182617 \n", "7972 72 -0.324438 -1.005054 \n", "\n", "[7973 rows x 92 columns]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "features_df = pd.read_csv('7k_w_descriptors.csv')\n", "features_df" ] }, { "cell_type": "code", "execution_count": 5, "id": "49998b8a-3925-4383-917a-116f70187d46", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n" ] } ], "source": [ "old_len = len(features_df)\n", "new_len = len(features_df.drop_duplicates())\n", "print(new_len - old_len)" ] }, { "cell_type": "code", "execution_count": 11, "id": "c2f08ca9-21f6-4a79-ab94-80556b8dab1d", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|█████████████████████████████████████| 6378/6378 [00:01<00:00, 3492.45it/s]\n", "100%|█████████████████████████████████████| 1595/1595 [00:00<00:00, 3576.37it/s]\n" ] } ], "source": [ "import torch\n", "from tqdm import tqdm\n", "import copy\n", "from sklearn.model_selection import train_test_split\n", "\n", "def create_splits(df):\n", " train, test = train_test_split(df, test_size=0.2)\n", " return train, test\n", "\n", "def create_samples(df, features):\n", " samples = []\n", " features_without_smiles = copy.deepcopy(features)\n", " features_without_smiles.remove('Smiles')\n", " for i, row in tqdm(df.iterrows(), total=len(df)):\n", " properties = torch.Tensor(row[features_without_smiles].to_list())\n", " sample = {'Smiles': row['Smiles'], 'property_tensor': properties}\n", " samples.append(sample)\n", " return samples\n", "\n", "train, val = create_splits(features_df.reset_index(drop=True))\n", "\n", "train = train.reset_index(drop=True)\n", "val = val.reset_index(drop=True)\n", "\n", "train_list = create_samples(train, new_features)\n", "val_list = create_samples(val, new_features)" ] }, { "cell_type": "code", "execution_count": 12, "id": "2fdb3171-deda-4c1f-ae4b-853d781ffdd5", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|███████████████████████████████████████| 20/20 [00:00<00:00, 106050.67it/s]\n" ] } ], "source": [ "from sklearn.metrics.pairwise import cosine_similarity\n", "\n", "prop_vectors = [el['property_tensor'] for el in train_list[:20]]\n", "\n", "sim_matrix = cosine_similarity(prop_vectors)\n", " \n", "n = len(prop_vectors)\n", "positive_pairs, negative_candidates = [], []\n", "sims = []\n", "\n", "positive_threshold = 0.9\n", "negative_threshold = 0.2\n", "\n", "for i in tqdm(range(n)):\n", " for j in range(i + 1, n):\n", " sim = sim_matrix[i, j]\n", "\n", " if sim > positive_threshold:\n", " positive_pairs.append((i, j, sim))\n", " elif sim < negative_threshold:\n", " negative_candidates.append((i, j, sim))\n", " sims.append(float(sim))\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "54f29e98-7c32-441c-bb1b-cdaf3fd1df49", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(3, 126)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(positive_pairs), len(negative_candidates)" ] }, { "cell_type": "code", "execution_count": 14, "id": "22e0f46e-2673-4840-95fd-f98914e57b78", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjgAAAGdCAYAAAAfTAk2AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs/XucZFdZLo4/e1dVV3fPTM8lc8skE3IBE2JuGEgIooCMJBCVfPVoomAkPwxHNHg0Hi7xcDOoEUVEMBpBwuUIwlEBBTUQIwGBXCAhQCAJJCSZJHO/9vStui7798eud613vXutvdeue3fv5/OZT/dUV+1atWvvtd71vM/7vEEURREKFChQoECBAgWWEcJhD6BAgQIFChQoUKDXKAKcAgUKFChQoMCyQxHgFChQoECBAgWWHYoAp0CBAgUKFCiw7FAEOAUKFChQoECBZYciwClQoECBAgUKLDsUAU6BAgUKFChQYNmhCHAKFChQoECBAssO5WEPYBhotVrYtWsX1qxZgyAIhj2cAgUKFChQoIAHoijCsWPHsG3bNoRhOkezIgOcXbt2Yfv27cMeRoECBQoUKFCgAzzxxBM48cQTU5+zIgOcNWvWAIhP0NTU1JBHU6BAgQIFChTwwfT0NLZv367W8TSsyACH0lJTU1NFgFOgQIECBQosMfjISwqRcYECBQoUKFBg2aEIcAoUKFCgQIECyw5FgFOgQIECBQoUWHYoApwCBQoUKFCgwLJDEeAUKFCgQIECBZYdigCnQIECBQoUKLDsUAQ4BQoUKFCgQIFlh74GOF/+8pfxsz/7s9i2bRuCIMBnPvOZzNfcfvvt+LEf+zFUq1U8/elPx4c//OHEc2688UacfPLJGB8fx4UXXoi7776794MvUKBAgQIFCixZ9DXAmZ2dxbnnnosbb7zR6/mPPvooLr30UrzoRS/Cfffdh9/5nd/Br//6r+Pzn/+8es4nP/lJXHvttXjb296Ge++9F+eeey4uvvhi7Nu3r18fo0CBAgUKFCiwxBBEURQN5I2CAJ/+9Kdx2WWXOZ/zxje+Ef/2b/+G+++/Xz12xRVX4MiRI7jlllsAABdeeCGe85zn4K/+6q8AxI0zt2/fjte97nV405ve5DWW6elprF27FkePHi2cjAsUKFCgQIElgjzr90hpcO644w7s2LHDeOziiy/GHXfcAQBYXFzEPffcYzwnDEPs2LFDPceGWq2G6elp41+BAgUKFChQYPlipAKcPXv2YMuWLcZjW7ZswfT0NObn53HgwAE0m03rc/bs2eM87g033IC1a9eqf0Un8QIFChQoUGB5Y6QCnH7huuuuw9GjR9W/J554YthDKlCgQIECBbrC4wdn8bdfegSztcawhzKSGKlu4lu3bsXevXuNx/bu3YupqSlMTEygVCqhVCpZn7N161bncavVKqrVal/GXKBAgQIFCgwD7/uvh/FP9zyJ9ZNj+KXnFJkJiZFicC666CLcdtttxmO33norLrroIgDA2NgYzj//fOM5rVYLt912m3pOgQIFChQosBIwPV8HABxt/yxgoq8BzszMDO677z7cd999AOIy8Pvuuw87d+4EEKeOrrzySvX83/iN38APf/hDvOENb8CDDz6Iv/7rv8b/+3//D7/7u7+rnnPttdfiAx/4AD7ykY/ggQcewGtf+1rMzs7iqquu6udHKVCgQIECBUYKjVZcBL3YbA15JKOJvqaovvGNb+BFL3qR+v+1114LAPi1X/s1fPjDH8bu3btVsAMAp5xyCv7t3/4Nv/u7v4u//Mu/xIknnoi/+7u/w8UXX6yec/nll2P//v1461vfij179uC8887DLbfckhAeFyhQoECBAssZ9XZgU2sUAY4NA/PBGSUUPjgFChQoUGCp44r334E7f3gIv/GC0/Cml54x7OEMBEvWB6dAgQIFChRYLphbbODfv7MbM32qcmo02ymqgsGxoghwChQoUKBAgT7gY3fuxG9+7F7c/JVH+3L8eluDUy80OFYUAU6BAgUKFCjQBxyYqQEADs0u9uX4jXZgUzA4dhQBToECBQoUKNAHUJVTq09SV5WiKhgcK4oAp0CBAgUKFOgDiGHpV4BTbxUMThqKAKdAgQIFChToAzSD06fjtxmcokzcjiLAKVCgQIECBfoACkD65caiNDhFisqKIsApUKBAgQIF+gDF4PQp/qAqqsVGsz9vsMRRBDgFCljwg73H8J/f25v9xAIFChRwoNHqrwanqKJKRxHgFChgwev+4Zv49Y9+AzsPzg17KAUKFFiiGJQGp95ccQ0JvFAEOAUKWEC+FYfn+uNfUaBAgeUPYlj6pcFZLBicVBQBToECFjT77F9RoECB5Y9+zyNFN/F0FAFOgQIW1JV/xZAHUqBAgSULSh31Yx6J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qKgo5OTk4PDhw9I2t9uNw4cPIzc3V8OS+YcQAqtWrcKBAwdw5MgRZGdn9/uaqqoqAEBmZmaASxdYra2tuHDhAjIzM5GTkwOLxaJo5+rqaly6dCns23nv3r1IS0vDI4880ud+emlXAMjOzkZGRoaiPZubm1FRUSG1Z25uLhobG1FZWSntc+TIEbjdbinYCxee4ObcuXM4dOgQhg8f3u9rqqqqYDQafW7lhKNvv/0WV69elf529dS2crt370ZOTg6mTp3a7756al9VWmc568Xbb78trFar2Ldvn/j666/FihUrhM1mE06nU+uiDdnKlStFUlKSOHbsmKirq5MebW1tQgghzp8/LzZt2iQ+//xzUVNTI95//30xZswY8cADD2hc8sFbt26dOHbsmKipqRH/+Mc/RH5+vkhJSRFXrlwRQgjx7LPPipEjR4ojR46Izz//XOTm5orc3FyNSz003d3dYuTIkaK4uFixXQ/t2tLSIk6fPi1Onz4tAIjt27eL06dPSyOHtmzZImw2m3j//ffFF198IebNmyeys7PF9evXpfcoKCgQ06ZNExUVFeLjjz8W48ePFwsXLtSqSjfVV107OzvF3LlzRVZWlqiqqlIcxx0dHUIIIT755BPxyiuviKqqKnHhwgXx+uuvi9TUVLFo0SKNa6aur/q2tLSI9evXi/LyclFTUyMOHTok7rnnHjF+/HjR3t4uvUe4tK0Q/f8tCyFEU1OTiI2NFa+99prP68Otff2BAY4fvfrqq2LkyJEiKipKzJgxQ3z66adaF8kvAKg+9u7dK4QQ4tKlS+KBBx4QycnJwmq1inHjxomf/vSnoqmpSduC34Knn35aZGZmiqioKHHbbbeJp59+Wpw/f156/vr16+K5554Tw4YNE7GxseLxxx8XdXV1GpZ46P76178KAKK6ulqxXQ/tevToUdW/3cWLFwsheoaKl5SUiPT0dGG1WkVeXp7P53D16lWxcOFCER8fLxITE8XSpUtFS0uLBrXpW191rampuelxfPToUSGEEJWVlcLhcIikpCQRHR0tJk2aJDZv3qwICEJJX/Vta2sTs2bNEqmpqcJisYhRo0aJ5cuX+1xwhkvbCtH/37IQQvz2t78VMTExorGx0ef14da+/mAQQoiAdhERERERBRlzcIoc+iwAAABZSURBVIiIiEh3GOAQERGR7jDAISIiIt1hgENERES6wwCHiIiIdIcBDhEREekOAxwiIiLSHQY4REREpDsMcIiIiEh3GOAQERGR7jDAISIiIt1hgENERES68/+8pnSB5k4hHAAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from matplotlib import pyplot as plt\n", "\n", "plt.plot(sims)" ] }, { "cell_type": "code", "execution_count": 15, "id": "79e7e873-7950-4123-ab13-299360ae19ca", "metadata": {}, "outputs": [], "source": [ "import os\n", "import torch\n", "from torch.utils.data import Dataset, DataLoader\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "from transformers import BertConfig, BertModel, AutoTokenizer\n", "import pickle\n", "import numpy as np\n", "from sklearn.metrics.pairwise import cosine_similarity\n", "\n", "def global_ap(x):\n", " return torch.mean(x.view(x.size(0), x.size(1), -1), dim=1)\n", "\n", "class SimSonEncoder(nn.Module):\n", " def __init__(self, config: BertConfig, max_len: int, dropout: float = 0.1):\n", " super(SimSonEncoder, self).__init__()\n", " self.config = config\n", " self.max_len = max_len\n", " \n", " self.bert = BertModel(config, add_pooling_layer=False)\n", " \n", " self.linear = nn.Linear(config.hidden_size, max_len)\n", " self.dropout = nn.Dropout(dropout)\n", " \n", " def forward(self, input_ids, attention_mask=None):\n", " if attention_mask is None:\n", " attention_mask = input_ids.ne(0)\n", " \n", " outputs = self.bert(\n", " input_ids=input_ids,\n", " attention_mask=attention_mask\n", " )\n", " \n", " hidden_states = outputs.last_hidden_state\n", " \n", " hidden_states = self.dropout(hidden_states)\n", " \n", " pooled = global_ap(hidden_states)\n", " \n", " out = self.linear(pooled)\n", " \n", " return out\n", "\n", "def initialize_model_and_tokenizer():\n", " \"\"\"Initialize BERT model from config and ChemBERTa tokenizer\"\"\"\n", " \n", " \n", " tokenizer = AutoTokenizer.from_pretrained(\"DeepChem/ChemBERTa-77M-MTR\")\n", " config = BertConfig(\n", " vocab_size=tokenizer.vocab_size,\n", " hidden_size=768,\n", " num_hidden_layers=4,\n", " num_attention_heads=12,\n", " intermediate_size=2048,\n", " max_position_embeddings=512,\n", " )\n", " model = SimSonEncoder(config=config, max_len=512).cuda()\n", " return model, tokenizer\n" ] }, { "cell_type": "code", "execution_count": 16, "id": "8a3adaff-da65-46b4-b9ee-95851d786a67", "metadata": {}, "outputs": [], "source": [ "import time\n", "\n", "\n", "class MolecularContrastiveDataset(Dataset):\n", " def __init__(self, data_list, tokenizer, positive_threshold=0.9, cache_path=None, split_type='train'):\n", " \"\"\"\n", " Dataset that only contains positive pairs for NT-Xent contrastive learning\n", " \"\"\"\n", " self.data_list = data_list\n", " self.tokenizer = tokenizer\n", " self.positive_threshold = positive_threshold\n", " self.cache_path = cache_path\n", " self.split_type = split_type\n", "\n", " # Load or compute pairs\n", " if cache_path and os.path.exists(cache_path) and os.path.getsize(cache_path) > 0:\n", " print(f\"Loading cached pairs from {cache_path}\")\n", " self._load_pairs()\n", " else:\n", " print(\"Computing positive pairs only...\")\n", " self._compute_positive_pairs()\n", " if cache_path:\n", " self._save_pairs()\n", " \n", " def _compute_positive_pairs(self):\n", " \"\"\"\n", " Compute ONLY positive pairs based on descriptor similarity\n", " \"\"\"\n", " # --- 1. Cosine-similarity matrix ---------------------------------------\n", " prop_vectors = torch.stack(\n", " [item['property_tensor'] for item in self.data_list]\n", " ).numpy()\n", " sim_matrix = cosine_similarity(prop_vectors)\n", "\n", " n = len(self.data_list)\n", " positive_pairs = []\n", " pairs_per_molecule = 1 # STRICTLY ONE FOR CREATING PROPER NEGATIVE PAIRS\n", " current_pairs_per_molecule = 0\n", " # --- 2. Collect only positive pairs ------------------------------------\n", " print(f'Collecting positive pairs with similarity threshold {self.positive_threshold}')\n", " for i in tqdm(range(n)):\n", " for j in range(i + 1, n):\n", " sim = sim_matrix[i, j]\n", " if sim > self.positive_threshold:\n", " positive_pairs.append((i, j, sim))\n", " current_pairs_per_molecule += 1\n", " if current_pairs_per_molecule > pairs_per_molecule:\n", " current_pairs_per_molecule = 0\n", " break\n", "\n", " # --- 3. Store only positive pairs --------------------------------------\n", " if len(positive_pairs) == 0:\n", " raise ValueError(\"No positive pairs found – lower the positive_threshold.\")\n", "\n", " # No shuffling - we want consistent positive pairs\n", " self.pairs = [(i, j) for i, j, _ in positive_pairs]\n", " self.descriptor_similarities = [sim for _, _, sim in positive_pairs]\n", "\n", " print(f\"Generated {len(self.pairs)} positive pairs\")\n", "\n", " def _save_pairs(self):\n", " \"\"\"Save computed pairs to cache file\"\"\"\n", " cache_data = {\n", " 'pairs': self.pairs,\n", " 'descriptor_similarities': self.descriptor_similarities\n", " }\n", " with open(self.cache_path, 'wb') as f:\n", " pickle.dump(cache_data, f)\n", " print(f\"Cached pairs saved to {self.cache_path}\")\n", " \n", " def _load_pairs(self):\n", " \"\"\"Load pairs from cache file\"\"\"\n", " with open(self.cache_path, 'rb') as f:\n", " cache_data = pickle.load(f)\n", " \n", " self.pairs = cache_data['pairs']\n", " self.descriptor_similarities = cache_data['descriptor_similarities']\n", " \n", " def __len__(self):\n", " return len(self.pairs)\n", " \n", " def __getitem__(self, idx):\n", " i, j = self.pairs[idx]\n", " desc_sim = self.descriptor_similarities[idx]\n", " \n", " # Get SMILES for both molecules\n", " smiles_i = self.data_list[i]['Smiles']\n", " smiles_j = self.data_list[j]['Smiles']\n", " if self.split_type == 'val':\n", " print(f'POSITIVE PAIR SMILES: \\n{smiles_i} \\n {smiles_j}')\n", " # Tokenize SMILES\n", " tokens_i = self.tokenizer(\n", " smiles_i, \n", " return_tensors='pt', \n", " padding='max_length', \n", " truncation=True, \n", " max_length=256\n", " )\n", " tokens_j = self.tokenizer(\n", " smiles_j, \n", " return_tensors='pt', \n", " padding='max_length', \n", " truncation=True, \n", " max_length=256\n", " )\n", " \n", " # Remove batch dimension\n", " tokens_i = {key: val.squeeze(0) for key, val in tokens_i.items()}\n", " tokens_j = {key: val.squeeze(0) for key, val in tokens_j.items()}\n", " \n", " # Get property vectors\n", " prop_vec_i = self.data_list[i]['property_tensor']\n", " prop_vec_j = self.data_list[j]['property_tensor']\n", " \n", " return {\n", " 'tokens_i': tokens_i,\n", " 'tokens_j': tokens_j,\n", " 'descriptor_similarity': torch.tensor(desc_sim, dtype=torch.float32),\n", " 'property_tensor_i': prop_vec_i,\n", " 'property_tensor_j': prop_vec_j\n", " }\n", "\n", "\n", "def contrastive_collate_fn(batch):\n", " \"\"\"\n", " Collate function that creates proper NT-Xent batches:\n", " - Element 0 and 1 are positive pairs\n", " - Element 2 and 3 are positive pairs \n", " - etc.\n", " \"\"\"\n", " batch_size = len(batch)\n", " \n", " # Ensure even batch size for proper pairing\n", " if batch_size % 2 != 0:\n", " batch = batch[:-1] # Drop last element if odd\n", " batch_size = len(batch)\n", " \n", " # Interleave: [sample1_i, sample1_j, sample2_i, sample2_j, ...]\n", " tokens_list = []\n", " desc_similarities = []\n", " \n", " for i in range(0, batch_size, 1):\n", " # Add first molecule of pair i\n", " tokens_list.append(batch[i]['tokens_i'])\n", " desc_similarities.append(batch[i]['descriptor_similarity'])\n", " \n", " # Add second molecule of pair i (positive pair)\n", " tokens_list.append(batch[i]['tokens_j'])\n", " desc_similarities.append(batch[i]['descriptor_similarity']) # Same similarity for both elements in pair\n", " \n", " # Stack all tokens\n", " tokens = {}\n", " for key in tokens_list[0].keys():\n", " tokens[key] = torch.stack([item[key] for item in tokens_list])\n", " \n", " desc_similarities_tensor = torch.stack(desc_similarities)\n", " \n", " return {\n", " 'tokens': tokens,\n", " 'descriptor_similarities': desc_similarities_tensor,\n", " }\n", "\n", "\n", "def create_dataloaders(train_list, val_list, tokenizer, batch_size=32, \n", " positive_threshold=0.85, cache_dir=\"cache\"):\n", " \"\"\"Create train and validation dataloaders for NT-Xent\"\"\"\n", " os.makedirs(cache_dir, exist_ok=True)\n", " \n", " # Ensure even batch size for proper pairing\n", " if batch_size % 2 != 0:\n", " batch_size += 1\n", " print(f\"Adjusted batch_size to {batch_size} (must be even for NT-Xent)\")\n", " \n", " train_cache = os.path.join(cache_dir, 'train_positive_pairs.pkl')\n", " val_cache = os.path.join(cache_dir, 'val_positive_pairs.pkl')\n", " \n", " train_dataset = MolecularContrastiveDataset(\n", " train_list, tokenizer, positive_threshold=positive_threshold, cache_path=train_cache\n", " )\n", " val_dataset = MolecularContrastiveDataset(\n", " val_list, tokenizer, positive_threshold=positive_threshold, cache_path=val_cache, split_type='val',\n", " )\n", " \n", " train_loader = DataLoader(\n", " train_dataset, batch_size=batch_size, shuffle=True, collate_fn=contrastive_collate_fn, drop_last=True, pin_memory=True\n", " )\n", " val_loader = DataLoader(\n", " val_dataset, batch_size=batch_size, shuffle=False, collate_fn=contrastive_collate_fn, drop_last=True, pin_memory=True\n", " )\n", " \n", " return train_loader, val_loader\n", "\n" ] }, { "cell_type": "code", "execution_count": 17, "id": "f956a50b-85a5-49df-b7c6-6e40dce160e1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model initialized with 23,299,840 trainable parameters\n" ] } ], "source": [ "def nt_xent_loss_with_temp_scaling(embeddings1, embeddings2, descriptor_similarity, base_temp=0.02):\n", " batch_size = embeddings1.shape[0]\n", " device = embeddings1.device\n", " #individual_temperatures = sigmoid_temp_scaling(descriptor_similarity, base_temp)\n", " #temperature = individual_temperatures.mean() # Single temperature for the whole batch\n", " temperature = base_temp\n", " # Normalize projections\n", " z_i = F.normalize(embeddings1, p=2, dim=1)\n", " z_j = F.normalize(embeddings2, p=2, dim=1)\n", " \n", " # Concatenate for similarity matrix calculation\n", " representations = torch.cat([z_i, z_j], dim=0)\n", " # Calculate cosine similarity between all pairs\n", " similarity_matrix = F.cosine_similarity(representations.unsqueeze(1), representations.unsqueeze(0), dim=2)\n", " #similarity_matrix = torch.clamp(similarity_matrix, min=-0.999, max=0.999)\n", " sim_ij = torch.diag(similarity_matrix, batch_size)\n", " sim_ji = torch.diag(similarity_matrix, -batch_size)\n", " positives = torch.cat([sim_ij, sim_ji], dim=0)\n", " \n", " # Create a mask to exclude self-comparisons\n", " nominator = torch.exp(positives / temperature)\n", " mask = (~torch.eye(batch_size * 2, batch_size * 2, dtype=torch.bool, device=device)).float()\n", " denominator = mask * torch.exp(similarity_matrix / temperature)\n", " \n", " # Calculate the final loss\n", " loss = -torch.log(nominator / torch.sum(denominator, dim=1))\n", " if torch.isnan(loss).any():\n", " print(similarity_matrix)\n", " print(f\"Temperature: {temperature}\")\n", " print(f\"Nominator range: {nominator.min().item():.6f} to {nominator.max().item():.6f}\")\n", " \n", " return torch.sum(loss) / (2 * batch_size)\n", "\n", "\n", "def sigmoid_temp_scaling(descriptor_similarity, base_temp=0.05, steepness=10.0, midpoint=0.5):\n", " \"\"\"Smooth sigmoid-based temperature scaling\"\"\"\n", " sigmoid_factor = torch.sigmoid(steepness * (descriptor_similarity - midpoint))\n", " temperature = base_temp * (2.0 - sigmoid_factor)\n", " return temperature\n", "\n", "\n", "def train_step(batch, model, optimizer, device, scheduler, base_temp=0.1):\n", " \"\"\"Single training step for NT-Xent\"\"\"\n", " model.train()\n", " optimizer.zero_grad()\n", " \n", " # Move batch to device\n", " tokens = {k: v.to(device) for k, v in batch['tokens'].items()}\n", " desc_similarities = batch['descriptor_similarities'].to(device)\n", " \n", " # Forward pass - get embeddings for all samples\n", " outputs = model(**tokens) # i1, j1, i2, j2 ...\n", " embeddings = outputs\n", " \n", " # Split embeddings: even indices are embeddings1, odd indices are embeddings2\n", " embeddings1 = embeddings[::2] # [0, 2, 4, ...]\n", " embeddings2 = embeddings[1::2] # [1, 3, 5, ...]\n", " \n", " # Get descriptor similarities for each pair (take every other one since they're duplicated)\n", " pair_desc_similarities = desc_similarities[::2]\n", " #print(f'FIRST TRAIN EMBED: {embeddings1}')\n", " #print(f'SECOND TRAIN EMBED: {embeddings2}')\n", " #print(f'COSINE SIM BETWEEN THEM TRAIN: {F.cosine_similarity(embeddings1, embeddings2, dim=1)}')\n", " # Calculate NT-Xent loss\n", " loss = nt_xent_loss_with_temp_scaling(embeddings1, embeddings2, pair_desc_similarities, base_temp=base_temp)\n", " \n", " # Backward pass\n", " loss.backward()\n", " optimizer.step()\n", " torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)\n", " scheduler.step()\n", " return loss.item()\n", "\n", "def val_step(batch, model, device, base_temp=0.1):\n", " \"\"\"Single validation step for NT-Xent\"\"\"\n", " model.eval()\n", " with torch.no_grad():\n", " # Move batch to device\n", " tokens = {k: v.to(device) for k, v in batch['tokens'].items()}\n", " desc_similarities = batch['descriptor_similarities'].to(device)\n", " \n", " # Forward pass\n", " outputs = model(**tokens)\n", " embeddings = outputs\n", " \n", " # Split embeddings\n", " embeddings1 = embeddings[::2]\n", " embeddings2 = embeddings[1::2]\n", " \n", " # Get descriptor similarities for pairs\n", " pair_desc_similarities = desc_similarities[::2]\n", " \n", " print(f'FIRST VAL EMBED: {embeddings1}')\n", " print(f'SECOND VAL EMBED: {embeddings2}')\n", " print(f'COSINE SIM BETWEEN THEM: {F.cosine_similarity(embeddings1, embeddings2, dim=1)}')\n", " #print(f'SECOND VAL EMBED: {embeddings2}')\n", " loss = nt_xent_loss_with_temp_scaling(embeddings1, embeddings2, pair_desc_similarities, base_temp=base_temp)\n", " print(f'VAL LOSS: {loss}')\n", " \n", " return loss.item()\n", "\n", "def train_epoch(train_loader, model, optimizer, scheduler, base_temp=0.01):\n", " \"\"\"Train for one epoch\"\"\"\n", " total_loss = 0\n", " num_batches = 0\n", " \n", " progress_bar = tqdm(train_loader, desc=\"Training\")\n", " \n", " for batch in progress_bar:\n", " loss = train_step(batch, model, optimizer, 'cuda', scheduler, base_temp=base_temp)\n", " total_loss += loss\n", " num_batches += 1\n", " \n", " # Calculate running average loss\n", " avg_loss = total_loss / num_batches\n", " \n", " # Update progress bar with current loss info\n", " progress_bar.set_postfix({\n", " 'Loss': f'{loss:.4f}',\n", " 'Avg Loss': f'{avg_loss:.4f}'\n", " })\n", " \n", " return total_loss / num_batches if num_batches > 0 else 0\n", "\n", "\n", "def validate_epoch(val_loader, model, base_temp=0.01):\n", " \"\"\"Validate for one epoch\"\"\"\n", " total_loss = 0\n", " num_batches = 0\n", " print('nah twin')\n", " return 0\n", " for batch in val_loader:\n", " loss = val_step(batch, model, 'cuda', base_temp=base_temp)\n", " total_loss += loss\n", " num_batches += 1\n", " \n", " return total_loss / num_batches if num_batches > 0 else 0\n", "\n", "def training_loop(train_loader, val_loader, model, tokenizer, epochs=50, patience=5, lr=1e-4, base_temp=0.02,\n", " device_name='cuda', save_path='best_model.pt'):\n", " \"\"\"Main training loop with early stopping\"\"\"\n", " device = torch.device(device_name if torch.cuda.is_available() else 'cpu')\n", " print(f\"Using device: {device}\")\n", " \n", " # Initialize model and optimizer\n", " optimizer = torch.optim.Adam(model.parameters(), lr=lr)\n", " optimizer.zero_grad()\n", "\n", " total_steps = epochs * len(train_loader)\n", " scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_mult=1, T_0=total_steps)\n", " # Early stopping variables\n", " best_val_loss = float('inf')\n", " no_improve_epochs = 0\n", " \n", " print(\"Starting training...\")\n", " \n", " for epoch in range(epochs):\n", " # Training\n", " with torch.autocast(dtype=torch.float16, device_type='cuda'):\n", " train_loss = train_epoch(train_loader, model, optimizer, scheduler, base_temp=base_temp)\n", " print('END TRAIN')\n", " # Validation\n", " val_loss = validate_epoch(val_loader, model)\n", " \n", " print(f\"Epoch {epoch + 1}/{epochs}: Train Loss = {train_loss:.4f}, Val Loss = {val_loss:.4f}\")\n", " \n", " # Early stopping check\n", " if val_loss < best_val_loss:\n", " best_val_loss = val_loss\n", " no_improve_epochs = 0\n", " # Save best model\n", " torch.save(model.state_dict(), save_path)\n", " print(f\"New best model saved with val loss: {val_loss:.4f}\")\n", " else:\n", " no_improve_epochs += 1\n", " print(f\"No improvement for {no_improve_epochs} epochs\")\n", " \n", " if no_improve_epochs >= patience:\n", " print(f\"Early stopping triggered after {epoch + 1} epochs\")\n", " break\n", " \n", " # Load best model\n", " print(f\"Loading best model from {save_path}\")\n", " model.load_state_dict(torch.load(save_path))\n", " model.eval()\n", " \n", " print(f\"Training completed. Best validation loss: {best_val_loss:.4f}\")\n", "\n", "\n", "model, tokenizer = initialize_model_and_tokenizer()\n", "#model.load_state_dict(torch.load('/home/jovyan/simson_training_bolgov/regression/actual_encoder_state.pkl', weights_only=False))\n", "print(f\"Model initialized with {sum(p.numel() for p in model.parameters() if p.requires_grad):,} trainable parameters\")\n" ] }, { "cell_type": "code", "execution_count": 18, "id": "c73e2bba-59c1-4b41-b2ff-235526dd2912", "metadata": {}, "outputs": [], "source": [ "!rm -rf cache" ] }, { "cell_type": "code", "execution_count": null, "id": "0072c8f5-c5e9-4590-9544-c73cf1fac1e8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Computing positive pairs only...\n", "Collecting positive pairs with similarity threshold 0.8\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|████████████████████████████████████| 6378/6378 [00:00<00:00, 55896.48it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Generated 12538 positive pairs\n", "Cached pairs saved to cache/train_positive_pairs.pkl\n", "Computing positive pairs only...\n", "Collecting positive pairs with similarity threshold 0.8\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|█████████████████████████████████████| 100/100 [00:00<00:00, 206209.64it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Generated 129 positive pairs\n", "Cached pairs saved to cache/val_positive_pairs.pkl\n", "Using device: cuda\n", "Starting training...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Training: 100%|█| 1567/1567 [00:31<00:00, 49.37it/s, Loss=0.9300, Avg Loss=0.989\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "END TRAIN\n", "nah twin\n", "Epoch 1/10: Train Loss = 0.9891, Val Loss = 0.0000\n", "New best model saved with val loss: 0.0000\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Training: 100%|█| 1567/1567 [00:31<00:00, 50.05it/s, Loss=2.7072, Avg Loss=2.712\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "END TRAIN\n", "nah twin\n", "Epoch 2/10: Train Loss = 2.7125, Val Loss = 0.0000\n", "No improvement for 1 epochs\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Training: 77%|▊| 1204/1567 [00:24<00:07, 49.73it/s, Loss=2.7080, Avg Loss=2.708" ] } ], "source": [ "train_loader, val_loader = create_dataloaders(\n", " train_list, val_list[:100], tokenizer, \n", " batch_size=128, positive_threshold=0.8\n", ")\n", "\n", "training_loop(\n", " train_loader, val_loader, model, tokenizer,\n", " epochs=10, patience=5, lr=1e-3, \n", " device_name='cuda', base_temp=0.1\n", ")\n" ] }, { "cell_type": "code", "execution_count": null, "id": "58343b16-1bdb-4476-ac61-e797fbc661d2", "metadata": {}, "outputs": [], "source": [ "print(train_list[:5], '\\n\\n', val_list[:5])" ] }, { "cell_type": "code", "execution_count": null, "id": "47561022-5f57-4b7b-b903-ef1f8773f903", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "5fcef978-3630-4201-9301-6963a8560517", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:.mlspace-bolgov_simson_training]", "language": "python", "name": "conda-env-.mlspace-bolgov_simson_training-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.11" } }, "nbformat": 4, "nbformat_minor": 5 }