Bird
Raised Fist0

Implement Data Augmentation - Flip, Crop, Normalize

CODINGbeginnerInterview freq: medium
Choose your preparation mode6 modes available
🤖
Implement Data Augmentation - Flip, Crop, Normalize
Enhance image datasets with flipping, cropping, and normalization.
codingbeginnerFreq: mediumEvaluation Coding
Prerequisites:Feature EngineeringConvolutional Neural Networks
📝
Problem Statement

Implement a function to perform data augmentation on images. The function should flip the image horizontally, crop a central region, and normalize pixel values to the range [0, 1].

Input:  A 3D NumPy array representing an image with shape (height, width, channels).
Output: A 3D NumPy array representing the augmented image with the same shape.
{
  "input": "np.array([[[255, 0, 0], [0, 255, 0]], [[0, 0, 255], [255, 255, 255]]])",
  "expected_output": "np.array([[[1.0, 1.0, 1.0], [0.0, 0.0, 1.0]], [[0.0, 1.0, 0.0], [1.0, 0.0, 0.0]]])"
}
🎯
Interview Angles
  • Testing understanding of image data manipulation using NumPy.
  • Evaluating ability to optimize data processing using vectorized operations.
  • Assessing knowledge of basic image preprocessing techniques in ML pipelines.
🔵
Naive Approach

The naive approach involves manually flipping the image by reversing the order of columns, cropping by slicing the central region, and normalizing by dividing each pixel value by 255.

Time: O(h * w) - each operation (flip, crop, normalize) processes each pixel once. Space: O(h * w) - storing the augmented image.
Why Insufficient
The naive approach is inefficient for large images due to multiple passes over the data and does not handle edge cases like non-square images gracefully.
🐍
Naive Code
Python
import numpy as np

def naive_augment(image):
    # Flip horizontally
    flipped_image = image[:, ::-1, :]
    # Crop central region
    h, w, _ = image.shape
    crop_size = min(h, w) // 2
    start_h = (h - crop_size) // 2
    start_w = (w - crop_size) // 2
    cropped_image = flipped_image[start_h:start_h+crop_size, start_w:start_w+crop_size, :]
    # Normalize
    normalized_image = cropped_image / 255.0
    return normalized_image

# Driver example
image = np.array([[[255, 0, 0], [0, 255, 0]], [[0, 0, 255], [255, 255, 255]]])
augmented_image = naive_augment(image)
print(augmented_image)
💡
Key Insight
Utilize NumPy's slicing and broadcasting capabilities to perform all operations in a single pass, minimizing data movement and leveraging efficient array operations.
🐍
Optimized Code
Python
import numpy as np

def optimized_augment(image):
    # Flip horizontally using slicing
    flipped_image = image[:, ::-1, :]
    # Determine crop size and perform central crop
    h, w, _ = flipped_image.shape
    crop_size = min(h, w) // 2
    start_h = (h - crop_size) // 2
    start_w = (w - crop_size) // 2
    cropped_image = flipped_image[start_h:start_h+crop_size, start_w:start_w+crop_size, :]
    # Normalize using broadcasting
    normalized_image = cropped_image / 255.0
    return normalized_image

# Driver example
image = np.array([[[255, 0, 0], [0, 255, 0]], [[0, 0, 255], [255, 255, 255]]])
augmented_image = optimized_augment(image)
print(augmented_image)
🔍
Line-by-Line
flipped_image = image[:, ::-1, :]
Flips the image horizontally by reversing the order of columns using slicing.
h, w, _ = flipped_image.shape
Extracts the dimensions of the image to determine the crop size.
crop_size = min(h, w) // 2
Calculates the size of the crop as half of the smaller dimension.
start_h = (h - crop_size) // 2
Determines the starting row for the central crop.
start_w = (w - crop_size) // 2
Determines the starting column for the central crop.
cropped_image = flipped_image[start_h:start_h+crop_size, start_w:start_w+crop_size, :]
Extracts the central crop from the flipped image.
normalized_image = cropped_image / 255.0
Normalizes pixel values to the range [0, 1] using broadcasting.
⚡
Complexity
Time: O(h * w) - each operation (flip, crop, normalize) processes each pixel once. Space: O(h * w) - storing the augmented image.
array([[[1.0, 1.0, 1.0], [0.0, 0.0, 1.0]], [[0.0, 1.0, 0.0], [1.0, 0.0, 0.0]]])
🔲
Edge Cases
Non-square image
The crop size is determined by the smaller dimension, ensuring a valid central crop.
crop_size = min(h, w) // 2
Single channel image
The function handles images with any number of channels due to the use of slicing and broadcasting.
flipped_image = image[:, ::-1, :]
⚠️
Common Mistakes
Symptom: IndexError due to out-of-bounds slicing.
Symptom: Output image has pixel values outside the range [0, 1].
Symptom: Significantly slower performance on large images.

Practice

(1/5)
1. What does the following Python code snippet output when implementing linear regression using gradient descent?
import numpy as np

X = np.array([[1, 2], [3, 4], [5, 6]])
y = np.array([1, 2, 3])
weights = np.zeros(X.shape[1])
learning_rate = 0.01

for _ in range(1):
    predictions = X.dot(weights)
    errors = predictions - y
    gradient = X.T.dot(errors) / len(y)
    weights -= learning_rate * gradient

print(weights)
easy
A. [0.01, 0.02]
B. [0.1, 0.2]
C. [0.03, 0.04]
D. [0.05, 0.06]

Solution

  1. Step 1: Calculate initial predictions and errors

    Initial predictions are [0, 0, 0] since weights are zeros. Errors are [-1, -2, -3].
  2. Step 2: Compute gradient and update weights

    Gradient is [-3, -6]. Updated weights are [0.01, 0.02].
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Initial weights zero, single update step. [OK]
Hint: Initial weights zero, single update step. [OK]
Common Mistakes:
2. What does the following Python code snippet output when implementing Mini-Batch SGD with Momentum?
import numpy as np

velocity = np.array([0.0, 0.0])
gradient = np.array([0.1, 0.2])
learning_rate = 0.01
momentum = 0.9

velocity = momentum * velocity - learning_rate * gradient
print(velocity)
easy
A. array([0.01, 0.02])
B. array([0.001, 0.002])
C. array([-0.001, -0.002])
D. array([-0.01, -0.02])

Solution

  1. Step 1: Calculate the velocity update

    The formula for velocity update is velocity = momentum * velocity - learning_rate * gradient. Given the initial velocity is zero, the update becomes velocity = -learning_rate * gradient.
  2. Step 2: Substitute the values

    Substitute learning_rate = 0.01 and gradient = [0.1, 0.2] into the formula: velocity = -0.01 * [0.1, 0.2] = [-0.001, -0.002].
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Velocity update is negative of learning rate times gradient [OK]
Hint: Velocity update is negative of learning rate times gradient [OK]
Common Mistakes:
3. What is the time complexity of performing a train-test split with stratification on a dataset with n samples?
easy
A. O(n)
B. O(n^2)
C. O(n log n)
D. O(log n)

Solution

  1. Step 1: Analyze the operation

    Train-test split with stratification involves iterating over the dataset once to ensure class distribution.
  2. Step 2: Determine complexity

    Since each element is processed once, the complexity is O(n).
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Stratified split processes each sample once. [OK]
Hint: Stratified split processes each sample once. [OK]
Common Mistakes:
4. Your mAP implementation is returning unusually high values. What is the most likely cause?
medium
A. Using precision instead of recall in calculations
B. Incorrect IoU threshold for positive matches
C. Not normalizing the precision values
D. Including false negatives in the precision calculation

Solution

  1. Step 1: Identify the Symptom

    High mAP values suggest too many matches are considered correct.
  2. Step 2: Analyze Each Option

    Incorrect IoU threshold for positive matches: A low IoU threshold can cause false positives to be counted as true positives, inflating mAP. Using precision instead of recall in calculations: Would affect recall, not mAP directly. Not normalizing the precision values: Would affect the scale, not the value. Including false negatives in the precision calculation: Would lower precision, not increase it.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check IoU threshold for false positives [OK]
Hint: Check IoU threshold for false positives [OK]
Common Mistakes:
5. How can you optimize the memory usage of the Adam optimizer in a large-scale deep learning model?
hard
A. Reduce the batch size to decrease memory footprint.
B. Use half-precision floating points for parameter storage.
C. Share momentum and velocity arrays across layers.
D. Store only the last two gradients instead of all past gradients.

Solution

  1. Step 1: Identify memory-intensive components

    Adam requires storing additional momentum and velocity arrays, which can be memory-intensive.
  2. Step 2: Optimize storage precision

    Using half-precision floating points reduces memory usage significantly while maintaining sufficient precision for most deep learning tasks.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Use half-precision to save memory in Adam [OK]
Hint: Use half-precision to save memory in Adam [OK]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard