本文章是由機器翻譯。

測試回合

多類 Logistic 回歸分類

James McCaffrey

下載代碼示例

James McCaffrey我認為 logistic 回歸 (LR) 分類是"你好,世界 !"機器學習 (毫升)。在標準的 LR 分類中,目標是預測可以在兩個分類值之一一些變數的值。例如,您可能想要預測一個人的性別 (男性或女性) 基於其高度和每年的收入。

多類 LR 分類擴展標準 LR 允許變數來預測有三個或更多值。例如,您可能想要預測一個人的政治傾向 (保守、 溫和或自由) 基於年齡、 年收入預測變數,等等。在本文中,我將解釋如何多類 LR 工作,向您展示如何執行它使用的 C#。

理解這篇文章將走向何方是的最佳方式是採取看看演示程式中圖 1。演示開始通過生成 1000 行的四個預測變數 (也稱為特徵),合成資料,要預測的變數可以在三個值之一。例如,生成的資料行可能類似于:

5.33  -4.89  0.15  -6.67  0.00  1.00  0.00

多類 Logistic 迴歸分析在行動
圖 1 多類 Logistic 迴歸分析在行動

前四個值表示已經標準化,所以 0.0 值功能完全平均的真實資料的預測值,大於 0.0 是大於功能平均,大小值和值小於 0.0 都小於功能平均。最後三個值是變數的要預測 N 1 編碼的版本。例如,如果你想要預測政治傾向,然後 (1 0 0) 代表保守 (0 1 0) 表示適度和 (0 0 1) 代表自由。

合成資料生成後,它被隨機分成 (80%的資料或 800 線) 的訓練集和測試集 (其餘 200 線)。訓練資料用於創建的預測模型和試驗資料用於估計新資料模型的預測精度要預測的值還不知道在哪裡。

F 功能與 c 類的多類 LR 模型會有 (f * c) 權重和偏置 c。這些都是必須確定的數位常量。對於這個演示中,有 4 * 3 = 12 權重和 3 偏置。培訓是估計值的權重和偏置的過程。培訓是一個反覆運算過程和演示將訓練反覆運算 (通常稱為毫升文學的時代) 的最大數目設置為 100。 用於訓練多類 LR 分類器的技術被稱為批次處理梯度下降。這種技術需要叫學習速率和重量衰減率兩個參數的值。這兩個值通常可以找到試驗和錯誤,並演示分配值 0.01 和 0.10,分別。

在訓練中,演示顯示進度消息每 10 世紀。如果你看看在消息圖 1你可以看到,訓練融合速度非常快,沒有任何改善後第一次 20 世紀。

經過培訓完成,演示顯示找不到 12 的權重和 3 偏置的最佳值。這些 15 的值定義的多類 LR 模型。演示使用這些值,計算模型 (92.63%正確或 741 個 800) 培訓資料和試驗資料 (90.00%正確或 180 個 200) 的預測準確性。

這篇文章假設你有中級或高級程式設計技能,但並不假定你知道任何關於多類 logistic 迴歸分析。該演示程式編碼使用 C# 中,但您應該能夠重構演示,其他程式設計語言沒有太多的麻煩。

理解多類 Logistic 迴歸分析

假設您想要預測基於年齡 (0) 一個人的政治傾向 (保守、 溫和的自由的),每年的收入 (1)、 高度 (3) 和教育水準 (4)。你編碼政治傾向與三個變數作為 (y0、 y1、 y2),其中保守是 (1,0,0) 中, 度是 (0,1,0) 和自由是 (0,0,1)。針對這一問題的多類 LR 模型將採取的形式:

z0 = (w00)(x0) + (w01)(x1) + (w02)(x2) + b0
y0 = 1.0 / (1.0 + e^-z0)
z1 = (w10)(x0) + (w11)(x1) + (w12)(x2) + b1
y1 = 1.0 / (1.0 + e^-z1)
z2 = (w20)(x0) + (w21)(x1) + (w22)(x2) + b2
y2 = 1.0 / (1.0 + e^-z2)

在這裡,wij 是與特徵變數的權重值關聯我和類變數 j 和 bj 是與類變數 j 關聯的偏置值。

多類 LR 所示的示例圖 2。一個訓練資料項目目有四個後面跟著三個輸出值 (1,0,0) 的預測值 (-5.40、-5.20、 5.30 5.10)。預測值是任意的但你可以想像他們代表一個人的年齡大於平均、 收入是低於平均、 高度大於平均教育水準是遠低於平均水準,和人的保守的政治傾向。

多類邏輯回歸的資料結構
圖 2 多類邏輯回歸的資料結構

每個權重矩陣的三列對應于三類值之一。每個列中的四個值對應于四個預測的 x 值。偏見陣列保存附加的、 特殊的重量 — — 一個用於每個類 — — 這並不是一個預測與相關聯。

請注意偏見陣列可能已經存儲為一個權重矩陣中的附加行。這往往是做在研究論文中,因為它簡化了數學方程。但出於演示目的實施,維護一個單獨的權重矩陣和陣列是稍微容易理解,在我看來偏見。

在多類 LR 輸出值被計算每個類。在圖 2,計算出的輸出值 0.35 0.33 0.32)。輸出值的總和為 1.0,並且可以被解釋為概率。因為最後輸出值 (勉強) 最大的三個,你得出結論輸出對應于 (0,0,1)。在此示例中,計算的輸出匹配的三個輸出在培訓資料項目目中,因此該模型取得了比較準確的預測。

通過首先總結產品的每個輸入的值倍其相應的權重值,然後添加相應的偏置值計算輸出值。這些款項的產品通常被稱為 z 值。Z 值來餵養什麼叫做邏輯斯諦乙狀結腸函數:1.0 / (1.0 + e ^-z) 其中 e 是數學常數和 ' ^' 意味著求冪運算。雖然它不是從顯然可以看出圖 2,物流的乙狀結腸函數的結果總是會介於 0.0 和 1.0 之間。

每個後勤乙狀結腸值用於計算最終的輸出值。總結後勤乙狀結腸值並將其用作除數。這個過程叫做 softmax 函數。如果你是新到所有這些 LR 概念,他們可以是非常令人困惑在第一次。但是,如果你看一遍中的示例圖 2 幾次,你最終會看到 LR 不是它第一次出現的那麼複雜。

權重和偏置值從哪裡來?確定這些值的過程被稱為培養模式。這個想法是使用一套培訓具有已知輸入和輸出值,然後嘗試不同的權重和偏置值,直到你在培訓資料中查找計算的輸出與 (通常稱為目標值) 的正確的輸出值之間的誤差減到最小的一組值的資料。

它並不可行的計算權重的確切值必須估計偏差,所以權重和偏置。有幾個所謂的數值優化技術,可以用來做到這一點。常用的方法包括 L BFGS 演算法、 反覆運算加權的最小二乘方法和粒子群優化演算法。該演示程式使用一種技術,已令人困惑稱為 (最小化計算和已知的輸出值之間的誤差) 兩個梯度下降和梯度上升 (最大化權重和偏置是最優的概率)。

演示的程式結構

演示程式,與一些小的編輯,以節省空間,結構在圖 3。若要創建該演示程式,我推出Visual Studio,選擇 C# 主控台應用程式範本。我的名字 LogisticMultiClassGradient 專案。演示有沒有重大的.NET 依賴關係,因此,任何版本的Visual Studio將工作。該演示是太長,無法顯示其全部內容,但所有的原始程式碼是本文附帶的下載中提供。我刪除了所有正常的錯誤檢查,以保持盡可能清晰的主要思想。

圖 3 演示程式結構

using System;
namespace LogisticMultiClassGradient
{
  class LogisticMultiProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Begin classification demo");
      ...
      Console.WriteLine("End demo");
      Console.ReadLine();
    }
    public static void ShowData(double[][] data,
      int numRows, int decimals, bool indices) { . . }
    public static void ShowVector(double[] vector,
      int decimals, bool newLine) { . . }
    static double[][] MakeDummyData(int numFeatures,
      int numClasses, int numRows, int seed) { . . }
    static void SplitTrainTest(double[][] allData,
      double trainPct, int seed, out double[][] trainData,
      out double[][] testData) { . . }
  }
  public class LogisticMulti
  {
    private int numFeatures;
    private int numClasses;
    private double[][] weights; // [feature][class]
    private double[] biases;    // [class]
    public LogisticMulti(int numFeatures,
      int numClasses) { . . }
    private double[][] MakeMatrix(int rows,
      int cols) { . . }
    public void SetWeights(double[][] wts,
      double[] b) { . . }
    public double[][] GetWeights() { . . }
    public double[] GetBiases() { . . }
    private double[] ComputeOutputs(double[] dataItem) { . . }
    public void Train(double[][] trainData, int maxEpochs,
      double learnRate, double decay) { . . }
    public double Error(double[][] trainData) { . . }
    public double Accuracy(double[][] trainData) { . . }
    private static int MaxIndex(double[] vector) { . . }
    private static int MaxIndex(int[] vector) { . . }
    private int[] ComputeDependents(double[] dataItem) { . . }
  }
}

範本代碼載入後,在解決方案資源管理器視窗中用滑鼠右鍵按一下檔 Program.cs 討論,又改名了到更具描述性的 LogisticMultiProgram.cs 和Visual Studio自動重命名類程式,對我來說。 在編輯器視窗頂部的原始程式碼,我刪除所有不需要使用語句,留下只是一個頂級的 System 命名空間的引用。

LogisticMultiProgram 類包含 MakeDummyData、 SplitTrainTest、 ShowData 和 ShowVector 的説明器方法。這些方法創建和顯示的綜合資料。所有分類邏輯都包含在一個名為 LogisticMulti 的程式定義的類。

Main 方法創建合成資料包含這些語句:

int numFeatures = 4;
int numClasses = 3;
int numRows = 1000;
double[][] data = MakeDummyData(numFeatures, 
  numClasses, numRows, 0);

方法 MakeDummyData 生成一組隨機權重和偏置,然後對於每一行的資料,生成隨機的輸入的值,結合權重和偏差的輸入的值,並計算一些相應的 N 1 編碼的輸出值。合成資料拆分成 80%培訓和 20%測試集,像這樣:

double[][] trainData;
double[][] testData;
SplitTrainTest(data, 0.80, 7, out trainData, out testData);
ShowData(trainData, 3, 2, true);
ShowData(testData, 3, 2, true);

參數,值 7 是一個隨機的種子,用只是因為它提供了一個好看的演示。多值分類器 LR 是創建和培訓這些語句:

LogisticMulti lc = new LogisticMulti(numFeatures, numClasses);
int maxEpochs = 100;
double learnRate = 0.01;
double decay = 0.10;
lc.Train(trainData, maxEpochs, learnRate, decay);

訓練參數 maxEpochs (100)、 學習速率 (0.01) 和重量衰變率 (0.10) 的值測定試驗和錯誤。優化大多數毫升訓練方法通常需要一些試驗,以獲得良好的預測精度。

經過訓練後,最佳權重和偏置值存儲在 LogisticMulti 物件中。他們正在檢索並顯示像這樣:

double[][] bestWts = lc.GetWeights();
double[] bestBiases = lc.GetBiases();
ShowData(bestWts, bestWts.Length, 3, true);
ShowVector(bestBiases, 3, true);

使用一個 void 的火車方法與 Get 方法相結合的一種替代設計旨在使其返回最佳權重矩陣和最好的偏見陣列作為輸出參數,或在一個聯合陣列,請重構方法,列車。評價訓練好的模型品質就像這樣:

double trainAcc = lc.Accuracy(trainData, weights);
Console.WriteLine(trainAcc.ToString("F4"));
double testAcc = lc.Accuracy(testData, weights);
Console.WriteLine(testAcc.ToString("F4"));

測試資料的模型精度較相關的兩個精度值。它為您提供一個粗略的估計,如何準確的模型會時提出了一種未知的輸出值的新資料。

實施多類 LR 培訓

LogisticMulti 類的建構函式定義如下:

public LogisticMulti(int numFeatures, int numClasses)
{
  this.numFeatures = numFeatures;
  this.numClasses = numClasses;
  this.weights = MakeMatrix(numFeatures, numClasses);
  this.biases = new double[numClasses];
}

方法 MakeMatrix 是一個為數組的陣列樣式矩陣分配記憶體的 helper 方法。權重矩陣和偏見陣列隱式初始化為 0.0 的所有值。一些研究人員喜歡的替代方法是顯式地初始化權重和偏置到小 (通常介於 0.001 和 0.01) 之間的隨機值。

方法 ComputeOutputs 的定義提出了圖 4。該方法返回一個陣列的值,另一個用於每個類,每個值介於 0.0 和 1.0 和值總和為 1.0 之間。

圖 4 方法 ComputeOutputs

private double[] ComputeOutputs(double[] dataItem)
{
  double[] result = new double[numClasses];
  for (int j = 0; j < numClasses; ++j) // compute z
  {
    for (int i = 0; i < numFeatures; ++i)
      result[j] += dataItem[i] * weights[i][j];
    result[j] += biases[j];
  }
  for (int j = 0; j < numClasses; ++j) // 1 / 1 + e^-z
    result[j] = 1.0 / (1.0 + Math.Exp(-result[j]));
  double sum = 0.0; // softmax scaling
  for (int j = 0; j < numClasses; ++j)
    sum += result[j];
  for (int j = 0; j < numClasses; ++j)
    result[j] = result[j] / sum;
  return result;
}

類定義包含一個類似的方法,ComputeDependents:

private int[] ComputeDependents(double[] dataItem)
{
  double[] outputs = ComputeOutputs(dataItem); // 0.0 to 1.0
  int maxIndex = MaxIndex(outputs);
  int[] result = new int[numClasses];
  result[maxIndex] = 1;
  return result;
}

方法 ComputeDependents 返回其中一個值是 1,其他值為 0 的整數陣列。 這些計算出的輸出值可比作中訓練資料,以確定有否模型作出正確的預測,反過來可以用來計算預測精度的已知的目標輸出值。

表示在非常高的級別偽代碼中,火車的方法:

loop maxEpochs times
  compute all weight gradients
  compute all bias gradients
  use weight gradients to update all weights
  use bias gradients to update all biases
end-loop

每個權重和偏置值有關聯的梯度值。漸變鬆散地說,是一個值,該值指示相隔多麼遙遠,和在什麼方向 (正數或負數) 計算的輸出值與目標輸出值進行比較。例如,假設一個重量,如果所有其他權重和偏置值保持不變,計算的輸出值為 0.7 的目標輸出值是 1.0。計算的值是太小了,所以梯度是價值約 0.3,將被添加到重量。如果的權重值增加,會增加計算的輸出值。我漏掉了一些細節,但其基本思想是相當簡單。

梯度訓練背後的數學運算使用微積分,並且是非常複雜的但幸運的是,你不需要完全理解數學運算,以實現的代碼。方法火車的定義的開頭:

public void Train(double[][] trainData, int maxEpochs,
  double learnRate, double decay)
{
  double[] targets = new double[numClasses];
  int msgInterval = maxEpochs / 10;
  int epoch = 0;
  while (epoch < maxEpochs)
  {
    ++epoch;
...

目標陣列將舉行訓練資料項目目中存儲的正確的輸出值。變數 msgInterval 控制次數顯示進度消息。然後,將顯示進度消息:

if (epoch % msgInterval == 0 && epoch != maxEpochs)
{
  double mse = Error(trainData);
  Console.Write("epoch = " + epoch);
  Console.Write(" error = " + mse.ToString("F4"));
  double acc = Accuracy(trainData);
  Console.WriteLine(" accuracy = " + acc.ToString("F4"));
}

因為毫升的訓練通常涉及一些審判和顯示進度消息的錯誤是非常有用的。接下來,分配的權重和偏置的梯度的存儲:

double[][] weightGrads = MakeMatrix(numFeatures, numClasses);
double[] biasGrads = new double[numClasses];

請注意這些出現在主要的 while 迴圈內的分配。因為 C# 初始化陣列為 0.0,初始化所有漸變。備用方法是以分配空間外面 while 迴圈,然後調用説明器方法的名字,如 ZeroMatrix 和 ZeroArray。接下來,所有的權重計算漸變:

for (int j = 0; j < numClasses; ++j) {
  for (int i = 0; i < numFeatures; ++i) {
    for (int r = 0; r < trainData.Length; ++r) {
      double[] outputs = ComputeOutputs(trainData[r]);
        for (int k = 0; k < numClasses; ++k)
          targets[k] = trainData[r][numFeatures + k];
        double input = trainData[r][i];
        weightGrads[i][j] += (targets[j] - outputs[j]) * input;
    }
  }
}

此代碼是多類 LR 的心。每個重量梯度是本質上的區別目標輸出值和計算出的輸出值。在相反的方向,應調整差值乘以相關的輸入值,要考慮到的輸入可以是負值,這意味著重量的事實。

我經常使用的一個有趣的替代是忽略輸入值的大小,並使用它的標誌:

double input = trainData[r][i];
int sign = (input > 0.0) ? 1 : -1;
weightGrads[i][j] += (targets[j] - outputs[j]) * sign;

以我的經驗,這種技術經常導致更好的模型。接下來,所有的偏見,梯度計算:

for (int j = 0; j < numClasses; ++j) {
  for (int i = 0; i < numFeatures; ++i) {
    for (int r = 0; r < trainData.Length; ++r) {
      double[] outputs = ComputeOutputs(trainData[r]);
      for (int k = 0; k < numClasses; ++k)
        targets[k] = trainData[r][numFeatures + k];
      double input = 1; // 1 is a dummy input
      biasGrads[j] += (targets[j] - outputs[j]) * input;
    }
  }
}

如果你檢查的代碼,你可以看到內迴圈,通過計算權重梯度,可以執行計算偏差梯度。我分開兩個梯度計算為清楚起見,這會降低性能。此外,可以刪除乘法所隱含的輸入值為 1。它,也增加了為清楚起見。接下來,權重已更新:

for (int i = 0; i < numFeatures; ++i) {
  for (int j = 0; j < numClasses; ++j) {
    weights[i][j] += learnRate * weightGrads[i][j];
    weights[i][j] *= (1 - decay);  // wt decay
  }
}

後增加或減少基於學習的權重及其梯度,權重值率分數降低使用重量衰變率。例如,演示使用 0.10,所以乘以一個典型的體重衰減值 (1-0.10) 乘以 0.90,即減少了 10%。重量衰減也被稱為正規化。這項技術防止失控的權重值。方法火車最後更新的偏差值:

...
    for (int j = 0; j < numClasses; ++j) {
      biases[j] += learnRate * biasGrads[j];
      biases[j] *= (1 - decay);
    }
  } // While
} // Train

培訓技術更新類成員權重矩陣和偏見中的陣列的地方。這些值定義的多類 LR 模型,並可以使用 Get 方法檢索。

總結

有兩個主要變化的梯度的訓練,被稱為批次處理和隨機。這篇文章給出了批量版本之間的差異計算和所有培訓專案的都目標輸出梯度在那裡通過求和計算。在隨機梯度的訓練,梯度估計利用只是單獨訓練資料項目目。一些實驗的基礎,應用於多類 LR 時批次處理培訓似乎給出了更精確的模型,但需要較長時間比隨機培訓。這是相當令人吃驚,因為隨機培訓通常適用于批量訓練神經網路。


James McCaffrey為在華盛頓州雷蒙德市的微軟研究院工作和曾在幾個 Microsoft 產品包括 Internet Explorer 和 Bing。  博士。 麥卡弗裡也可以撥打 jammc@microsoft.com

感謝以下的微軟技術專家對本文的審閱:陶德 · 貝略和愛麗森溶膠