Việc tích hợp AI như một thành phần thông thường trong dự án C# .NET, tận dụng hiệu năng biên dịch và giảm chi phí tích hợp, là con đường trực tiếp và hiệu quả nhất cho các đội nhóm đã quen thuộc và sử dụng hệ sinh thái .NET. Nếu sử dụng stack Python để phát triển các mô hình học máy trong dự án .NET, có thể phát sinh các chi phí bổ sung đáng kể như liên ngôn ngữ, microservice độc lập, container hóa, vận hành song stack. Việc biên dịch trực tiếp các mô ứng dụng như số liệu phù hợp, phân loại vector, nhận diện hình ảnh, đại mô hình thông minh, MCP vào dự án sẽ rất hữu ích, dù là đóng gói file đơn hay biên dịch AOT, và giúp nâng cao khả năng cạnh tranh sản phẩm hiện có.
Với lý do này, chúng tôi sẽ bắt đầu chuỗi bài viết về phát triển AI với C#, chia sẻ từ đầu đến cuối về ứng dụng thực tế của C# trong lĩnh vực AI. Bài viết này tập trung vào học sâu, một nhánh của học máy cố gắng mô phỏng nguyên lý hoạt động của não người thông qua cấu trúc phức tạp gọi là "mạng nơ-ron nhân tạo", từ đó học và trích xuất các quy luật高层次 từ lượng lớn dữ liệu.
I. Chuẩn bị môi trường thành phần
Có hai framework chính để phát triển học sâu bằng C#: TensorFlow.Net và TorchSharp (tương ứng với TensorFlow và PyTorch trong Python), cũng là các thư viện cốt lõi cho mô hình học sâu của ML.NET. So sánh chi tiết như sau:
| Đặc điểm | TensorFlow.Net & Keras | TorchSharp |
|---|---|---|
| Mục đích chính | Nền tảng học sâu mã nguồn mở từ đầu đến cuối được phát triển bởi Google | Được phát triển bởi Facebook, nổi tiếng với biểu đồ tính toán động và thiết kế Pythonic |
| Thiết kế API | Keras được tích hợp chặt chẽ như API cấp cao (tf.keras), cung cấp giao diện đơn giản trực quan | API gốc thiết kế theo phong cách Python, mã nguồn trực quan dễ hiểu |
| Đặc điểm chính | - Hệ sinh thái rộng lớn, khả năng triển khai mạnh mẽ, phù hợp dự án lớn và ứng dụng công nghiệp | Rất phổ biến trong nghiên cứu học thuật, cung cấp tính linh hoạt và tiện lợi khi gỡ lỗi, phát triển hỗ trợ môi trường sản xuất nhanh chóng trong những năm gần đây |
| Ứng dụng | Môi trường sản xuất, ứng dụng công nghiệp, dự án lớn | Nghiên cứu học thuật, phát triển nguyên mẫu nhanh, các tình huống cần tính linh hoạt |
| Triết lý cốt lõi | Mặc định biểu đồ tĩnh (Static Graph), định nghĩa trước rồi thực thi | Mặc định biểu đồ động (Dynamic Graph) thực thi ngay lập tức |
| Kích thước thư viện | Khoảng 237mb (nén khoảng 73mb) | Khoảng 250mb (nén khoảng 77mb) |
Cả hai framework đều rất mạnh mẽ với kích thước thư viện tương đương, bạn có thể lựa chọn dựa trên đặc điểm dự án và thói quen sử dụng. Tuy nhiên, trong quá trình thử nghiệm, chúng tôi nhận thấy TorchSharp có thể thực thi biên dịch AOT đơn giản bằng cách cấu hình đối tượng Module vào file rd.xml; trong khi TensorFlow.NET gặp nhiều lỗi khi chạy sau biên dịch AOT, các chức năng liên quan vẫn cần thử nghiệm thêm.
Trong bài viết này, chúng ta sẽ tập trung vào TensorFlow. Dưới đây là một ví dụ về mạng nơ-ron nhân tạo (ANN) đơn giản để minh họa cách sử dụng TensorFlow.NET.
II. Cấu trúc mã cốt lõi - TensorFlow.NET
Trọng tâm của học sâu là mạng nơ-ron sâu. Với mạng nơ-ron nhân tạo truyền tiếp (Feed-forward ANN) cơ bản, để tạo một ANN cần chuẩn bị ba yếu tố: tập dữ liệu, mô hình mạng và phương pháp huấn luyện. Mục tiêu của chúng ta là huấn luyện một mô hình với 500 bộ dữ liệu, khi nhập vào hai giá trị a và b, mô hình sẽ dự đoán chính xác nhất bình phương của tổng chúng, tức là aa + bb.
2.1 Cấu hình môi trường
Trước khi bắt đầu, chúng ta cần thêm các phụ thuộc liên quan đến TensorFlow.NET. TensorFlow.NET là thành phần mã nguồn mở với giấy phép Apache-2.0, chúng ta có thể thêm các gói nuget liên quan trực tiếp vào dự án:
| Tên gói | Ý nghĩa và công dụng |
|---|---|
SciSharp.TensorFlow.Redist |
Chứa các thư viện mã gốc **Native**, là bộ引擎 chạy framework. |
TensorFlow.NET |
**C# API wrapper** chính, cung cấp các lớp và phương pháp cốt lõi để thao tác tensor và biểu đồ tính toán. |
TensorFlow.Keras |
Là **mở rộng cấp cao** cho TensorFlow.NET, cung cấp giao diện xây dựng mô hình đơn giản dễ dùng như Keras. |
2.2 Chuẩn bị dữ liệu
Đối tượng dữ liệu chính mà TensorFlow sử dụng là NDArray. Chúng ta sẽ tạo ngẫu nhiên 500 bộ dữ liệu để huấn luyện. Trong đó, x là NDArray có kích thước 500×2, lưu trữ giá trị đầu vào; y là NDArray có kích thước 500×1, lưu trữ giá trị đầu ra. Trước khi tạo dữ liệu, có thể cố định hạt giống của trình tạo số ngẫu nhiên để đảm bảo mỗi lần tạo dữ liệu ngẫu nhiên đều giống nhau, giúp chúng ta dễ dàng so sánh kết quả khi thay đổi siêu tham số.
// Tạo dữ liệu
tf.set_random_seed(42); // Cố định hạt giống số ngẫu nhiên để tái tạo kết quả
// Tạo ngẫu nhiên x trong khoảng -10~10
var inputValues = np.random.randn(500, 2).astype(np.float32) * 10.0f;
// Tạo giá trị bình phương tương ứng y
var outputValues = np.sum(np.power(inputValues, 2), axis: 1);
outputValues = np.expand_dims(outputValues, 1); // Giữ nguyên kích thước (500, 1)
2.3 Xây dựng mô hình
Sau khi có dữ liệu, chúng ta có thể bắt đầu xây dựng mô hình. Chúng ta sẽ thiết kế một mô hình tuần tự (sequential model), nơi các lớp mạng nơ-ron được xếp chồng lên nhau theo thứ tự, dữ liệu chảy đơn hướng từ đầu vào đến đầu ra.
Trong Tensorflow, việc định nghĩa lớp cần chỉ định: số lượng nút, hàm kích hoạt, v.v. Tính năng Dropout cần được định nghĩa riêng (các hàm kích hoạt và thiết kế tham số Dropout trong mã chỉ mang tính minh họa, có thể sử dụng nhưng không nên tham khảo trực tiếp).
// Xây dựng mô hình
// Kích thước lớp đầu vào là 2, lớp đầu ra là 1
var neuralNetwork = keras.Sequential(); // Tạo mô hình tuần tự
neuralNetwork.add(keras.layers.Dense(64, activation: "sigmoid"));
neuralNetwork.add(keras.layers.Dense(32, activation: "relu")); // 32 nút với hàm kích hoạt relu
neuralNetwork.add(keras.layers.Dropout(0.001f)); // lớp dropout
neuralNetwork.add(keras.layers.Dense(1)); // lớp đầu ra
Sau khi hoàn thành thiết kế các lớp, cần gọi hàm compile để đặt chiến lược tối ưu hóa gradient và hàm mất mát của mô hình. Mã như sau:
// Biên dịch mô hình
neuralNetwork.compile(
optimizer: keras.optimizers.Adam(2e-3f), // tối ưu hóa gradient adam, tốc độ học 2e-3
loss: keras.losses.MeanSquaredError() // sử dụng mse làm hàm mất mát
);
2.4 Huấn luyện mô hình
Huấn luyện mô hình bằng cách gọi phương thức fit của mô hình, truyền vào đầu vào và đầu ra của tập huấn luyện. batch_size là kích thước lô huấn luyện.
// Huấn luyện mô hình
neuralNetwork.fit(
inputValues, outputValues, // đầu vào và đầu ra của tập huấn luyện
batch_size: 100, // chỉ để đơn giản hóa minh họa
epochs: 4000, // số lần lặp tối đa
verbose:0 // khoảng hiển thị thông tin, 0 là không hiển thị, 2 là hiển thị mỗi hai lần lặp.
);
Trong quá trình thử nghiệm, chúng tôi gặp một vấn đề là ở phiên bản sử dụng, tham số verbose dường như chỉ ảnh hưởng đến hàm callback, khi phân ngược thư viện thấy mã nguồn có vẻ đã cố định giá trị gọi ở底层 thành 1, do đó dù thay đổi thế nào, bảng tiến độ vẫn luôn hiển thị trên console khi huấn luyện. Tuy nhiên, điều này không ảnh hưởng đến kết quả. Có thể thấy, sau 4000 epoch, giá trị loss giảm xuống còn 5.302576.
Epoch: 4000/4000
0001/0001 [>............................] - 3ms/step - loss: 5.302576
2.5 Dự đoán mô hình và lấy dữ liệu
Truyền vào hai số bất kỳ, 3.14f và 2.5f:
// Tạo vector
var testInput = tf.constant(new float[,] { { 3.14f, 2.5f } });
var predictionResult = neuralNetwork.predict(testInput);
// Xác minh kết quả
Console.WriteLine($"Dự đoán: {predictionResult.numpy()[0,0]}");
Console.WriteLine($"Giá trị mong đợi: {3.14 * 3.14 + 2.5 * 2.5}");
Mô hình cho kết quả đầu ra:
Dự đoán: 15.160952
Giá trị mong đợi: 16.10960102081299
Sau khi hoàn thành huấn luyện và lưu trữ, chúng ta có thể tích hợp mô hình này vào mã nguồn của mình để thực hiện các tác vụ khác.
V. Kết luận
Trên đây là phần chia sẻ về việc tạo một mô hình mạng nơ-ron nhân đơn giản và huấn luyện dữ liệu để dự đoán bằng TensorFlow.NET trong C#. Thực tế, việc sử dụng TensorFlow.NET trong C# có nhiều điểm tương tự với việc sử dụng TensorFlow trong Python. Để đảm bảo tính nhất quán, TensorFlow.NET đã giữ lại cách viết theo kiểu hàm trong hầu hết các trường thông qua việc gọi phương thức tĩnh, về cơ bản tương đương với việc tạo một đối tượng mới. Khi tự đóng gói phương thức về sau, cần lưu ý tránh tạo trùng lặp:
// keras.Sequential()
public Sequential Sequential(List<ILayer> layers = null, string name = null)
{
return new Sequential(new SequentialArgs
{
Layers = layers,
Name = name
});
}
Lần tới chúng ta sẽ chia sẻ nội dung liên quan đến việc xây dựng dựa trên framework TorchSharp. Các ví dụ mã nguồn mô hình học máy phong phú và đầy đủ hơn đã được mở hoàn toàn, theo dõi kênh và trả lời AISharp để xem địa chỉ kho mã nguồn.