4,150 次浏览

Morvan博客-tensorflow2学习(二)

前面使用tensorflow2对线性数据进行拟合,此篇则是进行简单的神经网络搭建,对二次函数进行拟合。

搭建简单的神经网络

同样先使用tensorflow.compat.v1来跑morvan大佬的代码,需要注意要tf.disable_v2_behavior(),这样可以避免tf.placeholder() is not compatible with eager execution的运行错误。

import tensorflow.compat.v1 as tf
import numpy as np
import matplotlib.pyplot as plt

tf.disable_v2_behavior()


def add_layer(inputs, in_size, out_size, activation_function=None):
    Weights = tf.Variable(tf.random_normal((in_size, out_size)))
    biases = tf.Variable(tf.zeros((1, out_size)) + 0.1)

    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        outputs = Wx_plus_b
    else:
        outputs = activation_function(Wx_plus_b)
    return outputs


x_data = np.linspace(-1, 1, 300)[:, np.newaxis]
noise = np.random.normal(0, 0.05, x_data.shape)
y_data = np.square(x_data) - 0.5 + noise

xs = tf.placeholder(tf.float32, [None, 1])
ys = tf.placeholder(tf.float32, [None, 1])

l1 = add_layer(xs, 1, 10, activation_function=tf.nn.relu)
prediction = add_layer(l1, 10, 1, activation_function=None)

loss = tf.reduce_mean(tf.reduce_sum(tf.square(ys-prediction), reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)

init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(x_data, y_data)
    plt.ion()
    plt.show()

    for i in range(1000):
        sess.run(train_step, feed_dict={xs: x_data, ys: y_data})
        if i % 50 == 0:
            # print(sess.run(loss, feed_dict={xs: x_data, ys: y_data}))
            try:
                ax.lines.remove(lines[0])
            except Exception:
                pass
            prediction_value = sess.run(prediction, feed_dict={xs: x_data})
            lines = ax.plot(x_data, prediction_value, 'r-', lw=5)
            plt.pause(0.1)

ok,代码在原基础上没有做太多改动,使用tf.global_variables_initializer()替换了原来代码的变量初始化。使用with操作来自动关闭session句柄,所以最后拟合完数据,plot会自动关闭。

看看这个神经网络搭建的代码,以及morvan大佬的variable讲解视频中有个弹幕对为什么定义update操作的疑问。其实都一样,我们习惯于对命令式一步一步探秘的代码更为习惯,但是对于静态图我们需要有个整体的框架。

同样在morvan大佬在写神经网络的placeholder时,是先写了大部分的代码,再到后面将需要传递到session的数据,改为placeholder,这也是在morvan大佬手写代码可以学到的东西。

修改为eager execution代码时,遇到了一些问题就是,Morvan博客-tensorflow2学习(一)的代码修改,其实就是一层的线性拟合,对于参数的学习,其实使用var_list将要优化的参数传递进去就完事了。对于morvan的代码,我们可以看到我们只关注了整个模型的建立,对于backprop似乎完全没有提到(深度学习框架帮我们实现了自动求导),也即我们实质的关心到参数的更新,但是参数确实被更新了,这是在创建forward pass function的时候,optimizer会创建tape+minimize。

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt


def add_layer(inputs, Weights, biases, activation_function=None):

    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        outputs = Wx_plus_b
    else:
        outputs = activation_function(Wx_plus_b)
    return outputs, Weights, biases


def loss(predicted_y, target_y):
    return tf.math.reduce_mean(tf.math.square(predicted_y - target_y))


x_data = np.linspace(-1, 1, 300)[:, np.newaxis]
noise = np.random.normal(0, 0.05, x_data.shape)
y_data = np.square(x_data) - 0.5 + noise
x_data = x_data.astype(np.float32)
noise = noise.astype(np.float32)
y_data = y_data.astype(np.float32)

optimizer = tf.optimizers.SGD(learning_rate=0.1)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(x_data, y_data)
plt.ion()
plt.show()

Wl = tf.Variable(tf.random.normal((1, 10)))
bl = tf.Variable(tf.zeros((1, 10)) + 0.1)
Wp = tf.Variable(tf.random.normal((10, 1)))
bp = tf.Variable(tf.zeros((1, 1)) + 0.1)

for i in range(1000):
    with tf.GradientTape() as tape:
        l1, Wl, bl = add_layer(x_data, Wl, bl, activation_function=tf.nn.relu)
        prediction, Wp, bp = add_layer(l1, Wp, bp, activation_function=None)
        loss_val = loss(prediction, y_data)

        print('loss: {}\n'.format(loss_val))

    grads = tape.gradient(loss_val, [Wl, bl, Wp, bp])  # 这里只使用Wl,bl也能得到结果
    optimizer.apply_gradients(zip(grads, [Wl, bl, Wp, bp]))
    print('Wl: {},\n bl{},\n Wp: {},\n bp: {}\n\n'.format(Wl.numpy(), bl.numpy(), Wp.numpy(), bp.numpy()))
    if i % 50 == 0:
        try:
            ax.lines.remove(lines[0])
        except Exception:
            pass
        lines = ax.plot(x_data, prediction.numpy(), 'r-', lw=5)
        plt.pause(0.1)

上面的改动其实去看tensorflow2的tutorials以及api文档,使用对象来进行模型搭建更为符合其设计,也更为方便。博客的想法是迁移morvan的代码,保留morvan代码的格式,但是这样的魔改其实个人也不是十分推荐,但是改动的过程中也遇到了一些问题。

对于参数的更新来说,morvan代码其实不用考虑,由optimizer全权代理了。而对于动态图,参数的更新则需要加入一些考虑了,需要使用GradientTape进行”监听”需要更新的参数,代码中更新loss对Wl, bl, Wp, bp的梯度,然后使用optimizer.apply_gradient()进行应用梯度,去看一看tensorflow1的源码minimize()函数中也是这一过程。由于参数需要更新以及是动态图,weights和biases就得从add_layer()函数退出了,不然每次跑的是新的权重。

对于GradientTape对象需要注意,每一次使用gradient()函数就会释放GradientTapede拥有的资源,对于需要长期使用的梯度可以使用persistence=true这个参数。

系列:
Morvan博客-tensorflow2学习(一)
Morvan博客-tensorflow2学习(二)
Morvan博客-tensorflow2学习(三)
tensorflow2学习(四)

reference
1. https://www.bilibili.com/video/av16001891
2. https://morvanzhou.github.io/tutorials/machine-learning/tensorflow/
3. https://tensorflow.google.cn/api_docs/python/
4. https://tensorflow.google.cn/tutorials/customization/basics
5. https://stackoverflow.com/questions/55552538/tensorflow-2-0-attributeerror-tensor-name-is-meaningless-when-eager-execution