{"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":"# BirdCLEF 2023 - Baseline Training\n\nAs a first baseline model we fine-tune an EfficientNet neural network on the prepared dB scaled mel power specturm images. \n\nThe images have been prepared with [BC23 - Image Creation 128 x 256](https://www.kaggle.com/code/morodertobias/bc23-image-creation-128-x-256). Note these images might be suboptimal, but they should serve as a first start.","metadata":{}},{"cell_type":"code","source":"!pip install keras-cv","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-04-07T12:53:22.188678Z","iopub.execute_input":"2023-04-07T12:53:22.189347Z","iopub.status.idle":"2023-04-07T12:53:34.120760Z","shell.execute_reply.started":"2023-04-07T12:53:22.189308Z","shell.execute_reply":"2023-04-07T12:53:34.119424Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os, pathlib\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' \nfrom datetime import datetime\nimport collections\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom pydantic import BaseModel as ConfigBaseModel\nimport tensorflow as tf\nprint(\"tensorflow:\", tf.__version__)\nimport keras_cv\nprint(\"keras_cv:\", keras_cv.__version__)\nimport tensorflow_io as tfio\nprint(\"tfio:\", tfio.__version__)\nfrom sklearn.model_selection import train_test_split","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:53:34.124055Z","iopub.execute_input":"2023-04-07T12:53:34.124471Z","iopub.status.idle":"2023-04-07T12:53:43.287445Z","shell.execute_reply.started":"2023-04-07T12:53:34.124427Z","shell.execute_reply":"2023-04-07T12:53:43.286341Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"strategy = tf.distribute.MirroredStrategy()\nprint(\"Strategy:\", strategy)\nprint(\"Number of replicas:\", strategy.num_replicas_in_sync)","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:53:43.288816Z","iopub.execute_input":"2023-04-07T12:53:43.290250Z","iopub.status.idle":"2023-04-07T12:53:46.038206Z","shell.execute_reply.started":"2023-04-07T12:53:43.290202Z","shell.execute_reply":"2023-04-07T12:53:46.037080Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sorted(tf.config.list_logical_devices())","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:53:46.040519Z","iopub.execute_input":"2023-04-07T12:53:46.040990Z","iopub.status.idle":"2023-04-07T12:53:46.050067Z","shell.execute_reply.started":"2023-04-07T12:53:46.040950Z","shell.execute_reply":"2023-04-07T12:53:46.048910Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Config","metadata":{}},{"cell_type":"code","source":"class Config(ConfigBaseModel):\n    ## general\n    run_ts = datetime.now().strftime(\"%Y-%d-%m %H:%M:%S\")\n    debug = False\n    model_name = \"dev-b0-v8\"\n    test_size = 0.2\n    seed = 887\n    fit_verbose = 1 if (os.environ.get('KAGGLE_KERNEL_RUN_TYPE') == \"Interactive\") else 2\n    ## data\n    dataset_dir = \"/kaggle/input/ds-bc23-image-creation-128-x-256/train/\"\n    path_data = \"/kaggle/input/ds-bc23-image-creation-128-x-256/img_stats.csv\"\n    label = \"label\"\n    n_label = 264\n    img_size = (128, 256)\n    channels = 1\n    img_shape = (*img_size, channels)\n    ## model\n    base_model_weights = \"imagenet\"\n    dropout = 0.20\n    ## training\n    label_smoothing = 0.05\n    shuffle_size = 1028\n    steps_per_epoch = 300\n    batch_size = 128  # 16 * strategy.num_replicas_in_sync\n    valid_batch_size = batch_size\n    epochs = 30\n    patience = 4\n    monitor = \"val_loss\"  # val_loss\n    monitor_mode = \"auto\"\n    lr = 1e-3\n    ## aug\n    aug_proba = 0.8\n    \n    \n    \ncfg = Config()\nwith open(\"cfg.json\", \"w\") as f:\n    f.write(cfg.json(indent=2))\ncfg.dict()    ","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:54:19.933447Z","iopub.execute_input":"2023-04-07T12:54:19.933823Z","iopub.status.idle":"2023-04-07T12:54:19.955405Z","shell.execute_reply.started":"2023-04-07T12:54:19.933791Z","shell.execute_reply":"2023-04-07T12:54:19.954265Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preparation","metadata":{}},{"cell_type":"markdown","source":"## Load data","metadata":{}},{"cell_type":"code","source":"data = pd.read_csv(cfg.path_data)\ndata[\"path_img\"] = cfg.dataset_dir + data[\"filename\"]\nif cfg.debug:\n    data = data.iloc[:1000]\ndata","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:54:21.493926Z","iopub.execute_input":"2023-04-07T12:54:21.494597Z","iopub.status.idle":"2023-04-07T12:54:22.129399Z","shell.execute_reply.started":"2023-04-07T12:54:21.494560Z","shell.execute_reply":"2023-04-07T12:54:22.128217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dataset","metadata":{}},{"cell_type":"code","source":"AUTOTUNE = tf.data.AUTOTUNE\n\n\ndef show_img_stats(img):\n    if isinstance(img, tf.Tensor):\n        print((img.shape, img.dtype, img.numpy().min(), img.numpy().max()))\n    elif isinstance(img, np.array):\n        print((img.shape, img.dtype, img.min(), img.max()))\n    else:\n        print(f\"unexpected type: {type(img)}\")\n\n\ndef read_image(path_img):\n    img_data = tf.io.read_file(path_img)\n    img = tf.io.decode_jpeg(img_data, channels=cfg.channels)\n    img = tf.reshape(img, cfg.img_shape)\n    img = tf.cast(img, tf.float32)\n    return img\n\n\ndef decode_label(label):\n    return tf.one_hot(label, depth=cfg.n_label)","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:54:22.526655Z","iopub.execute_input":"2023-04-07T12:54:22.527498Z","iopub.status.idle":"2023-04-07T12:54:22.535496Z","shell.execute_reply.started":"2023-04-07T12:54:22.527459Z","shell.execute_reply":"2023-04-07T12:54:22.534326Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RandomRowMask(keras_cv.layers.BaseImageAugmentationLayer):\n    def __init__(self, param=10, num_mask=1, **kwargs):\n        super().__init__(**kwargs)\n        self.param = param\n        self.num_mask = num_mask\n\n    def augment_image(self, image, transformation=None, **kwargs):\n        image_shape = tf.shape(image)\n        num = self._random_generator.random_uniform((), 1, self.num_mask, dtype=tf.int32)\n        for _ in tf.range(num):\n            image = tfio.audio.time_mask(tf.squeeze(image), param=self.param)\n            image = tf.reshape(image, shape=image_shape)\n        return image\n    \n    \nclass RandomColumnMask(keras_cv.layers.BaseImageAugmentationLayer):\n    def __init__(self, param=10, num_mask=1, **kwargs):\n        super().__init__(**kwargs)\n        self.param = param\n        self.num_mask = num_mask\n\n    def augment_image(self, image, transformation=None, **kwargs):\n        image_shape = tf.shape(image)\n        num = self._random_generator.random_uniform((), 1, self.num_mask, dtype=tf.int32)\n        for _ in tf.range(num):        \n            image = tfio.audio.freq_mask(tf.squeeze(image), param=self.param)\n            image = tf.reshape(image, shape=image_shape)\n        return image\n    \n\n\naugmenter = keras_cv.layers.Augmenter(\n    layers=[\n        keras_cv.layers.RandomBrightness(factor=0.2),\n        keras_cv.layers.RandomContrast(factor=0.2),\n        keras_cv.layers.GridMask(ratio_factor=(0.05, 0.10)),\n        keras_cv.layers.RandomGaussianBlur(kernel_size=2, factor=0.1),\n        RandomRowMask(10, 3),\n        RandomColumnMask(40, 2)\n    ]\n)\n\n\ndef augment_image(img):\n    if tf.random.uniform([]) <= cfg.aug_proba:\n        img = augmenter(img)\n    return img","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:57:16.790245Z","iopub.execute_input":"2023-04-07T12:57:16.790761Z","iopub.status.idle":"2023-04-07T12:57:16.818906Z","shell.execute_reply.started":"2023-04-07T12:57:16.790711Z","shell.execute_reply":"2023-04-07T12:57:16.817345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_dataset(data, include_label=True, repeat=False, shuffle=False, augment=False, prefetch=False, batch_size=None):\n    slices = data[\"path_img\"].values\n    read_func = read_image\n    aug_func = augment_image\n    if include_label:\n        slices = slices, decode_label(data[cfg.label].values)\n        read_func = lambda path_img, label: (read_image(path_img), label)\n        aug_func = lambda img, label: (augment_image(img), label)\n    ds = tf.data.Dataset.from_tensor_slices(slices)\n    ds = ds.map(read_func, num_parallel_calls=AUTOTUNE)\n    if repeat: ds = ds.repeat()\n    if shuffle: ds = ds.shuffle(buffer_size=cfg.shuffle_size)\n    if augment: ds = ds.map(aug_func, num_parallel_calls=AUTOTUNE)\n    if batch_size: ds = ds.batch(batch_size)\n    if prefetch: ds = ds.prefetch(AUTOTUNE)\n    return ds","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:57:17.204928Z","iopub.execute_input":"2023-04-07T12:57:17.205294Z","iopub.status.idle":"2023-04-07T12:57:17.213816Z","shell.execute_reply.started":"2023-04-07T12:57:17.205263Z","shell.execute_reply":"2023-04-07T12:57:17.212678Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_training_dataset(data):\n    return create_dataset(\n        data,\n        include_label=True,\n        repeat=True,\n        shuffle=True,\n        augment=True,\n        prefetch=True,\n        batch_size=cfg.batch_size,\n    )\n\n\ndef create_validation_dataset(data):\n    return create_dataset(\n        data,\n        include_label=True,\n        repeat=False,\n        shuffle=False,\n        augment=False,\n        prefetch=True,\n        batch_size=cfg.valid_batch_size,\n    )","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:57:17.645375Z","iopub.execute_input":"2023-04-07T12:57:17.646064Z","iopub.status.idle":"2023-04-07T12:57:17.652973Z","shell.execute_reply.started":"2023-04-07T12:57:17.646025Z","shell.execute_reply":"2023-04-07T12:57:17.651719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Check augmentation","metadata":{}},{"cell_type":"code","source":"rec = data.sample(1).iloc[0]\nrec","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:57:18.639313Z","iopub.execute_input":"2023-04-07T12:57:18.640279Z","iopub.status.idle":"2023-04-07T12:57:18.653293Z","shell.execute_reply.started":"2023-04-07T12:57:18.640226Z","shell.execute_reply":"2023-04-07T12:57:18.652143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = read_image(rec.path_img)\nfig, axs = plt.subplots(3, 4, sharex='all', sharey='all', figsize=(16, 7))\nfor i, ax in enumerate(axs.flat):\n    if i == 0:\n        ax.imshow(img, cmap='viridis')\n        show_img_stats(img)\n    else:\n        img1 = augmenter(img, training=True)\n        ax.imshow(img1, cmap='viridis')\n        show_img_stats(img1)\nplt.tight_layout()\nplt.show()   ","metadata":{"execution":{"iopub.status.busy":"2023-04-07T12:57:19.480641Z","iopub.execute_input":"2023-04-07T12:57:19.481378Z","iopub.status.idle":"2023-04-07T12:57:21.824535Z","shell.execute_reply.started":"2023-04-07T12:57:19.481336Z","shell.execute_reply":"2023-04-07T12:57:21.823387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Check dataset","metadata":{}},{"cell_type":"code","source":"dev_data = data.sample(500)\ndev_ds = create_training_dataset(dev_data)\ndev_ds","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:44:47.120983Z","iopub.execute_input":"2023-04-05T19:44:47.121390Z","iopub.status.idle":"2023-04-05T19:44:49.746478Z","shell.execute_reply.started":"2023-04-05T19:44:47.121353Z","shell.execute_reply":"2023-04-05T19:44:49.745313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"elem = next(iter(dev_ds.take(1)))\nelem[1]","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:44:53.263795Z","iopub.execute_input":"2023-04-05T19:44:53.264624Z","iopub.status.idle":"2023-04-05T19:44:57.786375Z","shell.execute_reply.started":"2023-04-05T19:44:53.264584Z","shell.execute_reply":"2023-04-05T19:44:57.785314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(3, 4, sharex='all', sharey='all', figsize=(16, 8))\nfor i, ax in enumerate(axs.flat):\n    img = elem[0][i]\n    show_img_stats(img)\n    ax.imshow(img, cmap=\"viridis\")\n    ax.set_title(f\"label:{np.argmax(elem[1][i].numpy())}\")\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:44:57.788125Z","iopub.execute_input":"2023-04-05T19:44:57.788448Z","iopub.status.idle":"2023-04-05T19:44:59.563814Z","shell.execute_reply.started":"2023-04-05T19:44:57.788419Z","shell.execute_reply":"2023-04-05T19:44:59.561703Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Neural network","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications.efficientnet import EfficientNetB0 as BaseModel\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\nfrom tensorflow.keras import layers, losses, metrics, callbacks","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:45:07.717002Z","iopub.execute_input":"2023-04-05T19:45:07.717526Z","iopub.status.idle":"2023-04-05T19:45:07.726259Z","shell.execute_reply.started":"2023-04-05T19:45:07.717485Z","shell.execute_reply":"2023-04-05T19:45:07.725282Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_model(lr):\n    inputs = layers.Input(shape=cfg.img_shape, dtype=tf.float32)\n    x = tf.image.grayscale_to_rgb(inputs)\n    x = layers.Lambda(preprocess_input, name=\"preprocess_input\")(x)\n    base_model = BaseModel(include_top=False, weights=cfg.base_model_weights, pooling=\"avg\")\n    x = base_model(x, training=False)\n    x = layers.Dropout(cfg.dropout, name=\"top_dropout\")(x)\n    outputs = layers.Dense(cfg.n_label, name=\"logits\")(x)\n    model = tf.keras.Model(inputs=inputs, outputs=outputs, name=cfg.model_name)\n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(learning_rate=lr),\n        loss=tf.keras.losses.BinaryCrossentropy(from_logits=True, label_smoothing=cfg.label_smoothing),\n        metrics=['acc']\n    )\n    return model","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:45:08.300324Z","iopub.execute_input":"2023-04-05T19:45:08.300736Z","iopub.status.idle":"2023-04-05T19:45:08.309104Z","shell.execute_reply.started":"2023-04-05T19:45:08.300699Z","shell.execute_reply":"2023-04-05T19:45:08.307923Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Check model","metadata":{}},{"cell_type":"code","source":"tf.keras.backend.clear_session()\nwith strategy.scope():\n    dev_model = create_model(lr=cfg.lr)\ndev_model.summary(line_length=120)","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:45:10.565009Z","iopub.execute_input":"2023-04-05T19:45:10.566059Z","iopub.status.idle":"2023-04-05T19:45:16.285064Z","shell.execute_reply.started":"2023-04-05T19:45:10.566017Z","shell.execute_reply":"2023-04-05T19:45:16.283888Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dev_model.predict(dev_ds.take(1))","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:45:16.287433Z","iopub.execute_input":"2023-04-05T19:45:16.287817Z","iopub.status.idle":"2023-04-05T19:45:30.270187Z","shell.execute_reply.started":"2023-04-05T19:45:16.287778Z","shell.execute_reply":"2023-04-05T19:45:30.269141Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dev_model.evaluate(dev_ds.take(1), return_dict=True)","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:45:30.273369Z","iopub.execute_input":"2023-04-05T19:45:30.273743Z","iopub.status.idle":"2023-04-05T19:45:37.194495Z","shell.execute_reply.started":"2023-04-05T19:45:30.273713Z","shell.execute_reply":"2023-04-05T19:45:37.193472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training utils","metadata":{}},{"cell_type":"code","source":"def get_callbacks(filepath):\n    \"\"\"Get callbacks\"\"\"\n    cbs = [\n        callbacks.ModelCheckpoint(\n            filepath=filepath,\n            monitor=cfg.monitor,\n            mode=cfg.monitor_mode,\n            verbose=1,\n            save_best_only=True,\n            save_weights_only=True            \n        ),\n        callbacks.EarlyStopping(\n            monitor=cfg.monitor,\n            mode=cfg.monitor_mode,\n            verbose=1,\n            patience=cfg.patience,\n            restore_best_weights=False,\n        ),\n    ]\n    return cbs\n\n\ndef show_history(history):\n    \"\"\"Show history\"\"\"\n    history_frame = pd.DataFrame(history.history)\n    history_frame.index = pd.RangeIndex(1, len(history_frame) + 1, name=\"epoch\")\n    display(history_frame.style\\\n        .highlight_min(color='lightgreen', subset=['val_loss'])\\\n        .highlight_max(color='lightgreen', subset=['val_acc'])\n    )\n    fig, ax = plt.subplots(1, 2, figsize=(16, 6))\n    history_frame.loc[:, ['loss', 'val_loss']].plot(ax=ax[0], title='loss')\n    history_frame.loc[:, ['acc', 'val_acc']].plot(ax=ax[1], title='acc')\n    plt.tight_layout()\n    plt.show()\n    \n    \ndef compute_oof(model, valid_df):\n    \"\"\"Compute OOF\"\"\"\n    valid_ds = create_validation_dataset(valid_df)\n    oof_pred = model.predict(valid_ds, verbose=False)\n    oof_pred = pd.DataFrame(tf.nn.sigmoid(oof_pred).numpy(), index=valid_df.index)\n    oof = pd.concat({\"y_true\": valid_df[cfg.label], \"y_pred\": oof_pred}, axis=1)\n    return oof    ","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:45:37.198435Z","iopub.execute_input":"2023-04-05T19:45:37.199041Z","iopub.status.idle":"2023-04-05T19:45:37.210524Z","shell.execute_reply.started":"2023-04-05T19:45:37.199006Z","shell.execute_reply":"2023-04-05T19:45:37.208672Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def run_training(train_df, valid_df, model_name):\n    \"\"\"Run training\"\"\"\n    # prepare dataset\n    train_ds = create_training_dataset(train_df)\n    valid_ds = create_validation_dataset(valid_df)\n    # create model\n    tf.keras.backend.clear_session()\n    with strategy.scope():\n        model = create_model(lr=cfg.lr)\n    # fit\n    steps_per_epoch = cfg.steps_per_epoch\n    print(\"steps_per_epoch:\", steps_per_epoch)\n    path_weight = f\"/kaggle/working/weights_{model_name}.h5\"\n    print(\"path_weights:\", path_weight)\n    hist = model.fit(\n        train_ds,\n        epochs=cfg.epochs,\n        steps_per_epoch=steps_per_epoch,\n        validation_data=valid_ds,\n        callbacks=get_callbacks(path_weight),\n        verbose=cfg.fit_verbose\n    )\n    # restore\n    model.load_weights(path_weight)\n#     # save full model\n#     does not work: https://github.com/keras-team/keras/pull/17498 \n#     path_model = f\"/kaggle/working/{model_name}\"\n#     print(\"path_model:\", path_model)\n#     model.save(path_model)\n    # compute oof\n    oof = compute_oof(model, valid_df)\n    return hist, oof","metadata":{"execution":{"iopub.status.busy":"2023-04-05T19:45:37.212195Z","iopub.execute_input":"2023-04-05T19:45:37.212760Z","iopub.status.idle":"2023-04-05T19:45:37.224126Z","shell.execute_reply.started":"2023-04-05T19:45:37.212722Z","shell.execute_reply":"2023-04-05T19:45:37.222890Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Run training\nLet us start by training just a single split.","metadata":{}},{"cell_type":"code","source":"train_df, valid_df = train_test_split(data, test_size=cfg.test_size, stratify=data[cfg.label])\nprint(f\"Split: {len(train_df)} vs {len(valid_df)}\")\nmodel_name = f\"{cfg.model_name}\"\nprint(f\"model_name: {model_name}\")","metadata":{"execution":{"iopub.status.busy":"2023-03-31T13:28:26.429474Z","iopub.execute_input":"2023-03-31T13:28:26.429868Z","iopub.status.idle":"2023-03-31T13:28:26.494426Z","shell.execute_reply.started":"2023-03-31T13:28:26.429835Z","shell.execute_reply":"2023-03-31T13:28:26.493230Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"hist, oof = run_training(train_df, valid_df, model_name)","metadata":{"execution":{"iopub.status.busy":"2023-03-31T13:28:27.316166Z","iopub.execute_input":"2023-03-31T13:28:27.316560Z","iopub.status.idle":"2023-03-31T13:33:40.156516Z","shell.execute_reply.started":"2023-03-31T13:28:27.316511Z","shell.execute_reply":"2023-03-31T13:33:40.153906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_history(hist)","metadata":{"execution":{"iopub.status.busy":"2023-03-30T09:30:09.719611Z","iopub.execute_input":"2023-03-30T09:30:09.720369Z","iopub.status.idle":"2023-03-30T09:30:10.247996Z","shell.execute_reply.started":"2023-03-30T09:30:09.720333Z","shell.execute_reply":"2023-03-30T09:30:10.247001Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"oof.to_csv(\"oof.csv\")","metadata":{"execution":{"iopub.status.busy":"2023-03-30T09:30:25.079475Z","iopub.execute_input":"2023-03-30T09:30:25.079868Z","iopub.status.idle":"2023-03-30T09:30:31.000849Z","shell.execute_reply.started":"2023-03-30T09:30:25.079835Z","shell.execute_reply":"2023-03-30T09:30:30.999761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}