前沿:
先學習本篇文章之前,建議大家先學習我編寫的上一篇“使用Python從頭實現一個神經網絡”,再來學習學習本篇使用神經網絡進行房價預測。
介紹:
本次使用神經網絡進行房價的預測,利用數據樣本學習,得到相關因素預測房價。
數據介紹:
數據來源:
使用爬蟲工具爬取廣州某小區的售房信息。爬取到的數據如下:
選取變量
共有380條數據,七項指標,選取其中五項指標,分別爲總價,面積、房間數量、客廳數量、建造年份。
選取房子總價爲預測變量,面積、房間數量、客廳數量、建造年份爲輸入變量。
數據處理
對數據進行標準化,標準化方程如下:
其中,x'爲標準化後的數值,x爲原數值,爲該變量的平均值,爲變量的最大值。
例如:x代表總價變量,爲所有總價的平均值,也就是Excel中第一列的平均值,爲最高的總價。
神經網絡結構:
本次的選用的神經網絡結構如下:
我們的神經網絡結構較爲簡單,隱藏層爲一層,隱藏層節點數爲2,兩個偏置單元。
函數說明:
輸入層到隱藏層(sigmoid):
隱藏層到輸出層(迴歸函數):
代價函數:
之所以隱藏層到輸出層的函數改爲迴歸函數,是因爲我們不再使用神經網絡進行分類,而是預測。
分類問題得到的輸出結果是[0,1]之間,因此使用sigmoid函數;而我們預測的結果是一個大範圍的值。
比如我們預測到的房價可能是100萬,也可能是500萬,因此使用多元迴歸函數。
這麼做的意義,在於我們將原先的四個變量,重新轉化爲了隱藏層中的兩個節點。再利用兩個節點,進行多元線性迴歸。不再使用原先的四個變量直接多元迴歸,這也是隱藏層的意義。
ps:有人可能看過吳恩達的機器學習視頻(強烈推薦),代價函數使用的是另一種形式。那是因爲神經網絡用於分類時,輸出節點在[0,1]之間,使用那種形式可以學習的更好。
反向傳播和梯度下降:
由於上一篇已經將反向傳播和梯度下降講的很清楚了,因此不再重複。我們使用學習率爲α=0.01時,將380條數據迭代1000次,每10次輸出一次代價函數值,得到損失值和迭代次數如下圖:
可以看到,損失值在下降,說明學習率選取較好(幾次中比較好的一次)。
預測房價和實際房價對比
以380個數據樣本爲橫座標,房子總價爲縱座標。下圖中紅色散點爲數據樣本中真實房價,藍色散點爲預測房價,得到如下所示的散點圖。
可以看到,預測房價和實際房價大致呈現吻合關係,說明模型大致正確。
最後:
由於本人爲神經網絡的初學者, 可能存在解釋不清楚甚至錯誤的地方,希望大家多多指教。
如果大家想深入機器學習,可以觀看吳恩達的機器學習視頻,或者周志華的機器學習西瓜書(個人推薦)
我將我的數據和代碼放在了我的github上,大家如果需要數據和代碼,可以前往下載。
附件:代碼
- import numpy as np
- import pandas as pd
- from matplotlib import pyplot as plt
- def sigmoid(x):
- # 第一層到第二層的激活函數
- return 1 / (1 + np.exp(-x))
- def deriv_sigmoid(x):
- # 第一層到第二層的激活函數的求導函數
- fx = sigmoid(x)
- return fx * (1 - fx)
- def mse_loss(y_true, y_pred):
- # 使用方差作爲損失函數
- return ((y_true - y_pred) ** 2).mean()
- class OurNeuralNetwork:
- def __init__(self):
- # 第一層到第二層的函數
- self.w11 = np.random.normal()
- self.w12 = np.random.normal()
- self.w13 = np.random.normal()
- self.w14 = np.random.normal()
- self.w21 = np.random.normal()
- self.w22 = np.random.normal()
- self.w23 = np.random.normal()
- self.w24 = np.random.normal()
- # 第二層到第三層的函數
- self.w1 = np.random.normal()
- self.w2 = np.random.normal()
- # 截距項,Biases
- self.b1 = np.random.normal()
- self.b2 = np.random.normal()
- self.b3 = np.random.normal()
- def feedforward(self, x):
- # 前向傳播學習
- h1 = sigmoid(self.w11 * x[0] + self.w12 * x[1] + self.w13 * x[2] + self.w14 * x[3] + self.b1)
- h2 = sigmoid(self.w21 * x[0] + self.w22 * x[1] + self.w23 * x[2] + self.w24 * x[3] + self.b1)
- o1 = self.w1 * h1 + self.w2 * h2 + self.b3
- return o1
- #訓練函數
- def train(self, data, all_y_trues):
- learn_rate = 0.01 # 學習率
- epochs = 1000 # 訓練的次數
- # 畫圖數據
- self.loss = np.zeros(100)
- self.sum = 0;
- # 開始訓練
- for epoch in range(epochs):
- for x, y_true in zip(data, all_y_trues):
- # 計算h1
- h1 = sigmoid(self.w11 * x[0] + self.w12 * x[1] + self.w13 * x[2] + self.w14 * x[3] + self.b1)
- # 計算h2
- h2 = sigmoid(self.w21 * x[0] + self.w22 * x[1] + self.w23 * x[2] + self.w24 * x[3] + self.b2)
- #計算輸出節點
- y_pred = self.w1 * h1 + self.w2 * h2 + self.b3
- # 反向傳播計算導數
- d_L_d_ypred = -2 * (y_true - y_pred)
- d_ypred_d_w1 = h1
- d_ypred_d_w2 = h2
- d_ypred_d_b3 = 0
- d_ypred_d_h1 = self.w1
- d_ypred_d_h2 = self.w2
- sum_1=self.w11 * x[0] + self.w12 * x[1] + self.w13 * x[2] + self.w14 * x[3] + self.b1
- d_h1_d_w11 = x[0] * deriv_sigmoid(sum_1)
- d_h1_d_w12 = x[1] * deriv_sigmoid(sum_1)
- d_h1_d_w13 = x[2] * deriv_sigmoid(sum_1)
- d_h1_d_w14 = x[3] * deriv_sigmoid(sum_1)
- d_h1_d_b1 = deriv_sigmoid(sum_1)
- sum_2 = self.w21 * x[0] + self.w22 * x[1] + self.w23 * x[2] + self.w24 * x[3] + self.b2
- d_h1_d_w21 = x[0] * deriv_sigmoid(sum_2)
- d_h1_d_w22 = x[1] * deriv_sigmoid(sum_2)
- d_h1_d_w23 = x[2] * deriv_sigmoid(sum_2)
- d_h1_d_w24 = x[3] * deriv_sigmoid(sum_2)
- d_h1_d_b2 = deriv_sigmoid(sum_2)
- # 梯度下降法
- self.w11 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w11
- self.w12 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w12
- self.w13 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w13
- self.w14 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w14
- self.b1 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_b1
- self.w21 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h1_d_w21
- self.w22 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h1_d_w22
- self.w23 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h1_d_w23
- self.w24 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h1_d_w24
- self.b2 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h1_d_b2
- self.w1 -= learn_rate * d_L_d_ypred * d_ypred_d_w1
- self.w1 -= learn_rate * d_L_d_ypred * d_ypred_d_w2
- self.b3 -= learn_rate * d_L_d_ypred * d_ypred_d_b3
- if epoch % 10 == 0:
- y_preds = np.apply_along_axis(self.feedforward, 1, data)
- loss = mse_loss(all_y_trues, y_preds)
- print("Epoch %d loss: %.3f" % (epoch, loss))
- self.loss[self.sum] = loss
- self.sum = self.sum + 1
- # 文件的名字
- FILENAME = "../data.xlsx"
- # 禁用科學計數法
- pd.set_option('float_format', lambda x: '%.3f' % x)
- np.set_printoptions(suppress=True, threshold=np.nan)
- # 得到的DataFrame分別爲總價、面積、房間、客廳、年份
- data = pd.read_excel(FILENAME, header=0, usecols="A,D,H,I,J")
- # DataFrame轉化爲array
- DataArray = data.values
- Y = DataArray[:, 0]
- X = DataArray[:, 1:5]
- X = np.array(X)#轉化爲array,自變量
- Y = np.array(Y)#轉化爲array,因變量房價
- # 處理數據
- data = np.array(X)
- data_mean = np.sum(data, axis=0) / np.size(data, 0)
- data = (data - data_mean) / np.max(data)
- all_y_trues = np.array(Y)
- all_y_trues_mean = np.sum(all_y_trues) / np.size(all_y_trues)
- all_y_trues = (all_y_trues - all_y_trues_mean) / np.max(all_y_trues)
- # 訓練數據
- network = OurNeuralNetwork()
- network.train(data, all_y_trues)
- # 輸出神經網絡參數
- print("w11-->%.3f" % network.w11)
- print("w12-->%.3f" % network.w12)
- print("w13-->%.3f" % network.w13)
- print("w14-->%.3f" % network.w14)
- print("w21-->%.3f" % network.w21)
- print("w22-->%.3f" % network.w22)
- print("w23-->%.3f" % network.w23)
- print("w24-->%.3f" % network.w24)
- print("w1-->%.3f" % network.w1)
- print("w2-->%.3f" % network.w2)
- print("b1-->%.3f" % network.b1)
- print("b2-->%.3f" % network.b2)
- print("b3-->%.3f" % network.b3)
- # 標題顯示中文
- plt.rcParams['font.sans-serif']=['SimHei']
- plt.rcParams['axes.unicode_minus'] = False
- # 測試數據
- testData = np.array([99, 3, 2, 2014])
- testPrice = network.feedforward(testData)
- # 損失函數曲線圖
- plt.plot(np.arange(100), network.loss)
- plt.show()
- # 真實值與預測值對比
- y_preds = np.apply_along_axis(network.feedforward, 1, data)
- plt.plot(np.arange(380), all_y_trues,"r^")
- plt.plot(np.arange(380),y_preds,"bs")
- plt.title("紅色爲真實值,藍色爲預測值")
- plt.show()