{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Hi to all!!!\n# Earlier I published a notebook that allowed me to work in both google colab and kaggle notebook (https://www.kaggle.com/aikhmelnytskyy/resnet-tpu-on-colab-and-kaggle).\n# Now I want to publish my best model for today.\n# The model is a pure experiment. \n# I was wondering what would happen if I used Wavenet (https://www.kaggle.com/nxrprime/wavenet-with-shifted-rfc-proba-and-cbr).\n# As a result, I mixed Resnet and Wavenet and got this result.\n# The idea of ​​my experiment is that I use the Resnet model (or any other model) in the first stage, in the next stage I pass n layers of this model to the wavenet, and I concatenate the results. \n# If you like my notebooks, don't forget to upvote!!!"},{"metadata":{},"cell_type":"markdown","source":"# IMPORTANTLY! \n# This notebook didn't work after the changes to kaggle, but thanks to a discussion by Martin Görner and Allohvk (https://www.kaggle.com/c/rfcx-species-audio-detection/discussion/216408 ), I made the necessary changes in version 4 and now everything works. \n# Here are the changes:\n\nfrom\n\n@tf.function\n\ndef _preprocess_img(x, training=False, test=False):\n\nto\n\n#@tf.function\n\ndef _preprocess_img(x, training=False, test=False):\n\nAnd from \n\ndef _specaugment(image):\n\n        image = tfa.image.cutout(image, [HEIGHT, xsize[0]], offset=[HEIGHT//2, xoff[0]])\n        image = tfa.image.cutout(image, [HEIGHT, xsize[1]], offset=[HEIGHT//2, xoff[1]])\n        image = tfa.image.cutout(image, [ysize[0], WIDTH], offset=[yoff[0], WIDTH//2])\n        image = tfa.image.cutout(image, [ysize[1], WIDTH], offset=[yoff[1], WIDTH//2])\n        image = tf.squeeze(image, axis=0)\n        return image\n\nto\n        \n        #image = tfa.image.cutout(image, [HEIGHT, xsize[0]], offset=[HEIGHT//2, xoff[0]])\n        #image = tfa.image.cutout(image, [HEIGHT, xsize[1]], offset=[HEIGHT//2, xoff[1]])\n        #image = tfa.image.cutout(image, [ysize[0], WIDTH], offset=[yoff[0], WIDTH//2])\n        #image = tfa.image.cutout(image, [ysize[1], WIDTH], offset=[yoff[1], WIDTH//2])\n        image = tf.squeeze(image, axis=0)\n        return image\n\n"},{"metadata":{},"cell_type":"markdown","source":"Version 5 changes as shown in this discussion https://www.kaggle.com/c/rfcx-species-audio-detection/discussion/218930 (special thanks to the author)"},{"metadata":{},"cell_type":"markdown","source":"I used these notebooks as a basis: https://www.kaggle.com/mekhdigakhramanian/rfcx-resnet50-tpu https://www.kaggle.com/khoongweihao/resnet34-more-augmentations-mixup-tta-inference"},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!pip install image-classifiers","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Imports"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import random\nimport os\nimport tensorflow as tf\nimport tensorflow_addons as tfa\nimport numpy as np\nfrom pathlib import Path\nimport io\nimport matplotlib.pyplot as plt\nimport soundfile as sf\nimport librosa\nfrom kaggle_datasets import KaggleDatasets\nfrom tqdm import tqdm\nimport pandas as pd\nfrom sklearn.model_selection import StratifiedKFold\nimport seaborn as sns\nfrom IPython.display import Audio\n\nfrom classification_models.keras import Classifiers\n\ntf.__version__","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"SEED = 42\n\ndef seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    tf.random.set_seed(seed)\n    \nseed_everything(SEED)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Configs"},{"metadata":{"trusted":true},"cell_type":"code","source":"# from https://github.com/qubvel/classification_models\nResNet34, preprocess_input = Classifiers.get('resnet34')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"cfg = {\n    'parse_params': {\n        'cut_time': 11,\n    },\n    'data_params': {\n        'sample_time': 9, # assert 60 % sample_time == 0\n        'spec_fmax': 24000.0,\n        'spec_fmin': 40.0,\n        'spec_mel': 384,#284, \n        'mel_power': 2,\n        'img_shape': (384, 784)#(284, 512)\n    },\n    'model_params': {\n        'batchsize_per_tpu': 16,\n        'iteration_per_epoch': 64,\n        'epoch': 25, \n        'arch': ResNet34,\n        'arch_preprocess': preprocess_input,\n        'freeze_to': 0,  # Freeze to backbone.layers[:freeze_to]. If None, all layers in the backbone will be freezed.\n        'loss': {\n            'fn': tfa.losses.SigmoidFocalCrossEntropy,\n            'params': {},\n        },\n        'optim': {\n            'fn': tfa.optimizers.RectifiedAdam,\n            'params': {'lr': 2e-3, 'total_steps': 18*64, 'warmup_proportion': 0.3, 'min_lr': 1e-6},\n        },\n        'mixup': True # False\n    }\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# detect and init the TPU\ntpu = tf.distribute.cluster_resolver.TPUClusterResolver()\ntf.config.experimental_connect_to_cluster(tpu)\ntf.tpu.experimental.initialize_tpu_system(tpu)\nprint(\"All devices: \", tf.config.list_logical_devices('TPU'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"strategy = tf.distribute.experimental.TPUStrategy(tpu)\nAUTOTUNE = 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\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"CUT = cfg['parse_params']['cut_time']\nSR = 48000     # all wave's sample rate may be 48k\n\nTIME = cfg['data_params']['sample_time']\n\nFMAX = cfg['data_params']['spec_fmax']\nFMIN = cfg['data_params']['spec_fmin']\nN_MEL = cfg['data_params']['spec_mel']\n\nHEIGHT, WIDTH = cfg['data_params']['img_shape']\n\nCLASS_N = 24","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Explore the tfrecords, Create dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"raw_dataset = tf.data.TFRecordDataset([TRAIN_TFREC + '/00-148.tfrec'])\nraw_dataset","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## parse tfrecords"},{"metadata":{"trusted":true},"cell_type":"code","source":"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}\nparse_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\n@tf.function\ndef _parse_function(example_proto):\n    sample = tf.io.parse_single_example(example_proto, feature_description)\n    wav, _ = tf.audio.decode_wav(sample['audio_wav'], desired_channels=1) # mono\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(SR, tf.float32)\n        tmin_s = tmin * tf.cast(SR, tf.float32)\n        cut_s = tf.cast(CUT * SR, tf.float32)\n        all_s = tf.cast(60 * SR, 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, all_s) - cut_s)\n            ), tf.int32\n        )\n        cut_max = cut_min + CUT * SR\n        \n        _sample = {\n            'audio_wav': tf.reshape(wav[cut_min:cut_max], [CUT*SR]),\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(SR, tf.float32),\n            'f_min': fmin,\n            't_max': tmax - tf.cast(cut_min, tf.float32)/tf.cast(SR, tf.float32),\n            'f_max': fmax,\n            'is_tp': tp\n        }\n        return _sample\n    \n    samples = tf.map_fn(_cut_audio, labels, dtype=parse_dtype)\n    return samples\n\nparsed_dataset = raw_dataset.map(_parse_function).unbatch()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef _cut_wav(x):\n    # random cut in training\n    cut_min = tf.random.uniform([], maxval=(CUT-TIME)*SR, dtype=tf.int32)\n    cut_max = cut_min + TIME * SR\n    cutwave = tf.reshape(x['audio_wav'][cut_min:cut_max], [TIME*SR])\n    y = {}\n    y.update(x)\n    y['audio_wav'] = cutwave\n    y['t_min'] = tf.maximum(0.0, x['t_min'] - tf.cast(cut_min, tf.float32) / SR)\n    y['t_max'] = tf.maximum(0.0, x['t_max'] - tf.cast(cut_min, tf.float32) / SR)\n    return y\n    \n@tf.function\ndef _cut_wav_val(x):\n    # center crop in validation\n    cut_min = (CUT-TIME)*SR // 2\n    cut_max = cut_min + TIME * SR\n    cutwave = tf.reshape(x['audio_wav'][cut_min:cut_max], [TIME*SR])\n    y = {}\n    y.update(x)\n    y['audio_wav'] = cutwave\n    y['t_min'] = tf.maximum(0.0, x['t_min'] - tf.cast(cut_min, tf.float32) / SR)\n    y['t_max'] = tf.maximum(0.0, x['t_max'] - tf.cast(cut_min, tf.float32) / SR)\n    return y","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef _filtTP(x):\n    return x['is_tp'] == 1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_wav(sample, ax):\n    wav = sample[\"audio_wav\"].numpy()\n    rate = SR\n    ax.plot(np.arange(len(wav)) / rate, wav)\n    ax.set_title(\n        sample[\"recording_id\"].numpy().decode()\n        + (\"/%d\" % sample[\"species_id\"])\n        + (\"TP\" if sample[\"is_tp\"] else \"FP\"))\n\n    return Audio((wav * 2**15).astype(np.int16), rate=rate)\n\nfig, ax = plt.subplots(figsize=(15, 3))\nshow_wav(next(iter(parsed_dataset)), ax)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## create mel-spectrogram"},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef _wav_to_spec(x):\n    mel_power = cfg['data_params']['mel_power']\n    \n    stfts = tf.signal.stft(x[\"audio_wav\"], frame_length=2048, frame_step=512, fft_length=2048)\n    spectrograms = tf.abs(stfts) ** mel_power\n\n    # Warp the linear scale spectrograms into the mel-scale.\n    num_spectrogram_bins = stfts.shape[-1]\n    lower_edge_hertz, upper_edge_hertz, num_mel_bins = FMIN, FMAX, N_MEL\n    \n    linear_to_mel_weight_matrix = tf.signal.linear_to_mel_weight_matrix(\n      num_mel_bins, num_spectrogram_bins, SR, lower_edge_hertz,\n      upper_edge_hertz)\n    mel_spectrograms = tf.tensordot(\n      spectrograms, linear_to_mel_weight_matrix, 1)\n    mel_spectrograms.set_shape(spectrograms.shape[:-1].concatenate(\n      linear_to_mel_weight_matrix.shape[-1:]))\n\n    # Compute a stabilized log to get log-magnitude mel-scale spectrograms.\n    log_mel_spectrograms = tf.math.log(mel_spectrograms + 1e-6)\n\n    y = {\n        'audio_spec': tf.transpose(log_mel_spectrograms), # (num_mel_bins, frames)\n    }\n    y.update(x)\n    return y\n\nspec_dataset = parsed_dataset.filter(_filtTP).map(_cut_wav).map(_wav_to_spec)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(12,5))\nfor i, s in enumerate(spec_dataset.take(3)):\n    plt.subplot(1,3,i+1)\n    plt.imshow(s['audio_spec'])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import librosa.display\nimport matplotlib.patches as patches\n\ndef show_spectrogram(sample, ax, showlabel=False):\n    S_dB = sample[\"audio_spec\"].numpy()\n    img = librosa.display.specshow(S_dB, x_axis='time',\n                             y_axis='mel', sr=SR,\n                             fmax=FMAX, fmin=FMIN, ax=ax, cmap='magma')\n    ax.set(title=f'Mel-frequency spectrogram of {sample[\"recording_id\"].numpy().decode()}')\n    sid, fmin, fmax, tmin, tmax, istp = (\n            sample[\"species_id\"], sample[\"f_min\"], sample[\"f_max\"], sample[\"t_min\"], sample[\"t_max\"], sample[\"is_tp\"])\n    ec = '#00ff00' if istp == 1 else '#0000ff'\n    ax.add_patch(\n        patches.Rectangle(xy=(tmin, fmin), width=tmax-tmin, height=fmax-fmin, ec=ec, fill=False)\n    )\n\n    if showlabel:\n        ax.text(tmin, fmax, \n        f\"{sid.numpy().item()} {'tp' if istp == 1 else 'fp'}\",\n        horizontalalignment='left', verticalalignment='bottom', color=ec, fontsize=16)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(15,3))\nshow_spectrogram(next(iter(spec_dataset)), ax, showlabel=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# in validation, annotations will come to the center\nfig, ax = plt.subplots(figsize=(15,3))\nshow_spectrogram(next(iter(parsed_dataset.filter(_filtTP).map(_cut_wav_val).map(_wav_to_spec))), ax, showlabel=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for sample in spec_dataset.take(5):\n    fig, ax = plt.subplots(figsize=(15,3))\n    show_spectrogram(sample, ax, showlabel=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## create labels"},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef _create_annot(x):\n    targ = tf.one_hot(x[\"species_id\"], CLASS_N, on_value=x[\"is_tp\"], off_value=0)\n    \n    return {\n        'input': x[\"audio_spec\"],\n        'target': tf.cast(targ, tf.float32)\n    }\n\nannot_dataset = spec_dataset.map(_create_annot)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Preprocessing and data augmentation\n\nIn training, I use\n\n* gaussian noise\n* random flip left & right (NEW)\n* random brightness\n* specaugment"},{"metadata":{"trusted":true},"cell_type":"code","source":"#@tf.function\ndef _preprocess_img(x, training=False, test=False):\n    image = tf.expand_dims(x, axis=-1)\n    image = tf.image.resize(image, [HEIGHT, WIDTH])\n    image = tf.image.per_image_standardization(image)\n    \n    @tf.function\n    def _specaugment(image):\n        ERASE_TIME = 50\n        ERASE_MEL = 16\n        image = tf.squeeze(image, axis=2)\n        image = tfio.experimental.audio.time_mask(image, param=ERASE_TIME)\n        image = tfio.experimental.audio.freq_mask(image, param=ERASE_MEL)\n        image = tf.expand_dims(image, axis=2)\n        return image\n    \n    if training:\n        # gaussian\n        gau = tf.keras.layers.GaussianNoise(0.3)\n        image = tf.cond(tf.random.uniform([]) < 0.5, lambda: gau(image, training=True), lambda: image)\n        # brightness\n        image = tf.image.random_brightness(image, 0.2)\n        # random left right flip (NEW)\n        image = tf.image.random_flip_left_right(image)\n        # specaugment\n        #image = tf.cond(tf.random.uniform([]) < 0.5, lambda: _specaugment(image), lambda: image)\n        \n    if test:\n        # Insert augmentations for TTA here\n        #image = tf.cond(tf.random.uniform([]) < 0.5, lambda: _specaugment(image), lambda: image)\n        pass\n        \n    image = (image - tf.reduce_min(image)) / (tf.reduce_max(image) - tf.reduce_min(image)) * 255.0 # rescale to [0, 255]\n    image = tf.image.grayscale_to_rgb(image)\n    #image =tf.reshape(image, [-1])\n    image = cfg['model_params']['arch_preprocess'](image)\n\n    return image\n\n@tf.function\ndef _preprocess(x):\n    image = _preprocess_img(x['input'], training=True, test=False)\n    return (image, x[\"target\"])\n\n@tf.function\ndef _preprocess_val(x):\n    image = _preprocess_img(x['input'], training=False, test=False)\n    return (image, x[\"target\"])\n\n@tf.function\ndef _preprocess_test(x):\n    image = _preprocess_img(x['audio_spec'], training=False, test=True)\n    return (image, x[\"recording_id\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''\nfor inp, targ in annot_dataset.map(_preprocess).take(2):\n    plt.imshow(inp.numpy()[:,:,0])\n    t = targ.numpy()\n    if t.sum() == 0:\n        plt.title(f'FP')\n    else:\n        plt.title(f'{t.nonzero()[0]}')\n    plt.colorbar()\n    plt.show()'''","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"from tensorflow.keras.layers import *\nfrom tensorflow.keras import losses, models, optimizers\nfrom tensorflow.keras.optimizers import Adam\ndef create_model():\n    #with strategy.scope():\n    #backbone = cfg['model_params']['arch'](include_top=False, weights='imagenet')\n    \n    def Classifier(shape_):\n\n        backbone = cfg['model_params']['arch']((shape_), include_top=False, weights='imagenet')\n    \n    \n        \n        def cbr(x, out_layer, kernel, stride, dilation):\n            x = Conv2D(out_layer, kernel_size=kernel, dilation_rate=dilation, strides=stride, padding=\"same\")(x)\n            x = BatchNormalization()(x)\n            x = Activation(\"relu\")(x)\n            return x\n\n        def wave_block(x, filters, kernel_size, n):\n            dilation_rates = [2**i for i in range(n)]\n            x = Conv2D(filters = filters,\n                       kernel_size = 1,\n                       padding = 'same')(x)\n            res_x = x\n            for dilation_rate in dilation_rates:\n                tanh_out = Conv2D(filters = filters,\n                                  kernel_size = kernel_size,\n                                  padding = 'same', \n                                  activation = 'tanh', \n                                  dilation_rate = dilation_rate)(x)\n                sigm_out = Conv2D(filters = filters,\n                                  kernel_size = kernel_size,\n                                  padding = 'same',\n                                  activation = 'sigmoid', \n                                  dilation_rate = dilation_rate)(x)\n                x = Multiply()([tanh_out, sigm_out])\n                x = Conv2D(filters = filters,\n                           kernel_size = 1,\n                           padding = 'same')(x)\n                res_x = Add()([res_x, x])\n            return res_x\n\n        \n        #out1\n        def wavenet(layer):\n          \n          x = cbr(layer, 192, 7, 1, 1)\n          x = BatchNormalization()(x)\n          x = wave_block(x, 192, 3, 1)\n          x = cbr(x, 96, 7, 1, 1)\n          x = BatchNormalization()(x)\n          x = wave_block(x, 96, 3, 1)\n          x = cbr(x, 48, 5, 1, 1)\n          x = BatchNormalization()(x)\n          x = wave_block(x, 48, 3, 1)  \n          return x\n\n        def wavenet1(layer):\n          \n          x = cbr(layer, 4, 7, 1, 1)\n          x = BatchNormalization()(x)\n          x = wave_block(x, 3, 3, 1)\n          x = cbr(x, 3, 7, 1, 1)\n          x = BatchNormalization()(x)\n          x = wave_block(x, 16, 3, 1)\n          x = cbr(x, 3, 5, 1, 1)\n          return x\n        #x = BatchNormalization()(x)\n        \n        x0 = backbone#model\n        print('1')\n        #backbone.summary()\n        x1 = tf.keras.layers.GlobalAveragePooling2D()(x0.layers[-1].output)  #-3,-7,-9,-15  for EF5    \n        #x2 = tf.keras.layers.GlobalAveragePooling2D()(x0.layers[-3].output) # 803,799,797,791 for EF7\n        x3 = tf.keras.layers.GlobalAveragePooling2D()(x0.layers[-7].output)\n        #x4 = tf.keras.layers.GlobalAveragePooling2D()(x0.layers[-12].output)\n        x5 = tf.keras.layers.GlobalAveragePooling2D()(x0.layers[-18].output)\n        print('2')\n        x1=wavenet(x0.layers[-1].output)\n        x3=wavenet(x0.layers[-7].output)\n        x5=wavenet(x0.layers[-18].output)\n\n        x1 = tf.keras.layers.GlobalAveragePooling2D()(x1)\n        x3 = tf.keras.layers.GlobalAveragePooling2D()(x3)\n        x5 = tf.keras.layers.GlobalAveragePooling2D()(x5)\n       \n        \n        \n        print('4')\n        #x =  tf.concat([x1,x2,x3,x4,x5],axis = 1)\n       \n        x =  tf.concat([x1,x3,x5],axis = 1)\n      \n        x = tf.keras.layers.Dropout(0.7)(x)\n        x = tf.keras.layers.Dense(192)(x)\n        #x =  tf.keras.layers.BatchNormalization()(x)          \n        x = tf.keras.layers.Dropout(0.4)(x)\n        #x =  margin([x , label])\n        \n        output = tf.keras.layers.Softmax(dtype='float32')(x)\n        output =tf.keras.layers.Dense(CLASS_N)(x)\n        print('5')\n        model = tf.keras.models.Model(inputs = x0.input, outputs = output)\n        #model.compile(optimizer=optimizer, loss=loss_fn, metrics=[LWLRAP(CLASS_N)])\n        \n\n        \n        \n        return model\n    return Classifier([HEIGHT,WIDTH,3])\n\n\nmodel = create_model()\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef _mixup(inp, targ):\n    indice = tf.range(len(inp))\n    indice = tf.random.shuffle(indice)\n    sinp = tf.gather(inp, indice, axis=0)\n    starg = tf.gather(targ, indice, axis=0)\n    \n    alpha = 0.2\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#     y = tf.minimum(targ + starg, 1.0) # for multi-label???\n    return x, y","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tfrecs = sorted(tf.io.gfile.glob(TRAIN_TFREC + '/*.tfrec'))\nparsed_trainval = (tf.data.TFRecordDataset(tfrecs, num_parallel_reads=AUTOTUNE)\n                    .map(_parse_function, num_parallel_calls=AUTOTUNE).unbatch()\n                    .filter(_filtTP).enumerate())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Stratified 5-Fold"},{"metadata":{"trusted":true},"cell_type":"code","source":"indices = []\nspid = []\nrecid = []\n\nfor i, sample in tqdm(parsed_trainval.prefetch(AUTOTUNE)):\n    indices.append(i.numpy())\n    spid.append(sample['species_id'].numpy())\n    recid.append(sample['recording_id'].numpy().decode())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"table = pd.DataFrame({'indices': indices, 'species_id': spid, 'recording_id': recid})\ntable","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"skf = StratifiedKFold(n_splits=5, random_state=SEED, shuffle=True)\nsplits = list(skf.split(table.index, table.species_id))\n\nplt.hist([table.loc[splits[0][0], 'species_id'], table.loc[splits[0][1], 'species_id']], bins=CLASS_N,stacked=True)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_idx_filter(indice):\n    @tf.function\n    def _filt(i, x):\n        return tf.reduce_any(indice == i)\n    return _filt\n\n@tf.function\ndef _remove_idx(i, x):\n    return x","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Other setup"},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_train_dataset(batchsize, train_idx):\n    global parsed_trainval\n    parsed_train = (parsed_trainval\n                    .filter(create_idx_filter(train_idx))\n                    .map(_remove_idx))\n    \n    dataset = (parsed_train.cache()\n        .shuffle(len(train_idx))\n        .repeat()\n        .map(_cut_wav, num_parallel_calls=AUTOTUNE)\n        .map(_wav_to_spec, num_parallel_calls=AUTOTUNE)\n        .map(_create_annot, num_parallel_calls=AUTOTUNE)\n        .map(_preprocess, num_parallel_calls=AUTOTUNE)\n        .batch(batchsize))\n\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\ndef create_val_dataset(batchsize, val_idx):\n    global parsed_trainval\n    parsed_val = (parsed_trainval\n                  .filter(create_idx_filter(val_idx))\n                  .map(_remove_idx))\n\n    vdataset = (parsed_val\n        .map(_cut_wav_val, num_parallel_calls=AUTOTUNE)\n        .map(_wav_to_spec, num_parallel_calls=AUTOTUNE)\n        .map(_create_annot, num_parallel_calls=AUTOTUNE)\n        .map(_preprocess_val, num_parallel_calls=AUTOTUNE)\n        .batch(8*strategy.num_replicas_in_sync)\n        .cache())\n    return vdataset","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Metrics"},{"metadata":{"trusted":true},"cell_type":"code","source":"# from https://www.kaggle.com/carlthome/l-lrap-metric-for-tf-keras\n@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":"# Testset and Inference function"},{"metadata":{"trusted":true},"cell_type":"code","source":"def _parse_function_test(example_proto):\n    sample = tf.io.parse_single_example(example_proto, feature_description)\n    wav, _ = 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*SR*TIME:(i+1)*SR*TIME], [SR*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    })\n\ndef 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        with strategy.scope():\n            pred = model.predict_on_batch(tf.reshape(inp, [-1, HEIGHT, WIDTH, 3]))\n            prob = tf.sigmoid(pred)\n            prob = tf.reduce_max(tf.reshape(prob, [-1, 60//TIME, CLASS_N]), axis=1)\n\n        rec_id_stack = tf.reshape(rec_id, [-1, 60//TIME])\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(CLASS_N)}\n    })\n    sub = sub.sort_values('recording_id')\n    return sub","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_history(history, name):\n    plt.figure(figsize=(8,3))\n    plt.subplot(1,2,1)\n    plt.plot(history.history[\"loss\"])\n    plt.plot(history.history[\"val_loss\"])\n    plt.legend(['Train', 'Test'], loc='upper left')\n    plt.title(\"loss\")\n    # plt.yscale('log')\n\n    plt.subplot(1,2,2)\n    plt.plot(history.history[\"lwlrap\"])\n    plt.plot(history.history[\"val_lwlrap\"])\n    plt.legend(['Train', 'Test'], loc='upper left')\n    plt.title(\"metric\")\n\n    plt.savefig(name)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Now start training!"},{"metadata":{"trusted":true},"cell_type":"code","source":"class RMAC:\n    def __init__(self, shape, levels=3, power=None, overlap=0.4, norm_fm=False, sum_fm=True, verbose=False):\n        self.shape = shape\n        self.sum_fm = sum_fm\n        self.norm = norm_fm\n        self.power = power\n \n        # ported from Giorgios' Matlab code\n        steps = np.asarray([2, 3, 4, 5, 6, 7])\n        B, H, W, D = shape\n        w = min([W, H])\n        w2 = w // 2 - 1\n        b = np.asarray((max(H, W) - w)) / (steps - 1);\n        idx = np.argmin(np.abs(((w**2 - w*b)/(w**2))-overlap))\n \n        Wd = 0\n        Hd = 0\n        if H < W:\n            Wd = idx + 1\n        elif H > W:\n            Hd = idx + 1\n \n        self.regions = []\n        for l in range(levels):\n \n            wl = int(2 * w/(l+2));\n            wl2 = int(wl / 2 - 1);\n \n            b = 0 if not (l + Wd) else ((W - wl) / (l + Wd))\n            cenW = np.asarray(np.floor(wl2 + np.asarray(range(l+Wd+1)) * b), dtype=np.int32) - wl2\n            b = 0 if not (l + Hd) else ((H - wl) / (l + Hd))\n            cenH = np.asarray(np.floor(wl2 + np.asarray(range(l+Hd+1)) * b), dtype=np.int32) - wl2\n \n            for i in cenH:\n                for j in cenW:\n                    if i >= W or j >= H:\n                        continue\n                    ie = i+wl\n                    je = j+wl\n                    if ie >= W:\n                        ie = W\n                    if je >= H:\n                        je = H\n                    if ie - i < 1 or je - j < 1:\n                        continue\n                    self.regions.append((i,j,ie,je))\n \n        if verbose:\n            print('RMAC regions = %s' % self.regions)\n \n    def rmac(self, x):\n        y = []\n        for r in self.regions:\n            x_sliced = x[:, r[1]:r[3], r[0]:r[2], :]\n            if self.power is None:\n                x_maxed = tf.reduce_max(x_sliced, axis=(1,2))\n            else:\n                x_maxed = tf.reduce_mean((x_sliced ** self.power), axis=(2,3)) ** (1.0 / self.power)\n                x_maxed = tf.pow(tf.reduce_mean((tf.pow(x_sliced, self.power)), axis=(2,3)),(1.0 / self.power))\n            y.append(x_maxed)\n \n        y = tf.stack(y, axis=0)\n        y = tf.transpose(y, [1,0,2])\n \n        if self.norm:\n            y = tf.math.l2_normalize(y, 2)\n \n \n        if self.sum_fm:\n            y = tf.reduce_mean(y, axis=(1))\n \n        return y","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_and_inference(splits, split_id):\n    print(\"split_id\", split_id)\n    batchsize = cfg['model_params']['batchsize_per_tpu'] * strategy.num_replicas_in_sync\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_train_dataset(batchsize, idx_train_tf)\n    vdataset = create_val_dataset(batchsize, idx_val_tf)\n    \n    optimizer = cfg['model_params']['optim']['fn'](**cfg['model_params']['optim']['params'])\n    \n    with strategy.scope():\n        model = create_model()\n        model.compile(optimizer=optimizer, loss=loss_fn, metrics=[LWLRAP(CLASS_N)])\n        #model.summary()\n        \n       \n    \n    history = model.fit(dataset,\n                        steps_per_epoch=cfg['model_params']['iteration_per_epoch'],\n                        epochs=cfg['model_params']['epoch'],\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    plot_history(history, 'history_%d.png' % split_id)\n    \n    ### inference ###\n    model.load_weights('model_best_%d.h5' % split_id)\n    \n    return inference(model)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### # The whole training process in version 1 of this notebook.\n\n# To save time, I did not restart training for version 3"},{"metadata":{"trusted":true},"cell_type":"code","source":"#''' To train the medals, delete this line!!!!\n\n# train and inference\nfrom tensorflow.keras.layers import Lambda\n\n# N-fold ensemble\nsub = sum(\n    map(\n        lambda i: train_and_inference(splits, i).set_index('recording_id'),\n        range(len(splits))\n    )\n).reset_index()\n#'''","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n# Now let's make an ensemble with public models"},{"metadata":{"trusted":true},"cell_type":"code","source":"sub.describe()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# If you have any questions then ask. It would be interesting to hear ideas for improving this model. Good luck to all!"}],"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}