Demo project to show integration of Python Machine Learning functionality in a C# desktop application.
This is a flower image classifier with five classes, trained on the Kaggle data set which you can download yourself if you want to try running the training. You can also modify this code to work for any image classification experiments you might want to try.
The Classifier model is a standard Convolutional Neural Network. This is not intended to demonstrate a good or accurate solution, just the ability to run Python ML in C#. The model tends to over-fit on this data set. Go ahead and use this project as a basis for experimenting with different model architectures, hyper-parameter tuning or other data sets.
- 🐍 CSnakes - Python environment integrated into a C# project
- 💻 Avalonia UI - Cross-platform UI Framework for .NET
- 🔥 PyTorch - Machine Learning, Artificial Neural Networks
- 🖼️ SkiaSharp - .NET wrapper for the Skia image library
- 📈 LiveCharts2 - Data visualisation
In the UnitTest project you can find some code in CommunicationTests.cs and py_communication_test.py that demonstrates passing string and integers between C# and Python. For more info on conversion of data types between languages see the CSnakes User Guide.
Strings:
def greetings(name: str) -> str:
return f"Hello, {name}!"string name = "Fred";
var pyCommunicationTestModule = _env.PyCommunicationTest();
string greetingResult = pyCommunicationTestModule.Greetings(name);
Assert.Equal("Hello, Fred!", greetingResult);Integers:
def add(a: int, b: int) -> int:
return a + blong a = 5;
long b = 7;
var pyCommunicationTestModule = _env.PyCommunicationTest();
long addResult = pyCommunicationTestModule.Add(a, b);
Assert.Equal(12, addResult);For large amounts of data like image pixels it can be very slow to use Python types like list[int] and list[float]. It is faster and more efficient to use bytes. You can also use the Buffer Protocol when working with NumPy arrays. For more info see the CSnakes User Guide
def pass_bytes(data: bytes) -> bytes:
# do something to the bytes. increment each byte by 1
result_byte_array = bytearray(len(data))
for i in range(len(data)):
result_byte_array[i] = data[i] + 1
return bytes(result_byte_array)byte[] data = new byte[] { 1, 2, 3, 4, 5 };
var pyCommunicationTestModule = _env.PyCommunicationTest();
byte[] result = pyCommunicationTestModule.PassBytes(data);
Assert.Equal(new byte[] { 2, 3, 4, 5, 6 }, result);Data can be streamed back to the C# caller while Python is performing a long-running function. This is achieved using a Python Generator
def train_and_save(training_data_dir:str, model_file_name:str) -> Generator[str, int, bool]:
... long-running process
# use yield keyword to send value back to caller
yield f"Epoch: {epoch+1} of {epochs}"IGeneratorIterator<string, long, bool> messageGenerator = train.TrainAndSave(trainingDataDir, modelFileName);
foreach (string message in messageGenerator)
{
Debug.WriteLine($"{DateTime.Now} : {message}");
_env.Logger?.LogInformation(message);
}Things you need to know before you start.
- First time run will be SLOW. The Python interpreter is not included in this source code. The CSnakes library will automatically download and install Python 3.12 on the first run, set up a virtual environment and PIP install packages listed in
requirements.txt. Yes, at run-time, not build-time. Be patient, the application may look like it has hung.
var builder = Host.CreateApplicationBuilder(Array.Empty<string>());
builder.Services
.WithPython()
.WithHome(pythonHomePath)
.FromRedistributable(RedistributablePythonVersion.Python3_12) // downloads Python interpreter
.WithVirtualEnvironment(Path.Combine(pythonHomePath, ".venv")) // creates a virtual environment
.WithPipInstaller(); // pip install -r requirements.txt- Note that the line of code that saves the trained model is commented out. Uncomment it when you're ready to save for real.
# save the trained model
#torch.save(model.state_dict(), model_save_path) # TODO: uncomment this line to save the model- Requirements
- Cross-platform. Developed and tested on Windows but should work on Linux and Mac.
- .NET 9.0 SDK.
- Supports GPU Inference and Training, but assumes you have CUDA 12.9 installed. Will automatically fall back to CPU if you don't.

