优达学城《DeepLearning》项目1:预测每日自行车租赁客流量
在這個項目中,你將建立你的第一個神經網絡,并用它來預測每日自行車租賃客流量。我們已經提供了一些代碼,但是將神經網絡的實現留給了您(大部分)。在您提交了這個項目之后,您可以更自由地探索數據和模型。
%matplotlib inline
%load_ext autoreload
%autoreload 2
%config InlineBackend.figure_format = 'retina'import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
加載數據
使用神經網絡的一個關鍵步驟是正確地準備數據。不同尺度上的變量使得網絡很難有效地學習正確的權值。下面,我們編寫了加載和準備數據的代碼。你很快就會了解更多!
觀察數據
此數據集包含2011年1月1日至2012年12月31日期間每天每小時的騎單車人數。騎單車人數分為臨時用戶和注冊用戶,匯總在cnt列中。您可以看到上面數據的前幾行。
以下是數據集中前10天左右騎自行車的人數(有些日子在數據集中沒有24個條目,所以不完全是10天。)你可以在這里看到每小時租金。這個數據相當復雜!周末有較低的所有乘客和有尖峰時,人們騎自行車上下班的一周。看看上面的數據,我們也有關于溫度、濕度和風速的信息,所有這些都可能會影響騎手的數量。你將試圖用你的模型捕捉所有這些。
虛擬變量
虛擬變量,也叫啞變量和離散特征編碼,可用來表示分類變量、非數量因素可能產生的影響。
這里我們有一些分類變量,比如季節,天氣,月份。要將這些包含在我們的模型中,我們需要生成二進制虛擬變量。多虧了Pandas庫中的get_dummies()。
dummy_fields = ['season', 'weathersit', 'mnth', 'hr', 'weekday']
for each in dummy_fields:dummies = pd.get_dummies(rides[each], prefix=each, drop_first=False)rides = pd.concat([rides, dummies], axis=1)fields_to_drop = ['instant', 'dteday', 'season', 'weathersit', 'weekday', 'atemp', 'mnth', 'workingday', 'hr']
data = rides.drop(fields_to_drop, axis=1)
data.head()
| ? | yr | holiday | temp | hum | windspeed | casual | registered | cnt | season_1 | season_2 | ... | hr_21 | hr_22 | hr_23 | weekday_0 | weekday_1 | weekday_2 | weekday_3 | weekday_4 | weekday_5 | weekday_6 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0.24 | 0.81 | 0.0 | 3 | 13 | 16 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 1 | 0 | 0 | 0.22 | 0.80 | 0.0 | 8 | 32 | 40 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 2 | 0 | 0 | 0.22 | 0.80 | 0.0 | 5 | 27 | 32 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 3 | 0 | 0 | 0.24 | 0.75 | 0.0 | 3 | 10 | 13 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 4 | 0 | 0 | 0.24 | 0.75 | 0.0 | 0 | 1 | 1 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
5 rows × 59 columns
縮放標簽變量
為了使網絡訓練更容易,我們將標準化每個連續變量。也就是說,我們將移動和縮放變量,使它們的平均值為零,標準偏差為1。
比例因子被保存,這樣當我們使用網絡進行預測時,我們就可以將結果復原。
quant_features = ['casual', 'registered', 'cnt', 'temp', 'hum', 'windspeed']
# Store scalings in a dictionary so we can convert back later
scaled_features = {}
for each in quant_features:mean, std = data[each].mean(), data[each].std()scaled_features[each] = [mean, std]data.loc[:, each] = (data[each] - mean)/std
將數據拆分為訓練集、測試集和驗證集
我們將保存過去大約21天的數據,以便在訓練完網絡后用作test集。我們將使用這一組來進行預測,并與實際的車手數量進行比較。
# Save data for approximately the last 21 days
test_data = data[-21*24:]# Now remove the test data from the data set
data = data[:-21*24]# Separate the data into features and targets
target_fields = ['cnt', 'casual', 'registered']
features, targets = data.drop(target_fields, axis=1), data[target_fields]
test_features, test_targets = test_data.drop(target_fields, axis=1), test_data[target_fields]
我們將把數據分成兩組,一組用于訓練,另一組用于在訓練網絡時進行驗證。因為這是時間序列數據,所以我們將訓練歷史數據,然后嘗試預測未來數據(驗證集)。
# Hold out the last 60 days or so of the remaining data as a validation set
train_features, train_targets = features[:-60*24], targets[:-60*24]
val_features, val_targets = features[-60*24:], targets[-60*24:]
構建網絡
下面你將建立你的神經網絡。我們已經建好了結構。您將通過網絡實現前向傳播和反向傳遞。您還將設置超參數:學習速率、隱藏單元數和訓練過程數。
網絡有兩層,一層是隱藏層,一層是輸出層。隱藏層將使用sigmoid函數進行激活。輸出層只有一個節點,用于回歸,節點的輸出與節點的輸入相同。也就是說,激活函數是 f(x)=x。接收輸入信號并生成輸出信號但考慮閾值的函數稱為激活函數。我們通過網絡的每一層計算每個神經元的輸出。一層的所有輸出都成為下一層神經元的輸入。這個過程稱為前向傳播。
在神經網絡中,我們使用權值將信號從輸入層傳播到輸出層。我們使用權值將誤差從輸出反向傳播回網絡,以更新權值。這叫做反向傳播。
下面,您有以下任務:
- 實現sigmoid函數以用作激活函數。在__init__中將self.activation_function設置為sigmoid函數。
- 在訓練中實現前向傳播。
- 在訓練中實現反向傳播算法,包括輸出誤差的計算。
- 在run方法中實現前向傳播。
#############
# In the my_answers.py file, fill out the TODO sections as specified
#############from my_answers import NeuralNetwork
my_answers.py內容如下:
import numpy as npclass NeuralNetwork(object):def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate):# Set number of nodes in input, hidden and output layers.self.input_nodes = input_nodesself.hidden_nodes = hidden_nodesself.output_nodes = output_nodes# Initialize weightsself.weights_input_to_hidden = np.random.normal(0.0, self.input_nodes**-0.5, (self.input_nodes, self.hidden_nodes))self.weights_hidden_to_output = np.random.normal(0.0, self.hidden_nodes**-0.5, (self.hidden_nodes, self.output_nodes))self.lr = learning_rate#### TODO: Set self.activation_function to your implemented sigmoid function ###### Note: in Python, you can define a function with a lambda expression,# as shown below.#self.activation_function = lambda x : 1/(1+np.exp(-x)) # Replace 0 with your sigmoid calculation.### If the lambda code above is not something you're familiar with,# You can uncomment out the following three lines and put your # implementation there instead.#def sigmoid(x):return 1 / (1 + np.exp(-x)) # Replace 0 with your sigmoid calculation hereself.activation_function = sigmoiddef train(self, features, targets):''' Train the network on batch of features and targets. Arguments---------features: 2D array, each row is one data record, each column is a featuretargets: 1D array of target values'''n_records = features.shape[0]delta_weights_i_h = np.zeros(self.weights_input_to_hidden.shape)delta_weights_h_o = np.zeros(self.weights_hidden_to_output.shape)for X, y in zip(features, targets):final_outputs, hidden_outputs = self.forward_pass_train(X) # Implement the forward pass function below# Implement the backproagation function belowdelta_weights_i_h, delta_weights_h_o = self.backpropagation(final_outputs, hidden_outputs, X, y, delta_weights_i_h, delta_weights_h_o)self.update_weights(delta_weights_i_h, delta_weights_h_o, n_records)def forward_pass_train(self, X):''' Implement forward pass here Arguments---------X: features batch'''#### Implement the forward pass here ####### Forward pass #### TODO: Hidden layer - Replace these values with your calculations.hidden_inputs = np.dot(X, self.weights_input_to_hidden) # signals into hidden layerhidden_outputs = self.activation_function(hidden_inputs) # signals from hidden layer# TODO: Output layer - Replace these values with your calculations.final_inputs = np.dot(hidden_outputs, self.weights_hidden_to_output) # signals into final output layerfinal_outputs = final_inputs # signals from final output layerreturn final_outputs, hidden_outputsdef backpropagation(self, final_outputs, hidden_outputs, X, y, delta_weights_i_h, delta_weights_h_o):''' Implement backpropagationArguments---------final_outputs: output from forward passy: target (i.e. label) batchdelta_weights_i_h: change in weights from input to hidden layersdelta_weights_h_o: change in weights from hidden to output layers'''#### Implement the backward pass here ####### Backward pass #### TODO: Output error - Replace this value with your calculations.error = y - final_outputs # Output layer error is the difference between desired target and actual output.# TODO: Calculate the hidden layer's contribution to the errorhidden_error = np.dot(error, self.weights_hidden_to_output.T )# TODO: Backpropagated error terms - Replace these values with your calculations.output_error_term = error * 1hidden_error_term = hidden_error * hidden_outputs * (1 - hidden_outputs)# TODO: Add Weight step (input to hidden) and Weight step (hidden to output).# Weight step (input to hidden)delta_weights_i_h += hidden_error_term * X[:, None]# Weight step (hidden to output)delta_weights_h_o += output_error_term * hidden_outputs[:, None]return delta_weights_i_h, delta_weights_h_odef update_weights(self, delta_weights_i_h, delta_weights_h_o, n_records):''' Update weights on gradient descent stepArguments---------delta_weights_i_h: change in weights from input to hidden layersdelta_weights_h_o: change in weights from hidden to output layersn_records: number of records'''self.weights_hidden_to_output += self.lr * delta_weights_h_o / n_records # update hidden-to-output weights with gradient descent stepself.weights_input_to_hidden += self.lr * delta_weights_i_h / n_records # update input-to-hidden weights with gradient descent stepdef run(self, features):''' Run a forward pass through the network with input features Arguments---------features: 1D array of feature values'''#### Implement the forward pass here ##### TODO: Hidden layer - replace these values with the appropriate calculations.hidden_inputs = np.dot(features, self.weights_input_to_hidden) # signals into hidden layerhidden_outputs = self.activation_function(hidden_inputs) # signals from hidden layer# TODO: Output layer - Replace these values with the appropriate calculations.final_inputs = np.dot(hidden_outputs, self.weights_hidden_to_output) # signals into final output layerfinal_outputs = final_inputs # signals from final output layer return final_outputs#########################################################
# Set your hyperparameters here
##########################################################
iterations = 100
learning_rate = 0.1
hidden_nodes = 2
output_nodes = 1
def MSE(y, Y):return np.mean((y-Y)**2)
單元測試
運行這些單元測試來檢查你網絡實現的正確性。這將有助于您在開始嘗試培訓網絡之前確保網絡已正確實施。這些測試都必須成功才能通過項目。
import unittestinputs = np.array([[0.5, -0.2, 0.1]])
targets = np.array([[0.4]])
test_w_i_h = np.array([[0.1, -0.2],[0.4, 0.5],[-0.3, 0.2]])
test_w_h_o = np.array([[0.3],[-0.1]])class TestMethods(unittest.TestCase):########### Unit tests for data loading##########def test_data_path(self):# Test that file path to dataset has been unalteredself.assertTrue(data_path.lower() == 'bike-sharing-dataset/hour.csv')def test_data_loaded(self):# Test that data frame loadedself.assertTrue(isinstance(rides, pd.DataFrame))########### Unit tests for network functionality##########def test_activation(self):network = NeuralNetwork(3, 2, 1, 0.5)# Test that the activation function is a sigmoidself.assertTrue(np.all(network.activation_function(0.5) == 1/(1+np.exp(-0.5))))def test_train(self):# Test that weights are updated correctly on trainingnetwork = NeuralNetwork(3, 2, 1, 0.5)network.weights_input_to_hidden = test_w_i_h.copy()network.weights_hidden_to_output = test_w_h_o.copy()network.train(inputs, targets)self.assertTrue(np.allclose(network.weights_hidden_to_output, np.array([[ 0.37275328], [-0.03172939]])))self.assertTrue(np.allclose(network.weights_input_to_hidden,np.array([[ 0.10562014, -0.20185996], [0.39775194, 0.50074398], [-0.29887597, 0.19962801]])))def test_run(self):# Test correctness of run methodnetwork = NeuralNetwork(3, 2, 1, 0.5)network.weights_input_to_hidden = test_w_i_h.copy()network.weights_hidden_to_output = test_w_h_o.copy()self.assertTrue(np.allclose(network.run(inputs), 0.09998924))suite = unittest.TestLoader().loadTestsFromModule(TestMethods())
unittest.TextTestRunner().run(suite)
結果:
訓練網絡
在這里您將設置網絡的超參數。這里的策略是找到某種超參數,使得訓練集上的誤差很小,但不會過度擬合數據。如果網絡訓練時間過長或隱藏節點過多,則它可能會變得過于擬合訓練集,并且無法推廣到驗證集。也就是說,驗證集上的損失將隨著訓練集損失的下降而開始增加。
您還將使用一種稱為隨機梯度下降(SGD)的方法來訓練網絡。其思想是,對于每個訓練過程,您將隨機獲取數據樣本,而不是使用整個數據集。這樣可以更有效地訓練網絡。稍后您將了解更多關于SGD的信息。
選擇迭代次數
這是我們將用于訓練網絡的訓練數據中的樣本批數。使用的迭代次數越多,模型就越擬合數據。但是,如果您使用太多的迭代,這個過程的回報會急劇減少,并且會浪費計算資源。您希望在這里找到一個數字,其中網絡具有較低的訓練損失,并且驗證集損失最小。理想的迭代次數應該是在驗證集loss不再減少后不久就停止。
選擇學習率
這將縮放權重更新的大小。如果這個值太大,權重就會爆炸,網絡無法擬合數據。通常一個好的選擇是從0.1開始;但是,如果您有效地將學習率除以n_records記錄,請嘗試從學習率為1開始。無論哪種情況,如果網絡在擬合數據時出現問題,請嘗試降低學習率。注意,學習率越低,權值更新的步長越小,神經網絡收斂的時間越長。
選擇隱藏節點數
在優化了所有權重的模型中,隱藏的節點越多,模型的預測就越精確(一個完全優化的模型的權重畢竟可以為零。)但是,隱藏的節點越多,優化模型權重就越困難,次優權重越有可能導致過度擬合。由于過度擬合,該模型將記憶訓練數據,而不是學習真實的分布情況,并且不能很好地推廣到未知數據。
嘗試幾個不同的數字,看看它是如何影響性能的。您可以在loss字典中查看網絡性能的度量。如果隱藏單元的數量太少,那么模型就沒有足夠的空間來學習,如果隱藏單元的數量太多,那么學習的方向就有太多的選擇。這里的訣竅是在你選擇的隱藏單位數量上找到正確的平衡。您通常會發現,要使用的最佳隱藏節點數最終介于輸入和輸出節點數之間。
import sys####################
### Set the hyperparameters in you myanswers.py file ###
####################from my_answers import iterations, learning_rate, hidden_nodes, output_nodesN_i = train_features.shape[1]
network = NeuralNetwork(N_i, hidden_nodes, output_nodes, learning_rate)losses = {'train':[], 'validation':[]}
for ii in range(iterations):# Go through a random batch of 128 records from the training data setbatch = np.random.choice(train_features.index, size=128)X, y = train_features.iloc[batch].values, train_targets.iloc[batch]['cnt']network.train(X, y)# Printing out the training progresstrain_loss = MSE(np.array(network.run(train_features)).T, train_targets['cnt'].values)val_loss = MSE(np.array(network.run(val_features)).T, val_targets['cnt'].values)sys.stdout.write("\rProgress: {:2.1f}".format(100 * ii/float(iterations)) \+ "% ... Training loss: " + str(train_loss)[:5] \+ " ... Validation loss: " + str(val_loss)[:5])sys.stdout.flush()losses['train'].append(train_loss)losses['validation'].append(val_loss)
結果:
可視化loss:
plt.plot(losses['train'], label='Training loss')
plt.plot(losses['validation'], label='Validation loss')
plt.legend()
_ = plt.ylim()
看看你的預測效果
在這里,使用test數據來查看您的網絡對數據建模的情況。
fig, ax = plt.subplots(figsize=(8,4))mean, std = scaled_features['cnt']
predictions = np.array(network.run(test_features)).T*std + mean
ax.plot(predictions[0], label='Prediction')
ax.plot((test_targets['cnt']*std + mean).values, label='Data')
ax.set_xlim(right=len(predictions))
ax.legend()dates = pd.to_datetime(rides.iloc[test_data.index]['dteday'])
dates = dates.apply(lambda d: d.strftime('%b %d'))
ax.set_xticks(np.arange(len(dates))[12::24])
_ = ax.set_xticklabels(dates[12::24], rotation=45)
可選練習:思考你的結果
回答以下關于你模型結果的問題:
- 模型對數據的預測能力如何?
- 哪里失敗了?
- 為什么它失敗了呢?
我的回答:
- 從loss下降曲線可看出,訓練步數遠遠不夠。
- 從訓練集和驗證集loss差異可看出,模型發生了過擬合。
- 模型預測的趨勢基本對了,但是預測的值和真實情況差異很大,這表示模型可能需要具備更復雜的表示能力,也即提升隱藏單元個數。
?
嘗試調整超參數:
- iterations?= 1000 → 3000
- learning_rate?= 0.1 → 0.4
- hidden_nodes?= 2 → 20
- output_nodes?=?1
前后對比結果:
其他嘗試:
- iterations?= 5000
- learning_rate?= ?0.8
- hidden_nodes?= 10
- output_nodes?=?1
Progress: 100.0% ... Training loss: 0.055 ... Validation loss: 0.144
從上圖可看出,loss=0.14的模型,在預測效果上并不一定會比loss為0.25的模型更好。
?
?
?
總結
以上是生活随笔為你收集整理的优达学城《DeepLearning》项目1:预测每日自行车租赁客流量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 原创:田径女运动员进军娱乐圈,吴艳妮录综
- 下一篇: 优达学城《DeepLearning》2-