In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 공식 영문 문서의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 tensorflow/docs-l10n 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 docs-ko@tensorflow.org로 메일을 보내주시기 바랍니다.
텐서플로는 tf.random
모듈에서 유사 난수 생성기(pseudo random number generator, RNG)를 제공 합니다. 이 문서에서는 난수 생성기를 다루는 방법과 이 기능이 어떻게 다른 텐서플로의 서브 시스템과 상호작용 하는지 설명합니다.
텐서플로는 난수 생성 프로세스를 다루기 위한 두 가지 방식을 제공합니다:
tf.random.Generator
객체 사용을 통한 방식. 각 객체는 상태를 (tf.Variable
안에) 유지합니다. 이 상태는 매 숫자 생성때마다 변하게 됩니다.
tf.random.stateless_uniform
와 같은 순수-함수형으로 상태가 없는 랜덤 함수를 통한 방식. 같은 디바이스에서 동일한 인수를 (시드값 포함) 통해 해당 함수를 호출 하면 항상 같은 결과를 출력 합니다.
주의: tf.random.uniform
와 tf.random.normal
같은 구버전 TF 1.x의 RNG들은 아직 삭제되지 않았지만 사용을 권장하지 않습니다.
주의: 랜덤 함수는 텐서 플로 버전에 따라 동일함을 보장하지 않습니다. 참조: 버전 호환성
In [ ]:
import tensorflow as tf
# 분산 전략을 위해 2개의 가상 디바이스 cpu:0 and cpu:1 를 생성 합니다.
physical_devices = tf.config.experimental.list_physical_devices("CPU")
tf.config.experimental.set_virtual_device_configuration(
physical_devices[0], [
tf.config.experimental.VirtualDeviceConfiguration(),
tf.config.experimental.VirtualDeviceConfiguration()
])
tf.random.Generator
클래스각 RNG 호출마다 다른 결과를 생성하기 원할 경우 tf.random.Generator
클래스를 사용할 수 있습니다. 이는 내부 상태를 (tf.Variable
객체가 관리) 유지 합니다. 이 상태는 난수가 생성될때마다 업데이트 됩니다. 상태가 tf.Variable
에 의해 유지되기 때문에, 쉬운 체크포인팅(checkpointing), 자동적인 컨트롤 종속, 쓰레드(thread) 안전성의 장점이 있습니다.
클래스를 통해서 객체를 직접 생성하거나 전역 생성기를 반환하는 tf.random.get_global_generator()
를 호출하여 tf.random.Generator
를 사용할 수 있습니다. :
In [ ]:
g1 = tf.random.Generator.from_seed(1)
print(g1.normal(shape=[2, 3]))
g2 = tf.random.get_global_generator()
print(g2.normal(shape=[2, 3]))
객체를 생성하는데에는 여러 방법이 있습니다. 위에서 볼 수 있는것 처럼 가장 쉬운 방법은 Generator.from_seed
를 사용하는 것이며 이는 시드로부터 생성기를 생성 합니다. 시드는 0 이상의 정수입니다. from_seed
는 alg
를 추가적으로 전달 받을 수 있으며 이는 생성기가 사용할 RNG 알고리즘 입니다:
In [ ]:
g1 = tf.random.Generator.from_seed(1, alg='philox')
print(g1.normal(shape=[2, 3]))
추가적인 정보는 아래 알고리즘 섹션을 참고해 주세요.
생성기를 생성하는 다른 방법은 Generator.from_non_deterministic_state
를 사용하는 것 입니다. 이 방법을 통해서 생성된 생성기는 비결정 상태에서 시작 합니다. 이 상태는 시간과 운영 체제 등에 영향을 받습니다.
In [ ]:
g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
명백한 상태(explicit state)에서 생성기를 생성하는 방법 등 생성기를 초기화하는 방법은 여러가지가 있지만 해당 가이드에서는 다루지 않습니다.
전역 생성기를 사용하기 위해서 tf.random.get_global_generator
를 사용할 경우, 디바이스 환경에 유의해야 합니다. 전역 생성기는 tf.random.get_global_generator
가 처음 호출될때 (비결정 상태로) 생성되고 기본 디바이스에 배치됩니다. 예를 들어서 tf.random.get_global_generator
를 tf.device("gpu")
영역에서 처음으로 호출하였을 경우, 전역 생성기는 GPU에 할당 되며, 추후에 CPU에서 사용할시에 GPU-CPU간 복제를 하게 됩니다.
생성기를 다른 객체로 변경하기 위한 tf.random.set_global_generator
함수도 있습니다. 이 함수는 조심히 사용해야합니다. tf.function
가 이전의 생성기를 (약한 참조로) 사용하고 있을 수 있으며, 이를 변경하는 것은 가비지 콜렉션(garbage collection)을 발생시켜 tf.function
에 문제를 유발할 수 있습니다. 전역 생성기를 재설정 하는데에 더 좋은 방법은 Generator.reset_from_seed
와 같이 새로운 생성기를 생성하지 않는 "리셋" 함수를 사용하는 것 입니다.
In [ ]:
g = tf.random.Generator.from_seed(1)
print(g.normal([]))
print(g.normal([]))
g.reset_from_seed(1)
print(g.normal([]))
In [ ]:
g = tf.random.Generator.from_seed(1)
print(g.normal([]))
new_gs = g.split(3)
for new_g in new_gs:
print(new_g.normal([]))
print(g.normal([]))
split
은 RNG의 normal
과 같이 생성기의 (위의 예제에서는 g
) 상태를 변경 합니다. 서로 독립인것과 더불어 새로운 생성기는 (new_gs
) 또한 이전 생성기와 독립임을 보장 합니다 (g
).
새로운 생성기를 생성 하는 것은 디바이스간 복제의 오버헤드를 피하기 위해 사용하고 있는 생성기가 서로 다른 연산에서 동일한 디바이스에 있음을 확실히 하고 싶을때 유용합니다. 예를 들어:
In [ ]:
with tf.device("cpu"): # "cpu"를 사용하고 싶은 디바이스로 변경
g = tf.random.get_global_generator().split(1)[0]
print(g.normal([])) # 전역 생성기와는 다르게 g를 사용하는 것은 디바이스간 복제를 발생하지 않습니다.
참고: 이론적으로, split
대신에 from_seed
생성자(Constructor)를 사용할 수 있습니다. 그러나 이는 새로운 생성기가 전역 생성기에 독립임을 보장하지 않습니다. 또한 두 생성기의 시드가 동일하거나 랜덤 생성 스트림이 겹치는 시드를 생성하는 등의 위험이 있습니다.
split
을 분할(split)된 생성기를 통해서 호출하면 재귀적으로 분할할 수 있습니다. 재귀 깊이의 제한은 없지만, 오버플로우는 방지하도록 되어있습니다.
In [ ]:
g = tf.random.Generator.from_seed(1)
@tf.function
def foo():
return g.normal([])
print(foo())
사용자는 함수를 호출할때 생성기 객체가 여전히 살아 있음을 확인해야 합니다 (가비지 콜렉션이 되지 않아야 합니다).
In [ ]:
g = None
@tf.function
def foo():
global g
if g is None:
g = tf.random.Generator.from_seed(1)
return g.normal([])
print(foo())
print(foo())
In [ ]:
num_traces = 0
@tf.function
def foo(g):
global num_traces
num_traces += 1
return g.normal([])
foo(tf.random.Generator.from_seed(1))
foo(tf.random.Generator.from_seed(2))
print(num_traces)
In [ ]:
g = tf.random.Generator.from_seed(1)
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
def f():
print(g.normal([]))
results = strat.run(f)
이 사용법은 생성기의 디바이스가 복제에 따라 다르기 때문에 성능에 대한 이슈가 있습니다.
In [ ]:
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
try:
tf.random.Generator.from_seed(1)
except ValueError as e:
print("ValueError:", e)
Strategy.run
가 파라미터 함수를 전략 영역 안에서 암묵적으로 실행함을 유의해야합니다:
In [ ]:
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
def f():
tf.random.Generator.from_seed(1)
try:
strat.run(f)
except ValueError as e:
print("ValueError:", e)
In [ ]:
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
gs = tf.random.get_global_generator().split(2)
# to_args는 run함수를 위한 파라미터를 생성하는 API의 대안 입니다.
# 이는 추후에 API로 지원할 경우 교체될 예정 입니다.
def to_args(gs):
with strat.scope():
def f():
return [gs[tf.distribute.get_replica_context().replica_id_in_sync_group]]
return strat.run(f)
args = to_args(gs)
def f(g):
print(g.normal([]))
results = strat.run(f, args=args)
In [ ]:
print(tf.random.stateless_normal(shape=[2, 3], seed=[1, 2]))
print(tf.random.stateless_normal(shape=[2, 3], seed=[1, 2]))
상태가 없는 모든 RNG는 seed
파라미터를 필요로 합니다. 이 파라미터는 크기가 [2]
인 정수형 텐서입니다. 연산의 결과는 이 시드값에 의해 결정 됩니다.
이 알고리즘들에 대한 상세한 정보는 'Parallel Random Numbers: As Easy as 1, 2, 3' 논문을 참고해 주세요.