{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport random\nfrom datetime import datetime\nimport time\n\nimport numpy as np\nimport pandas as pd\nimport librosa\nfrom sklearn.model_selection import StratifiedKFold, train_test_split\nfrom sklearn.base import BaseEstimator\nimport tensorflow as tf\nfrom tensorflow import keras as keras\nfrom tensorflow.keras import backend as K\nimport tensorflow_addons as tfa\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install image-classifiers","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from classification_models.keras import Classifiers\n\nResNet34, preprocess_resnet34 = Classifiers.get('resnet34')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"BASE_INPUT_DIR = '/kaggle/input/'\nTEST_INPUT_DIR = os.path.join(BASE_INPUT_DIR, 'rfcx-species-audio-detection/test')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv(os.path.join(BASE_INPUT_DIR, 'rfcx-species-audio-detection/sample_submission.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef _one_sample_positive_class_precisions(example):\n    y_true, y_pred = example\n    y_true = tf.cast(y_true > 0, dtype=tf.int32)\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\n\nclass LWLRAP(tf.keras.metrics.Metric):\n\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":{"trusted":true},"cell_type":"code","source":"def get_lsep_loss(num_classes, weights_list=None):\n    if weights_list is None:\n        weights_list = np.ones(num_classes, dtype=np.float32)\n    \n    @tf.function\n    def lsep_loss(y_true, y_pred):\n        batch_size = tf.math.floordiv(K.sum(K.exp(y_true - y_true)), num_classes)\n        \n        y_t = K.reshape(y_true, (batch_size, num_classes))\n        y_p = K.reshape(y_pred, (batch_size, num_classes))\n        M_unit = tf.ones((batch_size, num_classes)) \n        M1 = (M_unit - y_t) * K.reshape(K.tile(weights_list, [batch_size]), (batch_size, num_classes))\n       \n        M_pairwise = tf.einsum('ij,ik->ijk', M1, y_t)    # shape = (batch_size, num_classes, num_classes)\n        M_large = tf.einsum('ij,ik->ijk', M_unit, y_p)  # shape = (batch_size, num_classes, num_classes)\n\n        M_diff = K.exp(K.permute_dimensions(M_large, (0, 2, 1)) - M_large)  # shape = (batch_size, num_classes, num_classes)\n        M = M_pairwise * M_diff  # shape = (batch_size, num_classes, num_classes)\n\n        return K.mean(K.log(1 + K.sum(K.sum(M, 2), 1)))\n    \n    return lsep_loss","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Constants & Configs"},{"metadata":{"trusted":true},"cell_type":"code","source":"NUM_SPECIES = 24\n\nIMG_SIZE = (224, 512, 3)\nIMG_HEIGHT = IMG_SIZE[0]\nIMG_WIDTH = IMG_SIZE[1]\n\nFMIN = 40.0\nFMAX = 24000.0\n\nSR = 48000\nN_MELS = 224\nN_FRAMES = 559\n\nCLIP_DURATION = 60\nSEGMENT_DURATION = 10","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"AUTOTUNE = tf.data.experimental.AUTOTUNE\n\ncfg = {\n    'preprocess': {\n        'frame_size': 2048,\n        'hop_length': 512,\n        'sub_segment_duration': 6,\n\n        'spec_aug_prob': 0.7,\n        'spec_aug_erase_time': 50,\n        'spec_aug_erase_mel': 16,\n        'spec_aug_num_time_cuts': 2,\n        'spec_aug_num_freq_cuts': 4,\n        \n        'gauss_noise_prob': 0.7,\n        'gauss_noise_std': 0.5,\n        \n        'random_brightness': 0.3,\n        \n        'do_mixup': True,\n        'mixup_alpha': 0.2,\n    },\n    'training': {\n        'use_tpu': False,\n        'n_folds': 5,\n        'batch_size': 32,\n        'shuffle_buffer_size': 2000,\n        'steps_per_epoch': 64,\n\n        'frozen_learning_rate': 1e-3,\n        'num_unfreeze_layers': None,\n\n        'learning_rate': 1e-4,\n        'min_lr': 1e-7,\n    },\n    'model': {\n        'backbone_arch': 'resnet34',  # effnet_b0, effnet_b1, resnet50, resnet34\n        'backbone_preprocess': preprocess_resnet34,\n    },\n    'inference': {\n        'segment_stride': 6\n    }\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tpu_strategy = None\n\nif cfg['training']['use_tpu']:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    print(\"All devices: \", tf.config.list_logical_devices('TPU'))\n    \n    tpu_strategy = tf.distribute.experimental.TPUStrategy(tpu)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Preprocessing"},{"metadata":{"trusted":true},"cell_type":"code","source":"feature_description = {\n    'idx': tf.io.FixedLenFeature([], tf.string, default_value=''),\n    'audio_wav': tf.io.FixedLenFeature([], tf.string, default_value=''),\n    'recording_id': tf.io.FixedLenFeature([], tf.string, default_value=''),\n    'cut_tmin': tf.io.FixedLenFeature([], tf.string, default_value=''),\n    'windows': tf.io.FixedLenFeature([], tf.string, default_value=''),\n    'species': tf.io.FixedLenFeature([], tf.string, default_value=''),\n}\n\n\n@tf.function\ndef _parse_input_tfrec(example_proto):\n    sample = tf.io.parse_single_example(example_proto, feature_description)\n    return {\n        'idx': tf.io.parse_tensor(sample['idx'], tf.int32),\n        'audio_wav': tf.io.parse_tensor(sample['audio_wav'], tf.float32),\n        'recording_id': tf.io.parse_tensor(sample['recording_id'], tf.string),\n        'cut_tmin': tf.io.parse_tensor(sample['cut_tmin'], tf.float32),\n        'windows': tf.io.parse_tensor(sample['windows'], tf.float32),\n        'species': tf.io.parse_tensor(sample['species'], tf.int32),\n    }\n\n\n@tf.function\ndef _cut_wav(x, samples, is_train):\n    idx = x['idx']\n    cut_tmin = x['cut_tmin']\n    main_spid, main_tmin, main_tmax = samples[idx, 0], samples[idx, 1], samples[idx, 2]\n    main_spid = tf.cast(main_spid, tf.int32)\n    main_tmin = tf.cast(main_tmin, tf.float32)\n    main_tmax = tf.cast(main_tmax, tf.float32)\n    main_tmin -= cut_tmin\n    main_tmax -= cut_tmin\n    sub_segment_duration = cfg['preprocess']['sub_segment_duration']\n    \n    if is_train:\n        if main_tmax - main_tmin < sub_segment_duration:\n            min_left = tf.maximum(0.0, main_tmax - sub_segment_duration)\n            max_left = tf.minimum(main_tmin, SEGMENT_DURATION - sub_segment_duration)\n        else:\n            shrinkage = (main_tmax - main_tmin) - sub_segment_duration\n            min_left = main_tmin\n            max_left = main_tmin + shrinkage\n        left_cut = tf.random.uniform([], minval=min_left, maxval=max_left)\n    else:\n        if main_tmax - main_tmin < sub_segment_duration:\n            extension = tf.maximum(0.0, sub_segment_duration - (main_tmax - main_tmin))/2\n            left_extend = extension\n            if main_tmax + extension > SEGMENT_DURATION:\n                left_extend += main_tmax + extension - SEGMENT_DURATION\n            left_cut = tf.maximum(0.0, main_tmin - left_extend)\n        else:\n            shrinkage = (main_tmax - main_tmin) - sub_segment_duration\n            left_cut = main_tmin + shrinkage/2\n    \n    left_cut_sample = tf.cast(tf.floor(left_cut * SR), tf.int32)\n    right_cut_sample = left_cut_sample + sub_segment_duration*SR\n    x = x.copy()\n    x['audio_wav'] = tf.reshape(x['audio_wav'][left_cut_sample:right_cut_sample], [sub_segment_duration*SR])\n    x['windows'] = [[main_tmin, main_tmax]]\n    x['species'] = [main_spid]\n    \n    return x\n\n\n@tf.function\ndef _wav_to_mel_spec(x):\n    frame_size = cfg['preprocess']['frame_size']\n    hop_length = cfg['preprocess']['hop_length']\n    \n    stfts = tf.signal.stft(\n        x[\"audio_wav\"],\n        frame_length=frame_size,\n        frame_step=hop_length,\n        fft_length=frame_size,\n        window_fn=tf.signal.hann_window,\n    )\n    hz_spec = tf.square(tf.abs(stfts))\n\n    n_hz_spec_bins = tf.shape(stfts)[-1]\n    linear_to_mel_weight_matrix = tf.signal.linear_to_mel_weight_matrix(N_MELS, n_hz_spec_bins, SR, FMIN, FMAX)\n    mel_spec = tf.tensordot(hz_spec, linear_to_mel_weight_matrix, 1)\n    mel_spec.set_shape(hz_spec.shape[:-1].concatenate(linear_to_mel_weight_matrix.shape[-1:]))\n    log_mel_spec = tf.math.log(mel_spec + 1e-6)/tf.math.log(10.0)\n\n    y = {\n        'mel_spec': tf.transpose(log_mel_spec)\n    }\n    y.update(x)\n    return y\n\n\n@tf.function\ndef _create_labels(x):\n    species, _ = tf.unique(tf.cast(x['species'], dtype=tf.int64))\n    species = tf.sort(species)\n    n_labels = tf.shape(species)[0]\n    species = tf.reshape(species, (-1, 1))\n    return tf.sparse.to_dense(tf.sparse.SparseTensor(species, tf.ones([tf.shape(species)[0]]), [NUM_SPECIES]))\n\n\ndef _create_idx_filter(indices):\n    @tf.function\n    def _filter_func(x):\n        return tf.reduce_any(indices == x['idx'])\n    return _filter_func\n\n\ndef _filter_indices(dataset, indices):\n    return dataset.filter(_create_idx_filter(indices))\n\n\n@tf.function\ndef augment_img(image):\n    \n    @tf.function\n    def _specaugment(image):\n        erase_time = cfg['preprocess']['spec_aug_erase_time']\n        erase_mel = cfg['preprocess']['spec_aug_erase_mel']\n        num_time_cuts = cfg['preprocess']['spec_aug_num_time_cuts']\n        num_freq_cuts = cfg['preprocess']['spec_aug_num_freq_cuts']\n        img_height = tf.shape(image)[0]\n        img_width = tf.shape(image)[1]\n        image = tf.expand_dims(image, axis=0)\n\n        xoff = tf.random.uniform([num_time_cuts], minval=erase_time//2, maxval=img_width - erase_time//2, dtype=tf.int32)\n        xsize = tf.random.uniform([num_time_cuts], minval=erase_time//2, maxval=erase_time, dtype=tf.int32)\n        yoff = tf.random.uniform([num_freq_cuts], minval=erase_mel//2, maxval=img_height-erase_mel//2, dtype=tf.int32)\n        ysize = tf.random.uniform([num_freq_cuts], minval=erase_mel//2, maxval=erase_mel, dtype=tf.int32)\n\n        for i in range(num_time_cuts):\n            image = tfa.image.cutout(image, [img_height, xsize[i]], offset=[img_height//2, xoff[i]])\n        for i in range(num_freq_cuts):\n            image = tfa.image.cutout(image, [ysize[i], img_width], offset=[yoff[i], img_width//2])\n\n        image = tf.squeeze(image, axis=0)\n        return image\n    \n    gauss_noise = tf.keras.layers.GaussianNoise(cfg['preprocess']['gauss_noise_std']) \n    image = tf.cond(\n        tf.random.uniform([]) < cfg['preprocess']['gauss_noise_prob'],\n        lambda: gauss_noise(image, training=True), lambda: image\n    )\n    image = tf.image.random_brightness(image, cfg['preprocess']['random_brightness'])\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_flip_up_down(image)\n    image = tf.cond(\n        tf.random.uniform([]) < cfg['preprocess']['spec_aug_prob'],\n        lambda: _specaugment(image), lambda: image\n    )\n\n    return image\n\n\n@tf.function\ndef _mixup(inp, targ):\n    indices = tf.range(len(inp))\n    indices = tf.random.shuffle(indices)\n    sinp = tf.gather(inp, indices, axis=0)\n    starg = tf.gather(targ, indices, axis=0)\n\n    alpha = cfg['preprocess']['mixup_alpha']\n    t = tf.compat.v1.distributions.Beta(alpha, alpha).sample([len(inp)])\n    tx = tf.reshape(t, [-1, 1, 1, 1])\n    ty = tf.reshape(t, [-1, 1])\n    x = inp * tx + sinp * (1 - tx)\n    y = targ * ty + starg * (1 - ty)\n    return x, y\n\n\n@tf.function\ndef preprocess_img(img, is_train):\n    img.set_shape([N_MELS, N_FRAMES])\n    img = tf.expand_dims(img, axis=-1)\n    img = tf.image.resize(img, IMG_SIZE[:-1])\n    img = tf.image.per_image_standardization(img)\n    if is_train:\n        img = augment_img(img)\n    img_min = tf.reduce_min(img)\n    img_max = tf.reduce_max(img)\n    img = (img - img_min)/(img_max - img_min)*255\n    img = tf.image.grayscale_to_rgb(img)\n    return img\n\n\ndef build_dataset(indices, samples, is_train):\n    \n    @tf.function\n    def _split_img_label(x):\n        return x['mel_spec'], _create_labels(x)\n    \n    @tf.function\n    def _cut_sub_segment(x):\n        return _cut_wav(x, samples, is_train)\n    \n    samples = samples[['species_id', 't_min', 't_max']].values\n    filepath = os.path.join(TRAIN_INPUT_DIR, 'train.cut_audio.tfrecord')\n    ds = tf.data.TFRecordDataset(filepath)\n    ds = ds.map(_parse_input_tfrec, num_parallel_calls=AUTOTUNE)\n    ds = _filter_indices(ds, indices)\n    ds = ds.cache()\n    if is_train:\n        ds = ds.shuffle(buffer_size=cfg['training']['shuffle_buffer_size'])\n        ds = ds.repeat()\n    ds = ds.map(_cut_sub_segment, num_parallel_calls=AUTOTUNE)\n    ds = ds.map(_wav_to_mel_spec, num_parallel_calls=AUTOTUNE)\n    ds = ds.map(_split_img_label, num_parallel_calls=AUTOTUNE)\n    ds = ds.map(lambda img, l: (preprocess_img(img, is_train), l), num_parallel_calls=AUTOTUNE)\n    ds = ds.batch(cfg['training']['batch_size'])\n    if is_train and cfg['preprocess']['do_mixup']:\n        ds = ds.map(_mixup, num_parallel_calls=AUTOTUNE)\n    return ds.prefetch(buffer_size=AUTOTUNE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class AttBlock(keras.layers.Layer):\n    \n    def __init__(self, num_outputs, activation='linear', temperature=1.):\n        super(AttBlock, self).__init__()\n        self.num_outputs = num_outputs\n        self.activation = activation\n        self.temperature = temperature\n        \n    def build(self, input_shape):\n        self.att = keras.layers.Conv1D(self.num_outputs, 1)\n        self.cla = keras.layers.Conv1D(self.num_outputs, 1)\n        self.bn_att = keras.layers.BatchNormalization()\n        \n    def call(self, x):\n        norm_att = tf.nn.softmax(tf.clip_by_value(self.att(x), -10, 10), axis=1)\n        cla = self.nonlinear_transform(self.cla(x))\n        x = tf.math.reduce_sum(norm_att * cla, axis=1)\n        return x, norm_att, cla\n\n    def nonlinear_transform(self, x):\n        if self.activation == 'linear':\n            return x\n        elif self.activation == 'sigmoid':\n            return tf.math.sigmoid(x)\n        \n        \nclass ModelNN(BaseEstimator):\n    \n    def __init__(\n        self,\n        n_classes=NUM_SPECIES,\n        backbone_arch=cfg['model']['backbone_arch'],\n    ):\n        self.n_classes = n_classes\n        self.backbone_arch = backbone_arch\n        self.estimator_ = None\n        \n    def compile_model(self, model, is_frozen):\n        if is_frozen:\n            learning_rate = cfg['training']['frozen_learning_rate']\n        else:\n            learning_rate = cfg['training']['learning_rate']\n            \n        lr_schedule = tf.keras.experimental.CosineDecayRestarts(\n            learning_rate, 1000, t_mul=2.0, m_mul=1.0, alpha=1e-8, name='consine_decay'\n        )\n        model.compile(\n            optimizer=keras.optimizers.Adam(learning_rate=lr_schedule),\n            loss='bce',\n            loss_weights=[1, 0.5],\n            metrics=[[LWLRAP(self.n_classes), 'accuracy'], [LWLRAP(self.n_classes), 'accuracy']]\n        )\n        return model\n        \n    def build_model(self, freeze_backbone):\n        inputs = keras.layers.Input(shape=IMG_SIZE)\n\n        if self.backbone_arch == 'effnet_b0':\n            model_backbone = keras.applications.EfficientNetB0(include_top=False, input_tensor=inputs)\n        elif self.backbone_arch == 'effnet_b1':\n            model_backbone = keras.applications.EfficientNetB1(include_top=False, input_tensor=inputs)\n        elif self.backbone_arch == 'effnet_b2':\n            model_backbone = keras.applications.EfficientNetB2(include_top=False, input_tensor=inputs)\n        elif self.backbone_arch == 'resnet50':\n            model_backbone = keras.applications.ResNet50(include_top=False, input_tensor=inputs)\n        elif self.backbone_arch == 'resnet34':\n            model_backbone = ResNet34(include_top=False, input_tensor=inputs, weights='imagenet')\n        else:\n            raise Exception(f'Unsupported backbone arch: {self.backbone_arch}')\n            \n        if freeze_backbone:\n            model_backbone.trainable = False\n        \n        x = tf.math.reduce_mean(model_backbone.output, axis=1)\n        x1 = keras.layers.MaxPool1D(pool_size=3, strides=1, padding='same')(x)\n        x2 = keras.layers.AveragePooling1D(pool_size=3, strides=1, padding='same')(x)\n        x = x1 + x2\n        x = keras.layers.Dropout(0.5)(x)\n        x = keras.layers.TimeDistributed(keras.layers.Dense(2048, activation='relu'))(x)\n        x = keras.layers.Dropout(0.5)(x)\n        (clipwise_output, _, segmentwise_output) = AttBlock(self.n_classes)(x)\n        segmentwise_output = tf.math.reduce_max(segmentwise_output, axis=1)\n        outputs = [\n            keras.layers.Activation('sigmoid', name='clipwise')(clipwise_output),\n            keras.layers.Activation('sigmoid', name='segmentwise')(segmentwise_output)\n        ]\n\n        model = keras.Model(inputs, outputs, name=\"EfficientNet\")\n        return self.compile_model(model, freeze_backbone)\n    \n    def unfreeze_backbone(self, num_layers=cfg['training']['num_unfreeze_layers']):\n        model = self.estimator_\n        selected_layers = model.layers[-num_layers:] if num_layers is not None else model.layers\n        for layer in selected_layers:\n            if not isinstance(layer, keras.layers.BatchNormalization):\n                layer.trainable = True\n        self.estimator_ = self.compile_model(model, False)\n  \n    def fit(self, train_dataset, val_dataset, n_epochs=25, weights_suffix='', with_callbacks=True):\n        if self.estimator_ is None:\n            if cfg['training']['use_tpu']:\n                with tpu_strategy.scope():\n                    self.estimator_ = self.build_model(freeze_backbone=True)\n            else:\n                self.estimator_ = self.build_model(freeze_backbone=True)\n        \n        model_checkpoint = keras.callbacks.ModelCheckpoint(\n            f'./weights{weights_suffix}.h5',\n            monitor='val_clipwise_lwlrap',\n            save_best_only=True,\n            save_weights_only=True,\n            mode='max'\n        )\n        lr_scheduler = keras.callbacks.ReduceLROnPlateau(\n            monitor='val_clipwise_lwlrap',\n            patience=5,\n            mode='max',\n            min_lr=cfg['training']['min_lr'],\n            verbose=2\n        )\n        early_stopping = keras.callbacks.EarlyStopping(\n            monitor='val_clipwise_lwlrap',\n            patience=15,\n            mode='max',\n            restore_best_weights=True\n        )\n        logs_cb = tf.keras.callbacks.CSVLogger(f'logs/metrics{weights_suffix}.csv')\n#         callbacks = [model_checkpoint, lr_scheduler, early_stopping, logs_cb]\n        callbacks = [model_checkpoint, early_stopping, logs_cb]\n\n        return self.estimator_.fit(\n            train_dataset,\n            epochs=n_epochs,\n            callbacks=callbacks if with_callbacks else None,\n            validation_data=val_dataset,\n            steps_per_epoch=cfg['training']['steps_per_epoch'],\n            verbose=2\n        )\n    \n    def predict(self, img):\n        return self.estimator_.predict(img)\n    \n    def load_weights(self, checkpoint_path):\n        if self.estimator_ is None:\n            self.estimator_ = self.build_model(freeze_backbone=False)\n            self.unfreeze_backbone()\n        self.estimator_.load_weights(checkpoint_path)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_audio(recording_id, train=True):\n    filepath = os.path.join(TRAIN_INPUT_DIR if train else TEST_INPUT_DIR, recording_id.decode() + '.flac')\n    data, _ = librosa.load(filepath, sr=SR)\n    return data\n\n\ndef cut_audio(audio_data, tmin, duration, sr=SR):\n    tmin_sample = tmin * sr\n    tmax_sample = tmin_sample + sr*duration\n    cut_audio = audio_data[tmin_sample:tmax_sample]\n    assert len(cut_audio) == sr*duration\n    return cut_audio\n\n\ndef get_mel_spec_img(audio_data):\n    return _wav_to_mel_spec({'audio_wav': audio_data})['mel_spec']\n\n\ndef cut_and_get_mel_spec_img(audio_data, t_min, duration):\n    audio_data = cut_audio(audio_data, t_min, duration)\n    return get_mel_spec_img(audio_data)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_test_dataset(recordings):\n    \n    def segment_recording(audio_data):\n        imgs = []\n        for t_min in range(0, 55, cfg['inference']['segment_stride']):\n            img = cut_and_get_mel_spec_img(audio_data, t_min, cfg['preprocess']['sub_segment_duration'])\n            img = preprocess_img(img, False)\n            imgs.append(img)\n        return tf.stack(imgs)\n    \n    ds = tf.data.Dataset.from_tensor_slices(recordings)\n    ds = ds.map(\n        lambda r: (r, tf.numpy_function(load_audio, [r, False], tf.float32))\n    ).map(\n        lambda r, audio_data: (r, tf.numpy_function(segment_recording, [audio_data], tf.float32))\n    )\n    return ds.prefetch(50)\n\n\ndef predict(model, recording_id):\n    audio_data = load_audio(recording_id, train=False)\n    imgs = []\n    for t_min in range(0, 55, cfg['inference']['segment_stride']):\n        t_max = t_min + SEGMENT_DURATION\n        img, _ = cut_and_get_mel_spec_img(audio_data, t_min, t_max)\n        img = preprocess_img(img, False)\n        imgs.append(img)\n    preds = model.predict(tf.stack(imgs))\n    return tf.reduce_max(preds, axis=0)\n\n\ndef generate_submission(model, submission):\n    submission = submission.copy()\n    ds = build_test_dataset(submission['recording_id'])\n    generate_start = time.time()\n    for i, (recording_id, imgs) in ds.enumerate().as_numpy_iterator():\n        start = time.time()\n        recording_id = recording_id.decode()\n        preds = model.predict(imgs)\n#         preds = tf.math.sqrt(preds[0] * preds[1])\n        preds = preds[1]\n        preds = tf.reduce_max(preds, axis=0)\n        assert submission['recording_id'].iloc[i] == recording_id\n        submission.iloc[i, 1:] = preds.numpy()\n        print(recording_id, 'elapsed:', round(time.time() - start, 2))\n    return submission","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"PROJECT_ID = 'kaggle-moa-296003'\nfrom google.cloud import storage\nstorage_client = storage.Client(project=PROJECT_ID)\n\nbucket = storage_client.get_bucket('tn-kaggle-data')\nblob = bucket.blob('weights.zip')\nblob.download_to_filename('./weights.zip')\n!unzip ./weights.zip\n!rm ./weights.zip","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_folds = []\n\nfor fold in range(5):\n    start = time.time()\n    model = ModelNN()\n    model.load_weights(f'./weights_{fold}_final.h5')\n\n    completed_submission = generate_submission(model, submission)\n    submission_folds.append(completed_submission)\n    completed_submission.to_csv(f'./fold_{fold}.csv', index=False)\n    print('Fold', fold, 'Elapsed', round(time.time() - start, 2))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from scipy.stats.mstats import gmean\n\nprobs = np.stack([sf.iloc[:, 1:].values for sf in submission_folds])\nsubmission.iloc[:, 1:] = gmean(probs, axis=0)\nsubmission.to_csv('./submission.csv', index=False)","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}