Tensorflow练习
模型保存
Keras接口的模型保存
model.save("xxx.h5") # 保存模型结构和权重
model.to_json() # 保存模型结构
# 加载
model.load_model('xxx.h5')
models.model_from_json() # json
model.load_weights() # json
tensorflow接口的模型保存
model.save('tf_model_savedmodel', save_format="tf") # 保存模型和结构
model.save_weights('./data/tf_model_weights.ckpt',save_format = "tf") # 仅保存权重
# 加载
tf.keras.models.load_model('tf_model_savedmodel') #
数据加载
数据加载是将其他类型的数据转换为tf.data.Dataset
类型。
最简单的数据加载在方式是从list
中加载,tf.data.Dataset.list_files()
可以从一系列满足正则条件的文件中(如图片)加载数据。
Dataset支持的操作
- Transformations.
Dataset.map
,Dataset.apply
- Batch.
Dataset.batch
- Cache. 当迭代完成后,会缓存在内存或者文件中,后续的迭代会使用缓存的数据
- Concatenate. 将两个
Dataset
类型concatenate,两者必须有相同的数据类型,返回新对象。 - Filter
- From_generator
- Reduce
- Shuffle
- Zip
- unbatch
- take
图片数据加载
可以使用tf.data.Dataset.list_files()
读取一系列照片路径,再配合tf.image
类进行读取预处理。
文本数据加载
使用tf.data.TextLineDataset()
对一系列文件路径进行每行文本读取,使用map等函数进行处理。
数据处理
特征列
特征列可以将类别特征转换为one-hot编码特征,将连续特征构建分桶特征,以及对多个特征生成交叉特征。
- numeric_column
- bucketized_column
- categorical_column_with_identity
- categorical_column_with_vocabulary_list
- categorical_column_with_vocabulary_file
- categorical_column_with_hash_bucket
- indicator_column
- embedding_column
- crossed_column
定义完特征列后通过tf.keras.layers.DenseFeatures
将数据进行特征处理。
图片数据处理
可以使用tf.data.Dataset.list_files()
读取一系列照片路径,然后使用map函数对其进行处理,如tf.io.read_file
,tf.image.decode_jpeg(img)
,tf.image.resize
.
tf.image
支持的操作
- crop
- crop_and_resize
- crop_to_bounding_box
- resize
也可以使用tf.keras.preprocessing.image
中的ImageDataGenerator
方法从文件中读取一些列照片。
文本数据处理
Tensorflow
中文本的处理需要使用到tf.keras.preprocessing
中的Tokenizen
词典和tf.keras.utils.Sequence
构建文本数据生成管道,并结合tf.keras.layers.experimental.preprocessing.TextVectorization
进行处理。
tf.strings
中的处理操作
- lower
- regex_replace
tf.keras.layers.experimental.preprocess.TextVectorization
进行处理,arg
- max_token
- standardize
- split
- ngrams
- output_mode
- pad_to_max_token
使用方法
- adapt,拟合数据
- get_vocabulary(),得到词典
张量
张量数据结构
张量可以分为常量和变量两类。常量值不可以改变,变量的值可一改变,可以使用assign
,assign_add
,assign_sub
方法。
结构操作
-
创建
range
,linspace
,zeros
,ones
,zeros_like
,fill
,uniform
,normal
,truncated_normal
, -
切片
gather
,gather_nd
,tf.where
,scatter_nd
-
维度变化
tf.reshape
,tf.squeeze
,tf.expand_dims
,tf.transpose
,tf.reshape
-
合并和分割
tf.split
,tf.stack
,tf.concat
数学运算
-
标量运算
tf.clip_by_value
,tf.clip_by_norm
-
向量运算
reduce_mean
,reduce_sum
,reduce_max
,reduce_prod
,tf.math.cumsum
,tf.math.cumprod
tf.argmax
,tf.math.top_k
-
矩阵运算
tf.matmul
,tf.transpose
,tf.linalg.inv
,tf.linalg.trace
,tf.linalg.norm
,
求导
张量的求导需要增加tape.watch()
optimizers.minimize() == loss.gradient, optimizers.apply_gradient
AutoGraph使用规范
AutoGraph是将eager转换为静态图,在函数前加入@t f.function
修饰
- 被@tf.function修饰的函数应尽可能使用TensorFlow中的函数而不是Python中的其他函数
- 避免在@tf.function修饰的函数内部定义tf.Variable
- 被@tf.function修饰的函数不可修改该函数外部的Python列表或字典等数据结构变量
AutoGraph的机制原理
执行步骤
- 创建计算图
- 执行计算图
当计算图已经创建时,不会在执行非计算图中的部分。如果调用被@tf.function装饰的函数时输入的参数不是Tensor类型,则每次都会重新创建计算图。
tf.Module
因为tf.function修饰的函数中不宜包含新建的变量,但是修改提前定义的变量显得封装不完美,解决方案是利用tf.Module
封装。
class DemoModule(tf.Module):
def __init__(self,init_value = tf.constant(0.0),name=None):
super(DemoModule, self).__init__(name=name)
with self.name_scope: #相当于with tf.name_scope("demo_module")
self.x = tf.Variable(init_value,dtype = tf.float32,trainable=True)
@tf.function(input_signature=[tf.TensorSpec(shape = [], dtype = tf.float32)])
def addprint(self,a):
with self.name_scope:
self.x.assign_add(a)
tf.print(self.x)
return(self.x)
网络构建
定义模型层
继承tf.keras.layers.Layer
类,需要实现初始化,build
和call
方法。
build
定义Layer
需要被训练的参数。
def build(self, input_shape):
self.w = self.add_weight("w",shape=(input_shape[-1], self.units), initializer='random_normal',trainable=True) #注意必须要有参数名称"w",否则会报错
self.b = self.add_weight("b",shape=(self.units,), initializer='random_normal', trainable=True)
super(Linear,self).build(input_shape) # 相当于设置self.built = True
def get_config(self):
config = super(Linear, self).get_config()
config.update({'units': self.units})
return config
定义模型
继承tf.keras.models.Model
类,实现初始化,build
和call
方法,build
中设置需要学习的参数(网络层)
class ImdbModel(models.Model):
def __init__(self):
super(ImdbModel, self).__init__()
def build(self,input_shape):
self.embedding = layers.Embedding(MAX_WORDS,7)
self.block1 = ResBlock(7)
self.block2 = ResBlock(5)
self.dense = layers.Dense(1,activation = "sigmoid")
super(ImdbModel,self).build(input_shape)
def call(self, x):
x = self.embedding(x)
x = self.block1(x)
x = self.block2(x)
x = layers.Flatten()(x)
x = self.dense(x)
return(x)
定义损失函数
损失函数接收两个张量y_ture
和y_pred
并输出一个标量作为损失函数值。可以继承与tf.keras.losses.Loss
类,要实现初始化和call
方法。
# wrap up
def focal_loss(alpha, gamma):
def focal_loss_fixed(y_true,· y_pred):
pt_1 = tf.where(tf.euqal(y_true, 1), y_pred, tf.ones_like(y_pred))
pt_0 = tf.where(tf.euqal(y_true, 0), y_pred, tf.zeros_like(y_pred))
loss = -tf.reduce_sum(alpha * tf.power(1. - pt_1, gamma) * tf.log(1e-07+pt_1)) - tf.reduce_sum((1-alpha) * tf.pow( pt_0, gamma) * tf.log(1. - pt_0 + 1e-07))
return loss
return focal_loss_fixed
# Class
class FocalLoss(losses.Loss):
def __init__(self,gamma=2.0,alpha=0.25):
self.gamma = gamma
self.alpha = alpha
def call(self,y_true,y_pred):
pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))
pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))
loss = -tf.reduce_sum(self.alpha * tf.pow(1. - pt_1, self.gamma) * tf.log(1e-07+pt_1)) \
-tf.reduce_sum((1-self.alpha) * tf.pow( pt_0, self.gamma) * tf.log(1. - pt_0 + 1e-07))
return loss
评估指标
与损失函数相同,评估指标需要接收两个张量y_true,y_pred作为输入参数,并输出一个标量作为评估值。用自定义类的方式实现需要继承tf.keras.metrics.Metric
。
class KS(metrics.Metric):
def __init__(self, name = "ks", **kwargs):
super(KS,self).__init__(name=name,**kwargs)
self.true_positives = self.add_weight(
name = "tp",shape = (101,), initializer = "zeros")
self.false_positives = self.add_weight(
name = "fp",shape = (101,), initializer = "zeros")
@tf.function
def update_state(self,y_true,y_pred):
y_true = tf.cast(tf.reshape(y_true,(-1,)),tf.bool)
y_pred = tf.cast(100*tf.reshape(y_pred,(-1,)),tf.int32)
for i in tf.range(0,tf.shape(y_true)[0]):
if y_true[i]:
self.true_positives[y_pred[i]].assign(
self.true_positives[y_pred[i]]+1.0)
else:
self.false_positives[y_pred[i]].assign(
self.false_positives[y_pred[i]]+1.0)
return (self.true_positives,self.false_positives)
@tf.function
def result(self):
cum_positive_ratio = tf.truediv(
tf.cumsum(self.true_positives),tf.reduce_sum(self.true_positives))
cum_negative_ratio = tf.truediv(
tf.cumsum(self.false_positives),tf.reduce_sum(self.false_positives))
ks_value = tf.reduce_max(tf.abs(cum_positive_ratio - cum_negative_ratio))
return ks_value
回调函数
tf.keras
的回调函数实际上是一个类,一般是在model.fit
时作为参数指定,用于控制在训练过程开始或者在训练过程结束,在每个epoch训练开始或者训练结束,在每个batch训练开始或者训练结束时执行一些操作,例如收集一些日志信息,改变学习率等超参数,提前终止训练过程等等。
所有回调函数都继承至keras.callbacks.Callbacks
基类,拥有params
和model
这两个属性。其中params
是一个dict,记录了训练相关参数 (例如 verbosity, batch size, number of epochs 等等)。model
即当前关联的模型的引用。
- BaseLogger。 收集每个epoch上metrics在各个batch上的平均值
- History。将BaseLogger计算的各个epoch的metrics结果记录到history这个dict变量中,并作为model.fit的返回值。该回调函数被所有模型默认添加,在BaseLogger之后被添加。
- EarlyStopping。 当被监控指标在设定的若干个epoch后没有提升,则提前终止训练。
- ModelCheckpoint。在每个epoch后保存模型。
- ReduceLROnPlateau。如果监控指标在设定的若干个epoch后没有提升,则以一定的因子减少学习率。
- TerminateOnNaN。如果遇到loss为NaN,提前终止训练。
- LearningRateScheduler。学习率控制器。给定学习率lr和epoch的函数关系,根据该函数关系在每个epoch前调整学习率。
GPU设置
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU
tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量按需使用
# 或者也可以设置GPU显存为固定使用量(例如:4G)
#tf.config.experimental.set_virtual_device_configuration(gpu0,
# [tf.config.experimental.VirtualDeviceConfiguration(memory_limit=4096)])
tf.config.set_visible_devices([gpu0],"GPU")