Giới thiệu

Các thư viện Machine Learning (ML) như TensorFlow, Keras , CNTK hay PyTorch sử dụng ngôn ngữ Python làm nền tảng và rất khó cho các ứng dụng .NET có thể truy cập đến các thư viện này. Vì vậy, các nhà phát triển .NET đã phát triển thư viện ML.NET như là chiếc cầu nối giữa các thư viện Python và ứng dụng .NET.

Chương trình DEMO

Bài viết này sẽ hướng dẫn cách dùng ML.NET trong Visual Studio 2017 (tôi đang dùng VS 2017 Community) bằng cách tạo một chương trình dựa trên bài viết Introduction to the ML.NET Library của James McCaffrey trong tạp chí MSDN tháng 11/2018.

Chương trình xây dựng một mô hình ML để dự đoán một bệnh nhân (patient) sẽ chết (die) hay sống sót (survive) dựa trên các đặc trưng (features) như tuổi (age), giới tính (sex) và chỉ số chức năng thận (score on a kidney medical test). Vì kết quả dự đoán chỉ có hai khả năng là sống hay chết nên đây là vấn đề phân loại nhị phân (binary classification problem) trong Machine Learning.

Bước 1: tạo ứng dụng C# Console (.NET Framework)

Mở Visual Studio 2017 tạo một dự án C# Console Application (.NET Framework) và đặt tên nó là MyFirstMLDOTNET. Trong cửa sổ Solution Explorer, tìm đến tập tin Program.cs và đổi tên thành MyFirstMLDOTNET.cs

Bước 2: cài đặt thư viện ML.NET

Cũng trong cửa sổ Solution Explorer, nhấn chuột phải vào dự án MyFirstMLDOTNET và chọn Manage NuGet Packages

Trong cửa sổ NuGet chọn tab Browse và gõ cụm từ “ML.NET” chọn Microsoft.ML và nhấn Install:

Nhấn OK từ hộp thoại Preview Changes và nhấn I Accept trong License Acceptance và chờ cài đặt. Nếu cài thành công sẽ xuất hiện thông điệp trong cửa sổ Output như sau:

Lúc này nếu thực thi ứng dụng chúng ta sẽ nhận lỗi sau:

Khắc phục lỗi này bằng cách nhấn chuột phải vào dự án MyFirstMLDOTNET và chọn Properties. Trong cửa sổ Properties chọn Build bên trái và thay đổi từ Any CPU đến x64 trong mục Platform target:

Nhấn Save và chọn mục Application. Trong Target framework chọn .NET Framework 4.7 trở lên, nếu chưa cài đặt thì chọn Install other frameworks sẽ đến trang chủ Microsoft để tải gói Developer Pack và cài đặt

Nếu chúng ta chọn .NET Framework phiên bản thấp hơn 4.7 sẽ gặp các lỗi khi thực thi một số thư viện toán học. Lúc này chúng ta thực thi lại chương trình sẽ không còn xuất hiện lỗi.

Bước 3: Tạo tập tin dữ liệu huấn luyện

Sau khi tạo thành công một ứng dụng, chúng ta sẽ tạo một tập tin chứa các dữ liệu dùng cho việc huấn luyện (training data) bằng cách nhấn chuột phải vào dự án MyFirstMLDOTNET và chọn Add > New Item và chọn kiểu Text File và gõ tận tập tin là myMLData.txt

Và nhấn Add. Trong tập tin myMLData.txt nhập nội dung sau:


48, +1, 4.40, survive

60, -1, 7.89, die

51, -1, 3.48, survive

66, -1, 8.41, die

40, +1, 3.05, survive

44, +1, 4.56, survive

80, -1, 6.91, die

52, -1, 5.69, survive

56, -1, 4.01, survive

55, -1, 4.48, survive

72, +1, 5.97, survive

57, -1, 6.71, die

50, -1, 6.40, survive

80, -1, 6.67, die

69, +1, 5.79, survive

39, -1, 5.42, survive

68, -1, 7.61, die

47, +1, 3.24, survive

45, +1, 4.29, survive

79, +1, 7.44, die

44, -1, 2.55, survive

52, +1, 3.71, survive

55, +1, 5.56, die

76, -1, 7.80, die

51, -1, 5.94, survive

46, +1, 5.52, survive

48, -1, 3.25, survive

58, +1, 4.71, survive

44, +1, 2.52, survive

68, -1, 8.38, die

Bước 4: sử dụng thư viện ML.NET

Trong tập tin MyFirstMLDOTNET.cs thay nội dung đã có bằng nội dung sau:


using System;

using Microsoft.ML.Runtime.Api;

using System.Threading.Tasks;

using Microsoft.ML.Legacy;

using Microsoft.ML.Legacy.Data;

using Microsoft.ML.Legacy.Transforms;

using Microsoft.ML.Legacy.Trainers;

using Microsoft.ML.Legacy.Models;


namespace MyFirstMLDOTNET

{

    class MyFirstMLDOTNET

    {

       public class myData

      {

       [Column(ordinal: "0", name: "Age")]

       public float Age;

       [Column(ordinal: "1", name: "Sex")]

       public float Sex;

       [Column(ordinal: "2", name: "Kidney")]

       public float Kidney;

       [Column(ordinal: "3", name: "Label")]

       public string Label;

   }

   public class myPrediction

   {

     [ColumnName("PredictedLabel")]

     public string PredictedLabels;

   }


static void Main(string[] args)

{

  Console.WriteLine("ML.NET (v0.3.0 preview) demo run");

  Console.WriteLine("Survival based on age, sex, kidney");

  var pipeline = new LearningPipeline();

  string dataPath = "..\\..\\myMLData.txt";

  pipeline.Add(new TextLoader(dataPath).

  CreateFrom<myData>(separator: ','));

  pipeline.Add(new Dictionarizer("Label"));

  pipeline.Add(new ColumnConcatenator("Features", "Age","Sex", "Kidney"));

  pipeline.Add(new Logistic​Regression​Binary​Classifier());

  pipeline.Add(new PredictedLabelColumnOriginalValueConverter()

         { PredictedLabelColumn = "PredictedLabel" });

  Console.WriteLine("\nStarting training \n");

  var model = pipeline.Train<myData, myPrediction>();

  Console.WriteLine("\nTraining complete \n");

  string ModelPath = "..\\..\\myFirstMLModel.zip";

  Task.Run(async () =>

  {

    await model.WriteAsync(ModelPath);

   }).GetAwaiter().GetResult();

  var testData = new TextLoader(dataPath).

  CreateFrom<myData>(separator: ',');

  var evaluator = new BinaryClassificationEvaluator();

  var metrics = evaluator.Evaluate(model, testData);

  double acc = metrics.Accuracy * 100;

  Console.WriteLine("Model accuracy = " + acc.ToString("F2") + "%");

  Console.WriteLine("Predict 50-year male, Kidney 4.80:");

  myData newPatient = new myData(){ Age = 50f, 
                Sex = -1f, Kidney = 4.80f };

  myPrediction prediction = model.Predict(newPatient);

  string result = prediction.PredictedLabels;

  Console.WriteLine("Prediction = " + result);

  Console.WriteLine("\nEnd ML.NET demo");

  Console.ReadLine();

  }

 }

}

Bước 5: thực thi ứng dụng

Thực thi sẽ cho kết quả:

Giải thích

Chúng ta tạo lớp myData bên trong lớp MyFirstMLDOTNET định nghĩa cấu trúc dữ liệu cho dữ liệu huấn luyện. Các thông tin gồm Age, Sex, Kidney và Label (survive hay die). Lưu ý rằng chúng ta dùng kiểu dữ liệu float (cho Age, Sex, Kidney) thay vì dùng double vì trong ML kiểu float là kiểu mặc định. Chúng ta cũng tạo ra lớp myPrediction để chứa thông tin về dự đoán.

Chúng ta tạo mô hình ML với các lệnh sau:


var pipeline = new LearningPipeline();

string dataPath = "..\\..\\myMLData.txt";

pipeline.Add(new TextLoader(dataPath).CreateFrom<myData>(separator: ','));

pipeline.Add(new Dictionarizer("Label"));

pipeline.Add(new ColumnConcatenator("Features", "Age", "Sex", "Kidney"));

pipeline.Add(new Logistic​Regression​Binary​Classifier());

pipeline.Add(new PredictedLabelColumnOriginalValueConverter() 
        { PredictedLabelColumn = "PredictedLabel" });

Đối tượng pipeline đại diện cho mô hình ML chưa được huấn luyện kết hợp với dữ liệu dùng cho huấn luyện. Mỗi hàng dữ liệu huấn luyện chứa giá trị cuối là “survive” hay “die” là các chuỗi (kiểu String) nhưng mô hình ML chỉ đọc được giá trị số nên lớp Dictionarizer được dùng để mã hóa hai chuỗi thành các giá trị 0 hay 1. ColumnConcatenator kết hợp các cột Age, Sex, và Kidney trong một cột gọi là Features. Ở đây kỹ thuật logistic regression được sử dụng để giải quyết vấn đề phân loại nhị phân của chúng ta.

Mô hình được huấn luyện và lưu lại như sau:


var model = pipeline.Train<myData, myPrediction>();

Console.WriteLine("\nTraining complete \n");

string ModelPath = "..\\..\\myFirstMLModel.zip";

Task.Run(async () =>

{

   await model.WriteAsync(ModelPath);

}).GetAwaiter().GetResult();

Đánh giá mô hình sau khi được huấn luyện:

 
var testData = new TextLoader(dataPath).
              CreateFrom<myData>(separator: ',');

var evaluator = new BinaryClassificationEvaluator();

var metrics = evaluator.Evaluate(model, testData);

double acc = metrics.Accuracy * 100;

Console.WriteLine("Model accuracy = " + acc.ToString("F2") + "%");

Cuối dùng là sử dụng mô hình để thực hiện dự đoán:

 
Console.WriteLine("Predict 50-year male, Kidney 4.80:");

myData newPatient = new myData(){ Age = 50f, 
         Sex = -1f, Kidney = 4.80f };

myPrediction prediction = model.Predict(newPatient);

string result = prediction.PredictedLabels;

Console.WriteLine("Prediction = " + result);

Lời cuối

Thư viện ML.NET đang được tiếp tục hoàn thiện và phát triển, là công cụ phục vụ nghiên cứu ML rất hữu ích cho các nhà phát triển và người học .NET. Chúng ta có thể khám phá thêm về thư viện ML.NET tại đây. James McCaffrey chuyên viết về các chủ đề ML trong .NET và chúng ta có thể tìm hiểu các bài viết khác của James tại đây.