anzhi2710gmailcom commited on
Commit
90d94ec
·
verified ·
1 Parent(s): d7174e0

Upload folder using huggingface_hub

Browse files
.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

  • SHA256: 5e7e022ad2cf313dd9423cc047c679f94ca3c7c9a8766d91b53d93562d2ae2a1
  • Pointer size: 131 Bytes
  • Size of remote file: 524 kB
conf/Figures/UniKP.png ADDED

Git LFS Details

  • SHA256: e705fd67114ec0169de46e4111e490835292364fc95508ed620419c14db14b40
  • Pointer size: 131 Bytes
  • Size of remote file: 115 kB
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