{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.18","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpuV5e8","dataSources":[{"sourceId":21154,"databundleVersionId":1243559,"sourceType":"competition"}],"dockerImageVersionId":31091,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import tensorflow as tf\nimport numpy as np\nimport pandas as pd\nfrom kaggle_datasets import KaggleDatasets\nimport math\n\nprint(\"Tensorflow version \" + tf.__version__)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-14T13:46:13.256366Z","iopub.execute_input":"2025-09-14T13:46:13.256516Z","iopub.status.idle":"2025-09-14T13:46:36.039366Z","shell.execute_reply.started":"2025-09-14T13:46:13.256498Z","shell.execute_reply":"2025-09-14T13:46:36.035232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    # 「TPU VM」では、このシンプルなコードでTPUを検出します\n    strategy = tf.distribute.TPUStrategy()\n    print('TPU found!')\nexcept Exception as e:\n    print(e)\n    # TPUが見つからない場合は、デフォルトの戦略を使用\n    print('Falling back to default strategy')\n    strategy = tf.distribute.get_strategy()\n\nprint(\"REPLICAS: \", strategy.num_replicas_in_sync)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-14T13:41:10.568291Z","iopub.execute_input":"2025-09-14T13:41:10.568957Z","iopub.status.idle":"2025-09-14T13:41:10.581357Z","shell.execute_reply.started":"2025-09-14T13:41:10.568920Z","shell.execute_reply":"2025-09-14T13:41:10.576565Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# GCS (Google Cloud Storage)上のデータセットへのパスを取得\n# TPUはGCSから直接データを読み込むことで高速化される\nGCS_DS_PATH = KaggleDatasets().get_gcs_path()\n\n# --- パラメータ設定 ---\nIMAGE_SIZE = [512, 512]\nEPOCHS = 20\n# バッチサイズはTPUのコア数(8)の倍数にすると効率が良い\nBATCH_SIZE = 16 * strategy.num_replicas_in_sync\n\n# データセットのファイルパスを取得\nGCS_PATH = GCS_DS_PATH + '/tfrecords-jpeg-512x512'\nTRAINING_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/train/*.tfrec')\nVALIDATION_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/val/*.tfrec')\nTEST_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/test/*.tfrec')\n\n# クラス名 (104種類の花)\nCLASSES = ['pink primrose', 'hard-leaved pocket orchid', 'canterbury bells', 'sweet pea', 'wild geranium', 'tiger lily', 'moon orchid', 'bird of paradise', 'monkshood', 'globe thistle', 'snapdragon', \"colt's foot\", 'king protea', 'spear thistle', 'yellow iris', 'globe-flower', 'purple coneflower', 'peruvian lily', 'ball moss', 'giant white arum lily', 'fire lily', 'pincushion flower', 'fritillary', 'red ginger', 'grape hyacinth', 'corn poppy', 'prince of wales feathers', 'stemless gentian', 'artichoke', 'sweet william', 'carnation', 'garden phlox', 'love in the mist', 'mexican aster', 'alpine sea holly', 'ruby-lipped cattleya', 'cape flower', 'great masterwort', 'siam tulip', 'lenten rose', 'barberton daisy', 'daffodil', 'sword lily', 'poinsettia', 'bolero deep blue', 'wallflower', 'marigold', 'buttercup', 'daisy', 'common dandelion', 'petunia', 'wild pansy', 'primula', 'sunflower', 'pelargonium', 'bishop of llandaff', 'gaura', 'geranium', 'orange dahlia', 'pink-yellow dahlia', 'cautleya spicata', 'japanese anemone', 'black-eyed susan', 'silverbush', 'californian poppy', 'osteospermum', 'spring crocus', 'bearded iris', 'windflower', 'tree poppy', 'gazania', 'azalea', 'water lily', 'rose', 'thorn apple', 'morning glory', 'passion flower', 'lotus', 'toad lily', 'anthurium', 'frangipani', 'hibiscus', 'balloon flower', 'foxglove', 'bougainvillea', 'camellia', 'mallow', 'mexican petunia', 'bromelia', 'blanket flower', 'trumpet creeper', 'blackberry lily', 'common tulip', 'wild rose', 'watercress', 'magnolia', 'cyclamen ', 'tree mallow', 'english marigold', 'butterbur', 'columbine', 'desert-rose', 'tree of heaven', 'standing cypress', 'gladiolus']","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def decode_image(image_data):\n    \"\"\"TFRecordから読み込んだバイナリデータを画像形式にデコード\"\"\"\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0  # [0, 255] -> [0, 1]に正規化\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n\ndef read_labeled_tfrecord(example):\n    \"\"\"ラベル付きデータ（学習/検証用）をパースする\"\"\"\n    LABELED_TFREC_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_TFREC_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    \"\"\"ラベルなしデータ（テスト用）をパースする\"\"\"\n    UNLABELED_TFREC_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_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    idnum = example['id']\n    return image, idnum\n\ndef data_augment(image, label):\n    \"\"\"データ拡張（学習データにのみ適用）\"\"\"\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_flip_up_down(image)\n    image = tf.image.random_saturation(image, 0.9, 1.1)\n    image = tf.image.random_brightness(image, 0.1)\n    # 他にも回転、ズーム、カットアウトなど様々な手法がある\n    return image, label\n\ndef get_dataset(filenames, labeled=True, ordered=False, augmented=False):\n    \"\"\"TFRecordファイルからtf.data.Datasetを構築する\"\"\"\n    AUTO = tf.data.experimental.AUTOTUNE\n    \n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)\n    \n    # 順序を保つ必要がない場合は、シャッフルや並列処理を効率化\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False\n    dataset = dataset.with_options(ignore_order)\n    \n    # パース処理\n    if labeled:\n        dataset = dataset.map(read_labeled_tfrecord, num_parallel_calls=AUTO)\n    else:\n        dataset = dataset.map(read_unlabeled_tfrecord, num_parallel_calls=AUTO)\n    \n    # データ拡張\n    if augmented:\n        dataset = dataset.map(data_augment, num_parallel_calls=AUTO)\n        \n    dataset = dataset.repeat() # 学習中にデータが尽きないようにリピート\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(buffer_size=AUTO) # 次のバッチをバックグラウンドで準備\n    return dataset\n\n# データセットのインスタンスを作成\ntrain_dataset = get_dataset(TRAINING_FILENAMES, labeled=True, augmented=True)\nvalid_dataset = get_dataset(VALIDATION_FILENAMES, labeled=True)\ntest_dataset = get_dataset(TEST_FILENAMES, labeled=False, ordered=True)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope():\n    # ImageNetで事前学習済みのEfficientNetB7を読み込む\n    # `include_top=False`で最終層（1000クラス分類層）を除外\n    pretrained_model = tf.keras.applications.EfficientNetB7(\n        weights='imagenet',\n        include_top=False,\n        input_shape=[*IMAGE_SIZE, 3]\n    )\n    pretrained_model.trainable = True # 転移学習のためにモデル全体を再学習可能にする\n\n    model = tf.keras.Sequential([\n        pretrained_model,\n        tf.keras.layers.GlobalAveragePooling2D(), # 特徴マップをベクトル化\n        tf.keras.layers.Dense(len(CLASSES), activation='softmax') # 104クラス分類の出力層\n    ])\n\n# モデルのコンパイル\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(),\n    loss='sparse_categorical_crossentropy',\n    metrics=['sparse_categorical_accuracy']\n)\n\nmodel.summary()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# データセットに含まれる画像の総数を計算\nnum_train_images = int(12753) # 事前に数えておく\nnum_valid_images = int(3712)\nnum_test_images = int(7382)\nsteps_per_epoch = num_train_images // BATCH_SIZE\nvalidation_steps = num_valid_images // BATCH_SIZE\n\nprint(\"学習を開始します...\")\nhistory = model.fit(\n    train_dataset,\n    epochs=EPOCHS,\n    steps_per_epoch=steps_per_epoch,\n    validation_data=valid_dataset,\n    validation_steps=validation_steps\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"テストデータで予測を行います...\")\n# テストデータセットはリピートしないように再設定\ntest_ds_for_predict = get_dataset(TEST_FILENAMES, labeled=False, ordered=True)\ntest_images_ds = test_ds_for_predict.map(lambda image, idnum: image)\ntest_ids_ds = test_ds_for_predict.map(lambda image, idnum: idnum).unbatch()\n\n# 予測の実行\nprobabilities = model.predict(test_images_ds)\npredictions = np.argmax(probabilities, axis=-1)\n\n# IDを取得\ntest_ids = next(iter(test_ids_ds.batch(num_test_images))).numpy().astype('U')\n\n# 提出用DataFrameを作成\nsubmission = pd.DataFrame(data={'id': test_ids, 'label': predictions})\nsubmission.to_csv('submission.csv', index=False)\n\nprint(\"提出ファイル 'submission.csv' を作成しました。\")\nprint(submission.head())","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}