Tensorflow 从 2.0 版本开始,Keras 就被深度集成在了其中1。同时,Tensorflow 2.0 相较于 1.x 版本,在很多方面都做出了改变。这里我尝试使用 tf.keras
针对 Regression 和 Classification 搭建简单的神经网络,记录一下过程,并进行简单的梳理。
Import
复制粘贴,一次引入,快捷高效,简单粗暴:
1 | import tensorflow as tf |
在这,也记录一下我当前的版本信息:
sys.version_info(major=3, minor=7, micro=6, releaselevel='final', serial=0)tensorflow 2.0.0tensorflow_core.keras 2.2.4-tfmatplotlib 3.1.1numpy 1.18.1pandas 0.25.3sklearn 0.22.1
Regression
Dataset
回归问题的数据集,使用的是 sklearn 中的“加州房价”:
1 | from sklearn.datasets import fetch_california_housing |
Data Split
按默认 3:1 的比例拆分数据(即 test_size=0.25
),选取出训练集、验证集、测试集。
1 | from sklearn.model_selection import train_test_split |
Normalization
一言不合正则化:
1 | from sklearn.preprocessing import StandardScaler |
这里我想简单说一说 fit_transform()
和 transform()
的区别,因为这个细节我之前也琢磨了一段时间,这里,还得提到另外一个函数,就是 fit()
。官方的 API 文档中2,对这三个函数是这样解释的:
fit(self, X[, y])
Compute the mean and std to be used for later scaling.
transform(self, X[, copy])
Perform standardization by centering and scaling.
fit_transform(self, X[, y])
Fit to data, then transform it.
简单而言,fit_transform()
就是整合了 fit()
和 transform()
的功能。当我在训练集上用 fit_transform()
做 scale 的时候,它会先计算均值和方差,并记录下来,这就是 fit()
的功能。然后,再对数据执行归一化,也就是 transform()
。
而验证集和测试集,都是用的训练集的均值和方差。也就是说,在训练集有过一次拟合后,验证集和测试集就可以直接根据训练集的均值和方差,调用 transform()
执行归一化。
Model
在网络搭建的过程中,相比 1.0 版本,Tensorflow 2.0 的确是引起舒♂适!
1 | # 搭建网络 |
通过 model.summary()
可以看到网络层次,就两个 dense layer,一个输入,一个输出:
Model: "sequential_1"_________________________________________________________________Layer (type) Output Shape Param # =================================================================dense_1 (Dense) (None, 30) 270 _________________________________________________________________dense_2 (Dense) (None, 1) 31 =================================================================Total params: 301Trainable params: 301Non-trainable params: 0
Train
使用 model.fit()
开启训练。with tf.Session() as sess:
什么的,甚至 tf.global_variables_initializer()
什么的,这里不需要了:
1 | history = model.fit(x_train_scaled, y_train, |
Plot
1 | def plot_learning_curves(history): |
Evaluate
1 | model.evaluate(x_test_scaled, y_test, verbose=0) |
看一下测试集的结果:
0.3782297415326732
Classification
Dataset & Data Split
分类问题的数据集使用的是 Keras 的 fashion_mnist
:
1 | # 使用 fashion_mnist 数据集 |
Normalization
由于 fit_transform()
要求输入的参数是一个二维矩阵,因此 x_train
的结构就要从 [None, 28, 28]
转换成 [None, 784]
,接着再转回来,所以要 reshape()
两次:
1 | from sklearn.preprocessing import StandardScaler |
Model
再次引起舒♂适。借助 tf.keras
的 API,搭建网络添加 layers 的时候,真的十分方便。方法也不止一种。例如,上面已经提到过的,通过给 Sequential()
传递一个 list 的形式:
1 | model = keras.models.Sequential([ |
这里的 loss 函数用的是 sparse_categorical_crossentropy
,因为数据集中的 y,都是一个个标量,例如 y_train[0]
的值是 4
。而要计算 loss,就需要是向量,因此需要经过 One-Hot,转成向量才可以。
另外,这里还可以通过调用 add()
函数来添加 layers:
1 | model = keras.models.Sequential() |
所以,这样一来,对于某些深度神经网络,就可以直接用 for
循环搭建,例如:
1 | for _ in range(20): |
后面要是有别的 layer,直接跟上就可以了。
Train
1 | history = model.fit(x_train_scaled, y_train, |
Plot
1 | def plot_learning_curves(history): |
Evaluate
结果大概看一下:
1 | model.evaluate(x_test_scaled, y_test, verbose=0) |
[0.4324875540494919, 0.8463]
关于 Callback 输出路径
模型训练的过程中,有遇到过一个 callback 输出路径的问题,应该算是 Tensorflow 的一个 bug。比如,当我想保存模型文件的时候,我会先建立一个路径:
1 | logdir = "./callbacks" |
由于最近我使用的环境是 Windows,所以就遇到了这样的报错:
ProfilerNotRunningError: Cannot stop profiling. No profiler is running.
说白了,就是不能有 /
。这个问题在 Github 的一个 Issue3 上也有讨论。
因此在 Mac 以及 Linux 环境下:
1 | logdir = "./callbacks" |
而在 Windows 环境下请使用:
1 | logdir = "callbacks" |
最后不得不说:
Eager Mode, YES!
参考链接
[1] Google, TensorFlow API Module: tf.keras Overview
[2] scikit-learn API: sklearn.preprocessing.StandardScaler
[3] Github Issue, TensorFlow keras callback using tensorboard, “ProfilerNotRunningError: Cannot stop profiling. No profiler is running.” #2279