In Keras fit(), how do class_weight and sample_weight differ?
answer
- same mechanism, different granularity
- one keyed by label, one per row
- they multiply when combined
- metrics= ignores the weights
- never weight the test set
basics
~20 sclass_weight is a dict mapping class index to a multiplier, so every sample of a class is weighted the same. sample_weight is an array with one weight per training row. Both scale the per-sample loss before it is averaged; only their granularity differs.
solid answer
~50 sBoth arguments reach the same place — the per-sample loss is multiplied by a weight before reduction — but they are addressed differently. `class_weight={0: 1.0, 1: 5.0}` is keyed by **class index**, so Keras looks up each sample's label and applies the matching multiplier; it is the compact way to counter class imbalance. `sample_weight` is an array aligned with `x`, one float per row, so you can down-weight noisy labels, up-weight recent data, or express per-row confidence — things a class key cannot say. If you pass both, the effective weight is their product. Two traps: metrics passed under `metrics=` ignore weights entirely, so a weighted run still reports unweighted accuracy unless you use `weighted_metrics=`; and with a `tf.data` pipeline you normally attach weights by yielding `(x, y, sample_weight)` tuples rather than passing an array to `fit()`.
code
python · 15 linesimport numpy as np
import keras
x = np.random.rand(1000, 8)
y = (np.random.rand(1000) < 0.05).astype("int32") # 5% positives
model = keras.Sequential([keras.layers.Dense(1, activation="sigmoid")])
model.compile(
optimizer="adam",
loss="binary_crossentropy",
metrics=["accuracy"], # unweighted
weighted_metrics=[keras.metrics.Recall()], # sees sample weights
)
model.fit(x, y, class_weight={0: 1.0, 1: 19.0}, epochs=1, verbose=0)go deeper
Know that class_weight is a dict keyed by class index and sample_weight is one number per training row, and that both make some samples count more in the loss.
Explain that both scale the per-sample loss before reduction and multiply when combined, and that compiled metrics stay unweighted unless you pass weighted_metrics.
Compare weighting with over/under-sampling and with moving the inference threshold, and insist that validation stays unweighted. Be ready to say why weighting a 40-sample class does not manufacture signal.
Own the framing: imbalance is a cost-of-error question before it is a training-argument question. Decide the operating point from the business cost matrix, then pick weighting, resampling, thresholding or better data collection to reach it, and hold the evaluation set unweighted as ground truth.
## Where the weights are applied A Keras loss is computed per sample and then reduced — by default, averaged over the batch. Weighting inserts one step: each sample's loss is multiplied by its weight before that reduction. Nothing about the optimizer, the architecture or the data changes; the model simply gets a bigger gradient contribution from the samples you marked important. That is the whole mechanism, and both arguments feed it. ## class_weight `class_weight` is a dict from class index to a float, e.g. `{0: 1.0, 1: 5.0}` — every sample labelled 1 counts five times as much as a sample labelled 0. Its purpose is **class imbalance**: with a 95/5 split, an unweighted model can reach 95% accuracy by never predicting the minority class, and up-weighting the rare class makes ignoring it expensive. A common heuristic is inverse frequency: `weight[c] = n_samples / (n_classes * count[c])`. That equalises the total loss mass per class. It is a starting point, not a law — heavily up-weighting a tiny, noisy class amplifies its label noise along with its signal. When labels have a rank of 2 or more, the class lookup needs to be unambiguous: either one-hot encode `y`, or keep an explicit trailing dimension of 1 for sparse integer labels. ## sample_weight `sample_weight` is an array with the same leading dimension as `x` — one weight per row. It expresses anything that varies *within* a class: - **Label confidence.** Human-labelled rows at 1.0, model-pseudo-labelled rows at 0.3. - **Recency.** Older examples decayed toward zero so the model tracks a drifting distribution. - **Business value.** Rows tied to expensive errors weighted up. - **Masking.** A weight of exactly 0 removes a sample's contribution entirely without removing the row — useful for padded timesteps in sequence models, where the weight array can be per-timestep. Anything `class_weight` can do, `sample_weight` can also do (build the array by looking up each label). The reverse is not true, which is the cleanest way to state the relationship in an interview. ## They compose Pass both and the effective per-sample weight is the product. That is occasionally what you want — a rare class *and* a decayed timestamp — and occasionally a surprise, when someone adds class weighting to a pipeline that already carries sample weights and the minority class ends up weighted far harder than intended. ## The metrics trap This is the detail that separates people who have run a weighted training job from people who have read about one. Metrics passed as `metrics=[...]` in `compile()` are **not** weighted. So a run with `class_weight` set reports plain, unweighted accuracy — which is arguably what you want (the true accuracy on the real distribution), but is definitely not what many people assume they are reading. `compile(weighted_metrics=[...])` is the opposite: those metrics receive the sample weights and report the weighted figure. Knowing that you can pass both lists, and that the two report different numbers on purpose, is the payoff. `evaluate()` also accepts `sample_weight`, so a weighted evaluation is possible; it does not take `class_weight`. ## Weighting versus resampling Weighting is not the only answer to imbalance, and a senior answer names the alternatives and their costs: - **Oversampling the minority** duplicates rows: the model sees the same examples repeatedly and can memorise them. - **Undersampling the majority** throws away real data. - **Weighting** keeps every row and changes only the loss, which is why it is usually the first thing to try — but it does not create information. With 40 positive examples, up-weighting them 50× gives you a model that is confidently wrong in 40 specific ways. - **Moving the decision threshold** at inference often achieves the operating point you actually wanted without touching training at all, and it is the cheapest experiment of the four. Whichever you choose, do **not** weight the validation or test set: those must reflect the real distribution, or the numbers you report are about a world that does not exist. ## With a tf.data pipeline When `x` is a dataset rather than arrays, the idiomatic route is for the pipeline itself to yield `(inputs, targets, sample_weights)` triples; `fit()` then unpacks the third element as the weights. That keeps the weight aligned with its row through shuffling and batching, which a separate array passed to `fit()` cannot guarantee once the pipeline reorders anything.
- You set class_weight and accuracy did not change at all — why?Metrics passed as `metrics=[...]` in `compile()` are unweighted, so they report performance on the real distribution regardless of the training weights. Use `compile(weighted_metrics=[...])` to see the weighted figure. Also check the confusion matrix rather than accuracy — class weighting usually trades majority-class precision for minority-class recall, which accuracy can hide entirely.
- When would you prefer sample_weight over class_weight for imbalance?When the importance varies within a class: noisy or pseudo-labelled rows down-weighted, recent rows up-weighted, per-row business cost, or a weight of 0 to mask padded timesteps. `sample_weight` can express everything `class_weight` can — just build the array by looking up each label — so it is the general tool and `class_weight` the shorthand.
- Should the validation set be weighted the same way as training?No. Validation and test numbers must describe the real distribution, so they stay unweighted; weighting them tells you how the model does in an imagined world. Weighting is a training-time signal about what to pay attention to. If the reported metric is not sensitive enough to the minority class, change the metric — recall, PR-AUC — not the weights.
- Does class weighting create information about a rare class?No. It only re-allocates gradient mass across data you already have. With 40 positives, weighting them 50× makes the model confident about those 40 examples, not about the class. When the minority is genuinely tiny, collecting more data, adjusting the decision threshold at inference, or reframing the problem as anomaly detection all beat turning the multiplier up.
saying these in an interview costs you the question
- Believing class_weight resamples or duplicates rows
- Expecting compiled metrics to reflect the weights
- Weighting the validation set the same as training
- Thinking class_weight and sample_weight cannot combine
- Treating up-weighting as a substitute for more minority data