{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Simple Neural Network for Multiome predictions\nThis notebook shows how to exploit the Sparse Matrices extracted in [this](https://www.kaggle.com/code/sbunzini/reduce-memory-usage-by-95-with-sparse-matrices) notebook to feed a simple Neural Networks and compute the prediction for the Multiome dataset. In particular, with this notebook we'll go through:\n- A **custom Sequence**: input pipeline to allow the NN to be feed with data coming from the Sparse Matrices.\n- A **Correlation Metric**\n- A **Correlation Loss Function**: inspired by https://www.kaggle.com/code/lucasmorin/msci-citeseq-tf-keras-nn-custom-loss\n- A simple **Neural Network**","metadata":{}},{"cell_type":"markdown","source":"### Install Requirements","metadata":{}},{"cell_type":"code","source":"!pip install --quiet tables","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-08-31T10:28:39.346234Z","iopub.execute_input":"2022-08-31T10:28:39.346639Z","iopub.status.idle":"2022-08-31T10:28:56.304541Z","shell.execute_reply.started":"2022-08-31T10:28:39.346606Z","shell.execute_reply":"2022-08-31T10:28:56.302895Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## Upgrade Tensorflow to the latest version\n# !conda install -c conda-forge cudatoolkit=11.2 cudnn=8.1.0\n!export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CONDA_PREFIX/lib/\n!pip install --upgrade tensorflow","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:28:56.308534Z","iopub.execute_input":"2022-08-31T10:28:56.309109Z","iopub.status.idle":"2022-08-31T10:30:58.599923Z","shell.execute_reply.started":"2022-08-31T10:28:56.309053Z","shell.execute_reply":"2022-08-31T10:30:58.598035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport gc\nfrom tqdm import tqdm\nfrom collections import Counter\nimport scipy.sparse as sps\nimport tensorflow as tf\nimport math\nfrom keras import backend as K\nimport tensorflow_probability as tfp","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:30:58.602461Z","iopub.execute_input":"2022-08-31T10:30:58.603502Z","iopub.status.idle":"2022-08-31T10:31:06.088779Z","shell.execute_reply.started":"2022-08-31T10:30:58.603446Z","shell.execute_reply":"2022-08-31T10:31:06.087387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tf.__version__","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:31:06.093260Z","iopub.execute_input":"2022-08-31T10:31:06.094179Z","iopub.status.idle":"2022-08-31T10:31:06.113198Z","shell.execute_reply.started":"2022-08-31T10:31:06.094143Z","shell.execute_reply":"2022-08-31T10:31:06.111748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"np.random.seed(42)","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:31:06.115147Z","iopub.execute_input":"2022-08-31T10:31:06.116075Z","iopub.status.idle":"2022-08-31T10:31:06.130437Z","shell.execute_reply.started":"2022-08-31T10:31:06.116039Z","shell.execute_reply":"2022-08-31T10:31:06.128768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"VALIDATION = True","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:31:06.132468Z","iopub.execute_input":"2022-08-31T10:31:06.132972Z","iopub.status.idle":"2022-08-31T10:31:06.145497Z","shell.execute_reply.started":"2022-08-31T10:31:06.132937Z","shell.execute_reply":"2022-08-31T10:31:06.144026Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Challenge Data","metadata":{}},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/open-problems-multimodal/\"\nFP_CELL_METADATA = os.path.join(DATA_DIR,\"metadata.csv\")\n\nFP_CITE_TRAIN_INPUTS = os.path.join(DATA_DIR,\"train_cite_inputs.h5\")\nFP_CITE_TRAIN_TARGETS = os.path.join(DATA_DIR,\"train_cite_targets.h5\")\nFP_CITE_TEST_INPUTS = os.path.join(DATA_DIR,\"test_cite_inputs.h5\")\n\nFP_MULTIOME_TRAIN_INPUTS = os.path.join(DATA_DIR,\"train_multi_inputs.h5\")\nFP_MULTIOME_TRAIN_TARGETS = os.path.join(DATA_DIR,\"train_multi_targets.h5\")\nFP_MULTIOME_TEST_INPUTS = os.path.join(DATA_DIR,\"test_multi_inputs.h5\")\n\nFP_SUBMISSION = os.path.join(DATA_DIR,\"sample_submission.csv\")\nFP_EVALUATION_IDS = os.path.join(DATA_DIR,\"evaluation_ids.csv\")","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:31:06.148513Z","iopub.execute_input":"2022-08-31T10:31:06.149630Z","iopub.status.idle":"2022-08-31T10:31:06.162699Z","shell.execute_reply.started":"2022-08-31T10:31:06.149571Z","shell.execute_reply":"2022-08-31T10:31:06.161086Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Sparse Data","metadata":{}},{"cell_type":"code","source":"DATA_DIR_SPARSE = \"/kaggle/input/open-problems-msci-multiome-sparse-matrices/\"\nFP_MULTIOME_TRAIN_INPUTS_SPARSE = os.path.join(DATA_DIR_SPARSE,\"train_multiome_input_sparse.npz\")\nFP_MULTIOME_TRAIN_TARGETS_SPARSE = os.path.join(DATA_DIR_SPARSE,\"train_multi_targets_sparse.npz\")\nFP_MULTIOME_TEST_INPUTS_SPARSE = os.path.join(DATA_DIR_SPARSE,\"test_multi_inputs_sparse.npz\")","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:31:06.165485Z","iopub.execute_input":"2022-08-31T10:31:06.166926Z","iopub.status.idle":"2022-08-31T10:31:06.177155Z","shell.execute_reply.started":"2022-08-31T10:31:06.166826Z","shell.execute_reply":"2022-08-31T10:31:06.175411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_X = sps.load_npz(FP_MULTIOME_TRAIN_INPUTS_SPARSE)\ntrain_y = sps.load_npz(FP_MULTIOME_TRAIN_TARGETS_SPARSE)","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:31:06.180501Z","iopub.execute_input":"2022-08-31T10:31:06.181344Z","iopub.status.idle":"2022-08-31T10:32:57.489052Z","shell.execute_reply.started":"2022-08-31T10:31:06.181294Z","shell.execute_reply":"2022-08-31T10:32:57.487571Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:32:57.496592Z","iopub.execute_input":"2022-08-31T10:32:57.497011Z","iopub.status.idle":"2022-08-31T10:32:57.729290Z","shell.execute_reply.started":"2022-08-31T10:32:57.496977Z","shell.execute_reply":"2022-08-31T10:32:57.727545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Sparse Matrices doesn't work with np.float16 or np.int16 data\n# train_X.data = train_X.data.astype(np.float16)\n# train_X.indices = train_X.indices.astype(np.int16)\n# train_y.data = train_X.data.astype(np.float16)\n# train_y.indices = train_X.indices.astype(np.int16)","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:32:57.732109Z","iopub.execute_input":"2022-08-31T10:32:57.733074Z","iopub.status.idle":"2022-08-31T10:32:57.739634Z","shell.execute_reply.started":"2022-08-31T10:32:57.732958Z","shell.execute_reply":"2022-08-31T10:32:57.737820Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Generator\nThis generator gets the both the input data (X_set) and the targets (y_set) in order to create a sequence of batches in the format required by Tensorflow. In particular, in order to use Tensorflow's SparseTensor, we need to convert the the CSR matrix to a particular COO Format as explained [there](https://www.tensorflow.org/api_docs/python/tf/sparse/SparseTensor). With this generator, we are able to keeps the sparse matrices in memory and convert them only when needed. ","metadata":{}},{"cell_type":"code","source":"class MultiomeSequence(tf.keras.utils.Sequence):\n\n    def __init__(self, x_set, y_set=None, batch_size=64):\n        self.x, self.y = x_set, y_set\n        self.batch_size = batch_size\n\n    def __len__(self):\n        return math.ceil(self.x.shape[0] / self.batch_size)\n\n    def __getitem__(self, idx):\n        \"\"\"\n        Return the idx-th batch\n        \"\"\"\n        batch_x = self.x[idx * self.batch_size:(idx + 1) * self.batch_size]\n        # Convert batch_x to TensorFlow COO Format\n        batch_x = batch_x.tocoo()\n        batch_x.data = batch_x.data.astype(np.float16)\n        pairs = np.column_stack((batch_x.row, batch_x.col)).astype(np.int64)\n        \n        # Convert batch_y to dense\n        if self.y is None:\n            batch_y = None\n        else:\n            batch_y = self.y[idx * self.batch_size:(idx + 1) * self.batch_size].toarray()\n\n        return tf.SparseTensor(indices=pairs, values=batch_x.data, dense_shape=batch_x.shape), batch_y","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:32:57.742202Z","iopub.execute_input":"2022-08-31T10:32:57.742767Z","iopub.status.idle":"2022-08-31T10:32:57.759284Z","shell.execute_reply.started":"2022-08-31T10:32:57.742686Z","shell.execute_reply":"2022-08-31T10:32:57.757559Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Custom Objective and Metric","metadata":{}},{"cell_type":"code","source":"lam = 0.03\n\nclass CorrelationLoss(tf.keras.losses.Loss):\n    \n    def call(self, y_true, y_pred):\n        y_true = tf.reshape(y_true, [-1])\n        y_pred = tf.reshape(y_pred, [-1])\n        return 1 - tfp.stats.correlation(y_true, y_pred, event_axis=None) + lam * tf.keras.losses.MeanSquaredError()(y_true,y_pred)\n\n    \nclass CorrelationMetric(tf.keras.metrics.Mean):\n\n    def __init__(self, name='correlation_metric', **kwargs):\n        super(CorrelationMetric, self).__init__(name=name, **kwargs)\n        # self.correlation = self.add_weight(name='corr', initializer='zeros')\n\n    def update_state(self, y_true, y_pred, **kwargs):\n        y_true = tf.reshape(y_true, [-1])\n        y_pred = tf.reshape(y_pred, [-1])\n        corr = tfp.stats.correlation(y_true, y_pred, event_axis=None)\n        super().update_state(corr, **kwargs)\n        # self.correlation.assign_add(corr)\n\n    # def result(self):\n    #     return self.correlation","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:32:57.762137Z","iopub.execute_input":"2022-08-31T10:32:57.763489Z","iopub.status.idle":"2022-08-31T10:32:57.782800Z","shell.execute_reply.started":"2022-08-31T10:32:57.763439Z","shell.execute_reply":"2022-08-31T10:32:57.780944Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"N_ROWS = train_X.shape[0]\nN_COLS = train_X.shape[1]\nN_TARGETS = train_y.shape[1]\nnoise = 0.1\n\nhidden_units = (256,128,64, 128, 256)\n\ndef base_model():\n    inp = tf.keras.Input(shape=(N_COLS), sparse=True)\n    out = tf.keras.layers.Dense(64, activation='selu')(inp)\n    out = tf.keras.layers.BatchNormalization()(out)\n    out = tf.keras.layers.GaussianNoise(noise)(out)\n    \n    for n_hidden in hidden_units:\n        out = tf.keras.layers.Dense(n_hidden, activation='selu')(out)\n        out = tf.keras.layers.BatchNormalization()(out)\n        out = tf.keras.layers.GaussianNoise(noise)(out)\n\n    # It is possible to use sparseTensor in output: https://www.tensorflow.org/guide/sparse_tensor#tfkeras\n    out = tf.keras.layers.Dense(N_TARGETS, activation='selu', name='prediction')(out)\n    \n    model = tf.keras.Model(\n        inputs = inp,\n        outputs = out\n    )\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:32:57.785166Z","iopub.execute_input":"2022-08-31T10:32:57.786170Z","iopub.status.idle":"2022-08-31T10:32:57.802592Z","shell.execute_reply.started":"2022-08-31T10:32:57.786121Z","shell.execute_reply":"2022-08-31T10:32:57.800936Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:32:57.804756Z","iopub.execute_input":"2022-08-31T10:32:57.805551Z","iopub.status.idle":"2022-08-31T10:32:58.034419Z","shell.execute_reply.started":"2022-08-31T10:32:57.805497Z","shell.execute_reply":"2022-08-31T10:32:58.032590Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train/Validation Split","metadata":{}},{"cell_type":"code","source":"if VALIDATION:\n    idxs = np.arange(train_X.shape[0], dtype=np.int32)\n    val_idx = np.random.choice(idxs, size=10_000, replace=False)\n    mask_idxs = np.ones(idxs.shape[0], dtype=np.int32)\n    mask_idxs[val_idx] = 0\n    mask_idxs = mask_idxs.astype(bool)\n    train_idx = idxs[mask_idxs]\n\n    del idxs, mask_idxs\n    \n    gc.collect()\n    \n    train_size = 50_000\n    val_size = 5_000\n    \n    val_X, val_y = train_X[val_idx[:val_size]], train_y[val_idx[:val_size]]\n    train_X, train_y = train_X[train_idx[:train_size]], train_y[train_idx[:train_size]]\n    \n    gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:32:58.037514Z","iopub.execute_input":"2022-08-31T10:32:58.038109Z","iopub.status.idle":"2022-08-31T10:33:07.614350Z","shell.execute_reply.started":"2022-08-31T10:32:58.038061Z","shell.execute_reply":"2022-08-31T10:33:07.612917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:33:07.616579Z","iopub.execute_input":"2022-08-31T10:33:07.617090Z","iopub.status.idle":"2022-08-31T10:33:07.840398Z","shell.execute_reply.started":"2022-08-31T10:33:07.617036Z","shell.execute_reply":"2022-08-31T10:33:07.838883Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"es = tf.keras.callbacks.EarlyStopping(\n    monitor='val_correlation_metric', min_delta=1e-05, patience=7, verbose=1,\n    mode='max')\n\nplateau = tf.keras.callbacks.ReduceLROnPlateau(\n    monitor='val_correlation_metric', factor=0.2, patience=3, verbose=1,\n    mode='max')","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:33:07.842213Z","iopub.execute_input":"2022-08-31T10:33:07.843731Z","iopub.status.idle":"2022-08-31T10:33:07.856278Z","shell.execute_reply.started":"2022-08-31T10:33:07.843697Z","shell.execute_reply":"2022-08-31T10:33:07.854565Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = base_model()\nmodel.compile(\n    tf.keras.optimizers.Adam(learning_rate=1e-4),\n    loss = CorrelationLoss(), # tf.keras.losses.MeanSquaredError(), # correlation_loss,\n    metrics =  CorrelationMetric(),# tf.keras.metrics.MeanSquaredError() # correlation_metric,\n)","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:33:07.858776Z","iopub.execute_input":"2022-08-31T10:33:07.859907Z","iopub.status.idle":"2022-08-31T10:33:11.980098Z","shell.execute_reply.started":"2022-08-31T10:33:07.859840Z","shell.execute_reply":"2022-08-31T10:33:11.978693Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.summary()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:33:11.982003Z","iopub.execute_input":"2022-08-31T10:33:11.982463Z","iopub.status.idle":"2022-08-31T10:33:12.049047Z","shell.execute_reply.started":"2022-08-31T10:33:11.982416Z","shell.execute_reply":"2022-08-31T10:33:12.047974Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:33:12.050415Z","iopub.execute_input":"2022-08-31T10:33:12.051320Z","iopub.status.idle":"2022-08-31T10:33:12.332196Z","shell.execute_reply.started":"2022-08-31T10:33:12.051272Z","shell.execute_reply":"2022-08-31T10:33:12.330800Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"training_generator = MultiomeSequence(train_X, train_y, batch_size=64)\nif VALIDATION:\n    validation_generator = MultiomeSequence(val_X, val_y, batch_size=64)","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:33:12.334235Z","iopub.execute_input":"2022-08-31T10:33:12.334681Z","iopub.status.idle":"2022-08-31T10:33:12.343368Z","shell.execute_reply.started":"2022-08-31T10:33:12.334637Z","shell.execute_reply":"2022-08-31T10:33:12.341757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:33:12.362118Z","iopub.execute_input":"2022-08-31T10:33:12.362506Z","iopub.status.idle":"2022-08-31T10:33:12.598432Z","shell.execute_reply.started":"2022-08-31T10:33:12.362477Z","shell.execute_reply":"2022-08-31T10:33:12.596776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 1\nif VALIDATION:\n    model.fit(\n        x=training_generator,\n        batch_size=64,\n        epochs=epochs,\n        validation_data=validation_generator,\n        callbacks=[es, plateau],\n        shuffle=True,\n        verbose = 1\n    )\n    del train_X, train_y, training_generator, val_X, val_y, train_idx, val_idx, validation_generator\nelse:\n    model.fit(\n        x=training_generator,\n        batch_size=64,\n        epochs=epochs,\n        callbacks=[es, plateau],\n        shuffle=True,\n        verbose = 1\n    )\n    \n    del train_X, train_y, training_generator","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:33:12.600244Z","iopub.execute_input":"2022-08-31T10:33:12.601791Z","iopub.status.idle":"2022-08-31T10:44:27.611913Z","shell.execute_reply.started":"2022-08-31T10:33:12.601742Z","shell.execute_reply":"2022-08-31T10:44:27.610368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T10:44:28.189452Z","iopub.status.idle":"2022-08-31T10:44:28.190496Z","shell.execute_reply.started":"2022-08-31T10:44:28.190116Z","shell.execute_reply":"2022-08-31T10:44:28.190150Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.save(f'model_multi')","metadata":{"execution":{"iopub.status.busy":"2022-08-31T08:29:15.447261Z","iopub.execute_input":"2022-08-31T08:29:15.447629Z","iopub.status.idle":"2022-08-31T08:29:18.751624Z","shell.execute_reply.started":"2022-08-31T08:29:15.447598Z","shell.execute_reply":"2022-08-31T08:29:18.750622Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model = tf.keras.models.load_model('../input/modelmulti/model_multi', compile=False)\nmodel = tf.keras.models.load_model('model_multi', compile=False)","metadata":{"execution":{"iopub.status.busy":"2022-08-31T08:33:13.037405Z","iopub.execute_input":"2022-08-31T08:33:13.038300Z","iopub.status.idle":"2022-08-31T08:33:17.467906Z","shell.execute_reply.started":"2022-08-31T08:33:13.038255Z","shell.execute_reply":"2022-08-31T08:33:17.466856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-08-31T08:33:17.472904Z","iopub.execute_input":"2022-08-31T08:33:17.473236Z","iopub.status.idle":"2022-08-31T08:33:17.724739Z","shell.execute_reply.started":"2022-08-31T08:33:17.473205Z","shell.execute_reply":"2022-08-31T08:33:17.723452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prediction\nCreate predictions for FP_MULTIOME_TEST_INPUTS_SPARSE","metadata":{}},{"cell_type":"code","source":"if not VALIDATION:\n    multiome_eval_df = pd.read_csv(\"../input/useful-data-processing/multiome_eval_ids_converted.csv\")\n    index_order = np.load('../input/useful-data-processing/correct_index_order_test.npy')\n    \n    groups = multiome_eval_df.groupby('cell_id')['gene_id'].apply(lambda x: list(x))\n    del multiome_eval_df\n    gc.collect()\n    \n    test_X = sps.load_npz(FP_MULTIOME_TEST_INPUTS_SPARSE)[index_order]\n    test_generator = MultiomeSequence(test_X, None, batch_size=64)\n    del index_order\n    gc.collect()\n    \n    # Predict all the columns for each cell_id\n    predictions = None\n    for idx in tqdm(range(test_generator.__len__())):\n        batch = test_generator.__getitem__(idx)\n        pred = model.predict_on_batch(batch[0]).astype(np.float16)\n        if predictions is None:\n            predictions = pred\n        else:\n            predictions = np.vstack((predictions, pred))\n            \n    del test_generator, test_X, model\n    gc.collect()\n    \n    # For each cell_id, extract only the predicted values required and flat following the order of eval_id[6812820:].\n    # 6812820 specifies the index in evaluation_ids.csv from which predictions for Multiome starts\n    final_predictions = np.array([], dtype=np.float16)\n    for i, cols in enumerate(tqdm(groups)):\n        # cols = list(map(int, cols.split(',')))\n        final_predictions = np.hstack((\n            final_predictions,\n            predictions[i, cols]\n        ))\n        \n    np.save('predictions_multi.npy', final_predictions)\n    \n    del predictions, final_predictions\n    \n    gc.collect()","metadata":{},"execution_count":null,"outputs":[]}]}