1. 程式人生 > >【123】TensorFlow 多個特徵值線性迴歸,並且使用訓練集、驗證集和測試集的例子

【123】TensorFlow 多個特徵值線性迴歸,並且使用訓練集、驗證集和測試集的例子

我們的目標是構建數學模型來預測房價。通常情況下,會有多個因素影響房價,因此使用多個特徵值做線性迴歸。數學上,每個特徵值視為一個自變數,相當與構建一個包含多個自變數的函式。
1.png

我寫了兩個 python 檔案,一個是用來訓練模型,並使用驗證集驗證模型。另一個是用測試集測試我的數學模型。

在程式中,使用到的特徵值是這些:latitude、 longitude、
housing_median_age、total_rooms、 total_bedrooms、population、 households、 median_income 和 rooms_per_person

訓練模型的檔案:

import
tensorflow as tf import numpy as np import matplotlib.pyplot as plt import pandas as pd import math from sklearn import metrics # 從CSV檔案中讀取資料,返回DataFrame型別的資料集合。 def zc_func_read_csv(): zc_var_dataframe = pd.read_csv("http://114.115.223.20/california_housing_train.csv", sep=",") # 打亂資料集合的順序。有時候資料檔案有可能是根據某種順序排列的,會影響到我們對資料的處理。
zc_var_dataframe = zc_var_dataframe.reindex(np.random.permutation(zc_var_dataframe.index)) return zc_var_dataframe # 預處理特徵值 def preprocess_features(california_housing_dataframe): selected_features = california_housing_dataframe[ ["latitude", "longitude", "housing_median_age"
, "total_rooms", "total_bedrooms", "population", "households", "median_income"] ] processed_features = selected_features.copy() # 增加一個新屬性:人均房屋數量。 processed_features["rooms_per_person"] = ( california_housing_dataframe["total_rooms"] / california_housing_dataframe["population"]) return processed_features # 預處理標籤 def preprocess_targets(california_housing_dataframe): output_targets = pd.DataFrame() # Scale the target to be in units of thousands of dollars. output_targets["median_house_value"] = ( california_housing_dataframe["median_house_value"] / 1000.0) return output_targets def zc_func_yhat_eval(zc_param_yhat): r = [] for ele in zc_param_yhat: r.append(ele[0]) return r # 根據數學模型計算預測值。公式是 y = w0 + w1 * x1 + w2 * x2 .... + w9 * x9 def zc_func_predict(zc_param_dataframe, zc_param_weight_arr): zc_var_result = [] for var_row_index in zc_param_dataframe.index: y = zc_param_weight_arr[0] y = y + zc_param_weight_arr[1] * zc_param_dataframe.loc[var_row_index].values[0] y = y + zc_param_weight_arr[2] * zc_param_dataframe.loc[var_row_index].values[1] y = y + zc_param_weight_arr[3] * zc_param_dataframe.loc[var_row_index].values[2] y = y + zc_param_weight_arr[4] * zc_param_dataframe.loc[var_row_index].values[3] y = y + zc_param_weight_arr[5] * zc_param_dataframe.loc[var_row_index].values[4] y = y + zc_param_weight_arr[6] * zc_param_dataframe.loc[var_row_index].values[5] y = y + zc_param_weight_arr[7] * zc_param_dataframe.loc[var_row_index].values[6] y = y + zc_param_weight_arr[8] * zc_param_dataframe.loc[var_row_index].values[7] y = y + zc_param_weight_arr[9] * zc_param_dataframe.loc[var_row_index].values[8] zc_var_result.append(y) return zc_var_result # 訓練形如 y = w0 + w1 * x1 + w2 * x2 + ... 的直線模型。x1 x2 ...是自變數, # w0 是常數項,w1 w2 ... 是對應自變數的權重。 # feature_arr 特徵值的矩陣。每一行是 [1.0, x1_data, x2_data, ...] # label_arr 標籤的陣列。相當於 y = kx + b 中的y。 # training_steps 訓練的步數。即訓練的迭代次數。 # period 誤差報告粒度 # learning_rate 在梯度下降演算法中,控制梯度步長的大小。 def zc_fn_train_line(feature_arr, label_arr, validate_feature_arr, validate_label_arr, training_steps, periods, learning_rate): feature_tf_arr = feature_arr label_tf_arr = np.array([[e] for e in label_arr]).astype(np.float32) # 整個訓練分成若干段,即誤差報告粒度,用periods表示。 # steps_per_period 表示平均每段有多少次訓練 steps_per_period = training_steps / periods # 存放 L2 損失的陣列 loss_arr = [] weight_arr_length = len(feature_arr[0]) # 開啟TF會話,在TF 會話的上下文中進行 TF 的操作。 with tf.Session() as sess: # 訓練集的均方根誤差RMSE。這是儲存誤差報告的陣列。 train_rmse_arr = [] # 驗證集的均方根誤差RMSE。 validate_rmse_arr = [] # 設定 tf 張量(tensor)。注意:TF會話中的註釋裡面提到的常量和變數是針對TF設定而言,不是python語法。 # 因為在TF運算過程中,x作為特徵值,y作為標籤 # 是不會改變的,所以分別設定成input 和 target 兩個常量。 # 這是 x 取值的張量。設一共有m條資料,可以把input理解成是一個m行2列的矩陣。矩陣第一列都是1,第二列是x取值。 input = tf.constant(feature_tf_arr) # 設定 y 取值的張量。target可以被理解成是一個m行1列的矩陣。 有些文章稱target為標籤。 target = tf.constant(label_tf_arr) # 設定權重變數。因為在每次訓練中,都要改變權重,來尋找L2損失最小的權重,所以權重是變數。 # 可以把權重理解成一個多行1列的矩陣。初始值是隨機的。行數就是權重數。 weights = tf.Variable(tf.random_normal([weight_arr_length, 1], 0, 0.1)) # 初始化上面所有的 TF 常量和變數。 tf.global_variables_initializer().run() # input 作為特徵值和權重做矩陣乘法。m行2列矩陣乘以2行1列矩陣,得到m行1列矩陣。 # yhat是新矩陣,yhat中的每個數 yhat' = w0 * 1 + w1 * x1 + w2 * x2 ...。 # yhat是預測值,隨著每次TF調整權重,yhat都會變化。 yhat = tf.matmul(input, weights) # tf.subtract計算兩個張量相減,當然兩個張量必須形狀一樣。 即 yhat - target。 yerror = tf.subtract(yhat, target) # 計算L2損失,也就是方差。 loss = tf.nn.l2_loss(yerror) # 梯度下降演算法。 zc_optimizer = tf.train.GradientDescentOptimizer(learning_rate) # 注意:為了安全起見,我們還會通過 clip_gradients_by_norm 將梯度裁剪應用到我們的優化器。 # 梯度裁剪可確保梯度大小在訓練期間不會變得過大,梯度過大會導致梯度下降法失敗。 zc_optimizer = tf.contrib.estimator.clip_gradients_by_norm(zc_optimizer, 5.0) zc_optimizer = zc_optimizer.minimize(loss) for _ in range(periods): for _ in range(steps_per_period): # 重複執行梯度下降演算法,更新權重數值,找到最合適的權重數值。 sess.run(zc_optimizer) # 每次迴圈都記錄下損失loss的值,並放到陣列loss_arr中。 loss_arr.append(loss.eval()) v_tmp_weight_arr = weights.eval() train_rmse_arr.append(math.sqrt( metrics.mean_squared_error(zc_func_yhat_eval(yhat.eval()), label_tf_arr))) validate_rmse_arr.append(math.sqrt( metrics.mean_squared_error(zc_func_predict(validate_feature_arr, v_tmp_weight_arr), validate_label_arr))) zc_weight_arr = weights.eval() zc_yhat = yhat.eval() return (zc_weight_arr, zc_yhat, loss_arr, train_rmse_arr, validate_rmse_arr) # end def train_line # 構建用於訓練的特徵值。 # zc_var_dataframe 原來資料的 Dataframe # 本質上是用二維陣列構建一個矩陣。裡面的每個一維陣列都是矩陣的一行,形狀類似下面這種形式: # 1.0, x1[0], x2[0], x3[0], ... # 1.0, x1[1], x2[1], x3[1], ... # ......................... # 其中x1, x2, x3 表示資料的某個維度,比如:"latitude","longitude","housing_median_age"。 # 也可以看作是公式中的多個自變數。 def zc_func_construct_tf_feature_arr(zc_var_dataframe): zc_var_result = [] # dataframe中每列的名稱。 zc_var_col_name_arr = [e for e in zc_var_dataframe] # 遍歷dataframe中的每行。 for row_index in zc_var_dataframe.index: zc_var_tf_row = [1.0] for i in range(len(zc_var_col_name_arr)): zc_var_tf_row.append(zc_var_dataframe.loc[row_index].values[i]) zc_var_result.append(zc_var_tf_row) return zc_var_result # 畫損失的變化圖。 # ax Axes # zc_param_learning_steps 訓練次數。 # zc_param_loss_arr 每次訓練,損失變化的記錄 def zc_func_paint_loss(ax, arr_train_rmse, arr_validate_rmse): ax.plot(range(0, len(arr_train_rmse)), arr_train_rmse, label="training", color="blue") ax.plot(range(0, len(arr_validate_rmse)), arr_validate_rmse, label="validate", color="orange") # 主函式 def zc_func_main(): california_housing_dataframe = zc_func_read_csv() # 對於訓練集,我們從共 17000 個樣本中選擇前 12000 個樣本。 training_examples = preprocess_features(california_housing_dataframe.head(12000)) training_targets = preprocess_targets(california_housing_dataframe.head(12000)) # 對於驗證集,我們從共 17000 個樣本中選擇後 5000 個樣本。 validation_examples = preprocess_features(california_housing_dataframe.tail(5000)) validation_targets = preprocess_targets(california_housing_dataframe.tail(5000)) fig = plt.figure() fig.set_size_inches(5,5) zc_var_train_feature_arr = zc_func_construct_tf_feature_arr(training_examples) zc_var_leaning_step_num = 500 (zc_weight_arr, zc_yhat, loss_arr, train_rmse_arr, validate_rmse_arr) = zc_fn_train_line(zc_var_train_feature_arr, training_targets["median_house_value"], validation_examples, validation_targets["median_house_value"], zc_var_leaning_step_num, 20, 0.002) # 畫損失變化圖。 zc_func_paint_loss(fig.add_subplot(1,1,1), train_rmse_arr, validate_rmse_arr) plt.show() print("Training RMSE " + str(train_rmse_arr[len(train_rmse_arr) - 1]) + " Validate RMSE: " + str(validate_rmse_arr[len(validate_rmse_arr) - 1])) print("wieghts:", zc_weight_arr) zc_func_main()

執行結果如下

1.png

然後我們用測試集來測試模型:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import math
from sklearn import metrics


# 從CSV檔案中讀取資料,返回DataFrame型別的資料集合。
def zc_func_read_csv(zc_param_csv_url):
#     zc_var_dataframe = pd.read_csv("http://49.4.2.82/california_housing_train.csv", sep=",")
    zc_var_dataframe = pd.read_csv(zc_param_csv_url, sep=",")
    # 打亂資料集合的順序。有時候資料檔案有可能是根據某種順序排列的,會影響到我們對資料的處理。
    zc_var_dataframe = zc_var_dataframe.reindex(np.random.permutation(zc_var_dataframe.index))
    return zc_var_dataframe

# 預處理特徵值
def preprocess_features(california_housing_dataframe):
    selected_features = california_housing_dataframe[
        ["latitude",
         "longitude",
         "housing_median_age",
         "total_rooms",
         "total_bedrooms",
         "population",
         "households",
         "median_income"]
    ]
    processed_features = selected_features.copy()
    # 增加一個新屬性:人均房屋數量。
    processed_features["rooms_per_person"] = (
        california_housing_dataframe["total_rooms"] /
        california_housing_dataframe["population"])
    return processed_features

# 預處理標籤
def preprocess_targets(california_housing_dataframe):
    output_targets = pd.DataFrame()
    # Scale the target to be in units of thousands of dollars.
    output_targets["median_house_value"] = (
        california_housing_dataframe["median_house_value"] / 1000.0)
    return output_targets


# 根據數學模型計算預測值。公式是 y = w0 + w1 * x1 + w2 * x2 .... + w9 * x9
def zc_func_predict(zc_param_dataframe, zc_param_weight_arr):
    zc_var_result = []
    for var_row_index in zc_param_dataframe.index:
        y = zc_param_weight_arr[0]
        y = y + zc_param_weight_arr[1] * zc_param_dataframe.loc[var_row_index].values[0]
        y = y + zc_param_weight_arr[2] * zc_param_dataframe.loc[var_row_index].values[1]
        y = y + zc_param_weight_arr[3] * zc_param_dataframe.loc[var_row_index].values[2]
        y = y + zc_param_weight_arr[4] * zc_param_dataframe.loc[var_row_index].values[3]
        y = y + zc_param_weight_arr[5] * zc_param_dataframe.loc[var_row_index].values[4]
        y = y + zc_param_weight_arr[6] * zc_param_dataframe.loc[var_row_index].values[5]
        y = y + zc_param_weight_arr[7] * zc_param_dataframe.loc[var_row_index].values[6]
        y = y + zc_param_weight_arr[8] * zc_param_dataframe.loc[var_row_index].values[7]
        y = y + zc_param_weight_arr[9] * zc_param_dataframe.loc[var_row_index].values[8]
        zc_var_result.append(y)

    return zc_var_result


def zc_func_main():
    california_housing_dataframe = zc_func_read_csv("http://114.115.223.20/california_housing_train.csv")
    # 對於驗證集,我們從共 17000 個樣本中選擇後 5000 個樣本。
    validation_examples = preprocess_features(california_housing_dataframe.tail(5000))
    #print(validation_examples.describe())
    validation_targets = preprocess_targets(california_housing_dataframe.tail(5000))
    # 通過訓練得到的模型權重
    zc_param_weight_arr = [-0.19103946,0.37296584,-0.7998271,0.25258455,0.03940793,-0.13476478,
                           -0.04755316,0.20973271,0.00714895,0.02928887]
    # 根據已經訓練得到的模型係數,計算預驗證集的測值。
    zc_var_validate_predict_arr = zc_func_predict(validation_examples, zc_param_weight_arr)
    # 計算驗證集的預測值和標籤之間的均方根誤差。
    validation_root_mean_squared_error = math.sqrt(
        metrics.mean_squared_error(zc_var_validate_predict_arr, validation_targets["median_house_value"]))
    print("validation RMSE:", validation_root_mean_squared_error)


    # 基於測試集資料進行評估
    test_dataframe = zc_func_read_csv("http://114.115.223.20/california_housing_test.csv")
    # 測試集的樣本
    test_examples = preprocess_features(test_dataframe)
    test_targets = preprocess_targets(test_dataframe)
    # 計算測試集的預測值
    zc_var_test_predict_arr = zc_func_predict(test_examples, zc_param_weight_arr)
    # 計算測試集的預測值和標籤之間的均方根誤差。
    test_root_mean_squared_error = math.sqrt(
        metrics.mean_squared_error(zc_var_test_predict_arr, test_targets["median_house_value"]))
    print("test RMSE:", test_root_mean_squared_error)

zc_func_main()

執行結果:

('validation RMSE:', 120.00240785178423)
('test RMSE:', 116.6171534701997)