Upload folder using huggingface_hub
Browse files- .gitattributes +26 -10
- README.md +282 -0
- conf/Figures/UniKP.jpg +3 -0
- conf/Figures/UniKP.png +3 -0
- conf/datasets/Generated_degree_unified_smiles_572.xlsx +0 -0
- conf/datasets/Generated_pH_unified_smiles_636.xlsx +3 -0
- conf/datasets/Kcat_combination_0918_wildtype_mutant.json +3 -0
- conf/datasets/Km_test_11722.pkl +3 -0
- conf/datasets/kcat_km_samples.xlsx +3 -0
- config.json +18 -0
- model/__init__.py +1 -0
- model/build_vocab.py +173 -0
- model/dataset.py +55 -0
- model/enumerator.py +223 -0
- model/pretrain_rnn.py +233 -0
- model/pretrain_trfm.py +174 -0
- model/sample.py +7 -0
- model/utils.py +194 -0
- requirements.txt +15 -0
- scripts/Preprocessing.py +148 -0
- scripts/UniKP_CBW.py +231 -0
- scripts/UniKP_CSW.py +226 -0
- scripts/UniKP_DMW.py +191 -0
- scripts/UniKP_Km.py +147 -0
- scripts/UniKP_LDS.py +220 -0
- scripts/UniKP_No_LDS.py +237 -0
- scripts/UniKP_kcat.py +180 -0
- scripts/UniKP_kcat_Km.py +144 -0
- scripts/UniKP_kcat_model.py +63 -0
- scripts/UniKP_pH.py +145 -0
- scripts/UniKP_pH_s2.py +186 -0
- scripts/UniKP_temperature.py +145 -0
- scripts/UniKP_temperature_s2.py +186 -0
- scripts/_bootstrap.py +8 -0
- scripts/build_corpus.py +23 -0
- scripts/demo_kcat.py +482 -0
- scripts/project_paths.py +23 -0
- scripts/smi2csv.py +36 -0
- weight/UniKP_model/.gitattributes +34 -0
- weight/UniKP_model/README.md +3 -0
- weight/UniKP_model/UniKP for Km.pkl +3 -0
- weight/UniKP_model/UniKP for kcat.pkl +3 -0
- weight/UniKP_model/UniKP for kcat_Km.pkl +3 -0
- weight/trfm_12_23000.pkl +3 -0
- weight/vocab.pkl +3 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,51 @@
|
|
| 1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 4 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bin.* filter=lfs diff=lfs merge=lfs -text
|
| 5 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 11 |
*.model filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
| 13 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 14 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 15 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
| 17 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 18 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 19 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 20 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 21 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 22 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 24 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 25 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
*.zstandard filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.db* filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.ark* filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
**/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
**/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
**/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*.gguf* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.ggml filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
*.llamafile* filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.pt2 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 46 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -textconf/Figures/UniKP.jpg filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
conf/Figures/UniKP.png filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
conf/datasets/Generated_pH_unified_smiles_636.xlsx filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
conf/datasets/Kcat_combination_0918_wildtype_mutant.json filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
conf/datasets/kcat_km_samples.xlsx filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: gpl-3.0
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
- zh
|
| 6 |
+
tags:
|
| 7 |
+
- OneScience
|
| 8 |
+
- life-science
|
| 9 |
+
- enzyme
|
| 10 |
+
- enzyme-kinetics
|
| 11 |
+
- UniKP
|
| 12 |
+
frameworks: PyTorch
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
<p align="center">
|
| 16 |
+
<strong><span style="font-size: 30px;">UniKP</span></strong>
|
| 17 |
+
</p>
|
| 18 |
+
|
| 19 |
+
# Model Introduction
|
| 20 |
+
|
| 21 |
+
UniKP is a unified framework for predicting enzyme kinetic parameters based on pretrained language models. Given a protein sequence and substrate structure, it can predict the enzyme turnover number $k_{cat}$, Michaelis constant $K_m$, and catalytic efficiency $k_{cat}/K_m$. UniKP uses a protein language model to extract enzyme sequence representations and combines them with substrate representations generated by a molecular language model to predict kinetic parameters.
|
| 22 |
+
|
| 23 |
+
Paper:
|
| 24 |
+
|
| 25 |
+
> **UniKP: a unified framework for the prediction of enzyme kinetic parameters**
|
| 26 |
+
> https://doi.org/10.1038/s41467-023-44113-1
|
| 27 |
+
|
| 28 |
+
# Model Description
|
| 29 |
+
|
| 30 |
+
UniKP takes the enzyme protein sequence and substrate SMILES as two separate inputs. On the protein side, ProtT5-XL-UniRef50 is used to extract sequence representations, while on the substrate side, a SMILES Transformer is used to generate molecular representations. The two feature representations are concatenated and then fed into trained regression models to predict $k_{cat}$, $K_m$, and $k_{cat}/K_m$, respectively.
|
| 31 |
+
|
| 32 |
+
# Use Cases
|
| 33 |
+
|
| 34 |
+
| Scenario | Description |
|
| 35 |
+
| --- | --- |
|
| 36 |
+
| Enzyme turnover number prediction | Predict $k_{cat}$ from a protein sequence and substrate SMILES |
|
| 37 |
+
| Michaelis constant prediction | Predict $K_m$ for an enzyme-substrate system |
|
| 38 |
+
| Catalytic efficiency prediction | Predict $k_{cat}/K_m$ |
|
| 39 |
+
| Enzyme discovery and screening | Predict and rank kinetic parameters for candidate enzyme sequences |
|
| 40 |
+
| Enzyme directed evolution | Compare predicted kinetic parameters of wild-type and mutant candidates |
|
| 41 |
+
| Environmental factor analysis | Use EF-UniKP-related implementations to investigate the effects of temperature and pH on $k_{cat}$ |
|
| 42 |
+
|
| 43 |
+
# Usage
|
| 44 |
+
|
| 45 |
+
## 1. Using OneCode
|
| 46 |
+
|
| 47 |
+
You can use the OneCode online environment for intelligent one-click AI4S programming:
|
| 48 |
+
|
| 49 |
+
[Try intelligent one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
|
| 50 |
+
|
| 51 |
+
## 2. Manual Installation and Usage
|
| 52 |
+
|
| 53 |
+
**Hardware Requirements**
|
| 54 |
+
|
| 55 |
+
- ProtT5-XL-UniRef50 is relatively large, so GPU/DCU acceleration is recommended for protein representation extraction.
|
| 56 |
+
- A CPU can be used for a small number of short sequences, but it will be significantly slower than an accelerator.
|
| 57 |
+
- For batch prediction or long-sequence tasks, it is recommended to reduce the batch size and adjust it according to the available device memory.
|
| 58 |
+
|
| 59 |
+
### Environment Setup
|
| 60 |
+
|
| 61 |
+
#### DCU Environment
|
| 62 |
+
|
| 63 |
+
```bash
|
| 64 |
+
# Activate DTK and CONDA first
|
| 65 |
+
conda create -n onescience311 python=3.11 -y
|
| 66 |
+
conda activate onescience311
|
| 67 |
+
|
| 68 |
+
pip install onescience[bio] \
|
| 69 |
+
-i http://mirrors.onescience.ai:3141/pypi/simple/ \
|
| 70 |
+
--trusted-host mirrors.onescience.ai
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
#### Environment Notes
|
| 74 |
+
- If you encounter missing dependencies or version incompatibilities during execution, refer to the dependency versions specified in `requirements.txt` and install or adjust the corresponding packages as needed.
|
| 75 |
+
|
| 76 |
+
### Model Weights
|
| 77 |
+
|
| 78 |
+
Complete UniKP inference requires three types of model resources:
|
| 79 |
+
|
| 80 |
+
1. ProtT5-XL-UniRef50;
|
| 81 |
+
2. UniKP regression models for $k_{cat}$, $K_m$, and $k_{cat}/K_m$;
|
| 82 |
+
3. The vocabulary and pretrained weights of the SMILES Transformer.
|
| 83 |
+
|
| 84 |
+
#### 1) ProtT5-XL-UniRef50
|
| 85 |
+
|
| 86 |
+
ProtT5-XL-UniRef50 needs to be downloaded separately:
|
| 87 |
+
|
| 88 |
+
```text
|
| 89 |
+
https://zenodo.org/records/4644188
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
It is recommended to place it as follows:
|
| 93 |
+
|
| 94 |
+
```text
|
| 95 |
+
UniKP/
|
| 96 |
+
└── weight/
|
| 97 |
+
└── prot_t5_xl_uniref50/
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
The current code will first try to use:
|
| 101 |
+
|
| 102 |
+
```python
|
| 103 |
+
T5Tokenizer.from_pretrained("weight/prot_t5_xl_uniref50")
|
| 104 |
+
T5EncoderModel.from_pretrained("weight/prot_t5_xl_uniref50")
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
If `weight/prot_t5_xl_uniref50` does not exist, the code will fall back to `"prot_t5_xl_uniref50"`. You can also adjust `scripts/project_paths.py` according to the actual local path.
|
| 108 |
+
|
| 109 |
+
#### 2) UniKP Regression Models
|
| 110 |
+
|
| 111 |
+
The official README provides download links for the models corresponding to the three tasks:
|
| 112 |
+
|
| 113 |
+
```text
|
| 114 |
+
https://huggingface.co/HanselYu/UniKP/tree/main
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
Typical files include:
|
| 118 |
+
|
| 119 |
+
```text
|
| 120 |
+
UniKP for kcat.pkl
|
| 121 |
+
UniKP for Km.pkl
|
| 122 |
+
UniKP for kcat_Km.pkl
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
- These files are included in `weight/UniKP_model`.
|
| 126 |
+
|
| 127 |
+
## 3. Quick Start
|
| 128 |
+
|
| 129 |
+
### Download the Model Package
|
| 130 |
+
|
| 131 |
+
```bash
|
| 132 |
+
hf download OneScience-Group/UniKP --local-dir ./UniKP
|
| 133 |
+
cd UniKP-main
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
- UniKP additionally depends on **ProtT5-XL-UniRef50** and the three types of **UniKP regression model weights**. Please prepare the required models according to the "Model Weights" section before proceeding.
|
| 137 |
+
- The SMILES Transformer-related code is located in `model/`. Before running, make sure that `weight/vocab.pkl` and `weight/trfm_12_23000.pkl` exist.
|
| 138 |
+
|
| 139 |
+
### Quick Verification
|
| 140 |
+
|
| 141 |
+
Check whether ProtT5 can be loaded locally:
|
| 142 |
+
|
| 143 |
+
```bash
|
| 144 |
+
python - <<'PY'
|
| 145 |
+
from transformers import T5Tokenizer, T5EncoderModel
|
| 146 |
+
path = "./weight/prot_t5_xl_uniref50"
|
| 147 |
+
T5Tokenizer.from_pretrained(path, do_lower_case=False)
|
| 148 |
+
T5EncoderModel.from_pretrained(path)
|
| 149 |
+
print("ProtT5 load OK")
|
| 150 |
+
PY
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
Check the regression model and SMILES Transformer resources:
|
| 154 |
+
|
| 155 |
+
```bash
|
| 156 |
+
ls -lh weight/UniKP_model/
|
| 157 |
+
ls -lh weight/vocab.pkl weight/trfm_12_23000.pkl
|
| 158 |
+
```
|
| 159 |
+
If all of the above checks pass, you can continue with the single-sample inference script:
|
| 160 |
+
|
| 161 |
+
```bash
|
| 162 |
+
python scripts/demo_kcat.py
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
# Example Data
|
| 166 |
+
|
| 167 |
+
The core inputs for UniKP inference are:
|
| 168 |
+
|
| 169 |
+
```text
|
| 170 |
+
Protein amino acid sequence
|
| 171 |
+
+
|
| 172 |
+
Substrate SMILES
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
Example:
|
| 176 |
+
|
| 177 |
+
| Input | Example |
|
| 178 |
+
| --- | --- |
|
| 179 |
+
| Protein sequence | `MSELMKLSAV...MAQR` |
|
| 180 |
+
| Substrate SMILES | `CC(O)O` |
|
| 181 |
+
|
| 182 |
+
The corresponding output can be:
|
| 183 |
+
|
| 184 |
+
```text
|
| 185 |
+
kcat
|
| 186 |
+
Km
|
| 187 |
+
kcat / Km
|
| 188 |
+
```
|
| 189 |
+
|
| 190 |
+
# Inference Examples
|
| 191 |
+
|
| 192 |
+
## Single-Sample kcat Prediction
|
| 193 |
+
|
| 194 |
+
Run the following command from the UniKP root directory:
|
| 195 |
+
|
| 196 |
+
```bash
|
| 197 |
+
python scripts/demo_kcat.py
|
| 198 |
+
```
|
| 199 |
+
|
| 200 |
+
This script reads an example protein sequence and substrate SMILES, extracts their representations using ProtT5-XL-UniRef50 and the SMILES Transformer, respectively, concatenates the representations, and then loads `weight/UniKP_model/UniKP for kcat.pkl` to perform kcat prediction.
|
| 201 |
+
|
| 202 |
+
The prediction result is printed to the terminal and saved as:
|
| 203 |
+
|
| 204 |
+
```text
|
| 205 |
+
UniKP_kcat_prediction.xlsx
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
By default, `demo_kcat.py` loads the kcat regression model:
|
| 209 |
+
|
| 210 |
+
```python
|
| 211 |
+
with open("weight/UniKP_model/UniKP for kcat.pkl", "rb") as f:
|
| 212 |
+
model = pickle.load(f)
|
| 213 |
+
```
|
| 214 |
+
|
| 215 |
+
To predict **Km**, replace the model path above with:
|
| 216 |
+
|
| 217 |
+
```python
|
| 218 |
+
with open("weight/UniKP_model/UniKP for Km.pkl", "rb") as f:
|
| 219 |
+
model = pickle.load(f)
|
| 220 |
+
```
|
| 221 |
+
|
| 222 |
+
To predict **kcat/Km**, replace it with:
|
| 223 |
+
|
| 224 |
+
```python
|
| 225 |
+
with open("weight/UniKP_model/UniKP for kcat_Km.pkl", "rb") as f:
|
| 226 |
+
model = pickle.load(f)
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
Except for the regression model, the protein representation extraction, SMILES representation extraction, feature concatenation, and prediction workflow remain unchanged. The model outputs are in `log10` space, and the script converts them back to the actual kinetic parameter values using `10 ** x`.
|
| 230 |
+
|
| 231 |
+
## Batch Prediction
|
| 232 |
+
|
| 233 |
+
The repository provides separate batch prediction scripts for kcat, Km, and kcat/Km:
|
| 234 |
+
|
| 235 |
+
```bash
|
| 236 |
+
python scripts/UniKP_kcat.py
|
| 237 |
+
python scripts/UniKP_Km.py
|
| 238 |
+
python scripts/UniKP_kcat_Km.py
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
The three scripts read the corresponding task data files and load the matching UniKP regression models to perform batch prediction on multiple protein sequences and substrate entries.
|
| 242 |
+
|
| 243 |
+
Before running, make sure that the data files for the corresponding task are located in `conf/datasets/`.
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
# Output Description
|
| 247 |
+
|
| 248 |
+
UniKP outputs the predicted values of the corresponding enzyme kinetic parameters.
|
| 249 |
+
|
| 250 |
+
| Parameter | Unit Used in the Official Example |
|
| 251 |
+
| --- | --- |
|
| 252 |
+
| $k_{cat}$ | s⁻¹ |
|
| 253 |
+
| $K_m$ | mM |
|
| 254 |
+
| $k_{cat}/K_m$ | s⁻¹·mM⁻¹ |
|
| 255 |
+
|
| 256 |
+
The internal predictions of the UniKP regression models are in `log10` space, so the output of `model.predict()` cannot be treated directly as the actual kinetic parameter values. Use:
|
| 257 |
+
|
| 258 |
+
```python
|
| 259 |
+
pred = model.predict(fused_vector)
|
| 260 |
+
pred_real = [10 ** x for x in pred]
|
| 261 |
+
```
|
| 262 |
+
|
| 263 |
+
The official example saves the results as:
|
| 264 |
+
|
| 265 |
+
```text
|
| 266 |
+
Kinetic_parameters_predicted_label.xlsx
|
| 267 |
+
```
|
| 268 |
+
|
| 269 |
+
# OneScience Official Information
|
| 270 |
+
|
| 271 |
+
| Platform | OneScience Main Repository | Skills Repository |
|
| 272 |
+
| --- | --- | --- |
|
| 273 |
+
| Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
|
| 274 |
+
| GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
# Citation and License
|
| 278 |
+
|
| 279 |
+
- Original UniKP paper: [UniKP: a unified framework for the prediction of enzyme kinetic parameters](https://doi.org/10.1038/s41467-023-44113-1).
|
| 280 |
+
- UniKP is licensed under the GNU General Public License version 3 (GPL-3.0).
|
| 281 |
+
- UniKP inference depends on third-party models and code such as ProtT5-XL-UniRef50 and the SMILES Transformer. Their respective licenses and terms of use must also be followed when using, modifying, or redistributing these resources.
|
| 282 |
+
- For research use, it is recommended to cite the original UniKP paper. If ProtT5 or the SMILES Transformer is used to generate representations, the corresponding projects should also be cited as required.
|
conf/Figures/UniKP.jpg
ADDED
|
Git LFS Details
|
conf/Figures/UniKP.png
ADDED
|
Git LFS Details
|
conf/datasets/Generated_degree_unified_smiles_572.xlsx
ADDED
|
Binary file (93.5 kB). View file
|
|
|
conf/datasets/Generated_pH_unified_smiles_636.xlsx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a3aaee418b09a2a7ff952f015d4af544121f376bd5a5ab2486cdfe8faafc011b
|
| 3 |
+
size 100403
|
conf/datasets/Kcat_combination_0918_wildtype_mutant.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:13643b0b36374f8d3f64d8b014882cf1b3b58946eeaae2b9dcd59e8b2c2d6719
|
| 3 |
+
size 12132719
|
conf/datasets/Km_test_11722.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:10f565a6b369e374d15c872ce732630de80a06e89dac17256e62b1dfaa75a790
|
| 3 |
+
size 3295007
|
conf/datasets/kcat_km_samples.xlsx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8a6488b25a0bb696ad656cc28e3a7bde18545d08f9fce6027dad2f389340ed53
|
| 3 |
+
size 5904797
|
config.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"directories": {
|
| 3 |
+
"conf": "conf",
|
| 4 |
+
"model": "model",
|
| 5 |
+
"scripts": "scripts",
|
| 6 |
+
"weight": "weight"
|
| 7 |
+
},
|
| 8 |
+
"paths": {
|
| 9 |
+
"datasets": "conf/datasets",
|
| 10 |
+
"figures": "conf/Figures",
|
| 11 |
+
"model_modules": "model",
|
| 12 |
+
"script_bootstrap": "scripts/_bootstrap.py",
|
| 13 |
+
"vocab": "weight/vocab.pkl",
|
| 14 |
+
"smiles_transformer_weight": "weight/trfm_12_23000.pkl",
|
| 15 |
+
"unikp_models": "weight/UniKP_model",
|
| 16 |
+
"prot_t5_model": "weight/prot_t5_xl_uniref50"
|
| 17 |
+
}
|
| 18 |
+
}
|
model/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Model and representation utilities used by UniKP scripts."""
|
model/build_vocab.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import pickle
|
| 3 |
+
from collections import Counter
|
| 4 |
+
|
| 5 |
+
from project_paths import VOCAB_PATH
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class TorchVocab(object):
|
| 9 |
+
"""
|
| 10 |
+
:property freqs: collections.Counter, コーパス中の単語の出現頻度を保持するオブジェクト
|
| 11 |
+
:property stoi: collections.defaultdict, string → id の対応を示す辞書
|
| 12 |
+
:property itos: collections.defaultdict, id → string の対応を示す辞書
|
| 13 |
+
"""
|
| 14 |
+
def __init__(self, counter, max_size=None, min_freq=1, specials=['<pad>', '<oov>'],
|
| 15 |
+
vectors=None, unk_init=None, vectors_cache=None):
|
| 16 |
+
"""
|
| 17 |
+
:param counter: collections.Counter, データ中に含まれる単語の頻度を計測するためのcounter
|
| 18 |
+
:param max_size: int, vocabularyの最大のサイズ. Noneの場合は最大値なし. defaultはNone
|
| 19 |
+
:param min_freq: int, vocabulary中の単語の最低出現頻度. この数以下の出現回数の単語はvocabularyに加えられない.
|
| 20 |
+
:param specials: list of str, vocabularyにあらかじめ登録するtoken
|
| 21 |
+
:param vectors: list of vectors, 事前学習済みのベクトル. ex)Vocab.load_vectors
|
| 22 |
+
"""
|
| 23 |
+
self.freqs = counter
|
| 24 |
+
counter = counter.copy()
|
| 25 |
+
min_freq = max(min_freq, 1)
|
| 26 |
+
|
| 27 |
+
self.itos = list(specials)
|
| 28 |
+
# special tokensの出現頻度はvocabulary作成の際にカウントされない
|
| 29 |
+
for tok in specials:
|
| 30 |
+
del counter[tok]
|
| 31 |
+
|
| 32 |
+
max_size = None if max_size is None else max_size + len(self.itos)
|
| 33 |
+
|
| 34 |
+
# まず頻度でソートし、次に文字順で並び替える
|
| 35 |
+
words_and_frequencies = sorted(counter.items(), key=lambda tup: tup[0])
|
| 36 |
+
words_and_frequencies.sort(key=lambda tup: tup[1], reverse=True)
|
| 37 |
+
|
| 38 |
+
# 出現頻度がmin_freq未満のものはvocabに加えない
|
| 39 |
+
for word, freq in words_and_frequencies:
|
| 40 |
+
if freq < min_freq or len(self.itos) == max_size:
|
| 41 |
+
break
|
| 42 |
+
self.itos.append(word)
|
| 43 |
+
|
| 44 |
+
# dictのk,vをいれかえてstoiを作成する
|
| 45 |
+
self.stoi = {tok: i for i, tok in enumerate(self.itos)}
|
| 46 |
+
|
| 47 |
+
self.vectors = None
|
| 48 |
+
if vectors is not None:
|
| 49 |
+
self.load_vectors(vectors, unk_init=unk_init, cache=vectors_cache)
|
| 50 |
+
else:
|
| 51 |
+
assert unk_init is None and vectors_cache is None
|
| 52 |
+
|
| 53 |
+
def __eq__(self, other):
|
| 54 |
+
if self.freqs != other.freqs:
|
| 55 |
+
return False
|
| 56 |
+
if self.stoi != other.stoi:
|
| 57 |
+
return False
|
| 58 |
+
if self.itos != other.itos:
|
| 59 |
+
return False
|
| 60 |
+
if self.vectors != other.vectors:
|
| 61 |
+
return False
|
| 62 |
+
return True
|
| 63 |
+
|
| 64 |
+
def __len__(self):
|
| 65 |
+
return len(self.itos)
|
| 66 |
+
|
| 67 |
+
def vocab_rerank(self):
|
| 68 |
+
self.stoi = {word: i for i, word in enumerate(self.itos)}
|
| 69 |
+
|
| 70 |
+
def extend(self, v, sort=False):
|
| 71 |
+
words = sorted(v.itos) if sort else v.itos
|
| 72 |
+
for w in words:
|
| 73 |
+
if w not in self.stoi:
|
| 74 |
+
self.itos.append(w)
|
| 75 |
+
self.stoi[w] = len(self.itos) - 1
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class Vocab(TorchVocab):
|
| 79 |
+
def __init__(self, counter, max_size=None, min_freq=1):
|
| 80 |
+
self.pad_index = 0
|
| 81 |
+
self.unk_index = 1
|
| 82 |
+
self.eos_index = 2
|
| 83 |
+
self.sos_index = 3
|
| 84 |
+
self.mask_index = 4
|
| 85 |
+
super().__init__(counter, specials=["<pad>", "<unk>", "<eos>", "<sos>", "<mask>"], max_size=max_size, min_freq=min_freq)
|
| 86 |
+
|
| 87 |
+
# override用
|
| 88 |
+
def to_seq(self, sentece, seq_len, with_eos=False, with_sos=False) -> list:
|
| 89 |
+
pass
|
| 90 |
+
|
| 91 |
+
# override用
|
| 92 |
+
def from_seq(self, seq, join=False, with_pad=False):
|
| 93 |
+
pass
|
| 94 |
+
|
| 95 |
+
@staticmethod
|
| 96 |
+
def load_vocab(vocab_path: str) -> 'Vocab':
|
| 97 |
+
with open(vocab_path, "rb") as f:
|
| 98 |
+
return pickle.load(f)
|
| 99 |
+
|
| 100 |
+
def save_vocab(self, vocab_path):
|
| 101 |
+
with open(vocab_path, "wb") as f:
|
| 102 |
+
pickle.dump(self, f)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
# テキストファイルからvocabを作成する
|
| 106 |
+
class WordVocab(Vocab):
|
| 107 |
+
def __init__(self, texts, max_size=None, min_freq=1):
|
| 108 |
+
print("Building Vocab")
|
| 109 |
+
counter = Counter()
|
| 110 |
+
for line in texts:
|
| 111 |
+
if isinstance(line, list):
|
| 112 |
+
words = line
|
| 113 |
+
else:
|
| 114 |
+
words = line.replace("\n", "").replace("\t", "").split()
|
| 115 |
+
|
| 116 |
+
for word in words:
|
| 117 |
+
counter[word] += 1
|
| 118 |
+
super().__init__(counter, max_size=max_size, min_freq=min_freq)
|
| 119 |
+
|
| 120 |
+
def to_seq(self, sentence, seq_len=None, with_eos=False, with_sos=False, with_len=False):
|
| 121 |
+
if isinstance(sentence, str):
|
| 122 |
+
sentence = sentence.split()
|
| 123 |
+
|
| 124 |
+
seq = [self.stoi.get(word, self.unk_index) for word in sentence]
|
| 125 |
+
|
| 126 |
+
if with_eos:
|
| 127 |
+
seq += [self.eos_index] # this would be index 1
|
| 128 |
+
if with_sos:
|
| 129 |
+
seq = [self.sos_index] + seq
|
| 130 |
+
|
| 131 |
+
origin_seq_len = len(seq)
|
| 132 |
+
|
| 133 |
+
if seq_len is None:
|
| 134 |
+
pass
|
| 135 |
+
elif len(seq) <= seq_len:
|
| 136 |
+
seq += [self.pad_index for _ in range(seq_len - len(seq))]
|
| 137 |
+
else:
|
| 138 |
+
seq = seq[:seq_len]
|
| 139 |
+
|
| 140 |
+
return (seq, origin_seq_len) if with_len else seq
|
| 141 |
+
|
| 142 |
+
def from_seq(self, seq, join=False, with_pad=False):
|
| 143 |
+
words = [self.itos[idx]
|
| 144 |
+
if idx < len(self.itos)
|
| 145 |
+
else "<%d>" % idx
|
| 146 |
+
for idx in seq
|
| 147 |
+
if not with_pad or idx != self.pad_index]
|
| 148 |
+
|
| 149 |
+
return " ".join(words) if join else words
|
| 150 |
+
|
| 151 |
+
@staticmethod
|
| 152 |
+
def load_vocab(vocab_path: str) -> 'WordVocab':
|
| 153 |
+
with open(vocab_path, "rb") as f:
|
| 154 |
+
return pickle.load(f)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def main():
|
| 158 |
+
parser = argparse.ArgumentParser(description='Build a vocabulary pickle')
|
| 159 |
+
parser.add_argument('--corpus_path', '-c', type=str, default='data/chembl24_corpus.txt', help='path to th ecorpus')
|
| 160 |
+
parser.add_argument('--out_path', '-o', type=str, default=str(VOCAB_PATH), help='output file')
|
| 161 |
+
parser.add_argument('--min_freq', '-m', type=int, default=500, help='minimum frequency for vocabulary')
|
| 162 |
+
parser.add_argument('--vocab_size', '-v', type=int, default=None, help='max vocabulary size')
|
| 163 |
+
parser.add_argument('--encoding', '-e', type=str, default='utf-8', help='encoding of corpus')
|
| 164 |
+
args = parser.parse_args()
|
| 165 |
+
|
| 166 |
+
with open(args.corpus_path, "r", encoding=args.encoding) as f:
|
| 167 |
+
vocab = WordVocab(f, max_size=args.vocab_size, min_freq=args.min_freq)
|
| 168 |
+
|
| 169 |
+
print("VOCAB SIZE:", len(vocab))
|
| 170 |
+
vocab.save_vocab(args.out_path)
|
| 171 |
+
|
| 172 |
+
if __name__=='__main__':
|
| 173 |
+
main()
|
model/dataset.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import torch
|
| 4 |
+
from torch.utils.data import Dataset, DataLoader
|
| 5 |
+
|
| 6 |
+
from .enumerator import SmilesEnumerator
|
| 7 |
+
from .utils import split
|
| 8 |
+
|
| 9 |
+
PAD = 0
|
| 10 |
+
MAX_LEN = 220
|
| 11 |
+
|
| 12 |
+
class Randomizer(object):
|
| 13 |
+
|
| 14 |
+
def __init__(self):
|
| 15 |
+
self.sme = SmilesEnumerator()
|
| 16 |
+
|
| 17 |
+
def __call__(self, sm):
|
| 18 |
+
sm_r = self.sme.randomize_smiles(sm) # Random transoform
|
| 19 |
+
if sm_r is None:
|
| 20 |
+
sm_spaced = split(sm) # Spacing
|
| 21 |
+
else:
|
| 22 |
+
sm_spaced = split(sm_r) # Spacing
|
| 23 |
+
sm_split = sm_spaced.split()
|
| 24 |
+
if len(sm_split)<=MAX_LEN - 2:
|
| 25 |
+
return sm_split # List
|
| 26 |
+
else:
|
| 27 |
+
return split(sm).split()
|
| 28 |
+
|
| 29 |
+
def random_transform(self, sm):
|
| 30 |
+
'''
|
| 31 |
+
function: Random transformation for SMILES. It may take some time.
|
| 32 |
+
input: A SMILES
|
| 33 |
+
output: A randomized SMILES
|
| 34 |
+
'''
|
| 35 |
+
return self.sme.randomize_smiles(sm)
|
| 36 |
+
|
| 37 |
+
class Seq2seqDataset(Dataset):
|
| 38 |
+
|
| 39 |
+
def __init__(self, smiles, vocab, seq_len=220, transform=Randomizer()):
|
| 40 |
+
self.smiles = smiles
|
| 41 |
+
self.vocab = vocab
|
| 42 |
+
self.seq_len = seq_len
|
| 43 |
+
self.transform = transform
|
| 44 |
+
|
| 45 |
+
def __len__(self):
|
| 46 |
+
return len(self.smiles)
|
| 47 |
+
|
| 48 |
+
def __getitem__(self, item):
|
| 49 |
+
sm = self.smiles[item]
|
| 50 |
+
sm = self.transform(sm) # List
|
| 51 |
+
content = [self.vocab.stoi.get(token, self.vocab.unk_index) for token in sm]
|
| 52 |
+
X = [self.vocab.sos_index] + content + [self.vocab.eos_index]
|
| 53 |
+
padding = [self.vocab.pad_index]*(self.seq_len - len(X))
|
| 54 |
+
X.extend(padding)
|
| 55 |
+
return torch.tensor(X)
|
model/enumerator.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Experimental Class for Smiles Enumeration, Iterator and SmilesIterator adapted from Keras 1.2.2
|
| 2 |
+
from rdkit import Chem
|
| 3 |
+
import numpy as np
|
| 4 |
+
import threading
|
| 5 |
+
|
| 6 |
+
class Iterator(object):
|
| 7 |
+
"""Abstract base class for data iterators.
|
| 8 |
+
|
| 9 |
+
# Arguments
|
| 10 |
+
n: Integer, total number of samples in the dataset to loop over.
|
| 11 |
+
batch_size: Integer, size of a batch.
|
| 12 |
+
shuffle: Boolean, whether to shuffle the data between epochs.
|
| 13 |
+
seed: Random seeding for data shuffling.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
def __init__(self, n, batch_size, shuffle, seed):
|
| 17 |
+
self.n = n
|
| 18 |
+
self.batch_size = batch_size
|
| 19 |
+
self.shuffle = shuffle
|
| 20 |
+
self.batch_index = 0
|
| 21 |
+
self.total_batches_seen = 0
|
| 22 |
+
self.lock = threading.Lock()
|
| 23 |
+
self.index_generator = self._flow_index(n, batch_size, shuffle, seed)
|
| 24 |
+
if n < batch_size:
|
| 25 |
+
raise ValueError('Input data length is shorter than batch_size\nAdjust batch_size')
|
| 26 |
+
|
| 27 |
+
def reset(self):
|
| 28 |
+
self.batch_index = 0
|
| 29 |
+
|
| 30 |
+
def _flow_index(self, n, batch_size=32, shuffle=False, seed=None):
|
| 31 |
+
# Ensure self.batch_index is 0.
|
| 32 |
+
self.reset()
|
| 33 |
+
while 1:
|
| 34 |
+
if seed is not None:
|
| 35 |
+
np.random.seed(seed + self.total_batches_seen)
|
| 36 |
+
if self.batch_index == 0:
|
| 37 |
+
index_array = np.arange(n)
|
| 38 |
+
if shuffle:
|
| 39 |
+
index_array = np.random.permutation(n)
|
| 40 |
+
|
| 41 |
+
current_index = (self.batch_index * batch_size) % n
|
| 42 |
+
if n > current_index + batch_size:
|
| 43 |
+
current_batch_size = batch_size
|
| 44 |
+
self.batch_index += 1
|
| 45 |
+
else:
|
| 46 |
+
current_batch_size = n - current_index
|
| 47 |
+
self.batch_index = 0
|
| 48 |
+
self.total_batches_seen += 1
|
| 49 |
+
yield (index_array[current_index: current_index + current_batch_size],
|
| 50 |
+
current_index, current_batch_size)
|
| 51 |
+
|
| 52 |
+
def __iter__(self):
|
| 53 |
+
# Needed if we want to do something like:
|
| 54 |
+
# for x, y in data_gen.flow(...):
|
| 55 |
+
return self
|
| 56 |
+
|
| 57 |
+
def __next__(self, *args, **kwargs):
|
| 58 |
+
return self.next(*args, **kwargs)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class SmilesIterator(Iterator):
|
| 64 |
+
"""Iterator yielding data from a SMILES array.
|
| 65 |
+
|
| 66 |
+
# Arguments
|
| 67 |
+
x: Numpy array of SMILES input data.
|
| 68 |
+
y: Numpy array of targets data.
|
| 69 |
+
smiles_data_generator: Instance of `SmilesEnumerator`
|
| 70 |
+
to use for random SMILES generation.
|
| 71 |
+
batch_size: Integer, size of a batch.
|
| 72 |
+
shuffle: Boolean, whether to shuffle the data between epochs.
|
| 73 |
+
seed: Random seed for data shuffling.
|
| 74 |
+
dtype: dtype to use for returned batch. Set to keras.backend.floatx if using Keras
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
def __init__(self, x, y, smiles_data_generator,
|
| 78 |
+
batch_size=32, shuffle=False, seed=None,
|
| 79 |
+
dtype=np.float32
|
| 80 |
+
):
|
| 81 |
+
if y is not None and len(x) != len(y):
|
| 82 |
+
raise ValueError('X (images tensor) and y (labels) '
|
| 83 |
+
'should have the same length. '
|
| 84 |
+
'Found: X.shape = %s, y.shape = %s' %
|
| 85 |
+
(np.asarray(x).shape, np.asarray(y).shape))
|
| 86 |
+
|
| 87 |
+
self.x = np.asarray(x)
|
| 88 |
+
|
| 89 |
+
if y is not None:
|
| 90 |
+
self.y = np.asarray(y)
|
| 91 |
+
else:
|
| 92 |
+
self.y = None
|
| 93 |
+
self.smiles_data_generator = smiles_data_generator
|
| 94 |
+
self.dtype = dtype
|
| 95 |
+
super(SmilesIterator, self).__init__(x.shape[0], batch_size, shuffle, seed)
|
| 96 |
+
|
| 97 |
+
def next(self):
|
| 98 |
+
"""For python 2.x.
|
| 99 |
+
|
| 100 |
+
# Returns
|
| 101 |
+
The next batch.
|
| 102 |
+
"""
|
| 103 |
+
# Keeps under lock only the mechanism which advances
|
| 104 |
+
# the indexing of each batch.
|
| 105 |
+
with self.lock:
|
| 106 |
+
index_array, current_index, current_batch_size = next(self.index_generator)
|
| 107 |
+
# The transformation of images is not under thread lock
|
| 108 |
+
# so it can be done in parallel
|
| 109 |
+
batch_x = np.zeros(tuple([current_batch_size] + [ self.smiles_data_generator.pad, self.smiles_data_generator._charlen]), dtype=self.dtype)
|
| 110 |
+
for i, j in enumerate(index_array):
|
| 111 |
+
smiles = self.x[j:j+1]
|
| 112 |
+
x = self.smiles_data_generator.transform(smiles)
|
| 113 |
+
batch_x[i] = x
|
| 114 |
+
|
| 115 |
+
if self.y is None:
|
| 116 |
+
return batch_x
|
| 117 |
+
batch_y = self.y[index_array]
|
| 118 |
+
return batch_x, batch_y
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
class SmilesEnumerator(object):
|
| 122 |
+
"""SMILES Enumerator, vectorizer and devectorizer
|
| 123 |
+
|
| 124 |
+
#Arguments
|
| 125 |
+
charset: string containing the characters for the vectorization
|
| 126 |
+
can also be generated via the .fit() method
|
| 127 |
+
pad: Length of the vectorization
|
| 128 |
+
leftpad: Add spaces to the left of the SMILES
|
| 129 |
+
isomericSmiles: Generate SMILES containing information about stereogenic centers
|
| 130 |
+
enum: Enumerate the SMILES during transform
|
| 131 |
+
canonical: use canonical SMILES during transform (overrides enum)
|
| 132 |
+
"""
|
| 133 |
+
def __init__(self, charset = '@C)(=cOn1S2/H[N]\\', pad=120, leftpad=True, isomericSmiles=True, enum=True, canonical=False):
|
| 134 |
+
self._charset = None
|
| 135 |
+
self.charset = charset
|
| 136 |
+
self.pad = pad
|
| 137 |
+
self.leftpad = leftpad
|
| 138 |
+
self.isomericSmiles = isomericSmiles
|
| 139 |
+
self.enumerate = enum
|
| 140 |
+
self.canonical = canonical
|
| 141 |
+
|
| 142 |
+
@property
|
| 143 |
+
def charset(self):
|
| 144 |
+
return self._charset
|
| 145 |
+
|
| 146 |
+
@charset.setter
|
| 147 |
+
def charset(self, charset):
|
| 148 |
+
self._charset = charset
|
| 149 |
+
self._charlen = len(charset)
|
| 150 |
+
self._char_to_int = dict((c,i) for i,c in enumerate(charset))
|
| 151 |
+
self._int_to_char = dict((i,c) for i,c in enumerate(charset))
|
| 152 |
+
|
| 153 |
+
def fit(self, smiles, extra_chars=[], extra_pad = 5):
|
| 154 |
+
"""Performs extraction of the charset and length of a SMILES datasets and sets self.pad and self.charset
|
| 155 |
+
|
| 156 |
+
#Arguments
|
| 157 |
+
smiles: Numpy array or Pandas series containing smiles as strings
|
| 158 |
+
extra_chars: List of extra chars to add to the charset (e.g. "\\\\" when "/" is present)
|
| 159 |
+
extra_pad: Extra padding to add before or after the SMILES vectorization
|
| 160 |
+
"""
|
| 161 |
+
charset = set("".join(list(smiles)))
|
| 162 |
+
#print(charset)
|
| 163 |
+
self.charset = "".join(charset.union(set(extra_chars)))
|
| 164 |
+
#print(self.charset)
|
| 165 |
+
self.pad = max([len(smile) for smile in smiles]) + extra_pad
|
| 166 |
+
|
| 167 |
+
def randomize_smiles(self, smiles):
|
| 168 |
+
"""Perform a randomization of a SMILES string
|
| 169 |
+
must be RDKit sanitizable"""
|
| 170 |
+
m = Chem.MolFromSmiles(smiles)
|
| 171 |
+
if m is None:
|
| 172 |
+
return None # Invalid SMILES
|
| 173 |
+
ans = list(range(m.GetNumAtoms()))
|
| 174 |
+
np.random.shuffle(ans)
|
| 175 |
+
nm = Chem.RenumberAtoms(m,ans)
|
| 176 |
+
return Chem.MolToSmiles(nm, canonical=self.canonical, isomericSmiles=self.isomericSmiles)
|
| 177 |
+
|
| 178 |
+
def transform(self, smiles):
|
| 179 |
+
"""Perform an enumeration (randomization) and vectorization of a Numpy array of smiles strings
|
| 180 |
+
#Arguments
|
| 181 |
+
smiles: Numpy array or Pandas series containing smiles as strings
|
| 182 |
+
"""
|
| 183 |
+
one_hot = np.zeros((smiles.shape[0], self.pad, self._charlen),dtype=np.int8)
|
| 184 |
+
|
| 185 |
+
if self.leftpad:
|
| 186 |
+
#print(smiles)
|
| 187 |
+
for i,ss in enumerate(smiles):
|
| 188 |
+
if self.enumerate:
|
| 189 |
+
ss = self.randomize_smiles(ss)
|
| 190 |
+
l = len(ss)
|
| 191 |
+
#print("???", ss)
|
| 192 |
+
diff = self.pad - l
|
| 193 |
+
for j,c in enumerate(ss):
|
| 194 |
+
one_hot[i,j+diff,self._char_to_int[c]] = 1
|
| 195 |
+
return one_hot
|
| 196 |
+
else:
|
| 197 |
+
for i,ss in enumerate(smiles):
|
| 198 |
+
if self.enumerate:
|
| 199 |
+
ss = self.randomize_smiles(ss)
|
| 200 |
+
for j,c in enumerate(ss):
|
| 201 |
+
one_hot[i,j,self._char_to_int[c]] = 1
|
| 202 |
+
return one_hot
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
def reverse_transform(self, vect):
|
| 206 |
+
""" Performs a conversion of a vectorized SMILES to a smiles strings
|
| 207 |
+
charset must be the same as used for vectorization.
|
| 208 |
+
#Arguments
|
| 209 |
+
vect: Numpy array of vectorized SMILES.
|
| 210 |
+
"""
|
| 211 |
+
smiles = []
|
| 212 |
+
for v in vect:
|
| 213 |
+
#mask v
|
| 214 |
+
v=v[v.sum(axis=1)==1]
|
| 215 |
+
#Find one hot encoded index with argmax, translate to char and join to string
|
| 216 |
+
smile = "".join(self._int_to_char[i] for i in v.argmax(axis=1))
|
| 217 |
+
smiles.append(smile)
|
| 218 |
+
return np.array(smiles)
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
|
model/pretrain_rnn.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import random
|
| 3 |
+
import numpy as np
|
| 4 |
+
from tqdm import tqdm
|
| 5 |
+
from torch.utils.data import DataLoader
|
| 6 |
+
import os
|
| 7 |
+
import math
|
| 8 |
+
import argparse
|
| 9 |
+
import torch
|
| 10 |
+
from torch import nn
|
| 11 |
+
from torch import optim
|
| 12 |
+
from torch.autograd import Variable
|
| 13 |
+
from torch.nn.utils import clip_grad_norm_
|
| 14 |
+
from torch.nn import functional as F
|
| 15 |
+
from .build_vocab import WordVocab
|
| 16 |
+
from .dataset import Seq2seqDataset
|
| 17 |
+
from project_paths import VOCAB_PATH
|
| 18 |
+
|
| 19 |
+
PAD = 0
|
| 20 |
+
UNK = 1
|
| 21 |
+
EOS = 2
|
| 22 |
+
SOS = 3
|
| 23 |
+
MASK = 4
|
| 24 |
+
|
| 25 |
+
class Encoder(nn.Module):
|
| 26 |
+
def __init__(self, input_size, embed_size, hidden_size,
|
| 27 |
+
n_layers=1, dropout=0.5):
|
| 28 |
+
super(Encoder, self).__init__()
|
| 29 |
+
self.input_size = input_size
|
| 30 |
+
self.hidden_size = hidden_size
|
| 31 |
+
self.embed_size = embed_size
|
| 32 |
+
self.embed = nn.Embedding(input_size, embed_size)
|
| 33 |
+
self.gru = nn.GRU(embed_size, hidden_size, n_layers,
|
| 34 |
+
dropout=dropout, bidirectional=True)
|
| 35 |
+
|
| 36 |
+
def forward(self, src, hidden=None):
|
| 37 |
+
# src: (T,B)
|
| 38 |
+
embedded = self.embed(src)# (T,B,H)
|
| 39 |
+
outputs, hidden = self.gru(embedded, hidden) # (T,B,2H), (2L,B,H)
|
| 40 |
+
# sum bidirectional outputs
|
| 41 |
+
outputs = (outputs[:, :, :self.hidden_size] +
|
| 42 |
+
outputs[:, :, self.hidden_size:])
|
| 43 |
+
return outputs, hidden # (T,B,H), (2L,B,H)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class Attention(nn.Module):
|
| 47 |
+
def __init__(self, hidden_size):
|
| 48 |
+
super(Attention, self).__init__()
|
| 49 |
+
self.hidden_size = hidden_size
|
| 50 |
+
self.attn = nn.Linear(self.hidden_size * 2, hidden_size)
|
| 51 |
+
self.v = nn.Parameter(torch.rand(hidden_size))
|
| 52 |
+
stdv = 1. / math.sqrt(self.v.size(0))
|
| 53 |
+
self.v.data.uniform_(-stdv, stdv)
|
| 54 |
+
|
| 55 |
+
def forward(self, hidden, encoder_outputs):
|
| 56 |
+
timestep = encoder_outputs.size(0)
|
| 57 |
+
h = hidden.repeat(timestep, 1, 1).transpose(0, 1)
|
| 58 |
+
encoder_outputs = encoder_outputs.transpose(0, 1) # [B*T*H]
|
| 59 |
+
attn_energies = self.score(h, encoder_outputs)
|
| 60 |
+
return F.relu(attn_energies).unsqueeze(1)
|
| 61 |
+
|
| 62 |
+
def score(self, hidden, encoder_outputs):
|
| 63 |
+
# [B*T*2H]->[B*T*H]
|
| 64 |
+
energy = F.softmax(self.attn(torch.cat([hidden, encoder_outputs], 2)))
|
| 65 |
+
energy = energy.transpose(1, 2) # [B*H*T]
|
| 66 |
+
v = self.v.repeat(encoder_outputs.size(0), 1).unsqueeze(1) # [B*1*H]
|
| 67 |
+
energy = torch.bmm(v, energy) # [B*1*T]
|
| 68 |
+
return energy.squeeze(1) # [B*T]
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class Decoder(nn.Module):
|
| 72 |
+
def __init__(self, embed_size, hidden_size, output_size,
|
| 73 |
+
n_layers=1, dropout=0.2):
|
| 74 |
+
super(Decoder, self).__init__()
|
| 75 |
+
self.embed_size = embed_size
|
| 76 |
+
self.hidden_size = hidden_size
|
| 77 |
+
self.output_size = output_size
|
| 78 |
+
self.n_layers = n_layers
|
| 79 |
+
|
| 80 |
+
self.embed = nn.Embedding(output_size, embed_size)
|
| 81 |
+
self.dropout = nn.Dropout(dropout, inplace=True)
|
| 82 |
+
self.attention = Attention(hidden_size)
|
| 83 |
+
self.gru = nn.GRU(hidden_size + embed_size, hidden_size,
|
| 84 |
+
n_layers, dropout=dropout)
|
| 85 |
+
self.out = nn.Linear(hidden_size * 2, output_size)
|
| 86 |
+
|
| 87 |
+
def forward(self, input, last_hidden, encoder_outputs):
|
| 88 |
+
# Get the embedding of the current input word (last output word)
|
| 89 |
+
embedded = self.embed(input).unsqueeze(0) # (1,B,N)
|
| 90 |
+
embedded = self.dropout(embedded)
|
| 91 |
+
# Calculate attention weights and apply to encoder outputs
|
| 92 |
+
attn_weights = self.attention(last_hidden[-1], encoder_outputs)
|
| 93 |
+
context = attn_weights.bmm(encoder_outputs.transpose(0, 1)) # (B,1,N)
|
| 94 |
+
context = context.transpose(0, 1) # (1,B,N)
|
| 95 |
+
# Combine embedded input word and attended context, run through RNN
|
| 96 |
+
rnn_input = torch.cat([embedded, context], 2)
|
| 97 |
+
output, hidden = self.gru(rnn_input, last_hidden)
|
| 98 |
+
output = output.squeeze(0) # (1,B,N) -> (B,N)
|
| 99 |
+
context = context.squeeze(0)
|
| 100 |
+
output = self.out(torch.cat([output, context], 1))
|
| 101 |
+
output = F.log_softmax(output, dim=1) # log???
|
| 102 |
+
return output, hidden, attn_weights
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class RNNSeq2Seq(nn.Module):
|
| 106 |
+
def __init__(self, in_size, hidden_size, out_size, n_layers):
|
| 107 |
+
super(RNNSeq2Seq, self).__init__()
|
| 108 |
+
self.encoder = Encoder(in_size, hidden_size, hidden_size, n_layers)
|
| 109 |
+
self.decoder = Decoder(hidden_size, hidden_size, out_size, n_layers)
|
| 110 |
+
|
| 111 |
+
def forward(self, src, trg, teacher_forcing_ratio=0.5): # (T,B)
|
| 112 |
+
batch_size = src.size(1)
|
| 113 |
+
max_len = trg.size(0)
|
| 114 |
+
vocab_size = self.decoder.output_size
|
| 115 |
+
outputs = Variable(torch.zeros(max_len, batch_size, vocab_size)).cuda() # (T,B,V)
|
| 116 |
+
encoder_output, hidden = self.encoder(src) # (T,B,H), (2L,B,H)
|
| 117 |
+
hidden = hidden[:self.decoder.n_layers] # (L,B,H)
|
| 118 |
+
output = Variable(trg.data[0, :]) # sos
|
| 119 |
+
for t in range(1, max_len):
|
| 120 |
+
output, hidden, attn_weights = self.decoder(output, hidden, encoder_output) # (B,V), (L,B,H)
|
| 121 |
+
outputs[t] = output
|
| 122 |
+
is_teacher = random.random() < teacher_forcing_ratio
|
| 123 |
+
top1 = output.data.max(dim=1)[1] # (B)
|
| 124 |
+
output = Variable(trg.data[t] if is_teacher else top1).cuda()
|
| 125 |
+
return outputs # (T,B,V)
|
| 126 |
+
|
| 127 |
+
def _encode(self, src):
|
| 128 |
+
# src: (T,B)
|
| 129 |
+
embedded = self.encoder.embed(src)# (T,B,H)
|
| 130 |
+
_, hidden = self.encoder.gru(embedded, None) # (T,B,2H), (2L,B,H)
|
| 131 |
+
hidden = hidden.detach().numpy()
|
| 132 |
+
return np.hstack(hidden[2:]) #(B,4H)
|
| 133 |
+
|
| 134 |
+
def encode(self, src):
|
| 135 |
+
# src: (T,B)
|
| 136 |
+
batch_size = src.shape[1]
|
| 137 |
+
if batch_size<=100:
|
| 138 |
+
return self._encode(src)
|
| 139 |
+
else: # Batch is too large to load
|
| 140 |
+
print('There are {:d} molecules. It will take a little time.'.format(batch_size))
|
| 141 |
+
st,ed = 0,100
|
| 142 |
+
out = self._encode(src[:,st:ed]) # (B,4H)
|
| 143 |
+
while ed<batch_size:
|
| 144 |
+
st += 100
|
| 145 |
+
ed += 100
|
| 146 |
+
out = np.concatenate([out, self._encode(src[:,st:ed])], axis=0)
|
| 147 |
+
return out
|
| 148 |
+
|
| 149 |
+
def parse_arguments():
|
| 150 |
+
parser = argparse.ArgumentParser(description='Hyperparams')
|
| 151 |
+
parser.add_argument('--n_epoch', '-e', type=int, default=20, help='number of epochs')
|
| 152 |
+
parser.add_argument('--vocab', '-v', type=str, default=str(VOCAB_PATH), help='vocabulary (.pkl)')
|
| 153 |
+
parser.add_argument('--train_data', type=str, default='data/chembl24_bert_train.csv', help='train corpus (.csv)')
|
| 154 |
+
parser.add_argument('--test_data', type=str, default='data/chembl24_bert_test.csv', help='test corpus (.csv)')
|
| 155 |
+
parser.add_argument('--out-dir', '-o', type=str, default='../result', help='output directory')
|
| 156 |
+
parser.add_argument('--name', '-n', type=str, default='ST', help='model name')
|
| 157 |
+
parser.add_argument('--seq_len', type=int, default=220, help='maximum length of the paired seqence')
|
| 158 |
+
parser.add_argument('--batch_size', '-b', type=int, default=16, help='batch size')
|
| 159 |
+
parser.add_argument('--n_worker', '-w', type=int, default=16, help='number of workers')
|
| 160 |
+
parser.add_argument('--hidden', type=int, default=256, help='length of hidden vector')
|
| 161 |
+
parser.add_argument('--n_layer', '-l', type=int, default=8, help='number of layers')
|
| 162 |
+
parser.add_argument('--n_head', type=int, default=8, help='number of attention heads')
|
| 163 |
+
parser.add_argument('--dropout', '-d', type=float, default=0.1, help='dropout rate')
|
| 164 |
+
parser.add_argument('--lr', type=float, default=1e-4, help='Adam learning rate')
|
| 165 |
+
parser.add_argument('--lr-decay', type=int, default=50000, help='lr decay step size')
|
| 166 |
+
parser.add_argument('--log-freq', type=int, default=100, help='log frequency')
|
| 167 |
+
parser.add_argument('--gpu', metavar='N', type=int, nargs='+', help='list of GPU IDs to use')
|
| 168 |
+
parser.add_argument('--checkpoint', '-c', type=str, default=None, help='Parameter to load')
|
| 169 |
+
parser.add_argument('-grad_clip', type=float, default=10.0, help='in case of gradient explosion')
|
| 170 |
+
return parser.parse_args()
|
| 171 |
+
|
| 172 |
+
def evaluate(model, val_loader, vocab):
|
| 173 |
+
model.eval()
|
| 174 |
+
total_loss = 0
|
| 175 |
+
for b, data in enumerate(val_loader):
|
| 176 |
+
sm1, sm2 = torch.t(data[0].cuda()), torch.t(data[1].cuda()) # (T,B)
|
| 177 |
+
with torch.no_grad():
|
| 178 |
+
output = model(sm1, sm2, teacher_forcing_ratio=0.0) # (T,B,V)
|
| 179 |
+
loss = F.nll_loss(output[1:].view(-1, len(vocab)),
|
| 180 |
+
sm2[1:].contiguous().view(-1),
|
| 181 |
+
ignore_index=PAD)
|
| 182 |
+
total_loss += loss.item()
|
| 183 |
+
return total_loss / len(val_loader)
|
| 184 |
+
|
| 185 |
+
def main():
|
| 186 |
+
args = parse_arguments()
|
| 187 |
+
hidden_size = 256
|
| 188 |
+
embed_size = 256
|
| 189 |
+
assert torch.cuda.is_available()
|
| 190 |
+
|
| 191 |
+
vocab = WordVocab.load_vocab(args.vocab)
|
| 192 |
+
print("[!] Instantiating models...")
|
| 193 |
+
encoder = Encoder(len(vocab), embed_size, hidden_size, n_layers=3, dropout=0.5)
|
| 194 |
+
decoder = Decoder(embed_size, hidden_size, len(vocab), n_layers=3, dropout=0.5)
|
| 195 |
+
model = RNNSeq2Seq(encoder, decoder).cuda()
|
| 196 |
+
optimizer = optim.Adam(model.parameters(), lr=args.lr)
|
| 197 |
+
train_dataset = Seq2seqDataset(args.train_data, vocab)
|
| 198 |
+
train_loader = DataLoader(train_dataset, batch_size=args.batch_size, shuffle=True, num_workers=args.n_worker)
|
| 199 |
+
val_dataset = Seq2seqDataset(args.test_data, vocab, is_train=False)
|
| 200 |
+
val_loader = DataLoader(val_dataset, batch_size=args.batch_size, shuffle=False, num_workers=args.n_worker)
|
| 201 |
+
print(model)
|
| 202 |
+
print('Total parameters:', sum(p.numel() for p in model.parameters()))
|
| 203 |
+
|
| 204 |
+
best_loss = None
|
| 205 |
+
for e in range(1, args.n_epoch):
|
| 206 |
+
for b,data in tqdm(enumerate(train_loader)):
|
| 207 |
+
model.train()
|
| 208 |
+
sm1, sm2 = torch.t(data[0].cuda()), torch.t(data[1].cuda()) # (T,B)
|
| 209 |
+
optimizer.zero_grad()
|
| 210 |
+
output = model(sm1, sm2, teacher_forcing_ratio=1.0) # (T,B,V)
|
| 211 |
+
loss = F.nll_loss(output[1:].view(-1, len(vocab)),
|
| 212 |
+
sm2[1:].contiguous().view(-1), ignore_index=PAD)
|
| 213 |
+
loss.backward()
|
| 214 |
+
clip_grad_norm_(model.parameters(), args.grad_clip)
|
| 215 |
+
optimizer.step()
|
| 216 |
+
if b%100==0:
|
| 217 |
+
print('Train {:3d}: iter {:5d} | loss {:.3f} | ppl {:.3f}'.format(e, b, loss.item(), math.exp(loss.item())))
|
| 218 |
+
if b%1000==0:
|
| 219 |
+
loss = evaluate(model, val_loader, vocab)
|
| 220 |
+
print('Val {:3d}: iter {:5d} | loss {:.3f} | ppl {:.3f}'.format(e, b, loss, math.exp(loss)))
|
| 221 |
+
# Save the model if the validation loss is the best we've seen so far.
|
| 222 |
+
|
| 223 |
+
print("[!] saving model...")
|
| 224 |
+
if not os.path.isdir(".save"):
|
| 225 |
+
os.makedirs(".save")
|
| 226 |
+
torch.save(model.state_dict(), './.save/rnnenum_%d_%d.pkl' % (e,b))
|
| 227 |
+
best_loss = loss
|
| 228 |
+
|
| 229 |
+
if __name__ == "__main__":
|
| 230 |
+
try:
|
| 231 |
+
main()
|
| 232 |
+
except KeyboardInterrupt as e:
|
| 233 |
+
print("[STOP]", e)
|
model/pretrain_trfm.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import math
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
import pandas as pd
|
| 7 |
+
import torch
|
| 8 |
+
from torch import nn
|
| 9 |
+
from torch import optim
|
| 10 |
+
from torch.autograd import Variable
|
| 11 |
+
from torch.nn import functional as F
|
| 12 |
+
from torch.utils.data import DataLoader
|
| 13 |
+
from tqdm import tqdm
|
| 14 |
+
|
| 15 |
+
from .build_vocab import WordVocab
|
| 16 |
+
from .dataset import Seq2seqDataset
|
| 17 |
+
from project_paths import VOCAB_PATH
|
| 18 |
+
|
| 19 |
+
PAD = 0
|
| 20 |
+
UNK = 1
|
| 21 |
+
EOS = 2
|
| 22 |
+
SOS = 3
|
| 23 |
+
MASK = 4
|
| 24 |
+
|
| 25 |
+
class PositionalEncoding(nn.Module):
|
| 26 |
+
"Implement the PE function. No batch support?"
|
| 27 |
+
def __init__(self, d_model, dropout, max_len=5000):
|
| 28 |
+
super(PositionalEncoding, self).__init__()
|
| 29 |
+
self.dropout = nn.Dropout(p=dropout)
|
| 30 |
+
|
| 31 |
+
# Compute the positional encodings once in log space.
|
| 32 |
+
pe = torch.zeros(max_len, d_model) # (T,H)
|
| 33 |
+
position = torch.arange(0., max_len).unsqueeze(1)
|
| 34 |
+
div_term = torch.exp(torch.arange(0., d_model, 2) * -(math.log(10000.0) / d_model))
|
| 35 |
+
pe[:, 0::2] = torch.sin(position * div_term)
|
| 36 |
+
pe[:, 1::2] = torch.cos(position * div_term)
|
| 37 |
+
pe = pe.unsqueeze(0)
|
| 38 |
+
self.register_buffer('pe', pe)
|
| 39 |
+
|
| 40 |
+
def forward(self, x):
|
| 41 |
+
x = x + Variable(self.pe[:, :x.size(1)],
|
| 42 |
+
requires_grad=False)
|
| 43 |
+
return self.dropout(x)
|
| 44 |
+
|
| 45 |
+
class TrfmSeq2seq(nn.Module):
|
| 46 |
+
def __init__(self, in_size, hidden_size, out_size, n_layers, dropout=0.1):
|
| 47 |
+
super(TrfmSeq2seq, self).__init__()
|
| 48 |
+
self.in_size = in_size
|
| 49 |
+
self.hidden_size = hidden_size
|
| 50 |
+
self.embed = nn.Embedding(in_size, hidden_size)
|
| 51 |
+
self.pe = PositionalEncoding(hidden_size, dropout)
|
| 52 |
+
self.trfm = nn.Transformer(d_model=hidden_size, nhead=4,
|
| 53 |
+
num_encoder_layers=n_layers, num_decoder_layers=n_layers, dim_feedforward=hidden_size)
|
| 54 |
+
self.out = nn.Linear(hidden_size, out_size)
|
| 55 |
+
|
| 56 |
+
def forward(self, src):
|
| 57 |
+
# src: (T,B)
|
| 58 |
+
embedded = self.embed(src) # (T,B,H)
|
| 59 |
+
embedded = self.pe(embedded) # (T,B,H)
|
| 60 |
+
hidden = self.trfm(embedded, embedded) # (T,B,H)
|
| 61 |
+
out = self.out(hidden) # (T,B,V)
|
| 62 |
+
out = F.log_softmax(out, dim=2) # (T,B,V)
|
| 63 |
+
return out # (T,B,V)
|
| 64 |
+
|
| 65 |
+
def _encode(self, src):
|
| 66 |
+
# src: (T,B)
|
| 67 |
+
embedded = self.embed(src) # (T,B,H)
|
| 68 |
+
embedded = self.pe(embedded) # (T,B,H)
|
| 69 |
+
output = embedded
|
| 70 |
+
for i in range(self.trfm.encoder.num_layers - 1):
|
| 71 |
+
output = self.trfm.encoder.layers[i](output, None) # (T,B,H)
|
| 72 |
+
penul = output.detach().numpy()
|
| 73 |
+
output = self.trfm.encoder.layers[-1](output, None) # (T,B,H)
|
| 74 |
+
if self.trfm.encoder.norm:
|
| 75 |
+
output = self.trfm.encoder.norm(output) # (T,B,H)
|
| 76 |
+
output = output.detach().numpy()
|
| 77 |
+
# mean, max, first*2
|
| 78 |
+
return np.hstack([np.mean(output, axis=0), np.max(output, axis=0), output[0,:,:], penul[0,:,:] ]) # (B,4H)
|
| 79 |
+
|
| 80 |
+
def encode(self, src):
|
| 81 |
+
# src: (T,B)
|
| 82 |
+
batch_size = src.shape[1]
|
| 83 |
+
if batch_size<=100:
|
| 84 |
+
return self._encode(src)
|
| 85 |
+
else: # Batch is too large to load
|
| 86 |
+
print('There are {:d} molecules. It will take a little time.'.format(batch_size))
|
| 87 |
+
st,ed = 0,100
|
| 88 |
+
out = self._encode(src[:,st:ed]) # (B,4H)
|
| 89 |
+
while ed<batch_size:
|
| 90 |
+
st += 100
|
| 91 |
+
ed += 100
|
| 92 |
+
out = np.concatenate([out, self._encode(src[:,st:ed])], axis=0)
|
| 93 |
+
return out
|
| 94 |
+
|
| 95 |
+
def parse_arguments():
|
| 96 |
+
parser = argparse.ArgumentParser(description='Hyperparams')
|
| 97 |
+
parser.add_argument('--n_epoch', '-e', type=int, default=5, help='number of epochs')
|
| 98 |
+
parser.add_argument('--vocab', '-v', type=str, default=str(VOCAB_PATH), help='vocabulary (.pkl)')
|
| 99 |
+
parser.add_argument('--data', '-d', type=str, default='data/chembl_25.csv', help='train corpus (.csv)')
|
| 100 |
+
parser.add_argument('--out-dir', '-o', type=str, default='../result', help='output directory')
|
| 101 |
+
parser.add_argument('--name', '-n', type=str, default='ST', help='model name')
|
| 102 |
+
parser.add_argument('--seq_len', type=int, default=220, help='maximum length of the paired seqence')
|
| 103 |
+
parser.add_argument('--batch_size', '-b', type=int, default=8, help='batch size')
|
| 104 |
+
parser.add_argument('--n_worker', '-w', type=int, default=16, help='number of workers')
|
| 105 |
+
parser.add_argument('--hidden', type=int, default=256, help='length of hidden vector')
|
| 106 |
+
parser.add_argument('--n_layer', '-l', type=int, default=4, help='number of layers')
|
| 107 |
+
parser.add_argument('--n_head', type=int, default=4, help='number of attention heads')
|
| 108 |
+
parser.add_argument('--lr', type=float, default=1e-4, help='Adam learning rate')
|
| 109 |
+
parser.add_argument('--gpu', metavar='N', type=int, nargs='+', help='list of GPU IDs to use')
|
| 110 |
+
return parser.parse_args()
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def evaluate(model, test_loader, vocab):
|
| 114 |
+
model.eval()
|
| 115 |
+
total_loss = 0
|
| 116 |
+
for b, sm in enumerate(test_loader):
|
| 117 |
+
sm = torch.t(sm.cuda()) # (T,B)
|
| 118 |
+
with torch.no_grad():
|
| 119 |
+
output = model(sm) # (T,B,V)
|
| 120 |
+
loss = F.nll_loss(output.view(-1, len(vocab)),
|
| 121 |
+
sm.contiguous().view(-1),
|
| 122 |
+
ignore_index=PAD)
|
| 123 |
+
total_loss += loss.item()
|
| 124 |
+
return total_loss / len(test_loader)
|
| 125 |
+
|
| 126 |
+
def main():
|
| 127 |
+
args = parse_arguments()
|
| 128 |
+
assert torch.cuda.is_available()
|
| 129 |
+
|
| 130 |
+
print('Loading dataset...')
|
| 131 |
+
vocab = WordVocab.load_vocab(args.vocab)
|
| 132 |
+
dataset = Seq2seqDataset(pd.read_csv(args.data)['canonical_smiles'].values, vocab)
|
| 133 |
+
test_size = 10000
|
| 134 |
+
train, test = torch.utils.data.random_split(dataset, [len(dataset)-test_size, test_size])
|
| 135 |
+
train_loader = DataLoader(train, batch_size=args.batch_size, shuffle=True, num_workers=args.n_worker)
|
| 136 |
+
test_loader = DataLoader(test, batch_size=args.batch_size, shuffle=False, num_workers=args.n_worker)
|
| 137 |
+
print('Train size:', len(train))
|
| 138 |
+
print('Test size:', len(test))
|
| 139 |
+
del dataset, train, test
|
| 140 |
+
|
| 141 |
+
model = TrfmSeq2seq(len(vocab), args.hidden, len(vocab), args.n_layer).cuda()
|
| 142 |
+
optimizer = optim.Adam(model.parameters(), lr=args.lr)
|
| 143 |
+
print(model)
|
| 144 |
+
print('Total parameters:', sum(p.numel() for p in model.parameters()))
|
| 145 |
+
|
| 146 |
+
best_loss = None
|
| 147 |
+
for e in range(1, args.n_epoch):
|
| 148 |
+
for b, sm in tqdm(enumerate(train_loader)):
|
| 149 |
+
sm = torch.t(sm.cuda()) # (T,B)
|
| 150 |
+
optimizer.zero_grad()
|
| 151 |
+
output = model(sm) # (T,B,V)
|
| 152 |
+
loss = F.nll_loss(output.view(-1, len(vocab)),
|
| 153 |
+
sm.contiguous().view(-1), ignore_index=PAD)
|
| 154 |
+
loss.backward()
|
| 155 |
+
optimizer.step()
|
| 156 |
+
if b%1000==0:
|
| 157 |
+
print('Train {:3d}: iter {:5d} | loss {:.3f} | ppl {:.3f}'.format(e, b, loss.item(), math.exp(loss.item())))
|
| 158 |
+
if b%10000==0:
|
| 159 |
+
loss = evaluate(model, test_loader, vocab)
|
| 160 |
+
print('Val {:3d}: iter {:5d} | loss {:.3f} | ppl {:.3f}'.format(e, b, loss, math.exp(loss)))
|
| 161 |
+
# Save the model if the validation loss is the best we've seen so far.
|
| 162 |
+
if not best_loss or loss < best_loss:
|
| 163 |
+
print("[!] saving model...")
|
| 164 |
+
if not os.path.isdir(".save"):
|
| 165 |
+
os.makedirs(".save")
|
| 166 |
+
torch.save(model.state_dict(), './.save/trfm_new_%d_%d.pkl' % (e,b))
|
| 167 |
+
best_loss = loss
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
if __name__ == "__main__":
|
| 171 |
+
try:
|
| 172 |
+
main()
|
| 173 |
+
except KeyboardInterrupt as e:
|
| 174 |
+
print("[STOP]", e)
|
model/sample.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .enumerator import SmilesEnumerator
|
| 2 |
+
|
| 3 |
+
sme = SmilesEnumerator()
|
| 4 |
+
smiles = ''
|
| 5 |
+
for i in range(10):
|
| 6 |
+
|
| 7 |
+
print(sme.randomize_smiles("CN1C(=NS(=O)(=O)c2ccc(Cl)cc2)C(=NN=P(c3ccccc3)(c4ccccc4)c5ccccc5)c6ccccc16COc1ccc(Cl)cc1c2cc([nH]n2)C(=O)Nc3ccc(OC)nc3"))
|
model/utils.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import math
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
from rdkit import Chem
|
| 5 |
+
from rdkit import rdBase
|
| 6 |
+
rdBase.DisableLog('rdApp.*')
|
| 7 |
+
|
| 8 |
+
# Split SMILES into words
|
| 9 |
+
def split(sm):
|
| 10 |
+
'''
|
| 11 |
+
function: Split SMILES into words. Care for Cl, Br, Si, Se, Na etc.
|
| 12 |
+
input: A SMILES
|
| 13 |
+
output: A string with space between words
|
| 14 |
+
'''
|
| 15 |
+
arr = []
|
| 16 |
+
i = 0
|
| 17 |
+
while i < len(sm)-1:
|
| 18 |
+
if not sm[i] in ['%', 'C', 'B', 'S', 'N', 'R', 'X', 'L', 'A', 'M', \
|
| 19 |
+
'T', 'Z', 's', 't', 'H', '+', '-', 'K', 'F']:
|
| 20 |
+
arr.append(sm[i])
|
| 21 |
+
i += 1
|
| 22 |
+
elif sm[i]=='%':
|
| 23 |
+
arr.append(sm[i:i+3])
|
| 24 |
+
i += 3
|
| 25 |
+
elif sm[i]=='C' and sm[i+1]=='l':
|
| 26 |
+
arr.append(sm[i:i+2])
|
| 27 |
+
i += 2
|
| 28 |
+
elif sm[i]=='C' and sm[i+1]=='a':
|
| 29 |
+
arr.append(sm[i:i+2])
|
| 30 |
+
i += 2
|
| 31 |
+
elif sm[i]=='C' and sm[i+1]=='u':
|
| 32 |
+
arr.append(sm[i:i+2])
|
| 33 |
+
i += 2
|
| 34 |
+
elif sm[i]=='B' and sm[i+1]=='r':
|
| 35 |
+
arr.append(sm[i:i+2])
|
| 36 |
+
i += 2
|
| 37 |
+
elif sm[i]=='B' and sm[i+1]=='e':
|
| 38 |
+
arr.append(sm[i:i+2])
|
| 39 |
+
i += 2
|
| 40 |
+
elif sm[i]=='B' and sm[i+1]=='a':
|
| 41 |
+
arr.append(sm[i:i+2])
|
| 42 |
+
i += 2
|
| 43 |
+
elif sm[i]=='B' and sm[i+1]=='i':
|
| 44 |
+
arr.append(sm[i:i+2])
|
| 45 |
+
i += 2
|
| 46 |
+
elif sm[i]=='S' and sm[i+1]=='i':
|
| 47 |
+
arr.append(sm[i:i+2])
|
| 48 |
+
i += 2
|
| 49 |
+
elif sm[i]=='S' and sm[i+1]=='e':
|
| 50 |
+
arr.append(sm[i:i+2])
|
| 51 |
+
i += 2
|
| 52 |
+
elif sm[i]=='S' and sm[i+1]=='r':
|
| 53 |
+
arr.append(sm[i:i+2])
|
| 54 |
+
i += 2
|
| 55 |
+
elif sm[i]=='N' and sm[i+1]=='a':
|
| 56 |
+
arr.append(sm[i:i+2])
|
| 57 |
+
i += 2
|
| 58 |
+
elif sm[i]=='N' and sm[i+1]=='i':
|
| 59 |
+
arr.append(sm[i:i+2])
|
| 60 |
+
i += 2
|
| 61 |
+
elif sm[i]=='R' and sm[i+1]=='b':
|
| 62 |
+
arr.append(sm[i:i+2])
|
| 63 |
+
i += 2
|
| 64 |
+
elif sm[i]=='R' and sm[i+1]=='a':
|
| 65 |
+
arr.append(sm[i:i+2])
|
| 66 |
+
i += 2
|
| 67 |
+
elif sm[i]=='X' and sm[i+1]=='e':
|
| 68 |
+
arr.append(sm[i:i+2])
|
| 69 |
+
i += 2
|
| 70 |
+
elif sm[i]=='L' and sm[i+1]=='i':
|
| 71 |
+
arr.append(sm[i:i+2])
|
| 72 |
+
i += 2
|
| 73 |
+
elif sm[i]=='A' and sm[i+1]=='l':
|
| 74 |
+
arr.append(sm[i:i+2])
|
| 75 |
+
i += 2
|
| 76 |
+
elif sm[i]=='A' and sm[i+1]=='s':
|
| 77 |
+
arr.append(sm[i:i+2])
|
| 78 |
+
i += 2
|
| 79 |
+
elif sm[i]=='A' and sm[i+1]=='g':
|
| 80 |
+
arr.append(sm[i:i+2])
|
| 81 |
+
i += 2
|
| 82 |
+
elif sm[i]=='A' and sm[i+1]=='u':
|
| 83 |
+
arr.append(sm[i:i+2])
|
| 84 |
+
i += 2
|
| 85 |
+
elif sm[i]=='M' and sm[i+1]=='g':
|
| 86 |
+
arr.append(sm[i:i+2])
|
| 87 |
+
i += 2
|
| 88 |
+
elif sm[i]=='M' and sm[i+1]=='n':
|
| 89 |
+
arr.append(sm[i:i+2])
|
| 90 |
+
i += 2
|
| 91 |
+
elif sm[i]=='T' and sm[i+1]=='e':
|
| 92 |
+
arr.append(sm[i:i+2])
|
| 93 |
+
i += 2
|
| 94 |
+
elif sm[i]=='Z' and sm[i+1]=='n':
|
| 95 |
+
arr.append(sm[i:i+2])
|
| 96 |
+
i += 2
|
| 97 |
+
elif sm[i]=='s' and sm[i+1]=='i':
|
| 98 |
+
arr.append(sm[i:i+2])
|
| 99 |
+
i += 2
|
| 100 |
+
elif sm[i]=='s' and sm[i+1]=='e':
|
| 101 |
+
arr.append(sm[i:i+2])
|
| 102 |
+
i += 2
|
| 103 |
+
elif sm[i]=='t' and sm[i+1]=='e':
|
| 104 |
+
arr.append(sm[i:i+2])
|
| 105 |
+
i += 2
|
| 106 |
+
elif sm[i]=='H' and sm[i+1]=='e':
|
| 107 |
+
arr.append(sm[i:i+2])
|
| 108 |
+
i += 2
|
| 109 |
+
elif sm[i]=='+' and sm[i+1]=='2':
|
| 110 |
+
arr.append(sm[i:i+2])
|
| 111 |
+
i += 2
|
| 112 |
+
elif sm[i]=='+' and sm[i+1]=='3':
|
| 113 |
+
arr.append(sm[i:i+2])
|
| 114 |
+
i += 2
|
| 115 |
+
elif sm[i]=='+' and sm[i+1]=='4':
|
| 116 |
+
arr.append(sm[i:i+2])
|
| 117 |
+
i += 2
|
| 118 |
+
elif sm[i]=='-' and sm[i+1]=='2':
|
| 119 |
+
arr.append(sm[i:i+2])
|
| 120 |
+
i += 2
|
| 121 |
+
elif sm[i]=='-' and sm[i+1]=='3':
|
| 122 |
+
arr.append(sm[i:i+2])
|
| 123 |
+
i += 2
|
| 124 |
+
elif sm[i]=='-' and sm[i+1]=='4':
|
| 125 |
+
arr.append(sm[i:i+2])
|
| 126 |
+
i += 2
|
| 127 |
+
elif sm[i]=='K' and sm[i+1]=='r':
|
| 128 |
+
arr.append(sm[i:i+2])
|
| 129 |
+
i += 2
|
| 130 |
+
elif sm[i]=='F' and sm[i+1]=='e':
|
| 131 |
+
arr.append(sm[i:i+2])
|
| 132 |
+
i += 2
|
| 133 |
+
else:
|
| 134 |
+
arr.append(sm[i])
|
| 135 |
+
i += 1
|
| 136 |
+
if i == len(sm)-1:
|
| 137 |
+
arr.append(sm[i])
|
| 138 |
+
return ' '.join(arr)
|
| 139 |
+
|
| 140 |
+
# 活性化関数
|
| 141 |
+
class GELU(nn.Module):
|
| 142 |
+
def forward(self, x):
|
| 143 |
+
return 0.5 * x * (1 + torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3))))
|
| 144 |
+
|
| 145 |
+
# 位置情報を考慮したFFN
|
| 146 |
+
class PositionwiseFeedForward(nn.Module):
|
| 147 |
+
def __init__(self, d_model, d_ff, dropout=0.1):
|
| 148 |
+
super(PositionwiseFeedForward, self).__init__()
|
| 149 |
+
self.w_1 = nn.Linear(d_model, d_ff)
|
| 150 |
+
self.w_2 = nn.Linear(d_ff, d_model)
|
| 151 |
+
self.dropout = nn.Dropout(dropout)
|
| 152 |
+
self.activation = GELU()
|
| 153 |
+
|
| 154 |
+
def forward(self, x):
|
| 155 |
+
return self.w_2(self.dropout(self.activation(self.w_1(x))))
|
| 156 |
+
|
| 157 |
+
# 正規化層
|
| 158 |
+
class LayerNorm(nn.Module):
|
| 159 |
+
def __init__(self, features, eps=1e-6):
|
| 160 |
+
super(LayerNorm, self).__init__()
|
| 161 |
+
self.a_2 = nn.Parameter(torch.ones(features))
|
| 162 |
+
self.b_2 = nn.Parameter(torch.zeros(features))
|
| 163 |
+
self.eps = eps
|
| 164 |
+
|
| 165 |
+
def forward(self, x):
|
| 166 |
+
mean = x.mean(-1, keepdim=True)
|
| 167 |
+
std = x.std(-1, keepdim=True)
|
| 168 |
+
return self.a_2 * (x - mean) / (std + self.eps) + self.b_2
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
class SublayerConnection(nn.Module):
|
| 172 |
+
def __init__(self, size, dropout):
|
| 173 |
+
super(SublayerConnection, self).__init__()
|
| 174 |
+
self.norm = LayerNorm(size)
|
| 175 |
+
self.dropout = nn.Dropout(dropout)
|
| 176 |
+
|
| 177 |
+
def forward(self, x, sublayer):
|
| 178 |
+
return x + self.dropout(sublayer(self.norm(x)))
|
| 179 |
+
|
| 180 |
+
# Sample SMILES from probablistic distribution
|
| 181 |
+
def sample(msms):
|
| 182 |
+
ret = []
|
| 183 |
+
for msm in msms:
|
| 184 |
+
ret.append(torch.multinomial(msm.exp(), 1).squeeze())
|
| 185 |
+
return torch.stack(ret)
|
| 186 |
+
|
| 187 |
+
def validity(smiles):
|
| 188 |
+
loss = 0
|
| 189 |
+
for sm in smiles:
|
| 190 |
+
mol = Chem.MolFromSmiles(sm)
|
| 191 |
+
if mol is None:
|
| 192 |
+
loss += 1
|
| 193 |
+
return 1-loss/len(smiles)
|
| 194 |
+
|
requirements.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is part of the OneScience IgGM project.
|
| 2 |
+
https://download.sourcefind.cn:65024/directlink/4/pytorch/DAS1.7/torch-2.5.1+das.opt1.dtk25042-cp311-cp311-manylinux_2_28_x86_64.whl
|
| 3 |
+
#https://download.sourcefind.cn:65024/file/4/triton/DAS1.7/triton-3.1.0+das.opt1.dtk25042-cp311-cp311-manylinux_2_28_x86_64.whl
|
| 4 |
+
#https://download.sourcefind.cn:65024/directlink/9/onesicence/dtk-25.04.2/openmm-8.3.1-cp311-cp311-linux_x86_64.whl
|
| 5 |
+
https://download.sourcefind.cn:65024/directlink/4/fastpt/DAS1.7/fastpt-2.1.1+das.dtk25042-cp311-cp311-linux_x86_64.whl
|
| 6 |
+
https://download.sourcefind.cn:65024/directlink/4/vision/DAS1.7/torchvision-0.20.1+das.opt1.dtk25042-cp311-cp311-manylinux_2_28_x86_64.whl
|
| 7 |
+
|
| 8 |
+
numpy==1.26.3
|
| 9 |
+
pandas
|
| 10 |
+
tqdm
|
| 11 |
+
rdkit
|
| 12 |
+
transformers
|
| 13 |
+
SentencePiece
|
| 14 |
+
scikit-learn
|
| 15 |
+
openpyxl
|
scripts/Preprocessing.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import numpy as np
|
| 3 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 4 |
+
from sklearn.metrics import r2_score
|
| 5 |
+
from scipy.stats import pearsonr
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
res = np.array(pd.read_excel('1_all_samples_metrics.xlsx', sheet_name='Sheet1')).T
|
| 9 |
+
sequence = res[1]
|
| 10 |
+
smiles = res[2]
|
| 11 |
+
Type = res[6]
|
| 12 |
+
Value = res[7]
|
| 13 |
+
Predict_Label = res[8]
|
| 14 |
+
Training_test = res[9]
|
| 15 |
+
print(sequence[0], smiles[0], Type[0], Value[0], Predict_Label[0], Training_test[0])
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def Whole_dataset():
|
| 19 |
+
# Calculate the whole dataset
|
| 20 |
+
Pcc = pearsonr(Value, Predict_Label)[0]
|
| 21 |
+
RMSE = np.sqrt(mean_squared_error(Value, Predict_Label))
|
| 22 |
+
MAE = mean_absolute_error(Value, Predict_Label)
|
| 23 |
+
r2 = r2_score(Value, Predict_Label)
|
| 24 |
+
print('***The whole set***')
|
| 25 |
+
print('Pcc:', Pcc, 'RMSE:', RMSE, 'MAE:', MAE, 'r2:', r2)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def test_dataset():
|
| 29 |
+
# Calculate the test dataset
|
| 30 |
+
Value_test = []
|
| 31 |
+
Predict_Label_test = []
|
| 32 |
+
for i in range(len(Training_test)):
|
| 33 |
+
if Training_test[i] == 1:
|
| 34 |
+
Value_test.append(Value[i])
|
| 35 |
+
Predict_Label_test.append(Predict_Label[i])
|
| 36 |
+
Value_test = np.array(Value_test)
|
| 37 |
+
Predict_Label_test = np.array(Predict_Label_test)
|
| 38 |
+
Pcc_test = pearsonr(Value_test, Predict_Label_test)[0]
|
| 39 |
+
RMSE_test = np.sqrt(mean_squared_error(Value_test, Predict_Label_test))
|
| 40 |
+
MAE_test = mean_absolute_error(Value_test, Predict_Label_test)
|
| 41 |
+
r2_test = r2_score(Value_test, Predict_Label_test)
|
| 42 |
+
print('***Test set***')
|
| 43 |
+
print('Pcc:', Pcc_test, 'RMSE:', RMSE_test, 'MAE:', MAE_test, 'r2:', r2_test)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def Wildtype_all_dataset():
|
| 47 |
+
# Calculate the Wildtype/Mutant dataset
|
| 48 |
+
Value_wildtype = []
|
| 49 |
+
Predict_Label_wildtype = []
|
| 50 |
+
for i in range(len(Type)):
|
| 51 |
+
if Type[i] == 'wildtype':
|
| 52 |
+
Value_wildtype.append(Value[i])
|
| 53 |
+
Predict_Label_wildtype.append(Predict_Label[i])
|
| 54 |
+
Value_wildtype = np.array(Value_wildtype)
|
| 55 |
+
Predict_Label_wildtype = np.array(Predict_Label_wildtype)
|
| 56 |
+
Pcc_test = pearsonr(Value_wildtype, Predict_Label_wildtype)[0]
|
| 57 |
+
RMSE_test = np.sqrt(mean_squared_error(Value_wildtype, Predict_Label_wildtype))
|
| 58 |
+
MAE_test = mean_absolute_error(Value_wildtype, Predict_Label_wildtype)
|
| 59 |
+
r2_test = r2_score(Value_wildtype, Predict_Label_wildtype)
|
| 60 |
+
print('***The whole wildtype set***')
|
| 61 |
+
print('Pcc:', Pcc_test, 'RMSE:', RMSE_test, 'MAE:', MAE_test, 'r2:', r2_test)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def Wildtype_test_dataset():
|
| 65 |
+
# Calculate the Wildtype/Mutant dataset
|
| 66 |
+
Value_wildtype = []
|
| 67 |
+
Predict_Label_wildtype = []
|
| 68 |
+
for i in range(len(Type)):
|
| 69 |
+
if Type[i] == 'wildtype' and Training_test[i] == 1:
|
| 70 |
+
Value_wildtype.append(Value[i])
|
| 71 |
+
Predict_Label_wildtype.append(Predict_Label[i])
|
| 72 |
+
Value_wildtype = np.array(Value_wildtype)
|
| 73 |
+
Predict_Label_wildtype = np.array(Predict_Label_wildtype)
|
| 74 |
+
Pcc_test = pearsonr(Value_wildtype, Predict_Label_wildtype)[0]
|
| 75 |
+
RMSE_test = np.sqrt(mean_squared_error(Value_wildtype, Predict_Label_wildtype))
|
| 76 |
+
MAE_test = mean_absolute_error(Value_wildtype, Predict_Label_wildtype)
|
| 77 |
+
r2_test = r2_score(Value_wildtype, Predict_Label_wildtype)
|
| 78 |
+
print('***The test wildtype set***')
|
| 79 |
+
print('Pcc:', Pcc_test, 'RMSE:', RMSE_test, 'MAE:', MAE_test, 'r2:', r2_test)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def Mutant_all_dataset():
|
| 83 |
+
# Calculate the Wildtype/Mutant dataset
|
| 84 |
+
Value_wildtype = []
|
| 85 |
+
Predict_Label_wildtype = []
|
| 86 |
+
for i in range(len(Type)):
|
| 87 |
+
if Type[i] != 'wildtype':
|
| 88 |
+
Value_wildtype.append(Value[i])
|
| 89 |
+
Predict_Label_wildtype.append(Predict_Label[i])
|
| 90 |
+
Value_wildtype = np.array(Value_wildtype)
|
| 91 |
+
Predict_Label_wildtype = np.array(Predict_Label_wildtype)
|
| 92 |
+
Pcc_test = pearsonr(Value_wildtype, Predict_Label_wildtype)[0]
|
| 93 |
+
RMSE_test = np.sqrt(mean_squared_error(Value_wildtype, Predict_Label_wildtype))
|
| 94 |
+
MAE_test = mean_absolute_error(Value_wildtype, Predict_Label_wildtype)
|
| 95 |
+
r2_test = r2_score(Value_wildtype, Predict_Label_wildtype)
|
| 96 |
+
print('***The whole mutant set***')
|
| 97 |
+
print('Pcc:', Pcc_test, 'RMSE:', RMSE_test, 'MAE:', MAE_test, 'r2:', r2_test)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def Mutant_test_dataset():
|
| 101 |
+
# Calculate the Wildtype/Mutant dataset
|
| 102 |
+
Value_wildtype = []
|
| 103 |
+
Predict_Label_wildtype = []
|
| 104 |
+
for i in range(len(Type)):
|
| 105 |
+
if Type[i] != 'wildtype' and Training_test[i] == 1:
|
| 106 |
+
Value_wildtype.append(Value[i])
|
| 107 |
+
Predict_Label_wildtype.append(Predict_Label[i])
|
| 108 |
+
Value_wildtype = np.array(Value_wildtype)
|
| 109 |
+
Predict_Label_wildtype = np.array(Predict_Label_wildtype)
|
| 110 |
+
Pcc_test = pearsonr(Value_wildtype, Predict_Label_wildtype)[0]
|
| 111 |
+
RMSE_test = np.sqrt(mean_squared_error(Value_wildtype, Predict_Label_wildtype))
|
| 112 |
+
MAE_test = mean_absolute_error(Value_wildtype, Predict_Label_wildtype)
|
| 113 |
+
r2_test = r2_score(Value_wildtype, Predict_Label_wildtype)
|
| 114 |
+
print('***The test mutant set***')
|
| 115 |
+
print('Pcc:', Pcc_test, 'RMSE:', RMSE_test, 'MAE:', MAE_test, 'r2:', r2_test)
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def New_substrate_enzyme_dataset():
|
| 119 |
+
# Calculate the test New_substrate_enzyme dataset
|
| 120 |
+
Trainingset_seq_smiles = []
|
| 121 |
+
for i in range(len(Training_test)):
|
| 122 |
+
if Training_test[i] == 0:
|
| 123 |
+
Trainingset_seq_smiles.append(sequence[i])
|
| 124 |
+
Trainingset_seq_smiles.append(smiles[i])
|
| 125 |
+
Value_test = []
|
| 126 |
+
Predict_Label_test = []
|
| 127 |
+
for i in range(len(Training_test)):
|
| 128 |
+
if Training_test[i] == 1 and (sequence[i] not in Trainingset_seq_smiles or smiles[i] not in Trainingset_seq_smiles):
|
| 129 |
+
Value_test.append(Value[i])
|
| 130 |
+
Predict_Label_test.append(Predict_Label[i])
|
| 131 |
+
Value_test = np.array(Value_test)
|
| 132 |
+
Predict_Label_test = np.array(Predict_Label_test)
|
| 133 |
+
Pcc_test = pearsonr(Value_test, Predict_Label_test)[0]
|
| 134 |
+
RMSE_test = np.sqrt(mean_squared_error(Value_test, Predict_Label_test))
|
| 135 |
+
MAE_test = mean_absolute_error(Value_test, Predict_Label_test)
|
| 136 |
+
r2_test = r2_score(Value_test, Predict_Label_test)
|
| 137 |
+
print('***The Test new_substrate_enzyme dataset***')
|
| 138 |
+
print('Pcc:', Pcc_test, 'RMSE:', RMSE_test, 'MAE:', MAE_test, 'r2:', r2_test)
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
if __name__ == '__main__':
|
| 142 |
+
# Whole_dataset()
|
| 143 |
+
# test_dataset()
|
| 144 |
+
# Wildtype_all_dataset()
|
| 145 |
+
# Wildtype_test_dataset()
|
| 146 |
+
# Mutant_all_dataset()
|
| 147 |
+
# Mutant_test_dataset()
|
| 148 |
+
New_substrate_enzyme_dataset()
|
scripts/UniKP_CBW.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
from collections import Counter
|
| 3 |
+
from scipy.ndimage import convolve1d
|
| 4 |
+
from scipy.ndimage import gaussian_filter1d
|
| 5 |
+
from scipy.signal.windows import triang
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from model.build_vocab import WordVocab
|
| 10 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 11 |
+
from model.utils import split
|
| 12 |
+
import json
|
| 13 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 14 |
+
import re
|
| 15 |
+
import gc
|
| 16 |
+
from sklearn import metrics
|
| 17 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 18 |
+
import numpy as np
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from sklearn.model_selection import KFold
|
| 21 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 22 |
+
from sklearn.metrics import r2_score
|
| 23 |
+
from sklearn.model_selection import train_test_split
|
| 24 |
+
import random
|
| 25 |
+
import pickle
|
| 26 |
+
import math
|
| 27 |
+
from project_paths import (
|
| 28 |
+
|
| 29 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 30 |
+
|
| 31 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 32 |
+
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def smiles_to_vec(Smiles):
|
| 39 |
+
pad_index = 0
|
| 40 |
+
unk_index = 1
|
| 41 |
+
eos_index = 2
|
| 42 |
+
sos_index = 3
|
| 43 |
+
mask_index = 4
|
| 44 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 45 |
+
def get_inputs(sm):
|
| 46 |
+
seq_len = 220
|
| 47 |
+
sm = sm.split()
|
| 48 |
+
if len(sm)>218:
|
| 49 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 50 |
+
sm = sm[:109]+sm[-109:]
|
| 51 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 52 |
+
ids = [sos_index] + ids + [eos_index]
|
| 53 |
+
seg = [1]*len(ids)
|
| 54 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 55 |
+
ids.extend(padding), seg.extend(padding)
|
| 56 |
+
return ids, seg
|
| 57 |
+
def get_array(smiles):
|
| 58 |
+
x_id, x_seg = [], []
|
| 59 |
+
for sm in smiles:
|
| 60 |
+
a,b = get_inputs(sm)
|
| 61 |
+
x_id.append(a)
|
| 62 |
+
x_seg.append(b)
|
| 63 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 64 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 65 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 66 |
+
trfm.eval()
|
| 67 |
+
x_split = [split(sm) for sm in Smiles]
|
| 68 |
+
xid, xseg = get_array(x_split)
|
| 69 |
+
X = trfm.encode(torch.t(xid))
|
| 70 |
+
return X
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def Seq_to_vec(Sequence):
|
| 74 |
+
for i in range(len(Sequence)):
|
| 75 |
+
if len(Sequence[i]) > 1000:
|
| 76 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 77 |
+
sequences_Example = []
|
| 78 |
+
for i in range(len(Sequence)):
|
| 79 |
+
zj = ''
|
| 80 |
+
for j in range(len(Sequence[i]) - 1):
|
| 81 |
+
zj += Sequence[i][j] + ' '
|
| 82 |
+
zj += Sequence[i][-1]
|
| 83 |
+
sequences_Example.append(zj)
|
| 84 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 85 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 86 |
+
gc.collect()
|
| 87 |
+
print(torch.cuda.is_available())
|
| 88 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 89 |
+
device = torch.device('cuda:1' if torch.cuda.is_available() else 'cpu')
|
| 90 |
+
model = model.to(device)
|
| 91 |
+
model = model.eval()
|
| 92 |
+
features = []
|
| 93 |
+
for i in range(len(sequences_Example)):
|
| 94 |
+
print('For sequence ', str(i+1))
|
| 95 |
+
sequences_Example_i = sequences_Example[i]
|
| 96 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 97 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 98 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 99 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 100 |
+
with torch.no_grad():
|
| 101 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 102 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 103 |
+
for seq_num in range(len(embedding)):
|
| 104 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 105 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 106 |
+
features.append(seq_emd)
|
| 107 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 108 |
+
for i in range(len(features)):
|
| 109 |
+
for k in range(len(features[0][0])):
|
| 110 |
+
for j in range(len(features[i])):
|
| 111 |
+
features_normalize[i][k] += features[i][j][k]
|
| 112 |
+
features_normalize[i][k] /= len(features[i])
|
| 113 |
+
return features_normalize
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def get_lds_kernel_window(kernel, ks, sigma):
|
| 117 |
+
assert kernel in ['gaussian', 'triang', 'laplace']
|
| 118 |
+
half_ks = (ks - 1) // 2
|
| 119 |
+
if kernel == 'gaussian':
|
| 120 |
+
base_kernel = [0.] * half_ks + [1.] + [0.] * half_ks
|
| 121 |
+
kernel_window = gaussian_filter1d(base_kernel, sigma=sigma) / max(gaussian_filter1d(base_kernel, sigma=sigma))
|
| 122 |
+
elif kernel == 'triang':
|
| 123 |
+
kernel_window = triang(ks)
|
| 124 |
+
else:
|
| 125 |
+
laplace = lambda x: np.exp(-abs(x) / sigma) / (2. * sigma)
|
| 126 |
+
kernel_window = list(map(laplace, np.arange(-half_ks, half_ks + 1))) / max(map(laplace, np.arange(-half_ks, half_ks + 1)))
|
| 127 |
+
return kernel_window
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def Smooth_Label(Label_new):
|
| 131 |
+
labels = Label_new
|
| 132 |
+
for i in range(len(labels)):
|
| 133 |
+
labels[i] = labels[i] - min(labels)
|
| 134 |
+
bin_index_per_label = [int(label*10) for label in labels]
|
| 135 |
+
# print(bin_index_per_label)
|
| 136 |
+
Nb = max(bin_index_per_label) + 1
|
| 137 |
+
print(Nb)
|
| 138 |
+
num_samples_of_bins = dict(Counter(bin_index_per_label))
|
| 139 |
+
print(num_samples_of_bins)
|
| 140 |
+
emp_label_dist = [num_samples_of_bins.get(i, 0) for i in range(Nb)]
|
| 141 |
+
print(emp_label_dist, len(emp_label_dist))
|
| 142 |
+
eff_label_dist = []
|
| 143 |
+
beta = 0.9
|
| 144 |
+
for i in range(len(emp_label_dist)):
|
| 145 |
+
eff_label_dist.append((1-math.pow(beta, emp_label_dist[i])) / (1-beta))
|
| 146 |
+
print(eff_label_dist)
|
| 147 |
+
eff_num_per_label = [eff_label_dist[bin_idx] for bin_idx in bin_index_per_label]
|
| 148 |
+
weights = [np.float32(1 / x) for x in eff_num_per_label]
|
| 149 |
+
weights = np.array(weights)
|
| 150 |
+
print(weights)
|
| 151 |
+
print(len(weights))
|
| 152 |
+
return weights
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def Kcat_predict(Ifeature, Label, weights):
|
| 156 |
+
for i in range(3, 10):
|
| 157 |
+
kf = KFold(n_splits=5, shuffle=True)
|
| 158 |
+
All_pre_label = []
|
| 159 |
+
All_real_label = []
|
| 160 |
+
for train_index, test_index in kf.split(Ifeature, Label):
|
| 161 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 162 |
+
Test_data, Test_label = Ifeature[test_index], Label[test_index]
|
| 163 |
+
model = ExtraTreesRegressor()
|
| 164 |
+
model.fit(Train_data, Train_label, sample_weight=weights[train_index])
|
| 165 |
+
Pre_label = model.predict(Test_data)
|
| 166 |
+
All_pre_label.extend(Pre_label)
|
| 167 |
+
All_real_label.extend(Test_label)
|
| 168 |
+
res = pd.DataFrame({'Value': All_real_label, 'Predict_Label': All_pre_label})
|
| 169 |
+
res.to_excel('CBW/'+str(i)+'_0.9_Re_weighting_Kcat_5_cv'+'.xlsx')
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
if __name__ == '__main__':
|
| 173 |
+
# Dataset Load
|
| 174 |
+
with open(KCAT_DATASET_PATH, 'r') as file:
|
| 175 |
+
datasets = json.load(file)
|
| 176 |
+
# print(len(datasets))
|
| 177 |
+
# datasets = datasets[:50]
|
| 178 |
+
sequence = [data['Sequence'] for data in datasets]
|
| 179 |
+
Smiles = [data['Smiles'] for data in datasets]
|
| 180 |
+
Label = [float(data['Value']) for data in datasets]
|
| 181 |
+
ECNumber = [data['ECNumber'] for data in datasets]
|
| 182 |
+
Organism = [data['Organism'] for data in datasets]
|
| 183 |
+
Substrate = [data['Substrate'] for data in datasets]
|
| 184 |
+
Type = [data['Type'] for data in datasets]
|
| 185 |
+
for i in range(len(Label)):
|
| 186 |
+
if Label[i] == 0:
|
| 187 |
+
Label[i] = -10000000000
|
| 188 |
+
else:
|
| 189 |
+
Label[i] = math.log(Label[i], 10)
|
| 190 |
+
Label = np.array(Label)
|
| 191 |
+
print(max(Label), min(Label))
|
| 192 |
+
# Feature Extractor
|
| 193 |
+
# smiles_input = smiles_to_vec(Smiles)
|
| 194 |
+
# sequence_input = Seq_to_vec(sequence)
|
| 195 |
+
# feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 196 |
+
with open("PreKcat_new/features_17010_PreKcat.pkl", "rb") as f:
|
| 197 |
+
feature = pickle.load(f)
|
| 198 |
+
# feature = feature[:50]
|
| 199 |
+
# Input dataset
|
| 200 |
+
feature_new = []
|
| 201 |
+
Label_new = []
|
| 202 |
+
sequence_new = []
|
| 203 |
+
Smiles_new = []
|
| 204 |
+
ECNumber_new = []
|
| 205 |
+
Organism_new = []
|
| 206 |
+
Substrate_new = []
|
| 207 |
+
Type_new = []
|
| 208 |
+
for i in range(len(Label)):
|
| 209 |
+
if -10000000000 < Label[i] and '.' not in Smiles[i]:
|
| 210 |
+
feature_new.append(feature[i])
|
| 211 |
+
Label_new.append(Label[i])
|
| 212 |
+
sequence_new.append(sequence[i])
|
| 213 |
+
Smiles_new.append(Smiles[i])
|
| 214 |
+
ECNumber_new.append(ECNumber[i])
|
| 215 |
+
Organism_new.append(Organism[i])
|
| 216 |
+
Substrate_new.append(Substrate[i])
|
| 217 |
+
Type_new.append(Type[i])
|
| 218 |
+
print(len(Label_new), min(Label_new), max(Label_new))
|
| 219 |
+
feature_new = np.array(feature_new)
|
| 220 |
+
Label_new = np.array(Label_new)
|
| 221 |
+
sl_label = [Label_new[i] for i in range(len(Label_new))]
|
| 222 |
+
weights = Smooth_Label(sl_label)
|
| 223 |
+
# weights = np.ones([len(Label_new)], dtype=float)
|
| 224 |
+
# for i in range(len(weights)):
|
| 225 |
+
# if Label_new[i] > 5:
|
| 226 |
+
# weights[i] = 2
|
| 227 |
+
# sum_weights = np.sum(weights)
|
| 228 |
+
# for i in range(len(weights)):
|
| 229 |
+
# weights[i] /= sum_weights
|
| 230 |
+
# Modelling
|
| 231 |
+
Kcat_predict(feature_new, Label_new, weights)
|
scripts/UniKP_CSW.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
from collections import Counter
|
| 3 |
+
from scipy.ndimage import convolve1d
|
| 4 |
+
from scipy.ndimage import gaussian_filter1d
|
| 5 |
+
from scipy.signal.windows import triang
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from model.build_vocab import WordVocab
|
| 10 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 11 |
+
from model.utils import split
|
| 12 |
+
import json
|
| 13 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 14 |
+
import re
|
| 15 |
+
import gc
|
| 16 |
+
from sklearn import metrics
|
| 17 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 18 |
+
import numpy as np
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from sklearn.model_selection import KFold
|
| 21 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 22 |
+
from sklearn.metrics import r2_score
|
| 23 |
+
from sklearn.model_selection import train_test_split
|
| 24 |
+
import random
|
| 25 |
+
import pickle
|
| 26 |
+
import math
|
| 27 |
+
from project_paths import (
|
| 28 |
+
|
| 29 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 30 |
+
|
| 31 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 32 |
+
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def smiles_to_vec(Smiles):
|
| 39 |
+
pad_index = 0
|
| 40 |
+
unk_index = 1
|
| 41 |
+
eos_index = 2
|
| 42 |
+
sos_index = 3
|
| 43 |
+
mask_index = 4
|
| 44 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 45 |
+
def get_inputs(sm):
|
| 46 |
+
seq_len = 220
|
| 47 |
+
sm = sm.split()
|
| 48 |
+
if len(sm)>218:
|
| 49 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 50 |
+
sm = sm[:109]+sm[-109:]
|
| 51 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 52 |
+
ids = [sos_index] + ids + [eos_index]
|
| 53 |
+
seg = [1]*len(ids)
|
| 54 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 55 |
+
ids.extend(padding), seg.extend(padding)
|
| 56 |
+
return ids, seg
|
| 57 |
+
def get_array(smiles):
|
| 58 |
+
x_id, x_seg = [], []
|
| 59 |
+
for sm in smiles:
|
| 60 |
+
a,b = get_inputs(sm)
|
| 61 |
+
x_id.append(a)
|
| 62 |
+
x_seg.append(b)
|
| 63 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 64 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 65 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 66 |
+
trfm.eval()
|
| 67 |
+
x_split = [split(sm) for sm in Smiles]
|
| 68 |
+
xid, xseg = get_array(x_split)
|
| 69 |
+
X = trfm.encode(torch.t(xid))
|
| 70 |
+
return X
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def Seq_to_vec(Sequence):
|
| 74 |
+
for i in range(len(Sequence)):
|
| 75 |
+
if len(Sequence[i]) > 1000:
|
| 76 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 77 |
+
sequences_Example = []
|
| 78 |
+
for i in range(len(Sequence)):
|
| 79 |
+
zj = ''
|
| 80 |
+
for j in range(len(Sequence[i]) - 1):
|
| 81 |
+
zj += Sequence[i][j] + ' '
|
| 82 |
+
zj += Sequence[i][-1]
|
| 83 |
+
sequences_Example.append(zj)
|
| 84 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 85 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 86 |
+
gc.collect()
|
| 87 |
+
print(torch.cuda.is_available())
|
| 88 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 89 |
+
device = torch.device('cuda:1' if torch.cuda.is_available() else 'cpu')
|
| 90 |
+
model = model.to(device)
|
| 91 |
+
model = model.eval()
|
| 92 |
+
features = []
|
| 93 |
+
for i in range(len(sequences_Example)):
|
| 94 |
+
print('For sequence ', str(i+1))
|
| 95 |
+
sequences_Example_i = sequences_Example[i]
|
| 96 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 97 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 98 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 99 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 100 |
+
with torch.no_grad():
|
| 101 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 102 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 103 |
+
for seq_num in range(len(embedding)):
|
| 104 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 105 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 106 |
+
features.append(seq_emd)
|
| 107 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 108 |
+
for i in range(len(features)):
|
| 109 |
+
for k in range(len(features[0][0])):
|
| 110 |
+
for j in range(len(features[i])):
|
| 111 |
+
features_normalize[i][k] += features[i][j][k]
|
| 112 |
+
features_normalize[i][k] /= len(features[i])
|
| 113 |
+
return features_normalize
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def get_lds_kernel_window(kernel, ks, sigma):
|
| 117 |
+
assert kernel in ['gaussian', 'triang', 'laplace']
|
| 118 |
+
half_ks = (ks - 1) // 2
|
| 119 |
+
if kernel == 'gaussian':
|
| 120 |
+
base_kernel = [0.] * half_ks + [1.] + [0.] * half_ks
|
| 121 |
+
kernel_window = gaussian_filter1d(base_kernel, sigma=sigma) / max(gaussian_filter1d(base_kernel, sigma=sigma))
|
| 122 |
+
elif kernel == 'triang':
|
| 123 |
+
kernel_window = triang(ks)
|
| 124 |
+
else:
|
| 125 |
+
laplace = lambda x: np.exp(-abs(x) / sigma) / (2. * sigma)
|
| 126 |
+
kernel_window = list(map(laplace, np.arange(-half_ks, half_ks + 1))) / max(map(laplace, np.arange(-half_ks, half_ks + 1)))
|
| 127 |
+
return kernel_window
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def Smooth_Label(Label_new):
|
| 131 |
+
labels = Label_new
|
| 132 |
+
for i in range(len(labels)):
|
| 133 |
+
labels[i] = labels[i] - min(labels)
|
| 134 |
+
bin_index_per_label = [int(label*10) for label in labels]
|
| 135 |
+
# print(bin_index_per_label)
|
| 136 |
+
Nb = max(bin_index_per_label) + 1
|
| 137 |
+
print(Nb)
|
| 138 |
+
num_samples_of_bins = dict(Counter(bin_index_per_label))
|
| 139 |
+
print(num_samples_of_bins)
|
| 140 |
+
emp_label_dist = [num_samples_of_bins.get(i, 0) for i in range(Nb)]
|
| 141 |
+
print(emp_label_dist, len(emp_label_dist))
|
| 142 |
+
eff_label_dist = []
|
| 143 |
+
eff_label_dist = emp_label_dist
|
| 144 |
+
eff_num_per_label = [eff_label_dist[bin_idx] for bin_idx in bin_index_per_label]
|
| 145 |
+
weights = [math.sqrt(np.float32(1 / x)) for x in eff_num_per_label]
|
| 146 |
+
weights = np.array(weights)
|
| 147 |
+
print(weights)
|
| 148 |
+
print(len(weights))
|
| 149 |
+
return weights
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def Kcat_predict(Ifeature, Label, weights):
|
| 153 |
+
kf = KFold(n_splits=5, shuffle=True)
|
| 154 |
+
All_pre_label = []
|
| 155 |
+
All_real_label = []
|
| 156 |
+
for train_index, test_index in kf.split(Ifeature, Label):
|
| 157 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 158 |
+
Test_data, Test_label = Ifeature[test_index], Label[test_index]
|
| 159 |
+
model = ExtraTreesRegressor()
|
| 160 |
+
model.fit(Train_data, Train_label, sample_weight=weights[train_index])
|
| 161 |
+
Pre_label = model.predict(Test_data)
|
| 162 |
+
All_pre_label.extend(Pre_label)
|
| 163 |
+
All_real_label.extend(Test_label)
|
| 164 |
+
res = pd.DataFrame({'Value': All_real_label, 'Predict_Label': All_pre_label})
|
| 165 |
+
res.to_excel('CSW/Root_Cost_sensitive_Re_weighting_Kcat_5_cv'+'.xlsx')
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
if __name__ == '__main__':
|
| 169 |
+
# Dataset Load
|
| 170 |
+
with open(KCAT_DATASET_PATH, 'r') as file:
|
| 171 |
+
datasets = json.load(file)
|
| 172 |
+
# print(len(datasets))
|
| 173 |
+
# datasets = datasets[:50]
|
| 174 |
+
sequence = [data['Sequence'] for data in datasets]
|
| 175 |
+
Smiles = [data['Smiles'] for data in datasets]
|
| 176 |
+
Label = [float(data['Value']) for data in datasets]
|
| 177 |
+
ECNumber = [data['ECNumber'] for data in datasets]
|
| 178 |
+
Organism = [data['Organism'] for data in datasets]
|
| 179 |
+
Substrate = [data['Substrate'] for data in datasets]
|
| 180 |
+
Type = [data['Type'] for data in datasets]
|
| 181 |
+
for i in range(len(Label)):
|
| 182 |
+
if Label[i] == 0:
|
| 183 |
+
Label[i] = -10000000000
|
| 184 |
+
else:
|
| 185 |
+
Label[i] = math.log(Label[i], 10)
|
| 186 |
+
Label = np.array(Label)
|
| 187 |
+
print(max(Label), min(Label))
|
| 188 |
+
# Feature Extractor
|
| 189 |
+
# smiles_input = smiles_to_vec(Smiles)
|
| 190 |
+
# sequence_input = Seq_to_vec(sequence)
|
| 191 |
+
# feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 192 |
+
with open("PreKcat_new/features_17010_PreKcat.pkl", "rb") as f:
|
| 193 |
+
feature = pickle.load(f)
|
| 194 |
+
# Input dataset
|
| 195 |
+
feature_new = []
|
| 196 |
+
Label_new = []
|
| 197 |
+
sequence_new = []
|
| 198 |
+
Smiles_new = []
|
| 199 |
+
ECNumber_new = []
|
| 200 |
+
Organism_new = []
|
| 201 |
+
Substrate_new = []
|
| 202 |
+
Type_new = []
|
| 203 |
+
for i in range(len(Label)):
|
| 204 |
+
if -10000000000 < Label[i] and '.' not in Smiles[i]:
|
| 205 |
+
feature_new.append(feature[i])
|
| 206 |
+
Label_new.append(Label[i])
|
| 207 |
+
sequence_new.append(sequence[i])
|
| 208 |
+
Smiles_new.append(Smiles[i])
|
| 209 |
+
ECNumber_new.append(ECNumber[i])
|
| 210 |
+
Organism_new.append(Organism[i])
|
| 211 |
+
Substrate_new.append(Substrate[i])
|
| 212 |
+
Type_new.append(Type[i])
|
| 213 |
+
print(len(Label_new), min(Label_new), max(Label_new))
|
| 214 |
+
feature_new = np.array(feature_new)
|
| 215 |
+
Label_new = np.array(Label_new)
|
| 216 |
+
sl_label = [Label_new[i] for i in range(len(Label_new))]
|
| 217 |
+
weights = Smooth_Label(sl_label)
|
| 218 |
+
# weights = np.ones([len(Label_new)], dtype=float)
|
| 219 |
+
# for i in range(len(weights)):
|
| 220 |
+
# if Label_new[i] > 5:
|
| 221 |
+
# weights[i] = 2
|
| 222 |
+
# sum_weights = np.sum(weights)
|
| 223 |
+
# for i in range(len(weights)):
|
| 224 |
+
# weights[i] /= sum_weights
|
| 225 |
+
# Modelling
|
| 226 |
+
Kcat_predict(feature_new, Label_new, weights)
|
scripts/UniKP_DMW.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
from collections import Counter
|
| 3 |
+
from scipy.ndimage import convolve1d
|
| 4 |
+
from scipy.ndimage import gaussian_filter1d
|
| 5 |
+
from scipy.signal.windows import triang
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from model.build_vocab import WordVocab
|
| 10 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 11 |
+
from model.utils import split
|
| 12 |
+
import json
|
| 13 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 14 |
+
import re
|
| 15 |
+
import gc
|
| 16 |
+
from sklearn import metrics
|
| 17 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 18 |
+
import numpy as np
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from sklearn.model_selection import KFold
|
| 21 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 22 |
+
from sklearn.metrics import r2_score
|
| 23 |
+
from sklearn.model_selection import train_test_split
|
| 24 |
+
import random
|
| 25 |
+
import pickle
|
| 26 |
+
import math
|
| 27 |
+
from project_paths import (
|
| 28 |
+
|
| 29 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 30 |
+
|
| 31 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 32 |
+
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def smiles_to_vec(Smiles):
|
| 39 |
+
pad_index = 0
|
| 40 |
+
unk_index = 1
|
| 41 |
+
eos_index = 2
|
| 42 |
+
sos_index = 3
|
| 43 |
+
mask_index = 4
|
| 44 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 45 |
+
def get_inputs(sm):
|
| 46 |
+
seq_len = 220
|
| 47 |
+
sm = sm.split()
|
| 48 |
+
if len(sm)>218:
|
| 49 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 50 |
+
sm = sm[:109]+sm[-109:]
|
| 51 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 52 |
+
ids = [sos_index] + ids + [eos_index]
|
| 53 |
+
seg = [1]*len(ids)
|
| 54 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 55 |
+
ids.extend(padding), seg.extend(padding)
|
| 56 |
+
return ids, seg
|
| 57 |
+
def get_array(smiles):
|
| 58 |
+
x_id, x_seg = [], []
|
| 59 |
+
for sm in smiles:
|
| 60 |
+
a,b = get_inputs(sm)
|
| 61 |
+
x_id.append(a)
|
| 62 |
+
x_seg.append(b)
|
| 63 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 64 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 65 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 66 |
+
trfm.eval()
|
| 67 |
+
x_split = [split(sm) for sm in Smiles]
|
| 68 |
+
xid, xseg = get_array(x_split)
|
| 69 |
+
X = trfm.encode(torch.t(xid))
|
| 70 |
+
return X
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def Seq_to_vec(Sequence):
|
| 74 |
+
for i in range(len(Sequence)):
|
| 75 |
+
if len(Sequence[i]) > 1000:
|
| 76 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 77 |
+
sequences_Example = []
|
| 78 |
+
for i in range(len(Sequence)):
|
| 79 |
+
zj = ''
|
| 80 |
+
for j in range(len(Sequence[i]) - 1):
|
| 81 |
+
zj += Sequence[i][j] + ' '
|
| 82 |
+
zj += Sequence[i][-1]
|
| 83 |
+
sequences_Example.append(zj)
|
| 84 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 85 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 86 |
+
gc.collect()
|
| 87 |
+
print(torch.cuda.is_available())
|
| 88 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 89 |
+
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 90 |
+
model = model.to(device)
|
| 91 |
+
model = model.eval()
|
| 92 |
+
features = []
|
| 93 |
+
for i in range(len(sequences_Example)):
|
| 94 |
+
print('For sequence ', str(i+1))
|
| 95 |
+
sequences_Example_i = sequences_Example[i]
|
| 96 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 97 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 98 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 99 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 100 |
+
with torch.no_grad():
|
| 101 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 102 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 103 |
+
for seq_num in range(len(embedding)):
|
| 104 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 105 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 106 |
+
features.append(seq_emd)
|
| 107 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 108 |
+
for i in range(len(features)):
|
| 109 |
+
for k in range(len(features[0][0])):
|
| 110 |
+
for j in range(len(features[i])):
|
| 111 |
+
features_normalize[i][k] += features[i][j][k]
|
| 112 |
+
features_normalize[i][k] /= len(features[i])
|
| 113 |
+
return features_normalize
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def Kcat_predict(Ifeature, Label, weights):
|
| 117 |
+
kf = KFold(n_splits=5, shuffle=True)
|
| 118 |
+
All_pre_label = []
|
| 119 |
+
All_real_label = []
|
| 120 |
+
for train_index, test_index in kf.split(Ifeature, Label):
|
| 121 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 122 |
+
Test_data, Test_label = Ifeature[test_index], Label[test_index]
|
| 123 |
+
model = ExtraTreesRegressor()
|
| 124 |
+
# , sample_weight=weights[train_index]
|
| 125 |
+
model.fit(Train_data, Train_label, sample_weight=weights[train_index])
|
| 126 |
+
Pre_label = model.predict(Test_data)
|
| 127 |
+
All_pre_label.extend(Pre_label)
|
| 128 |
+
All_real_label.extend(Test_label)
|
| 129 |
+
res = pd.DataFrame({'Value': All_real_label, 'Predict_Label': All_pre_label})
|
| 130 |
+
res.to_excel('DMW/DMW_No_Normalize_2_LDS_Kcat_5_cv'+'.xlsx')
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
if __name__ == '__main__':
|
| 134 |
+
# Dataset Load
|
| 135 |
+
with open(KCAT_DATASET_PATH, 'r') as file:
|
| 136 |
+
datasets = json.load(file)
|
| 137 |
+
# print(len(datasets))
|
| 138 |
+
# datasets = datasets[:50]
|
| 139 |
+
sequence = [data['Sequence'] for data in datasets]
|
| 140 |
+
Smiles = [data['Smiles'] for data in datasets]
|
| 141 |
+
Label = [float(data['Value']) for data in datasets]
|
| 142 |
+
ECNumber = [data['ECNumber'] for data in datasets]
|
| 143 |
+
Organism = [data['Organism'] for data in datasets]
|
| 144 |
+
Substrate = [data['Substrate'] for data in datasets]
|
| 145 |
+
Type = [data['Type'] for data in datasets]
|
| 146 |
+
for i in range(len(Label)):
|
| 147 |
+
if Label[i] == 0:
|
| 148 |
+
Label[i] = -10000000000
|
| 149 |
+
else:
|
| 150 |
+
Label[i] = math.log(Label[i], 10)
|
| 151 |
+
Label = np.array(Label)
|
| 152 |
+
print(max(Label), min(Label))
|
| 153 |
+
# Feature Extractor
|
| 154 |
+
# smiles_input = smiles_to_vec(Smiles)
|
| 155 |
+
# sequence_input = Seq_to_vec(sequence)
|
| 156 |
+
# feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 157 |
+
with open("PreKcat_new/features_17010_PreKcat.pkl", "rb") as f:
|
| 158 |
+
feature = pickle.load(f)
|
| 159 |
+
# Input dataset
|
| 160 |
+
feature_new = []
|
| 161 |
+
Label_new = []
|
| 162 |
+
sequence_new = []
|
| 163 |
+
Smiles_new = []
|
| 164 |
+
ECNumber_new = []
|
| 165 |
+
Organism_new = []
|
| 166 |
+
Substrate_new = []
|
| 167 |
+
Type_new = []
|
| 168 |
+
for i in range(len(Label)):
|
| 169 |
+
if -10000000000 < Label[i] and '.' not in Smiles[i]:
|
| 170 |
+
feature_new.append(feature[i])
|
| 171 |
+
Label_new.append(Label[i])
|
| 172 |
+
sequence_new.append(sequence[i])
|
| 173 |
+
Smiles_new.append(Smiles[i])
|
| 174 |
+
ECNumber_new.append(ECNumber[i])
|
| 175 |
+
Organism_new.append(Organism[i])
|
| 176 |
+
Substrate_new.append(Substrate[i])
|
| 177 |
+
Type_new.append(Type[i])
|
| 178 |
+
print(len(Label_new), min(Label_new), max(Label_new))
|
| 179 |
+
feature_new = np.array(feature_new)
|
| 180 |
+
Label_new = np.array(Label_new)
|
| 181 |
+
# sl_label = [Label_new[i] for i in range(len(Label_new))]
|
| 182 |
+
# weights = Smooth_Label(sl_label)
|
| 183 |
+
weights = np.ones([len(Label_new)], dtype=float)
|
| 184 |
+
for i in range(len(weights)):
|
| 185 |
+
if Label_new[i] > 4:
|
| 186 |
+
weights[i] = 2
|
| 187 |
+
# sum_weights = np.sum(weights)
|
| 188 |
+
# for i in range(len(weights)):
|
| 189 |
+
# weights[i] /= sum_weights
|
| 190 |
+
# Modelling
|
| 191 |
+
Kcat_predict(feature_new, Label_new, weights)
|
scripts/UniKP_Km.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
import json
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
import re
|
| 9 |
+
import gc
|
| 10 |
+
from sklearn import metrics
|
| 11 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pandas as pd
|
| 14 |
+
from sklearn.model_selection import KFold
|
| 15 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 16 |
+
from sklearn.metrics import r2_score
|
| 17 |
+
from sklearn.model_selection import train_test_split
|
| 18 |
+
import random
|
| 19 |
+
import pickle
|
| 20 |
+
import math
|
| 21 |
+
from sklearn.model_selection import train_test_split
|
| 22 |
+
import xgboost
|
| 23 |
+
import lightgbm
|
| 24 |
+
from sklearn.model_selection import GridSearchCV
|
| 25 |
+
from project_paths import (
|
| 26 |
+
|
| 27 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 28 |
+
|
| 29 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 30 |
+
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def smiles_to_vec(Smiles):
|
| 36 |
+
pad_index = 0
|
| 37 |
+
unk_index = 1
|
| 38 |
+
eos_index = 2
|
| 39 |
+
sos_index = 3
|
| 40 |
+
mask_index = 4
|
| 41 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 42 |
+
def get_inputs(sm):
|
| 43 |
+
seq_len = 220
|
| 44 |
+
sm = sm.split()
|
| 45 |
+
if len(sm)>218:
|
| 46 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 47 |
+
sm = sm[:109]+sm[-109:]
|
| 48 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 49 |
+
ids = [sos_index] + ids + [eos_index]
|
| 50 |
+
seg = [1]*len(ids)
|
| 51 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 52 |
+
ids.extend(padding), seg.extend(padding)
|
| 53 |
+
return ids, seg
|
| 54 |
+
def get_array(smiles):
|
| 55 |
+
x_id, x_seg = [], []
|
| 56 |
+
for sm in smiles:
|
| 57 |
+
a,b = get_inputs(sm)
|
| 58 |
+
x_id.append(a)
|
| 59 |
+
x_seg.append(b)
|
| 60 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 61 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 62 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 63 |
+
trfm.eval()
|
| 64 |
+
x_split = [split(sm) for sm in Smiles]
|
| 65 |
+
xid, xseg = get_array(x_split)
|
| 66 |
+
X = trfm.encode(torch.t(xid))
|
| 67 |
+
return X
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def Seq_to_vec(Sequence):
|
| 71 |
+
for i in range(len(Sequence)):
|
| 72 |
+
if len(Sequence[i]) > 1000:
|
| 73 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 74 |
+
sequences_Example = []
|
| 75 |
+
for i in range(len(Sequence)):
|
| 76 |
+
zj = ''
|
| 77 |
+
for j in range(len(Sequence[i]) - 1):
|
| 78 |
+
zj += Sequence[i][j] + ' '
|
| 79 |
+
zj += Sequence[i][-1]
|
| 80 |
+
sequences_Example.append(zj)
|
| 81 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 82 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 83 |
+
gc.collect()
|
| 84 |
+
print(torch.cuda.is_available())
|
| 85 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 86 |
+
device = torch.device('cuda:1' if torch.cuda.is_available() else 'cpu')
|
| 87 |
+
model = model.to(device)
|
| 88 |
+
model = model.eval()
|
| 89 |
+
features = []
|
| 90 |
+
for i in range(len(sequences_Example)):
|
| 91 |
+
print('For sequence ', str(i+1))
|
| 92 |
+
sequences_Example_i = sequences_Example[i]
|
| 93 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 94 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 95 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 96 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 97 |
+
with torch.no_grad():
|
| 98 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 99 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 100 |
+
for seq_num in range(len(embedding)):
|
| 101 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 102 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 103 |
+
features.append(seq_emd)
|
| 104 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 105 |
+
for i in range(len(features)):
|
| 106 |
+
for k in range(len(features[0][0])):
|
| 107 |
+
for j in range(len(features[i])):
|
| 108 |
+
features_normalize[i][k] += features[i][j][k]
|
| 109 |
+
features_normalize[i][k] /= len(features[i])
|
| 110 |
+
return features_normalize
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def Kcat_predict(Ifeature_ini, Label_ini):
|
| 114 |
+
Myseed = random.randint(0, 1000)
|
| 115 |
+
print(Myseed)
|
| 116 |
+
Ifeature, Ifeature_test, Label, Label_test = train_test_split(Ifeature_ini, Label_ini, test_size=0.2,
|
| 117 |
+
random_state=Myseed)
|
| 118 |
+
model = ExtraTreesRegressor()
|
| 119 |
+
model.fit(Ifeature, Label)
|
| 120 |
+
Pre_label = model.predict(Ifeature_test)
|
| 121 |
+
Pcc = np.corrcoef(Label_test, Pre_label)[1][0]
|
| 122 |
+
RMSE = np.sqrt(mean_squared_error(Label_test, Pre_label))
|
| 123 |
+
MAE = mean_absolute_error(Label_test, Pre_label)
|
| 124 |
+
r2 = r2_score(Label_test, Pre_label)
|
| 125 |
+
print(r2, Pcc, RMSE, MAE)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
if __name__ == '__main__':
|
| 129 |
+
# Dataset Load
|
| 130 |
+
with open(KM_TEST_PATH, 'rb') as file:
|
| 131 |
+
datasets = pickle.load(file)
|
| 132 |
+
# datasets = datasets
|
| 133 |
+
# print(datasets)
|
| 134 |
+
sequence = datasets['Sequence']
|
| 135 |
+
smiles = datasets['smiles']
|
| 136 |
+
Label = datasets['log10_KM']
|
| 137 |
+
print(len(smiles), len(Label))
|
| 138 |
+
# smiles_input = smiles_to_vec(smiles)
|
| 139 |
+
# sequence_input = Seq_to_vec(sequence)
|
| 140 |
+
# feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 141 |
+
# with open("Km/Km_features_11722_PreKcat.pkl", "wb") as f:
|
| 142 |
+
# pickle.dump(feature, f)
|
| 143 |
+
with open("Km/Km_features_11722_PreKcat.pkl", "rb") as f:
|
| 144 |
+
feature = pickle.load(f)
|
| 145 |
+
feature = np.array(feature)
|
| 146 |
+
Label = np.array(Label)
|
| 147 |
+
Kcat_predict(feature, Label)
|
scripts/UniKP_LDS.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
from collections import Counter
|
| 3 |
+
from scipy.ndimage import convolve1d
|
| 4 |
+
from scipy.ndimage import gaussian_filter1d
|
| 5 |
+
from scipy.signal.windows import triang
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from model.build_vocab import WordVocab
|
| 10 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 11 |
+
from model.utils import split
|
| 12 |
+
import json
|
| 13 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 14 |
+
import re
|
| 15 |
+
import gc
|
| 16 |
+
from sklearn import metrics
|
| 17 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 18 |
+
import numpy as np
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from sklearn.model_selection import KFold
|
| 21 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 22 |
+
from sklearn.metrics import r2_score
|
| 23 |
+
from sklearn.model_selection import train_test_split
|
| 24 |
+
import random
|
| 25 |
+
import pickle
|
| 26 |
+
import math
|
| 27 |
+
from project_paths import (
|
| 28 |
+
|
| 29 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 30 |
+
|
| 31 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 32 |
+
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def smiles_to_vec(Smiles):
|
| 39 |
+
pad_index = 0
|
| 40 |
+
unk_index = 1
|
| 41 |
+
eos_index = 2
|
| 42 |
+
sos_index = 3
|
| 43 |
+
mask_index = 4
|
| 44 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 45 |
+
def get_inputs(sm):
|
| 46 |
+
seq_len = 220
|
| 47 |
+
sm = sm.split()
|
| 48 |
+
if len(sm)>218:
|
| 49 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 50 |
+
sm = sm[:109]+sm[-109:]
|
| 51 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 52 |
+
ids = [sos_index] + ids + [eos_index]
|
| 53 |
+
seg = [1]*len(ids)
|
| 54 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 55 |
+
ids.extend(padding), seg.extend(padding)
|
| 56 |
+
return ids, seg
|
| 57 |
+
def get_array(smiles):
|
| 58 |
+
x_id, x_seg = [], []
|
| 59 |
+
for sm in smiles:
|
| 60 |
+
a,b = get_inputs(sm)
|
| 61 |
+
x_id.append(a)
|
| 62 |
+
x_seg.append(b)
|
| 63 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 64 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 65 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 66 |
+
trfm.eval()
|
| 67 |
+
x_split = [split(sm) for sm in Smiles]
|
| 68 |
+
xid, xseg = get_array(x_split)
|
| 69 |
+
X = trfm.encode(torch.t(xid))
|
| 70 |
+
return X
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def Seq_to_vec(Sequence):
|
| 74 |
+
for i in range(len(Sequence)):
|
| 75 |
+
if len(Sequence[i]) > 1000:
|
| 76 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 77 |
+
sequences_Example = []
|
| 78 |
+
for i in range(len(Sequence)):
|
| 79 |
+
zj = ''
|
| 80 |
+
for j in range(len(Sequence[i]) - 1):
|
| 81 |
+
zj += Sequence[i][j] + ' '
|
| 82 |
+
zj += Sequence[i][-1]
|
| 83 |
+
sequences_Example.append(zj)
|
| 84 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 85 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 86 |
+
gc.collect()
|
| 87 |
+
print(torch.cuda.is_available())
|
| 88 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 89 |
+
device = torch.device('cuda:1' if torch.cuda.is_available() else 'cpu')
|
| 90 |
+
model = model.to(device)
|
| 91 |
+
model = model.eval()
|
| 92 |
+
features = []
|
| 93 |
+
for i in range(len(sequences_Example)):
|
| 94 |
+
print('For sequence ', str(i+1))
|
| 95 |
+
sequences_Example_i = sequences_Example[i]
|
| 96 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 97 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 98 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 99 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 100 |
+
with torch.no_grad():
|
| 101 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 102 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 103 |
+
for seq_num in range(len(embedding)):
|
| 104 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 105 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 106 |
+
features.append(seq_emd)
|
| 107 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 108 |
+
for i in range(len(features)):
|
| 109 |
+
for k in range(len(features[0][0])):
|
| 110 |
+
for j in range(len(features[i])):
|
| 111 |
+
features_normalize[i][k] += features[i][j][k]
|
| 112 |
+
features_normalize[i][k] /= len(features[i])
|
| 113 |
+
return features_normalize
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def get_lds_kernel_window(kernel, ks, sigma):
|
| 117 |
+
assert kernel in ['gaussian', 'triang', 'laplace']
|
| 118 |
+
half_ks = (ks - 1) // 2
|
| 119 |
+
if kernel == 'gaussian':
|
| 120 |
+
base_kernel = [0.] * half_ks + [1.] + [0.] * half_ks
|
| 121 |
+
kernel_window = gaussian_filter1d(base_kernel, sigma=sigma) / max(gaussian_filter1d(base_kernel, sigma=sigma))
|
| 122 |
+
elif kernel == 'triang':
|
| 123 |
+
kernel_window = triang(ks)
|
| 124 |
+
else:
|
| 125 |
+
laplace = lambda x: np.exp(-abs(x) / sigma) / (2. * sigma)
|
| 126 |
+
kernel_window = list(map(laplace, np.arange(-half_ks, half_ks + 1))) / max(map(laplace, np.arange(-half_ks, half_ks + 1)))
|
| 127 |
+
return kernel_window
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def Smooth_Label(Label_new):
|
| 131 |
+
labels = Label_new
|
| 132 |
+
for i in range(len(labels)):
|
| 133 |
+
labels[i] = labels[i] - min(labels)
|
| 134 |
+
bin_index_per_label = [int(label*4) for label in labels]
|
| 135 |
+
# print(bin_index_per_label)
|
| 136 |
+
Nb = max(bin_index_per_label) + 1
|
| 137 |
+
num_samples_of_bins = dict(Counter(bin_index_per_label))
|
| 138 |
+
emp_label_dist = [num_samples_of_bins.get(i, 0) for i in range(Nb)]
|
| 139 |
+
print(emp_label_dist, len(emp_label_dist))
|
| 140 |
+
lds_kernel_window = get_lds_kernel_window(kernel='gaussian', ks=3, sigma=1)
|
| 141 |
+
print(lds_kernel_window)
|
| 142 |
+
eff_label_dist = convolve1d(np.array(emp_label_dist), weights=lds_kernel_window, mode='constant')
|
| 143 |
+
print(eff_label_dist, emp_label_dist)
|
| 144 |
+
eff_num_per_label = [eff_label_dist[bin_idx] for bin_idx in bin_index_per_label]
|
| 145 |
+
weights = [np.float32(1 / x) for x in eff_num_per_label]
|
| 146 |
+
weights = np.array(weights)
|
| 147 |
+
# print(weights)
|
| 148 |
+
return weights
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def Kcat_predict(Ifeature, Label, weights):
|
| 152 |
+
kf = KFold(n_splits=5, shuffle=True)
|
| 153 |
+
All_pre_label = []
|
| 154 |
+
All_real_label = []
|
| 155 |
+
for train_index, test_index in kf.split(Ifeature, Label):
|
| 156 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 157 |
+
Test_data, Test_label = Ifeature[test_index], Label[test_index]
|
| 158 |
+
model = ExtraTreesRegressor()
|
| 159 |
+
# , sample_weight=weights[train_index]
|
| 160 |
+
model.fit(Train_data, Train_label, sample_weight=weights[train_index])
|
| 161 |
+
Pre_label = model.predict(Test_data)
|
| 162 |
+
All_pre_label.extend(Pre_label)
|
| 163 |
+
All_real_label.extend(Test_label)
|
| 164 |
+
res = pd.DataFrame({'Value': All_real_label, 'Predict_Label': All_pre_label})
|
| 165 |
+
res.to_excel('LDS/31_LDS_Kcat_5_cv'+'.xlsx')
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
if __name__ == '__main__':
|
| 169 |
+
# Dataset Load
|
| 170 |
+
with open(KCAT_DATASET_PATH, 'r') as file:
|
| 171 |
+
datasets = json.load(file)
|
| 172 |
+
# print(len(datasets))
|
| 173 |
+
# datasets = datasets[:50]
|
| 174 |
+
sequence = [data['Sequence'] for data in datasets]
|
| 175 |
+
Smiles = [data['Smiles'] for data in datasets]
|
| 176 |
+
Label = [float(data['Value']) for data in datasets]
|
| 177 |
+
ECNumber = [data['ECNumber'] for data in datasets]
|
| 178 |
+
Organism = [data['Organism'] for data in datasets]
|
| 179 |
+
Substrate = [data['Substrate'] for data in datasets]
|
| 180 |
+
Type = [data['Type'] for data in datasets]
|
| 181 |
+
for i in range(len(Label)):
|
| 182 |
+
if Label[i] == 0:
|
| 183 |
+
Label[i] = -10000000000
|
| 184 |
+
else:
|
| 185 |
+
Label[i] = math.log(Label[i], 10)
|
| 186 |
+
Label = np.array(Label)
|
| 187 |
+
print(max(Label), min(Label))
|
| 188 |
+
# Feature Extractor
|
| 189 |
+
# smiles_input = smiles_to_vec(Smiles)
|
| 190 |
+
# sequence_input = Seq_to_vec(sequence)
|
| 191 |
+
# feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 192 |
+
with open("PreKcat_new/features_17010_PreKcat.pkl", "rb") as f:
|
| 193 |
+
feature = pickle.load(f)
|
| 194 |
+
# feature = feature[:50]
|
| 195 |
+
# Input dataset
|
| 196 |
+
feature_new = []
|
| 197 |
+
Label_new = []
|
| 198 |
+
sequence_new = []
|
| 199 |
+
Smiles_new = []
|
| 200 |
+
ECNumber_new = []
|
| 201 |
+
Organism_new = []
|
| 202 |
+
Substrate_new = []
|
| 203 |
+
Type_new = []
|
| 204 |
+
for i in range(len(Label)):
|
| 205 |
+
if -10000000000 < Label[i] and '.' not in Smiles[i]:
|
| 206 |
+
feature_new.append(feature[i])
|
| 207 |
+
Label_new.append(Label[i])
|
| 208 |
+
sequence_new.append(sequence[i])
|
| 209 |
+
Smiles_new.append(Smiles[i])
|
| 210 |
+
ECNumber_new.append(ECNumber[i])
|
| 211 |
+
Organism_new.append(Organism[i])
|
| 212 |
+
Substrate_new.append(Substrate[i])
|
| 213 |
+
Type_new.append(Type[i])
|
| 214 |
+
print(len(Label_new), min(Label_new), max(Label_new))
|
| 215 |
+
feature_new = np.array(feature_new)
|
| 216 |
+
Label_new = np.array(Label_new)
|
| 217 |
+
sl_label = [Label_new[i] for i in range(len(Label_new))]
|
| 218 |
+
weights = Smooth_Label(sl_label)
|
| 219 |
+
# Modelling
|
| 220 |
+
Kcat_predict(feature_new, Label_new, weights)
|
scripts/UniKP_No_LDS.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
from collections import Counter
|
| 3 |
+
from scipy.ndimage import convolve1d
|
| 4 |
+
from scipy.ndimage import gaussian_filter1d
|
| 5 |
+
from scipy.signal.windows import triang
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from model.build_vocab import WordVocab
|
| 10 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 11 |
+
from model.utils import split
|
| 12 |
+
import json
|
| 13 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 14 |
+
import re
|
| 15 |
+
import gc
|
| 16 |
+
from sklearn import metrics
|
| 17 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 18 |
+
import numpy as np
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from sklearn.model_selection import KFold
|
| 21 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 22 |
+
from sklearn.metrics import r2_score
|
| 23 |
+
from sklearn.model_selection import train_test_split
|
| 24 |
+
import random
|
| 25 |
+
import pickle
|
| 26 |
+
import math
|
| 27 |
+
from project_paths import (
|
| 28 |
+
|
| 29 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 30 |
+
|
| 31 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 32 |
+
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def smiles_to_vec(Smiles):
|
| 39 |
+
pad_index = 0
|
| 40 |
+
unk_index = 1
|
| 41 |
+
eos_index = 2
|
| 42 |
+
sos_index = 3
|
| 43 |
+
mask_index = 4
|
| 44 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 45 |
+
def get_inputs(sm):
|
| 46 |
+
seq_len = 220
|
| 47 |
+
sm = sm.split()
|
| 48 |
+
if len(sm)>218:
|
| 49 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 50 |
+
sm = sm[:109]+sm[-109:]
|
| 51 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 52 |
+
ids = [sos_index] + ids + [eos_index]
|
| 53 |
+
seg = [1]*len(ids)
|
| 54 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 55 |
+
ids.extend(padding), seg.extend(padding)
|
| 56 |
+
return ids, seg
|
| 57 |
+
def get_array(smiles):
|
| 58 |
+
x_id, x_seg = [], []
|
| 59 |
+
for sm in smiles:
|
| 60 |
+
a,b = get_inputs(sm)
|
| 61 |
+
x_id.append(a)
|
| 62 |
+
x_seg.append(b)
|
| 63 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 64 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 65 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 66 |
+
trfm.eval()
|
| 67 |
+
x_split = [split(sm) for sm in Smiles]
|
| 68 |
+
xid, xseg = get_array(x_split)
|
| 69 |
+
X = trfm.encode(torch.t(xid))
|
| 70 |
+
return X
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def Seq_to_vec(Sequence):
|
| 74 |
+
for i in range(len(Sequence)):
|
| 75 |
+
if len(Sequence[i]) > 1000:
|
| 76 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 77 |
+
sequences_Example = []
|
| 78 |
+
for i in range(len(Sequence)):
|
| 79 |
+
zj = ''
|
| 80 |
+
for j in range(len(Sequence[i]) - 1):
|
| 81 |
+
zj += Sequence[i][j] + ' '
|
| 82 |
+
zj += Sequence[i][-1]
|
| 83 |
+
sequences_Example.append(zj)
|
| 84 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 85 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 86 |
+
gc.collect()
|
| 87 |
+
print(torch.cuda.is_available())
|
| 88 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 89 |
+
device = torch.device('cuda:1' if torch.cuda.is_available() else 'cpu')
|
| 90 |
+
model = model.to(device)
|
| 91 |
+
model = model.eval()
|
| 92 |
+
features = []
|
| 93 |
+
for i in range(len(sequences_Example)):
|
| 94 |
+
print('For sequence ', str(i+1))
|
| 95 |
+
sequences_Example_i = sequences_Example[i]
|
| 96 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 97 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 98 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 99 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 100 |
+
with torch.no_grad():
|
| 101 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 102 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 103 |
+
for seq_num in range(len(embedding)):
|
| 104 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 105 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 106 |
+
features.append(seq_emd)
|
| 107 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 108 |
+
for i in range(len(features)):
|
| 109 |
+
for k in range(len(features[0][0])):
|
| 110 |
+
for j in range(len(features[i])):
|
| 111 |
+
features_normalize[i][k] += features[i][j][k]
|
| 112 |
+
features_normalize[i][k] /= len(features[i])
|
| 113 |
+
return features_normalize
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def get_lds_kernel_window(kernel, ks, sigma):
|
| 117 |
+
assert kernel in ['gaussian', 'triang', 'laplace']
|
| 118 |
+
half_ks = (ks - 1) // 2
|
| 119 |
+
if kernel == 'gaussian':
|
| 120 |
+
base_kernel = [0.] * half_ks + [1.] + [0.] * half_ks
|
| 121 |
+
kernel_window = gaussian_filter1d(base_kernel, sigma=sigma) / max(gaussian_filter1d(base_kernel, sigma=sigma))
|
| 122 |
+
elif kernel == 'triang':
|
| 123 |
+
kernel_window = triang(ks)
|
| 124 |
+
else:
|
| 125 |
+
laplace = lambda x: np.exp(-abs(x) / sigma) / (2. * sigma)
|
| 126 |
+
kernel_window = list(map(laplace, np.arange(-half_ks, half_ks + 1))) / max(map(laplace, np.arange(-half_ks, half_ks + 1)))
|
| 127 |
+
return kernel_window
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def Smooth_Label(Label_new):
|
| 131 |
+
labels = Label_new
|
| 132 |
+
for i in range(len(labels)):
|
| 133 |
+
labels[i] = labels[i] - min(labels)
|
| 134 |
+
bin_index_per_label = [int(label*4) for label in labels]
|
| 135 |
+
# print(bin_index_per_label)
|
| 136 |
+
Nb = max(bin_index_per_label) + 1
|
| 137 |
+
num_samples_of_bins = dict(Counter(bin_index_per_label))
|
| 138 |
+
emp_label_dist = [num_samples_of_bins.get(i, 0) for i in range(Nb)]
|
| 139 |
+
print(emp_label_dist, len(emp_label_dist))
|
| 140 |
+
plt.figure(figsize=(6,4))
|
| 141 |
+
# plt.savefig('emp_label_dist.jpg')
|
| 142 |
+
lds_kernel_window = get_lds_kernel_window(kernel='gaussian', ks=5, sigma=2)
|
| 143 |
+
print(lds_kernel_window)
|
| 144 |
+
eff_label_dist = convolve1d(np.array(emp_label_dist[:-10]), weights=lds_kernel_window, mode='constant')
|
| 145 |
+
# plt.figure(figsize=(6,4))
|
| 146 |
+
print(eff_label_dist, emp_label_dist[-10:])
|
| 147 |
+
eff_label_dist = eff_label_dist.tolist()
|
| 148 |
+
eff_label_dist.extend(emp_label_dist[-10:])
|
| 149 |
+
# print(eff_label_dist)
|
| 150 |
+
plt.bar([i for i in range(len(eff_label_dist))], eff_label_dist, color='r')
|
| 151 |
+
plt.bar([i for i in range(len(emp_label_dist))], emp_label_dist, color='b')
|
| 152 |
+
plt.savefig('Square_52_label_dist_distribution.jpg')
|
| 153 |
+
print(eff_label_dist, len(eff_label_dist))
|
| 154 |
+
eff_num_per_label = [eff_label_dist[bin_idx] for bin_idx in bin_index_per_label]
|
| 155 |
+
weights = [np.float32(1 / x)*np.float32(1 / x) for x in eff_num_per_label]
|
| 156 |
+
weights = np.array(weights)
|
| 157 |
+
# print(weights)
|
| 158 |
+
return weights
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def Kcat_predict(Ifeature, Label, weights):
|
| 162 |
+
for i in range(3):
|
| 163 |
+
kf = KFold(n_splits=5, shuffle=True)
|
| 164 |
+
All_pre_label = []
|
| 165 |
+
All_real_label = []
|
| 166 |
+
for train_index, test_index in kf.split(Ifeature, Label):
|
| 167 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 168 |
+
Test_data, Test_label = Ifeature[test_index], Label[test_index]
|
| 169 |
+
model = ExtraTreesRegressor()
|
| 170 |
+
# , sample_weight=weights[train_index]
|
| 171 |
+
model.fit(Train_data, Train_label)
|
| 172 |
+
Pre_label = model.predict(Test_data)
|
| 173 |
+
All_pre_label.extend(Pre_label)
|
| 174 |
+
All_real_label.extend(Test_label)
|
| 175 |
+
res = pd.DataFrame({'Value': All_real_label, 'Predict_Label': All_pre_label})
|
| 176 |
+
res.to_excel(str(i+1)+'_No_LDS_Kcat_5_cv'+'.xlsx')
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
if __name__ == '__main__':
|
| 180 |
+
# Dataset Load
|
| 181 |
+
with open(KCAT_DATASET_PATH, 'r') as file:
|
| 182 |
+
datasets = json.load(file)
|
| 183 |
+
# print(len(datasets))
|
| 184 |
+
# datasets = datasets[:50]
|
| 185 |
+
sequence = [data['Sequence'] for data in datasets]
|
| 186 |
+
Smiles = [data['Smiles'] for data in datasets]
|
| 187 |
+
Label = [float(data['Value']) for data in datasets]
|
| 188 |
+
ECNumber = [data['ECNumber'] for data in datasets]
|
| 189 |
+
Organism = [data['Organism'] for data in datasets]
|
| 190 |
+
Substrate = [data['Substrate'] for data in datasets]
|
| 191 |
+
Type = [data['Type'] for data in datasets]
|
| 192 |
+
for i in range(len(Label)):
|
| 193 |
+
if Label[i] == 0:
|
| 194 |
+
Label[i] = -10000000000
|
| 195 |
+
else:
|
| 196 |
+
Label[i] = math.log(Label[i], 10)
|
| 197 |
+
Label = np.array(Label)
|
| 198 |
+
print(max(Label), min(Label))
|
| 199 |
+
# Feature Extractor
|
| 200 |
+
# smiles_input = smiles_to_vec(Smiles)
|
| 201 |
+
# sequence_input = Seq_to_vec(sequence)
|
| 202 |
+
# feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 203 |
+
with open("PreKcat_new/features_17010_PreKcat.pkl", "rb") as f:
|
| 204 |
+
feature = pickle.load(f)
|
| 205 |
+
# Input dataset
|
| 206 |
+
feature_new = []
|
| 207 |
+
Label_new = []
|
| 208 |
+
sequence_new = []
|
| 209 |
+
Smiles_new = []
|
| 210 |
+
ECNumber_new = []
|
| 211 |
+
Organism_new = []
|
| 212 |
+
Substrate_new = []
|
| 213 |
+
Type_new = []
|
| 214 |
+
for i in range(len(Label)):
|
| 215 |
+
if -10000000000 < Label[i] and '.' not in Smiles[i]:
|
| 216 |
+
feature_new.append(feature[i])
|
| 217 |
+
Label_new.append(Label[i])
|
| 218 |
+
sequence_new.append(sequence[i])
|
| 219 |
+
Smiles_new.append(Smiles[i])
|
| 220 |
+
ECNumber_new.append(ECNumber[i])
|
| 221 |
+
Organism_new.append(Organism[i])
|
| 222 |
+
Substrate_new.append(Substrate[i])
|
| 223 |
+
Type_new.append(Type[i])
|
| 224 |
+
print(len(Label_new), min(Label_new), max(Label_new))
|
| 225 |
+
feature_new = np.array(feature_new)
|
| 226 |
+
Label_new = np.array(Label_new)
|
| 227 |
+
# sl_label = [Label_new[i] for i in range(len(Label_new))]
|
| 228 |
+
# weights = Smooth_Label(sl_label)
|
| 229 |
+
weights = np.ones([len(Label_new)], dtype=float)
|
| 230 |
+
for i in range(len(weights)):
|
| 231 |
+
if Label_new[i] > 5:
|
| 232 |
+
weights[i] = 2
|
| 233 |
+
sum_weights = np.sum(weights)
|
| 234 |
+
for i in range(len(weights)):
|
| 235 |
+
weights[i] /= sum_weights
|
| 236 |
+
# Modelling
|
| 237 |
+
Kcat_predict(feature_new, Label_new, weights)
|
scripts/UniKP_kcat.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
import json
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
import re
|
| 9 |
+
import gc
|
| 10 |
+
from sklearn import metrics
|
| 11 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pandas as pd
|
| 14 |
+
from sklearn.model_selection import KFold
|
| 15 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 16 |
+
from sklearn.metrics import r2_score
|
| 17 |
+
from sklearn.model_selection import train_test_split
|
| 18 |
+
import random
|
| 19 |
+
import pickle
|
| 20 |
+
import math
|
| 21 |
+
from project_paths import (
|
| 22 |
+
|
| 23 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 24 |
+
|
| 25 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 26 |
+
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def smiles_to_vec(Smiles):
|
| 32 |
+
pad_index = 0
|
| 33 |
+
unk_index = 1
|
| 34 |
+
eos_index = 2
|
| 35 |
+
sos_index = 3
|
| 36 |
+
mask_index = 4
|
| 37 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 38 |
+
def get_inputs(sm):
|
| 39 |
+
seq_len = 220
|
| 40 |
+
sm = sm.split()
|
| 41 |
+
if len(sm)>218:
|
| 42 |
+
# print('SMILES is too long ({:d})'.format(len(sm)))
|
| 43 |
+
sm = sm[:109]+sm[-109:]
|
| 44 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 45 |
+
ids = [sos_index] + ids + [eos_index]
|
| 46 |
+
seg = [1]*len(ids)
|
| 47 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 48 |
+
ids.extend(padding), seg.extend(padding)
|
| 49 |
+
return ids, seg
|
| 50 |
+
def get_array(smiles):
|
| 51 |
+
x_id, x_seg = [], []
|
| 52 |
+
for sm in smiles:
|
| 53 |
+
a,b = get_inputs(sm)
|
| 54 |
+
x_id.append(a)
|
| 55 |
+
x_seg.append(b)
|
| 56 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 57 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 58 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 59 |
+
trfm.eval()
|
| 60 |
+
x_split = [split(sm) for sm in Smiles]
|
| 61 |
+
xid, xseg = get_array(x_split)
|
| 62 |
+
X = trfm.encode(torch.t(xid))
|
| 63 |
+
return X
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def Seq_to_vec(Sequence):
|
| 67 |
+
for i in range(len(Sequence)):
|
| 68 |
+
if len(Sequence[i]) > 1000:
|
| 69 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 70 |
+
sequences_Example = []
|
| 71 |
+
for i in range(len(Sequence)):
|
| 72 |
+
zj = ''
|
| 73 |
+
for j in range(len(Sequence[i]) - 1):
|
| 74 |
+
zj += Sequence[i][j] + ' '
|
| 75 |
+
zj += Sequence[i][-1]
|
| 76 |
+
sequences_Example.append(zj)
|
| 77 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 78 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 79 |
+
gc.collect()
|
| 80 |
+
print(torch.cuda.is_available())
|
| 81 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 82 |
+
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 83 |
+
model = model.to(device)
|
| 84 |
+
model = model.eval()
|
| 85 |
+
features = []
|
| 86 |
+
for i in range(len(sequences_Example)):
|
| 87 |
+
print('For sequence ', str(i+1))
|
| 88 |
+
sequences_Example_i = sequences_Example[i]
|
| 89 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 90 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 91 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 92 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 93 |
+
with torch.no_grad():
|
| 94 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 95 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 96 |
+
for seq_num in range(len(embedding)):
|
| 97 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 98 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 99 |
+
features.append(seq_emd)
|
| 100 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 101 |
+
for i in range(len(features)):
|
| 102 |
+
for k in range(len(features[0][0])):
|
| 103 |
+
for j in range(len(features[i])):
|
| 104 |
+
features_normalize[i][k] += features[i][j][k]
|
| 105 |
+
features_normalize[i][k] /= len(features[i])
|
| 106 |
+
return features_normalize
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def Kcat_predict(Ifeature, Label, sequence_new, Smiles_new, ECNumber_new, Organism_new, Substrate_new, Type_new):
|
| 110 |
+
for i in range(10):
|
| 111 |
+
# Generate training or test set index
|
| 112 |
+
ALL_index = [j for j in range(len(Ifeature))]
|
| 113 |
+
train_index = np.array(random.sample(ALL_index, int(len(ALL_index)*0.9)))
|
| 114 |
+
Training_or_test = []
|
| 115 |
+
for j in range(len(ALL_index)):
|
| 116 |
+
if ALL_index[j] in train_index:
|
| 117 |
+
Training_or_test.append(0)
|
| 118 |
+
else:
|
| 119 |
+
Training_or_test.append(1)
|
| 120 |
+
Training_or_test = np.array(Training_or_test)
|
| 121 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 122 |
+
model = ExtraTreesRegressor()
|
| 123 |
+
model.fit(Train_data, Train_label)
|
| 124 |
+
Pre_all_label = model.predict(Ifeature)
|
| 125 |
+
res = pd.DataFrame({'sequence': sequence_new, 'smiles': Smiles_new, 'ECNumber': ECNumber_new,
|
| 126 |
+
'Organism': Organism_new, 'Substrate': Substrate_new, 'Type': Type_new,
|
| 127 |
+
'Label': Label, 'Predict_Label': Pre_all_label, 'Training or test': Training_or_test})
|
| 128 |
+
res.to_excel('PreKcat_new/'+str(i+1)+'_all_samples_metrics.xlsx')
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
if __name__ == '__main__':
|
| 132 |
+
# Dataset Load
|
| 133 |
+
with open(KCAT_DATASET_PATH, 'r') as file:
|
| 134 |
+
datasets = json.load(file)
|
| 135 |
+
# print(len(datasets))
|
| 136 |
+
sequence = [data['Sequence'] for data in datasets]
|
| 137 |
+
Smiles = [data['Smiles'] for data in datasets]
|
| 138 |
+
Label = [float(data['Value']) for data in datasets]
|
| 139 |
+
ECNumber = [data['ECNumber'] for data in datasets]
|
| 140 |
+
Organism = [data['Organism'] for data in datasets]
|
| 141 |
+
Substrate = [data['Substrate'] for data in datasets]
|
| 142 |
+
Type = [data['Type'] for data in datasets]
|
| 143 |
+
for i in range(len(Label)):
|
| 144 |
+
if Label[i] == 0:
|
| 145 |
+
Label[i] = -10000000000
|
| 146 |
+
else:
|
| 147 |
+
Label[i] = math.log(Label[i], 10)
|
| 148 |
+
print(max(Label), min(Label))
|
| 149 |
+
# Feature Extractor
|
| 150 |
+
smiles_input = smiles_to_vec(Smiles)
|
| 151 |
+
sequence_input = Seq_to_vec(sequence)
|
| 152 |
+
feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 153 |
+
with open("PreKcat_new/features_16838_PreKcat.pkl", "wb") as f:
|
| 154 |
+
pickle.dump(feature, f)
|
| 155 |
+
Label = np.array(Label)
|
| 156 |
+
# Input dataset
|
| 157 |
+
feature_new = []
|
| 158 |
+
Label_new = []
|
| 159 |
+
sequence_new = []
|
| 160 |
+
Smiles_new = []
|
| 161 |
+
ECNumber_new = []
|
| 162 |
+
Organism_new = []
|
| 163 |
+
Substrate_new = []
|
| 164 |
+
Type_new = []
|
| 165 |
+
for i in range(len(Label)):
|
| 166 |
+
if -10000000000 < Label[i] and '.' not in Smiles[i]:
|
| 167 |
+
feature_new.append(feature[i])
|
| 168 |
+
Label_new.append(Label[i])
|
| 169 |
+
sequence_new.append(sequence[i])
|
| 170 |
+
Smiles_new.append(Smiles[i])
|
| 171 |
+
ECNumber_new.append(ECNumber[i])
|
| 172 |
+
Organism_new.append(Organism[i])
|
| 173 |
+
Substrate_new.append(Substrate[i])
|
| 174 |
+
Type_new.append(Type[i])
|
| 175 |
+
print(len(Label_new), min(Label_new), max(Label_new))
|
| 176 |
+
Label_new = np.array(Label_new)
|
| 177 |
+
feature_new = np.array(feature_new)
|
| 178 |
+
# Modelling
|
| 179 |
+
Kcat_predict(feature_new, Label_new, sequence_new, Smiles_new, ECNumber_new,
|
| 180 |
+
Organism_new, Substrate_new, Type_new)
|
scripts/UniKP_kcat_Km.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
import json
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
import re
|
| 9 |
+
import gc
|
| 10 |
+
from sklearn import metrics
|
| 11 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pandas as pd
|
| 14 |
+
from sklearn.model_selection import KFold
|
| 15 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 16 |
+
from sklearn.metrics import r2_score
|
| 17 |
+
from sklearn.model_selection import train_test_split
|
| 18 |
+
import random
|
| 19 |
+
import pickle
|
| 20 |
+
import math
|
| 21 |
+
from sklearn.model_selection import train_test_split
|
| 22 |
+
import xgboost
|
| 23 |
+
import lightgbm
|
| 24 |
+
from sklearn.model_selection import GridSearchCV
|
| 25 |
+
from project_paths import (
|
| 26 |
+
|
| 27 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 28 |
+
|
| 29 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 30 |
+
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def smiles_to_vec(Smiles):
|
| 36 |
+
pad_index = 0
|
| 37 |
+
unk_index = 1
|
| 38 |
+
eos_index = 2
|
| 39 |
+
sos_index = 3
|
| 40 |
+
mask_index = 4
|
| 41 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 42 |
+
def get_inputs(sm):
|
| 43 |
+
seq_len = 220
|
| 44 |
+
sm = sm.split()
|
| 45 |
+
if len(sm)>218:
|
| 46 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 47 |
+
sm = sm[:109]+sm[-109:]
|
| 48 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 49 |
+
ids = [sos_index] + ids + [eos_index]
|
| 50 |
+
seg = [1]*len(ids)
|
| 51 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 52 |
+
ids.extend(padding), seg.extend(padding)
|
| 53 |
+
return ids, seg
|
| 54 |
+
def get_array(smiles):
|
| 55 |
+
x_id, x_seg = [], []
|
| 56 |
+
for sm in smiles:
|
| 57 |
+
a,b = get_inputs(sm)
|
| 58 |
+
x_id.append(a)
|
| 59 |
+
x_seg.append(b)
|
| 60 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 61 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 62 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 63 |
+
trfm.eval()
|
| 64 |
+
x_split = [split(sm) for sm in Smiles]
|
| 65 |
+
xid, xseg = get_array(x_split)
|
| 66 |
+
X = trfm.encode(torch.t(xid))
|
| 67 |
+
return X
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def Seq_to_vec(Sequence):
|
| 71 |
+
for i in range(len(Sequence)):
|
| 72 |
+
if len(Sequence[i]) > 1000:
|
| 73 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 74 |
+
sequences_Example = []
|
| 75 |
+
for i in range(len(Sequence)):
|
| 76 |
+
zj = ''
|
| 77 |
+
for j in range(len(Sequence[i]) - 1):
|
| 78 |
+
zj += Sequence[i][j] + ' '
|
| 79 |
+
zj += Sequence[i][-1]
|
| 80 |
+
sequences_Example.append(zj)
|
| 81 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 82 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 83 |
+
gc.collect()
|
| 84 |
+
print(torch.cuda.is_available())
|
| 85 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 86 |
+
device = torch.device('cuda:1' if torch.cuda.is_available() else 'cpu')
|
| 87 |
+
model = model.to(device)
|
| 88 |
+
model = model.eval()
|
| 89 |
+
features = []
|
| 90 |
+
for i in range(len(sequences_Example)):
|
| 91 |
+
print('For sequence ', str(i+1))
|
| 92 |
+
sequences_Example_i = sequences_Example[i]
|
| 93 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 94 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 95 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 96 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 97 |
+
with torch.no_grad():
|
| 98 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 99 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 100 |
+
for seq_num in range(len(embedding)):
|
| 101 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 102 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 103 |
+
features.append(seq_emd)
|
| 104 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 105 |
+
for i in range(len(features)):
|
| 106 |
+
for k in range(len(features[0][0])):
|
| 107 |
+
for j in range(len(features[i])):
|
| 108 |
+
features_normalize[i][k] += features[i][j][k]
|
| 109 |
+
features_normalize[i][k] /= len(features[i])
|
| 110 |
+
return features_normalize
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def Kcat_predict(Ifeature, Label):
|
| 114 |
+
kf = KFold(n_splits=5, shuffle=True)
|
| 115 |
+
All_pre_label = []
|
| 116 |
+
All_real_label = []
|
| 117 |
+
for train_index, test_index in kf.split(Ifeature, Label):
|
| 118 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 119 |
+
Test_data, Test_label = Ifeature[test_index], Label[test_index]
|
| 120 |
+
model = ExtraTreesRegressor()
|
| 121 |
+
model.fit(Train_data, Train_label)
|
| 122 |
+
Pre_label = model.predict(Test_data)
|
| 123 |
+
All_pre_label.extend(Pre_label)
|
| 124 |
+
All_real_label.extend(Test_label)
|
| 125 |
+
res = pd.DataFrame({'Value': All_real_label, 'Predict_Label': All_pre_label})
|
| 126 |
+
res.to_excel('Kcat_Km/Kcat_Km_5_cv.xlsx')
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
if __name__ == '__main__':
|
| 130 |
+
res = np.array(pd.read_excel(KCAT_KM_SAMPLES_PATH, sheet_name='main')).T
|
| 131 |
+
Smiles = res[1]
|
| 132 |
+
sequences = res[2]
|
| 133 |
+
Value = res[0]
|
| 134 |
+
for i in range(len(Value)):
|
| 135 |
+
Value[i] = math.log(Value[i], 10)
|
| 136 |
+
print(max(Value), min(Value))
|
| 137 |
+
smiles_input = smiles_to_vec(Smiles)
|
| 138 |
+
sequence_input = Seq_to_vec(sequences)
|
| 139 |
+
feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 140 |
+
with open("Kcat_Km/Kcat_Km_features_910.pkl", "wb") as f:
|
| 141 |
+
pickle.dump(feature, f)
|
| 142 |
+
feature = np.array(feature)
|
| 143 |
+
Label = np.array(Value)
|
| 144 |
+
Kcat_predict(feature, Label)
|
scripts/UniKP_kcat_model.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
import json
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
import re
|
| 9 |
+
import gc
|
| 10 |
+
from sklearn import metrics
|
| 11 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pandas as pd
|
| 14 |
+
from sklearn.model_selection import KFold
|
| 15 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 16 |
+
from sklearn.metrics import r2_score
|
| 17 |
+
from sklearn.model_selection import train_test_split
|
| 18 |
+
import random
|
| 19 |
+
import pickle
|
| 20 |
+
import math
|
| 21 |
+
from sklearn.model_selection import KFold
|
| 22 |
+
from project_paths import (
|
| 23 |
+
|
| 24 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 25 |
+
|
| 26 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 27 |
+
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def Kcat_predict(Ifeature, Label):
|
| 33 |
+
for i in range(5):
|
| 34 |
+
model = ExtraTreesRegressor()
|
| 35 |
+
model.fit(Ifeature, Label)
|
| 36 |
+
with open('PreKcat_new/'+str(i)+"_model.pkl", "wb") as f:
|
| 37 |
+
pickle.dump(model, f)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
if __name__ == '__main__':
|
| 41 |
+
with open(KCAT_DATASET_PATH, 'r') as file:
|
| 42 |
+
datasets = json.load(file)
|
| 43 |
+
# print(len(datasets))
|
| 44 |
+
Label = [float(data['Value']) for data in datasets]
|
| 45 |
+
Smiles = [data['Smiles']for data in datasets]
|
| 46 |
+
for i in range(len(Label)):
|
| 47 |
+
if Label[i] == 0:
|
| 48 |
+
Label[i] = -10000000000
|
| 49 |
+
else:
|
| 50 |
+
Label[i] = math.log(Label[i], 10)
|
| 51 |
+
with open("PreKcat_new/features_16838_PreKcat.pkl", "rb") as f:
|
| 52 |
+
feature = pickle.load(f)
|
| 53 |
+
Label = np.array(Label)
|
| 54 |
+
Label_new = []
|
| 55 |
+
feature_new = []
|
| 56 |
+
for i in range(len(Label)):
|
| 57 |
+
if -10000000000 < Label[i] and '.' not in Smiles[i]:
|
| 58 |
+
Label_new.append(Label[i])
|
| 59 |
+
feature_new.append(feature[i])
|
| 60 |
+
print(len(Label_new))
|
| 61 |
+
Label_new = np.array(Label_new)
|
| 62 |
+
feature_new = np.array(feature_new)
|
| 63 |
+
Kcat_predict(feature_new, Label_new)
|
scripts/UniKP_pH.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
import json
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
import re
|
| 9 |
+
import gc
|
| 10 |
+
from sklearn import metrics
|
| 11 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pandas as pd
|
| 14 |
+
from sklearn.model_selection import KFold
|
| 15 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 16 |
+
from sklearn.metrics import r2_score
|
| 17 |
+
from sklearn.model_selection import train_test_split
|
| 18 |
+
import random
|
| 19 |
+
import pickle
|
| 20 |
+
import math
|
| 21 |
+
from project_paths import (
|
| 22 |
+
|
| 23 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 24 |
+
|
| 25 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 26 |
+
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def smiles_to_vec(Smiles):
|
| 32 |
+
pad_index = 0
|
| 33 |
+
unk_index = 1
|
| 34 |
+
eos_index = 2
|
| 35 |
+
sos_index = 3
|
| 36 |
+
mask_index = 4
|
| 37 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 38 |
+
def get_inputs(sm):
|
| 39 |
+
seq_len = 220
|
| 40 |
+
sm = sm.split()
|
| 41 |
+
if len(sm)>218:
|
| 42 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 43 |
+
sm = sm[:109]+sm[-109:]
|
| 44 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 45 |
+
ids = [sos_index] + ids + [eos_index]
|
| 46 |
+
seg = [1]*len(ids)
|
| 47 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 48 |
+
ids.extend(padding), seg.extend(padding)
|
| 49 |
+
return ids, seg
|
| 50 |
+
def get_array(smiles):
|
| 51 |
+
x_id, x_seg = [], []
|
| 52 |
+
for sm in smiles:
|
| 53 |
+
a,b = get_inputs(sm)
|
| 54 |
+
x_id.append(a)
|
| 55 |
+
x_seg.append(b)
|
| 56 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 57 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 58 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 59 |
+
trfm.eval()
|
| 60 |
+
x_split = [split(sm) for sm in Smiles]
|
| 61 |
+
xid, xseg = get_array(x_split)
|
| 62 |
+
X = trfm.encode(torch.t(xid))
|
| 63 |
+
return X
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def Seq_to_vec(Sequence):
|
| 67 |
+
for i in range(len(Sequence)):
|
| 68 |
+
if len(Sequence[i]) > 1000:
|
| 69 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 70 |
+
sequences_Example = []
|
| 71 |
+
for i in range(len(Sequence)):
|
| 72 |
+
zj = ''
|
| 73 |
+
for j in range(len(Sequence[i]) - 1):
|
| 74 |
+
zj += Sequence[i][j] + ' '
|
| 75 |
+
zj += Sequence[i][-1]
|
| 76 |
+
sequences_Example.append(zj)
|
| 77 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 78 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 79 |
+
gc.collect()
|
| 80 |
+
print(torch.cuda.is_available())
|
| 81 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 82 |
+
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 83 |
+
model = model.to(device)
|
| 84 |
+
model = model.eval()
|
| 85 |
+
features = []
|
| 86 |
+
for i in range(len(sequences_Example)):
|
| 87 |
+
print('For sequence ', str(i+1))
|
| 88 |
+
sequences_Example_i = sequences_Example[i]
|
| 89 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 90 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 91 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 92 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 93 |
+
with torch.no_grad():
|
| 94 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 95 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 96 |
+
for seq_num in range(len(embedding)):
|
| 97 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 98 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 99 |
+
features.append(seq_emd)
|
| 100 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 101 |
+
for i in range(len(features)):
|
| 102 |
+
for k in range(len(features[0][0])):
|
| 103 |
+
for j in range(len(features[i])):
|
| 104 |
+
features_normalize[i][k] += features[i][j][k]
|
| 105 |
+
features_normalize[i][k] /= len(features[i])
|
| 106 |
+
return features_normalize
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def Kcat_predict(Ifeature, Label):
|
| 110 |
+
kf = KFold(n_splits=5, shuffle=True)
|
| 111 |
+
All_pre_label = []
|
| 112 |
+
All_real_label = []
|
| 113 |
+
for train_index, test_index in kf.split(Ifeature, Label):
|
| 114 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 115 |
+
Test_data, Test_label = Ifeature[test_index], Label[test_index]
|
| 116 |
+
model = ExtraTreesRegressor()
|
| 117 |
+
model.fit(Train_data, Train_label)
|
| 118 |
+
Pre_label = model.predict(Test_data)
|
| 119 |
+
All_pre_label.extend(Pre_label)
|
| 120 |
+
All_real_label.extend(Test_label)
|
| 121 |
+
res = pd.DataFrame({'Value': All_real_label, 'Predict_Label': All_pre_label})
|
| 122 |
+
res.to_excel('pH/pH_Kcat_5_cv.xlsx')
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
if __name__ == '__main__':
|
| 126 |
+
# Dataset Load
|
| 127 |
+
database = np.array(pd.read_excel(PH_SMILES_PATH)).T
|
| 128 |
+
sequence = database[1]
|
| 129 |
+
smiles = database[3]
|
| 130 |
+
pH = database[5].reshape([len(smiles), 1])
|
| 131 |
+
Label = database[4]
|
| 132 |
+
for i in range(len(Label)):
|
| 133 |
+
Label[i] = math.log(Label[i], 10)
|
| 134 |
+
print(max(Label), min(Label))
|
| 135 |
+
# Feature Extractor
|
| 136 |
+
# smiles_input = smiles_to_vec(smiles)
|
| 137 |
+
# sequence_input = Seq_to_vec(sequence)
|
| 138 |
+
# print(sequence_input.shape, sequence_input.shape, pH.shape)
|
| 139 |
+
# feature = np.concatenate((smiles_input, sequence_input, pH), axis=1)
|
| 140 |
+
# with open("pH/features_636_pH_PreKcat.pkl", "wb") as f:
|
| 141 |
+
# pickle.dump(feature, f)
|
| 142 |
+
with open("pH/features_636_pH_PreKcat.pkl", "rb") as f:
|
| 143 |
+
feature = pickle.load(f)
|
| 144 |
+
# Modelling
|
| 145 |
+
Kcat_predict(feature, Label)
|
scripts/UniKP_pH_s2.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
import json
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
import re
|
| 9 |
+
import gc
|
| 10 |
+
from sklearn import metrics
|
| 11 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 12 |
+
from sklearn.linear_model import LinearRegression
|
| 13 |
+
import numpy as np
|
| 14 |
+
import pandas as pd
|
| 15 |
+
from sklearn.model_selection import KFold
|
| 16 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 17 |
+
from sklearn.metrics import r2_score
|
| 18 |
+
from sklearn.model_selection import train_test_split
|
| 19 |
+
import random
|
| 20 |
+
import pickle
|
| 21 |
+
import math
|
| 22 |
+
from project_paths import (
|
| 23 |
+
|
| 24 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 25 |
+
|
| 26 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 27 |
+
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def smiles_to_vec(Smiles):
|
| 33 |
+
pad_index = 0
|
| 34 |
+
unk_index = 1
|
| 35 |
+
eos_index = 2
|
| 36 |
+
sos_index = 3
|
| 37 |
+
mask_index = 4
|
| 38 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 39 |
+
def get_inputs(sm):
|
| 40 |
+
seq_len = 220
|
| 41 |
+
sm = sm.split()
|
| 42 |
+
if len(sm)>218:
|
| 43 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 44 |
+
sm = sm[:109]+sm[-109:]
|
| 45 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 46 |
+
ids = [sos_index] + ids + [eos_index]
|
| 47 |
+
seg = [1]*len(ids)
|
| 48 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 49 |
+
ids.extend(padding), seg.extend(padding)
|
| 50 |
+
return ids, seg
|
| 51 |
+
def get_array(smiles):
|
| 52 |
+
x_id, x_seg = [], []
|
| 53 |
+
for sm in smiles:
|
| 54 |
+
a,b = get_inputs(sm)
|
| 55 |
+
x_id.append(a)
|
| 56 |
+
x_seg.append(b)
|
| 57 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 58 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 59 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 60 |
+
trfm.eval()
|
| 61 |
+
x_split = [split(sm) for sm in Smiles]
|
| 62 |
+
xid, xseg = get_array(x_split)
|
| 63 |
+
X = trfm.encode(torch.t(xid))
|
| 64 |
+
return X
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def Seq_to_vec(Sequence):
|
| 68 |
+
for i in range(len(Sequence)):
|
| 69 |
+
if len(Sequence[i]) > 1000:
|
| 70 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 71 |
+
sequences_Example = []
|
| 72 |
+
for i in range(len(Sequence)):
|
| 73 |
+
zj = ''
|
| 74 |
+
for j in range(len(Sequence[i]) - 1):
|
| 75 |
+
zj += Sequence[i][j] + ' '
|
| 76 |
+
zj += Sequence[i][-1]
|
| 77 |
+
sequences_Example.append(zj)
|
| 78 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 79 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 80 |
+
gc.collect()
|
| 81 |
+
print(torch.cuda.is_available())
|
| 82 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 83 |
+
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 84 |
+
model = model.to(device)
|
| 85 |
+
model = model.eval()
|
| 86 |
+
features = []
|
| 87 |
+
for i in range(len(sequences_Example)):
|
| 88 |
+
print('For sequence ', str(i+1))
|
| 89 |
+
sequences_Example_i = sequences_Example[i]
|
| 90 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 91 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 92 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 93 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 94 |
+
with torch.no_grad():
|
| 95 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 96 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 97 |
+
for seq_num in range(len(embedding)):
|
| 98 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 99 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 100 |
+
features.append(seq_emd)
|
| 101 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 102 |
+
for i in range(len(features)):
|
| 103 |
+
for k in range(len(features[0][0])):
|
| 104 |
+
for j in range(len(features[i])):
|
| 105 |
+
features_normalize[i][k] += features[i][j][k]
|
| 106 |
+
features_normalize[i][k] /= len(features[i])
|
| 107 |
+
return features_normalize
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def Kcat_predict(feature, pH, sequence, smiles, Label):
|
| 111 |
+
# Generate index
|
| 112 |
+
Train_Validation_index = random.sample(range(len(feature)), int(len(feature)*0.8))
|
| 113 |
+
Test_index = []
|
| 114 |
+
for i in range(len(feature)):
|
| 115 |
+
if i not in Train_Validation_index:
|
| 116 |
+
Test_index.append(i)
|
| 117 |
+
Validation_index = random.sample(Train_Validation_index, int(len(Train_Validation_index)*0.2))
|
| 118 |
+
Train_index = []
|
| 119 |
+
for i in range(len(feature)):
|
| 120 |
+
if i not in Validation_index and i not in Test_index:
|
| 121 |
+
Train_index.append(i)
|
| 122 |
+
print(len(Train_index), len(Validation_index), len(Test_index))
|
| 123 |
+
Training_Validation_Test = []
|
| 124 |
+
for i in range(len(feature)):
|
| 125 |
+
if i in Train_index:
|
| 126 |
+
Training_Validation_Test.append(0)
|
| 127 |
+
elif i in Validation_index:
|
| 128 |
+
Training_Validation_Test.append(1)
|
| 129 |
+
else:
|
| 130 |
+
Training_Validation_Test.append(2)
|
| 131 |
+
Train_index = np.array(Train_index)
|
| 132 |
+
Validation_index = np.array(Validation_index)
|
| 133 |
+
Test_index = np.array(Test_index)
|
| 134 |
+
print(Train_index.shape, Validation_index.shape, Test_index.shape)
|
| 135 |
+
# First model
|
| 136 |
+
print(feature[Train_index].shape, pH[Train_index].shape)
|
| 137 |
+
model_1_input = np.concatenate((feature[Train_index], pH[Train_index]), axis=1)
|
| 138 |
+
model_first = ExtraTreesRegressor()
|
| 139 |
+
model_first.fit(model_1_input, Label[Train_index])
|
| 140 |
+
# Second model
|
| 141 |
+
with open("PreKcat_new/0_model.pkl", "rb") as f:
|
| 142 |
+
model_base = pickle.load(f)
|
| 143 |
+
Kcat_baseline = model_base.predict(feature[Validation_index]).reshape([len(Validation_index), 1])
|
| 144 |
+
model_1_2_input = np.concatenate((feature[Validation_index], pH[Validation_index]), axis=1)
|
| 145 |
+
Kcat_calibrated = model_first.predict(model_1_2_input).reshape([len(Validation_index), 1])
|
| 146 |
+
kcat_fused = np.concatenate((Kcat_baseline, Kcat_calibrated), axis=1)
|
| 147 |
+
model_second = LinearRegression()
|
| 148 |
+
model_second.fit(kcat_fused, Label[Validation_index])
|
| 149 |
+
# Final prediction
|
| 150 |
+
model_1_3_input = np.concatenate((feature, pH), axis=1)
|
| 151 |
+
Kcat_calibrated_3 = model_first.predict(model_1_3_input).reshape([len(feature), 1])
|
| 152 |
+
Kcat_baseline_3 = model_base.predict(feature).reshape([len(feature), 1])
|
| 153 |
+
kcat_fused_3 = np.concatenate((Kcat_baseline_3, Kcat_calibrated_3), axis=1)
|
| 154 |
+
Predicted_value = model_second.predict(kcat_fused_3).reshape([len(feature)])
|
| 155 |
+
Training_Validation_Test = np.array(Training_Validation_Test).reshape([len(feature)])
|
| 156 |
+
pH = np.array(pH).reshape([len(Label)])
|
| 157 |
+
Kcat_baseline_3 = np.array(Kcat_baseline_3).reshape([len(feature)])
|
| 158 |
+
Kcat_calibrated_3 = np.array(Kcat_calibrated_3).reshape([len(feature)])
|
| 159 |
+
print(Training_Validation_Test.shape)
|
| 160 |
+
# save
|
| 161 |
+
res = pd.DataFrame({'Value': Label,
|
| 162 |
+
'sequence': sequence,
|
| 163 |
+
'smiles': smiles,
|
| 164 |
+
'pH': pH,
|
| 165 |
+
'Prediction_first_base': Kcat_baseline_3,
|
| 166 |
+
'Prediction_first_pH': Kcat_calibrated_3,
|
| 167 |
+
'Prediction_second': Predicted_value,
|
| 168 |
+
'Training_Validation_Test': Training_Validation_Test})
|
| 169 |
+
res.to_excel('pH/s2_pH_Kcat.xlsx')
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
if __name__ == '__main__':
|
| 173 |
+
# Dataset Load
|
| 174 |
+
database = np.array(pd.read_excel(PH_SMILES_PATH)).T
|
| 175 |
+
sequence = database[1]
|
| 176 |
+
smiles = database[3]
|
| 177 |
+
pH = database[5]
|
| 178 |
+
Label = database[4]
|
| 179 |
+
for i in range(len(Label)):
|
| 180 |
+
Label[i] = math.log(Label[i], 10)
|
| 181 |
+
print(max(Label), min(Label))
|
| 182 |
+
pH = np.array(pH).reshape([len(Label), 1])
|
| 183 |
+
with open("pH/features_636_pH_PreKcat.pkl", "rb") as f:
|
| 184 |
+
feature = pickle.load(f)
|
| 185 |
+
# Modelling
|
| 186 |
+
Kcat_predict(feature, pH, sequence, smiles, Label)
|
scripts/UniKP_temperature.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
import json
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
import re
|
| 9 |
+
import gc
|
| 10 |
+
from sklearn import metrics
|
| 11 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pandas as pd
|
| 14 |
+
from sklearn.model_selection import KFold
|
| 15 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 16 |
+
from sklearn.metrics import r2_score
|
| 17 |
+
from sklearn.model_selection import train_test_split
|
| 18 |
+
import random
|
| 19 |
+
import pickle
|
| 20 |
+
import math
|
| 21 |
+
from project_paths import (
|
| 22 |
+
|
| 23 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 24 |
+
|
| 25 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 26 |
+
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def smiles_to_vec(Smiles):
|
| 32 |
+
pad_index = 0
|
| 33 |
+
unk_index = 1
|
| 34 |
+
eos_index = 2
|
| 35 |
+
sos_index = 3
|
| 36 |
+
mask_index = 4
|
| 37 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 38 |
+
def get_inputs(sm):
|
| 39 |
+
seq_len = 220
|
| 40 |
+
sm = sm.split()
|
| 41 |
+
if len(sm)>218:
|
| 42 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 43 |
+
sm = sm[:109]+sm[-109:]
|
| 44 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 45 |
+
ids = [sos_index] + ids + [eos_index]
|
| 46 |
+
seg = [1]*len(ids)
|
| 47 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 48 |
+
ids.extend(padding), seg.extend(padding)
|
| 49 |
+
return ids, seg
|
| 50 |
+
def get_array(smiles):
|
| 51 |
+
x_id, x_seg = [], []
|
| 52 |
+
for sm in smiles:
|
| 53 |
+
a,b = get_inputs(sm)
|
| 54 |
+
x_id.append(a)
|
| 55 |
+
x_seg.append(b)
|
| 56 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 57 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 58 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 59 |
+
trfm.eval()
|
| 60 |
+
x_split = [split(sm) for sm in Smiles]
|
| 61 |
+
xid, xseg = get_array(x_split)
|
| 62 |
+
X = trfm.encode(torch.t(xid))
|
| 63 |
+
return X
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def Seq_to_vec(Sequence):
|
| 67 |
+
for i in range(len(Sequence)):
|
| 68 |
+
if len(Sequence[i]) > 1000:
|
| 69 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 70 |
+
sequences_Example = []
|
| 71 |
+
for i in range(len(Sequence)):
|
| 72 |
+
zj = ''
|
| 73 |
+
for j in range(len(Sequence[i]) - 1):
|
| 74 |
+
zj += Sequence[i][j] + ' '
|
| 75 |
+
zj += Sequence[i][-1]
|
| 76 |
+
sequences_Example.append(zj)
|
| 77 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 78 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 79 |
+
gc.collect()
|
| 80 |
+
print(torch.cuda.is_available())
|
| 81 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 82 |
+
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 83 |
+
model = model.to(device)
|
| 84 |
+
model = model.eval()
|
| 85 |
+
features = []
|
| 86 |
+
for i in range(len(sequences_Example)):
|
| 87 |
+
print('For sequence ', str(i+1))
|
| 88 |
+
sequences_Example_i = sequences_Example[i]
|
| 89 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 90 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 91 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 92 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 93 |
+
with torch.no_grad():
|
| 94 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 95 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 96 |
+
for seq_num in range(len(embedding)):
|
| 97 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 98 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 99 |
+
features.append(seq_emd)
|
| 100 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 101 |
+
for i in range(len(features)):
|
| 102 |
+
for k in range(len(features[0][0])):
|
| 103 |
+
for j in range(len(features[i])):
|
| 104 |
+
features_normalize[i][k] += features[i][j][k]
|
| 105 |
+
features_normalize[i][k] /= len(features[i])
|
| 106 |
+
return features_normalize
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def Kcat_predict(Ifeature, Label):
|
| 110 |
+
kf = KFold(n_splits=5, shuffle=True)
|
| 111 |
+
All_pre_label = []
|
| 112 |
+
All_real_label = []
|
| 113 |
+
for train_index, test_index in kf.split(Ifeature, Label):
|
| 114 |
+
Train_data, Train_label = Ifeature[train_index], Label[train_index]
|
| 115 |
+
Test_data, Test_label = Ifeature[test_index], Label[test_index]
|
| 116 |
+
model = ExtraTreesRegressor()
|
| 117 |
+
model.fit(Train_data, Train_label)
|
| 118 |
+
Pre_label = model.predict(Test_data)
|
| 119 |
+
All_pre_label.extend(Pre_label)
|
| 120 |
+
All_real_label.extend(Test_label)
|
| 121 |
+
res = pd.DataFrame({'Value': All_real_label, 'Predict_Label': All_pre_label})
|
| 122 |
+
res.to_excel('degree/degree_Kcat_5_cv.xlsx')
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
if __name__ == '__main__':
|
| 126 |
+
# Dataset Load
|
| 127 |
+
database = np.array(pd.read_excel(DEGREE_SMILES_PATH)).T
|
| 128 |
+
sequence = database[1]
|
| 129 |
+
smiles = database[3]
|
| 130 |
+
pH = database[5].reshape([len(smiles), 1])
|
| 131 |
+
Label = database[4]
|
| 132 |
+
for i in range(len(Label)):
|
| 133 |
+
Label[i] = math.log(Label[i], 10)
|
| 134 |
+
print(max(Label), min(Label))
|
| 135 |
+
# Feature Extractor
|
| 136 |
+
# smiles_input = smiles_to_vec(smiles)
|
| 137 |
+
# sequence_input = Seq_to_vec(sequence)
|
| 138 |
+
# print(sequence_input.shape, sequence_input.shape, pH.shape)
|
| 139 |
+
# feature = np.concatenate((smiles_input, sequence_input), axis=1)
|
| 140 |
+
# with open("degree/features_572_degree_PreKcat.pkl", "wb") as f:
|
| 141 |
+
# pickle.dump(feature, f)
|
| 142 |
+
with open("degree/features_572_degree_PreKcat.pkl", "rb") as f:
|
| 143 |
+
feature = pickle.load(f)
|
| 144 |
+
# Modelling
|
| 145 |
+
Kcat_predict(feature, Label)
|
scripts/UniKP_temperature_s2.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
import json
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
import re
|
| 9 |
+
import gc
|
| 10 |
+
from sklearn import metrics
|
| 11 |
+
from sklearn.ensemble import ExtraTreesRegressor
|
| 12 |
+
from sklearn.linear_model import LinearRegression
|
| 13 |
+
import numpy as np
|
| 14 |
+
import pandas as pd
|
| 15 |
+
from sklearn.model_selection import KFold
|
| 16 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error
|
| 17 |
+
from sklearn.metrics import r2_score
|
| 18 |
+
from sklearn.model_selection import train_test_split
|
| 19 |
+
import random
|
| 20 |
+
import pickle
|
| 21 |
+
import math
|
| 22 |
+
from project_paths import (
|
| 23 |
+
|
| 24 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 25 |
+
|
| 26 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 27 |
+
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def smiles_to_vec(Smiles):
|
| 33 |
+
pad_index = 0
|
| 34 |
+
unk_index = 1
|
| 35 |
+
eos_index = 2
|
| 36 |
+
sos_index = 3
|
| 37 |
+
mask_index = 4
|
| 38 |
+
vocab = WordVocab.load_vocab(VOCAB_PATH)
|
| 39 |
+
def get_inputs(sm):
|
| 40 |
+
seq_len = 220
|
| 41 |
+
sm = sm.split()
|
| 42 |
+
if len(sm)>218:
|
| 43 |
+
print('SMILES is too long ({:d})'.format(len(sm)))
|
| 44 |
+
sm = sm[:109]+sm[-109:]
|
| 45 |
+
ids = [vocab.stoi.get(token, unk_index) for token in sm]
|
| 46 |
+
ids = [sos_index] + ids + [eos_index]
|
| 47 |
+
seg = [1]*len(ids)
|
| 48 |
+
padding = [pad_index]*(seq_len - len(ids))
|
| 49 |
+
ids.extend(padding), seg.extend(padding)
|
| 50 |
+
return ids, seg
|
| 51 |
+
def get_array(smiles):
|
| 52 |
+
x_id, x_seg = [], []
|
| 53 |
+
for sm in smiles:
|
| 54 |
+
a,b = get_inputs(sm)
|
| 55 |
+
x_id.append(a)
|
| 56 |
+
x_seg.append(b)
|
| 57 |
+
return torch.tensor(x_id), torch.tensor(x_seg)
|
| 58 |
+
trfm = TrfmSeq2seq(len(vocab), 256, len(vocab), 4)
|
| 59 |
+
trfm.load_state_dict(torch.load(TRFM_PATH))
|
| 60 |
+
trfm.eval()
|
| 61 |
+
x_split = [split(sm) for sm in Smiles]
|
| 62 |
+
xid, xseg = get_array(x_split)
|
| 63 |
+
X = trfm.encode(torch.t(xid))
|
| 64 |
+
return X
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def Seq_to_vec(Sequence):
|
| 68 |
+
for i in range(len(Sequence)):
|
| 69 |
+
if len(Sequence[i]) > 1000:
|
| 70 |
+
Sequence[i] = Sequence[i][:500] + Sequence[i][-500:]
|
| 71 |
+
sequences_Example = []
|
| 72 |
+
for i in range(len(Sequence)):
|
| 73 |
+
zj = ''
|
| 74 |
+
for j in range(len(Sequence[i]) - 1):
|
| 75 |
+
zj += Sequence[i][j] + ' '
|
| 76 |
+
zj += Sequence[i][-1]
|
| 77 |
+
sequences_Example.append(zj)
|
| 78 |
+
tokenizer = T5Tokenizer.from_pretrained(PROT_T5_MODEL, do_lower_case=False)
|
| 79 |
+
model = T5EncoderModel.from_pretrained(PROT_T5_MODEL)
|
| 80 |
+
gc.collect()
|
| 81 |
+
print(torch.cuda.is_available())
|
| 82 |
+
# 'cuda:0' if torch.cuda.is_available() else
|
| 83 |
+
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
| 84 |
+
model = model.to(device)
|
| 85 |
+
model = model.eval()
|
| 86 |
+
features = []
|
| 87 |
+
for i in range(len(sequences_Example)):
|
| 88 |
+
print('For sequence ', str(i+1))
|
| 89 |
+
sequences_Example_i = sequences_Example[i]
|
| 90 |
+
sequences_Example_i = [re.sub(r"[UZOB]", "X", sequences_Example_i)]
|
| 91 |
+
ids = tokenizer.batch_encode_plus(sequences_Example_i, add_special_tokens=True, padding=True)
|
| 92 |
+
input_ids = torch.tensor(ids['input_ids']).to(device)
|
| 93 |
+
attention_mask = torch.tensor(ids['attention_mask']).to(device)
|
| 94 |
+
with torch.no_grad():
|
| 95 |
+
embedding = model(input_ids=input_ids, attention_mask=attention_mask)
|
| 96 |
+
embedding = embedding.last_hidden_state.cpu().numpy()
|
| 97 |
+
for seq_num in range(len(embedding)):
|
| 98 |
+
seq_len = (attention_mask[seq_num] == 1).sum()
|
| 99 |
+
seq_emd = embedding[seq_num][:seq_len - 1]
|
| 100 |
+
features.append(seq_emd)
|
| 101 |
+
features_normalize = np.zeros([len(features), len(features[0][0])], dtype=float)
|
| 102 |
+
for i in range(len(features)):
|
| 103 |
+
for k in range(len(features[0][0])):
|
| 104 |
+
for j in range(len(features[i])):
|
| 105 |
+
features_normalize[i][k] += features[i][j][k]
|
| 106 |
+
features_normalize[i][k] /= len(features[i])
|
| 107 |
+
return features_normalize
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def Kcat_predict(feature, pH, sequence, smiles, Label):
|
| 111 |
+
# Generate index
|
| 112 |
+
Train_Validation_index = random.sample(range(len(feature)), int(len(feature)*0.8))
|
| 113 |
+
Test_index = []
|
| 114 |
+
for i in range(len(feature)):
|
| 115 |
+
if i not in Train_Validation_index:
|
| 116 |
+
Test_index.append(i)
|
| 117 |
+
Validation_index = random.sample(Train_Validation_index, int(len(Train_Validation_index)*0.2))
|
| 118 |
+
Train_index = []
|
| 119 |
+
for i in range(len(feature)):
|
| 120 |
+
if i not in Validation_index and i not in Test_index:
|
| 121 |
+
Train_index.append(i)
|
| 122 |
+
print(len(Train_index), len(Validation_index), len(Test_index))
|
| 123 |
+
Training_Validation_Test = []
|
| 124 |
+
for i in range(len(feature)):
|
| 125 |
+
if i in Train_index:
|
| 126 |
+
Training_Validation_Test.append(0)
|
| 127 |
+
elif i in Validation_index:
|
| 128 |
+
Training_Validation_Test.append(1)
|
| 129 |
+
else:
|
| 130 |
+
Training_Validation_Test.append(2)
|
| 131 |
+
Train_index = np.array(Train_index)
|
| 132 |
+
Validation_index = np.array(Validation_index)
|
| 133 |
+
Test_index = np.array(Test_index)
|
| 134 |
+
print(Train_index.shape, Validation_index.shape, Test_index.shape)
|
| 135 |
+
# First model
|
| 136 |
+
print(feature[Train_index].shape, pH[Train_index].shape)
|
| 137 |
+
model_1_input = np.concatenate((feature[Train_index], pH[Train_index]), axis=1)
|
| 138 |
+
model_first = ExtraTreesRegressor()
|
| 139 |
+
model_first.fit(model_1_input, Label[Train_index])
|
| 140 |
+
# Second model
|
| 141 |
+
with open("PreKcat_new/0_model.pkl", "rb") as f:
|
| 142 |
+
model_base = pickle.load(f)
|
| 143 |
+
Kcat_baseline = model_base.predict(feature[Validation_index]).reshape([len(Validation_index), 1])
|
| 144 |
+
model_1_2_input = np.concatenate((feature[Validation_index], pH[Validation_index]), axis=1)
|
| 145 |
+
Kcat_calibrated = model_first.predict(model_1_2_input).reshape([len(Validation_index), 1])
|
| 146 |
+
kcat_fused = np.concatenate((Kcat_baseline, Kcat_calibrated), axis=1)
|
| 147 |
+
model_second = LinearRegression()
|
| 148 |
+
model_second.fit(kcat_fused, Label[Validation_index])
|
| 149 |
+
# Final prediction
|
| 150 |
+
model_1_3_input = np.concatenate((feature, pH), axis=1)
|
| 151 |
+
Kcat_calibrated_3 = model_first.predict(model_1_3_input).reshape([len(feature), 1])
|
| 152 |
+
Kcat_baseline_3 = model_base.predict(feature).reshape([len(feature), 1])
|
| 153 |
+
kcat_fused_3 = np.concatenate((Kcat_baseline_3, Kcat_calibrated_3), axis=1)
|
| 154 |
+
Predicted_value = model_second.predict(kcat_fused_3).reshape([len(feature)])
|
| 155 |
+
Training_Validation_Test = np.array(Training_Validation_Test).reshape([len(feature)])
|
| 156 |
+
pH = np.array(pH).reshape([len(Label)])
|
| 157 |
+
Kcat_baseline_3 = np.array(Kcat_baseline_3).reshape([len(feature)])
|
| 158 |
+
Kcat_calibrated_3 = np.array(Kcat_calibrated_3).reshape([len(feature)])
|
| 159 |
+
print(Training_Validation_Test.shape)
|
| 160 |
+
# save
|
| 161 |
+
res = pd.DataFrame({'Value': Label,
|
| 162 |
+
'sequence': sequence,
|
| 163 |
+
'smiles': smiles,
|
| 164 |
+
'pH': pH,
|
| 165 |
+
'Prediction_first_base': Kcat_baseline_3,
|
| 166 |
+
'Prediction_first_pH': Kcat_calibrated_3,
|
| 167 |
+
'Prediction_second': Predicted_value,
|
| 168 |
+
'Training_Validation_Test': Training_Validation_Test})
|
| 169 |
+
res.to_excel('degree/s2_degree_Kcat.xlsx')
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
if __name__ == '__main__':
|
| 173 |
+
# Dataset Load
|
| 174 |
+
database = np.array(pd.read_excel(DEGREE_SMILES_PATH)).T
|
| 175 |
+
sequence = database[1]
|
| 176 |
+
smiles = database[3]
|
| 177 |
+
pH = database[5]
|
| 178 |
+
Label = database[4]
|
| 179 |
+
for i in range(len(Label)):
|
| 180 |
+
Label[i] = math.log(Label[i], 10)
|
| 181 |
+
print(max(Label), min(Label))
|
| 182 |
+
pH = np.array(pH).reshape([len(Label), 1])
|
| 183 |
+
with open("degree/features_572_degree_PreKcat.pkl", "rb") as f:
|
| 184 |
+
feature = pickle.load(f)
|
| 185 |
+
# Modelling
|
| 186 |
+
Kcat_predict(feature, pH, sequence, smiles, Label)
|
scripts/_bootstrap.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
ROOT_DIR = Path(__file__).resolve().parents[1]
|
| 6 |
+
root = str(ROOT_DIR)
|
| 7 |
+
if root not in sys.path:
|
| 8 |
+
sys.path.insert(0, root)
|
scripts/build_corpus.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import argparse
|
| 3 |
+
import pandas as pd
|
| 4 |
+
from tqdm import tqdm
|
| 5 |
+
|
| 6 |
+
from model.utils import split
|
| 7 |
+
|
| 8 |
+
def main():
|
| 9 |
+
parser = argparse.ArgumentParser(description='Build a corpus file')
|
| 10 |
+
parser.add_argument('--in_path', '-i', type=str, default='data/chembl24_bert_train.csv', help='input file')
|
| 11 |
+
parser.add_argument('--out_path', '-o', type=str, default='data/chembl24_corpus.txt', help='output file')
|
| 12 |
+
args = parser.parse_args()
|
| 13 |
+
|
| 14 |
+
smiles = pd.read_csv(args.in_path)['first'].values
|
| 15 |
+
with open(args.out_path, 'a') as f:
|
| 16 |
+
for sm in tqdm(smiles):
|
| 17 |
+
f.write(split(sm)+'\n')
|
| 18 |
+
print('Built a corpus file!')
|
| 19 |
+
|
| 20 |
+
if __name__=='__main__':
|
| 21 |
+
main()
|
| 22 |
+
|
| 23 |
+
|
scripts/demo_kcat.py
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _bootstrap
|
| 2 |
+
import torch
|
| 3 |
+
from model.build_vocab import WordVocab
|
| 4 |
+
from model.pretrain_trfm import TrfmSeq2seq
|
| 5 |
+
from model.utils import split
|
| 6 |
+
|
| 7 |
+
from transformers import T5EncoderModel, T5Tokenizer
|
| 8 |
+
|
| 9 |
+
import re
|
| 10 |
+
import gc
|
| 11 |
+
import numpy as np
|
| 12 |
+
import pandas as pd
|
| 13 |
+
import pickle
|
| 14 |
+
import math
|
| 15 |
+
from project_paths import (
|
| 16 |
+
|
| 17 |
+
DEGREE_SMILES_PATH, KCAT_DATASET_PATH, KCAT_KM_SAMPLES_PATH, KM_TEST_PATH,
|
| 18 |
+
|
| 19 |
+
PH_SMILES_PATH, PROT_T5_MODEL, TRFM_PATH, UNIKP_MODEL_DIR, VOCAB_PATH,
|
| 20 |
+
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# ============================
|
| 25 |
+
# SMILES -> vector
|
| 26 |
+
# ============================
|
| 27 |
+
|
| 28 |
+
def smiles_to_vec(Smiles):
|
| 29 |
+
|
| 30 |
+
pad_index = 0
|
| 31 |
+
unk_index = 1
|
| 32 |
+
eos_index = 2
|
| 33 |
+
sos_index = 3
|
| 34 |
+
|
| 35 |
+
vocab = WordVocab.load_vocab(
|
| 36 |
+
VOCAB_PATH
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def get_inputs(sm):
|
| 41 |
+
|
| 42 |
+
seq_len = 220
|
| 43 |
+
|
| 44 |
+
sm = sm.split()
|
| 45 |
+
|
| 46 |
+
if len(sm) > 218:
|
| 47 |
+
print(
|
| 48 |
+
"SMILES too long:",
|
| 49 |
+
len(sm)
|
| 50 |
+
)
|
| 51 |
+
sm = sm[:109] + sm[-109:]
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
ids = [
|
| 55 |
+
vocab.stoi.get(
|
| 56 |
+
token,
|
| 57 |
+
unk_index
|
| 58 |
+
)
|
| 59 |
+
for token in sm
|
| 60 |
+
]
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
ids = (
|
| 64 |
+
[sos_index]
|
| 65 |
+
+
|
| 66 |
+
ids
|
| 67 |
+
+
|
| 68 |
+
[eos_index]
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
seg = [1] * len(ids)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
padding = [
|
| 76 |
+
pad_index
|
| 77 |
+
] * (
|
| 78 |
+
seq_len - len(ids)
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
ids.extend(padding)
|
| 83 |
+
seg.extend(padding)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
return ids, seg
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def get_array(smiles):
|
| 91 |
+
|
| 92 |
+
x_id = []
|
| 93 |
+
x_seg = []
|
| 94 |
+
|
| 95 |
+
for sm in smiles:
|
| 96 |
+
|
| 97 |
+
a, b = get_inputs(sm)
|
| 98 |
+
|
| 99 |
+
x_id.append(a)
|
| 100 |
+
x_seg.append(b)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
return (
|
| 104 |
+
torch.tensor(x_id),
|
| 105 |
+
torch.tensor(x_seg)
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
# load SMILES Transformer
|
| 111 |
+
|
| 112 |
+
trfm = TrfmSeq2seq(
|
| 113 |
+
len(vocab),
|
| 114 |
+
256,
|
| 115 |
+
len(vocab),
|
| 116 |
+
4
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
trfm.load_state_dict(
|
| 121 |
+
torch.load(
|
| 122 |
+
TRFM_PATH,
|
| 123 |
+
map_location="cpu"
|
| 124 |
+
)
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
trfm.eval()
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
x_split = [
|
| 133 |
+
split(sm)
|
| 134 |
+
for sm in Smiles
|
| 135 |
+
]
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
xid, xseg = get_array(
|
| 139 |
+
x_split
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
X = trfm.encode(
|
| 144 |
+
torch.t(xid)
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
return X
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
# ============================
|
| 153 |
+
# Protein sequence -> vector
|
| 154 |
+
# ============================
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def Seq_to_vec(Sequence):
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
for i in range(len(Sequence)):
|
| 161 |
+
|
| 162 |
+
if len(Sequence[i]) > 1000:
|
| 163 |
+
|
| 164 |
+
Sequence[i] = (
|
| 165 |
+
Sequence[i][:500]
|
| 166 |
+
+
|
| 167 |
+
Sequence[i][-500:]
|
| 168 |
+
)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
sequences_Example = []
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
for seq in Sequence:
|
| 176 |
+
|
| 177 |
+
spaced = ""
|
| 178 |
+
|
| 179 |
+
for aa in seq[:-1]:
|
| 180 |
+
|
| 181 |
+
spaced += aa + " "
|
| 182 |
+
|
| 183 |
+
spaced += seq[-1]
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
sequences_Example.append(
|
| 187 |
+
spaced
|
| 188 |
+
)
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
# load ProtT5
|
| 193 |
+
|
| 194 |
+
tokenizer = T5Tokenizer.from_pretrained(
|
| 195 |
+
PROT_T5_MODEL,
|
| 196 |
+
do_lower_case=False
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
model = T5EncoderModel.from_pretrained(
|
| 201 |
+
PROT_T5_MODEL
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
gc.collect()
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
device = torch.device(
|
| 209 |
+
"cuda:0"
|
| 210 |
+
if torch.cuda.is_available()
|
| 211 |
+
else "cpu"
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
print(
|
| 216 |
+
"Using device:",
|
| 217 |
+
device
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
model = model.to(device)
|
| 222 |
+
|
| 223 |
+
model.eval()
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
features = []
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
for i, seq in enumerate(sequences_Example):
|
| 232 |
+
|
| 233 |
+
print(
|
| 234 |
+
"Processing protein:",
|
| 235 |
+
i + 1
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
seq = [
|
| 240 |
+
re.sub(
|
| 241 |
+
r"[UZOB]",
|
| 242 |
+
"X",
|
| 243 |
+
seq
|
| 244 |
+
)
|
| 245 |
+
]
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
ids = tokenizer.batch_encode_plus(
|
| 249 |
+
seq,
|
| 250 |
+
add_special_tokens=True,
|
| 251 |
+
padding=True
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
input_ids = torch.tensor(
|
| 256 |
+
ids["input_ids"]
|
| 257 |
+
).to(device)
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
attention_mask = torch.tensor(
|
| 261 |
+
ids["attention_mask"]
|
| 262 |
+
).to(device)
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
with torch.no_grad():
|
| 267 |
+
|
| 268 |
+
embedding = model(
|
| 269 |
+
input_ids=input_ids,
|
| 270 |
+
attention_mask=attention_mask
|
| 271 |
+
)
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
embedding = (
|
| 276 |
+
embedding
|
| 277 |
+
.last_hidden_state
|
| 278 |
+
.cpu()
|
| 279 |
+
.numpy()
|
| 280 |
+
)
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
seq_len = (
|
| 285 |
+
attention_mask[0] == 1
|
| 286 |
+
).sum()
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
seq_embedding = (
|
| 290 |
+
embedding[0]
|
| 291 |
+
[:seq_len-1]
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
features.append(
|
| 296 |
+
seq_embedding
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
# mean pooling
|
| 302 |
+
|
| 303 |
+
features_normalize = np.zeros(
|
| 304 |
+
[
|
| 305 |
+
len(features),
|
| 306 |
+
len(features[0][0])
|
| 307 |
+
],
|
| 308 |
+
dtype=float
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
for i in range(len(features)):
|
| 314 |
+
|
| 315 |
+
for k in range(
|
| 316 |
+
len(features[0][0])
|
| 317 |
+
):
|
| 318 |
+
|
| 319 |
+
for j in range(
|
| 320 |
+
len(features[i])
|
| 321 |
+
):
|
| 322 |
+
|
| 323 |
+
features_normalize[i][k] += (
|
| 324 |
+
features[i][j][k]
|
| 325 |
+
)
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
features_normalize[i][k] /= (
|
| 329 |
+
len(features[i])
|
| 330 |
+
)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
return features_normalize
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
# ============================
|
| 339 |
+
# Main
|
| 340 |
+
# ============================
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
if __name__ == "__main__":
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
# 示例蛋白序列
|
| 347 |
+
sequences = [
|
| 348 |
+
"MEDIPDTSRPPLKYVKGIPLIKYFAEALESLQDFQAQPDDLLISTYPKSGTTWVSEILDMIYQDGDVEKCRRAPVFIRVPFLEFKAPGIPTGLEVLKDTPAPRLIKTHLPLALLPQTLLDQKVKVVYVARNAKDVAVSYYHFYRMAKVHPDPDTWDSFLEKFMAGEVSYGSWYQHVQEWWELSHTHPVLYLFYEDMKENPKREIQKILKFVGRSLPEETVDLIVQHTSFKEMKNNSMANYTTLSPDIMDHSISAFMRKGISGDWKTTFTVAQNERFDADYAKKMEGCGLSFRTQL"
|
| 349 |
+
]
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
# 示例底物 SMILES
|
| 353 |
+
|
| 354 |
+
Smiles = [
|
| 355 |
+
"OC1=CC=C(C[C@@H](C(O)=O)N)C=C1"
|
| 356 |
+
]
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
print(
|
| 361 |
+
"Extracting protein embedding..."
|
| 362 |
+
)
|
| 363 |
+
|
| 364 |
+
seq_vec = Seq_to_vec(
|
| 365 |
+
sequences
|
| 366 |
+
)
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
print(
|
| 370 |
+
"Extracting SMILES embedding..."
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
smiles_vec = smiles_to_vec(
|
| 374 |
+
Smiles
|
| 375 |
+
)
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
print(
|
| 379 |
+
"Protein vector:",
|
| 380 |
+
seq_vec.shape
|
| 381 |
+
)
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
print(
|
| 385 |
+
"SMILES vector:",
|
| 386 |
+
smiles_vec.shape
|
| 387 |
+
)
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
# concatenate
|
| 392 |
+
|
| 393 |
+
fused_vector = np.concatenate(
|
| 394 |
+
(
|
| 395 |
+
smiles_vec,
|
| 396 |
+
seq_vec
|
| 397 |
+
),
|
| 398 |
+
axis=1
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
print(
|
| 404 |
+
"Fused vector:",
|
| 405 |
+
fused_vector.shape
|
| 406 |
+
)
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
# load UniKP model
|
| 411 |
+
|
| 412 |
+
with open(
|
| 413 |
+
UNIKP_MODEL_DIR / "UniKP for kcat.pkl",
|
| 414 |
+
"rb"
|
| 415 |
+
) as f:
|
| 416 |
+
|
| 417 |
+
model = pickle.load(f)
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
# prediction
|
| 422 |
+
|
| 423 |
+
pred = model.predict(
|
| 424 |
+
fused_vector
|
| 425 |
+
)
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
# log10 inverse transform
|
| 429 |
+
|
| 430 |
+
pred_value = [
|
| 431 |
+
math.pow(
|
| 432 |
+
10,
|
| 433 |
+
x
|
| 434 |
+
)
|
| 435 |
+
for x in pred
|
| 436 |
+
]
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
print("\n========== Result ==========")
|
| 441 |
+
|
| 442 |
+
print(
|
| 443 |
+
"Protein:",
|
| 444 |
+
sequences[0][:50],
|
| 445 |
+
"..."
|
| 446 |
+
)
|
| 447 |
+
|
| 448 |
+
print(
|
| 449 |
+
"SMILES:",
|
| 450 |
+
Smiles[0]
|
| 451 |
+
)
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
print(
|
| 455 |
+
"Predicted kcat:",
|
| 456 |
+
pred_value[0],
|
| 457 |
+
"s^-1"
|
| 458 |
+
)
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
# save
|
| 463 |
+
|
| 464 |
+
result = pd.DataFrame(
|
| 465 |
+
{
|
| 466 |
+
"sequence": sequences,
|
| 467 |
+
"SMILES": Smiles,
|
| 468 |
+
"kcat": pred_value
|
| 469 |
+
}
|
| 470 |
+
)
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
result.to_excel(
|
| 474 |
+
"UniKP_kcat_prediction.xlsx",
|
| 475 |
+
index=False
|
| 476 |
+
)
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
print(
|
| 480 |
+
"\nSaved:"
|
| 481 |
+
" UniKP_kcat_prediction.xlsx"
|
| 482 |
+
)
|
scripts/project_paths.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
ROOT_DIR = Path(__file__).resolve().parents[1]
|
| 5 |
+
CONF_DIR = ROOT_DIR / "conf"
|
| 6 |
+
DATASETS_DIR = CONF_DIR / "datasets"
|
| 7 |
+
FIGURES_DIR = CONF_DIR / "Figures"
|
| 8 |
+
WEIGHT_DIR = ROOT_DIR / "weight"
|
| 9 |
+
UNIKP_MODEL_DIR = WEIGHT_DIR / "UniKP_model"
|
| 10 |
+
PROT_T5_DIR = WEIGHT_DIR / "prot_t5_xl_uniref50"
|
| 11 |
+
PROT_T5_MODEL = str(PROT_T5_DIR if PROT_T5_DIR.exists() else "prot_t5_xl_uniref50")
|
| 12 |
+
|
| 13 |
+
VOCAB_PATH = WEIGHT_DIR / "vocab.pkl"
|
| 14 |
+
TRFM_PATH = WEIGHT_DIR / "trfm_12_23000.pkl"
|
| 15 |
+
KCAT_DATASET_PATH = DATASETS_DIR / "Kcat_combination_0918_wildtype_mutant.json"
|
| 16 |
+
KM_TEST_PATH = DATASETS_DIR / "Km_test_11722.pkl"
|
| 17 |
+
KCAT_KM_SAMPLES_PATH = DATASETS_DIR / "kcat_km_samples.xlsx"
|
| 18 |
+
PH_SMILES_PATH = DATASETS_DIR / "Generated_pH_unified_smiles_636.xlsx"
|
| 19 |
+
DEGREE_SMILES_PATH = DATASETS_DIR / "Generated_degree_unified_smiles_572.xlsx"
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def output_path(*parts):
|
| 23 |
+
return ROOT_DIR.joinpath(*parts)
|
scripts/smi2csv.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import numpy as np
|
| 3 |
+
import pandas as pd
|
| 4 |
+
|
| 5 |
+
def main():
|
| 6 |
+
parser = argparse.ArgumentParser(description='Convert smi file to csv file')
|
| 7 |
+
parser.add_argument('--in_path', '-i', type=str, default='data/GDB/GDB08.smi', help='input file')
|
| 8 |
+
parser.add_argument('--out_path_1', '-o1', type=str, default='data/gdb08_bert_train.csv', help='output file (train)')
|
| 9 |
+
parser.add_argument('--out_path_2', '-o2', type=str, default='data/gdb08_bert_test.csv', help='output file (test)')
|
| 10 |
+
parser.add_argument('--max', '-m', type=int, default=2e6, help='Maximum number of molecules')
|
| 11 |
+
args = parser.parse_args()
|
| 12 |
+
print('Input file: {}'.format(args.in_path))
|
| 13 |
+
print('Start preprocessing')
|
| 14 |
+
|
| 15 |
+
smiles = []
|
| 16 |
+
with open(args.in_path) as f:
|
| 17 |
+
lines = f.readlines()
|
| 18 |
+
for l in lines:
|
| 19 |
+
smiles.append(l.replace('\n', ''))
|
| 20 |
+
del lines
|
| 21 |
+
smiles = np.array(smiles)
|
| 22 |
+
N = len(smiles)
|
| 23 |
+
print('The dataset contains {} molecules'.format(N))
|
| 24 |
+
|
| 25 |
+
rands = np.random.choice(N, min(N,args.max), replace=False)
|
| 26 |
+
smiles_train = smiles[rands[:N//2]]
|
| 27 |
+
df_train = pd.DataFrame(data=smiles_train, columns=['canonical_smiles'])
|
| 28 |
+
df_train.to_csv(args.out_path_1, index=False)
|
| 29 |
+
del smiles_train, df_train
|
| 30 |
+
smiles_test = smiles[rands[N//2:]]
|
| 31 |
+
df_test = pd.DataFrame(data=smiles_test, columns=['canonical_smiles'])
|
| 32 |
+
df_test.to_csv(args.out_path_2, index=False)
|
| 33 |
+
print('Each set contains {} molecules'.format(N//2))
|
| 34 |
+
|
| 35 |
+
if __name__=='__main__':
|
| 36 |
+
main()
|
weight/UniKP_model/.gitattributes
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
weight/UniKP_model/README.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: gpl-3.0
|
| 3 |
+
---
|
weight/UniKP_model/UniKP for Km.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f0b8d597cf4e5f73431980950cb89423415a260dbbf9be0bb1d8810712bf9c07
|
| 3 |
+
size 147957236
|
weight/UniKP_model/UniKP for kcat.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fe90811273401698a2c25ab32959f13c0087a14feb8ca310cf4b44dcad819fd5
|
| 3 |
+
size 205501172
|
weight/UniKP_model/UniKP for kcat_Km.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1f81f8715a87790c542023b1bab1da6055a60b9db22c20c04a8846d9b09ba844
|
| 3 |
+
size 11476980
|
weight/trfm_12_23000.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6b56c8c05d048e7c7d143c4e3ba2bc6f76e5eda2358798cf636210406a700eb2
|
| 3 |
+
size 22128521
|
weight/vocab.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:21a66c850a3222547ec0fbd30c05fe587d66d22d3de2ee2195c58250fe486fb7
|
| 3 |
+
size 1446
|