diff --git "a/examples/cell_classification.ipynb" "b/examples/cell_classification.ipynb" --- "a/examples/cell_classification.ipynb" +++ "b/examples/cell_classification.ipynb" @@ -2,191 +2,583 @@ "cells": [ { "cell_type": "markdown", - "id": "65a2b29a-c678-4874-a1bf-5af3a7d00ed9", + "id": "234afff3", "metadata": {}, "source": [ - "## Geneformer Fine-Tuning for Classification of Cardiomyopathy Disease States" + "## Geneformer Fine-Tuning for Cell Annotation Application" ] }, { - "cell_type": "markdown", - "id": "1792e51c-86c3-406f-be5a-273c4e4aec20", + "cell_type": "code", + "execution_count": 2, + "id": "1cbe6178-ea4d-478a-80a8-65ffaa4c1820", "metadata": {}, + "outputs": [], "source": [ - "### Please note that, as usual with deep learning models, we **highly** recommend tuning learning hyperparameters for all fine-tuning applications as this can significantly improve model performance. Example below uses previously optimized hyperparameters, but one can optimize hyperparameters with the argument n_hyperopt_trials=n in cc.validate() where n>0 and represents the number of trials for hyperparameter optimization." + "import os\n", + "GPU_NUMBER = [0]\n", + "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \",\".join([str(s) for s in GPU_NUMBER])\n", + "os.environ[\"NCCL_DEBUG\"] = \"INFO\"" ] }, { - "cell_type": "markdown", - "id": "3dad7564-b464-4d37-9188-17c0ae4ae59f", + "cell_type": "code", + "execution_count": 3, + "id": "a9885d9f-00ac-4c84-b6a3-b7b648a90f0f", "metadata": {}, + "outputs": [], "source": [ - "### Train cell classifier with 70% of data (with hyperparameters previously optimized based on 15% of data as validation set) and evaluate on held-out test set of 15% of data" + "# imports\n", + "from collections import Counter\n", + "import datetime\n", + "import pickle\n", + "import subprocess\n", + "import seaborn as sns; sns.set()\n", + "from datasets import load_from_disk\n", + "from sklearn.metrics import accuracy_score, f1_score\n", + "from transformers import BertForSequenceClassification\n", + "from transformers import Trainer\n", + "from transformers.training_args import TrainingArguments\n", + "\n", + "from geneformer import DataCollatorForCellClassification" ] }, { "cell_type": "markdown", - "id": "9027e51e-7830-4ab8-aebf-b9779b3ea2c1", + "id": "68bd3b98-5409-4105-b7af-f1ff64ea6a72", "metadata": {}, "source": [ - "### Fine-tune the model for cell state classification" + "## Prepare training and evaluation datasets" ] }, { "cell_type": "code", - "execution_count": 2, - "id": "efe3b79b-aa8f-416c-9755-7f9299d6a81e", + "execution_count": 15, + "id": "5735f1b7-7595-4a02-be17-2c5b970ad81a", "metadata": {}, "outputs": [], "source": [ - "import datetime\n", - "from geneformer import Classifier\n", + "# load cell type dataset (includes all tissues)\n", + "train_dataset=load_from_disk(\"/path/to/cell_type_train_data.dataset\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4297a02-4c4c-434c-ae55-3387a0b239b5", + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + }, + "tags": [] + }, + "outputs": [], + "source": [ + "dataset_list = []\n", + "evalset_list = []\n", + "organ_list = []\n", + "target_dict_list = []\n", "\n", - "current_date = datetime.datetime.now()\n", - "datestamp = f\"{str(current_date.year)[-2:]}{current_date.month:02d}{current_date.day:02d}{current_date.hour:02d}{current_date.minute:02d}{current_date.second:02d}\"\n", - "datestamp_min = f\"{str(current_date.year)[-2:]}{current_date.month:02d}{current_date.day:02d}\"\n", + "for organ in Counter(train_dataset[\"organ_major\"]).keys():\n", + " # collect list of tissues for fine-tuning (immune and bone marrow are included together)\n", + " if organ in [\"bone_marrow\"]: \n", + " continue\n", + " elif organ==\"immune\":\n", + " organ_ids = [\"immune\",\"bone_marrow\"]\n", + " organ_list += [\"immune\"]\n", + " else:\n", + " organ_ids = [organ]\n", + " organ_list += [organ]\n", + " \n", + " print(organ)\n", + " \n", + " # filter datasets for given organ\n", + " def if_organ(example):\n", + " return example[\"organ_major\"] in organ_ids\n", + " trainset_organ = train_dataset.filter(if_organ, num_proc=16)\n", + " \n", + " # per scDeepsort published method, drop cell types representing <0.5% of cells\n", + " celltype_counter = Counter(trainset_organ[\"cell_type\"])\n", + " total_cells = sum(celltype_counter.values())\n", + " cells_to_keep = [k for k,v in celltype_counter.items() if v>(0.005*total_cells)]\n", + " def if_not_rare_celltype(example):\n", + " return example[\"cell_type\"] in cells_to_keep\n", + " trainset_organ_subset = trainset_organ.filter(if_not_rare_celltype, num_proc=16)\n", + " \n", + " # shuffle datasets and rename columns\n", + " trainset_organ_shuffled = trainset_organ_subset.shuffle(seed=42)\n", + " trainset_organ_shuffled = trainset_organ_shuffled.rename_column(\"cell_type\",\"label\")\n", + " trainset_organ_shuffled = trainset_organ_shuffled.remove_columns(\"organ_major\")\n", + " \n", + " # create dictionary of cell types : label ids\n", + " target_names = list(Counter(trainset_organ_shuffled[\"label\"]).keys())\n", + " target_name_id_dict = dict(zip(target_names,[i for i in range(len(target_names))]))\n", + " target_dict_list += [target_name_id_dict]\n", + " \n", + " # change labels to numerical ids\n", + " def classes_to_ids(example):\n", + " example[\"label\"] = target_name_id_dict[example[\"label\"]]\n", + " return example\n", + " labeled_trainset = trainset_organ_shuffled.map(classes_to_ids, num_proc=16)\n", + " \n", + " # create 80/20 train/eval splits\n", + " labeled_train_split = labeled_trainset.select([i for i in range(0,round(len(labeled_trainset)*0.8))])\n", + " labeled_eval_split = labeled_trainset.select([i for i in range(round(len(labeled_trainset)*0.8),len(labeled_trainset))])\n", + " \n", + " # filter dataset for cell types in corresponding training set\n", + " trained_labels = list(Counter(labeled_train_split[\"label\"]).keys())\n", + " def if_trained_label(example):\n", + " return example[\"label\"] in trained_labels\n", + " labeled_eval_split_subset = labeled_eval_split.filter(if_trained_label, num_proc=16)\n", "\n", - "output_prefix = \"cm_classifier_test\"\n", - "output_dir = f\"/path/to/output_dir/{datestamp}\"\n", - "!mkdir $output_dir" + " dataset_list += [labeled_train_split]\n", + " evalset_list += [labeled_eval_split_subset]" ] }, { "cell_type": "code", - "execution_count": 3, - "id": "f070ab20-1b18-4941-a5c7-89e23b519261", + "execution_count": 20, + "id": "83e20521-597a-4c54-897b-c4d42ea622c2", + "metadata": {}, + "outputs": [], + "source": [ + "trainset_dict = dict(zip(organ_list,dataset_list))\n", + "traintargetdict_dict = dict(zip(organ_list,target_dict_list))\n", + "\n", + "evalset_dict = dict(zip(organ_list,evalset_list))" + ] + }, + { + "cell_type": "markdown", + "id": "10eb110d-ba43-4efc-bc43-1815d6912647", + "metadata": {}, + "source": [ + "## Fine-Tune With Cell Classification Learning Objective and Quantify Predictive Performance" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "cd7b1cfb-f5cb-460e-ae77-769522ece054", + "metadata": {}, + "outputs": [], + "source": [ + "def compute_metrics(pred):\n", + " labels = pred.label_ids\n", + " preds = pred.predictions.argmax(-1)\n", + " # calculate accuracy and macro f1 using sklearn's function\n", + " acc = accuracy_score(labels, preds)\n", + " macro_f1 = f1_score(labels, preds, average='macro')\n", + " return {\n", + " 'accuracy': acc,\n", + " 'macro_f1': macro_f1\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "beaab7a4-cc13-4e8f-b137-ed18ff7b633c", + "metadata": {}, + "source": [ + "### Please note that, as usual with deep learning models, we **highly** recommend tuning learning hyperparameters for all fine-tuning applications as this can significantly improve model performance. Example hyperparameters are defined below, but please see the \"hyperparam_optimiz_for_disease_classifier\" script for an example of how to tune hyperparameters for downstream applications." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "d24e1ab7-0131-44bd-b458-1ce5ba31853e", "metadata": {}, "outputs": [], "source": [ - "filter_data_dict={\"cell_type\":[\"Cardiomyocyte1\",\"Cardiomyocyte2\",\"Cardiomyocyte3\"]}\n", - "training_args = {\n", - " \"num_train_epochs\": 0.9,\n", - " \"learning_rate\": 0.000804,\n", - " \"lr_scheduler_type\": \"polynomial\",\n", - " \"warmup_steps\": 1812,\n", - " \"weight_decay\":0.258828,\n", - " \"per_device_train_batch_size\": 12,\n", - " \"seed\": 73,\n", - "}\n", + "# set model parameters\n", + "# max input size\n", + "max_input_size = 2 ** 11 # 2048\n", "\n", - "# OF NOTE: token_dictionary_file must be set to the gc-30M token dictionary if using a 30M series model\n", - "# (otherwise the Classifier will use the current default model dictionary)\n", - "# 30M token dictionary: https://huggingface.co/ctheodoris/Geneformer/blob/main/geneformer/gene_dictionaries_30m/token_dictionary_gc30M.pkl\n", - "cc = Classifier(classifier=\"cell\",\n", - " cell_state_dict = {\"state_key\": \"disease\", \"states\": \"all\"},\n", - " filter_data=filter_data_dict,\n", - " training_args=training_args,\n", - " max_ncells=None,\n", - " freeze_layers = 2,\n", - " num_crossval_splits = 1,\n", - " forward_batch_size=200,\n", - " nproc=16)" + "# set training hyperparameters\n", + "# max learning rate\n", + "max_lr = 5e-5\n", + "# how many pretrained layers to freeze\n", + "freeze_layers = 0\n", + "# number gpus\n", + "num_gpus = 1\n", + "# number cpu cores\n", + "num_proc = 16\n", + "# batch size for training and eval\n", + "geneformer_batch_size = 12\n", + "# learning schedule\n", + "lr_schedule_fn = \"linear\"\n", + "# warmup steps\n", + "warmup_steps = 500\n", + "# number of epochs\n", + "epochs = 10\n", + "# optimizer\n", + "optimizer = \"adamw\"" ] }, { "cell_type": "code", - "execution_count": 4, - "id": "0bced2e8-0a49-418e-a7f9-3981be256bd6", + "execution_count": 20, + "id": "05164c24-5fbf-4372-b26c-a43f3777a88d", "metadata": {}, "outputs": [ { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9c409ca656ed4cb0b280d95e326c1bc7", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Saving the dataset (0/3 shards): 0%| | 0/115367 [00:00:54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] }, { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "facb7207b57948aebb3f8681346e17d4", - "version_major": 2, - "version_minor": 0 - }, + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [10280/10280 13:33, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
10.0870000.0680670.9854040.9568390.985483
20.0444000.0752890.9850790.9550690.984898
30.0667000.0787030.9837820.9532400.983959
40.0374000.0571320.9899450.9706190.989883
50.0250000.0616440.9883230.9611260.988211
60.0224000.0653230.9892960.9697370.989362
70.0186000.0637100.9896200.9694360.989579
80.0398000.0659190.9899450.9680650.989802
90.0302000.0613590.9902690.9717000.990314
100.0134000.0591810.9915670.9745990.991552

" + ], "text/plain": [ - "Saving the dataset (0/1 shards): 0%| | 0/17228 [00:00" ] }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "# previously balanced splits with prepare_data and validate functions\n", - "# argument attr_to_split set to \"individual\" and attr_to_balance set to [\"disease\",\"lvef\",\"age\",\"sex\",\"length\"]\n", - "train_ids = [\"1447\", \"1600\", \"1462\", \"1558\", \"1300\", \"1508\", \"1358\", \"1678\", \"1561\", \"1304\", \"1610\", \"1430\", \"1472\", \"1707\", \"1726\", \"1504\", \"1425\", \"1617\", \"1631\", \"1735\", \"1582\", \"1722\", \"1622\", \"1630\", \"1290\", \"1479\", \"1371\", \"1549\", \"1515\"]\n", - "eval_ids = [\"1422\", \"1510\", \"1539\", \"1606\", \"1702\"]\n", - "test_ids = [\"1437\", \"1516\", \"1602\", \"1685\", \"1718\"]\n", - "\n", - "train_test_id_split_dict = {\"attr_key\": \"individual\",\n", - " \"train\": train_ids+eval_ids,\n", - " \"test\": test_ids}\n", - "\n", - "# Example input_data_file for 30M model: https://huggingface.co/datasets/ctheodoris/Genecorpus-30M/tree/main/example_input_files/cell_classification/disease_classification/human_dcm_hcm_nf.dataset\n", - "cc.prepare_data(input_data_file=\"/path/to/human_dcm_hcm_nf_2048_w_length.dataset\",\n", - " output_directory=output_dir,\n", - " output_prefix=output_prefix,\n", - " split_id_dict=train_test_id_split_dict)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "73fe8b29-dd8f-4bf8-82c1-53196d73ed49", - "metadata": {}, - "outputs": [ + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "691e875524e441bca22b790a0f4a2a35", - "version_major": 2, - "version_minor": 0 - }, + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [257/257 00:07]\n", + "
\n", + " " + ], "text/plain": [ - " 0%| | 0/1 [00:00" ] }, "metadata": {}, "output_type": "display_data" }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Some weights of the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "****** Validation split: 1/1 ******\n", - "\n" + "kidney\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" ] }, { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c2c4f53aa71a49b89c32c8ba573b0b0c", - "version_major": 2, - "version_minor": 0 - }, + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [29340/29340 45:43, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
10.3269000.2991930.9125000.8230670.909627
20.2242000.2395800.9264770.8502370.923902
30.2216000.2428100.9302270.8785530.930349
40.1661000.2641780.9334090.8847590.933031
50.1441000.2792820.9350000.8876590.934987
60.1128000.3076470.9359090.8892390.935365
70.0846000.3263990.9328410.8924470.933191
80.0683000.3326260.9365910.8916290.936354
90.0655000.3481740.9352270.8894840.935040
100.0461000.3553500.9350000.8945780.934971

" + ], "text/plain": [ - "Filter (num_proc=16): 0%| | 0/115367 [00:00" ] }, "metadata": {}, "output_type": "display_data" }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "adf76144219747558bf39b7e776a68b3", - "version_major": 2, - "version_minor": 0 - }, + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [734/734 00:27]\n", + "
\n", + " " + ], "text/plain": [ - "Filter (num_proc=16): 0%| | 0/115367 [00:00" ] }, "metadata": {}, @@ -196,10 +588,25 @@ "name": "stderr", "output_type": "stream", "text": [ - "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /gladstone/theodoris/home/ctheodoris/Geneformer and are newly initialized: ['bert.pooler.dense.bias', 'bert.pooler.dense.weight', 'classifier.bias', 'classifier.weight']\n", - "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n", - "Detected kernel version 4.18.0, which is below the recommended minimum of 5.5.0; this can cause the process to hang. It is recommended to upgrade the kernel to the minimum version or higher.\n", - "/gladstone/theodoris/home/ctheodoris/Geneformer/geneformer/collator_for_classification.py:581: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + "Some weights of the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lung\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" ] }, @@ -209,26 +616,100 @@ "\n", "
\n", " \n", - " \n", - " [7020/7020 26:02, Epoch 0/1]\n", + " \n", + " [21750/21750 30:32, Epoch 10/10]\n", "
\n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
00.1424000.3891660.8897970.69307410.3376000.3415230.9063600.7599790.899310
20.2119000.2589540.9284290.8355340.925903
30.2086000.2820810.9304210.8427860.928013
40.1444000.2530470.9354790.8717120.935234
50.1092000.2688330.9394640.8761730.938870
60.1327000.2826970.9405360.8832710.940191
70.0818000.2958640.9408430.8842010.940170
80.0359000.3066000.9419160.8847770.941578
90.0508000.3116770.9405360.8834370.940294
100.0358000.3153600.9408430.8835510.940612

" @@ -244,193 +725,1201 @@ "name": "stderr", "output_type": "stream", "text": [ - "/gladstone/theodoris/home/ctheodoris/Geneformer/geneformer/collator_for_classification.py:581: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" ] }, { "data": { - "text/html": [], + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [544/544 00:19]\n", + "
\n", + " " + ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "train_valid_id_split_dict = {\"attr_key\": \"individual\",\n", - " \"train\": train_ids,\n", - " \"eval\": eval_ids}\n", - "\n", - "# Example 6 layer 30M Geneformer model: https://huggingface.co/ctheodoris/Geneformer/blob/main/gf-6L-30M-i2048/model.safetensors\n", - "all_metrics = cc.validate(model_directory=\"/path/to/Geneformer\",\n", - " prepared_input_data_file=f\"{output_dir}/{output_prefix}_labeled_train.dataset\",\n", - " id_class_dict_file=f\"{output_dir}/{output_prefix}_id_class_dict.pkl\",\n", - " output_directory=output_dir,\n", - " output_prefix=output_prefix,\n", - " split_id_dict=train_valid_id_split_dict)\n", - " # to optimize hyperparameters, set n_hyperopt_trials=100 (or alternative desired # of trials)" - ] - }, - { - "cell_type": "markdown", - "id": "6eca8ab4-6f4d-4dd6-9b90-edfb5cc7417c", - "metadata": {}, - "source": [ - "### Evaluate the model" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "f580021e-2b70-4ebc-943c-2bfe6177e1b5", - "metadata": {}, - "outputs": [ + }, { "name": "stderr", "output_type": "stream", "text": [ - "Hyperparameter tuning is highly recommended for optimal results. No training_args provided; using default hyperparameters.\n" + "Some weights of the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" ] - } - ], - "source": [ - "cc = Classifier(classifier=\"cell\",\n", - " cell_state_dict = {\"state_key\": \"disease\", \"states\": \"all\"},\n", - " forward_batch_size=200,\n", - " nproc=16)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "b05398b4-bca1-44b0-8160-637489f16646", - "metadata": {}, - "outputs": [ + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "brain\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [8880/8880 11:14, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
10.1631000.1566400.9703450.7364550.960714
20.1498000.1348970.9688440.7471140.960726
30.1056000.1153540.9722220.7752710.964932
40.0869000.2079180.9688440.7079270.958257
50.0564000.1065480.9740990.8398380.971611
60.0376000.1174370.9782280.8565780.975665
70.0305000.1278850.9744740.8562960.973531
80.0193000.1432030.9778530.8593620.975776
90.0074000.1537580.9725980.8528350.972314
100.0172000.1539110.9759760.8581960.974498

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [222/222 00:04]\n", + "
\n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Some weights of the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "placenta\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [6180/6180 10:28, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
10.1287000.1251750.9606260.9357520.959463
20.0640000.2156070.9514560.9205790.949828
30.0513000.2030440.9611650.9341950.959470
40.0453000.1157010.9789640.9663870.978788
50.0482000.1494840.9735710.9589270.973305
60.0409000.1343390.9789640.9674660.978899
70.0016000.1599000.9784250.9667130.978211
80.0024000.1253510.9795040.9680640.979428
90.0094000.1201320.9805830.9696310.980506
100.0015000.1378640.9789640.9671800.978825

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [155/155 00:05]\n", + "
\n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Some weights of the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "immune\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [17140/17140 22:02, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
10.2889000.2315820.9367700.8684050.934816
20.2032000.2062920.9373540.8886610.939555
30.1835000.1958110.9449420.8911490.944008
40.1510000.2195810.9476650.9065780.947093
50.0900000.2471200.9466930.8988120.945808
60.0604000.2496620.9484440.9050140.947975
70.0713000.2727670.9494160.9115140.949748
80.0526000.3050510.9453310.9023480.944987
90.0269000.2941350.9486380.9040580.948296
100.0345000.2920290.9501950.9085470.949753

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [429/429 00:13]\n", + "
\n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Some weights of the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "large_intestine\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "8e93a706295b49a1996b275eba3e9f31", - "version_major": 2, - "version_minor": 0 - }, + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [33070/33070 43:02, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
10.3062000.3124310.9082660.7862420.900768
20.2239000.2480960.9251010.8412510.920987
30.1736000.2599970.9259070.8503480.926290
40.1629000.2823060.9250000.8736690.925531
50.1434000.2544940.9379030.8767490.937836
60.1045000.2899420.9346770.8753330.934339
70.0803000.3139140.9354840.8772710.934986
80.0635000.3398680.9362900.8822670.936187
90.0425000.3457840.9389110.8829630.938682
100.0389000.3521990.9395160.8855090.939497

" + ], "text/plain": [ - " 0%| | 0/87 [00:00" ] }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "all_metrics_test = cc.evaluate_saved_model(\n", - " model_directory=f\"{output_dir}/{datestamp_min}_geneformer_cellClassifier_{output_prefix}/ksplit1/\",\n", - " id_class_dict_file=f\"{output_dir}/{output_prefix}_id_class_dict.pkl\",\n", - " test_data_file=f\"{output_dir}/{output_prefix}_labeled_test.dataset\",\n", - " output_directory=output_dir,\n", - " output_prefix=output_prefix,\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "b45404e4-87cc-421d-84f5-1f9cbc09aa31", - "metadata": {}, - "outputs": [ + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, { "data": { + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " [827/827 00:26]\n", + "
\n", + " " + ], "text/plain": [ - "
" + "" ] }, "metadata": {}, "output_type": "display_data" }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Some weights of the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pancreas\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, { "data": { - "image/png": "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B6ClpYXg4GCFVqZjx47len5+1875GlatWhU6OjqYN29ermXk3AcRlSwGNURqpm3btjhw4ACkUins7e3zPM/Z2RkAsG/fPoVWnb/++gtZWVn5XsPCwgJmZmbYuXMnhg0bBqFQmOt5Oenp6ekK6a1atYKWlhaePXum1H324XWqVauG/fv3Y9iwYfIg7uXLl7h+/XqurVcfIxAIoKmpqTDIOC0tDXv37s31/IcPH+LevXsKXVD79+9HxYoV5S0zbdu2RXBwMPT19VG7du1C14mIigeDGiI1061bN+zbtw/ff/89hgwZgsaNG0NbWxuvX7/GpUuX0KFDB3Tq1AmWlpbo0aMHNm7cCC0tLbRo0QIPHz7EunXrlLq0cjN9+nSMGjUKX331FYYOHQoTExNERUXh7Nmz+PnnnwEA1tbWAICNGzeid+/e0NLSgoWFBWrVqoWxY8diyZIleP78OVxdXSESifDu3TuEhYVBT08PY8eOhYaGBsaNG4dp06Zh9OjR+OqrryAWi7F8+fJcu4UKok2bNvjtt98wceJE9O/fH/Hx8Vi3bl2egZmxsTFGjRoFHx8fVKtWDXv37sX58+fh6+srXxPI09MTR44cweDBgzF06FDY2NhAKpUiKioK586dw7fffptvgElExYNBDZGa0dTUxKpVq7Bp0ybs2bMHa9asgaamJmrUqAEnJyd5oAEAc+fOhZGREXbv3o2QkBDY2tpi2bJlmDBhwkev07p1a2zevBkrVqyAv78/0tPTUaNGDYWtC5ydnTFixAjs3r0b27dvh1QqxaZNm+TplpaW2LRpEw4cOICMjAxUq1YNDRs2xMCBA+Vl9OvXDwDw66+/wsfHBzVr1sSIESNw5cqVIg3AdXFxwbx587B27VqMHDkS1atXx1dffQUDAwP873//Uzrf1tYWffr0wbJly/DkyRMYGxtj6tSp8jWBAKBChQr4/fffsWbNGmzduhUvXryArq4uTExM0KJFC9SsWbPQ9SSiwhPIZHmsNkVERERUhnBKNxEREakFBjVERESkFhjUEBERkVpgUENERERqgUENERERqQUGNURERKQWGNQQERGRWmBQQ0RERGqBQQ0RERGpBQY1REREpBYY1BAREZFa+D/CdhBolX930AAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [18280/18280 23:32, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
10.3401000.3432000.8962440.6556610.879469
20.1783000.2240330.9308900.8597720.925342
30.1542000.2080340.9412840.8870120.939485
40.1212000.2166600.9403720.8807160.939431
50.0999000.2542550.9405540.8890880.938300
60.0658000.2674290.9427430.8976820.942815
70.0612000.2825090.9454780.8987970.943881
80.0368000.3017810.9438370.9038160.944163
90.0354000.3170260.9425600.9022410.942071
100.0142000.3132590.9467540.9049550.946129

" + ], "text/plain": [ - "

" + "" ] }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "cc.plot_conf_mat(\n", - " conf_mat_dict={\"Geneformer\": all_metrics_test[\"conf_matrix\"]},\n", - " output_directory=output_dir,\n", - " output_prefix=output_prefix,\n", - " custom_class_order=[\"nf\",\"hcm\",\"dcm\"],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "0038d701-ab94-46d2-b390-803be0850019", - "metadata": {}, - "outputs": [ + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, { "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [457/457 00:11]\n", + "
\n", + " " + ], "text/plain": [ - "
" + "" ] }, "metadata": {}, "output_type": "display_data" }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Some weights of the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.decoder.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight']\n", + "- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", + "- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at /n/home01/ctheodoris/models/210602_111318_geneformer_27M_L6_emb256_SL2048_E3_B12_LR0.001_LSlinear_WU10000_Oadamw_DS12/models/ and are newly initialized: ['bert.pooler.dense.weight', 'bert.pooler.dense.bias', 'classifier.weight', 'classifier.bias']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "liver\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, { "data": { - "image/png": "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XzxkSG7ZskVXXXWVVqxYcdiLjT322GO6/PLL9d5779V6q6acnBzdc889wXGemJiowYMHH/SYpKQk3X333cEfv/vuu/rFL36hDRs2hHzOQCCgxYsX68EHH9SYMWMOe5QbAID/xNWkAQBNwt13360lS5Zoy5YtKi8v1+23365nnnlGAwYMUEREhFavXh28D/BPf/pTff755yFvd1RXVVVV+vjjj/Xxxx8rNjZWPXr0UPv27ZWQkKCCggJt3bpVK1asCD4+NjZWd9xxR8jnevDBB7V3717Nnj1bgUBAL774ol577TX169dPmZmZiomJUVFRkbZu3aq1a9cGX2Lcp0+fQ55r1KhRGjp0qObPn69AIKBHH31Ur7/+uvr06aPExERt2bJFixcvVnV1tTIyMjR58mQ99thjtf46A4GAFi1apEWLFikiIkKdOnVSly5dlJKSorKyMu3evVuLFy8+6F7Pd955Z8hbXF100UXatm2b/vrXv0qSvvrqK82cOVPdunVT9+7dlZCQoNLSUmVlZWnVqlXcWxgA0CCMYQBAk9CsWTO98soruv7664Ojd/PmzYdcufiiiy7Sbbfdps8//7xBny8hISH4dllZmZYuXVrrxbvatWunxx57TD179gz5/ujoaD377LN6+umn9dJLL6m0tFSVlZX67rvv9N1334X8mKioKA0cODDk+x5//HFde+21wTG+ffv24O2W9uvataueeOIJLVu2rM6/zurqaq1fvz5426hQj73rrrt06aWX1vp8N910k7p166aHH35Ye/bsUSAQ0Nq1a7V27dpaP6Z///4NPpIPAGh6GMMAgCajdevWmjp1qt599119+OGHWrt2rUpKStSiRQv17dtXF198sUaPHu3L55o+fbqWLFmiefPmadmyZdq0aZP27NmjsrIyxcbGqkWLFurZs6fGjBmjCRMmHPLy6P8UERGhm266SZMmTdL06dP17bffasOGDcrNzVVVVZUSEhLUtm1bde/eXcOGDdPo0aMPOS93v/T0dL355puaOnWqPv74Y61bt06lpaVq3ry5OnXqpAkTJujcc89VXFzcYcfw//t//0+XX365vv32Wy1ZskTr16/Xrl27VFxcrIiICKWmpqpbt246+eSTdf755x90u6jaTJgwQePGjdPHH3+s2bNna/ny5crJyVFJSYni4uKUkZGhLl266IQTTtDo0aMPujczAAB1FRY42veOABBUVlamTZs2qVOnTiFfIggAAHA84HsaNAZcQAsAAAAA0OQwhgEAAAAATQ5jGAAAAADQ5DCGAQAAAABNDmMYAAAAANDkMIYBAAAAAE0OYxgAAAAA0OQwhgEAAAAATQ5jGAAAAADQ5DCGAQAAAABNDmMYAAAAANDkMIYBAAAAAE0OYxgAAAAA0OQwhgEAAAAATQ5jGAAAAADQ5DCGAQAAAABNDmMYAAAAANDkMIYBAAAAAE0OYxgAAAAA0OQwhgEAAAAATQ5jGAAAAADQ5DCGAQAAAABNDmMYAHy2YcMG/eY3v9EZZ5yhAQMGqEePHurRo4cmTZpknYZGaN68ecE/Y0899ZR1DtCk8PUHHN8irQMAeOuZ2tM64ZhbnbfaOuGILVy4UFdddZXKy8utU5zx8Q0/s0445s5++gXrBDRSO/YttU4w0TZ9gHUCgEaIMQwAPnrwwQeDQ/i8887T8OHDlZKSIklKTU01LAMAAMCBGMMA4JPdu3drzZo1kqSTTz5Zjz32mHERAAAAasM5wwDgk127dgXf7tOnj2EJAAAADocxDAA+qaioCL4dHR1tWAIAAIDD4WXSAJq0efPmafLkyZKkG264QTfeeKOysrL0+uuva8aMGdq5c6fCwsLUvn17nX766ZoyZYoSExMPeo4xY8Zox44dB/3c008/raeffvqgn9v/EmrgaKvvn+FQlixZog8++EALFizQnj17VFRUpPj4eLVv316DBw/WuHHjNGzYMIWFhQU/Zvv27Ro7dqwk6cILL9Qjjzyi7Oxsvf766/rss8+0c+dOhYeHq2PHjrr00kt14YUXKjLy/74V2blzp15//XXNnDlTO3bsUHh4uLp166aJEyfqggsuOOhzAcfC119/rTfffFNLly5Vfn6+mjdvrr59++qyyy7TyJEj6/1c//73v/Xdd99p7969Ki0tVWJiojp16qTBgwfrrLPOUv/+/Q/6mFB/R23bti34dbJ7927Fxsaqa9euuuKKK3TGGWcc9HWyYcMGvfbaa5o7d6527dqlmJgY9e7dW5MmTQp+rQJNGWMYAA4we/Zs3XrrrcrLyzvo51evXq3Vq1frgw8+0KuvvqpWrVrZBAKH0dA/wwUFBbrrrrs0Y8aMkO9bsWKFVqxYoddee02vvfaahg4dWmvLkiVLdMMNN2jv3r0H/fyyZcu0bNkyffHFF3ryyScVFRWlmTNn6pZbblFRUdFBj128eLEWL16suXPn6tFHH63j7wLQMDU1Nbrvvvs0derUg35+165d2rVrlz777DNNmjRJp59++mGfa/fu3br55pv13XffHfK+vLy84J/xF154QV988YXatm1b63N98cUXuv322w/6OikpKdH8+fM1f/58XX755brvvvsUFhamd955R/fff78qKyuDjy0rK9OcOXM0Z84cXX/99brpppvq8tsBNFqMYQD4X6tWrdKLL76oyspKXXjhhTrhhBOUkJCgzZs36x//+If27NmjLVu26M4779Qrr7wS/LgHH3xQZWVlWrt2rZ544glJ0oQJE3T22Wdb/VLQRB3pn+H9CgoKNHHiRG3cuFGSFBsbq/Hjx2vgwIFKTU1VcXGx1q9fr2+++UZr1qxRIBCotWXXrl267rrrlJ+fr3PPPVfDhw9XfHy81qxZozfeeEMFBQX64osv9Nxzz2nkyJG6/vrrFRkZqYkTJ2rQoEGKjo7WkiVL9NZbb6m8vFzTp0/X8OHDdeGFFx613z9gv4ceeig4hCMiInTOOedo2LBhio6O1qpVqzRt2jS99tprysrK8nye7du3a+LEidq3b58kKTk5WRMmTFDfvn2VlJSkgoICrV27VrNmzdKWLVs8v6ZWrlyp559/XpI0ceJEDR48WFFRUVq8eLHefvttlZeX64033tDAgQMVHx+ve++9VykpKbrooovUq1cvhYWFae7cuZo+fbqqq6v117/+VcOHD9ewYcN8+l0Djj+MYQD4XzNmzFDLli314osvqlu3bge977LLLtOPfvQj7dixQ3PnztX333+vvn37SlLwpXJJSUnBx3fu3Fnjxo07dvGAjvzP8H533313cAj369dPzzzzjDIyMkJ+ru+//17NmjWrtWXu3LlKSkrS66+/rkGDBgV/fsKECTrnnHN08cUXq7y8XC+//LLee+89tWzZUi+99JI6duwYfOzZZ5+tMWPGaMqUKZKkF198kTGMo27RokX6+9//LkmKi4vTc889pyFDhgTff+6552rKlCn66U9/qk8//bTW56mpqdGvfvWr4BAePXq0/vjHPyo5OTnk4+fMmVPr+6Qfjgq3bt1aL730kjp16hT8+bPPPltjx47VlVdeqUAgoKeeekqFhYXq1auXXnjhBTVv3jz42PPOO0+D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80zw5375y18eizwAAAAAaDQ4MuyQ6upq/fd//7eee+45nXDCCRo4cKAyMzOVlJSksrIybdq0SZ9++qk2btwY/JhLL71U55xzjmE1AAAAABx/OGfYQdXV1Zo/f77mz59f62Oio6N144036uqrrz6GZQAAAADQONR5DOPoS0xM1KuvvqqlS5dq6dKl2rp1q3Jzc5WXl6fIyEilpKSoW7duGj58uC666CKlpaVZJwMAAADAcanOY7iKWysddeHh4Ro2bJiGDRtmnQIAAAAAjVqdx3CAl0kDAAAAABqJehwZZg0DAAAAABoHLqAFAAAAAGhyOGcYAAAAANDkcM4wAAAAAKDJ4WXSAAAAAIAmh5dJAwAAAACanLofGWYMAwAAAAAaCY4MAwAAAACaHM4ZBgAAAAA0OXUew5UcGQYAAAAANBIcGQYAAAAANDlhgQB3EAYAAAAANC3h1gEAAAAAABxrjGEAAAAAQJPDGAYAAAAANDmMYQAAAABAk8MYBgAAAAA0OYxhAAAAAECTwxgGAAAAADQ5jGEAAAAAQJPDGAYAAAAANDmMYQAAAABAkxNpHQA0Fj1Te1onAE740xUnWycAThh4/6+sEwAntE0fYJ0AhMSRYQAAAABwxPbt29WjRw/16NFDd911V8jHTJo0KfiYpm7MmDHq0aOHxowZU++P5cgwAAAAAOAg8+bN0/z58yVJF154odq1a2dc5D/GMAAAAADgIPPnz9fTTz8tSRo6dChjGAAAAABg67XXXrNOaBQ4ZxgAAAAA0OQwhgEAAAAATQ4vkwYAAADgrHnz5mny5MmSpBtuuEE33nijNm7cqNdff12zZ89WVlaWoqKi1LlzZ40fP16XX365oqOjQz7XmDFjtGPHDrVt21ZffPGFKioqNHXqVP373//Wpk2blJ2drdatW+uLL7445GMXLFigDz74QAsXLtTevXtVXl6utLQ09e/fX+ecc47OOOMMhYWFHfbXs3TpUr366qtauHChcnJylJqaqu7du+viiy/WhAkT6vR7MmnSpODFrdasWeP52OLiYr377rv6+uuvtWbNGuXm5kqS0tPT1b17d5100kk6++yz1bx5c0nSU089FTxXeL/9v/8H2v97GMrWrVv19ttva86cOdqxY4eKioqUnJysrl27auzYsbr00ksVFxd32F/n7t279eKLL2rmzJnavXu3YmNjlZmZGfz/c2xs7GGfwwtjGAAAAMBx45///KfuuecelZaWBn+utLRUS5Ys0ZIlSzR16lQ9//zzat26tefzbN++Xdddd53Wrl3r+biCggLdcccd+vLLLw953+7du7V79259+umnGjJkiJ588kmlpaXV+lxPP/20nnnmGdXU1AR/bs+ePdqzZ49mz56tf//737rllls8e+rjX//6l+6//37l5eUd8r4dO3Zox44d+vLLL/XZZ5/5ch5yTU2N/vKXv+iFF15QVVXVQe/Lzs5Wdna25s2bpxdffFHPPPOM+vbtW+tzffXVV7rllltUXFwc/LmysjLl5eVp2bJleu+99/Tss882qJcxDAAAAOC4sGLFCj377LOqqqrSOeecoxEjRig2Nlbr1q3TtGnTtHfvXq1fv14//elP9e677yoxMTHk81RUVOjGG2/U2rVrNXDgQJ155plq1aqVCgsLDxrHRUVF+vGPf6z169dL+uFo6IQJE9S1a1dFR0drx44d+vjjj7Vq1SotWLBAV155pd5++23FxMQc8jlffvllPfXUU8Efjx07VqeeeqoSEhK0YcMGTZs2TZ988okCgYAvv1dvvPGGHnjggeCPe/XqpXHjxikzM1MRERHKysrS0qVLNWvWrIM+bsKECerVq5c+/vhj/fOf/5Qk3XTTTerevftBjwt1VPbOO+/UBx98IElKSkrS+PHj1b9/fyUlJSk7O1uzZs0KHuWdPHmypk2bpk6dOh3yPEuWLNENN9ygyspKSVKfPn107rnnKiMjQ3v27NGHH36o77//XjfddFPwMUeCMQwAAADguPDll18qLi5Ozz//vIYNG3bQ+6666ipdffXVWrZsmbZs2aI///nPuu+++0I+z969e7V3717deuutuvbaa2v9fPfdd19wCF955ZW69dZbFRUVddBjrr76av3pT3/Sc889p9WrV+uvf/2rbr755oMes23bNv35z3+WJEVEROiPf/zjIS+Jvuqqq/Tzn/9cn376ad1+MzwsX75cDz30kCQpMjJS9957r3784x+HfBl3SUmJFi5cGPxxly5d1KVLF61atSr4cyeccMIhv9//6c033wwO4eHDh+vxxx8/5Cj5T37yE33++ef69a9/reLiYt1zzz36xz/+cdBjqqurdc899wRH7qRJk3TPPfcoPPz/Lnc1efJkPfroo3r55Zfr8LtROy6g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", + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [18690/18690 26:56, Epoch 10/10]\n", + "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
EpochTraining LossValidation LossAccuracyMacro F1Weighted F1
10.3885000.3855030.8781880.6738870.871348
20.3159000.3027750.9074370.7541820.903474
30.2426000.3218440.9079720.7795040.905881
40.2386000.3231190.9115390.7909220.910299
50.1601000.3282030.9156410.7934900.913836
60.1631000.3489420.9174250.8136040.916911
70.1241000.3737990.9168900.8203550.916688
80.1187000.3994740.9168900.8188390.916640
90.0668000.4143630.9176030.8307030.917226
100.0758000.4138280.9190300.8281490.918506

" + ], "text/plain": [ - "

" + "" ] }, "metadata": {}, "output_type": "display_data" - } - ], - "source": [ - "cc.plot_predictions(\n", - " predictions_file=f\"{output_dir}/{output_prefix}_pred_dict.pkl\",\n", - " id_class_dict_file=f\"{output_dir}/{output_prefix}_id_class_dict.pkl\",\n", - " title=\"disease\",\n", - " output_directory=output_dir,\n", - " output_prefix=output_prefix,\n", - " custom_class_order=[\"nf\",\"hcm\",\"dcm\"],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "167f8023-82fa-4c05-8f0c-ea45b9c9c199", - "metadata": {}, - "outputs": [ + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n", + ":54: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " batch = {k: torch.tensor(v, dtype=torch.int64) for k, v in batch.items()}\n" + ] + }, { "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " \n", + " [468/468 00:39]\n", + "
\n", + " " + ], "text/plain": [ - "{'conf_matrix': nf hcm dcm\n", - " nf 3794 385 328\n", - " hcm 562 8680 566\n", - " dcm 13 485 2415,\n", - " 'macro_f1': 0.8426513907521005,\n", - " 'acc': 0.864232644532157,\n", - " 'all_roc_metrics': None}" + "" ] }, - "execution_count": 5, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "all_metrics_test" + "for organ in organ_list:\n", + " print(organ)\n", + " organ_trainset = trainset_dict[organ]\n", + " organ_evalset = evalset_dict[organ]\n", + " organ_label_dict = traintargetdict_dict[organ]\n", + " \n", + " # set logging steps\n", + " logging_steps = round(len(organ_trainset)/geneformer_batch_size/10)\n", + " \n", + " # reload pretrained model\n", + " model = BertForSequenceClassification.from_pretrained(\"/path/to/pretrained_model/\", \n", + " num_labels=len(organ_label_dict.keys()),\n", + " output_attentions = False,\n", + " output_hidden_states = False).to(\"cuda\")\n", + " \n", + " # define output directory path\n", + " current_date = datetime.datetime.now()\n", + " datestamp = f\"{str(current_date.year)[-2:]}{current_date.month:02d}{current_date.day:02d}\"\n", + " output_dir = f\"/path/to/models/{datestamp}_geneformer_CellClassifier_{organ}_L{max_input_size}_B{geneformer_batch_size}_LR{max_lr}_LS{lr_schedule_fn}_WU{warmup_steps}_E{epochs}_O{optimizer}_F{freeze_layers}/\"\n", + " \n", + " # ensure not overwriting previously saved model\n", + " saved_model_test = os.path.join(output_dir, f\"pytorch_model.bin\")\n", + " if os.path.isfile(saved_model_test) == True:\n", + " raise Exception(\"Model already saved to this directory.\")\n", + "\n", + " # make output directory\n", + " subprocess.call(f'mkdir {output_dir}', shell=True)\n", + " \n", + " # set training arguments\n", + " training_args = {\n", + " \"learning_rate\": max_lr,\n", + " \"do_train\": True,\n", + " \"do_eval\": True,\n", + " \"evaluation_strategy\": \"epoch\",\n", + " \"save_strategy\": \"epoch\",\n", + " \"logging_steps\": logging_steps,\n", + " \"group_by_length\": True,\n", + " \"length_column_name\": \"length\",\n", + " \"disable_tqdm\": False,\n", + " \"lr_scheduler_type\": lr_schedule_fn,\n", + " \"warmup_steps\": warmup_steps,\n", + " \"weight_decay\": 0.001,\n", + " \"per_device_train_batch_size\": geneformer_batch_size,\n", + " \"per_device_eval_batch_size\": geneformer_batch_size,\n", + " \"num_train_epochs\": epochs,\n", + " \"load_best_model_at_end\": True,\n", + " \"output_dir\": output_dir,\n", + " }\n", + " \n", + " training_args_init = TrainingArguments(**training_args)\n", + "\n", + " # create the trainer\n", + " trainer = Trainer(\n", + " model=model,\n", + " args=training_args_init,\n", + " data_collator=DataCollatorForCellClassification(),\n", + " train_dataset=organ_trainset,\n", + " eval_dataset=organ_evalset,\n", + " compute_metrics=compute_metrics\n", + " )\n", + " # train the cell type classifier\n", + " trainer.train()\n", + " predictions = trainer.predict(organ_evalset)\n", + " with open(f\"{output_dir}predictions.pickle\", \"wb\") as fp:\n", + " pickle.dump(predictions, fp)\n", + " trainer.save_metrics(\"eval\",predictions.metrics)\n", + " trainer.save_model(output_dir)" ] } ], @@ -450,7 +1939,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.15" + "version": "3.10.11" + }, + "vscode": { + "interpreter": { + "hash": "eba1599a1f7e611c14c87ccff6793920aa63510b01fc0e229d6dd014149b8829" + } } }, "nbformat": 4,