Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/framework/random_seed.py: 41%

44 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2015 The TensorFlow Authors. All Rights Reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================== 

15 

16"""For seeding individual ops based on a graph-level seed. 

17""" 

18 

19import weakref 

20 

21from tensorflow.python.eager import context 

22from tensorflow.python.framework import config 

23from tensorflow.python.framework import ops 

24from tensorflow.python.util import deprecation 

25from tensorflow.python.util.tf_export import tf_export 

26 

27 

28DEFAULT_GRAPH_SEED = 87654321 

29_MAXINT32 = 2**31 - 1 

30 

31_graph_to_seed_dict = weakref.WeakKeyDictionary() 

32 

33 

34def _truncate_seed(seed): 

35 return seed % _MAXINT32 # Truncate to fit into 32-bit integer 

36 

37 

38@tf_export(v1=['random.get_seed', 'get_seed']) 

39@deprecation.deprecated_endpoints('get_seed') 

40def get_seed(op_seed): 

41 """Returns the local seeds an operation should use given an op-specific seed. 

42 

43 Given operation-specific seed, `op_seed`, this helper function returns two 

44 seeds derived from graph-level and op-level seeds. Many random operations 

45 internally use the two seeds to allow user to change the seed globally for a 

46 graph, or for only specific operations. 

47 

48 For details on how the graph-level seed interacts with op seeds, see 

49 `tf.compat.v1.random.set_random_seed`. 

50 

51 Args: 

52 op_seed: integer. 

53 

54 Returns: 

55 A tuple of two integers that should be used for the local seed of this 

56 operation. 

57 """ 

58 eager = context.executing_eagerly() 

59 

60 if eager: 

61 global_seed = context.global_seed() 

62 else: 

63 global_seed = ops.get_default_graph().seed 

64 

65 if global_seed is not None: 

66 if op_seed is None: 

67 # pylint: disable=protected-access 

68 if hasattr(ops.get_default_graph(), '_seed_used'): 

69 ops.get_default_graph()._seed_used = True 

70 if eager: 

71 op_seed = context.internal_operation_seed() 

72 else: 

73 op_seed = _graph_to_seed_dict.setdefault(ops.get_default_graph(), 0) 

74 _graph_to_seed_dict[ops.get_default_graph()] += 1 

75 

76 seeds = _truncate_seed(global_seed), _truncate_seed(op_seed) 

77 else: 

78 if op_seed is not None: 

79 seeds = DEFAULT_GRAPH_SEED, _truncate_seed(op_seed) 

80 else: 

81 seeds = None, None 

82 

83 if seeds == (None, None) and config.is_op_determinism_enabled(): 

84 raise RuntimeError( # pylint: disable=g-doc-exception 

85 'Random ops require a seed to be set when determinism is enabled. ' 

86 'Please set a seed before running the op, e.g. by calling ' 

87 'tf.random.set_seed(1).') 

88 

89 # Avoid (0, 0) as the C++ ops interpret it as nondeterminism, which would 

90 # be unexpected since Python docs say nondeterminism is (None, None). 

91 if seeds == (0, 0): 

92 return (0, _MAXINT32) 

93 return seeds 

94 

95 

96@tf_export(v1=['random.set_random_seed', 'set_random_seed']) 

97def set_random_seed(seed): 

98 """Sets the graph-level random seed for the default graph. 

99 

100 Operations that rely on a random seed actually derive it from two seeds: 

101 the graph-level and operation-level seeds. This sets the graph-level seed. 

102 

103 Its interactions with operation-level seeds is as follows: 

104 

105 1. If neither the graph-level nor the operation seed is set: 

106 A random seed is used for this op. 

107 2. If the graph-level seed is set, but the operation seed is not: 

108 The system deterministically picks an operation seed in conjunction with 

109 the graph-level seed so that it gets a unique random sequence. Within the 

110 same version of tensorflow and user code, this sequence is deterministic. 

111 However across different versions, this sequence might change. If the 

112 code depends on particular seeds to work, specify both graph-level 

113 and operation-level seeds explicitly. 

114 3. If the graph-level seed is not set, but the operation seed is set: 

115 A default graph-level seed and the specified operation seed are used to 

116 determine the random sequence. 

117 4. If both the graph-level and the operation seed are set: 

118 Both seeds are used in conjunction to determine the random sequence. 

119 

120 To illustrate the user-visible effects, consider these examples: 

121 

122 To generate different sequences across sessions, set neither 

123 graph-level nor op-level seeds: 

124 

125 ```python 

126 a = tf.random.uniform([1]) 

127 b = tf.random.normal([1]) 

128 

129 print("Session 1") 

130 with tf.compat.v1.Session() as sess1: 

131 print(sess1.run(a)) # generates 'A1' 

132 print(sess1.run(a)) # generates 'A2' 

133 print(sess1.run(b)) # generates 'B1' 

134 print(sess1.run(b)) # generates 'B2' 

135 

136 print("Session 2") 

137 with tf.compat.v1.Session() as sess2: 

138 print(sess2.run(a)) # generates 'A3' 

139 print(sess2.run(a)) # generates 'A4' 

140 print(sess2.run(b)) # generates 'B3' 

141 print(sess2.run(b)) # generates 'B4' 

142 ``` 

143 

144 To generate the same repeatable sequence for an op across sessions, set the 

145 seed for the op: 

146 

147 ```python 

148 a = tf.random.uniform([1], seed=1) 

149 b = tf.random.normal([1]) 

150 

151 # Repeatedly running this block with the same graph will generate the same 

152 # sequence of values for 'a', but different sequences of values for 'b'. 

153 print("Session 1") 

154 with tf.compat.v1.Session() as sess1: 

155 print(sess1.run(a)) # generates 'A1' 

156 print(sess1.run(a)) # generates 'A2' 

157 print(sess1.run(b)) # generates 'B1' 

158 print(sess1.run(b)) # generates 'B2' 

159 

160 print("Session 2") 

161 with tf.compat.v1.Session() as sess2: 

162 print(sess2.run(a)) # generates 'A1' 

163 print(sess2.run(a)) # generates 'A2' 

164 print(sess2.run(b)) # generates 'B3' 

165 print(sess2.run(b)) # generates 'B4' 

166 ``` 

167 

168 To make the random sequences generated by all ops be repeatable across 

169 sessions, set a graph-level seed: 

170 

171 ```python 

172 tf.compat.v1.random.set_random_seed(1234) 

173 a = tf.random.uniform([1]) 

174 b = tf.random.normal([1]) 

175 

176 # Repeatedly running this block with the same graph will generate the same 

177 # sequences of 'a' and 'b'. 

178 print("Session 1") 

179 with tf.compat.v1.Session() as sess1: 

180 print(sess1.run(a)) # generates 'A1' 

181 print(sess1.run(a)) # generates 'A2' 

182 print(sess1.run(b)) # generates 'B1' 

183 print(sess1.run(b)) # generates 'B2' 

184 

185 print("Session 2") 

186 with tf.compat.v1.Session() as sess2: 

187 print(sess2.run(a)) # generates 'A1' 

188 print(sess2.run(a)) # generates 'A2' 

189 print(sess2.run(b)) # generates 'B1' 

190 print(sess2.run(b)) # generates 'B2' 

191 ``` 

192 

193 @compatibility(TF2) 

194 'tf.compat.v1.set_random_seed' is compatible with eager mode. However, 

195 in eager mode this API will set the global seed instead of the 

196 graph-level seed of the default graph. In TF2 this API is changed to 

197 [tf.random.set_seed] 

198 (https://www.tensorflow.org/api_docs/python/tf/random/set_seed). 

199 @end_compatibility 

200 

201 Args: 

202 seed: integer. 

203 """ 

204 if context.executing_eagerly(): 

205 context.set_global_seed(seed) 

206 else: 

207 ops.get_default_graph().seed = seed 

208 

209 

210@tf_export('random.set_seed', v1=[]) 

211def set_seed(seed): 

212 """Sets the global random seed. 

213 

214 Operations that rely on a random seed actually derive it from two seeds: 

215 the global and operation-level seeds. This sets the global seed. 

216 

217 Its interactions with operation-level seeds is as follows: 

218 

219 1. If neither the global seed nor the operation seed is set: A randomly 

220 picked seed is used for this op. 

221 2. If the global seed is set, but the operation seed is not: 

222 The system deterministically picks an operation seed in conjunction with 

223 the global seed so that it gets a unique random sequence. Within the 

224 same version of tensorflow and user code, this sequence is deterministic. 

225 However across different versions, this sequence might change. If the 

226 code depends on particular seeds to work, specify both global 

227 and operation-level seeds explicitly. 

228 3. If the operation seed is set, but the global seed is not set: 

229 A default global seed and the specified operation seed are used to 

230 determine the random sequence. 

231 4. If both the global and the operation seed are set: 

232 Both seeds are used in conjunction to determine the random sequence. 

233 

234 To illustrate the user-visible effects, consider these examples: 

235 

236 If neither the global seed nor the operation seed is set, we get different 

237 results for every call to the random op and every re-run of the program: 

238 

239 ```python 

240 print(tf.random.uniform([1])) # generates 'A1' 

241 print(tf.random.uniform([1])) # generates 'A2' 

242 ``` 

243 

244 (now close the program and run it again) 

245 

246 ```python 

247 print(tf.random.uniform([1])) # generates 'A3' 

248 print(tf.random.uniform([1])) # generates 'A4' 

249 ``` 

250 

251 If the global seed is set but the operation seed is not set, we get different 

252 results for every call to the random op, but the same sequence for every 

253 re-run of the program: 

254 

255 ```python 

256 tf.random.set_seed(1234) 

257 print(tf.random.uniform([1])) # generates 'A1' 

258 print(tf.random.uniform([1])) # generates 'A2' 

259 ``` 

260 

261 (now close the program and run it again) 

262 

263 ```python 

264 tf.random.set_seed(1234) 

265 print(tf.random.uniform([1])) # generates 'A1' 

266 print(tf.random.uniform([1])) # generates 'A2' 

267 ``` 

268 

269 The reason we get 'A2' instead 'A1' on the second call of `tf.random.uniform` 

270 above is because the second call uses a different operation seed. 

271 

272 Note that `tf.function` acts like a re-run of a program in this case. When 

273 the global seed is set but operation seeds are not set, the sequence of random 

274 numbers are the same for each `tf.function`. For example: 

275 

276 ```python 

277 tf.random.set_seed(1234) 

278 

279 @tf.function 

280 def f(): 

281 a = tf.random.uniform([1]) 

282 b = tf.random.uniform([1]) 

283 return a, b 

284 

285 @tf.function 

286 def g(): 

287 a = tf.random.uniform([1]) 

288 b = tf.random.uniform([1]) 

289 return a, b 

290 

291 print(f()) # prints '(A1, A2)' 

292 print(g()) # prints '(A1, A2)' 

293 ``` 

294 

295 If the operation seed is set, we get different results for every call to the 

296 random op, but the same sequence for every re-run of the program: 

297 

298 ```python 

299 print(tf.random.uniform([1], seed=1)) # generates 'A1' 

300 print(tf.random.uniform([1], seed=1)) # generates 'A2' 

301 ``` 

302 

303 (now close the program and run it again) 

304 

305 ```python 

306 print(tf.random.uniform([1], seed=1)) # generates 'A1' 

307 print(tf.random.uniform([1], seed=1)) # generates 'A2' 

308 ``` 

309 

310 The reason we get 'A2' instead 'A1' on the second call of `tf.random.uniform` 

311 above is because the same `tf.random.uniform` kernel (i.e. internal 

312 representation) is used by TensorFlow for all calls of it with the same 

313 arguments, and the kernel maintains an internal counter which is incremented 

314 every time it is executed, generating different results. 

315 

316 Calling `tf.random.set_seed` will reset any such counters: 

317 

318 ```python 

319 tf.random.set_seed(1234) 

320 print(tf.random.uniform([1], seed=1)) # generates 'A1' 

321 print(tf.random.uniform([1], seed=1)) # generates 'A2' 

322 tf.random.set_seed(1234) 

323 print(tf.random.uniform([1], seed=1)) # generates 'A1' 

324 print(tf.random.uniform([1], seed=1)) # generates 'A2' 

325 ``` 

326 

327 When multiple identical random ops are wrapped in a `tf.function`, their 

328 behaviors change because the ops no long share the same counter. For example: 

329 

330 ```python 

331 @tf.function 

332 def foo(): 

333 a = tf.random.uniform([1], seed=1) 

334 b = tf.random.uniform([1], seed=1) 

335 return a, b 

336 print(foo()) # prints '(A1, A1)' 

337 print(foo()) # prints '(A2, A2)' 

338 

339 @tf.function 

340 def bar(): 

341 a = tf.random.uniform([1]) 

342 b = tf.random.uniform([1]) 

343 return a, b 

344 print(bar()) # prints '(A1, A2)' 

345 print(bar()) # prints '(A3, A4)' 

346 ``` 

347 

348 The second call of `foo` returns '(A2, A2)' instead of '(A1, A1)' because 

349 `tf.random.uniform` maintains an internal counter. If you want `foo` to return 

350 '(A1, A1)' every time, use the stateless random ops such as 

351 `tf.random.stateless_uniform`. Also see `tf.random.experimental.Generator` for 

352 a new set of stateful random ops that use external variables to manage their 

353 states. 

354 

355 Args: 

356 seed: integer. 

357 """ 

358 set_random_seed(seed)