{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Классификация кошек и собак","metadata":{}},{"cell_type":"markdown","source":"## Данные","metadata":{}},{"cell_type":"code","source":"!ls /kaggle/input","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:16:54.218684Z","iopub.execute_input":"2026-09-11T19:16:54.219468Z","iopub.status.idle":"2026-09-11T19:16:54.339864Z","shell.execute_reply.started":"2026-09-11T19:16:54.219441Z","shell.execute_reply":"2026-09-11T19:16:54.338643Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport zipfile\n\nINPUT = next((d for d, _, files in os.walk('/kaggle/input') if 'train.zip' in files), None)\n\nprint(INPUT, os.listdir(INPUT))\n\nDATA = '/kaggle/temp/data' if os.path.isdir('/kaggle/temp') else '/tmp/data'\n\nfor name in ['train', 'test']:\n    with zipfile.ZipFile(f'{INPUT}/{name}.zip') as z:\n        z.extractall(DATA)\n\nTRAIN_DIR = f'{DATA}/train'\nTEST_DIR = f'{DATA}/test'\nSAMPLE_SUBMISSION = f'{INPUT}/sample_submission.csv'\nOUT_CSV = '/kaggle/working/submission.csv'\nCHECKPOINT = '/kaggle/working/head.keras'\n\nprint(len(os.listdir(TRAIN_DIR)), len(os.listdir(TEST_DIR)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:16:54.341849Z","iopub.execute_input":"2026-09-11T19:16:54.342206Z","iopub.status.idle":"2026-09-11T19:17:08.952017Z","shell.execute_reply.started":"2026-09-11T19:16:54.342173Z","shell.execute_reply":"2026-09-11T19:17:08.951377Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Подготовка данных\n\n1 = dog, 0 = cat","metadata":{}},{"cell_type":"code","source":"import random\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom sklearn.model_selection import train_test_split\n\nprint(tf.__version__, keras.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:17:08.952839Z","iopub.execute_input":"2026-09-11T19:17:08.953029Z","iopub.status.idle":"2026-09-11T19:17:26.551582Z","shell.execute_reply.started":"2026-09-11T19:17:08.953011Z","shell.execute_reply":"2026-09-11T19:17:26.550946Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filenames = os.listdir(TRAIN_DIR)\ncategories = []\nfor filename in filenames:\n    category = filename.split('.')[0]\n    if 'cat' in category:\n        categories.append(0)\n    else:\n        categories.append(1)\n\ndf = pd.DataFrame({\n    'filename': filenames,\n    'label': categories\n})\n\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:17:26.553236Z","iopub.execute_input":"2026-09-11T19:17:26.553646Z","iopub.status.idle":"2026-09-11T19:17:26.619440Z","shell.execute_reply.started":"2026-09-11T19:17:26.553623Z","shell.execute_reply":"2026-09-11T19:17:26.618550Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df['label'].value_counts().plot.bar()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:17:26.620562Z","iopub.execute_input":"2026-09-11T19:17:26.620841Z","iopub.status.idle":"2026-09-11T19:17:26.847274Z","shell.execute_reply.started":"2026-09-11T19:17:26.620818Z","shell.execute_reply":"2026-09-11T19:17:26.846594Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample = random.choice(filenames)\nplt.imshow(plt.imread(f\"{TRAIN_DIR}/\" + sample))\nplt.title(sample)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:17:26.848161Z","iopub.execute_input":"2026-09-11T19:17:26.848460Z","iopub.status.idle":"2026-09-11T19:17:27.089932Z","shell.execute_reply.started":"2026-09-11T19:17:26.848430Z","shell.execute_reply":"2026-09-11T19:17:27.088787Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df = pd.DataFrame({\n    'filename': os.listdir(TEST_DIR)\n})\n\ntrain_paths = (TRAIN_DIR + \"/\" + df['filename']).tolist()\ntest_paths = (TEST_DIR + \"/\" + test_df['filename']).tolist()\n\nprint(len(train_paths), len(test_paths))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:17:27.092175Z","iopub.execute_input":"2026-09-11T19:17:27.092741Z","iopub.status.idle":"2026-09-11T19:17:27.119273Z","shell.execute_reply.started":"2026-09-11T19:17:27.092681Z","shell.execute_reply":"2026-09-11T19:17:27.118266Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BATCH_SIZE = 64\nAUTOTUNE = tf.data.AUTOTUNE\n\n\ndef load_image(path, size):\n    img = tf.io.read_file(path)\n    img = tf.io.decode_image(img, channels=3, expand_animations=False)\n    img = tf.image.resize(img, size)\n    img.set_shape(size + (3,))\n    return img\n\n\ndef extract_features(model_fn, size, paths):\n    base = model_fn(weights='imagenet', include_top=False, input_shape=size + (3,), pooling='avg')\n\n    inputs = keras.Input(shape=size + (3,))\n    x = layers.Rescaling(1. / 127.5, offset=-1)(inputs)\n    extractor = keras.Model(inputs, base(x))\n\n    ds = (tf.data.Dataset.from_tensor_slices(paths)\n          .map(lambda p: load_image(p, size), num_parallel_calls=AUTOTUNE)\n          .batch(BATCH_SIZE)\n          .prefetch(AUTOTUNE))\n\n    return extractor.predict(ds, verbose=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:17:27.120498Z","iopub.execute_input":"2026-09-11T19:17:27.120788Z","iopub.status.idle":"2026-09-11T19:17:27.524856Z","shell.execute_reply.started":"2026-09-11T19:17:27.120755Z","shell.execute_reply":"2026-09-11T19:17:27.523947Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"MODELS = [\n    (keras.applications.ResNet50V2, (224, 224)),\n    (keras.applications.InceptionV3, (299, 299)),\n    (keras.applications.Xception, (299, 299)),\n]\n\ntrain_features, test_features = [], []\n\nfor model_fn, size in MODELS:\n    print(model_fn.__name__, size)\n    train_features.append(extract_features(model_fn, size, train_paths))\n    test_features.append(extract_features(model_fn, size, test_paths))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:17:27.526067Z","iopub.execute_input":"2026-09-11T19:17:27.526413Z","iopub.status.idle":"2026-09-11T19:29:50.000419Z","shell.execute_reply.started":"2026-09-11T19:17:27.526374Z","shell.execute_reply":"2026-09-11T19:29:49.999737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = np.concatenate(train_features, axis=1)\nX_test = np.concatenate(test_features, axis=1)\n\ndel train_features, test_features\n\nprint(X.shape, X_test.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:29:50.002695Z","iopub.execute_input":"2026-09-11T19:29:50.003114Z","iopub.status.idle":"2026-09-11T19:29:50.324221Z","shell.execute_reply.started":"2026-09-11T19:29:50.003089Z","shell.execute_reply":"2026-09-11T19:29:50.323295Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y = keras.utils.to_categorical(df['label'].values, 2)\n\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42,\n                                                  stratify=df['label'])\n\nprint(X_train.shape, X_val.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:29:50.325349Z","iopub.execute_input":"2026-09-11T19:29:50.325747Z","iopub.status.idle":"2026-09-11T19:29:50.524986Z","shell.execute_reply.started":"2026-09-11T19:29:50.325680Z","shell.execute_reply":"2026-09-11T19:29:50.524112Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Модель","metadata":{}},{"cell_type":"code","source":"inputs = keras.Input(shape=(X.shape[1],))\nx = layers.Dropout(0.5)(inputs)\noutputs = layers.Dense(2, activation='softmax')(x)\n\nhead = keras.Model(inputs, outputs)\nhead.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\nhead.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:29:50.526102Z","iopub.execute_input":"2026-09-11T19:29:50.526441Z","iopub.status.idle":"2026-09-11T19:29:50.566035Z","shell.execute_reply.started":"2026-09-11T19:29:50.526413Z","shell.execute_reply":"2026-09-11T19:29:50.565443Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"earlystop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)\nlearning_rate_reduction = keras.callbacks.ReduceLROnPlateau(monitor='val_loss', patience=1,\n                                                           factor=0.5, min_lr=1e-6, verbose=1)\nmodelCheckpoint = keras.callbacks.ModelCheckpoint(CHECKPOINT, monitor='val_loss',\n                                                  save_best_only=True, verbose=1)\n\ncallbacks = [earlystop, learning_rate_reduction, modelCheckpoint]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:29:50.566901Z","iopub.execute_input":"2026-09-11T19:29:50.567174Z","iopub.status.idle":"2026-09-11T19:29:50.572378Z","shell.execute_reply.started":"2026-09-11T19:29:50.567154Z","shell.execute_reply":"2026-09-11T19:29:50.571603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = head.fit(X_train, y_train, validation_data=(X_val, y_val),\n                   epochs=30, batch_size=128, callbacks=callbacks)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:29:50.573565Z","iopub.execute_input":"2026-09-11T19:29:50.573829Z","iopub.status.idle":"2026-09-11T19:30:03.796393Z","shell.execute_reply.started":"2026-09-11T19:29:50.573809Z","shell.execute_reply":"2026-09-11T19:30:03.795790Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))\nax1.plot(history.history['loss'], color='b', label='Training loss')\nax1.plot(history.history['val_loss'], color='r', label='Validation loss')\nax1.legend()\nax2.plot(history.history['accuracy'], color='b', label='Training accuracy')\nax2.plot(history.history['val_accuracy'], color='r', label='Validation accuracy')\nax2.legend()\nplt.show()\n\nprint('лучшая val_accuracy:', max(history.history['val_accuracy']))\nprint('лучший val_loss:', min(history.history['val_loss']))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:30:03.797358Z","iopub.execute_input":"2026-09-11T19:30:03.797727Z","iopub.status.idle":"2026-09-11T19:30:04.030180Z","shell.execute_reply.started":"2026-09-11T19:30:03.797671Z","shell.execute_reply":"2026-09-11T19:30:04.029169Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Прогноз результата","metadata":{}},{"cell_type":"code","source":"predict = head.predict(X_test, verbose=1)\npredict[:3]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:30:04.031341Z","iopub.execute_input":"2026-09-11T19:30:04.031672Z","iopub.status.idle":"2026-09-11T19:30:05.814581Z","shell.execute_reply.started":"2026-09-11T19:30:04.031637Z","shell.execute_reply":"2026-09-11T19:30:05.813943Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df['label'] = predict[:, 1]\ntest_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:30:05.815777Z","iopub.execute_input":"2026-09-11T19:30:05.816112Z","iopub.status.idle":"2026-09-11T19:30:05.825350Z","shell.execute_reply.started":"2026-09-11T19:30:05.816088Z","shell.execute_reply":"2026-09-11T19:30:05.824458Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.hist(test_df['label'], bins=50)\nplt.xlabel('вероятность собаки')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:30:05.826331Z","iopub.execute_input":"2026-09-11T19:30:05.826690Z","iopub.status.idle":"2026-09-11T19:30:05.974556Z","shell.execute_reply.started":"2026-09-11T19:30:05.826666Z","shell.execute_reply":"2026-09-11T19:30:05.973875Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_test = test_df.head(9).reset_index(drop=True)\n\nplt.figure(figsize=(12, 12))\nfor index, row in sample_test.iterrows():\n    plt.subplot(3, 3, index + 1)\n    plt.imshow(plt.imread(f\"{TEST_DIR}/\" + row['filename']))\n    plt.axis('off')\n    plt.title(f\"{row['filename']}  p(dog)={row['label']:.3f}\")\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:30:05.975588Z","iopub.execute_input":"2026-09-11T19:30:05.975939Z","iopub.status.idle":"2026-09-11T19:30:07.037599Z","shell.execute_reply.started":"2026-09-11T19:30:05.975904Z","shell.execute_reply":"2026-09-11T19:30:07.036485Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submission","metadata":{}},{"cell_type":"code","source":"test_df['id'] = test_df['filename'].str.split('.').str[0].astype(int)\n\nsubmission_df = pd.read_csv(SAMPLE_SUBMISSION)[['id']].merge(test_df[['id', 'label']], on='id')\nsubmission_df['label'] = submission_df['label'].clip(0.005, 0.995)\n\nsubmission_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:30:07.038889Z","iopub.execute_input":"2026-09-11T19:30:07.039195Z","iopub.status.idle":"2026-09-11T19:30:07.099210Z","shell.execute_reply.started":"2026-09-11T19:30:07.039164Z","shell.execute_reply":"2026-09-11T19:30:07.098549Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df.to_csv(OUT_CSV, index=False)\n\nprint(len(submission_df))\nprint(submission_df['label'].describe())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-11T19:30:07.100242Z","iopub.execute_input":"2026-09-11T19:30:07.100584Z","iopub.status.idle":"2026-09-11T19:30:07.130613Z","shell.execute_reply.started":"2026-09-11T19:30:07.100560Z","shell.execute_reply":"2026-09-11T19:30:07.129775Z"}},"outputs":[],"execution_count":null}]}