2018 年 6 月

磁碟區 33 編號 6

本文章是由機器翻譯。

測試回合-使用 CNTK 的類神經迴歸

James McCaffrey |2018 年 6 月

程式碼下載位於msdn.com/magazine/0518magcode

迴歸問題的目標是要進行預測,其中要預測的值是單一數值。例如,您可能會

想要預測的人員,根據其權數、 年齡和性別高度。有許多技術可以用來處理迴歸問題。在本文中,我將說明如何建立類神經網路迴歸模型使用 CNTK 程式庫。

若要查看示範程式中是好的方法,請參閱這篇文章會向其中圖 1。示範程式建立已知遊艇 Hydrodynamics 資料集進行效能評定的迴歸模型。目標是要預測的防禦遊艇輪廓,六個預測量變數為基礎的量值: buoyancy 輪廓、 prismatic 係數、 長度位移比例、 資料交換杯吧比例、 長度不同比例和 Froude 數目的中心。

圖 1 使用 CNTK 類神經網路的迴歸

示範程式具有兩個隱藏層,每一個都有五個處理節點建立類神經網路。定型之後, 模型用來進行預測,其中兩個的資料項目。第一個項目都具有每天 0.52、 0.79、 0.55、 0.41、 0.65 (0.00) 的預測值。預測的輪廓抵抗 0.0078,而實際抵抗 0.0030。第二個項目有預測值 (1.00、 1.00、 0.55、 0.56、 0.46、 1.00)。預測的輪廓抵抗 0.8125,而實際抵抗 0.8250。模型似乎很精確。

本文假設您有中繼或更好的程式設計技巧,但不會假設您了解太多 CNTK 或類神經網路。示範使用 Python,機器學習的預設語言編碼,但即使您不知道 Python 應該能夠依照步驟進行,不需要太多的困難。示範程式的程式碼所示整個本文。示範程式所使用的遊艇輪廓資料檔案可以位於bit.ly/2Ibsm5D,也是本文章的下載中。

了解的資料

建立機器學習模型時,資料準備幾乎都是專案最耗時的部分。未經處理的資料集 308 的項目,且看起來像:

-2.3 0.568 4.78 3.99 3.17 0.125 0.11
-2.3 0.568 4.78 3.99 3.17 0.150 0.27
...
-5.0 0.530 4.78 3.75 3.15 0.125 0.09
...
-2.3 0.600 4.34 4.23 2.73 0.450 46.66

檔案是以空格分隔。前六個值是 (通常稱為機器學習術語中的功能) 的預測值。在每一行的最後一個值是位移的"residuary 抵抗每單位加權。 」

因為沒有多個預測量變數,所以它不在圖表中顯示完整的資料集。但您可以藉由檢查圖形中的取得的資料結構的粗略想法圖 2。圖形會繪製只 prismatic 係數預測量值和輪廓防禦功能。您可以看到,prismatic 係數的值,本身不提供您足夠的資訊來進行預測精確的輪廓防禦功能。

圖 2 的部分遊艇輪廓資料

使用類神經網路,時通常會需要將資料標準化才能建立良好的預測模型。我可以使用最小最大正規化輪廓抵抗值和六個預測量值上。我放入 Excel 試算表的未經處理資料,並針對每個資料行,計算的最大和最小值。然後,針對每個資料行,我要取代與每個值 v (v-min) / (max-min)。例如,最小 prismatic 係數值是 0.53 而最大值 0.60。資料行中的第一個值是 0.568 和會正規化為 (0.568-0.53) / (0.60-0.53) = 0.038 / 0.07 = 0.5429。

之後正規化,插入標記 | 預測值和 | 抵抗至 Excel 試算表以便資料可以輕鬆地讀取 CNTK 資料讀取器物件。接著我匯出資料以 tab 鍵分隔檔案。產生的資料看起來像:

|predictors  0.540000  0.542857 . . |resistance  0.001602
|predictors  0.540000  0.542857 . . |resistance  0.004166
...

最小最大正規化的替代方案包括 z-score 正規化和訂單量級正規化。

示範程式

中會顯示完整的範例程式,以節省空間,少數稍加編輯圖 3。已移除所有一般錯誤檢查。使用兩個空格字元,而不是個人的喜好設定,並儲存空間為一般的四個縮排。請注意,' \' 字元使用 Python 行接續符號。

圖 3 迴歸示範程式

# hydro_reg.py
# CNTK 2.4 with Anaconda 4.1.1 (Python 3.5, NumPy 1.11.1)
# Predict yacht hull resistance based on six predictors

import numpy as np
import cntk as C

def create_reader(path, input_dim, output_dim, rnd_order,
  sweeps):
  x_strm = C.io.StreamDef(field='predictors',
    shape=input_dim, is_sparse=False)
  y_strm = C.io.StreamDef(field='resistance',
    shape=output_dim, is_sparse=False)
  streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
  deserial = C.io.CTFDeserializer(path, streams)
  mb_src = C.io.MinibatchSource(deserial,
    randomize=rnd_order, max_sweeps=sweeps)
  return mb_src

# ========================================================

def main():
  print("\nBegin yacht hull regression \n")
  print("Using CNTK version = " + \
    str(C.__version__) + "\n")
  input_dim = 6  # center of buoyancy, etc.
  hidden_dim = 5
  output_dim = 1  # residuary resistance
  train_file = ".\\Data\\hydro_data_cntk.txt"
  # data resembles:
  # |predictors 0.540  0.542 . . |resistance  0.001
  # |predictors 0.540  0.542 . . |resistance  0.004
  # 1. create neural network model
  X = C.ops.input_variable(input_dim, np.float32)
  Y = C.ops.input_variable(output_dim)
  print("Creating a 6-(5-5)-1 tanh regression NN for \
yacht hull dataset ")
  with C.layers.default_options():
    hLayer1 = C.layers.Dense(hidden_dim,
      activation=C.ops.tanh, name='hidLayer1')(X)
    hLayer2 = C.layers.Dense(hidden_dim,
      activation=C.ops.tanh, name='hidLayer2')(hLayer1)  
    oLayer = C.layers.Dense(output_dim,
      activation=None, name='outLayer')(hLayer2)
  model = C.ops.alias(oLayer)  # alias
  # 2. create learner and trainer
  print("Creating a squared error batch=11 Adam \
fixed LR=0.005 Trainer \n")
  tr_loss = C.squared_error(model, Y)
  max_iter = 50000
  batch_size = 11
  learn_rate = 0.005
  learner = C.adam(model.parameters, learn_rate, 0.99)
  trainer = C.Trainer(model, (tr_loss), [learner])
  # 3. create reader for train data
  rdr = create_reader(train_file, input_dim, output_dim,
    rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
  hydro_input_map = {
    X : rdr.streams.x_src,
    Y : rdr.streams.y_src
  }
  # 4. train
  print("Starting training \n")
  for i in range(0, max_iter):
    curr_batch = rdr.next_minibatch(batch_size,
      input_map=hydro_input_map)
    trainer.train_minibatch(curr_batch)
    if i % int(max_iter/10) == 0:
      mcee = trainer.previous_minibatch_loss_average
      print("batch %6d: mean squared error = %8.4f" % \
        (i, mcee))
  print("\nTraining complete")
  # (could save model to disk here)
  # 5. use trained model to make some predictions
  np.set_printoptions(precision=2, suppress=True)
  inpts = np.array(
    [[0.520000, 0.785714, 0.550000, 0.405512, \
      0.648352, 0.000000],
     [1.000000, 1.000000, 0.550000, 0.562992, \
      0.461538, 1.000000]],
    dtype=np.float32)
  actuals = np.array([0.003044, 0.825028],
    dtype=np.float32)
  for i in range(len(inpts)):
    print("\nInput: ", inpts[i])
    pred = model.eval(inpts[i])
    print("predicted resistance: %0.4f" % pred[0][0])
    print("actual resistance:    %0.4f" % actuals[i])
  print("\nEnd yacht hull regression ")

# ========================================================

if __name__ == "__main__":
  main()

安裝 CNTK 可能很有點困難。第一次您安裝 Python,其中包含所需的 Python 解譯器、 SciPy、 NumPy 等所需的封裝,再加上有用的公用程式,例如 pip Anaconda 的發佈。我使用 Anaconda3 4.1.1 64 位元,具有 Python 3.5。在安裝之後 Anaconda,您安裝 CNTK Python 封裝時,不在獨立系統上,以使用 pip 公用程式。從一般的殼層,我所用的命令為:

>pip install https://cntk.ai/PythonWheel/CPU-Only/cntk-2.4-cp35-cp35m-win_amd64.whl

Hydro_reg.py 示範都有一個 helper 函數,create_reader。您可以將 create_reader 視為未定案 CNTK 迴歸問題。您必須在大部分情況下變更是資料檔中的標記名稱。

所有控制邏輯都是在單一的 main 函式。程式碼開始:

def main():
  print("Begin yacht hull regression \n")
  print("Using CNTK version = " + \
    str(C.__version__) + "\n")
  input_dim = 6  # center of buoyancy, etc.
  hidden_dim = 5
  output_dim = 1  # residuary resistance
  train_file = ".\\Data\\hydro_data_cntk.txt"
...

因為 CNTK 是年輕且連續的開發,最好要顯示的版本,正在使用 (2.4 在此情況下)。輸入節點的數目取決於資料集的結構。迴歸問題中,輸出節點的數目一律設定為 1。隱藏層的數目和每個隱藏層中的處理節點數目是可用的參數,必須由試驗。

示範程式就會用於培訓 308 的所有項目。另一個方法是將資料集分割成定型集 (通常是資料的 80%) 和測試集 (其餘的 20%)。定型之後, 您可以計算模型測試資料集,以確認度量資訊是在定型資料上類似的遺失和精確度的度量。

建立類神經網路模型

示範設定 CNTK 物件來保存預測工具和 true 輪廓抵抗值:

X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim)

因為很少需要有 64 位元精確度 CNTK 依預設使用 32 位元值。Input_variable 函式名稱可以是如果您還不熟悉 CNTK 有點混淆。在這裡,"input_ 」 是指傳回的物件保留來自的輸入資料 (對應至輸入和輸出的類神經網路) 的值。

使用這些陳述式建立類神經網路:

print("Creating a 6-(5-5)-1 NN")
with C.layers.default_options():
  hLayer1 = C.layers.Dense(hidden_dim,
    activation=C.ops.tanh, name='hidLayer1')(X)
  hLayer2 = C.layers.Dense(hidden_dim,
    activation=C.ops.tanh, name='hidLayer2')(hLayer1)  
  oLayer = C.layers.Dense(output_dim,
    activation=None, name='outLayer')(hLayer2)
model = C.ops.alias(oLayer)  # alias

還有這裡相當多的位元。Python"與"陳述式可用來將一組常見的參數值傳遞給多個函式。在此情況下,類神經網路的加權和徵才偏差值會初始化使用 CNTK 預設值。類神經網路會高度機密為初始的加權和徵才偏差值,因此提供的非預設值是無法了解類神經網路時再試一次的第一個項目-到常見的情況。

類神經網路會有兩個隱藏的層。X 物件做為第一個隱藏層中輸入第一個隱藏的層會做為輸入與第二個隱藏層。而第二個隱藏層會做為輸入至輸出層。

兩個隱藏的層使用 tanh (雙曲正切) 啟動。兩個主要的替代方案是羅吉斯 sigmoid 並加以改正線性單位 (ReLU) 啟動。輸出層會使用 「 無 」 啟動,這表示輸出節點的值無法修改。這是要用於迴歸問題的設計模式。使用沒有啟用有時稱為使用身分識別啟用函式,因為識別數學函式 f (x) = x,沒有任何作用。

示範程式會建立別名具名 「 模型 」 的輸出層。這項技術是選擇性,而且是難以察覺的位元。這裡的概念是,類神經網路基本上複雜的數學函式。輸出節點在概念上代表整個兩層的網路和網路模式。

定型模型

CNTK 功能的核心是定型類神經網路模型的能力。定型會由這些陳述式:

tr_loss = C.squared_error(model, Y)
max_iter = 50000
batch_size = 11
learn_rate = 0.005
learner = C.adam(model.parameters, learn_rate, 0.99)
trainer = C.Trainer(model, (tr_loss), [learner])

讓定型物件知道如何調整加權和減少錯誤的徵才偏差需要遺失 (錯誤) 函式。CNTK 2.4 有九個遺失函式,但簡單 squared_error 幾乎都是適用於迴歸問題。相當於更新作業數目和必須由試驗的反覆項目數目。

定型模組物件需要學習模組物件。您可以將學習模組做為演算法。CNTK 支援八個學習演算法。針對迴歸問題,通常得到很好的結果,使用基本的隨機梯度下降或更趨精密完美的 Adam (「 彈性動量預估 」)。

批次大小可由 CNTK 來決定執行加權和偏差更新的頻率。示範設定批次大小為 11。因此,308 的項目將會分組為 308 / 11 = 28 隨機選取的批次。就會分析每個批次,然後執行更新。學習速率控制粗細和偏差的調整的大小。良好的判斷值的批次大小,最大數目的反覆項目,並學習速度通常是最大的挑戰時建立類神經網路的預測模型。

示範呼叫的程式定義 create_reader 函式,也會建立讀取器物件。而且 input_map 建立來告訴讀者位置功能值,以及值-因為預測的位置:

rdr = create_reader(train_file, input_dim, output_dim,
  rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
hydro_input_map = {
  X : rdr.streams.x_src,
  Y : rdr.streams.y_src
}

Rnd_order 參數,可確保將會以不同方式處理項目,在每個階段,請務必防止訓練出一的資料。INFINITELY_REPEAT 引數會允許透過 308 項目資料集的多個傳遞的訓練。

模型已定型之後準備,如下所示:

for i in range(0, max_iter):
  curr_batch = rdr.next_minibatch(batch_size,
    input_map=hydro_input_map)
  trainer.train_minibatch(curr_batch)
  if i % int(max_iter/10) == 0:
    mcee = trainer.previous_minibatch_loss_average
    print("batch %6d: mean squared error = %8.4f" % \
      (i, mcee))

Next_minibatch 函式會提取 11 資料中的項目。定型函式會使用 Adam 演算法,來更新加權和徵才根據計算的輪廓抵抗值與實際的防禦功能值之間的平方誤差的偏差。目前的 11 個項目的批次的平方的錯誤會顯示每個 50000 / 10 = 5,000 個批次,所以您以視覺化方式可以監視訓練進度:您想要查看通常減少的遺失/錯誤值。

使用模型

此模型已培訓之後,範例程式可讓一些預測。首先,正規化的資料集的兩個任意項目預測量值是選取 (而且項目 99 238),並放入陣列的陣列樣式矩陣:

inpts = np.array(
  [[0.520000, 0.785714, 0.550000, 0.405512,
    0.648352, 0.000000],
   [1.000000, 1.000000, 0.550000, 0.562992,
    0.461538, 1.000000]],
  dtype=np.float32)

接下來,對應的正規化實際輪廓抵抗值會放入陣列中:

actuals = np.array([0.003044, 0.825028], dtype=np.float32)

然後,預測量值來計算預測使用 model.eval 函式的值,並會顯示預測值和實際值:

for i in range(len(inpts)):
  print("\nInput: ", inpts[i])
  pred = model.eval(inpts[i])
  print("predicted resistance: %0.4f" % pred[0][0])
  print("actual resistance:    %0.4f" % actuals[i])
print("End yacht hull regression ")

請注意,陣列的陣列矩陣,以單一值形式傳回預測的輪廓抵抗值。因此,本身的值是 [0] [0] (第 0 列、 資料行 0)。處理 CNTK 的形狀向量和矩陣是重要的語法項挑戰。使用 CNTK 時我會花許多時間列印物件,並顯示其圖形,沿著 print(something.shape)。

總結

建立類神經網路迴歸模型時,沒有任何預先定義的精確度度量。如果您想要計算預測精確度,您必須定義表示預測的值要夠靠近,好對應的實際值才能被視為正確。一般而言,您會指定百分比/所佔百分比,例如 0.10,並評估為正確,如果它位於該百分比的實際值的預測的值。

因為示範模型適用於標準化資料時,如果您使用模型進行預測新的、 先前未察覺的預測值,您必須使用相同的最小最大值用在定型資料上加以正規化。同樣地,預測的輪廓抵抗值 pv,都正規化,因此您必須藉由計算 pv 則反正規化 * (max-min) + 最小值。

詞彙 「 迴歸 」 可以有數個不同的意義。本文一詞是指其目標是要預測單一數值 (輪廓抵抗) 問題狀況。典型的統計資料的線性迴歸項技術會比類神經網路迴歸,但通常比較不精確更簡單。機器學習羅吉斯迴歸技術預測單一數值介於 0.0 到 1.0,其解譯為機率,並接著使用來預測類別值,例如"male"(p < 0.5) 或 「 女性 」 (p > 0.5) 之間。


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