{"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":"Hello fellow Kagglers,\n\nThis notebook demonstrates how to create TFRecords for the Google Landmark Recognition 2021 competition. Since the dataset is huge with over 1.5 million images the dataset has to be split in 3 parts.\nThe datasets are made public and can be found here:\n\n[Part 1](https://www.kaggle.com/markwijkhuizen/landmark-recognition-2021-tfrecords-384-part-1)\n\n[Part 2](https://www.kaggle.com/markwijkhuizen/landmark-recognition-2021-tfrecords-384-part-2)\n\n[Part 3](https://www.kaggle.com/markwijkhuizen/landmark-recognition-2021-tfrecords-384-part-3)\n\nThe resolution is set to 384 with 384 being the smaller side of the image, thus images can for example have a resolution of 384x512 or 512x384.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\n\nfrom tqdm.notebook import tqdm\nfrom kaggle_datasets import KaggleDatasets\nfrom multiprocessing import cpu_count\n\nimport sys\nimport cv2\nimport imageio\nimport joblib\nimport pickle\n\n# Activate pandas progress apply bar\ntqdm.pandas()\n\nprint(f'tensorflow version: {tf.__version__}')\nprint(f'tensorflow keras version: {tf.keras.__version__}')\nprint(f'python version: P{sys.version}')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:14:49.104906Z","iopub.execute_input":"2021-08-24T09:14:49.105258Z","iopub.status.idle":"2021-08-24T09:14:54.587645Z","shell.execute_reply.started":"2021-08-24T09:14:49.105232Z","shell.execute_reply":"2021-08-24T09:14:54.586745Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Smaller side of the image, can be adjusted\nIMG_SIZE = 384\nN_CHANNELS = 3\nVERSION = '1A'\n\n# Total number of images\nN_ROWS = 1580470\n# Higher resolution will require more splits due to the 20GB dataset limit\nN_SPLITS = 3\nPART_N = 1","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:14:54.588983Z","iopub.execute_input":"2021-08-24T09:14:54.589276Z","iopub.status.idle":"2021-08-24T09:14:54.594152Z","shell.execute_reply.started":"2021-08-24T09:14:54.589233Z","shell.execute_reply":"2021-08-24T09:14:54.593236Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Read train CSV with correct data types\ndtype = { 'id': 'string', 'landmark_id': np.uint32 }\ntrain = pd.read_csv('/kaggle/input/landmark-recognition-2021/train.csv', dtype=dtype)","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:14:54.595482Z","iopub.execute_input":"2021-08-24T09:14:54.595754Z","iopub.status.idle":"2021-08-24T09:14:56.066215Z","shell.execute_reply.started":"2021-08-24T09:14:54.595727Z","shell.execute_reply":"2021-08-24T09:14:56.065350Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The landmark ids are not continious, as shown below. To get continous labels in a single line of code the landmark ids are converted to categories, where the label is the ordinal encoded category.","metadata":{}},{"cell_type":"code","source":"print(f'First 10 landmark ids: {train[\"landmark_id\"].unique()[:10]}')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:18:13.151486Z","iopub.execute_input":"2021-08-24T09:18:13.151985Z","iopub.status.idle":"2021-08-24T09:18:13.183108Z","shell.execute_reply.started":"2021-08-24T09:18:13.151956Z","shell.execute_reply":"2021-08-24T09:18:13.182149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['label'] = train['landmark_id'].astype('category').cat.codes","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:14:56.067302Z","iopub.execute_input":"2021-08-24T09:14:56.067566Z","iopub.status.idle":"2021-08-24T09:14:56.136013Z","shell.execute_reply.started":"2021-08-24T09:14:56.067541Z","shell.execute_reply":"2021-08-24T09:14:56.135000Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Create mappings between the labels and landmark ids. This can be needed when converting predicted model labels back to landmark ids for example.","metadata":{}},{"cell_type":"code","source":"label2landmark_id = train[['label', 'landmark_id']].drop_duplicates().set_index('label').squeeze().to_dict()\nlandmark_id2label = {v: k for k, v in label2landmark_id.items()}","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:19:14.287034Z","iopub.execute_input":"2021-08-24T09:19:14.287383Z","iopub.status.idle":"2021-08-24T09:19:14.467438Z","shell.execute_reply.started":"2021-08-24T09:19:14.287355Z","shell.execute_reply":"2021-08-24T09:19:14.466422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with open('label2landmark_id.pkl', 'wb') as f:\n    pickle.dump(label2landmark_id, f)\n\nwith open('landmark_id2label.pkl', 'wb') as f:\n    pickle.dump(landmark_id2label, f)","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:19:15.057877Z","iopub.execute_input":"2021-08-24T09:19:15.058341Z","iopub.status.idle":"2021-08-24T09:19:15.088523Z","shell.execute_reply.started":"2021-08-24T09:19:15.058294Z","shell.execute_reply":"2021-08-24T09:19:15.087553Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(train.head())","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:19:15.836060Z","iopub.execute_input":"2021-08-24T09:19:15.836384Z","iopub.status.idle":"2021-08-24T09:19:15.853313Z","shell.execute_reply.started":"2021-08-24T09:19:15.836357Z","shell.execute_reply":"2021-08-24T09:19:15.852515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(train.info())","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:19:15.998555Z","iopub.execute_input":"2021-08-24T09:19:15.999071Z","iopub.status.idle":"2021-08-24T09:19:16.175439Z","shell.execute_reply.started":"2021-08-24T09:19:15.999035Z","shell.execute_reply":"2021-08-24T09:19:16.174474Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Google Cloud File Paths","metadata":{}},{"cell_type":"code","source":"# Get the Google Cloud Storage path for a given image\ndef to_gcs_file_path(i):\n    return f'{GCS_DS_PATH}/train/{i[0]}/{i[1]}/{i[2]}/{i}.jpg'\n\n# Get Google Cloud Path to dataset\nGCS_DS_PATH = KaggleDatasets().get_gcs_path('landmark-recognition-2021')\n\n# Assign Google Cloud Path\ntrain['gcs_file_path'] = train['id'].progress_apply(to_gcs_file_path).astype('string')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:19:54.777101Z","iopub.execute_input":"2021-08-24T09:19:54.777675Z","iopub.status.idle":"2021-08-24T09:19:59.583226Z","shell.execute_reply.started":"2021-08-24T09:19:54.777641Z","shell.execute_reply":"2021-08-24T09:19:59.582467Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# File Path","metadata":{}},{"cell_type":"code","source":"def to_file_path(i):\n    return f'/kaggle/input/landmark-recognition-2021/train/{i[0]}/{i[1]}/{i[2]}/{i}.jpg'\n\ntrain['file_path'] = train['id'].progress_apply(to_file_path).astype('string')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:19:59.584511Z","iopub.execute_input":"2021-08-24T09:19:59.584899Z","iopub.status.idle":"2021-08-24T09:20:03.583946Z","shell.execute_reply.started":"2021-08-24T09:19:59.584866Z","shell.execute_reply":"2021-08-24T09:20:03.582715Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(train.head())","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:22:52.646793Z","iopub.execute_input":"2021-08-24T09:22:52.647294Z","iopub.status.idle":"2021-08-24T09:22:52.663802Z","shell.execute_reply.started":"2021-08-24T09:22:52.647250Z","shell.execute_reply":"2021-08-24T09:22:52.662859Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Save the updated train DataFrame\ntrain.to_pickle('train.pkl.xz')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:20:12.780041Z","iopub.execute_input":"2021-08-24T09:20:12.780417Z","iopub.status.idle":"2021-08-24T09:22:52.644816Z","shell.execute_reply.started":"2021-08-24T09:20:12.780378Z","shell.execute_reply":"2021-08-24T09:22:52.643742Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Part Selection","metadata":{}},{"cell_type":"markdown","source":"The dataset has to be split into multiple parts due to limited disk space. In the next cell the start and end index of the DataFrame are computed.","metadata":{}},{"cell_type":"code","source":"START_IDX = int(N_ROWS * ((PART_N - 1) / N_SPLITS))\nEND_IDX = int(N_ROWS * (PART_N / N_SPLITS))\n\nprint(f'START_IDX: {START_IDX}, END_IDX: {END_IDX}')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:24:34.474493Z","iopub.execute_input":"2021-08-24T09:24:34.474901Z","iopub.status.idle":"2021-08-24T09:24:34.481120Z","shell.execute_reply.started":"2021-08-24T09:24:34.474870Z","shell.execute_reply":"2021-08-24T09:24:34.480165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Drop all indices which do note belong to this part\nDROP_IDXS = train.loc[(train.index < START_IDX) | (train.index >= END_IDX)].index\ntrain.drop(DROP_IDXS, inplace=True)\n\nprint(f'DataFrame idx min: {train.index.min()}, idx max: {train.index.max()}')\n\n# Reset index\ntrain.reset_index(drop=True, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:25:05.484959Z","iopub.execute_input":"2021-08-24T09:25:05.485301Z","iopub.status.idle":"2021-08-24T09:25:06.404488Z","shell.execute_reply.started":"2021-08-24T09:25:05.485272Z","shell.execute_reply":"2021-08-24T09:25:06.403411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Shuffling the DataFrame is import, as otherwise batches could consists of a single class. Shuffling the DataFrame makes sure every TFRecord will consists of a random set of classes.","metadata":{}},{"cell_type":"code","source":"# Shuffle DataFrame\ntrain = train.sample(frac=1, random_state=42)","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:26:58.578732Z","iopub.execute_input":"2021-08-24T09:26:58.579150Z","iopub.status.idle":"2021-08-24T09:26:58.875736Z","shell.execute_reply.started":"2021-08-24T09:26:58.579120Z","shell.execute_reply":"2021-08-24T09:26:58.874468Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(train.head())","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:26:59.891700Z","iopub.execute_input":"2021-08-24T09:26:59.892079Z","iopub.status.idle":"2021-08-24T09:26:59.905475Z","shell.execute_reply.started":"2021-08-24T09:26:59.892046Z","shell.execute_reply":"2021-08-24T09:26:59.904472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(train.info())","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:27:00.025734Z","iopub.execute_input":"2021-08-24T09:27:00.026083Z","iopub.status.idle":"2021-08-24T09:27:00.298778Z","shell.execute_reply.started":"2021-08-24T09:27:00.026055Z","shell.execute_reply":"2021-08-24T09:27:00.298061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Process Image","metadata":{}},{"cell_type":"markdown","source":"Processing the image is rather easy. The image is resized to have a smaller side of size 384. The computing intensize LANCZOS algorithm is used for resizing. I am not an expert on resizing algorithms, however the [PIL documentation](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#filters-comparison-table) on filters state this algorithm performs the best.","metadata":{}},{"cell_type":"code","source":"def process_image(file_path):\n    img = imageio.imread(file_path)\n    h, w, _ = img.shape\n\n    r = IMG_SIZE / min(w, h)\n    # Check whether image is bigger than IMG_SIZE\n    if min(h,w) > IMG_SIZE:\n        w_resize = int(w * r)\n        h_resize = int(h * r)\n        # Resize using LANCZOS algorithm\n        img = cv2.resize(img, (w_resize, h_resize), interpolation=cv2.INTER_LANCZOS4)\n        # Save as JPEG with quality set to 70, just as original images\n        img_jpeg = tf.io.encode_jpeg(img, quality=70, optimize_size=True).numpy()\n        return img_jpeg, h_resize, w_resize\n    # Otherwise use original image\n    else:\n        with open(file_path, 'rb') as f:\n            img_jpeg = f.read()\n        return img_jpeg, h, w","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:31:01.592868Z","iopub.execute_input":"2021-08-24T09:31:01.593226Z","iopub.status.idle":"2021-08-24T09:31:01.600931Z","shell.execute_reply.started":"2021-08-24T09:31:01.593195Z","shell.execute_reply":"2021-08-24T09:31:01.599829Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Sample Size","metadata":{}},{"cell_type":"markdown","source":"This helper function computer the estimated part size, this should be less than 20GB due to disk space limitations.","metadata":{}},{"cell_type":"code","source":"def output_size(N):\n    mean_size = 0\n    for fp in tqdm(train['file_path'].sample(N, random_state=42)):\n        img_jpeg, h, w = process_image(fp)\n        mean_size += len(img_jpeg) / N\n        \n    print(f'Estimated TFRecord output size: {len(train) * mean_size / 2**30:.2f}GB')\n\noutput_size(int(1e3))","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:31:38.677966Z","iopub.execute_input":"2021-08-24T09:31:38.678326Z","iopub.status.idle":"2021-08-24T09:31:55.479582Z","shell.execute_reply.started":"2021-08-24T09:31:38.678295Z","shell.execute_reply":"2021-08-24T09:31:55.478677Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Split in Data Chunks","metadata":{}},{"cell_type":"markdown","source":"Split the dataset in chunks of 3000 images to get TFRecords of approximately 100MB","metadata":{}},{"cell_type":"code","source":"def split_in_chunks(data, chunk_size):\n    return [data[:, i:i + CHUNK_SIZE] for i in range(0, len(data[1]), CHUNK_SIZE)]","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:33:20.093606Z","iopub.execute_input":"2021-08-24T09:33:20.094009Z","iopub.status.idle":"2021-08-24T09:33:20.099592Z","shell.execute_reply.started":"2021-08-24T09:33:20.093969Z","shell.execute_reply":"2021-08-24T09:33:20.098439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"CHUNK_SIZE = int(3e3)\n\ntrain_split = split_in_chunks(np.array((train['file_path'], train['label'])), CHUNK_SIZE)\n\nprint(f'train_split chunks: {len(train_split)}')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:33:20.209614Z","iopub.execute_input":"2021-08-24T09:33:20.209978Z","iopub.status.idle":"2021-08-24T09:33:20.261123Z","shell.execute_reply.started":"2021-08-24T09:33:20.209946Z","shell.execute_reply":"2021-08-24T09:33:20.260197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Make TFRecords","metadata":{}},{"cell_type":"markdown","source":"This next function creates the actual TFrecords. The images are processed using my favourite parallelism package [joblib](https://joblib.readthedocs.io/en/latest/#).","metadata":{}},{"cell_type":"code","source":"def to_tf_records(data_split, name):\n    for idx, (fps, lbls) in enumerate(tqdm(data_split)):\n        \n        # Create image processing jobs and execute them in parallel\n        jobs = [joblib.delayed(process_image)(fp) for fp in fps]\n        imgs_resized = joblib.Parallel(\n            n_jobs=cpu_count(),\n            verbose=0,\n            batch_size=64,\n            pre_dispatch=64*cpu_count(),\n            require='sharedmem'\n        )(jobs)\n        tfrecord_name = f'{VERSION}_PART_{PART_N}_{name}_batch_{idx}.tfrecords'\n        \n        # Create the actual TFRecords\n        with tf.io.TFRecordWriter(tfrecord_name) as file_writer:\n            for (img, h, w), lbl in zip(imgs_resized, lbls):\n                record_bytes = tf.train.Example(features=tf.train.Features(feature={\n                    # Image as JPEG bytes\n                    'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img])),\n                    # Label of image\n                    'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[int(lbl)])),\n                    # Height of image\n                    'height': tf.train.Feature(int64_list=tf.train.Int64List(value=[int(h)])),\n                    # Width of image\n                    'width': tf.train.Feature(int64_list=tf.train.Int64List(value=[int(w)])),\n                })).SerializeToString()\n                file_writer.write(record_bytes)\n\n# Create TFRecords\nto_tf_records(train_split, 'train')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:41:59.725473Z","iopub.execute_input":"2021-08-24T09:41:59.726010Z","iopub.status.idle":"2021-08-24T09:42:29.085418Z","shell.execute_reply.started":"2021-08-24T09:41:59.725977Z","shell.execute_reply":"2021-08-24T09:42:29.084446Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Check TFRecords","metadata":{}},{"cell_type":"markdown","source":"Check whether the TFRecords are succesfully created","metadata":{}},{"cell_type":"code","source":"# Imagenet mean and standard deviation per channel\nIMAGENET_MEAN = tf.constant([0.485, 0.456, 0.406], dtype=tf.float32)\nIMAGENET_STD = tf.constant([0.229, 0.224, 0.225], dtype=tf.float32)\n\n# Number of channels, 3 for RGB images\nN_CHANNELS = tf.constant(3, dtype=tf.int64)","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:42:29.086573Z","iopub.execute_input":"2021-08-24T09:42:29.086805Z","iopub.status.idle":"2021-08-24T09:42:29.091717Z","shell.execute_reply.started":"2021-08-24T09:42:29.086782Z","shell.execute_reply":"2021-08-24T09:42:29.090760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function to decode the TFRecords\ndef decode_tfrecord(record_bytes):\n    features = tf.io.parse_single_example(record_bytes, {\n        'image': tf.io.FixedLenFeature([], tf.string),\n        'label': tf.io.FixedLenFeature([], tf.int64),\n        'width': tf.io.FixedLenFeature([], tf.int64),\n        'height': tf.io.FixedLenFeature([], tf.int64),\n    })\n\n    image = tf.io.decode_jpeg(features['image'])\n    label = features['label']\n    height = features['height']\n    width = features['width']\n    \n    # Cutout Random Square if image is not square\n    if height != width:\n        if height > width:\n            offset = tf.random.uniform(shape=(), minval=0, maxval=height-width, dtype=tf.int64)\n            image = tf.slice(image, [offset, 0, 0], [width, width, N_CHANNELS])\n        else:\n            offset = tf.random.uniform(shape=(), minval=0, maxval=width-height, dtype=tf.int64)\n            image = tf.slice(image, [0, offset, 0], [height, height, N_CHANNELS])\n    \n    # Reshape and Normalize\n    size = tf.math.reduce_min([height, width])\n    # Explicit reshape needed for TPU, tell cimpiler dimensions of image\n    image = tf.reshape(image, [size, size, N_CHANNELS])\n    # Some images are smaller than 384x384 and need to be upscaled\n    image = tf.image.resize(image, [IMG_SIZE, IMG_SIZE])\n    # Convert to float32 and normalize to range 0-1\n    image = tf.cast(image, tf.float32)  / 255.0\n    # Normalize according to ImageNet mean and standard deviation\n    image = (image - IMAGENET_MEAN) / IMAGENET_STD\n    \n    return image, label","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:44:33.256077Z","iopub.execute_input":"2021-08-24T09:44:33.256431Z","iopub.status.idle":"2021-08-24T09:44:33.267231Z","shell.execute_reply.started":"2021-08-24T09:44:33.256401Z","shell.execute_reply":"2021-08-24T09:44:33.266241Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Shows a batch of images\ndef show_batch(dataset, rows=4, cols=3):\n    imgs, lbls = next(iter(dataset))\n    fig, axes = plt.subplots(nrows=rows, ncols=cols, figsize=(cols*4, rows*4))\n    for r in range(rows):\n        for c in range(cols):\n            img = imgs[r*cols+c].numpy().astype(np.float32)\n            img += abs(img.min())\n            img /= img.max()\n            axes[r, c].imshow(img)\n            axes[r, c].set_title(f'Label: {lbls[r*cols+c]}')","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:45:30.186098Z","iopub.execute_input":"2021-08-24T09:45:30.186734Z","iopub.status.idle":"2021-08-24T09:45:30.194350Z","shell.execute_reply.started":"2021-08-24T09:45:30.186688Z","shell.execute_reply":"2021-08-24T09:45:30.193530Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_train_dataset():\n    FNAMES_TRAIN_TFRECORDS = tf.io.gfile.glob('./*.tfrecords')\n    train_dataset = tf.data.TFRecordDataset(FNAMES_TRAIN_TFRECORDS, num_parallel_reads=1)\n    train_dataset = train_dataset.map(decode_tfrecord, num_parallel_calls=1)\n    train_dataset = train_dataset.batch(32)\n    \n    return train_dataset","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:45:30.354343Z","iopub.execute_input":"2021-08-24T09:45:30.354934Z","iopub.status.idle":"2021-08-24T09:45:30.359463Z","shell.execute_reply.started":"2021-08-24T09:45:30.354901Z","shell.execute_reply":"2021-08-24T09:45:30.358491Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = get_train_dataset()\nshow_batch(train_dataset)","metadata":{"execution":{"iopub.status.busy":"2021-08-24T09:45:30.789627Z","iopub.execute_input":"2021-08-24T09:45:30.790053Z","iopub.status.idle":"2021-08-24T09:45:33.275207Z","shell.execute_reply.started":"2021-08-24T09:45:30.790002Z","shell.execute_reply":"2021-08-24T09:45:33.274253Z"},"trusted":true},"execution_count":null,"outputs":[]}]}