philm 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- philm/__init__.py +1 -0
- philm/config/config_repr.test.yaml +7 -0
- philm/config/config_repr.train.yaml +7 -0
- philm/config/config_repr.val.yaml +7 -0
- philm/config/config_test.train.yaml +7 -0
- philm/config/config_test.val.yaml +7 -0
- philm/config/config_test.yaml +7 -0
- philm/config/config_train.yaml +12 -0
- philm/config/config_train_perm.yaml +44 -0
- philm/model/__init__.py +0 -0
- philm/model/data_loader.py +27 -0
- philm/model/evaluate.py +104 -0
- philm/model/extract_repr.py +75 -0
- philm/model/model.py +48 -0
- philm/model/optimize.py +60 -0
- philm/model/predict.py +72 -0
- philm/model/train.py +75 -0
- philm/model/train_permutation.py +377 -0
- philm/model/trainer.py +142 -0
- philm/model/utils.py +111 -0
- philm/scripts/__init__.py +0 -0
- philm/scripts/infer_interactions.py +545 -0
- philm/scripts/permutation_pvalues.py +209 -0
- philm/scripts/split_data.py +328 -0
- philm/scripts/summarize.py +107 -0
- philm-0.0.1.dist-info/METADATA +386 -0
- philm-0.0.1.dist-info/RECORD +31 -0
- philm-0.0.1.dist-info/WHEEL +5 -0
- philm-0.0.1.dist-info/entry_points.txt +9 -0
- philm-0.0.1.dist-info/licenses/LICENSE +21 -0
- philm-0.0.1.dist-info/top_level.txt +1 -0
philm/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.1"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
data:
|
|
2
|
+
train_X: data/Phage_train.tsv
|
|
3
|
+
train_Y: data/Bact_arc_train.tsv
|
|
4
|
+
val_X: data/Phage_val.tsv
|
|
5
|
+
val_Y: data/Bact_arc_val.tsv
|
|
6
|
+
model:
|
|
7
|
+
batch_size: 512 # Batch size
|
|
8
|
+
patience: 20 # Number of epochs with no improvement after which training will be stopped
|
|
9
|
+
num_epochs: 8000 # Maximal epochs for training
|
|
10
|
+
n_trials: 10 # Number of trials for choosing the optimal combinations of parameters, default=10
|
|
11
|
+
path: results/PHILM_best_model.pth
|
|
12
|
+
para_path: results/PHILM_best_params.yaml
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Fixed-parameter PHILM null-permutation training config.
|
|
2
|
+
# Place this file in config/config_train_permutation.yaml and run from the repo root:
|
|
3
|
+
# python model/train_permutation.py -c config/config_train_permutation.yaml --perm-id 0
|
|
4
|
+
# For 100 HPC array jobs, pass --perm-id ${SLURM_ARRAY_TASK_ID}.
|
|
5
|
+
|
|
6
|
+
data:
|
|
7
|
+
train_X: data/Phage_train.tsv
|
|
8
|
+
train_Y: data/Bact_arc_train.tsv
|
|
9
|
+
val_X: data/Phage_val.tsv
|
|
10
|
+
val_Y: data/Bact_arc_val.tsv
|
|
11
|
+
|
|
12
|
+
permutation:
|
|
13
|
+
# Overridden by --perm-id.
|
|
14
|
+
id: 0
|
|
15
|
+
|
|
16
|
+
# Seed used as: seed = base_seed + perm_id, unless --seed is supplied.
|
|
17
|
+
base_seed: 19910728
|
|
18
|
+
|
|
19
|
+
# Null model: shuffle phage/X sample rows relative to fixed bacteria/Y rows.
|
|
20
|
+
# This breaks the paired phage-bacteria relationship before training.
|
|
21
|
+
method: shuffle
|
|
22
|
+
|
|
23
|
+
# Avoid cases where a Y row is still paired with its original X row.
|
|
24
|
+
avoid_self_pairs: true
|
|
25
|
+
|
|
26
|
+
# Recommended: permute both train and validation pairs for a matched null-training workflow.
|
|
27
|
+
permute_train_X: true
|
|
28
|
+
permute_val_X: true
|
|
29
|
+
|
|
30
|
+
map_path_template: results/permutation_null/perm_{perm_id_padded}/PHILM_perm_{perm_id_padded}.sample_permutation.tsv
|
|
31
|
+
|
|
32
|
+
runtime:
|
|
33
|
+
device: auto
|
|
34
|
+
|
|
35
|
+
model:
|
|
36
|
+
batch_size: 512
|
|
37
|
+
patience: 20
|
|
38
|
+
num_epochs: 8000
|
|
39
|
+
|
|
40
|
+
# Output paths. {perm_id_padded} will be 000, 001, ..., 099.
|
|
41
|
+
path_template: results/permutation_null/perm_{perm_id_padded}/PHILM_perm_{perm_id_padded}.pth
|
|
42
|
+
para_path_template: results/permutation_null/perm_{perm_id_padded}/PHILM_perm_{perm_id_padded}.params.yaml
|
|
43
|
+
log_path_template: results/permutation_null/perm_{perm_id_padded}/PHILM_perm_{perm_id_padded}.train.log
|
|
44
|
+
|
philm/model/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# data_loader.py
|
|
2
|
+
import torch
|
|
3
|
+
from torch.utils.data import Dataset, DataLoader
|
|
4
|
+
|
|
5
|
+
class MGX_MVX_Dataset(Dataset):
|
|
6
|
+
def __init__(self, train_X_data, train_Y_data):
|
|
7
|
+
self.train_X_data = train_X_data
|
|
8
|
+
self.train_Y_data = train_Y_data
|
|
9
|
+
|
|
10
|
+
def __len__(self):
|
|
11
|
+
return len(self.train_X_data)
|
|
12
|
+
|
|
13
|
+
def __getitem__(self, idx):
|
|
14
|
+
X = torch.tensor(self.train_X_data.loc[idx], dtype=torch.float32)
|
|
15
|
+
y = torch.tensor(self.train_Y_data.loc[idx], dtype=torch.float32)
|
|
16
|
+
return X, y
|
|
17
|
+
|
|
18
|
+
class Predict_Dataset(Dataset):
|
|
19
|
+
def __init__(self, X_data):
|
|
20
|
+
self.X_data = X_data
|
|
21
|
+
|
|
22
|
+
def __len__(self):
|
|
23
|
+
return len(self.X_data)
|
|
24
|
+
|
|
25
|
+
def __getitem__(self, idx):
|
|
26
|
+
X = torch.tensor(self.X_data.loc[idx], dtype=torch.float32)
|
|
27
|
+
return X
|
philm/model/evaluate.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import sys, yaml, argparse
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from torch.utils.data import DataLoader
|
|
5
|
+
|
|
6
|
+
from .data_loader import MGX_MVX_Dataset
|
|
7
|
+
from .model import MLP_NODE
|
|
8
|
+
from .utils import ft_pearson_corr_list, ft_cos_sim_list, ft_r2score_list, pearson_corr_list, cos_sim_list, r2score_list
|
|
9
|
+
|
|
10
|
+
# Device configuration
|
|
11
|
+
if torch.cuda.is_available():
|
|
12
|
+
device = torch.device(0) # Use GPU 0
|
|
13
|
+
else:
|
|
14
|
+
device = torch.device('cpu') # Fallback to CPU
|
|
15
|
+
|
|
16
|
+
def load_config(config_path):
|
|
17
|
+
with open(config_path, 'r') as file:
|
|
18
|
+
return yaml.safe_load(file)
|
|
19
|
+
|
|
20
|
+
def test_model(args):
|
|
21
|
+
# Load configuration
|
|
22
|
+
config = load_config(args.config)
|
|
23
|
+
|
|
24
|
+
# read data
|
|
25
|
+
test_X_data = pd.read_table(config['data']['test_X'], header=None)
|
|
26
|
+
test_Y_data = pd.read_table(config['data']['test_Y'], header=None)
|
|
27
|
+
dataset = MGX_MVX_Dataset(test_X_data, test_Y_data)
|
|
28
|
+
|
|
29
|
+
# load model
|
|
30
|
+
# define hyperparameters (must be the same as during training)
|
|
31
|
+
input_dim = test_X_data.shape[1] # Should match training input_dim
|
|
32
|
+
output_dim = test_Y_data.shape[1] # Use the output_dim from training
|
|
33
|
+
|
|
34
|
+
batch_size=config['model']['batch_size']
|
|
35
|
+
hidden_size=config['model']['hidden_size']
|
|
36
|
+
|
|
37
|
+
# load data
|
|
38
|
+
test_loader = DataLoader(dataset, batch_size=batch_size)
|
|
39
|
+
|
|
40
|
+
model = MLP_NODE(
|
|
41
|
+
input_dim=input_dim,
|
|
42
|
+
output_dim=output_dim,
|
|
43
|
+
hidden_size=hidden_size
|
|
44
|
+
)
|
|
45
|
+
model.load_state_dict(torch.load(config['model']['path'], weights_only=False, map_location=device))
|
|
46
|
+
model.to(device)
|
|
47
|
+
model.eval()
|
|
48
|
+
|
|
49
|
+
preds = torch.tensor([]).to(device)
|
|
50
|
+
grounds = torch.tensor([]).to(device)
|
|
51
|
+
with torch.no_grad():
|
|
52
|
+
for X_batch, Y_batch in test_loader:
|
|
53
|
+
X_batch, Y_batch = X_batch.to(device), Y_batch.to(device)
|
|
54
|
+
|
|
55
|
+
pred = model(X_batch)
|
|
56
|
+
pred = pred.to(device)
|
|
57
|
+
preds = torch.cat((preds, pred), 0)
|
|
58
|
+
grounds = torch.cat((grounds, Y_batch), 0)
|
|
59
|
+
torch.cuda.empty_cache()
|
|
60
|
+
|
|
61
|
+
prediction = preds
|
|
62
|
+
ground_truth = grounds
|
|
63
|
+
|
|
64
|
+
print(prediction.shape)
|
|
65
|
+
prefix=config['output_prefix']
|
|
66
|
+
dt = pd.DataFrame(prediction.cpu().numpy())
|
|
67
|
+
dt.to_csv(prefix+"_pred.tsv", sep="\t", header=False, index=False)
|
|
68
|
+
|
|
69
|
+
# feature-wise metrics
|
|
70
|
+
pcc_lst = ft_pearson_corr_list(prediction, ground_truth)
|
|
71
|
+
pcc_dt = pd.DataFrame(pcc_lst)
|
|
72
|
+
pcc_dt.to_csv(prefix+"_pcc_ft.tsv", sep="\t", header=["PCC"], index=False)
|
|
73
|
+
|
|
74
|
+
cos_lst = ft_cos_sim_list(prediction, ground_truth)
|
|
75
|
+
cos_dt = pd.DataFrame(cos_lst.cpu().numpy())
|
|
76
|
+
cos_dt.to_csv(prefix+"_cos_sim_ft.tsv", sep="\t", header=["Cos_Sim"], index=False)
|
|
77
|
+
|
|
78
|
+
r2_lst = ft_r2score_list(prediction, ground_truth)
|
|
79
|
+
r2_dt = pd.DataFrame(r2_lst)
|
|
80
|
+
r2_dt.to_csv(prefix+"_r2_ft.tsv", sep="\t", header=["R2"], index=False)
|
|
81
|
+
|
|
82
|
+
# sample-wise metrics
|
|
83
|
+
pcc_lst = pearson_corr_list(prediction, ground_truth)
|
|
84
|
+
pcc_dt = pd.DataFrame(pcc_lst)
|
|
85
|
+
pcc_dt.to_csv(prefix+"_pcc.tsv", sep="\t", header=["PCC"], index=False)
|
|
86
|
+
|
|
87
|
+
cos_lst = cos_sim_list(prediction, ground_truth)
|
|
88
|
+
cos_dt = pd.DataFrame(cos_lst.cpu().numpy())
|
|
89
|
+
cos_dt.to_csv(prefix+"_cos_sim.tsv", sep="\t", header=["Cos_Sim"], index=False)
|
|
90
|
+
|
|
91
|
+
r2_lst = r2score_list(prediction, ground_truth)
|
|
92
|
+
r2_dt = pd.DataFrame(r2_lst)
|
|
93
|
+
r2_dt.to_csv(prefix+"_r2.tsv", sep="\t", header=["R2"], index=False)
|
|
94
|
+
|
|
95
|
+
def main():
|
|
96
|
+
from philm import __version__
|
|
97
|
+
parser = argparse.ArgumentParser(description=f'PHILM {__version__} evaluate model')
|
|
98
|
+
parser.add_argument('-c', '--config', required=True, type=str,
|
|
99
|
+
help='config file path')
|
|
100
|
+
args = parser.parse_args()
|
|
101
|
+
test_model(args)
|
|
102
|
+
|
|
103
|
+
if __name__ == '__main__':
|
|
104
|
+
main()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import sys, yaml, argparse
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from torch.utils.data import DataLoader
|
|
5
|
+
|
|
6
|
+
from .data_loader import MGX_MVX_Dataset, Predict_Dataset
|
|
7
|
+
from .model import MLP_NODE
|
|
8
|
+
|
|
9
|
+
# Device configuration
|
|
10
|
+
if torch.cuda.is_available():
|
|
11
|
+
device = torch.device(0) # Use GPU 0
|
|
12
|
+
else:
|
|
13
|
+
device = torch.device('cpu') # Fallback to CPU
|
|
14
|
+
|
|
15
|
+
def load_config(config_path):
|
|
16
|
+
with open(config_path, 'r') as file:
|
|
17
|
+
return yaml.safe_load(file)
|
|
18
|
+
|
|
19
|
+
def extract_repr(args):
|
|
20
|
+
# Load configuration
|
|
21
|
+
config = load_config(args.config)
|
|
22
|
+
|
|
23
|
+
# read data
|
|
24
|
+
test_X_data = pd.read_table(config['data']['test_X'], header=None)
|
|
25
|
+
dataset = Predict_Dataset(test_X_data)
|
|
26
|
+
|
|
27
|
+
# load model
|
|
28
|
+
# define hyperparameters (must be the same as during training)
|
|
29
|
+
input_dim = test_X_data.shape[1] # Should match training input_dim
|
|
30
|
+
output_dim = config['model']['output_dim']
|
|
31
|
+
batch_size=config['model']['batch_size']
|
|
32
|
+
hidden_size=config['model']['hidden_size']
|
|
33
|
+
|
|
34
|
+
# load data
|
|
35
|
+
dataloader = DataLoader(dataset, batch_size=batch_size)
|
|
36
|
+
|
|
37
|
+
model = MLP_NODE(
|
|
38
|
+
input_dim=input_dim,
|
|
39
|
+
output_dim=output_dim,
|
|
40
|
+
hidden_size=hidden_size
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
model.load_state_dict(torch.load(config['model']['path'], weights_only=False, map_location=device))
|
|
44
|
+
model.to(device)
|
|
45
|
+
model.eval()
|
|
46
|
+
|
|
47
|
+
repr1_list = torch.tensor([]).to(device)
|
|
48
|
+
repr2_list = torch.tensor([]).to(device)
|
|
49
|
+
with torch.no_grad():
|
|
50
|
+
for X_batch in dataloader:
|
|
51
|
+
X_batch = X_batch.to(device)
|
|
52
|
+
repr1 = model.get_repr1(X_batch)
|
|
53
|
+
repr1_list = torch.cat((repr1_list, repr1), 0)
|
|
54
|
+
repr2 = model.get_repr2(X_batch)
|
|
55
|
+
repr2_list = torch.cat((repr2_list, repr2), 0)
|
|
56
|
+
torch.cuda.empty_cache()
|
|
57
|
+
|
|
58
|
+
print(repr1_list.shape)
|
|
59
|
+
prefix=config['output_prefix']
|
|
60
|
+
dt = pd.DataFrame(repr1_list.cpu().numpy())
|
|
61
|
+
dt.to_csv(prefix+"_repr1.tsv", sep="\t", header=False, index=False)
|
|
62
|
+
|
|
63
|
+
dt = pd.DataFrame(repr2_list.cpu().numpy())
|
|
64
|
+
dt.to_csv(prefix+"_repr2.tsv", sep="\t", header=False, index=False)
|
|
65
|
+
|
|
66
|
+
def main():
|
|
67
|
+
from philm import __version__
|
|
68
|
+
parser = argparse.ArgumentParser(description=f'PHILM {__version__} extract representations')
|
|
69
|
+
parser.add_argument('-c', '--config', required=True, type=str,
|
|
70
|
+
help='config file path')
|
|
71
|
+
args = parser.parse_args()
|
|
72
|
+
extract_repr(args)
|
|
73
|
+
|
|
74
|
+
if __name__ == '__main__':
|
|
75
|
+
main()
|
philm/model/model.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torchdiffeq
|
|
4
|
+
|
|
5
|
+
class ODEFunc(nn.Module):
|
|
6
|
+
"""
|
|
7
|
+
Defines the neural ODE function.
|
|
8
|
+
"""
|
|
9
|
+
def __init__(self, hidden_dim):
|
|
10
|
+
super(ODEFunc, self).__init__()
|
|
11
|
+
layers = []
|
|
12
|
+
prev_dim = hidden_dim
|
|
13
|
+
for _ in range(2):
|
|
14
|
+
# print(prev_dim, h_dim)
|
|
15
|
+
layers.append(nn.Linear(prev_dim, hidden_dim))
|
|
16
|
+
layers.append(nn.Tanh())
|
|
17
|
+
prev_dim = hidden_dim
|
|
18
|
+
self.model = nn.Sequential(*layers)
|
|
19
|
+
|
|
20
|
+
def forward(self, t, x):
|
|
21
|
+
return self.model(x)
|
|
22
|
+
|
|
23
|
+
class MLP_NODE(nn.Module):
|
|
24
|
+
def __init__(self, input_dim, hidden_size, output_dim):
|
|
25
|
+
super(MLP_NODE, self).__init__()
|
|
26
|
+
self.fc1 = nn.Linear(input_dim, hidden_size)
|
|
27
|
+
self.ode_func = ODEFunc(hidden_size)
|
|
28
|
+
self.ode_solver = torchdiffeq.odeint # Using adjoint method for memory efficiency
|
|
29
|
+
self.fc2 = nn.Linear(hidden_size, output_dim)
|
|
30
|
+
|
|
31
|
+
def forward(self, x):
|
|
32
|
+
x = torch.flatten(x, 1) # Flatten the input tensor except for the batch dimension
|
|
33
|
+
x = self.fc1(x)
|
|
34
|
+
t = torch.tensor([0, 1], dtype=torch.float32, device=x.device) # Time interval for ODE solver, moved to the same device as x
|
|
35
|
+
x = self.ode_solver(self.ode_func, x, t, rtol=1e-4, atol=1e-4)[-1] # Get the result at the final time point
|
|
36
|
+
x = self.fc2(x)
|
|
37
|
+
return x
|
|
38
|
+
|
|
39
|
+
def get_repr1(self, x):
|
|
40
|
+
repr1 = self.fc1(x)
|
|
41
|
+
return repr1
|
|
42
|
+
|
|
43
|
+
def get_repr2(self, x):
|
|
44
|
+
x = torch.flatten(x, 1) # Flatten the input tensor except for the batch dimension
|
|
45
|
+
x = self.fc1(x)
|
|
46
|
+
t = torch.tensor([0, 1], dtype=torch.float32, device=x.device) # Time interval for ODE solver, moved to the same device as x
|
|
47
|
+
repr2 = self.ode_solver(self.ode_func, x, t, rtol=1e-4, atol=1e-4)[-1] # Get the result at the final time point
|
|
48
|
+
return repr2
|
philm/model/optimize.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import optuna
|
|
2
|
+
import torch, os
|
|
3
|
+
from .trainer import train_model
|
|
4
|
+
|
|
5
|
+
def determine_nodelayer_no(input_num, output_num):
|
|
6
|
+
pool = [4, 8, 16, 32, 64, 128, 256, 320, 512, 640, 1024, 2048, 2560, 4096]
|
|
7
|
+
print(input_num, output_num)
|
|
8
|
+
val = input_num + output_num
|
|
9
|
+
hidden_dim = min(pool, key=lambda x: abs(x - val/2))
|
|
10
|
+
|
|
11
|
+
# print(hidden_dim)
|
|
12
|
+
hidden_size_list = [hidden_dim]
|
|
13
|
+
indices = [pool.index(hidden_dim)]
|
|
14
|
+
if (pool.index(hidden_dim)-1) > 0:
|
|
15
|
+
indices.append(pool.index(hidden_dim)-1)
|
|
16
|
+
hidden_size_list.append(pool[pool.index(hidden_dim)-1])
|
|
17
|
+
if (pool.index(hidden_dim)+1) < len(pool):
|
|
18
|
+
indices.append(pool.index(hidden_dim)+1)
|
|
19
|
+
hidden_size_list.append(pool[pool.index(hidden_dim)+1])
|
|
20
|
+
return hidden_size_list
|
|
21
|
+
|
|
22
|
+
def objective(trial, train_X_data, train_Y_data, val_X_data, val_Y_data, batch_size, patience, num_epochs, device):
|
|
23
|
+
|
|
24
|
+
input_num = train_X_data.shape[1]
|
|
25
|
+
output_num = train_Y_data.shape[1]
|
|
26
|
+
hidden_size_list = determine_nodelayer_no(input_num, output_num)
|
|
27
|
+
print("candidates:", hidden_size_list)
|
|
28
|
+
|
|
29
|
+
# Suggest hyperparameters
|
|
30
|
+
learning_rate = trial.suggest_float("learning_rate", 1e-6, 1e-2, log=True)
|
|
31
|
+
hidden_size = trial.suggest_categorical("hidden_size", hidden_size_list)
|
|
32
|
+
weight_decay = trial.suggest_float("weight_decay", 1e-8, 1e-5, log=True)
|
|
33
|
+
|
|
34
|
+
# Ensure the directory exists
|
|
35
|
+
os.makedirs("checkpoints", exist_ok=True)
|
|
36
|
+
|
|
37
|
+
model_path = os.path.join("checkpoints", f"model_trial_{trial.number}.pth") # Save models in a folder named 'models'
|
|
38
|
+
log_path = os.path.join("checkpoints", f"train_{trial.number}.log")
|
|
39
|
+
|
|
40
|
+
# Call the train function and get the validation loss
|
|
41
|
+
best_val_loss = train_model(
|
|
42
|
+
train_X_data=train_X_data,
|
|
43
|
+
train_Y_data=train_Y_data,
|
|
44
|
+
val_X_data=val_X_data,
|
|
45
|
+
val_Y_data=val_Y_data,
|
|
46
|
+
device=device,
|
|
47
|
+
num_epochs=num_epochs,
|
|
48
|
+
batch_size=batch_size,
|
|
49
|
+
learning_rate=learning_rate,
|
|
50
|
+
hidden_size=hidden_size,
|
|
51
|
+
patience=patience,
|
|
52
|
+
weight_decay=weight_decay,
|
|
53
|
+
model_path=model_path,
|
|
54
|
+
log_path=log_path
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Store the model name in the trial's user attributes for later reference
|
|
58
|
+
trial.set_user_attr("model_path", model_path)
|
|
59
|
+
|
|
60
|
+
return best_val_loss
|
philm/model/predict.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import sys, yaml, argparse
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from torch.utils.data import DataLoader
|
|
5
|
+
|
|
6
|
+
from .data_loader import Predict_Dataset
|
|
7
|
+
from .model import MLP_NODE
|
|
8
|
+
|
|
9
|
+
# Device configuration
|
|
10
|
+
if torch.cuda.is_available():
|
|
11
|
+
device = torch.device(0) # Use GPU 0
|
|
12
|
+
else:
|
|
13
|
+
device = torch.device('cpu') # Fallback to CPU
|
|
14
|
+
|
|
15
|
+
def load_config(config_path):
|
|
16
|
+
with open(config_path, 'r') as file:
|
|
17
|
+
return yaml.safe_load(file)
|
|
18
|
+
|
|
19
|
+
def predict(args):
|
|
20
|
+
# Load configuration
|
|
21
|
+
config = load_config(args.config)
|
|
22
|
+
|
|
23
|
+
# read data
|
|
24
|
+
test_X_data = pd.read_table(config['data']['test_X'], header=None)
|
|
25
|
+
dataset = Predict_Dataset(test_X_data)
|
|
26
|
+
|
|
27
|
+
# load model
|
|
28
|
+
# define hyperparameters (must be the same as during training)
|
|
29
|
+
input_dim = test_X_data.shape[1] # Should match training input_dim
|
|
30
|
+
output_dim = config['model']['output_dim'] # Use the output_dim from training
|
|
31
|
+
batch_size=config['model']['batch_size']
|
|
32
|
+
hidden_size=config['model']['hidden_size']
|
|
33
|
+
|
|
34
|
+
# load data
|
|
35
|
+
test_loader = DataLoader(dataset, batch_size=batch_size)
|
|
36
|
+
|
|
37
|
+
model = MLP_NODE(
|
|
38
|
+
input_dim=input_dim,
|
|
39
|
+
output_dim=output_dim,
|
|
40
|
+
hidden_size=hidden_size
|
|
41
|
+
)
|
|
42
|
+
model.load_state_dict(torch.load(config['model']['path'], weights_only=False, map_location=device))
|
|
43
|
+
model.to(device)
|
|
44
|
+
model.eval()
|
|
45
|
+
|
|
46
|
+
preds = torch.tensor([]).to(device)
|
|
47
|
+
with torch.no_grad():
|
|
48
|
+
for X_batch in test_loader:
|
|
49
|
+
X_batch = X_batch.to(device)
|
|
50
|
+
|
|
51
|
+
pred = model(X_batch)
|
|
52
|
+
pred = pred.to(device)
|
|
53
|
+
preds = torch.cat((preds, pred), 0)
|
|
54
|
+
torch.cuda.empty_cache()
|
|
55
|
+
|
|
56
|
+
prediction = preds
|
|
57
|
+
|
|
58
|
+
print(prediction.shape)
|
|
59
|
+
prefix=config['output_prefix']
|
|
60
|
+
dt = pd.DataFrame(prediction.cpu().numpy())
|
|
61
|
+
dt.to_csv(prefix+"_pred.tsv", sep="\t", header=False, index=False)
|
|
62
|
+
|
|
63
|
+
def main():
|
|
64
|
+
from philm import __version__
|
|
65
|
+
parser = argparse.ArgumentParser(description=f'PHILM {__version__} predict model')
|
|
66
|
+
parser.add_argument('-c', '--config', required=True, type=str,
|
|
67
|
+
help='config file path')
|
|
68
|
+
args = parser.parse_args()
|
|
69
|
+
predict(args)
|
|
70
|
+
|
|
71
|
+
if __name__ == '__main__':
|
|
72
|
+
main()
|
philm/model/train.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import yaml
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import torch
|
|
5
|
+
import sys, os, argparse
|
|
6
|
+
from .trainer import train_model
|
|
7
|
+
from .optimize import objective
|
|
8
|
+
import optuna
|
|
9
|
+
import shutil
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def load_config(config_path):
|
|
13
|
+
with open(config_path, 'r') as file:
|
|
14
|
+
return yaml.safe_load(file)
|
|
15
|
+
|
|
16
|
+
def train_model(args):
|
|
17
|
+
# Load configuration
|
|
18
|
+
config = load_config(args.config)
|
|
19
|
+
|
|
20
|
+
# Load data
|
|
21
|
+
train_X_data = pd.read_table(config['data']['train_X'], header=None) # Shape: (samples, bacterial_features)
|
|
22
|
+
train_Y_data = pd.read_table(config['data']['train_Y'], header=None) # Shape: (samples, viral_features)
|
|
23
|
+
val_X_data = pd.read_table(config['data']['val_X'], header=None) # Shape: (samples, bacterial_features)
|
|
24
|
+
val_Y_data = pd.read_table(config['data']['val_Y'], header=None) # Shape: (samples, viral_features)
|
|
25
|
+
|
|
26
|
+
# Load fixed parameters
|
|
27
|
+
batch_size = config['model']['batch_size']
|
|
28
|
+
patience = config['model']['patience']
|
|
29
|
+
num_epochs = config['model']['num_epochs']
|
|
30
|
+
n_trials = config['model']['n_trials']
|
|
31
|
+
|
|
32
|
+
# Load target model path and parameter path
|
|
33
|
+
saved_model_path = config['model']['path']
|
|
34
|
+
para_path = config['model']['para_path']
|
|
35
|
+
|
|
36
|
+
# Device configuration
|
|
37
|
+
if torch.cuda.is_available():
|
|
38
|
+
device = torch.device(0) # Use GPU 0
|
|
39
|
+
else:
|
|
40
|
+
device = torch.device('cpu') # Fallback to CPU
|
|
41
|
+
print(f'Using device: {device}')
|
|
42
|
+
|
|
43
|
+
# Create an Optuna study and optimize
|
|
44
|
+
study = optuna.create_study(direction="minimize")
|
|
45
|
+
study.optimize(lambda trial: objective(trial, train_X_data, train_Y_data, val_X_data, val_Y_data, batch_size, patience, num_epochs, device), n_trials=n_trials)
|
|
46
|
+
|
|
47
|
+
# Print the best parameters, score, and model name
|
|
48
|
+
print("Best Parameters:", study.best_params)
|
|
49
|
+
print("Best Val Loss:", study.best_value)
|
|
50
|
+
print("Best Model Name:", study.best_trial.user_attrs["model_path"])
|
|
51
|
+
|
|
52
|
+
ori_model_path = study.best_trial.user_attrs["model_path"]
|
|
53
|
+
# os.rename(ori_model_path, saved_model_path)
|
|
54
|
+
os.makedirs(os.path.dirname(saved_model_path), exist_ok=True)
|
|
55
|
+
shutil.copy2(ori_model_path, saved_model_path)
|
|
56
|
+
|
|
57
|
+
# Save best parameters and model name to a YAML file
|
|
58
|
+
best_params = {
|
|
59
|
+
'learning_rate': study.best_params.get('learning_rate', None),
|
|
60
|
+
'hidden_size': study.best_params.get('hidden_size', None),
|
|
61
|
+
'weight_decay': study.best_params.get('weight_decay', None)
|
|
62
|
+
}
|
|
63
|
+
with open(para_path, "w") as f:
|
|
64
|
+
yaml.dump(best_params, f, default_flow_style=False)
|
|
65
|
+
|
|
66
|
+
def main():
|
|
67
|
+
from philm import __version__
|
|
68
|
+
parser = argparse.ArgumentParser(description=f'PHILM {__version__} train model')
|
|
69
|
+
parser.add_argument('-c', '--config', required=True, type=str,
|
|
70
|
+
help='config file path')
|
|
71
|
+
args = parser.parse_args()
|
|
72
|
+
train_model(args)
|
|
73
|
+
|
|
74
|
+
if __name__ == '__main__':
|
|
75
|
+
main()
|