tensorflow2.0关注简洁和易用,有以下更新:
学习了解这十大更新并尝试实现它们。tensorflow2.0的发布,将会对深度学习届的研究人员、开发人员产生较大影响,能够更加便捷使用先进的深度学习,推动深度学习发展。
读本文之前,要有一些Tensorflow
和keras
的基础知识,可以看下面两份材料:
import pandas as pd
columns = ["Age", "WorkClass", "fnlwgt", "Education", "EducationNum",
"MaritalStatus", "Occupation", "Relationship", "Race", "Gender",
"CapitalGain", "CapitalLoss", "HoursPerWeek", "NativeCountry", "Income"]
data = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data',
header=None,
names=columns)
data.head()
这个数据集是用来做二分类的,判断一个人的年收入是否超过5万美元。
做一些基本的数据预处理,并把数据4:1分成二份
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
import numpy as np
#label Encode
le = LabelEncoder()
data=data.apply(le.fit_transform)
#分离数据特征并转换成numpy arrays
X = data.iloc[:,0:-1].values
y= data['Income'].values
#4:1分开
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=7)
tensorflow2.0中不用再用session,也不用再定义图。eager execution,而是直接像numpy那样来使用。
#tensorflow2.0
model.fit(X_train,y_train,validation_data=(X_valid,y_valid),epochs=12,batch_size=256,callbacks=[es_cb])
#tensorflow1.10及以后,加入keras
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
sess.run(tf.table_initializer())
model.fit(X_train,y_train,validation_data=(X_valid,y_valid),epochs=12,batch_size=256,callbacks=[es_cb])
eager executin可以使我们实现命令行方式编程,简单快速,但是在分布式训练和部署时,tensorflow1.x的图方式更有优势。tensorflow2.0同样保持了这个优点,可以通过tf.function和autograph来实现。
tf.function可以使python编程方式内容通过autograph功能来实现静态图的功能,目前支持if-else,for loop,while loop,迭代器等。但还有一些限制.tf.function将作为decorator装饰器来起作用。
import tensorflow as tf
import time
#定义前向运算,加tf.function
@tf.function
def single_layer1(x,y):
return tf.nn.relu(tf.matmul(x,y))
#定义前向,不加tf.fucntion
def single_layer2(x,y):
return tf.nn.relu(tf.matmul(x,y))
#生成随机数
x = tf.random.uniform((2,3))
y = tf.random.uniform((3,5))
start1= time.time()
single_layer1(x,y)
duration1 = time.time()-start1
start2= time.time()
single_layer2(x,y)
duration2 = time.time()-start2
print("tf.function:",duration1)
print("no tf.function:",duration2)
对这种情况,加tf.function并没有特别效果,需要做另外专用的配置,这里不再叙述。
tensorflow1.x中tf.variable_scope使同一个scope中的变量容易收集起来,并复用。tensorflow2.0中不再需要,使用keras后所有变量的控制和获取变得简单。
#定义模型
model = tf.keras.models.Sequential([
tf.keras.layers.Dropout(rate=0.2,input_shape=X_train.shape[1:]),
tf.keras.layers.Dense(units=64,activation='relu'),
tf.keras.layers.Dropout(rate=0.2),
tf.keras.layers.Dense(units=64,activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(units=1,activation='sigmoid')
])
out_probs = model(X_train.astype(np.float32), training=True)
print(out_probs)
#以上代码输出数据的原始输入,只是一次前向运算。training=True只是用来控制dropout。
#也可以进行训练
model.compile(optimizer='adam',loss = 'binary_crossentropy',metrics=['accuracy'])
model.fit(X_train,y_train,validation_data=(X_test,y_test),epochs=5,batch_size=64)
#可以获得模型的可训练参数
model.trainable_variables # 与model.trainable_weights相同
研究人员或企业人员,会根据需求自定义一些模型结构。Tensorflow2.0会支持更便捷基于现有层来自定义。自定义需要用到Layer
这个类,
tf.keras.layers.Layer
基于这个类来按需定义。先自定义一个层,然后再使用这个层来自定义一个模型来做前向运算。
#查看tf.keras.Layer功能
help(tf.keras.Layer)
#部分输出,指出我们需要实现Layer中的那些内容
We recommend that descendants of `Layer` implement the following methods:
|
| * `__init__()`: Save configuration in member variables
| * `build()`: Called once from `__call__`, when we know the shapes of inputs
| and `dtype`. Should have the calls to `add_weight()`, and then
| call the super's `build()` (which sets `self.built = True`, which is
| nice in case the user wants to call `build()` manually before the
| first `__call__`).
| * `call()`: Called in `__call__` after making sure `build()` has been called
| once. Should actually perform the logic of applying the layer to the
| input tensors (which should be passed in as the first argument).
可以看出,我们需要做如下定义:
build()
方法中填加变量参数call()
方法中实现前向运算环节class MyDenseLayer(tf.keras.layers.Layer):
def __init__(self,num_units=None):
super().__init__()
self.units=num_units
def build(self,input_shape):
self.kernel = self.add_weight("kernel",shape=[input_shape[-1],self.units])#input_shape这个参数名随便起
self.bias = self.add_variable("bias",shape=[self.units,])#可以看来add_weight和add_variable是一样的
#定义前向运算
def call(self,inputs):
return tf.nn.relu(tf.matmul(inputs,self.kernel)+self.bias)
#初始化layer
layer = MyDenseLayer(10)
#输入数据
output = layer(tf.random.uniform((10,3)))
print(output)
#显示变量
print(layer.variables)#layer.weights也是一样的
tensorflow2.0支持自动微分,可以自动求损失对模型参数的梯度。tf.GradientTape
提供对变量梯度的记录。
tf.keras.backend.clear_session()
class CustomModel(tf.keras.models.Model):
def __init__(self):
super().__init__()
self.do1 = tf.keras.layers.Dropout(rate=0.2,input_shape=(14,))
self.fc1= tf.keras.layers.Dense(units=64,activation='relu')
self.do2 = tf.keras.layers.Dropout(rate=0.2)
self.fc2 = tf.keras.layers.Dense(units=64,activation='relu')
self.do3 = tf.keras.layers.Dropout(rate=0.2)
self.out = tf.keras.layers.Dense(units=1,activation='sigmoid')
def call(self,inputs):
x = self.do1(inputs)
x = self.fc1(x)
x = self.do2(x)
x = self.fc2(x)
x = self.do3(x)
return self.out(x)
model = CustomModel()
如上代码,与我们之前定义的模型是一模一样的,要训练这个模型,需要定义优化和损失函数
loss_func = tf.keras.losses.BinaryCrossentropy(from_logits=True)#from_logits=True写不写都行,默认是认为y_pred是概率,但写了可能会更稳定(这是说明文档中的原话)
optimizer = tf.keras.optimizers.Adam()
还需要定义评价指标来评估模型训练过程中的性能。
train_loss = tf.keras.metrics.Mean(name='train_loss')
train_acc = tf.keras.metrics.BinaryAccuracy(name='train_acc')
test_loss = tf.keras.metrics.Mean(name='valid_loss')
test_acc = tf.keras.metrics.BinaryAccuracy(name='valid_acc')
tf.data
提供了通用的数据读取方法,特别是数据量大的时候。
X_train,y_train = X_train.astype(np.float32),y_train.astype(np.int32)
X_test,y_test = X_test.astype(np.float32),y_test.astype(np.int32)
y_train,y_test = y_train.reshape(-1,1),y_test.reshape(-1,1)
#batch size =64
train_ds = tf.data.Dataset.from_tensor_slices((X_train,y_train)).batch(64)
test_ds = tf.data.Dataset.from_tensor_slices((X_test,y_test)).batch(64)
接着就可以基于tf.GradientTape
来训练模型。
#训练模型
@tf.function
def train_on_batch(features,labels):
#定义GradientTape
with tf.GradientTape() as tape:
#前向运算
logits = model(features)
#求损失
loss = loss_func(labels,logits)#交换顺序也没关系
#计算梯度
gradients = tape.gradient(loss,model.trainable_variables)
optimizer.apply_gradients(zip(gradients,model.trainable_variables))
train_loss.update_state(loss)
train_acc.update_state(labels,logits)#这两个写不写updata_state都可以,以后默认就不写了,省事
#测试模型
@tf.function
def test_on_batch(features,labels):
logits = model(features)
t_loss = loss_func(labels,logits)
test_loss(t_loss)
test_acc(labels,logits)
#用上边两个函数完成5个epoch训练
start = time.time()
for epoch in range(5):
for i,(features,labels) in enumerate(train_ds):
train_on_batch(features,labels)
for i,(features,labels) in enumerate(test_ds):
test_on_batch(features,labels)
template='Epoch {}, train_loss: {} ,train_acc:{},test_loss:{},test_acc:{}'
print(template.format(epoch+1,train_loss.result(),train_acc.result()*100,test_loss.result(),test_acc.result()*100))
# Reset the metrics for the next epoch
train_loss.reset_states()
train_acc.reset_states()
test_loss.reset_states()
test_acc.reset_states()
duration=time.time()-start
print("total time:",duration)
可以将tf.function去掉做对比,看看训练时间的差异,可以得到eager模式与图模式的区别。
有tf.function的情况下:
无tf.function的情况下:
#另外数据如果加prefetch会加快训练
train_ds = tf.data.Dataset.from_tensor_slices((X_train,y_train)).batch(64).prefetch(128)
test_ds = tf.data.Dataset.from_tensor_slices((X_test,y_test)).batch(64).prefetch(128)
有tf.function时候,加prefetch(都是多次测量的结果)
无tf.function时候,加prefetch,感觉提升不是很多,可能是因为数据小,模型简单的原因。
一个单独的模块叫做DataSets
,提供数据集的准备工作,可以通过pip来安装。
可以从以下两个地方学习了解:
- https://www.youtube.com/watch?v=-nTe44WT0ZI
- https://github.com/tensorflow/datasets
如下,mnist的准备流程:
```python
import tensorflow_datasets as tfds
# You can fetch the DatasetBuilder class by string
mnist_builder = tfds.builder("mnist")
# Download the dataset
mnist_builder.download_and_prepare()
# Construct a tf.data.Dataset: train and test
ds_train, ds_test = mnist_builder.as_dataset(split=[tfds.Split.TRAIN, tfds.Split.TEST])
# Prepare batches of 128 from the training set
ds_train = ds_train.batch(128)
# Load in the dataset in the simplest way possible
for features in ds_train:
image, label = features["image"], features["label"]
import matplotlib.pyplot as plt
%matplotlib inline
# You can convert a TensorFlow tensor just by using
# .numpy()
plt.imshow(image[0].numpy().reshape(28, 28), cmap=plt.cm.binary)
plt.show()
技术由英伟达提供,效果不错,详细参见算法原文。会利用fp16和fp32的优势。使用英伟达的显卡时,可以安装tensorflow2.0gpu版本来运行。
os.environ['TF_ENABLE_AUTO_MIXED_PRECISION'] = '1'
这个操作会自动使tensorflow的operations进行改变。会看到模型性能的提升。也可以自己优化tensorflow core operations.具体参看文档
tensorflow2.0提供更简单的分布式训练方法,单机多卡,多机多卡,都会易用。
首先指定分布式训练的策略:
mirrored_strategy = tf.distribute.MirroredStrategy()
#该策略使所有gpu有同的模型结构和参数
#如下开始使用
with mirrored_strategy.scope():
model = tf.keras.Sequential([
tf.keras.layers.Dense(1,input_shape=(1,))
])
model.compile(optimizer='sgd',loss='mse')
model.fit(X_train,y_train,validation_data(X_test,y_test),batch_size=128,epochs=10)
以上内容只对单机多卡有用,多机多卡参见:文档
使用tensorboard可以在jupyter notebook中直接查看训练情况.新的tensorboard可以看memory profiling,confuse matrix,
可以查看文档.
#jupyter notebook中执行
%load_ext tensorboard.notebook
######################
from datetime import datetime
import os
# Make a directory to keep the training logs
os.mkdir("logs")
# Set the callback
logdir = "logs"
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=logdir)
# Define the model
model = tf.keras.Sequential([
tf.keras.layers.Dropout(rate=0.2, input_shape=X_train.shape[1:]),
tf.keras.layers.Dense(units=64, activation='relu'),
tf.keras.layers.Dropout(rate=0.2),
tf.keras.layers.Dense(units=64, activation='relu'),
tf.keras.layers.Dropout(rate=0.2),
tf.keras.layers.Dense(units=1, activation='sigmoid')
])
# Compile the model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# Split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=7)
# The TensorBoard extension
%tensorboard --logdir logs/
# Pass the TensorBoard callback you defined
model.fit(X_train, y_train,
validation_data=(X_test, y_test),
batch_size=64,
epochs=10,
callbacks=[tensorboard_callback],
verbose=False)
由于python比较慢,所以tensorflow团队开发了一个swift版本的,可以看一下官方文档.