深度學習:利用神經網絡在少量數據情況下預測房價走勢

深度學習:利用神經網絡在少量數據情況下預測房價走勢

在前面兩個神經網絡的運用例子中,我們主要使用神經網絡對輸入數據預測出一個離散性結果,也就是預測的結果都是0,1,要不就是1到46中任意一個數,這些結果都是離散化,相互間不兼容。我們這節要用神經網絡對輸入數據預測出一個連續型結果,例如我們預測下個月房價的價格區間,明天的溫度區間等等。

本節我們使用網絡對房價進行預測,判斷房價在未來一段時間內會處於怎樣的價格區間,當模型構建好後,你把所關心地區歷年來足夠多的房價數據輸入網絡,你就可以得到它未來的價格變動走勢,如此一來你便能抓住正確的買房出手時機了。

由於我們自己的數據匱乏,相關部分總是把數據當做“機密”來管控,所以我們本節使用的訓練數據仍然來自於美國,美國政府公開了很多公共數據以便社會研究,在我們使用的Keras框架中,包含了美國1970年波士頓郊區的房地產相關信息,例如犯罪率,房地產稅,不同戶型的房價等等,這次我們要使用這些信息來預測房價的中位數。這次我們使用的數據有幾個特點,一是數據量很小,總共才五百多條,其次是數據的格式不一樣,有些數據以百分比來表示,有些數據處於範圍1到12,而有些數據範圍在0到100,因此數據的規格化處理也是本節的重點。

首先我們還是從框架中加載數據集:

from keras.datasets import boston_housing

(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()

上面代碼運行後,會把相關數據下載到本地。我們運行下面代碼看看數據集的大小:

print(train_data.shape)

print(test_data.shape)

這兩句代碼輸出後,得到結果如下:

(404,13)

(102, 13)

也就是說訓練數據集只有404條,測試數據集只有102條。而每條數據集都包含13個方面的特徵,它包括以下幾種數據信息:

1,城鎮中非商業用地的比率

2,每塊面積25000英尺的地皮上,住宅用地的比率。

3,城鎮中非零售商業用地面積

4,是否沿河,1表示沿河,0表示不沿河

5,一氧化氮含量

6,住宅的平均單間數

7,1940年前自有產權房的數量

8,住宅與波士頓五個工作區的距離

9,接入高速公路的便利指數

10,每一萬美金所要繳納的全額房產稅

11,城鎮中的師生比

12,1000*(BK - 0.63)^2, BK表示黑人比率

13,底層人口的百分比

我們要預測的是自有產權房的中位價格,單位是千美元,我們看看訓練數據集對應的結果:

深度學習:利用神經網絡在少量數據情況下預測房價走勢

從數據看,房價中位數在一萬到一萬五美金,記住這是1970年的房價。接下來我們需要把數據的格式統一起來,如果把不同格式,不同單位的數據雜糅一起輸入網絡會影響結果的準確度。數據規格化的做法是,計算每一個種類數據的均值,然後每個數據實例減去該均值,然後在除以數據的標準差,用Python很容易實現這種數值處理:

mean = train_data.mean(axis=0)

train_data -= mean

std = train_data.std(axis = 0)

train_data /= std

test_data -= mean

test_data /= std

經過規格化處理後,所以類型的數據都有了統一格式。這裡要注意的是,我們對測試數據集進行規格化處理是,均值和方差都來自訓練數據,切記不要隨意對測試數據集本身做任何變換,因為這樣會影響到預測結果的準確度。

接下來我們可以構建網絡來處理數據了。由於數據量很小,因此我們的網絡不能架構過大,基本上用兩個中間層,每層64個節點即可。一個需要注意的問題在於,當訓練數據越少時,過度擬合就會越嚴重,只有縮小網絡的結構,我們才能抑制過度擬合的嚴重程度。以下是我們構建網絡的代碼:

from keras import models

from keras import layers

def build_model():

'''

由於後面我們需要反覆構造同一種結構的網絡,所以我們把網絡的構造代碼放在一個函數中,

後面只要直接調用該函數就可以將網絡迅速初始化

'''

model = models.Sequential()

model.add(layers.Dense(64, activation='relu', input_shape=(train_data.shape[1],)))

model.add(layers.Dense(64, activation='relu'))

model.add(layers.Dense(1))

model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])

return model

我們構造的網絡最後一層只有一個節點,因為我們只需要預測一個結果,同時該節點沒有激活函數,當我們要預測的結果處於某個區間內時,我們就不用添加激活函數。如果使用激活函數的話,輸出結果的取值範圍就會收到影響,例如如果我們在最後一層的節點使用sigmoid函數,那麼結果範圍就會落入0到1之間,然而房價中位數可以落入任何非負區間,於是將其轉換為0到1之間是不合理的。

還需要注意的是,這裡用到的損失函數叫’mse’,中文名叫均方誤差,其公式如下:

MSE = 1/n * (Y - Y’)^2,其中Y對應的是正確結果,Y’對應的是網絡預測結果。當我們需要預測某個數值區間時,我們就使用MSE作用損失函數。在第三個參數中,我們使用mae,它表示平均絕對誤差,它用來描述預測結果與正確結果之差的絕對值,例如MAE = 0.5, 那意味著我們的預測結果與正確結果相差500美元,記住我們的單位是千美元。

接下來我們可以把訓練數據分成兩組,一組用於訓練網絡,一組用於校驗訓練的結果。目前有一個問題是,數據量太小,這導致的結果是,我們對數據劃分的不同方式都會對校驗結果產生不同影響,如此一來我們就不能對網絡的訓練情況有確切的掌握。處理這種情況的有效辦法叫k-fold交叉檢驗,k一般取4到5,選其中的k-1分數據來訓練,剩下1份來校驗。網絡總共訓練k次,每一份數據都有機會作為校驗集,最後把k次校驗的結果做一次平均。用代碼或許能產生更好的解釋效果:

import numpy as np

k = 4

num_val_samples = len(train_data) // k #整數除法

num_epochs = 10

all_scores = []

for i in range(k):

print('processing fold #', i)

#依次把k分數據中的每一份作為校驗數據集

val_data = train_data[i * num_val_samples : (i+1) * num_val_samples]

val_targets = train_targets[i* num_val_samples : (i+1) * num_val_samples]

#把剩下的k-1分數據作為訓練數據,如果第i分數據作為校驗數據,那麼把前i-1份和第i份之後的數據連起來

partial_train_data = np.concatenate([train_data[: i * num_val_samples],

train_data[(i+1) * num_val_samples:]], axis = 0)

partial_train_targets = np.concatenate([train_targets[: i * num_val_samples],

train_targets[(i+1) * num_val_samples: ]],

axis = 0)

print("build model")

model = build_model()

#把分割好的訓練數據和校驗數據輸入網絡

model.fit(partial_train_data, partial_train_targets, epochs = num_epochs,

batch_size = 1, verbose = 0)

print("evaluate the model")

val_mse, val_mae = model.evaluate(val_data, val_targets, verbose = 0)

all_scores.append(val_mae)

print(all_scores)

上面代碼運行後,輸出結果如下:

深度學習:利用神經網絡在少量數據情況下預測房價走勢

上面的結果表明,我們的預測結果跟正確結果相比大概相差2.4*1000也就是2400美元左右,這個誤差還是過大。為了縮小誤差,我們提升訓練的循環次數,並把每次訓練中的相關信息,例如對校驗數據的判斷準確率記錄下來,以便觀察訓練對網絡的影響,相關代碼如下:

import numpy as np

k = 4

num_val_samples = len(train_data) // k #整數除法

num_epochs = 200

all_mae_histories = []

for i in range(k):

print('processing fold #', i)

#依次把k分數據中的每一份作為校驗數據集

val_data = train_data[i * num_val_samples : (i+1) * num_val_samples]

val_targets = train_targets[i* num_val_samples : (i+1) * num_val_samples]

#把剩下的k-1分數據作為訓練數據,如果第i分數據作為校驗數據,那麼把前i-1份和第i份之後的數據連起來

partial_train_data = np.concatenate([train_data[: i * num_val_samples],

train_data[(i+1) * num_val_samples:]], axis = 0)

partial_train_targets = np.concatenate([train_targets[: i * num_val_samples],

train_targets[(i+1) * num_val_samples: ]],

axis = 0)

print("build model")

model = build_model()

#把分割好的訓練數據和校驗數據輸入網絡

history = model.fit(partial_train_data, partial_train_targets,

validation_data=(val_data, val_targets),

epochs = num_epochs,

batch_size = 1, verbose = 0)

mae_history = history.history['val_mean_absolute_error']

all_mae_histories.append(mae_history)

上面代碼把每次訓練的循環次數提高到200,並把每次訓練後,對校驗數據判斷的誤差記錄下來,後面我們會按照慣例把數據繪製出來,通過圖形的方式查看模型在訓練過程中的變化。

我們最外層有4論循環,在內部訓練網絡時,又有200輪循環,也就是num_epochs的值,網絡每進行一個epcho訓練就會得到一個偏差結果,於是最外層第一輪循環得到200個偏差結果記作x1[0],x1[1]…x1[199],最外層第二輪同樣有200個偏差,記作x2[0]x2[1]…x2[199],依次類推。我們計算4個大循環對應的誤差平均值,也就是(x1[0]+x2[0]+x3[0]+x4[0])/2,(x1[1]+x2[1]+x3[1]+x4[1])/4,依次類推,如此計算下來我們得到200個平均值,這些平均值將用來衡量200個epoch訓練中,模型精準度的變化,我們先看看上面的平均值如何用代碼實現:

average_mae_history = [

np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)

]

接著我們把200次循環的誤差繪製出來,代碼如下:

import matplotlib.pyplot as plt

plt.plot(range(1, len(average_mae_history) + 1), average_mae_history)

plt.xlabel('Epochs')

plt.ylabel('Validation MAE')

plt.show()

上面代碼運行後,結果如下:

深度學習:利用神經網絡在少量數據情況下預測房價走勢

從上圖看到,前10個數據點誤差值存有巨大的差異,因此統計時要忽略掉這些點,後面的數據段誤差值變動很劇烈,但我們從圖中很難看出不同點之間的差異究竟是多少。為了把第10個epoch後面的數據差異更明顯的展現出來,我們對數據做一些變換:

def smooth_curve(points, factor=0.9):

smoothed_points = []

for point in points:

if smoothed_points:

previous = smoothed_points[-1]

smoothed_points.append(previous * factor + point * (1 - factor))

else:

smoothed_points.append(point)

return smoothed_points

smooth_mae_history = smooth_curve(average_mae_history[10:])

plt.plot(range(1, len(smooth_mae_history)+1), smooth_mae_history)

plt.xlabel('Epochs')

plt.ylabel('Validation MAE')

plt.show()

上面代碼運行後結果如下:

深度學習:利用神經網絡在少量數據情況下預測房價走勢

上面代碼用的方法叫指數滑動平均,它的計算公式為:

深度學習:利用神經網絡在少量數據情況下預測房價走勢

指數滑動平均具有把反覆跳動的數據進行平滑的作用,使得我們能從反覆變動的數據看出它潛在的變化趨勢,例如上圖,原來跳動很難看出數據趨勢的數據點經過滑動平均後,變成了一條趨勢明顯的曲線,我們可以看到在epochs=30的時候,校驗誤差處於最低點,過了30後,誤差不斷升高,由此我們可以把循環訓練的次數設定在30左右,這樣得出的模型準確度最好。經過校驗數據的檢測後,我們可以調整各項參數,然後再把模型重新訓練一遍:

model = build_model()

model.fit(train_data, train_targets, epochs = 30, batch_size = 16, verbose = 0)

test_mse_score, test_mae_score = model.evaluate(test_data, test_targets)

print(test_mae_score)

上面代碼運行後,得到的結果為2.86,也就是說,我們模型對測試數據中位數的預測誤差在2860作用,由於測試數據的中位值在5000多,因此這個誤差還是比較大,主要原因在於數據量過小的原因。

從這個例子我們可以看到當我們預測的數據具有連續性,例如價格時,使用的損失函數是MSE,也叫均方誤差。matric要設置成’mae’,也就是平均絕對誤差。當訓練數據對應的特徵有不同單位時,一定要把他們進行規格化處理,例如我們訓練的數據中,有些數值對應房子的價格,有些數值對應房子的大小,兩種數據性質不同,單位不一樣,所以要分別進行規格化處理。

當可用於訓練的數據量不大時,我們可以使用k-fold校驗法,這樣能有效提升模型的準確度,同時當訓練數據量不足時,網絡的中間層要儘量少,這樣可以防止過度擬合。

這三節,我們用三個簡單的實例介紹了神經網絡在實際項目中的運用,從後面章節開始,我們將會慢慢的將網絡運用到更復雜的運用實例,例如圖像識別等應用中。更多詳細講解,請點擊鏈接。


分享到:


相關文章: