08_1_Keras高层API&自定义层or网络
Keras.Metrics(Keras高层API)
Keras != tf.keras
主要使用以下功能:
- datasets
- layers
- losses
- metrics
- optimizers
Outline
在机器学习中,经常要记录loss、accuracy。它们至少是step或epoch平均的值,这样才能比较好反映training的状态。
新建res = [],每一个batch加一次loss。[loss1,loss2,…],随后打印res.avg,这样可以看出前后两次打印整个model的变化情况,这个功能叫做meter(测量表),Metrics表示测量的意思。
- Metrics
- update_state
- result().numpy()
- reset_states
如何使用:
- 新建Metrics
- 给其中添加数据,loss1, loss2 …
- 得到结果
- 清零
Step1.Build a meter
新建准确度meter:metrics.Accuracy()
新建平均值:metrics.Mean()
Step2.Update data
往测量尺中添加数据
添加loss:loss_meter.update_state(loss)
添加acc:acc_meter.update_state(y,pred) #把真实的y和预测的y添加后会自动计算acc
Step3.Get Average data
loss_meter.result().numpy()
acc_meter.result().numpy()
Step4.Clear buffer
loss_meter.reset_states()
acc_meter.reset_states()
实战:
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics
def preprocess(x, y):
x = tf.cast(x, dtype=tf.float32) / 255.
y = tf.cast(y, dtype=tf.int32)
return x,y
batchsz = 128
(x, y), (x_val, y_val) = datasets.mnist.load_data()
print('datasets:', x.shape, y.shape, x.min(), x.max())
db = tf.data.Dataset.from_tensor_slices((x,y))
db = db.map(preprocess).shuffle(60000).batch(batchsz).repeat(10)
ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))
ds_val = ds_val.map(preprocess).batch(batchsz)
network = Sequential([layers.Dense(256, activation='relu'),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(32, activation='relu'),
layers.Dense(10)])
network.build(input_shape=(None, 28*28))
network.summary()
optimizer = optimizers.Adam(lr=0.01)
####创建两个meter
acc_meter = metrics.Accuracy()
loss_meter = metrics.Mean()
for step, (x,y) in enumerate(db):
with tf.GradientTape() as tape:
# [b, 28, 28] => [b, 784]
x = tf.reshape(x, (-1, 28*28))
# [b, 784] => [b, 10]
out = network(x)
# [b] => [b, 10]
y_onehot = tf.one_hot(y, depth=10)
# [b]
loss = tf.reduce_mean(tf.losses.categorical_crossentropy(y_onehot, out, from_logits=True))
####添加loss
loss_meter.update_state(loss)
grads = tape.gradient(loss, network.trainable_variables)
optimizer.apply_gradients(zip(grads, network.trainable_variables))
if step % 100 == 0:
####求平均值
print(step, 'loss:', loss_meter.result().numpy())
####清空meter
loss_meter.reset_states()
# evaluate
if step % 500 == 0:
####两种方式,自己计算或者meter接口实现
total, total_correct = 0., 0
acc_meter.reset_states()
for step, (x, y) in enumerate(ds_val):
# [b, 28, 28] => [b, 784]
x = tf.reshape(x, (-1, 28*28))
# [b, 784] => [b, 10]
out = network(x)
# [b, 10] => [b]
pred = tf.argmax(out, axis=1)
pred = tf.cast(pred, dtype=tf.int32)
# bool type
correct = tf.equal(pred, y)
# bool tensor => int tensor => numpy
####累计正确值
total_correct += tf.reduce_sum(tf.cast(correct, dtype=tf.int32)).numpy()
####累计总数
total += x.shape[0]
####meter接口实现
acc_meter.update_state(y, pred)
print(step, 'Evaluate Acc:', total_correct/total, acc_meter.result().numpy())
Compile&Fit(Keras高层API)
Outline
- Compile
- 类似装载的功能,指定training的loss的选择、优化器的选择、评估指标的选择
- Fit
- Compile后,通过Fit完成一系列标准的创建流程
- Evaluate
- 在test/evaluat dataset进行测试
- Predict
- 完成新的sample预测
之前标准的创建流程:
Individual loss and optimize(过去的loss、优化代码)
有些流程在不同深度学习代码是一样的,因此Keras提供了标准步骤的接口:Compile&fit
Now
指定优化器为Adam;loss为category loss;测试的指标为标准的accuracy
回顾老代码:
loss指定了;acc指定了;优化器指定了
还没指定:for x,y in db;网络forward的过程
之前的写法:
现在的写法:
还没有实现测试test。
Standard Progressbar(标准打印信息)
Individual evaluation(过去的测试代码)
衡量模型的性能。测试的epoch为1,测试需要指定测试集
Now
指定metrics、validation_data、validation_freq(每多少个epoch做一次validation)
Evaluation(输出)
前面2个是train loss/accuracy,后面2个是test loss/accuracy
Test
evaluate
和fit中的validation是一样的,区别在fit的validation是training中间进行的,中间如果test-acc > 0.99 -> save -> break(early stopping)。
evaluate相当于一次测试,并且ds_val也可以用其他的测试集。
evaluate
的输出:
Predict(预测)
network.predict(x)或network(x)
自定义网络
Outline
- keras.Sequential
- 容器,串联层
- keras.layers.Layer
- keras.Model
keras.Sequential
创建参数w和b:
network.build(input_shape=(None,28*28)) #一般这样,前期可打印-summary
- 或直接
network(x)
管理参数:
model.trainable_variables
[w1,b1,w2,b2,…w5,b5]model.call()
call Dense1 Dense2,forward求更新偏导自动喂
Layer/Model
需要继承keras.layers.Layer
/keras.Model
如果需要自定义层:
需要继承自Layer类,并且实现以下两个函数:
-
__init__
-
call
model(x)其实是调用了model.__call__() -> 调用call()方法
(其实Sequential也是继承自Model类)
Model类还有compile/fir/evaluate/predict接口
实现自定义层的例子:
标准的Dense层:layers.Dense(512)
,如果build([None,784])
,则w:[784,512] b[512]
自己实现MyDense
:
- 继承自
layers.Layer
- 实现
__init__
和call
其中__init__
中参数的名字’w’和’b’基本上没什么意义;w的[inp_dim,outp_dim]
和我们用的相似;self.add_variable
而不是简单的constant
是为了让变量能够在模型中运算。
call
中training参数表示你是在training还是test。
自定义网络:
继承自keras.Model
。图中的call的功能和Sequential类似,不过它内部还能加更多的操作(例如x=x-1)
实战:自定义层和网络
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics
from tensorflow import keras
def preprocess(x, y):
"""
x is a simple image, not a batch
"""
x = tf.cast(x, dtype=tf.float32) / 255.
x = tf.reshape(x, [28*28])
y = tf.cast(y, dtype=tf.int32)
y = tf.one_hot(y, depth=10)
return x,y
batchsz = 128
(x, y), (x_val, y_val) = datasets.mnist.load_data()
print('datasets:', x.shape, y.shape, x.min(), x.max())
db = tf.data.Dataset.from_tensor_slices((x,y))
db = db.map(preprocess).shuffle(60000).batch(batchsz)
ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))
ds_val = ds_val.map(preprocess).batch(batchsz)
sample = next(iter(db))
print(sample[0].shape, sample[1].shape)
network = Sequential([layers.Dense(256, activation='relu'),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(32, activation='relu'),
layers.Dense(10)])
network.build(input_shape=(None, 28*28))
network.summary()
####自己定义的层
class MyDense(layers.Layer):
def __init__(self, inp_dim, outp_dim):
super(MyDense, self).__init__()
self.kernel = self.add_weight('w', [inp_dim, outp_dim])
self.bias = self.add_weight('b', [outp_dim])
def call(self, inputs, training=None):
out = inputs @ self.kernel + self.bias
return out
####自己定义的模型
class MyModel(keras.Model):
def __init__(self):
super(MyModel, self).__init__()
self.fc1 = MyDense(28*28, 256)
self.fc2 = MyDense(256, 128)
self.fc3 = MyDense(128, 64)
self.fc4 = MyDense(64, 32)
self.fc5 = MyDense(32, 10)
def call(self, inputs, training=None):
x = self.fc1(inputs)
x = tf.nn.relu(x)
x = self.fc2(x)
x = tf.nn.relu(x)
x = self.fc3(x)
x = tf.nn.relu(x)
x = self.fc4(x)
x = tf.nn.relu(x)
x = self.fc5(x)
return x
####新建自己的模型
network = MyModel()
#优化模型
network.compile(optimizer=optimizers.Adam(lr=0.01),
loss=tf.losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
#测试模型
network.fit(db, epochs=5, validation_data=ds_val,
validation_freq=2)
network.evaluate(ds_val)
sample = next(iter(ds_val))
x = sample[0]
y = sample[1] # one-hot
pred = network.predict(x) # [b, 10]
# convert back to number
y = tf.argmax(y, axis=1)
pred = tf.argmax(pred, axis=1)
print(pred)
print(y)