一、构建网络的第二种方式
- 通过输入层和输出层来构建网络,网络包含输入和输出中间的隐藏层,这会返回一个Model对象,通过该对象可以调用model.compile和model.fit函数,非常方便。
import tensorflow as tf
import tensorflow.keras.layers
x = layers.Input(shape=(784,))
h_1 = layers.Dense(x)
h_2 = layers.Conv2D(h1)
...
h_n = layers.Dense(h_n_1)
out = layers.Dense(h_n)
model = Model(x,out)
二、对比第一种构建网络方式
第一种方式
1. 第二种方式的优点
- 建立模型更加的泛化,更加通俗易懂
①第一种使用Squential来构建网络,非常的无脑简单,但是当我们需要Squential中间某些层的量来计算一些东西时,此时会变得比较复杂,例如VAE中要在encoder部分计算KL散度,需要对encoder网络的输出做一个函数变换,这其实变得优点困难,当然我们也可以自定义层(熟练之后),也可以使用lambda层,但无疑这回增加我们的难度;
②第二种构建方式,可以很巧妙的利用这些隐藏层的结果去做一些事情,例如构建loss,下面展示一下这种巧妙方式在VAE中是如何应用的。
③第二种构建模型的方式依然可以使用model.compile和model.fit函数,有几方面的原因:
(1)整个模型就一个model(因为有的模型会有两个model,例如GAN)
(2)整个构建过程最终返回的是一个Model()对象,所以可以调用类的方法compile和fit。
latent_dimension = 50
def sampling(agrs):
mean,logvar = agrs[0],agrs[1]
eps = tf.random.normal(tf.shape(mean))
return mean + eps*tf.exp(logvar * 0.5)
x = layers.Input(shape=(784*2,))
h1 = layers.Dense(200,activation='softplus')(x)
h2 = layers.Dense(200,activation='softplus')(h1)
mean = layers.Dense(latent_dimension)(h2)
log_var = layers.Dense(latent_dimension)(h2)
z = layers.Lambda(sampling,output_shape=(latent_dimension,))([mean,log_var])
h3 = layers.Dense(200,activation='softplus')
h4 = layers.Dense(200,activation='softplus')
h5 = layers.Dense(200,activation='softplus')
end = layers.Dense(784)
z1 = h3(z)
z2 = h4(z1)
z3 = h5(z2)
out = end(z3)
model = tf.keras.Model(x,out)
cross_ent = tf.reduce_sum(tf.nn.sigmoid_cross_entropy_with_logits(labels=tf.split(x,num_or_size_splits=2,axis=-1)[0],logits=out),axis=-1)
KL = -tf.reduce_sum(1+log_var-tf.square(mean)-tf.exp(log_var),axis=-1)
dvae_loss = tf.reduce_mean(cross_ent + KL)
model.add_loss(dvae_loss)
model.compile(optimizer='adam')
history = model.fit(train_dataset,epochs=80,validation_data=test_dataset)
第二种方式的缺点
- 相比于第一种建模方式,第二种很繁琐,同时注意到当我们需要更改网络结构时,第二种方式改起来也比较繁琐
第二种方式的使用场景
- 网络比较简单,不然代码量会很多,不符合写代码的准则。例如加入现在我们要构建100层神经网络,那么如果用第二种方式,那我选择die!
总结
- 网络结构比较简单时,方式一和方式二都可以,当网络结构很复杂时,首选用Squential构建网络,后面会讲比较复杂的用Squential构建网络,这时候将不能使用compile和fit函数。