2017 年 10 月

第 32 卷,第 10 期

本文章是由機器翻譯。

測試回合 - 使用 C# 類神經網路的時間序列迴歸

James McCaffrey

James McCaffrey時間序列迴歸問題的目標是根據歷程記錄的時間資料進行預測。例如,如果您有每月銷售資料 (透過年份或兩個課程) 時,您可以預測下個月份的銷售。時間序列迴歸是通常非常困難,而且有許多不同的技術,您可以使用。

在本文中,我將示範如何執行時間序列迴歸分析使用輪流視窗與類神經網路結合的資料。這個概念最佳範例所說明。示範程式中查看圖 1。示範程式會分析 airline 乘客人員旅行 1949 年 1 月和年 12 月 1960年之間的每個月的數目。

示範循環視窗時間序列迴歸

圖 1 輪流視窗時間序列迴歸示範

示範資料來自您可以在網際網路上的許多地方找到的已知基準測試資料集,而且在 cluded 本文章的下載一起。未經處理資料看起來像:

"1949-01";112
"1949-02";118
"1949-03";132
"1949-04";129
"1949-05";121
"1949-06";135
"1949-07";148
"1949-08";148
...
"1960-11";390
"1960-12";432

沒有 144 未經處理資料的項目。第一個欄位是年份和月份。第二個欄位是國際 airline 乘客總數的月份,以千為單位。示範建立定型資料來產生 140 定型項目使用大小為 4 輪流視窗。每個旅客計數除以 100 都被正規化定型資料:

[  0]   1.12   1.18   1.32   1.29   1.21
[  1]   1.18   1.32   1.29   1.21   1.35
[  2]   1.32   1.29   1.21   1.35   1.48
[  3]   1.29   1.21   1.35   1.48   1.48
...
[139]   6.06   5.08   4.61   3.90   4.32

請注意,會移除資料中的明確時間值。第一個視窗是由第四個旅客計數 1.12、 1.18、 1.32 版 (1.29),可做預測值,後面接著第五個計數 (1.21),也就是要預測的值所組成。下一個視窗是由第二到第五個計數 1.18、 1.32 版、 1.29 (1.21),也就是下一組預測值,後面接著第六個計數 (1.35),要預測的值所組成。簡單地說,每一組的四個連續旅客計數用來預測下一步的計數。

示範四個輸入的節點、 12 隱藏的處理節點與單一輸出節點建立類神經網路。輸入節點的數目與循環的視窗中的預測值的數目。視窗大小必須是防止他人探索由試驗,這是這項技術的最大缺點。類神經網路,隱藏節點的數目必須也取決於試驗,這一律是適用於類神經網路。只有一個輸出節點的原因是時間序列迴歸,預測一個時間單位繼續進行。

類神經網路具有 (4 * 12) + (12 * 1) = 60 節點對節點加權和 (12 + 1) = 13 徵才偏差,其本質上定義的類神經網路模型。示範程式使用循環視窗資料,定型使用基本的隨機後傳播演算法搭配設定為 0.01 到固定的數目的反覆項目設定為 10000 學習速度的網路。

在訓練期間,這個示範顯示預測的輸出值與正確的輸出值,每個 2000 反覆項目之間的平均平方的誤差。定型錯誤會使解譯更為困難,並監視大部分以查看是否真的奇怪發生 (這是很常見)。在此情況下,錯誤似乎穩定後約 4,000 反覆項目。

定型之後, 示範程式碼顯示 73 加權和徵才偏差值,再次大多是例行性檢查。時間序列迴歸問題,您通常必須使用自訂的精確度度量。在這裡,正確預測一個其中,非正規化的預測的旅客計數是加號或減號 30 於實際計數。與該定義中,示範程式達成 91.43 百分比精確度,也就是正確的 128 和 12 140 預測的旅客計數的錯誤。

示範結束時,會使用定型類神經網路年 1 月 1961,定型資料的範圍超過第一個時間週期的預測旅客計數。這稱為外推。預測是 433 開心。無法使用該值做為預測量變數,預測年 2 月 1961年等等。

本文假設您有中繼或更高版本程式設計技巧,並有基本知識的類神經網路的運作方式,但不會假設您有任何了解時間序列迴歸。使用 C# 程式碼示範程式,但您不應該有太多重構另一個語言,例如 Java 或 Python 程式碼的問題。示範親字母組太長而無法完整顯示,但檔案中有可用的完整原始程式碼下載該 accompa nies 這篇文章。

時間序列迴歸

時間序列迴歸問題通常顯示使用中的一個例如折線圖圖 2。藍色列表示 144 實際、 未正規化,旅客計數以千為單位,從透過年 12 月 1960 年 1 月 1949年。類神經網路時間序列模型所產生的光線紅線表示預測的旅客計數。請注意,因為此模型會使用循環視窗具有四個預測量值,第一個預測旅客計數不會發生直到月 = 5。此外,我會針對定型資料的範圍之外的九個月進行預測。這些會以紅色虛線。

時間序列迴歸線圖

圖 2 時間序列迴歸線圖

除了定型資料範圍以外的時間進行預測,時間序列迴歸分析可用來識別異常資料點。這並不會發生與示範旅客計數資料 — 您可以查看預測非常符合實際計數的計數。例如,月份 t 的實際旅客計數 = 67 為 302 (藍色附近的中心點圖 2) 和預測的計數是 272。但假設月份 t 實際計數 = 67 已 400。會有明顯的視覺指示,月份 67 的實際計數已極端值。

您也可以用於以程式設計方式找出異常資料時間序列迴歸。比方說,您可以加上旗標其中實際資料值與預測的值不一致的多個部分固定 thresh 舊,任何時間值例如四倍之標準差的預測與實際資料值。

示範程式

程式碼示範程式,我啟動 Visual Studio 建立的新 C# 主控台應用程式並將它命名為類神經 TimeSeries。我使用 Visual Studio 2015,但示範程式沒有顯著的.NET Framework 相依性,因此任何新的版本將可以正常運作。

將範本程式碼載入到編輯器視窗之後,我以滑鼠右鍵按一下方案總管] 中 win d 中的 Program.cs 檔案並重新將檔案命名為 NeuralTimeSeriesProgram.cs,然後允許 Visual Studio 會自動重新命名類別為我親字母組。在範本產生的程式碼頂端,我刪除所有不必要使用陳述式,留下只有一個參考最上層系統命名空間。

中會顯示整體的程式結構,以節省空間,少數稍加編輯圖 3

圖 3 NeuralTimeSeries 程式結構

using System;
namespace NeuralTimeSeries
{
  class NeuralTimeSeriesProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Begin times series demo");
      Console.WriteLine("Predict airline passengers ");
      Console.WriteLine("January 1949 to December 1960 ");

      double[][] trainData = GetAirlineData();
      trainData = Normalize(trainData);
      Console.WriteLine("Normalized training data:");
      ShowMatrix(trainData, 5, 2, true);  // first 5 rows

      int numInput = 4; // Number predictors
      int numHidden = 12;
      int numOutput = 1; // Regression

      Console.WriteLine("Creating a " + numInput + "-" + numHidden +
        "-" + numOutput + " neural network");
      NeuralNetwork nn = new NeuralNetwork(numInput, numHidden,
        numOutput);

      int maxEpochs = 10000;
      double learnRate = 0.01;
      double[] weights = nn.Train(trainData, maxEpochs, learnRate);
      
      Console.WriteLine("Model weights and biases: ");
      ShowVector(weights, 2, 10, true);

      double trainAcc = nn.Accuracy(trainData, 0.30); 
      Console.WriteLine("\nModel accuracy (+/- 30) on training " +
        "data = " + trainAcc.ToString("F4"));

      double[] future = new double[] { 5.08, 4.61, 3.90, 4.32 };
      double[] predicted = nn.ComputeOutputs(future); 
      Console.WriteLine("January 1961 (t=145): ");
      Console.WriteLine((predicted[0] * 100).ToString("F0"));

      Console.WriteLine("End time series demo ");
      Console.ReadLine();
    } // Main

    static double[][] Normalize(double[][] data) { . . }

    static double[][] GetAirlineData() {. . }

    static void ShowMatrix(double[][] matrix, int numRows,
      int decimals, bool indices) { . . }

    static void ShowVector(double[] vector, int decimals,
      int lineLen, bool newLine) { . . }

  public class NeuralNetwork { . . }
} // ns

示範使用簡單單一隱藏層類神經網路,重新實作。或者,您可以使用類神經網路程式庫如 Microsoft 認知 Toolkit (CNTK) 以及本文章中呈現的技術。

示範開始藉由設定定型資料中所示圖 4

圖 4 設定定型資料

double[][] trainData = GetAirlineData();
trainData = Normalize(trainData);
Console.WriteLine("Normalized training data:");
ShowMatrix(trainData, 5, 2, true);

Method GetAirlineData is defined as:

static double[][] GetAirlineData()
{
  double[][] airData = new double[140][];
  airData[0] = new double[] { 112, 118, 132, 129, 121 };
  airData[1] = new double[] { 118, 132, 129, 121, 135 };
...
  airData[139] = new double[] { 606, 508, 461, 390, 432 };
  return airData;
}

此處的循環視窗資料是硬式編碼與視窗大小為 4。如果之前撰寫時間序列程式,請撰寫簡短的公用程式來產生循環視窗資料從原始資料。在大部分的非示範案例,您會從文字檔讀取未經處理資料,並以程式設計方式產生循環視窗的資料,其中視窗大小參數化,您可以試驗不同的大小。

只要正常化方法會將所有資料值都除以常數 100 中。我沒有單純的實際原因。我的第一個在-tempts 非常不良的結果,會造成非標準化資料原本正規化之後, 我的結果比較好。理論上,您的資料時使用的類神經網路,不需要正規化,但實際上的正規化通常會很大的差異。

類神經網路建立如下所示:

int numInput = 4; 
int numHidden = 12;
int numOutput = 1; 
NeuralNetwork nn =
  new NeuralNetwork(numInput, numHidden, numOutput);

輸入節點的數目是設定為四個,因為每個循環的視窗有四個預測量值。輸出節點的數目是設定為其中一個,因為每一組視窗值用來進行預測下個月。隱藏節點的數目設定為 12,並已由試驗。

類神經網路是定型和評估這些陳述式:

int maxEpochs = 10000;
double learnRate = 0.01;
double[] weights = nn.Train(trainData, maxEpochs, learnRate);
ShowVector(weights, 2, 10, true);

定型方法會使用基本後傳播。有許多變化,包括使用動量或適應性學習速率來增加定型速度和使用 L1 或 L2 正則化或中輟以防止模型過度配度。ShowVector 以實際的值為 2 的小數位數的格式顯示向量的 helper 方法會每一行的 10 個值。

建立類神經網路時間序列模型之後,會評估其預測精確度:

double trainAcc = nn.Accuracy(trainData, 0.30);
Console.WriteLine("\nModel accuracy (+/- 30) on " + 
  " training data = " + trainAcc.ToString("F4"));

時間序列迴歸,決定將預測的值是否為正確取決於要調查的問題。Airline 旅客資料,精確度方法會標示為非正規化的預測的計數是加號或減號 30 的未經處理的實際計數正確預測的旅客計數。示範資料前, 五個預測,t = 5,t = 9 正確,但預測的 t = 10 不正確:

t  actual  predicted
= = = = = = = = = = =
 5   121     129
 6   135     128
 7   148     137
 8   148     153
 9   136     140
10   119     141

示範程式完成使用最後四個旅客計數 (t = 141 至 144) 來預測旅客計數定型資料的範圍之外第一個時間週期 (t = 145 = 年 1 月 1961年):

double[] predictors = new double[] { 5.08, 4.61, 3.90, 4.32 };
double[] forecast = nn.ComputeOutputs(predictors); 
Console.WriteLine("Predicted for January 1961 (t=145): ");
Console.WriteLine((forecast[0] * 100).ToString("F0"));
Console.WriteLine("End time series demo");

請注意,因為此時間序列模型定型使用正規化 (除以 100) 的資料,預測會也正規化,因此示範會顯示預測的值乘以 100。

時間序列分析的類神經網路

當您定義的類神經網路時,您就必須指定啟用輸出層節點和隱藏層節點所使用的函式。簡言之,建議使用隱藏的啟動和啟用輸出的 iden tity 函式的雙曲正切 (tanh) 函式。

使用類神經網路程式庫或像是 Microsoft CNTK 或 Azure 機器學習系統時,您必須明確指定啟動函式。示範程式限定這些啟用函式。索引鍵的程式碼就會發生 ComputeOutputs 方法中。計算值的隱藏的節點就像這樣:

for (int j = 0; j < numHidden; ++j) 
  for (int i = 0; i < numInput; ++i)
    hSums[j] += this.iNodes[i] * this.ihWeights[i][j];

for (int i = 0; i < numHidden; ++i)  // Add biases
  hSums[i] += this.hBiases[i];

for (int i = 0; i < numHidden; ++i)   // Apply activation
  this.hNodes[i] = HyperTan(hSums[i]); // Hardcoded

在這裡,函式 HyperTan 是程式定義,以避免極端值:

private static double HyperTan(double x) {
  if (x < -20.0) return -1.0; // Correct to 30 decimals
  else if (x > 20.0) return 1.0;
  else return Math.Tanh(x);
}

使用 tanh 隱藏節點啟用一個很合理,因為並不常見,替代方式是使用密切相關的羅吉斯 sigmoid 函數。例如:

private static double LogSig(double x) {
  if (x < -20.0) return 0.0; // Close approximation
  else if (x > 20.0) return 1.0;
  else return 1.0 / (1.0 + Math.Exp(x));
}

因為 identity 函數只 f (x) = x,其使用它的輸出節點啟用只花俏種說法不使用任何明確啟用。示範中的程式碼方法 ComputeOutputs 是:

for (int j = 0; j < numOutput; ++j) 
  for (int i = 0; i < numHidden; ++i)
    oSums[j] += hNodes[i] * hoWeights[i][j];

for (int i = 0; i < numOutput; ++i)  // Add biases
  oSums[i] += oBiases[i];

Array.Copy(oSums, this.oNodes, oSums.Length);

輸出節點的產品的總和會直接複製到輸出節點而不套用明確啟用。請注意,oNodes NeuralNetwork 類別成員的一個資料格,而非單一的變數陣列。

啟用函式的選擇會影響定型方法中實作的上一步傳播演算法中的程式碼。定型方法會使用每個啟用函式的微積分衍生項目。Y 的衍生物 = tanh(x) 是 (1 + y) * (1-y)。示範程式碼中:

// Hidden node signals
for (int j = 0; j < numHidden; ++j) {
  derivative = (1 + hNodes[j]) * (1 - hNodes[j]); // tanh
  double sum = 0.0; 
  for (int k = 0; k < numOutput; ++k)
    sum += oSignals[k] * hoWeights[j][k]; 
  hSignals[j] = derivative * sum;
}

如果您使用羅吉斯 sigmoid 啟用 y 衍生 = logsig(x) 是 y * (1-y)。對於輸出啟用,共 y de-rivative 微積分 = x 是只常數 1。在定型方法相關的程式碼是:

for (int k = 0; k < numOutput; ++k) {
  errorSignal = tValues[k] - oNodes[k];
  derivative = 1.0;  // For Identity activation
  oSignals[k] = errorSignal * derivative;
}

很明顯地,乘以 1 沒有任何作用。我編碼以做為一種文件集的作法。

總結

有許多不同的技術,可用來執行時間序列迴歸分析。在主題上的維基百科文章列出數十種技術,例如在許多方面,分類是非參數化和線性與非線性與參數化。我認為,使用類神經網路的方式與循環視窗資料的主要優點是 re sulting 模型通常是 (但並非一定) 多個非神經模型精確度。類神經網路工作方法的主要缺點是必須試驗學習速率,以取得很好的結果。

大部分的時間序列的迴歸分析技術使用輪流視窗的資料或類似的配置。不過,有一些 ad vanced 技術,可以使用未經處理資料,其中不視窗化。特別是,較新的方法就是使用了稱為短期長時間的記憶體類神經網路。這種方法通常會產生非常精確的預測模型。


Dr。James McCaffrey適用於 Microsoft Research Redmond,Wash.他已投入許多 Microsoft 產品,包括 Internet Explorer 和 Bing。Dr。在可到達 McCaffrey jamccaff@microsoft.com

非常感謝下列 Microsoft 技術專家已檢閱本文章:John Krumm、 Chris Lee 和 Adith Swaminathan


MSDN Magazine 論壇中的這篇文章的討論