{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":70203,"databundleVersionId":8068726,"sourceType":"competition"},{"sourceId":6119,"sourceType":"modelInstanceVersion","modelInstanceId":4602},{"sourceId":6127,"sourceType":"modelInstanceVersion","modelInstanceId":4598}],"dockerImageVersionId":30699,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nos.environ[\"KERAS_BACKEND\"] = \"jax\"  # \"jax\" or \"tensorflow\" or \"torch\" \n\nimport keras_cv\nimport keras\nimport keras.backend as K\nimport tensorflow as tf\nimport tensorflow_io as tfio\n\nimport numpy as np \nimport pandas as pd\n\nfrom glob import glob\nfrom tqdm import tqdm\n\nimport librosa\nimport IPython.display as ipd\nimport librosa.display as lid\n\nimport matplotlib.pyplot as plt\nimport matplotlib as mpl\n\ncmap = mpl.cm.get_cmap('coolwarm')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_metadata_csv_path = \"/kaggle/input/birdclef-2024/train_metadata.csv\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(train_metadata_csv_path)\ndf.head(10)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ebird_taxonomy_path = \"/kaggle/input/birdclef-2024/eBird_Taxonomy_v2021.csv\"\nebird_taxonomy = pd.read_csv(ebird_taxonomy_path)\nebird_taxonomy.head(10)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"primary_values = df['primary_label'].unique()\nprint(primary_values), print(primary_values.size)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_PATH = '/kaggle/input/birdclef-2024'","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CFG:\n    seed = 42\n    \n    # Input image size and batch size\n    img_size = [128, 384]\n    batch_size = 64\n    \n    # Audio duration, sample rate, and length\n    duration = 15 # second\n    sample_rate = 32000\n    audio_len = duration*sample_rate\n    \n    # STFT parameters\n    nfft = 2028\n    window = 2048\n    hop_length = audio_len // (img_size[1] - 1)\n    fmin = 20\n    fmax = 16000\n    \n    # Number of epochs, model name\n    epochs = 20\n    preset = 'efficientnetv2_l'\n    \n    # Data augmentation parameters\n    augment=True\n\n    # Class Labels for BirdCLEF 24\n    class_names = sorted(os.listdir('/kaggle/input/birdclef-2024/train_audio/'))\n    num_classes = len(class_names)\n    class_labels = list(range(num_classes))\n    label2name = dict(zip(class_labels, class_names))\n    name2label = {v:k for k,v in label2name.items()}","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(f'{BASE_PATH}/train_metadata.csv')\ndf['filepath'] = BASE_PATH + '/train_audio/' + df.filename\ndf['target'] = df.primary_label.map(CFG.name2label)\ndf['filename'] = df.filepath.map(lambda x: x.split('/')[-1])\ndf['xc_id'] = df.filepath.map(lambda x: x.split('/')[-1].split('.')[0])\n\n# Display rwos\ndf.head(2)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def load_audio(filepath):\n#     audio, sr = librosa.load(filepath)\n#     print(sr)\n#     return audio, sr\n# audio, sr = load_audio(df.iloc[0]['filepath'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def load_audio_check(filepath):\n#     audio, sr = librosa.load(filepath)\n#     print(audio.shape)\n#     print(sr)\n#     length_ms = (len(audio) / sr) * 1000\n#     return int(length_ms)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load_audio_check(df.iloc[0]['filepath'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# audio.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# display(ipd.Audio(audio, rate=22050))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import soundfile as sf\naudio, sr = librosa.load(df.iloc[0]['filepath'])\naudio_data, samplerate = sf.read(df.iloc[0]['filepath'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(sr , samplerate)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(ipd.Audio(audio, rate=32000))\ndisplay(ipd.Audio(audio_data, rate=samplerate))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(audio_data.shape)\nprint(audio.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# import soundfile as sf\n\n# def get_audio_length_ms(file_path):\n#     audio_data, samplerate = sf.read(file_path)\n#     display(ipd.Audio(audio_data, rate=32000))\n#     print(samplerate)\n#     print(audio_data.shape)\n#     print((audio_data.dtype))\n#     length_ms = (len(audio_data) / samplerate) * 1000\n#     return int(length_ms)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get_audio_length_ms(df.iloc[0]['filepath'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\nimport cartopy.crs as ccrs\nfrom itertools import cycle\n\n\nmin_longitude = df['longitude'].min()\nmax_longitude = df['longitude'].max()\nmin_latitude = df['latitude'].min()\nmax_latitude = df['latitude'].max()\n\n# Create a map\nfig, ax = plt.subplots(figsize=(30, 20), subplot_kw={'projection': ccrs.PlateCarree()})\nax.set_extent([min_longitude, max_longitude, min_latitude, max_latitude])\n\n# Define a color cycle for different primary labels\ncolor_cycle = cycle(plt.cm.tab10.colors)\n\n# Plot data points for each primary label with a different color\nfor label, group in df.groupby('primary_label'):\n    color = next(color_cycle)\n    ax.scatter(group['longitude'], group['latitude'], transform=ccrs.PlateCarree(), label=label, color=color, alpha=0.5)\n\n# Add coastlines\nax.coastlines()\n\n# Add legend\nplt.legend()\n\nplt.title('Data Points on Map')\nplt.show()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Import required packages\nfrom sklearn.model_selection import train_test_split\n\ntrain_df, valid_df = train_test_split(df, test_size=0.2)\n\nprint(f\"Num Train: {len(train_df)} | Num Valid: {len(valid_df)}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train_df = pd.concat([train_df]*2, ignore_index = True)\n\n# print(len(train_df))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Decodes Audio\ndef build_decoder(with_labels=True, dim=1024):\n    def get_audio(filepath):\n        file_bytes = tf.io.read_file(filepath)\n        audio = tfio.audio.decode_vorbis(file_bytes)  # decode .ogg file\n        audio = tf.cast(audio, tf.float32)\n        if tf.shape(audio)[1] > 1:  # stereo -> mono\n            audio = audio[..., 0:1]\n        audio = tf.squeeze(audio, axis=-1)\n        return audio\n\n    def crop_or_pad(audio, target_len, pad_mode=\"constant\"):\n        audio_len = tf.shape(audio)[0]\n        diff_len = abs(\n            target_len - audio_len\n        )  # find difference between target and audio length\n        if audio_len < target_len:  # do padding if audio length is shorter\n            pad1 = tf.random.uniform([], maxval=diff_len, dtype=tf.int32)\n            pad2 = diff_len - pad1\n            audio = tf.pad(audio, paddings=[[pad1, pad2]], mode=pad_mode)\n        elif audio_len > target_len:  # do cropping if audio length is larger\n            idx = tf.random.uniform([], maxval=diff_len, dtype=tf.int32)\n            audio = audio[idx : (idx + target_len)]\n        return tf.reshape(audio, [target_len])\n\n    def apply_preproc(spec):\n        # Standardize\n        mean = tf.math.reduce_mean(spec)\n        std = tf.math.reduce_std(spec)\n        spec = tf.where(tf.math.equal(std, 0), spec - mean, (spec - mean) / std)\n\n        # Normalize using Min-Max\n        min_val = tf.math.reduce_min(spec)\n        max_val = tf.math.reduce_max(spec)\n        spec = tf.where(\n            tf.math.equal(max_val - min_val, 0),\n            spec - min_val,\n            (spec - min_val) / (max_val - min_val),\n        )\n        return spec\n\n    def get_target(target):\n        target = tf.reshape(target, [1])\n        target = tf.cast(tf.one_hot(target, CFG.num_classes), tf.float32)\n        target = tf.reshape(target, [CFG.num_classes])\n        return target\n\n    def decode(path):\n        # Load audio file\n        audio = get_audio(path)\n        # Crop or pad audio to keep a fixed length\n        audio = crop_or_pad(audio, dim)\n        # Audio to Spectrogram\n        spec = keras.layers.MelSpectrogram(\n            num_mel_bins=CFG.img_size[0],\n            fft_length=CFG.nfft,\n            sequence_stride=CFG.hop_length,\n            sampling_rate=CFG.sample_rate,\n        )(audio)\n        # Apply normalization and standardization\n        spec = apply_preproc(spec)\n        # Spectrogram to 3 channel image (for imagenet)\n        spec = tf.tile(spec[..., None], [1, 1, 3])\n        spec = tf.reshape(spec, [*CFG.img_size, 3])\n        return spec\n\n    def decode_with_labels(path, label):\n        label = get_target(label)\n        return decode(path), label\n\n    return decode_with_labels if with_labels else decode","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def build_augmenter():\n    augmenters = [\n        keras_cv.layers.MixUp(alpha=0.4),\n        keras_cv.layers.RandomCutout(height_factor=(1.0, 1.0),\n                                     width_factor=(0.06, 0.12)), # time-masking\n        keras_cv.layers.RandomCutout(height_factor=(0.06, 0.1),\n                                     width_factor=(1.0, 1.0)), # freq-masking\n    ]\n    \n    def augment(img, label):\n        data = {\"images\":img, \"labels\":label}\n        for augmenter in augmenters:\n            if tf.random.uniform([]) < 0.35:\n                data = augmenter(data, training=True)\n        return data[\"images\"], data[\"labels\"]\n    \n    return augment","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def build_dataset(paths, labels=None, batch_size=32, \n                  decode_fn=None, augment_fn=None, cache=True,\n                  augment=False, shuffle=2048):\n\n    if decode_fn is None:\n        decode_fn = build_decoder(labels is not None, dim=CFG.audio_len)\n\n    if augment_fn is None:\n        augment_fn = build_augmenter()\n        \n    AUTO = tf.data.experimental.AUTOTUNE\n    slices = (paths,) if labels is None else (paths, labels)\n    ds = tf.data.Dataset.from_tensor_slices(slices)\n    ds = ds.map(decode_fn, num_parallel_calls=AUTO)\n    ds = ds.cache() if cache else ds\n    if shuffle:\n        opt = tf.data.Options()\n        ds = ds.shuffle(shuffle, seed=CFG.seed)\n        opt.experimental_deterministic = False\n        ds = ds.with_options(opt)\n    ds = ds.batch(batch_size, drop_remainder=True)\n    ds = ds.map(augment_fn, num_parallel_calls=AUTO) if augment else ds\n    ds = ds.prefetch(AUTO)\n    return ds","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train\ntrain_paths = train_df.filepath.values\ntrain_labels = train_df.target.values\ntrain_ds = build_dataset(train_paths, train_labels, batch_size=CFG.batch_size,\n                         shuffle=True, augment=CFG.augment)\n\n# Valid\nvalid_paths = valid_df.filepath.values\nvalid_labels = valid_df.target.values\nvalid_ds = build_dataset(valid_paths, valid_labels, batch_size=CFG.batch_size,\n                         shuffle=False, augment=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # Create an input layer for the model\n# inp = keras.layers.Input(shape=(None, None, 3))\n# # Pretrained backbone\n# backbone = keras_cv.models.EfficientNetV2Backbone.from_preset(\n#     CFG.preset,\n# )\n# out = keras_cv.models.ImageClassifier(\n#     backbone=backbone,\n#     num_classes=CFG.num_classes,\n#     name=\"classifier\"\n# )(inp)\n# # Build model\n# model = keras.models.Model(inputs=inp, outputs=out)\n# # Compile model with optimizer, loss and metrics\n# model.compile(optimizer=\"adam\",\n#               loss=keras.losses.CategoricalCrossentropy(label_smoothing=0.02),\n#               metrics=[keras.metrics.AUC(name='auc')],\n#              )\n# model.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import keras\nimport keras_cv.models\n\n# Create an input layer for the model\ninp = keras.layers.Input(shape=(None, None, 3))\n\n# Pretrained backbone\nbackbone = keras_cv.models.EfficientNetV2Backbone.from_preset(CFG.preset)\n    \n    \n# Adding pretrained backbone\nout = keras_cv.models.ImageClassifier(\n    backbone=backbone,\n    num_classes=CFG.num_classes,\n    name=\"classifier\"\n)(inp)\n\n# Build model\nmodel = keras.models.Model(inputs=inp, outputs=out)\n\nmodel.compile(optimizer=\"adam\",\n              loss=tf.keras.losses.CategoricalFocalCrossentropy(\n                        alpha=0.25,\n                        gamma=2.0,\n                        from_logits=False,\n                        label_smoothing=0.0,\n                        axis=-1,\n                        reduction=tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE,\n                        name='categorical_focal_crossentropy'\n                    ),\n              metrics=[keras.metrics.AUC(name='auc')],\n             )\n\nmodel.summary()\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import math\n\ndef get_lr_callback(batch_size=8, mode='cos', epochs=20, plot=False):\n    lr_start, lr_max, lr_min = 5e-5, 8e-6 * batch_size, 1e-5\n    lr_ramp_ep, lr_sus_ep, lr_decay = 3, 0, 0.75\n\n    def lrfn(epoch):  # Learning rate update function\n        if epoch < lr_ramp_ep: lr = (lr_max - lr_start) / lr_ramp_ep * epoch + lr_start\n        elif epoch < lr_ramp_ep + lr_sus_ep: lr = lr_max\n        elif mode == 'exp': lr = (lr_max - lr_min) * lr_decay**(epoch - lr_ramp_ep - lr_sus_ep) + lr_min\n        elif mode == 'step': lr = lr_max * lr_decay**((epoch - lr_ramp_ep - lr_sus_ep) // 2)\n        elif mode == 'cos':\n            decay_total_epochs, decay_epoch_index = epochs - lr_ramp_ep - lr_sus_ep + 3, epoch - lr_ramp_ep - lr_sus_ep\n            phase = math.pi * decay_epoch_index / decay_total_epochs\n            lr = (lr_max - lr_min) * 0.5 * (1 + math.cos(phase)) + lr_min\n        return lr\n\n    if plot:  # Plot lr curve if plot is True\n        plt.figure(figsize=(10, 5))\n        plt.plot(np.arange(epochs), [lrfn(epoch) for epoch in np.arange(epochs)], marker='o')\n        plt.xlabel('epoch'); plt.ylabel('lr')\n        plt.title('LR Scheduler')\n        plt.show()\n\n    return keras.callbacks.LearningRateScheduler(lrfn, verbose=False)  # Create lr callback","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lr_cb = get_lr_callback(CFG.batch_size, plot=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ckpt_cb = keras.callbacks.ModelCheckpoint(\"best_model_of_version7.weights.h5\",\n                                         monitor='val_auc',\n                                         save_best_only=True,\n                                         save_weights_only=True,\n                                         mode='max')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping\nfrom sklearn.model_selection import StratifiedKFold\nimport matplotlib.pyplot as plt\n\n# Define early stopping callback\nearly_stopping_cb = EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)\n\n# Define the number of splits\nn_splits = 5  # for example\n\n# Define your cross-validation splitter\nskf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=42)  # You can adjust parameters as needed\n\n# Dictionary to store models\nmodels_dict = {}\n\n# Dictionary to store training history\nhistory_dict = {}\n\n# Loop over the folds\nfor fold, (train_index, valid_index) in enumerate(skf.split(train_df, train_df['target'])):\n    print(f\"Fold {fold+1}/{n_splits}\")\n    \n    # Get train and validation data for this fold\n    train_fold_df = train_df.iloc[train_index]\n    valid_fold_df = train_df.iloc[valid_index]\n    \n    # Prepare train and validation datasets for this fold\n    train_paths_fold = train_fold_df.filepath.values\n    train_labels_fold = train_fold_df.target.values\n    train_ds_fold = build_dataset(train_paths_fold, train_labels_fold, batch_size=CFG.batch_size,\n                                  shuffle=True, augment=CFG.augment)\n    \n    valid_paths_fold = valid_fold_df.filepath.values\n    valid_labels_fold = valid_fold_df.target.values\n    valid_ds_fold = build_dataset(valid_paths_fold, valid_labels_fold, batch_size=CFG.batch_size,\n                                  shuffle=False, augment=False)\n    \n    # Train the model\n    history_fold = model.fit(\n        train_ds_fold, \n        validation_data=valid_ds_fold, \n        epochs=CFG.epochs,\n        callbacks=[lr_cb, ckpt_cb, early_stopping_cb],  # Added early stopping callback\n        verbose=1\n    )\n    \n    # Store the model\n    models_dict[f\"model_fold_{fold+1}\"] = model\n    \n    # Store the training history\n    history_dict[f\"history_fold_{fold+1}\"] = history_fold.history\n    \n    # Print results\n    print(\"Training Metrics:\")\n    print(history_fold.history)\n    \n    # Evaluate on validation data\n    eval_result = model.evaluate(valid_ds_fold)\n    print(\"Validation Metrics:\")\n    print(\"Loss:\", eval_result[0])\n    print(\"AUC:\", eval_result[1])\n    \n    break\n    \n\n# # Plot loss and accuracy curves for each fold\n# for fold in range(n_splits):\n#     history_fold = history_dict[f\"history_fold_{fold+1}\"]\n#     plt.figure(figsize=(12, 4))\n#     plt.subplot(1, 2, 1)\n#     plt.plot(history_fold['loss'], label='Training Loss')\n#     plt.plot(history_fold['val_loss'], label='Validation Loss')\n#     plt.title(f'Fold {fold+1} Loss')\n#     plt.xlabel('Epochs')\n#     plt.ylabel('Loss')\n#     plt.legend()\n    \n#     plt.subplot(1, 2, 2)\n#     plt.plot(history_fold['auc'], label='Training AUC')\n#     plt.plot(history_fold['val_auc'], label='Validation AUC')\n#     plt.title(f'Fold {fold+1} AUC')\n#     plt.xlabel('Epochs')\n#     plt.ylabel('AUC')\n#     plt.legend()\n    \n#     plt.show()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # Initialize variables to accumulate metric values\n# total_loss = 0\n# total_auc = 0\n\n# # Evaluate each model in models_dict\n# for fold, model_name in enumerate(models_dict):\n#     # Get the model\n#     model = models_dict[model_name]\n    \n#     # Get the validation dataset for this fold\n#     valid_paths_fold = valid_df.filepath.values\n#     valid_labels_fold = valid_df.target.values\n#     valid_ds_fold = build_dataset(valid_paths_fold, valid_labels_fold, batch_size=CFG.batch_size,\n#                                   shuffle=False, augment=False)\n    \n#     # Evaluate the model on the validation dataset\n#     eval_result = model.evaluate(valid_ds_fold)\n    \n#     # Accumulate metric values\n#     total_loss += eval_result[0]\n#     total_auc += eval_result[1]\n\n# # Compute the average\n# avg_loss = total_loss / len(models_dict)\n# avg_auc = total_auc / len(models_dict)\n\n# # Print the average metrics\n# print(\"Average Loss:\", avg_loss)\n# print(\"Average AUC:\", avg_auc)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# history = model.fit(\n#     train_ds, \n#     validation_data=valid_ds, \n#     epochs=CFG.epochs,\n#     callbacks=[lr_cb, ckpt_cb], \n#     verbose=1\n# )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# best_epoch = np.argmax(history.history[\"val_auc\"])\n# best_score = history.history[\"val_auc\"][best_epoch]\n# print('>>> Best AUC: ', best_score)\n# print('>>> Best Epoch: ', best_epoch+1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}