認識波士頓住房價資料集
- 透過 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 | 人口中地位較低人群的百分比 |
+ 程式碼: ```python 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,代表 \(|\text{y}_ \text{pred}-\text{y}_ \text{true}|\),更能直觀的表達與目標值的差異,如 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)