{"cells":[{"metadata":{},"cell_type":"markdown","source":"게임 모델을 발전시켜보세요.\nKernel 상단에 과제 수행 시 어떤 방법을 어떤 이유로 선택했는지 작성한다\n\n\nQuick Draw 게임을 해보니 제한시간안에 그림을 그리면 AI 가 \"이건가요?\" \"저건가요?\" 말하다가 \"아 알겠어요 이건 만리장성입니다.\" 이런 게임이다. 사실 엄청 놀랬다. 완성된 그림을 주는게 아니라 한 획, 한 획 그리는 와중에 정답을 말하는 AI의 성능이 대단하다고 생각했다. class 가 340개 라는 한정이 있지만 모든 사람이 동그라미 조차도 같게 그리지 않으므로 만리장성을 맞춘건 대단하다고 생각한다.\n\n데이터셋을 받아서 구조 분석을 하는데에는 Getting Started 커널을 참고했다. 나의 예상과는 다르게 인풋 데이터가 이미지가 아닌 선들의 좌표 배열이었다. 이 좌표를 이용해서 다시 이미지로 변환한 다음 input으로 사용할 예정이다. 이미지를 분류하는데는 CNN이 성능이 가장 좋으니깐 CNN을 포커스로 잡고 진행하였다. \n\n데이터가 선1, 선2, 선3 이런식으로 시간의 속성이 들어가서 lstm 을 적용해도 괜찮을것 같았지만 자신있는 CNN으로 과제를 진행하고 시간이 남는다면 바뀌보는것도 괜찮을것 같다.\n\n1차 사용 방법 \n\nCNN : 이미지의 데이터가 많은 상황에서 CNN은 최고의 성능을 낸다. \n\nkeras : 원래 tensorflow를 사용했었지만, 이를 통해 keras를 알게되었다. input, kernel, output 을 블록으로 설명한 부분이 인상깊었고 뿌리가 tensorflow 와 많이 벗어나지 않아 습득하고 사용하는데 많은 부담감은 없었다. 코드가 훨씬 깔끔하고 사용하기 쉽지만 keras가 tensorflow를 커버할 수 없다고 한다. 이는 좀 더 사용해봐야 알것같다.\n\n\n2차 사용 방법\n\n배치 사이즈와 에폭수를 늘려보았다.\n\n클래스 마다(.csv) 2500개의 값중 recognized == True 을 만족하는 2000개의 값만 사용한다. 이를 무작정 10000값으로 늘려봤다. 하지만 한번에 로드하는것은 메모리가 데이터셋보다 작으면 문제가 생기고 '좌표를 이용해서 다시 이미지로 변환'하는 과정이 생각보다 너무 오래걸려서 다른 방법을 생각해봐야 했다.\n\n1차 코드는 '전체 데이터셋을 한번에 읽어오는 방법'이다. 최종적으로 (680000, 1025) 배열을 만들었다. keras 에서 model.fit_generator() 함수를 보고 스텝 바이 스텝으로 data generator를 반드는 것을 적용해보았다.\n\n"},{"metadata":{},"cell_type":"markdown","source":"1. training data 구조 분석\n\nI'll use an adaptation of Inversion's 'Getting Started' kernel."},{"metadata":{},"cell_type":"markdown","source":"glob 폴데에 있는 모든 파일 접근해서 list 형태로 변환\n\ntqdm for문의 상태바 보여줌"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport re\nfrom glob import glob\nfrom tqdm import tqdm\nimport numpy as np\nimport pandas as pd\nimport ast\nimport matplotlib.pyplot as plt\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\n"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"fnames = glob('../input/train_simplified/*.csv') #<class 'list'>\ncnames = ['countrycode', 'drawing', 'key_id', 'recognized', 'timestamp', 'word']\ndrawlist = []\nfor f in fnames[0:6]: # num of word : 5\n    first = pd.read_csv(f, nrows=10) # make sure we get a recognized drawing\n    first = first[first.recognized==True].head(2) # top head 2 get \n    drawlist.append(first)\ndraw_df = pd.DataFrame(np.concatenate(drawlist), columns=cnames) # <class 'pandas.core.frame.DataFrame'>\ndraw_df","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":""},{"metadata":{},"cell_type":"markdown","source":"그림 그린 데이터는 이렇게 숫자들의 배열로 저장 \n```\ndrawing.values = [[선1점1, 선1점2, 선1점3, ... 선1점n], [선2점1, 선2점2, 선2점3, ... 선2점n], ..., [선i점1, 선i점2, 선i점3, ... 선i점n]]\n```"},{"metadata":{"trusted":true},"cell_type":"code","source":"draw_df.drawing.values[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"evens = range(0,11,2)\nodds = range(1,12, 2)\n# We have drawing images, 2 per label, consecutively\ndf1 = draw_df[draw_df.index.isin(evens)]\ndf2 = draw_df[draw_df.index.isin(odds)]\n\nexample1s = [ast.literal_eval(pts) for pts in df1.drawing.values]\nexample2s = [ast.literal_eval(pts) for pts in df2.drawing.values]\nlabels = df2.word.tolist()\n\nfor i, example in enumerate(example1s):\n    plt.figure(figsize=(6,3))\n    \n    for x,y in example:\n        plt.subplot(1,2,1)\n        plt.plot(x, y, marker='.')\n        plt.axis('off')\n\n    for x,y, in example2s[i]:\n        plt.subplot(1,2,2)\n        plt.plot(x, y, marker='.')\n        plt.axis('off')\n        label = labels[i]\n        plt.title(label, fontsize=10)\n\n    plt.show()  ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%reset -f\n# reset this program, deleting all pre-made variables, modules, functions, etc, that were before this cell","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"데이터를 보았다.\n이제 모델을 만들어보자.\ncsv의 데이터가 x,y점의 좌표를 줘서 모델의 input으로 주기 위해 이미지로 바꾸는작업이 필요하다.\n\nDask 패키지는 Pandas 데이터프레임 형식으로 빅데이터를 처리하기 위한 파이썬 패키지이다."},{"metadata":{"trusted":true},"cell_type":"code","source":"\nimport os\nfrom glob import glob\nimport re\nimport ast\nimport numpy as np \nimport pandas as pd\nfrom PIL import Image, ImageDraw \nfrom tqdm import tqdm\nfrom dask import bag\n\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras.models import Sequential\nfrom keras.utils import to_categorical\nfrom tensorflow.keras.layers import Dense, Dropout, Flatten\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D\nfrom tensorflow.keras.metrics import top_k_categorical_accuracy\nfrom tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def pyth_test (x1, x2):\n   \n    print (x1 + x2)\n\npyth_test(1,2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"*The data are also available in a zip file (automatically extracted inside the Kernels environment).\n카글은 대단하다."},{"metadata":{"trusted":true},"cell_type":"code","source":"path = '../input/train_simplified/'\nclassfiles = os.listdir(path)\n\nnumstonames = {i: v[:-4].replace(\" \", \"_\") for i, v in enumerate(classfiles)} # sleeping bag -> sleeping_bag\nfiles = [os.path.join(path, file) for i, file in enumerate(classfiles)]\nword_mapping = {file.split('/')[-1][:-4]:i for i, file in enumerate(files)}\n\nnum_classes = len(files)    #340\nimheight, imwidth = 32, 32 # size of an image\nims_per_class = 2000  #max? # in the code above and above, there existed more than 100 thousand images per class(/label)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_it(strokes):\n    image = Image.new(\"P\", (256,256), color=255) #\"P\":(8-bit pixels, mapped to any other mode using a color palette)\n    image_draw = ImageDraw.Draw(image)\n    for stroke in ast.literal_eval(strokes):\n        for i in range(len(stroke[0])-1):\n            image_draw.line([stroke[0][i], \n                             stroke[1][i],\n                             stroke[0][i+1], \n                             stroke[1][i+1]],\n                            fill=0, width=5)\n    image = image.resize((imheight, imwidth))\n    return np.array(image)/255.\n\ndef imageGenerator2(batchsize, validation=False):\n    print(batchsize)\n    df = []\n    check = []\n    T2 = []\n    for file in files:\n        if validation:\n            df.append(pd.read_csv(file, nrows=110000, usecols=[1, 5]).tail(10000).sample(1000))\n        else:\n            df.append(pd.read_csv(file, nrows=100000, usecols=[1, 5]).sample(1000))\n                \n    df = pd.concat(df) \n    df['word'] = df['word'].map(word_mapping)\n    df = df.sample(frac=1).reset_index(drop=True)\n    y = to_categorical(df['word'].values, num_classes)\n    print(y.shape)\n    imagebag = bag.from_sequence(df['drawing'].values).map(draw_it)\n    X = np.array(imagebag.compute())\n    X = X.reshape(X.shape[0],imheight, imwidth, 1)\n    print(X.shape)\n    i = 0\n    while True:\n        if i+batchsize<=y.shape[0]:\n#             print(\"if\",i+batchsize)\n            y_yield = y[i:i+batchsize]\n            X_yield = X[i:i+batchsize]\n#             print(y_yield.shape)\n#             print(y_yield)\n#             print(X_yield.shape)\n#             print(X_yield)\n            i += batchsize\n            #yield (X_yield, y_yield)\n        else:\n            break\n\ntrain_generator = imageGenerator2(batchsize=1000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_it(strokes):\n    image = Image.new(\"P\", (256,256), color=255) #\"P\":(8-bit pixels, mapped to any other mode using a color palette)\n    image_draw = ImageDraw.Draw(image)\n    for stroke in ast.literal_eval(strokes):\n        for i in range(len(stroke[0])-1):\n            image_draw.line([stroke[0][i], \n                             stroke[1][i],\n                             stroke[0][i+1], \n                             stroke[1][i+1]],\n                            fill=0, width=5)\n    image = image.resize((imheight, imwidth))\n    return np.array(image)/255.\n\ndef imageGenerator(batchsize, validation=False):\n    #print(batchsize)\n    while True:\n        df = []\n        check = []\n        T2 = []\n        for file in files:\n            if validation:\n                df.append(pd.read_csv(file, nrows=1100, usecols=[1, 5]).tail(100).sample(100))\n            else:\n                df.append(pd.read_csv(file, nrows=1000, usecols=[1, 5]).sample(100))\n                \n        df = pd.concat(df) \n        df['word'] = df['word'].map(word_mapping)\n        df = df.sample(frac=1).reset_index(drop=True)\n        y = to_categorical(df['word'].values, num_classes)\n  \n        imagebag = bag.from_sequence(df['drawing'].values).map(draw_it)\n        X = np.array(imagebag.compute())\n        X = X.reshape(X.shape[0],imheight, imwidth, 1)\n    \n        i = 0\n        while True:\n            if i+batchsize<=y.shape[0]:\n                y_yield = y[i:i+batchsize]\n                X_yield = X[i:i+batchsize]\n                i += batchsize\n                yield (X_yield, y_yield)\n            else:\n                break\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"build the cnn architecture using keras.\n"},{"metadata":{},"cell_type":"markdown","source":"Keras 공식 예제 소스\n\n참고 : https://tykimos.github.io/2017/01/27/CNN_Layer_Talk/\n\nConv2D(32, kernel_size=(3, 3), padding='same', activation='relu', input_shape=(imheight, imwidth, 1))\n- 입력 : (32, 32) 채널은 1개\n- 중간 : (3, 3)커널 필터개수 32개\n- 아웃 : (32, 32) 채널은 32개\n- 가중치 개수 : 3x3x32 = 288개\n- *참고로 케라스 코드에서는 가장 첫번째 레이어를 제외하고는 입력 형태를 자동으로 계산하므로 이 부분은 신경쓰지 않아도 됩니다.*\n\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\n- pool_size=(수직, 수평) 비율 즉, 반으로 줄임"},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# model = Sequential()\n# model.add(Conv2D(32, kernel_size=(3, 3), padding='same', activation='relu', input_shape=(imheight, imwidth, 1)))\n# model.add(MaxPooling2D(pool_size=(2, 2)))\n\n# model.add(Conv2D(64, kernel_size=(3, 3), padding='same', activation='relu'))\n# model.add(MaxPooling2D(pool_size=(2, 2)))\n# model.add(Dropout(0.2))\n\n# model.add(Flatten())\n# model.add(Dense(680, activation='relu'))\n# model.add(Dropout(0.5))\n# model.add(Dense(num_classes, activation='softmax'))\n\n# model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"model.compile() : 모델을 정의했으니 모델에 손실함수와 최적화 알고리즘을 적용해보자.\n\n- 다중 클래스 문제이므로 ‘categorical_crossentropy’으로 지정\n- 경사 하강법 알고리즘 중 하나인 ‘adam’을 사용\n- 평가 척도를 나타내며 분류 문제에서는 일반적으로 ‘accuracy’으로 지정\n\nmodel.fit() : 모델을 학습시켜보자\n- 훈련 데이터셋 , batch 사이즈, epoch 수, 검증 데이터셋, "},{"metadata":{"trusted":true},"cell_type":"code","source":"# def top_3_accuracy(x,y): \n#     t3 = top_k_categorical_accuracy(x,y, 3)\n#     return t3\n\n# reduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, \n#                                    verbose=1, mode='auto', min_delta=0.005, cooldown=5, min_lr=0.0001)\n# earlystop = EarlyStopping(monitor='val_top_3_accuracy', mode='max', patience=5) \n# callbacks = [reduceLROnPlat, earlystop]\n\n# model.compile(loss='categorical_crossentropy',\n#               optimizer='adam',\n#               metrics=['accuracy', top_3_accuracy])\n\n# model.fit(x=X_train, y=y_train,\n#           batch_size = 128,\n#           epochs = 100,\n#           validation_data = (X_val, y_val),\n#           callbacks = callbacks,\n#           verbose = 1)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# train_generator = imageGenerator(batchsize=1000)\n# valid_generator = imageGenerator(batchsize=1000, validation=True)\n\n# model.fit_generator(train_generator, steps_per_epoch=350, epochs=130, validation_data=valid_generator, validation_steps=5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"잘 돌아가는걸 확인했다.\n\n이제 test set 을 넣어보자.\n\nmodel.predict() : 모델 사용하기 "},{"metadata":{"trusted":true},"cell_type":"code","source":"#%% get test set\n# ttvlist = []\n# reader = pd.read_csv('../input/test_simplified.csv', index_col=['key_id'],\n#     chunksize=2048)\n# for chunk in tqdm(reader, total=55):\n#     imagebag = bag.from_sequence(chunk.drawing.values).map(draw_it)\n#     testarray = np.array(imagebag.compute())\n#     testarray = np.reshape(testarray, (testarray.shape[0], imheight, imwidth, 1))\n#     testpreds = model.predict(testarray, verbose=0)\n#     ttvs = np.argsort(-testpreds)[:, 0:3]  # top 3\n#     ttvlist.append(ttvs)\n    \n# ttvarray = np.concatenate(ttvlist)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# preds_df = pd.DataFrame({'first': ttvarray[:,0], 'second': ttvarray[:,1], 'third': ttvarray[:,2]})\n# preds_df = preds_df.replace(numstonames)\n# preds_df['words'] = preds_df['first'] + \" \" + preds_df['second'] + \" \" + preds_df['third']\n\n# sub = pd.read_csv('../input/sample_submission.csv', index_col=['key_id'])\n# sub['word'] = preds_df.words.values\n# sub.to_csv('subcnn_small.csv')\n# sub.head()","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}