{"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 in \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 \"../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\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nimport os","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"def apk(actual, predicted, k):\n    if actual not in predicted:  # 정답이 제출한 값에 없으면 score는 0\n        return 0.0\n    for i in range(k):\n        if actual in  predicted[:i+1]:  # 제출한 값의 범위를 첫번째, 첫번째 ~ 두번째, *** 첫번째 ~ N번째 까지 에 정답이 있다면.\n            return 1.0 / len(predicted[:i+1])  # (1.0 / 비교 범위 길이) 가 score임","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\ndata_path = \"/kaggle/input/quickdraw-doodle-recognition/\" \nprint(os.listdir(data_path))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nsub_df = pd.read_csv(data_path+'sample_submission.csv')\nprint(\"test data 수:\",len(sub_df))\nsub_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_file_path = data_path + 'train_raw/'\neiffel_df = pd.read_csv(train_file_path + 'The Eiffel Tower.csv')\neiffel_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_simple_file_path = data_path + 'train_simplified/'\neiffel_simple_df = pd.read_csv(train_simple_file_path + 'The Eiffel Tower.csv')\neiffel_simple_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import json\nraw_images = [json.loads(draw) for draw in eiffel_df.head()['drawing'].values]\nsimple_images = [json.loads(draw) for draw in eiffel_simple_df.head()['drawing'].values]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nfor index in range(3):\n    f, (ax1, ax2) = plt.subplots(ncols=2,nrows=1,figsize=(8,4))\n    for x,y,t in raw_images[index]:\n        ax1.plot(x, y, marker='.')\n    for x,y in simple_images[index]:\n        ax2.plot(x, y, marker='.')\n    ax1.set_title('raw drawing')\n    ax2.set_title('simplified drawing')    \n    ax1.invert_yaxis()\n    ax2.invert_yaxis()\n    ax1.legend(range(len(raw_images[index])))\n    ax2.legend(range(len(simple_images[index])))\n    plt.show()  ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"======== 첫번째 raw drawing의 첫 획 Data 중 5개 Point 정보 =========\")\nprint(\"x좌표: \", json.loads(eiffel_df['drawing'][0])[0][0][:5])\nprint(\"y좌표: \", json.loads(eiffel_df['drawing'][0])[0][1][:5])\nprint(\"msec: \", json.loads(eiffel_df['drawing'][0])[0][2][:5])\n\nprint(\"======== 첫번째 Simplified drawing의 첫 획 Data 중 5개 Point 정보 =========\")\nprint(\"x좌표: \", json.loads(eiffel_simple_df['drawing'][0])[0][0][:5])\nprint(\"y좌표: \", json.loads(eiffel_simple_df['drawing'][0])[0][1][:5])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_csvs= os.listdir(train_file_path)\nprint(\"train_raw 폴더 내 파일 수:\", len(train_csvs))\nprint(train_csvs[:5])\n\nfile_size = 0\nlabel_names = []\n\nfor csv_file in train_csvs:\n    file_size += os.path.getsize(train_file_path + csv_file) # data file들의 용량을 계산\n    label_names.append(csv_file.replace('.csv','')) \nprint(\"파일 크기 : \", file_size//(1024*1024*1024) ,\"GB\")\n\nlabel_names = sorted(label_names,key=lambda x : str.lower(x+'.csv')) # at kaggle notebook ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_raw_df = pd.read_csv(data_path+\"test_raw.csv\")\ntest_raw_df.head()\nprint(test_raw_df.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_raw_df.shape[0]%len(label_names) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import cv2\nimport numpy as np\ndef draw_raw_cv2(raw_strokes, size=128, lw=6, last_drop_r=0.0, second_strokes = None):  \n    ofs = lw*2 # 완성된 이미지에 테두리 공백 \n    limit_ett = 20*1000 # 최대 시간 20초\n    npstrokes = [] \n    mminfo={\"xmin\":float('inf'),\"ymin\":float('inf'), \"xmax\":float('-inf'),\"ymax\":float('-inf')} \n    \n    # strokes drop augmentation\n    drop_num = int(np.random.random()*last_drop_r *len(raw_strokes))\n    if drop_num>0:\n        raw_strokes = raw_strokes[:-drop_num]\n    \n    # mixup augmentation\n    if second_strokes is not None:\n        first_ett = raw_strokes[-1][-1][-1]\n        end_fist_st_len = len(raw_strokes)\n        raw_strokes.extend(second_strokes)\n        \n    for t, stroke in enumerate(raw_strokes):\n        npstroke = np.array(stroke)\n        #print(npstroke.shape)\n        npstrokes.append(npstroke)        \n        mminfo[\"xmin\"] = min(mminfo[\"xmin\"], min(npstroke[0]))\n        mminfo[\"xmax\"] = max(mminfo[\"xmax\"], max(npstroke[0]))\n        mminfo[\"ymin\"] = min(mminfo[\"ymin\"], min(npstroke[1]))\n        mminfo[\"ymax\"] = max(mminfo[\"ymax\"], max(npstroke[1]))\n        \n    ett=npstrokes[-1][-1][-1] # 얼마나 빨리 완료하는가 20초 이하  \n    \n    nimg = np.zeros((size,size,3),dtype=float)\n    # print(mminfo) # min 좌표에 음수가 있는 경우도 있음.   \n    org_width = mminfo[\"xmax\"] - mminfo[\"xmin\"] \n    org_height = mminfo[\"ymax\"] - mminfo[\"ymin\"]\n    ratio = max(org_width,org_height) / (size-ofs*2)\n    if ratio == 0 :\n        print('ratio 0 case ? null data ? log for debugging',mminfo)\n        return nimg\n    pre_st_t = 0 \n    for t, stroke in enumerate(npstrokes):\n        stroke[0] = (stroke[0] - mminfo[\"xmin\"])/ratio + ofs\n        stroke[1] = (stroke[1] - mminfo[\"ymin\"])/ratio + ofs\n        inertia_x = 0\n        inertia_y = 0\n        if second_strokes is not None and t == end_fist_st_len:\n            pre_st_t = 0\n        for i in range(len(stroke[0]) - 1): # 각 stroke의 Point loop, 마지막 좌표 전까지\n            color = min((1.0 - 0.95*float(t)/len(npstrokes)),1.0) # 획 순에 대한 color\n            sx = int(stroke[0][i])\n            sy = int(stroke[1][i])\n            st = stroke[2][i]\n            ex = int(stroke[0][i + 1])\n            ey = int(stroke[1][i + 1])\n            et = stroke[2][i+1]\n            \n            color_v = min((((sx-ex)**2+(sy-ey)**2)**0.5 / (abs(et-st)+1) *5), 1.0) ## like 속력, 비 정상 data 가 있음. et-st 가 음수인경우 et-st가 0인 경우\n            if i==0:\n                color_a = 0\n            else:\n                color_a = min((((inertia_x-ex)**2+(inertia_y-ey)**2)**0.5 / (abs(et-st)+1) *5), 1.0) ## 획의 변화량, like 가속력, 첫점은 255            \n            nimg = cv2.line(nimg, (sx, sy), (ex, ey), (color,color_v,color_a), lw)\n            # print(color_v,color_a)\n            if i==0:\n                color_inter = min((float(st-pre_st_t)*10/limit_ett),1.0)\n                if t == 0 or (second_strokes is not None and t == end_fist_st_len):\n                    color_inter = 1.0 # 첫 stroke의 첫번째 점 표시\n                nimg = cv2.circle(nimg, (sx, sy), lw, (0.0,0.0,color_inter), -1) ##interval time\n                \n            if i==len(stroke[0])-2 and t == len(raw_strokes) -1: #마지막 획에 마지막 점\n                color_end = (float(ett)/(limit_ett))\n                nimg = cv2.circle(nimg, (ex, ey), lw, (0.0,color_end,0.0), -1) ##end time\n\n            if second_strokes is not None and i==len(stroke[0])-2 and t == end_fist_st_len -1: #마지막 획에 마지막 점\n                color_end = (float(first_ett)/(limit_ett))\n                nimg = cv2.circle(nimg, (ex, ey), lw, (0.0,color_end,0.0), -1) ##end time\n                \n            inertia_x = ex + (ex-sx)\n            inertia_y = ey + (ey-sy)\n            pre_st_t=et     \n            \n    return nimg\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nimport numpy as np\nimport json\nfrom tensorflow.keras.utils import to_categorical      \nclass DoodelGenerator(tf.keras.utils.Sequence):\n    def __init__(self, df_files, input_shape, batchsize,label_num=340, lw=3, state='Train', last_drop_r=0.0, mixup_r = 0.0):\n        self.df_files = df_files\n        self.file_sel = 0 # 파일 list 중 현재 fit하는데 사용할 파일 index\n        self.batchsize = batchsize\n        self.input_shape = input_shape\n        self.label_num = label_num\n        self.lw = lw\n        self.state = state\n        self.last_drop_r = last_drop_r\n        self.mixup_r = mixup_r\n        self.on_epoch_end()\n        self.len = -(-len(self.df)//self.batchsize) \n\n    def __len__(self):\n        return self.len\n\n    def __getitem__(self, index):\n        batch_idx = self.idx[index*self.batchsize:(index+1)*self.batchsize] # batch size 만큼 index를 뽑음\n        h,w,ch = self.input_shape\n        X = np.zeros((len(batch_idx), h,w,ch)) #batch\n        y = np.zeros((len(batch_idx), self.label_num))\n        df = self.df.loc[batch_idx]\n        mixup_num = int(self.batchsize*self.mixup_r)\n        mixup_df = self.df.loc[np.random.randint(0,len(self.df),size=mixup_num)]\n        mixup_strokes=[]\n        mixup_labels=[]\n        for raw_strokes, label in mixup_df.values:\n            mixup_strokes.append(json.loads(raw_strokes))\n            mixup_labels.append(label)\n            \n        for i in range(self.batchsize):\n            raw_strokes = json.loads(df.drawing.values[i])\n            if i < len(mixup_strokes):\n                X[i, :, :, ] = draw_raw_cv2(raw_strokes, size=h, lw=self.lw\n                                        , last_drop_r = self.last_drop_r,second_strokes=mixup_strokes[i])\n                if self.state != 'Test':\n                    ysm_mix = self.smooth_labels(to_categorical(mixup_labels[i], num_classes=self.label_num))\n                    ysm_org = self.smooth_labels(to_categorical(df.y.values[i], num_classes=self.label_num))\n                    y[i, :] = (ysm_mix*0.5) + (ysm_org*0.5)\n            else:\n                X[i, :, :, ] = draw_raw_cv2(raw_strokes, size=h, lw=self.lw\n                                            , last_drop_r = self.last_drop_r)\n            \n                if self.state != 'Test':\n                    y[i, :] = to_categorical(df.y.values[i], num_classes=self.label_num)\n            \n        if self.state != 'Test':\n            return X,y\n        else:\n            return X\n    \n    def get_cur_df(self): # 현재 로딩되어 있는 파일을 반환하는 함수, holdout set 평가시 사용\n        return self.df \n    \n    def smooth_labels(self,labels, factor=0.1): # mix up augmentation 사용시 사용\n        labels *= (1 - factor)\n        labels += (factor / labels.shape[0])\n        return labels\n    \n    def on_epoch_end(self):\n        self.df = pd.read_csv(self.df_files[self.file_sel])\n        print('current step file : ', self.df_files[self.file_sel], 'state:', self.state, 'df_len:', self.df.shape[0])\n        self.idx = np.tile(np.arange(len(self.df)),2) # train file size가 flexible함으로 idx 배열을 연장\n        if self.state == 'Train':\n            np.random.shuffle(self.idx)        \n        self.file_sel = (self.file_sel+1)%len(self.df_files) # next csv file roll\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hold_out_set= 'train_k99'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install efficientnet\nimport efficientnet.tfkeras as efn\ndef build_model(backbone= efn.EfficientNetB0, input_shape = (128,128,3), use_imagenet = 'imagenet'):\n    base_model = backbone(input_shape=input_shape, weights=use_imagenet,include_top= False)\n    x = base_model.output\n    x = tf.keras.layers.GlobalAvgPool2D(name='gap')(x)\n    predictions = tf.keras.layers.Dense(len(label_names), activation='softmax', name='prediction')(x)\n    model = tf.keras.Model(inputs=base_model.input, outputs=predictions)\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def mapk(actual, predicted, k=3): # 학습 후 hold out set 을 평가 하는데 사용할 함수\n    return np.mean([apk(a, p, k) for a, p in zip(actual, predicted)])\n\ndef preds2catids(predictions): # submission을 위해 top3 category로 변환할 함수\n    return pd.DataFrame(np.argsort(-predictions, axis=1)[:, :3], columns=['a', 'b', 'c'])\n\ndef top_3_accuracy(y_true, y_pred):\n    return tf.keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=3)\n\ndef map_at3(y_true, y_pred): # train 과정 중에 평가를 위한 함수\n    map3 = tf.keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=1)*0.5\n    map3 += tf.keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=2)*0.17\n    map3 += tf.keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=3)*0.33    \n    return map3","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"recipes = []\nrecipes.append({'backbone':efn.EfficientNetB7, \"batch_size\":30, 'name':'Efb7','val_sel':7,'input_shape':(128,128,3),'lw':2})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ext_data_path = '/kaggle/input/doodle-model/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import random\nfor i, recipe in enumerate(recipes):\n    model_name = recipe[\"name\"] + '_val_' + str(recipe[\"val_sel\"])  + '_ep2'\n    best_save_model_file = model_name + '.h5' \n    print('best_save_model_file path : ',best_save_model_file)\n\n    model = build_model(backbone= recipe['backbone'], input_shape = recipe['input_shape'], use_imagenet = None)\n    model.load_weights(ext_data_path + best_save_model_file)\n    test_datagen = DoodelGenerator([data_path+\"test_raw.csv\"], input_shape=recipe['input_shape'], lw=recipe['lw']\n                                   , batchsize=recipe['batch_size'],state='Test')\n    test_predictions = model.predict(test_datagen, verbose=1)\n    top3 = preds2catids(test_predictions)\n    id2cat = {k: cat.replace(' ', '_') for k, cat in enumerate(label_names)}\n    top3cats = top3.replace(id2cat) \n    sub_df['word'] = top3cats['a'] + ' ' + top3cats['b'] + ' ' + top3cats['c']\n    submission = sub_df[['key_id', 'word']]\n    submission.to_csv('submission_'+model_name+'.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from collections import Counter,OrderedDict\nfrom operator import itemgetter\n\ndef balancing_predictions(test_prob, factor = 0.1, minfactor = 0.001, patient = 5, permit_cnt=332, max_search=10000, label_num=340):\n    maxk = float('inf')\n    s_cnt = np.zeros(label_num)\n    for i in range(max_search):\n        ctop1 = Counter(np.argmax(test_prob,axis=1))\n        ctop1 = sorted(ctop1.items(), key=itemgetter(1), reverse=True)\n        if maxk > ctop1[0][1]:\n            maxk = ctop1[0][1]\n        else:\n            s_cnt[ctop1[0][0]]+=1\n            if np.max(s_cnt)>patient:\n                if factor< minfactor:\n                    print('stop min factor')\n                    break\n                s_cnt=np.zeros(label_num)\n                factor*=0.99\n                print('reduce factor: ', factor, ', current max category num: ', ctop1[0][1])\n\n        if ctop1[0][1] <= permit_cnt:\n            print('idx: ',ctop1[0][0] ,', num: ', ctop1[0][1]) \n            break\n        test_prob[:,ctop1[0][0]] *= (1.0-factor)\n        \n    return test_prob","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"bal_test_prob = balancing_predictions(test_predictions)\nbal_top3 = preds2catids(bal_test_prob)\nbal_top3cats = bal_top3.replace(id2cat) \nsub_df['word'] = bal_top3cats['a'] + ' ' + bal_top3cats['b'] + ' ' + bal_top3cats['c']\nbal_submission = sub_df[['key_id', 'word']]\nbal_submission.to_csv('submission_bal_'+model_name+'.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}