빅데이타 & 머신러닝/머신러닝

연예인 얼굴 인식 모델을 만들어보자 - #2. CNN 모델을 만들고 학습시켜 보자

Terry Cho 2017. 6. 15. 23:37

연예인 얼굴 인식 모델을 만들어보자

#2 CNN 모델을 만들고 학습 시켜보기

조대협 (http://bcho.tistroy.com)

선행 학습 자료

이 글은 딥러닝 컨볼루셔널 네트워크 (이하 CNN)을 이용하여 사람의 얼굴을 인식하는 모델을 만드는 튜토리얼이다. 이 글을 이해하기 위해서는 머신러닝과 컨볼루셔널 네트워크등에 대한 사전 지식이 필요한데, 사전 지식이 부족한 사람은 아래 글을 먼저 읽어보기를 추천한다.

 

머신러닝의 개요 http://bcho.tistory.com/1140

머신러닝의 기본 원리는 http://bcho.tistory.com/1139

이산 분류의 원리에 대해서는 http://bcho.tistory.com/1142

인공 신경망에 대한 개념은 http://bcho.tistory.com/1147

컨볼루셔널 네트워크에 대한 개념 http://bcho.tistory.com/1149

학습용 데이타 전처리 http://bcho.tistory.com/1176

학습용 데이타 전처리를 스케일링 하기 http://bcho.tistory.com/1177

손글씨를 CNN을 이용하여 인식하는 모델 만들기 http://bcho.tistory.com/1156

손글씨 인식 CNN 모델을 이용하여 숫자 인식 하기 http://bcho.tistory.com/1157

환경

본 예제는 텐서플로우 1.1과 파이썬 2.7 그리고 Jupyter 노트북 환경 및 구글 클라우드를 사용하여 개발되었다.

준비된 데이타

학습에 사용한 데이타는 96x96 사이즈의 얼굴 이미지로, 총 5명의 사진(안젤리나 졸리, 니콜키드만, 제시카 알바, 빅토리아 베컴,설현)을 이용하였으며, 인당 학습 데이타 40장 테스트 데이타 10장으로 총 250장의 얼굴 이미지를 사용하였다.

사전 데이타를 준비할때, 정면 얼굴을 사용하였으며, 얼굴 각도 변화 폭이 최대한 적은 이미지를 사용하였다. (참고 : https://www.slideshare.net/Byungwook/ss-76098082 ) 만약에 이 모델로 학습이 제대로 되지 않는다면 학습에 사용된 데이타가 적절하지 않은것이기 때문에 데이타를 정재해서 학습하기를 권장한다.

데이타 수집 및 정재 과정에 대한 내용은 http://bcho.tistory.com/1177 를 참고하기 바란다.

 

컨볼루셔널 네트워크 모델

얼굴 인식을 위해서, 머신러닝 모델 중 이미지 인식에 탁월한 성능을 보이는 CNN 모델을 사용하였다. 테스트용 모델이기 때문에 모델은 복잡하지 않게 설계하였다.

 

학습과 예측에 사용되는 이미지는 96x96픽셀의 RGB 컬러 이미지를 사용하였다.

아래 그림과 같은 모델을 사용했는데, 총 4개의 Convolutional 계층과, 2개의 Fully connected 계층, 하나의 Dropout 계층을 사용하였다.


Convolutional 계층의 크기는 각각 16,32,64,128개를 사용하였고, 사용된 Convolutional 필터의 사이즈는 3x3 이다.

Fully connected 계층은 각각 512, 1024를 사용하였고 Dropout 계층에서는 Keep_prob값을 0.7로 둬서 30%의 뉴론이 drop out 되도록 하여 학습을 진행하였다.

 

학습 결과 5개의 카테고리에 대해서 총 200장의 이미지로 맥북 프로 i7 CPU 기준 7000 스텝정도의 학습을 진행한 결과 테스트 정확도 기준 90% 정도의 정확도를 얻을 수 있었다.

코드 설명

텐서플로우로 구현된 코드를 살펴보자

파일에서 데이타 읽기

먼저 학습 데이타를 읽어오는 부분이다.

학습과 테스트에서 읽어드리는 데이타의 포맷은 다음과 같다

 

/Users/terrycho/training_data_class5_40/validate/s1.jpg,Sulhyun,3

이미지 파일 경로, 사람 이름 , 숫자 라벨

 

파일에서 데이타를 읽어서 처리 하는 함수는 read_data_batch(), read_data(), get_input_queue()  세가지 함수가 사용된다.

  • get_input_queue() 함수는 CSV 파일을 한줄씩 읽어서, 파일 경로 및 숫자 라벨 두가지를 리턴할 수 있는 큐를 만들어서 리턴한다.

  • read_data() 함수는 get_input_queue()에서 리턴한 큐로 부터 데이타를 하나씩 읽어서 리턴한다.

  • read_batch_data()함수는 read_data() 함수를 이용하여, 데이타를 읽어서 일정 단위(배치)로 묶어서 리턴을 하고, 그 과정에서 이미지 데이타를 뻥튀기 하는 작업을 한다.

즉 호출 구조는 다음과 같다.

 

read_batch_data():

 → Queue = get_input_queue()

 → image,label = read_data(Queue)

 → image_data = 이미지 데이타 뻥튀기

Return image_data,label

 

실제 코드를 보자

get_input_queue

get_input_queue() 함수는 CSV 파일을 읽어서 image와 labels을 리턴하는 input queue를 만들어서 리턴하는 함수이다.

 

def get_input_queue(csv_file_name,num_epochs = None):

   train_images = []

   train_labels = []

   for line in open(csv_file_name,'r'):

       cols = re.split(',|\n',line)

       train_images.append(cols[0])

       # 3rd column is label and needs to be converted to int type

       train_labels.append(int(cols[2]) )

                           

   input_queue = tf.train.slice_input_producer([train_images,train_labels],

                                              num_epochs = num_epochs,shuffle = True)

   

   return input_queue

 

CSV 파일을 순차적으로 읽은 후에, train_images와 train_labels라는 배열에 넣은 다음 tf.train.slice_input_producer를 이용하여 큐를 만들어냈다. 이때 중요한 점은 shuffle=True라는 옵션을 준것인데, 만약에 이 옵션을 주지 않으면, 학습 데이타를 큐에서 읽을때 CSV에서 읽은 순차적으로 데이타를 리턴한다. 즉 현재 데이타 포맷은 Jessica Alba가 40개, Jolie 가 40개, Nicole Kidman이 40개 .. 식으로 순서대로 들어가 있기 때문에, Jessica Alba를 40개 리턴한 후 Jolie를 40개 리턴하는 식이 된다.  이럴 경우 Convolutional 네트워크가 Jessica Alba에 치우쳐지기 때문에 제대로 학습이 되지 않는다. Shuffle은 필수이다.

read_data()

input_queue에서 데이타를 읽는 부분인데 특이한 점은 input_queue에서 읽어드린 이미지 파일명의 파일을 읽어서 데이타 객체로 저장해야 한다. 텐서플로우에서는 tf.image.decode_jpeg, tf.image.decode_png 등을 이용하여 이러한 기능을 제공한다.

def read_data(input_queue):

   image_file = input_queue[0]

   label = input_queue[1]

   

   image =  tf.image.decode_jpeg(tf.read_file(image_file),channels=FLAGS.image_color)

   

   return image,label,image_file

read_data_batch()

마지막으로 read_data_batch() 함수 부분이다.get_input_queue에서 읽은 큐를 가지고 read_data함수에 넣어서 이미지 데이타와 라벨을 읽어서 리턴하는 값을 받아서 일정 단위로 (배치) 묶어서 리턴하는 함수이다. 중요한 부분이 데이타를 뻥튀기 하는 부분이 있다.

이 모델에서 학습 데이타가 클래스당 40개 밖에 되지 않기 때문에 학습데이타가 부족하다. 그래서 여기서 사용한 방법은 read_data에서 리턴된 이미지 데이타에 대해서 tf.image.random_xx 함수를 이용하여 좌우를 바꾸거나, brightness,contrast,hue,saturation 함수를 이용하여 매번 색을 바꿔서 리턴하도록 하였다.

 

def read_data_batch(csv_file_name,batch_size=FLAGS.batch_size):

   input_queue = get_input_queue(csv_file_name)

   image,label,file_name= read_data(input_queue)

   image = tf.reshape(image,[FLAGS.image_size,FLAGS.image_size,FLAGS.image_color])

   

   # random image

   image = tf.image.random_flip_left_right(image)

   image = tf.image.random_brightness(image,max_delta=0.5)

   image = tf.image.random_contrast(image,lower=0.2,upper=2.0)

   image = tf.image.random_hue(image,max_delta=0.08)

   image = tf.image.random_saturation(image,lower=0.2,upper=2.0)

   

   batch_image,batch_label,batch_file = tf.train.batch([image,label,file_name],batch_size=batch_size)

   #,enqueue_many=True)

   batch_file = tf.reshape(batch_file,[batch_size,1])

 

   batch_label_on_hot=tf.one_hot(tf.to_int64(batch_label),

       FLAGS.num_classes, on_value=1.0, off_value=0.0)

   return batch_image,batch_label_on_hot,batch_file

 

그리고 마지막 부분에 label을 tf.one_hot을 이용해서 변환한것을 볼 수 있는데, 입력된 label은 0,1,2,3,4 과 같은 단일 정수이다. 그런데, CNN에서 나오는 결과는 정수가 아니라 클래스가 5개인 (분류하는 사람이 5명이기 때문에) 행렬이다. 즉 Jessica Alba일 가능성이 90%이고, Jolie일 가능성이 10%이면 결과는 [0.9,0.1,0,0,0] 식으로 리턴이 되기 때문에, 입력된 라벨 0은 [1,0,0,0,0], 라벨 1은 [0,1,0,0,0] 라벨 2는 [0,0,1,0,0] 식으로 변환되어야 한다. tf.one_hot 이라는 함수가 이 기능을 수행해준다.

 

모델 코드

모델은 앞서 설명했듯이 4개의 Convolutional 계층과, 2개의 Fully connected 계층 그리고 Dropout 계층을 사용한다. 각각의 계층별로는 코드가 다르지 않고 인지만 다르니 하나씩 만 설명하도록 한다.

 

Convolutional 계층

아래 코드는 두번째 Convolutional 계층의 코드이다.

  • FLAGS.conv2_layer_size 는 이 Convolutional 계층의 뉴런의 수로 32개를 사용한다.

  • FLAGS.conv2_filter_size 는 필터 사이즈를 지정하는데, 3x3 을 사용한다.

  • FLAGS.stride2 = 1 는 필터의 이동 속도로 한칸씩 이동하도록 정의했다.

 

# convolutional network layer 2

def conv2(input_data):

   FLAGS.conv2_filter_size = 3

   FLAGS.conv2_layer_size = 32

   FLAGS.stride2 = 1

   

   with tf.name_scope('conv_2'):

       W_conv2 = tf.Variable(tf.truncated_normal(

                       [FLAGS.conv2_filter_size,FLAGS.conv2_filter_size,FLAGS.conv1_layer_size,FLAGS.conv2_layer_size],

                                             stddev=0.1))

       b2 = tf.Variable(tf.truncated_normal(

                       [FLAGS.conv2_layer_size],stddev=0.1))

       h_conv2 = tf.nn.conv2d(input_data,W_conv2,strides=[1,1,1,1],padding='SAME')

       h_conv2_relu = tf.nn.relu(tf.add(h_conv2,b2))

       h_conv2_maxpool = tf.nn.max_pool(h_conv2_relu

                                       ,ksize=[1,2,2,1]

                                       ,strides=[1,2,2,1],padding='SAME')

       

       

   return h_conv2_maxpool

 

다음 Weight 값 W_conv2 와 Bias 값 b2를 지정한후에, 간단하게 tf.nn.conv2d 함수를 이용하면 2차원의 Convolutional 네트워크를 정의해준다. 다음 결과가 나오면 이 결과를 액티베이션 함수인 relu 함수에 넣은 후에, 마지막으로 max pooling 을 이용하여 결과를 뽑아낸다.

 

각 값의 의미에 대해서는 http://bcho.tistory.com/1149 의 컨볼루셔널 네트워크 개념 글을 참고하기 바란다.

같은 방법으로 총 4개의 Convolutional 계층을 중첩한다.

 

Fully Connected 계층

앞서 정의한 4개의 Convolutional 계층을 통과하면 다음 두개의 Fully Connected 계층을 통과하게 되는데 모양은 다음과 같다.

  • FLAGS.fc1_layer_size = 512 를 통하여 Fully connected 계층의 뉴런 수를 512개로 지정하였다.

 

# fully connected layer 1

def fc1(input_data):

   input_layer_size = 6*6*FLAGS.conv4_layer_size

   FLAGS.fc1_layer_size = 512

   

   with tf.name_scope('fc_1'):

       # 앞에서 입력받은 다차원 텐서를 fcc에 넣기 위해서 1차원으로 피는 작업

       input_data_reshape = tf.reshape(input_data, [-1, input_layer_size])

       W_fc1 = tf.Variable(tf.truncated_normal([input_layer_size,FLAGS.fc1_layer_size],stddev=0.1))

       b_fc1 = tf.Variable(tf.truncated_normal(

                       [FLAGS.fc1_layer_size],stddev=0.1))

       h_fc1 = tf.add(tf.matmul(input_data_reshape,W_fc1) , b_fc1) # h_fc1 = input_data*W_fc1 + b_fc1

       h_fc1_relu = tf.nn.relu(h_fc1)

   

   return h_fc1_relu

 

Fully connected 계층은 단순하게 relu(W*x + b) 함수이기 때문에 이 함수를 위와 같이 그대로 적용하였다.

마지막 계층

Fully connected 계층을 거쳐 나온 데이타는 Dropout 계층을 거친후에, 5개의 카테고리에 대한 확률로 결과를 내기 위해서 final_out 계층을 거치게 되는데, 이 과정에서 softmax 함수를 사용해야 하나, 학습 과정에서는 별도로 softmax 함수를 사용하지 않는다. softmax는 나온 결과의 합이 1.0이 되도록 값을 변환해주는 것인데, 학습 과정에서는 5개의 결과 값이 어떤 값이 나오던 가장 큰 값에 해당하는 것이 예측된 값이기 때문에, 그 값과 입력된 라벨을 비교하면 되기 때문이다.

즉 예를 들어 Jessica Alba일 확률이 100%면 실제 예측에서는 [1,0,0,0,0] 식으로 결과가 나와야 되지만, 학습 중는 Jessica Alaba 로 예측이 되었다고만 알면 되기 때문에 결과가 [1292,-0.221,-0.221,-0.221] 식으로 나오더라도 최대값만 찾으면 되기 때문에 별도로 softmax 함수를 적용할 필요가 없다. Softmax 함수는 연산 비용이 큰 함수이기 때문에 일반적으로 학습 단계에서는 적용하지 않는다.

 

마지막 계층의 코드는 다음과 같다.

# final layer

def final_out(input_data):

 

   with tf.name_scope('final_out'):

       W_fo = tf.Variable(tf.truncated_normal([FLAGS.fc2_layer_size,FLAGS.num_classes],stddev=0.1))

       b_fo = tf.Variable(tf.truncated_normal(

                       [FLAGS.num_classes],stddev=0.1))

       h_fo = tf.add(tf.matmul(input_data,W_fo) , b_fo) # h_fc1 = input_data*W_fc1 + b_fc1

       

   # 최종 레이어에 softmax 함수는 적용하지 않았다.

       

   return h_fo

전체 네트워크 모델 정의

이제 각 CNN의 각 계층을 함수로 정의 하였으면 각 계층을 묶어 보도록 하자. 묶는 법은 간단하다 앞 계층에서 나온 계층을 순서대로 배열하고 앞에서 나온 결과를 뒤의 계층에 넣는 식으로 묶으면 된다.

 

# build cnn_graph

def build_model(images,keep_prob):

   # define CNN network graph

   # output shape will be (*,48,48,16)

   r_cnn1 = conv1(images) # convolutional layer 1

   print ("shape after cnn1 ",r_cnn1.get_shape())

   

   # output shape will be (*,24,24,32)

   r_cnn2 = conv2(r_cnn1) # convolutional layer 2

   print ("shape after cnn2 :",r_cnn2.get_shape() )

   

   # output shape will be (*,12,12,64)

   r_cnn3 = conv3(r_cnn2) # convolutional layer 3

   print ("shape after cnn3 :",r_cnn3.get_shape() )

 

   # output shape will be (*,6,6,128)

   r_cnn4 = conv4(r_cnn3) # convolutional layer 4

   print ("shape after cnn4 :",r_cnn4.get_shape() )

   

   # fully connected layer 1

   r_fc1 = fc1(r_cnn4)

   print ("shape after fc1 :",r_fc1.get_shape() )

 

   # fully connected layer2

   r_fc2 = fc2(r_fc1)

   print ("shape after fc2 :",r_fc2.get_shape() )

   

   ## drop out

   # 참고 http://stackoverflow.com/questions/34597316/why-input-is-scaled-in-tf-nn-dropout-in-tensorflow

   # 트레이닝시에는 keep_prob < 1.0 , Test 시에는 1.0으로 한다.

   r_dropout = tf.nn.dropout(r_fc2,keep_prob)

   print ("shape after dropout :",r_dropout.get_shape() )

   

   # final layer

   r_out = final_out(r_dropout)

   print ("shape after final layer :",r_out.get_shape() )

 

   return r_out

 

이 build_model 함수는 image 를 입력 값으로 받아서 어떤 카테고리에 속할지를 리턴하는 컨볼루셔널 네트워크이다.  중간에 Dropout 계층이 추가되어 있는데, tf.nn.dropout함수를 이용하면 간단하게 dropout 계층을 구현할 수 있다. r_fc2는 Dropout 계층 앞의 Fully Connected 계층에서 나온 값이고,  두번째 인자로 남긴 keep_prob는 Dropout 비율이다.

 

   r_dropout = tf.nn.dropout(r_fc2,keep_prob)

   print ("shape after dropout :",r_dropout.get_shape() )

 

모델 학습

데이타를 읽는 부분과 학습용 모델 정의가 끝났으면 실제로 학습을 시켜보자

 

def main(argv=None):

   

   # define placeholders for image data & label for traning dataset

   

   images = tf.placeholder(tf.float32,[None,FLAGS.image_size,FLAGS.image_size,FLAGS.image_color])

   labels = tf.placeholder(tf.int32,[None,FLAGS.num_classes])

   image_batch,label_batch,file_batch = read_data_batch(TRAINING_FILE)

 

먼저 학습용 모델에 넣기 위한 image 데이타를 읽어드릴 placeholder를 images로 정의하고, 다음으로 모델에 의해 계산된 결과와 비교하기 위해서 학습데이타에서 읽어드린 label 데이타를 저장하기 위한 placeholder를 labels로 정의한다. 다음 image_batch,label_batch,fle_batch 변수에 배치로 학습용 데이타를 읽어드린다. 그리고 dropout 계층에서 dropout 비율을 지정할 keep_prob를 place holder로 정의한다.

각 변수가 지정되었으면, build_model 함수를 호출하여, images 값과 keep_prob 값을 넘겨서 Convolutional 네트워크에 값을 넣도록 그래프를 정의하고 그 결과 값을 prediction으로 정의한다.

 

   keep_prob = tf.placeholder(tf.float32) # dropout ratio

   prediction = build_model(images,keep_prob)

   # define loss function

   loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction,labels=labels))

   tf.summary.scalar('loss',loss)

 

   #define optimizer

   optimizer = tf.train.AdamOptimizer(FLAGS.learning_rate)

   train = optimizer.minimize(loss)

 

중간 중간에 학습 과정을 시각화 하기 위해서 tf.summary.scalar 함수를 이용하여 loss 값을 저장하였다.

 

그래프 생성이 완료 되었으면, 학습에서 계산할 비용 함수를 정의한다. 비용함수는 sofrmax cross entopy 함수를 이용하여, 모델에 의해서 예측된 값 prediction 과, 학습 파일에서 읽어드린 label 값을 비교하여 loss 값에 저장한다.

그리고 이 비용 최적화 함수를 위해서 옵티마이져를 AdamOptimizer를 정의하여, loss 값을 최적화 하도록 하였다.

 

학습용 모델 정의와, 비용 함수, 옵티마이저 정의가 끝났으면 학습 중간 중간 학습된 모델을 테스트하기 위한 Validation 관련 항목등을 정의한다.

 

   # for validation

   #with tf.name_scope("prediction"):

   validate_image_batch,validate_label_batch,validate_file_batch = read_data_batch(VALIDATION_FILE)

   label_max = tf.argmax(labels,1)

   pre_max = tf.argmax(prediction,1)

   correct_pred = tf.equal(tf.argmax(prediction,1),tf.argmax(labels,1))

   accuracy = tf.reduce_mean(tf.cast(correct_pred,tf.float32))

           

   tf.summary.scalar('accuracy',accuracy)

      

   startTime = datetime.now()

 

학습용 데이타가 아니라 검증용 데이타를 VALIDATION_FILE에서 읽어서 데이타를 validate_image_batch,validate_label_batch,validate_file_batch에 저장한다. 다음, 정확도 체크를 위해서 학습에서 예측된 라벨값과, 학습 데이타용 라벨값을 비교하여 같은지 틀린지를 비교하고, 이를 가지고 평균을 내서 정확도 (accuracy)로 사용한다.

 

학습용 모델과, 테스트용 데이타 등이 준비되었으면 이제 학습을 시작한다.

학습을 시직하기 전에, 학습된 모델을 저장하기 위해서 tf.train.Saver()를 지정한다. 그리고, 그래프로 loss와 accuracy등을 저장하기 위해서 Summary write를 저장한다.

다음 tf.global_variable_initializer()를 수행하여 변수를 초기화 하고, queue에서 데이타를 읽기 위해서 tf.train.Corrdinator를 선언하고 tf.start_queue_runners를 지정하여, queue 러너를 실행한다.

 

   #build the summary tensor based on the tF collection of Summaries

   summary = tf.summary.merge_all()

   

   with tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)) as sess:

       saver = tf.train.Saver() # create saver to store training model into file

       summary_writer = tf.summary.FileWriter(FLAGS.log_dir,sess.graph)

       

       init_op = tf.global_variables_initializer() # use this for tensorflow 0.12rc0

       coord = tf.train.Coordinator()

       threads = tf.train.start_queue_runners(sess=sess, coord=coord)

       sess.run(init_op)

 

변수 초기화와 세션이 준비되었기 때문에 이제 학습을 시작해보자. for 루프를 이용하여 총 10,000 스텝의 학습을 하도록 하였다.

 

       for i in range(10000):

           images_,labels_ = sess.run([image_batch,label_batch])

 

다음 image_batch와 label_batch에서 값을 읽어서 앞에서 정의한 모델에 넣고 train 그래프 (AdamOptimizer를 정의한)를 실행한다.

 

           sess.run(train,feed_dict={images:images_,labels:labels_,keep_prob:0.7})

 

이때 앞에서 읽은 images_와, labels_ 데이타를 피딩하고 keep_prob 값을 0.7로 하여 30% 정도의 값을 Dropout 시킨다.

 

다음 10 스텝 마다 학습 상태를 체크하도록 하였다.

           

           if i % 10 == 0:

               now = datetime.now()-startTime

               print('## time:',now,' steps:',i)         

               

               # print out training status

               rt = sess.run([label_max,pre_max,loss,accuracy],feed_dict={images:images_

                                                         , labels:labels_

                                                         , keep_prob:1.0})

               print ('Prediction loss:',rt[2],' accuracy:',rt[3])

위와 같이 loss 값과 accuracy 값을 받아서 출력하여 현재 모델의 비용 함수 값과 정확도를 측정하고

 

               # validation steps

               validate_images_,validate_labels_ = sess.run([validate_image_batch,validate_label_batch])

               rv = sess.run([label_max,pre_max,loss,accuracy],feed_dict={images:validate_images_

                                                         , labels:validate_labels_

                                                         , keep_prob:1.0})

               print ('Validation loss:',rv[2],' accuracy:',rv[3])

학습용 데이타가 아니라 위와 같이 테스트용 데이타를 피딩하여, 테스트용 데이타로 정확도를 검증한다. 이때 keep_prob를 1.0으로 해서 Dropout 없이 100% 네트워크를 활용한다.

 

               if(rv[3] > 0.9):

                   Break

 

만약에 테스트 정확도가 90% 이상이면 학습을 멈춘다. 그리고 아래와 같이 Summary

 

               # validation accuracy

               summary_str = sess.run(summary,feed_dict={images:validate_images_

                                                         , labels:validate_labels_

                                                         , keep_prob:1.0})

 

               summary_writer.add_summary(summary_str,i)

               summary_writer.flush()

 

마지막으로 다음과 같이 학습이 다된 모델을 saver.save를 이용하여 저장하고, 사용된 리소스들을 정리한다.

       saver.save(sess, 'face_recog') # save session

       coord.request_stop()

       coord.join(threads)

       print('finish')

   

main()

 

이렇게 학습을 끝내면 본인의 경우 약 7000 스텝에서 테스트 정확도 91%로 끝난것을 확인할 수 있다.

 

아래는 텐서보드를 이용하여 학습 과정을 시각화한 내용이다.

 


 

코드는 공개가 가능하지만 학습에 사용한 데이타는 저작권 문제로 공유가 불가능하다. 약 200장의 사진만 제대로 수집을 하면 되기 때문에 각자 수집을 해서 학습을 도전해보는 것을 권장한다. (더 많은 인물에 대한 시도를 해보는것도 좋겠다.)

정리 하며

혹시나 이 튜토리얼을 따라하면서 학습 데이타를 공개할 수 있는 분들이 있다면 다른 분들에게도 많은 도움이 될것이라고 생각한다. 가능하면 데이타가 공개되었으면 좋겠다.

전체 코드는 https://github.com/bwcho75/facerecognition/blob/master/1.%2BFace%2BRecognition%2BTraining.ipynb 에 있다.

그리고 직접 사진을 수집해보면, 데이타 수집 및 가공이 얼마나 어려운지 알 수 있기 때문에 직접 한번 시도해보는 것도 권장한다. 아래는 크롬브라우져 플러그인으로 구글 검색에서 나온 이미지를 싹 긁을 수 있는 플러그인이다. Bulk Download Images (ZIG)

https://www.youtube.com/watch?v=k5ioaelzEBM

 



이 플러그인을 이용하면 손쉽게 특정 인물의 데이타를 수집할 수 있다.

다음 글에서는 학습이 끝난 데이타를 이용해서 실제로 예측을 해보는 부분에 대해서 소개하도록 하겠다.

 

 

 

그리드형