我们来做个 TensorFlow 的快速入门模型分享。
这次的学习目标就是模型构建的一些相关 API,其中模型的构建,包括 Model 和 layers,然后我们模型的损失函数、优化器、损失等等,主要包括 losses、optimizer、metrics。其中这个 optimizer 呢,之前我们刚刚接触过,已经讲解过了。
接着,我们来看看「模型构建」,我们在 Tensorflow 当中推荐使用 Keras 来构建模型,它是一个广为流行的高级神经网络 API,而且当我们使用 Keras 模块来构建模型的时候,它的速度是非常非常快的。它既简单、快速,又不失灵活的特性,让大家喜欢的不得了,现在 TensorFlow 官方已经过内置和做了全面支持。
接下来呢,我们就来看看这里面是如何去构建的。
主要是怎么去构建的呢? Keras 当中有两个重要的概念,就是模型(Model) 和层(Layer)。那「层」是干嘛的呢?它是将我们的各种计算和变量流程进行封装,比如说 CNN 当中的卷积层、池化层,其实还有全链接层都给它封装好,不需要你自己去构建。Keras 它在 tf.keras.layers 模块当中内置了很多各种各样的结构,大量常用的预定义层。
模型呢,它主要是将各个层组装在一起是吧。前面是几个全连接层,或者最后几个全连接层,前面是几个卷积池化层。我们在把它组织在一起时,需要用到 Model 类,也相当于我们可以通过一个函数或者一个方法直接 Model 将东西装进去之后,直接返回了一个模型的输出结果。
我们接着来看看怎么构建。Keras 当中的模型是以类方式去呈现的,我们需要去继承 Model 模块,在继承之后还需要去实现的两个方法,一个是重写 init() 方法,也就是初始化方法、构造函数;另外一个叫做 call(input),这个方法主要用来调用这个模型的。
比如我要去调用模型,输出这个模型结构,你需要去实现 call(),我们看一下怎么做的。
class MyModel(tf.keras.Model): def __init__(self): super().__init__() # 此处可以添加初始化代码 # 在 call 方法中会用到的层,在这里初始化 def call(self, input): # 此处可以添加模型调用的代码 # 调用不同层获得的结果,作为下一个层的参数 # 可以 1~n 层 # 获得最终结果 return output # 添加自定义的方法
以下是代码的详细解释:
我们先定义一个 MyModel 类,让它继承 Keras 的 Model 类。接着就去提供一个 init 初始化方法,在初始化方法中,第一步先 super 一下 init,这是调用父类的方法,接着往下就可以去写一些初始化相关的内容了。比如你可以在这里自定义一些层,或者初始化一些参数等等,都是可以在这里面去实现的。
往下走呢,还有一个 call(self, input) 方法,你看,这里有两个参数,其中 input 是用来接收输入的信息,你这个模型的输入是什么呢?数据从哪里来的呢?它都是从 input 这个地方输入的。
我们在输入之后,就要构建每一层来达到作为下一层的输入,比如第一层构建好输出结果,作为第二层的输入,进而再获得一个输出结果。一直走到最后,就把这几层的 output 返回出去,就可以了。
接着我们看这张图,主要展示了我们在定义模型的时候应该怎么去做。你自己的定义模型类,先继承 tf.keras.model,接着呢用构造器去初始化模型所需的层,当然有些层是可以我们自定义的。然后我们通过调用 call 方法来进行重写,重写之后我们就可以去进行一个模型定义了。
你看,在整个使用过程中,其实是非常简单、容易、方便的,不需要我们自己再去写一大堆的前向传播的整个模型过程了。你在这里头可以使用一些 layers 内置的层来去进行实现,非常简单的。
我们继续来看一个案例,使用 Model 去建一个线性回归模型。那我们应该怎么建立呢?其实很简单,你看下面得代码,也已经写得很详细了,接着我们来看看具体是怎么实现的。
import tensorflow as tf X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) y = tf.constant([[10.0], [20.0]]) # 1、构建线性模型 class Linear(tf.keras.Model): def __init__(self): super().__init__() self.dense = tf.keras.layers.Dense( units=1, activation=None, kernel_initializer=tf.zeros_initializer(), bias_initializer=tf.zeros_initializer() ) def call(self, input): output = self.dense(input) return output model = Linear() optimizer = tf.keras.optimizers.SGD(learning_rate=0.01) for i in range(100): with tf.GradientTape() as tape: y_pred = model(X) loss = 0.5 * tf.reduce_mean(tf.square(y_pred - y)) # 使用 model.variables 这一属性直接获得模型中的所有变量 grads = tape.gradient(loss, model.variables) optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables)) print(model.variables)
以下是代码的详细解释:
先定义一个 Linear 类,并且让它继承 tf.keras.model,接着在 init 初始化方法中初始化层,又因为现在是一个线性回归,所以这里我们只有一个神经元。
这个 dance 代表什么呢? 它表示一个全连接层,全连接层当只有一个神经元的时候,是不是代表了就是一个逻辑回归,或者一个线性回归呀?然后,激活函数没有,因为线性回归不含激活函数。
还有什么呢?还有线性回归神经元当中的参数权重和偏置,是不是要进行初始化?初始化的时候,你导入的 Tensorflow 当中可以用 zeros_initializer() 这种 0 初始化器进行一个初始化。
当我把线性回归模型初始化之后,也就相当于一层,紧接着调用线性回归的时候,也就是把输入通过 input 传给 dense(input) 这里,经过神经元计算出来,也就是一个线性计算,直接输出结果。由于 activation = None,代表没有激活函数,所以得到输出结果 output。
后面接下来的,就简单了。
先是构建模型,接着是构建优化器,通过 for 循环语句来循环 100 遍。
通过 model(x) 中的 x,可以接收到给定的 x 数据 —— 其中在顶部我们已经给定过的一些 x 和 y 的数据。当我们就将数据输入到模型之后,即可得到预测值。接着往下是,loss = 0.5 * reduce_mean(tf.square(y_pred - y)),再往后的结构都是一模一样的了。
有没有注意到一点,我们要获取变量,怎么获取呢?前面的变量是我们自己去进行初始化的,现在不需要了,主要是 dance 层进行初始化,我们的权重偏置了。
我们如果要让它的梯度跟变量一一对应的时候,应该怎么做?需要去调用 model 当中的一个叫做 variables 的属性变量,它可以获取我们当中的权重偏置,并且与我们的 grads 进行一一对应地变成一个列表,列表里面是一个元组。
最后,我们就可以获取优化过后的 variables 最新变化值,它一开始初始化的值是 0。
总结
我们上面使用 Model 快速建立一个模型,总结一下,很简单的:
1、我们的权重不需要初始化了
2、我们构建模型也比较简单,使用 dance 就可以,不需要我们进行线性计算。
事实上,如果模型越来越复杂的话,那么这个方法的优势就会越来越明显得。