前言
近日,使用tensorflow的频率比较高,使用过程中也是遇到了一些大大小小的问题。有些着实让人脑瓜子疼。此刻借着模型训练的时间,开始码码字。本来标题想取:
- 什么?2021了,还有人用Tensorflow?
- 震惊!代码练习生竟然在用······Tensorflow?
- 我和tensorflow不共戴天
- 我想给tensorflow来一大嘴巴子
- ······
最后,用了这个当我遇到tensorflow2.x时
。无论学习还是生活中,我们都会遇到各种各样的人或事或物。当我们遇到时,会发生什么?我们是会充满期待的。当我遇到···时,我会···
。这个句式是我喜欢的,大部分人习惯在前半部分大胆设想,后半句夸下豪言壮语。这里取前半句,是因为已经发生了,而省去后半句,恰恰是因为豪言壮语很容易翻车。
这是一篇记录使用tensorflow过程中遇到的一些小而折磨人的问题的博文。但我预言这也将是一篇持久的对tensorflow的血泪吐槽文。
如何看待Keras正式从TensorFlow中分离?
不知道为什么想到了这个知乎话题。六月份的某天,Keras 之父 Francois Chollet宣布将 Keras 的代码从 TensorFlow 代码库中分离出来,移回到了自己的 repo。乍一看,还以为以后tensorflow的keras接口用不了了。但人家只是把keras代码搬回了属于自己的repo。原本的tf.keras
还是能用的。
For you as a user, absolutely nothing changes, now or in the future.
底下全是一片叫好,天下苦tensorflow久已。而我也并不看好这对情侣或者说组合。各自单飞,独自美丽不好吗?keras何必委曲求全做别人的嫁衣。
抛开keras,tensorflow还剩什么?
我想这应该是吐槽后,该冷静思考的问题。而回答这个问题,是需要去阅读官方文档以及实践的。所以,那个句式的后半句也可以是下面的记录。才疏学浅,当厚积薄发。
言归正传,之后遇到的bug都记录在下面部分。
—————————————–———-—-————分割线——————-—————————————————–—
tf.config.run_functions_eagerly(True)
有关Eager Execution
戳这里
然后以下是我粗俗的理解:
这是即时运行和计算图运行相关的概念。即时运行可以让你的程序立马返回结果,计算图运行会先构建计算图(记录你的程序执行行为及顺序),在最后按照构建的图进行计算。
有些晦涩难理解。
模型训练时一般有:
1 2 3 4
| @tf.function def train_step(): with tf.GradientTape() as tape: ···train model code···
|
这在模型训练过程中是会构建计算图的(具体参考戳这里),构建计算图可以,这时如果在代码中print(x)
一下,就会发现这是没有具体值的,而且没有.numpy()
属性。返回的即计算图中节点的符号句柄
。所以我为什么要在这里print
呢?当然是为了调试代码(/滑稽.jpg)。
1
| Tensor("x:0", shape=(32, 32), dtype=int32)
|
官网提到tensorflow2.x是默认开启Eager Execution
的,然而代码中(如上)使用了@tf.function
装饰器,默认以图的方式执行。
1
| The code in a Function can be executed both eagerly and as a graph. By default, Function executes its code as a graph.
|
要关闭默认方式,可以通过设置:tf.config.run_functions_eagerly(True)
来实现。或者干脆不要加这个装饰器。
最后,Eager Execution
增强了开发和调试的交互性,而@tf.function
计算图执行在分布式训练、性能优化和生产部署方面具有优势。简而言之,Eager Execution
适合开发过程中调试,@tf.function
适合线上部署。
——————-2021.9.17更新———————
自定义
参考->这里
定义模型
抛开keras的sequential
, 使用 tensorflow定义模型时,可以有两种继承选择:tf.keras.Model
和 tf.Module
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class TFModel(tf.Module): def __init__(self, **kwargs): super().__init__(**kwargs) self.w = tf.Variable(5.0) self.b = tf.Variable(0.0)
def __call__(self, x): return self.w * x + self.b tf_model = TFModel()
class KerasModel(tf.keras.Model): def __init__(self, **kwargs): super().__init__(**kwargs) self.w = tf.Variable(5.0) self.b = tf.Variable(0.0)
def call(self, x, **kwargs): return self.w * x + self.b keras_model = KerasModel()
|
定义训练循环:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| import tensorflow as tf import random import numpy as np
def train_model(x_train,y_train,x_valid,y_valid,model,epochs = 5,batch_size = 64, lr =0.001, print_freq = 10): loss_object = tf.keras.losses.SparseCategoricalCrossentropy() optimizer = tf.keras.optimizers.Adam(learning_rate=lr)
train_loss = tf.keras.metrics.Mean(name='train_loss') train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')
test_loss = tf.keras.metrics.Mean(name='test_loss') test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy') for epoch in range(epochs): train_loss.reset_states() train_accuracy.reset_states() test_loss.reset_states() test_accuracy.reset_states() for step in range(int(len(x_train)/batch_size)): rand_id = np.asarray(random.sample(range(len(x_train)), batch_size)) bs_x_train = x_train[rand_id] bs_y_train = y_train[rand_id] with tf.GradientTape() as tape: predictions = model(bs_x_train) loss = loss_object(bs_y_train, predictions) gradients = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) train_loss(loss) train_accuracy(bs_y_train, predictions) predictions = model(x_valid) t_loss = loss_object(y_valid, predictions) test_loss(t_loss) test_accuracy(y_valid, predictions) if step%10==0: template = 'Epoch {},step {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}' print(template.format(epoch+1, step, train_loss.result(), train_accuracy.result()*100, test_loss.result(), test_accuracy.result()*100))
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)) return model
|
若是继承自tf.keras.Model
则可以使用 model.compile()
去设置参数, 使用model.fit()
进行训练。
1 2 3 4 5 6 7 8
| keras_model = KerasModel() keras_model.compile( run_eagerly=False, optimizer=tf.keras.optimizers.SGD(learning_rate=0.1), loss=tf.keras.losses.mean_squared_error, )
|
——————-2021.9.18更———————
种子
为了确保每次运行结果的稳定,设置固定种子是有必要的。
1 2 3
| random.seed(2021) np.random.seed(2021) tf.random.set_seed(2021)
|
——————-2021.9.19更———————
当自定义模型时,继承tf.keras.Model
则需要实现call
方法而不是__call__
。如果是tf.Module
就实现__call__
。尽量使用tf.keras.Model
,因为真的很方便。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class KerasModel(tf.keras.Model): def __init__(self, **kwargs): super().__init__(**kwargs) self.w = tf.Variable(5.0) self.b = tf.Variable(0.0)
def call(self, x, **kwargs): return self.w * x + self.b model = KerasModel() bst_model_path = "./best.model.h5"
early_stopping = tf.keras.callbacks.EarlyStopping(monitor='val_accuracy', patience=5)
model_checkpoint = tf.keras.callbacks.ModelCheckpoint(bst_model_path, save_best_only=True, save_weights_only=True)
model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False), optimizer=tf.keras.optimizers.Adam(1e-3), metrics=['accuracy']) model.fit(x_train, train_y, batch_size=16, validation_data=(x_valid,valid_y), epochs=200, callbacks=[early_stopping,model_checkpoint] )
|
——————-2021.10.4更———————
从python生成器中加载数据
官方文档:consuming_python_generators
code template:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import tensorflow as tf
def get_dataset(args, batch_size,shuffle=False): output_types = (tf.int32, tf.int32, tf.int32, tf.int32) output_shapes = ((None,), (None,), (None,2), (None,) ) dataset = tf.data.Dataset.from_generator(generator_fn, args=args, output_types= output_types, output_shapes = output_shapes ) if shuffle: dataset = dataset.shuffle(buffer_size = 1000*batch_size) dataset = dataset.padded_batch(batch_size=batch_size, padded_shapes=output_shapes, padding_values=(0,0,0,0)) dataset = dataset.prefetch(1) return dataset
|
output_types
是必须的,buffer_size
一般取大于等于数据集大小。generator_fn
为生成器函数。如下:
1 2 3 4 5
| def count(stop): i = 0 while i<stop: yield i i += 1
|
——————-2021.10.5更———————
有关call()
子类化tf.keras.Model
时,在实现call()
函数需要注意的是接受的参数一般只能是两个:inputs
,training
training
一般给用户自定义训练模式提供一定的自由度,training
为布尔类型。当然,training
是非必须的。
1 2 3 4 5 6 7 8 9
| class KerasModel(tf.keras.Model): def __init__(self, **kwargs): super().__init__(**kwargs) self.w = tf.Variable(5.0) self.b = tf.Variable(0.0)
def call(self, x): return self.w * x + self.b
|
如果模型需要多个输入时:可以通过 inputs = (x1,x2,x3,...)
将inputs
传入
1 2 3
| ··省略·· def call(self, inputs): x1,x2,x3,... = inputs
|
当然也可以通过**kwargs
将其他数据传入。而不是像这样:
1 2
| def call(self, x1,x2,x3,...): ···
|
类型转换
1
| tf.cast(x, dtype=tf.int64)
|
——————-2021.10.16更———————
tf.keras.layers.MultiHeadAttention
1 2 3
| multi = tf.keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim)
out = multi(v,k,q,mask*-1e9)
|
如果mask是0、1矩阵,记得乘以-1e9 ,否者掩码无效。
TODO