AI Interview interview questions
NumPy stores numerical data in contiguous blocks of memory using a fixed, uniform type, and performs operations on entire arrays at once using optimized, compiled code under the hood, a technique known as vectorization. A plain Python list stores generic objects scattered in memory and requires the interpreter to loop over them one at a time in slow, interpreted bytecode. Rewriting a loop-based numerical calculation as a vectorized NumPy operation commonly speeds it up by one or two orders of magnitude.
Common approaches include processing the data in smaller chunks rather than loading it all at once, using a library designed for out-of-core or distributed computation that can operate on data larger than available memory, or downsampling the data during initial exploration and only working with the full dataset for a final training run. Storing data in an efficient columnar format on disk also reduces how much needs to be read for any given operation.
A typical approach wraps the model behind a lightweight web framework such as FastAPI or Flask, exposing an endpoint that accepts input, runs the loaded model, and returns a prediction as a response. For higher-throughput needs, a dedicated model-serving tool can handle batching, versioning and hardware acceleration more efficiently than a hand-rolled endpoint. Either way, the serving code is usually kept separate from the training code, since the two have very different performance and reliability requirements.
Frequent culprits include row-by-row looping over a pandas DataFrame instead of using vectorized operations, repeatedly recomputing the same expensive transformation instead of caching it, and reading data from disk in an inefficient format or in many small pieces. Addressing these usually means rewriting loops as vectorized pandas or NumPy operations, caching intermediate results that do not change between runs, and switching to a more efficient file format designed for fast, columnar reads.
A GPU contains thousands of simpler cores well suited to performing the same operation across huge amounts of data simultaneously, which matches how most deep learning training works, giving it a dramatic speed advantage for that kind of workload. A CPU has far fewer, more general-purpose cores better suited to varied, sequential logic. In practice, GPUs are used heavily for training large models, while CPUs remain perfectly adequate, and often more cost-effective, for lighter inference workloads or classical machine learning models.
Data leakage happens when information from outside the training data, particularly from the test or validation set, inadvertently influences the model during training, making its performance during evaluation look better than it will actually be in the real world. A common accidental cause is applying a transformation such as scaling or filling missing values to the entire dataset before splitting it into training and test sets, letting statistics from the test data quietly leak into the training process.
Processing requests one at a time, especially on a GPU, wastes a lot of the hardware's parallel capacity, since a single small request may not fully use the available compute. Grouping several requests into one batch lets the same amount of computation serve many inputs at once, significantly increasing overall throughput, at the cost of a small amount of added latency while requests wait briefly to be grouped together before running.
Without clear versioning, it becomes very difficult to reproduce a past result, understand exactly why a model behaves differently after a change, or roll back to a previous version if a new model performs worse in production. Versioning the dataset alongside the model ensures that a given model can always be tied back to the exact data it was trained on, which matters enormously when diagnosing unexpected behaviour or meeting requirements for auditability.
Training code is typically run occasionally, can tolerate being slower, and often needs access to large historical datasets and experimentation tools. Inference code needs to be fast, reliable and lightweight, since it runs continuously in production and directly affects user-facing latency. Mixing the two together makes both harder to maintain, and can accidentally introduce training-only dependencies or slow paths into the exact code that needs to respond quickly to real requests.
The general approach mirrors profiling any other Python code: use a tool such as cProfile or a line-by-line profiler to measure where time is actually being spent, rather than assuming which step is slow. For data pipelines specifically, it is also worth checking whether pandas operations are being applied in a vectorized way or looped over row by row, and whether time is dominated by actual computation or simply by reading and writing data to disk.
pandas can be slow when a row-wise loop or an apply call with a custom Python function is used instead of a built-in vectorized operation, since each row then has to pass through the Python interpreter individually rather than being processed in bulk by optimized internal code. Rewriting such logic using pandas' own vectorized methods, or falling back to underlying NumPy operations directly, typically produces a large speedup without changing what the code actually computes.
Many preprocessing steps, such as transforming each row or file independently, are naturally parallel since one item does not depend on another. Splitting the work across multiple processes lets each one handle a portion of the data at the same time on a different CPU core, sidestepping the GIL entirely since each process has its own interpreter. The main overhead to watch for is the cost of splitting and recombining the data, which can eat into the gains if each individual chunk of work is very small.
If the same expensive feature calculation is needed repeatedly, either across multiple runs or multiple parts of a pipeline, storing the computed result to disk or in memory the first time and reusing it afterward avoids repeating the work unnecessarily. This is especially valuable for features that depend only on raw input data that rarely changes, though care is needed to invalidate the cache whenever the underlying data or the feature logic itself is updated.
Common strategies include removing rows or columns with too many missing values when little useful information would be lost, filling gaps with a simple statistic such as the mean or the most common value, or using a more sophisticated method that predicts a likely value based on other related columns. The right choice depends heavily on how much data is missing, whether it is missing at random, and what the specific model being trained can tolerate.
Reproducibility generally requires fixing random seeds used by any process that involves randomness, such as data shuffling or model initialization, recording the exact versions of every library used, and capturing precisely which version of the dataset and which configuration values produced a given result. Without all of these pinned down together, rerunning what looks like the same experiment can quietly produce a different outcome, making it hard to trust or compare results over time.