{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Imports and Utilities"},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport gc\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom functools import partial\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\nfrom IPython.display import clear_output\n\n%matplotlib inline\n\nimport librosa\nfrom librosa import feature as lf\nfrom kaggle_datasets import KaggleDatasets\nfrom sklearn.model_selection import train_test_split, KFold, StratifiedKFold\nfrom sklearn.metrics import label_ranking_average_precision_score, label_ranking_loss\n\nimport tensorflow as tf\nimport tensorflow_addons as tfa\nimport tensorflow.keras.layers as L\nimport tensorflow.keras.backend as K\nfrom tensorflow.keras import Model\nfrom tensorflow.keras.metrics import Metric\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.callbacks import Callback, ModelCheckpoint\n\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Default Configurations for Hyperparameters/constants"},{"metadata":{"trusted":true},"cell_type":"code","source":"AUTOTUNE = tf.data.experimental.AUTOTUNE\nGCS_DS_PATH = KaggleDatasets().get_gcs_path('rfcx-species-audio-detection')\n\nTRAIN_TFREC = GCS_DS_PATH + \"/tfrecords/train\"\nTEST_TFREC = GCS_DS_PATH + \"/tfrecords/test\"\n\nclass cfg:\n    parse_params = {\n        'cut_time': 10,\n    }\n    data_params = {\n        'sampling_rate': 48000, # twice of fmax, following nyquist rate\n        'sample_time': 6,  # assert 60 % sample_time == 0\n        'spec_fmax': 24000.0,\n        'spec_fmin': 40.0,\n        'spec_mel': 384,\n        'mel_power': 2,\n        'img_shape': (384, 768)\n    }\n    model_params = {\n        \"batch_size\": 16,\n        \"iteration_per_epoch\": 64,\n        \"epochs\": 50,\n        \"model_arch\": tf.keras.applications.ResNet50,\n        \"preprocess\": tf.keras.applications.resnet50.preprocess_input,\n        \"freeze\": 0,\n        \"loss\": {\n            \"fn\": tfa.losses.SigmoidFocalCrossEntropy,\n            \"params\": {}\n        },\n        \"optim\": {\n            \"fn\": tfa.optimizers.RectifiedAdam,\n            \"params\": {\n                \"lr\": 1e-3,\n                \"total_steps\": 50 * 64,  # epochs * iterations/epoch\n                \"warmup_proportion\": 0.3,\n                \"min_lr\": 1e-6\n            }\n        },\n        \"mixup\": False\n    }\n    # feature description for the tfrecords\n    # this will parsed as arguments into tf.io.parse_single_example\n    feature_description = {\n        'recording_id': tf.io.FixedLenFeature([], tf.string, default_value=''),\n        'audio_wav': tf.io.FixedLenFeature([], tf.string, default_value=''),\n        'label_info': tf.io.FixedLenFeature([], tf.string, default_value=''),\n    }\n    parse_dtype = {\n        'audio_wav': tf.float32,\n        'recording_id': tf.string,\n        'species_id': tf.int32,\n        'songtype_id': tf.int32,\n        't_min': tf.float32,\n        'f_min': tf.float32,\n        't_max': tf.float32,\n        'f_max': tf.float32,\n        'is_tp': tf.int32\n    }\n    CLASSES = 24\n    pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#train_tfrecs = sorted(tf.io.gfile.glob(TRAIN_TFREC + '/*.tfrec'))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Create Dataset"},{"metadata":{},"cell_type":"markdown","source":"## Train + Validation"},{"metadata":{"trusted":true},"cell_type":"code","source":"# parse the tfrecords\n@tf.function\ndef parse_fun(sample):\n    sample = tf.io.parse_single_example(sample, cfg.feature_description) # this returns a dicionary of the features for a single tfrec\n    audio, _ = tf.audio.decode_wav(sample[\"audio_wav\"], desired_channels=1)\n    label_info = tf.strings.split(sample['label_info'], sep='\"')[1]\n    labels = tf.strings.split(label_info, sep=';')\n    \n    @tf.function\n    def _cut_audio(label):\n        items = tf.strings.split(label, sep=',')\n        spid = tf.squeeze(tf.strings.to_number(items[0], tf.int32))\n        soid = tf.squeeze(tf.strings.to_number(items[1], tf.int32))\n        tmin = tf.squeeze(tf.strings.to_number(items[2]))\n        fmin = tf.squeeze(tf.strings.to_number(items[3]))\n        tmax = tf.squeeze(tf.strings.to_number(items[4]))\n        fmax = tf.squeeze(tf.strings.to_number(items[5]))\n        tp = tf.squeeze(tf.strings.to_number(items[6], tf.int32))\n\n        tmax_s = tmax * tf.cast(cfg.data_params[\"sampling_rate\"], tf.float32)\n        tmin_s = tmin * tf.cast(cfg.data_params[\"sampling_rate\"], tf.float32)\n        cut_s = tf.cast(cfg.parse_params[\"cut_time\"] * cfg.data_params[\"sampling_rate\"], tf.float32)\n        all_s = tf.cast(60 * cfg.data_params[\"sampling_rate\"], tf.float32)\n        tsize_s = tmax_s - tmin_s\n        cut_min = tf.cast(\n            tf.maximum(0.0, \n                tf.minimum(tmin_s - (cut_s - tsize_s) / 2,\n                           tf.minimum(tmax_s + (cut_s - tsize_s) / 2,\n                                      all_s) - cut_s)\n            ), tf.int32\n        )\n        cut_max = cut_min + cfg.parse_params[\"cut_time\"] * cfg.data_params[\"sampling_rate\"]\n        \n        _sample = {\n            'audio_wav': tf.reshape(audio[cut_min:cut_max], [cfg.parse_params[\"cut_time\"]*cfg.data_params[\"sampling_rate\"]]),\n            'recording_id': sample['recording_id'],\n            'species_id': spid,\n            'songtype_id': soid,\n            't_min': tmin - tf.cast(cut_min, tf.float32)/tf.cast(cfg.data_params[\"sampling_rate\"], tf.float32),\n            'f_min': fmin,\n            't_max': tmax - tf.cast(cut_min, tf.float32)/tf.cast(cfg.data_params[\"sampling_rate\"], tf.float32),\n            'f_max': fmax,\n            'is_tp': tp\n        }\n        return _sample\n    \n    samples = tf.map_fn(_cut_audio, labels, dtype=cfg.parse_dtype)\n    return samples\n    pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef filter_tp(sample):\n    \"\"\"\n\n    :param sample: Processed dictionary from _parse_function\n    :return: boolean, whether belongs to true positive or false positive\n    \"\"\"\n    return sample[\"is_tp\"] == 1\n    pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#fil_truePos = True\n\n#tfrds = tf.data.TFRecordDataset(train_tfrecs, num_parallel_reads=AUTOTUNE)\n#parsed_trainval = tfrds.map(parse_fun, num_parallel_calls=AUTOTUNE).unbatch()\n\n#if fil_truePos:\n#    parsed_trainval = parsed_trainval.filter(filter_tp)\n    \n# enumerate samples for ease in splitting\n#parsed_trainval = parsed_trainval.enumerate()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef cut_audio(sample, istrain=True):\n    # random cutting for train samples\n    if istrain:\n        cut_min = tf.random.uniform([],\n                                    maxval=(cfg.parse_params[\"cut_time\"]-cfg.data_params[\"sample_time\"]) * cfg.data_params[\"sampling_rate\"],\n                                    dtype=tf.int32)\n    else:\n        # center cropping for validation data\n        cut_min = (cfg.parse_params[\"cut_time\"] - cfg.data_params[\"sample_time\"]) * cfg.data_params[\"sampling_rate\"]//2\n    \n    cut_max = cut_min + cfg.data_params[\"sample_time\"] * cfg.data_params[\"sampling_rate\"]\n    cutaudio = tf.reshape(\n        sample[\"audio_wav\"][cut_min:cut_max], [cfg.data_params[\"sample_time\"] * cfg.data_params[\"sampling_rate\"]]\n    )\n\n    result = {}\n    result.update(sample)\n    result[\"audio_wav\"] = cutaudio\n    result[\"t_min\"] = tf.maximum(0.0, sample[\"t_min\"] - tf.cast(cut_min, tf.float32)/cfg.data_params[\"sampling_rate\"])\n    result[\"t_max\"] = tf.maximum(0.0, sample[\"t_max\"] - tf.cast(cut_min, tf.float32)/cfg.data_params[\"sampling_rate\"])\n    return result\n    pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef waveToSpec(sample):\n    mel_power = cfg.data_params[\"mel_power\"]\n    stfts = tf.signal.stft(sample[\"audio_wav\"],\n                           frame_length=2048,\n                           frame_step=512,\n                           fft_length=2048)\n    spectograms = tf.abs(stfts) ** mel_power\n\n    # convert into mel scale\n    mel_weight = tf.signal.linear_to_mel_weight_matrix(\n        num_mel_bins=cfg.data_params[\"spec_mel\"],  # or can be said as the MFCCs, though theoretically, ideal value should be in 30-50 range, let's try with 224 for image size\n        num_spectrogram_bins=stfts.shape[-1],\n        sample_rate=cfg.data_params[\"sampling_rate\"],\n        lower_edge_hertz=cfg.data_params[\"spec_fmin\"],\n        upper_edge_hertz=cfg.data_params[\"spec_fmax\"]\n    )\n    mel_spectrograms = tf.tensordot(\n        spectograms, mel_weight, 1\n    )\n    mel_spectrograms.set_shape(spectograms.shape[:-1].concatenate(mel_weight.shape[-1:]))\n    log_mel_spectograms = tf.math.log(mel_spectrograms + 1e-6)\n\n    results = {\n        \"audio_spec\": tf.transpose(log_mel_spectograms)  # of shape (num_mel_spec_bins, num_frames)\n    }\n    results.update(sample)\n    return results\n    pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef create_annot(sample):\n    target = tf.one_hot(sample[\"species_id\"],\n                        24,\n                        on_value=sample[\"is_tp\"],\n                        off_value=0)\n\n    return {\n        \"input\": sample[\"audio_spec\"],  # obtained from creating spectograms from audio np arrays\n        \"target\": tf.cast(target, tf.float32)\n    }\n    pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef toImage(logmelSpec):\n    # expand one dimension axis to be treated as image\n    image = tf.expand_dims(logmelSpec, axis=-1)\n    image = tf.image.resize(image, cfg.data_params[\"img_shape\"])\n    image = tf.image.per_image_standardization(image)\n\n    # no augmentation at this stage\n    image = (image - tf.reduce_mean(image))/(tf.reduce_max(image) * tf.reduce_min(image)) * 255.0\n    image = tf.image.grayscale_to_rgb(image)\n    # image = cfg.model_params[\"preprocess\"](image)\n    return image\n    pass\n\n\n@tf.function\ndef preprocess_img(sample):\n    image = toImage(sample[\"input\"])\n    return image, sample[\"target\"]\n    pass","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_idx_filter(idx):\n    @tf.function\n    def _filt(i, x):\n        return tf.reduce_any(idx == i)\n    return _filt\n\n@tf.function\ndef _remove_idx(i, sample):\n    return sample","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_dataset(batchsize, idxs, istrain=True):\n    global parsed_trainval\n    parsed_data = (parsed_trainval\n                    .filter(create_idx_filter(idxs))\n                    .map(_remove_idx))\n    \n    if istrain:\n        dataset = (parsed_data.cache()\n                   .shuffle(len(idxs))\n                   .repeat())\n    \n    dataset = parsed_data.map(partial(cut_audio, istrain=istrain), num_parallel_calls=AUTOTUNE).map(waveToSpec, num_parallel_calls=AUTOTUNE).map(create_annot, num_parallel_calls=AUTOTUNE).map(preprocess_img, num_parallel_calls=AUTOTUNE)\n        \n    if istrain:\n        dataset = dataset.batch(batchsize)\n        if cfg.model_params['mixup']:\n            dataset = (dataset.map(_mixup, num_parallel_calls=AUTOTUNE)\n                        .prefetch(AUTOTUNE))\n        else:\n            dataset = dataset.prefetch(AUTOTUNE)\n        return dataset\n    else:\n        dataset = dataset.batch(8).cache()\n        return dataset\n    pass","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Test"},{"metadata":{"trusted":true},"cell_type":"code","source":"def _parse_function_test(sample):\n    sample = tf.io.parse_single_example(sample, cfg.feature_description)\n    audio, _ = tf.audio.decode_wav(sample['audio_wav'], desired_channels=1) # mono\n    \n    @tf.function\n    def _cut_audio(i):\n        _sample = {\n            'audio_wav': tf.reshape(wav[i*cfg.data_params[\"sampling_rate\"]*cfg.data_params[\"sample_time\"]:\n                                        (i+1)*cfg.data_params[\"sampling_rate\"]*cfg.data_params[\"sample_time\"]],\n                                    [cfg.data_params[\"sampling_rate\"]*cfg.data_params[\"sample_time\"]]),\n            'recording_id': sample['recording_id']\n        }\n        return _sample\n\n    return tf.map_fn(_cut_audio, tf.range(60//TIME), dtype={\n        'audio_wav': tf.float32,\n        'recording_id': tf.string\n    })","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Cross Validation"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"#%%time\n#\n#indices = []\n#spid = []\n#recid = []\n\n#for i, sample in tqdm(parsed_trainval.prefetch(AUTOTUNE), total=1216):\n#    indices.append(i.numpy())\n#    spid.append(sample['species_id'].numpy())\n#    recid.append(sample['recording_id'].numpy().decode())\n#\n#table = pd.DataFrame({'indices': indices, 'recording_id': recid, 'species_id': spid})\n#display(table)\n#skf = StratifiedKFold(n_splits=5, random_state=42, shuffle=True)\n#splits = list(skf.split(table.index, table.species_id))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Metrics"},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef _one_sample_positive_class_precisions(example):\n    y_true, y_pred = example\n\n    retrieved_classes = tf.argsort(y_pred, direction='DESCENDING')\n    class_rankings = tf.argsort(retrieved_classes)\n    retrieved_class_true = tf.gather(y_true, retrieved_classes)\n    retrieved_cumulative_hits = tf.math.cumsum(tf.cast(retrieved_class_true, tf.float32))\n\n    idx = tf.where(y_true)[:, 0]\n    i = tf.boolean_mask(class_rankings, y_true)\n    r = tf.gather(retrieved_cumulative_hits, i)\n    c = 1 + tf.cast(i, tf.float32)\n    precisions = r / c\n\n    dense = tf.scatter_nd(idx[:, None], precisions, [y_pred.shape[0]])\n    return dense\n\nclass LWLRAP(tf.keras.metrics.Metric):\n    def __init__(self, num_classes, name='lwlrap'):\n        super().__init__(name=name)\n\n        self._precisions = self.add_weight(\n            name='per_class_cumulative_precision',\n            shape=[num_classes],\n            initializer='zeros',\n        )\n\n        self._counts = self.add_weight(\n            name='per_class_cumulative_count',\n            shape=[num_classes],\n            initializer='zeros',\n        )\n\n    def update_state(self, y_true, y_pred, sample_weight=None):\n        precisions = tf.map_fn(\n            fn=_one_sample_positive_class_precisions,\n            elems=(y_true, y_pred),\n            dtype=(tf.float32),\n        )\n\n        increments = tf.cast(precisions > 0, tf.float32)\n        total_increments = tf.reduce_sum(increments, axis=0)\n        total_precisions = tf.reduce_sum(precisions, axis=0)\n\n        self._precisions.assign_add(total_precisions)\n        self._counts.assign_add(total_increments)        \n\n    def result(self):\n        per_class_lwlrap = self._precisions / tf.maximum(self._counts, 1.0)\n        per_class_weight = self._counts / tf.reduce_sum(self._counts)\n        overall_lwlrap = tf.reduce_sum(per_class_lwlrap * per_class_weight)\n        return overall_lwlrap\n\n    def reset_states(self):\n        self._precisions.assign(self._precisions * 0)\n        self._counts.assign(self._counts * 0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Callbacks"},{"metadata":{"trusted":true},"cell_type":"code","source":"class MonitorModel(Callback):\n    def on_train_begin(self,logs={}):\n        self.losses = []\n        self.val_losses =[]\n        self.accuracy = []\n        self.val_accuracy =[]\n\n    def on_epoch_end(self,epoch,logs={}):\n        clear_output(wait=True)\n        self.val_losses.append(logs.get('val_loss'))\n        self.losses.append(logs.get('loss'))\n\n        self.val_accuracy.append(logs.get('val_lwlrap'))\n        self.accuracy.append(logs.get('lwlrap'))\n\n        plt.figure(figsize=(10,5))\n        plt.subplot(1,2,1)\n        plt.plot(self.val_losses,color=\"green\",label=\"val_loss\")\n        plt.plot(self.losses,color=\"red\",label=\"loss\")\n        plt.legend()\n        plt.title(\"loss curve\");\n\n        plt.subplot(1,2,2)\n        plt.plot(self.val_accuracy,color=\"green\",label=\"val_lwlrap\")\n        plt.plot(self.accuracy,color=\"red\",label=\"lwlrap\")\n        plt.legend()\n        plt.title(\"LWLRAP curve\");\n        plt.tight_layout()\n        plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_model_resnet50():\n    \"\"\"\n    Create a resnet 50 model with added FC layers\n    Experimental: Not implemented for TPU\n    :return: tf.keras.Model-like model\n    \"\"\"\n    # load the backbone without pretrained imagenet weights\n    backbone = cfg.model_params[\"model_arch\"](include_top=False, weights=\"imagenet\")\n\n    # freeze the layers if loaded trainable weights or upto certain layers\n    if not cfg.model_params[\"freeze\"]:\n        for layer in backbone.layers:\n            layer.trainable = False\n\n    else:\n        # freeze freeze all the layers except given last layers\n        for layer in backbone.layers[: cfg.model_params[\"freeze\"]]:\n            layer.trainable = False\n        pass\n\n    # add fc layers with global average pooling\n    model = Sequential([\n        backbone,\n        L.GlobalAveragePooling2D(),\n        L.BatchNormalization(),\n        L.Dropout(0.3),\n        L.Dense(1024, activation=\"relu\"),\n        L.BatchNormalization(),\n        L.Dropout(0.4),\n        L.Dense(cfg.CLASSES, bias_initializer=tf.keras.initializers.Constant(-2.))\n    ])\n\n    return model\n    pass","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_and_inference(splits, split_id):\n    batchsize = cfg.model_params['batch_size']\n    print(\"batchsize\", batchsize)\n    loss_fn = cfg.model_params['loss']['fn'](from_logits=True, **cfg.model_params['loss']['params'])\n\n    idx_train_tf = tf.constant(splits[split_id][0])\n    idx_val_tf = tf.constant(splits[split_id][1])\n\n    dataset = create_dataset(batchsize, idx_train_tf)\n    vdataset = create_dataset(batchsize, idx_val_tf, istrain=False)\n    \n    optimizer = cfg.model_params['optim']['fn'](**cfg.model_params['optim']['params'])\n    \n\n    model = create_model_resnet50()\n    model.compile(optimizer=optimizer, loss=loss_fn, metrics=[LWLRAP(cfg.CLASSES)])\n        \n    history = model.fit(dataset,\n                        steps_per_epoch=cfg.model_params['iteration_per_epoch'],\n                        epochs=cfg.model_params['epochs'],\n                        validation_data=vdataset,\n                        callbacks=[\n                            tf.keras.callbacks.ReduceLROnPlateau(\n                                'val_lwlrap', patience=10\n                            ),\n                            tf.keras.callbacks.ModelCheckpoint(\n                                filepath='model_best_%d.h5' % split_id,\n                                save_weights_only=True,\n                                monitor='val_lwlrap',\n                                mode='max',\n                                save_best_only=True),\n                        ])\n    \n    ### inference ###\n    model.load_weights('model_best_%d.h5' % split_id)\n    return inference(model), history","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"def inference(model):\n    tdataset = (tf.data.TFRecordDataset(tf.io.gfile.glob(TEST_TFREC + '/*.tfrec'), num_parallel_reads=AUTOTUNE)\n        .map(_parse_function_test, num_parallel_calls=AUTOTUNE).unbatch()\n        .map(_wav_to_spec, num_parallel_calls=AUTOTUNE)\n        .map(_preprocess_test, num_parallel_calls=AUTOTUNE)\n        .batch(128*(60//TIME)).prefetch(AUTOTUNE))\n    \n    rec_ids = []\n    probs = []\n    for inp, rec_id in tqdm(tdataset):\n        pred = model.predict_on_batch(tf.reshape(inp, [-1, 384, 786, 3]))\n        prob = tf.sigmoid(pred)\n        prob = tf.reduce_max(tf.reshape(prob, [-1, 60//6, 24]), axis=1)\n\n        rec_id_stack = tf.reshape(rec_id, [-1, 60//6])\n        for rec in rec_id.numpy():\n            assert len(np.unique(rec)) == 1\n        rec_ids.append(rec_id_stack.numpy()[:,0])\n        probs.append(prob.numpy())\n        \n    crec_ids = np.concatenate(rec_ids)\n    cprobs = np.concatenate(probs)\n    \n    sub = pd.DataFrame({\n        'recording_id': list(map(lambda x: x.decode(), crec_ids.tolist())),\n        **{f's{i}': cprobs[:,i] for i in range(24)}\n    })\n    sub = sub.sort_values('recording_id')\n    return sub","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# N-fold ensemble\n#sub = sum(\n#    map(\n#        lambda i: train_and_inference(splits, i)[0].set_index('recording_id'),\n#        range(len(splits))\n#    )\n#).reset_index()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}