TemStaPro-main / scripts /temstapro
anzhi2710gmailcom's picture
Upload folder using huggingface_hub
4b5b766 verified
Raw
History Blame Contribute Delete
11.9 kB
#!/usr/bin/env python3
# Program that makes thermostability predictions
from optparse import OptionParser
from datetime import datetime
import sys
import os
import numpy
from torch.utils.data import DataLoader
from torch.utils.data import TensorDataset
import torch
PARAMETERS = {
"PT_MODEL_PATH": "Rostlab/prot_t5_xl_half_uniref50-enc",
"DATASET": "major",
"EMB_TYPE": "mean",
"CLASSIFIER_TYPE": "imbal",
"THRESHOLDS": {
":(40-65]:": ["40", "45", "50", "55", "60", "65"],
":(40-80]:": ["40", "45", "50", "55", "60", "65", "70", "75", "80"],
},
"SEEDS": ["1", "2", "3", "4", "5"],
"INPUT_SIZE": 1024,
"HIDDEN_LAYER_SIZES": [256, 128],
"DEVICE": torch.device("cuda:0" if torch.cuda.is_available() else "cpu"),
"THRESHOLDS_RANGE": ":(40-65]:",
"TEMPERATURE_RANGES": {
":(40-65]:": ["<40", "[40-45)", "[45-50)", "[50-55)", "[55-60)",
"[60-65)", "65<="],
":(40-80]:": ["<40", "[40-45)", "[45-50)", "[50-55)", "[55-60)",
"[60-65)", "[65-70)", "[70-75)", "[75-80)", "80<="],
},
"THERMOPHILICITY_LABELS": {
"mesophilic": ["<40", "[40-45)", "<45"],
"thermophilic": ["[45-50)", "[50-55)", "[55-60)", "[60-65)",
"65<=", "[65-70)", "[70-75)", "<75"],
"hyperthermophilic": ["[75-80)", "80<="]
},
"PRINT_THERMOPHILICITY": {
":(40-65]:": False,
":(40-80]:": True
}
}
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.dirname(SCRIPT_DIR)
parser = OptionParser()
parser.add_option("--input-fasta", "-f", dest="fasta",
default=None, help="path to the input FASTA file.")
parser.add_option("--embeddings-dir", "-e", dest="emb_dir",
default=None, help="path to the directory to which embeddings "+\
"files will be saved (cache).")
parser.add_option("--PT-directory", "-d", dest="pt_dir",
default=None, help="path to the directory of ProtTrans model.")
parser.add_option("--temstapro-directory", "-t", dest="tsp_dir",
default=PROJECT_DIR, help="path to the directory of TemStaPro program "+\
"with its dependencies.")
parser.add_option("--more-thresholds", dest="more_thresholds",
action="store_true", help="option for the mode that outputs "+\
"additional predictions for upper temperature thresholds and the "+\
"thremophilicity label")
parser.add_option("--mean-output", dest="mean_out",
default=None, help="path to the output TSV file with mean predictions. "+\
"Predictions made from the mean embeddings are always printed to STDOUT."+\
" If this option is given, the output is directed to the given file")
parser.add_option("--per-res-output", dest="per_res_out",
default=None, help="path to the output TSV file with per-residue "+\
"predictions.")
parser.add_option("--per-segment-output", dest="per_segment_out",
default=None, help="path to the output TSV file with per-residue "+\
"predictions made for each segment of the sequence.")
parser.add_option("--segment-size", dest="segment_size",
default=41, help="option to set the window size for average smoothening "+\
"of per residue embeddings ('per-segment-output' option). Default: 41.")
parser.add_option("--window-size-predictions", "-w",
dest="window_size_predictions",
default=81, help="option to set the window size for average smoothening "+\
"of per residue predictions for plotting (option for 'per-res-output' "+\
"and 'per-segment-output'). Default: 81.")
parser.add_option("--per-residue-plot-dir", "-p", dest="plot_dir",
default=None, help="path to the directory to which inferences "+\
"plots will be saved (option for 'per-res-output' and "+\
"'per-res-segment-output' modes. Default: './'.")
parser.add_option("--curve-smoothening", "-c", dest="curve_smoothening",
default=False, action="store_true",
help="option for 'per-segment-output' run mode, which adjusts the "+\
"plot by making an additional smoothening of the curve.")
parser.add_option("--portion-size", dest="portion_size",
default=1000,
help="option to set the portions', into which to divide the input "+\
"of sequences, maximum size. If no division is needed, set the "+\
"option to 0. Default: 1000.")
parser.add_option("--version", "-v", dest="version",
default=False, action="store_true",
help="print version of the program and exit.")
(options, args) = parser.parse_args()
if(options.version):
print(f"TemStaPro 0.2.{int(os.popen('git rev-list --count HEAD').read().strip())-61}")
exit()
options.window_size_predictions = int(options.window_size_predictions)
options.segment_size = int(options.segment_size)
if(options.more_thresholds): PARAMETERS['THRESHOLDS_RANGE'] = ":(40-80]:"
try:
assert (options.fasta != None), f"{sys.argv[0]}: a FASTA file is required."
except AssertionError as message:
print(message, file=sys.stderr)
exit()
try:
assert (options.pt_dir != None), (
f"{sys.argv[0]}: a path to the ProtTrans model location is required."
)
except AssertionError as message:
print(message, file=sys.stderr)
exit()
temstapro_dir = os.path.abspath(options.tsp_dir)
PARAMETERS["CLASSIFIERS_DIR"] = os.path.join(temstapro_dir, "weight")
# Importing local modules
sys.path.append(os.path.join(temstapro_dir, "scripts"))
sys.path.append(os.path.join(temstapro_dir, "model"))
import prottrans_models
import data_process
import model_flow
import results
# Standardization of the FASTA file
(sequences, orig_headers, orig_seqs) = prottrans_models.process_FASTA(options.fasta)
# Loading the ProtTrans model
print("%s: beginning to load the model " % datetime.now(), file=sys.stderr)
pt_model, tokenizer = prottrans_models.load_model_and_tokenizer(options.pt_dir,
PARAMETERS["PT_MODEL_PATH"])
print("%s: finished loading the model" % datetime.now(), file=sys.stderr)
# Dividing sequences into portions
options.portion_size = int(options.portion_size)
if(options.portion_size == 0): options.portion_size = len(sequences)
per_res_mode = (options.per_res_out or options.per_segment_out)
for i in range(0, len(list(sequences.keys())), options.portion_size):
portion_keys = list(sequences.keys())[i:i+options.portion_size]
sequences_portion = {}
for key in portion_keys:
sequences_portion[key] = sequences[key]
# Check which sequences do not have embeddings generated
if(options.emb_dir and os.path.exists(options.emb_dir)):
seqs_wo_emb_portion = data_process.get_sequences_without_embeddings(
sequences_portion, options.emb_dir, per_res=per_res_mode)
else:
seqs_wo_emb_portion = sequences_portion
embeddings = {}
per_res_dataset = {}
per_res_sequences_portion = {}
if(len(seqs_wo_emb_portion)):
gen_emb_start = datetime.now()
print(f"{datetime.now()}: beginning to generate embeddings", file=sys.stderr)
# Generating embeddings
embeddings = prottrans_models.get_embeddings(pt_model, tokenizer,
seqs_wo_emb_portion,
per_residue=per_res_mode,
per_protein=True)
gen_emb_end = datetime.now()
# If cache given, save embeddings
if(options.emb_dir and os.path.exists(options.emb_dir)):
if(per_res_mode):
prottrans_models.save_embeddings(seqs_wo_emb_portion, embeddings,
options.emb_dir, "per_res")
prottrans_models.save_embeddings(seqs_wo_emb_portion, embeddings,
options.emb_dir, "mean")
elif(options.emb_dir and not os.path.exists(options.emb_dir)):
print("The given directory (option -e) does not exist, "+\
"embeddings' PT files will not be saved.", file=sys.stderr)
try:
prottrans_models.print_embeddings_generation_stats(i,
options.portion_size, embeddings, seqs_wo_emb_portion,
gen_emb_start, gen_emb_end)
except ZeroDivisionError:
print(f"{sys.argv[0]}: no embeddings were generated.", file=sys.stderr)
sys.exit(1)
# Collecting the required type of embeddings
dataset = data_process.collect_mean_embeddings(sequences_portion,
embeddings=embeddings, emb_dir=options.emb_dir,
input_size=PARAMETERS["INPUT_SIZE"])
if(options.per_res_out):
per_res_dataset = data_process.collect_per_res_embeddings(sequences_portion,
orig_seqs, embeddings=embeddings, emb_dir=options.emb_dir,
input_size=PARAMETERS["INPUT_SIZE"])
per_res_sequences_portion = per_res_dataset["z_test"]
elif(options.per_segment_out):
per_res_dataset = data_process.collect_per_res_embeddings(sequences_portion,
orig_seqs, embeddings=embeddings,
emb_dir=options.emb_dir, input_size=PARAMETERS["INPUT_SIZE"], smoothen=True,
window_size=options.segment_size)
per_res_sequences_portion = per_res_dataset["z_test"]
test_loader, per_res_test_loader = model_flow.prepare_data_loaders([
dataset, per_res_dataset], 'test')
print("%s: beginning to make inferences" % datetime.now(),
file=sys.stderr)
averaged_inferences, binary_inferences, labels, clashes = model_flow.make_inferences(
sequences_portion, per_res_sequences_portion, test_loader,
per_res_test_loader, PARAMETERS, PARAMETERS["THRESHOLDS_RANGE"])
print("%s: finished making inferences" % datetime.now(), file=sys.stderr)
# Processing results
for j, loader in enumerate([test_loader, per_res_test_loader]):
if(loader is None): break
for seq in averaged_inferences[j].keys():
labels[j][seq].append(results.get_temperature_label(
averaged_inferences[j][seq],
PARAMETERS["TEMPERATURE_RANGES"][PARAMETERS["THRESHOLDS_RANGE"]], left_hand=True))
labels[j][seq].append(results.get_temperature_label(
averaged_inferences[j][seq],
PARAMETERS["TEMPERATURE_RANGES"][PARAMETERS["THRESHOLDS_RANGE"]], left_hand=False))
clashes[j][seq].append(results.detect_clash(averaged_inferences[j][seq],
left_hand=True))
# Processing printing of mean predictions
if(options.mean_out):
os.system(f"mkdir -p {os.path.dirname(options.mean_out)}")
f_mean = open(options.mean_out, "w") if i == 0 else open(options.mean_out, "a")
else:
f_mean = sys.stdout
if(i == 0): results.print_inferences_header(f_mean,
PARAMETERS["THRESHOLDS"][PARAMETERS["THRESHOLDS_RANGE"]],
PARAMETERS["PRINT_THERMOPHILICITY"][PARAMETERS["THRESHOLDS_RANGE"]])
results.print_inferences(averaged_inferences[0], binary_inferences[0],
orig_headers, labels[0], clashes[0],
PARAMETERS["THERMOPHILICITY_LABELS"], f_mean, orig_seqs,
"mean", PARAMETERS["PRINT_THERMOPHILICITY"][PARAMETERS["THRESHOLDS_RANGE"]])
# Printing per-residue inferences
if(options.per_res_out):
os.system(f"mkdir -p {os.path.dirname(options.per_res_out)}")
f_per_res = open(options.per_res_out, "w") if i == 0 else open(options.per_res_out, "a")
if(i == 0): results.print_inferences_header(f_per_res,
PARAMETERS["THRESHOLDS"][PARAMETERS["THRESHOLDS_RANGE"]],
PARAMETERS["PRINT_THERMOPHILICITY"][PARAMETERS["THRESHOLDS_RANGE"]])
results.print_inferences(averaged_inferences[1], binary_inferences[1],
orig_headers, labels[1],
clashes[1], PARAMETERS["THERMOPHILICITY_LABELS"],
f_per_res, per_res_sequences_portion, "per-res",
PARAMETERS["PRINT_THERMOPHILICITY"][PARAMETERS["THRESHOLDS_RANGE"]])
elif(options.per_segment_out):
os.system(f"mkdir -p {os.path.dirname(options.per_segment_out)}")
f_per_res = open(options.per_segment_out, "w") if i == 0 else open(options.per_segment_out, "a")
if(i == 0): results.print_inferences_header(f_per_res,
PARAMETERS["THRESHOLDS"][PARAMETERS["THRESHOLDS_RANGE"]],
PARAMETERS["PRINT_THERMOPHILICITY"][PARAMETERS["THRESHOLDS_RANGE"]])
results.print_inferences(averaged_inferences[1], binary_inferences[1],
orig_headers, labels[1],
clashes[1], PARAMETERS["THERMOPHILICITY_LABELS"], f_per_res,
per_res_sequences_portion, "per-segment",
PARAMETERS["PRINT_THERMOPHILICITY"][PARAMETERS["THRESHOLDS_RANGE"]])
# Plotting inferences
if(options.plot_dir):
os.system(f"mkdir -p {options.plot_dir}")
results.plot_inferences(
options.per_res_out, options.per_segment_out,
averaged_inferences[1],
PARAMETERS["THRESHOLDS"][PARAMETERS["THRESHOLDS_RANGE"]], options.plot_dir,
options.window_size_predictions, options.segment_size,
options.curve_smoothening)