tensorflow实战之模型搭建:tf.keras、eager、Estimators框架

这篇是紧接上一篇,这里主要推荐tf.keras、eager、Estimators这三种作为2.x版本的框架作为我们在工程实践中使用的框架。接下来这篇中将详细介绍如何使用这三种框架来搭建自己的模型,以及其中的注意要点。疫情太严重没法出门了,只有把之前学过的来个总结,好吧,开始。


目录

一、数据集

二、Estimators训练回归模型

2.1、输入数据

2.2、模型网络搭建

2.3、Estimator(估算器)的执行

2.4、验证估算器模型

2.5、预测使用估算器模型

2.6、为估算器添加钩子函数

三、tf.keras训练回归模型

3.1、tf.keras网络搭建

3.2、tf.keras模型搭建

3.3、tf.keras模型训练

3.4、tf.keras模型预测

四、eager训练回归模型

4.1、定义动态图网络

4.2、训练动态图网络

总结


 

一、数据集

这里我们采用自己生成的数据集来拟合一个线性回归模型,并将这些数据作为我们搭建的模型的输入。生成代码如下:

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_fneval_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,必须提供的是 losstrain_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类搭建模型的步骤主要有:

  1. 构建输入层:与tensorflow中的占位符类似,用于指定输入数据的形状
  2. 构建网络:搭建模型
  3. 创建模型:使用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 模块的优化方法的实例来指定,比如:AdamOptimizerRMSPropOptimizerGradientDescentOptimizer
  • loss:训练过程中使用的损失函数(通过最小化损失函数来训练模型)。 常见的选择包括:均方误差(mse),categorical_crossentropy和binary_crossentropy。 损失函数由名称或通过从tf.keras.losses模块传递可调用对象来指定。
  • metrics:训练过程中,监测的指标(Used to monitor training)。
    指定方法:名称 或 可调用对象 from the tf.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三种框架有一个基本的了解后,我们需要将其运用与我们的工程实践中,我们需要合理的去使用这些框架来完成我们的实际工程中的网络。接下来,我们将使用这些框架,并介绍一些实践中如何灵活的运用,当然有兴趣的可以继续看看这些框架的其他相关的知识。期待其他的篇章吧。。。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值