{"metadata":{"kaggle":{"accelerator":"tpu1vmV38","dataSources":[{"sourceId":39272,"databundleVersionId":4629629,"sourceType":"competition"},{"sourceId":4913500,"sourceType":"datasetVersion","datasetId":2849545}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.14"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Breast Cancer Detection\n","metadata":{}},{"cell_type":"markdown","source":"*The following is a solution to the RSNA Screeening Mammography Breast Cancer Detection Dataset*","metadata":{}},{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nimport tensorflow_io as tfio\nimport tensorflow_datasets as tfds\nimport keras\nimport os\nimport time\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import roc_curve\nfrom sklearn.model_selection import train_test_split\n\nimport cv2\n\nimport os\nimport logging\nimport random\nimport pickle\nimport math\n\nfrom multiprocessing import cpu_count\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Detect hardware, return appropriate distribution strategy\ntry:\n    TPU = tf.distribute.cluster_resolver.TPUClusterResolver()  # TPU detection. No parameters necessary if TPU_NAME environment variable is set. On Kaggle this is always the case.\n    print('Running on TPU ', TPU.master())\nexcept ValueError:\n    print('Running on CPU or GPU')\n    TPU = None\n\nif TPU:\n    IS_TPU = True\n    tf.config.experimental_connect_to_cluster(TPU)\n    tf.tpu.experimental.initialize_tpu_system(TPU)\n    STRATEGY = tf.distribute.experimental.TPUStrategy(TPU)\nelse:\n    IS_TPU = False\n    STRATEGY = tf.distribute.get_strategy() # default distribution strategy in Tensorflow. Works on CPU and single GPU.\n\n#clear_output()\nN_REPLICAS = STRATEGY.num_replicas_in_sync\nprint(f'N_REPLICAS: {N_REPLICAS}, IS_TPU: {IS_TPU}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Image dimensions\nIMG_HEIGHT = 1456\nIMG_WIDTH = 728\nN_CHANNELS = 1\nINPUT_SHAPE = (IMG_HEIGHT, IMG_WIDTH, 1)\nN_SAMPLES_TFRECORDS = 548\n\n# Peak Learning Rate\nEPOCHS = 15\n\n# Batch size\nBATCH_SIZE = 8 * N_REPLICAS\n\n# Augmentation\nBRIGHTNESS = 0.10\nCONTRAST = (0.90, 1.10)\nJPEG_QUALITY = (90, 100)\nCROP_RATIO = (0.80, 1.00)\n\nIS_INTERACTIVE = os.environ['KAGGLE_KERNEL_RUN_TYPE'] == 'Interactive'\nVERBOSE = 1 if IS_INTERACTIVE else 2\n\n# Tensorflow AUTO flag\nAUTO = tf.data.experimental.AUTOTUNE\n\nprint(f'BATCH_SIZE: {BATCH_SIZE}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"MIXED_PRECISION = False\nDEVICE = 'TPU'\n\nif MIXED_PRECISION:\n    if 'TPU' in DEVICE:\n        policy_type = 'mixed_bfloat16'\n    else:\n        policy_type = 'mixed_float16'\nelse:\n    policy_type = 'float32'\npolicy = tf.keras.mixed_precision.Policy(policy_type)\ntf.keras.mixed_precision.set_global_policy(policy)\nprint(f'Computation dtype: {tf.keras.mixed_precision.global_policy().compute_dtype}')\nprint(f'Variable dtype: {tf.keras.mixed_precision.global_policy().variable_dtype}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preporcessing","metadata":{}},{"cell_type":"markdown","source":"Preprocessing taken from [Paul Bacher](https://www.kaggle.com/code/paulbacher/training-rsna-bcd-keras-model) Rsna training notebook","metadata":{}},{"cell_type":"code","source":"# TFRecord file paths\ntfrecords = sorted(tf.io.gfile.glob('/kaggle/input/dataset-rsna-bcd-1456x728-final-tfrecords/*.tfrecords'))\nprint(f'Found {len(tfrecords)} TFRecords')\n\n# Train Test Split\ntrain_tfrecords, valid_tfrecords = train_test_split(\n    tfrecords,\n    train_size=0.80,\n    random_state=42,\n    shuffle=True)\nprint(f'# Train TFRecords: {len(train_tfrecords)}, # Valid TFRecords: {len(valid_tfrecords)}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_dataset(tfrecords, batch_size=32, valid=False):\n    ignore_order = tf.data.Options()\n    ignore_order.experimental_deterministic = False\n    # Create dataset path/label\n    dataset = tf.data.TFRecordDataset(tfrecords,num_parallel_reads=AUTO,compression_type='GZIP')\n\n    # Decode mapping\n    dataset = dataset.map(decoder, num_parallel_calls=AUTO)\n    # Val/Debug cases\n    if not valid:\n        dataset = dataset.filter(undersample_majority)\n        \n        dataset = dataset.map(augmenter, num_parallel_calls=AUTO)\n        dataset = dataset.with_options(ignore_order)\n        \n        dataset = dataset.shuffle(1024)\n        dataset = dataset.repeat()\n    if valid:\n        dataset = dataset.map(expand_label, num_parallel_calls=AUTO)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\ndef decoder(record_bytes):\n    features = tf.io.parse_single_example(record_bytes, {\n        'image': tf.io.FixedLenFeature([], tf.string),\n        'target': tf.io.FixedLenFeature([], tf.int64),\n        'patient_id': tf.io.FixedLenFeature([], tf.int64)})\n    img = tf.io.decode_png(features['image'], channels=N_CHANNELS)\n    img = tf.reshape(img, [IMG_HEIGHT, IMG_WIDTH, N_CHANNELS])\n    label = features['target']\n    return img, label\n\ndef undersample_majority(img, label):\n    return label == 1 or tf.random.uniform([]) > 2/3\n\ndef tf_rand_int(minval, maxval, dtype=tf.int64):\n    minval = tf.cast(minval, dtype)\n    maxval = tf.cast(maxval, dtype)\n    return tf.random.uniform(shape=(), minval=minval, maxval=maxval, dtype=dtype)\n\ndef augmenter(img, label):\n    # Pixels\n    img = tf.image.random_brightness(img, BRIGHTNESS)\n    img = tf.image.random_contrast(img, *CONTRAST)\n    img = tf.image.random_jpeg_quality(img, *JPEG_QUALITY)\n    # Crop\n    ratio = tf.random.uniform([], *CROP_RATIO)\n    img_height_crop = tf.cast(ratio * IMG_HEIGHT, tf.int32)\n    img_width_crop = tf.cast(ratio * IMG_WIDTH, tf.int32)\n    img_height_offset = tf_rand_int(0, IMG_HEIGHT - img_height_crop)\n    img_width_offset = 0\n    img = tf.slice(img, [img_height_offset, img_width_offset, 0],\n                   [img_height_crop, img_width_crop, N_CHANNELS])\n    img = tf.image.resize(img, [IMG_HEIGHT, IMG_WIDTH])\n    # Clip\n    img = tf.clip_by_value(img, 0, 255)\n    img = tf.cast(img, tf.uint8)\n    img = img / 255\n    label = tf.expand_dims(label, axis=-1)\n    return img, label\n\ndef expand_label(img,label):\n    img = img / 255\n    label = tf.expand_dims(label, axis=-1)\n    return img, label\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get Train/Validation datasets\ntrain_dataset = create_dataset(train_tfrecords, valid=False)\nvalid_dataset = create_dataset(valid_tfrecords, valid=True)\n\nTRAIN_STEPS_PER_EPOCH = (len(train_tfrecords) * N_SAMPLES_TFRECORDS) // BATCH_SIZE\nVAL_STEPS_PER_EPOCH = (len(valid_tfrecords) * N_SAMPLES_TFRECORDS) // BATCH_SIZE\nprint(f'TRAIN_STEPS_PER_EPOCH: {TRAIN_STEPS_PER_EPOCH}, VAL_STEPS_PER_EPOCH: {VAL_STEPS_PER_EPOCH}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Sanity checking\ndef check_dataset(dataset):\n    image, label = next(iter(dataset))\n    image = image.numpy()\n    #clear_output()\n    print(f\"X_batch shape: {image.shape}, y_batch shape: {label.shape}\")\n    print(f\"X_batch dtype: {image.dtype}, y_batch dtype: {label.dtype}\")\n    print(f\"X_batch min: {image.min():.2f}, max: {image.max():.2f}\")\n\ncheck_dataset(train_dataset)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Creating the Model","metadata":{}},{"cell_type":"code","source":"@keras.saving.register_keras_serializable()\nclass pFBeta(tf.keras.Metric):\n    def __init__(self, beta=1, name='pF1', **kwargs):\n        super().__init__(name=name, **kwargs)\n        self.beta = beta\n        self.epsilon = 1e-10\n        self.pos = self.add_weight(name='pos', initializer='zeros')\n        self.ctp = self.add_weight(name='ctp', initializer='zeros')\n        self.cfp = self.add_weight(name='cfp', initializer='zeros')\n\n    def update_state(self, y_true, y_pred, sample_weight=None):\n        y_true = tf.keras.ops.cast(y_true, tf.float32)\n        y_pred = tf.keras.ops.clip(y_pred, 0, 1)\n        pos = tf.keras.ops.cast(tf.keras.ops.sum(y_true), tf.float32)\n        ctp = tf.keras.ops.cast(tf.keras.ops.sum(y_pred[y_true == 1]), tf.float32)\n        cfp = tf.keras.ops.cast(tf.keras.ops.sum(y_pred[y_true == 0]), tf.float32)\n        self.pos.assign_add(pos)\n        self.ctp.assign_add(ctp)\n        self.cfp.assign_add(cfp)\n\n    def result(self):\n        beta2 = self.beta * self.beta\n        prec = self.ctp / (self.ctp + self.cfp + self.epsilon)\n        reca = self.ctp / (self.pos + self.epsilon)\n        return (1 + beta2) * prec * reca / ((beta2 * prec + reca) + self.epsilon) \n    \n    def reset_state(self):\n        self.pos.assign(0.)\n        self.ctp.assign(0.)\n        self.cfp.assign(0.)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def model():\n        \n    # Architecture\n    inputs = keras.Input((1456, 728, 1), name='inputs')\n\n    x = keras.ops.cast(inputs, tf.float32)\n    x = keras.ops.repeat(x, repeats=3, axis=3)\n\n    efficientnet = keras.applications.EfficientNetB4(\n        weights='imagenet',\n        input_shape=(1456, 728, 3),\n        include_top=False,\n        pooling=\"avg\",\n    )(x)\n\n\n    x = keras.layers.Dropout(0.30)(efficientnet)\n    outputs_1 = keras.layers.Dense(1, activation='sigmoid')(x)\n\n    model = keras.Model(inputs, outputs_1, name=\"model\")\n\n    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), \n                      loss=tf.keras.losses.BinaryCrossentropy(), \n                      metrics=[pFBeta(),\n                       tf.metrics.AUC(name='AUC'),\n                       tf.metrics.F1Score(threshold=0.50, name='F1'),\n                       tf.metrics.Precision(name='Prec'),\n                       tf.metrics.Recall(name='Reca'),\n                       tf.metrics.BinaryAccuracy(name='BinAcc')])\n    \n\n    # Print the model summary\n    print(model.summary())\n    return model\n\n    \ntf.keras.backend.clear_session()\ntf.config.optimizer.set_jit(\"autoclustering\")\n\nmodel = model()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = model.fit(\n    x=train_dataset,\n    epochs=25,\n    validation_data=valid_dataset,\n    class_weight={0: 1, 1: 5},\n    steps_per_epoch=TRAIN_STEPS_PER_EPOCH)\nwith STRATEGY.scope():\n    model.save(\"/kaggle/working/model_v8.keras\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}