{"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":"code","source":"import functools\nimport datetime\nimport pandas as pd\nimport numpy as np\nfrom glob import glob\nimport random\nimport os\nimport time\nimport librosa\nimport math\nimport pickle\nimport os\n\n\nimport tensorflow_addons as tfa\nimport numpy as np\nimport matplotlib.pyplot as plt \nimport tensorflow as tf\nimport tensorflow.keras as keras\nfrom tensorflow.keras.applications import EfficientNetB0\n\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gpus = tf.config.experimental.list_physical_devices('GPU')\nfor gpu in gpus: \n    tf.config.experimental.set_memory_growth(gpu, True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"THRESHOLD = 0.9\n# HASH_SEG = \"c9eace7b\"\n# NOCALL_HASH_SEG = \"e860ba16\"\nNOCALL = \"nocall\"\nBIRD_LIST = ['aldfly', 'ameavo', 'amebit', 'amecro', 'amegfi', 'amekes', 'amepip', 'amered', 'amerob', 'amewig', 'amewoo', 'amtspa', 'annhum', 'astfly', 'baisan', 'baleag', 'balori', 'banswa', 'barswa', 'bawwar', 'belkin1', 'belspa2', 'bewwre', 'bkbcuc', 'bkbmag1', 'bkbwar', 'bkcchi', 'bkchum', 'bkhgro', 'bkpwar', 'bktspa', 'blkpho', 'blugrb1', 'blujay', 'bnhcow', 'boboli', 'bongul', 'brdowl', 'brebla', 'brespa', 'brncre', 'brnthr', 'brthum', 'brwhaw', 'btbwar', 'btnwar', 'btywar', 'buffle', 'buggna', 'buhvir', 'bulori', 'bushti', 'buwtea', 'buwwar', 'cacwre', 'calgul', 'calqua', 'camwar', 'cangoo', 'canwar', 'canwre', 'carwre', 'casfin', 'caster1', 'casvir', 'cedwax', 'chispa', 'chiswi', 'chswar', 'chukar', 'clanut', 'cliswa', 'comgol', 'comgra', 'comloo', 'commer', 'comnig', 'comrav', 'comred', 'comter', 'comyel', 'coohaw', 'coshum', 'cowscj1', 'daejun', 'doccor', 'dowwoo', 'dusfly', 'eargre', 'easblu', 'easkin', 'easmea', 'easpho', 'eastow', 'eawpew', 'eucdov', 'eursta', 'evegro', 'fiespa', 'fiscro', 'foxspa', 'gadwal', 'gcrfin', 'gnttow', 'gnwtea', 'gockin', 'gocspa', 'goleag', 'grbher3', 'grcfly', 'greegr', 'greroa', 'greyel', 'grhowl', 'grnher', 'grtgra', 'grycat', 'gryfly', 'haiwoo', 'hamfly', 'hergul', 'herthr', 'hoomer', 'hoowar', 'horgre', 'horlar', 'houfin', 'houspa', 'houwre', 'indbun', 'juntit1', 'killde', 'labwoo', 'larspa', 'lazbun', 'leabit', 'leafly', 'leasan', 'lecthr', 'lesgol', 'lesnig', 'lesyel', 'lewwoo', 'linspa', 'lobcur', 'lobdow', 'logshr', 'lotduc', 'louwat', 'macwar', 'magwar', 'mallar3', 'marwre', 'merlin', 'moublu', 'mouchi', 'moudov', 'norcar', 'norfli', 'norhar2', 'normoc', 'norpar', 'norpin', 'norsho', 'norwat', 'nrwswa', 'nutwoo', 'olsfly', 'orcwar', 'osprey', 'ovenbi1', 'palwar', 'pasfly', 'pecsan', 'perfal', 'phaino', 'pibgre', 'pilwoo', 'pingro', 'pinjay', 'pinsis', 'pinwar', 'plsvir', 'prawar', 'purfin', 'pygnut', 'rebmer', 'rebnut', 'rebsap', 'rebwoo', 'redcro', 'redhea', 'reevir1', 'renpha', 'reshaw', 'rethaw', 'rewbla', 'ribgul', 'rinduc', 'robgro', 'rocpig', 'rocwre', 'rthhum', 'ruckin', 'rudduc', 'rufgro', 'rufhum', 'rusbla', 'sagspa1', 'sagthr', 'savspa', 'saypho', 'scatan', 'scoori', 'semplo', 'semsan', 'sheowl', 'shshaw', 'snobun', 'snogoo', 'solsan', 'sonspa', 'sora', 'sposan', 'spotow', 'stejay', 'swahaw', 'swaspa', 'swathr', 'treswa', 'truswa', 'tuftit', 'tunswa', 'veery', 'vesspa', 'vigswa', 'warvir', 'wesblu', 'wesgre', 'weskin', 'wesmea', 'wessan', 'westan', 'wewpew', 'whbnut', 'whcspa', 'whfibi', 'whtspa', 'whtswi', 'wilfly', 'wilsni1', 'wiltur', 'winwre3', 'wlswar', 'wooduc', 'wooscj2', 'woothr', 'y00475', 'yebfly', 'yebsap', 'yehbla', 'yelwar', 'yerwar', 'yetvir']\nHOARD_DIR = \"/home/data/hoard/cbirdcall/\"\nDATASET_NAME = \"b1-model-1fold-attn-128-large-4epoch\"\n# DATASET_NAME = \"b1-model-1fold-attn-huge-clean\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_clip_sr(filename, sr=33075):\n    clip, sr_native = librosa.core.audio.__audioread_load(filename, offset=0.0, duration=None, dtype=np.float32)\n    clip = librosa.to_mono(clip)\n    if sr_native > 0:\n        clip = librosa.resample(clip, sr_native, sr, res_type='kaiser_fast')\n    return clip, sr\n\n\ndef normalize_relative(img):\n    return normalize(img, img.min(), img.max())\n\ndef normalize(x, min_value, max_value):\n    return (x - min_value) / (max_value - min_value)\n\ndef standardize_relative(x):\n    return standardize(x, np.mean(x), np.std(x))\n\ndef standardize(x, mean, sd):\n    return (x - mean) / sd\n\ndef cleaned_filename(filename):\n    if filename[-4:] == \".mp3\":\n        filename = filename[:-4]\n    \n    return filename\n\ndef zero_padded(n_windows, data):\n    fixed_length_data = np.zeros((n_windows,) + data.shape[1:])\n    fixed_length_data[:data.shape[0]] = data[:n_windows]\n    return fixed_length_data\n\ndef wrap_padded(required_length, data, axis=0):\n    while data.shape[axis] < required_length:\n        data = np.concatenate([data, data], axis=axis)\n\n    if axis == 0:\n        return data[:required_length]\n    if axis == 1:\n        return data[:, :required_length]\n    \n    raise ValueError(\"unexpected axis {}\".format(axis))\n\ndef load_hd5(filename, dataset_name, split=False):\n    filename = cleaned_filename(filename)\n\n    with h5py.File(filename + \".hd5\", 'r') as f:\n        data = f[dataset_name][()]\n    \n    sr = DEFAULT_SR\n\n    if split:\n        data = [np.squeeze(a) for a in np.split(data, data.shape[0], 0)]\n        sr = [DEFAULT_SR] * len(data)\n\n    return data, sr\n\ndef to_bird_integers(bird_index, birds):\n    labels = np.zeros(len(bird_index), dtype=np.int16)\n    for b in birds:\n        if b in bird_index:\n            labels[bird_index.index(b)] = 1\n    return labels\n\ndef get_bird_index(df):\n    bird_index = set()\n\n    for birds in df[\"ebird_code\"].values:\n        if isinstance(birds, str):\n            bird_index.add(birds)\n        else:\n            for bird in birds:\n                bird_index.add(bird)\n    \n    return [b for b in BIRD_LIST if b in bird_index] # now bird_index is sorted in BIRD_LIST order\n\n# ------------------------------------------------------------------------\n#\n#                           MODEL PERSISTANCE\n#\n# ------------------------------------------------------------------------\n\ndef model_file(path, name, fold):\n    if fold is not None:\n        return \"{}/{}_fold_{}\".format(path, name, fold)\n    return \"{}/{}_whole\".format(path, name)\n\ndef model_weights_file(path, name, fold):\n    if path[-1] == \"/\":\n        path = path[:-1]\n\n    if fold is not None:\n        return \"{}/{}_fold_{}/{}\".format(path, name, fold, name)\n    return \"{}/{}_whole/{}\".format(path, name, name)\n\ndef model_files_for(path, name, folds, include_final):\n    names = []\n\n    for i in range(folds):\n        names.append(model_file(path, name, i))\n    \n    if include_final:\n        names.append(model_file(path, name, fold=None))\n    \n    return names\n\ndef full_contents(path):\n    return [path + f for f in os.listdir(path)]\n\ndef timed(op, **kwargs):\n    start = datetime.datetime.now()\n\n    op(**kwargs)\n\n    print(\"Operation Duration: {}\".format(datetime.datetime.now() - start))\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Pipeline():\n    def __init__(self, processes, seed=987):\n        self.processes = processes\n        if seed is not None:\n            random.seed(seed)\n            np.random.seed(seed)\n    \n    def apply(self, samples, sampling_rates):\n        if not isinstance(samples, list):\n            samples = [samples]\n        if not isinstance(sampling_rates, list):\n            sampling_rates = [sampling_rates]\n\n        for proc in self.processes:\n            samples, sampling_rates = proc(samples, sampling_rates)\n\n        if len(samples) == 1:\n            samples = samples[0]\n            sampling_rates = sampling_rates[0]\n\n        return samples, sampling_rates  \nclass SplitInputPipeline(Pipeline):\n    def __init__(self, processes):\n        super().__init__(processes)\n    \n    def apply(self, samples, sampling_rates):\n        samples = [np.squeeze(a) for a in np.split(samples, samples.shape[0], 0)]\n\n        samples, sr = super().apply(samples, [sampling_rates] * len(samples))\n\n        samples = np.stack(samples)\n\n        if len(samples.shape) < 4:\n            samples = np.expand_dims(samples, 0)\n        \n        return samples, sr\n\n    @classmethod\n    def from_pipeline(cls, pipeline):\n        return cls(pipeline.processes)\nclass NoopPipeline(Pipeline):\n    def __init__(self):\n        super().__init__([])\n    \n    def apply(self, samples, sampling_rates):\n        return samples, sampling_rates\n\n# ------------------------------------------------------------------------------------------------------------------\n#\n#                                                   SPECTOGRAM PROCS\n#\n# ------------------------------------------------------------------------------------------------------------------\n\ndef sharpen(s, sr):\n    s = normalize_relative(np.squeeze(s))\n    s = Image.fromarray(np.uint8(s*255))\n    e = ImageEnhance.Sharpness(s.convert(\"RGB\"))\n    s = e.enhance(4)\n\n    return normalize_relative(np.array(s)), sr\n\ndef rand_crop(s, sr, dimensions):\n    x_offset = np.random.randint(0, s.shape[1]-dimensions[1])\n    y_offset = np.random.randint(0, s.shape[0]-dimensions[0])\n    return s[y_offset:y_offset+dimensions[0], x_offset:x_offset+dimensions[1], :], sr\n\ndef noop(s, sr, ):\n    return s, sr\n\ndef make_random_rescale_proc(x_prop, y_prop, shape=None):\n    def resize(s, sr):\n        new_x = s.shape[1] + np.random.randint(1, s.shape[1] * x_prop)\n        new_y = s.shape[0] + np.random.randint(1, s.shape[0] * x_prop)\n        resized = skimage.transform.resize(s, (new_y, new_x))\n        crop_shape = shape or s.shape\n        return rand_crop(resized, sr, crop_shape)\n    \n    return resize\n\ndef make_random_crop_proc(dimensions):\n    return functools.partial(rand_crop, dimensions=dimensions)\n\ndef make_split_spect_proc(overlap=0.1, interval_secs=None,  width=None, wrap=False):\n    assert overlap < 1\n    def split_spec_proc(spect, sr):\n        if spect.shape[1] < width:\n            return np.expand_dims(wrap_padded(width, spect, axis=1), 0), sr\n\n        segments = []\n        has_channels=True\n        if len(spect.shape) == 2:\n            has_channels = False\n            spect = np.expand_dims(spect, 2)\n\n        total_samples = spect.shape[1]\n        channels = spect.shape[2]\n        height = spect.shape[0]\n\n        if width:\n            interval = width\n        else:\n            interval = int(interval_secs * sr)\n        \n        hop = int(interval * (1 - overlap))\n\n        i=0\n        j=0\n        while i < total_samples and j < total_samples:\n            j = i + interval\n            if wrap:\n                nxt = spect[:, i:j, :]\n                pad_width = interval - nxt.shape[1]\n                nxt = np.concatenate([nxt, spect[:, :pad_width]], axis=1)\n            else:\n                nxt = np.zeros((height, interval, channels))\n                final_index = min(j, total_samples - i)\n\n                nxt[:, :final_index, :] = spect[:, i:j, :]\n\n            segments.append(nxt)\n            i = i + hop\n\n        final = np.stack(segments)\n        \n        if not has_channels:\n            final = np.squeeze(final, 3)\n\n        return final, sr\n    \n    return split_spec_proc\n\ndef to_db_proc(s, sr):\n    return librosa.power_to_db(s, ref=np.max), sr\n\ndef greyscale_proc(s, sr):\n    return np.repeat(s[..., np.newaxis], 3, -1), sr\n\ndef safe_spec_augment(s, **kwargs):\n    spec = s.copy()\n\n    return spec_augment(spec, **kwargs)\n\ndef spec_augment(s, max_masks, freq_mask_prop, time_mask_prop, filler, random_masks):\n    # source: https://www.kaggle.com/davids1992/specaugment-quick-implementation\n\n    n_masks = random.randint(1, max_masks) if random_masks else max_masks\n    for _ in range(n_masks):\n        all_frames_num, all_freqs_num = s.shape[:2]\n        freq_percentage = random.uniform(0.0, freq_mask_prop)\n        \n        num_freqs_to_mask = int(freq_percentage * all_freqs_num)\n        f0 = np.random.uniform(low=0.0, high=all_freqs_num - num_freqs_to_mask)\n        f0 = int(f0)\n        s[:, f0:f0 + num_freqs_to_mask] = filler\n\n        time_percentage = random.uniform(0.0, time_mask_prop)\n        \n        num_frames_to_mask = int(time_percentage * all_frames_num)\n        t0 = np.random.uniform(low=0.0, high=all_frames_num - num_frames_to_mask)\n        t0 = int(t0)\n        s[t0:t0 + num_frames_to_mask, :] = filler\n\n    return s\n\ndef make_spec_augment_proc(max_masks=2, freq_mask_prop=0.15, time_mask_prop=0.3, filler=0, random_masks=False):    \n    return lambda s, sr: (safe_spec_augment(s, max_masks=max_masks, freq_mask_prop=freq_mask_prop, time_mask_prop=time_mask_prop, filler=filler, random_masks=random_masks), sr)\n\n# ------------------------------------------------------------------------------------------------------------------\n#\n#                                                   WAVEFORM PROCS\n#\n# ------------------------------------------------------------------------------------------------------------------\n\ndef label_events(signal, window_size, step_size, label_fn, offset=None):\n    labels = []\n    start = 0\n    \n    if offset is not None:\n        start = offset\n\n    while start + window_size <= len(signal):\n        window = signal[start:start+window_size]\n        label = label_fn(window)\n        labels.append({\n            \"start\": start,\n            \"end\": start+window_size,\n            \"value\": label\n        })\n        start += step_size\n\n    return labels\n\ndef extract_call(signal, sr, window_seconds, top_n):\n    segments = []\n    window = sr * window_seconds\n    \n    if window > len(signal):\n        return signal, {\"start\": 0, \"end\": len(signal), \"value\": 0}\n    for i in range(window_seconds * 3):\n        offset = int(i * sr / 3)\n        labels = label_events(signal, window, window, lambda sig: np.sum(sig ** 2), offset)\n        segments.extend(labels)\n    \n    segments = sorted(segments, key=lambda x: x[\"value\"], reverse=True)\n    to_use = random.sample(segments[:top_n], 1)[0]\n\n    return signal[to_use[\"start\"]:to_use[\"end\"]], to_use\n\ndef make_extract_call_at_random_proc(n_seconds):\n    def extract_at_random(w, sr):\n        length = int(n_seconds * sr)\n        start = random.randint(0, len(w) - length)\n\n        return w[start:start+length], sr\n    return extract_at_random\n\ndef make_extract_call_proc(window_seconds, top_n):\n    def extract_call_proc(w, sr):\n        extract, _ = extract_call(w, sr, window_seconds, top_n)\n        return extract, sr\n    return extract_call_proc\n\ndef extract_start(w, sr, seconds):\n    return w[:int(seconds * sr)], sr\n\ndef longest(mels):\n    others = []\n    \n    if len(mels) == 1:\n        return mels[0], others\n    \n    l = mels[0]\n    for m in mels[1:]:\n        if m.shape[1] > l.shape[1]:\n            others.append(l)\n            l = m\n        else:\n            others.append(m)\n            \n    return l, others\n\ndef expand_proc(s, sr):\n    return np.expand_dims(s, 0), sr\n\n\ndef mel_mix_proc(spects, srs):\n    longest_mel, other_mels = longest(spects)\n\n    final_length = longest_mel.shape[1]\n\n    for m in other_mels:\n        offset = random.randint(0, final_length - m.shape[1])\n        m = m + longest_mel[:, offset:offset+m.shape[1], :] / 2        \n        longest_mel[:, offset:offset+m.shape[1], :] = m\n    \n    return longest_mel, srs[0]\n\n\ndef mean_mix_proc(spects, srs):\n    return np.mean(spects, axis=0), srs[0]\n\ndef make_mix_proc(weights=None):\n    if weights is not None:\n        assert np.sum(weights) == 1\n\n    def mix_ws(ws, srs):\n        count = len(ws)\n        mix = np.zeros(ws[0].shape)\n        for i, w in enumerate(ws):\n            if weights:\n                processed = w * weights[i]\n            else:\n                processed = w / count\n            mix += processed\n        \n        return [mix], [srs[0]]\n    \n    return mix_ws\n\ndef make_multi_scale_melspect_proc(ffts, hop_length, height, fmin=0):\n    def to_melspect(w, sr):\n        spects = []\n\n\n        for n_fft in ffts:\n            spect = librosa.feature.melspectrogram(y=w, sr=sr, n_fft=n_fft, hop_length=hop_length, n_mels=height, fmin=fmin)\n            spects.append(spect)\n        \n        return np.stack(spects, axis=2), sr\n    \n    return to_melspect\n\ndef make_multi_rep_proc(n_fft, hop_length, height, fmin=0):\n    def to_melspect(w, sr):\n        cq_hop_length = math.floor(hop_length / 2 ** 6) * 2 ** 6\n\n        mel =  librosa.feature.melspectrogram(y=w, sr=sr, n_fft=n_fft, hop_length=hop_length, n_mels=height, fmin=fmin)\n        mel = librosa.power_to_db(mel)\n        cq = librosa.cqt(w,sr=sr, hop_length=cq_hop_length, n_bins=height, bins_per_octave=math.ceil(height/7), fmin=fmin)[:, :mel.shape[1]]\n        cq = librosa.power_to_db(abs(cq))\n        ch = librosa.feature.chroma_stft(w, sr=sr, hop_length=hop_length, n_chroma=height) \n        ch = librosa.power_to_db(ch)\n\n        return np.stack([mel, cq, ch], axis=2), sr\n    \n    return to_melspect\n\ndef interwoven(spect, window_length, target_length):\n    if spect.shape[1] < window_length:\n        final = np.zeros((spect.shape[0], target_length))\n        final[:, :spect.shape[1]] = spect\n        return final\n\n    n_windows = math.ceil(target_length / window_length)\n    max_start = spect.shape[1] - window_length\n    hop = spect.shape[1]  / n_windows\n    diff = window_length - hop\n    spread = int(diff / 2)\n\n    final = []\n    start=0\n    j=0\n    for i in range(n_windows):\n        start = max(min(int(i * hop) - spread, max_start), 0)\n        j = max(start + window_length, window_length)\n        final.append(spect[:, start:j])\n    \n    return np.concatenate(final, axis=1)[:, :target_length]\n\ndef make_wrap_proc(required_length, axis):\n    def wrap_proc(s, sr):\n        s = wrap_padded(required_length, s, axis)\n        return s, sr\n    return wrap_proc\n\ndef interwoven_split(spect, window_length, n_windows):\n    if spect.shape[1] < window_length:\n        spect = wrap_padded(window_length, spect, axis=1)[:, :window_length]\n        return np.stack([spect] * n_windows)\n\n    max_start = spect.shape[1] - window_length\n    hop = spect.shape[1]  / n_windows\n    diff = window_length - hop\n    spread = int(diff / 2)\n\n    final = []\n    start=0\n    j=0\n    for i in range(n_windows):\n        start = max(min(int(i * hop) - spread, max_start), 0)\n        j = max(start + window_length, window_length)\n        final.append(spect[:, start:j])\n    \n    return np.stack(final, axis=0)\n\ndef make_multi_hop_melspect_proc(base_fft, hop, height, width, n_channels, overlap, fmin=0):\n    make_spect = make_split_spect_proc(overlap=overlap, interval_secs=None, width=width, wrap=True)\n\n    def to_melspect(w, sr):\n        spects = []\n        mel_hop = hop\n        fft = base_fft\n        n_windows = None\n\n        for i in range(n_channels):\n            spect = librosa.feature.melspectrogram(y=w, sr=sr, n_fft=fft, hop_length=mel_hop, n_mels=height, fmin=fmin)\n            spect = librosa.power_to_db(spect, ref=np.max)\n\n            if not n_windows:\n                if spect.shape[1] < width: # if the sound spectogram is shorter than the given window\n                    spect = wrap_padded(width, spect, axis=1)\n                    spect, _ = make_spect(spect, sr)\n                    return np.stack([spect, spect, spect], axis=3), sr\n\n                spect, _ = make_spect(spect, sr)\n                n_windows = spect.shape[0]\n            else:\n                spect = interwoven_split(spect, width, n_windows) \n\n            spects.append(spect)\n\n            mel_hop *= 2\n            fft *= 2\n\n        return np.stack(spects, axis=3), sr\n    \n    return to_melspect\n\ndef make_wav_to_melspect_proc(n_fft, hop_length, height, fmin=0):\n    return lambda w, sr: (librosa.feature.melspectrogram(y=w, sr=sr, n_fft=n_fft, hop_length=hop_length, n_mels=height, fmin=fmin), sr)\n\ndef make_stretch_proc(proportion):\n    proportion = 1 + proportion\n    return lambda w, sr: (librosa.core.resample(w, sr, int(sr * proportion)), int(sr * proportion))\n\ndef trim_proc(w, sr):\n    w, _ = librosa.effects.trim(w)\n    return w, sr\n    \ndef make_pitch_shift_proc(steps):\n    return lambda w, sr: librosa.effects.pitch_shift(w, sr, steps)\n\ndef make_shift_proc(proportion):\n    if proportion < 0:\n        return lambda w, sr: w[:int(w.shape[0]*proportion)]\n    \n    return lambda w, sr: w[int(w.shape[0]*proportion):]\n\n\n# ------------------------------------------------------------------------------------------------------------------\n#\n#                                                       GENERIC PROCS\n#\n# ------------------------------------------------------------------------------------------------------------------\n\ndef make_normalize_proc(min_val, max_val):\n    value_range = (max_val - min_val)\n    return lambda w, sr: ((w - min_val) / value_range, sr)\n\ndef make_standardize_proc(mean, sd):\n    return lambda w, sr: (standardize(w, mean, sd), sr)\n\ndef self_standardize_proc(w, sr):\n    return standardize_relative(w), sr\n\ndef make_sr_assertion_proc(expected_sr):\n    def assert_sr(x, sr):\n        if sr != expected_sr:\n            raise ValueError(\"expected sampling rate to be {}, got {}\".format(expected_sr, sr))\n        \n        return x, sr\n    return assert_sr\n\n# ------------------------------------------------------------------------------------------------------------------\n#\n#                                                       UTILS\n#\n# ------------------------------------------------------------------------------------------------------------------\n\ndef one_to_one(proc):\n    def apply(xs, srs):\n        new_xs = []\n        new_srs = []\n\n        for i, x in enumerate(xs):\n            new_x, new_sr = proc(x, srs[i])\n            new_xs.append(new_x)\n            new_srs.append(new_sr)\n        \n        return new_xs, new_srs\n    \n    return apply\n\ndef with_prob(proc, prob):\n    def apply_with_prob(w, sr):\n        if random.uniform(0, 1) >= prob:\n            proc(w, sr)\n        return w, sr\n    \n    return apply_with_prob\n\ndef one_to_many(proc):\n    def apply(xs, srs):\n        new_xs = []\n        new_srs = []\n\n        for i, x in enumerate(xs):\n            new_x, new_sr = proc(x, srs[i])\n            new_xs.extend(new_x)\n            new_srs.extend(new_sr)\n        \n        return new_xs, new_srs\n    \n    return apply\n\ndef many_to_one(proc):\n    def apply(xs, srs):\n        new_x, new_sr = proc(xs, srs)\n        \n        return [new_x], [new_sr]\n    \n    return apply","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nclass Audio():\n    def __init__(self, path, sample_rate):\n        self.audio, self.sr = get_clip_sr(path, sr=sample_rate)\n    \n    def get_interval(self, start, end):\n        assert end > start\n        return self.audio[int(start * self.sr):int(end * self.sr)]\n    \n    def __len__(self):\n        return int(self.audio.shape[0] / self.sr)\n    \nclass InferenceIterator():\n    def __init__(self, data_dir, df, sampling_rate, pipeline=None, save_dir=None, cache=None):\n        if data_dir[-1] != \"/\":\n            data_dir = data_dir + \"/\"\n        \n        self.df = df.reset_index()\n        self.data_dir = data_dir\n        self.index = 0\n        self.audio = None\n        self.audio_id = None\n        self.prev_end = 0\n        self.sampling_rate = sampling_rate\n        self.pipeline = pipeline\n        self.cache = cache if cache is not None else []\n        self.save_dir = save_dir\n        \n    def __iter__(self):\n        return self\n    \n    def reset(self):\n        self.index = 0\n\n    def __data_file(self, index):\n        return f\"rep_{index}.pkl\"\n\n    def get_next_sample(self):\n        if self.index >= len(self.df):\n            self.reset()\n            raise StopIteration\n\n        row = self.df.loc[self.index, :]\n\n        if row[\"audio_id\"] != self.audio_id:\n            filepath = self.data_dir + row[\"audio_id\"]\n            if os.path.exists(filepath + \".mp3\"):\n                filepath = filepath + \".mp3\"\n            else:\n                filepath = filepath + \".wav\"\n\n            self.audio = Audio(filepath, self.sampling_rate)\n            self.prev_end = 0\n            self.audio_id = row[\"audio_id\"]\n\n        end = row[\"seconds\"]\n        \n        prev_end = end - 5\n        sample = self.audio.get_interval(prev_end, end) \n        index = self.index\n\n        self.prev_end = end\n        self.index += 1\n\n        return sample, row, index\n\n    def __next__(self):\n        sample = []\n\n        if self.save_dir is not None and self.index in self.cache:\n            with open(self.save_dir + self.__data_file(self.index), 'rb') as file:\n                loaded_sample, loaded_row = pickle.load(file)\n\n            self.index += 1\n            return loaded_sample, loaded_row\n\n        while len(sample) < 300:\n            sample, row, index = self.get_next_sample()\n\n        if self.pipeline:\n            sample, _ = self.pipeline.apply(sample, self.sampling_rate)\n\n        if self.save_dir is not None and index not in self.cache: \n            with open(self.save_dir + self.__data_file(index), 'wb') as file:\n                pickle.dump((sample, row), file, pickle.HIGHEST_PROTOCOL)\n            self.cache.append(index)           \n        \n        return sample, row","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# pipeline = Pipeline([ # ends up with a single window that spans exactly 5 seconds\n#         one_to_one(trim_proc),\n#         one_to_one(self_standardize_proc),\n#         one_to_one(make_multi_scale_melspect_proc([8192, 4096, 1200], hop_length=690, height=240)),\n#         one_to_one(to_db_proc),\n#         one_to_one(make_split_spect_proc(overlap=0, interval_secs=None, width=240)),\n# ])\n\n\npipeline = Pipeline([ # ends up with a single window that spans exactly 5 seconds\n        one_to_one(trim_proc),\n        one_to_one(self_standardize_proc),\n        one_to_one(make_multi_scale_melspect_proc([8192, 4096, 1200], hop_length=690, height=128)),\n        one_to_one(make_wrap_proc(240, axis=1)),\n        one_to_one(to_db_proc),\n        one_to_one(expand_proc)\n])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_TEST_DIR = '../input/birdsong-recognition' if os.path.exists('../input/birdsong-recognition/test_audio') else '../input/birdcall-check'\nAUDIO_DIR = f'{BASE_TEST_DIR}/test_audio'\ndf_test = pd.read_csv(f'{BASE_TEST_DIR}/test.csv')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df12 = df_test[df_test.site.isin(['site_1', 'site_2'])]\n\ndf3 = df_test[df_test.site.isin(['site_3'])]\n\nnew_df3 = {\n    \"site\": [],\n    \"row_id\": [],\n    \"seconds\": [],\n    \"audio_id\": []\n}\n\nfor row in df3.values:\n    signal, sr = librosa.load(AUDIO_DIR + f\"/{row[-1]}.mp3\", sr=33075)    \n    signal_seconds = len(signal) / sr\n    start = 5.0\n    while start <= signal_seconds:\n        new_df3[\"site\"].append(row[0])\n        new_df3[\"row_id\"].append(f\"{row[1]}_{int(start)}\")\n        new_df3[\"seconds\"].append(start)\n        new_df3[\"audio_id\"].append(row[-1])  \n        start += 5.0\n\ndf3 = pd.DataFrame(new_df3)\ndel new_df3\n\ndata_df = pd.concat([df12, df3]).reset_index(drop=True)\ndata_df[\"labels\"] = [\"unknown\"] * len(data_df)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DummyModel():\n    def __init__(self, output_dim):\n        self.output_dim = output_dim\n    \n    def predict(self, X):\n        return np.random.rand(self.output_dim)\n\n# model = DummyModel(265)\n# output_dim = 265\n# model_fns = [\n#     lambda: DummyModel(output_dim),\n#     lambda: DummyModel(output_dim),\n#     lambda: DummyModel(output_dim),\n# ]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def single_ensemble(model_fns, data_dir, df, sr, pipeline, in_batch_size, **kwargs):\n    itr = InferenceIterator(data_dir, df, sr, pipeline=pipeline)\n\n    preds, ids, truth = ensemble(model_fns, itr, in_batch_size, **kwargs)    \n    preds = [np.squeeze(q) for q in np.split(np.mean(np.stack(preds), axis=0), len(ids))]\n\n    final_df = pd.DataFrame({'row_id': ids, 'preds': preds, \"truth\": truth})\n    \n    return final_df\n\ndef nocall_ensemble(nocall_model_fns, model_fns, data_dir, df, sr, pipeline, in_batch_size, **kwargs):\n    itr = InferenceIterator(data_dir, df, sr, pipeline=pipelinem)\n\n\n    nocall_preds, ids, truth = ensemble(nocall_model_fns, itr, in_batch_size, **kwargs)\n    nocall_preds = [np.squeeze(q) for q in np.split(np.mean(np.stack(nocall_preds), axis=0), len(ids))]   \n\n    nocall_df = pd.DataFrame({'row_id': ids, 'preds': nocall_preds, \"truth\": truth})\n    has_call = nocall_df[nocall_df[\"preds\"].apply(lambda x: np.argmax(x) == 1)][\"row_id\"].values\n    call_df = df[df[\"row_id\"].isin(has_call)].reset_index(drop=True)\n\n    print(f\"number of call rows: {len(call_df)}\")\n\n    itr = InferenceIterator(data_dir, call_df, sr, pipeline)\n\n    preds, ids, truth = ensemble(model_fns, itr, in_batch_size, **kwargs)    \n    preds = [np.squeeze(q) for q in np.split(np.mean(np.stack(preds), axis=0), len(call_df))]\n\n    final_df = pd.DataFrame({'row_id': ids, 'preds': preds, \"truth\": truth})\n    \n    return nocall_df, final_df\n\ndef ensemble(model_fns, itr, in_batch_size, verbose=0, **kwargs):\n    ids = []\n    truth = []\n    ensemble_preds = []\n    all_preds = []\n    batch = []\n    start = datetime.datetime.now()\n    \n    for i, make_model in enumerate(model_fns):\n        model = make_model()\n        model_preds = []\n        \n        for X, row in itr:\n            batch.append(np.squeeze(X))\n            if i == 0:\n                ids.append(row[\"row_id\"])\n                truth.append(row[\"labels\"])\n            if len(batch) == in_batch_size:\n                preds = model.predict(np.array(batch), **kwargs)\n                model_preds.extend(np.split(preds, len(batch)))\n                batch = []            \n                \n        if len(batch) > 0:\n            preds = model.predict(np.array(batch), **kwargs)\n            model_preds.extend(np.split(preds, len(batch)))\n            batch = []\n        if verbose == 1:\n            print(\"Operation Duration: {}\".format(datetime.datetime.now() - start))\n\n        ensemble_preds.append(model_preds)\n    \n    return ensemble_preds, ids, truth","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dir_name = f\"../input/{DATASET_NAME}/models/kaggle/tmp/\"\n# nocall_model_names = os.listdir(dir_name)\n# nocall_model_fns = [functools.partial(tf.keras.models.load_model, dir_name + f, compile=False, custom_objects={'F1Score': tfa.metrics.F1Score}) for f in nocall_model_names if NOCALL_HASH_SEG in f]\n\nmodel_names = os.listdir(dir_name)\nmodel_fns = [functools.partial(tf.keras.models.load_model, dir_name + f, compile=False, custom_objects={'F1Score': tfa.metrics.F1Score}) for f in model_names]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"calls_df = single_ensemble(\n    model_fns, \n    AUDIO_DIR,\n    data_df, \n    sr=33075, \n    pipeline=pipeline, \n    in_batch_size=256, \n    verbose=1,\n    workers=3\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_birds(bird_list, pred, threshold):\n    if len(bird_list) == 2:\n        return [bird_list[np.argmax(pred)]]\n    \n    if NOCALL in bird_list:\n        best_pred = np.argmax(pred)\n        \n        if bird_list[best_pred] == NOCALL:\n            return [NOCALL]\n    \n    bird_indices = np.where(pred > threshold)[0]\n    \n    if len(bird_indices) == 0:\n        return [NOCALL]\n    \n    return [bird_list[i] for i in bird_indices if bird_list[i] != NOCALL]\n\ndef to_list(pred, bird_list):\n    ary = [0] * len(bird_list)\n    for p in pred:\n        ary[bird_list.index(p)] = 1\n    return ary  ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"calls_df[\"set_pred\"] = calls_df[\"preds\"].apply(lambda x: get_birds(BIRD_LIST, x, THRESHOLD))\n\ndf = calls_df\n\ndf[\"num_p\"] = df[\"set_pred\"].apply(lambda x: to_list(x, [NOCALL] + BIRD_LIST))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[\"id\"] = df[\"row_id\"].apply(lambda x: x.split(\"_\")[2])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dfs3 = df[df[\"row_id\"].apply(lambda x: \"site_3\" in x)]\ndfs12 = df[df[\"row_id\"].apply(lambda x: not \"site_3\" in x)]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"group = dfs3.groupby(\"id\")\n\nsite3preds = {}\n\nfor idx, g in group:\n    final_preds = set()\n    for i, row in g.iterrows():\n        for p in row[\"set_pred\"]:\n            if p is not NOCALL:\n                final_preds.add(p)\n    if len(final_preds) == 0:\n        final_preds = [NOCALL]\n    \n    site3preds[\"site_3_\" +idx] = \" \".join(list(final_preds))\n    \n        ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dfs12[\"birds\"] = dfs12[\"set_pred\"].apply(lambda x: \" \".join(x))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = dfs12[[\"row_id\", \"birds\"]]\n\nfor key, value in site3preds.items():\n    submission = submission.append({\"row_id\": key, \"birds\": value}, ignore_index=True)\nsubmission.tail(20)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission[\"birds\"].value_counts()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}