※ 2 자리수 숫자 CNN 분류기
%tensorflow_version 2.x
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
x, y = load_digits(return_X_y=True)
x.shape, y.shape, set(y)
→ ((1797, 64), (1797,), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
▶ 학습데이터와 테스트 데이터를 나눕니다
m = len(y)//2
x_train = x[:m]
y_train = y[:m]
x_test = x[m:m*2]
y_test = y[m:m*2]
x_train = np.reshape(x_train, [-1, 8, 8])
x_test = np.reshape(x_test, [-1, 8, 8])
x_train.shape
→ (898, 8, 8)
▶ 이미지를 2개씩 좌우로 붙여서 합성데이터를 만듭니다
x_train_l, x_train_r = np.split(x_train, 2, axis=0) # 데이터를 반 나눕니다
x_train_lr = np.concatenate((x_train_l, x_train_r), axis=2) # 두 데이터를 좌우로 붙입니다
x_test_l, x_test_r = np.split(x_test, 2, axis=0)
x_test_lr = np.concatenate((x_test_l, x_test_r), axis=2)
y_train = np.reshape(y_train, [-1, 2])
y_test = np.reshape(y_test, [-1, 2])
x_train_lr.shape, x_test_lr.shape, y_train.shape, y_test.shape
→ ((449, 8, 16), (449, 8, 16), (449, 2), (449, 2))
class MyModel(keras.Model): # github.com/sogangori/choongang20/
def __init__(self):
super(MyModel, self).__init__()
self.k = 10 # 클래스 갯수
self.seq = 2 # 자릿수
self.opt = tf.optimizers.SGD(learning_rate=0.01)#Stochatic Gradient Descent 확률적 경사 하강
self.conv0 = keras.layers.Conv2D(16, [3,3], padding='same', activation=keras.activations.relu)
self.conv1 = keras.layers.Conv2D(32, [3,3], padding='same', activation=keras.activations.relu)
self.pool0 = keras.layers.MaxPool2D([2,2], padding='same')
self.pool1 = keras.layers.MaxPool2D([2,2], padding='same')
self.flatten = keras.layers.Flatten()
self.dense = keras.layers.Dense(units=self.k * self.seq)
def call(self, x):
#x (1797, 64)
x_4d = tf.reshape(x, [-1,8,8*2,1])
x_4d = tf.cast(x_4d, tf.float32)
net = self.conv0(x_4d)
net = self.pool0(net)
net = self.conv1(net)
net = self.pool1(net)
net = self.flatten(net)
h = self.dense(net)
h = tf.reshape(h, [-1, self.seq, self.k]) # 2:두자리수, 10:10개의 클래스
h = tf.nn.softmax(h, axis=2)
return h
def get_loss(self, y, h):
#학습할때 nan이 발생하는 경우 값을 clip(자르다) (최소값, 최대값)
h = tf.clip_by_value(h, 1e-8, 1 - 1e-8) # h 가 0이나 1이 되지 않도록 하는 안전장치
cross_entropy = - (y * tf.math.log(h) + (1 - y) * tf.math.log(1 - h))
loss = tf.reduce_mean(cross_entropy)
return loss
def get_accuracy(self, y, h):
predict = tf.argmax(h, -1)
is_equal = tf.equal(y, predict)
self.acc = tf.reduce_mean(tf.cast(is_equal, tf.float32)) # True > 1, False > 0 로 cast
self.acc_all = tf.reduce_mean(tf.cast(tf.reduce_all(is_equal, axis=1), tf.float32))
def fit(self, x, y, epoch=1):
# x : (m, 8, 16), y: (m, 2)
y_hot = tf.one_hot(y, depth=self.k, axis=-1)#(m, 2, 10)
for i in range(epoch):
with tf.GradientTape() as tape: #경사 기록 장치
h = self.call(x)
loss = self.get_loss(y_hot, h)
grads = tape.gradient(loss, self.trainable_variables) #경사 계산
self.opt.apply_gradients(zip(grads, self.trainable_variables)) # 가중치에서 경사를 빼기
self.get_accuracy(y, h)
if i%10==0:
print('%d/%d loss:%.3f acc:%.3f acc_all:%.3f'%(i, epoch, loss, self.acc, self.acc_all))
model = MyModel()
* x_4d = tf.cast(x_4d, tf.float32)
- tf.cast() : 새로운 형태로 캐스팅하는데 사용한다. 부동소수점형에서 정수형으로 바꾼 경우 소수점 버린다.
Boolean형태인 경우 True이면 1, False이면 0을 출력한다.
model.fit(x_train_lr, y_train, epoch=10) #학습
0/10000 loss:0.179 acc:0.714 acc_all:0.528
10/10000 loss:0.178 acc:0.717 acc_all:0.532
20/10000 loss:0.178 acc:0.720 acc_all:0.537
30/10000 loss:0.177 acc:0.723 acc_all:0.537
7340/10000 loss:0.010 acc:1.000 acc_all:1.000
7350/10000 loss:0.010 acc:1.000 acc_all:1.000
7360/10000 loss:0.010 acc:1.000 acc_all:1.000
# 테스트셋의 성능
h = model(x_test)
model.get_accuracy(y_test, h)
print('개별정확도',model.acc.numpy(),'두자리 모두 맞춘 정확도', model.acc_all.numpy())
'Deep learning > Code' 카테고리의 다른 글
rnn_2_number_with_blank (0) | 2020.01.30 |
---|---|
cnn_2_number_with_blank (0) | 2020.01.30 |
keras_cnn_expert (0) | 2020.01.30 |
keras_logistic_regression_expert (0) | 2020.01.29 |
gradientDescent (0) | 2020.01.29 |