你的第一個神經網絡——共享單車預測器

引言

本文是學習完集智學園《PyTorch入門課程:火炬上的深度學習》系列課以後的梳理。bash

該系列課程包含大量的實操任務,如文本分類,手寫數字識別,翻譯器,做曲機,AI遊戲等。而在實操中還融入了大量機器學習領域的基礎和經典知識,如反向傳播原理神經元原理剖析,NLP領域的RNNLSTMword2vec,以及圖像領域的遷移學習卷積神經網絡等等,很是適合想要入門機器學習的同窗,是當前網絡上絕無僅有的好課程。網絡

我的學完後受益不淺,在此真誠地向張江老師表示感謝數據結構

同系列文章:用線性迴歸擬合案例,透徹理解深度學習的反向傳播app

正文

本文的任務,是經過訓練一個簡單的神經網絡預測模型,實現了根據天氣,季節,日期,星期等自變量,去預測對應日期的單車數量。主要包含的知識點爲:數據預處理中的one-hot編碼歸一化操做,以及構建神經網絡的流程機器學習

任務中使用到的數據結構以下。數據已共享在網盤中ide

image

其中,最後一列cnt爲目標函數,即單車的數量。表中其餘數據,咱們選擇season, hr, mnth,weekday, weathersit, temp, hum,holiday, yr等做爲因變量,其餘數據拋棄不用。每條數據以小時爲單位,共約2w條數據,咱們選取部分繪圖看一下函數

image

整個任務實現流程能夠分爲如下三個步驟:學習

  1. 數據預處理
  2. 訓練模型
  3. 驗證模型

下面咱們開始任務測試

1. 數據預處理

數據預處理是整個建模步驟很是重要的環節,甚至會直接影響到訓練效果。在這個環節咱們的核心目標其實仍是圍繞一點:爲了讓訓練效果更好。優化

咱們觀察數據會發現,原始數據不夠好,直接拿來訓練的話,極可能會影響到訓練過程和最終的訓練效果。

那麼具體哪裏很差,咱們又該怎麼處理呢?

1.1. 對類型變量的處理:one-hot編碼

在咱們這個案例中,季節天氣等幾個變量都是咱們爲了平常生活方便而約定的規則,屬於類型變量,他們的數字大小,並無實際的意義,好比星期二並不表明它比星期一的值大,直接把大小輸入模型,會形成神經網絡的「誤解」:即值越大,會更強烈地影響網絡內部的變化。因此咱們須要對這些類型變量作二進制處理。

仍是拿星期舉例,咱們須要把星期的標識,所有轉化爲二進制編碼,哪一位爲1,就表明對應的類型(其實就至關於,會激活與之相關的神經元)

image

具體到代碼實現,pandas有現成的方法,幫助咱們快速生成one-hot編碼

dummies = pd.get_dummies(rides['weekday'], prefix='weekday', drop_first=False
複製代碼

one-hot編碼後的「星期

下面展現真實代碼。咱們要操做的對象是'season', 'weathersit', 'mnth', 'hr', 'weekday'這幾個屬性。處理完成後,將生成的數據再合併進原來的數據中 。最後,去掉原數據集中不相關的特徵

# 這是咱們要處理的特徵組
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() # 打印查看
複製代碼

one-hot編碼後的數據

完成類型編碼的轉化以後,先彆着急開始,咱們還有一步要作

1.2. 數據值歸一化

在不少時候,原是數據中不一樣單位的變量,他們的數值差別有可能會很是大。好比在咱們的案例中,溫度的範圍可能在0-50之間浮動,而溼度則是在0-1之間浮動的小數。而有時的初始數據甚至還會出現幾千幾萬的數量級。若是直接進行訓練,固然也不是不能夠,只不過神經網絡的權重會學習的很是慢,並且最終的效果可能也很差。

因此,咱們須要對不一樣單位的數值進行歸一化處理,歸一化的方式有不少,好比把數值按正態分佈轉化爲[-1,1]區間。在這裏使用經常使用的z-score標準化方法。

新數據 = (原數據 - 均值) / 標準差

# 標準化處理,這裏將目標數量也作了歸一化
quant_features = ['cnt', 'temp', 'hum', 'windspeed']
for each in quant_features:
    # 快速求均值和方差
    mean, std = data[each].mean(), data[each].std()
    # 選取列名爲each的特定列, 替換爲歸一化後的數據
    data.loc[:, each] = (data[each] - mean) / std
data.head() # 打印查看
複製代碼

歸一化結果

數據預處理環節就結束了,總結來看分爲兩點,這兩點也是絕大部分神經網絡訓練以前都須要進行的步驟。

  1. 類型變量值的大小可能會影響訓練效果,須要進行one-hot編碼處理
  2. 不一樣數據的尺度不一樣,須要進行歸一化處理,以提升訓練速度和訓練效果

下面,咱們就開始進入正式的訓練過程

2. 構建模型,開始訓練

使用pytorch構建網絡真是一件很爽的事情,你不須要關注梯度如何傳播,線性映射和非線性映射具體如何運做,你只須要按照pytorch的方法用幾行代碼構建模型,而後給他輸入,獲取輸出就能夠了。

這部分開始,代碼量就上升了,我會盡可能講的詳細,註釋也會盡量的全面

2.1. 構建輸入和目標函數

在這裏,咱們的輸入爲全部會影響當前單車數量的自變量,輸出爲對應的單車數量,固然,不要忘了分訓練集和測試集,訓練集用於訓練,測試集用於測試最終模型的效果。

target_fields = ['cnt']
# 訓練集,去掉最後21天的數據
train_data = data[:-21 * 24]
# 測試集,最後21天的數據
test_data = data[-21 * 24:]

# 定義輸入,和預測目標
features, targets = train_data.drop(target_fields, axis=1), train_data[target_fields]
test_features, test_target = test_data.drop(target_fields, axis=1), test_data[target_fields]
複製代碼

上面代碼定義的輸入和預測目標,仍是dataframe格式,咱們須要把數據轉化爲array或者tensor,這裏咱們轉成array格式。同時,爲了方便計算,須要調整目標函數的維度。從0維(數據量)轉化爲1維(數據量 x 1)

2.2. 構建模型

咱們的案例不須要使用到多複雜的模型,只須要確保在中間有一層非線性映射,用以擬合曲線便可,構建模型就是這麼簡單。

同時,pytorch也封裝了很多損失函數方法,在這裏使用簡單MSE模型,直接調用便可。在線性迴歸擬合案例,透徹理解深度學習的反向傳播一文中,有MSE的具體實現說明,很簡單,一看就懂了。

input_size = np.array(features).shape[1]
hidden_size = 10
output_size = 1
neu = torch.nn.Sequential(
    torch.nn.Linear(input_size, hidden_size), # 一層線性映射
    torch.nn.Sigmoid(), # 一層sigmoid非線性映射
    torch.nn.Linear(hidden_size, output_size) # 再一層線性映射
)
cost = torch.nn.MSELoss()
optimizer = torch.optim.SGD(neu.parameters(), lr = 0.01)
複製代碼

劃重點:

  1. torch.nn.Linear: 線性映射
  2. torch.nn.Sigmoid: 非線性映射,能夠擬合曲線
  3. torch.nn.MSELoss(): MSE損失函數
  4. torch.optim.SGD(neu.parameters(), lr = 0.01):參數優化器,能夠實現反向傳播過程,更新全部帶梯度的參數等功能,具體使用能夠繼續往下看。lr表明學習率

到這裏,模型已經構建好,下面進入訓練過程(抽象出模型後,全部的訓練過程都大同小異)

2.3 訓練過程

首先定義一個batch_size。至關於定義了,把原始數據以128條爲最小訓練單位進行訓練。

batch_size = 128

image

分batch是爲了充分訓練模型,並且增長數據的隨機性,效果會更好。好比咱們的20000條數據,不分批的話,那麼跑完20000條數據,模型才被訓練了一次,偏差及計算了一次,反向傳播了一次,權重調整了一次,這個效率就很低了;而若是把20000分紅一小撮,每一撮是一個完整的訓練過程,那麼效率就大大提升了。並且,這樣能夠增長訓練數據的隨機性。

接着,咱們就直接上代碼了。看着代碼量比較多,但其實很簡單。

Losses = []
for i in range(10000):
    batch_loss = []

    for start in range(0, len(X), batch_size):
        end = start + batch_size if start + batch_size < len(X) else len(X)
        # 獲取本批次的輸入和預測目標
        xx = torch.tensor(X[start: end, :], dtype=torch.float, requires_grad = True)
        yy =  torch.tensor(Y[start: end, :], dtype=torch.float, requires_grad = True)

        # 輸入數據,獲得預測值
        predict = neu(xx)

        # 使用損失函數比較預測數據和真實數據,獲得loss
        loss = cost(predict, yy)

        # 反向傳播,優化權重
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # 記錄loss
        batch_loss.append(loss)
    Losses.append(batch_loss[1:])
複製代碼

步驟說明:

  1. batch中獲取本批次的輸入xx和預測目標yy
  2. xx輸入前面構造好的神經網絡模型neu,獲得預測數值predict
  3. 在損失函數cost中對比預測值predict和真實值yy, 獲得損失值loss
  4. 在優化器optimizer中完成反向傳播,優化權重,清空梯度的操做
  5. 記錄loss

這裏經過監聽loss的變化來判斷訓練狀況,若是loss的變化趨勢是。一開始迅速降低,到必定值後趨於穩定再也不降低,一般就說明模型表現良好,訓練完成。

loss曲線

上圖是我記錄的,每一個batch最低的loss,能夠看到,大約在600輪訓練後,模型就趨於穩定。

這個時候,把最後一步的predict拿出來,和真實值yy作對比,會發現驚人的一致。

image

固然這裏的一致並不能說明什麼問題,訓練數據表現好不能證實模型效果好,這就好像你用A推演出B的結論,再用A去驗證B正確同樣荒謬。正確的作法是,咱們用和A有同樣特徵的C,去嘗試推演,看是否能得出B。

3. 驗證模型

驗證的步驟,就是把上面訓練過程最後一步的模型拿過來,放到測試集上跑一遍,觀察預測值和真實值的差別

對比test_predict和test_yy的差別

image

上圖觀察可知,不管是工做日仍是週末,也無論當天的天氣如何,咱們的預測基本可以貼近真實值。不過中間有幾天的數據差別較大,最後分析數據發現,那幾天是一個很是規的節假日,致使歷史數據不具備參考意義。

到這裏,整個訓練過程就完成了,咱們獲得了一個最簡單的模型,只要給定當天的氣溫,日期,星期等因素,它就能夠預測出當天的單車使用數量。

結束

感謝您看到這裏。

在從此的一段時間裏,我還會嘗試圖像識別,文本分類和翻譯,AI遊戲等真實案例。全部學習案例都來自張江老師的PyTorch與深度學習課程。

望與你們共勉。

相關文章
相關標籤/搜索