導讀:ML.NET系列文章
本文將基於ML.NET v0.2預覽版,介紹機器學習中的分類和回歸兩個重要概念,並實現白葡萄酒品質預測。
本系列前面的文章也提到了一些,經典的機器學習最主要的特點就是模擬,具體來說就是定義出一個y=f(x)函數,x就是我們定義的特征值(它可能是一個/組標量,也可能是一個/組向量),y就是目標值,這個函數的目標是要無限滿足所有(x, y)都出現在函數y=f(x)在坐標軸上經過的地方,這有時候也叫函數逼近。試想,只要這個函數是模擬出了對某類問題的輸入和輸出值(x, y)的運算過程,那再來一個新的輸入值x,是不是就可以求解出輸出值y是什么了!基於這個道理,機器學習才有了預知未來的能力。那么,對未來的預知最常見的兩種情況:一個是為了分類,一個是為了回歸。“分類”的概念容易理解,它要解決的目標值是標簽類的數據,比如屬於什么物種,是否是垃圾郵件,什么類型的電影之類的問題,而“回歸”的概念需要一點想像力,它要解決的目標值是純粹的實數的數據,比如下一場考試得多少分,今年世界杯冠軍是進幾個球獲勝,那個小伙能吸引到周邊妹子的魅力值有多高等等。是不是一下子覺得很神奇,興致就來了?!
再容我多說幾句,分類預測與回歸預測,同樣是建模,解決的問題“可以是”不一樣的(注意這個——可以是),簡單地來看,分類要預測一個離散的標簽,多個標簽互相之間沒有關系,目的是要選其中最恰當的一個;而回歸是預測一個連續變化的數,且必須是在一定范圍內有延續關系的數,預測的結果值是變化過程中的其中一個。分類和回歸其實也能說有一些相同的地方,分類算法也是有預測到一個連續的值,但是這些連續值對應的是一個類別在概率上不同的面而已;而回歸算法照樣可以預測離散值,但是以整數形式預測離散值的,本質上是把類別下的每一個子類都按字典形式找一個數值對應上罷了。於是,得出一個奇妙的結論,分類和回歸問題能夠以合適的手段進行轉換。這個結論的作用,就是說當以分類或回歸之一進行機器學習找不到合適的算法訓練出模型時,不妨嘗試轉換到另一個方向去尋求突破。
好了,胡謅了一些暈乎乎的理論后,本文繼續使用ML.NET帶來一個回歸模型的案例,根據白葡萄酒的各項釀制成分參數來預測其品質好壞。這次的數據集取自著名的競賽網站——Kaggle.com的其中一個考題UCI Wine Quality Dataset,數據內容類似如下:
fixed.acidity,volatile.acidity,citric.acid,residual.sugar,chlorides,free.sulfur.dioxide,total.sulfur.dioxide,density,pH,sulphates,alcohol,quality,id 6.5,0.28,0.35,15.4,0.042,55,195,0.9978,3.23,0.5,9.6,6,3900 7.3,0.19,0.49,15.55,0.058,50,134,0.9998,3.42,0.36,9.1,7,3901 6.4,0.16,0.32,8.75,0.038,38,118,0.99449,3.19,0.41,10.7,5,3902 7.4,0.19,0.3,12.8,0.053,48.5,229,0.9986,3.14,0.49,9.1,7,3903 ...
各個字段說明是這樣:
成分 1 - fixed acidity 非揮發性酸 2 - volatile acidity 揮發性酸度 3 - citric acid 檸檬酸 4 - residual sugar 剩余糖分 5 - chlorides 氯化物 6 - free sulfur dioxide 游離二氧化 7 - total sulfur dioxide 總二氧化硫 8 - density 密度 9 - pH 酸鹼度 10 - sulphates 硫酸鹽 11 - alcohol 酒精 輸出標簽 (品酒師的感受值): 12 - 品質(分值0-10) 其它: 13 - id (樣本序號)
首先創建訓練和預測用的數據結構:
public class WineData { [Column(ordinal: "0")] public float FixedAcidity; [Column(ordinal: "1")] public float VolatileAcidity; [Column(ordinal: "2")] public float CitricACID; [Column(ordinal: "3")] public float ResidualSugar; [Column(ordinal: "4")] public float Chlorides; [Column(ordinal: "5")] public float FreeSulfurDioxide; [Column(ordinal: "6")] public float TotalSulfurDioxide; [Column(ordinal: "7")] public float Density; [Column(ordinal: "8")] public float PH; [Column(ordinal: "9")] public float Sulphates; [Column(ordinal: "10")] public float Alcohol; [Column(ordinal: "11", name: "Label")] public float Quality; [Column(ordinal: "12")] public float Id; } public class WinePrediction { [ColumnName("Score")] public float PredictionQuality; }
訓練部分,這次使用了一個對象叫ColumnDropper,可以用來在訓練開始前舍棄掉不需要的字段,比如id,對結果沒有任何影響,因此可以去掉。
static PredictionModel<WineData, WinePrediction> Train() { var pipeline = new LearningPipeline(); pipeline.Add(new Microsoft.ML.Data.TextLoader(DataPath).CreateFrom<WineData>(useHeader: true, separator: ',', trimWhitespace: false)); pipeline.Add(new ColumnDropper() { Column = new[] { "Id" } }); pipeline.Add(new ColumnConcatenator("Features", "FixedAcidity", "VolatileAcidity", "CitricACID", "ResidualSugar", "Chlorides", "FreeSulfurDioxide", "TotalSulfurDioxide", "Density", "PH", "Sulphates", "Alcohol")); pipeline.Add(new FastTreeRegressor()); var model = pipeline.Train<WineData, WinePrediction>(); return model; }
評估部分,要注意三個值 ,Rmsj是均方根值,用來展示有效度的,LossFn是損失值,顧名思義損失的程度嘛,當然越低越好了,RSquared是誤差值,用來反映擬合度好壞的,如果接近0甚至負數,說明訓練出來的模型已瞎,還不如人眼了。
static void Evaluate(PredictionModel<WineData, WinePrediction> model) { var testData = new Microsoft.ML.Data.TextLoader(TestDataPath).CreateFrom<WineData>(useHeader: true, separator: ',', trimWhitespace: false); var evaluator = new Microsoft.ML.Models.RegressionEvaluator(); var metrics = evaluator.Evaluate(model, testData); Console.WriteLine("Rms=" + metrics.Rms); Console.WriteLine("LossFn=" + metrics.LossFn); Console.WriteLine("RSquared = " + metrics.RSquared); }
預測部分,如上一篇那樣,通過新版本的TextLoader進行加載,並且處理成集合。
static void Predict(PredictionModel<WineData, WinePrediction> model) { using (var environment = new TlcEnvironment()) { var textLoader = new Microsoft.ML.Data.TextLoader(TestDataPath).CreateFrom<WineData>(useHeader: true, separator: ',', trimWhitespace: false); var experiment = environment.CreateExperiment(); var output = textLoader.ApplyStep(null, experiment) as ILearningPipelineDataStep; experiment.Compile(); textLoader.SetInput(environment, experiment); experiment.Run(); var data = experiment.GetOutput(output.Data); var wineDatas = new List<WineData>(); using (var cursor = data.GetRowCursor((a => true))) { var getters = new ValueGetter<float>[]{ cursor.GetGetter<float>(0), cursor.GetGetter<float>(1), cursor.GetGetter<float>(2), cursor.GetGetter<float>(3), cursor.GetGetter<float>(4), cursor.GetGetter<float>(5), cursor.GetGetter<float>(6), cursor.GetGetter<float>(7), cursor.GetGetter<float>(8), cursor.GetGetter<float>(9), cursor.GetGetter<float>(10), cursor.GetGetter<float>(11), cursor.GetGetter<float>(12) }; while (cursor.MoveNext()) { float value0 = 0; float value1 = 0; float value2 = 0; float value3 = 0; float value4 = 0; float value5 = 0; float value6 = 0; float value7 = 0; float value8 = 0; float value9 = 0; float value10 = 0; float value11 = 0; float value12 = 0; getters[0](ref value0); getters[1](ref value1); getters[2](ref value2); getters[3](ref value3); getters[4](ref value4); getters[5](ref value5); getters[6](ref value6); getters[7](ref value7); getters[8](ref value8); getters[9](ref value9); getters[10](ref value10); getters[11](ref value11); getters[12](ref value12); var wdata = new WineData() { FixedAcidity = value0, VolatileAcidity = value1, CitricACID = value2, ResidualSugar = value3, Chlorides = value4, FreeSulfurDioxide = value5, TotalSulfurDioxide = value6, Density = value7, PH = value8, Sulphates = value9, Alcohol = value10, Quality = value11, Id = value12, }; wineDatas.Add(wdata); } } var predictions = model.Predict(wineDatas); var wineDataAndPredictions = wineDatas.Zip(predictions, (wineData, prediction) => (wineData, prediction)); Console.WriteLine($"Wine Id: {wineDataAndPredictions.Last().wineData.Id}, Quality: {wineDataAndPredictions.Last().wineData.Quality} | Prediction: { wineDataAndPredictions.Last().prediction.PredictionQuality}"); Console.WriteLine(); } }
最后調用Main函數部分。
static void Main(string[] args) { var model = Train(); Evaluate(model); Predict(model); }
好了,運行起來結果如下:
怎么樣,沒喝過酒咱也能當一把品酒師,專業程度照樣不會差!
本案例代碼和數據:下載
更多案例值得期待!
不少新入門的伙伴來問,有沒有給力的學習課程,想早一點跨入機器學習的門檻,我在《機器學習課程不完全收錄(持續更新)》收集了一些,都是容易學起來的課程,理論與實踐都有,不論怎么說比我講的要高出不知多少個檔次了,可以按自己的喜好選擇。