Argus Technology — Machine Learning Project Engineer

2019–2025
Argus Technology — Machine Learning Project Engineer

2019-2025 — Project-based computer vision, deep learning, pattern recognition and signal processing, combining models, data pipelines and CPU/GPU performance engineering with C/C++, Python, OpenCV, CUDA and OpenMP.

From 2019 to 2025, I worked with Argus Technology in Ankara on a project basis as a Machine Learning Project Engineer. My work concentrated on computer vision, deep learning, pattern recognition, signal processing and performance-oriented software.

Because the work was project-based, this public description does not identify clients, datasets, field systems, camera layouts or proprietary architectures. The objective is not to reveal which closed project used which data. It is to describe the engineering problems that appeared between model development and a usable software system.

I worked primarily with C and C++, together with Python, OpenCV, CUDA, OpenMP, Keras and TensorFlow, mostly on Debian-based Linux systems. This period made the distinction between "model accuracy" and "production-usable AI component" much more concrete through performance, data handling and resource-management constraints.

Treating Computer Vision as a Pipeline

The performance of an image-processing or deep-learning system is not just the execution time of the model call. Acquisition, decoding, color or size conversion, normalization, tensor preparation, inference, post-processing and delivery of the result all contribute to end-to-end latency.

I therefore stopped treating the model as a box isolated from the rest of the system. An optimization is valuable only if it improves the complete pipeline. Saving a few milliseconds inside inference may be irrelevant when image decoding, copying or I/O dominates the request.

OpenCV preprocessing and classical image-processing steps could be just as important as the neural network. Inconsistent preprocessing can reduce the effective quality of a strong model, while unnecessary memory copies can erase part of the benefit gained from GPU acceleration.

Performance-Sensitive Components in C and C++

I used C and C++ when processing required more explicit control over memory layout, copying, loops and parallel execution.

With large image arrays or repeated feature extraction, algorithmic complexity and memory access patterns matter together. Two implementations with similar Big-O complexity can behave very differently because of cache behavior and data layout.

This period also changed the way I interpreted profiler output. The hottest function is not always the complete explanation. What the CPU is waiting for, how many times data is copied, whether I/O stalls computation and whether parallelization overhead cancels the theoretical gain all belong to the same performance analysis.

CPU Parallelism with OpenMP

I used OpenMP for workloads that could be divided into sufficiently independent CPU tasks. The goal was not to attach parallel for to every loop.

Work distribution, shared-data access, cache contention and the size of the parallel region all mattered. A small task split across many threads can become slower because scheduling and synchronization cost exceed the useful work.

I therefore treated thread count and partitioning as measured parameters rather than fixed assumptions.

CUDA and CPU/GPU Work Partitioning

With CUDA, one of the central questions was the boundary between computation and data movement. A kernel can be extremely fast while the overall application remains slow if every invocation moves large buffers between CPU and GPU.

I worked on deciding which steps justified GPU execution, how long data could remain resident in device memory, how large each kernel invocation should be and which operations were better left on the CPU.

GPU use by itself is not a performance result. Total latency, throughput, memory consumption and transfer cost have to be evaluated together.

That view continued into my later heterogeneous-computing work. My IBM POWER9 digital-forensics and AI work is a broader example of evaluating CPU SIMD, OpenMP and CUDA within the same compute problem.

Deep Learning and Model Development

I worked with Keras and TensorFlow for deep-learning development. Data preparation, training, validation and inference had to be treated as separate stages with different failure modes.

A model that performs well on its training or validation distribution is not guaranteed to behave the same way on field data. Image scale, illumination, compression, sensor characteristics and preprocessing differences can all shift the input distribution.

I therefore looked beyond a single metric such as accuracy. The type of error, false-positive/false-negative balance and the system consequence of a wrong decision were also relevant.

Different projects emphasized different objectives. Batch processing and interactive inference do not necessarily use the same model-selection criteria. Latency, throughput and accuracy had to be prioritized according to the actual system requirement.

Pattern Recognition and Feature Representation

My work also included pattern-recognition methods outside deep learning. I worked with representations that extracted information relevant to the problem rather than always using the raw signal directly.

I did not treat classical features and learned representations as mutually exclusive generations of technology. Depending on data volume, explainability and compute constraints, a simpler feature and classifier can still provide lower cost and more predictable failure behavior.

That engineering view later carried into similarity search, face recognition and speech processing: the most complex model is not automatically the best system design.

Signal-Processing Perspective

The work was not limited to images. Signal-processing and pattern-recognition problems were evaluated with the same performance mindset.

Filtering, transforms, windowing and feature extraction have numerical requirements, but they also have computational cost. Repeated allocations or data copies that seem negligible for one sample can become dominant at scale.

This became useful groundwork for my later speech and audio-processing systems.

The Boundary Between Python and Native Code

Python was productive for experimentation, data preparation and model orchestration. Keeping every performance-critical operation at the Python level, however, was not always appropriate.

I used the boundary between Python, native C/C++ and GPU components according to the actual bottleneck. High-level orchestration could remain in Python while dense computation stayed in optimized libraries or lower-level code.

The principle was not "Python is slow." The real question was whether the bottleneck was actually at the language boundary and whether moving it to native code justified the additional engineering complexity.

Linux as Part of Reproducible Execution

Debian-based Linux systems were an important runtime environment for this work. Compiler chains, native dependencies, GPU drivers and Python packages all influenced whether the same code and model would behave reproducibly.

Source code and model weights can remain unchanged while library or driver versions alter numerical behavior or performance. I therefore treated the execution environment as part of the experiment and production behavior rather than as a separate installation concern.

Latency and Throughput Are Different Requirements

During this period I became more explicit about separating latency from throughput. The time required to process one image and the number of images a system can process sustainably per second are different questions.

Larger batches may increase GPU utilization and throughput while increasing waiting time for an individual item. A configuration that is ideal for offline processing may be poor for an interactive application.

For that reason, "faster" is not a technical requirement until the target metric is defined.

Memory, I/O and Data Movement

In image and model pipelines, data movement can cost as much as computation. File or network input, decoding, matrix copies, CPU/GPU transfers and output serialization can leave processors waiting for the next stage.

Low CPU utilization therefore does not prove that the system has spare compute capacity. The real bottleneck may be I/O, memory bandwidth or synchronization.

A kernel-only benchmark can therefore be misleading. An end-to-end pipeline profile is closer to what users or services actually experience.

Balancing Accuracy Against Production Cost

A small improvement in an accuracy metric may not be valuable for every system if it greatly increases inference cost. Model size, memory footprint, loading time and the number of concurrent instances that fit on the hardware also affect the decision.

I learned to evaluate model choice together with the system's operating budget rather than only an academic metric. A smaller and more predictable model can provide better total system behavior in some contexts than a heavier model with higher theoretical accuracy.

Separating Experimental and Production Code

In machine-learning work, a notebook or research prototype and a long-running application do not have the same quality requirements. Rapid iteration is useful during experimentation, while production needs explicit failure behavior, resource management, reproducibility and version control.

I therefore did not consider a successful experiment to be a production component by default. Data preparation had to become explicit, dependencies needed control, input/output contracts had to be defined and failure paths had to be handled.

From a Model to a Software System

During the Argus period I increasingly stopped treating machine learning as a "model file" problem. In a real application, the model shared one processing path with data preparation, validation, resource management, concurrency, I/O and the surrounding service or user interface.

I therefore evaluated model accuracy together with whether the result could be reproduced within the required time and resource budget. In performance-sensitive work, algorithm choice, data movement, copying and hardware placement all became architectural decisions.

Failure Behavior and Reproducibility

Performance work was useful only when the optimized path remained reproducible and diagnosable. A faster implementation that silently changed preprocessing, numeric behavior or error handling was not an improvement. I therefore treated test inputs, comparable outputs and controlled runtime dependencies as part of optimization work.

This was especially important when moving between Python, native libraries and GPU execution. The same high-level operation could pass through different numeric kernels or library versions. Performance measurements were meaningful only when the functional contract being measured stayed stable.

Confidentiality and Public Technical Scope

I do not carry client, dataset or product-architecture details into public descriptions of this work. The engineering content can still be made concrete through computer vision, signal processing, representation choices, CPU/GPU partitioning and measurable performance targets.

The 2019-2025 projects became an important source of the model + data pipeline + hardware + performance view that I later applied to digital forensics and large-scale AI systems.