首页 > 解决方案 > 在 tf.keras 中理解线性回归模型调优的问题

问题描述

我正在使用合成数据 Colab 练习进行线性回归,它使用玩具数据集探索线性回归。建立和训练了一个线性回归模型,可以使用学习率、时期和批量大小。我很难理解迭代是如何完成的,以及它如何与“epoch”和“batch size”相关联。我基本上不知道如何训练实际模型、如何处理数据以及如何完成迭代。为了理解这一点,我想通过手动计算每个步骤来遵循这一点。因此,我希望每个步骤都有斜率和截距系数。这样我就可以看到“计算机”使用什么样的数据,放入模型中,在每次特定迭代中产生什么样的模型结果以及迭代是如何完成的。我首先尝试获取每一步的斜率和截距,但是失败了,因为只有在最后才输出斜率和截距。我修改后的代码(原版,刚刚添加:)

  print("Slope")
  print(trained_weight)
  print("Intercept")
  print(trained_bias)

代码:

import pandas as pd
import tensorflow as tf
from matplotlib import pyplot as plt

#@title Define the functions that build and train a model
def build_model(my_learning_rate):
  """Create and compile a simple linear regression model."""
  # Most simple tf.keras models are sequential. 
  # A sequential model contains one or more layers.
  model = tf.keras.models.Sequential()

  # Describe the topography of the model.
  # The topography of a simple linear regression model
  # is a single node in a single layer. 
  model.add(tf.keras.layers.Dense(units=1, 
                                  input_shape=(1,)))

  # Compile the model topography into code that 
  # TensorFlow can efficiently execute. Configure 
  # training to minimize the model's mean squared error. 
  model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=my_learning_rate),
                loss="mean_squared_error",
                metrics=[tf.keras.metrics.RootMeanSquaredError()])
 
  return model           


def train_model(model, feature, label, epochs, batch_size):
  """Train the model by feeding it data."""

  # Feed the feature values and the label values to the 
  # model. The model will train for the specified number 
  # of epochs, gradually learning how the feature values
  # relate to the label values. 
  history = model.fit(x=feature,
                      y=label,
                      batch_size=batch_size,
                      epochs=epochs)

  # Gather the trained model's weight and bias.
  trained_weight = model.get_weights()[0]
  trained_bias = model.get_weights()[1]
  print("Slope")
  print(trained_weight)
  print("Intercept")
  print(trained_bias)
  # The list of epochs is stored separately from the 
  # rest of history.
  epochs = history.epoch

  # Gather the history (a snapshot) of each epoch.
  hist = pd.DataFrame(history.history)

 # print(hist)
  # Specifically gather the model's root mean 
  #squared error at each epoch. 
  rmse = hist["root_mean_squared_error"]

  return trained_weight, trained_bias, epochs, rmse

print("Defined create_model and train_model")

#@title Define the plotting functions
def plot_the_model(trained_weight, trained_bias, feature, label):
  """Plot the trained model against the training feature and label."""

  # Label the axes.
  plt.xlabel("feature")
  plt.ylabel("label")

  # Plot the feature values vs. label values.
  plt.scatter(feature, label)

  # Create a red line representing the model. The red line starts
  # at coordinates (x0, y0) and ends at coordinates (x1, y1).
  x0 = 0
  y0 = trained_bias
  x1 = my_feature[-1]
  y1 = trained_bias + (trained_weight * x1)
  plt.plot([x0, x1], [y0, y1], c='r')

  # Render the scatter plot and the red line.
  plt.show()

def plot_the_loss_curve(epochs, rmse):
  """Plot the loss curve, which shows loss vs. epoch."""

  plt.figure()
  plt.xlabel("Epoch")
  plt.ylabel("Root Mean Squared Error")

  plt.plot(epochs, rmse, label="Loss")
  plt.legend()
  plt.ylim([rmse.min()*0.97, rmse.max()])
  plt.show()

print("Defined the plot_the_model and plot_the_loss_curve functions.")

my_feature = ([1.0, 2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,  9.0, 10.0, 11.0, 12.0])
my_label   = ([5.0, 8.8,  9.6, 14.2, 18.8, 19.5, 21.4, 26.8, 28.9, 32.0, 33.8, 38.2])

learning_rate=0.05
epochs=1
my_batch_size=12

my_model = build_model(learning_rate)
trained_weight, trained_bias, epochs, rmse = train_model(my_model, my_feature, 
                                                         my_label, epochs,
                                                         my_batch_size)
plot_the_model(trained_weight, trained_bias, my_feature, my_label)
plot_the_loss_curve(epochs, rmse)

在我的具体情况下,我的输出是:

前一

现在我尝试在一个简单的 Excel 表中复制它并手动计算 rmse:

埃索

但是,我得到 21.8 而不是 23.1?另外我的损失不是535.48,而是476.82

因此,我的第一个问题是:我的错误在哪里,rmse 是如何计算的?

第二个问题:如何获得每次特定迭代的 rmse?假设 epoch 为 4,batch size 为 4。

考试

这给出了 4 个时期和 3 个批次,每 4 个示例(观察)。我不明白如何通过这些迭代训练模型。那么如何获得每个回归模型和 rmse 的系数呢?不仅针对每个 epoch(所以 4),而且针对每个迭代。我认为每个时代都有 3 次迭代。所以我认为总共有 12 个线性回归模型?我想看看这12个模型。在没有给出信息的情况下,起点使用的初始值是什么,使用什么样的斜率和截距?从真正的第一点开始。我没有具体说明这一点。然后我希望能够了解每一步如何调整斜率和截距。这将来自我认为的梯度下降算法。但这将是超级优势。

更新:我知道初始值(斜率和截距)是随机选择的。

标签: regressionpythontensorflowkerasmatplotlib

解决方案


基础

问题陈述

让我们考虑一组样本的线性回归模型,X其中每个样本由一个特征表示x。作为模型训练的一部分,我们正在寻找w.x + b使((w.x+b) -y )^2(平方损失)最小的线。对于一组数据点,我们采用每个样本的均方损失,即所谓的均方误差 (MSE)。代表权重和偏差的wb统称为权重。

拟合线/训练模型

  1. 我们有一个用于解决线性回归问题的封闭形式的解决方案,并且是(X^T.X)^-1.X^T.y
  2. 我们还可以使用梯度下降法来搜索最小化平方损失的权重。tensorflow、pytorch 等框架使用梯度下降来搜索权重(称为训练)。

渐变体面

用于学习回归的梯度体面算法看起来像打击

w, b = some initial value
While model has not converged:
    y_hat = w.X + b
    error = MSE(y, y_hat) 
    back propagate (BPP) error and adjust weights

上述循环的每次运行称为一个时期。但是由于资源的限制,计算y_haterror并且 BPP 没有在完整的数据集上执行,而是将数据分成更小的批次,并且一次对一个批次执行上述操作。此外,我们通常会固定 epoch 的数量并监控模型是否收敛。

w, b = some initial value
for i in range(number_of_epochs)
    for X_batch,y_batch in get_next_batch(X, y)
        y_hat = w.X_batch + b
        error = MSE(y_batch, y_hat) 
    back propagate (BPP) error and adjust weights

Keras 批量实现

假设我们想添加均方根误差,以便在训练时跟踪模型性能。Keras的实现方式如下

w, b = some initial value
for i in range(number_of_epochs)
    all_y_hats = []
    all_ys = []
    for X_batch,y_batch in get_next_batch(X, y)
        y_hat = w.X_batch + b
        error = MSE(y_batch, y_hat)

        all_y_hats.extend(y_hat) 
        all_ys.extend(y_batch)

        batch_rms_error = RMSE(all_ys, all_y_hats)

    back propagate (BPP) error and adjust weights

正如您在上面看到的,预测是累积的,RMSE 是根据累积的预测计算的,而不是取所有先前批次 RMSE 的平均值。

keras 中的实现

现在我们的基础很清楚了,让我们看看如何在 keras 中实现相同的跟踪。keras 有回调,所以我们可以挂钩on_batch_begin回调并累积all_y_hatsand all_ys。在on_batch_end回调上 keras 给了我们计算的RMSE. RMSE我们将使用我们的累积手动计算all_y_hatsall_ys并验证它是否与 keras 计算的相同。我们还将保存权重,以便稍后绘制正在学习的线。

import numpy as np
from sklearn.metrics import mean_squared_error
import keras
import matplotlib.pyplot as plt

# Some training data
X = np.arange(16)
y = 0.5*X +0.2

batch_size = 8
all_y_hats = []
learned_weights = [] 

class CustomCallback(keras.callbacks.Callback):
  def on_batch_begin(self, batch, logs={}):    
    w = self.model.layers[0].weights[0].numpy()[0][0]
    b = self.model.layers[0].weights[1].numpy()[0]    
    s = batch*batch_size
    all_y_hats.extend(b + w*X[s:s+batch_size])    
    learned_weights.append([w,b])

  def on_batch_end(self, batch, logs={}):    
    calculated_error = np.sqrt(mean_squared_error(all_y_hats, y[:len(all_y_hats)]))
    print (f"\n Calculated: {calculated_error},  Actual: {logs['root_mean_squared_error']}")
    assert np.isclose(calculated_error, logs['root_mean_squared_error'])

  def on_epoch_end(self, batch, logs={}):
    del all_y_hats[:]    


model = keras.models.Sequential()
model.add(keras.layers.Dense(1, input_shape=(1,)))
model.compile(optimizer=keras.optimizers.RMSprop(lr=0.01), loss="mean_squared_error",  metrics=[keras.metrics.RootMeanSquaredError()])
# We should set shuffle=False so that we know how baches are divided
history = model.fit(X,y, epochs=100, callbacks=[CustomCallback()], batch_size=batch_size, shuffle=False) 

输出:

Epoch 1/100
 8/16 [==============>...............] - ETA: 0s - loss: 16.5132 - root_mean_squared_error: 4.0636
 Calculated: 4.063645694548688,  Actual: 4.063645839691162

 Calculated: 8.10112834945773,  Actual: 8.101128578186035
16/16 [==============================] - 0s 3ms/step - loss: 65.6283 - root_mean_squared_error: 8.1011
Epoch 2/100
 8/16 [==============>...............] - ETA: 0s - loss: 14.0454 - root_mean_squared_error: 3.7477
 Calculated: 3.7477213352845675,  Actual: 3.7477214336395264
-------------- truncated -----------------------

达达!断言assert np.isclose(calculated_error, logs['root_mean_squared_error'])从未失败,因此我们的计算/理解是正确的。

线

最后,让我们根据均方误差损失绘制由 BPP 算法调整的线。我们可以使用下面的代码创建一个 png 图像,其中包含每批学习的线条以及训练数据。

for i, (w,b) in enumerate(learned_weights):
  plt.close()
  plt.axis([-1, 18, -1, 10])
  plt.scatter(X, y)
  plt.plot([-1,17], [-1*w+b, 17*w+b], color='green')
  plt.savefig(f'img{i+1}.png')

下面是以上图片的gif动画,按学习顺序排列。

在此处输入图像描述

学习的超平面(在这种情况下为线)y = 0.5*X +5.2

在此处输入图像描述


推荐阅读