{"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":"gpu","dataSources":[{"sourceId":21154,"databundleVersionId":1243559,"sourceType":"competition"}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import f1_score, accuracy_score\nimport os\nfrom tensorflow.keras import layers, models","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.616423Z","iopub.execute_input":"2026-01-21T08:36:16.616849Z","iopub.status.idle":"2026-01-21T08:36:16.621232Z","shell.execute_reply.started":"2026-01-21T08:36:16.616812Z","shell.execute_reply":"2026-01-21T08:36:16.620484Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMAGE_SIZE = [192, 192]\nCLASSES = 104\nBATCH_SIZE = 32\nEPOCHS = 60\nLEARNING_RATE = 0.001\n\nBASE_PATH = '/kaggle/input/tpu-getting-started'\nDATA_PATH = f'{BASE_PATH}/tfrecords-jpeg-{IMAGE_SIZE[0]}x{IMAGE_SIZE[1]}'\n\nif not os.path.exists(DATA_PATH):\n    IMAGE_SIZE = [224, 224]\n    DATA_PATH = f'{BASE_PATH}/tfrecords-jpeg-{IMAGE_SIZE[0]}x{IMAGE_SIZE[1]}'\n\nprint(f\"Размер изображений: {IMAGE_SIZE[0]}x{IMAGE_SIZE[1]}\")\nprint(f\"Batch size: {BATCH_SIZE}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.622483Z","iopub.execute_input":"2026-01-21T08:36:16.622698Z","iopub.status.idle":"2026-01-21T08:36:16.638871Z","shell.execute_reply.started":"2026-01-21T08:36:16.622679Z","shell.execute_reply":"2026-01-21T08:36:16.638269Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.image.resize(image, IMAGE_SIZE)\n    image = tf.cast(image, tf.float32) / 255.0\n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.639909Z","iopub.execute_input":"2026-01-21T08:36:16.640451Z","iopub.status.idle":"2026-01-21T08:36:16.650378Z","shell.execute_reply.started":"2026-01-21T08:36:16.640424Z","shell.execute_reply":"2026-01-21T08:36:16.64968Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def read_labeled_tfrecord(example):\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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.652062Z","iopub.execute_input":"2026-01-21T08:36:16.652322Z","iopub.status.idle":"2026-01-21T08:36:16.663766Z","shell.execute_reply.started":"2026-01-21T08:36:16.652302Z","shell.execute_reply":"2026-01-21T08:36:16.663222Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def read_unlabeled_tfrecord(example):\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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.664479Z","iopub.execute_input":"2026-01-21T08:36:16.664671Z","iopub.status.idle":"2026-01-21T08:36:16.681929Z","shell.execute_reply.started":"2026-01-21T08:36:16.664652Z","shell.execute_reply":"2026-01-21T08:36:16.681175Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def augment(image, label):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_brightness(image, 0.2)\n    image = tf.image.random_contrast(image, 0.8, 1.2)\n    return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.682689Z","iopub.execute_input":"2026-01-21T08:36:16.683009Z","iopub.status.idle":"2026-01-21T08:36:16.695636Z","shell.execute_reply.started":"2026-01-21T08:36:16.682982Z","shell.execute_reply":"2026-01-21T08:36:16.695049Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_dataset(filenames, labeled=True, training=False):\n    dataset = tf.data.TFRecordDataset(filenames)\n    \n    if labeled:\n        dataset = dataset.map(read_labeled_tfrecord)\n    else:\n        dataset = dataset.map(read_unlabeled_tfrecord)\n    \n    if training:\n        dataset = dataset.map(augment)\n        dataset = dataset.repeat()\n        dataset = dataset.shuffle(1024)\n    \n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(tf.data.AUTOTUNE)\n    return dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.696536Z","iopub.execute_input":"2026-01-21T08:36:16.696777Z","iopub.status.idle":"2026-01-21T08:36:16.710319Z","shell.execute_reply.started":"2026-01-21T08:36:16.696758Z","shell.execute_reply":"2026-01-21T08:36:16.709663Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_efficient_cnn():\n    model = tf.keras.Sequential([\n        tf.keras.layers.Conv2D(32, (3, 3), activation='relu', \n                              input_shape=(*IMAGE_SIZE, 3)),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.Conv2D(32, (3, 3), activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.MaxPooling2D((2, 2)),\n        tf.keras.layers.Dropout(0.2),\n        \n        tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.MaxPooling2D((2, 2)),\n        tf.keras.layers.Dropout(0.3),\n        \n        tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.MaxPooling2D((2, 2)),\n        tf.keras.layers.Dropout(0.4),\n        \n        tf.keras.layers.Conv2D(256, (3, 3), activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.GlobalAveragePooling2D(),\n        tf.keras.layers.Dropout(0.5),\n        \n        tf.keras.layers.Dense(128, activation='relu'),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.Dropout(0.3),\n        \n        tf.keras.layers.Dense(CLASSES, activation='softmax')\n    ])\n    \n    optimizer = tf.keras.optimizers.Adam(\n        learning_rate=LEARNING_RATE\n    )\n    \n    model.compile(\n        optimizer=optimizer,\n        loss='sparse_categorical_crossentropy',\n        metrics=['accuracy']\n    )\n    \n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.711264Z","iopub.execute_input":"2026-01-21T08:36:16.711537Z","iopub.status.idle":"2026-01-21T08:36:16.726153Z","shell.execute_reply.started":"2026-01-21T08:36:16.711517Z","shell.execute_reply":"2026-01-21T08:36:16.725417Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_filenames = tf.io.gfile.glob(DATA_PATH + '/train/*.tfrec')\nval_filenames = tf.io.gfile.glob(DATA_PATH + '/val/*.tfrec')\ntest_filenames = tf.io.gfile.glob(DATA_PATH + '/test/*.tfrec')\n\nprint(f\"Тренировочных файлов: {len(train_filenames)}\")\nprint(f\"Валидационных файлов: {len(val_filenames)}\")\nprint(f\"Тестовых файлов: {len(test_filenames)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.821062Z","iopub.execute_input":"2026-01-21T08:36:16.821306Z","iopub.status.idle":"2026-01-21T08:36:16.837168Z","shell.execute_reply.started":"2026-01-21T08:36:16.821286Z","shell.execute_reply":"2026-01-21T08:36:16.836413Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds = get_dataset(train_filenames, labeled=True, training=True)\nval_ds = get_dataset(val_filenames, labeled=True, training=False)\ntest_ds = get_dataset(test_filenames, labeled=False, training=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.838687Z","iopub.execute_input":"2026-01-21T08:36:16.838884Z","iopub.status.idle":"2026-01-21T08:36:16.958452Z","shell.execute_reply.started":"2026-01-21T08:36:16.838866Z","shell.execute_reply":"2026-01-21T08:36:16.957678Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_steps = 200\nval_steps = 100\n\nprint(f\"\\nШагов на эпоху: {train_steps}\")\nprint(f\"Шагов валидации: {val_steps}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.959359Z","iopub.execute_input":"2026-01-21T08:36:16.959664Z","iopub.status.idle":"2026-01-21T08:36:16.964167Z","shell.execute_reply.started":"2026-01-21T08:36:16.959635Z","shell.execute_reply":"2026-01-21T08:36:16.963531Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Создание модели...\")\nmodel = build_efficient_cnn()\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:16.965074Z","iopub.execute_input":"2026-01-21T08:36:16.965456Z","iopub.status.idle":"2026-01-21T08:36:17.124423Z","shell.execute_reply.started":"2026-01-21T08:36:16.965427Z","shell.execute_reply":"2026-01-21T08:36:17.123724Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callbacks = [\n    tf.keras.callbacks.ModelCheckpoint(\n        'best_model.weights.h5',\n        monitor='val_accuracy',\n        save_best_only=True,\n        save_weights_only=True,\n        mode='max'\n    ),\n    tf.keras.callbacks.EarlyStopping(\n        monitor='val_accuracy',\n        patience=10,\n        restore_best_weights=True,\n        mode='max'\n    ),\n    tf.keras.callbacks.ReduceLROnPlateau(\n        monitor='val_accuracy',\n        factor=0.5,\n        patience=5,\n        min_lr=1e-6,\n        mode='max',\n        verbose=1\n    )\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:17.126294Z","iopub.execute_input":"2026-01-21T08:36:17.126578Z","iopub.status.idle":"2026-01-21T08:36:17.131057Z","shell.execute_reply.started":"2026-01-21T08:36:17.126556Z","shell.execute_reply":"2026-01-21T08:36:17.130517Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = model.fit(\n    train_ds,\n    steps_per_epoch=train_steps,\n    epochs=EPOCHS,\n    validation_data=val_ds,\n    validation_steps=val_steps,\n    callbacks=callbacks,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:36:17.131908Z","iopub.execute_input":"2026-01-21T08:36:17.132335Z","iopub.status.idle":"2026-01-21T08:49:09.07771Z","shell.execute_reply.started":"2026-01-21T08:36:17.132313Z","shell.execute_reply":"2026-01-21T08:49:09.077068Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.load_weights('best_model.weights.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:49:09.078611Z","iopub.execute_input":"2026-01-21T08:49:09.078893Z","iopub.status.idle":"2026-01-21T08:49:09.213833Z","shell.execute_reply.started":"2026-01-21T08:49:09.07887Z","shell.execute_reply":"2026-01-21T08:49:09.213287Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_images = []\nval_labels = []\n\nfor images, labels in val_ds.take(val_steps):\n    val_images.append(images.numpy())\n    val_labels.append(labels.numpy())\n\nval_images = np.concatenate(val_images, axis=0)\nval_labels = np.concatenate(val_labels, axis=0)\n\nprint(f\"Валидационных изображений: {len(val_images)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:49:09.214611Z","iopub.execute_input":"2026-01-21T08:49:09.214809Z","iopub.status.idle":"2026-01-21T08:49:10.758577Z","shell.execute_reply.started":"2026-01-21T08:49:09.21479Z","shell.execute_reply":"2026-01-21T08:49:10.757939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_predictions = model.predict(val_images, batch_size=BATCH_SIZE, verbose=1)\nval_preds = np.argmax(val_predictions, axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:49:10.75942Z","iopub.execute_input":"2026-01-21T08:49:10.759704Z","iopub.status.idle":"2026-01-21T08:49:16.391567Z","shell.execute_reply.started":"2026-01-21T08:49:10.759675Z","shell.execute_reply":"2026-01-21T08:49:16.390915Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_acc = accuracy_score(val_labels, val_preds)\nval_f1 = f1_score(val_labels, val_preds, average='macro')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:49:16.392507Z","iopub.execute_input":"2026-01-21T08:49:16.392805Z","iopub.status.idle":"2026-01-21T08:49:16.400586Z","shell.execute_reply.started":"2026-01-21T08:49:16.392781Z","shell.execute_reply":"2026-01-21T08:49:16.400008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"РЕЗУЛЬТАТЫ:\")\nprint(f\"Точность (accuracy): {val_acc:.4f}\")\nprint(f\"F1-score: {val_f1:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:49:16.401359Z","iopub.execute_input":"2026-01-21T08:49:16.401573Z","iopub.status.idle":"2026-01-21T08:49:16.412519Z","shell.execute_reply.started":"2026-01-21T08:49:16.401553Z","shell.execute_reply":"2026-01-21T08:49:16.411961Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(12, 4))\n\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='Тренировка')\nplt.plot(history.history['val_accuracy'], label='Валидация')\nplt.title('Точность')\nplt.xlabel('Эпоха')\nplt.ylabel('Точность')\nplt.legend()\nplt.grid(True)\n\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Тренировка')\nplt.plot(history.history['val_loss'], label='Валидация')\nplt.title('Потери')\nplt.xlabel('Эпоха')\nplt.ylabel('Потери')\nplt.legend()\nplt.grid(True)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-21T08:49:16.413338Z","iopub.execute_input":"2026-01-21T08:49:16.413609Z","iopub.status.idle":"2026-01-21T08:49:16.717476Z","shell.execute_reply.started":"2026-01-21T08:49:16.413581Z","shell.execute_reply":"2026-01-21T08:49:16.716739Z"}},"outputs":[],"execution_count":null}]}