Hi Rekan Dev,
Met Lebaran ya, semoga pahala puasanya diterima Allah SWT.
Btw, beberapa waktu lampau Tim ML.NET Microsoft sempet bikin questionaire tentang support deep learning di ekosistem .NET.
Nah sepertinya kalau saya tebak-tebak bakalan mengarah ke library yang satu ini yaitu TorchSharp yaitu binding library .NET ke Torch library yaitu library ML open source yang dikembangkan oleh FAIR, Facebook AI Research Lab. Nah karena binding langsung ke dll nativenya, ya harusnya bisa lebih cepat dari versi pythonnya yaitu Pytorch.
Di beberapa posting sebelumnya mungkin kita pernah membahas Scisharp Stack, yaitu kumpulan library ML untuk .NET, salah satunya yaitu Tensorflow.NET dan Keras.Net, terbukti 2 library tersebut bisa jalan lebih cepat dari versi Pythonnya.
Kenapa TorchSharp ? mungkin beberapa gambar berikut bisa menjelaskan sendiri.
Yang jelas ini kabar baik untuk .NET Dev yang mau bangun sendiri model deep learningnya menggunakan Torch library.
Oke kali ini kita bikin contoh sederhana (hello world) versi TorchSharp. Menggunakan data IRIS yang bisa di download di UCI Machine Learning Repository: Iris Data Set data ini tentang klasifikasi Iris berdasarkan panjang lebar sepal dan petal.
Pastikan beberapa hal ini sudah di siapkan di pc rekan-rekan:
- Install VSCode atau visual studio dari Download Visual Studio Code – Mac, Linux, Windows
- Lalu install .NET 6 kalau belum terinstall, dapatkan dari Download .NET (Linux, macOS, and Windows) (microsoft.com)
- Clone projectnya dari repo ini Gravicode/TorchSharp.Learn: This is repo contains some demos of TorchSharp Library (github.com)
- Silakan buka dengan visual studio lalu di Run.
Penjelasan Program
Nah pada struktur project itu tedapat 3 folder yaitu:
- Experiment: berisi fungsi untuk melakukan training dan testing model
- Helper: berisi fungsi-fungsi untuk otak-atik dataset
- Model: struktur model NN yang mau dibuat
Nah modelnya sendiri kita buat menggunakan 1 hidden layer dengan fungsi aktivasi Relu. Input ada 4 node, hidden layer ada 64 node, dan output ada 3 yaitu untuk masing” class Iris. Lalu kita gunakan softmax untuk menentukan nilai terbesar dari 3 node output.
public class IrisModel : Module
{
private Module layer1 = Linear(4, 64);
private Module layer2 = Linear(64, 3);
private Module relu1 = ReLU();
//private Module dropout1 = Dropout(0.25);
private Module logsm = nn.Softmax(1);
public IrisModel(string name, torch.Device device = null) : base(name)
{
RegisterComponents();
if (device != null && device.type == DeviceType.CUDA)
this.to(device);
}
public override Tensor forward(Tensor input)
{
//1 hidden layer
var l11 = layer1.forward(input);
var l12 = relu1.forward(l11);
var l31 = layer2.forward(l12);
return logsm.forward(l31);
}
}
Untuk training kita gunakan Adam untuk optimizer, dan loss function kita gunakan cross entropy untuk multi-class classification
internal static void TrainingLoop(string dataset, int timeout, Device device, Module model, IEnumerable<(Tensor, Tensor)> train, IEnumerable<(Tensor, Tensor)> test)
{
var optimizer = optim.Adam(model.parameters());
//var optimizer = torch.optim.SGD(model.parameters(), learningRate: 0.01);
var criterion = cross_entropy_loss();//# cross entropy loss
Stopwatch totalTime = new Stopwatch();
totalTime.Start();
for (var epoch = 1; epoch <= _epochs; epoch++)
{
Train(model, optimizer, criterion, device, train, epoch, 1, train.Count());
Test(model, criterion, device, test, test.Count());
Console.WriteLine($"End-of-epoch memory use: {GC.GetTotalMemory(false)}");
if (totalTime.Elapsed.TotalSeconds > timeout) break;
}
totalTime.Stop();
Console.WriteLine($"Elapsed time: {totalTime.Elapsed.TotalSeconds:F1} s.");
Console.WriteLine("Saving model to '{0}'", dataset + ".model.bin");
model.save(dataset + ".model.bin");
}
private static void Train(
Module model,
optim.Optimizer optimizer,
Loss loss,
Device device,
IEnumerable<(Tensor, Tensor)> dataLoader,
int epoch,
long batchSize,
long size)
{
model.train();
int batchId = 1;
Console.WriteLine($"Epoch: {epoch}...");
foreach (var (data, target) in dataLoader)
{
using (var d = torch.NewDisposeScope())
{
optimizer.zero_grad();
var prediction = model.forward(data);
var output = loss(prediction, target);
output.backward();
optimizer.step();
if (batchId % _logInterval == 0)
{
Console.WriteLine($"\rTrain: epoch {epoch} [{batchId * batchSize} / {size}] Loss: {output.ToSingle():F4}");
}
batchId++;
}
}
}
private static void Test(
Module model,
Loss loss,
Device device,
IEnumerable<(Tensor, Tensor)> dataLoader,
long size)
{
model.eval();
double testLoss = 0;
int correct = 0;
foreach (var (data, target) in dataLoader)
{
using (var d = torch.NewDisposeScope())
{
var prediction = model.forward(data);
var output = loss(prediction, target);
testLoss += output.ToSingle();
correct += prediction.argmax(1).eq(target).sum().ToInt32();
}
}
Console.WriteLine($"Size: {size}, Total: {size}");
Console.WriteLine($"\rTest set: Average loss {(testLoss / size):F4} | Accuracy {((double)correct / size):P2}");
}
Berikut adalah outputnya, kita bisa juga simpan hasil trainingnya ke dalam file.
Selamat berexplorasi,
Salam Dev ;D