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:
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 LogisticRegressionBinaryClassifier()); 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 LogisticRegressionBinaryClassifier()); 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.