Thư viện Machine Learning cho nhà phát triển .NET

Giới thiệu

Các thư viện Machine Learning (ML) như TensorFlowKeras , 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
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:

1
2
3
4
5
6
7
8
9
10
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:

1
2
3
4
5
6
7
8
9
10
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.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *