tf.stop_gradient 사용해서 학습시킬 가중치 조절해보기

2020. 3. 21. 21:51분석 Python/Tensorflow

광고 한번만 눌러주세요 ㅎㅎ 블로그 운영에 큰 힘이 됩니다.

하고 싶은 것은 아래 그림과 같다.
일단 인풋에서 Target1을 맞추는 Network1을 만든다. 그리고 Loss를 정하고, Optimizer를 설정한다.
그리고 Output1을 가지고 다시 Network2를 통해 Output2를 만든다. 그리고 Target2와 비교한다.
이때 내가 하고 싶은 것은 Output1을 학습할 때는 Network1의 파라미터만 학습을 시키고 싶고,
Output2를 도출할 때는 앞에 Network1을 freezing 시키고 나서, Network2의 Parameter만 학습시키고 싶다.

그래서 찾은 방법은 tf.stop_gradient였다. 

https://scelesticsiva.github.io/2018/01/22/stop-gradients/

 

Stop gradients in Tensorflow

This blog post is on how to use tf.stop_gradient to restrict the flow of gradients through certain parts of the network There are several scenerios that may arise where you have to train a particular part of the network and keep the rest of the network in

scelesticsiva.github.io

tf.stop_gradient는 network의 특정 파트만 학습시키고 싶을 때 사용할 수 있다. 
만약 내가 w1은 고정시키고 w2 만 학습시키고 싶다고 하자.
w1을 matrix 곱을 한 것을 tf.stop_gradient로 정지시키고 그것을 hidden이라 정한다.
다음에 w2를 matrix 곱을 해서 학습시키는 코드까지 만들면 다음과 같다.

x = tf.placeholder(tf.float32,[3,2])
y = tf.placeholder(tf.float32,[3,4])
w1 = tf.Variable(tf.ones([2,3]))
w2 = tf.Variable(tf.ones([3,4]))
hidden = tf.stop_gradient(tf.matmul(x,w1))
output = tf.matmul(hidden,w2)
loss = output - y
optimizer = tf.train.GradientDescentOptimizer(1).minimize(loss)

이제 실제로 그렇게 되는지 확인해보자!

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print("*****before gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())
    w1_,w2_,_ = sess.run([w1,w2,optimizer],feed_dict = {x:np.random.normal(size = (3,2)),y:np.random.normal(size = (3,4))})
    print("*****after gradient descent*****")
    print("w1---\n",w1_,"\n","w2---\n",w2_)

그러면 실제로 w1은 학습이 안되고, 오로지 w2 만 학습이 된다.

이와 같은 방법을 응용하여, 아래처럼 할 수 있게 간단한 예제를 만들어 봤다.

일단 output1을 만든다. 그다음에 tf.stop_gradient로 정지시키고, 다시 output2라는 network를 만든다.

import tensorflow as tf
import numpy as np
tf.reset_default_graph()
x = tf.placeholder(tf.float32,[3,2])
y1 = tf.placeholder(tf.float32,[3,3])
y2 = tf.placeholder(tf.float32,[3,4])
w1 = tf.Variable(tf.ones([2,3]),name="w1")
w2 = tf.Variable(tf.ones([3,4]),name="w2")
output1 = tf.matmul(x,w1)
########################################
hidden2 = tf.stop_gradient(output1)
output2 = tf.matmul(hidden2,w2)
loss1 = output1 - y1
loss2 = output2 - y2
t_var = tf.trainable_variables()
t_var

실제 우리가 학습시키고자 하는 변수는 총 2개이다.
확실하게 하기 위해서, 다음과 같이 할 수 있다.

## option1
optimizer1 = tf.train.GradientDescentOptimizer(1).minimize(loss1,var_list = [t_var[0]])
optimizer2 = tf.train.GradientDescentOptimizer(1).minimize(loss2,var_list = [t_var[1]])
## option2
optimizer1 = tf.train.GradientDescentOptimizer(1).minimize(loss1)
optimizer2 = tf.train.GradientDescentOptimizer(1).minimize(loss2)

>> 현재까지 하다가 발견한 것은 학습시킬 때 var_list로 지정만 해줘도... 앞에 gradient가 학습이 안 되는 것을 확인했다.. 즉, 굳이 stop_gradient를 할 필요가 없었다... 흐음 일단 계속 진행한다.

X = np.random.normal(size = (3,2))
Y1 = np.random.normal(size = (3,3))
Y2 = np.random.normal(size = (3,4))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print("*****before gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())
    for _ in range(3) :
        w1_,w2_,_ = sess.run([w1,w2,optimizer2],
                             feed_dict = {x:X,
                                          y2:Y2})
    print("*****after gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())
    for _ in range(3) :
        w1_,w2_,_ = sess.run([w1,w2,optimizer1],
                             feed_dict = {x:X,
                                          y1:Y1
                                          })
    print("*****after gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())
    for _ in range(3) :
        w1_,w2_,_ = sess.run([w1,w2,optimizer2],
                             feed_dict = {x:X,
                                          y2:Y2})
    print("*****after gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())

결과를 확인해보자.
보면, w2만 학습시키고 싶을 때는 w2 만 학습하고, w1을 학습시키고 싶을 때는 w1 만 학습하는 것을 확인하였다.

한 가지 좀 이상한 것은 eval 값과 sess.run을 해서 나오는 값이 달랐다.
다른 글을 찾아보면 기능은 똑같은 것 같은데, 왜 다른 값이 나오는지 모르겠다...

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print("*****before gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())
    for _ in range(3) :
        w1_,w2_,_ = sess.run([w1,w2,optimizer2],
                             feed_dict = {x:X,
                                          y2:Y2})
    
    print("*****after gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())
    print("something wrong!!")
    print("w1---\n",w1_,"\n","w2---\n",w2_)
    for _ in range(3) :
        w1_,w2_,_ = sess.run([w1,w2,optimizer1],
                             feed_dict = {x:X,
                                          y1:Y1
                                          })
    print("*****after gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())
    print("something wrong!!")
    print("w1---\n",w1_,"\n","w2---\n",w2_)
    for _ in range(3) :
        w1_,w2_,_ = sess.run([w1,w2,optimizer2],
                             feed_dict = {x:X,
                                          y2:Y2})
    print("*****after gradient descent*****")
    print("w1---\n",w1.eval(),"\n","w2---\n",w2.eval())
    print("something wrong!!")
    print("w1---\n",w1_,"\n","w2---\n",w2_)

흐음... 원인은 못 찾았다. 

-일단 끝-

728x90