{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score\nimport warnings\nwarnings.filterwarnings('ignore')\nimport matplotlib.pyplot as plt\n\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\nIMAGE_PATH = \"../input/osic-pulmonary-fibrosis-progression\"\nX_full = pd.read_csv(IMAGE_PATH + \"/train.csv\")\nX_test_full = pd.read_csv(IMAGE_PATH + \"/test.csv\")\n#remove duplicates in training and test sets\nX_concat = pd.concat([X_full,X_test_full])\nX_dropDups = X_concat.drop_duplicates(keep=False)\ntrain_df = X_dropDups\ntest_df = X_test_full\n\n#add pixel_array to dataset\nimport cv2\nimport math\nimport pydicom\n\ndef chunks(l,n):\n    # Credit: Ned Batchelder\n    # Link: http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks\n    \"\"\"Yield successive n-sized chunks from 1.\"\"\"\n    for i in range(0,len(l),n):\n        yield l[i:i+n]\n\n#Credit: Sentdex\n#Link: https://www.kaggle.com/sentdex/first-pass-through-data-w-3d-convnet\ndef mean(l):\n    return sum(l)/len(l)\n\nIMG_PX_SIZE = 50\nHM_SLICES = 20\ndata_dir = '../input/osic-pulmonary-fibrosis-progression/train/'\n\n\n#create new dataframe for storing patient IDs and CT pixel array\n\ndef create_df(data_dir,IMG_PX_SIZE,HM_SLICES):\n    patients = os.listdir(data_dir)\n    new_slices = []\n    CT_scans = []\n    patient_IDs = []\n    for patient in patients:\n        try:\n            path = data_dir + patient\n            slices = [pydicom.read_file(path + '/' + s) for s in os.listdir(path)]\n            slices.sort(key = lambda x: int(x.InstanceNumber))\n            new_slices = []\n\n            slices = [cv2.resize(np.array(each_slice.pixel_array),(IMG_PX_SIZE,IMG_PX_SIZE)) for each_slice in slices]\n\n            chunk_sizes = math.ceil(len(slices) / HM_SLICES)\n\n            for slice_chunk in chunks(slices, chunk_sizes):\n                slice_chunk = list(map(mean, zip(*slice_chunk)))\n                new_slices.append(slice_chunk)\n\n            if len(new_slices) < HM_SLICES:\n                while len(new_slices) < HM_SLICES:\n                    new_slices.append(new_slices[-1])\n\n            if len(new_slices) > HM_SLICES:\n                while len(new_slices) > HM_SLICES:\n                    new_val = list(map(mean, zip(*[new_slices[HM_SLICES-1],new_slices[HM_SLICES],])))\n                    del new_slices[HM_SLICES]\n                    new_slices[HM_SLICES-1] = new_val\n            CT_scans.append(np.array(new_slices))\n            patient_IDs.append(patient)\n\n        except Exception as e:\n            # some images cannot be decoded with pydicom\n            pass\n\n    pixel_df = pd.DataFrame({'Patient': patient_IDs,\n                            'PixelArray': CT_scans})\n    del patient_IDs, CT_scans\n\n    return pixel_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def getBaselineFVC(data_dir,df):\n    patients = os.listdir(data_dir)\n    baselineFVC = []\n    patient_IDs = []\n    for patient in patients:\n        idx = (df['Patient'] == patient).idxmax()\n        baselineFVC.append(df.iloc[idx]['FVC'])\n        patient_IDs.append(patient)\n    baselineFVC_df = pd.DataFrame({'Patient': patient_IDs,\n                                 'BaselineFVC': baselineFVC})\n    del patient_IDs, baselineFVC\n    return baselineFVC_df\n\nbaselineFVC_train_df = getBaselineFVC('../input/osic-pulmonary-fibrosis-progression/train/',train_df)\n\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n#X_trainingData = pd.merge(X_trainingData, pixel_df, on='Patient')\ntrain_df_pix = create_df(data_dir,IMG_PX_SIZE,HM_SLICES)\ndata_dir_test = '../input/osic-pulmonary-fibrosis-progression/test/'\ntest_df_pix = create_df(data_dir_test,IMG_PX_SIZE,HM_SLICES)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df=pd.merge(train_df,train_df_pix,on='Patient')\ntrain_df=pd.merge(train_df,baselineFVC_train_df,on='Patient')\ntest_df=pd.merge(test_df,test_df_pix,on='Patient')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#source: https://www.pyimagesearch.com/2019/02/04/keras-multiple-inputs-and-mixed-data/\nfrom sklearn.preprocessing import OneHotEncoder\nfrom sklearn.preprocessing import MinMaxScaler\nfrom scipy import sparse\n\ndef process_patient_attributes(df,train,test):\n    continuous = ['Weeks','FVC','BaselineFVC','Age']\n    # performin min-max scaling each continuous feature column to\n    # the range [0, 1]\n    cs = MinMaxScaler()\n    trainContinuous = cs.fit_transform(train[continuous])\n    testContinuous = cs.transform(test[continuous])\n    # one-hot encode the zip code categorical data (by definition of\n    # one-hot encoding, all output features are now in the range [0, 1])\n    categorical = ['Sex','SmokingStatus']\n    binarizer = OneHotEncoder().fit(df[categorical])\n    trainCategorical = binarizer.transform(train[categorical])\n    testCategorical = binarizer.transform(test[categorical])\n    # construct our training and testing data points by concatenating\n    # the categorical features with the continuous features\n    trainX = sparse.hstack([trainCategorical,trainContinuous]).toarray()\n    testX = sparse.hstack([testCategorical,testContinuous]).toarray()\n    # return the concatenated training and testing data\n    return(trainX,testX)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# import the necessary packages\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import BatchNormalization\nfrom tensorflow.keras.layers import Conv3D\nfrom tensorflow.keras.layers import MaxPooling3D\nfrom tensorflow.keras.layers import Activation\nfrom tensorflow.keras.layers import Dropout\nfrom tensorflow.keras.layers import Dense\nfrom tensorflow.keras.layers import Flatten\nfrom tensorflow.keras.layers import Input\nfrom tensorflow.keras.models import Model\n\ndef create_mlp(dim, regress=False):\n    # define our MLP network\n    model = Sequential()\n    model.add(Dense(8, input_dim=dim, activation=\"relu\"))\n    model.add(Dense(4, activation=\"relu\"))\n    # check to see if the regression node should be added\n    if regress:\n        model.add(Dense(1, activation=\"linear\"))\n    # return our model\n    return model\n\ndef create_cnn(x):\n    ''' # initialize the input shape and channel dimension, assuming\n    # TensorFlow/channels-last ordering\n    inputShape = (height, width, depth,1)\n    chanDim = -1\n    # define the model input\n    inputs = Input(shape=inputShape)\n    # loop over the number of filters\n    for (i, f) in enumerate(filters):\n        # if this is the first CONV layer then set the input\n        # appropriately\n        if i == 0:\n            x = inputs\n        # CONV => RELU => BN => POOL\n        x = Conv3D(f, (3, 3, 1), padding=\"same\")(x)\n        x = Activation(\"relu\")(x)\n        x = BatchNormalization(axis=chanDim)(x)\n        x = MaxPooling3D(pool_size=(2, 2, 1))(x)\n\n        # flatten the volume, then FC => RELU => BN => DROPOUT\n        x = Flatten()(x)\n        x = Dense(16)(x)\n        x = Activation(\"relu\")(x)\n        x = BatchNormalization(axis=chanDim)(x)\n        x = Dropout(0.5)(x)\n        # apply another FC layer, this one to match the number of nodes\n        # coming out of the MLP\n        x = Dense(4)(x)\n        x = Activation(\"relu\")(x)\n        # check to see if the regression node should be added\n        if regress:\n            x = Dense(1, activation=\"linear\")(x)\n        # construct the CNN\n        model = Model(inputs, x)\n        # return the CNN\n        return model\n        '''\n    n_classes = 2\n    batch_size = 10\n        #                # 5 x 5 x 5 patches, 1 channel, 32 features to compute.\n    weights = {'W_conv1':tf.Variable(tf.random.normal([3,3,3,1,32])),\n               #       5 x 5 x 5 patches, 32 channels, 64 features to compute.\n               'W_conv2':tf.Variable(tf.random.normal([3,3,3,32,64])),\n               #                                  64 features\n               'W_fc':tf.Variable(tf.random.normal([54080,1024])),\n               'out':tf.Variable(tf.random.normal([1024, n_classes]))}\n\n    biases = {'b_conv1':tf.Variable(tf.random.normal([32])),\n               'b_conv2':tf.Variable(tf.random.normal([64])),\n               'b_fc':tf.Variable(tf.random.normal([1024])),\n               'out':tf.Variable(tf.random.normal([n_classes]))}\n\n    #                            image X      image Y        image Z\n    x = tf.reshape(x, shape=[-1, IMG_PX_SIZE, IMG_PX_SIZE, HM_SLICES, 1])\n\n    conv1 = tf.nn.relu(Conv3d(x, weights['W_conv1']) + biases['b_conv1'])\n    conv1 = maxpool3d(conv1)\n\n\n    conv2 = tf.nn.relu(Conv3d(conv1, weights['W_conv2']) + biases['b_conv2'])\n    conv2 = maxpool3d(conv2)\n\n    fc = tf.reshape(conv2,[-1, 54080])\n    fc = tf.nn.relu(tf.matmul(fc, weights['W_fc'])+biases['b_fc'])\n    fc = tf.nn.dropout(fc, keep_rate)\n\n    output = tf.matmul(fc, weights['out'])+biases['out']\n\n    return output","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom tensorflow.keras.layers import Dense\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.layers import concatenate\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"split = train_test_split(train_df[['Weeks','FVC','BaselineFVC','Age','Sex','SmokingStatus']],train_df['PixelArray'],test_size = 0.25,random_state = 42)\ntrainAttrX, validAttrX, trainImagesX, validImagesX = split","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"maxFVC = trainAttrX['FVC'].max()\ntrainY = np.asarray(trainAttrX['FVC']/maxFVC)\nvalidY = np.asarray(validAttrX['FVC']/maxFVC)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"trainImagesX = trainImagesX.to_numpy()\nvalidImagesX = validImagesX.to_numpy()\n(trainAttrX,validAttrX) = process_patient_attributes(train_df,trainAttrX,validAttrX)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in range(len(trainImagesX)):\n    trainImagesX[i] = np.asarray(trainImagesX[i])\n    try:\n        trainImagesX[i] = tf.convert_to_tensor(trainImagesX[i])\n    except Exception as e:\n        print(str(e))\nfor i in range(len(validImagesX)):\n    validImagesX[i] = np.asarray(validImagesX[i])\n    try:\n        validImagesX[i] = tf.convert_to_tensor(validImagesX[i])\n    except Exception as e:\n        print(str(e))\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# create the MLP and CNN models\nmlp = create_mlp(trainAttrX.shape[1], regress=False)\ncnn = create_cnn(trainImagesX)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# create the input to our final set of layers as the *output* of both\n# the MLP and CNN\ncombinedInput = concatenate([mlp.output, cnn.output])\n# our final FC layer head will have two dense layers, the final one\n# being our regression head\nx = Dense(4, activation=\"relu\")(combinedInput)\nx = Dense(1, activation=\"linear\")(x)\n# our final model will accept categorical/numerical data on the MLP\n# input and images on the CNN input, outputting a single value (the\n# predicted price of the house)\nmodel = Model(inputs=[mlp.input, cnn.input], outputs=x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# compile the model using mean absolute percentage error as our loss,\n# implying that we seek to minimize the absolute percentage difference\n# between our price *predictions* and the *actual prices*\nopt = Adam(lr=1e-3, decay=1e-3 / 200)\nmodel.compile(loss=\"mean_absolute_percentage_error\", optimizer=opt)\n# train the model\nprint(\"[INFO] training model...\")\n\nmodel.fit(\n    x=[trainAttrX, trainImagesX], y=trainY,\n    validation_data=([validAttrX, validImagesX], validY),\n    epochs=200, batch_size=8)\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# make predictions on the testing data\nprint(\"[INFO] predicting FVC...\")\npreds = model.predict([validAttrX, validImagesX])","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}