1. 程式人生 > >TensorFlow零基礎入門教程(二)之谷歌官方教程

TensorFlow零基礎入門教程(二)之谷歌官方教程

其實內容有一定難度,不適合入門資料,同樣的內容可以講的很簡單,需要一定的知識儲備。

使用 TensorFlow 的基本步驟

學習目標:
* 學習基本的 TensorFlow 概念
* 在 TensorFlow 中使用 LinearRegressor 類並基於單個輸入特徵預測各城市街區的房屋價值中位數
* 使用均方根誤差 (RMSE) 評估模型預測的準確率
* 通過調整模型的超引數提高模型準確率

設定

在此第一個單元格中,我們將載入必要的庫。

import math

from IPython import display
from matplotlib import
cm from matplotlib import gridspec from matplotlib import pyplot as plt import numpy as np import pandas as pd from sklearn import metrics import tensorflow as tf from tensorflow.python.data import Dataset tf.logging.set_verbosity(tf.logging.ERROR) pd.options.display.max_rows = 10 pd.options.display.float_format = '{:.1f}'
.format

接下來,我們將載入資料集。

california_housing_dataframe = pd.read_csv("https://storage.googleapis.com/mledu-datasets/california_housing_train.csv", sep=",")

python
california_housing_dataframe = california_housing_dataframe.reindex(
np.random.permutation(california_housing_dataframe.index))
california_housing_dataframe["median_house_value"] /= 1000.0
california_housing_dataframe

image.png
## 檢查資料

建議您在使用資料之前,先對它有一個初步的瞭解。

我們會輸出關於各列的一些實用統計資訊快速摘要:樣本數、均值、標準偏差、最大值、最小值和各種分位數。

california_housing_dataframe.describe()

構建第一個模型

在本練習中,我們將嘗試預測 median_house_value,它將是我們的標籤(有時也稱為目標)。我們將使用 total_rooms 作為輸入特徵。

注意:我們使用的是城市街區級別的資料,因此該特徵表示相應街區的房間總數。

為了訓練模型,我們將使用 TensorFlow Estimator API 提供的 LinearRegressor 介面。此 API 負責處理大量低級別模型搭建工作,並會提供執行模型訓練、評估和推理的便利方法。

第 1 步:定義特徵並配置特徵列

為了將我們的訓練資料匯入 TensorFlow,我們需要指定每個特徵包含的資料型別。在本練習及今後的練習中,我們主要會使用以下兩類資料:

  • 分類資料:一種文字資料。在本練習中,我們的住房資料集不包含任何分類特徵,但您可能會看到的示例包括家居風格以及房地產廣告詞。

  • 數值資料:一種數字(整數或浮點數)資料以及您希望視為數字的資料。有時您可能會希望將數值資料(例如郵政編碼)視為分類資料(我們將在稍後的部分對此進行詳細說明)。

在 TensorFlow 中,我們使用一種稱為“特徵列”的結構來表示特徵的資料型別。特徵列僅儲存對特徵資料的描述;不包含特徵資料本身。

一開始,我們只使用一個數值輸入特徵 total_rooms。以下程式碼會從 california_housing_dataframe 中提取 total_rooms 資料,並使用 numeric_column 定義特徵列,這樣會將其資料指定為數值:

# Define the input feature: total_rooms.
my_feature = california_housing_dataframe[["total_rooms"]]
print(my_feature)

# Configure a numeric feature column for total_rooms.
feature_columns = [tf.feature_column.numeric_column("total_rooms")]

第 2 步:定義目標

接下來,我們將定義目標,也就是 median_house_value。同樣,我們可以從 california_housing_dataframe 中提取它:

# Define the label.
targets = california_housing_dataframe["median_house_value"]

第 3 步:配置 LinearRegressor

接下來,我們將使用 LinearRegressor 配置線性迴歸模型,並使用 GradientDescentOptimizer(它會實現小批量隨機梯度下降法 (SGD))訓練該模型。learning_rate 引數可控制梯度步長的大小。

注意:為了安全起見,我們還會通過 clip_gradients_by_norm梯度裁剪應用到我們的優化器。梯度裁剪可確保梯度大小在訓練期間不會變得過大,梯度過大會導致梯度下降法失敗。

# Use gradient descent as the optimizer for training the model.
my_optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.0000001)
my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)

# Configure the linear regression model with our feature columns and optimizer.
# Set a learning rate of 0.0000001 for Gradient Descent.
linear_regressor = tf.estimator.LinearRegressor(
    feature_columns=feature_columns,
    optimizer=my_optimizer
)

第 4 步:定義輸入函式

要將加利福尼亞州住房資料匯入 LinearRegressor,我們需要定義一個輸入函式,讓它告訴 TensorFlow 如何對資料進行預處理,以及在模型訓練期間如何批處理、隨機處理和重複資料。

首先,我們將 Pandas 特徵資料轉換成 NumPy 陣列字典。然後,我們可以使用 TensorFlow Dataset API 根據我們的資料構建 Dataset 物件,並將資料拆分成大小為 batch_size 的多批資料,以按照指定週期數 (num_epochs) 進行重複。

注意:如果將預設值 num_epochs=None 傳遞到 repeat(),輸入資料會無限期重複。

然後,如果 shuffle 設定為 True,則我們會對資料進行隨機處理,以便資料在訓練期間以隨機方式傳遞到模型。buffer_size 引數會指定 shuffle 將從中隨機抽樣的資料集的大小。

最後,輸入函式會為該資料集構建一個迭代器,並向 LinearRegressor 返回下一批資料。

def my_input_fn(features, targets, batch_size=1, shuffle=True, num_epochs=None):
    """Trains a linear regression model of one feature.

    Args:
      features: pandas DataFrame of features
      targets: pandas DataFrame of targets
      batch_size: Size of batches to be passed to the model
      shuffle: True or False. Whether to shuffle the data.
      num_epochs: Number of epochs for which data should be repeated. None = repeat indefinitely
    Returns:
      Tuple of (features, labels) for next data batch
    """

    # Convert pandas data into a dict of np arrays.
    features = {key:np.array(value) for key,value in dict(features).items()}                                           

    # Construct a dataset, and configure batching/repeating
    ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit
    ds = ds.batch(batch_size).repeat(num_epochs)

    # Shuffle the data, if specified
    if shuffle:
      ds = ds.shuffle(buffer_size=10000)

    # Return the next batch of data
    features, labels = ds.make_one_shot_iterator().get_next()
    return features, labels

第 5 步:訓練模型

現在,我們可以在 linear_regressor 上呼叫 train() 來訓練模型。我們會將 my_input_fn 封裝在 lambda 中,以便可以將 my_featuretarget 作為引數傳入(有關詳情,請參閱此 TensorFlow 輸入函式教程),首先,我們會訓練 100 步。

_ = linear_regressor.train(
    input_fn = lambda:my_input_fn(my_feature, targets),
    steps=100
)

第 6 步:評估模型

我們基於該訓練資料做一次預測,看看我們的模型在訓練期間與這些資料的擬合情況。

注意:訓練誤差可以衡量您的模型與訓練資料的擬合情況,但並不能衡量模型泛化到新資料的效果。在後面的練習中,您將探索如何拆分資料以評估模型的泛化能力。

# Create an input function for predictions.
# Note: Since we're making just one prediction for each example, we don't 
# need to repeat or shuffle the data here.
prediction_input_fn =lambda: my_input_fn(my_feature, targets, num_epochs=1, shuffle=False)

# Call predict() on the linear_regressor to make predictions.
predictions = linear_regressor.predict(input_fn=prediction_input_fn)

# Format predictions as a NumPy array, so we can calculate error metrics.
predictions = np.array([item['predictions'][0] for item in predictions])

# Print Mean Squared Error and Root Mean Squared Error.
mean_squared_error = metrics.mean_squared_error(predictions, targets)
root_mean_squared_error = math.sqrt(mean_squared_error)
print "Mean Squared Error (on training data): %0.3f" % mean_squared_error
print "Root Mean Squared Error (on training data): %0.3f" % root_mean_squared_error

這是出色的模型嗎?您如何判斷誤差有多大?

由於均方誤差 (MSE) 很難解讀,因此我們經常檢視的是均方根誤差 (RMSE)。RMSE 的一個很好的特性是,它可以在與原目標相同的規模下解讀。

我們來比較一下 RMSE 與目標最大值和最小值的差值:

min_house_value = california_housing_dataframe["median_house_value"].min()
max_house_value = california_housing_dataframe["median_house_value"].max()
min_max_difference = max_house_value - min_house_value

print "Min. Median House Value: %0.3f" % min_house_value
print "Max. Median House Value: %0.3f" % max_house_value
print "Difference between Min. and Max.: %0.3f" % min_max_difference
print "Root Mean Squared Error: %0.3f" % root_mean_squared_error

輸出誤差:

Min. Median House Value: 14.999
Max. Median House Value: 500.001
Difference between Min. and Max.: 485.002
Root Mean Squared Error: 237.417

我們的誤差跨越目標值的近一半範圍,可以進一步縮小誤差嗎?

這是每個模型開發者都會煩惱的問題。我們來制定一些基本策略,以降低模型誤差。

首先,我們可以瞭解一下根據總體摘要統計資訊,預測和目標的符合情況。

calibration_data = pd.DataFrame()
calibration_data["predictions"] = pd.Series(predictions)
calibration_data["targets"] = pd.Series(targets)
calibration_data.describe()

image.png
好的,此資訊也許有幫助。平均值與模型的 RMSE 相比情況如何?各種分位數呢?

我們還可以將資料和學到的線視覺化。我們已經知道,單個特徵的線性迴歸可繪製成一條將輸入 x 對映到輸出 y 的線。

首先,我們將獲得均勻分佈的隨機資料樣本,以便繪製可辨的散點圖。

sample = california_housing_dataframe.sample(n=300)

然後,我們根據模型的偏差項和特徵權重繪製學到的線,並繪製散點圖。該線會以紅色顯示。

# Get the min and max total_rooms values.
x_0 = sample["total_rooms"].min()
x_1 = sample["total_rooms"].max()

# Retrieve the final weight and bias generated during training.
weight = linear_regressor.get_variable_value('linear/linear_model/total_rooms/weights')[0]
bias = linear_regressor.get_variable_value('linear/linear_model/bias_weights')

# Get the predicted median_house_values for the min and max total_rooms values.
y_0 = weight * x_0 + bias 
y_1 = weight * x_1 + bias

# Plot our regression line from (x_0, y_0) to (x_1, y_1).
plt.plot([x_0, x_1], [y_0, y_1], c='r')

# Label the graph axes.
plt.ylabel("median_house_value")
plt.xlabel("total_rooms")

# Plot a scatter plot from our data sample.
plt.scatter(sample["total_rooms"], sample["median_house_value"])

# Display graph.
plt.show()

image.png
這條初始線看起來與目標相差很大。看看您能否回想起摘要統計資訊,並看到其中蘊含的相同資訊。

綜上所述,這些初始健全性檢查提示我們也許可以找到更好的線。

調整模型超引數

對於本練習,為方便起見,我們已將上述所有程式碼放入一個函式中。您可以使用不同的引數呼叫該函式,以瞭解相應效果。

我們會在 10 個等分的時間段內使用此函式,以便觀察模型在每個時間段的改善情況。

對於每個時間段,我們都會計算訓練損失並繪製相應圖表。這可以幫助您判斷模型收斂的時間,或者模型是否需要更多迭代。

此外,我們還會繪製模型隨著時間的推移學習的特徵權重和偏差項值的曲線圖。您還可以通過這種方式檢視模型的收斂效果。

def train_model(learning_rate, steps, batch_size, input_feature="total_rooms"):
  """Trains a linear regression model of one feature.

  Args:
    learning_rate: A `float`, the learning rate.
    steps: A non-zero `int`, the total number of training steps. A training step
      consists of a forward and backward pass using a single batch.
    batch_size: A non-zero `int`, the batch size.
    input_feature: A `string` specifying a column from `california_housing_dataframe`
      to use as input feature.
  """

  periods = 10
  steps_per_period = steps / periods
  # 設定特徵的字串
  my_feature = input_feature
  my_feature_data = california_housing_dataframe[[my_feature]]
  # 標籤
  my_label = "median_house_value"
  targets = california_housing_dataframe[my_label]

  # Create feature columns
  feature_columns = [tf.feature_column.numeric_column(my_feature)]

  # Create input functions
  training_input_fn = lambda:my_input_fn(my_feature_data, targets, batch_size=batch_size)
  prediction_input_fn = lambda: my_input_fn(my_feature_data, targets, num_epochs=1, shuffle=False)

  # Create a linear regressor object.
  my_optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
  my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)
  linear_regressor = tf.estimator.LinearRegressor(
      feature_columns=feature_columns,
      optimizer=my_optimizer
  )

  # Set up to plot the state of our model's line each period.
  plt.figure(figsize=(15, 6))
  plt.subplot(1, 2, 1)
  plt.title("Learned Line by Period")
  plt.ylabel(my_label)
  plt.xlabel(my_feature)
  sample = california_housing_dataframe.sample(n=300)
  plt.scatter(sample[my_feature], sample[my_label])
  colors = [cm.coolwarm(x) for x in np.linspace(-1, 1, periods)]

  # Train the model, but do so inside a loop so that we can periodically assess
  # loss metrics.
  print "Training model..."
  print "RMSE (on training data):"
  root_mean_squared_errors = []
  for period in range (0, periods):
    # Train the model, starting from the prior state.
    linear_regressor.train(
        input_fn=training_input_fn,
        steps=steps_per_period
    )
    # Take a break and compute predictions.
    predictions = linear_regressor.predict(input_fn=prediction_input_fn)
    predictions = np.array([item['predictions'][0] for item in predictions])

    # Compute loss.
    root_mean_squared_error = math.sqrt(
        metrics.mean_squared_error(predictions, targets))
    # Occasionally print the current loss.
    print "  period %02d : %0.2f" % (period, root_mean_squared_error)
    # Add the loss metrics from this period to our list.
    root_mean_squared_errors.append(root_mean_squared_error)
    # Finally, track the weights and biases over time.
    # Apply some math to ensure that the data and line are plotted neatly.
    y_extents = np.array([0, sample[my_label].max()])

    weight = linear_regressor.get_variable_value('linear/linear_model/%s/weights' % input_feature)[0]
    bias = linear_regressor.get_variable_value('linear/linear_model/bias_weights')

    x_extents = (y_extents - bias) / weight
    x_extents = np.maximum(np.minimum(x_extents,
                                      sample[my_feature].max()),
                           sample[my_feature].min())
    y_extents = weight * x_extents + bias
    plt.plot(x_extents, y_extents, color=colors[period]) 
  print "Model training finished."

  # Output a graph of loss metrics over periods.
  plt.subplot(1, 2, 2)
  plt.ylabel('RMSE')
  plt.xlabel('Periods')
  plt.title("Root Mean Squared Error vs. Periods")
  plt.tight_layout()
  plt.plot(root_mean_squared_errors)

  # Output a table with calibration data.
  calibration_data = pd.DataFrame()
  calibration_data["predictions"] = pd.Series(predictions)
  calibration_data["targets"] = pd.Series(targets)
  display.display(calibration_data.describe())

  print "Final RMSE (on training data): %0.2f" % root_mean_squared_error

有適用於模型調整的標準啟發法嗎?

這是一個常見的問題。簡短的答案是,不同超引數的效果取決於資料。因此,不存在必須遵循的規則,您需要對自己的資料進行測試。

即便如此,我們仍在下面列出了幾條可為您提供指導的經驗法則:

  • 訓練誤差應該穩步減小,剛開始是急劇減小,最終應隨著訓練收斂達到平穩狀態。
  • 如果訓練尚未收斂,嘗試執行更長的時間。
  • 如果訓練誤差減小速度過慢,則提高學習速率也許有助於加快其減小速度。
    • 但有時如果學習速率過高,訓練誤差的減小速度反而會變慢。
  • 如果訓練誤差變化很大,嘗試降低學習速率。
    • 較低的學習速率和較大的步數/較大的批量大小通常是不錯的組合。
  • 批量大小過小也會導致不穩定情況。不妨先嚐試 100 或 1000 等較大的值,然後逐漸減小值的大小,直到出現效能降低的情況。

重申一下,切勿嚴格遵循這些經驗法則,因為效果取決於資料。請始終進行試驗和驗證。