認識波士頓住房價資料集
- 透過 tensorflow 引入資料集
- 波士頓住房價資料集是 1970 年代中期波士頓的郊區資料,包含犯罪率、當地財產稅等。
from tensorflow.keras.datasets import boston_housing (train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
- 與先前兩個範例最大的差別是,資料點明顯較少,共 404 + 102 = 506 筆。
print(train_data.shape) print(train_targets.shape) print(test_data.shape) print(test_targets.shape) > (404, 13) (404,) (102, 13) (102,)
- 需注意,每個特徵都有不同的單位刻度
查看特徵分布
特徵值範圍分析:
特徵 最小值 最大值 平均值 標準差 特徵意義 CRIM 0.006 88.976 3.614 8.602 城鎮人均犯罪率 ZN 0.000 100.000 11.364 23.322 佔地面積超過25000平方呎的住宅用地比例 INDUS 0.460 27.740 11.137 6.860 每個城鎮非零售商業用地的比例 CHAS 0.000 1.000 0.069 0.254 Charles River 虛擬變數 (1 if tract bounds river; 0 otherwise) NOX 0.385 0.871 0.555 0.116 一氧化氮濃度 RM 3.561 8.780 6.285 0.703 每棟住宅的平均房間數 AGE 2.900 100.000 68.575 28.149 1940年之前建成的自用房屋比例 DIS 1.130 12.126 3.795 2.106 到波士頓5個就業中心的加權距離 RAD 1.000 24.000 9.549 8.707 到高速公路的可達性指數 TAX 187.000 711.000 408.237 168.537 每10000美元的房產稅率 PTRATIO 12.600 22.000 18.456 2.165 城鎮師生比例 B 0.320 396.900 356.674 91.295 1000(Bk - 0.63)^2,其中Bk為城鎮中黑人的比例 LSTAT 1.730 37.970 12.653 7.141 人口中地位較低人群的百分比 程式碼:
import numpy as np import pandas as pd # 合併訓練和測試資料 combined_data = np.vstack((train_data, test_data)) print("Combined data shape:", combined_data.shape) # 創建特徵名稱列表 (Boston Housing Dataset 的特徵) feature_names = [ 'CRIM', # 城鎮人均犯罪率 'ZN', # 佔地面積超過25000平方呎的住宅用地比例 'INDUS', # 每個城鎮非零售商業用地的比例 'CHAS', # Charles River 虛擬變數 (1 if tract bounds river; 0 otherwise) 'NOX', # 一氧化氮濃度 'RM', # 每棟住宅的平均房間數 'AGE', # 1940年之前建成的自用房屋比例 'DIS', # 到波士頓5個就業中心的加權距離 'RAD', # 到高速公路的可達性指數 'TAX', # 每10000美元的房產稅率 'PTRATIO', # 城鎮師生比例 'B', # 1000(Bk - 0.63)^2,其中Bk為城鎮中黑人的比例 'LSTAT' # 人口中地位較低人群的百分比 ] # 創建 DataFrame 以方便分析 df = pd.DataFrame(combined_data, columns=feature_names) # 分析每個特徵的範圍 feature_analysis = pd.DataFrame({ 'Min': df.min(), 'Max': df.max(), 'Mean': df.mean(), 'Std': df.std() }).round(3) print("\n特徵值範圍分析:") print(feature_analysis) ```
準備資料
- 首先我們先做正規化處理(normalization),常用的方法是減去特徵的平均值並除以標準差,使平均值為 0 且標準差為 1。
mean = train_data.mean(axis=0)
std = train_data.std(axis=0)
train_data -= mean
train_data /= std
test_data -= mean
test_data /= std
建立模型
- 一般來說,訓練資料愈少,overfitting 的情況會愈嚴重,所有我們採用較小型的神經網路。
from keras import models
from keras import layers
def build_model():
model = keras.Sequential([
layers.Dense(64, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(1)
])
model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
return model
- 注意到最後我們以
Dense(1)
結束,沒有套用激活函數,原因是我們希望輸出一個浮點數型別的數值,即迴歸值。若是套用 sigmoid 函數,則神經網路只能輸出 0 到 1 之間的預測值。在此例中,因為最後一層是純線性的,所以神經網路可以自由地預測任何範圍內的值。 - 我們這邊使用的評量指標是
mae
,代表 ,更能直觀的表達與目標值的差異,如 MAE = 0.5,代表與預測值的差異是 $500 美元。
K-fold 驗證
由於資料點很少,驗證資料也會很少,所以驗證分數會因驗證集的選用而有很大的變化,造成驗證分數會有很大的變異性(variance),導致評斷模型優劣的可靠性降低。
因此,我們可以採用K-fold 交叉驗證(K-fold cross validation)。一般會將資料拆分為 K 個區塊,通常 K = 4 or 5。
示意圖:
- 將 data 分成 K 個部分:
data[0:n,:]
,data[n:2*n,:]
,data[2*n:3*n,:]
,data[3*n:4*n,:]
,data[4*n:5*n,:]
- 每次取其中一個區塊當驗證集,其餘區塊當訓練集,最後再取 K 次測試分數的平均值作為驗證分數。
- 將 data 分成 K 個部分:
from os import kill
k = 5
n = len(train_data) // k
epochs = 100
scores = []
for i in range(k):
print('Processing fold #', i)
val_data = train_data[i * n: (i+1) * n]
val_targets = train_targets[i * n: (i+1) * n]
partial_train_data = np.concatenate(
[train_data[:i * n],
train_data[(i+1) * n:]],
axis=0
)
partial_train_targets = np.concatenate(
[train_targets[:i * n],
train_targets[(i+1) * n:]],
axis=0
)
model = build_model()
model.fit(partial_train_data, partial_train_targets,
epochs=epochs, batch_size=16, verbose=0)
val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
scores.append(val_mae)
- 以此為例我們會得到
print(scores)
print(np.mean(scores))
> [1.720955491065979, 2.8763468265533447, 2.1907858848571777, 2.566359043121338, 2.54329252243042]
> 2.379547953605652
- 最後我們可以用求出來的最佳 epochs 代入全部資料,重新訓練最終的模型。
# 使用全部訓練數據重新訓練模型
final_model = build_model()
final_history = final_model.fit(
train_data, train_targets,
epochs=best_epoch, # 使用找到的最佳 epoch 數
batch_size=16,
verbose=1
)
- 最後我們就可以用 model 來進行預測
predictions = model.predict(test_data)