{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":21154,"databundleVersionId":1243559,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport math\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping, ModelCheckpoint\nfrom sklearn.utils.class_weight import compute_class_weight\n\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n    print(f\"Found {len(gpus)} GPU(s)\")\n    for gpu in gpus:\n        tf.config.experimental.set_memory_growth(gpu, True)\n        details = tf.config.experimental.get_device_details(gpu)\n        print(f\"GPU: {gpu.name}\")\n        print(f\"  Device type: {details.get('device_name', 'N/A')}\")\n        \n    strategy = tf.distribute.MirroredStrategy()\n    print(f'\\nNumber of devices in strategy: {strategy.num_replicas_in_sync}')\n\n    BASE_BATCH_SIZE = 32\n    BATCH_SIZE = BASE_BATCH_SIZE * strategy.num_replicas_in_sync\n    print(f\"Using batch size: {BATCH_SIZE} (base: {BASE_BATCH_SIZE} * {strategy.num_replicas_in_sync})\")\n\nstrategy_scope = strategy.scope\n\nIMG_SIZE = 224\nNUM_CLASSES = 104\nEPOCHS = 25\nBATCH_SIZE = 32\nAUTOTUNE = tf.data.AUTOTUNE\n\nTRAIN_GLOB = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-512x512/train/*.tfrec\"\nVAL_GLOB = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-512x512/val/*.tfrec\"\nTEST_GLOB = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-512x512/test/*.tfrec\"\n\nFEATURE_DESCRIPTION = {\n    'id': tf.io.FixedLenFeature([], tf.string),\n    'image': tf.io.FixedLenFeature([], tf.string),\n    'class': tf.io.FixedLenFeature([], tf.int64, default_value=-1),\n}\n\ndef decode_example(example_proto, img_size, labeled=True):\n    x = tf.io.parse_single_example(example_proto, FEATURE_DESCRIPTION)\n\n    image = tf.image.decode_jpeg(x['image'], channels=3)\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    image = tf.image.resize(image, [img_size, img_size])\n\n    if labeled:\n        label = tf.cast(x['class'], tf.int32)\n        return image, tf.one_hot(label, NUM_CLASSES)\n    else:\n        return image, x['id']\n\ndef preprocess_train(image, label):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_flip_up_down(image)\n\n    image = tf.image.random_brightness(image, 0.2)\n    image = tf.image.random_contrast(image, 0.8, 1.2)\n\n    if tf.random.uniform([]) > 0.5:\n        scale = tf.random.uniform([], 1.0, 1.2)\n        new_size = tf.cast(tf.cast(IMG_SIZE, tf.float32) * scale, tf.int32)\n        image = tf.image.resize(image, [new_size, new_size])\n\n        image = tf.image.random_crop(image, [IMG_SIZE, IMG_SIZE, 3])\n    \n    image = tf.clip_by_value(image, 0.0, 1.0)\n    return image, label\n\ndef make_dataset(file_pattern, batch_size, training=False, labeled=True):\n    files = tf.io.gfile.glob(file_pattern)\n    ds = tf.data.TFRecordDataset(files, num_parallel_reads=AUTOTUNE)\n    ds = ds.map(lambda x: decode_example(x, IMG_SIZE, labeled=labeled), AUTOTUNE)\n    if training:\n        ds = ds.map(preprocess_train, AUTOTUNE)\n        ds = ds.shuffle(2048, reshuffle_each_iteration=True)\n    ds = ds.batch(batch_size)\n    ds = ds.prefetch(AUTOTUNE)\n    return ds\n\nprint(\"Testing data loading without augmentation...\")\ntest_ds_no_aug = tf.data.TFRecordDataset(tf.io.gfile.glob(TRAIN_GLOB)[:1])\nfor record in test_ds_no_aug.take(1):\n    example = tf.io.parse_single_example(record, FEATURE_DESCRIPTION)\n    image = tf.image.decode_jpeg(example['image'], channels=3)\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    image = tf.image.resize(image, [IMG_SIZE, IMG_SIZE])\n    print(f\"Original image shape: {image.shape}\")\n    print(f\"Image dtype: {image.dtype}\")\n    print(f\"Min pixel value: {tf.reduce_min(image).numpy():.3f}\")\n    print(f\"Max pixel value: {tf.reduce_max(image).numpy():.3f}\")\n\nprint(\"\\nCreating datasets...\")\ntrain_ds = make_dataset(TRAIN_GLOB, BATCH_SIZE, training=True, labeled=True)\nval_ds = make_dataset(VAL_GLOB, BATCH_SIZE, training=False, labeled=True)\n\nprint(\"\\nCalculating class weights...\")\n\nall_labels = []\ntry:\n    for images, labels in train_ds.take(20):  # Используем меньше батчей\n        batch_labels = tf.argmax(labels, axis=1).numpy()\n        all_labels.extend(batch_labels)\n    all_labels = np.array(all_labels)\n    \n    class_weights = compute_class_weight(\n        class_weight='balanced',\n        classes=np.arange(NUM_CLASSES),\n        y=all_labels\n    )\n    class_weight_dict = {i: class_weights[i] for i in range(NUM_CLASSES)}\n    \n    print(f\"Samples used for class weights: {len(all_labels)}\")\n    print(f\"Min class weight: {np.min(class_weights):.2f}\")\n    print(f\"Max class weight: {np.max(class_weights):.2f}\")\n    \nexcept Exception as e:\n    print(f\"Error calculating class weights: {e}\")\n    print(\"Using uniform class weights\")\n    class_weight_dict = {i: 1.0 for i in range(NUM_CLASSES)}\n\nprint(\"\\nTesting one batch...\")\nfor images, labels in train_ds.take(1):\n    print(f\"Batch images shape: {images.shape}\")\n    print(f\"Batch labels shape: {labels.shape}\")\n    print(f\"Sample labels: {tf.argmax(labels[:5], axis=1).numpy()}\")\n\ndef conv_block(x, filters, kernel_size=3, stride=1):\n    x = layers.Conv2D(filters, kernel_size, strides=stride, padding='same',\n                     kernel_initializer='he_normal')(x)\n    x = layers.BatchNormalization(momentum=0.9, epsilon=1e-5)(x)\n    x = layers.Activation('relu')(x)\n    return x\n\ndef residual_block(x, filters, downsample=False):\n    shortcut = x\n    stride = 2 if downsample else 1\n    \n    x = conv_block(x, filters, 3, stride)\n    x = conv_block(x, filters, 3, 1)\n    \n    if downsample or shortcut.shape[-1] != filters:\n        shortcut = layers.Conv2D(filters, 1, strides=stride,\n                               kernel_initializer='he_normal')(shortcut)\n        shortcut = layers.BatchNormalization(momentum=0.9, epsilon=1e-5)(shortcut)\n    \n    x = layers.Add()([x, shortcut])\n    x = layers.Activation('relu')(x)\n    return x\n\ndef build_model():\n    inputs = layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3))\n\n    x = conv_block(inputs, 64, 3, 1)\n    x = conv_block(x, 64, 3, 1)\n    x = conv_block(x, 128, 3, 2)\n\n    for _ in range(3):\n        x = residual_block(x, 128)\n\n    x = residual_block(x, 256, downsample=True)\n    for _ in range(3):\n        x = residual_block(x, 256)\n    \n    x = residual_block(x, 512, downsample=True)\n    for _ in range(2):\n        x = residual_block(x, 512)\n\n    x = layers.GlobalAveragePooling2D()(x)\n\n    x = layers.Dense(512, activation='relu', kernel_initializer='he_normal')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.4)(x)\n    \n    x = layers.Dense(256, activation='relu', kernel_initializer='he_normal')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.3)(x)\n    \n    x = layers.Dense(128, activation='relu', kernel_initializer='he_normal')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.2)(x)\n    \n    outputs = layers.Dense(NUM_CLASSES, activation='softmax')(x)\n    \n    model = tf.keras.Model(inputs, outputs)\n    return model\n\n\nprint(\"\\nBuilding model...\")\nwith strategy.scope():\n    model = build_model()\n    \n    def lr_schedule(epoch):\n        \"\"\"Learning Rate Schedule\"\"\"\n        lr = 1e-3\n        if epoch > 30:\n            lr = 5e-4\n        if epoch > 60:\n            lr = 1e-4\n        if epoch > 80:\n            lr = 5e-5\n        return lr\n\n    callbacks = [\n        tf.keras.callbacks.LearningRateScheduler(lr_schedule),\n        ReduceLROnPlateau(\n            monitor='val_accuracy',\n            factor=0.5,\n            patience=5,\n            min_lr=1e-6,\n            verbose=1\n        ),\n        EarlyStopping(\n            monitor='val_accuracy',\n            patience=15,\n            restore_best_weights=True,\n            min_delta=0.001,\n            verbose=1\n        ),\n        ModelCheckpoint(\n            'best_model.h5',\n            monitor='val_accuracy',\n            save_best_only=True,\n            verbose=1\n        )\n    ]\n\n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),\n        loss='categorical_crossentropy',\n        metrics=[\n            'accuracy',\n            tf.keras.metrics.TopKCategoricalAccuracy(k=3, name='top3_accuracy'),\n            tf.keras.metrics.TopKCategoricalAccuracy(k=5, name='top5_accuracy')\n        ],\n    )\n\nmodel.summary()\n\n# Обучение\nprint(\"\\nStarting training...\")\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=callbacks,\n    class_weight=class_weight_dict,\n    verbose=1\n)\n\nprint(\"\\nEvaluating model...\")\nval_loss, val_acc, val_top3, val_top5 = model.evaluate(val_ds, verbose=0)\nprint(f\"Validation accuracy: {val_acc:.4f}\")\nprint(f\"Validation top-3 accuracy: {val_top3:.4f}\")\nprint(f\"Validation top-5 accuracy: {val_top5:.4f}\")\n\ntry:\n    model.load_weights('best_model.h5')\n    print(\"\\nLoaded best model weights\")\n    val_loss, val_acc, val_top3, val_top5 = model.evaluate(val_ds, verbose=0)\n    print(f\"Best model validation accuracy: {val_acc:.4f}\")\nexcept:\n    print(\"\\nUsing final model weights\")\n\nprint(\"\\nPredicting on test data...\")\ntest_ds = make_dataset(TEST_GLOB, BATCH_SIZE, training=False, labeled=False)\n\nids = []\npreds = []\nconfidences = []\n\nfor batch_images, batch_ids in test_ds:\n    batch_preds = model.predict(batch_images, verbose=0)\n    batch_preds_labels = np.argmax(batch_preds, axis=1)\n    batch_confidences = np.max(batch_preds, axis=1)\n    \n    preds.extend(batch_preds_labels)\n    confidences.extend(batch_confidences)\n    ids.extend([_id.numpy().decode() for _id in batch_ids])\n\nconfidences = np.array(confidences)\nprint(f\"\\nPrediction confidence statistics:\")\nprint(f\"Mean confidence: {np.mean(confidences):.4f}\")\nprint(f\"Std confidence: {np.std(confidences):.4f}\")\nprint(f\"Confidence > 0.5: {np.sum(confidences > 0.5) / len(confidences):.2%}\")\nprint(f\"Confidence > 0.8: {np.sum(confidences > 0.8) / len(confidences):.2%}\")\n\nsub = pd.DataFrame({\"id\": ids, \"label\": preds})\nsub.to_csv(\"submission.csv\", index=False)\nprint(f\"\\nSubmission saved to submission.csv\")\nprint(f\"Number of test samples: {len(sub)}\")\n\nunique_classes, class_counts = np.unique(preds, return_counts=True)\nprint(f\"\\nPrediction analysis:\")\nprint(f\"Unique predicted classes: {len(unique_classes)}\")\nprint(f\"Most common predictions:\")\n\nsorted_indices = np.argsort(class_counts)[::-1][:10]\nfor idx in sorted_indices:\n    cls = unique_classes[idx]\n    count = class_counts[idx]\n    percentage = count / len(preds) * 100\n    print(f\"  Class {cls}: {count} samples ({percentage:.1f}%)\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-06T14:15:33.176313Z","iopub.execute_input":"2025-12-06T14:15:33.17687Z","iopub.status.idle":"2025-12-06T14:26:57.568264Z","shell.execute_reply.started":"2025-12-06T14:15:33.176845Z","shell.execute_reply":"2025-12-06T14:26:57.567217Z"}},"outputs":[],"execution_count":null}]}