AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Grade 10 AI & Computer Science Practice Questions — Set 10

20 questions from the Grade 10 bank, each with its answer and a full explanation. Set 10 of 11 · 221 questions in this grade.

Reading is revision; testing is practice. Take the same questions as a timed quiz →

Question 181 · Text Classification: Categorizing Documents · hard

An Indian telecom operator is building an SMS spam filter using a Multinomial Naive Bayes classifier with Laplace (add-one) smoothing, trained on this labelled dataset of 4 messages (2 per class): **Spam messages:** "win free prize", "win cash now" **Ham messages:** "meeting scheduled now", "project review meeting" Word counts per class: | Class | win | free | prize | cash | now | meeting | scheduled | project | review | Total words | |---|---|---|---|---|---|---|---|---|---|---| | Spam | 2 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 6 | | Ham | 0 | 0 | 0 | 0 | 1 | 2 | 1 | 1 | 1 | 6 | Vocabulary size |V| = 9 (union of all distinct words across both classes), and the class priors are P(Spam) = P(Ham) = 0.5 (2 documents each, out of 4 total). Using P(word | class) = (count(word, class) + 1) / (total words in class + |V|), classify the test message "win now" and compute its exact posterior probability P(Spam | message) — how is the message classified, and what is that probability?

  1. Neither class is favoured — since the priors P(Spam) and P(Ham) are both 0.5, the classifier reports a tie at P(Spam | message) = P(Ham | message) = 0.50.
  2. Ham — the “now” token, shared by both classes' training data, tips the posterior in Ham's favour, giving P(Ham | message) = 0.75.
  3. Spam — P(Spam | message) equals exactly 0.75, since the smoothed likelihood ratio for “win” across classes is 3:1 while “now” contributes no discriminating power.
  4. Spam — but with posterior probability 1.00, because without Laplace smoothing P(win | Ham) is zero, so Ham is eliminated outright.

Answer: C. Spam — P(Spam | message) equals exactly 0.75, since the smoothed likelihood ratio for “win” across classes is 3:1 while “now” contributes no discriminating power.

ExplanationMultinomial Naive Bayes treats a message as an unordered bag of words and multiplies P(class) by P(word|class) for every word in the message, assuming the words are conditionally independent given the class. With Laplace smoothing, P(word|class) = (count(word,class) + 1) / (N_class + |V|), where N_class = 6 (total tokens in each class) and |V| = 9, so every denominator here is 6 + 9 = 15. For the test message "win now": P(win|Spam) = (2+1)/15 = 3/15 = 0.2, and P(now|Spam) = (1+1)/15 = 2/15 ≈ 0.1333. P(win|Ham) = (0+1)/15 = 1/15 ≈ 0.0667, and P(now|Ham) = (1+1)/15 = 2/15 ≈ 0.1333. Score(Spam) = P(Spam) × P(win|Spam) × P(now|Spam) = 0.5 × 0.2 × 0.1333 = 1/75. Score(Ham) = P(Ham) × P(win|Ham) × P(now|Ham) = 0.5 × 0.0667 × 0.1333 = 1/225. Normalizing, P(Spam|message) = (1/75) / (1/75 + 1/225) = (3/225) / (4/225) = 3/4 = 0.75, and P(Ham|message) = 1/4 = 0.25. The key structural insight: "now" occurs exactly once in each class's training data, so P(now|Spam) = P(now|Ham) = 2/15 exactly, and this common factor cancels out of the ratio Score(Spam)/Score(Ham). The outcome is therefore decided entirely by "win": P(win|Spam)/P(win|Ham) = (3/15)/(1/15) = 3, a 3:1 posterior odds ratio that normalizes to 0.75 and 0.25. The message is classified as Spam with P(Spam|message) = 0.75. The Ham-favoured option simply reverses this ratio. The claim of posterior probability 1.00 describes what would happen only if Laplace smoothing were skipped: the raw maximum-likelihood estimate P(win|Ham) would be 0/6 = 0, collapsing Score(Ham) to exactly zero and forcing an artificial certainty — smoothing exists precisely to prevent unseen words from zeroing out an entire class. The 50/50 option wrongly assumes equal priors keep the posterior at 0.5; priors only set the starting point before the (unequal) likelihoods update it.

Question 182 · Document Clustering: Grouping Similar Texts · hard

A news-aggregator app builds term-frequency vectors for three short articles about a UPI outage during a cricket final, over the 2-word vocabulary {upi, cricket} (after stop-word removal): D1 (a long detailed report) = (8, 6), D2 (a short push-notification alert) = (4, 3), D3 (an outage-only piece that never mentions cricket) = (5, 0). A clustering pipeline merges the pair of documents with the highest cosine similarity first. Which pair is merged first, and what is their cosine similarity, correct to two decimal places?

  1. D1 and D2 merge first: their vectors (8,6) and (4,3) are parallel, giving a cosine similarity of exactly 1.00 regardless of D1 being twice as long as D2.
  2. D1 and D3 merge first: their cosine similarity works out to 0.80, the highest of the three pairwise values.
  3. D2 and D3 merge first: their term-frequency vectors are closest in raw magnitude, giving the highest cosine similarity of 0.80.
  4. Euclidean distance, not cosine similarity, correctly identifies D2 and D3 (distance ≈3.16) as the pair a length-sensitive clustering pipeline would merge first.

Answer: A. D1 and D2 merge first: their vectors (8,6) and (4,3) are parallel, giving a cosine similarity of exactly 1.00 regardless of D1 being twice as long as D2.

ExplanationCosine similarity between two term-frequency vectors u and v is cos(θ) = (u·v)/(|u||v|). Computing all three pairs over the {upi, cricket} axes: cos(D1,D2) = (8×4 + 6×3)/(√(8²+6²) × √(4²+3²)) = (32+18)/(10×5) = 50/50 = 1.00 cos(D1,D3) = (8×5 + 6×0)/(√(8²+6²) × √(5²+0²)) = 40/(10×5) = 0.80 cos(D2,D3) = (4×5 + 3×0)/(√(4²+3²) × √(5²+0²)) = 20/(5×5) = 0.80 D1 and D2 have the highest cosine similarity, 1.00, because (8,6) is exactly 2 × (4,3): the two vectors point in the identical direction in the upi–cricket plane even though D1's raw word counts are double D2's. This is exactly why cosine similarity, not Euclidean distance, is the standard metric for document clustering — it measures the angle between vectors (i.e., topic proportions) and is invariant to document length. Checking every pair matters here: D1–D3 and D2–D3 both also compute to a legitimate value, 0.80, so a solver who computes only one pair and assumes it's the maximum lands on 0.80 instead of the true maximum of 1.00. Euclidean distance tells the opposite story: it is smallest between D2 and D3 (√((4-5)²+(3-0)²) = √10 ≈ 3.16, versus 5.00 between D1 and D2), because Euclidean distance is sensitive to raw magnitude — the two shorter documents merely look artificially close to each other, while the topically identical pair D1–D2 is penalized simply for D1 being longer. A clustering pipeline built on cosine similarity is immune to that length distortion and correctly merges D1 with D2 first.

Question 183 · TF-IDF and BM25: Weighting Terms · hard

A student builds a search engine that indexes 500 NCERT-based text chunks of equal length, so for the query term "photosynthesis" every chunk's document length exactly equals the corpus's average document length (|d| = avgdl). Chunk A contains "photosynthesis" tf = 4 times; chunk B contains it tf = 40 times. Because both chunks are equally long, BM25's length-normalization factor (1 − b + b·|d|/avgdl) equals 1 for both, isolating pure term-frequency saturation. Using BM25 with k1 = 1.5, by what factor does chunk B's term score exceed chunk A's, and how does this compare with what a plain raw-count TF-IDF score (w = tf × idf) would predict for the same 10× jump in tf?

  1. BM25's score rises by only about 1.33× (from ≈1.82×idf to ≈2.41×idf, approaching the ceiling (k1+1)×idf = 2.5×idf) while raw-count TF-IDF, being linear in tf, would predict a full 10× rise.
  2. Both BM25 and raw-count TF-IDF rise by exactly 10×, since BM25's saturation term only affects ranking when document lengths differ from avgdl, not when tf itself changes.
  3. BM25's score already reaches its maximum value of (k1+1)×idf = 2.5×idf at tf = 4, so increasing tf to 40 produces no further change in the BM25 score at all.
  4. BM25's score decreases from chunk A to chunk B because the larger tf triggers a stronger length-normalization penalty in the denominator, outweighing the numerator's growth.

Answer: A. BM25's score rises by only about 1.33× (from ≈1.82×idf to ≈2.41×idf, approaching the ceiling (k1+1)×idf = 2.5×idf) while raw-count TF-IDF, being linear in tf, would predict a full 10× rise.

ExplanationBecause both chunks have |d| = avgdl, the BM25 length-normalization factor (1 − b + b·|d|/avgdl) reduces to 1 regardless of b, so the term score simplifies to idf(t) × [tf·(k1+1)] / (tf + k1). Since "photosynthesis" is the same term drawn from the same 500-chunk corpus in both cases, idf(t) is identical in both scores and cancels out of their ratio, leaving the saturation function f(tf) = tf·(k1+1)/(tf+k1) to compare directly. With k1 = 1.5, so k1+1 = 2.5: f(4) = (4 × 2.5) / (4 + 1.5) = 10/5.5 ≈ 1.818 f(40) = (40 × 2.5) / (40 + 1.5) = 100/41.5 ≈ 2.410 The ratio f(40)/f(4) ≈ 2.410/1.818 ≈ 1.325, so chunk B's BM25 score is only about 1.33 times chunk A's — even though its raw term frequency is 10 times larger. This is BM25's saturation behaviour in action: f(tf) is bounded above by (k1+1) = 2.5 as tf → ∞, so the marginal gain from repeating a term keeps shrinking once tf is already sizeable. At tf = 40, f(tf) has already reached about 96% of that ceiling (2.410 out of 2.5), while at tf = 4 it sits at only about 73% of the ceiling (1.818 out of 2.5) — so the function is nowhere near saturated at tf = 4, ruling out any claim that the maximum is hit that early. Plain raw-count TF-IDF (w = tf × idf) has no such ceiling — it is exactly linear in tf, so with idf unchanged a 10× increase in tf produces exactly a 10× increase in score. The length-normalization term is irrelevant here precisely because |d| = avgdl makes it equal to 1 for both chunks, so it cannot be responsible for any decrease — the denominator's growth (tf + k1) is outpaced by the numerator's growth (tf·(k1+1)) as tf rises, so the score keeps increasing, just at a shrinking rate. This saturating behaviour is exactly why BM25 is preferred over raw tf-idf for ranking: it resists keyword-stuffed documents from dominating results purely by repeating query terms, while still rewarding genuine relevance.

Question 184 · PageRank: Ranking by Importance · hard

A Class 10 CS club builds a toy 3-page web to study Google's PageRank algorithm: Page A (a JEE formula-sheet page) links to both Page B (an NCERT solutions page) and Page C (a scholarship-listing page); Page B links only to Page C; and Page C links only back to Page A — no other links exist anywhere in this toy web. Using the standard PageRank recurrence PR(p) = (1−d)/N + d·Σ_{q→p} PR(q)/L(q), where L(q) is the number of outgoing links from page q, with damping factor d = 0.85 and N = 3 pages, the club starts the power-iteration method with every page assigned equal initial importance PR₀ = 1/3. After carrying out exactly one iteration of the update — computing every page's new score simultaneously from the PR₀ values — what is PR₁(C), the updated PageRank score of Page C?

  1. 0.475, from adding the (1−d)/N base term to d times [PR₀(A)/2 + PR₀(B)/1], since Page A's importance is split across its two outgoing links while Page B's is not split at all.
  2. 0.617, from adding the (1−d)/N base term to d times [PR₀(A) + PR₀(B)], since both inbound pages contribute their full PageRank score to Page C.
  3. 0.425, from computing d times [PR₀(A)/2 + PR₀(B)/1] alone, since the random-jump term only matters once the ranks have stabilised and can be skipped on the first iteration.
  4. 0.333, from adding the (1−d)/N base term to d times [PR₀(A) + PR₀(B)] divided by 2, since Page C has exactly two incoming links to average its score over.

Answer: A. 0.475, from adding the (1−d)/N base term to d times [PR₀(A)/2 + PR₀(B)/1], since Page A's importance is split across its two outgoing links while Page B's is not split at all.

ExplanationPageRank models a "random surfer" who, at each step, either jumps to a uniformly random page with probability (1−d), or follows an outgoing link chosen uniformly at random from the current page's outgoing links with probability d. This gives PR(p) = (1−d)/N + d·Σ_{q→p} PR(q)/L(q), where L(q) is page q's out-degree. In this toy web, Page C has exactly two inbound links: one from Page A, whose out-degree is 2 (it links to both B and C), and one from Page B, whose out-degree is 1 (it links only to C). Starting from PR₀(A) = PR₀(B) = PR₀(C) = 1/3 and updating synchronously, Page C's weighted inbound contribution is PR₀(A)/2 + PR₀(B)/1 = (1/3)/2 + (1/3)/1 = 1/6 + 1/3 = 1/2. Scaling by the damping factor gives d × 1/2 = 0.85 × 0.5 = 0.425, and adding the random-jump base term (1 − d)/N = 0.15/3 = 0.05 gives PR₁(C) = 0.425 + 0.05 = 0.475. The core idea being tested is that PageRank is not a vote count: Page A's importance must be divided across all of its outgoing links, so only half of Page A's score (1/6) flows to C — the other half flows to B — while Page B's entire score flows to C because C is B's only outgoing link. Treating every inbound page as contributing its full, undivided score inflates the result to roughly 0.617. Dropping the (1 − d)/N random-jump term — on the mistaken belief that teleportation only matters after the ranks converge, when it in fact applies identically at every single iteration — understates the result to 0.425. And averaging the two inbound contributions over the inlink count instead of dividing each one individually by its own source's out-degree collapses the result to 1/3 ≈ 0.333, which conflates PageRank with a simple backlink average rather than a link-weighted flow of importance.

Question 185 · Web Crawling: Downloading the Internet · hard

An Indian search-engine crawler uses a Bloom filter to check whether a URL has already been fetched, avoiding wasted bandwidth on repeat downloads of Indian government and news pages. The filter's bit array has m = 8,000,000 bits, it uses k = 5 independent hash functions per URL, and n = 1,000,000 distinct URLs have already been inserted. Using the standard Bloom filter approximation, what is the probability that the filter reports a false positive (declares an unvisited URL "already crawled") for the next new URL checked?

  1. ≈2.2%, from p ≈ (1 − e^(−kn/m))^k = (1 − e^(−0.625))^5
  2. ≈46.5%, from p ≈ 1 − e^(−kn/m) = 1 − e^(−0.625)
  3. ≈62.5%, from p ≈ kn/m = 5,000,000/8,000,000
  4. ≈53.5%, from p ≈ e^(−kn/m) = e^(−0.625)

Answer: A. ≈2.2%, from p ≈ (1 − e^(−kn/m))^k = (1 − e^(−0.625))^5

ExplanationEach URL is hashed into k = 5 bit positions out of m = 8,000,000. For any single hash application, the probability a particular bit stays 0 is (1 − 1/m); after all n·k = 5,000,000 hash insertions from n = 1,000,000 URLs, the probability a given bit is still 0 is approximately (1 − 1/m)^(kn) ≈ e^(−kn/m), using the standard limit (1 − 1/m)^m ≈ e^(−1) for large m. Here kn/m = 5,000,000 / 8,000,000 = 0.625, so the probability a bit remains 0 is e^(−0.625) ≈ 0.5353, and the probability a given bit has instead been set to 1 by some earlier URL is 1 − 0.5353 = 0.4647. A false positive on the new URL occurs only when ALL k = 5 of its hashed bit positions happen to already be set to 1 from previous insertions. Treating the bit states as approximately independent (the standard Bloom filter approximation), the false-positive probability is p ≈ (1 − e^(−kn/m))^k = (0.4647)^5 ≈ 0.0217, i.e., about 2.2%. The correct calculation needs two steps: first find the probability that one bit is set (0.4647), then raise it to the k-th power because every one of the 5 bits must independently collide. Stopping after step one (46.5%) drastically overstates the error rate, since hitting one set bit is far more likely than hitting five simultaneously. Treating kn/m as a probability outright (62.5%) ignores that multiple hashes can strike the same bit and that the fraction of set bits grows with exponential saturation, not linearly. Using e^(−kn/m) alone (53.5%) reports the probability that a bit is still empty — the complement of what a false positive actually requires.

Question 186 · Graph Neural Networks: Learning on Graphs · hard

A UPI payment-tracking graph has four users as nodes — A, B, C, and D — with undirected edges A–B, A–C, B–C, and C–D (so C is directly connected to each of A, B, and D, while A and B are also linked to each other). Each node's initial feature h⁽⁰⁾ is its average monthly UPI transaction count, in hundreds: h_A⁽⁰⁾ = 2, h_B⁽⁰⁾ = 4, h_C⁽⁰⁾ = 1, h_D⁽⁰⁾ = 6. One GraphSAGE-style mean-aggregation layer updates node C's representation as h_C⁽¹⁾ = ReLU(W_self · h_C⁽⁰⁾ + W_neigh · mean_{u ∈ N(C)} h_u⁽⁰⁾ + b), where N(C) is the set of C's neighbours only (C itself is excluded from that set and enters solely through the W_self term), with W_self = 0.5, W_neigh = 1.5, and b = −3. What is h_C⁽¹⁾?

  1. h_C^(1) equals 3.5, obtained by averaging only the three true neighbours A, B, D and then applying the self and neighbour weights plus bias.
  2. h_C^(1) equals 15.5, since summing the neighbour features without dividing by the neighbour count still satisfies permutation invariance.
  3. h_C^(1) equals 2.375, produced by averaging all four nodes A, B, C, D together as if they formed C's neighbourhood before applying the weights.
  4. h_C^(1) equals 6.5, obtained by applying the self and neighbour weights to the correct neighbourhood mean but omitting the bias term entirely.

Answer: A. h_C^(1) equals 3.5, obtained by averaging only the three true neighbours A, B, D and then applying the self and neighbour weights plus bias.

ExplanationC's neighbours are exactly the nodes joined to C by an edge — A, B, and D (edges C–A, C–B, C–D) — so N(C) = {A, B, D}, and C's own feature is deliberately kept out of this set because the self and neighbour information are combined through two separate weights, W_self and W_neigh, rather than pooled into one blended average. The neighbourhood mean is therefore (h_A⁽⁰⁾ + h_B⁽⁰⁾ + h_D⁽⁰⁾)/3 = (2 + 4 + 6)/3 = 4. Substituting into the update rule gives W_self·h_C⁽⁰⁾ + W_neigh·mean + b = 0.5(1) + 1.5(4) + (−3) = 0.5 + 6 − 3 = 3.5, and since 3.5 is already positive, ReLU leaves it unchanged, so h_C⁽¹⁾ = 3.5. Summing the neighbour features instead of averaging them (2 + 4 + 6 = 12) inflates the result to 0.5 + 1.5(12) − 3 = 15.5; this mistake removes the mean aggregator's defining property of staying insensitive to how many neighbours a node has, which is exactly why mean (not sum) is used when node degree varies across a graph. Folding C's own feature into its neighbourhood — averaging over {A, B, C, D} instead of {A, B, D} — gives a mean of 13/4 = 3.25 and a final value of 0.5(1) + 1.5(3.25) − 3 = 2.375, which double-counts C's self-information since W_self already injects it as a separate term. Dropping the bias term b altogether gives 0.5 + 6 = 6.5, which silently shifts the layer's learned operating point away from what training actually fit.

Question 187 · Community Detection: Finding Groups · hard

Six students on a hostel floor form a friendship graph with two clusters bridged by a single cross-cluster friendship: within Cluster 1, students A, B, C are all mutual friends (edges AB, BC, CA); within Cluster 2, students D, E, F are all mutual friends (edges DE, EF, FD); and C is additionally friends with D (edge CD), the only edge connecting the two clusters. This gives 7 edges total, with degrees deg(A)=deg(B)=2, deg(C)=3, deg(D)=3, deg(E)=deg(F)=2. Using the partition {A,B,C} and {D,E,F}, and the modularity formula Q = Σ_c [e_c/m − (d_c/2m)²], where e_c is the number of internal edges in community c, d_c is the sum of degrees of nodes in c, and m is the total number of edges, what is the exact value of Q for this partition?

  1. Q = 6/7 ≈ 0.857, obtained by taking the fraction of all edges that lie within a community and treating that alone as the modularity score
  2. Q = −1/14 ≈ −0.071, obtained by dividing each community's internal edge count by 2m instead of m while keeping the degree term as (d_c/2m)²
  3. Q = 5/14 ≈ 0.357, obtained from Σ_c [e_c/m − (d_c/2m)²] using e₁ = e₂ = 3, d₁ = d₂ = 7, and m = 7
  4. Q = −8/7 ≈ −1.143, obtained by using (d_c/m)² instead of (d_c/2m)² for the expected-edge term in each community

Answer: C. Q = 5/14 ≈ 0.357, obtained from Σ_c [e_c/m − (d_c/2m)²] using e₁ = e₂ = 3, d₁ = d₂ = 7, and m = 7

ExplanationFirst pin down the graph's numbers. Total edges m = 7 (AB, BC, CA, DE, EF, FD, CD). Degree sum check: deg(A)+deg(B)+deg(C)+deg(D)+deg(E)+deg(F) = 2+2+3+3+2+2 = 14 = 2m, as it must be. For the partition {A,B,C} and {D,E,F}: internal edges e₁ = {AB, BC, CA} = 3, and e₂ = {DE, EF, FD} = 3 (the bridge CD is the only inter-community edge, so 3+3+1 = 7 checks out). Internal degree sums: d₁ = deg(A)+deg(B)+deg(C) = 2+2+3 = 7, and d₂ = deg(D)+deg(E)+deg(F) = 3+2+2 = 7 (and d₁+d₂ = 14 = 2m, as it must). Applying Q = Σ_c [e_c/m − (d_c/2m)²]: Q = [3/7 − (7/14)²] + [3/7 − (7/14)²] = 2 × [3/7 − 1/4] = 2 × [12/28 − 7/28] = 2 × 5/28 = 5/14 ≈ 0.357. The −(d_c/2m)² term is the null-model correction: it subtracts the fraction of edges you'd expect inside community c if edges were rewired at random while preserving each node's degree. Dropping it and reporting the raw internal-edge fraction (6/7 ≈ 0.857) mistakes edge density for modularity — a graph can pack most of its edges inside two groups purely because those groups are large hubs, without the grouping being statistically meaningful; modularity only rewards edge concentration in excess of what degree alone predicts. Using (d_c/m)² instead of (d_c/2m)² inflates the correction term by a factor of 4 (since m = 7 here, (7/7)² = 1 versus (7/14)² = 1/4), driving the score to −8/7 — a value that isn't even in modularity's valid range and signals the factor of 2 was dropped from the denominator, which specifically accounts for each edge being counted at both of its endpoints when summing degrees. Using e_c/2m instead of e_c/m for the internal-edge fraction halves that term unnecessarily: e_c already counts each internal edge once, so dividing by m (not 2m) correctly expresses it as a fraction of all edges; the 2m in the degree term is doing a separate job — normalizing a degree sum, not an edge count — and conflating the two produces −1/14, wrongly making a partition with 6 of 7 edges inside the two clusters look worse than a random split.

Question 188 · Image Classification: Teaching Machines to See · hard

An ISRO Earth-observation pipeline classifies 32×32 pixel RGB tiles cropped from Cartosat imagery into land-cover categories (water, vegetation, urban, bare soil). The first convolutional layer of the CNN uses 16 filters, each 5×5 in spatial extent, applied with stride 1 and no zero-padding. What are the spatial dimensions and depth of this layer's output feature map, and how many learnable parameters (weights plus biases) does the layer contain?

  1. Sliding 16 filters of size 5×5×3 across the tile with stride 1 and no padding yields a 28×28×16 output using 1,216 parameters.
  2. Since each 5×5 filter is applied per channel independently, the layer produces a 28×28×16 output but only needs 416 parameters.
  3. With zero-padding preserving spatial size, the layer produces a 32×32×16 output while still using 1,216 parameters.
  4. Because output depth equals the number of input channels, the layer produces a 28×28×3 output using 1,216 parameters.

Answer: A. Sliding 16 filters of size 5×5×3 across the tile with stride 1 and no padding yields a 28×28×16 output using 1,216 parameters.

ExplanationEach of the 16 filters must span the full depth of its input, so a 5×5 filter applied to a 3-channel RGB tile is really a 5×5×3 tensor: 5 × 5 × 3 = 75 weights. Adding one bias per filter gives 76 parameters per filter, and 16 filters contribute 16 × 76 = 1,216 learnable parameters in total. This count depends only on the filter's footprint (5×5), the input depth (3), and the number of filters (16) — it does not depend on the tile's spatial size at all, which is why the same 1,216 appears whether the tile is 32×32 or any other size. The spatial dimensions of the output are governed by a separate rule. With no padding and stride 1, the standard convolution output-size formula floor((W − F)/S) + 1 applies along both height and width: floor((32 − 5)/1) + 1 = 27 + 1 = 28. So the feature map measures 28×28 in the plane. Its depth equals the number of filters used, not the number of input channels: each filter, however many channels it scans, collapses them into a single 2-D activation map (one dot-product per spatial position), so 16 filters produce 16 stacked activation maps. Combining both results, the layer's output is a 28×28×16 volume built from 1,216 parameters — matching the rule that a filter's weight count must include the full input depth, and that padding (or its absence) — not the filter count — is what determines whether spatial size shrinks.

Question 189 · Image Segmentation: Pixel-Level Classification · hard

An ISRO Cartosat-based road-extraction model performs binary semantic segmentation on a 4×4 satellite image tile (1 = road pixel, 0 = background pixel). The model outputs a per-pixel sigmoid probability, which is thresholded at 0.5 to produce the predicted mask below. Ground-truth mask: ``` 1 1 0 0 1 1 0 0 0 0 1 1 0 0 1 1 ``` Predicted mask: ``` 1 1 1 0 1 1 0 0 0 0 1 1 0 1 1 1 ``` Comparing the two masks pixel-by-pixel gives 8 true positives, 2 false positives, 0 false negatives, and 6 true negatives. What is the Intersection-over-Union (IoU, i.e., Jaccard Index) of the predicted road mask against the ground truth, rounded to two decimal places?

  1. 0.80 — computed as 8 true positives divided by the union of 10 pixels (8 true positives + 2 false positives + 0 false negatives), since IoU = TP / (TP + FP + FN).
  2. 0.875 — computed as 14 correctly labelled pixels (8 true positives + 6 true negatives) divided by all 16 pixels in the tile, which is the overall pixel accuracy rather than IoU.
  3. 0.89 — computed as twice the true positives (16) divided by the sum of the predicted-road and actual-road pixel counts (10 + 8 = 18), which is the Dice coefficient rather than IoU.
  4. 1.00 — computed as 8 true positives divided by the 8 ground-truth road pixels, which is the recall (sensitivity) and ignores the 2 false-positive pixels the model wrongly labelled as road.

Answer: A. 0.80 — computed as 8 true positives divided by the union of 10 pixels (8 true positives + 2 false positives + 0 false negatives), since IoU = TP / (TP + FP + FN).

ExplanationOverlaying the predicted mask on the ground-truth mask cell by cell: the true-positive (road correctly predicted as road) cells are (1,1), (1,2), (2,1), (2,2), (3,3), (3,4), (4,3), (4,4) — 8 pixels. The false-positive (background wrongly predicted as road) cells are (1,3) and (4,2) — 2 pixels. Every one of the 8 ground-truth road pixels was recovered, so there are 0 false negatives, leaving the remaining 6 cells as true negatives (8 + 2 + 0 + 6 = 16, which checks out against the 4×4 = 16 total pixels). IoU is defined as the size of the intersection of the predicted and ground-truth road regions divided by the size of their union: IoU = TP / (TP + FP + FN) = 8 / (8 + 2 + 0) = 8/10 = 0.80. The distractors correspond to real evaluation metrics computed correctly but for the wrong quantity — a common source of error when reading segmentation benchmarks. Pixel accuracy, (TP + TN)/total = 14/16 = 0.875, looks reassuringly high here only because the background class dominates the tile; it does not penalize the model's tendency to over-predict road pixels the way IoU does. The Dice coefficient, 2·TP/(2·TP + FP + FN) = 16/18 ≈ 0.89, weights the intersection twice, which is why it is always ≥ IoU for the same confusion counts and should never be reported interchangeably with it. Recall (sensitivity), TP/(TP + FN) = 8/8 = 1.00, only checks whether every true road pixel was found; it is silent about the false-positive pixels (1,3) and (4,2), so a model that predicts "road everywhere" could still score a perfect recall of 1.00 while its IoU would collapse. Because IoU is the only one of the four that penalizes both missed road pixels and spuriously predicted ones, it is the standard metric for comparing segmentation models such as this ISRO road-extraction network.

Question 190 · Data Augmentation: More Data from Less · hard

A 64×64 pixel satellite crop-classification image (pixel indices run 0 to 63 along each axis, so the centre of the image is at (31.5, 31.5)) is rotated by 30° about its centre as part of a data-augmentation pipeline, using the standard 2D rotation matrix [[cosθ, −sinθ], [sinθ, cosθ]] applied to coordinates measured from the centre and then translated back to absolute pixel coordinates. What happens to the pixel that originally sat at the top-left corner, (x, y) = (0, 0), after this rotation?

  1. After the rotation, (x', y') ≈ (20.0, -11.5) — since y' < 0 lies outside the valid row range [0, 63], this corner pixel's original colour is lost off-canvas, so the output must be padded or cropped to stay 64×64.
  2. Computing the rotation gives (x', y') ≈ (20.0, 43.0), and because both values lie within [0, 63], the corner pixel remains inside the canvas — so a 30° rotation causes no loss of corner information here.
  3. Applying the rotation formula gives (x', y') ≈ (20.0, -43.0); since y' is negative and its magnitude exceeds the canvas side length, rotations must be capped below 10° to guarantee no corner ever leaves the frame.
  4. Working through the rotation yields (x', y') ≈ (20.0, -11.5), but since x' still lies within [0, 63], the pixel counts as valid, because only one of the two coordinates needs to fall inside the canvas range.

Answer: A. After the rotation, (x', y') ≈ (20.0, -11.5) — since y' < 0 lies outside the valid row range [0, 63], this corner pixel's original colour is lost off-canvas, so the output must be padded or cropped to stay 64×64.

ExplanationShift the corner into centre-relative coordinates: (dx, dy) = (0 − 31.5, 0 − 31.5) = (−31.5, −31.5). With θ = 30°, cos30° = √3/2 ≈ 0.8660 and sin30° = 0.5, the rotation matrix gives the rotated relative coordinates: x'_rel = dx·cosθ − dy·sinθ = (−31.5)(0.8660) − (−31.5)(0.5) = −27.28 + 15.75 = −11.53 y'_rel = dx·sinθ + dy·cosθ = (−31.5)(0.5) + (−31.5)(0.8660) = −15.75 − 27.28 = −43.03 Translating back by adding the centre (31.5, 31.5): x' = −11.53 + 31.5 ≈ 19.97 ≈ 20.0 y' = −43.03 + 31.5 ≈ −11.53 ≈ −11.5 So the corner lands at approximately (20.0, −11.5). Valid pixel rows run from 0 to 63, and y' is negative, so this location lies entirely above the canvas — the corner's colour information has nowhere to land inside the 64×64 output grid. Note that a pixel is only valid if BOTH coordinates lie in range; having x' = 20.0 inside [0, 63] does not rescue a pixel whose y' has left the frame. This is exactly why rotation-based augmentation pipelines — say, when stretching a small ISRO Bhuvan or Sentinel crop-classification dataset by rotating each labelled tile — must choose between cropping inward to the largest rotation-safe rectangle or padding the exposed corners with a fill value. Either way, real image content near the corners is discarded or replaced; that quiet loss is the actual "cost" hidden behind the seemingly free extra training images that rotation augmentation appears to hand you.

Question 191 · Model Compression: Shrinking Giant Networks · hard

An AI team is building an offline crop-disease detection app for farmers under a Digital India rural-connectivity initiative. Their trained CNN has 40 million parameters, stored in standard 32-bit floating point (FP32), so each parameter costs 4 bytes. To fit the app inside the Play Store's "Android Go" size budget for entry-level phones, the team applies two compression steps in sequence: first, structured pruning that removes entire convolutional filters — physically eliminating 50% of the parameters rather than just zeroing them — and second, INT8 post-training quantization, which stores each surviving parameter in 1 byte instead of 4. Assume 1 MB = 1,000,000 bytes throughout. What is the final size of the deployed model in megabytes, and what is the overall compression ratio relative to the original FP32 model?

  1. 20 MB, an 8x reduction from the original size — structured pruning shrinks the model to 20 million parameters by physically removing filters (not just zeroing them), and INT8 quantization then stores each surviving parameter in 1 byte instead of 4, giving 20,000,000 bytes against the original 160,000,000 bytes.
  2. 40 MB, a 4x reduction from the original size — this treats quantization as the only effective compression step, storing all 40 million original parameters in INT8 while ignoring that structured pruning had already cut the parameter count in half.
  3. 80 MB, a 2x reduction from the original size — this credits only the pruning step, storing the surviving 20 million parameters at their original 4-byte FP32 precision instead of also converting them to INT8.
  4. 20 MB, a 4x reduction from the original size — the byte count for the final model is computed correctly, but the ratio is measured against the 80 MB intermediate size left after pruning rather than against the true 160 MB original FP32 model.

Answer: A. 20 MB, an 8x reduction from the original size — structured pruning shrinks the model to 20 million parameters by physically removing filters (not just zeroing them), and INT8 quantization then stores each surviving parameter in 1 byte instead of 4, giving 20,000,000 bytes against the original 160,000,000 bytes.

ExplanationWork through the two stages separately before combining them, since pruning and quantization compress different things — parameter count versus bytes per parameter. Original model: 40,000,000 parameters x 4 bytes (FP32) = 160,000,000 bytes = 160 MB. This is the baseline every ratio must be measured against. Stage 1 — structured pruning: because whole filters are physically removed (not merely set to zero), the tensor itself shrinks, so the parameter count genuinely drops by 50%: 40,000,000 x 0.5 = 20,000,000 parameters. At this intermediate checkpoint, still in FP32, the size is 20,000,000 x 4 = 80,000,000 bytes = 80 MB. This intermediate number matters only as a checkpoint — it is not the original model, so it must never be used as the denominator for the final compression ratio. Stage 2 — INT8 quantization: each of the 20,000,000 surviving parameters is now stored in 1 byte instead of 4, giving 20,000,000 x 1 = 20,000,000 bytes = 20 MB. Overall compression ratio: compare the final size to the true original, 160 MB / 20 MB = 8. The deployed model is 8 times smaller than the original FP32 network, comfortably inside a 25 MB Android Go budget. The two compression mechanisms are multiplicative because they act on independent quantities — parameter count (pruning) and bytes per parameter (quantization) — so the correct combined ratio is (1/0.5) x (32-bit/8-bit) = 2 x 4 = 8x, matching 160/20 exactly. Applying quantization alone without accounting for the pruning that already happened underestimates the savings at 4x. Applying pruning alone while leaving the survivors in FP32 captures only 2x. And correctly computing the final byte count but then dividing by the 80 MB post-pruning checkpoint instead of the 160 MB original also lands on 4x — the arithmetic is clean, but the wrong baseline is used, which is precisely why the problem states the ratio must be relative to the original FP32 model.

Question 192 · Pruning: Removing Unnecessary Weights · hard

A fully-connected layer has exactly 1,000,000 weights, each stored as a 32-bit (4-byte) float in dense format, for a total of 4,000,000 bytes. Magnitude-based pruning zeroes out the smallest-magnitude 20% of these weights, and the resulting sparse layer is stored using a coordinate (COO) sparse format, where every surviving nonzero weight needs its 4-byte float value plus a 2-byte (16-bit) index recording its position — 6 bytes per stored entry. Compared to the original dense storage, how much memory does this sparse representation actually require?

  1. 4,800,000 bytes — 20% more memory than the dense layer's 4,000,000 bytes, because the 800,000 surviving weights each need a 4-byte value plus a 2-byte index (6 bytes total), and at 20% sparsity this index overhead outweighs the savings from the zeroed weights; sparse storage only becomes cheaper than dense once sparsity exceeds 1/3.
  2. 3,200,000 bytes — 20% less memory than the dense layer, since only the 800,000 surviving weights need to be stored, each still costing just 4 bytes, and the zeroed weights are simply skipped with no storage cost at all.
  3. 1,200,000 bytes — 70% less memory than the dense layer, since pruning 20% of the weights leaves only 200,000 active weights, and each of these is stored as a 6-byte value-index pair.
  4. The sparse representation is guaranteed to use less memory than the dense layer at any nonzero pruning level, because omitting the storage of zeroed weights can only reduce the total memory footprint compared to storing every weight explicitly.

Answer: A. 4,800,000 bytes — 20% more memory than the dense layer's 4,000,000 bytes, because the 800,000 surviving weights each need a 4-byte value plus a 2-byte index (6 bytes total), and at 20% sparsity this index overhead outweighs the savings from the zeroed weights; sparse storage only becomes cheaper than dense once sparsity exceeds 1/3.

ExplanationStart with the dense baseline: this layer has N = 1,000,000 weights, each stored as a 32-bit (4-byte) float, giving a dense memory cost of 4 × 1,000,000 = 4,000,000 bytes — every other number here gets compared against this. Magnitude-based pruning at 20% sparsity zeroes out 20% of the weights, not 20% remaining — so the count set to zero is 0.20 × 1,000,000 = 200,000, and the surviving nonzero count is 1,000,000 − 200,000 = 800,000. In COO (coordinate) sparse format, every surviving weight must carry not just its value but also an explicit index recording where it sits in the original matrix — without that index the position of every zero is lost and the layer can't be reconstructed. That costs 4 bytes (value) + 2 bytes (16-bit index) = 6 bytes per surviving weight, so the sparse memory cost is 800,000 × 6 = 4,800,000 bytes. Comparing the two: 4,800,000 ÷ 4,000,000 = 1.2 — the sparse representation uses 20% MORE memory than the dense array, even though one-fifth of the weights are exactly zero. The index overhead is eating more bytes than the savings from removing those weights. This is a general algebraic fact, not a special case. For a layer with N weights and sparsity s (the pruned fraction), sparse cost = 6N(1 − s) bytes while dense cost = 4N bytes. Sparse storage only wins when 6(1 − s) < 4, i.e. 1 − s < 2/3, i.e. s > 1/3 ≈ 33.3%. Below that threshold, index-based sparse formats are strictly worse than simply keeping the dense array — this is why unstructured (per-weight) pruning has to clear roughly a third of the weights before it shrinks a model in RAM or on disk at all. It's also why pruning aimed at memory-constrained deployment — for instance, running an on-device AI model on a budget Indian Android phone — usually favors structured pruning (dropping entire neurons, channels, or filters): that removes weights in blocks that need no per-weight index, so every pruned unit is pure savings regardless of the overall sparsity level.

Question 193 · Edge Deployment: ML on Devices · hard

A fintech team building an offline UPI-fraud-detection model for budget Android phones deploys a MobileNet-style INT8 network on a mid-range NPU rated at 2 TMAC/s (2×10¹² INT8 multiply-accumulates per second) with 25 GB/s of memory bandwidth shared between the CPU and NPU. They profile one 1×1 pointwise convolution layer that takes a 7×7×256 INT8 input feature map and produces a 7×7×256 INT8 output (256 input channels, 256 output channels, no bias, weights and activations both quantized to 1 byte each). Applying the roofline performance model to this layer in isolation, which statement about its performance is correct?

  1. The layer performs 3,211,264 MACs while moving 90,624 bytes (65,536 for weights, 12,544 for input activations, 12,544 for output activations), giving an arithmetic intensity of about 35.4 MAC per byte. Since the NPU's ridge point is 2×10¹² ÷ 25×10⁹ = 80 MAC per byte, the layer sits below the ridge point and is memory-bound, so its runtime is set by data movement: 90,624 bytes ÷ 25 GB/s ≈ 3.6 microseconds, about 2.3 times longer than the 1.6 microseconds the same MACs would take if the NPU's compute throughput were the limiting factor.
  2. The layer's arithmetic intensity of about 35.4 MAC per byte exceeds the NPU's raw bandwidth figure of 25 GB/s, so the layer is compute-bound and its runtime is set entirely by the NPU's 2 TMAC/s throughput, giving a runtime of about 1.6 microseconds.
  3. Because the input activations were already produced on-chip by the previous layer and stay resident in the NPU's local buffer, only the 65,536 bytes of INT8 weights must be streamed from DRAM; dividing this by the 25 GB/s bandwidth gives a memory time of about 2.6 microseconds, which still exceeds the compute time and makes the layer memory-bound.
  4. Since the compute time of about 1.6 microseconds and the memory transfer time of about 3.6 microseconds occur on separate hardware paths that do not overlap, the total runtime is their sum, about 5.2 microseconds, with the layer classified as memory-bound because the memory term dominates the sum.

Answer: A. The layer performs 3,211,264 MACs while moving 90,624 bytes (65,536 for weights, 12,544 for input activations, 12,544 for output activations), giving an arithmetic intensity of about 35.4 MAC per byte. Since the NPU's ridge point is 2×10¹² ÷ 25×10⁹ = 80 MAC per byte, the layer sits below the ridge point and is memory-bound, so its runtime is set by data movement: 90,624 bytes ÷ 25 GB/s ≈ 3.6 microseconds, about 2.3 times longer than the 1.6 microseconds the same MACs would take if the NPU's compute throughput were the limiting factor.

ExplanationStart with the arithmetic: the 1×1 convolution over a 7×7×256 input producing 256 output channels performs H·W·Cin·Cout = 7·7·256·256 = 3,211,264 multiply-accumulates. Because the layer is quantized to INT8, every weight and activation element costs 1 byte, so the data moved is 256·256 = 65,536 bytes of weights, plus 7·7·256 = 12,544 bytes of input activations, plus 7·7·256 = 12,544 bytes of output activations, for a total of 90,624 bytes. Dividing gives an arithmetic intensity of 3,211,264 ÷ 90,624 ≈ 35.4 MAC per byte. The NPU's ridge point — the arithmetic intensity at which compute time and memory time are exactly balanced — is peak compute divided by bandwidth: 2×10¹² MAC/s ÷ 25×10⁹ byte/s = 80 MAC per byte. Because this layer's intensity (35.4) is below the ridge point (80), it does not have enough reuse per byte moved to keep the NPU's multiply-accumulate units fed continuously, so it is memory-bound rather than compute-bound. Once a layer is memory-bound, its runtime is governed by the slower of the two resources, and on real hardware compute and memory transfer run concurrently — the NPU streams new bytes in while computing on values already fetched, rather than idling through a transfer phase and then computing in a separate phase afterward — so runtime = max(compute time, memory time), not their sum. Compute time is 3,211,264 MACs ÷ 2×10¹² MAC/s ≈ 1.6 microseconds; memory time is 90,624 bytes ÷ 25×10⁹ byte/s ≈ 3.6 microseconds. The larger figure, about 3.6 microseconds, sets the actual runtime — roughly 2.3 times what a compute-only estimate would predict. This is exactly why MobileNet-style edge models replace large dense convolutions with depthwise-separable and pointwise (1×1) convolutions to cut FLOP counts, yet on-device profiling so often shows these very layers as the bottleneck anyway: shrinking compute without shrinking the weight and activation bytes moved per MAC pushes arithmetic intensity down, past the ridge point, and the NPU ends up sitting idle waiting on DRAM traffic rather than being kept busy — a core reason edge-deployment engineers profile bytes moved, not just FLOPs, when optimizing on-device latency.

Question 194 · Containerization with Docker: Packaging Applications for Production · hard

A student deploys a Flask-based UPI transaction fraud-detection API using two different Dockerfiles that produce functionally identical images. Version A (dependencies copied and installed first): ``` FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["python", "app.py"] ``` Version B (entire source copied first): ``` FROM python:3.11-slim WORKDIR /app COPY . . RUN pip install -r requirements.txt CMD ["python", "app.py"] ``` Assume: the base image layer is already cached (0 seconds) for both Dockerfiles; running `pip install -r requirements.txt` from scratch takes 90 seconds; copying the application source with COPY takes 2 seconds; and the CMD instruction only updates image metadata, adding no measurable time. The student edits only app.py — requirements.txt is left untouched — and rebuilds the image from each Dockerfile with Docker's layer cache enabled. How long does the rebuild take with each Dockerfile, and why?

  1. Version A rebuilds in 2 seconds because only the COPY . . layer's cache misses; Version B rebuilds in 92 seconds because that same cache miss cascades downstream and forces the 90-second pip install to rerun too, even though requirements.txt itself never changed.
  2. Both versions rebuild in exactly 2 seconds, since Docker checksums each RUN and COPY instruction independently against its own input files, so an unchanged requirements.txt always keeps the pip install layer cached no matter where COPY . . sits in the file.
  3. Every rebuild costs the full 92 seconds in both Dockerfiles, because modifying any file in the build context invalidates the Docker cache for every instruction that follows the FROM line, regardless of instruction ordering.
  4. Version B rebuilds in 2 seconds and Version A rebuilds in 92 seconds, because placing COPY . . immediately after WORKDIR lets Docker skip dependency resolution entirely and reuse the previously installed package set.

Answer: A. Version A rebuilds in 2 seconds because only the COPY . . layer's cache misses; Version B rebuilds in 92 seconds because that same cache miss cascades downstream and forces the 90-second pip install to rerun too, even though requirements.txt itself never changed.

ExplanationDocker builds a Dockerfile as a sequence of cached layers, and each instruction's cache validity depends on two things: its own inputs being unchanged, and its parent layer having been a cache hit. Once any layer misses the cache, every instruction after it is rebuilt too, even if that later instruction's own inputs are identical to last time — this cascading rule is exactly what separates the two Dockerfiles here. In Version A, editing app.py leaves requirements.txt untouched, so COPY requirements.txt . still hits the cache, and since its parent layer is a hit and the pip command string is unchanged, RUN pip install -r requirements.txt also hits the cache — that 90-second step is skipped entirely. Only COPY . . sees new content (the edited app.py) and reruns, costing 2 seconds. Total: 2 seconds. In Version B, COPY . . comes first and already includes the changed app.py, so it misses the cache and reruns (2 seconds). Because this layer missed, RUN pip install -r requirements.txt now has a non-cached parent, so Docker reruns it too — a full 90-second reinstall — even though requirements.txt never changed. Total: 2 + 90 = 92 seconds. The 90-second gap is precisely why production Dockerfiles copy and install dependency manifests before copying the full source tree: it isolates the expensive, infrequently-changing install step from the cheap, frequently-changing source-code layer.

Question 195 · CI/CD Pipelines: Automating Software Delivery · hard

A Bengaluru fintech startup's CI/CD pipeline for its UPI payments service has six stages with these dependencies and durations: Checkout (2 min, no dependencies) triggers Lint (3 min) and Unit Tests (9 min), which both depend only on Checkout and run in parallel. Build (6 min) depends on BOTH Lint AND Unit Tests completing — it cannot start until both are done. Once Build finishes, Integration Tests (5 min) and Deploy (4 min) both depend only on Build and run in parallel; the pipeline run is complete only once both of these finish. Assuming the runner cluster has unlimited parallel capacity, so every stage starts the instant its dependencies are satisfied, what is the minimum total time, in minutes, for one complete pipeline run to finish?

  1. 29 minutes
  2. 16 minutes
  3. 22 minutes
  4. 21 minutes

Answer: C. 22 minutes

ExplanationThis is a critical-path scheduling problem — the same DAG (directed acyclic graph) model that GitHub Actions' `needs:` and GitLab CI's `needs` keyword implement. The key rule: a stage with multiple parent stages cannot start until ALL of its parents finish. A join waits on the slowest incoming branch, not the fastest — otherwise it would start work on unverified code that a sibling branch is still checking. Trace the timeline stage by stage. Checkout has no dependencies, so it runs from minute 0 to minute 2. Lint and Unit Tests both depend only on Checkout, so both start at minute 2: Lint finishes at minute 5 (2+3), Unit Tests finishes at minute 11 (2+9). Build depends on both, so it must wait for the slower of the two — Unit Tests — and starts at minute 11, not minute 5; Lint simply sits idle for those extra 6 minutes. Build takes 6 minutes, finishing at minute 17. Integration Tests and Deploy both depend only on Build, so both start at minute 17: Integration Tests finishes at minute 22 (17+5), Deploy finishes at minute 21 (17+4). The run as a whole is not done until every stage is done, so it completes at minute 22. In formula form, the minimum duration is the longest (critical) path through the graph: 2 + max(3, 9) + 6 + max(5, 4) = 2 + 9 + 6 + 5 = 22 minutes. Adding every stage's duration together (2+3+9+6+5+4 = 29 minutes) is the classic error of treating the pipeline as fully sequential, which throws away exactly the parallelism that DAG-based CI/CD is designed to exploit. Substituting Lint's 3 minutes for Unit Tests' 9 at the first join (2+3+6+5 = 16 minutes) or substituting Deploy's 4 minutes for Integration Tests' 5 at the second join (2+9+6+4 = 21 minutes) both make the same underlying mistake: assuming a join fires as soon as any one parent completes, rather than waiting for the slowest one. A real pipeline configured that way would let Build start compiling before Unit Tests had even reported pass/fail — a race condition, not a speedup.

Question 196 · Linear Algebra Foundations: The Hidden Math Behind Netflix and Google · hard

A Netflix-style Indian streaming service records these 1-to-5-star ratings for three films — RRR, Pathaan, and 3 Idiots — from two users: User A rated them 5, 3, 4 and User B rated them 2, 5, 5. Because different users rate on different personal scales, the recommendation engine first subtracts each user's own average rating from their scores (mean-centering) before measuring similarity, producing the adjusted cosine similarity used in real collaborative-filtering systems. What is the adjusted cosine similarity between User A and User B, and what does its sign reveal about their taste correlation?

  1. +√3/2 ≈ 0.87 — computed directly from the raw ratings without mean-centering, incorrectly suggesting the two users have strongly similar taste.
  2. -√3/2 ≈ -0.87 — using mean-centered (adjusted) cosine similarity, correctly indicating User A and User B's tastes are strongly negatively correlated despite both giving positive raw star ratings.
  3. -1/4 (-0.25) — obtained by dividing the centred dot product by the product of the squared vector norms instead of the norms themselves.
  4. -1.22 — obtained by dividing the centred dot product by only User B's vector norm, a value that is impossible for any cosine similarity.

Answer: B. -√3/2 ≈ -0.87 — using mean-centered (adjusted) cosine similarity, correctly indicating User A and User B's tastes are strongly negatively correlated despite both giving positive raw star ratings.

ExplanationReal recommender systems never compare raw star ratings directly, because some users rate generously and others harshly — the fix is mean-centering each user's vector before measuring similarity. Step 1: center each vector on its own average. User A's mean is (5+3+4)/3 = 4, giving the centered vector (1, -1, 0). User B's mean is (2+5+5)/3 = 4, giving (-2, 1, 1). Step 2: take the dot product of the centered vectors: (1)(-2) + (-1)(1) + (0)(1) = -2 - 1 + 0 = -3. Step 3: compute the norms: ||A_c|| = √(1² + (-1)² + 0²) = √2, and ||B_c|| = √((-2)² + 1² + 1²) = √6. Step 4: divide the dot product by the product of the norms: -3/(√2·√6) = -3/√12 = -3/(2√3) = -√3/2 ≈ -0.87. Since both users happen to average exactly 4 stars, their raw rating vectors look superficially alike (both mostly 4s and 5s) — computing cosine similarity on the uncentered ratings gives +√3/2 ≈ 0.87, the opposite conclusion. But once each user's personal generosity bias is stripped away, User A rated RRR above their own average while rating Pathaan and 3 Idiots below it, and User B did exactly the reverse. The centered vectors therefore point in nearly opposite directions (the angle between them is 150°, since cos 150° = -√3/2), so the adjusted similarity correctly flags these two users as having opposing taste — precisely why production systems like Netflix's collaborative filter mean-center ratings rather than comparing them raw.

Question 197 · Cross-Validation and Model Selection: Choosing the Right Model for Your Problem · hard

A team at an Indian fintech startup is building a UPI fraud-detection classifier and must choose between two candidate models using 5-fold cross-validation on the same 1,000-transaction dataset. Model A (logistic regression) records fold accuracies of 91%, 89%, 90%, 92%, and 88%. Model B (a decision tree of depth 10) records fold accuracies of 93%, 85%, 94%, 87%, and 91%. Both models come out to an identical mean cross-validation accuracy of 90%. Based on a rigorous comparison of the fold-to-fold variability (sample standard deviation) of the two models, which model should the team select for deployment, and why?

  1. Model A, because both models share the same mean accuracy (90%) but Model A's fold accuracies are far more consistent (standard deviation ≈1.6 percentage points vs Model B's ≈3.9 percentage points), indicating more reliable generalization to unseen transactions.
  2. Model B, because its single best fold accuracy (94%) exceeds Model A's best fold accuracy (92%), showing that Model B has higher peak predictive capability.
  3. Neither model, because identical mean cross-validation accuracies mean the two models are statistically indistinguishable and can be deployed with equal confidence.
  4. Model B, because a decision tree of depth 10 has strictly greater representational capacity than logistic regression, so it is guaranteed to generalize better to new fraud patterns.

Answer: A. Model A, because both models share the same mean accuracy (90%) but Model A's fold accuracies are far more consistent (standard deviation ≈1.6 percentage points vs Model B's ≈3.9 percentage points), indicating more reliable generalization to unseen transactions.

ExplanationBoth models tie exactly on mean cross-validation accuracy: for Model A, (91+89+90+92+88)/5 = 450/5 = 90%; for Model B, (93+85+94+87+91)/5 = 450/5 = 90%. When means tie, sound model selection falls back to the spread of scores across folds, because a model whose accuracy swings widely between folds is more sensitive to which particular transactions happened to land in each validation split — a hallmark of overfitting to fold-specific noise rather than learning a generalizable fraud signal. Computing the sample standard deviation (dividing by k−1 = 4, the standard unbiased estimator for a small number of folds): Model A's deviations from 90% are +1, −1, 0, +2, −2 percentage points, so the squared deviations sum to 1+1+0+4+4 = 10, giving variance 10/4 = 2.5 and standard deviation √2.5 ≈ 1.58 percentage points. Model B's deviations are +3, −5, +4, −3, +1, so the squared deviations sum to 9+25+16+9+1 = 60, giving variance 60/4 = 15 and standard deviation √15 ≈ 3.87 percentage points. Model A is therefore roughly 2.4 times more consistent across folds than Model B despite the identical mean, which is strong evidence that the depth-10 decision tree is overfitting to particular training folds and would carry more deployment risk on new UPI transaction patterns. Picking on a single standout fold (94% for Model B) ignores that the same model also cratered to 85% on another fold — cherry-picking the best split tells you nothing about expected performance on a held-out transaction stream. Declaring the models "statistically indistinguishable" from a tied mean alone is also unjustified: the whole point of computing fold-level variance is that it reveals a real difference in reliability that the mean conceals. And higher model capacity (a deeper tree) never guarantees better generalization — it only guarantees a better fit to the training folds, with generalization an empirical question that here is answered by the variance, not the architecture. Model A is the correct deployment choice.

Question 198 · Introduction to Machine Learning with Python · hard

A Python data-science team is training a simple no-bias linear regression model to score UPI transactions for fraud risk: ŷ = w·x, where x is the transaction amount in thousands of rupees. The model is trained on two examples, (x=2, y=3) and (x=4, y=5), using the mean squared error loss ```python import numpy as np x = np.array([2, 4]) # transaction amount, ₹ thousands y = np.array([3, 5]) # true risk-score labels w = 0.5 # current weight alpha = 0.01 # learning rate y_hat = w * x gradient = -(2 / len(x)) * np.sum(x * (y - y_hat)) w_new = w - alpha * gradient ``` Given the current weight w = 0.5, what is the value of w_new after this single gradient descent step, rounded to two decimal places?

  1. 0.34
  2. 0.66
  3. 0.58
  4. 0.82

Answer: B. 0.66

ExplanationStart by computing the model's current predictions with w = 0.5: ŷ = w·x gives ŷ₁ = 0.5·2 = 1 and ŷ₂ = 0.5·4 = 2. The residuals (y − ŷ) are therefore 3 − 1 = 2 and 5 − 2 = 3. For the loss L(w) = (1/n)Σ(yᵢ − w·xᵢ)², differentiating with respect to w gives dL/dw = −(2/n)Σxᵢ(yᵢ − ŷᵢ). Plugging in the residuals: Σxᵢ(yᵢ − ŷᵢ) = 2·2 + 4·3 = 4 + 12 = 16. With n = 2, the gradient is −(2/2)·16 = −16. The gradient descent update rule subtracts the learning rate times the gradient: w_new = w − α·(dL/dw) = 0.5 − 0.01·(−16) = 0.5 + 0.16 = 0.66. The value 0.34 comes from flipping the update rule's sign — adding α times the gradient instead of subtracting it (0.5 + 0.01·(−16) = 0.34) — which is gradient ascent, moving toward higher loss rather than lower. The value 0.58 results from dropping the factor of 2 that comes from differentiating the squared term, using gradient = −(1/n)Σxᵢ(yᵢ − ŷᵢ) = −8 instead of −16. The value 0.82 results from forgetting to average over the n training examples (treating the loss as a raw sum rather than a mean), which doubles the gradient's magnitude to −32 and produces an oversized update. Only w_new = 0.66 correctly applies both the chain-rule factor of 2 and the correctly signed update rule.

Question 199 · File Handling in Python: Reading and Writing Data · hard

A Class 10 student stores a CBSE mock-test mark sheet in a plain text file named `students.txt`, with a header on the first line: ``` Name,Marks Aarav,92 Bhavya,88 Chirag,79 Divya,95 ``` To compute the class total while skipping the header row, the student writes: ```python with open("students.txt", "r") as f: header = f.readline() total = 0 count = 0 for row in f: name, marks = row.strip().split(",") total += int(marks) count += 1 print(count, total) ``` What exact output does this program print, and why?

  1. A ValueError crash — the for loop re-reads the file from its start, so the header row is treated as data, and int() cannot convert the text 'Marks' into a number.
  2. `0 0` — readline() is assumed to consume the entire file in one call, so the for loop that follows finds no remaining lines to iterate over.
  3. `4 354` — the for loop resumes reading exactly where readline() left the file's cursor, so it processes only the four mark rows after the header.
  4. `3 262` — both the header and Aarav's row get skipped, so the loop starts summing only from Bhavya's marks onward.

Answer: C. `4 354` — the for loop resumes reading exactly where readline() left the file's cursor, so it processes only the four mark rows after the header.

ExplanationA Python file object is its own iterator, and it holds exactly one internal cursor shared by every read method called on it. `f.readline()` advances that cursor by one line, and a later `for row in f:` does not rewind or reopen the file — it simply resumes pulling lines from wherever the cursor already sits. So `header = f.readline()` consumes only `"Name,Marks\n"`, leaving the cursor positioned right before `"Aarav,92\n"`. The loop then walks the four remaining lines in order — Aarav (92), Bhavya (88), Chirag (79), Divya (95) — incrementing `count` once per row and building `total` as 92, then 92+88=180, then 180+79=259, then 259+95=354. Because `header` is captured but never used inside the loop, the CSV-style header is skipped cleanly without being counted or summed; this is precisely the standard idiom real scripts use to skip a header row before processing tabular data. The final `print(count, total)` therefore outputs `4 354`. The crash scenario wrongly assumes each `for line in f:` rescans the file from byte zero regardless of prior reads, ignoring that `readline()` and the `for` loop share one stream position. The `0 0` scenario wrongly conflates `readline()` (advances by one line) with `read()` (consumes the whole file). The `3 262` scenario compounds the pointer-sharing misunderstanding by assuming an extra, nonexistent skip beyond the header.

Question 200 · Feature Engineering Techniques · hard

A used-car pricing dataset for a neural network model contains the following six odometer readings (in kilometres) recorded in the training data: 15000, 42000, 8000, 97000, 128000, and 61000. Before training, the 'Odometer Reading' feature is rescaled using Min-Max normalization based on the minimum and maximum values found in this training data. A car in the test set has an odometer reading of 62500 km. What is this reading's Min-Max normalized value, rounded to three decimal places?

  1. 0.488
  2. 0.454
  3. 0.546
  4. 0.420

Answer: B. 0.454

ExplanationMin-Max normalization rescales a feature using x' = (x - min) / (max - min), where the minimum and maximum must be identified by scanning the entire training set, not assumed from the first few values seen. Checking all six training readings (15000, 42000, 8000, 97000, 128000, 61000) shows the true minimum is 8000 km and the true maximum is 128000 km, giving a range of 120000. For the test reading of 62500 km, x' = (62500 - 8000) / 120000 = 54500 / 120000 ≈ 0.454, which is the correctly normalized value. The value 0.420 comes from mistakenly treating 15000 as the minimum (perhaps because it appears earlier or looks like a natural starting point) instead of scanning the full list and finding the true minimum of 8000. The value 0.546 comes from inverting the formula to (max - x) / (max - min), which measures how far the reading is from the maximum rather than from the minimum, and not coincidentally equals one minus the correct answer. The value 0.488 comes from dividing the raw reading directly by the maximum (62500/128000) while skipping the subtraction of the minimum entirely, which abandons the Min-Max formula altogether rather than applying it correctly.
← Set 9Set 11 →