2018年3月29日 星期四

TensorFlow的第一步

請參考原始資料:
https://colab.research.google.com/notebooks/mlcc/first_steps_with_tensor_flow.ipynb?hl=en#scrollTo=EL8-9d4ZJNR7
(我對原資料的理解不一定正確,請自行修正)

要運用tensorflow 的第步驟如下:

第一步:定義特性及設定特性行位

  首先我們要告訴 tensorflow 所謂的 「特性行位」feature column是資料內的那一行。tensorflow 才會知道從那些資料去進行分析。而所謂的特性行位的資料行,大致分成兩種:
一是類別型資料(Catagorical Data),例如這行的資料都是形容詞:大中小、紅黃綠、方扁圓 等等。
另一種是數字型資料(Numerical Data),例如面積:30米平方、60米平方、90米平方,RGB:(200,179,87),時間差:20秒,90秒,45秒 等等很明確的數字資料。
在TensorFlow 內,透過 feature column 的結構來指定特性行位,而這個feature column 只儲存特性行位的此行的說明,並不會儲存此行的所有資料。
在本範例,我們只用資料內的一行 total_rooms 來做為特性行位,下面兩行,第一行是把其中的 total_rooms 從 california_housing_dataframe 取出來,存成 my_feature, 第二行則是將 my_feature指定給feature_column,並設定為數字型資料numeric_column:

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

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

你可以參考 tensorflow 對 feature_column.numeric_column 的說明,它其實包含了其他的參數,但在這裡,先簡化,用一行來示範即可。

第二步:定義目標

再來我們要定義目標,即,要估算的的目標是為何,給了特性和目標給tensorflow去學習,tensorflow 才可以依據新的feature 來推算目標值可能為何。在這範例中,我們要估的是 Median_house_value:

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

第三步:設定線性迴歸(LinearRegressor)

在學習使用tensorflow 的範例裡,我們用較單純的線性迴歸方法來做學習。並使用 GradientDescentOptimizer (即使用 Mini-Batch Stochastic Gradient Descent(SGD)). learning_rate 參數可以控制推估步驟的大小。同時我們也使用透過 clip_gradients_by_norm 函式來使用 gradient clipping方法,以避免誤差越算越大,造成估算錯誤。

# 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
)

第四步:設定輸入的部分

在匯入 California housing data 到 LinearRegressor 函式內之前,我們必須先定設定好輸入函式的部分,好告訴 tensorflow 如何處理匯進來的資料,好在學習的過程中進行如何分批次,打散資料,重覆這些動作等等。
記得嗎?我們在第一步驟只是先定義特性行的名稱,資料並還沒匯入喔。
所以,首先,我們必須把pandas 的 特性資料 轉成 numpy 的 array 型式的 dict. 然後我們就可以用 TensorFlow 的 Dataset API 來建構 dataset 物件,再根據給定的 batch_size 來將資料分散成幾個群組(batches), 以便用來重覆執行每次的學習(num_epochs)。(如果指定 num_epochs=None 給 repeat(), 那輸入資料將會被無止䀆地重覆?
再來,如果 shuffle 設為 True, 那在訓練期間的資料匯入時就會被打散,這樣資料會比較平均一些。buffer_size 參數則是用來指定打散後匯進 dataset 的資料量大小。
最後,我們的輸入函式會建構一個 iterator 給 dataset, 並傳回下一個要處理的批次資料給 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

第五步:開始訓練

至此,我們可以準備呼叫在 linear_regressor 的 train() 來訓練了。
_ = linear_regressor.train(
    input_fn = lambda:my_input_fn(my_feature, targets),
    steps=100
) 

第六步:評估此模型

我們可以開始針對訓練來測試訓練結果,看看我們的模型是否符合我們的訓練設定。
# 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): {}".format(mean_squared_error))
print ("Root Mean Squared Error (on training data): {}".format(root_mean_squared_error))

Mean Squared Error (on training data): 56367.025 Root Mean Squared Error (on training data): 237.417

這是好的估算模型嗎?我們怎麼判定誤差是多少才算 好?
Mean Squared Error(MSE 均方差) 並不好判讀,所以可以改成 Root Mean Squared Error(RMSE 均方差根) 來判讀,均方差根的好處是不會受範圍的影響。
我們可以比較一下 target 中最大和最小值的 median_house_value 的 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: {}".format(min_house_value)) 
print ("Max. Median House Value: {}".format(max_house_value)) 
print ("Difference between Min. and Max.: {}".format(min_max_difference)) 
print ("Root Mean Squared Error: {}".format(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()

predictionstargets
count17000.017000.0
mean0.1207.3
std0.1116.0
min0.015.0
25%0.1119.4
50%0.1180.4
75%0.2265.0
max1.9500.0

這樣的資訊(說實在的,我不懂)也許夠用了,我們來把它視覺化看看。
首先,我們胡亂取一些sample 的資料,好畫一張看得懂的分佈圖。

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()


嗯嗯,不太懂那條紅色代表 RMSE 的什麼意思⋯⋯ 😅

再進階一下:

調整這個模型的一些參數⋯⋯

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

調整一:

調整參數以改進耗損並接近目標分布狀況。 如果⋯⋯超過跑超過5分鐘以上,表示 RMSE 被你操掛了,要重新調整一下參數。

train_model(
    learning_rate=0.00001,
    steps=100,
    batch_size=1
)


沒有留言:

張貼留言