{"cells":[{"metadata":{},"cell_type":"markdown","source":"#Keras를 활용한 CNN From scratch!\n \n* 사람들이 그린 복잡한 이미지 인식/분류를 하는 문제이다\n* 따라서 이미지 인식에 특화된 CNN을 사용하였다.\n* Pretrained Network 대신 keras를 사용해 기본모델부터 생성하였다.\n* 데이터 전처리와 데이터 입출력의 경우 ['Image-Based CNN'] (https://www.kaggle.com/jpmiller/image-based-cnn) 커널을 참조하였다.\n* 데이터 전처리의 한계로 한 클래스당 2000개의 데이터셋을 사용하였다. 더 많은 데이터를 위해 Generator를 사용한 모델로 발전시키면 좋을 것 같다.\n* CNN 모델의 경우 convolution-Pooling을 3 Layer 사용하였다\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nimport ast\nimport os\nfrom glob import glob\nfrom tqdm import tqdm\nfrom dask import bag\nimport cv2\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow import keras\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Flatten\nfrom keras.layers import Conv2D, MaxPooling2D\nfrom keras.metrics import top_k_categorical_accuracy\nfrom keras.metrics import sparse_top_k_categorical_accuracy\nfrom keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Predicted 된 데이터의 Label과 Class_name을 매칭하기 위한 Dictionary를 먼저 형성한다.\n\n* ims_per_class는 학습+테스트 셋을 합한 데이터의 수를 지정한다. 여기서는 각 Class마다 2000개씩 사용하였다.\n* 데이터 : 총 340*2000=680000개를 사용하였다"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"classfiles=os.listdir('../input/train_simplified/')\nnumstonames={i : v[:-4].replace(' ','_') for i , v in enumerate(classfiles)}\n\nnum_class=340\nimheight,imwidth=32,32\nims_per_class=2000","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* 주어진 Stroke를 cv2를 이용해 line으로 변환하고 이를 다시 32*32픽셀의 이미지로 변환한다"},{"metadata":{"trusted":true},"cell_type":"code","source":"def stroke_to_img(strokes):\n    img=np.zeros((256,256))\n    for each in ast.literal_eval(strokes):\n        for i in range(len(each[0])-1):\n            cv2.line(img,(each[0][i],each[1][i]),(each[0][i+1],each[1][i+1]),255,5)\n    img=cv2.resize(img,(32,32))\n    img=img/255\n    return img","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Stoke 확인하기\n* Random한 클래스의 그림을 2장 띄워서 stroke_to_img 함수를 이용해 그림과 Label을 확인한다."},{"metadata":{"trusted":true},"cell_type":"code","source":"rd=np.random.randint(340)\nranclass=numstonames[rd]\nranclass=ranclass.replace('_',' ')\nrdpath='../input/train_simplified/'+ranclass+'.csv'\none=pd.read_csv(rdpath,usecols=['drawing','recognized','word'],nrows=10)\none=one[one.recognized==True].head(2)\nname=one['word'].head(1)\nstrk=one['drawing']\npic=[]\nfor s in strk:\n    pic.append(stroke_to_img(s))\nname=name.values\n\nfig,axarr = plt.subplots(1,2)\ntitle_obj = plt.title(name)\nplt.getp(title_obj, 'text')           \naxarr[0].imshow(pic[1])\naxarr[1].imshow(pic[0])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Train Set과 Test Set이 포함된 전체 arrary를 형성한다."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_grand=[]\nclass_paths = glob('../input/train_simplified/*.csv')\nfor i , c in enumerate(tqdm(class_paths[0:num_class])):\n    train=pd.read_csv(c,usecols=['drawing','recognized'],nrows=ims_per_class*2)\n    train=train[train.recognized==True].head(ims_per_class)\n    imagebag=bag.from_sequence(train.drawing.values).map(stroke_to_img)\n    trainarray=np.array(imagebag.compute())\n    trainarray=np.reshape(trainarray,(ims_per_class,-1))\n    labelarray=np.full((train.shape[0],1),i)\n    trainarray=np.concatenate((labelarray,trainarray),axis=1)\n    train_grand.append(trainarray)\n\ntrain_grand=np.array([train_grand.pop() for i in np.arange(num_class)])\ntrain_grand=train_grand.reshape((-1,(imheight*imwidth+1)))\n\ndel trainarray\ndel train","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Validation Data를 위해 비율을 설정하고 Random Shuffle 후 나눈다."},{"metadata":{"trusted":true},"cell_type":"code","source":"valfrac=0.2\ncutpt=int(valfrac*train_grand.shape[0])\n\nnp.random.shuffle(train_grand)\ny_train, x_train=train_grand[cutpt:,0],train_grand[cutpt:,1:]\ny_val,x_val=train_grand[0:cutpt,0], train_grand[0:cutpt,1:]\n\ndel train_grand\n\nx_train=x_train.reshape(x_train.shape[0],imheight,imwidth,1)\nx_val=x_val.reshape(x_val.shape[0],imheight,imwidth,1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#CNN 모델 설계\n\n* 구조 : INPUT -> [[CONV -> RELU]-> POOL]*3-> [FC-> RELU]-> FC\n\n* 그림을 인식하기 위해서 각 부분별 계층구조를 위해 Convolution을 3번 해주었다.\n* convolution에서 activation은 렐루로, 마지막 class activation은 340개의 label를 위한 softmax를 사용하였다."},{"metadata":{"trusted":true},"cell_type":"code","source":"model =Sequential()\nmodel.add(Conv2D(32,kernel_size=(3,3),padding='same',activation='relu',input_shape=(imheight,imwidth,1)))\nmodel.add(MaxPooling2D(pool_size=(2,2)))\nmodel.add(Conv2D(64,kernel_size=(3,3),padding='same',activation='relu'))\nmodel.add(MaxPooling2D(pool_size=(2,2)))\nmodel.add(Conv2D(64,kernel_size=(3,3),padding='same',activation='relu'))\nmodel.add(MaxPooling2D(pool_size=(2,2)))\nmodel.add(Flatten())\nmodel.add(Dropout(0.5))\nmodel.add(Dense(680,activation='relu'))\nmodel.add(Dense(num_class,activation='softmax'))\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Label Category의 경우 One-Hot Encoding을 메모리 이유로 사용하지 않았기 때문에 'sparse_categorical_crossentropy'를 사용하였다\n* reduceLROnPlat으로 콜백이 호출될때마다 학습률을 줄인다.\n* Earlystop의 조건으로는 val_acc를 사용하였다.\n* Optimizer는 널리 쓰이는 'adam'을 사용하였다\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"def top_3_accuracy(x,y):\n    t3=sparse_top_k_categorical_accuracy(x,y,3)\n    return t3\n\nreduceLROnPlat=ReduceLROnPlateau(monitor='val_loss',factor=0.5,patience=3,verbose=1,mode='auto',min_delta=0.005,cooldown=5,min_lr=0.0001)\nearlystop=EarlyStopping(monitor='val_acc',mode='max',patience=5)\ncallbacks=[reduceLROnPlat,earlystop]\n\nmodel.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy',top_3_accuracy])\n\nhistory=model.fit(x=x_train,y=y_train,batch_size=32,epochs=20,validation_data=(x_val,y_val),callbacks=callbacks,verbose=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loss & Accuracy Graph\n* Traing, Validation 정확도와 손실 그래프이다"},{"metadata":{"trusted":true},"cell_type":"code","source":"acc=history.history['acc']\nval_acc=history.history['val_acc']\nloss= history.history['loss']\nval_loss=history.history['val_loss']\n\nepochs=range(1,len(acc)+1)\n\nplt.plot(epochs,acc,'bo',label='Training acc')\nplt.plot(epochs,val_acc,'b',label='Validation acc')\nplt.title('Training and validation accuracy')\nplt.legend()\n\nplt.figure()\n\nplt.plot(epochs,loss,'bo',label='Training loss')\nplt.plot(epochs,val_loss,'b',label='Validation loss')\nplt.title('Training and validation loss')\nplt.legend()\n\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ttvlist=[]\nreader=pd.read_csv('../input/test_simplified.csv',index_col=['key_id'],chunksize=2048)\nfor chunk in tqdm(reader,total=55):\n    imagebag=bag.from_sequence(chunk.drawing.values).map(stroke_to_img)\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]\n    ttvlist.append(ttvs)\nttvarray=np.concatenate(ttvlist)\npred_df=pd.DataFrame({'first': ttvarray[:,0],'second':ttvarray[:,1],'third':ttvarray[:,2]})\npred_df=pred_df.replace(numstonames)\npred_df['words']=pred_df['first']+' '+pred_df['second']+' '+pred_df['third']\n\nsub=pd.read_csv('../input/sample_submission.csv',index_col=['key_id'])\nsub['word']=pred_df.words.values\nsub.to_csv('submission_summer.csv')","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}