準備資料以建置模型
了解如何使用 ML.NET 準備資料,以進行額外的處理或建置模型。
資料通常都是未經處理且疏鬆的。 ML.NET 機器學習演算法預期會輸入或特徵為單一的數字向量。 同樣地,預測 (標籤) 的值必須經過編碼 (尤其是類別資料)。 因此,資料準備的其中一個目標就是將資料轉換成 ML.NET 演算法所預期格式。
將資料分成訓練集和測試集
下一節概述訓練模型中稱為過度學習和學習不足的常見問題。 使用保留集分割您的資料和驗證模型,可協助您識別並減輕這些問題。
過度學習與學習不足
過度學習與學習不足是訓練模型時最常遇到的兩個問題。 學習不足表示選取的訓練工具不足以用於訓練資料集,而且通常會導致訓練期間發生高度損失情形,以及測試資料集上的分數/計量較低。 若要解決此問題,您需要選取更強大的模型,或執行更多特徵工程。 過度學習則相反,當模型學習訓練資料學得太好時,就會發生這種情況。 這通常會導致訓練期間的損失計量較低,但測試資料集上的損失情形偏高。
這些概念就好比為了準備測驗而學習。 假設您事先知道問題和答案。 經過學習之後,您會在測驗中取得完美的分數。 好消息! 不過,當您再次參加測驗時,問題已重新排列,且使用稍微不同的字詞,則您會獲得較低的分數。 這表示您記住了答案,但實際上並未了解測試中的概念。 而這就是過度學習的範例。 學習不足則相反,您獲得的學習教材無法正確代表測驗中所要評估的內容。 因此,您會嘗試猜測答案,因為您沒有足夠的知識可以正確回答問題。
分割資料
採用下列輸入資料,並將其載入名為 data
的 IDataView
中:
var homeDataList = new HomeData[]
{
new()
{
NumberOfBedrooms = 1f,
Price = 100_000f
},
new()
{
NumberOfBedrooms = 2f,
Price = 300_000f
},
new()
{
NumberOfBedrooms = 6f,
Price = 600_000f
},
new()
{
NumberOfBedrooms = 3f,
Price = 300_000f
},
new()
{
NumberOfBedrooms = 2f,
Price = 200_000f
}
};
若要將資料分割成訓練/測試集,請使用 TrainTestSplit(IDataView, Double, String, Nullable<Int32>) 方法。
// Apply filter
TrainTestData dataSplit = mlContext.Data.TrainTestSplit(data, testFraction: 0.2);
testFraction
參數會用來取用 0.2 或 20% 的資料集以進行測試。 其餘 80% 用於訓練。
結果會是具有兩個 IDataViews 的 DataOperationsCatalog.TrainTestData,您可以透過 TrainSet 和 TestSet 來加以存取。
篩選資料
有時候,並非資料集中的所有資料都與分析有關。 其中一個移除無關資料的方法便是篩選。 DataOperationsCatalog
包含一組篩選作業,可接受一個包含所有資料的 IDataView
,並傳回僅包含相關資料的 IDataView。 請務必注意,因為篩選作業並非和 TransformsCatalog
中項目相似的 IEstimator
或 ITransformer
,它們無法作為 EstimatorChain
或 TransformerChain
資料準備管線的一部分包含在其中。
採用下列輸入資料,並將其載入名為 data
的 IDataView
中:
HomeData[] homeDataList = new HomeData[]
{
new ()
{
NumberOfBedrooms=1f,
Price=100000f
},
new ()
{
NumberOfBedrooms=2f,
Price=300000f
},
new ()
{
NumberOfBedrooms=6f,
Price=600000f
}
};
若要根據資料行的值篩選資料,請使用 FilterRowsByColumn
方法。
// Apply filter
IDataView filteredData = mlContext.Data.FilterRowsByColumn(data, "Price", lowerBound: 200000, upperBound: 1000000);
以上範例會擷取資料集中價格介於 200000 及 1000000 之間的資料列。 套用此篩選的結果只會傳回資料中最後兩個資料列,並排除第一個資料列,因為其價格為 100000,而非介於指定的範圍。
取代遺漏值
遺漏值在資料集中經常出現。 其中一種處理遺漏值的方式是若資料中存在任何或其他有意義值 (例如平均數),則將它們取代成指定類型的預設值。
採用下列輸入資料,並將其載入名為 data
的 IDataView
中:
HomeData[] homeDataList = new HomeData[]
{
new ()
{
NumberOfBedrooms=1f,
Price=100000f
},
new ()
{
NumberOfBedrooms=2f,
Price=300000f
},
new ()
{
NumberOfBedrooms=6f,
Price=float.NaN
}
};
請注意我們清單中的最後一個項目針對 Price
具有遺漏值。 若要取代 Price
資料行中的遺漏值,請使用 ReplaceMissingValues
方法來填入該遺漏值。
重要
ReplaceMissingValue
只能針對數字資料運作。
// Define replacement estimator
var replacementEstimator = mlContext.Transforms.ReplaceMissingValues("Price", replacementMode: MissingValueReplacingEstimator.ReplacementMode.Mean);
// Fit data to estimator
// Fitting generates a transformer that applies the operations of defined by estimator
ITransformer replacementTransformer = replacementEstimator.Fit(data);
// Transform data
IDataView transformedData = replacementTransformer.Transform(data);
ML.NET 支援各種取代模式。 以上範例使用 Mean
取代模式,這種模式會使用資料行的平均值填入遺漏值。 取代之結果會將 200,000 填入我們資料中最後一個項目的 Price
屬性,因為它是 100,000 和 300,000 的平均。
使用正規器
正規化是一種資料前置處理技術,用來將特徵調整到相同範圍,通常介於 0 到 1 之間,以便機器學習演算法更精確地處理這些特徵。 例如,年齡的範圍和收入的範圍差異便非常大,因為年齡的範圍通常介於 0 到 100,收入的範圍則涵蓋零到數千不等。 請前往轉換頁面以取得詳細清單及正規化轉換的描述。
最小-最大正規化
採用下列輸入資料,並將其載入名為 data
的 IDataView
中:
HomeData[] homeDataList = new HomeData[]
{
new ()
{
NumberOfBedrooms = 2f,
Price = 200000f
},
new ()
{
NumberOfBedrooms = 1f,
Price = 100000f
}
};
正規化可以套用至具有單一數值和向量的資料行。 搭配 NormalizeMinMax
方法使用最小值和最大值正規化將 Price
資料行中的資料正規化。
// Define min-max estimator
var minMaxEstimator = mlContext.Transforms.NormalizeMinMax("Price");
// Fit data to estimator
// Fitting generates a transformer that applies the operations of defined by estimator
ITransformer minMaxTransformer = minMaxEstimator.Fit(data);
// Transform data
IDataView transformedData = minMaxTransformer.Transform(data);
原始的價格值 [200000,100000]
會使用 MinMax
正規化公式轉換成 [ 1, 0.5 ]
,該公式的輸出值範圍介於 0 到 1。
資料收納
Binning (資料收納) 會將連續值轉換成輸入的離散表示。 例如,假設您的其中一個特徵為年齡。 資料收納會建立該值的範圍,而非使用實際的年齡值。 其中一個收納可能是 0 到 18,另一收納則可能是 19 到 35 等。
採用下列輸入資料,並將其載入名為 data
的 IDataView
中:
HomeData[] homeDataList = new HomeData[]
{
new ()
{
NumberOfBedrooms=1f,
Price=100000f
},
new ()
{
NumberOfBedrooms=2f,
Price=300000f
},
new ()
{
NumberOfBedrooms=6f,
Price=600000f
}
};
使用 NormalizeBinning
方法將資料正規化成收納。 maximumBinCount
參數可讓您指定分類資料所需要的收納數。 在此範例中,資料會放入兩個收納。
// Define binning estimator
var binningEstimator = mlContext.Transforms.NormalizeBinning("Price", maximumBinCount: 2);
// Fit data to estimator
// Fitting generates a transformer that applies the operations of defined by estimator
var binningTransformer = binningEstimator.Fit(data);
// Transform Data
IDataView transformedData = binningTransformer.Transform(data);
資料收納的結果會建立 [0,200000,Infinity]
的收納界限。 因此,結果產生的收納將會是 [0,1,1]
,因為第一個觀察到的結果介於 0 到 200000,其他的則大於 200000,但小於無限大。
使用類別資料
最常見的資料類型之一是類別資料。 類別資料具有數目有限的類別。 例如,美國各州,或在一組圖片中找到的動物類型清單。 無論類別資料是特徵或標籤,都必須對應到數值,以便用來產生機器學習模型。 根據您正在解決的問題而定,在 ML.NET 中使用類別資料的方式有很多種。
索引鍵值對應
在 ML.NET 中,索引鍵是代表類別的整數值。 索引鍵值對應最常用來將字串標籤對應至唯一的整數值以進行訓練,然後在模型用來進行預測時對應回其字串值。
用來執行索引鍵值對應的轉換為 MapValueToKey 和 MapKeyToValue。
MapValueToKey
會在模型中加入對應的字典,以便在進行預測時 MapKeyToValue
可執行反向轉換。
One Hot 編碼
One Hot 編碼會接受一組有限的值,並將這些值對應至整數,而這些整數的二進位標記法在字串中的唯一位置上有單一 1
值。 如果沒有類別資料的隱含順序,則 One Hot 編碼是最佳選擇。 下表顯示以郵遞區號作為原始值的範例。
原始值 | One Hot 編碼值 |
---|---|
98052 | 00...01 |
98100 | 00...10 |
... | ... |
98109 | 10...00 |
將類別資料轉換成 One Hot 編碼數字的轉換就是 OneHotEncoding
。
雜湊
雜湊是將類別資料轉換成數字的另一種方式。 雜湊函式會將任意大小的資料 (例如文字字串) 對應到具有固定範圍的數字。 雜湊可以是快速且有空間效率的特徵向量化方式。 機器學習中值得注意的其中一個雜湊範例是電子郵件的垃圾郵件篩選,其不是維護已知字詞的字典,而是將電子郵件中的每個字詞雜湊處理並新增至大型特徵向量。 以此方式使用雜湊,可避免使用不在字典中的字詞來規避惡意垃圾郵件篩選的問題。
ML.NET 會提供雜湊轉換來對文字、日期和數值資料執行雜湊。 如同值與索引鍵的對應,雜湊轉換的輸出是索引鍵類型。
使用文字資料
與類別資料一樣,文字資料必須先轉換成數值特徵,才能用來來建置機器學習模型。 請前往轉換頁面以取得詳細清單及文字轉換的描述。
使用如下的資料,該資料已載入 IDataView
:
ReviewData[] reviews = new ReviewData[]
{
new ReviewData
{
Description="This is a good product",
Rating=4.7f
},
new ReviewData
{
Description="This is a bad product",
Rating=2.3f
}
};
ML.NET 會提供 FeaturizeText
轉換,該轉換會採用文字字串值,並透過套用一系列個別轉換從文字中建立一組特徵。
// Define text transform estimator
var textEstimator = mlContext.Transforms.Text.FeaturizeText("Description");
// Fit data to estimator
// Fitting generates a transformer that applies the operations of defined by estimator
ITransformer textTransformer = textEstimator.Fit(data);
// Transform data
IDataView transformedData = textTransformer.Transform(data);
產生的轉換會將 Description
資料行中文字值轉換成數字向量,且看起來會和以下輸出相似:
[ 0.2041241, 0.2041241, 0.2041241, 0.4082483, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0.2041241, 0, 0, 0, 0, 0.4472136, 0.4472136, 0.4472136, 0.4472136, 0.4472136, 0 ]
組成 FeaturizeText
的轉換也可以個別套用,以更精細地控制特徵產生。
// Define text transform estimator
var textEstimator = mlContext.Transforms.Text.NormalizeText("Description")
.Append(mlContext.Transforms.Text.TokenizeIntoWords("Description"))
.Append(mlContext.Transforms.Text.RemoveDefaultStopWords("Description"))
.Append(mlContext.Transforms.Conversion.MapValueToKey("Description"))
.Append(mlContext.Transforms.Text.ProduceNgrams("Description"))
.Append(mlContext.Transforms.NormalizeLpNorm("Description"));
textEstimator
包含 FeaturizeText
方法所執行作業的子集。 更複雜管線之優點是對套用到資料的轉換進行控制和其可見度。
使用第一個項目作為範例,下列是由 textEstimator
所定義轉換步驟產生結果的詳細描述:
原始文字:This is a good product
轉換 | 描述 | 結果 |
---|---|---|
1.NormalizeText | 根據預設,將所有字母轉換成小寫 | this is a good product |
2.TokenizeWords | 將字串分割成個別的字組 | ["this","is","a","good","product"] |
3.RemoveDefaultStopWords | 移除停用字詞,例如 is 和 a。 | ["good","product"] |
4.MapValueToKey | 根據輸入資料,將值對應到索引鍵 (類別) | [1,2] |
5.ProduceNGrams | 將文字轉換成連續字組的序列 | [1,1,1,0,0] |
6.NormalizeLpNorm | 使用輸入的 lp-norm 縮放輸入 | [ 0.577350529, 0.577350529, 0.577350529, 0, 0 ] |