使用计算机执行随机事件操作中,随机函数是按照一定算法模拟产生的,其结果是可见的。故计算机产生的随机操作是被称为伪随机,随机可被复现。随机种子可以找到生成随机的规律以达到复现这次随机的操作。
我对随机种子的通俗理解就是:作为执行随机操作的带规则的暗号(一种暗号代表一种规则)。第一次执行随机操作设置seed暗号,计算机执行伪随机操作。第二次或者后面执行随机操作时,将seed暗号对上,就会寻找第一次随机操作的规律以复现第一次随机操作得到的结果。
那我们为什么要复现第一次的随机呢?
答案是要保留第一次执行的结果。试想一下,一个模型在不同的随机中会达到不同的预测结果,90%,89%,94%,……,执行同一代码会因为计算机模型随机的规则不同产生这么多伪随机,导致最终结果无法预测,使得预测精度永远无法到达最佳。如果将实验最佳的94%的规律保存保存起来,封装模型后就能使得每次执行该模型都会得到94%的预测精度。
依赖随机种子的操作是从两个种子中派生的:图形级别种子和操作级别种子
随机种子的作用范围比作全局和局部的作用范围,在此基础之上同一个随机种子还得考虑一个计数问题:
1.先考虑随机种子的作用范围(从全局变量和局部变量的角度思考),作用范围决定复现范围
2.同一个随机种子,在其作用范围下会进行计数操作,每次计数结果都会产生伪随机性
先解决随机种子中的计数问题:
(1)不设置随机种子
这种相当于操作时分配了随机种子,在分配种子的情况下,第一次操作计数1,第二操作计数2,但因为随机种子没有指定,所以无法复现这次随机操作。
种子计数
计数操作:随机种子系统分配,作用范围在程序执行中,所以两个打印中的随机操作是同一个种子的计数操作
import numpy as np
#随机种子系统分配,作用范围在程序执行中,所以两个打印中的随机操作是同一个种子的计数操作
print(np.random.rand(1, 5))#第一次计数
print(np.random.rand(1, 5))#第二次计数
结果:第一次随机,第二次随机
[[0.17051135 0.97529881 0.89418946 0.50140075 0.66125893]]
[[0.44943569 0.31041837 0.55052514 0.07441008 0.48824769]]
(2)放到函数中可以体现出,对应第一次和第二次的操作,随机种子因为没有被设置,随机种子的作用范围是在fun0()中,每次执行fun0()都会重新分配随机种子,两次执行函数的结果不同
**无法复现:**将随机函数放到fun0中,这里的随机种子的作用范围就只在fun0()中起作用,每次fun0()都会重新分配随机种子,所以两次执行相同函数无法复现
def fun0():
print(np.random.rand(1, 5))#第一次计数
print(np.random.rand(1, 5))#第二次计数
fun0()
fun0()
第一次执行函数结果
[[0.68932653 0.23582158 0.54445028 0.52733226 0.6735728 ]]
[[0.94994109 0.35874023 0.51411189 0.37284521 0.71009123]]
第二次执行函数结果
[[0.63869668 0.80899726 0.16438696 0.06589287 0.70395222]]
[[0.21223191 0.69264513 0.07276136 0.26785799 0.29842939]]
(3)在函数中设置随机种子,函数中在seed=1234下进行计数操作,两个打印结果不同
可以复现:相较于(2),给fun()函数增加1234的随机种子,每次执行fun()都是在seed=1234规律下进行,所以两次结果复现
def fun():
np.random.seed(1234)
print(np.random.rand(1, 5))#第一次计数
print(np.random.rand(1, 5))#第二次计数
fun()
fun()
第一次执行函数结果
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]]
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]]
第二次执行函数结果
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]]
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]]
第一次和第二次因为随机种子相同,故而结果复现。
种子范围:
(1)全局范围
np.random.seed(1234) #全局种子,全局复现
def fun2():
print(np.random.rand(1, 5))
print(np.random.rand(1, 5))
def fun3():
print(np.random.rand(1, 5))
print(np.random.rand(1, 5))
fun2()
fun2()
fun3()
fun3()
**范围:**这个种子的作用范围是全局,对于fun2(),fun3()的函数,只要执行random操作都是在这个范围下进行计数的,出现8个不同结果。
第一次跑结果:
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]]
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]]
[[0.35781727 0.50099513 0.68346294 0.71270203 0.37025075]]
[[0.56119619 0.50308317 0.01376845 0.77282662 0.88264119]]
[[0.36488598 0.61539618 0.07538124 0.36882401 0.9331401 ]]
[[0.65137814 0.39720258 0.78873014 0.31683612 0.56809865]]
[[0.86912739 0.43617342 0.80214764 0.14376682 0.70426097]]
[[0.70458131 0.21879211 0.92486763 0.44214076 0.90931596]]
但,这个函数无论怎么跑,都是会产生相同的8个结果,这就是因为全局的种子在起作用,全局复现.
再跑一次结果:
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]]
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]]
[[0.35781727 0.50099513 0.68346294 0.71270203 0.37025075]]
[[0.56119619 0.50308317 0.01376845 0.77282662 0.88264119]]
[[0.36488598 0.61539618 0.07538124 0.36882401 0.9331401 ]]
[[0.65137814 0.39720258 0.78873014 0.31683612 0.56809865]]
[[0.86912739 0.43617342 0.80214764 0.14376682 0.70426097]]
[[0.70458131 0.21879211 0.92486763 0.44214076 0.90931596]]
(2)局部范围 第一种
def fun2():
np.random.seed(1234)#1234种子,局部复现
print(np.random.rand(1, 5))
print(np.random.rand(1, 5))
def fun3():#1234种子,全局复现
print(np.random.rand(1, 5))
print(np.random.rand(1, 5))
fun2()
fun2()
fun3()
fun3()
这里的随机种子仅设置在fun2()范围内,所以,前两个结果不同是因为seed=1234下的计数操作,一二和三四相同是因为fun2()的两次执行都是seed=1234。
五六七八都不同,因为他不在复现范围内,数据在1234种子的计数操作中,能够在1234的全局复现。所以只要执行fun2()都会得到前两个结果。只要执行这个程序,得到的结果只有一种。
第一次跑结果:
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]] 1
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]] 2
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]] 1
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]] 2
[[0.35781727 0.50099513 0.68346294 0.71270203 0.37025075]] 3
[[0.56119619 0.50308317 0.01376845 0.77282662 0.88264119]] 4
[[0.36488598 0.61539618 0.07538124 0.36882401 0.9331401 ]] 5
[[0.65137814 0.39720258 0.78873014 0.31683612 0.56809865]] 6
再跑一次结果:
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]]
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]]
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]]
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]]
[[0.35781727 0.50099513 0.68346294 0.71270203 0.37025075]]
[[0.56119619 0.50308317 0.01376845 0.77282662 0.88264119]]
[[0.36488598 0.61539618 0.07538124 0.36882401 0.9331401 ]]
[[0.65137814 0.39720258 0.78873014 0.31683612 0.56809865]]
(3)局部范围 第二种
def fun2():
#随机种子,不会复现
print(np.random.rand(1, 5))
print(np.random.rand(1, 5))
def fun3():
np.random.seed(1234)#1234种子,局部复现
print(np.random.rand(1, 5))
print(np.random.rand(1, 5))
fun2()
fun2()
fun3()
fun3()
五六和七八相同叫复现,五六不同叫计数。一二三四是随机分配的种子的计数操作。
结果是再次运行操作时,一二三四会随机,五六七八还是不变
结果:
[[0.11615226 0.3427353 0.34684771 0.93112869 0.55646673]] *
[[0.53208177 0.39310779 0.97887637 0.19903748 0.5545923 ]] *
[[0.30238599 0.35561907 0.01597365 0.37605328 0.43928673]] *
[[0.97388219 0.6356066 0.33832807 0.62468604 0.22892785]] *
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]] 1
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]] 2
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]] 1
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]] 2
再次跑程序结果:
[[0.55843385 0.69376482 0.03878164 0.92269791 0.12950639]]
[[0.61162718 0.50353035 0.3889969 0.47456832 0.23182931]]
[[0.39501976 0.22830436 0.50689852 0.48358058 0.45633601]]
[[0.3627926 0.70313669 0.04387424 0.54884913 0.96772476]]
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]]
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]]
[[0.19151945 0.62210877 0.43772774 0.78535858 0.77997581]]
[[0.27259261 0.27646426 0.80187218 0.95813935 0.87593263]]