📗강의노트/핸즈온 머신러닝

[핸즈온 머신러닝] 제 9장 연습문제 풀이

728x90
반응형

Exercise Part. 9

Tensorflow

1. 계산을 집접 실행하지 않고 계산 그래프를 만드는 주요 장점과 단점은 무엇인가요?

(1) 장점 : 텐서플로가 자동 미분 가능 / 병렬로 계산 가능 / 내부 구조를 살피기 쉬움

(2) 단점 : 익숙하지 않음 / 단계별 디버깅 수정이 어려움

2. a_val = a.eval(session=sess)와 a_val = sesss.run(a)는 동일한 문장인가요?

그렇다. 동일하다.

3. a_val, b_val = a.eval(session=sess),b.eval(session=sess)와 a_val,b_val = sess.run([a,b])는 동일한 문장인가?

부수효과로 인해 동일하지 않다. 첫 번째는 그래프를 두 번 실행하지만 두 번째는 그래프를 한 번 실행한다. 그리고 속도도 더 빠르다.

4. 같은 세션에서 두 개의 그래프를 실행할 수 있나요?

개별적인 두 개의 그래프가 아니라 하나의 그래프로 합쳐야 실행할 수 있다.

5. 만약 w를 가진 그래프 g를 만들고 스레드 두 개를 시작해 각 스레드에서 동일한 그래프 g를 사용하는 세션을 열면, 각 세션은 변수 w를 따로 가지게 될까요? 아니면 공유할까요?

분산 텐서플로에서 두 개의 세션이 같은 클러스터에 접속하여 같은 컨테이너를 사용하면 동일한 변수 w의 값을 공유하는 것이다. (로컬 텐서플로에서는 각각의 값을 갖는다 )

6. 변수는 언제 초기화되고 언제 소멸되나요?

변수는 초기화 함수가 호출되면 초기화 되고, 세션이 종료되면 사라진다.

분산 텐서플로의 경우에는 컨테이너에 변수가 존재하기에 세션을 종료해도 사라지지 않고 삭제하려면 컨테이너를 리셋해야한다.

7. 플레이스홀더와 변수의 차이점은 무엇인가요?

변수는 값을 가진 연산. 그래프 내에서 계속 동일한 값을 가진다.

플레이스홀더는 아무런 값도 가지고 있지 않다. 표현하려는 텐서의 크기와 타입에 대한 정보를 가지고 있을 뿐이다. feed_dict로 수를 넘겨주어야 실행이 된다.

8. 플레이스홀더에 의존하는 연산을 평가하기 위해 그래프를 실행할 때 플레이스홀더에 값을 주입하지 않으면 어떻게 될까요? 플레이스홀더에 의존하지 않는 연산이라면 어떻게 될까요?

값을 주입하지 않으면 예외가 발생하고, 의존하지 않는 연산이라면 그대로 실행이 된다.

9. 그래프를 실행할 때, 어떤 연산자의 출력값을 주입할 수 있나요? 아니면 플레이스홀더의 값만 가능한가요?

당연히 출력값도 주입가능하다. 하지만 매우 드문 경우이다.

10. (실행 단계에서) 변수에 원하는 값을 어떻게 설정할 수 있나요?

플레이스홀더에 feed_dict로 값을 넘겨주면 연산을 실행한다.

11. 후진 모드 자동 미분으로 변수 10개에 대한 비용 함수의 그래디언트를 계산하려면 그래프를 몇 번 순회해야 하나요? 전진 모드 자동 미분이나 기호 미분의 경우는 어떨까요?

후진 모드 자동 미분 : 변수 개수에 상관없이 변수에 대한 비용 함수의 그래디언트를 계산하기 위해 그래프를 두 번 순회한다.

전진 모드 자동 미분 : 변수마다 한 번씩 실행한다.

기호 미분 : 그래디언트를 계산하기 위해 다른 그래프를 만든다. 그래서 원본 그래프를 순회하지 않는다.

12. 텐서플로를 사용해 미니배치 경사 하강법으로 로지스틱 회귀를 구헌해라. moons 데이터셋에 훈련시키고 평가해라. 그리고 다음 부가 기능을 추가하라.

In [1]:
from sklearn.datasets import make_moons

m = 1000
X_moons, y_moons = make_moons(m,noise=0.1,random_state=42)
In [7]:
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'NanumBarunGothic'

plt.plot(X_moons[y_moons == 1,0],X_moons[y_moons == 1,1], 'go', label='양성')
plt.plot(X_moons[y_moons == 0,0],X_moons[y_moons == 0,1],'r^',label='음성')
plt.legend()
plt.show()
In [8]:
# 편향 추가하기
X_moons_bias = np.c_[np.ones((m,1)), X_moons]
In [9]:
X_moons_bias[:5]
Out[9]:
array([[ 1.        , -0.05146968,  0.44419863],
       [ 1.        ,  1.03201691, -0.41974116],
       [ 1.        ,  0.86789186, -0.25482711],
       [ 1.        ,  0.288851  , -0.44866862],
       [ 1.        , -0.83343911,  0.53505665]])
In [12]:
# y_moons를 열벡터로 만들기
y_moons_colvector = y_moons.reshape(-1,1)
In [14]:
y_moons_colvector[:5]
Out[14]:
array([[1],
       [1],
       [1],
       [1],
       [0]])
In [19]:
# train test 나누기
test_ratio = 0.2
test_size = int(m * test_ratio)
X_train = X_moons_bias[:-test_size]
X_test = X_moons_bias[-test_size:]
y_train = y_moons_colvector[:-test_size]
y_test = y_moons_colvector[-test_size:]
In [25]:
# 배치 만들기 
def minibatch(X_train,y_train,batch_size):
    rnd_indices = np.random.randint(0,len(X_train),batch_size) # batch_size만큼 만들기
    X_batch = X_train[rnd_indices]
    y_batch = y_train[rnd_indices]
    return X_batch, y_batch
In [28]:
X_batch, y_batch = minibatch(X_train,y_train,5) # batch_size = 5
X_batch, y_batch
Out[28]:
(array([[ 1.        ,  1.37501729, -0.40317036],
        [ 1.        , -0.83067666,  0.4712851 ],
        [ 1.        ,  0.23704458, -0.14710941],
        [ 1.        , -0.9689649 ,  0.09106498],
        [ 1.        ,  0.83270089, -0.52616073]]), array([[1],
        [0],
        [1],
        [0],
        [1]]))
In [31]:
# 먼저 그래프 리셋하기 
import tensorflow as tf
tf.reset_default_graph()

# moons의 input은 두 개의 입력 특성을 가져서 2차원이다. 
n_inputs = 2
In [33]:
X = tf.placeholder(tf.float32,shape=(None,n_inputs+1))
y = tf.placeholder(tf.float32,shape=(None,1))
theta = tf.Variable(tf.random_uniform([n_inputs + 1,1], -1.0,1.0, seed=42))
logits = tf.matmul(X,theta)
y_proba = 1 / (1+tf.exp(-logits))
In [34]:
y_proba = tf.sigmoid(logits) # 한 번에 가능

Cross Entropy

J(θ)=1mi=1m[y(i)log(p^(i))+(1y(i))log(1p^(i))]

In [35]:
epsilon = 1e-7  # 로그 계산시 오버플로우 피하기 위해서
loss = -tf.reduce_mean(y*tf.log(y_proba + epsilon) + (1-y)*tf.log(1-y_proba + epsilon))
In [38]:
# 그냥 함수 써도됨
loss = tf.losses.log_loss(y,y_proba)
In [40]:
lr = 0.01
optimizer = tf.train.GradientDescentOptimizer(learning_rate=lr)
train = optimizer.minimize(loss)
In [42]:
# 모델 훈련
n_epochs=1000
batch_size=50
n_batches = int(np.ceil(m / batch_size))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for epoch in range(n_epochs):
        for batch_index in range(n_batches):
            X_batch,y_batch = minibatch(X_train,y_train,batch_size)
            sess.run(train,feed_dict={X:X_batch,y:y_batch})
        loss_val = loss.eval({X:X_test,y:y_test})
        if epoch % 100 == 0:
            print("Epoch : {0}, Loss : {1}".format(epoch,loss_val))
    y_proba_val = y_proba.eval(feed_dict={X:X_test,y:y_test})
    
Epoch : 0, Loss : 0.8750166296958923
Epoch : 100, Loss : 0.3515768051147461
Epoch : 200, Loss : 0.31099650263786316
Epoch : 300, Loss : 0.294767290353775
Epoch : 400, Loss : 0.2863621115684509
Epoch : 500, Loss : 0.2813054025173187
Epoch : 600, Loss : 0.2784678637981415
Epoch : 700, Loss : 0.2765762209892273
Epoch : 800, Loss : 0.2759423851966858
Epoch : 900, Loss : 0.2749108374118805
In [44]:
# 양성으로 추정할 확률
y_proba_val[:5]
Out[44]:
array([[0.54508024],
       [0.7019736 ],
       [0.5152467 ],
       [0.9908825 ],
       [0.503746  ]], dtype=float32)
In [45]:
y_pred = (y_proba_val >= 0.5)
y_pred[:5]
Out[45]:
array([[ True],
       [ True],
       [ True],
       [ True],
       [ True]])
In [50]:
# Precision(정밀도) , Recall(재현율)
from sklearn.metrics import precision_score, recall_score

print(precision_score(y_test,y_pred))
print(recall_score(y_test,y_pred))
0.8627450980392157
0.8888888888888888
In [59]:
# 열 벡터를 1차원으로
y_pred_index = y_pred.reshape(-1) 
plt.plot(X_test[y_pred_index,1], X_test[y_pred_index,2],'go',label="양성")
plt.plot(X_test[~y_pred_index,1], X_test[~y_pred_index,2],'r^',label='음성')
# ~는 해당 인덱스 하나만 골라내기 
plt.legend()
plt.show()

재사용이 용이하도록 logistic_regression() 함수 안에서 그래프를 정의합니다.

훈련하는 동안 일정한 간격으로 Saver 객체를 사용해 체크포인트를 저장하고 훈련이 끝날 때 최종 모델을 저장합니다.

훈련이 중지되고 다시 시작할 때 마지막 체크포인트를 복원합니다.

텐서보드에서 그래프가 잘 정돈되어 보이도록 이름 범위를 사용하여 그래프를 정의합니다.

서머리(summary)를 추가해 텐서보드에서 학습 곡선을 나타냅니다.

학습률, 미니배치 크기 같은 하이퍼파라미터를 바꾸어 보면서 학습 곡선의 모양을 관찰합니다.

In [64]:
# 모델 성능 향상을 위해 특성을 추가하
X_train_enhanced = np.c_[X_train,np.square(X_train[:,1]),
                        np.square(X_train[:,2]),
                         X_train[:,1] ** 3,
                         X_train[:,2] ** 3
                        ]
X_test_enhanced = np.c_[X_test,np.square(X_test[:,1]),
                        np.square(X_test[:,2]),
                         X_test[:,1] ** 3,
                         X_test[:,2] ** 3
                        ]
In [66]:
X_train_enhanced[:5]
Out[66]:
array([[ 1.00000000e+00, -5.14696757e-02,  4.44198631e-01,
         2.64912752e-03,  1.97312424e-01, -1.36349734e-04,
         8.76459084e-02],
       [ 1.00000000e+00,  1.03201691e+00, -4.19741157e-01,
         1.06505890e+00,  1.76182639e-01,  1.09915879e+00,
        -7.39511049e-02],
       [ 1.00000000e+00,  8.67891864e-01, -2.54827114e-01,
         7.53236288e-01,  6.49368582e-02,  6.53727646e-01,
        -1.65476722e-02],
       [ 1.00000000e+00,  2.88850997e-01, -4.48668621e-01,
         8.34348982e-02,  2.01303531e-01,  2.41002535e-02,
        -9.03185778e-02],
       [ 1.00000000e+00, -8.33439108e-01,  5.35056649e-01,
         6.94620746e-01,  2.86285618e-01, -5.78924095e-01,
         1.53179024e-01]])
In [67]:
tf.reset_default_graph()
In [70]:
def logistic_regression(X,y,initializer=None, seed=42,learning_rate=0.01):
    n_inputs_with_bias = int(X.get_shape()[1])
    with tf.name_scope('logistic_regression'):
        with tf.name_scope('model'):
            if initializer is None:
                initializer = tf.random_uniform([n_inputs_with_bias ,1],-1.0,1.0,seed=seed)
            
            theta = tf.Variable(initializer)
            logits = tf.matmul(X,theta)
            y_proba = tf.sigmoid(logits)
        with tf.name_scope('train'):
            loss = tf.losses.log_loss(y,y_proba)
            optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
            train = optimizer.minimize(loss)
            loss_summary = tf.summary.scalar('log_loss',loss)
            
        with tf.name_scope('init'):
            init = tf.global_variables_initializer()
            
        with tf.name_scope('save'):
            saver = tf.train.Saver()
    return y_proba, loss, train, loss_summary, init, saver
                
                
        
In [77]:
# 텐서보드를 위해 서머리를 저장할 로그 디렉토리 이름을 생성하는 함수를 만듬 
from datetime import datetime
def log_dir(prefix=''):
    now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
    root_logdir = "tf_logs"
    if prefix:
        prefix += "-"
    name = prefix + "run-" + now
    return "{}/{}/".format(root_logdir,name)
In [80]:
# logistic_regression 이용해서 그래프 만들기 
# 텐서보드용 서머리를 로그 디렉토리에 만들기 위해 Filewriter도 만듬
# 변수 추가 더해서
n_inputs = 2 + 4
logdir = log_dir("logreg")

X = tf.placeholder(tf.float32, shape=(None,n_inputs + 1))
y = tf.placeholder(tf.float32, shape=(None,1))

y_proba, loss, train,loss_summary, init, saver = logistic_regression(X,y)

file_writer = tf.summary.FileWriter(logdir,tf.get_default_graph())
In [92]:
import os
n_epochs = 10001
batch_size = 50
n_batches = int(np.ceil(m / batch_size))

checkpoint_path = "tmp/my_logreg_model.ckpt"
checkpoint_epoch_path = checkpoint_path + ".epoch"
final_model_path = "./my_logreg_model"

with tf.Session() as sess:
    if os.path.isfile(checkpoint_epoch_path):
        # 체크포인트 파일이 있으면 모델을 복원후 에포크 횟수를 로드
        with open(checkpoint_epoch_path,'rb') as f:
            start_epoch = int(f.read())
        print('중지되었던 훈련, 에포크를 이어갑니다', start_epoch)
        saver.restore(sess,checkpoint_path)
    else:
        start_epoch = 0
        sess.run(init)
        
    for epoch in range(n_epochs):
        for batch_index in range(n_batches):
            X_batch,y_batch = minibatch(X_train_enhanced,y_train,batch_size)
            sess.run(train,feed_dict={X:X_batch,y:y_batch})
        loss_val,summary_str = sess.run([loss,loss_summary],feed_dict={X:X_test_enhanced,y:y_test})
        file_writer.add_summary(summary_str,epoch)
        if epoch % 500 == 0:
            print("Epoch : {0}, Loss : {1}".format(epoch,loss_val))
            saver.save(sess,checkpoint_path)
            with open(checkpoint_epoch_path,'wb') as f:
                f.write(b"%d" % (epoch+1) )
                
    saver.save(sess,final_model_path)
    y_proba_val = y_proba.eval(feed_dict={X:X_test_enhanced,y:y_test})
    os.remove(checkpoint_epoch_path)
    
Epoch : 0, Loss : 0.8115613460540771
Epoch : 500, Loss : 0.17495481669902802
Epoch : 1000, Loss : 0.1264137625694275
Epoch : 1500, Loss : 0.10192203521728516
Epoch : 2000, Loss : 0.08698955178260803
Epoch : 2500, Loss : 0.07678104192018509
Epoch : 3000, Loss : 0.06933686137199402
Epoch : 3500, Loss : 0.06365238130092621
Epoch : 4000, Loss : 0.05918924883008003
Epoch : 4500, Loss : 0.05551479384303093
Epoch : 5000, Loss : 0.052476271986961365
Epoch : 5500, Loss : 0.04990600049495697
Epoch : 6000, Loss : 0.04771680012345314
Epoch : 6500, Loss : 0.04583606868982315
Epoch : 7000, Loss : 0.04415738582611084
Epoch : 7500, Loss : 0.04266523942351341
Epoch : 8000, Loss : 0.04135194793343544
Epoch : 8500, Loss : 0.040161214768886566
Epoch : 9000, Loss : 0.0390775091946125
Epoch : 9500, Loss : 0.03810756281018257
Epoch : 10000, Loss : 0.03720899671316147
In [93]:
y_pred = (y_proba_val >= 0.5)
precision_score(y_test,y_pred)
Out[93]:
0.9797979797979798
In [94]:
recall_score(y_test,y_pred)
Out[94]:
0.9797979797979798
In [95]:
y_pred_idx = y_pred.reshape(-1) # 열 벡터 대신 1차원 배열
plt.plot(X_test[y_pred_idx, 1], X_test[y_pred_idx, 2], 'go', label="양성")
plt.plot(X_test[~y_pred_idx, 1], X_test[~y_pred_idx, 2], 'r^', label="음성")
plt.legend()
plt.show()
# 더 잘 분류한 것을 볼 수 있다. 
In [107]:
# 하이퍼파라미터 조정하기 
from scipy.stats import reciprocal

n_search_iteration = 10

for search_iteration in range(n_search_iteration):
    batch_size = np.random.randint(1,100)
    learning_rate = reciprocal(0.0001,0.1).rvs(random_state=search_iteration)
    
    n_inputs = 6
    logdir = log_dir('logreg')
    
    print("Repeat :", search_iteration)
    print("logdir : ",logdir)
    print("Batch_size",batch_size)
    print('learning_rate',learning_rate)
    print("Training :",end="")
    
    tf.reset_default_graph()
    
    X = tf.placeholder(tf.float32,shape=(None,n_inputs+1))
    y = tf.placeholder(tf.float32,shape=(None,1))
    
    y_proba, loss,train,loss_summary,init,saver = logistic_regression(X,y,
                                                                      learning_rate=learning_rate)
    file_writer = tf.summary.FileWriter(logdir,tf.get_default_graph())
    
    n_epochs=10001
    n_batches = int(np.ceil(m/batch_size))
    
    final_model_path = "./my_logreg_model_%d" % search_iteration
    
    with tf.Session() as sess:
        sess.run(init)
        
        for epoch in range(n_epochs):
            for batch_index in range(n_batches):
                X_batch,y_batch = minibatch(X_train_enhanced,y_train,batch_size)
                sess.run(train,feed_dict={X:X_batch,y:y_batch})
            loss_val, summary_str = sess.run([loss,loss_summary],feed_dict=
                                            {X:X_test_enhanced,y:y_test})
            file_writer.add_summary(summary_str,epoch)
            if epoch % 500 ==0:
                print(".",end="")
        
        saver.save(sess,final_model_path)
        
        print()
        y_proba_val = y_proba.eval(feed_dict={X:X_test_enhanced,y:y_test})
        y_pred = (y_proba_val >= 0.5)
        
        print("Precision : {0}, Recall : {1}".format(precision_score(y_test,y_pred),
                                                     recall_score(y_test,y_pred)))
Repeat : 0
logdir :  tf_logs/logreg-run-20190413142110/
Batch_size 12
learning_rate 0.004430375245218265
Training :.....................
Precision : 0.9797979797979798, Recall : 0.9797979797979798
Repeat : 1
logdir :  tf_logs/logreg-run-20190413142736/
Batch_size 7
learning_rate 0.0017826497151386947
Training :.....................
Precision : 0.9797979797979798, Recall : 0.9797979797979798
Repeat : 2
logdir :  tf_logs/logreg-run-20190413143819/
Batch_size 77
learning_rate 0.00203228544324115
Training :.....................
Precision : 0.9696969696969697, Recall : 0.9696969696969697
Repeat : 3
logdir :  tf_logs/logreg-run-20190413143927/
Batch_size 77
learning_rate 0.004491523825137997
Training :.....................
Precision : 0.9797979797979798, Recall : 0.9797979797979798
Repeat : 4
logdir :  tf_logs/logreg-run-20190413144031/
Batch_size 44
learning_rate 0.07963234721775589
Training :.....................
Precision : 0.9801980198019802, Recall : 1.0
Repeat : 5
logdir :  tf_logs/logreg-run-20190413144219/
Batch_size 77
learning_rate 0.0004634250583294876
Training :.....................
Precision : 0.8952380952380953, Recall : 0.9494949494949495
Repeat : 6
logdir :  tf_logs/logreg-run-20190413144324/
Batch_size 5
learning_rate 0.047706818419354494
Training :.....................
Precision : 0.9801980198019802, Recall : 1.0
Repeat : 7
logdir :  tf_logs/logreg-run-20190413145856/
Batch_size 58
learning_rate 0.0001694044709524274
Training :.....................
Precision : 0.8623853211009175, Recall : 0.9494949494949495
Repeat : 8
logdir :  tf_logs/logreg-run-20190413150001/
Batch_size 41
learning_rate 0.04171461199412461
Training :.....................
Precision : 0.9801980198019802, Recall : 1.0
Repeat : 9
logdir :  tf_logs/logreg-run-20190413150128/
Batch_size 91
learning_rate 0.00010742922968438615
Training :.....................
Precision : 0.8269230769230769, Recall : 0.8686868686868687


728x90
반응형