{"cells":[{"metadata":{"_uuid":"3b49c074-f48f-4ba6-9536-54aff216ffa3","_cell_guid":"24fcf49b-cb66-441a-8af1-5f7e7fcfca97","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\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\nimport torch","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"CFG = {\n    'image_target_cols': [\n        'pe_present_on_image', # only image level\n    ],\n    \n    'exam_target_cols': [\n        'negative_exam_for_pe', # exam level\n        #'qa_motion',\n        #'qa_contrast',\n        #'flow_artifact',\n        'rv_lv_ratio_gte_1', # exam level\n        'rv_lv_ratio_lt_1', # exam level\n        'leftsided_pe', # exam level\n        'chronic_pe', # exam level\n        #'true_filling_defect_not_pe',\n        'rightsided_pe', # exam level\n        'acute_and_chronic_pe', # exam level\n        'central_pe', # exam level\n        'indeterminate' # exam level\n    ], \n    \n    'image_weight': 0.07361963,\n    'exam_weights': [0.0736196319, 0.2346625767, 0.0782208589, 0.06257668712, 0.1042944785, 0.06257668712, 0.1042944785, 0.1877300613, 0.09202453988],\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(\"../input/rsna-str-pulmonary-embolism-detection/train.csv\")\ntrain.head(3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Simulate model predicting by taking average for each labels"},{"metadata":{"trusted":true},"cell_type":"code","source":"img_label_mean = train[CFG['image_target_cols']].mean(axis=0)\nexam_label_mean = train[CFG['exam_target_cols']].mean(axis=0)\nprint('===img label mean===\\n{} \\n\\n\\n===exam label mean===\\n{}\\n'.format(img_label_mean, exam_label_mean))\n\ntemp_df = train.copy()*0\ntemp_df[CFG['image_target_cols']] += img_label_mean.values\ntemp_df[CFG['exam_target_cols']] += exam_label_mean.values\n\nprint(temp_df.head(3))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Pytorch Implementation"},{"metadata":{"trusted":true},"cell_type":"code","source":"def rsna_torch_wloss(CFG, y_true_img, y_true_exam, y_pred_img, y_pred_exam, chunk_sizes):\n\n    # transform into torch tensors\n    y_true_img, y_true_exam, y_pred_img, y_pred_exam = torch.tensor(y_true_img, dtype=torch.float32), torch.tensor(y_true_exam, dtype=torch.float32), torch.tensor(y_pred_img, dtype=torch.float32), torch.tensor(y_pred_exam, dtype=torch.float32)\n    \n    # split into chunks (each chunks is for a single exam)\n    y_true_img_chunks, y_true_exam_chunks, y_pred_img_chunks, y_pred_exam_chunks = torch.split(y_true_img, chunk_sizes, dim=0), torch.split(y_true_exam, chunk_sizes, dim=0), torch.split(y_pred_img, chunk_sizes, dim=0), torch.split(y_pred_exam, chunk_sizes, dim=0)\n    \n    label_w = torch.tensor(CFG['exam_weights']).view(1, -1)\n    img_w = CFG['image_weight']\n    bce_func = torch.nn.BCELoss(reduction='none')\n    \n    total_loss = torch.tensor(0, dtype=torch.float32)\n    total_weights = torch.tensor(0, dtype=torch.float32)\n    \n    for i, (y_true_img_, y_true_exam_, y_pred_img_, y_pred_exam_) in enumerate(zip(y_true_img_chunks, y_true_exam_chunks, y_pred_img_chunks, y_pred_exam_chunks)):\n        exam_loss = bce_func(y_pred_exam_[0, :], y_true_exam_[0, :])\n        exam_loss = torch.sum(exam_loss*label_w, 1)[0] # Kaggle uses a binary log loss equation for each label and then takes the mean of the log loss over all labels.\n        \n        image_loss = bce_func(y_pred_img_, y_true_img_)\n        img_num = chunk_sizes[i]\n        qi = torch.sum(y_true_img_)/img_num\n        image_loss = torch.sum(img_w*qi*image_loss)\n        \n        total_loss += exam_loss+image_loss\n        total_weights += label_w.sum() + img_w*qi*img_num\n        #print(exam_loss, image_loss, img_num);assert False\n        \n    final_loss = total_loss/total_weights\n    return final_loss\n\nwith torch.no_grad():\n    loss = rsna_torch_wloss(CFG, train[CFG['image_target_cols']].values, train[CFG['exam_target_cols']].values, \n                      temp_df[CFG['image_target_cols']].values, temp_df[CFG['exam_target_cols']].values, \n                      list(train.groupby('StudyInstanceUID', sort=False)['SOPInstanceUID'].count()))\n\n    print(loss)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Numpy Implementation"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.metrics import log_loss\n\ndef cross_entropy(predictions, targets, epsilon=1e-12, reduction='none'):\n    \"\"\"\n    Computes cross entropy between targets (encoded as one-hot vectors)\n    and predictions. \n    Input: predictions (N, k1, k2, ...) ndarray\n           targets (N, k1, k2, ...) ndarray\n           reduction: 'none' | 'mean' | 'sum'\n    Returns: scalar\n    \"\"\"\n    predictions = np.clip(predictions, epsilon, 1. - epsilon)\n    \n    ce = -(targets*np.log(predictions) + (1.-targets)*np.log(1.-predictions))\n    \n    if reduction == 'none':\n        return ce\n    \n    ce = np.sum(ce)\n    if reduction == 'sum':\n        return ce\n    \n    if reduction == 'mean':\n        ce /= predictions.shape[0]\n        return ce\n\n    assert False, \"reduction should be 'none' | 'mean' | 'sum'\".format(reduction)\n    \ndef rsna_np_wloss(CFG, y_true_img, y_true_exam, y_pred_img, y_pred_exam, split_indices):\n\n    # split into chunks (each chunks is for a single exam)\n    y_true_img_chunks, y_true_exam_chunks, y_pred_img_chunks, y_pred_exam_chunks = np.split(y_true_img, split_indices[1:-1], axis=0), np.split(y_true_exam, split_indices[1:-1], axis=0), np.split(y_pred_img, split_indices[1:-1], axis=0), np.split(y_pred_exam, split_indices[1:-1], axis=0)\n    \n    label_w = np.array(CFG['exam_weights']).reshape((1, -1))\n    img_w = CFG['image_weight']\n    bce_func = cross_entropy\n    \n    total_loss = 0.\n    total_weights = 0.\n    #print(len(y_true_img_chunks))\n    \n    for i, (y_true_img_, y_true_exam_, y_pred_img_, y_pred_exam_) in enumerate(zip(y_true_img_chunks, y_true_exam_chunks, y_pred_img_chunks, y_pred_exam_chunks)):\n        exam_loss = bce_func(y_pred_exam_[0, :], y_true_exam_[0, :])\n        exam_loss = np.sum(exam_loss*label_w, 1)[0] # Kaggle uses a binary log loss equation for each label and then takes the mean of the log loss over all labels.\n        \n        image_loss = bce_func(y_pred_img_, y_true_img_)\n        img_num = split_indices[i+1]-split_indices[i]\n        qi = np.sum(y_true_img_)/img_num\n        image_loss = np.sum(img_w*qi*image_loss)\n        \n        total_loss += exam_loss+image_loss\n        total_weights += label_w.sum() + img_w*qi*img_num\n        #print(exam_loss, image_loss, img_num);assert False\n        \n        \n    final_loss = total_loss/total_weights\n    return final_loss\n\nimg_counts = train.groupby('StudyInstanceUID', sort=False)['SOPInstanceUID'].count()\nsplit_indices = np.concatenate([[0], np.cumsum(img_counts)])\nloss = rsna_np_wloss(CFG, train[CFG['image_target_cols']].values, train[CFG['exam_target_cols']].values, \n                     temp_df[CFG['image_target_cols']].values, temp_df[CFG['exam_target_cols']].values, \n                     list(split_indices))\n\nprint(loss)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.StudyInstanceUID.unique()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img_counts","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> Thanks @pgeiger @sudhiriitb for pointing out that help me resolve my confusion about the competition metric"},{"metadata":{},"cell_type":"markdown","source":"### Take away: Try to implement with Tensorflow :)"}],"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}