난수 생성 - np.random.seed, np.random.RandomState
numpy.random 모듈은 python 내장 random 함수를 보강하여 다양한 종류의 확률 분포로부터 효과적으로 표본 값을 생성하는데 사용 가능하다.
numpy.random은 매우 큰 표본을 생성하는데 파이썬 내장 모듈보다 수십 배 빠르다.
numpy.random을 엄밀하게 말하면 유사 난수라 부르는데, 이는 난수 생성기의 시드값에 따라 정해진 난수를 알고리즘으로 생성하기 때문이다. 즉, 컴퓨터 프로그램에서 무작위 수를 구할때는 어떠한 특정 시작 숫자를 기준으로 컴퓨터가 정해진 알고리즘에 의해 마치 난수처럼 보이는 수열을 생성한다. 이러한 시작 숫자를 시드(seed)라고 한다. 또한 생성된 난수는 다음번 난수 생성을 위한 시드가 된다. 시드는 보통 자동으로 정해지지만 사람이 수동으로 정할 수 있다.
수동으로 시드를 정한다면 이후의 난수들을 예측할 수 있다. 이는 프로그램을 돌릴때 결과의 재현성을 위해 사용한다. 예를 들어 딥러닝 프로그램에 parameter를 특정 시드를 사용하여 정해준다면 parameter의 초기값에 의해 성능이 바뀌는 일이 일어나지 않는다.
Numpy의 난수 생성기의 시드값은 다음과 같이 변경 가능하다.
import numpy as np
np.numpy.seed(123)
이를 이용하여 numpy에서 제공하는 시드값은 전역 난수 시드값을 이용한다.
a = np.random.randn(5)
# 결과
array([-1.0856306 , 0.99734545, 0.2829785 , -1.50629471, -0.57860025])
b = np.random.randn(5)
# 결과
array([ 1.65143654, -2.42667924, -0.42891263, 1.26593626, -0.8667404 ])
다음의 과정을 본다면 시드를 123으로 준다면 처음 난수 결과값은 항상 -1.0856306이 나올 것이다. 다음 난수 결과값은 -1.0856306을 시드로 사용하여 생성한다.
np.random.seed(123)
a = np.random.randn(1)
# 결과
array([-1.0856306])
b = np.random.randn(1)
#결과
array([0.99734545])
다음과 같이 시드 123을 사용하여 난수을 2개 만들어보면 위의 a의 5개의 난수 값의 처음 두 개와 일치하는 것을 볼 수 있다.
여기에서 문제는 np.random.seed를 사용하여 난수를 생성한다면 앞으로 numpy를 사용하여 만드는 모든 난수값이 시드의 값에 영향을 받게 된다. 이는 서로 다른 시드를 사용하고 싶을 때 고려해줄 것이 많게 된다. 따라서 난수 생성기로부터 격리된 난수 생성기를 만들고 싶다면 다음과 같은 코드를 사용하면 된다.
np.random.seed(123)
rns = np.random.RandomState(123)
a = np.random.randn(5)
#결과
array([-1.0856306 , 0.99734545, 0.2829785 , -1.50629471, -0.57860025])
rns.randn(5)
#결과
array([-1.0856306 , 0.99734545, 0.2829785 , -1.50629471, -0.57860025])
np.random.RandomState를 사용한다면 rns라는 object가 생기며 이를 통해 난수에 접근 가능하다. 즉 특정 시드값을 기준으로 생성되는 난수가 필요할 때만 접근 가능하다.
예를 들어 전역 시드값을 설정해주는 np.random.seed(123)을 주었으면 위에서 보았듯이 난수를 설정하는데 있어 초기값은 -1.0856306이 나와야 하며 a를 통해 난수 5개를 만들었을때 이를 확인 가능하다. 또한 random.RandomState를 통해 생성한 rns난수생성기를 통해 생성된 5개의 난수를 보면 a의 결과와 같은 것을 확인 가능하다. 이는 서로 다르게 시드값이 적용되는 것을 의미한다.