TensorFlow上手(二)

上一篇介绍了TensorFlow一些最基本的概念,在实操中,大多数用得是Keras包,因此,小编会着重介绍这个库的使用以及tf中最重要的概念——梯度带。

传送门:TensorFlow上手(一)

掌握tf梯度带的实现细节

  • 基本训练循环

下面我们通过最简单的一元线性回归理解tf.GradientTape的具体使用过程。

首先,生成1000个带有噪音的样本:y=3x+2+ε

# 实际的线
TRUE_W = 3.0
TRUE_B = 2.0
NUM_EXAMPLES = 1000
# 随机向量x
x = tf.random.normal(shape=[NUM_EXAMPLES])
# 生成噪声
noise = tf.random.normal(shape=[NUM_EXAMPLES])
# 计算y
y = x * TRUE_W + TRUE_B + noise

import matplotlib.pyplot as plt
plt.scatter(x, y, c="b")
plt.show()

接着,我们初始化系数权重w和b,封装在类里面:

class MyModel(tf.Module):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # 初始化权重值为`5.0`,偏差值为`0.0`
        # 实际项目中,应该随机初始化
        self.w = tf.Variable(5.0,name='w')
        self.b = tf.Variable(0.0,name='b')

    def __call__(self, x):
        return self.w * x + self.b

这里,系数权重w,b都是一个变量,说明可以进行更改,下面我们会看到如何进行更新的。

然后,我们定义损失函数和编写训练函数:

# 计算整个批次的单个损失值
def loss(target_y, predicted_y):
    return tf.reduce_mean(tf.square(target_y - predicted_y))


# 给定一个可调用的模型,输入,输出和学习率...
def train(model, x, y, learning_rate):

    with tf.GradientTape() as t:
    # 可训练变量由GradientTape自动跟踪
        current_loss = loss(y, model(x))

    # 使用GradientTape计算相对于W和b的梯度
    dw, db = t.gradient(current_loss, [model.w, model.b])

    # 减去由学习率缩放的梯度
    model.w.assign_sub(learning_rate * dw)
    model.b.assign_sub(learning_rate * db)

这里,current_loss计算系数w,b未更新前的损失,并且对w,b分别求梯度,然后,利用方法assign_sub执行梯度下降更新参数值。

除此之外,我们还要定义一个函数,去接收、监视每一次迭代训练的参数(梯度、权重变化等):

Ws, bs = [], []
epochs = range(20)
# 定义用于训练的循环
def training_loop(model, x, y):
    # 收集W值和b值的历史记录以供以后绘制
    for epoch in epochs:
        # 用单个大批次处理更新模型
        train(model, x, y, learning_rate=0.1)
        # 在更新之前进行跟踪
        Ws.append(model.w.numpy())
        bs.append(model.b.numpy())
        current_loss = loss(y, model(x))
        print("Epoch %2d: W=%1.2f b=%1.2f, loss=%2.5f" % (epoch, Ws[-1], bs[-1], current_loss))

model = MyModel()
print("Starting: W=%1.2f b=%1.2f, loss=%2.5f" % (model.w, model.b, loss(y, model(x))))
# 开始训练
training_loop(model, x, y)

这里,在一次迭代中,调用train函数,会把变量w,b值进行更新,然后用列表Ws和bs接收更新后的值,紧接着会计算更新后的损失值,并把他们都打印出来,完成一次更新。

我们来看看结果:

画图出来更加有立体感:

  • 利用keras完成相同过程

利用keras实现这个过程更简洁:

class MyModelKeras(tf.keras.Model):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # 初始化权重为`5.0`,偏差为`0.0`
        # 实际中应该随机初始化该值
        self.w = tf.Variable(5.0,name='w')
        self.b = tf.Variable(0.0,name='b')

    def __call__(self, x, **kwargs):
        return self.w * x + self.b


keras_model = MyModelKeras()

# 编译设置培训参数
keras_model.compile(
    # 默认情况下,fit()调用tf.function()。
    # Debug时你可以关闭这一功能,但是现在是打开的。
    run_eagerly=False,
    # 使用内置的优化器,配置为对象
    optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),
    # Keras内置MSE
    # 您也可以使用损失函数像上面一样进行定义
    loss=tf.keras.losses.mean_squared_error,
)

keras_model.fit(x, y, epochs=20, batch_size=x.shape[0])

可以看到,complie()里面定义了训练需要的各种参数,loss定义损失函数,这里是均方误差,optimizer是指定优化器,也就是使用哪一种梯度下降方法,小编在梯度下降一文有图文并茂的详细介绍,这里是常规的随机梯度下降法(SGD)。

传送门:梯度下降法

迭代完成后,通过weights可以得到最终更新的权重系数。

Sequential模型

Keras分为序列化模型和函数式API,前者比较基础,我们简单介绍一下。

利用keras.layers的Dense可以定义一层的神经元个数、激活函数,并且对其命名:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

# Define Sequential model with 3 layers
model = keras.Sequential(
    [
        layers.Dense(2, activation="relu", name="layer1"),
        layers.Dense(3, activation="relu", name="layer2"),
        layers.Dense(4, name="layer3"),
    ]
)

这里定义了3层,可以当作3个隐藏层,需要指定输入层的维度,才能完成所有层数的初始化。

x = tf.ones((1, 4))
y = model(x)
print("Number of weights after calling the model:", len(model.weights)) 
model.summary()

这里,modex参数是一个维度为4的张量,因此,输入层是4个神经元,传到第一层权重系数w有4*2=8个,加上偏倚项2个,一共10个参数;接下来就是第二层和第三层,分别是3个神经元和4个神经元,所以是9个参数和16个参数。而weights存储的是每一层的w和b总体,一层2个,一共3层,所以打印结果是6个。

keras.Sequential定义比较灵活,也可以先初始化,再通过add方法添加层:

model = keras.Sequential()
model.add(keras.Input(shape=(250, 250, 3))) # 250x250 RGB images
model.add(layers.Conv2D(32, 5, strides=2, activation="relu"))
model.add(layers.Conv2D(32, 3, activation="relu"))
model.add(layers.MaxPooling2D(3))

# Can you guess what the current output shape is at this point? Probably not.
# Let's just print it:
model.summary()

Functional API

定义最简单的一个模型:

inputs = keras.Input(shape=(784,)) #输入层
dense = layers.Dense(64, activation="relu") #第一层隐藏层
x = dense(inputs)
x = layers.Dense(64, activation="relu")(x) #第二层隐藏层
outputs = layers.Dense(10)(x) #输出层

model = keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")
model.summary()

函数式API提供可视化的方法:

keras.utils.plot_model(model, "my_first_model.png", show_shapes=True)

我们可以进行数据训练和评估:

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype("float32") / 255
x_test = x_test.reshape(10000, 784).astype("float32") / 255

model.compile(
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    optimizer=keras.optimizers.RMSprop(),
    metrics=["accuracy"],
)

history = model.fit(x_train, y_train, batch_size=64, epochs=2, validation_split=0.2)
test_scores = model.evaluate(x_test, y_test, verbose=2)
print("Test loss:", test_scores[0])
print("Test accuracy:", test_scores[1])

keras内置history属性,可以查看每次迭代的指标:

下面我们给出自编码(AutoEncoder)的一般代码流程:

encoder_input = keras.Input(shape=(28, 28, 1), name="original_img")

x = layers.Conv2D(16, 3, activation="relu",name='encoder_1')(encoder_input)
x = layers.Conv2D(32, 3, activation="relu",name='encoder_2')(x)
x = layers.MaxPooling2D(3,name='encoder_3')(x)
x = layers.Conv2D(32, 3, activation="relu",name='encoder_4')(x)
x = layers.Conv2D(16, 3, activation="relu",name='encoder_5')(x)
encoder_output = layers.GlobalMaxPooling2D(name='encoder_out')(x)

x = layers.Reshape((4, 4, 1),name='decoder_5')(encoder_output)
x = layers.Conv2DTranspose(16, 3, activation="relu",name='decoder_4')(x)
x = layers.Conv2DTranspose(32, 3, activation="relu",name='decoder_3')(x)
x = layers.UpSampling2D(3,name='decoder_2')(x)
x = layers.Conv2DTranspose(16, 3, activation="relu",name='decoder_1')(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation="relu",name='decoder_out')(x)

decoder = keras.Model(encoder_input, decoder_output, name="autoencoder")
decoder.summary()

AutoEncoder的解码架构与编码架构严格对称。

Conv2D层的反面是Conv2DTranspose层,MaxPooling2D层的反面是Upsampling2D层。这是卷积神经网络的卷积层和池化层。

实际上,无论是encoder层还是decoder层,都是可以重用的:

encoder_input = keras.Input(shape=(28, 28, 1), name="original_img")

x = layers.Conv2D(16, 3, activation="relu",name='encoder_1')(encoder_input)
x = layers.Conv2D(32, 3, activation="relu",name='encoder_2')(x)
x = layers.MaxPooling2D(3,name='encoder_3')(x)
x = layers.Conv2D(32, 3, activation="relu",name='encoder_4')(x)
x = layers.Conv2D(16, 3, activation="relu",name='encoder_5')(x)
encoder_output = layers.GlobalMaxPooling2D(name='encoder_out')(x)

encoder = keras.Model(encoder_input, encoder_output, name="encoder")
encoder.summary()

decoder_input = keras.Input(shape=(16,), name="encoded_img")
x = layers.Reshape((4, 4, 1),name='decoder_5')(decoder_input)
x = layers.Conv2DTranspose(16, 3, activation="relu",name='decoder_4')(x)
x = layers.Conv2DTranspose(32, 3, activation="relu",name='decoder_3')(x)
x = layers.UpSampling2D(3,name='decoder_2')(x)
x = layers.Conv2DTranspose(16, 3, activation="relu",name='decoder_1')(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation="relu",name='decoder_out')(x)

decoder = keras.Model(decoder_input, decoder_output, name="decoder")
decoder.summary()

autoencoder_input = keras.Input(shape=(28, 28, 1), name="img")
encoded_img = encoder(autoencoder_input)
decoded_img = decoder(encoded_img)
autoencoder = keras.Model(autoencoder_input, decoded_img, name="autoencoder")
autoencoder.summary()

参考资料:

https://keras.io/zh/optimizers/

https://tensorflow.google.cn/guide/keras/functional

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

整得咔咔响

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值