Android 6.0 이상에서의 백그라운드 서비스 생존 방법에 대한 고찰


개요

문제점

  • Android 6.0에서의 Doze 모드
  • 삼성 스마트 매니저

전략

  • Wakelock
  • Alarm Manager 방식
  • Google Cloud Message 방식
  • Screen ON

화면 항상 켜기

<RelativeLayout 
    ....
    android:keepScreenOn="true"
    ....
>
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

Wakelock

<uses-permission android:name="android.permission.WAKE_LOCK" />

Wakelock 생성 방법

PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
        "MyWakelockTag");
wakeLock.acquire();

Broadcast Receiver Wakelok 방법

 WakefulBroadcastReceiver,

https://developer.android.com/training/scheduling/wakelock.html#wakeful

Scheduling Repeating Alarms

삼성 스마트 매니저 극복 방법

http://gun0912.tistory.com/64
http://blog.soundl.ly/2016/04/blog-post_12.html


SQLite 탐색기 및 기초 쿼리


sqlite-browser는 쉽게 구할 수 있지만 생각보다 query 실행시 멈침 현상이 심하다.
검색 도중 python flask로 구현된 web-based 도구가 있어서 이것을 사용 한다.
sqlite-web이며 설치화 실행 모습은 아래와 같다.

sqlite-web tool

$ sudo apt-get install python-pip ## pip를 이미 설치하였다면 skip 합니다.
$ sudo pip install --upgrade pip  ## pip 업그레이드 합니다.
$ sudo apt install python-setuptools ## sqlite-web설치를 위해 필요
$ sudo pip install sqlite-web     ## sqlite-web 설치 합니다.
## 포트번호 3000(기본값은 8080)으로 현재 디렉토리의 test.db 파일을 웹으로 보고싶은 경우 아래 명령과 같이 입력합니다.
## 만약 test.db파일이 없으면 새로 생성하게 됩니다.
$ sqlite_web -p 3000 ./test.db ## 하이픈 아니고 언더바임에 주의하세요. 
 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
 127.0.0.1 - - [20/Sep/2016 16:25:24] "GET / HTTP/1.1" 200 -
 Created new window in existing browser session.

실행

select

select * from log oder by _id desc limit 20
SELECT Region, count(*)
FROM item
WHERE Region is not null
GROUP BY Region

실행 결과

Region, count
Denmark, 4
Sweden, 1
USA, 10

하위 20개 선택

SELECT *
FROM "log"
order by _id DESC limit 20



postman 크롬 앱

https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en


사용법

https://stackoverflow.com/questions/16015548/tool-for-sending-multipart-form-data-request


- 멀티 파트 전송도 가능하다. 

두 개 데이터 프레임 병합, 빼기


공통 키를 기준으로 병합

하나이상의 key를 가지고 두 개의 데이터 프레임을 join하는 연산

# merge two data frames by ID
total <- merge(data frameA,data frameB,by="ID")

두 개의 데이터로 병합

# merge two data frames by ID and Country
total <- merge(data frameA,data frameB,by=c("ID","Country"))

빼기

Larger - Smaller

small

     ID       CSF1PO CSF1PO.1 D10S1248 D10S1248.1 D12S391 D12S391.1
203079_BA_M     10       11       14         16      -9        -9
203079_BA_F      8       12       14         17      -9        -9
203080_BA_M     10       12       13         13      -9        -9

big

      ID      CSF1PO CSF1PO.1 D10S1248 D10S1248.1 D12S391 D12S391.1
203078_MG_M     -9       -9       15         15      18        20
203078_MG_F     -9       -9       14         15      17        19
203079_BA_M     10       11       14         16      -9        -9
203079_BA_F      8       12       14         17      -9        -9
203080_BA_M     10       12       13         13      -9        -9
203080_BA_F     10       11       14         16      -9        -9
203081_MG_M     10       12       14         16      -9        -9
203081_MG_F     11       12       15         16      -9        -9
203082_MG_M     11       11       13         15      -9        -9
203082_MG_F     11       11       13         14      -9        -9

Solution

BigDF[ !(BigDF$ID %in% SmallDF$ID), ]

'AI > R Basic' 카테고리의 다른 글

Jupyter Notebook으로 R 실행  (3) 2018.04.15
Data Transformation: dplyr package  (0) 2018.04.14
R Factor 검색  (0) 2017.11.13
RStudio v1.1 Released  (0) 2017.10.10
rJava load 에러  (0) 2017.04.16


  1. Powerpoint 2013을 실행합니다.
  2. [파일 - 옵션]을 클릭합니다.
  3. [언어 교정 – 자동 고침 옵션 – 자동 고침 – 자동으로 맞춤법 검사기의 추천 단어를 사용]체크를 해제합니다.


2018년에서 바라본 딥러닝의 비판적 평가


  • data hungry

    • 데이터가 매번 너무 많이 필요하다. 세상의 모든 문제를 무한한 데이터가 제공된다고 가정하고 풀 수는 없다.
  • shallow & limited capacity for transfer

    • deep은 아키텍쳐가 깊은거지 딥하게 이해 하는 것 아니다.
    • 너무 트레이닝에 오버핏 된다.
    • adversarial하게 학습 모델을 저격하는 데이터 예제를 쉽게 만들 수 있다.
  • no natural way to deal with hieracyhical strcture

    • 맨 마지막의 featrue는 너무 flatten 하다.
    • 지식은 계측정 관계가 있는 딥러닝은 그렇지 못하다
  • struggled with open-ended inference (개방향 문제에 약함)

    • VQA도 그냥 호랑이 어디, 호랑이는 몇마리? 이런건 잘함
    • open-ended inference란 이 호랑이들은 지금 이곳에 왜 있는가?와 같은 것들이다. 이러한 데이터 영역을 벗어난 오픈 문제들에는 너무나도 약하다.
  • not sufficiently transparent

    • dconv 등과 같은 기술이 있지만 여전히 충분히 이해할만큼 내부가 보이지 않는다.
  • not well intergrated with prior knowledge

    • 사전에 잘 정의된 지식으로 우리는 비행기도 만들고 우주산업도 발전 시켰다.
    • 하지만 딥러닝은 너무 data-driven이다. 이게 인간이 알 수 없었던 무언가를 보여주기도 하지만, 역설적으로 GAN으로 너무 밑 바닥 부터하면 너무 학습도 어렵고 나온 결과를 해석하기도 어렵다. 사전에 왜 잘 정의된 지식과 통합하지 못하는가?
    • GAN의 manipulation 하기도 쉽지 않다.
    • Physical world의 잘알려진 공식 f= ma를 만들자도 있지만, 그결과는
      • 영상으로 부터 가속도 등등을 약간 배운것이지 저 공식을 유도한 내용은 아니다.
  • not inherently distinguish causation from correlation

    • 상관 관계와 인과 관계를 구분 못한다.
      • 예: 알통이 굵으면 보수다.
  • presumes a largely stable world

    • 너무나도 불안정하다.
  • its answer often cannot be full trusted

    • 딥러닝이 낸 결과를 이해할 수 없기에 신뢰하기도 어렵다.
  • difficult to engineer with

    • 해본 사람은 알겠지만 디버깅도 어렵고 새로운 문제에 적용해서 쓸만하게 동작시키는 것도 쉽지 않다.

크게 나누면 두개

  • generalizability

    • 세상의 모든 문제를 infinite 데이터로만 학습 할 수는 없다.
  • Abstraction

    • 더 높은 지식으로 올라가지 못한다.

참고문헌


하드디스크 무료 소프트웨어 복구 방법


2012에 구매한 하디드스크를 쓰고 있다가 최근에 큰 봉변을 당했다.
DropBox GoogleDrive auto-sync를 켜둔 덕분에 간당 간당 HDD가 결국 인식불능에 도달 했다.

HDD는 2년이상이되면 급속도로 배드섹터가 많이 생기고 신뢰성이 떨어지는 장비인데 이것을 6년 가까이 사용했으니 당연한 일이다.

석박사기간 통틀어 6년간의 연구데이터가 몽땅 날라가 버리니 정말 후회가 몰려왔다. 가장 최근에 백업한 데이터가 2017.2월이니 난감한 상황이다.

어떻게든 공짜로 해결하려고 사투한 2주간의 기록을 남긴다.

  • 하드 인식이 안되면 외장 도커를 이용해서 연결
  • 절대로 포맷하지 않음
  • 문제가 생기면 Write는 무조건 중지

주의: 정말 소중한 데이터라면 전문가에게 돈을 주고 의뢰 하는게 맞습니다. 이 방법대로 했다가 데이터 날라가는것은 저는 책임지지 않습니다.

배드섹터 복구

아래 프로그램을 사용해서 복구 했다.
DRevitalize+2.42 (한글 버전)

DRevitalize+2.42.egg

연구실 후배가 설치해서 돌려준 프로그램이다.
실제 공식 홈페이지에가면 영문으로 더 상위 버전이 있긴하다.

아래와 같이 2TB 가량의 하드에 있는 배트섹터를 복구 했다.

실제론 약 3만개가 있었고 2주간 걸렸다.
배트섹터 없는 구간은 빠르지만 BAD Sector 구간에 오면 10KB이하로 속도가 급격히 떨어져서 시간이 엄청나게 오래 걸린다.

하지만 확실히 모두 고치고나면 인식이 된다.

파티션 복구

파티션까지 없어진 상황이면 이것도 복구해야 한다. 필자는 그랬다.

검색하면 도구가 많이 나온다. 하지만 대부분 Scan완료후 복구를 시도하면 10GB이상은 Pro구매 하라는 팝업창만 나온다.

정말 사람 답답한데 이런거 가지고 과금청구하는 악덕 프로그래머들에게 화가날 뿐이다.

검색 끝에 TestDisk라는 GPL라이센스를 따르는 도구를 찾았다.
역사가 오래된만큼 Windows, , uBuntu등의 다양한 OS를 지원 했다.

실제로도 상용툴에서 보여준 성능을 그대로 보여주었다.

TestDisk Dwonload
TestDisk 사용 공식 설명서

필자는 7.1 버전을 윈도우즈10 에서 사용하요 성공적으로 파티션을 복구 했다.

성공 로그
재부팅하면 파티션이 정상 복구되어 있다.

  • Partition Find and Mount
    • 무료지만 Full scan으로 검색하면 partition이 나오긴 하지만 Fragement되어서 나와서 신뢰가 안간다.


멀티 부팅 윈도우즈 설치 후 우분투 설치 (Install uBuntu alongside Windows10)


Legacy 설치 기준

  • 윈도우즈10과 Ubuntu LTS 16.04 기준
  • UEFI가 둘다 아니다.
  • legacy로 둘다 설치
  • 윈도우즈 10부터 먼저 설치한다.

BIOS 설정

  • Fast Boot를 disable
  • Secure Boot를 disable

윈도우즈 10을 Legacy BIOS에서 설치

쉽게 설치 가능

기존 파티선 축소 (윈도우즈10)

디스크 관리자에서 기존 운영체제가 설치된 파티션을 축소한다.
그래서 새 볼륨을 생성한다.

파티션 설정

우분투 설치 창에서 기타를 선택한다.
자동 옵션들은 잘 동작하지 않는다.

SWAP 파티션 생성

메모리 넉넉하면 생성안해도 된다고 한다.
그래도 일단 생성 했다.

아까 쭐인 남은공간을 선택해서 아래와 같이 설정해서 SWAP영역을 생성한다.
메모리 8GB여서 4GB 정도만 주었다.

루트 파티션 생성

이제 남은 모든 공간을 아래와 같이 루트 파티션으로 생성해 준다.

부트로더 설치경로 설정

아래와 같이 물리적 저장 장치들이 해당 이름으로 연결 되어 있다고 하면,

  • sda가 HDD
  • sdb가 SDD

기본적으로 부트로더를 설치할 장치가 보통 /dev/sda로 잡힌다.
sdb에 즉 SSD에 운영체제가 있다면 반드시 변경해서 /dev/sdb에 설치해야한다.

그렇지 않으면 부팅시에 GRUB 부트로더가 나오지 않는다.

정상적으로 설치되면 아래와 같이 GRUB으로 선택 화면이 나오게 된다.

요점은 windows boot loader가 설치된 곳에 ubuntu의 GRUB을 설치해야 멀티 부팅 화면이 제대로 나오게 된다. 추후 UEFI 모드가 활성화된 방법에서는 EFI 파티션이 보이므로 좀 더 쉽게 이 부분을 찾을 수 있다.

참고자료

UEFI 설치 기준 (Widnows10, ubuntu 둘다)

legacy 방법과 기본적으로 파티션 설정은 같다.
우분투설치시 기타선택 후에 아래의 스크린샷과 같이 GRUB이 설치될 위치를 윈도우즈 부트 매니저가 있는 EFI 파티션으로 선택해 준다.

partition

위 스샷에서 부트로더 설치할 장치는 EFI가 있는 nvme0n1p1이 된다. 이 부분을 잘 못 선택하면 EFI 경고가 뜨고 부팅이 제대로 이뤄지지 않을 수 있음을 알려 준다.

계속을 누르면 아래와 같이 설치가 진행 되고
설치

완료되어 재부팅하면 아래와 같이 나온다.
Ubuntu를 선택하면 우분투로 들어가며, Windows Boot Manager를 선택하면 윈도우즈로 접속 된다.
Grub

참고자료


몬슨 파워모니터 (Monsoon Power Monitor)를 이용한 일체형 배터리 타입의 스마트폰 전력 소모 측정


이전 포스트에서 Monsoon Power Monitor를 이용해서 전력 소모를 측정하는 법을 다뤘다.

요즘 Mobile Device는 대부분 non-removable 배터리를 채택 하고 있기 때문에 이러한 장치들에 대해서 어떻게 측정하는지를 이번 포스트에서 다룬다.

보통 논문을 보면 사진을 작게 집어 넣어서 사실 잘 안보인다.

그래서 직접 크게 사진을 찍어서 아래와 같이 첨부한다.

전극 기판 추출

일단 스마트폰을 teardown 해야 한다.
아래 사진은 nexus 5를 한것이다.
Google에 teardown 치면 왠만한 기기들은 다 있다. 보면서 공구 사서 따라하면 된다.

그 다음 battery를 추출한 다음 아래와 같이 기판을 분리한다.

다시 충전 하지 않으면 보통 배터리가 터지진 않으니 너무 겁먹지 말고 기판을 칼로 잘라내면 된다.

자르기 쉽게 고분자로 구성된 이음 부분이 있다. 그 부만 제거하면 쉽게 분리된다.

몬슨과 연결

이제 해당 기판을 다시 스마트폰에 연결하고
양극 음극에 맞춰서 몬슨 VCC Ground를 해준다.

그럼 정상적으로 스마트폰이 켜지고 전력 소모가 측정된다.

어떤 스마트폰이나 스마트시계와 같은 웨러블 장치는 기판에 보호 회로가 있어서 무한대 저항이 걸려서 cutoff 될 수도 있다. 그럴 때는 납땜을 해서 연결해야 한다.

사실 이러한 저전력 연구는 제조사에서 하면 별것도 아닌데 이런 off the shelf 제품을 가지고 직접 하려고하면 완제품이다보니 이래저래 잔손이 많이 간다.


학습 모델의 재사용 (Transfer Learning)


DNN 모델을 스케치 단계서 부터 전부 다시 학습하는 것은 어려운 일이다. 정말 많은 데이터를 필요로 하기 때문에 학습이 거의 대부분 실패 한다.

여러 방법이 있을 수 있지만 여기서는 Transfer Learning을 다룬다. 잘알려진pretrainted model을 이용해서 마지막 final layer만 수정해서 본인의 목적에 맞게 다시 학습 시키는 방법이다.
혹자는 Fine tuning이라 부르기도 한다.

본인 보유한 데이터 양에 따라 어느 정도 레이어들을 수정할지가 결정된다. 보통 앞부분 layer는 추상적인 feature를 extraction하는 레이어이므로 건들지 않고 마지막 레이러들을 수정한다.

하지만 pretrained model이 현재 수행하려고 하는 작업에 전혀 관련이 없다면Transfer Learning이 의미가 퇴색 된다.
이 경우 unsupervised pre-training방법으로 RBM 또는 Autoencoder방법을 사용하거나 실제로 labeling 데이터를 왕창 모아서 학습을 해야한다.
하지만 scarth부터 random initialization 상태에서 학습하는 것은 매우 도전적인 일이므로 추천 하진 않는다.

결국 아무 때나 Deep Learning을 쓰기엔 무리가 있다.

개념설명

아래와 같이 ImageNet모델이 있다고 가정한다. 1000개를 classify할 수 있다.

이것을 가지고 고양이 Tigger, Misty, Neither 세개를 구분하는 모델을 만든다. 당연히 이런 특정 고양이 이미지는 많이 없다. 따라서 pre-trained 모델을 사용 한다.

이 때 학습할 때 마지막 softmax layer만 학습하게 된다.
이전 데이터는 freeze하게 된다. 이것은 deep learning framework에 따라 구현코드가 달라진다. 이후에는 TensorFlow를 이용해서 이 방법을 다룬다.

  • trainablePrameter =0, freeze = 1

그리고 이러한 freeze레이어의 숫자는 training data의 양에 따라서 다르게 적용할 수 있다.

Tensorflow 구현 (개념 설명)

전부다 저장하고 restore하는 코드

[...] # construct the original model

with tf.Session() as sess:
	saver.restore(sess, "./my_original_model.ckpt")
	[...] # Train it on your new task

부분적으로 복구하는 방법

init = tf.global_variables_initializer()
reuse_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope="hidden[123]")
reuse_vars_dict = dict([(var.name, var.name) for var in reuse_vars])
original_saver = tf.Saver(reuse_vars_dict) # saver to restore the original model

new_saver = tf.Saver() # saver to save the new model

with tf.Session() as sess:
	sess.run(init)
	original_saver.restore("./my_original_model.ckpt") # restore layers 1 to 3
	[...] # train the new model
	new_saver.save("./my_new_model.ckpt") # save the whole model

좀 더 상세한 내용은 이전 포스트를 참조한다.

실제코드

MNIST 데이터를 가지고 fully connected DNN의 pre-trained model을 로드해서 fine-tuning 하는 방법을 다룬다.
전체 구현코드는 Github Link에 있다.

  • 모델 학습
  • 전체 로드
  • 일부분 로드
  • 앞부분 레이어 고정후 뒷 부분만 학습
  • 고정레이어 cache후 뒷 부분만 학습 (트레이닝 속도 향상)

기본 라이브러리 로딩

import tensorflow as tf
# Common imports
import numpy as np
import os
from tensorflow.examples.tutorials.mnist import input_data

def reset_graph(seed=42):
    tf.reset_default_graph()
    tf.set_random_seed(seed)
    np.random.seed(seed)
def leaky_relu(z, name=None):
    return tf.maximum(0.01 * z, z, name=name)

mnist = input_data.read_data_sets("./")
Extracting ./train-images-idx3-ubyte.gz
Extracting ./train-labels-idx1-ubyte.gz
Extracting ./t10k-images-idx3-ubyte.gz
Extracting ./t10k-labels-idx1-ubyte.gz

모델 생성

하이퍼파라메터 설정

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 50
n_hidden3 = 50
n_hidden4 = 50
n_hidden5 = 50
n_outputs = 10

n_epochs = 20
batch_size = 50

모델 생성

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2")
    hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3")
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4")
    hidden5 = tf.layers.dense(hidden4, n_hidden5, activation=tf.nn.relu, name="hidden5")
    logits = tf.layers.dense(hidden5, n_outputs, name="outputs")

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

learning_rate = 0.01
threshold = 1.0

optimizer = tf.train.GradientDescentOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(loss)
capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var)
              for grad, var in grads_and_vars]
training_op = optimizer.apply_gradients(capped_gvs)

init = tf.global_variables_initializer()
saver = tf.train.Saver()

학습

with tf.Session() as sess:
    init.run()
    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        if epoch % 5 == 0:
            acc_train = accuracy.eval(feed_dict={X: X_batch, y: y_batch})
            acc_test = accuracy.eval(feed_dict={X: mnist.validation.images, y: mnist.validation.labels})
            print(epoch, "Batch accuracy:", acc_train, "Validation accuracy:", acc_test)

    save_path = saver.save(sess, "./my_model_final.ckpt")
0 Batch accuracy: 0.94 Validation accuracy: 0.9006
5 Batch accuracy: 1.0 Validation accuracy: 0.9642
10 Batch accuracy: 0.96 Validation accuracy: 0.9712
15 Batch accuracy: 0.98 Validation accuracy: 0.9772
20 Batch accuracy: 1.0 Validation accuracy: 0.9774
25 Batch accuracy: 1.0 Validation accuracy: 0.9786
30 Batch accuracy: 1.0 Validation accuracy: 0.9778
35 Batch accuracy: 1.0 Validation accuracy: 0.9776

모델 로딩후 재학습

여기서 부터 모델을 불러와서 다시 fine-tuning 하는 부분을 구현 한다.

reset_graph()
saver = tf.train.import_meta_graph("./my_model_final.ckpt.meta")

로딩가능한 operation이 무엇인지 확인한다.

for op in tf.get_default_graph().get_operations():
    print(op.name)
X
y
hidden1/kernel/Initializer/random_uniform/shape
hidden1/kernel/Initializer/random_uniform/min
hidden1/kernel/Initializer/random_uniform/max
[생략]
[생략]
save/RestoreV2_11/tensor_names
save/RestoreV2_11/shape_and_slices
save/RestoreV2_11
save/Assign_11
save/restore_all

파라메터가 너무 많기 때문에 TensorBoard로 확인한다.
그다음 아래와 같이 get_tensor_by_name get_operation_by_name으로 로드한다.

X = tf.get_default_graph().get_tensor_by_name("X:0")
y = tf.get_default_graph().get_tensor_by_name("y:0")

accuracy = tf.get_default_graph().get_tensor_by_name("eval/accuracy:0")

training_op = tf.get_default_graph().get_operation_by_name("GradientDescent")

다른 사람의 편의를 위해서 import operation collection으로 미리 정의해 줄수도 있다.

for op in (X, y, accuracy, training_op):
    tf.add_to_collection("my_important_ops", op)

이렇게 하면 다른 사람들이 쉽게 로딩 가능하다.

X, y, accuracy, training_op = tf.get_collection("my_important_ops")

아래와 같이 이제 본인의 데이터로 본격적으로 학습이 가능하다.

with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")
    # continue training the model...
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt

아니면 모델을 로드에서 Testing만 가능하다.

with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_new_model_final.ckpt")    
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9743
1 Test accuracy: 0.9744
2 Test accuracy: 0.9756
3 Test accuracy: 0.9743
4 Test accuracy: 0.9751
5 Test accuracy: 0.975
6 Test accuracy: 0.9741
7 Test accuracy: 0.9742
8 Test accuracy: 0.9751
9 Test accuracy: 0.9748
10 Test accuracy: 0.9744
11 Test accuracy: 0.9747
12 Test accuracy: 0.9746
13 Test accuracy: 0.9747
14 Test accuracy: 0.9746
15 Test accuracy: 0.9747
16 Test accuracy: 0.9742
17 Test accuracy: 0.9749
18 Test accuracy: 0.9746
19 Test accuracy: 0.9748
20 Test accuracy: 0.975
21 Test accuracy: 0.9746
22 Test accuracy: 0.9745
23 Test accuracy: 0.975
24 Test accuracy: 0.9744
25 Test accuracy: 0.9743
26 Test accuracy: 0.9743
27 Test accuracy: 0.9745
28 Test accuracy: 0.9746
29 Test accuracy: 0.9749
30 Test accuracy: 0.9746
31 Test accuracy: 0.9747
32 Test accuracy: 0.9747
33 Test accuracy: 0.9743
34 Test accuracy: 0.9746
35 Test accuracy: 0.9746
36 Test accuracy: 0.9749
37 Test accuracy: 0.9751
38 Test accuracy: 0.9748
39 Test accuracy: 0.9743

다른 방법은 그냥 원래 모델에 대한 code가 있다면 그것을 로딩해도 된다. 그렇게 하면import_meta_graph()를 호출하지 않아도 된다.

마지막 4번째 레어어만 수정해서 재학습하기 (not freezing the lower layers)

  • import_meta_graph()로 전체 graph를 모두 불러온다음 4번째 Layer를 무시한다.
  • 즉 3번째 레이어 까지만 재사용한다.
  • 그리고 output 레이어도 재설정한다.
  • 그리고 이것으로 optimizer를 이용해서 최적화한다.
  • 이렇게 생성된 결과를 새로운 파일에 저장한다.
reset_graph()

n_hidden4 = 20  # new layer
n_outputs = 10  # new layer

saver = tf.train.import_meta_graph("./my_model_final.ckpt.meta")

X = tf.get_default_graph().get_tensor_by_name("X:0")
y = tf.get_default_graph().get_tensor_by_name("y:0")

hidden3 = tf.get_default_graph().get_tensor_by_name("dnn/hidden4/Relu:0")

new_hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="new_hidden4")
new_logits = tf.layers.dense(new_hidden4, n_outputs, name="new_outputs")

with tf.name_scope("new_loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=new_logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("new_eval"):
    correct = tf.nn.in_top_k(new_logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

with tf.name_scope("new_train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)

init = tf.global_variables_initializer()
new_saver = tf.train.Saver()

새로운 레이어를 정의하고 loss namesapce를 다르게 정의 했기 때문에saver.restore()후에 값이 중복 되지 않는다.

with tf.Session() as sess:
    init.run()
    saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = new_saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9665
...
...
39 Test accuracy: 0.9751

마지막 4번째 레어어만 수정해서 재학습하기 (freezing the lower layers)

구현을 위해서는 2가지 방법이 존재한다.

  • tf.GraphKeys.TRAINABLE_VARIABLES, scope="outputs")을 이용한 방법
  • tf.stop_gradient를 이용한방법

tf.GraphKeys.TRAINABLE_VARIABLES 이용

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300 # reused
n_hidden2 = 50  # reused
n_hidden3 = 50  # reused
n_hidden4 = 20  # new!
n_outputs = 10  # new!

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")       # reused
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # reused
    hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # reused
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # new!
    logits = tf.layers.dense(hidden4, n_outputs, name="outputs")                         # new!

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

학습할 대상을 정규식에 의해서 scope을 정해서 불러온다.
결과는 아래와 같다.

tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
                                   scope="outputs")
[<tf.Variable 'outputs/kernel:0' shape=(20, 10) dtype=float32_ref>,
<tf.Variable 'outputs/bias:0' shape=(10,) dtype=float32_ref>]
with tf.name_scope("train"):                                         # not shown in the book
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)     # not shown
    train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
                                   scope="outputs")
    training_op = optimizer.minimize(loss, var_list=train_vars)
init = tf.global_variables_initializer()
new_saver = tf.train.Saver()

따로 새롭게 namesapce를 정의하지 않고 load할 때 정규식을 이용해서 일부분만 불러온다.
이렇게 하면 name을 변경할 필요 없다.

tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]|outputs") # regular expression
[<tf.Variable 'hidden1/kernel:0' shape=(784, 300) dtype=float32_ref>,
 <tf.Variable 'hidden1/bias:0' shape=(300,) dtype=float32_ref>,
 <tf.Variable 'hidden2/kernel:0' shape=(300, 50) dtype=float32_ref>,
 <tf.Variable 'hidden2/bias:0' shape=(50,) dtype=float32_ref>,
 <tf.Variable 'hidden3/kernel:0' shape=(50, 50) dtype=float32_ref>,
 <tf.Variable 'hidden3/bias:0' shape=(50,) dtype=float32_ref>,
 <tf.Variable 'outputs/kernel:0' shape=(20, 10) dtype=float32_ref>,
 <tf.Variable 'outputs/bias:0' shape=(10,) dtype=float32_ref>]
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]|outputs]") # regular expression
reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3

init = tf.global_variables_initializer()
saver = tf.train.Saver()

with tf.Session() as sess:
    init.run()
    restore_saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9221
...
...
39 Test accuracy: 0.9556

tf.stop_gradient 이용

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300 # reused
n_hidden2 = 50  # reused
n_hidden3 = 50  # reused
n_hidden4 = 20  # new!
n_outputs = 10  # new!

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")
with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
                              name="hidden1") # reused frozen
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,
                              name="hidden2") # reused frozen
    hidden2_stop = tf.stop_gradient(hidden2)
    hidden3 = tf.layers.dense(hidden2_stop, n_hidden3, activation=tf.nn.relu,
                              name="hidden3") # reused, not frozen
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu,
                              name="hidden4") # new!
    logits = tf.layers.dense(hidden4, n_outputs, name="outputs") # new!
with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)

위와 같이 hidden2_stop = tf.stop_gradient(hidden2)을 사용해서 중간 레이어를 만든다.
그 이후에는 트레이닝 코드는 위 방식과 정확히 일치한다.

%%time
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]") # regular expression
reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3

init = tf.global_variables_initializer()
saver = tf.train.Saver()

with tf.Session() as sess:
    init.run()
    restore_saver.restore(sess, "./my_model_final.ckpt")

    for epoch in range(n_epochs):
        for iteration in range(mnist.train.num_examples // batch_size):
            X_batch, y_batch = mnist.train.next_batch(batch_size)
            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
        accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,
                                                y: mnist.test.labels})
        print(epoch, "Test accuracy:", accuracy_val)

    save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9654
...
...
19 Test accuracy: 0.9738
CPU times: user 23.1 s, sys: 852 ms, total: 23.9 s
Wall time: 13.5 s

Fronzen Layer를 cache해서 학습속도를 올리는 방법

Frozen 레이어는 변화하지 않기 때문에 이것은 cache해서 재사용 할 수 있다.

reset_graph()

n_inputs = 28 * 28  # MNIST
n_hidden1 = 300 # reused
n_hidden2 = 50  # reused
n_hidden3 = 50  # reused
n_hidden4 = 20  # new!
n_outputs = 10  # new!

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int64, shape=(None), name="y")

with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
                              name="hidden1") # reused frozen
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,
                              name="hidden2") # reused frozen & cached
    hidden2_stop = tf.stop_gradient(hidden2)
    hidden3 = tf.layers.dense(hidden2_stop, n_hidden3, activation=tf.nn.relu,
                              name="hidden3") # reused, not frozen
    hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu,
                              name="hidden4") # new!
    logits = tf.layers.dense(hidden4, n_outputs, name="outputs") # new!

with tf.name_scope("loss"):
    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
    loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("eval"):
    correct = tf.nn.in_top_k(logits, y, 1)
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    training_op = optimizer.minimize(loss)
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                               scope="hidden[123]") # regular expression
reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3

init = tf.global_variables_initializer()
saver = tf.train.Saver()

핵심 방법은 1번만 트레이닝해서 h2_cache를 만든다음 이것을 suffling한 index를 가지고 epoch을 돌면서 training하는 것이다.
메모리가 충분하다면 이러한 방법이 가능하다.
뒷 부분을 계산하지 않기 때문에 training속도를 증가 시킬 수 있다.13.5초에서9.63초로 속도가 빨라진 것을 알 수 있다.

%%time
import numpy as np

n_batches = mnist.train.num_examples // batch_size

with tf.Session() as sess:
    init.run()
    restore_saver.restore(sess, "./my_model_final.ckpt")
    
    h2_cache = sess.run(hidden2, feed_dict={X: mnist.train.images})
    h2_cache_test = sess.run(hidden2, feed_dict={X: mnist.test.images}) # not shown in the book

    for epoch in range(n_epochs):
        shuffled_idx = np.random.permutation(mnist.train.num_examples)
        hidden2_batches = np.array_split(h2_cache[shuffled_idx], n_batches)
        y_batches = np.array_split(mnist.train.labels[shuffled_idx], n_batches)
        for hidden2_batch, y_batch in zip(hidden2_batches, y_batches):
            sess.run(training_op, feed_dict={hidden2:hidden2_batch, y:y_batch})

        accuracy_val = accuracy.eval(feed_dict={hidden2: h2_cache_test, # not shown
                                                y: mnist.test.labels})  # not shown
        print(epoch, "Test accuracy:", accuracy_val)                    # not shown

    save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
0 Test accuracy: 0.9648
...
...
19 Test accuracy: 0.9736
CPU times: user 20.7 s, sys: 428 ms, total: 21.1 s
Wall time: 9.63 s

참고문헌

Coursera deep learning ai, Andrew Ng.
Hands-On Machine Learning with Scikit-Learn and Tensorflow, Aureien Geron


TensorFlow 모델을 저장하고 불러오기 (save and restore)


해당 튜토리얼에 사용한 코드는 개인 GitHub Link에서 확인 할 수 있다.

저장 복구를 위해서는 두개의 파일이 필요하다.

a) Meta graph

Tensorflow graph를 저장 하게 된다. 즉 all variables, operations, collections 등을 저장 한다. .meta로 확장자를 가진다.

b) Checkpoint file

binary 파일로 weights, biases, gradients 등을 저장 한다.
0.11부터는 두개의 파일로 저장된다.

  • model.ckpt.data-00000-of-00001
  • model.ckpt.index

.data파일의 경우 training variable를 가지고 있다.
여전히 checkpoint파일도 보유하고 있지만 이것은 단순히 최근 상태만을 기록하고 있다.

모델 저장 방법

saver = tf.train.Saver()를 통해서 가능 하다.

import tensorflow as tf
w1 = tf.Variable(tf.random_normal(shape=[2]), name='w1')
w2 = tf.Variable(tf.random_normal(shape=[5]), name='w2')
saver = tf.train.Saver()
sess = tf.Session()
sess.run(tf.global_variables_initializer())
saver.save(sess, 'my_test_model')

# This will save following files in Tensorflow v >= 0.11
# my_test_model.data-00000-of-00001
# my_test_model.index
# my_test_model.meta
# checkpoint

만약 1000 interations 이후에 model을 저장하고 싶다면 아래와 같이 한다.

saver.save(sess, "my_test_model", global_step=1000)

이렇게 하면 아래처럼 모델뒤에 -1000이라는 숫자가 이름에 붙어서 나오게 됩니다.

my_test_model-1000.index
my_test_model-1000.meta
my_test_model-1000.data-00000-of-00001
checkpoint

모델의 구조는 같기 때문에 .meta파일의 경우 1000 interation당 매번 생성할 필요는 없다.

모델 값만 저장하고 graph는 저장하지 않는 코드는 아래와 같다.

saver.save(sess, 'my-model', global_step=step,write_meta_graph=False)

만약 최근 2시간동안 4개의 모델만 저장하고 싶다면 아래와 같이 옵션을 설정한다.

#saves a model every 2 hours and maximum 4 latest models are saved.
saver = tf.train.Saver(max_to_keep=4, keep_checkpoint_every_n_hours=2)

망약 tf.train.Saver()에 아무것도 지정하지 않았다면 모든 변수들을 저장하게 된다.
특정 variables/collections을 저장하고 싶다면 그것을 지정하면 된다.
List, dictionary자료구조도 받으니 그것을 잘 활용 한다.

import tensorflow as tf
w1 = tf.Variable(tf.random_normal(shape=[2]), name='w1')
w2 = tf.Variable(tf.random_normal(shape=[5]), name='w2')
saver = tf.train.Saver([w1,w2])
sess = tf.Session()
sess.run(tf.global_variables_initializer())
saver.save(sess, 'my_test_model',global_step=1000)

모델 읽기

두 가지 일을 해야한다.

a) 네트워크 생성

.meta파일을 생성 했으므로 이것을 불러오는 방식으로 network을 재생성 할 수 있다.
.meta파일을 불러오기 위해서는 tf.train.import() 함수를 이용한다.

saver = tf.train.import_meta_graph('my_test_model-1000.meta')

이렇게 하면 현재 그래프에 이어 붙는 형식으로 동작하므로tf.reset_default_graph()를 실행해서 default graph로 초기화 해주는 것이 안전하다.

b) 파라메터 로딩

tf.train.Saver()를 이용해서 파라메터를 로딩한다.

with tf.Session() as sess:
  new_saver = tf.train.import_meta_graph('my_test_model-1000.meta')
  new_saver.restore(sess, tf.train.latest_checkpoint('./'))
with tf.Session() as sess:    
    saver = tf.train.import_meta_graph('my-model-1000.meta')
    saver.restore(sess, tf.train.latest_checkpoint('./'))
    print(sess.run('w1:0'))
##Model has been restored. Above statement will print the saved value of w1.

저장된 모델로 실제 작업하기

이제 위해서 다룬 내용을 토대로 종합적으로 간단한 neural net.을 생성하고 이것을 저장한다음 다시 불러오는 코드를 작성해 본다.
이런한 작업은 추후에 transfer learning이나 testing만 별도로 작업하기 위해서 사용 될 수 있다.

아래의 코드는 $y=(w1+w2) \times b$ 를 구현한 내용이다.
여기서 핵심은 추후에 variable operation을 각각 불러오기 위해서 name을 반드시 주어야 한다.
나중에 본인 model을 공유할 때도 이름을 잘 정해주어야 다른 사람이 가져다가 본인들 목적에 맞춰서 fine-tuning해서 사용할 수 있다.

모델 생성 및 저장

import tensorflow as tf

# Prepare to feed input, i.e. feed_dict and placeholders
w1 = tf.placeholder(tf.float32, name="w1")
w2 = tf.placeholder(tf.float32, name="w2")
b1 = tf.Variable(2.0,dtype=tf.float32, name="bias")
feed_dict = {'w1': 4.0, 'w2': 8.0}

# Define a test operation that we will restore
w3 = w1 + w2
w4 = tf.multiply(w3, b1, name="op_to_restore")
sess = tf.Session()
sess.run(tf.global_variables_initializer())

# Create a saver object which will save all the variables
saver = tf.train.Saver()

# Run the operation by feeding input
result = sess.run(w4, {w1:feed_dict['w1'], w2:feed_dict['w2']})
print(result)
# Prints 24 which is sum of (w1+w2)*b1

# Now, save the graph
saver.save(sess, './my_test_model', global_step=1000)

실행결과는 24이다.

모델 불러오기와서 새로운 입력값으로 처리

모델을 복구하고 feed_dict을 다르게 입력하는 코드이다.

import tensorflow as tf

sess=tf.Session()    
#First let's load meta graph and restore weights
saver = tf.train.import_meta_graph('my_test_model-1000.meta')
saver.restore(sess,tf.train.latest_checkpoint('./'))


# Now, let's access and create placeholders variables and
# create feed-dict to feed new data

graph = tf.get_default_graph()
w1 = graph.get_tensor_by_name("w1:0")
w2 = graph.get_tensor_by_name("w2:0")
feed_dict ={w1:13.0,w2:17.0}

#Now, access the op that you want to run. 
op_to_restore = graph.get_tensor_by_name("op_to_restore:0")

print (sess.run(op_to_restore,feed_dict))
#This will print 60 which is calculated 
#using new values of w1 and w2 and saved value of b1. 

실행결과는 60이다.

위 예제는 Tensor를 이용해서 간단하게 구현하다보니 모두get_tensor_by_name()으로 가능하지만 실제로 operation과 placeholder는 각각 다르게 load해야 한다.

  • placeholder 불러오기

    • graph.get_tensor_by_name()
  • operation 불러오기

    • graph.get_operation_by_name()

로딩 가능한 현재 graph에서의 operation의 종류이다.

for op in tf.get_default_graph().get_operations():
    print(op.name)

모델을 불러오고 operation과 layer 추가

import tensorflow as tf

sess=tf.Session()    
#First let's load meta graph and restore weights
saver = tf.train.import_meta_graph('my_test_model-1000.meta')
saver.restore(sess,tf.train.latest_checkpoint('./'))


# Now, let's access and create placeholders variables and
# create feed-dict to feed new data

graph = tf.get_default_graph()
w1 = graph.get_tensor_by_name("w1:0")
w2 = graph.get_tensor_by_name("w2:0")
feed_dict ={w1:13.0,w2:17.0}

#Now, access the op that you want to run. 
op_to_restore = graph.get_tensor_by_name("op_to_restore:0")

#Add more to the current graph
add_on_op = tf.multiply(op_to_restore,2)

print (sess.run(add_on_op,feed_dict))
#This will print 120.

실행결과 120이다.

참고자료

http://cv-tricks.com/tensorflow-tutorial/save-restore-tensorflow-models-quick-complete-tutorial/


Batch 크기의 결정 방법


보통 vectorization방법으로 gradient descent알고리즘의 효율을 높이게 된다.

하지만 input 데이터가 너무 크다면 그 방법을 사용할 수 없다.
메모리 문제도 발생하고 한 번 interation (epoch)당 시간이 너무 오래 걸리기 때문이다.

Batch-gradient descent mini-bach gradient descent의 cost 그래프의 차이는 아래와 같다.

Choosing your mini-batch size

  • Mini-batch 크기가 전체 트레이닝 셋 데이터 사이즈인 m과 같다면 이것은 Batch gradient descent방법이다.

    • (+) 상대적으로 아래의 contour그래프를 보면 알 수 있듯이 적은 noise Large step으로 글로벌 미니멈에 수렴한다.
    • (+) vectorization 효율이 좋다.
    • (-) interation당 속도가 느리다.
    • (-) 메모리 여유 공간에 따라서 실행 불가능 할 수도 있다.
  • Mini-batch 크기가 1이라면,Stochastic gradient descent라고 부른다.

    • (+) 적은 메모리로 동작 가능하다. noise한 부분은 learning rate을 작게하면 완화 할 수 있다.
    • (-) vectorization 효율이 없다. training data를 1개만 사용하기 때문이다.
  • Mini-batch크기를 너무 작게도 크게도 하지 않는다.

    • (+) vectorization을 효과 본다.
    • (+) interation당 긴 시간없이 progress를 만들 수 있다.

최종적 가이드라인

  • 데이터가 별로 없다면 batch gradient descent를 쓴다.
    • ex 2,000 정도
  • mini-batch를 선택
    • 64, 128, 256, 512 사이즈로 선택한다. 메모리 사이즈에 맞춰서
  • CPU/GPU memory 사이즈에 맞춰서 조절 한다.

Take-away message
SGD GD가 같은 글로벌 cost 최소점에 도달 할 수 있다는 것은 증명이 되어 있지만, neural netwrok은 convex가 아니기 때문에 batch 크기의 설정 방법에 따라 수렴하는 조건이 다를 수 있다.

Batch size는 일반적으로 메모리가 감당할 수 있는 정도까지 최대한 크게 잡는게 좋을것 같다.

참고자료

Coursera DeepLearning ai, Andrew Ng


딥러닝 입력 데이터 정규화 (Normalizing inputs)


학습을 수행하기 전에 값의 범위를 normalization 하는 것은 중요하다. 그 이유는 아래와 같다.

입력 변수가 MLP에서와 같이 선형 적으로 결합된다면 적어도 이론 상으로는 입력을 표준화하는 것이 거의 필요하지 않습니다.
그 이유는 해당 weight bais를 변경하여 입력 벡터를 재조정하면 이전과 완전히 똑같은 결과를 남길 수 있기 때문입니다.
그러나 입력을 Standardization하면 학습을 더 빨리하고 지역 최적의 상태에 빠지게 될 가능성을 줄이는 다양한 실용적인 이유가 있습니다.
또한, 표준화 된 입력을 통해 Gradient Descent  Bayesian estimation을 보다 편리하게 수행 할 수 있습니다.

Normalization

수식 : (요소값 - 최소값) / (최대값 - 최소값)
설명 : 전체 구간을 0~1사이의 값으로 맞춰 준다.

Standardization

수식 : (요소값 - 평균) / 표준편차
설명 : 평균은 0 표준편차는

$$\mu = \frac{1}{m} \sum_{i=1}^{m}{x^{(i)}}$$
$$x := x -\mu$$
$$\sigma^{2}=\frac{1}{m}\sum_{i=1}^{m}{(x^{(i)}-\mu)^{2}}$$

직관적 이해

아래와 같이 Unnormalized된 상태에서는 Learning Rate을 매우 작게 설정해야 정상적을 학습이 된다.
이유는 cost 그래프가 elongated하기 때문이다. 따라서 elongated contour의 모습을 가진다.
아래와 같이 Input의 Range가 서로 다르다면 Gradient Descent Algorithm을 적용하는것이 매우 까다로워지는 상황이 발생 한다.

하지만 normalization을 적용하면 좀 더 spherical contour를 가지게 된다.
이렇게 하면 좀 더 Gradient Descent Algorithm으로 쉽게 그리고 빠르게 최적화 지점을 찾게 된다.

구현 코드

아래와 같은 코드를 가질 때
x가 1.0
y가 0.0
이렇게 1.0~0 사이의 값으로 입력과 출력이 정규화가 잘되면 학습이 잘 이뤄 진다.
Learning rate을 적절히 값을 잡아도 학습이 잘된다.

import tensorflow as tf
x = tf.constant(1.0, name='input')
w = tf.Variable(0.8, name='weight')
y = tf.mul(w, x, name='output')
y_ = tf.constant(0.0, name='correct_value')
loss = tf.pow(y - y_, 2, name='loss')
train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss)

for value in [x, w, y, y_, loss]:
    tf.scalar_summary(value.op.name, value)

summaries = tf.merge_all_summaries()

sess = tf.Session()
summary_writer = tf.train.SummaryWriter('log_simple_stats', sess.graph)

sess.run(tf.initialize_all_variables())
for i in range(100):
    if i % 10 ==0:
        print("epoch {}, output: {}".format(i, sess.run(y)))
    summary_writer.add_summary(sess.run(summaries), i)
    sess.run(train_step)
epoch 80, output: 0.01321229338645935
epoch 90, output: 0.007910688407719135

하지만 x의 범위르 10배만 넓혀서 10으로 하면 학습이 실패 한다.
이 때는 Learning rate을 더 작게 주어야 한다.

import tensorflow as tf
x = tf.constant(10.0, name='input')
w = tf.Variable(0.8, name='weight')
y = tf.mul(w, x, name='output')
y_ = tf.constant(0.0,