代码写作套路【1】使用tensorflow构建模型的2种方法

代码写作套路【1】使用tensorflow构建模型的2种方法_第1张图片

tensorflow是深度学习训练的常用框架,其代码书写有一些可以学习的套路。这个系列的博客将总结tensorflow构建深度学习网络并训练的几种套路。更进一步,结合tensorboard的使用,在这个系列的博客里还会介绍如何使用tensorboard来可视化实验结果。

目录

  • 前言
    • 数据准备
  • 模型构建
    • 使用Sequential
    • 使用model class
    • 给层加参数
  • 实验结果可视化
    • 输出中间层的结果
  • 模型训练
    • compile + fit
    • tf.GradientTape
  • 可视化
  • 参考
  • 备注

前言

相信正在看这篇博客的你已经知道tensorflow是做什么的,也正在面对需要使用tensorflow进行深度学习的问题,你也一定在网上搜索了许多别人写的tensorflow框架下的代码。你一定发现了,不同人写的代码风格是不一样的,有时候这种风格各异会增加阅读代码的困难程度。

这正是我写这系列博客的目的,我同系列的博客里记录了,我遇到的写tensorflow代码的风格以及它们的特点。这个系列的博客会长期更新。

为了方便阅读了这篇博客的你阅读其他博客,这篇博客会设置一个统一的训练问题以及训练环境,接下来的所有博客都是基于这个设置进行的。

# packages
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import datetime
# configs 
input_shape = 1
output_shape = 1
EPOCH = 20

数据准备

这系列博客目的是分享tensorflow代码风格,采用的数据毫无实际意义,是随机产生的数据。

# data prepare
x_train = np.linspace(-1, 1, 100)
y_train = x_train * x_train * x_train + 0.1 * np.random.random(x_train.size)

x_test = np.linspace(-1, 1, 100)
y_test = x_train * x_train * x_train + 0.2 * np.random.random(x_test.size)

train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))

模型构建

使用Sequential

这里的模型结构比较朴素简单,可以构建最简单,无分支的深度学习模型。下面的代码参考了keras官网对Sequential class的描述。使用Sequential可以将模型分层叠好,这里需要注意模型的输入输出结构,以及模型模块值域是否能匹配预期。(例如有的激活函数的值域大于0,有的是-1到1之间,需要具体问题具体分析。)

# model define
def build_model(input_shape, output_shape):
    model = tf.keras.models.Sequential()
    model.add(tf.keras.layers.Dense(10, input_dim=input_shape,kernel_initializer='random_normal',bias_initializer='zeros'))
    model.add(tf.keras.layers.LeakyReLU(alpha=0.01))
    model.add(tf.keras.layers.Dense(10, input_dim=10,kernel_initializer='random_normal',bias_initializer='zeros'))
    model.add(tf.keras.layers.LeakyReLU(alpha=0.01))
    model.add(tf.keras.layers.Dense(1, input_dim=10,kernel_initializer='random_normal',bias_initializer='zeros'))
    model.add(tf.keras.layers.LeakyReLU(alpha=0.01))
    return model

使用model class

这种构建方法更为灵活,可以处理有分支的模型,详情见「知乎帖子」tensorflow笔记:高级封装——keras。事实上官网也对这种代码模式有介绍,见keras官网对model class的描述

def build_model(input_shape, output_shape):
    input = tf.keras.layers.Input(shape=(input_shape))
    dense1 = tf.keras.layers.Dense(10, input_dim=input_shape,kernel_initializer='random_normal',bias_initializer='zeros',name='dense1')(input)
    relu1 = tf.keras.layers.LeakyReLU(alpha=0.01,name='relu1')(dense1)
    dense2 = tf.keras.layers.Dense(10, input_dim=10,kernel_initializer='random_normal',bias_initializer='zeros',name='dense2')(relu1)
    relu2 = tf.keras.layers.LeakyReLU(alpha=0.01,name='relu2')(dense2)
    dense3 = tf.keras.layers.Dense(1, input_dim=10,kernel_initializer='random_normal',bias_initializer='zeros',name='dense3')(relu2)
    relu3 = tf.keras.layers.LeakyReLU(alpha=0.01,name='relu3')(dense3)
    output = relu3
    model = tf.keras.models.Model(inputs = input, outputs = output)
    return model

给层加参数

tf.keras.layers.Dense()中本身可以加一个激活函数,例如inter_1 = tf.keras.layers.Dense(inter_shape, activation=tf.nn.relu)(inputs)的写法。当然,我们也可以选择单独写一个激活函数层。例如outputs = tf.keras.activations.relu(inter_5, alpha=0.0, max_value=max_value, threshold=threshold)的写法。

def build_model(input_shape, output_shape, inter_shape, max_value, threshold):
    inputs = tf.keras.Input(shape=input_shape)
    inter_1 = tf.keras.layers.Dense(inter_shape, activation=tf.nn.relu)(inputs)
    inter_2 = tf.keras.layers.Dense(inter_shape, activation=tf.nn.relu)(inter_1)
    inter_3  = tf.keras.layers.Dense(inter_shape, activation=tf.nn.relu)(inter_2)
    inter_4 = tf.keras.layers.Dense(inter_shape, activation=tf.nn.relu)(inter_3)
    inter_5 = tf.keras.layers.Dense(output_shape)(inter_4)
    outputs = tf.keras.activations.relu(inter_5, alpha=0.0, max_value=max_value, threshold=threshold)
    model = tf.keras.Model(inputs=inputs, outputs=outputs)
    return model**

实验结果可视化

存储实验数据是训练模型中极其关键的一步,我想没有人希望自己费了半天劲训练的模型,最后由于实验数据没存好,不得不重新训练一遍。这里也体现了tensorflow框架的优势。tensorflow+tensorboard是进行实验的绝佳搭配,先用tensorflow进行模型训练,训练过程中,将实验数据保留在tensorboard可以识别的文件,再用tensorboard进行实验结果的可视化。

# summary
log_dir="log/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)

输出中间层的结果

重新构建一个模型,其输入为原模型的输入,其输出为指定层的输出。这里我们需要层的名字用于找到这层,层的名字可以用model.summary()查看

part_model = tf.keras.Model(inputs = model.inputs, outputs = model.get_layer(layer_name).output)
tmp = part_model.predict(x_train)

这里参考博客:Keras框架下输出模型中间结果

模型训练

compile + fit

tensorflow的模型在训练前首先需要compile一下,在compile中定义损失函数和优化器。compile后进行fit操作,在fit中定义训练数据集,训练的轮次,batch的大小以及实验结果的可视化。

model = build_model(input_shape, output_shape)
# model compile and train
model.compile(loss='mse',optimizer=keras.optimizers.Adam())
model.fit (
    x = x_train,
    y = y_train,
    batch_size = 12,
    epochs = 20,
    validation_data=(x_test,y_test),
    shuffle=True,
    callbacks=[tensorboard_callback]
)

tf.GradientTape

model.fit()比较适合刚入门的新手,这种训练方法较为简单粗暴,对于已经比较熟悉深度学习训练的油条,可以使用tf.GradientTape()进行模型训练。

model = build_model(input_shape, output_shape)
loss_object = tf.keras.losses.MeanSquaredError(reduction='auto', name='mean_squared_error')
optimizer = tf.keras.optimizers.Adam()
train_loss = tf.keras.metrics.Mean('train_loss', dtype=tf.float32)
test_loss = tf.keras.metrics.Mean('test_loss', dtype=tf.float32)
def train_step(model, optimizer, x_train, y_train):
    with tf.GradientTape() as tape:
        predictions = model(x_train, training=True)
        loss = loss_object(y_train, predictions)
    grads = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))
    train_loss(loss)
def test_step(model, x_test, y_test):
    predictions = model(x_test)
    loss = loss_object(y_test, predictions)
    test_loss(loss)

log_dir="log/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
train_log_dir = log_dir + '/train'
test_log_dir = log_dir + '/test'
train_summary_writer = tf.summary.create_file_writer(train_log_dir)
test_summary_writer = tf.summary.create_file_writer(test_log_dir)

for epoch in range(EPOCHS):
    for (x_train, y_train) in train_dataset:
        train_step(model, optimizer, x_train, y_train)
        with train_summary_writer.as_default():
            tf.summary.scalar('loss', train_loss.result(), step=epoch)
            tf.summary.scalar('accuracy', train_accuracy.result(), step=epoch)

    for (x_test, y_test) in test_dataset:
        test_step(model, x_test, y_test)
        with test_summary_writer.as_default():
            tf.summary.scalar('loss', test_loss.result(), step=epoch)
            tf.summary.scalar('accuracy', test_accuracy.result(), step=epoch)

    template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'
    print (template.format(epoch+1,
                         train_loss.result(), 
                         train_accuracy.result()*100,
                         test_loss.result(), 
                         test_accuracy.result()*100))

    # Reset metrics every epoch
    train_loss.reset_states()
    test_loss.reset_states()
    train_accuracy.reset_states()
    test_accuracy.reset_states()

可视化

运行python脚本,再开一个terminal运行

tensorboard --logdir log

即可查看tensorboard上面的实验结果可视化,具体菜单里每一个选项对应的结果是什么含义可以参考官网上的内容。这里放一张在tensorboard上面的截图。
代码写作套路【1】使用tensorflow构建模型的2种方法_第2张图片

参考

  1. tensorboard官网上关于神经网络搭建的教学博客

备注

  1. 模型无法训练的情况,OOM(out of memory):模型过大,无法放入显存(GPU训练时)or内存(CPU训练时),此时只能减小模型规模。
  2. 完整代码见
  3. 定义的指标若是对本次实验没有指导价值很可能会出现异常:例如在本此实验中,一个回归类问题引入了accuracy来衡量实验结果,这并不合理。即便如此,在训练过程中依旧正常地打印了accuracy,且结果一直是0。这个现象启发我们应该使用针对问题合理可用的指标进行评测。平时读论文时也应该多关注留意各个论文都使用什么指标来衡量实验结果
  4. 官网上的代码参考意义极大,建议直接查看官网代码。

你可能感兴趣的:(套路,深度学习,tensorflow)