본문 바로가기
머신러닝 딥러닝

0928 CNN 구현(tf 1.15, tf 2.x)

by 대금부는개발자 2021. 9. 28.
SMALL

 

CNN은 이미지나 영상에 특화된 구조.

다른 처리는 그렇게 어울리지 않아요!

 

CNN 구조(architecture)

 

x data는 pixel data,

tdata는 어떤 이미지냐?

 

우리의 Training Data Set이 몇 차원인지 몰라요  convolution layer에 들어가려면 4차원으로 변환 필요.

inputlayer는 입력 data의 차원을 convolution layer에 들어가는 형태로 조절해 주는 역할

 

convolution layer 합성곱 연산(stride, padding)을 진행 → 입력 data에 대해 filter 적용

 

filter - 해당 이미지의 특징을 뽑아내는 거름망 역할(random 적용)

반복 시 weight와 bias가 업데이트 → filter가 업데이트됨. → 이미지의 특징을 좀 더 잘 뽑아냄

 filter 하나 당 결과 data 하나 나와요 → feature map이 여러 개(가로 세로 여러 개)

 channel(이미지일 경우: R G B)

 

일반적으로 zero padding을 적용 안 하면 입력으로 들어온 data 보다 convolution 연산을 수행 하서 결과로 만든 feature map의 크기는 조금 작아져요.

filter의 개수에 따라 전체 data 량이 증가 → 너무 많으면 학습이 지연

 

결과로 나온 feature map에 대해 relu 연산 진행 → 최종적으로 나온 data

 activation map(feature map의 묶음)

 

filter의 개수가 너무 많을 경우 이미지 size를 줄여줘요(전체 데이터량을 줄여줘요)

 pooling layer(특징은 그대로 살리면서 단순히 size만 줄임)

이미지 size를 보고 판단 → 옵션 개념

pooling을 통과하면 size는 줄어들지만, filter의 개수만큼 이미지의 양은 많아져요.

 

원하는 만큼 convolution layer, pooling layer 반복

→ 진행되면 될수록 이미지의 크기는 줄어들어요. filter에 의해 이미지의 개수는 늘어나요.

 

FC Layer 들어가기 전에 일반적으로 Dropout을 집어넣어요(옵션, 하지만 대부분 들어가요)

신경망에서 L1, L2 규제를 사용할 수도 있지만 Dropout은 신경망에만 있는 기능이에요!

Dropout → overfitting 방지(node는 존재하지만 학습에 다 참여는 시키지 않아요)

→ 성능 개선

 

여기서 나오는 FC Layer은 DNN과 완전 같지는 않지만 비슷한 개념으로 생각해 주세요.

 

Hidden layer도 옵션 개념(써도 되고 안 써도 돼요)

 

 

 

CNN은 model 부분이 크게 두 가지 부분으로 분리해서 생각할 수 있어요!

1. convolution & pooling 부분 → Feature extraction(특징 추출)

2. DNN 부분 → Classification(학습, 분류 작업)

 

왜 CNN을 사용하나요? DNN이 hidden layer의 개수가 월등히 많아요.

일반적으로(같은 성능을 내는) CNN과 DNN을 비교해 보면,

CNN(convolution + DNN- 상대적으로 적은 hidden later의 Parameter의 개수 →

DNN(다수의 hidden layer)의 Parameter(weight, filter)의 개수보다 월등히 적어요!

CNN의 Parameter(weight, filter)의 수는 DNN의 Parameter의 수의 약 20%예요! → 속도가 비교가 안돼요!

 

 

code로 구현해 보아요!

CNN_MNIST_TF1.15 구현.

 

# MNIST 예제를 CNN으로 구현해 보아요!

import numpy as np
import pandas as pd  # file(csv, excel, json) 처리는 무조건 pandas
import tensorflow as tf
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import classification_report 

 

# 메모리에 존재하는 tensorflow graph를 reset(이름 충돌 방지, 1.15 버전에서 필요 2.x에선 필요 없어요.)

tf.reset_default_graph() 

 


# Raw Data Loading
df = pd.read_csv('./data/mnist/train.csv')
display(df) # 42000 rows × 785 columns(이미지 42000장)
# label은 이미지의 숫자, pixel은 1차원으로 되어있는 이미지 data 
# MNIST는 각 이미지를 2차원으로 된 흑백 이미지를 1차원으로 표현
# MNIST는 결측치와 이상치는 존재하지 않아요!

# 이미지 Data 확인(matplotlib을 이용해서 그림을 그려보아요 - 10장)
img_data = df.drop('label', axis=1, inplace=False).values # numpy array만 뽑아내요. 2차원

print(img_data)

fig = plt.figure() # 도화지 준비
ax = list() # subplot 채우면서 넣어줘요

for i in range(10):
    ax.append(fig.add_subplot(2,5,i+1)) # 2행 5열(10개를 찍기 위해)의 subplot을 만들어서 ax(list) 안에 subplot을 저장
    ax[i].imshow(img_data[i].reshape(28,28), cmap='gray') # i는 행 
    
plt.tight_layout()
plt.show()

 

 

 

# 데이터 전처리(preprocessing)

# Data split(Train Data와 Test Data 분리)
train_x_data, test_x_data, train_t_data, test_t_data = \
train_test_split(df.drop('label', axis=1, inplace=False), # x data
                 df['label'], #t data
                 test_size=0.3,
                 stratify=df['label'],
                 random_state=0)

# MinMaxScaler를 이용한 Nimalization(정규화)
scaler = MinMaxScaler()
scaler.fit(train_x_data)
train_x_data_norm = scaler.transform(train_x_data)
test_x_data_norm = scaler.transform(test_x_data)

# Tensorflow 1.15 버전을 이용해서 multinomial 처리를 해야 해요!
# Label에 대한 one-hot encoding처리가 필요 1.15 버전!!(2.x 버전에서 sparse옵션 사용)

# tensor node를 실행하기 위해 session 필요
sess= tf.Session()

# 지금 1차원인데 one hot encoding으로 2차원 변환
# depth를 줘야 어떻게 바뀌는지 알아요 # depth는 label의 종류 개수
train_t_data_onehot = sess.run(tf.one_hot(train_t_data, depth=10))
test_t_data_onehot = sess.run(tf.one_hot(test_t_data, depth=10))

 

 

# CNN 형태의 Tensorflow Graph를 그려보아요!

# placeholder
# scalar가 아닌 이상 반드시 shape지정
X = tf.placeholder(shape=[None,784], dtype=tf.float32)   
T = tf.placeholder(shape=[None,10], dtype=tf.float32)

# X 데이터는 convolution 연산을 하기 위한 이미지 데이터로 사용되어야 해요!
# 따라서 X 데이터의 차원을 4차원(이미지 개수, 세로, 가로, channel)으로 변환
# -1은 이미지가 몇개인지 몰라서(계산하고 나머지 이미지 개수로 잡아!), 흑백이면 channel을 1로 써도 돼요
x_img = tf.reshape(X, [-1,28,28,1])  # None 이 -1, 784 → 1, 이미지 입력 data 완성

# 첫번째 convolution 연산을 수행할 filter 생성 → filter는 update가 되는 존재
# filter의 형태 ([세로, 가로, 채널, filter의 개수])
W1 = tf.Variable(tf.random.normal([3,3,1,32]))  # filter의 개수는 내가 지정 이미지 한 장당 32개

L1 = tf.nn.conv2d(x_img,
                  W1,
                  strides=[1,1,1,1], # 한 칸씩 옮겨가면서
                  padding='SAME')    # VALID : padding을 사용하지 않아요! 이미지 사이즈가 줄어요!
                                     # SAME : padding을 사용해서 원본과 결과의 크기가 같도록 조절.

# print(L1.shape)  
# (?, 28, 28, 32) →(결과이미지의 개수, 세로, 가로 , channel) => Feature Map
#? 는 아직 들어오는 data가 몇 개인지 몰라요.
# padding을 SAME으로 만들어서 size 줄지 않아요
# filter가 32가 있어서 channel이 32개 만들어졌어요! 

# Relu 연산
L1 = tf.nn.relu(L1)  # 첫 번째 convolution  결과 → activation map

# pooling layer ( MAX Pooling )
L1 = tf.nn.max_pool(L1,
                    ksize=[1,2,2,1], # filter와 비슷한 kernel
                    strides=[1,2,2,1],
                    padding='SAME')

# print(L1.shape)  # (?, 14, 14, 32) 


# 첫 번째 convolution과 두 번째 convolution의 input
# 두 번째 convolution 연산을 수행할 필터를 생성 (세로, 가로, 채널, 개수)
# 세로 가로는 자유롭게 지정 일단 똑같이 3x3
# 입력으로 들어온 channel수 32
# filter는 마음대로 지정 64 → 이미지의 사이즈는 점점 작아지기 때문에 filter를 통해 이미지의 수를 늘려요.
W2 = tf.Variable(tf.random.normal([3,3,32,64]))

L2 = tf.nn.conv2d(L1,
                  W2,
                  strides=[1,1,1,1], # 한 칸씩 옮겨가면서
                  padding='SAME') # VALID : padding을 사용 x → 이미지 size 줄어요
      
# Relu 연산
L2 = tf.nn.relu(L2)   # 첫 번째 convolution  결과 → activation map

# pooling layer ( MAX Pooling )
L2 = tf.nn.max_pool(L2,
                    ksize=[1,2,2,1],
                    strides=[1,2,2,1],
                    padding='SAME')

print(L2.shape)     # (?, 7, 7, 64)  size가 또 줄었어요 → 최종 이미지 입력 data

L2 = tf.reshape(L2, [-1, 7*7*64])   # 1 → 이미지개수(2차원), 7x7, 64개 이미지 → column 

# Feature Extraction(특징추출 완료)
##############################################
# FC layer를 이용해서(DNN) 학습을 진행 → 데이터가 2차원(이미지가 여러 개) , 이미지 한 장이 1차원

# weight & bias
# He's 초기화 or xavier초기화 하기 위해 get_variable
# 'weight3'는 그냥 의미론적 이름 
# 지금까지와 다르게 입력 data가 X 가 아니라 L2 (?, 7, 7, 64)로 바뀌었어요
# convolition 통과 결과 4차원을 2차원으로 변화 
# 7*7*64는 행, 중간에 hidden layer를 넣을 거기에 node 수는 임의로 잡아요 256
# 7x7 이미지 64개를 하나의 column으로 표현 7*7*64

W3 = tf.get_variable('weight3', 
                     shape=[7*7*64,256],    # weight의 모양, 256 : hidden layer의 node 수
                     initializer=tf.contrib.layers.variance_scaling_initializer())   # He's 초기화 
b3 = tf.Variable(tf.random.normal([256]))

_layer3 = tf.nn.relu(tf.matmul(L2,W3) + b3) # logit
layer3 = tf.nn.dropout(_layer3, rate=0.5)   # dropout 50%


W4 = tf.get_variable('weight4', 
                     shape=[256,10],    
                     initializer=tf.contrib.layers.variance_scaling_initializer())   # He's 초기화 
b4 = tf.Variable(tf.random.normal([10]))

# hypothesis
logit = tf.matmul(layer3,W4) + b4
H = tf.nn.softmax(logit)

# loss
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logit,
                                                                 labels=T))

# train
train = tf.train.AdamOptimizer(learning_rate=1e-2).minimize(loss)

# session & 초기화
sess.run(tf.global_variables_initializer())


# 반복학습(batch처리를 해야해요! - 메모리 문제 때문에)
for step in range(400):
    
    tmp, loss_val = sess.run([train, loss], feed_dict={X:train_x_data_norm,
                                                       T:train_t_data_onehot})

    if step % 40 == 0:
        print('loss : {}'.format(loss_val))

        # loss : 0.15766535699367523

 

 

# evaluation

# 우리의 Hyphothesis는 0~9까지의 확률 값을 알려줘요.
# H : [0.02, 0.03, ... 0.09, 0.5]
#     [0.21, 0.33, ... 0.08, 0.2]
# 입력값(이미지)을 여러 개 주면 2차원 배열로 나와요.

# test data에 대한 예측값과 test data 비교
predict = tf.argmax(H,1)  

print(classification_report(test_t_data,
                            sess.run(predict, feed_dict={X:test_x_data_norm})))

 

#     accuracy   0.93     12600


 

 

 

 

# Tensorflow 2.6 버전 구현!!

 

import numpy as np

import pandas as pd

import tensorflow as tf

from tensorflow.keras.models import Sequential

from tensorflow.keras.layers import Flatten, Dense, Conv2D    # Conv2D → convolution처리 layer

from tensorflow.keras.layers import MaxPooling2D, Dropout   # pooling, dropout 기능 추가  

from tensorflow.keras.optimizers import Adam

from sklearn.model_selection import train_test_split

from sklearn.preprocessing import MinMaxScaler

 

 

# Raw Data Loading

df = pd.read_csv('/content/drive/MyDrive/9월 14일/mnist/train.csv')

display(df) # 42000 rows × 785 columns(이미지 42000장)

# label은 이미지의 숫자, pixel은 1차원으로 되어있는 이미지 data 

# MNIST는 각 이미지를 2차원으로 된 흑백 이미지를 1차원으로 표현

# MNIST는 결측치와 이상치는 존재하지 않아요!

 

# 이미지 Data 확인(matplotlib을 이용해서 그림을 그려보아요 - 10장)

img_data = df.drop('label', axis=1, inplace=False).values # numpy array만 뽑아내요. 2차원

 

print(img_data)




# 데이터 전처리(Data preprocessing)

 

# Data split(Train Data와 Test Data 분리)

train_x_data, test_x_data, train_t_data, test_t_data = \

train_test_split(df.drop('label', axis=1, inplace=False), # x data

                 df['label'],

                 test_size=0.3,

                 stratify=df['label'],

                 random_state=0)

 

# MinMaxScaler를 이용한 Nimalization(정규화)

scaler = MinMaxScaler()

scaler.fit(train_x_data)

train_x_data_norm = scaler.transform(train_x_data)

test_x_data_norm = scaler.transform(test_x_data)

 

# Tensorflow 2.6 버전을 이용하기 때문에 Label에 대한 one-hot encoding처리가 필요 없어요!  sparse사용 

# 여기까지 비슷

 

# Keras를 이용한 구현

 

model = Sequential()    # model box생성

 

# input layer 없이 바로 첫 번째 convolution layer 넣을 거예요.

model.add(Conv2D(filters=32,   # 아까와 다르게 간단히 filter숫자만~

                 kernel_size=(3,3),  # filter의 size와 같은 의미 

                 activation='relu',

                 input_shape=(28,28,1)))  # 초기에 입력으로 들어온 input data(xdata)의 shape  

    

model.add(MaxPooling2D(pool_size=(2,2)))  # 2x2 size pooling, stride는 kernel_size와 동일하게 자동으로 잡혀요.

 

# 두 번째 convolution layer 설정

model.add(Conv2D(filters=64,

                 kernel_size=(3,3),    # filter의 size,  padding 설정도 알아서 정해줘서 안 해줘도 돼요!

                 activation='relu'))   # 입력이 아니라 넘어오니까 input shape 필요하지 않아요.

 

model.add(MaxPooling2D(pool_size=(2,2))) # pooling 4차원 데이터

 

model.add(Flatten())              # 1차원으로 변환

model.add(Dropout(rate=0.5)) # overfitting 방지, 데이터의 50%만 사용 

model.add(Dense(units=256 # hidden layer 

                activation='relu'))

model.add(Dense(units=10,    # outputlayer 

                activation='softmax'))  

 

print(model.summary())         # 자동으로 정리해서 알려줘요.

 

# model.add(Flatten()) # 4차원 데이터를 1차원으로

model.compile(optimizer=Adam(learning_rate=1e-3),

              loss='sparse_categorical_crossentropy',  # 이진 분류일 때는 binary, multinomial은 categorical

              metrics=['accuracy'])

 

history = model.fit(train_x_data_norm.reshape(-1,28,28,1), #2차원 데이터를 4차원으로 reshape

                    train_t_data,

                    epochs=200,

                    batch_size=100,

                    verbose=1,

                    validation_split=0.3)

 

model.evaluate(test_x_data_norm.reshape(-1,28,28,1), test_t_data)

 

#loss: 0.1396 - accuracy: 0.9895

#[0.13955789804458618, 0.9895238280296326]

 

LIST

'머신러닝 딥러닝' 카테고리의 다른 글

0930 cat&dog CNN CSV파일  (0) 2021.09.30
0929 Dogs vs Cats csv 파일로 저장  (0) 2021.09.29
0927 CNN(Convolution Neural Network)  (0) 2021.09.27
0917 CNN 합성곱 신경망  (0) 2021.09.17
0917 Image 처리의 기본  (0) 2021.09.17

댓글