반응형

drive mount

# 모든 과정은 colab을 통해 진행했습니다.
from google.colab import drive
drive.mount('/content/drive')

library import

#라이브러리 import

#형분석 설치
!pip install konlpy

import os
import re
import urllib.request
import pandas as pd
from konlpy.tag import Kkma
import tqdm
from wordcloud import WordCloud

data download

urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings_train.txt", filename="ratings_train.txt")
urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings_test.txt", filename="ratings_test.txt")
data = pd.read_table('ratings_train.txt')
print(data.shape)
data.head()

형태소 분석

#명사추출
def preprocessing_nouns(text, kkma, remove_stopwords=False, stop_words=[]):
    #한글 추출
    text=re.sub("[^가-힣ㄱ-ㅎㅏ-ㅣ]"," ", text)
    #명사 추출
    word_text=kkma.nouns(text)
    #불용어 및 두글자 이상 명사 추출
    if remove_stopwords:
        word=[token for token in word_text if not token in stop_words and len(token)>1]
    return word


kkma = Kkma()
stop_words=['은','는','이','가', '하','아','것','들','의','있','되','수','보','주','등','한']

document = data['document'].iloc[:3000]


nouns_text=[]

for text in tqdm.tqdm(document):
    try:
        nouns_text.append(preprocessing_nouns(text, kkma, remove_stopwords=True, stop_words=stop_words))
    except:
        nouns_text.append([])

 

nouns_text[:5]

Word Count - TF(Term Frequency)

tf_words=[]

for i in range(len(nouns_text)):
    tf_words.extend(nouns_text[i])

word_count = {} # 사전을 만든다
for word in tf_words: # 모든 단어에 대해서
    if word in word_count: # 사전에 단어가 있으면
        word_count[word] += 1 # 단어의 개수를 1 증가 시킨다
    else: # 없으면
        word_count[word] = 1 # 단어의 개수를 1로 한다

Word Count - DF(Document Frequency)

DF의 경우는 문서 내의 단어가 1번등장하던 10번등장하던 1번등장으로 생각합니다. 따라서 문서내의 동일한 단어를 중복제거 합니다.

df_words=[]

for i in range(len(nouns_text)):
    # set을 통해 문서 내 중복단어 제거
    df_words.extend(list(set(nouns_text[i])))

word_count = {} # 사전을 만든다
for word in df_words: # 모든 단어에 대해서
    if word in word_count: # 사전에 단어가 있으면
        word_count[word] += 1 # 단어의 개수를 1 증가 시킨다
    else: # 없으면
        word_count[word] = 1 # 단어의 개수를 1로 한다

WordCloud

 
wc = WordCloud(width=1000, 
               height=400,
               font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf' )
cloud = wc.fit_words(word_count)
cloud.to_image()

WordCloud - Option

워드클라우드 옵션을 살펴보면 워드클라우드 크기(width, height), 글자 방향(prefer_horizontal), 불용어(stopwords), 배경색(background_color) 등이 있습니다. 이외의 옵션은 직접 사용하며, 확인해보면 됩니다.

wc = WordCloud(width=1000, 
               height=400,
               font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf',
               #배경 흰색
               background_color='white',
               #글자 수평
               prefer_horizontal = 1,
               #최대 단어 100개
               max_words=100)
cloud = wc.fit_words(word_count)
cloud.to_image()

WordCloud - 상위빈도키워드

보통 wordcloud를 그릴 경우 모든 단어가 아닌 상위 빈도수의 키워드를 나타내길 원합니다. 예를 들어 고객의 리뷰를 분석할 경우 고객이 가장 많이 언급한 단어가 고객 opinion의 단서가 될 수 있기 때문입니다.

 

sort를 통해 count기반으로 정렬 후 상위 50개를 추출하여 다시 딕셔너리 형태로 변경해줍니다.

from operator import itemgetter
빈도수 기준으로 sort
sorted_words = sorted(word_count.items(), key=itemgetter(1), reverse=True)
상위빈도 키워드 5개 추출
print(sorted_words[:5])

word_dict = {}
for n , i in sorted_words[:50]:
  word_dict[n] = i

print("영화 wordcount :",word_dict['영화'])
print("딕셔너리 갯수 :",len(word_dict))

wc = WordCloud(width=1000, 
               height=400,
               font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf' )
cloud = wc.fit_words(word_dict)
cloud.to_image()

반응형

'데이터분석 > NLP' 카테고리의 다른 글

[Keras] RNN 내부동작 알아보기  (0) 2021.08.05
[Keras] Embedding에 대해 알아보자.  (3) 2021.08.05
[soynlp] 띄어쓰기 교정 모델  (0) 2020.05.11
반응형

이번 포스팅은 기본적으로 RNN의 대한 개념을 안다는 가정하에 RNN의 input, output, training parms에 대해 알아보려 합니다. 

우선 앞서 Embedding에 대해 알아봤습니다.

https://taeguu.tistory.com/69

 

[Keras] Embedding에 대해 알아보자.

최근 NLP를 공부하며, 막연히 남들이 작성한 코드를 통해 분류모델을 만들었습니다. 그러다 보니 모델안의 데이터의 흐름을 알 수 없어, RNN계열, 더 나아가 트랜드포머, 버트등의 모델을 이해하

taeguu.tistory.com

 

RNN 들어가기 전

RNN을 공부하면서 이해가 가지 않는 부분들이 많았습니다. 은닉층과 출력층은 같은 크기인가? RNN의 아웃풋은 Y값인가? 아니면 은닉값인가? 

 

RNN을 공부하다 보면 아래의 그림들을 많이 볼 수 있습니다. 왼쪽의 그림은 RNN을 통과하여 결과값 까지의 흐름을 나타낸 것이며, 오른쪽 그림은 RNN내부의 구조를 나타낸 것입니다. 따라서 이번 RNN을 공부하는데 있어 결과값은 출력하지 않으며 RNN은 은닉상태를 출력한다고 생각하면 됩니다. 두 그림을 같은 그림으로 혼동하시면 안됩니다.

 

RNN

RNN의 특징은 입력이 은닉층 노드(A)에서 활성화 함수(tanh)를 지나 나온 결과를 출력으로 보냄과 동시에 은닉층 노드(A)로 다시 보내 다음 들어올 입력을 계산할 때 도움을 줍니다. 그림은 아래와 같습니다. 

 

왼쪽의 그림을 나열하면 오른쪽의 그림과 같이 펼칠 수 있습니다.

 

예를 들어 "나는 밥을 먹었다" 라는 문장을 RNN의 입력으로 받는다고 생각하면, 우선 "나는"의 입력벡터가 x0의 입력으로 들어가며 은닉측 노드(A)와 활성화함수를 지난 결과값이 h0 출력 벡터로 보내집니다. 그 다음 "밥을"의 입력벡터가 x1의 입력으로 들어가며, x0의 은닉측에서 나온 값은 x1의 은닉층으로 보내지며, 이를 은닉 상태(hidden state)라고 합니다.

RNN

Input

Embedding에서 나온 output이 RNN의 입력으로 들어가게 됩니다.

앞선 예에서 아래 3개의 문장은 전처리 및 임베딩을 거쳐 3, 4, 100의 Shape을 가졌습니다.

["나는 밥을 먹었다", "나는 학교에 갔다", 오늘 학교에 선생님이 오셨다"]

복습하자면, 3은 샘플의 수(batchsize), 4는 sequence_length, 100은 embedding에서 설정한 단어의 임베딩차원 이었습니다. 

그렇다면 임베딩을 거쳐 RNN을 통과한 output은 어떤 형태일까요? 기억해야 할 점은 지금 설명하는 부분은 분류, 번역등의 task를 위한 출력층까지의 단계를 설명하는 것이 아니라 RNN 층에 대한 설명이며, RNN층이 리턴하는 결과값은 출력층의 값이 아닌 은닉상태 입니다.

 

 

Output 1

"나는 밥을 먹었다"의 문장을 RNN의 입력으로 넣어보겠습니다. 앞서 Keras Embedding층을 거친 값을 가져오겠습니다. 나는 밥을 먹었다는 띄어쓰기 기준으로 3개의 단어지만 앞서 가장 큰 길이인 4로 padding을 했기 때문에 시퀀스 길이가 4이며, 단어 백터의 차원의 경우는 100으로 Embedding를 통과했습니다. 또한, RNN의 입력을 위해 batch size를 추가하였습니다. 하나의 문장이기 때문에 1을 추가했습니다. RNN 층은 (batchsize, sequence_length, input_dim) 크기의 3D 텐서를 입력으로 받습니다.

 

1, 4 , 100의 입력이 들어가 1, 3의 출력이 나왔습니다. 1, 3은 마지막 시점의 은닉 상태 입니다. 출력 벡터 차원은 hidden_size의 값인 3입니다. 

 

아래 그림은 나는 밥을 먹었다의 문장이 embedding을 거쳐 rnn의 입력, 출력과정을 나타낸 그림입니다.

Output 2

RNN에는 return_sequences라는 옵션이 있습니다. 기본은 Fasle입니다. True일 경우 어떤 output을 출력할까요? 

기존 1, 3출력이 아닌 1, 4, 3의 출력을 나타냅니다. 그렇다면 return_sequences=True의 역할은 무엇일까요? 바로 모든 셀의 은닉상태를 retrun한다고 생각하면됩니다. 

 

아래 그림은 나는 밥을 먹었다의 문장이 embedding을 거쳐 rnn(return_sequences=True)의 입력, 출력과정을 나타낸 그림입니다.

반응형

'데이터분석 > NLP' 카테고리의 다른 글

[NLP] Python WordCloud 그리기  (0) 2021.11.21
[Keras] Embedding에 대해 알아보자.  (3) 2021.08.05
[soynlp] 띄어쓰기 교정 모델  (0) 2020.05.11
반응형

최근 NLP를 공부하며, 막연히 남들이 작성한 코드를 통해 분류모델을 만들었습니다.

 

그러다 보니 모델안의 데이터의 흐름을 알 수 없어, RNN계열, 더 나아가 트랜드포머, 버트등의 모델을 이해하는데 어려움을 느꼈습니다. 

 

그래서 데이터의 흐름을 알아가고자 keras의 Sequential 선언 후 사용하는 Embedding에 대해서 알아보고자 합니다.

 

Embedding 선언

    model = Sequential()
    model.add(Embedding(len(word_index) + 1,
                     300,
                     input_length=max_len))

keras에서는 Sequential() 선언 후 Embedding을 추가합니다. 

 

인수

 

(len(word_index)) + 1 : 단어 목록의 크기

300 : 임베딩의 차원

input_length=max_len : 인풋 시퀀스 길이

 

예시

아래 세가지의 문장이 있습니다.

["나는 밥을 먹었다", "나는 학교에 갔다", 오늘 학교에 선생님이 오셨다"]

 

기계는 text를 이해하지 못하기 때문에 정수 인코딩을 진행합니다. 따라서, 아래와 같이 변환합니다.

[[1, 3, 4], [1, 2, 5], [6, 2, 7, 8]]

정수 인코딩을 살펴보면, 1 = "나는" , 2 = "학교에", 8 ="오셨다"로 변환된 것을 볼 수 있습니다.

 

다음 모든 문장의 길이를 맞추기 위해 padding을 해줍니다. 위의 문장 중 가장긴 문장은 "오늘 학교에 선생님이 오셧다" 이며 이문장의 길이는 "4"입니다. 따라서, 모든 문장의 길이를 4로 맞춰줍니다.

[[1, 3, 4, 0], [1, 2, 5, 0], [6, 2, 7, 8]]

 

자 이제 Embedding에 들어갈 수 있는 전처리가 완료되었습니다. 앞의 인수를 적용하게되면, 첫 번째 단어 목록의 크기의 경우 1~8단어가 있습니다. 그런데 +1을 해주는 이유는 패딩을 위한 토큰0을 포함하기 위함입니다. 

임베딩의 차원은 100으로 정하며, input_length는 앞서 정한 4로 맞춥니다. 그럼 준비한 input data를 Embedding에 넣어 output의 형태를 확인해보겠습니다.

기존의 3, 4의 input data가 3, 4, 100의 output data로 출력되었습니다. 이렇게 된 이유는 각각의 단어들 예를들어 나는이란 단어가 기존의 1로 정수 인코딩 되었다면, Embedding층을 통해 100차원으로 변경된 것 입니다. 이부분을 확인하기 위해 첫번째 문장의 나는과 두번째 문장의 나는을 출력해 보겠습니다.

 

각 벡터의 값이 같음을 확인할 수 있습니다. 또한 여기서 중요한 점은 이 벡터들은 고정된 값이 아닌 학습을 통해 변화되는 값임을 인지해야합니다. 

아래 그림은 위에서 만든 model의 summary 입니다. 왜 학습 Param이 900일까요?

이유는 앞서 정의한 단어의 크기 9 x 백터의 차원 100을 나타내는 것 입니다.

 

다음은 Embedding을 거친 값이 RNN에서 어떻게 작동하는지 확인하는 글을 포스팅하겠습니다.

반응형

'데이터분석 > NLP' 카테고리의 다른 글

[NLP] Python WordCloud 그리기  (0) 2021.11.21
[Keras] RNN 내부동작 알아보기  (0) 2021.08.05
[soynlp] 띄어쓰기 교정 모델  (0) 2020.05.11
반응형
soyspacing
  • 참조 : https://github.com/lovit/soynlp

  • 카카오에서 공개한 khaiii의 경우 뛰어난 성능을 보이지만 띄어쓰기가 잘 되어있지 않으면 성능이 좋지 않습니다. 그래서 lovit님이 공개한 soynlp의 soyspacing을 wikipedia 문서를 통해 학습하려고 합니다.

  • 데이터셋은 한국어임베딩 github에서 다운받았습니다.

Library

In [91]:
!pip install soyspacing
Looking in indexes: http://ftp.daumkakao.com/pypi/simple
Requirement already satisfied: soyspacing in /home/ubuntu/anaconda3/envs/tg_python3/lib/python3.6/site-packages (1.0.17)
Requirement already satisfied: numpy>=1.12.0 in /home/ubuntu/anaconda3/envs/tg_python3/lib/python3.6/site-packages (from soyspacing) (1.18.3)
WARNING: You are using pip version 20.0.2; however, version 20.1 is available.
You should consider upgrading via the '/home/ubuntu/anaconda3/envs/tg_python3/bin/python -m pip install --upgrade pip' command.
In [93]:
from tqdm import tqdm_notebook
import pandas as pd
from soyspacing.countbase import CountSpace

Data Load

In [54]:
wiki= []

with open("/home/ubuntu/dataset/processed/processed_wiki_ko.txt", 'r') as f:
    for s in tqdm_notebook(f):
        wiki.append(s)

wiki = pd.Series(wiki).map(lambda x: x.replace("\\n", " "))
/home/ubuntu/anaconda3/envs/tg_python3/lib/python3.6/site-packages/ipykernel/__main__.py:4: TqdmDeprecationWarning: This function will be removed in tqdm==5.0.0
Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook`

Data Write

In [55]:
# 50000건의 wiki 데이터를 text파일로 저장
with open("wiki.txt", 'w') as output:
    for row in wiki:
        output.write(str(row) )

Train

In [56]:
# soyspacing 학습(wiki문서 약31만건)
from soyspacing.countbase import CountSpace
corpus_fname = 'wiki.txt'
model = CountSpace()
model.train(corpus_fname)
all tags length = 21121548 --> 19479615, (num_doc = 311236)

Model Save and Load

In [ ]:
#Model save
model_fname = 'space-correct_wiki.model'
model.save_model(model_fname, json_format=False)

#model Loac
model_fname = 'space-correct_wiki.model'
model = CountSpace()
model.load_model(model_fname, json_format=False)

Evaluate

In [107]:
text = ['아버지가방에들어가신다', '고양이가죽을먹는다', '문재인대통령이연설을시작했다', '현대자동차투싼슈퍼카닷컴']
for n in text:
    print(model.correct(n)[0])
아버지가 방에 들어가신다
고양이가 죽을 먹는다
문재인 대통령이 연설을 시작했다
현대자동차 투싼슈퍼카닷컴
In [111]:
text = ['철구가의자에앉았다', '손나은은청순가련멋쟁이', '오늘저녁은뼈숯불구이다!']
for n in text:
    print(model.correct(n)[0])
철구가의 자에 앉았다
손나은은청순가련멋쟁이
오늘 저녁은뼈숯불구이다!
  • 아래 문장은 일부로 어려운 문장을 넣어봤습니다.
  • 모든 모델이 완벽하지는 않다고 생각하며, khaiii와 함께 사용시 좋아지지않을까? 하는 생각입니다.
In [115]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
반응형

'데이터분석 > NLP' 카테고리의 다른 글

[NLP] Python WordCloud 그리기  (0) 2021.11.21
[Keras] RNN 내부동작 알아보기  (0) 2021.08.05
[Keras] Embedding에 대해 알아보자.  (3) 2021.08.05
반응형
vggnet-using-keras

VGGNet

image

  • 이번에는 VGGNet에 대하여 알아보겠습니다. VGGNet은 14년도 ImageNet 대회에서 2등을 하고 그해 localization대회에서 1등을 차지했습니다.VGGNet의 특징은 더 깊어지고 더 작은 필터를 사용했다는것입니다. 이웃픽셀을 포함할 수 있는 가장작은 필터인 3x3 필터를 사용했습니다. 그리고 작은 필터를 유지하고 주기적으로 pooling을 수행했습니다.
  • 왜 작은 필터를 사용할까요? 우선 필터의 크기가 작으면 파라미터의 수가 적고 depth를 더 키울 수 있습니다. 3x3 with stride1 CONV를 3번 쌓게 되면 effective receptive field는 무엇일까요? 정답은 7x7입니다. 왜 이렇게 되는지 살펴보겠습니다. 첫 번째 layer에서 하나의 픽셀은 CONV를 통해 3x3의 정보를 가지게 됩니다 두 번째 레이어에서 하나의 픽셀은 3x3의 정보를 가진 상태에서 또다시 3x3의 CONV를 통해 5x5의 정보를 가집니다. 이런식으로 3x3의 CONV를 3번 쌓게되면 7x7의 effective receptive field 가지게 됩니다. 이 부분이 VGGNet이 AlexNet 보다 깊게 층을 쌓을 수 있었던 이유입니다.

Model - VGG16

  • VGGNet 뒤의 숫자는 layer의 수를 나타 냅니다 image

ImageNet weight를 가진 VGG16 불러오기

In [1]:
from keras.applications.vgg16 import VGG16
import numpy as np
#include_top은 네트워크의 최상단에 완전 연결 레이어를 넣을지 여부입니다..
model = VGG16(weights='imagenet', include_top=True,  input_shape=(224, 224, 3))
model.summary()
Using TensorFlow backend.
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg16_weights_tf_dim_ordering_tf_kernels.h5
553467904/553467096 [==============================] - 36s 0us/step
Model: "vgg16"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 224, 224, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
predictions (Dense)          (None, 1000)              4097000   
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
_________________________________________________________________

VGG16 구현하기

  • 쉽게 구현할 수 있습니다.
  • 오직 3X3 CONV with stride1, pad1 그리고 2X2 MAX POOL with stride2 로 구성되어있습니다
In [2]:
from keras.layers import Input, Conv2D, MaxPooling2D
from keras.layers import Dense, Flatten
from keras.models import Model, Sequential

input_shape = (224, 224, 3)
model = Sequential()
#conv1-1,1-2
model.add(Conv2D(filters=64, kernel_size=(3,3), strides = 1, padding="same", activation="relu",input_shape=input_shape))
model.add(Conv2D(filters=64, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
#MAXPOOL1
model.add(MaxPooling2D(pool_size=(2, 2), strides = 2))

#conv2-1, 2-2
model.add(Conv2D(filters=128, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
model.add(Conv2D(filters=128, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
#MAXPOOL2
model.add(MaxPooling2D(pool_size=(2, 2), strides = 2))

#conv3-1, 3-2, 3-3
model.add(Conv2D(filters=256, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
model.add(Conv2D(filters=256, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
model.add(Conv2D(filters=256, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
#MAXPOOL3
model.add(MaxPooling2D(pool_size=(2, 2), strides = 2))

#conv4-1, 4-2, 4-3
model.add(Conv2D(filters=512, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
#MAXPOOL4
model.add(MaxPooling2D(pool_size=(2, 2), strides = 2))

#conv5-1, 5-2, 5-3
model.add(Conv2D(filters=512, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
model.add(Conv2D(filters=512, kernel_size=(3,3), strides = 1, padding="same", activation="relu"))
#MAXPOOL5
model.add(MaxPooling2D(pool_size=(2, 2), strides = 2))

model.add(Flatten())
#FC6, 7, 8
model.add(Dense(4096, activation='relu'))
model.add(Dense(4096, activation='relu'))
model.add(Dense(1000, activation='softmax'))
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 224, 224, 64)      1792      
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 224, 224, 64)      36928     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 112, 112, 64)      0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 112, 112, 128)     73856     
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 112, 112, 128)     147584    
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 56, 56, 128)       0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 56, 56, 256)       295168    
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 56, 56, 256)       590080    
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 56, 56, 256)       590080    
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 28, 28, 256)       0         
_________________________________________________________________
conv2d_8 (Conv2D)            (None, 28, 28, 512)       1180160   
_________________________________________________________________
conv2d_9 (Conv2D)            (None, 28, 28, 512)       2359808   
_________________________________________________________________
conv2d_10 (Conv2D)           (None, 28, 28, 512)       2359808   
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 14, 14, 512)       0         
_________________________________________________________________
conv2d_11 (Conv2D)           (None, 14, 14, 512)       2359808   
_________________________________________________________________
conv2d_12 (Conv2D)           (None, 14, 14, 512)       2359808   
_________________________________________________________________
conv2d_13 (Conv2D)           (None, 14, 14, 512)       2359808   
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 7, 7, 512)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 25088)             0         
_________________________________________________________________
dense_1 (Dense)              (None, 4096)              102764544 
_________________________________________________________________
dense_2 (Dense)              (None, 4096)              16781312  
_________________________________________________________________
dense_3 (Dense)              (None, 1000)              4097000   
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
_________________________________________________________________

Model - VGG19

  • vgg19는 keras applications을 통해서만 가져오겠습니다.
  • 보시면 VGG16보다 Pooling사이에 CONV가 한층 더 추가된걸로 보입니다.
  • effective receptive field를 통해 params수는 VGG16에 비해 많이 증가되지 않았습니다.

ImageNet weight를 가진 VGG19 불러오기

In [3]:
from keras.applications.vgg19 import VGG19
#include_top은 네트워크의 최상단에 완전 연결 레이어를 넣을지 여부입니다..
model = VGG19(weights='imagenet', include_top=True,  input_shape=(224, 224, 3))
model.summary()
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg19_weights_tf_dim_ordering_tf_kernels.h5
574717952/574710816 [==============================] - 37s 0us/step
Model: "vgg19"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         (None, 224, 224, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv4 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv4 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv4 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
predictions (Dense)          (None, 1000)              4097000   
=================================================================
Total params: 143,667,240
Trainable params: 143,667,240
Non-trainable params: 0
_________________________________________________________________
In [2]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
반응형
반응형

LRN(Local Response Normalization)

LRN(Local Response Normalization)은 현재는 많이 사용되지 않습니다. 그러나 Image Net에서 최초의 CNN우승 모델인 AlexNet에서 사용했으며 작동방식에 대해 알아보도록 하겠습니다. 

 

LRN을 검색해보면 Local Response Normalization (LRN) layer implements the lateral inhibition한다고 나와있습니다.

lateral inhibition이란 무었일까요? 

 

"측면 억제(lateral inhibition)는 신경생리학 용어로, 한 영역에 있는 신경 세포가 상호 간 연결되어 있을 때 한 그 자신의 축색이나 자신과 이웃 신경세포를 매개하는 중간신경세포(interneuron)를 통해 이웃에 있는 신경 세포를 억제하려는 경향이다".

잘...이해가 되지를 않습니다. 우선 이 그림을 한번 보겠습니다. 측면 억제의 유명한 그림인 헤르만 격자입니다.

hermann-grid

검은사각형안에 흰색의 선이 지나가고 있습니다. 신기한 것은 흰색의 선에 집중하지 않을 때 회색의 점이 보이는데 이러한 현상이 측면 억제(lateral inhibition)에 의해 발생하는 것 입니다. 이는 흰색으로 둘러싸인 측면에서 억제를 발생시키기 때문에 흰색이 더 반감되어 보입니다.

 

다시 AlexNet의 LRN(Local Response Normalization)로 돌아오겠습니다.  

그렇다면 AlexNet은 왜 측면 억제(lateral inhibition)를 사용했을까요? 바로 ReLU의 사용때문입니다.ReLU는 양수의 방향으로는 입력의값을 그대로 사용합니다.그렇게되면 CONV나 POOLING시 매우 높은 하나의 픽셀값이 주변의 픽셀에 영향을 미치게되겠죠? 이런 부분을 방지하기 위해 다른 ActivationMap의 같은 위치에있는 픽셀끼리 정규화를해줍니다. 이것이바로 AlexNet에서사용되는 LRN이죠.

 

텐서플로우에서 LRN을 사용하려면 tf.nn.local_response_normalization을 통해 사용할 수 있지만 현재는 Batch Normalization을 사용합니다. 조금 더 깊게 공부하고 싶으신분은 아래 공식을 보시면 될 것 같습니다.

  LRN(Local Response Normalization)

 

반응형

'데이터분석 > vision' 카테고리의 다른 글

VGGNet using keras  (0) 2019.11.07
AlexNet using keras  (0) 2019.11.06
LeNet-5 using keras  (0) 2019.10.30
Lecture 9: CNN Architectures  (0) 2019.10.27
Lecture 7: Training Neural Networks, part I  (0) 2019.10.17
반응형
alexnet-using-keras
In [1]:
import gc
import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt

# 교차검증 lib
from sklearn.model_selection import StratifiedKFold,train_test_split
from tqdm import tqdm_notebook
from sklearn.metrics import accuracy_score, roc_auc_score
#모델 lib
from keras.datasets import mnist
from keras.utils.np_utils import to_categorical
from keras.preprocessing.image import ImageDataGenerator, load_img
from keras.models import Sequential, Model
from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau
from keras.layers import Dense, Dropout, Flatten, Activation, Conv2D, AveragePooling2D,BatchNormalization, MaxPooling2D
from keras import layers
from keras.optimizers import Adam,RMSprop, SGD

#모델
from keras.applications import VGG16, VGG19, resnet50

#경고메세지 무시
import warnings
warnings.filterwarnings(action='ignore')

import os
import gc
Using TensorFlow backend.

AlexNet

  • 이번에는 AlexNet에 대하여 알아보겠습니다. AlexNet 12년에 등장해서 딥러닝을 부흥시켰습니다. 그이유는 최초로 ImageNet대회에서 CNN으로 우승했기 때문입니다. 구조는 LeNet-5와 비슷하고 레이어만 증가했습니다.

  • AlexNex이 개발되었을 시대에는 컴퓨팅파워가 낮아 3GB의 GPU밖에 없어 AlexNet으로 ImageNet을 학습시킬 수 없었습니다. 그래서 model parallel을 통해 두개의 GPU을 연결해 병렬로 처리했습니다. 그래서 자세히 그림을보시면 conv1에서 depth가 96이아니라 48입니다.

image.png

Data load

  • AlexNet을 통해 개와 고양이를 분류하는 이진분류 문제를 풀어보겠습니다.
In [2]:
# https://www.kaggle.com/bulentsiyah/dogs-vs-cats-classification-vgg16-fine-tuning
filenames = os.listdir("../input/dogs-vs-cats/train/train")
categories = []
for filename in filenames:
    category = filename.split('.')[0]
    if category == 'dog':
        categories.append(1)
    else:
        categories.append(0)

train = pd.DataFrame({
    'filename': filenames,
    'category': categories
})
train.head()
Out[2]:
filename category
0 dog.3984.jpg 1
1 cat.4062.jpg 0
2 cat.4106.jpg 0
3 cat.12098.jpg 0
4 dog.4663.jpg 1

Visualization

In [3]:
#Visualizing the data
sample = filenames[2]
image = load_img("../input/dogs-vs-cats/train/train/"+sample)
plt.imshow(image)
plt.show()

train/test data Split

In [4]:
train["category"] = train["category"].astype('str')


its = np.arange(train.shape[0])
train_idx, test_idx = train_test_split(its, train_size = 0.8, random_state=42)

df_train = train.iloc[train_idx, :]
X_test = train.iloc[test_idx, :]

its = np.arange(df_train.shape[0])
train_idx, val_idx = train_test_split(its, train_size = 0.8, random_state=42)
X_train = df_train.iloc[train_idx, :]
X_val = df_train.iloc[val_idx, :]

print(X_train.shape)
print(X_val.shape)
print(X_test.shape)
X_train['category'].value_counts()
(16000, 2)
(4000, 2)
(5000, 2)
Out[4]:
1    8048
0    7952
Name: category, dtype: int64

AlexNet- Details/Retrospectives

  • 최초의 ReLU를 사용했으며
  • local response normalization사용하였는데 현재는 사용하지 않습니다. 현재는 Batch Normalizetion을 사용합니다.
  • heavy data augmentation
  • dropout 0.5
  • batch size 128
  • SGD Momentum 0.9
  • Learning rate은 1e-2시작하며 val accuracy가 증가하지 않으면 10으로 나누어 줍니다.
  • L2 weight decay 5e-4
In [5]:
# Parameter
image_size = 227
img_size = (image_size, image_size)
nb_train_samples = len(X_train)
nb_validation_samples = len(X_val)
nb_test_samples = len(X_test)
epochs = 20
#batch size 128
batch_size =128

# Define Generator config
train_datagen =ImageDataGenerator(
    rescale=1./255,
    rotation_range=10,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True
    )

val_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
In [6]:
#generator
train_generator = train_datagen.flow_from_dataframe(
    dataframe=X_train, 
    directory="../input/dogs-vs-cats/train/train",
    x_col = 'filename',
    y_col = 'category',
    target_size = img_size,
    color_mode='rgb',
    class_mode='binary',
    batch_size=batch_size,
    seed=42
)

validation_generator = val_datagen.flow_from_dataframe(
    dataframe=X_val, 
    directory="../input/dogs-vs-cats/train/train",
    x_col = 'filename',
    y_col = 'category',
    target_size = img_size,
    color_mode='rgb',
    class_mode='binary',
    batch_size=batch_size,
)

test_generator = test_datagen.flow_from_dataframe(
    dataframe=X_test,
    directory="../input/dogs-vs-cats/train/train",
    x_col = 'filename',
    y_col=None,
    target_size= img_size,
    color_mode='rgb',
    class_mode=None,
    batch_size=batch_size,
    shuffle=False
)
Found 16000 validated image filenames belonging to 2 classes.
Found 4000 validated image filenames belonging to 2 classes.
Found 5000 validated image filenames.

image

Model - AlexNet

In [7]:
#INPUT
input_shape = (227, 227, 3)
model = Sequential()
#CONV1
model.add(Conv2D(96, (11, 11), strides=4,padding='valid', input_shape=input_shape))
#MAX POOL1
model.add(MaxPooling2D(pool_size=(3, 3), strides=2))
#NORM1  Local response normalization 사용하였는데 현재는 사용하지 않습니다. 현재는 Batch Normalizetion을 사용합니다.
model.add(BatchNormalization())
#CONV2
model.add(Conv2D(256, (3, 3), activation='relu', padding='same'))
#MAX POOL1
model.add(MaxPooling2D(pool_size=(3, 3), strides=2))
#NORM2
model.add(BatchNormalization())
#CONV3
model.add(Conv2D(384, (3, 3),strides=1, activation='relu', padding='same'))
#CONV4
model.add(Conv2D(384, (3, 3),strides=1, activation='relu', padding='same'))
#CONV5
model.add(Conv2D(256, (3, 3),strides=1, activation='relu', padding='same'))
#MAX POOL3
model.add(MaxPooling2D(pool_size=(3, 3), strides=2))
model.add(Flatten())
#FC6 예측 class가 적어 FC layer을 조정했습니다.
model.add(Dense(1024, activation='relu'))
model.add(Dropout(0.5))
#FC7
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.5))
#FC8 이진 분류이기 때문에 sigmoid
model.add(Dense(1, activation='sigmoid'))
# SGD Momentum 0.9, L2 weight decay 5e-4
optimizer =  SGD(lr=0.01, decay=5e-4, momentum=0.9)
model.compile(loss='binary_crossentropy',
              optimizer=optimizer, metrics=['accuracy'])
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 55, 55, 96)        34944     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 27, 27, 96)        0         
_________________________________________________________________
batch_normalization_1 (Batch (None, 27, 27, 96)        384       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 27, 27, 256)       221440    
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 13, 13, 256)       0         
_________________________________________________________________
batch_normalization_2 (Batch (None, 13, 13, 256)       1024      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 13, 13, 384)       885120    
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 13, 13, 384)       1327488   
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 13, 13, 256)       884992    
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 6, 6, 256)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 9216)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 1024)              9438208   
_________________________________________________________________
dropout_1 (Dropout)          (None, 1024)              0         
_________________________________________________________________
dense_2 (Dense)              (None, 512)               524800    
_________________________________________________________________
dropout_2 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 513       
=================================================================
Total params: 13,318,913
Trainable params: 13,318,209
Non-trainable params: 704
_________________________________________________________________

Train / predict

Train

In [8]:
def get_steps(num_samples, batch_size):
    if (num_samples % batch_size) > 0 :
        return (num_samples // batch_size) + 1
    else :
        return num_samples // batch_size
In [9]:
%%time
from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau

#model path
MODEL_SAVE_FOLDER_PATH = './model/'
if not os.path.exists(MODEL_SAVE_FOLDER_PATH):
    os.mkdir(MODEL_SAVE_FOLDER_PATH)

model_path = MODEL_SAVE_FOLDER_PATH + 'AlexNet.hdf5'

patient = 5
callbacks_list = [
    # Learning rate 1e-2, reduced by 10  manually when val accuracy plateaus
    ReduceLROnPlateau(
        monitor = 'val_accuracy', 
        #콜백 호출시 학습률(lr)을 10으로 나누어줌
        factor = 0.1, 
        #5epoch 동안 val_accuracy가 상승하지 않으면 lr 조정
        patience = patient, 
        #최소학습률
        min_lr=0.00001,
        verbose=1,
        mode='min'
    ),
    ModelCheckpoint(
        filepath=model_path,
        monitor ='val_accuracy',
        # val_loss가 좋지 않으면 모델파일을 덮어쓰지 않는다
        save_best_only = True,
        verbose=1,
        mode='min') ]
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 534 µs
In [10]:
history = model.fit_generator(
    train_generator,
    steps_per_epoch = get_steps(nb_train_samples, batch_size),
    epochs=epochs,
    validation_data = validation_generator,
    validation_steps = get_steps(nb_validation_samples, batch_size),
    callbacks = callbacks_list
)
gc.collect()
Epoch 1/20
125/125 [==============================] - 231s 2s/step - loss: 0.6550 - accuracy: 0.6219 - val_loss: 0.6832 - val_accuracy: 0.5770

Epoch 00001: val_accuracy improved from inf to 0.57700, saving model to ./model/AlexNet.hdf5
Epoch 2/20
125/125 [==============================] - 213s 2s/step - loss: 0.5462 - accuracy: 0.7245 - val_loss: 0.6127 - val_accuracy: 0.7203

Epoch 00002: val_accuracy did not improve from 0.57700
Epoch 3/20
125/125 [==============================] - 216s 2s/step - loss: 0.4708 - accuracy: 0.7766 - val_loss: 0.4568 - val_accuracy: 0.7160

Epoch 00003: val_accuracy did not improve from 0.57700
Epoch 4/20
125/125 [==============================] - 213s 2s/step - loss: 0.3999 - accuracy: 0.8168 - val_loss: 0.3649 - val_accuracy: 0.7610

Epoch 00004: val_accuracy did not improve from 0.57700
Epoch 5/20
125/125 [==============================] - 212s 2s/step - loss: 0.3593 - accuracy: 0.8436 - val_loss: 0.5346 - val_accuracy: 0.7875

Epoch 00005: val_accuracy did not improve from 0.57700
Epoch 6/20
125/125 [==============================] - 212s 2s/step - loss: 0.3185 - accuracy: 0.8619 - val_loss: 0.3869 - val_accuracy: 0.8745

Epoch 00006: ReduceLROnPlateau reducing learning rate to 0.0009999999776482583.

Epoch 00006: val_accuracy did not improve from 0.57700
Epoch 7/20
125/125 [==============================] - 211s 2s/step - loss: 0.2708 - accuracy: 0.8857 - val_loss: 0.3340 - val_accuracy: 0.8905

Epoch 00007: val_accuracy did not improve from 0.57700
Epoch 8/20
125/125 [==============================] - 211s 2s/step - loss: 0.2458 - accuracy: 0.8966 - val_loss: 0.1782 - val_accuracy: 0.9025

Epoch 00008: val_accuracy did not improve from 0.57700
Epoch 9/20
125/125 [==============================] - 212s 2s/step - loss: 0.2332 - accuracy: 0.9041 - val_loss: 0.1363 - val_accuracy: 0.9068

Epoch 00009: val_accuracy did not improve from 0.57700
Epoch 10/20
125/125 [==============================] - 210s 2s/step - loss: 0.2249 - accuracy: 0.9068 - val_loss: 0.1963 - val_accuracy: 0.9087

Epoch 00010: val_accuracy did not improve from 0.57700
Epoch 11/20
125/125 [==============================] - 212s 2s/step - loss: 0.2212 - accuracy: 0.9113 - val_loss: 0.1615 - val_accuracy: 0.8960

Epoch 00011: ReduceLROnPlateau reducing learning rate to 9.999999310821295e-05.

Epoch 00011: val_accuracy did not improve from 0.57700
Epoch 12/20
125/125 [==============================] - 214s 2s/step - loss: 0.2156 - accuracy: 0.9108 - val_loss: 0.3811 - val_accuracy: 0.9070

Epoch 00012: val_accuracy did not improve from 0.57700
Epoch 13/20
125/125 [==============================] - 211s 2s/step - loss: 0.2139 - accuracy: 0.9106 - val_loss: 0.3198 - val_accuracy: 0.9087

Epoch 00013: val_accuracy did not improve from 0.57700
Epoch 14/20
125/125 [==============================] - 210s 2s/step - loss: 0.2117 - accuracy: 0.9123 - val_loss: 0.1410 - val_accuracy: 0.9080

Epoch 00014: val_accuracy did not improve from 0.57700
Epoch 15/20
125/125 [==============================] - 208s 2s/step - loss: 0.2123 - accuracy: 0.9141 - val_loss: 0.2429 - val_accuracy: 0.9090

Epoch 00015: val_accuracy did not improve from 0.57700
Epoch 16/20
125/125 [==============================] - 208s 2s/step - loss: 0.2084 - accuracy: 0.9143 - val_loss: 0.2101 - val_accuracy: 0.9093

Epoch 00016: ReduceLROnPlateau reducing learning rate to 1e-05.

Epoch 00016: val_accuracy did not improve from 0.57700
Epoch 17/20
125/125 [==============================] - 206s 2s/step - loss: 0.2089 - accuracy: 0.9158 - val_loss: 0.0953 - val_accuracy: 0.9097

Epoch 00017: val_accuracy did not improve from 0.57700
Epoch 18/20
125/125 [==============================] - 206s 2s/step - loss: 0.2094 - accuracy: 0.9136 - val_loss: 0.3337 - val_accuracy: 0.9100

Epoch 00018: val_accuracy did not improve from 0.57700
Epoch 19/20
125/125 [==============================] - 205s 2s/step - loss: 0.2097 - accuracy: 0.9143 - val_loss: 0.1391 - val_accuracy: 0.9090

Epoch 00019: val_accuracy did not improve from 0.57700
Epoch 20/20
125/125 [==============================] - 211s 2s/step - loss: 0.2059 - accuracy: 0.9158 - val_loss: 0.2226 - val_accuracy: 0.9097

Epoch 00020: val_accuracy did not improve from 0.57700
Out[10]:
212

predict

In [11]:
%%time
test_generator.reset()
prediction = model.predict_generator(
    generator = test_generator,
    steps = get_steps(nb_test_samples, batch_size),
    verbose=1
)
print('Test accuracy : ', roc_auc_score(X_test['category'].astype('int'), prediction, average='macro'))
40/40 [==============================] - 21s 533ms/step
Test accuracy :  0.9668627839696784
CPU times: user 15.2 s, sys: 5.23 s, total: 20.4 s
Wall time: 21.4 s

acc / loss plot

In [12]:
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
epochs = range(len(acc))

plt.plot(epochs, acc, label='Training acc')
plt.plot(epochs, val_acc, label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.ylim(0.5,1)
plt.show()
In [13]:
loss = history.history['loss']
val_loss = history.history['val_loss']

plt.plot(epochs, loss, label='Training loss')
plt.plot(epochs, val_loss, label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.ylim(0,0.5)
plt.show()

Result

  • testset의 정확도는 96.6%나왔습니다.
  • loss는 점점 떨어지지만 조금 불안정해보입니다
In [1]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
반응형
반응형
lenet-5-using-keras-99-2
In [1]:
import gc
import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt

# 교차검증 lib
from sklearn.model_selection import StratifiedKFold,train_test_split
from tqdm import tqdm_notebook

#모델 lib
from keras.datasets import mnist
from keras.utils.np_utils import to_categorical
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential, Model
from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau
from keras.layers import Dense, Dropout, Flatten, Activation, Conv2D, AveragePooling2D
from keras import layers
from keras.optimizers import Adam,RMSprop

#모델
from keras.applications import VGG16, VGG19, resnet50

#경고메세지 무시
import warnings
warnings.filterwarnings(action='ignore')
Using TensorFlow backend.

LeNet-5

  • 최초로 산업에 성공적으로 적용된 CNN모델이다 image.png

data load

In [2]:
import os
os.listdir('../input/digit-recognizer')
datapath = '../input/digit-recognizer'
In [3]:
train =pd.read_csv(datapath+'/train.csv')
print(train.shape)
train.head()
(42000, 785)
Out[3]:
label pixel0 pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 pixel8 ... pixel774 pixel775 pixel776 pixel777 pixel778 pixel779 pixel780 pixel781 pixel782 pixel783
0 1 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
2 1 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
3 4 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0

5 rows × 785 columns

In [4]:
test =pd.read_csv(datapath+'/test.csv')
print(test.shape)
test.head()
(28000, 784)
Out[4]:
pixel0 pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 pixel8 pixel9 ... pixel774 pixel775 pixel776 pixel777 pixel778 pixel779 pixel780 pixel781 pixel782 pixel783
0 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0

5 rows × 784 columns

In [5]:
train_labels = train['label']
train = (train.iloc[:,1:].values).astype('float32')
test = test.values.astype('float32')
In [6]:
#Visualizing the data
sample = train[10, :].reshape(28,28)
plt.imshow(sample, cmap='gray')
plt.show()
print('label : ', train_labels[10])
label :  8

Preprocessing

  • LeNet-1 모델은 28x28의 이미지를 사용했습니다.
  • LeNet-5에서는 MNIST의 28x28 테스트 영상을 32x32 이미지의 중심에 배치하여 처리하였습니다.큰사이즈의 이미지 사용으로 인해 작은부분의 고려가 작은사이즈 보다 훨씬 더 고려되어 성능이 더욱 향상되었습니다.
In [7]:
train = train.reshape(42000, 28, 28, 1)
test= test.reshape(28000, 28, 28, 1)
# change shape using pad
train = np.pad(train, ((0,0),(2,2),(2,2),(0,0)), 'constant')
test = np.pad(test, ((0,0),(2,2),(2,2),(0,0)), 'constant')

print('train shape : ', train.shape)
print('test shape : ', test.shape)
train shape :  (42000, 32, 32, 1)
test shape :  (28000, 32, 32, 1)
In [8]:
# int64 -> float32 ,  scaling
train = train.astype('float32')/255
test = test.astype('float32')/255
X_train, X_val, y_train, y_val = train_test_split(train, train_labels, test_size=0.20, random_state=42)

#One-hot encoding the labels
print('X_train shape : ', X_train.shape)
print('X_val shape : ', X_val.shape)
print('y_train : ', y_train.shape)
print('y_val : ', y_val.shape)
y_train = to_categorical(y_train)
y_val = to_categorical(y_val)
print('y_train_to_categorical : ', y_train.shape)
print('y_val_to_categorical : ', y_val.shape)
X_train shape :  (33600, 32, 32, 1)
X_val shape :  (8400, 32, 32, 1)
y_train :  (33600,)
y_val :  (8400,)
y_train_to_categorical :  (33600, 10)
y_val_to_categorical :  (8400, 10)

Model

  • [32x32x1] INPUT
  • [28x28x6] CONV1: 6 5x5 filters at stride 1, pad 0
  • [6x14x14] Average POOL1: 2x2 filters at stride 2
  • [16x10x10] CONV2: 256 5x5 filters at stride 1, pad 0
  • [16x5x5] Average POOL2: 2x2 filters at stride 2
  • [120] FC6: 120 neurons
  • [84] FC7: 84 neurons
  • [10] FC8: 10 neurons (class scores)
  • LeNet-5모델은 Non-overlapping pooling을 사용했다. image
In [9]:
#lenet-5 model
model = Sequential()
#Conv layer 1
model.add(layers.Conv2D(filters=6, kernel_size=(5, 5),strides=1, activation='relu', input_shape=(32,32,1)))
#Pooling layer 1
model.add(AveragePooling2D(pool_size = 2, strides = 2))
#Conv Layer2
model.add(layers.Conv2D(filters=16, kernel_size=(5, 5),strides=1, activation='relu'))
#Pooling layer 2
model.add(AveragePooling2D(pool_size = 2, strides = 2))
model.add(layers.Flatten())
#FC Layer 3
model.add(layers.Dense(120, activation='relu'))
#FC Layer 4
model.add(layers.Dense(84, activation='relu'))
#FC Layer 5
model.add(layers.Dense(10, activation = 'softmax'))


# compile
model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 28, 28, 6)         156       
_________________________________________________________________
average_pooling2d_1 (Average (None, 14, 14, 6)         0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 10, 10, 16)        2416      
_________________________________________________________________
average_pooling2d_2 (Average (None, 5, 5, 16)          0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 400)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 120)               48120     
_________________________________________________________________
dense_2 (Dense)              (None, 84)                10164     
_________________________________________________________________
dense_3 (Dense)              (None, 10)                850       
=================================================================
Total params: 61,706
Trainable params: 61,706
Non-trainable params: 0
_________________________________________________________________

Train and predict

In [10]:
datagen = ImageDataGenerator(
        rotation_range=10,  
        zoom_range = 0.10,  
        width_shift_range=0.1, 
        height_shift_range=0.1)
In [11]:
patient = 4
callbacks_list = [
    ReduceLROnPlateau(
        monitor = 'val_loss', 
        #학습률을 절반으로 줄입니다.
        factor = 0.5, 
        #patience 만큼 val_loss가 감소하지 않으면 학습률을 줄입니다.
        patience = patient / 2, 
        #min Reduces learning
        min_lr=0.00001,
        verbose=1,
        mode='min'
    )]
In [12]:
%%time
epochs =30
batch_size = 64
history = model.fit_generator(datagen.flow(X_train,y_train, batch_size=batch_size),
                              epochs = epochs, validation_data = (X_val,y_val),
                              steps_per_epoch=X_train.shape[0] // batch_size
                              ,callbacks=callbacks_list,verbose = 1)
Epoch 1/30
525/525 [==============================] - 18s 35ms/step - loss: 0.6216 - accuracy: 0.8020 - val_loss: 0.1816 - val_accuracy: 0.9450
Epoch 2/30
525/525 [==============================] - 15s 29ms/step - loss: 0.2233 - accuracy: 0.9317 - val_loss: 0.1542 - val_accuracy: 0.9527
Epoch 3/30
525/525 [==============================] - 15s 29ms/step - loss: 0.1758 - accuracy: 0.9464 - val_loss: 0.0894 - val_accuracy: 0.9735
Epoch 4/30
525/525 [==============================] - 15s 29ms/step - loss: 0.1458 - accuracy: 0.9562 - val_loss: 0.0741 - val_accuracy: 0.9777
Epoch 5/30
525/525 [==============================] - 15s 29ms/step - loss: 0.1208 - accuracy: 0.9624 - val_loss: 0.0798 - val_accuracy: 0.9755
Epoch 6/30
525/525 [==============================] - 16s 31ms/step - loss: 0.1092 - accuracy: 0.9667 - val_loss: 0.0671 - val_accuracy: 0.9792
Epoch 7/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0936 - accuracy: 0.9712 - val_loss: 0.0548 - val_accuracy: 0.9832
Epoch 8/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0918 - accuracy: 0.9715 - val_loss: 0.0622 - val_accuracy: 0.9814
Epoch 9/30
525/525 [==============================] - 15s 28ms/step - loss: 0.0811 - accuracy: 0.9744 - val_loss: 0.0531 - val_accuracy: 0.9832
Epoch 10/30
525/525 [==============================] - 15s 28ms/step - loss: 0.0761 - accuracy: 0.9767 - val_loss: 0.0439 - val_accuracy: 0.9862
Epoch 11/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0712 - accuracy: 0.9774 - val_loss: 0.0421 - val_accuracy: 0.9880
Epoch 12/30
525/525 [==============================] - 16s 30ms/step - loss: 0.0662 - accuracy: 0.9788 - val_loss: 0.0393 - val_accuracy: 0.9882
Epoch 13/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0635 - accuracy: 0.9799 - val_loss: 0.0487 - val_accuracy: 0.9852
Epoch 14/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0624 - accuracy: 0.9806 - val_loss: 0.0409 - val_accuracy: 0.9879

Epoch 00014: ReduceLROnPlateau reducing learning rate to 0.0005000000237487257.
Epoch 15/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0490 - accuracy: 0.9850 - val_loss: 0.0346 - val_accuracy: 0.9899
Epoch 16/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0445 - accuracy: 0.9861 - val_loss: 0.0357 - val_accuracy: 0.9887
Epoch 17/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0413 - accuracy: 0.9871 - val_loss: 0.0367 - val_accuracy: 0.9890

Epoch 00017: ReduceLROnPlateau reducing learning rate to 0.0002500000118743628.
Epoch 18/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0405 - accuracy: 0.9870 - val_loss: 0.0279 - val_accuracy: 0.9913
Epoch 19/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0370 - accuracy: 0.9885 - val_loss: 0.0314 - val_accuracy: 0.9907
Epoch 20/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0355 - accuracy: 0.9885 - val_loss: 0.0278 - val_accuracy: 0.9914

Epoch 00020: ReduceLROnPlateau reducing learning rate to 0.0001250000059371814.
Epoch 21/30
525/525 [==============================] - 15s 28ms/step - loss: 0.0331 - accuracy: 0.9895 - val_loss: 0.0283 - val_accuracy: 0.9923
Epoch 22/30
525/525 [==============================] - 15s 28ms/step - loss: 0.0321 - accuracy: 0.9897 - val_loss: 0.0282 - val_accuracy: 0.9915

Epoch 00022: ReduceLROnPlateau reducing learning rate to 6.25000029685907e-05.
Epoch 23/30
525/525 [==============================] - 16s 30ms/step - loss: 0.0310 - accuracy: 0.9901 - val_loss: 0.0264 - val_accuracy: 0.9924
Epoch 24/30
525/525 [==============================] - 15s 29ms/step - loss: 0.0274 - accuracy: 0.9917 - val_loss: 0.0263 - val_accuracy: 0.9919
CPU times: user 9min 34s, sys: 49.4 s, total: 10min 23s
Wall time: 7min 42s
In [13]:
#predict
submission =pd.read_csv(datapath+'/sample_submission.csv')
pred = model.predict(test)
pred = np.argmax(pred,axis = 1)
submission['Label'] = pred
submission.to_csv('submission.csv',index=False)
submission.head()
Out[13]:
ImageId Label
0 1 2
1 2 0
2 3 9
3 4 0
4 5 3

Acc/Loss plot

In [14]:
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

epochs = range(len(acc))

plt.plot(epochs, acc, label='Training acc')
plt.plot(epochs, val_acc, label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.ylim(0.9,1)
plt.show()
In [15]:
loss = history.history['loss']
val_loss = history.history['val_loss']

plt.plot(epochs, loss, label='Training loss')
plt.plot(epochs, val_loss, label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.ylim(0,0.5)
plt.show()

conclusion

image

  • 간단한 Renet-5 모델로 정확도 99%이상을 달성했습니다.
  • 교차검증을 통한 앙상블로 정확도를 더 높일 수 있을 것 같습니다
In [1]:
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
반응형

+ Recent posts