{"cells":[{"metadata":{},"cell_type":"markdown","source":"I've released [this crazingly fast (< 10 mins) kernel](https://www.kaggle.com/kneroma/inference-tpu-rfcx-audio-detection-fast) which uses a set of pre-computed MFCCs. \n\nThe problem is that those MFCCs are static, and if you change any params (**DURATION**, **STRIDE**, ...), you can no more use them. This is not fair. I will release my code that can help  you re-computing them whenever you need.\n\n* I use **joblib** to parallelize the computations, so it must require less than 1 hour to compute the MFCCs for the whole test dataset, and just 30 mins if STRIDE = DURATION\n* I directly use **soundfile** to read audios instead of **librosa** as soundfile is faster"},{"metadata":{},"cell_type":"markdown","source":"<h3><font color=\"blue\">Is this kernel useful for you ? Don't forget upvoting it, it really  motivates me in enhancing my work and sharing it with you :)</h3></font>"},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2020-12-27T08:05:00.353947Z","iopub.status.busy":"2020-12-27T08:05:00.351498Z","iopub.status.idle":"2020-12-27T08:05:03.624446Z","shell.execute_reply":"2020-12-27T08:05:03.62339Z"},"id":"kSCcqxf0c_O7","papermill":{"duration":3.295502,"end_time":"2020-12-27T08:05:03.624573","exception":false,"start_time":"2020-12-27T08:05:00.329071","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"import numpy as np\nimport librosa as lb\nimport soundfile as sf\nimport pandas as pd\nfrom pathlib import Path\n\nfrom tqdm.notebook import tqdm\n\n\nimport time","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-12-27T08:05:03.664229Z","iopub.status.busy":"2020-12-27T08:05:03.663369Z","iopub.status.idle":"2020-12-27T08:05:03.665921Z","shell.execute_reply":"2020-12-27T08:05:03.665517Z"},"id":"rJhYZVIDc_O9","papermill":{"duration":0.024061,"end_time":"2020-12-27T08:05:03.666018","exception":false,"start_time":"2020-12-27T08:05:03.641957","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"NUM_CLASSES = 24\nSR = 32_000\nDURATION = 10\nSTRIDE = DURATION//2\n\nBATCH_START = 0\nBATCH_SIZE = 400\n\nNJOBS = 2\n\nTEST_AUDIO_ROOT = Path(\"../input/rfcx-species-audio-detection/test\")\nTEST_MFCC_SAVE_ROOT = Path(f\"test_mfcc_d{DURATION}_s{STRIDE}_sr{SR}_{BATCH_START:04d}_{BATCH_START+BATCH_SIZE:04d}\")\nTEST_MFCC_SAVE_ROOT.mkdir(exist_ok=True)","execution_count":null,"outputs":[]},{"metadata":{"id":"pbqOPUExc_O-","papermill":{"duration":0.016845,"end_time":"2020-12-27T08:05:03.699716","exception":false,"start_time":"2020-12-27T08:05:03.682871","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-12-27T08:05:03.740251Z","iopub.status.busy":"2020-12-27T08:05:03.73975Z","iopub.status.idle":"2020-12-27T08:05:03.743651Z","shell.execute_reply":"2020-12-27T08:05:03.743234Z"},"id":"4fwNhdbJc_O-","papermill":{"duration":0.02684,"end_time":"2020-12-27T08:05:03.743735","exception":false,"start_time":"2020-12-27T08:05:03.716895","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"class MelSpecComputer:\n    def __init__(self, sr, n_mels, fmin, fmax):\n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax\n\n    def __call__(self, y):\n\n        melspec = lb.feature.melspectrogram(\n            y, sr=self.sr, n_mels=self.n_mels, fmin=self.fmin, fmax=self.fmax,\n        )\n\n        melspec = lb.power_to_db(melspec).astype(np.float32)\n        return melspec","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-12-27T08:05:03.791803Z","iopub.status.busy":"2020-12-27T08:05:03.790616Z","iopub.status.idle":"2020-12-27T08:05:03.793062Z","shell.execute_reply":"2020-12-27T08:05:03.793525Z"},"id":"Nk0haSTIc_O_","papermill":{"duration":0.033047,"end_time":"2020-12-27T08:05:03.79362","exception":false,"start_time":"2020-12-27T08:05:03.760573","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"def mono_to_color(X, eps=1e-6, mean=None, std=None):\n    X = np.stack([X, X, X], axis=-1)\n\n    # Standardize\n    mean = mean or X.mean()\n    std = std or X.std()\n    X = (X - mean) / (std + eps)\n\n    # Normalize to [0, 255]\n    _min, _max = X.min(), X.max()\n\n    if (_max - _min) > eps:\n        V = np.clip(X, _min, _max)\n        V = 255 * (V - _min) / (_max - _min)\n        V = V.astype(np.uint8)\n    else:\n        V = np.zeros_like(X, dtype=np.uint8)\n\n    return V\n\n\ndef normalize(image, mean=None, std=None):\n    image = image / 255.0\n    if mean is not None and std is not None:\n        image = (image - mean) / std\n    return np.moveaxis(image, 2, 0).astype(np.float32)\n\n\ndef crop_or_pad(y, length, sr, is_train=True):\n    if len(y) < length:\n        y = np.concatenate([y, np.zeros(length - len(y))])\n    elif len(y) > length:\n        if not is_train:\n            start = 0\n        else:\n            start = np.random.randint(len(y) - length)\n\n        y = y[start:start + length]\n\n    y = y.astype(np.float32, copy=False)\n\n    return y","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-12-27T08:05:03.882841Z","iopub.status.busy":"2020-12-27T08:05:03.879358Z","iopub.status.idle":"2020-12-27T08:05:03.884996Z","shell.execute_reply":"2020-12-27T08:05:03.885392Z"},"id":"Q2MieafPc_PB","papermill":{"duration":0.035536,"end_time":"2020-12-27T08:05:03.88549","exception":false,"start_time":"2020-12-27T08:05:03.849954","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"class RFCXDataset:\n\n    def __init__(self, data, sr, n_mels=128, fmin=0, fmax=None, num_classes=NUM_CLASSES, root=None):\n\n        self.data = data\n        \n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax or self.sr//2\n\n\n        self.num_classes = num_classes\n#         self.duration = duration\n#         self.audio_length = self.duration*self.sr\n        \n        self.root =  root or TEST_AUDIO_ROOT\n\n        self.mel_spec_computer = MelSpecComputer(sr=self.sr, n_mels=self.n_mels, fmin=self.fmin, fmax=self.fmax)\n        \n        self.res_type = \"kaiser_best\"\n\n\n    def __len__(self):\n        return len(self.data)\n    \n    def load(self, record):\n        y, _ = lb.load(self.root.joinpath(record).with_suffix(\".flac\").as_posix(), sr=self.sr, res_type=self.res_type)\n        return y\n    \n    def load2(self, record):\n        y, orig_sr = sf.read(self.root.joinpath(record).with_suffix(\".flac\").as_posix())\n        y = lb.resample(y, orig_sr=orig_sr, target_sr=self.sr, res_type=self.res_type)\n        return y\n    \n    def read_index(self, idx):\n        d = self.data.iloc[idx]\n        record = d[\"recording_id\"]\n        \n        y = self.load2(record)\n        \n        window = DURATION*self.sr\n        stride = STRIDE*self.sr\n            \n        y = np.stack([y[i:i+window] for i in range(0, 60*self.sr+stride-window, stride)])\n\n#         y = crop_or_pad(y, self.audio_length, sr=self.sr)\n        \n        return y\n            \n    def process(self, y):\n        melspec = self.mel_spec_computer(y) \n        image = mono_to_color(melspec)\n        image = normalize(image, mean=None, std=None)\n        return image\n\n    def __getitem__(self, idx):\n\n        y = self.read_index(idx)\n        \n        image = np.stack([self.process(_y) for _y in y])\n\n        return image\n    \n    def to_mfcc(self, idx):\n        record = self.data.iloc[idx][\"recording_id\"]\n        mfcc = self[idx]\n        \n        np.save(TEST_MFCC_SAVE_ROOT.joinpath(record).with_suffix(\".npy\").as_posix(), mfcc)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-12-27T08:05:03.923755Z","iopub.status.busy":"2020-12-27T08:05:03.923074Z","iopub.status.idle":"2020-12-27T08:05:03.925861Z","shell.execute_reply":"2020-12-27T08:05:03.925426Z"},"id":"xV9UJt8Mc_PC","papermill":{"duration":0.023364,"end_time":"2020-12-27T08:05:03.925939","exception":false,"start_time":"2020-12-27T08:05:03.902575","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# def get_duration(audio_name, root=TEST_AUDIO_ROOT):\n#     return lb.get_duration(filename=root.joinpath(audio_name).with_suffix(\".flac\"))","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-12-27T08:05:03.964403Z","iopub.status.busy":"2020-12-27T08:05:03.963877Z","iopub.status.idle":"2020-12-27T08:05:18.438105Z","shell.execute_reply":"2020-12-27T08:05:18.437604Z"},"id":"rVwlOrbxc_PD","outputId":"9b53ec99-634a-4b30-f9b5-75036184482b","papermill":{"duration":14.495358,"end_time":"2020-12-27T08:05:18.438221","exception":false,"start_time":"2020-12-27T08:05:03.942863","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"%%time\n\ndata = pd.DataFrame({\n    \"recording_id\": [path.stem for path in Path(TEST_AUDIO_ROOT).glob(\"*.flac\")],\n})\n\nprint(data.shape)\ndata.head()","execution_count":null,"outputs":[]},{"metadata":{"id":"x1WwgO0sc_PF","papermill":{"duration":0.018712,"end_time":"2020-12-27T08:05:18.74522","exception":false,"start_time":"2020-12-27T08:05:18.726508","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-12-27T08:05:18.78594Z","iopub.status.busy":"2020-12-27T08:05:18.785444Z","iopub.status.idle":"2020-12-27T08:05:18.789579Z","shell.execute_reply":"2020-12-27T08:05:18.788896Z"},"id":"DajVt4q7c_PF","papermill":{"duration":0.026055,"end_time":"2020-12-27T08:05:18.789711","exception":false,"start_time":"2020-12-27T08:05:18.763656","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"ds = RFCXDataset(data=data, sr=SR)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-12-27T08:05:18.831385Z","iopub.status.busy":"2020-12-27T08:05:18.830615Z","iopub.status.idle":"2020-12-27T08:05:22.359206Z","shell.execute_reply":"2020-12-27T08:05:22.358244Z"},"id":"a4z6e5v8c_PF","outputId":"6bb5a3ef-c74e-4ded-d9f9-a861a2c0ae04","papermill":{"duration":3.551127,"end_time":"2020-12-27T08:05:22.359324","exception":false,"start_time":"2020-12-27T08:05:18.808197","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"%%time\n\nx = ds[1]\nprint(x.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"x.nbytes/(1024**2)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# %timeit ds.to_mfcc(0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import joblib\npool = joblib.Parallel(n_jobs=NJOBS)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mapper = joblib.delayed(ds.to_mfcc)\ntasks = []\nfor idx in range(BATCH_START, min(BATCH_START + BATCH_SIZE, len(ds))):\n# for idx in range(25):\n    tasks.append(mapper(idx))\n    \nres = pool(tqdm(tasks))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}