一、TF2 常用命令

TF2 常用命令

1、模型保存

1.1、方式一:直接save为tf2.0版本的模型
model.save(【model_save_path】, save_format='tf')
1.2、变量Checkpoint格式的保存和恢复
1.2.1 tf.train.Checkpoint(optimizer=optimizer, model=model)

参考:https://tf.wiki/zh_hans/basic/tools.html#zh-hans-chechpoint

1.2.2 tf.train.CheckpointManager
 tf.train.CheckpointManager(checkpoint, directory='./save', checkpoint_name='model.ckpt', max_to_keep=k)

directory 参数为文件保存的路径, checkpoint_name 为文件名前缀(不提供则默认为 ckpt ),
max_to_keep 为保留的 Checkpoint 数目

1.3 Checkpoint 中变量参数的获取
from tensorflow.python.training import py_checkpoint_reader
# load checkpoint
checkpoint_path = '../1pretrain_models/albert_zh_tiny/albert_model.ckpt'
# print(path.getcwdu())
print(checkpoint_path)
# read data from checkpoint file
reader = py_checkpoint_reader.NewCheckpointReader(checkpoint_path)
var_to_shape_map = reader.get_variable_to_shape_map() # 变量映射map
1.4 已建立TF2模型的变量获取
from transformers import BertTokenizer,TFAlbertModel,AlbertConfig, AlbertTokenizer
tfalbert = TFAlbertModel(albert_config)
tf.convert_to_tensor([[1, 2, 3, 4]])
tfalbert.summary()
print(tfalbert.variables)	# 输出所有变量
for var in tfalbert.variables:
    print(var.name, end=',')
    print(var.shape)
    print(var)   # var 即为当前变量对象本身

变量赋值:

# 这里综合了上述1.3 和1.4的例子
tfablert_variables.get('tf_extend_albert_model/albert/embeddings/token_type_embeddings/embeddings:0').assign(reader.get_tensor('bert/embeddings/token_type_embeddings'))

2、模型创建

3、模型训练

4、模型评估

5、tf.strings

tf.strings.lower(text) # 文本转换为小写
tf.strings.regex_replace(text, '<br />', ' ') # 正则替换,将<br />替换为空格。
tf.strings.split(line, '\t') # 按照\t切分文本
tf.strings.to_number('1')	# 将字符串转换为数值,应该是float,可借助tf.cast(a, int32)进行转化

6、GPU使用

1、系统层面设置可用显卡

os.environ['CUDA_VISIBLE_DEVICES'] = '5, 6'
gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
tf.config.experimental.set_visible_devices(devices=gpus[0:2], device_type='GPU')

7、tf.xx方法【工具包】

7.1 常用整理
7-1  常用整理
1) tf.expand_dims(【矩阵数据】, axis=0)     # 扩展维度,axis位置处增加一维
2) tf.eye(num_rows, num_columns=None, batch_shape=None, dtype=dtypes.float32, name=None)
	示例:
		tf.eye(3, 4)
		>> <tf.Tensor: shape=(3, 4), dtype=float32, numpy=
			array([[1., 0., 0., 0.],
			       [0., 1., 0., 0.],
			       [0., 0., 1., 0.]], dtype=float32)>

3) tf.Variable(self,
               initial_value=None,
               trainable=None,
               validate_shape=True,
               caching_device=None,
               name=None,
               variable_def=None,
               dtype=None,
               import_scope=None,
               constraint=None,
               synchronization=VariableSynchronization.AUTO,
               aggregation=VariableAggregation.NONE,
               shape=None)
	解释:
		tf中变量的声明,参数挺多。
	
	示例:
		x = tf.Variable([[1,2], [3, 4]], dtype=tf.float32)
		>> <tf.Variable 'Variable:0' shape=(2, 2) dtype=float32, numpy=
			array([[1., 2.],
			       [3., 4.]], dtype=float32)>
	方法:
		x.numpy() 	# 获取变量值,结果是numpy中的类型的。
4) tf.reshape(tensor, shape, name=None)
	解释:
		reshape一个tensor,改变张量的形状。
	示例:
		t1 = [
			[1, 2, 3],
	  	 	[4, 5, 6]
	  	 ]
	  	 tf.reshape(t1, [6])
	  	 >> <tf.Tensor: shape=(6,), dtype=int32, numpy=array([1, 2, 3, 4, 5, 6], dtype=int32)>
	
	注:`tf.reshape其本质上不会改变张量元素的存储顺序,所以,该操作实际上非常迅速,并且是可逆的。`

5) tf.squeeze (input, axis=None, name=None)
	解释:
		移除input中维度为1维度。也可以通过axis指定某个为1的维度。
	示例:
		a shape: [1, 2, 1, 3, 4]
		tf.squeeze(a)
		a.shape
	  	 >> TensorShape([2, 3, 4])
	
	注:`tf.squeeze 其本质上不会改变张量元素的存储顺序,所以,该操作实际上非常迅速,并且是可逆的。`

6) tf.expand_dims(input, axis, name=None)
	解释:
		在input的axis维处,扩增一个维度。
	示例:
		a shape: [1, 2, 1, 3, 4]
		tf.expand_dims(a, axis=0)
		a.shape
	  	 >> TensorShape([1, 1, 2, 3, 4])
		tf.expand_dims(a, axis=2)
		a.shape
	  	 >> TensorShape([1, 1, 1, 2, 3, 4])
	
	注:`tf.expand_dims其本质上不会改变张量元素的存储顺序,所以,该操作实际上非常迅速,并且是可逆的。`
	`tf.transpose可以交换张量的维度,与tf.reshape,tf.squeeze,tf.expand_dims不同,它会改变张量元素的存储顺序。`
7.2 tf.concat
7-2  tf.concat(values, axis, name='concat') 
	解释:矩阵的拼接,这和numpy中的np.concatenate()用法是一样的。
	如下示例,axis=0,表示在第一个维度的拼接;axis=1,表示在第二个维度的拼接;axis=-1,表示在最后一个维度
	的拼接。通过结果可以发现,张量维度的变化都是在axis维的变化,比如a为(2, 3, 4, 5),经过axis=0的拼接后,结果的shape为(6, 3, 4, 5)即(a.shape[0]+b.shape[0]+c.shape[0], 3, 4, 5)
	可参考:
		https://sixiangdefairy.blog.csdn.net/article/details/89390832

	--values 矩阵,一般为张量
	--axis   拼接维度
	--name 	 拼接操作名
	
	示例讲解:
		a shape:(2, 3, 4, 5),
		b shape:(2, 3, 4, 5),
		c shape:(2, 3, 4, 5)
		那么:
		tf.concat([a, b, c], axis=0)	axis = -4 >> [2*3, 3, 4, 5]
			解释:其实将正常的单个列表的维度上,将下标为axis的维度 乘以 需要拼接张量的数量即可。
		tf.concat([a, b, c], axis=1)	axis = -3 >> [2, 3*3, 4, 5]
		tf.concat([a, b, c], axis=2)	axis = -2 >> [2, 3, 4*3, 5]
		tf.concat([a, b, c], axis=3)	axis = -1 >> [2, 3, 4, 5*3]
		综上所述,tf.concat其实相对于原数组中元素并没有改变原矩阵的维度大小,同时确定了axis的取值为[-4, 4)
		注意:其实,当a,b,c的中间维度不一致时,下列的表达方式更为准确:
		tf.concat([a, b, c], axis=0)	axis = -4 >> [a.shape[0]+b.shape[0]+c.shape[0], 3, 4, 5]
		tf.concat([a, b, c], axis=1)	axis = -3 >> [2, a.shape[1]+b.shape[1]+c.shape[1], 4, 5]
		...
7.3 tf.stack
7-3	 stack(values, axis=0, name="stack")
	解释:
		其实和tf.concat都是对多个张量进行合并,但是tf.stack是堆叠,会增加维度;而tf.concat是拼接,不会增加维度。
		其实,大家对于tf.concat和tf.stack的维度变换其实很不理解,大家其实可以参考下:
			https://sixiangdefairy.blog.csdn.net/article/details/89388103
		就很简单的理解了。
	
	示例讲解:
		a shape:(2, 3, 4, 5),
		b shape:(2, 3, 4, 5),
		c shape:(2, 3, 4, 5)
		那么:
		tf.stack([a, b, c], axis=0)   或 axis = -5 	>> [3, 2, 3, 4, 5]
			解释:其实正常把最外层列表加上的维度是[3, 2, 3, 4, 5], 然后其实本函数的本质就是将列表维度3放到下标为axis的位置。
		tf.stack([a, b, c], axis=1)   或 axis = -4 	>> [2, 3, 3, 4, 5]
		tf.stack([a, b, c], axis=2)   或 axis = -3  >> [2, 3, 3, 4, 5]
		tf.stack([a, b, c], axis=3)   或 axis = -2  >> [2, 3, 4, 3, 5]
		tf.stack([a, b, c], axis=4)   或 axis = -1  >> [2, 3, 4, 5, 3]
		综上所述,tf.stack其实是相对于原数组中的每个元素是增加了一个维度的,同时确定了axis的取值为:[-5, 5)
7.4 tf.split
7-4	 split(value, num_or_size_splits, axis=0, num=None, name="split")
		解释:
			tf.split是tf.concat的逆运算,可以指定分割份数平均分割,也可以通过指定每份的记录数量进行分割。
			可参考:
				https://sixiangdefairy.blog.csdn.net/article/details/89388140
		示例讲解:
			a shape:(4, 5, 6, 10)
			那么:
				tf.split(a, 2, axis=0)  或 axis = -4 >> 2 * [4/2, 5, 6, 10]
				tf.split(a, 5, axis=1)  或 axis = -3 >> 5 * [4, 5/5, 6, 10]
				tf.split(a, 2, axis=2)  或 axis = -2 >> 2 * [4, 5, 6/2, 10]
				tf.split(a, 2, axis=3)  或 axis = -2 >> 2 * [4, 5, 6, 10/2]
			综上所述,tf.split其实和tf.concat一样,并没有改变维度大小,同时确定了axis的取值为:[-4, 4),
			结果其实是:num_or_size_splits * [维度1, 维度2/num_or_size_splits , 维度3, 维度4],这
			儿假设axis=1或-3
		
		这儿主要解释了tf.split的维度变换,其实num_or_size_splits还可以使用列表的方式,指定分割的数量,比
		如:
			split0, split1, split2 = tf.split(a, [2, 5, 4], axis=3)
		
		注:该方法,返回的是一个列表,列表的数量为
			num_or_size_splits if type(num_or_size_splits)==int else len(num_or_size_splits)
		每个元素的维度,均与原维度保持一致。
7.5 tf.unstack
7-5   unstack(value, num=None, axis=0, name="unstack")
	  解释:
	  	很明显,这个正好和unstack相反,同时解析结果会进行降1维。
	  	-- axis 表示需要分解的维度
	  
	  示例:
	  	a shape: [2, 3, 4, 5]
	  	那么:
	  		a.unstack(a, axis=0) 或  axis = -4  >> [2, 3, 4, 5] >> 2 * [3, 4, 5]
	  			解释:其实就是将shape中维度为axis的值提前,其他shape顺延即可。
	  		a.unstack(a, axis=1) 或  axis = -3  >> [3, 2, 4, 5] >> 3 * [2, 4, 5]
	  		a.unstack(a, axis=2) 或  axis = -2  >> [4, 2, 3, 5] >> 4 * [2, 3, 5]
	  		a.unstack(a, axis=3) 或  axis = -1  >> [5, 2, 3, 4] >> 5 * [2, 3, 4]
	  	综上所述,tf.stack其实是降低了一个维度的,同时确定了axis的取值为:[-4, 4)
	  	
	  	注意:最终返回的结果是一个列表,每个元素的维度相对于原维度都降了一维。
7.6 tf.transpose
7-6  tf.transpose(a, perm=None, conjugate=False, name="transpose")
	解释:
		矩阵的维度转换。
		可参考:
			https://sixiangdefairy.blog.csdn.net/article/details/89388060
		底层实现原理:		
			# 通过底层实现tf.transpose()效果
			test_data = np.arange(1, 121).reshape([2, 3, 4, 5])
			a, b, c, d = test_data.shape
			print(a, b, c, d)
			# 底层遍历实现各维度转置
			perm = [0, 1, 3, 2]
			target_data = np.zeros([2, 3, 5, 4])
			for i in range(c):
			    for j in range(d):
			        target_data[:, :, j, i] = test_data[:, :, i, j]
			print('', target_data)
			print(target_data.shape)
			 
			# through tf.transpose() to get the result
			tr_0_1_3_2 = tf.transpose(test_data, perm)
			tr_0_1_3_2 = sess.run(tr_0_1_3_2)
			print(target_data == tr_0_1_3_2)    # 通过比较底层实现结果和tf.transpose验证结果
			# [[[[ True  True  True  True]
			#    [ True  True  True  True]
			#    [ True  True  True  True]
			#    [ True  True  True  True]
			#    [ True  True  True  True]] 。。。]]
		
		通过上面的维度变换,应该首先明白任何矩阵的任何维度,只有最后一个维度是有数据的,那就好理解了,通过
		perm指定维度结构,然后通过两两维度变换,保证最后一维指定位置的数据不变即可。大家可能现在有疑问,举个
		例子,[3,2][2,3]的数据是一样的,是不是就可以理解了。
		
		示例解释:
			主要辅助理解维度变换
			a shape: [2, 3, 4, 5]
			a.transpose(a, perm=[0, 1, 2, 3]) >> [2, 3, 4, 5]
				解释:
					原理其实很简单,就是把perm中的数字当做a.shape的下标,然后再perm中按照a.shape下标的值
					进行赋值,是不是就简单明了了。
			a.transpose(a, perm=[1, 0, 2, 3]) >> [3, 2, 4, 5]
			a.transpose(a, perm=[1, 2, 0, 3]) >> [3, 4, 2, 5]
			a.transpose(a, perm=[1, 2, 3, 0]) >> [3, 4, 5, 2]
			...
			这些变换很多,其实是一组下标值的全排列。
			综上所述,首先变换前后维度没有变换,其次大家如果了解tf.stack其实,两者有相似之处,可参考:
				https://sixiangdefairy.blog.csdn.net/article/details/89388103
				# 总结:
				#     tf.stack() 中 stacks shape: 2 * (维1,维2, 维3, 维4)可理解为:(2, 维1,维2, 
				# 维3, 维4)
				#     当axis=0时, 就相当于tf.transpose(stacks, [0, 1, 2, 3, 4])
				#     当axis=1时, 就相当于tf.transpose(stacks, [1, 0, 2, 3, 4])
				#     当axis=2时, 就相当于tf.transpose(stacks, [1, 2, 0, 3, 4])
				#     当axis=3时, 就相当于tf.transpose(stacks, [1, 2, 3, 0, 4])
				#     当axis=0时, 就相当于tf.transpose(stacks, [1, 2, 3, 4, 0])
				这儿值都是一样的,大家可以运行验证一下。
				
	注:`tf.transpose可以交换张量的维度,与tf.reshape不同,它会改变张量元素的存储顺序。`
7.7 tf.constant
7-7  constant(value, dtype=None, shape=None, name="Const")
	解释:
		创建一个常量的张量。可以作为将numpy或者数组等转为张量的方法。
	
	示例:
	tf.constant([1, 2, 3, 4, 5, 6])
 	>> <tf.Tensor: shape=(6,), dtype=int32,numpy=array([1, 2, 3, 4, 5, 6], dtype=int32)>
			
7.8 tf.range
7-8  range(start, limit=None, delta=1, dtype=None, name="range")
	解释:
		创建一个数序列的张量。取值范围[start, limit)
		-- start : 序列的开始
		-- limit : 序列的结束,不包括
		-- delta : 序列中数值之间的间隔
	
	示例:
	tf.range(start=3, limit=18, delta=3)
 	>> <tf.Tensor: shape=(5,), dtype=int32, numpy=array([ 3,  6,  9, 12, 15], dtype=int32)>
	tf.range(start=3, limit=1, delta=-0.5)
	>> 	<tf.Tensor: shape=(4,), dtype=float32, numpy=array([3. , 2.5, 2. , 1.5], dtype=float32)>
7.9 tf.linspace
7-9  lin_space(start, stop, num, name=None)
	解释:
		将start到stop划分为num等份。取值范围[start, stop]
		-- start : 序列的开始
		-- stop  : 序列的结束,包括
		-- num   : 序列划分等份的数量
		注:start和stop的数据类型必须是`bfloat16`, `half`, `float32`, `float64`其中之一。
			num的数据类型必须是`int32`, `int64`其中之一。
	
	示例:
	tf.linspace(10.0, 12.0, 3, name="linspace") 
 	>> [ 10.0  11.0  12.0]
7.10 tf.zeros
7-9  zeros(shape, dtype=dtypes.float32, name=None)
	解释:
		创建一个所有元素都是零的矩阵。
	
	示例:
	tf.zeros([3, 4], tf.int32)
 	>> <tf.Tensor: shape=(3, 4), dtype=int32, numpy=
		  array([[0, 0, 0, 0],
		         [0, 0, 0, 0],
		         [0, 0, 0, 0]], dtype=int32)>
7.10 tf.zeros_like
7-9  zeros_like(input, dtype=None, name=None)
	解释:
		创建一个所有元素都是零的矩阵, 其shape和input的shape保持一致。
	
	示例:
	tensor = tf.constant([[1, 2, 3], [4, 5, 6]])
	tf.zeros_like(tensor)
 	>> <tf.Tensor: shape=(2, 3), dtype=int32, numpy=
	    array([[0, 0, 0],
	           [0, 0, 0]], dtype=int32)>
7.11 tf.gather
7-9  tf.gather(params, indices, validate_indices=None, axis=None, batch_dims=0, name=None)
	解释:
		不规则切片的一种。
			
	示例:
		a shape: [5, 6, 7, 8]
		同时获取:[0, :, :, :],[2, :, :, :],[4, :, :, :]
		使用:tf.gather(a, [0, 2, 4], axis=0)
		同时获取:[:, 0, :, :],[:, 2, :, :],[:, 4, :, :]
		使用:tf.gather(a, [0, 2, 4], axis=1)
	
		---------------------
		也可以同时获取:
		[0, :, 1, :], [1, :, 1, :], [2, :, 1, :]
		[0, :, 2, :], [1, :, 2, :], [2, :, 2, :]
		[0, :, 4, :], [1, :, 4, :], [2, :, 4, :]
		即:
			tf.gather(tf.gather(scores, [0, 1, 2], axis=0), [1, 2, 4], axis=-2)
	
	答疑:为什么不直接使用a[:, 0:3, :, :]?
		确实张量是可以直接通过切片的方式获取值的,但是他有局限性,即只能获取固定区间列的或者固定值列的内容,
		对于上述示例要求获取0, 2, 4,并不可以实现。
	
	综上所述,通过`tf.gather获取的数据的维度和原维度保持一致`,所以可以实现嵌套使用的方式。
	
7.12 tf.gather_nd
7-12  tf.gather_nd(params, indices, batch_dims=0, name=None)
	解释:
		不规则切片的一种。为了弥补tf.gather的缺点。
			
	示例:
		a shape: [5, 6, 7, 8]
		同时获取:
		[0, 1, :, :], [1, 2, :, :], [2, 3, :, :]
		即:
			b = tf.gather_nd(scores, indices=[(0, 1), (1, 2), (2, 3)])
			b.shape
			>> TensorShape([3, 7, 8])
	
	`答疑`:为什么不直接使用tf.gather?
		通过当前示例和tf.gather的示例2可以发现,tf.gather只能局限的获取到保持一个维度不同,另外的维度必须
		是相同的,而tf.gather_nd就解决了这个问题,直接录入对应下标即可。
	
	`缺陷`:
		通过上述例子,虽然说解决了tf.gather的部分问题,其实,对于维度跳跃式的数据获取,并不能实现。比如:需
		要获取[0, 1, :, 3], [1, 2, :, 3], [2, 3, :, 4],比较无力。
		所以,这儿就需要引入下面的tf.boolean_mask,虽然解决,但是使用不太方便。
	
	综上所述,通过`tf.gather_nd获取的维度会改变,结果维度是 a.dims - len(indices[0]) + 1`,即和indices中需
	要获取的定位数据有关。
	
7.13 tf.boolean_mask
7-13  tf.boolean_mask(tensor, mask, axis=None, name="boolean_mask")
	解释:
		不规则切片的一种。为了弥补tf.gather和tf.gather_nd的缺点,其实这个方式像是一种保底策略,比较笨重。
			
	示例:
		a shape: [4, 10, 7]
		同时获取:
		[0, 0, :], [2, 4, :], [3, 6, :]
		即:
			s = tf.boolean_mask(scores,
		    	[
			    	[True,False,False,False,False,False,False,False,False,False],
			     	[False,False,False,False,False,False,False,False,False,False],
			     	[False,False,False,False,True,False,False,False,False,False],
			     	[False,False,False,False,False,False,True,False,False,False]
		     	]
		    )
			tf.shape
			>> TensorShape([3, 7])
	

	综上所述,通过`tf.boolean_mask获取的维度会改变,结果维度是 a.dims - len(mask.shape) + 1`,即和mask维度
	大小有关,通过这个方法可以获取所有的需求数据,但是我有个大胆的想法:我都能知道Ture,False了,我复制粘贴
	可能比这个还快,所以啊,这玩意儿就是个兜底的方法。
	
7.14 tf.scatter_nd
7-14  tf.scatter_nd(indices, updates, shape, name=None)
	解释:
		不规则切片的一种。这个和tf.gather_nd有些相反,tf.gather_nd用于手机张量的给定位置的元素,而
		tf.scatter_nd可以将某些值插入到一个给定shape的全0的张量的指定位置处。
		-- indices : 需要置换数据的位置
		-- updates : 被替换的数据,与tensor的位置一一对应
		-- shape   : 生成该shape的零矩阵
		
	示例:
		经典示例:
			c = tf.constant([
					[-1,1,-1],
					[2,2,-2],
					[3,-3,3]
				],dtype=tf.float32)
			indices = tf.where(c<0)
			res = tf.scatter_nd(indices,tf.gather_nd(c,indices),c.shape)
			res
			>> <tf.Tensor: shape=(3, 3), dtype=float32, numpy=
				array([[-1.,  0., -1.],
				       [ 0.,  0., -2.],
				       [ 0., -3.,  0.]], dtype=float32)>
		
		-----------
		简单示例:
			d = c - tf.scatter_nd([[0,0],[2,1]], [c[0,0],c[2,1]], c.shape)
			解释:将张量的第[0,0][2,1]两个位置元素替换为0得到新的张量

	综上所述,通过`tf.scatter_nd获取的维度和参数中的shape保持一致`
7.15 tf.where
7-15  tf.where(condition, x=None, y=None, name=None)
	解释:
		返回满足条件condition的所有元素。返回结果是一个满足结果的位置列表。
		
	示例:
		tf.where([True, False, False, True])
		>> <tf.Tensor: shape=(2, 1), dtype=int64, numpy=
			  array([[0],
			         [3]])>
		tf.where([[[True, False], [False, True], [True, True]]])
		>> <tf.Tensor: shape=(4, 3), dtype=int64, numpy=
		  array([[0, 0, 0],
		         [0, 1, 1],
		         [0, 2, 0],
		         [0, 2, 1]])>
		其实,返回的结果都是2维的,axis=1中的列表数据中,每个数据代表满足condition条件的维度位置。

	综上所述,通过tf.where获取的`维度是2维`的,即[None, None]

8、tf.data.xx

8.0 tf.data.Dataset
  1. 底层对应的类为tf.python.data.ops.dataset_ops.DatasetV2
  2. 常用方法:
2-1 xx.map(map_func, num_parallel_calls=None, deterministic=None )
    >>> dataset = Dataset.range(1, 6)  # ==> [ 1, 2, 3, 4, 5 ]
    >>> dataset = dataset.map(lambda x: x + 1)
    >>> list(dataset.as_numpy_iterator())
    [2, 3, 4, 5, 6]
    
	-- num_parallel_calls = tf.data.experimental.AUTOTUNE # 让tensorflow自动选择合适的cpu处理数量值为-1,术语tensorflow的并行化策略。
2-2 xx.buffer(buffer_size, seed=None, reshuffle_each_iteration=None)
	shuffle是防止数据过拟合的重要手段
	buffer_size: A tf.int64 scalar tf.Tensor, representing the number of elements from this 
	dataset from which the new dataset will sample.
	
	reference:https://blog.csdn.net/weixin_43593330/article/details/103243455
	解释:
		tf会每次将buffer_size个数据抽放到缓冲区,然后随机去掉一个数据,用没被选中的item替换或填充【我的理解】,最后选出来的数据不会重复。
		不过若进行过batch_size,那么每次取出的结果会按照batch_size进行分组。
	
	示例:
	dataset = tf.data.Dataset.from_tensor_slices(np.array([1, 2, 3, 4, 5, 6, 7]))
	dataset = dataset.shuffle(3)
	for ele in dataset:
	    print(ele.numpy())
	3
	2
	5
	1
	6
	4
	7
	dataset = dataset.batch(3)
	for ele in dataset:
	    print(ele.numpy())
	[3 4 5]
	[2 7 1]
	[6]
2-3 xx.batch(batch_size, drop_remainder=False)
	Combines consecutive elements of this dataset into batches.
	将此数据集的连续元素组合成批。
	
	示例:
		>>> dataset = tf.data.Dataset.range(8)
	    >>> dataset = dataset.batch(3)
	    >>> list(dataset.as_numpy_iterator())
	    [array([0, 1, 2]), array([3, 4, 5]), array([6, 7])]
		
		>>> dataset = tf.data.Dataset.range(8)
	    >>> dataset = dataset.batch(3, drop_remainder=True)
	    >>> list(dataset.as_numpy_iterator())
	    [array([0, 1, 2]), array([3, 4, 5])]
	   	
		reference:https://blog.csdn.net/zkbaba/article/details/103206468
		
	   	-- drop_remainder 如果为True,那么最后不够成批量及batch_size将会被舍弃。若为False,则也会保留。
2-4 xx.pretetch(buffer_size)
	使用 Dataset.prefetch() 方法进行数据预加载后的训练流程,在 GPU 进行训练的同时 CPU 进行数据预加载,
	提高了训练效率。

	-- buffer_size = tf.data.experimental.AUTOTUNE  # 同map,让tensorflow自动选择合适的数值。

2-5 xx.as_numpy_iterator()
	获取该Dataset的真实数据,为一个迭代器。
	
	示例:
		for i in ds_data.as_numpy_iterator():
		    print(i)
2-6 xx.window(size, shift=None, stride=1, drop_remainder=False)
	将数据按照窗格分割,一版跟在刚转换的Tensor对象后
	
	示例1:
	import tensorflow as tf
	dataset = tf.data.Dataset.from_tensor_slices(tf.range(10))
	dataset = dataset.window(5,drop_remainder=True)
	dataset = dataset.flat_map(lambda window: window.batch(5))
	dataset = dataset.map(lambda window: (window[:-1],window[-1:]))
	for X,y in dataset:
	    print("Input:",X.numpy(),"Target:",y.numpy())
	
	>>> Input: [0 1 2 3] Target: [4]
	>>>	Input: [1 2 3 4] Target: [5]
	>>>	Input: [2 3 4 5] Target: [6]
	>>>	Input: [3 4 5 6] Target: [7]
	>>>	Input: [4 5 6 7] Target: [8]
	>>>	Input: [5 6 7 8] Target: [9]	
	
	示例2:
	def batch_dataset(dataset):
    	dataset_batched = dataset.batch(window_size,drop_remainder=True)
    	return dataset_batched
	def trans_to_dataset(df_x, df_y, window_size):
	    ds_data = tf.data.Dataset.from_tensor_slices(tf.constant(df_x.values[:-1],dtype =\
	    	tf.float32)).window(window_size,shift=1).flat_map(batch_dataset) # the latest data has no label, so ignore it
	    ds_label = tf.data.Dataset.from_tensor_slices(tf.one_hot(tf.constant(df_y.values\
	    	[window_size:]), depth=3))
	    # 在组合到一起之前,应该是可以进行分割!!但是这里先不这样用了
	    dataset = tf.data.Dataset.zip((ds_data,ds_label)).batch(128).cache()
	    return dataset
	
	window_size = 20
	train_db = trans_to_dataset(train_df, train_label,window_size)
	train_db = train_db.shuffle(240620).repeat(2)
	
	详解:
	# 第1步: tf.constant()可以将numpy.arry类型转化为tensor, df.values正是np.array
	# 第2步: tf.data.Dataset.from_tensor_slices()是可以把数据转化为tensor的另一种形式,貌似是做成了一个迭代器?(可不可以直接从array过来)
	# 第3步:用这个可迭代的tensor,的window方法,按照(window_size, shift)把原始数据扩展为目标数据集应有的元素
	# 第4步:继续对上面的数据批量批处理,.flat_map(),处理目的是按照我们的window_size把数据集分割小窗口后用[]括起来,实现方法是第5步
	# 第5步:定义分割窗口并括起来的功能函数dataset_batched = dataset.batch(window_size,drop_remainder=True),记得drop掉最后不整齐的数据
	# 第6步:把X和标签数据Y分别处理成可迭代并且已经[]括起来的数据后,tf.data.Dataset.zip组合到一起,变成训练集。

	
2-7 tf.timestamp()
	解释:从1970年开始计算的时间戳
	
	示例:【规范】
	def printbar():
	    today_ts = tf.timestamp()  # 时间戳
	    print('today_ts:', today_ts)
	
	    hour = tf.cast(today_ts // 3600 % 24 + 8, tf.int32)  # 小时,和北京时间差8个时差
	    minite = tf.cast((today_ts % 3600) // 60, tf.int32)	 # 分钟
	    second = tf.cast(tf.floor(today_ts % 60), tf.int32)	 # 秒钟
	
	    def timeformat(m):
	        if tf.strings.length(tf.strings.format("{}", m)) == 1:
	            return (tf.strings.format('0{}', m))
	        else:
	            return (tf.strings.format('{}', m))
	
	    timestring = tf.strings.join([timeformat(hour), timeformat(minite), timeformat(second)], separator=':')
	    tf.print('====' * 8 + timestring)
2-8 tf.cast(x, dtype, name=None)
	解释:改变张量的数据类型
	输入或者需要改变的数据类型为:uint8, uint16, uint32, uint64, int8, int16, `int32`, int64, 
	float16, `float32`, float64, complex64, complex128, bfloat16
	
	示例:
	x = tf.constant([1.8, 2.2], dtype=tf.float32)
	tf.dtypes.cast(x, tf.int32)  # [1, 2], dtype=tf.int32
	
2-9 tf.constant(value, dtype=None, shape=None, name="Const")
	解释:tensorflow中的常量转换,value可以是数值,可以是数值列表
	-- dtype 指定数值类型
	
	方法:xx.numpy() # 获取对应numpy数据
		 tf.rank(xx) # 获取到xx的维度大小,也是tf.Tensor类型
	返回:tf.Tensor()

	示例:
	i = tf.constant(1) # tf.int32 类型常量
	l = tf.constant(1,dtype = tf.int64) # tf.int64 类型常量
	f = tf.constant(1.23) #tf.float32 类型常量
	d = tf.constant(3.14,dtype = tf.double) # tf.double 类型常量
	s = tf.constant("hello world") # tf.string类型常量
	b = tf.constant(True) #tf.bool类型常量
	print(tf.int64 == np.int64) 
	print(tf.bool == np.bool)
	print(tf.double == np.float64)
	print(tf.string == np.unicode) # tf.string类型和np.unicode类型不等价
	>> True
	>> True
	>> True
	>> False
2-10 tf.rank(input, name=None)
	解释:获取input的维度大小,返回tf.Tensor类型
		相当于numpy.ndim

	示例:
	t = tf.constant([[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]])
	tf.rank(t)  # <tf.Tensor: shape=(), dtype=int32, numpy=3>
	tf.rank(t).numpy()  # 3
8.1 tf.data.TextLineDataset()
官方原话:
class TextLineDataset(dataset_ops.Dataset):
  """A `Dataset` comprising lines from one or more text files."""
    def __init__(self, filenames, compression_type=None, buffer_size=None):
        Creates a `TextLineDataset`.
        Args:
           filenames: A `tf.string` tensor containing one or more filenames.
           compression_type: (Optional.) A `tf.string` scalar evaluating to one of
           `""` (no compression), `"ZLIB"`, or `"GZIP"`.
           buffer_size: (Optional.) A `tf.int64` scalar denoting the number of bytes
           to buffer. A value of 0 results in the default buffering values chosen
           based on the compression type.

中文含义:

参数:
	--filenames:                         单个或者多个string格式的文件名或者目录
	--compression_type:   				可选!!!格式是ZLIB或者GZIP
	--buffer_size:                      可选!!!决定缓冲字节数多少

tf.data.TextLineDataset 接口提供了一种方法从数据文件中读取。我们提供只需要提供文件名(1个或者多个)。
这个接口会自动构造一个dataset,类中保存的元素:文中一行,就是一个元素,是string类型的tensor。

举例:

# 文件路径可以用list包括起来,多个路径
input_files = ['./input_file11', './input_file22']             
dataset = tf.data.TextLineDataset(input_files)

9、tf.keras.layers.experimental.preprocessing.xx

9.1 tf.keras.layers.experimental.preprocessing.TextVectorization
		def __init__(self,
               max_tokens=None,	# 输入的文本最大长度
               standardize=LOWER_AND_STRIP_PUNCTUATION,	# 小写并去除标点符号和首尾空格
               split=SPLIT_ON_WHITESPACE,	# 按照空格切割
               ngrams=None,
               output_mode=INT,
               output_sequence_length=None,	# 输出序列长度,也是输出向量的长度
               pad_to_max_tokens=True,
               **kwargs):
               ...
          If desired, the user can call this layer's adapt() method on a dataset.
		  When this layer is adapted, it will analyze the dataset, determine the
		  frequency of individual string values, and create a 'vocabulary' from them.
		  This vocabulary can have unlimited size or be capped, depending on the
		  configuration options for this layer; if there are more unique values in the
		  input than the maximum vocabulary size, the most frequent terms will be used
		  to create the vocabulary.

		  The processing of each sample contains the following steps:
		    1) standardize each sample (usually lowercasing + punctuation stripping)
		    2) split each sample into substrings (usually words)
		    3) recombine substrings into tokens (usually ngrams)
		    4) index tokens (associate a unique int value with each token)
		    5) transform each sample using this index, either into a vector of ints or
		       a dense float vector.
		 
       		将文本数据处理为文本向量,生成长度为output_sequence_length的向量。
    	
    	示例:
    	vectorize_layer = TextVectorization(output_sequence_length=15)
		vectorize_layer.adapt(tf.data.Dataset.from_tensor_slices([['1 2', '2 3', '3 4' , 'a b', 'c d']]))	# adapt 中的数据需要时二维的(None, 1)
		vectorize_layer([['1 2 c']]).numpy()
		>>> Out[67]: array([[9, 3, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

10、tf.keras.losses.xx

损失类选择

10.1 tf.keras.losses.BinaryCrossentropy
二分类交叉熵:
 			loss =  z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))

多分类使用

11、tf.keras.metrics.xx

11.1 tf.keras.metrics.Accuracy

计算预测值 与 真实值的准确度

tf.keras.metrics.Accuracy
(
    name='accuracy',
    dtype=None
)

xx.update_state
(
    y_true,
    y_pred,
    sample_weight=None
)

用法:
	m = tf.keras.metrics.Accuracy()
	m.update_state([1, 2, 3, 4], [0, 2, 3, 4])
	# Out[4]: <tf.Variable 'UnreadVariable' shape=() dtype=float32, numpy=4.0>
	m.result().numpy()
	# Out[5]: 0.75
	m.reset_states()
	
	m.update_state([1, 2, 3, 4], [0, 2, 3, 4], sample_weight = [1, 1, 0, 0])
	# Out[7]: <tf.Variable 'UnreadVariable' shape=() dtype=float32, numpy=2.0>
	m.result().numpy()
	# Out[8]: 0.5
11.2、tf.keras.metrics.Mean

计算给定值的(加权)平均数

tf.keras.metrics.Mean
(
    name='mean',
    dtype=None
)

用法:
	m = tf.keras.metrics.Mean()
	m.update_state([1, 3, 5, 7])
	# Out[10]: <tf.Variable 'UnreadVariable' shape=() dtype=float32, numpy=4.0>
	m.result().numpy()
	# Out[11]: 4.0
	m.reset_states()
	
	m.update_state([1, 3, 5, 7], sample_weight=[1, 1, 0, 0])
	# Out[13]: <tf.Variable 'UnreadVariable' shape=() dtype=float32, numpy=2.0>
	m.result().numpy()
	# Out[14]: 2.0

12、tf.keras.utils.xx

12.1 keras.utils.to_categorical(labels, num_classes=10)

将分类的标签,转换为one-hot的形式

二分类:
keras.utils.to_categorical(np.random.randint(2, size=(1000, 1)), num_classes=10)

多分类:
keras.utils.to_categorical(np.random.randint(10, size=(1000, 1)), num_classes=10)

13、tf.keras.layers.xx

13.1 tf.keras.layers.LSTM
	LSTM(3,return_sequences = True,input_shape=(None,3))
	
	详解:
		输入:LSTM的输入一定是3维的,分别是(Batch, 时间序列,属性维度)。
		输出:return_sequences = False时,输出是2维的,分别是(Batch, lstm_units)。
		     return_sequences = True时,输出是3维的,分别是(Batch, 时间序列,lstm_units),可进行多次调用。
		所以,LSTM可以直接连接Dense层。但是Conve层必须先flat才能Dense层。

14、tf.keras.callbacks.xx

如何使用回调函数呢,其实实在model.fit中添加参数即可:

#使用回调函数
#tensorBoard、EarlyStopping、ModelCheckPoint
logdir = os.path.join("callbacks")
if not os.path.exists(logdir):
    os.mkdir(logdir)
output_model_file = os.path.join(logdir,"fashion_mnist_model.h5")

callbacks = [
    keras.callbacks.TensorBoard(log_dir=logdir), #log_dir将输出的日志保存在所要保存的路径中
    keras.callbacks.ModelCheckpoint(output_model_file, save_best_only = True), 
    keras.callbacks.EarlyStopping(patience=5, min_delta=1e-3),
]
#训练模型会,返回一个结果保存在history中
history = model.fit(x_train_scaled, y_train, epochs=50, 
                    validation_data=(x_valid_scaled, y_valid), 
                    callbacks=callbacks) #使用回调函数

14.1 tf.keras.callbacks.TensorBoard
	tf.keras.callbacks.TensorBoard(
	    log_dir='logs', histogram_freq=0, write_graph=True, write_images=False,
	    update_freq='epoch', profile_batch=2, embeddings_freq=0,
	    embeddings_metadata=None, **kwargs
	)
	-- log_dir: 保存可视化目录
	-- histogram_freq: 按照epochs为单位,保存数据的频率。
	-- write_graph: 是否可视化图形,当为True时,日志文件会非常大。
	-- write_images: 是否写入模型权重,并在tb中可视化为图形。
	-- update_freq: 和histogram_greq时配合使用的,即保存tb基于什么为单位。
	-- profile_batch : [不懂]。
	-- embeddings_freq: 可视化嵌入层,[不太懂]。
	-- embeddings_metadata: []。
详解:
TensorBoard:是Tensorflow自带的一个强大的可视化工具,也是一个web应用程序套件,它通过将tensorflow程序输出
的日志文件的信息可视化使得tensorflow程序的理解、调试和优化更加简单高效。Tensorboard的可视化依赖于
tensorflow程序运行输出的日志文件,因而tensorboard和tensorflow程序在不同的进程中运行。

	示例:
	logdir = str(Path('./data/autograph/' + stamp))
	tb_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1)

	可视化TB图:
		tensorboard --logdir=path_to_your_logs
		图示包含内容有:
			直方图 HISTOGRAMS
			折线图 DISTRIBUTIONS
			GRAPHS面板 
			SCALARS面板
			OVERVIEW
14.2 tf.keras.callbacks.EarlyStopping
	tf.keras.callbacks.EarlyStopping(
	    monitor='val_loss', min_delta=0, patience=0, verbose=0, mode='auto',
	    baseline=None, restore_best_weights=False
	)
	-- monitor: 需要监视的量,val_loss,val_acc。
	-- patience: 当early stop被激活(如发现loss相比上一个epoch训练没有下降),则经过patience个epoch后停止训练。
	-- mode: ‘auto’,‘min’,'max’之一,在min模式训练,如果检测值停止下降则终止训练。在max模式下,当检测值不再上升的时候则停止训练。
	-- min_delta:阈值。
详解:
为了获得性能良好的神经网络,网络定型过程中需要进行许多关于所用设置(超参数)的决策。超参数之一是定型周期
(epoch)的数量:亦即应当完整遍历数据集多少次(一次为一个epoch)?如果epoch数量太少,网络有可能发生欠拟合
(即对于定型数据的学习不够充分);如果epoch数量太多,则有可能发生过拟合(即网络对定型数据中的“噪声”而非信号拟
合)。早停法旨在解决epoch数量需要手动设置的问题。它也可以被视为一种能够避免网络发生过拟合的正则化方法(与
L1/L2权重衰减和丢弃法类似)。根本原因就是因为继续训练会导致测试集上的准确率下降。那继续训练导致测试准确率下降
的原因猜测可能是1. 过拟合 2. 学习率过大导致不收敛。

	示例:
	#当loss在200个epoch后没有提升,则提前终止训练。                                                 
	stop_callback = tf.keras.callbacks.EarlyStopping(monitor = "loss", patience= 200)  
14.3 tf.keras.callbacks.ModelCheckPoint
	tf.keras.callbacks.ModelCheckpoint(
	    filepath, monitor='val_loss', verbose=0, save_best_only=False,
	    save_weights_only=False, mode='auto', save_freq='epoch', options=None, **kwargs
	)
	解释:在训练中途进行模型的保存。
	--filepath: 保存到的文件路径
	-- monitor: 判断的依据
	-- verbose: [不清楚]
	-- save_best_only: 只保存最好的模型
	-- mode: 与monitor配合使用,按照最大或者最小monitor值进行最好模型的判断
	-- save_weights_only: 如果为True,model.save_weights(filepath),如果为False,model.save(filepath)
	-- save_freq: 默认是'epoch',如果为integer则表示多少个batches。

示例:
	EPOCHS = 10
	checkpoint_filepath = '/tmp/checkpoint'
	model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
	    filepath=checkpoint_filepath,
	    save_weights_only=True,
	    monitor='val_acc',
	    mode='max',
	    save_best_only=True)
	
	# Model weights are saved at the end of every epoch, if it's the best seen
	# so far.
	model.fit(epochs=EPOCHS, callbacks=[model_checkpoint_callback])
	
	# The model weights (that are considered the best) are loaded into the model.
	model.load_weights(checkpoint_filepath)
14.4 tf.keras.callbacks.ReduceLROnPlateau
	tf.keras.callbacks.ReduceLROnPlateau(
	    monitor='val_loss', factor=0.1, patience=10, verbose=0, mode='auto',
	    min_delta=0.0001, cooldown=0, min_lr=0, **kwargs
	)
	解释:自动调整学习率
	-- monitor: 被监控的数据对象
	-- factor: 学习率降低值为:new_lr = lr * factor
	-- patience: 多少个epoches后,monitor值没变,那么则更新学习率lr
	-- verbose: 0:quiet,1:update_messages
	-- mode: {'auto', 'min', 'max'},auto自动会按照monitor标准监测,如果是min则低于一个值没有再降低,则改变学习率,max则相反,不能再升高,那么就更改学习率。
	-- min_delta: [不清楚]threshold for measuring the new optimum, to only focus on significant changes.
	-- cooldown: [不清楚]
	-- min_lr: 学习率的下界
	
	示例:
		#如果loss在100个epoch后没有提升,学习率减半。                                                                     
		lr_callback = tf.keras.callbacks.ReduceLROnPlateau(monitor="loss",factor = 0.5, patience = 100)   

15、Tensor

15.1 类型详解
15.2 属性和方法
ts = tf.Tensor() # 假设

ts.dtype	# 数据类型
ts.shape	# 数据尺寸

ts.numpy()	# 获取对应的numpy结果
tf.rank(ts)	# 获取对应的维度,返回结果仍是tf.Tensor
tf.rank(ts).numpy 和 ts.numpy().ndim 和 np.ndim(ts.numpy()) 一样的结果
15.3 常量和变量
15.3.1 常量:

常量数据的改变,会开辟新的内存空间。可用id(xx)验证内存hash地址。

tf.constant()
15.3.2 变量:

变量数据的改变,不会开辟新的内存空间。一般被设置为被训练的参数。

v = tf.Variable([1.0,2.0],name = "v")

方法:
	v.assign_add([1.0, 1.0])	# 变量加法

16、计算Graph

Tensorflow2 共有三种计算图: 静态计算图,动态计算图,Autograph

tf1.0+ 使用的是静态计算图,执行计算图,需要开启会话Session。
tf2.0+ 采用的是动态图,无需开启Session,可立即执行逻辑。优点:方便程序的调试,容易使用。缺点:运行效率低,因为动态图需要多次使用python进程和tf中的c++进程的通信,而静态图构建完后几乎全部是在tf内核上的c++代码执行,效率更高。那么,如果在tf2.0+中使用静态图,可以通过 @tf.function 装饰器将Python函数转换为对应的tf计算图构建代码,即相当于在tf1.0+中使用Session执行代码。那使用tf.function构建静态图的方式就是AutoGraph

16.1 静态计算图

使用静态计算图分两步,第一步定义计算图,第二步在会话中执行计算图。

16.1.1 tf1.0+静态计算图
import tensorflow as tf

#定义计算图
g = tf.Graph()
with g.as_default():
    #placeholder为占位符,执行会话时候指定填充对象
    x = tf.placeholder(name='x', shape=[], dtype=tf.string)  
    y = tf.placeholder(name='y', shape=[], dtype=tf.string)
    z = tf.string_join([x,y],name = 'join',separator=' ')

#执行计算图
with tf.Session(graph = g) as sess:
    print(sess.run(fetches = z,feed_dict = {x:"hello",y:"world"}))
   
16.1.2 tf2.0+中调用tf1.0+静态计算图

tf2.0 中在 tf.compat.v1子模块中保留了对TensorFlow1.0静态图的API。

import tensorflow as tf

g = tf.compat.v1.Graph()
with g.as_default():
    x = tf.compat.v1.placeholder(name='x', shape=[], dtype=tf.string)
    y = tf.compat.v1.placeholder(name='y', shape=[], dtype=tf.string)
    z = tf.strings.join([x,y],name = "join",separator = " ")

with tf.compat.v1.Session(graph = g) as sess:
    # fetches的结果非常像一个函数的返回值,而feed_dict中的占位符相当于函数的参数序列。
    result = sess.run(fetches = z,feed_dict = {x:"hello",y:"world"})
    print(result)
16.2 动态计算图

动态计算图已经不区分计算图的定义和执行了,而是定义后立即执行。因此称之为 Eager Excution.

16.2.1 tf2.0+中的动态计算图
# 动态计算图在每个算子处都进行构建,构建后立即执行

x = tf.constant("hello")
y = tf.constant("world")
z = tf.strings.join([x,y],separator=" ")

tf.print(z)
>> hellow world
# 可以将动态计算图代码的输入和输出关系封装成函数

def strjoin(x,y):
    z =  tf.strings.join([x,y],separator = " ")
    tf.print(z)
    return z

result = strjoin(tf.constant("hello"),tf.constant("world"))
print(result)

>> hello world
>> tf.Tensor(b'hello world', shape=(), dtype=string)
16.2 静态计算图Autograph

解决:动态计算图运行效率相对较低。
使用@tf.function装饰器转换tf2.0+中的动态图为tf1.0+中的动态图。在tf1.0+中,构建计算图,第一步定义计算图,第二步创建会话,第三步是执行计算图。在tf2.0+中,如果采用@tf.function 装饰器的方式,即Autograph,第一步定义函数,第二步调用函数即可。
应用场景:后台开发的时候,使用动态图方式进行代码调试,应用到线上的时候,使用Autograph会更快速,我在一个小模型下测试过两种方式的执行速度,Autograph方式是动态图方式的4+倍。

import tensorflow as tf

# 使用autograph构建静态图

@tf.function
def strjoin(x,y):
    z =  tf.strings.join([x,y],separator = " ")
    tf.print(z)
    return z

result = strjoin(tf.constant("hello"),tf.constant("world"))
print(result)

>> hello world
>> tf.Tensor(b'hello world', shape=(), dtype=string)

Autograph编码规范

  • 1,被@tf.function修饰的函数应尽可能使用TensorFlow中的函数而不是Python中的其他函数。例如使用tf.print而不是print,使用tf.range而不是range,使用tf.constant(True)而不是True.

  • 2,避免在@tf.function修饰的函数内部定义tf.Variable.

  • 3,被@tf.function修饰的函数不可修改该函数外部的Python列表或字典等数据结构变量。

1,被@tf.function修饰的函数应尽量使用TensorFlow中的函数而不是Python中的其他函数。

import numpy as np
import tensorflow as tf

@tf.function
def np_random():
    a = np.random.randn(3,3)
    tf.print(a)
    
@tf.function
def tf_random():
    a = tf.random.normal((3,3))
    tf.print(a)
#np_random每次执行都是一样的结果。
np_random()
np_random()

#tf_random每次执行都会有重新生成随机数。
tf_random()
tf_random()
2,避免在@tf.function修饰的函数内部定义tf.Variable.

# 避免在@tf.function修饰的函数内部定义tf.Variable.

x = tf.Variable(1.0,dtype=tf.float32)
@tf.function
def outer_var():
    x.assign_add(1.0)
    tf.print(x)
    return(x)

# 正确运行
outer_var() 
outer_var()

@tf.function
def inner_var():
    x = tf.Variable(1.0,dtype = tf.float32)
    x.assign_add(1.0)
    tf.print(x)
    return(x)

#执行将报错
#inner_var()
#inner_var()
3,被@tf.function修饰的函数不可修改该函数外部的Python列表或字典等结构类型变量。

tensor_list = []

#@tf.function #加上这一行切换成Autograph结果将不符合预期!!!
def append_tensor(x):
    tensor_list.append(x)
    return tensor_list

append_tensor(tf.constant(5.0))
append_tensor(tf.constant(6.0))
print(tensor_list)
>> [<tf.Tensor: shape=(), dtype=float32, numpy=5.0>, <tf.Tensor: shape=(), dtype=float32, numpy=6.0>]

tensor_list = []

@tf.function #加上这一行切换成Autograph结果将不符合预期!!!
def append_tensor(x):
    tensor_list.append(x)
    return tensor_list


append_tensor(tf.constant(5.0))
append_tensor(tf.constant(6.0))
print(tensor_list)
>> [<tf.Tensor 'x:0' shape=() dtype=float32>]

Autograph基本原理
第一件事情是创建计算图。

即创建一个静态计算图,跟踪执行一遍函数体中的Python代码,确定各个变量的Tensor类型,并根据执行顺序将算子添加到计算图中。 在这个过程中,如果开启了autograph=True(默认开启),会将Python控制流转换成TensorFlow图内控制流。 主要是将if语句转换成 tf.cond算子表达,将while和for循环语句转换成tf.while_loop算子表达,并在必要的时候添加 tf.control_dependencies指定执行顺序依赖关系。

第二件事情是执行计算图。

需要注意的是,如果调用被@tf.function装饰的函数时输入的参数不是Tensor类型,则每次都会重新创建计算图。

reference: AutoGraph的机制原理

17、自动微分GradientTape-自动前向反向传播

在我们刚入门深度学习的时候,我们就直接接触到了如何让结果更准确,那就是让loss最小,那么每次需要更新的变量值是如何改变的,其实就是通过数学公式的反向传播求梯度的方式确定的。
tf2.0+中提供了tf.GradientTape来记录正向的运算过程,自动对变量值微分。

17.1 求导数

变量求导:

import tensorflow as tf
import numpy as np 

# f(x) = a*x**2 + b*x + c的导数

x = tf.Variable(0.0,name = "x",dtype = tf.float32)
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)

with tf.GradientTape() as tape:
    y = a*tf.pow(x,2) + b*x + c
    
dy_dx = tape.gradient(y,x)
print(dy_dx)

>> tf.Tensor(-2.0, shape=(), dtype=float32)

常量求导:

# 对常量张量也可以求导,需要增加watch
x = tf.Variable(0.0,name = "x",dtype = tf.float32)
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)

with tf.GradientTape() as tape:
    tape.watch([a,b,c])
    y = a*tf.pow(x,2) + b*x + c
    
dy_dx,dy_da,dy_db,dy_dc = tape.gradient(y,[x,a,b,c])
print(dy_da)
print(dy_dc)

>> tf.Tensor(0.0, shape=(), dtype=float32)
>> tf.Tensor(1.0, shape=(), dtype=float32)

二阶导数

# 可以求二阶导数,但需要记录多个运行过程
with tf.GradientTape() as tape2:
    with tf.GradientTape() as tape1:   
        y = a*tf.pow(x,2) + b*x + c
    dy_dx = tape1.gradient(y,x)   
dy2_dx2 = tape2.gradient(dy_dx,x)

print(dy2_dx2)

>> tf.Tensor(2.0, shape=(), dtype=float32)

在Autograph中使用

@tf.function
def f(x):   
    a = tf.constant(1.0)
    b = tf.constant(-2.0)
    c = tf.constant(1.0)
    
    # 自变量转换成tf.float32
    x = tf.cast(x,tf.float32)
    with tf.GradientTape() as tape:
        tape.watch(x)
        y = a*tf.pow(x,2)+b*x+c
    dy_dx = tape.gradient(y,x) 
    
    return((dy_dx,y))

tf.print(f(tf.constant(0.0)))
tf.print(f(tf.constant(1.0)))
>> (-2, 1)
>> (0, 0)
17.2 和优化器配合使用
# 求f(x) = a*x**2 + b*x + c的最小值
# 使用optimizer.apply_gradients

x = tf.Variable(0.0,name = "x",dtype = tf.float32)
a = tf.constant(1.0)
b = tf.constant(-2.0)
c = tf.constant(1.0)

optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
for _ in range(1000):
    with tf.GradientTape() as tape:
        y = a*tf.pow(x,2) + b*x + c
    dy_dx = tape.gradient(y,x)
    optimizer.apply_gradients(grads_and_vars=[(dy_dx,x)])
    
tf.print("y =",y,"; x =",x)

>> y = 0 ; x = 0.999998569

使用结构为:

# 给x应用计算的导数:dy_dx
tf.keras.optimizers.SGD(learning_rate=0.01).apply_gradients(grads_and_vars=[(dy_dx,x)])
# optimizer.minimize相当于先用tape求gradient,再apply_gradient
tf.keras.optimizers.SGD(learning_rate=0.01).minimize(function,[x])     

18、tf.random.xx

18.1 tf.random.uniform
	tf.random.uniform(
	    shape, minval=0, maxval=None, dtype=tf.dtypes.float32, seed=None, name=None
	)
	解释:生成shape大小的,最小值为minval,最大值为maxval,数据类型为dtype的均匀分布的值。
18.2 tf.random.set_seed
	tf.random.set_seed(
	    seed
	)
	解释:赋值随机种子
18.3 tf.random.truncated_normal
	tf.random.truncated_normal(
	    shape, mean=0.0, stddev=1.0, dtype=tf.dtypes.float32, seed=None, name=None
	)
	解释:从截断的正态分布中输出随机值。
	生成的值服从具有指定平均值和标准偏差的正态分布,如果生成的值大于平均值2个标准偏差的值则丢弃重新选
择。
	
	在正态分布的曲线中,横轴区间(μ-σ,μ+σ)内的面积为68.268949%。
	横轴区间(μ-2σ,μ+2σ)内的面积为95.449974%。
	横轴区间(μ-3σ,μ+3σ)内的面积为99.730020%。
	X落在(μ-3σ,μ+3σ)以外的概率小于千分之三,在实际问题中常认为相应的事件是不会发生的,基本上可以把
区间(μ-3σ,μ+3σ)看作是随机变量X实际可能的取值区间,这称之为正态分布的“3σ”原则。
	在tf.random.truncated_normal中如果x的取值在区间(μ-2σ,μ+2σ)之外则重新进行选择。这样保证了生成的值都
在均值附近。
	-- shape 形状
	-- mean  均值
	-- stddev 标准差,平方是方差
	-- seed 同tf.random.set_seed设置随机种子,当设置随机种子以后,生成的数据是一样的。

19、tf.linalg.xx

19.1 常用方法
1) tf.linalg.diag(diagonal,
                name="diag",
                k=0,
                num_rows=-1,
                num_cols=-1,
                padding_value=0,
                align="RIGHT_LEFT")
    解释:
    	创建对角矩阵。
    示例:
    	tf.linalg.diag([1, 2, 3, 5, 5, 5])
    	>> <tf.Tensor: shape=(6, 6), dtype=int32, numpy=
			array([[1, 0, 0, 0, 0, 0],
			       [0, 2, 0, 0, 0, 0],
			       [0, 0, 3, 0, 0, 0],
			       [0, 0, 0, 5, 0, 0],
			       [0, 0, 0, 0, 5, 0],
			       [0, 0, 0, 0, 0, 5]], dtype=int32)>

2) 

20、数据-预处理

20.1 序列预处理
keras.preprocessing.sequence.pad_sequences(sequences,maxlen=None,dtype='int32',padding='pre',truncating='pre', value=0.)

解释: sequences:浮点数或整数构成的两层嵌套列表
maxlen:None或整数,为序列的最大长度。大于此长度的序列将被截短,小于此长度的序列将在后部填0.在命名实体识别任务中,主要是指句子的最大长度
dtype:返回的numpy array的数据类型 padding:‘pre’或‘post’,确定当需要补0时,在序列的起始还是结尾补
truncating:‘pre’或‘post’,确定当需要截断序列时,从起始还是结尾截断
value:浮点数,此值将在填充时代替默认的填充值0

20.2 转换为tf_record

参考:
tf_record详解
csv.reader详解
unicodedata库执行英文数据标准化

21、TF版本,CUDA版本,Cudnn版本对应

https://www.tensorflow.org/install/source#common_installation_problems

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值