CodeBlanks Logo Icon CodeBlanks

Training KMeans

Train a KMeans model.

Choosing k and training

For k we'll compare values 2-6 with the silhouette score. A sensible number is also sought from the business side: 2 segments are too coarse for marketing, 8 segments unmanageable.

for k in range(2, 7):
    km = KMeans(n_clusters=k, n_init=10, random_state=0).fit(Xs)
    print(k, round(silhouette_score(Xs, km.labels_), 3))

When the technical criterion and the business need conflict, both must be weighed; the highest silhouette means nothing if the segments can't be used.

Stability check

An ARI comparison across different seeds confirms the chosen k is stable. The segment sizes are also checked: a segment with a single customer, or with 90% of customers, is unusable.

km = KMeans(n_clusters=3, n_init=10, random_state=0).fit(Xs)
print(np.bincount(km.labels_))   # segment sizes

Balanced, stable segments make campaigns reach meaningful groups.

Practice Choose k

Train KMeans (n_init=10, random_state=0) on the scaled data for k = 2-5. On each line print k, the silhouette score with round(..., 3) and the size of the smallest segment (np.bincount(km.labels_).min()). Weigh the silhouette and the segment size together.

Starter code

model.py
import numpy as np

rng = np.random.default_rng(5)
n = 400
grup = rng.integers(0, 3, n)
harcama = np.array([300, 1500, 800])[grup] + rng.normal(0, 120, n)
ziyaret = np.array([2, 4, 12])[grup] + rng.normal(0, 1.2, n)
sepet = harcama / np.maximum(ziyaret, 1)
X = np.column_stack([harcama, ziyaret, sepet])
from sklearn.preprocessing import StandardScaler
Xs = StandardScaler().fit_transform(X)
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

# buraya yaz

Sample solution

model.py
import numpy as np

rng = np.random.default_rng(5)
n = 400
grup = rng.integers(0, 3, n)
harcama = np.array([300, 1500, 800])[grup] + rng.normal(0, 120, n)
ziyaret = np.array([2, 4, 12])[grup] + rng.normal(0, 1.2, n)
sepet = harcama / np.maximum(ziyaret, 1)
X = np.column_stack([harcama, ziyaret, sepet])
from sklearn.preprocessing import StandardScaler
Xs = StandardScaler().fit_transform(X)
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

for k in range(2, 6):
    km = KMeans(n_clusters=k, n_init=10, random_state=0).fit(Xs)
    print(k, round(silhouette_score(Xs, km.labels_), 3), np.bincount(km.labels_).min())

Expected output

2 0.501 141
3 0.699 128
4 0.711 12
5 0.659 12

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 If the silhouette is almost equal for k=3 and k=4, which is preferred?

  • Both are rejected
  • The more interpretable for the business, usually fewer clusters Correct answer
  • Always the larger
  • A random choice

Explanation: Fewer segments are easier to explain and act on.

2 Count the segment sizes.

Question
print(np.____(km.labels_))
Answer
print(np.bincount(km.labels_))

Explanation: bincount gives how many times each cluster number appears.

3 Match the checks.

Term Meaning
silhouette Separation quality
ARI across seeds Stability
bincount Segment sizes
business need Interpretability

All lessons in Intermediate