{"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":"markdown","source":"# Train and validtion split\n\nThis kernel takes the train_metadata.csv and splits it to train and validation sets. Each sample in the sets consists of a 5-seconds segment from an audio file.\n\nThis kernel works like this:\n1. Each bird has some audio files. Split those audio files to train and validation.\n2. For each audio file, split it to segments of 5 seconds.\n3. The data is extremely unbalanced - I sample again and again segments in order to balance the data (I trust the augmentation so that the network will *not* see the same example over and over","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport torchaudio\nimport json\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-03-23T08:53:38.305275Z","iopub.execute_input":"2022-03-23T08:53:38.305594Z","iopub.status.idle":"2022-03-23T08:53:38.310363Z","shell.execute_reply.started":"2022-03-23T08:53:38.305559Z","shell.execute_reply":"2022-03-23T08:53:38.309137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_DIR = '../input/birdclef-2022'\nTRAIN_RATIO = 2.0/3.0\n\nmetadata = pd.read_csv(f'{BASE_DIR}/train_metadata.csv')\nwith open(f'{BASE_DIR}/scored_birds.json') as json_file:\n    scored_birds = set(json.load(json_file))","metadata":{"execution":{"iopub.status.busy":"2022-03-23T08:53:38.316513Z","iopub.execute_input":"2022-03-23T08:53:38.316750Z","iopub.status.idle":"2022-03-23T08:53:38.393458Z","shell.execute_reply.started":"2022-03-23T08:53:38.316705Z","shell.execute_reply":"2022-03-23T08:53:38.392710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Phase 1 - split audio files to train and validation","metadata":{}},{"cell_type":"code","source":"def sample(df: pd.DataFrame):\n    frac = TRAIN_RATIO\n    if len(df) == 2:\n        frac = 0.5\n\n    if len(df) > 1:\n        train = df.sample(frac=frac, random_state=123)\n        val = df.drop(train.index)\n        train['start'] = train['end'] = val['start'] = val['end'] = np.nan\n    else:\n        # In case a bird has only only one file, split it in the middle of the file\n        frames, _ = torchaudio.load('../input/birdclef-2022/train_audio/' + df.iloc[0].filename)\n        n_frames = frames.shape[1]\n        train = df.copy()\n        val = df.copy()\n        train['start'] = 0\n        train['end'] = val['start'] = int(TRAIN_RATIO*n_frames)\n        val['end'] = n_frames\n\n    return train, val\n\n\n# Split the metadata dataframe into 2 dataframes - train and validation\ndef train_val_split(metadata_df, scored_birds):\n    train_df_arr = []\n    val_df_arr = []\n\n    # First split all the scored birds one by one\n    for bird in scored_birds:\n        curr_metadata = metadata_df[metadata_df.primary_label == bird]\n        curr_train, curr_val = sample(curr_metadata)\n        train_df_arr.append(curr_train)\n        val_df_arr.append(curr_val)\n        metadata_df = metadata_df.drop(curr_metadata.index)\n\n    # Now split all the other birds metadata.\n    curr_train, curr_val = sample(metadata_df)\n    train_df_arr.append(curr_train)\n    val_df_arr.append(curr_val)\n    return pd.concat(train_df_arr), pd.concat(val_df_arr)","metadata":{"execution":{"iopub.status.busy":"2022-03-23T08:53:38.395624Z","iopub.execute_input":"2022-03-23T08:53:38.396237Z","iopub.status.idle":"2022-03-23T08:53:38.409989Z","shell.execute_reply.started":"2022-03-23T08:53:38.396173Z","shell.execute_reply":"2022-03-23T08:53:38.409104Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_metadata, val_metadata = train_val_split(metadata, scored_birds)","metadata":{"execution":{"iopub.status.busy":"2022-03-23T08:53:38.411665Z","iopub.execute_input":"2022-03-23T08:53:38.412029Z","iopub.status.idle":"2022-03-23T08:53:39.358320Z","shell.execute_reply.started":"2022-03-23T08:53:38.411986Z","shell.execute_reply":"2022-03-23T08:53:39.357583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Phase 2 - split audio files to train and validation\nAt this point, I assume that most of the segments will contain the sound of the relevant bird. This may not be correct to all of the segments, but this is a point to begin with.","metadata":{}},{"cell_type":"code","source":"def split_row_to_segments(row):\n    frames, frame_rate = torchaudio.load('../input/birdclef-2022/train_audio/' + row.filename)\n    start = 0\n    end = frames.shape[1]\n    if row['start'] is not None and not np.isnan(row['start']):\n        start = int(row['start'])\n    if row['end'] is not None and not np.isnan(row['end']):\n        end = int(row['end'])\n\n    segments = [s for s in range(start, end, 5*frame_rate)]\n    # If the last segment is less than a second seconds, remove it\n    if end - segments[-1] < frame_rate:\n        del segments[-1]\n\n    res = []\n    for s in segments:\n        row_res = \\\n            {\n                'primary_label': row.primary_label,\n                'secondary_labels': row.secondary_labels,\n                'filename': row.filename,\n                'start': s,\n                'end': min(s + 5 * frame_rate, frames.shape[1]),\n                'rating': row.rating\n            }\n        res.append(row_res)\n\n    return pd.DataFrame(res)\n\n\ndef split_to_segments(metadata_df):\n    segments_df_arr = []\n    for _, row in tqdm(metadata_df.iterrows(), total=len(metadata_df)):\n        segments_df_arr.append(split_row_to_segments(row))\n    return pd.concat(segments_df_arr, ignore_index=True)","metadata":{"execution":{"iopub.status.busy":"2022-03-23T08:53:39.360642Z","iopub.execute_input":"2022-03-23T08:53:39.361129Z","iopub.status.idle":"2022-03-23T08:53:39.373081Z","shell.execute_reply.started":"2022-03-23T08:53:39.361086Z","shell.execute_reply":"2022-03-23T08:53:39.372213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_samples = split_to_segments(train_metadata)\nval_samples = split_to_segments(val_metadata)","metadata":{"execution":{"iopub.status.busy":"2022-03-23T08:53:39.374357Z","iopub.execute_input":"2022-03-23T08:53:39.374610Z","iopub.status.idle":"2022-03-23T09:21:16.569260Z","shell.execute_reply.started":"2022-03-23T08:53:39.374584Z","shell.execute_reply":"2022-03-23T09:21:16.568313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Phase 3 - sample again and again segments in order to balance the data.","metadata":{}},{"cell_type":"code","source":"def count_num_primary(samples, scored_birds):\n    num_primary = {bird: 0 for bird in scored_birds}\n    for bird, group in samples.groupby('primary_label'):\n        if bird in scored_birds:\n            num_primary[bird] = len(group)\n    return num_primary","metadata":{"execution":{"iopub.status.busy":"2022-03-23T09:21:16.571772Z","iopub.execute_input":"2022-03-23T09:21:16.572042Z","iopub.status.idle":"2022-03-23T09:21:16.579780Z","shell.execute_reply.started":"2022-03-23T09:21:16.572010Z","shell.execute_reply":"2022-03-23T09:21:16.578807Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def over_sample(samples, scored_birds):\n    num_primary = count_num_primary(samples, scored_birds)\n    mx = np.max(list(num_primary.values()))\n    df_arr = []\n    for bird, group in samples.groupby('primary_label'):\n        if bird not in scored_birds:\n            df_arr.append(group)\n        else:\n            n_multiply = mx // num_primary[bird]\n            mod = mx % num_primary[bird]\n            df_arr += [group]*n_multiply + [group.sample(n=mod)]\n    res = pd.concat(df_arr, ignore_index=True)\n\n    return res","metadata":{"execution":{"iopub.status.busy":"2022-03-23T09:21:16.581119Z","iopub.execute_input":"2022-03-23T09:21:16.581358Z","iopub.status.idle":"2022-03-23T09:21:16.593559Z","shell.execute_reply.started":"2022-03-23T09:21:16.581330Z","shell.execute_reply":"2022-03-23T09:21:16.592847Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Print the inbalance of the data before\nprint(count_num_primary(train_samples, scored_birds))","metadata":{"execution":{"iopub.status.busy":"2022-03-23T09:23:27.322575Z","iopub.execute_input":"2022-03-23T09:23:27.322876Z","iopub.status.idle":"2022-03-23T09:23:27.353277Z","shell.execute_reply.started":"2022-03-23T09:23:27.322847Z","shell.execute_reply":"2022-03-23T09:23:27.352313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_samples = over_sample(train_samples, scored_birds)\nval_samples_oversampled = over_sample(val_samples, scored_birds)","metadata":{"execution":{"iopub.status.busy":"2022-03-23T09:23:35.018137Z","iopub.execute_input":"2022-03-23T09:23:35.019130Z","iopub.status.idle":"2022-03-23T09:23:36.452435Z","shell.execute_reply.started":"2022-03-23T09:23:35.019078Z","shell.execute_reply":"2022-03-23T09:23:36.451578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Print the balanceness of the data after\nprint(count_num_primary(train_samples, scored_birds))","metadata":{"execution":{"iopub.status.busy":"2022-03-23T09:23:37.896273Z","iopub.execute_input":"2022-03-23T09:23:37.896747Z","iopub.status.idle":"2022-03-23T09:23:37.929518Z","shell.execute_reply.started":"2022-03-23T09:23:37.896698Z","shell.execute_reply":"2022-03-23T09:23:37.928425Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_samples.to_csv('train_samples.csv', index=False)\nval_samples.to_csv('val_samples.csv', index=False)\nval_samples_oversampled.to_csv('val_samples_oversampled.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-03-23T09:24:12.372106Z","iopub.execute_input":"2022-03-23T09:24:12.372413Z","iopub.status.idle":"2022-03-23T09:24:13.682977Z","shell.execute_reply.started":"2022-03-23T09:24:12.372382Z","shell.execute_reply":"2022-03-23T09:24:13.682327Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### farther work\nThis is a point to start training, but there are still improvments that should be done:\n\n- In phase 1: Split the train and validation audio files, so that the training set will contain at least one file per a bird voice type (\"type\" column in train_metadata.csv)\n- In phase 2, try to filter out segments containing only background.","metadata":{}}]}