{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpuV5e8","dataSources":[{"sourceType":"competition","sourceId":21154,"databundleVersionId":1243559}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"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\nfor 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":{"iopub.status.busy":"2026-04-08T04:14:12.963647Z","iopub.execute_input":"2026-04-08T04:14:12.963788Z","iopub.status.idle":"2026-04-08T04:14:16.884893Z","shell.execute_reply.started":"2026-04-08T04:14:12.963770Z","shell.execute_reply":"2026-04-08T04:14:16.883966Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport tensorflow as tf\n\nprint(\"TF version:\", tf.__version__)\nprint(\"Logical TPU devices:\", tf.config.list_logical_devices(\"TPU\"))\nprint(\"All logical devices:\", tf.config.list_logical_devices())\n\ntry:\n    resolver = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print(\"TPU master:\", resolver.master())\n    tf.config.experimental_connect_to_cluster(resolver)\n    tf.tpu.experimental.initialize_tpu_system(resolver)\n    print(\"Initialized TPU devices:\", tf.config.list_logical_devices(\"TPU\"))\n    strategy = tf.distribute.TPUStrategy(resolver)\nexcept Exception as e:\n    print(\"TPU init failed:\", repr(e))\n    strategy = tf.distribute.get_strategy()\n\nprint(\"REPLICAS:\", strategy.num_replicas_in_sync)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:03:34.216068Z","iopub.execute_input":"2026-03-23T15:03:34.216308Z","iopub.status.idle":"2026-03-23T15:04:03.235275Z","shell.execute_reply.started":"2026-03-23T15:03:34.216290Z","shell.execute_reply":"2026-03-23T15:04:03.234137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from kaggle_datasets import KaggleDatasets\n\nGCS_DS_PATH = KaggleDatasets().get_gcs_path('competitions/tpu-getting-started')\nprint(GCS_DS_PATH) # what do gcs paths look like?","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:03.235772Z","iopub.execute_input":"2026-03-23T15:04:03.236179Z","iopub.status.idle":"2026-03-23T15:04:03.246562Z","shell.execute_reply.started":"2026-03-23T15:04:03.236161Z","shell.execute_reply":"2026-03-23T15:04:03.245720Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print(f\"Running on TPU: ${tpu.master()}\")\nexcept ValueError:\n    tpu = None\n    \nif tpu:\n    IMAGE_SIZE = [512,512] # or 192, 224, 331, 512\nelse:\n    IMAGE_SIZE = [224,224]\n\nGCS_PATH = f\"{GCS_DS_PATH}/tfrecords-jpeg-{IMAGE_SIZE[0]}x{IMAGE_SIZE[1]}\"\n\nTRAINING_FILENAMES = tf.io.gfile.glob(f\"{GCS_PATH}/train/*.tfrec\")\nVALIDATION_FILENAMES = tf.io.gfile.glob(f\"{GCS_PATH}/val/*.tfrec\")\nTEST_FILENAMES = tf.io.gfile.glob(f\"{GCS_PATH}/test/*.tfrec\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:03.246952Z","iopub.execute_input":"2026-03-23T15:04:03.247192Z","iopub.status.idle":"2026-03-23T15:04:03.298793Z","shell.execute_reply.started":"2026-03-23T15:04:03.247177Z","shell.execute_reply":"2026-03-23T15:04:03.297862Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.data.experimental import AUTOTUNE\n\nCLASSES = ['pink primrose',    'hard-leaved pocket orchid', \n           'canterbury bells', 'sweet pea',     \n           'wild geranium',    'tiger lily',           \n           'moon orchid',      'bird of paradise', \n           'monkshood',        'globe thistle',         # 00 - 09\n           \n           'snapdragon',       \"colt's foot\",               \n           'king protea',      'spear thistle', \n           'yellow iris',      'globe-flower',\n           'purple coneflower','peruvian lily',\n           'balloon flower',   'giant white arum lily', # 10 - 19\n           \n           'fire lily',        'pincushion flower',\n           'fritillary',       'red ginger',\n           'grape hyacinth',    'corn poppy',\n           'prince of wales feathers', 'stemless gentian',\n           'artichoke',        'sweet william',         # 20 - 29\n           \n           'carnation',        'garden phlox',\n           'love in the mist', 'cosmos',\n           'alpine sea holly', 'ruby-lipped cattleya',\n           'cape flower',      'great masterwort',\n           'siam tulip',       'lenten rose',           # 30 - 39\n           \n           'barberton daisy',  'daffodil',\n           'sword lily',       'poinsettia',\n           'bolero deep blue', 'wallflower',\n           'marigold',         'buttercup',\n           'daisy',            'common dandelion',      # 40 - 49\n           \n           'petunia',          'wild pansy',\n           'primula',          'sunflower',\n           'lilac hibiscus',   'bishop of llandaff',\n           'gaura',            'geranium',\n           'orange dahlia',    'pink-yellow dahlia',    # 50 - 59\n           \n           'cautleya spicata', 'japanese anemone',\n           'black-eyed susan', 'silverbush',\n           'californian poppy','osteospermum',\n           'spring crocus',    'iris',\n           'windflower',       'tree poppy',            # 60 - 69\n           \n           'gazania',          'azalea',\n           'water lily',       'rose',\n           'thorn apple',      'morning glory',\n           'passion flower',   'lotus',\n           'toad lily',        'anthurium',             # 70 - 79\n           \n           'frangipani',       'clematis',\n           'hibiscus',         'columbine',\n           'desert-rose',      'tree mallow',\n           'magnolia',         'cyclamen ',\n           'watercress',       'canna lily',            # 80 - 89\n           \n           'hippeastrum ',     'bee balm',\n           'pink quill',       'foxglove',\n           'bougainvillea',    'camellia',\n           'mallow',           'mexican petunia',\n           'bromelia',         'blanket flower',        # 90 - 99\n           \n           'trumpet creeper',  'blackberry lily',\n           'common tulip',     'wild rose']                                                                                                                                               # 100 - 102\n\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.cast(image, tf.float32)/255.0\n    image = tf.reshape(image, [*IMAGE_SIZE,3])\n    return image\n\ndef read_labeled_tfrecord(example):\n    LABELED_TFR_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"class\": tf.io.FixedLenFeature([], tf.int64)\n    }\n    example = tf.io.parse_single_example(example, LABELED_TFR_FORMAT)\n    image = decode_image(example[\"image\"])\n    label = tf.cast(example[\"class\"], tf.int32)\n    return image, label\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFR_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"id\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFR_FORMAT)\n    image = decode_image(example[\"image\"])\n    idnum = example[\"id\"]\n    return image, idnum\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False # disable order, increase speed\n    \n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE)\n    dataset = dataset.with_options(ignore_order)\n    if labeled:\n        dataset = dataset.map(read_labeled_tfrecord, num_parallel_calls=AUTOTUNE)\n    else:\n        dataset = dataset.map(read_unlabeled_tfrecord, num_parallel_calls=AUTOTUNE)\n    return dataset\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:03.299295Z","iopub.execute_input":"2026-03-23T15:04:03.299467Z","iopub.status.idle":"2026-03-23T15:04:03.307906Z","shell.execute_reply.started":"2026-03-23T15:04:03.299451Z","shell.execute_reply":"2026-03-23T15:04:03.307188Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import re\nfrom tensorflow import keras\n\nBATCH_SIZE = 16 * strategy.num_replicas_in_sync\n\n# Random erasing, following https://github.com/zhunzhong07/Random-Erasing\n@tf.function\ndef random_black_box(image, prob_apply=0.25, min_area=0.02, max_area=0.4, min_aspect=0.3):\n    print(image.shape)\n    p = tf.random.uniform([], 0, 1)\n    if p <= prob_apply:\n        h,w,c = image.shape\n        origin_area = h*w\n        target_area = tf.random.uniform([], min_area, max_area) * origin_area\n        aspect_ratio = tf.random.uniform([], min_aspect, 1/min_aspect)\n        \n        erase_h = tf.cast(tf.round(tf.sqrt(target_area * aspect_ratio)), tf.int32)\n        erase_w = tf.cast(tf.round(tf.sqrt(target_area / aspect_ratio)), tf.int32)\n              \n        if erase_w < w:\n            x1 = tf.random.uniform([], 0, w-erase_w, dtype=tf.int32)\n        else:\n            erase_w = w\n            x1 = 0\n        if erase_h < h:\n            y1 = tf.random.uniform([], 0, h-erase_h, dtype=tf.int32)\n        else:\n            erase_h = h\n            y1 = 0\n        \n        image = tf.tensor_scatter_nd_update(image,\n          tf.stack(tf.meshgrid(x1+tf.range(erase_w), y1+tf.range(erase_h)), axis=-1),\n          tf.zeros([erase_h, erase_w, c]))\n        return image\n    else:\n        return image\n\nrng = tf.random.Generator.from_seed(4711, alg='philox')\ndef data_augment(image, label):\n    seed = rng.make_seeds(2)[0]\n    image = tf.image.stateless_random_brightness(image, 0.3, seed)\n    image = tf.image.stateless_random_contrast(image, 0.75, 1.25, seed)\n    image = tf.image.stateless_random_flip_left_right(image, seed)\n    image = tf.image.stateless_random_flip_up_down(image, seed)\n    image = random_black_box(image, prob_apply=0.40)\n    return image, label\n\ndef get_training_dataset():\n    dataset = load_dataset(TRAINING_FILENAMES, labeled=True)\n    dataset = dataset.map(data_augment, num_parallel_calls=AUTOTUNE)\n    dataset = dataset.repeat()\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset\n\ndef get_validation_dataset(ordered=False):\n    dataset = load_dataset(VALIDATION_FILENAMES, labeled=True, ordered=ordered)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.cache()\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset\n    \ndef get_test_dataset(ordered=False):\n    dataset = load_dataset(TEST_FILENAMES, labeled=False, ordered=ordered)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset\n\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\nNUM_TRAINING_IMAGES = count_data_items(TRAINING_FILENAMES)\nNUM_VALIDATION_IMAGES = count_data_items(VALIDATION_FILENAMES)\nNUM_TEST_IMAGES = count_data_items(TEST_FILENAMES)\n\nprint(f\"\"\"\n{NUM_TRAINING_IMAGES} training images\n{NUM_VALIDATION_IMAGES} validation images\n{NUM_TEST_IMAGES} unlabeled testing images\"\"\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:03.308469Z","iopub.execute_input":"2026-03-23T15:04:03.308628Z","iopub.status.idle":"2026-03-23T15:04:03.346616Z","shell.execute_reply.started":"2026-03-23T15:04:03.308613Z","shell.execute_reply":"2026-03-23T15:04:03.345794Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compute class weights\nfrom collections import Counter\ndataset = load_dataset(TRAINING_FILENAMES, labeled=True)\ndataset = dataset.map(lambda img,lbl: lbl, num_parallel_calls=AUTOTUNE)\ndataset = dataset.batch(BATCH_SIZE)\ndataset = dataset.prefetch(AUTOTUNE)\n\ncounter = Counter()\nfor batch in dataset:\n    counter.update([*batch.numpy()])\nmax_count = max([counter[i] for i in range(len(CLASSES))])\nweight_per_class = {id: max_count/counter[id] for id in range(len(CLASSES))}\ndel(counter)\ndel(dataset)\ndel(batch)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:03.347052Z","iopub.execute_input":"2026-03-23T15:04:03.347205Z","iopub.status.idle":"2026-03-23T15:04:04.855959Z","shell.execute_reply.started":"2026-03-23T15:04:03.347190Z","shell.execute_reply":"2026-03-23T15:04:04.854719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nfrom matplotlib import pyplot\n\nvalues = [weight_per_class[w] for w in range(len(CLASSES))]\n\n# normalize values for colormap\nnorm = pyplot.Normalize()(np.array(values))\n\n# convert palette to list of colors\ncolors = list(pyplot.cm.YlGnBu(norm))\n\npyplot.figure(figsize=(20,10))\n\nsns.barplot(\n    x=CLASSES,\n    y=values,\n    hue=CLASSES,          # required by new seaborn API\n    palette=colors,       # must be list\n    legend=False\n)\n\npyplot.xticks(rotation=60, horizontalalignment='right', size='small')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:04.856612Z","iopub.execute_input":"2026-03-23T15:04:04.856818Z","iopub.status.idle":"2026-03-23T15:04:06.364205Z","shell.execute_reply.started":"2026-03-23T15:04:04.856801Z","shell.execute_reply":"2026-03-23T15:04:06.363178Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ds_train = get_training_dataset()\nds_val = get_validation_dataset()\nds_test = get_test_dataset()\n\nprint(f\"\"\"\nTraining data: {ds_train}\nValidation data: {ds_val}\nTest data: {ds_test}\n\"\"\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:06.364921Z","iopub.execute_input":"2026-03-23T15:04:06.365115Z","iopub.status.idle":"2026-03-23T15:04:06.696005Z","shell.execute_reply.started":"2026-03-23T15:04:06.365098Z","shell.execute_reply":"2026-03-23T15:04:06.694861Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Training data shapes:\")\nfor image, label in ds_train.take(3):\n    print(image.numpy().shape, label.numpy().shape)\nprint(f\"Training data labels: \\n{label.numpy()}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:06.696625Z","iopub.execute_input":"2026-03-23T15:04:06.696792Z","iopub.status.idle":"2026-03-23T15:04:07.571100Z","shell.execute_reply.started":"2026-03-23T15:04:06.696777Z","shell.execute_reply":"2026-03-23T15:04:07.569818Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Test data shapes:\")\nfor image, idnum in ds_test.take(3):\n    print(image.numpy().shape, idnum.numpy().shape)\nprint(f\"Test data labels: \\n{idnum.numpy().astype('U')}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:07.571760Z","iopub.execute_input":"2026-03-23T15:04:07.571937Z","iopub.status.idle":"2026-03-23T15:04:07.655521Z","shell.execute_reply.started":"2026-03-23T15:04:07.571919Z","shell.execute_reply":"2026-03-23T15:04:07.654550Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image, label = next(ds_train.take(1).as_numpy_iterator())\npyplot.figure(figsize=(20,20))\nfor i in range(16):\n    pyplot.subplot(4,4,i+1)\n    pyplot.imshow(image[i,:,:,:])\n    pyplot.axis(\"off\")\n    pyplot.title(CLASSES[label[i]])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:07.656413Z","iopub.execute_input":"2026-03-23T15:04:07.656629Z","iopub.status.idle":"2026-03-23T15:04:09.604135Z","shell.execute_reply.started":"2026-03-23T15:04:07.656610Z","shell.execute_reply":"2026-03-23T15:04:09.602937Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q efficientnet","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:09.604632Z","iopub.execute_input":"2026-03-23T15:04:09.604793Z","iopub.status.idle":"2026-03-23T15:04:14.012107Z","shell.execute_reply.started":"2026-03-23T15:04:09.604777Z","shell.execute_reply":"2026-03-23T15:04:14.010918Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow import keras\nimport efficientnet.tfkeras as efn\n\nwith strategy.scope():\n    enb7 = efn.EfficientNetB7(weights='noisy-student', include_top=False,\n                             input_shape=[*IMAGE_SIZE,3])\n    enb7.trainable = False\n    model = keras.Sequential([\n        enb7,\n        keras.layers.GlobalAveragePooling2D(),\n        keras.layers.Dense(2048, activation='relu'),\n        keras.layers.Dropout(0.5),\n        keras.layers.Dense(1024, activation='relu'),\n        keras.layers.Dropout(0.5),\n        keras.layers.Dense(len(CLASSES), activation='softmax')\n    ])\nprint(model.summary())\nkeras.utils.plot_model(model)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:14.015179Z","iopub.execute_input":"2026-03-23T15:04:14.015357Z","iopub.status.idle":"2026-03-23T15:04:20.654820Z","shell.execute_reply.started":"2026-03-23T15:04:14.015336Z","shell.execute_reply":"2026-03-23T15:04:20.654126Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.compile(\n    optimizer=\"Nadam\",\n    loss='sparse_categorical_crossentropy',\n    metrics=['sparse_categorical_accuracy']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:20.655435Z","iopub.execute_input":"2026-03-23T15:04:20.655906Z","iopub.status.idle":"2026-03-23T15:04:20.663980Z","shell.execute_reply.started":"2026-03-23T15:04:20.655887Z","shell.execute_reply":"2026-03-23T15:04:20.663314Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"LR_START = 0.0001\nLR_MAX = 0.00005 * strategy.num_replicas_in_sync\nLR_MIN = 0.00001\nLR_RAMPUP_EPOCHS = 4\nLR_SUSTAIN_EPOCHS = 6\nLR_EXP_DECAY = 0.8\n\ndef lrfn(epoch):\n    if epoch < LR_RAMPUP_EPOCHS:\n        lr = np.random.random_sample()*LR_START\n    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n        lr = LR_MAX\n    else:\n        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY ** (epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n    return lr\n\n#local_save_options = tf.saved_model.SaveOptions(experimental_io_device='/job:localhost')\nlr_callback = keras.callbacks.LearningRateScheduler(lrfn, verbose=True)\ncp_callback = keras.callbacks.ModelCheckpoint(\n        \"best_model.hdf5.keras\",\n        monitor=\"val_sparse_categorical_accuracy\",\n        save_best_only=True,\n        verbose=1\n        #options=local_save_options\n    )\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:20.664502Z","iopub.execute_input":"2026-03-23T15:04:20.664651Z","iopub.status.idle":"2026-03-23T15:04:22.123482Z","shell.execute_reply.started":"2026-03-23T15:04:20.664637Z","shell.execute_reply":"2026-03-23T15:04:22.122690Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"STEPS_PER_EPOCH = NUM_TRAINING_IMAGES // BATCH_SIZE\nhistories = []","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:22.123961Z","iopub.execute_input":"2026-03-23T15:04:22.124145Z","iopub.status.idle":"2026-03-23T15:04:22.146408Z","shell.execute_reply.started":"2026-03-23T15:04:22.124129Z","shell.execute_reply":"2026-03-23T15:04:22.145603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 4\nhistories.append(model.fit(\n    ds_train,\n    validation_data=ds_val,\n    epochs=EPOCHS,\n    steps_per_epoch=STEPS_PER_EPOCH,\n    class_weight=weight_per_class,\n    callbacks=[lr_callback, cp_callback]))\nmodel.load_weights(\"best_model.hdf5.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-23T15:04:22.146908Z","iopub.execute_input":"2026-03-23T15:04:22.147084Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.figure(figsize=(16,8))\npyplot.plot(np.hstack([history.history[\"loss\"] for history in histories]))\npyplot.plot(np.hstack([history.history[\"val_loss\"] for history in histories]))\npyplot.title(\"Loss vs. Validation Loss\")\npyplot.xlabel(\"Epoch\")\npyplot.legend([\"train\", \"valid.\"])","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}