Machine Learning
Neural Networks
Python
C++
Model Deployment

Training a Neural Network in Python and deploying in C

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

A common production workflow is training a model in Python and running inference in C or C++ for integration and latency reasons. The hard part is not training itself, but keeping preprocessing, tensor shapes, and outputs consistent across languages. A robust deployment path includes portable export format, signature validation, and parity tests before release.

Train And Export In Python

Train with Python tooling, then export a serving-ready artifact.

python
1import numpy as np
2import tensorflow as tf
3
4x = np.random.rand(3000, 10).astype("float32")
5y = (x.sum(axis=1) > 5.0).astype("float32")
6
7model = tf.keras.Sequential([
8    tf.keras.layers.Dense(32, activation="relu", input_shape=(10,)),
9    tf.keras.layers.Dense(1, activation="sigmoid"),
10])
11
12model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])
13model.fit(x, y, epochs=3, batch_size=64, verbose=0)
14
15model.save("saved_model_classifier")

SavedModel is a practical default when TensorFlow runtime is used on C side.

Validate Export Signatures

Before integration, inspect saved signatures so C code uses correct input and output names.

python
1loaded = tf.saved_model.load("saved_model_classifier")
2print(list(loaded.signatures.keys()))
3serve = loaded.signatures["serving_default"]
4print(serve.structured_input_signature)
5print(serve.structured_outputs)

This avoids guessing tensor names in C++ code.

C++ Inference Skeleton

cpp
1#include <iostream>
2#include <vector>
3#include "tensorflow/cc/saved_model/loader.h"
4#include "tensorflow/core/framework/tensor.h"
5
6int main() {
7    tensorflow::SavedModelBundleLite bundle;
8    tensorflow::SessionOptions session_options;
9    tensorflow::RunOptions run_options;
10
11    auto status = tensorflow::LoadSavedModel(
12        session_options,
13        run_options,
14        "saved_model_classifier",
15        {"serve"},
16        &bundle
17    );
18
19    if (!status.ok()) {
20        std::cerr << status.message() << std::endl;
21        return 1;
22    }
23
24    tensorflow::Tensor input(tensorflow::DT_FLOAT, tensorflow::TensorShape({1, 10}));
25    auto flat = input.flat<float>();
26    for (int i = 0; i < 10; ++i) flat(i) = 0.5f;
27
28    std::vector<std::pair<std::string, tensorflow::Tensor>> feeds = {
29        {"serving_default_dense_input:0", input}
30    };
31
32    std::vector<tensorflow::Tensor> outputs;
33    status = bundle.GetSession()->Run(feeds, {"StatefulPartitionedCall:0"}, {}, &outputs);
34
35    if (!status.ok()) {
36        std::cerr << status.message() << std::endl;
37        return 1;
38    }
39
40    std::cout << outputs[0].flat<float>()(0) << std::endl;
41    return 0;
42}

Node names vary by model; always derive from actual signature inspection.

Keep Preprocessing Identical

Most cross-language deployment bugs come from preprocessing mismatch.

  • feature ordering differs,
  • normalization constants differ,
  • missing categorical encoding mapping.

Treat preprocessing as part of model contract. Export metadata that C runtime can consume directly.

Parity Testing Strategy

Before production, run the same input vectors through Python and C inference pipelines.

Recommended checks:

  1. deterministic test dataset with fixed seed,
  2. max absolute difference threshold,
  3. class decision parity near threshold cases,
  4. batch and single-item parity.

Automate this in CI for every new model artifact.

Runtime Integration Choices

You have multiple deployment options.

  • Link TensorFlow C++ runtime directly.
  • Use ONNX Runtime in C++ after model conversion.
  • Host model in dedicated serving service and call over RPC.

Choice depends on latency budget, packaging constraints, and operations maturity.

Operational Checklist

  • Warm up model on startup.
  • Validate input shape at API boundary.
  • Track per-request latency and error metrics.
  • Version model and preprocessing together.
  • Roll out with canary traffic before full switch.

Operational rigor matters more than benchmark wins from one architecture tweak.

Release Discipline

Treat every exported model as a versioned artifact with immutable metadata. Clear release notes and rollback plans reduce incident risk when model and runtime dependencies evolve independently.

Common Pitfalls

  • Hardcoding tensor names without re-validating after retraining.
  • Ignoring preprocessing parity across Python and C pipelines.
  • Testing only single-sample inference and missing batch-path issues.
  • Underestimating binary packaging complexity of ML runtimes.
  • Skipping automated cross-language regression checks.

Summary

  • Train in Python, deploy in C or C++ with explicit model contract validation.
  • Inspect export signatures and use exact tensor names.
  • Keep preprocessing identical across both environments.
  • Run parity tests before production rollout.
  • Plan runtime integration and operations from day one.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.