{"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":"none","dataSources":[{"sourceId":20270,"databundleVersionId":1222630,"sourceType":"competition"},{"sourceId":1322517,"sourceType":"datasetVersion","datasetId":689329},{"sourceId":12946066,"sourceType":"datasetVersion","datasetId":8192604}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os, re, math, glob\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()\n    strategy = tf.distribute.TPUStrategy(tpu)\n    REPLICAS = strategy.num_replicas_in_sync\n    print(f\"[INFO] Using TPU with {REPLICAS} replicas.\")\nexcept Exception as e:\n    print(f\"[INFO] TPU init failed ({e}). Falling back to CPU/GPU.\")\n    strategy = tf.distribute.get_strategy()\n    REPLICAS = strategy.num_replicas_in_sync\n    print(f\"[INFO] Using Default Strategy with {REPLICAS} replicas.\")\n\nAUTO = tf.data.AUTOTUNE","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from kaggle_datasets import KaggleDatasets\nBASEPATH = \"/kaggle/input/siim-isic-melanoma-classification\"\ndf_train = pd.read_csv(os.path.join(BASEPATH, 'train.csv'))\ndf_test  = pd.read_csv(os.path.join(BASEPATH, 'test.csv'))\ndf_sub   = pd.read_csv(os.path.join(BASEPATH, 'sample_submission.csv'))\nprint(f\"[INFO] train={df_train.shape}, test={df_test.shape}, sub={df_sub.shape}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tfrec_shape = 384\ncomp_data = \"2020\"\n\ncrop_size = {256: 250, 384: 370, 512: 500, 768: 750}\nif comp_data == \"2020\":\n    net_size = {256: 248, 384: 370, 512: 500, 768: 750}\nelse:\n    net_size = {256: 250, 384: 370, 512: 500, 768: 750}\n\nif comp_data == \"2020\":\n    hair_augm = {256: False, 384: False, 512: False, 768: False}\nelse:\n    hair_augm = {256: True, 384: True, 512: True, 768: False}\n\nif comp_data == \"2020\":\n    epochs_num = {256: 13, 384: 3, 512: 15, 768: 15}\nelse:\n    epochs_num = {256: 25, 384: 25, 512: 12, 768: 10}\n\nCFG = dict(\n    batch_size = 256,\n    read_size = tfrec_shape,\n    crop_size = crop_size[tfrec_shape],\n    net_size  = net_size[tfrec_shape],\n    LR_START = 0.000003,\n    LR_MAX   = 0.000020,\n    LR_MIN   = 0.000001,\n    LR_RAMPUP_EPOCHS  = 5,\n    LR_SUSTAIN_EPOCHS = 0,\n    LR_EXP_DECAY = 0.8,\n    epochs = epochs_num[tfrec_shape],\n    rot = 180.0, shr = 1.5, hzoom = 6.0, wzoom = 6.0, hshift = 6.0, wshift = 6.0,\n    DROP_FREQ = 0, DROP_CT = 0, DROP_SIZE = 0,\n    hair_augm = hair_augm[tfrec_shape],\n    optimizer = 'adam',\n    label_smooth_fac = 0.05,\n    tta_steps = 25,\n)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path(f'melanoma-{tfrec_shape}x{tfrec_shape}')\nfiles_train = np.sort(np.array(tf.io.gfile.glob(GCS_PATH + '/train*.tfrec')))\nfiles_test  = np.sort(np.array(tf.io.gfile.glob(GCS_PATH + '/test*.tfrec')))\nprint(f\"[INFO] TFRecords → train: {len(files_train)} | test: {len(files_test)}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(f).group(1)) for f in filenames]\n    return np.sum(n)\n\ndef read_unlabeled_tfrecord(example, return_image_name):\n    tfrec_format = {\n        'image'      : tf.io.FixedLenFeature([], tf.string),\n        'image_name' : tf.io.FixedLenFeature([], tf.string),\n    }\n    e = tf.io.parse_single_example(example, tfrec_format)\n    return e['image'], e['image_name'] if return_image_name else 0\n\ndef get_mat(rotation, shear, height_zoom, width_zoom, height_shift, width_shift):\n    rotation = math.pi * rotation / 180.\n    shear = math.pi * shear / 180.\n    def get_3x3_mat(lst):\n        return tf.reshape(tf.concat([lst],axis=0), [3,3])\n    c1 = tf.math.cos(rotation); s1 = tf.math.sin(rotation)\n    one = tf.constant([1],dtype='float32'); zero = tf.constant([0],dtype='float32')\n    rotation_matrix = get_3x3_mat([c1, s1, zero, -s1, c1, zero, zero, zero, one])\n    c2 = tf.math.cos(shear); s2 = tf.math.sin(shear)\n    shear_matrix = get_3x3_mat([one, s2, zero, zero, c2, zero, zero, zero, one])\n    zoom_matrix  = get_3x3_mat([one/height_zoom, zero, zero, zero, one/width_zoom, zero, zero, zero, one])\n    shift_matrix = get_3x3_mat([one, zero, height_shift, zero, one, width_shift, zero, zero, one])\n    return tf.keras.backend.dot(tf.keras.backend.dot(rotation_matrix, shear_matrix),\n                                tf.keras.backend.dot(zoom_matrix, shift_matrix))\n\ndef transform(image, cfg):\n    DIM = cfg[\"read_size\"]; XDIM = DIM%2\n    rot = cfg['rot'] * tf.random.normal([1], dtype='float32')\n    shr = cfg['shr'] * tf.random.normal([1], dtype='float32')\n    h_zoom = 1.0 + tf.random.normal([1], dtype='float32') / cfg['hzoom']\n    w_zoom = 1.0 + tf.random.normal([1], dtype='float32') / cfg['wzoom']\n    h_shift = cfg['hshift'] * tf.random.normal([1], dtype='float32')\n    w_shift = cfg['wshift'] * tf.random.normal([1], dtype='float32')\n    m = get_mat(rot,shr,h_zoom,w_zoom,h_shift,w_shift)\n    x = tf.repeat(tf.range(DIM//2, -DIM//2,-1), DIM)\n    y = tf.tile(tf.range(-DIM//2, DIM//2), [DIM])\n    z = tf.ones([DIM*DIM], dtype='int32')\n    idx = tf.stack([x,y,z])\n    idx2 = tf.keras.backend.dot(m, tf.cast(idx, dtype='float32'))\n    idx2 = tf.cast(idx2, dtype='int32')\n    idx2 = tf.clip_by_value(idx2, -DIM//2+XDIM+1, DIM//2)\n    idx3 = tf.stack([DIM//2-idx2[0,], DIM//2-1+idx2[1,]])\n    d = tf.gather_nd(image, tf.transpose(idx3))\n    return tf.reshape(d,[DIM, DIM,3])\n\ndef prepare_image(img, cfg=None, augment=True):\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.image.resize(img, [cfg['read_size'], cfg['read_size']])\n    img = tf.cast(img, tf.float32) / 255.0\n    if augment:\n        img = transform(img, cfg)\n        img = tf.image.random_crop(img, [cfg['crop_size'], cfg['crop_size'], 3])\n        img = tf.image.random_flip_left_right(img)\n        img = tf.image.random_hue(img, 0.01)\n        img = tf.image.random_saturation(img, 0.7, 1.3)\n        img = tf.image.random_contrast(img, 0.8, 1.2)\n        img = tf.image.random_brightness(img, 0.1)\n    else:\n        img = tf.image.central_crop(img, cfg['crop_size'] / cfg['read_size'])\n    img = tf.image.resize(img, [cfg['net_size'], cfg['net_size']])\n    img = tf.reshape(img, [cfg['net_size'], cfg['net_size'], 3])\n    return img\n\ndef get_dataset(files, cfg, augment=False, shuffle=False, repeat=False, labeled=False, return_image_names=True):\n    ds = tf.data.TFRecordDataset(files, num_parallel_reads=AUTO).cache()\n    if repeat: ds = ds.repeat()\n    if shuffle:\n        ds = ds.shuffle(1024*8)\n        opt = tf.data.Options(); opt.experimental_deterministic = False; ds = ds.with_options(opt)\n    ds = ds.map(lambda e: read_unlabeled_tfrecord(e, return_image_names), num_parallel_calls=AUTO)\n    ds = ds.map(lambda img, imgname: (prepare_image(img, cfg=cfg, augment=augment), imgname), num_parallel_calls=AUTO)\n    ds = ds.batch(cfg['batch_size'] * max(1, REPLICAS)).prefetch(AUTO)\n    return ds\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model (DenseNet201) for inference","metadata":{}},{"cell_type":"code","source":"MODEL_ZOO = {\n    'DenseNet201': (\n        tf.keras.applications.DenseNet201,\n        tf.keras.applications.densenet.preprocess_input,\n        224\n    ),\n}\n\n# def get_model(cfg, model_name='DenseNet201', weights=None, dropout_rate=None):\n#     constructor, preprocess_fn, default_size = MODEL_ZOO[model_name]\n#     input_size = int(cfg.get('net_size', default_size))\n#     if dropout_rate is None:\n#         dropout_rate = float(cfg.get('dropout', 0.0))\n#     inputs = tf.keras.Input(shape=(input_size, input_size, 3), name='imgIn')\n#     x = tf.keras.layers.Lambda(preprocess_fn, name='preprocess')(inputs)\n#     base = constructor(include_top=False, weights=weights, input_shape=(input_size, input_size, 3), pooling='avg')\n#     x = base(x)\n#     if dropout_rate and dropout_rate > 0:\n#         x = tf.keras.layers.Dropout(dropout_rate)(x)\n#     outputs = tf.keras.layers.Dense(1, activation='sigmoid', name='pred')(x)\n#     model = tf.keras.Model(inputs, outputs, name=f'{model_name}_melanoma_cls')\n#     return model\n\ndef get_model(cfg, model):\n    \n    model_input = tf.keras.Input(shape=(cfg['net_size'], cfg['net_size'], 3), name='imgIn')\n    dummy = tf.keras.layers.Lambda(lambda x:x)(model_input)\n    outputs = []\n\n    constructor = getattr(efn, model)\n    x = constructor(include_top=False, weights=model_weights, input_shape=(cfg['net_size'], cfg['net_size'], 3), pooling='avg')(dummy)\n    x = tf.keras.layers.Dense(1, activation='sigmoid')(x)\n    outputs.append(x)\n    \n    model = tf.keras.Model(model_input, outputs, name='aNetwork')\n    model.summary()\n    \n    return model\n    \ndef build_for_infer(cfg):\n    with strategy.scope():\n        model = get_model(cfg, 'EfficientNetB2')\n        model.compile(optimizer='adam', loss='binary_crossentropy', metrics=[tf.keras.metrics.AUC(name='auc')])\n    return model\n\n# ==============================\n# Checkpoint auto-discovery\n# ==============================\ndef find_checkpoint():\n    import os\n    # 1) env overrides\n    override_file = os.environ.get(\"CHECKPOINT_FILE\")\n    override_dir  = os.environ.get(\"CHECKPOINT_DIR\")\n    if override_file and tf.io.gfile.exists(override_file):\n        return override_file, \"weights_file\"\n    if override_dir and tf.io.gfile.exists(override_dir):\n        h5s = tf.io.gfile.glob(os.path.join(override_dir, \"*.h5\")) + tf.io.gfile.glob(os.path.join(override_dir, \"*.keras\"))\n        if h5s: return h5s[0], \"weights_file\"\n        if tf.io.gfile.exists(os.path.join(override_dir, \"saved_model.pb\")):\n            return override_dir, \"saved_model\"\n        ckpt_idx = tf.io.gfile.glob(os.path.join(override_dir, \"*.ckpt.index\"))\n        if ckpt_idx: return ckpt_idx[0][:-6], \"tf_checkpoint\"\n    # 2) auto-scan /kaggle/input\n    for root, dirs, files in tf.io.gfile.walk(\"/kaggle/input\"):\n        if \"siim-isic-melanoma-classification\" in root:\n            continue\n        # weights files\n        h5s = [f for f in files if f.endswith(\".h5\") or f.endswith(\".keras\")]\n        if h5s:\n            return os.path.join(root, h5s[0]), \"weights_file\"\n        # saved model\n        if \"saved_model.pb\" in files:\n            return root, \"saved_model\"\n        # tf checkpoint\n        ckpt_idx = [f for f in files if f.endswith(\".ckpt.index\")]\n        if ckpt_idx:\n            ckpt_prefix = ckpt_idx[0][:-6]\n            return os.path.join(root, ckpt_prefix), \"tf_checkpoint\"\n    return None, None","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Inference (TTA) and submission","metadata":{}},{"cell_type":"code","source":"import re","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def infer_and_submit(cfg, out_csv=\"submission.csv\"):\n    import math\n    model = build_for_infer(cfg)\n    ckpt_path, kind = find_checkpoint()\n    assert ckpt_path is not None, \"No checkpoint found under /kaggle/input. Set CHECKPOINT_FILE or CHECKPOINT_DIR.\"\n    print(f\"[INFO] Loading checkpoint '{ckpt_path}' (kind={kind})\")\n    if kind == \"weights_file\":\n        model.load_weights(ckpt_path)\n    elif kind == \"saved_model\":\n        with strategy.scope():\n            model = tf.keras.models.load_model(ckpt_path, compile=False)\n    elif kind == \"tf_checkpoint\":\n        model.load_weights(ckpt_path)\n    else:\n        raise ValueError(f\"Unknown checkpoint kind: {kind}\")\n\n    # --- exact steps so we can reshape robustly ---\n    cnt_test = count_data_items(files_test)\n    bs = cfg['batch_size'] * max(1, REPLICAS)\n    n_needed = cnt_test * cfg['tta_steps']\n    steps = math.ceil(n_needed / bs)\n\n    ds_testAug = get_dataset(files_test, cfg, augment=True, repeat=True,\n                             labeled=False, return_image_names=False)\n    print(f\"[INFO] Predicting with TTA (steps={steps}, tta_steps={cfg['tta_steps']}, batch_size={bs})\")\n    raw = model.predict(ds_testAug, verbose=1, steps=steps)\n\n    # ---- Robust TTA aggregation ----\n    preds = np.asarray(raw).reshape(-1)                 # (steps*bs,)\n    preds = preds[:n_needed]                            # trim to exactly N*TTA\n    preds = preds.reshape(cfg['tta_steps'], cnt_test)   # (TTA, N)\n    probs_mean = preds.mean(axis=0)                     # (N,)\n\n    # --- names from non-augmented pipeline ---\n    ds_names = get_dataset(files_test, cfg, augment=False, repeat=False,\n                           labeled=False, return_image_names=True)\n    image_names = np.array([img_name.numpy().decode(\"utf-8\")\n                            for img, img_name in iter(ds_names.unbatch())])\n\n    pred = pd.DataFrame(dict(image_name=image_names, target=probs_mean))\n    pred = pred.sort_values(\"image_name\").reset_index(drop=True)\n\n    sub = pd.read_csv(os.path.join(BASEPATH, 'sample_submission.csv'))\n    out = sub[['image_name']].merge(pred, on='image_name', how='left')\n    if out['target'].isna().any():\n        out['target'] = out['target'].fillna(0.5)\n\n    out.to_csv(out_csv, index=False)\n    print(f\"[INFO] Wrote {out_csv} with {len(out)} rows.\")\n    return out_csv\n\n\nif __name__ == \"__main__\":\n    csv_path = infer_and_submit(CFG, out_csv=\"submission.csv\")\n    print(f\"[DONE] Submission saved to: {csv_path}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}