reniew's blog
cs and deep learning

CS20(TensorFlow) Lecture Note (5): word2vec + manage experiments

|

스탠포드의 TensorFlow 강의인 cs20 강의의 lecture note를 정리한 글입니다. 강의는 오픈되지 않아서 Lecture note, slide 위주로 정리된 글임을 참고 해주시길 바랍니다. 강의의 자세한 Syllabus 및 자료들을 아래 링크를 참고해 주세요.

CS20: TensorFlow for Deep Learning Research


Post list

5. word2vec + manage experiments

이때까지는 간단한 모델을 만드는 방법에 대해서 알아보았다. 이번 강의에서는 이전 보다는 좀 더 복잡한 모델인인 word2vec을 예제로 모델을 만들어 보도록 한다. 이번 모델을 만들면서 variable sharing, model sharing 그리고 manage our experiments에 대해서 알아보도록 할 것이다.

Word2vec

단어 임베딩을 하는 방법 중에서 가장 널리 알려지고 많이 사용되는 기술은 word2vec일 것이다. 내용에 대해서는 아마 대부분이 알고 있을 것이라 생각하고 자세한 내용은 설명하지 않는다. 만약 잘 모른다면 다음의 글들을 참고하자 : paper1, paper2, blog1, blog2

word2vec의 두 가지 모델(skip-gram, CBOW)중에서 이번 강의에서는 skip-gram 모델을 구현해보도록 한다.

Implementing word2vec

여기서는 Session을 사용할 것이다. eager를 사용하는 모델은 examples/04_word2vec_eager.py 파일을 참고하자.

우선은 우리가 사용할 라이브러리들을 임포트한다.

import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

import numpy as np
from tensorflow.contrib.tensorboard.plugins import projector
import tensorflow as tf

import utils
import word2vec_utils

word2vec_utils은 중간에 사용되는 몇 가지 기능들을 미리 만들어 놓은 파이썬 파일이다. 그리고 다음으로는 모델의 하이퍼 파라미터를 정의하자.

VOCAB_SIZE = 50000
BATCH_SIZE = 128
EMBED_SIZE = 128            # dimension of the word embedding vectors
SKIP_WINDOW = 1             # the context window
NUM_SAMPLED = 64            # number of negative examples to sample
LEARNING_RATE = 1.0
NUM_TRAIN_STEPS = 100000
VISUAL_FLD = 'visualization'
SKIP_STEP = 5000

DOWNLOAD_URL = 'http://mattmahoney.net/dc/text8.zip'
EXPECTED_BYTES = 31344016
NUM_VISUALIZE = 3000        # number of tokens to visualize

우선은 이제 데이터를 다운받고 tf.data를 정의해야 한다. 데이터의 구조에 대해서 먼저 설명하면, skip-gram에서는 input값은 중간의 단어가 되고 output은 단어 주변의 context 단어가 된다. 하지만 여기서 구현할 때는 단어 자체를 input으로 넣지 않고 흔한 단어들에 대해서 dictionary를 만들고 input은 중간 단어에 대한 vocabulary에서의 index값을 줄 것이다. 예를 들어 만약 vocabulary에서 1000번째 단어인 경우에는 input = 3 이 된다.

데이터를 다운로드하고, 각 데이터를 정해진 hyperparameter에 맞게 input 값인 인덱스들을 배치사이즈로 만들어 주는 함수를 미리 정의했다. 이 함수는 word2vec_utils.py에 정의되어 있으며 이 과정의 세부적인 내용은 해당 파이썬 파일을 참고하자.

여기서는 해당 함수를 사용해서 데이터를 tf.data로 불러온 후 iterator를 정의하자.

dataset = tf.data.Dataset.from_generator(gen,
                              (tf.int32, tf.int32),
                              (tf.TensorShape([BATCH_SIZE]), tf.TensorShape([BATCH_SIZE, 1])))

iterator = dataset.make_initializable_iterator()
center_words, target_words = iterator.get_next()

skip-gram모델에서의 파라미터는 매트릭스 형태인데, 이 매트릭스의 row vector가 단어 임베딩 벡터가 된다. 따라서 매트릭스의 크기는 [VOCAB_SIZE, EMBED_SIZE]가 된다. 해당 파라미터 매트릭스는 보통 random distribution을 따르도록 초기화하는데, 여기서는 uniform distribution을 따르도록 초기화 하자.

embed_matrix = tf.get_variable('embed_matrix',
                                shape=[VOCAB_SIZE, EMBED_SIZE],
                                initializer=tf.random_uniform_initializer())

skip-gram모델에서 단어는 원래 one-hot 인코딩 되어 있고 파라미터와 곱해질 떄 아래 그림 처럼 결국 특정 행만 계산된다. 결국 나머지는 모두 0이 됨에도 불구하고 모두 계산된다. TensorFlow에서는 이와 같은 문제를 해결하기 위한 함수인 tf.nn.embedding_lookup함수를 제공한다. 따라서 이 함수를 통해 batch의 단어들에 해당하는 row의 vector 값들만 사용 할 수 있다.

12

tf.nn.embedding_lookup함수의 구조는 아래와 같다.

tf.nn.embedding_lookup(
    params,
    ids,
    partition_strategy='mod',
    name=None,
    validate_indices=True,
    max_norm=None
)

따라서 위의 함수를 다음과 같이 사용한다.

embed = tf.nn.embedding_lookup(embed_matrix, center_words, name='embedding')

이제 loss함수를 정의해야 한다. loss함수로 NCE함수를 사용할 것이다. 이미 tf에서 이 함수를 제공하고 있으므로 사용하도록 하자. NCE함수는 아래와 같이 구성되어 있다.

tf.nn.nce_loss(
    weights,
    biases,
    labels,
    inputs,
    num_sampled,
    num_classes,
    num_true=1,
    sampled_values=None,
    remove_accidental_hits=False,
    partition_strategy='mod',
    name='nce_loss'
)

(위 함수의 인자 중에서 3번 째가 실제로는 input이고, 4번째가 label이다)

NCE loss를 사용하기 위해 nce_weight과 nce_bias를 따로 만들어 준 후 loss 함수를 정의하자.

nce_weight = tf.get_variable('nce_weight', shape=[VOCAB_SIZE, EMBED_SIZE],
                initializer=tf.truncated_normal_initializer(stddev=1.0 / (EMBED_SIZE ** 0.5)))
nce_bias = tf.get_variable('nce_bias', initializer=tf.zeros([VOCAB_SIZE]))

loss = tf.reduce_mean(tf.nn.nce_loss(weights=nce_weight,
                                        biases=nce_bias,
                                        labels=target_words,
                                        inputs=embed,
                                        num_sampled=NUM_SAMPLED,
                                        num_classes=VOCAB_SIZE), name='loss')

이제 optimizer만 정의하면된다. gradient descent optimizer를 사용한다.

optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)

이제 정의한 graph를 실행하면 된다. Session을 통해 실행하자.

sess.run(iterator.initializer)
    sess.run(tf.global_variables_initializer())

    total_loss = 0.0 # we use this to calculate late average loss in the last SKIP_STEP steps
    writer = tf.summary.FileWriter('graphs/word2vec_simple', sess.graph)

    for index in range(NUM_TRAIN_STEPS):
        try:
            loss_batch, _ = sess.run([loss, optimizer])
            total_loss += loss_batch
            if (index + 1) % SKIP_STEP == 0:
                print('Average loss at step {}: {:5.1f}'.format(index, total_loss / SKIP_STEP))
                total_loss = 0.0
        except tf.errors.OutOfRangeError:
            sess.run(iterator.initializer)
    writer.close()

여기까지 하면 tensorflow로 만든 word2vec 모델이 다 끝났다. 매우 짧은 코드만으로도 복잡한 모델인 word2vec의 skip-gram을 구현했다. 코드를 다시 보면 매우 간단하지만 다시 사용하기는 어려울 것이다. 그렇다면 어떻게 해야 다시 사용하기 쉽도록 코드를 구성할 것인가?

정답은 Class 구조로 만드는 것이다. 위의 코드들을 Class구조로 만들면 다음과 같이 구성된다.

import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

import numpy as np
from tensorflow.contrib.tensorboard.plugins import projector
import tensorflow as tf

import utils
import word2vec_utils

VOCAB_SIZE = 50000
BATCH_SIZE = 128
EMBED_SIZE = 128            # dimension of the word embedding vectors
SKIP_WINDOW = 1             # the context window
NUM_SAMPLED = 64            # number of negative examples to sample
LEARNING_RATE = 1.0
NUM_TRAIN_STEPS = 100000
VISUAL_FLD = 'visualization'
SKIP_STEP = 5000

DOWNLOAD_URL = 'http://mattmahoney.net/dc/text8.zip'
EXPECTED_BYTES = 31344016
NUM_VISUALIZE = 3000       


def word2vec(dataset):

    with tf.name_scope('data'):
        iterator = dataset.make_initializable_iterator()
        center_words, target_words = iterator.get_next()

    with tf.name_scope('embed'):
        embed_matrix = tf.get_variable('embed_matrix',
                                        shape=[VOCAB_SIZE, EMBED_SIZE],
                                        initializer=tf.random_uniform_initializer())
        embed = tf.nn.embedding_lookup(embed_matrix, center_words, name='embedding')

    with tf.name_scope('loss'):
        nce_weight = tf.get_variable('nce_weight', shape=[VOCAB_SIZE, EMBED_SIZE],
                        initializer=tf.truncated_normal_initializer(stddev=1.0 / (EMBED_SIZE ** 0.5)))
        nce_bias = tf.get_variable('nce_bias', initializer=tf.zeros([VOCAB_SIZE]))

        loss = tf.reduce_mean(tf.nn.nce_loss(weights=nce_weight,
                                            biases=nce_bias,
                                            labels=target_words,
                                            inputs=embed,
                                            num_sampled=NUM_SAMPLED,
                                            num_classes=VOCAB_SIZE), name='loss')


    with tf.name_scope('optimizer'):
        optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)

    utils.safe_mkdir('checkpoints')

    with tf.Session() as sess:
        sess.run(iterator.initializer)
        sess.run(tf.global_variables_initializer())

        total_loss = 0.0
        writer = tf.summary.FileWriter('graphs/word2vec_simple', sess.graph)

        for index in range(NUM_TRAIN_STEPS):
            try:
                loss_batch, _ = sess.run([loss, optimizer])
                total_loss += loss_batch
                if (index + 1) % SKIP_STEP == 0:
                    print('Average loss at step {}: {:5.1f}'.format(index, total_loss / SKIP_STEP))
                    total_loss = 0.0
            except tf.errors.OutOfRangeError:
                sess.run(iterator.initializer)
        writer.close()

def gen():
    yield from word2vec_utils.batch_gen(DOWNLOAD_URL, EXPECTED_BYTES, VOCAB_SIZE,
                                        BATCH_SIZE, SKIP_WINDOW, VISUAL_FLD)

def main():
    dataset = tf.data.Dataset.from_generator(gen,
                                (tf.int32, tf.int32),
                                (tf.TensorShape([BATCH_SIZE]), tf.TensorShape([BATCH_SIZE, 1])))
    word2vec(dataset)

if __name__ == '__main__':
    main()

코드가 조금더 길어졌지만, 이렇게 만듬으로써 이 모델을 재사용하기 쉬워졌다.

How to structure yout TensorFlow model

TensorFlow로 모델을 만드는 흐름에 대해서 다시 얘기해보자. 대부분의 코드들은 다음의 구조를 가질 것이다.

Phase1: assemble your graph

  1. 데이터 불러오기(tf.data or placeholder)
  2. 파라미터 정의
  3. inference 모델 정의
  4. loss 함수 정의
  5. optimizer 정의

Phase2: execute the computation

  1. 모든 변수 초기화
  2. 데이터 iterator, feed 초기화
  3. inference 모델 실행(각 input에 대해 학습한 결과 계산)
  4. cost계산
  5. 파라미터 갱신

대부분 위의 흐름을 크게 벗어나지 않을 것이다.

Variable Sharing

word2vec 모델을 TensorBoard로 그래프를 보면 다음과 같이 나온다.

gra

그래프를 보면 노드들이 다 흩어져 있는 것을 볼 수 있다. 만약 word2vec보다 조금 더 복잡한 모델이라면 그래프를 보기가 매우 어려울 것이다. 그렇다면 이런 그래프를 좀더 보기좋게 비슷한 것들끼리 그룹화를 할 수 있다면 어떨까? tf.name_scope를 사용하면 쉽게 grouping을 할 수 있다.

tf.name_scope는 다음과 같이 사용할 수 있다.

with tf.name_scope(name_of_that_scope):
	# declare op_1
	# declare op_2
	# ...

이전의 전체 python 코드를 보면 name_scope로 묶여있는 것을 볼 수 있다. 이렇게 묶은 후 TensorBoard로 그래프를 보면 아래와 같이 좀 더 명확하게 보기 쉽다.

graph

Variable Scpoe

TensorFlow를 사용하다보면 name_scope와 variable_scope를 언제 구분해서 사용하는지 의문이 들 때가 있다. 이번에는 variable_scope에 대해서 알아보자.

두 개의 input을 받고, 2 hidden layer를 가지는 신경망을 생각해보자. 그러면 아래와 같이 Neural Network를 정의하고 사용할 것이다.

def two_hidden_layers(x):
    assert x.shape.as_list() == [200, 100]
    w1 = tf.Variable(tf.random_normal([100, 50]), name="h1_weights")
    b1 = tf.Variable(tf.zeros([50]), name="h1_biases")
    h1 = tf.matmul(x, w1) + b1
    assert h1.shape.as_list() == [200, 50]  
    w2 = tf.Variable(tf.random_normal([50, 10]), name="h2_weights")
    b2 = tf.Variable(tf.zeros([10]), name="h2_biases")
    logits = tf.matmul(h1, w2) + b2
    return logits

logits1 = two_hidden_layers(x1)
logits2 = two_hidden_layers(x2)

TensorFlow는 함수를 실행할 때 마다 다른 variable집합을 만든다. 따라서 위의 two_hidden_layers()를 호출할 때마다 get_variable이 실행되서 새로운 variable을 만들 것이다. 따라서 중복으로 생성하기 때문에 아래와 같은 error message가 나온다.

ValueError: Variable h1_weights already exists, disallowed. Did you mean to set reuse=True or reuse=tf.AUTO_REUSE in VarScope?

이런 Variable의 중복을 방지하기 위해 VarScope를 사용한다.

def fully_connected(x, output_dim, scope):
    with tf.variable_scope(scope) as scope:
        w = tf.get_variable("weights", [x.shape[1], output_dim], initializer=tf.random_normal_initializer())
        b = tf.get_variable("biases", [output_dim], initializer=tf.constant_initializer(0.0))
        return tf.matmul(x, w) + b

def two_hidden_layers(x):
    h1 = fully_connected(x, 50, 'h1')
    h2 = fully_connected(h1, 10, 'h2')

with tf.variable_scope('two_layers') as scope:
    logits1 = two_hidden_layers(x1)
    scope.reuse_variables()
    logits2 = two_hidden_layers(x2)

위와 같이 작성하면 중복 error가 발생하지 않는다.

Graph collections

모델을 만들 때 variable을 graph의 서로 다른 부분에 같이 넣는 상황이 있을 수 있다. tf.get_collection을 사용하면 특정 variable 모음에 접근할 수 있게 한다.

tf.get_collection(
    key,
    scope=None
)

Default로 모든 variabls은 tf.GraphKeys.GLOBAL_VARIABLES에 들어가 있다. ‘my_scope’의 모든 variable들을 사용하려면 다음과 같이 사용할 수 있다.

tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='my_scope')

만약에 Variable중에서 옵션 중 trainable=True로 설정한 변수들 사용하고 싶으면, tf.GraphKeys.TRAINABLE_VARIABLES collection을 사용하면 된다.

Manage experiments

우리는 앞서 word2vec을 적은 데이터셋으로 만들어보고 결과도 나름 잘나오는 것을 확인했다. 하지만 실제로는 더 많은 데이터셋이 필요하고, 따라서 시간도 훨씬 많이 걸릴 것이다. 복잡한 모델일 수록 학습에 필요한 시간은 급격히 늘어날 것이다. 예를 들어 기계번역 분야는 하루정도는 최소 학습시켜야 하고 경우에 따라 더 많이 학습을 해야 한다.

이렇게 몇일씩 걸리는 모델을 학습하면 모델이 학습이 끝나기 전까지는 우리는 전혀 결과를 알 수 없다. 심지어 중간에 컴퓨터에 문제라도 발생하게 되면 결과를 확인조차 할 수 없다.

그리고 또 하나의 문제점은 모델에 대해서 실험할 때 여러 요인들을 바꿔가며 실험하는데 이러한 요소들에 따른 비교를 하기가 어렵다.

따라서 이번에는 우리가 모델을 실험할 때 사용할 수 있는 몇가지 기능들에 대해서 알아보도록 한다. 알아볼 것들은 tf.train.Saver(), TensorFlow’s random state, visualization에 대해서 알아보도록 할 것이다.

tf.train.Saver()

tf.train.Saver()를 사용하면 주기적으로 우리의 모델의 파라미터값들을 저장할 수 있다. graph의 변수들을 binary파일로 저장한다. 이 Class의 save함수는 다음과 같이 구성된다.

tf.train.Saver.save(
    sess,
    save_path,
    global_step=None,
    latest_filename=None,
    meta_graph_suffix='meta',
    write_meta_graph=True,
    write_state=True
)

예를 들어서 만약에 1000 step마다 변수들을 저장하고 싶다면 아래와 같이 작성하면 된다.

saver = tf.train.Saver()

with tf.Session() as sess:

    for step in range(training_steps):
	sess.run([optimizer])
	if (step + 1) % 1000 == 0:
	   saver.save(sess, 'checkpoint_directory/model_name', global_step=global_step)

보통 흔히 쓰는 말로 graph의 변수를 저장하는 step을 ‘checkpoint’라 표현한다. 코드를 보면 ‘global_step’이라는 변수가 새로 나와있는데, 이 값을 설정해주면 학습과정을 좀 더 명확히 이해할 수 있어 좋다. 선언시에는 학습이 되지 않도록 trainable=False로 지정하고 0으로 초기화한다.

global_step = tf.Variable(0, dtype=tf.int32, trainable=False, name='global_step')

그리고 global_step은 학습이 진행될 때마다 점점 증가해야 되는데 따로 연산을 만들 필요없이 optimizer에 옵션으로 넣어주면 자동으로 증가한다.

optimizer = tf.train.GradientDescentOptimizer(lr).minimize(loss,global_step=global_step)

저장해둔 checkpoint를 복구하기 위해서는 saver.restore()함수를 사용한다.

saver.restore(sess, 'checkpoints/skip-gram-10000')

checkpoint들이 저장되어 있는 directory에서 가장 최근의 checkpoint를 사용하고 싶으면 아래와 같이 작성하면 자동으로 가장 최신의 checkpoint를 찾을 수 있다.

ckpt = tf.train.get_checkpoint_state(os.path.dirname('checkpoints/checkpoint'))
if ckpt and ckpt.model_checkpoint_path:
     saver.restore(sess, ckpt.model_checkpoint_path)

따라서 모델을 만들 때 우선 checkpoint가 있는지 확인을 하고 있다면 그 checkpoint부터 다시 학습을 시작하면 된다. 따라서 기존의 word2vec 코드에서 checkpoint를 확인하고 사용하는 부분을 추가하면 다음과 같다.

saver = tf.train.Saver()

initial_step = 0
utils.safe_mkdir('checkpoints')

with tf.Session() as sess:
    sess.run(self.iterator.initializer)
    sess.run(tf.global_variables_initializer())

    # if a checkpoint exists, restore from the latest checkpoint
    ckpt = tf.train.get_checkpoint_state(os.path.dirname('checkpoints/checkpoint'))
    if ckpt and ckpt.model_checkpoint_path:
        saver.restore(sess, ckpt.model_checkpoint_path)

    writer = tf.summary.FileWriter('graphs/word2vec' + str(self.lr), sess.graph)

    for index in range(num_train_steps):
        try:
            sess.run(self.optimizer)
            # save the model every 1000 steps
            if (index + 1) % 1000 == 0:
                saver.save(sess, 'checkpoints/skip-gram', index)
        except tf.errors.OutOfRangeError:
            sess.run(self.iterator.initializer)

    writer.close()

기본적으로는 tf.Saver.save를 사용하면 모든 변수가 자동으로 저장된다. 보통은 이 방법을 사용하기를 추천하지만, 경우에 따라 몇개의 변수만 따로 저장하고 싶은 경우에도 사용할 수 있다. 특정 변수를 list 혹은 dictionary 형태로 Saver 객체의 인자로 설정하면 그 변수들만 저장된다. 아래의 예시를 참고하자.

v1 = tf.Variable(..., name='v1')
v2 = tf.Variable(..., name='v2')

saver = tf.train.Saver({'v1': v1, 'v2': v2})

saver = tf.train.Saver([v1, v2])

saver = tf.train.Saver({v.op.name: v for v in [v1, v2]})

tf.summary

보통 우리는 matplotlib을 사용해서 우리의 losses, accuracy를 시각화했는데, TensorFlow를 사용하면 그럴 필요가 없다. TensorBoard를 활용하면 우리의 요약된 자료들을 쉽게 시각화 해준다.

보통 시각화를 많이하는 값인 loss, average loss, accuracy를 시각화를 해보자. 시각화는 scalar plot, histogram, image 형태 모두 가능하다. 우선은 우리가 사용할 값들을 summary operation을 사용한뒤 하나의 namescope로 정의한다.

def _create_summaries(self):
     with tf.name_scope("summaries"):
            tf.summary.scalar("loss", self.loss)
            tf.summary.scalar("accuracy", self.accuracy)            
            tf.summary.histogram("histogram loss", self.loss)
            # because you have several summaries, we should merge them all
            # into one op to make it easier to manage
            self.summary_op = tf.summary.merge_all()

summary는 하나의 연산(operation)이므로 session으로 실행해줘야 한다.

loss_batch, _, summary = sess.run([model.loss, model.optimizer, model.summary_op],
                                  feed_dict=feed_dict)

Filewriter를 통해 summary를 write하면 TensorBoard를 통해 확인할 수 있다.

writer.add_summary(summary, global_step=step)

이제 TensorBoard를 명령 프롬프트로 실행시킨 후 http://localhost:6006 을 들어가보면 다음과 같이 확인 할 수 있다.

loss

histo

graph 폴더에서 두개의 sub-폴더를 만들어서 summary들을 저장하면 여러 모델 혹은 여러 하이퍼파라미터들에 따른 비교를 할 수 있다.

loss-

마지막으로 image로 표현하는 방법은 다음의 함수를 사용한다.

tf.summary.image(name, tensor, max_outputs=3, collections=None)

Control randomization

텐서플로우를 사용하다 보면, Random한 값을 사용해야 할 때가 많이 있을 것이다. random 값을 가질 수 있는 방법은 여러가지 있는데, 이런 random 값을 어느정도 제어할 수 있는 방법이 있다. seed를 사용하는 것인데 크게 두 가지 정도로 구분되어 사용된다.

  • random seed in operation level

operation 단계에서 random seed 를 할당하는 방법이다. 아래의 여러 예들을 보며 사용 방법을 익혀보자.

c = tf.random_uniform([], -10, 10, seed=2)

with tf.Session() as sess:
	print sess.run(c) # >> 3.57493
	print sess.run(c) # >> -5.97319
c = tf.random_uniform([], -10, 10, seed=2)

with tf.Session() as sess:
	print sess.run(c) # >> 3.57493

with tf.Session() as sess:
	print sess.run(c) # >> 3.57493
c = tf.random_uniform([], -10, 10, seed=2)
d = tf.random_uniform([], -10, 10, seed=2)

with tf.Session() as sess:
	print sess.run(c) # >> 3.57493
	print sess.run(d) # >> 3.57493
  • random seed at graph level with tf.Graph.seed

만약 a.pyb.py 두 파일이 똑같이 아래와 같은 코드로 구성되었을 때 실행시키면 어떻게 되는지 알아보자.

import tensorflow as tf

tf.set_random_seed(2)
c = tf.random_uniform([], -10, 10)
d = tf.random_uniform([], -10, 10)

with tf.Session() as sess:
	print sess.run(c)
	print sess.run(d)
$ python a.py
>> -4.00752
>> -2.98339

$ python b.py
>> -4.00752
>> -2.98339

이와 같이 seed를 graph 단계에서 주면 다른 파일이더라도 같은 값을 가진다.

Autodiff (how TensorFlow takes gradients)

텐서플로우는 자동 미분기능을 제공하는데, 우리가 명시적으로 사용하기 위한 함수도 존재한다. tf.gradients()를 사용하면 우리가 원하는 함수를 우리가 정한 변수로 미분할 수 있다. 함수는 아래와 같이 구성된다.

tf.gradients(ys, xs, grad_ys=None, name='gradients', colocate_gradients_with_ops=False, gate_gradients=False, aggregation_method=None)

ys는 미분할 함수이고, xs로 미분을 하겠다는 것이다. 그리고 여러 변수로 미분을 하거나 chain rule을 통해 미분도 가능하다. 아래의 예제를 보자.

x = tf.Variable(2.0)
y = 2.0 * (x ** 3)

grad_y = tf.gradients(y, x)
with tf.Session() as sess:
	sess.run(x.initializer)
	print sess.run(grad_y) # >> 24.0
x = tf.Variable(2.0)
y = 2.0 * (x ** 3)
z = 3.0 + y ** 2

grad_z = tf.gradients(z, [x, y])
with tf.Session() as sess:
	sess.run(x.initializer)
	print sess.run(grad_z) # >> [768.0, 32.0]
# 768 is the gradient of z with respect to x, 32 with respect to y

Comments