这篇是紧接上一篇,这里主要推荐tf.keras、eager、Estimators这三种作为2.x版本的框架作为我们在工程实践中使用的框架。接下来这篇中将详细介绍如何使用这三种框架来搭建自己的模型,以及其中的注意要点。疫情太严重没法出门了,只有把之前学过的来个总结,好吧,开始。
目录
一、数据集
这里我们采用自己生成的数据集来拟合一个线性回归模型,并将这些数据作为我们搭建的模型的输入。生成代码如下:
import numpy as np
import matplotlib.pyplot as plt
#生成模拟数据
train_X = np.linspace(-1, 1, 100)
train_Y = 2 * train_X + np.random.randn(*train_X.shape) * 0.3 # y=2x,但是加入了噪声
#图形显示
plt.plot(train_X, train_Y, 'ro', label='Original data')
plt.legend()
plt.show()
二、Estimators训练回归模型
创建的 Estimator 的 Tensorflow 项目,主要由以下几步完成:
- 输入函数:由tf.data.Dataset组成,分为训练输入函数(train_input_fn)和测试输入函数(eval_input_fn);
- 模型函数:由tf.layers和tf.metrics组成,用于训练、测试和监控模型参数状况等功能;
- 估算器:将个部分组合在一起,控制数据在模型中计算和流动。
2.1、输入数据
在 Estimator 中,我们输入必须是一个函数,这个函数必须返回特征和标签(或者只有特征),所以我们需要把前面tensorflow实战之数据加载的数据加载封装到一个函数中。因为训练输入和验证输入是不一样的,所以需要两个输入函数:train_input_fn
和 eval_input_fn
。
def GenerateData(datasize = 100 ):
train_X = np.linspace(-1, 1, datasize) #train_X为-1到1之间连续的100个浮点数
train_Y = 2 * train_X + np.random.randn(*train_X.shape) * 0.3 # y=2x,但是加入了噪声
return train_X, train_Y #以生成器的方式返回
train_data = GenerateData()
test_data = GenerateData(20)
batch_size=10
def train_input_fn(train_data, batch_size): #定义训练数据集输入函数
#构造数据集的组成:一个特征输入,一个标签输入
dataset = tf.data.Dataset.from_tensor_slices( ( train_data[0],train_data[1]) )
dataset = dataset.shuffle(1000).repeat().batch(batch_size) #将数据集乱序、重复、批次划分.
return dataset #返回数据集
def eval_input_fn(data, labels, batch_size): # 定义测试或应用模型时,数据集的输入函数
# batch不允许为空
assert batch_size is not None, "batch_size must not be None"
if labels is None: # 如果评估,则没有标签
inputs = data
else:
inputs = (data, labels)
# 构造数据集
dataset = tf.data.Dataset.from_tensor_slices(inputs)
dataset = dataset.batch(batch_size) # 按批次划分
return dataset # 返回数据集
2.2、模型网络搭建
定义好模型的输入之后我们需要定义网络的框架,并返回一个定义好的 tf.estimator.EstimatorSpec
对象。同时,不同的模式下,返回的对象也不同,提供的参数也不同。不同的 mode的参数设置如下
:
- 训练模式,即
mode == tf.estimator.ModeKeys.TRAIN
,必须提供的是loss
和train_op
。 - 验证模式,即
mode == tf.estimator.ModeKeys.EVAL
,必须提供的是loss
。 - 预测模式,即
mode == tf.estimator.ModeKeys.PREDICT
,必须提供的是predicitions
。
def my_model(features, labels, mode, params): # 自定义模型函数:参数是固定的。一个特征,一个标签
# 定义网络结构
W = tf.Variable(tf.random_normal([1]), name="weight")
b = tf.Variable(tf.zeros([1]), name="bias")
# 前向结构
predictions = tf.multiply(tf.cast(features, dtype=tf.float32), W) + b
if mode == tf.estimator.ModeKeys.PREDICT: # 预测处理
return tf.estimator.EstimatorSpec(mode, predictions=predictions)
# 定义损失函数
loss = tf.losses.mean_squared_error(labels=labels, predictions=predictions)
meanloss = tf.metrics.mean(loss) # 添加评估输出项
metrics = {'meanloss': meanloss}
if mode == tf.estimator.ModeKeys.EVAL: # 测试处理
return tf.estimator.EstimatorSpec(mode, loss=loss, eval_metric_ops=metrics)
# return tf.estimator.EstimatorSpec( mode, loss=loss)
# 训练处理.
assert mode == tf.estimator.ModeKeys.TRAIN
optimizer = tf.train.AdagradOptimizer(learning_rate=params['learning_rate'])
train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)
2.3、Estimator(估算器)的执行
- 创建一个
tf.estimator.Estimator
对象
tf.reset_default_graph() # 清空图
tf.logging.set_verbosity(tf.logging.INFO) # 能够控制输出信息 ,
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333) # 构建gpu_options,防止显存占满
session_config = tf.ConfigProto(gpu_options=gpu_options)
# 构建估算器
estimator = tf.estimator.Estimator(model_fn=my_model, model_dir='./myestimatormode', params={'learning_rate': 0.1},
config=tf.estimator.RunConfig(session_config=session_config))
-
训练模型
# 匿名输入方式
estimator.train(lambda: train_input_fn(train_data, batch_size), steps=200)
tf.logging.info("训练完成.") # 输出训练完成
最终运行结果:
2.4、验证估算器模型
与训练一样,我们可以直接调用estimator的evaluate方法,并传入函数即可。另外,还可以直接使用估算器输入函数的方法进行输入数据,函数为tf.estimator.input.numpu_input_fn函数实现。具体实现:
#第一种方法:直接构造输入函数
test_input_fn = tf.estimator.inputs.numpy_input_fn(test_data[0],test_data[1],batch_size=1,shuffle=False)
train_metrics = estimator.evaluate(input_fn=test_input_fn)
#第二种方法:调用我们定义的输入函数
train_metrics = estimator.evaluate(input_fn=lambda: eval_input_fn(train_data[0],train_data[1],batch_size=1))
print("train_metrics",train_metrics)
最终运行结果:
2.5、预测使用估算器模型
调用estimator的predict方法,将测试数据传入模型进行预测。
- 调用我们自定义的eval_fn,并将label标签传入值设置为None;
- 也可以使用tf.estimator.input.numpu_input_fn生成输入函数。
具体实现:
#方法一:使用我们自定义的输入函数,验证
predictions = estimator.predict(input_fn=lambda: eval_input_fn(test_data[0],None,batch_size))
print("predictions",list(predictions))
#方法二:使用tf.estimator.inputs.numpy_input_fn来构造输入数据
new_samples = np.array( [6.4, 3.2, 4.5, 1.5], dtype=np.float32) #定义输入
predict_input_fn = tf.estimator.inputs.numpy_input_fn( new_samples,num_epochs=1, batch_size=1,shuffle=False)
predictions = list(estimator.predict(input_fn=predict_input_fn))
print( "输入, 结果: {} {}\n".format(new_samples,predictions))
输出结果:
2.6、为估算器添加钩子函数
当我们训练完模型后如何想要输出里面的一些参数,比如:loss,权重之类的,别着急我们也有方法。这里,我们需要在模型创建中创建张量,也就是说对于要获取的数,我们可以通过tf.identity函数来复制张量,并进行重命名。然后,使用tf.train.LoggingTensorHook定义钩子函数,在模型训练的时候将logging_hook放入estimator.train中。具体实现可以看我们的代码:
def my_model(features, labels, mode, params):
...
# 定义损失函数
loss = tf.losses.mean_squared_error(labels=labels, predictions=predictions)
lossout = tf.identity(loss, name="loss")
...
...
tensors_to_log = {"钩子函数输出": "loss","权重W:":"weight","偏置":"bias"}
logging_hook = tf.train.LoggingTensorHook( tensors=tensors_to_log, every_n_iter=1)
estimator.train(lambda: train_input_fn(train_data, batch_size),steps=200,hooks=[logging_hook])
...
输出结果:
完整代码链接:https://github.com/kingqiuol/learning_tensorflow/blob/master/model/estimators.py
三、tf.keras训练回归模型
tf.keras API是Tensorflow中Keras框架的高级API,tf.keras与Keras框架开发过程相似。
3.1、tf.keras网络搭建
在tf.keras API中,搭建模型有两种方法:用model类搭建模型;用sequential搭建模型。这里先将数据加载搞清楚,这里还是通过Dataset来加载数据,具体代码如下:
import numpy as np
import os
#在内存中生成模拟数据
def GenerateData(datasize = 100 ):
train_X = np.linspace(-1, 1, datasize) #train_X为-1到1之间连续的100个浮点数
train_Y = 2 * train_X + np.random.randn(*train_X.shape) * 0.3 # y=2x,但是加入了噪声
return train_X, train_Y #以生成器的方式返回
train_data = GenerateData()
batch_size=10
#构造数据集的组成:一个特征输入,一个标签输入
dataset = tf.data.Dataset.from_tensor_slices( ( train_data[0],train_data[1]) )
#将数据集乱序、重复、批次划分.
dataset = dataset.shuffle(1000).repeat().batch(batch_size)
- 用model类搭建模型
使用model类搭建模型的步骤主要有:
- 构建输入层:与tensorflow中的占位符类似,用于指定输入数据的形状
- 构建网络:搭建模型
- 创建模型:使用tf.keras.Model来构建模型
具体代码实现如下:
#直接使用model定义网络
def model():
#定义输入数据形状
inputs = tf.keras.Input(shape=(1,))
#搭建网络
outputs= tf.keras.layers.Dense(1)(inputs)
#搭建模型并返回
model = tf.keras.Model(inputs=inputs, outputs=outputs)
return model
当然也可以通过继承Model类来搭建模型,具体操作如下:
(1)定义一个字子类继承tf.keras.Model类
(2)在__init__方法中定义各层网络
(3)在call方法中将层连接起来构建网络
具体代码实现如下:
#继承Model类来搭建模型
class my_model(tf.keras.Model):
def __init__(self):
super(my_model,self).__init__()
self.fc=tf.keras.layers.Dense(1)
def call(self, inputs, training=None, mask=None):
x=self.fc(inputs)
return x
- 用sequential类搭建模型
sequential类搭建模型一般是先定义模型,再搭建模型,这刚好与Model类搭建模型相反。具体代码实现如下:
def model1():
model=tf.keras.Sequential()
model.add(tf.keras.layers.Dense(1, input_shape=(1,)))
# model.add(tf.keras.layers.Dense(1,batch_input_shape=(None,1)))
# model.add(tf.keras.layers.Dense(1, input_dim=1))
return model
搭建完成后可以使用summary()函数来打印网络。
model=model()
model.summary()#打印网络
输出结果如下:
3.2、tf.keras模型搭建
构建模型后,通过调用compile方法配置其训练过程:
# 选择损失函数和优化方法
model.compile(loss='mse',optimizer='sgd',metrics=['accuracy'])
# model.compile(loss=tf.losses.mean_squared_error,optimizer=tf.keras.optimizers.SGD)
tf.keras.Model.compile有三个重要参数:
- optimizer:训练过程的优化方法。此参数通过
tf.train
模块的优化方法的实例来指定,比如:AdamOptimizer
,RMSPropOptimizer
,GradientDescentOptimizer
。 loss
:训练过程中使用的损失函数(通过最小化损失函数来训练模型)。 常见的选择包括:均方误差(mse),categorical_crossentropy和binary_crossentropy。 损失函数由名称或通过从tf.keras.losses模块传递可调用对象来指定。metrics
:训练过程中,监测的指标(Used to monitor training)。
指定方法:名称 或 可调用对象 from thetf.keras.metrics
模块。
3.3、tf.keras模型训练
模型训练有两种方法:
- 在使用train_on_batch方法,这种方法集成度较低
- fit方法
#使用test_on_batch
for step in range(200):
cost=model.train_on_batch(train_data[0],train_data[1])
if step%10==0:
print("loss",cost)
# #直接使用fit来训练
model.fit(x=train_data[0],y=train_data[1],batch_size=batch_size,epochs=20)
输出结果如下:
对于训练好的参数,还可以通过get_weights方法获取参数。对于直接使用Model类创建的网络和使用Sequential类创建的网络,两者的使用方法不同,具体如下:
#直接使用Model类创建的网络
w,b=model.get_weights()
print("weights:",w)
print("bias:",b)
#直接使用Sequential的网络类创建
w,b=model.layers[0].get_weights()
print("weights:",w)
print("bias:",b)
当然,我们也可以保存训练好的网络,具体如下:
- 生成h5py类型的文件
#保存模型
model.save('my_model.h5')
- 生成tensorflow格式的文件
#生成tf格式模型
model.save_weights('./keraslog/kerasmodel') #如果是以 '.h5'或'.keras'结尾,默认会生成keras格式模型
#生成tf格式模型,手动指定
os.makedirs("./kerash5log", exist_ok=True)
model.save_weights('./kerash5log/kerash5model',save_format = 'h5')#可以指定save_format为h5 或tf来生成对应的格式
3.4、tf.keras模型预测
tf.keras也有模型的预测函数evalue方法和prefict方法。
cost = model.evaluate(train_data[0], train_data[1], batch_size = 10)#测试
print ('test loss: ', cost)
a = model.predict(train_data[0], batch_size = 10)#预测
print(a[:10])
print(train_data[1][:10])
输出结果如下:
100/100 [==============================] - 0s 278us/sample - loss: 0.1073
test loss: 0.1072970487177372
[[-2.0070317]
[-1.9664692]
[-1.9259067]
[-1.8853441]
[-1.8447815]
[-1.8042191]
[-1.7636566]
[-1.723094 ]
[-1.6825316]
[-1.641969 ]]
[-2.01088065 -1.57917425 -2.25677826 -2.85679992 -2.01848988 -2.10365978
-1.91662901 -2.14872541 -2.21663289 -1.58879547]
如果我们想加载现有模型文件,则可以如下:
#加载
model = tf.keras.models.load_model('my_model.h5')
a = model.predict(train_data[0], batch_size = 10)
print("加载后的测试",a[:10])
输出结果如下:
加载后的测试 [[-1.810153 ]
[-1.7737786]
[-1.7374041]
[-1.7010297]
[-1.6646551]
[-1.6282808]
[-1.5919063]
[-1.5555317]
[-1.5191574]
[-1.4827828]]
完整代码链接:https://github.com/kingqiuol/learning_tensorflow/blob/master/model/keras.py
四、eager训练回归模型
4.1、定义动态图网络
定义动态图网络与定义静态图不同:
- 动态图不支持占位符
- 动态图不能使用优化器的minimize方法,需要tf.GradientTape()方法与优化器的apply_gradients方法组合
具体代码如下
# 定义学习参数
W = tf.Variable(tf.random.uniform([1]),dtype=tf.float32, name="weight")
b = tf.Variable(tf.zeros([1]),dtype=tf.float32, name="bias")
def getcost(x,y):#定义函数,计算loss值
# 前向结构
z = tf.cast(tf.multiply(np.asarray(x,dtype = np.float32), W)+ b,dtype = tf.float32)
cost =tf.reduce_mean( tf.square(y - z))#loss值
return cost
def grad( inputs, targets):#获取模型参数的梯度。
with tf.GradientTape() as tape:
# tape.watch([W,b])
loss_value = getcost(inputs, targets)
return tape.gradient(loss_value,[W,b])
# 随机梯度下降法作为优化器
learning_rate = 0.01
optimizer = tf.keras.optimizers.SGD(learning_rate=learning_rate)
4.2、训练动态图网络
training_epochs = 20 #迭代训练次数
display_step = 2
plotdata = { "batchsize":[], "loss":[] }#收集训练参数
step=0
for epoch in range(training_epochs):
for (x, y) in zip(train_X, train_Y):
step+=1
grads = grad(x, y)
optimizer.apply_gradients(zip(grads, [W, b]))
# 显示训练中的详细信息
if step % display_step == 0:
cost = getcost(x, y)
print("Epoch:", step , "cost=", cost.numpy(), "W=", W.numpy(), "b=", b.numpy())
plotdata["batchsize"].append(step*len(train_X))
plotdata["loss"].append(cost.numpy())
print ("cost=", getcost (train_X, train_Y).numpy() , "W=", W.numpy(), "b=", b.numpy())
#显示模型
plt.plot(train_X, train_Y, 'ro', label='Original data')
plt.plot(train_X, W * train_X + b, label='Fitted line')
plt.legend()
plt.show()
最终网络训练过程如下:
显示最终模型为:
当然,我们也可以看看训练的loss曲线是什么样的:
def moving_average(a, w=10):#定义生成loss可视化的函数
if len(a) < w:
return a[:]
return [val if idx < w else sum(a[(idx-w):idx])/w for idx, val in enumerate(a)]
plotdata["avgloss"] = moving_average(plotdata["loss"])
plt.figure(1)
plt.subplot(211)
plt.plot(plotdata["batchsize"], plotdata["avgloss"], 'b--')
plt.xlabel('Minibatch number')
plt.ylabel('Loss')
plt.title('Minibatch run vs. Training loss')
plt.show()
完整代码链接:https://github.com/kingqiuol/learning_tensorflow/blob/master/model/dynamic_graph.py
总结
对tf.keras、eager、Estimators三种框架有一个基本的了解后,我们需要将其运用与我们的工程实践中,我们需要合理的去使用这些框架来完成我们的实际工程中的网络。接下来,我们将使用这些框架,并介绍一些实践中如何灵活的运用,当然有兴趣的可以继续看看这些框架的其他相关的知识。期待其他的篇章吧。。。