【123】TensorFlow 多個特徵值線性迴歸,並且使用訓練集、驗證集和測試集的例子
阿新 • • 發佈:2018-12-26
我們的目標是構建數學模型來預測房價。通常情況下,會有多個因素影響房價,因此使用多個特徵值做線性迴歸。數學上,每個特徵值視為一個自變數,相當與構建一個包含多個自變數的函式。
我寫了兩個 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()
執行結果如下
然後我們用測試集來測試模型:
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)