使用機器學習預測天氣(第三部分神經網絡)

概述

  這是使用機器學習預測平均氣溫系列文章的最後一篇文章了,做爲最後一篇文章,我將使用google的開源機器學習框架tensorflow來構建一個神經網絡迴歸器。關於tensorflow的介紹、安裝、入門,請本身google,這裏就不作講述。html

  這篇文章我主要講解一下幾點:python

  • 瞭解人工神經網絡理論
  • tensorflow高級API:Estimators
  • 構建DNN模型預測天氣

人工神經網絡基礎理論

  上一篇文章主要講解了如何構建線性迴歸模型(這是最基礎的機器學習算法)來預測內布拉斯加州林肯市天天的平均氣溫。線性迴歸模型很是有效,而且能夠被用於數值化(好比分類)、預測(好比預測天氣)。線性迴歸算法也比較與侷限性,它要求數據之間具備線性關係。
  針對於非線性關係的場景,數據挖掘和機器學習有數不清的算法來處理。近些年最火的要數神經網絡算法了,它能夠處理機器學習領域的好多問題。神經網絡算法具有線性和非線性學習算法的能力。
  神經網絡受到大腦中的生物神經元的啓發,它們在複雜的交互網絡中工做,根據已經收集的信息的歷史來傳輸,收集和學習信息。咱們感興趣的計算神經網絡相似於大腦的神經元,由於它們是接收輸入信號(數字量)的神經元(節點)的集合,處理輸入並將處理後的信號發送給其餘下游代理 網絡。 信號做爲經過神經網絡的數字量的處理是一個很是強大的特徵,不限於線性關係。
  在這個系列中,我一直關注一種稱爲監督學習的特定類型的機器學習,也就說說訓練的數據結果是已知的,根據歷史已知的輸入和輸出,預測將來的輸入對應的輸出。 此外,預測的類型是數值的真實值,這意味着咱們使用的是迴歸預測算法。
  從圖形上看,相似於本文中描述的神經網絡如圖:

上面描述的神經網絡在最左邊包含一個輸入層,即圖中的x1和x2,這兩個特徵是神經網絡輸入值。這兩個特徵被輸入到神經網絡中,經過被稱爲隱藏層的兩層神經元進行處理和傳輸。這個描述顯示了兩個隱藏層,每層包含三個神經元(節點)。 該信號而後離開神經網絡,並做爲單個數值預測值彙總在輸出層。
  讓我花一點時間來解釋箭頭背後的含義,箭頭表示數據在層間從一個節點到另外一個節點的傳輸處理。 每一個箭頭表明一個數值的數學變換,從箭頭的底部開始,而後乘以特定於該路徑特定的權重。 一個圖層中的每一個節點將以這種方式獲得一個值。 而後彙總全部在節點收斂的值。 這個就是我以前提到的神經網絡的線性操做。

  在每一個節點上進行求和以後,將一個特殊的非線性函數應用到總和上,這在上面的圖像中被描述爲Fn(...)。 這種將非線性特徵引入神經網絡的特殊功能稱爲激活功能。 激活函數所帶來的這種非線性特性賦予了多層神經網絡以其功能。 若是不是將非線性加入到過程當中,則全部層都將有效地代數地組合成一個常數運算,其中包括將輸入乘以某個平坦係數值(即線性模型)。
好吧,這一切都很好,可是這怎麼轉化爲學習算法呢? 那麼最直接的答案就是評估正在進行的預測,即模型「y」的輸出,到實際預期值(目標),並對權重進行一系列調整,以改善總體 預測準確性。
  在迴歸機器學習算法的世界中,經過使用成本(又名「損失」或「客觀」)函數(即平方偏差之和(SSE))評估準確性。 請注意,我把這個聲明推廣到整個機器學習的連續體,而不只僅是神經網絡。 在前面的文章中,普通最小二乘算法完成了這一工做,它發現了使偏差平方和(即最小二乘)最小化的係數組合。
  咱們的神經網絡迴歸器會作一樣的事情。 它將迭代訓練數據提取特徵值,計算成本函數(使用SSE),並以最小化成本函數的方式調整權重。 經過算法迭代推送特徵的過程和評估如何根據成本函數來調整權重。
  模型優化算法在構建魯棒神經網絡中很是重要。 例如經過網絡體系結構(即寬度和深度)饋送,而後根據成本函數進行評估,調整權重。 當優化器函數肯定權重調整不會致使成本函數計算代價的變化,該模型被認爲是「學習」。算法

TensorFlow Estimator API

  tensorflow由好幾個部分組成,其中最經常使用的是Core API,它爲用戶提供了一套低級別的API來定義和訓練使用符號操做的任何機器學習算法。這也是TensorFlow的核心功能,雖然Core API能應對大多數的應用場景,但我更關注Estimator API。
  TensorFlow團隊開發了Estimator API,使平常開發人員能夠更方便地使用該庫。這個API提供了訓練模型、評估模型、以及和Sci-Kit庫類似的對未知數據的預測接口,這是經過實現各類算法的通用接口來實現的。另外,構建在高級API中的是機器學習最佳實踐,抽象和可伸縮性的負載。
  全部這些機器學習的優勢使得基礎Estimator類中實現的一套工具以及多個預先封裝的模型類型,下降了使用TensorFlow的入門門檻,所以能夠應用於平常問題。經過抽象出諸如編寫訓練循環或處理會話之類的問題,開發人員可以專一於更重要的事情,如快速嘗試多個模型和模型架構,以找到最適合他們須要的模型。
  在這篇文章中,我將介紹如何使用很是強大的深度神經網絡估計器之一DNN Regressor。數組

創建一個DNNRegressor來預測天氣

  咱們先導入一些咱們須要用到的庫。安全

import pandas as pd  
import numpy as np  
import tensorflow as tf  
from sklearn.metrics import 
explained_variance_score, \  
    mean_absolute_error, \
    median_absolute_error
from sklearn.model_selection import train_test_split

咱們來處理一下數據,全部的數據我都放在了Github上,你們能夠去查閱clone。網絡

# read in the csv data into a pandas data frame and set the date as the index
df = pd.read_csv('end-part2_df.csv').set_index('date')

# execute the describe() function and transpose the output so that it doesn't overflow the width of the screen
df.describe().T


# execute the info() function
df.info()
<class 'pandas.core.frame.DataFrame'>  
Index: 997 entries, 2015-01-04 to 2017-09-27  
Data columns (total 39 columns):  
meantempm          997 non-null int64  
maxtempm           997 non-null int64  
mintempm           997 non-null int64  
meantempm_1        997 non-null float64  
meantempm_2        997 non-null float64  
meantempm_3        997 non-null float64  
meandewptm_1       997 non-null float64  
meandewptm_2       997 non-null float64  
meandewptm_3       997 non-null float64  
meanpressurem_1    997 non-null float64  
meanpressurem_2    997 non-null float64  
meanpressurem_3    997 non-null float64  
maxhumidity_1      997 non-null float64  
maxhumidity_2      997 non-null float64  
maxhumidity_3      997 non-null float64  
minhumidity_1      997 non-null float64  
minhumidity_2      997 non-null float64  
minhumidity_3      997 non-null float64  
maxtempm_1         997 non-null float64  
maxtempm_2         997 non-null float64  
maxtempm_3         997 non-null float64  
mintempm_1         997 non-null float64  
mintempm_2         997 non-null float64  
mintempm_3         997 non-null float64  
maxdewptm_1        997 non-null float64  
maxdewptm_2        997 non-null float64  
maxdewptm_3        997 non-null float64  
mindewptm_1        997 non-null float64  
mindewptm_2        997 non-null float64  
mindewptm_3        997 non-null float64  
maxpressurem_1     997 non-null float64  
maxpressurem_2     997 non-null float64  
maxpressurem_3     997 non-null float64  
minpressurem_1     997 non-null float64  
minpressurem_2     997 non-null float64  
minpressurem_3     997 non-null float64  
precipm_1          997 non-null float64  
precipm_2          997 non-null float64  
precipm_3          997 non-null float64  
dtypes: float64(36), int64(3)  
memory usage: 311.6+ KB

  請注意,咱們剛剛記錄下了1000個氣象數據記錄,而且全部的特徵都是數字性質的。 另外,因爲咱們在第一篇文章中的努力工做,全部記錄都是完整的,由於它們不缺乏任何值(沒有非空值)。
  如今我將刪除「mintempm」和「maxtempm」這兩列,由於它們對幫助咱們預測平均溫度毫無心義。 咱們正在試圖預測將來,因此咱們顯然不能掌握有關將來的數據。 我還將從目標(y)中分離出特徵(X)。session

# First drop the maxtempm and mintempm from the dataframe
df = df.drop(['mintempm', 'maxtempm'], axis=1)

# X will be a pandas dataframe of all columns except meantempm
X = df[[col for col in df.columns if col != 'meantempm']]

# y will be a pandas series of the meantempm
y = df['meantempm']

  和全部監督機器學習應用程序同樣,我將把個人數據集分紅訓練集和測試集。 可是,爲了更好地解釋訓練這個神經網絡的迭代過程,我將使用一個額外的數據集,我將其稱爲「驗證集合」。 對於訓練集,我將利用80%的數據,對於測試和驗證集,它們將分別爲剩餘數據的10%。爲了分解這些數據,我將再次使用Scikit Learn 庫的train_test_split()函數。數據結構

# split data into training set and a temporary set using sklearn.model_selection.traing_test_split
X_train, X_tmp, y_train, y_tmp = train_test_split(X, y, test_size=0.2, random_state=23)  
# take the remaining 20% of data in X_tmp, y_tmp and split them evenly
X_test, X_val, y_test, y_val = train_test_split(X_tmp, y_tmp, test_size=0.5, random_state=23)

X_train.shape, X_test.shape, X_val.shape  
print("Training instances   {}, Training features   {}".format(X_train.shape[0], X_train.shape[1]))  
print("Validation instances {}, Validation features {}".format(X_val.shape[0], X_val.shape[1]))  
print("Testing instances    {}, Testing features    {}".format(X_test.shape[0], X_test.shape[1]))  
Training instances   797, Training features   36  
Validation instances 100, Validation features 36  
Testing instances    100, Testing features    36

構建神經網絡模型時要採起的第一步是實例化tf.estimator.DNNRegressor()類。類的構造函數有多個參數,但我將重點關注如下參數:架構

  • feature_columns:一種相似列表的結構,包含要輸入到模型中的要素的名稱和數據類型的定義
  • hidden_​​units:一個相似列表的結構,包含神經網絡數量寬度和深度的定義
  • optimizer:tf.Optimizer子類的一個實例,在訓練期間優化模型的權重;它的默認值是AdaGrad優化器。
  • activation_fn:激活功能,用於在每一層向網絡引入非線性;默認是ReLU
  • model_dir:要建立的目錄,其中包含模型的元數據和其餘檢查點保存

我將首先定義一個數字特徵列的列表。要作到這一點,我使用tf.feature_column.numeric_column()函數返回一個FeatureColumn實例。app

feature_cols = [tf.feature_column.numeric_column(col) for col in X.columns]

  使用定義的特性列,我如今能夠實例化DNNRegressor類並將其存儲在迴歸變量中。 我指定我想要一個有兩層深度的神經網絡,其中兩層的寬度都是50個節點。 我還指出,我但願個人模型數據存儲在一個名爲tf_wx_model的目錄中。

regressor = tf.estimator.DNNRegressor(feature_columns=feature_cols,  
                                      hidden_units=[50, 50],
                                      model_dir='tf_wx_model')
INFO:tensorflow:Using default config.  
INFO:tensorflow:Using config: {'_tf_random_seed': 1, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_model_dir': 'tf_wx_model', '_log_step_count_steps': 100, '_keep_checkpoint_every_n_hours': 10000, '_save_summary_steps': 100, '_keep_checkpoint_max': 5, '_session_config': None}

接下來我想要作的是定義一個可重用的函數,這個函數一般被稱爲「輸入函數」,我將調用wx_input_fn()。 這個函數將被用來在訓練和測試階段將數據輸入到個人神經網絡中。有許多不一樣的方法來創建輸入函數,但我將描述如何定義和使用一個基於tf.estimator.inputs.pandas_input_fn(),由於個人數據是在一個pandas數據結構。

def wx_input_fn(X, y=None, num_epochs=None, shuffle=True, batch_size=400):  
    return tf.estimator.inputs.pandas_input_fn(x=X,
                                               y=y,
                                               num_epochs=num_epochs,
                                               shuffle=shuffle,
                                               batch_size=batch_size)

  請注意,這個wx_input_fn()函數接受一個必選參數和四個可選參數,而後將這些參數交給TensorFlow輸入函數,專門用於返回的pandas數據。 這是TensorFlow API一個很是強大的功能。函數的參數定義以下:

  • X:輸入要輸入到三種DNNRegressor接口方法中的一種(訓練,評估和預測)
  • y:X的目標值,這是可選的,不會被提供給預測調用
  • num_epochs:可選參數。 當算法在整個數據集上執行一次時,就會出現一個新紀元。
  • shuffle:可選參數,指定每次執行算法時是否隨機選擇數據集的批處理(子集)
  • batch_size:每次執行算法時要包含的樣本數

  經過定義咱們的輸入函數,咱們如今能夠訓練咱們基於訓練數據集上的神經網絡。 對於熟悉TensorFlow高級API的讀者,您可能會注意到我對本身的模型的培訓方式有點不合常規。至少從TensorFlow網站上的當前教程和網絡上的其餘教程的角度來看。一般狀況下,您將看到以下所示的內容。

regressor.train(input_fn=input_fn(training_data, num_epochs=None, shuffle=True), steps=some_large_number)

.....
lots of log info  
....

而後,做者將直接展現evaluate()函數,而且幾乎沒有提示它在作什麼或爲何存在這一行代碼。

regressor.evaluate(input_fn=input_fn(eval_data, num_epochs=1, shuffle=False), steps=1)

.....
less log info  
....

在此以後,假設全部的訓練模型都是完美的,他們會直接跳到執行predict()函數。

predictions = regressor.predict(input_fn=input_fn(pred_data, num_epochs=1, shuffle=False), steps=1)

  我但願可以提供一個合理的解釋,說明如何訓練和評估這個神經網絡,以便將這個模型擬合或過分擬合到訓練數據上的風險降到最低。所以,咱們再也不拖延,讓我定義一個簡單的訓練循環,對訓練數據進行訓練,按期對評估數據進行評估。

evaluations = []  
STEPS = 400  
for i in range(100):  
    regressor.train(input_fn=wx_input_fn(X_train, y=y_train), steps=STEPS)
    evaluations.append(regressor.evaluate(input_fn=wx_input_fn(X_val,
                                                               y_val,
                                                               num_epochs=1,
                                                               shuffle=False)))
INFO:tensorflow:Create CheckpointSaverHook.  
INFO:tensorflow:Saving checkpoints for 1 into tf_wx_model/model.ckpt.  
INFO:tensorflow:step = 1, loss = 1.11335e+07  
INFO:tensorflow:global_step/sec: 75.7886  
INFO:tensorflow:step = 101, loss = 36981.3 (1.321 sec)  
INFO:tensorflow:global_step/sec: 85.0322  
... A WHOLE LOT OF LOG OUTPUT ...
INFO:tensorflow:step = 39901, loss = 5205.02 (1.233 sec)  
INFO:tensorflow:Saving checkpoints for 40000 into tf_wx_model/model.ckpt.  
INFO:tensorflow:Loss for final step: 4557.79.  
INFO:tensorflow:Starting evaluation at 2017-12-05-13:48:43  
INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000  
INFO:tensorflow:Evaluation [1/1]  
INFO:tensorflow:Finished evaluation at 2017-12-05-13:48:43  
INFO:tensorflow:Saving dict for global step 40000: average_loss = 10.2416, global_step = 40000, loss = 1024.16  
INFO:tensorflow:Starting evaluation at 2017-12-05-13:48:43  
INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000  
INFO:tensorflow:Finished evaluation at 2017-12-05-13:48:43  
INFO:tensorflow:Saving dict for global step 40000: average_loss = 10.2416, global_step = 40000, loss = 1024.16

  上面的循環迭代了100次。 在循環體中,我調用了迴歸器對象的train()方法,並將其傳遞給了個人可重用的wx_input_fn(),後者又經過了個人訓練功能集和目標。 我有意地將默認參數num_epochs等於None,基本上這樣說:「我不在意你經過訓練集多少次,只是繼續訓練算法對每一個默認batch_size 400」(大約一半的訓練 組)。 我還將shuffle參數設置爲默認值True,以便在訓練時隨機選擇數據以免數據中的任何順序關係。 train()方法的最後一個參數是我設置爲400的步驟,這意味着訓練集每一個循環將被批處理400次。
  這給了我一個很好的時間以更具體的數字來解釋一個epoch的意義。 回想一下,當一個訓練集的全部記錄都經過神經網絡訓練一次時,就會出現一個epoch。 因此,若是咱們的訓練集中有大約800(準確的說是797)個記錄,而且每一個批次選擇400個,那麼每兩個批次咱們就完成了一個時間。 所以,若是咱們遍歷整個訓練集100個迭代400個步驟,每一個批次大小爲400(每一個批次的一個半個時間),咱們獲得:

(100 x 400 / 2) = 20,000 epochs

  如今你可能想知道爲何我爲循環的每次迭代執行和evaluate()方法,並在列表中捕獲它的輸出。 首先讓我解釋一下每次train()方法被觸發時會發生什麼。它隨機選擇一批訓練記錄,並經過網絡推送,直到作出預測,併爲每條記錄計算損失函數。 而後根據計算出的損失根據優化器的邏輯調整權重,這對於減小下一次迭代的總體損失的方向作了很好的調整。 通常而言,只要學習速率足夠小,這些損失值隨着時間的推移而逐漸降低。
  然而,通過必定數量的這些學習迭代以後,權重開始不只受到數據總體趨勢的影響,並且還受到非實際的噪聲在全部實際數據中的繼承。 在這一點上,網絡受到訓練數據特性的過分影響,而且變得沒法推廣關於整體數據的預測。這與我以前提到的高級TensorFlow API許多其餘教程不足之處有關。 在訓練期間週期性地打破這一點很是重要,並評估模型如何推廣到評估或驗證數據集。 經過查看第一個循環迭代的評估輸出,讓咱們花些時間看看evaluate()函數返回的結果。

evaluations[0]  
{'average_loss': 31.116383, 'global_step': 400, 'loss': 3111.6382}

正如你所看到的,它輸出的是平均損失(均方偏差)和訓練中的步驟的總損失(平方偏差和),這一步是第400步。 在訓練有素的網絡中,你一般會看到一種趨勢,即訓練和評估損失或多或少地平行降低。 然而,在某個時間點的過分配置模型中,實際上在過擬合開始出現的地方,驗證訓練集將再也不看到其evaluate()方法的輸出下降。 這是你想中止進一步訓練模型的地方,最好是在變化發生以前。
  如今咱們對每一個迭代都有一個評估集合,讓咱們將它們做爲訓練步驟的函數來繪製,以確保咱們沒有過分訓練咱們的模型。 爲此,我將使用matplotlib的pyplot模塊中的一個簡單的散點圖。

import matplotlib.pyplot as plt  
%matplotlib inline

# manually set the parameters of the figure to and appropriate size
plt.rcParams['figure.figsize'] = [14, 10]

loss_values = [ev['loss'] for ev in evaluations]  
training_steps = [ev['global_step'] for ev in evaluations]

plt.scatter(x=training_steps, y=loss_values)  
plt.xlabel('Training steps (Epochs = steps / 2)')  
plt.ylabel('Loss (SSE)')  
plt.show()

  從上面的圖表看來,在全部這些迭代以後,我並無過分配置模型,由於評估損失歷來沒有呈現出朝着增長價值的方向的顯着變化。如今,我能夠安全地繼續根據個人剩餘測試數據集進行預測,並評估模型如何預測平均天氣溫度。
  與我已經演示的其餘兩種迴歸方法相似,predict()方法須要input_fn,我將使用可重用的wx_input_fn()傳遞input_fn,將測試數據集交給它,將num_epochs指定爲None,shuffle爲False,所以它將依次送入全部的數據進行測試。
  接下來,我作一些從predict()方法返回的dicts迭代的格式,以便我有一個numpy的預測數組。而後,我使用sklearn方法explain_variance_score(),mean_absolute_error()和median_absolute_error()來預測數組,以測量預測與已知目標y_test的關係。

pred = regressor.predict(input_fn=wx_input_fn(X_test,  
                                              num_epochs=1,
                                              shuffle=False))
predictions = np.array([p['predictions'][0] for p in pred])

print("The Explained Variance: %.2f" % explained_variance_score(  
                                            y_test, predictions))  
print("The Mean Absolute Error: %.2f degrees Celcius" % mean_absolute_error(  
                                            y_test, predictions))  
print("The Median Absolute Error: %.2f degrees Celcius" % median_absolute_error(  
                                            y_test, predictions))
INFO:tensorflow:Restoring parameters from tf_wx_model/model.ckpt-40000  
The Explained Variance: 0.88  
The Mean Absolute Error: 3.11 degrees Celcius  
The Median Absolute Error: 2.51 degrees Celcius

我已經使用了與上一篇文章有關的線性迴歸技術相同的指標,以便咱們不只能夠評估這個模型,還能夠對它們進行比較。 正如你所看到的,這兩個模型的表現至關相似,更簡單的線性迴歸模型略好一些。然而,你能夠經過修改學習速率,寬度和深度等參數來優化機器學習模型。

總結

  本文演示瞭如何使用TensorFlow高級API Estimator子類DNNRegressor。而且,我也描述了神經網絡理論,他們是如何被訓練的,以及在過程當中認識到過分擬合模型的危險性的重要性。
  爲了演示這個創建神經網絡的過程,我創建了一個模型,可以根據本系列第一篇文章收集的數字特徵預測次日的平均溫度。寫這些文章的目的不是爲了創建一個很是好的模型預測天氣,個人目標是:

  • 演示從數據收集,數據處理,探索性數據分析,模型選擇,模型構建和模型評估中進行分析(機器學習,數據科學,不管...)項目的通常過程。
  • 演示如何使用兩個流行的Python庫StatsModels和Scikit Learn來選擇不違反線性迴歸技術關鍵假設的有意義的功能。
  • 演示如何使用高級別的TensorFlow API,並直觀地瞭解全部這些抽象層下正在發生的事情。
  • 討論與過分擬合模型相關的問題。
  • 解釋試驗多個模型類型以最好地解決問題。

相關文章

使用機器學習預測天氣第二部分
使用機器學習預測天氣第一部分
英文原文

來獲取我更多有趣的博文

相關文章
相關標籤/搜索