সরাসরি পর্বে যান
পর্ব ৪১ · ৭৭-এর মধ্যে · মডিউল ৮

PCA-র বাইরে — kernel PCA, ICA আর NMF

Beyond PCA — Kernel PCA, Independent Component Analysis and Non-negative Matrix Factorization

Taught in Bangla, this lesson goes past PCA: kernel PCA built by hand with double-centred Gram matrices and matched to KernelPCA, FastICA separating mixed signals through non-Gaussianity, and NMF with Lee–Seung updates turning fictional shop baskets into readable profiles. Real in-browser runs compare reconstruction error with SVD and factor analysis.

এই পর্বে যা শিখবেন

  • PCA-র তিনটি সীমা, আর probabilistic PCA ও factor analysis-এর মডেল এক অনুচ্ছেদে
  • Kernel PCA হাতে: Gram matrix-এর double centring, eigen-decomposition আর projection, scikit-learn-এর KernelPCA-এর সঙ্গে মিলিয়ে
  • ICA কেন non-Gaussian উৎস চায়, whitening আর FastICA কী করে, আর চিহ্ন-মাপ-ক্রমের অস্পষ্টতা
  • NMF-এর multiplicative update হাতে ও NumPy-তে, আর অংশগুলোকে কেনাকাটার প্রোফাইল হিসেবে পড়া
  • PCA, kernel PCA, ICA, NMF আর factor analysis-এর মধ্যে প্রশ্ন দেখে বাছাই

AI-এর ভিত্তি কোর্সের পর্ব ৬৫-এ PCA আপনি SVD দিয়ে চালিয়েছেন, explained variance আর পুনর্গঠন দেখেছেন; এই কোর্সের পর্ব ০৮-এ দেখেছেন PCA আসলে covariance matrix-এর eigenvector, আর পর্ব ০৯-এ truncated SVD দিয়ে low-rank approximation. এই পর্ব সেখান থেকে শুরু করে তিনটি প্রশ্নের উত্তর খোঁজে, যা PCA দিতে পারে না: গঠন বাঁকা হলে কী করবেন, কয়েকটি স্বাধীন উৎস মিশে গেলে কীভাবে আলাদা করবেন, আর ডেটা যদি শুধু অঋণাত্মক অংশ যোগ করে তৈরি হয়। চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python, scikit-learn 1.8.0); প্রথমবার scikit-learn নামতে প্রায় দশ সেকেন্ড লাগে।

১ · PCA কোথায় থামে — চারটি পদ্ধতি, চারটি প্রশ্ন

একটি বিয়েবাড়ির রান্নাঘরে তিনজন বাবুর্চি একসঙ্গে কথা বলছেন, আর ঘরের তিন কোণে তিনটি ফোন রেকর্ড করছে। প্রতিটি রেকর্ডিংয়ে তিনজনের গলা মিশে আছে, ভিন্ন অনুপাতে। PCA জিজ্ঞেস করে, “কোন দিকে শব্দের ওঠানামা সবচেয়ে বেশি?” এর উত্তর একটি “গড় গোলমাল” দেয়, কোনো একজন বাবুর্চির গলা নয়। ICA জিজ্ঞেস করে, “কোন মিশ্রণ উল্টালে তিনটি স্বাধীন গলা পাওয়া যায়?” একই ডেটা, ভিন্ন প্রশ্ন, ভিন্ন উত্তর।

PCA-র উত্তর তিনটি শর্তে বাঁধা। এক, সে linear: নতুন অক্ষগুলো মূল feature-এর সরল ওজনযুক্ত যোগ, তাই বৃত্ত বা পেঁচানো গঠন খুলতে পারে না। দুই, সে শুধু দ্বিতীয় moment (variance আর covariance) দেখে, আর অক্ষগুলো পরস্পর লম্ব হতে বাধ্য; ফলে উপাদানগুলো uncorrelated হয়, কিন্তু স্বাধীন নয়। তিন, অক্ষে ঋণাত্মক ওজন থাকে: “চাল +০.৫, বই −০.৩” ধরনের উপাদান গণিতে ঠিক, কিন্তু কেনাকাটার ভাষায় পড়া কঠিন। নিচের ছবিতে চারটি পদ্ধতি একই ধরনের প্রশ্নের চারটি রূপ।

চারটি পদ্ধতি, প্রত্যেকে আলাদা জিনিস খোঁজে

PCA, kernel PCA, ICA and NMF ask different questions of the same data

PCA, kernel PCA, ICA and NMF compared as four small pictures PCA: সবচেয়ে বেশি variance অক্ষগুলো পরস্পর লম্ব PC1 লম্বা তীর, PC2 তার ওপর লম্ব Kernel PCA: φ-এর জগতে PCA বাঁকা গঠন খুলে যায় RBF ICA: স্বাধীন উৎসের দিক অক্ষ লম্ব হওয়া জরুরি নয় মেশানো মেঘের দুই বাহু = দুটি উৎসের দিক NMF: শুধু যোগ, সব ঘর ≥ ০ অংশ জুড়ে সম্পূর্ণ ছবি = + ক্রেতা সংসার ছাত্র-গ্যাজেট
ছবি ৪১.১ — চারটি পদ্ধতিই ডেটাকে কম কয়েকটি উপাদানে প্রকাশ করে (dimensionality reduction), কিন্তু “ভালো উপাদান” মানে প্রত্যেকের কাছে আলাদা: বেশি variance, বাঁকা গঠনের অক্ষ, পরিসংখ্যানে স্বাধীনতা, বা অঋণাত্মক অংশ।

Probabilistic PCA আর factor analysis — এক অনুচ্ছেদে

PCA-কে একটি সম্ভাবনার মডেল হিসেবেও লেখা যায়। ধরুন প্রতিটি ডেটা বিন্দু x = Wz + μ + ε, যেখানে z হলো k মাত্রার গোপন কারণ (latent factor), z ~ N(০, I); W একটি d × k matrix; μ গড়; আর ε হলো noise, ε ~ N(০, σ²I). Michael Tipping আর Christopher Bishop ১৯৯৯ সালে দেখান, এই মডেলের maximum likelihood (পর্ব ১৮) সমাধানে W-এর কলামগুলো ঠিক প্রথম k টি principal component-এর জগৎ জুড়ে থাকে। এর নাম probabilistic PCA. লাভ: একটি likelihood পাওয়া যায় (model তুলনা আর অনুপস্থিত মান সামলানো সহজ), আর x-এর বণ্টন একটি পর্ব ১৯-এর multivariate Gaussian, যার covariance WWᵀ + σ²I. Noise-কে প্রতিটি feature-এ আলাদা হতে দিলে, অর্থাৎ σ²I-এর জায়গায় একটি diagonal matrix Ψ, পাওয়া যায় পুরোনো factor analysis. অংশ ৮-এ দুটিকে কাজে দেখবেন।

২ · Kernel PCA — Gram matrix কেন্দ্রে এনে eigenvector

পর্ব ২৯-এ দেখেছেন, যে algorithm শুধু dot product ব্যবহার করে, তাতে x·z-এর জায়গায় একটি kernel k(x, z) = φ(x)·φ(z) বসালে φ না বানিয়েই উঁচু মাত্রায় কাজ করা যায়। Bernhard Schölkopf, Alexander Smola আর Klaus-Robert Müller ১৯৯৮ সালে (Neural Computation) দেখান, PCA-ও এমন একটি algorithm. কারণটি সহজ: n টি বিন্দুর ডেটা matrix X (গড় শূন্য) হলে covariance XᵀX/n-এর eigenvector যে দিক দেয়, Gram matrix K = XXᵀ-এর eigenvector সেই একই দিকে প্রতিটি বিন্দুর অবস্থান দেয় (পর্ব ০৯-এর SVD-এর দুই পাশ)। আর K-এর প্রতিটি ঘর একটি dot product. তাই Kij = k(xi, xj) বসালেই φ-এর জগতে PCA, যার নাম kernel PCA.

একটি বাধা আছে। PCA-র আগে ডেটার গড় শূন্য করতে হয়, কিন্তু φ(x) আমাদের হাতে নেই, তাই φ-এর গড় বাদ দেওয়া সরাসরি সম্ভব নয়। সমাধান হলো K-কেই কেন্দ্রে আনা (kernel centring). 1n দিয়ে বোঝাই n × n matrix, যার প্রতিটি ঘর ১/n. তাহলে

K̃ = K − 1nK − K1n + 1nK1n

ঘর ধরে পড়লে: K̃ij = Kij − (i-তম সারির গড়) − (j-তম কলামের গড়) + (সব ঘরের গড়)। এর নাম double centring. ছোট একটি উদাহরণে দেখুন, যেখানে উত্তর আগে থেকেই জানা: linear kernel-এ K̃ হওয়া উচিত গড় বাদ দেওয়া ডেটার Gram matrix.

K̃ᵢⱼ = Kᵢⱼ − rᵢ − cⱼ + gφ না ছুঁয়ে গড় বাদ

            
  1. শুরু। তিনটি সংখ্যা ০, ১, ৫; linear kernel মানে সাধারণ গুণ। K-এর ঘরগুলো সব জোড়ার গুণফল। গড় ২, তাই এখনো কেন্দ্রে নয়।
  2. তিনটি গড়। সারির গড় (০, ২, ১০), প্রতিসম বলে কলামেরও একই; নয়টি ঘরের যোগফল ৩৬, গড় ৪.
  3. সারির গড় বাদ। দ্বিতীয় সারি থেকে ২, তৃতীয় সারি থেকে ১০ বাদ গেল।
  4. কলামের গড় বাদ। এবার প্রতিটি কলাম থেকে ০, ২ আর ১০. দুবার বাদ দিতে গিয়ে সব ঘরের গড় দুবার কাটা পড়েছে।
  5. সব ঘরের গড় ফেরত। তাই একবার g = ৪ যোগ। ফল K̃-এর প্রতিটি সারির যোগফল শূন্য, যা কেন্দ্রে আনা ডেটার চিহ্ন।
  6. যাচাই আর projection. গড় বাদ দেওয়া ডেটা (−২, −১, ৩)-এর বাইরের গুণফল হুবহু K̃. একটিমাত্র অশূন্য eigenvalue ১৪; √λ · α দিলে ফেরত আসে (−২, −১, ৩), অর্থাৎ প্রথম উপাদানে প্রতিটি বিন্দুর অবস্থান।

বাকি কাজ সাধারণ eigen-decomposition (পর্ব ০৮)। K̃α = λα থেকে eigenvalue বড় থেকে ছোট ক্রমে সাজান। প্রতিটি α-কে একক দৈর্ঘ্যের রাখলে train বিন্দুগুলোর projection হলো √λ · α. K̃ হলো পর্ব ১০-এর PSD matrix, তাই সব λ ≥ ০ (গণনার গোলমালে খুব ছোট ঋণাত্মক আসতে পারে)। Covariance-এর eigenvalue হলো λ/n. নতুন একটি বিন্দু x-এর জন্য লাগে k(x, xi)-এর একটি সারি, যেটিও train-এর গড় দিয়ে একইভাবে কেন্দ্রে আনতে হয়; scikit-learn-এর KernelPCA.transform এটি নিজেই করে।

৩ · সমকেন্দ্রিক বৃত্তে kernel PCA, হাতে আর scikit-learn-এ

নিচের ব্লক scikit-learn-এর make_circles দিয়ে ২০০টি বিন্দুর দুটি সমকেন্দ্রিক বৃত্ত বানায় (seed দেওয়া)। তারপর হাতে RBF Gram matrix, double centring আর scipy.linalg.eigh দিয়ে kernel PCA করে, KernelPCA-এর সঙ্গে মেলায়, আর linear PCA-র সঙ্গে তুলনা করে। তুলনার মাপ: প্রথম উপাদানের অক্ষে একটিমাত্র কাটা দাগ দিয়ে দুই বৃত্ত সবচেয়ে ভালো কতটা আলাদা হয়। লক্ষ করুন, PCA নিজে লেবেল দেখে না; লেবেল শুধু ফল মাপতে।

import numpy as np
import matplotlib.pyplot as plt
from scipy.linalg import eigh
from sklearn.datasets import make_circles
from sklearn.decomposition import PCA, KernelPCA

X, y = make_circles(n_samples=200, factor=0.3, noise=0.05, random_state=41)
gamma, n = 2.0, len(X)
sq = ((X[:, None, :] - X[None, :, :]) ** 2).sum(axis=2)   # সব জোড়ার দূরত্বের বর্গ
K = np.exp(-gamma * sq)                                   # RBF Gram matrix, 200 x 200
J = np.full((n, n), 1 / n)
Kc = K - J @ K - K @ J + J @ K @ J                        # double centring
lam, A = eigh(Kc)                                         # ছোট থেকে বড় ক্রমে
lam, A = lam[::-1][:2], A[:, ::-1][:, :2]                 # সবচেয়ে বড় দুটি
Z_mine = A * np.sqrt(lam)                                 # projection = √λ · α
kp = KernelPCA(n_components=2, kernel="rbf", gamma=gamma).fit(X)
Z_skl = kp.transform(X)
sign = np.sign((Z_mine * Z_skl).sum(axis=0))              # eigenvector-এর চিহ্ন ইচ্ছামতো
print("my eigenvalues     :", np.round(lam, 3))
print("sklearn eigenvalues:", np.round(kp.eigenvalues_, 3))
print("sign flips:", sign, " max |diff| =", f"{np.abs(Z_mine * sign - Z_skl).max():.1e}")

def split_acc(z):                                         # এক অক্ষে একটি কাটা দাগে সেরা accuracy
    return max(max(((z > c) == y).mean(), ((z <= c) == y).mean()) for c in z)

Z_lin = PCA(n_components=2).fit_transform(X)
for name, Z in [("linear PCA", Z_lin), ("kernel PCA", Z_mine)]:
    print(f"{name}: one cut on component 1 -> accuracy {split_acc(Z[:, 0]):.3f}")

fig, ax = plt.subplots(1, 3, figsize=(11, 3.6))
for a, Z, t in zip(ax, [X, Z_lin, Z_mine], ["original", "linear PCA", "RBF kernel PCA"]):
    a.scatter(Z[:, 0], Z[:, 1], c=y, cmap="coolwarm", s=10)
    a.set(title=t, xlabel="component 1", ylabel="component 2")
fig.subplots_adjust(wspace=0.35, bottom=0.17)

আসল আউটপুট (ব্রাউজারের Python), নিচে তিনটি ছবি:

my eigenvalues     : [29.661 24.46 ]
sklearn eigenvalues: [29.661 24.46 ]
sign flips: [-1.  1.]  max |diff| = 1.1e-14
linear PCA: one cut on component 1 -> accuracy 0.690
kernel PCA: one cut on component 1 -> accuracy 1.000

হাতে বানানো eigenvalue আর scikit-learn-এর eigenvalues_ হুবহু এক (২৯.৬৬১ আর ২৪.৪৬০)। এগুলো কেন্দ্রে আনা K-এর eigenvalue; covariance-এর ভাষায় ভাগ করতে হবে n = ২০০ দিয়ে। প্রথম উপাদানের চিহ্ন উল্টো এসেছে (−১), কারণ α আর −α দুটিই সমান বৈধ eigenvector; চিহ্ন মিলিয়ে নিলে দুই হিসাবের সবচেয়ে বড় পার্থক্য ১.১ × ১০⁻¹⁴, শুধু floating-point-এর গোলমাল। আসল খবর শেষ দুই লাইনে: linear PCA শুধু অক্ষ ঘোরায়, তাই বৃত্ত দুটি বৃত্তই থাকে, আর একটি কাটা দাগে সেরা accuracy ০.৬৯০. RBF kernel PCA-র প্রথম উপাদানে ভেতরের বৃত্ত এক পাশে, বাইরেরটি অন্য পাশে: accuracy ১.০০০. ডান ছবিতে দুটি খাড়া স্তূপ এটাই দেখায়।

γ বাছাই লেবেল ছাড়া কঠিন। এখানে γ = ২ ভালো কাজ করেছে, কারণ বৃত্ত দুটির দূরত্ব আর γ-এর মাপ মিলেছে। খুব ছোট γ-তে kernel PCA প্রায় linear PCA-র মতো আচরণ করে, খুব বড় γ-তে প্রতিটি বিন্দু প্রায় একা (Gram matrix প্রায় একক matrix). Unsupervised কাজে সঠিক উত্তর জানা থাকে না, তাই γ বাছতে হয় পরের কাজ দিয়ে (যেমন পরে একটি classifier-এর cross-validation), বা পুনর্গঠনের ত্রুটি দিয়ে। আর Gram matrix n × n: ২০০টি বিন্দুতে ৪০,০০০ ঘর, এক লাখ বিন্দুতে ১০¹⁰ ঘর। বড় ডেটায় পর্ব ৩১-এর Nyström বা random features লাগে।

Pre-image সমস্যা: সাধারণ PCA-তে উপাদান থেকে মূল জগতে ফেরা সহজ (W দিয়ে গুণ আর গড় যোগ)। Kernel PCA-র উপাদান থাকে φ-এর জগতে, আর সেখানকার একটি বিন্দুর ঠিক বিপরীত মূল x সাধারণত থাকেই না। একে pre-image problem বলে। Sebastian Mika ও সহকর্মীরা ১৯৯৯ সালে noise কমানোর কাজে একটি পুনরাবৃত্ত সমাধান দেন; scikit-learn-এর KernelPCA(fit_inverse_transform=True) আলাদা একটি kernel ridge regression শিখে আনুমানিক উল্টো পথ দেয়। দুটিই আনুমানিক, হুবহু নয়।

৪ · ICA — মেশানো গলা থেকে আলাদা বক্তা

Independent component analysis (ICA)-র মডেল খুব সরল: x = As. এখানে s হলো কয়েকটি অদেখা উৎসের মান (যেমন তিন বাবুর্চির গলা), A একটি অজানা মিশ্রণের matrix (কোন ফোন কার গলা কতটা শোনে), আর x হলো যা আমরা মাপি। লক্ষ্য: শুধু x দেখে A আর s দুটিই বের করা। A বা s কোনোটি না জেনে উৎস আলাদা করার এই কাজের নাম blind source separation. একমাত্র অনুমান: উৎসগুলো পরিসংখ্যানে স্বাধীন, আর (বড়জোর একটি ছাড়া) non-Gaussian.

প্রথম ধাপ whitening: x-কে এমনভাবে রূপান্তর করা যাতে covariance একক matrix হয় (পর্ব ১৯-এর হেলানো উপবৃত্ত গোল করা)। এটি PCA দিয়েই হয়, আর এতে কাজের অর্ধেক শেষ: whitening-এর পর বাকি থাকে শুধু একটি অজানা ঘূর্ণন। কিন্তু কোন ঘূর্ণন? দ্বিতীয় moment আর কিছু বলতে পারে না, কারণ whitened ডেটার যেকোনো ঘূর্ণনের covariance-ও একক matrix. এখানে ঢোকে উঁচু moment, বিশেষ করে kurtosis.

whitened y = R(θ) sকোন θ সঠিক?

            
  1. Whitening-এর পর। covariance একক, তাই PCA-র কাজ শেষ। কিন্তু উৎস ফেরত পেতে একটি ঘূর্ণন কোণ θ এখনো অজানা।
  2. Gaussian-এর দেয়াল। গোল Gaussian মেঘ যেকোনো দিকে ঘোরালে একই দেখায়। তাই দুটি Gaussian উৎস কোনো পদ্ধতিই আলাদা করতে পারে না; ঘূর্ণনটি পরিচয়হীন।
  3. Kurtosis. চতুর্থ moment থেকে ৩ বাদ দিলে Gaussian-এ শূন্য। Uniform-এ −১.২, Laplace-এ +৩. শূন্য থেকে দূরত্বই non-Gaussianity-র একটি সহজ মাপ।
  4. মেশালে কী হয়। একই κ-এর দুটি স্বাধীন উৎস θ কোণে মেশালে κ গুণ হয় cos⁴θ + sin⁴θ দিয়ে, যা θ = ০°-এ ১ আর ৪৫°-এ সবচেয়ে কম ০.৫.
  5. সংখ্যায়। Uniform-এর −১.২০ মিশ্রণে −০.৬০, Laplace-এর ৩.০০ মিশ্রণে ১.৫০. এটি central limit theorem-এর ছোট রূপ: স্বাধীন জিনিস যোগ করলে ফল বেশি Gaussian.
  6. উল্টো পথ আর তার সীমা। তাই ICA সেই দিক খোঁজে যেখানে বণ্টন সবচেয়ে কম Gaussian. কিন্তু চিহ্ন, মাপ আর ক্রম ডেটা থেকে নির্ধারণ করা যায় না, কারণ এগুলো বদলালেও x একই থাকে।

Kurtosis সহজ, কিন্তু কয়েকটি বড় মানের প্রতি খুব সংবেদনশীল। আরও শক্ত মাপ negentropy: একই variance-এর Gaussian-এর entropy থেকে আসল বণ্টনের entropy বাদ দেওয়া (AI-এর ভিত্তি কোর্সের পর্ব ৩৮-এ entropy). একই variance-এ Gaussian-এর entropy সবচেয়ে বেশি, তাই negentropy সবসময় ≥ ০, আর শূন্য শুধু Gaussian-এ। Aapo Hyvärinen আর Erkki Oja ১৯৯৭ সালে (Neural Computation) FastICA প্রকাশ করেন, যা negentropy-র একটি আনুমানিক রূপ (scikit-learn-এ ডিফল্ট fun="logcosh", অর্থাৎ G(u) = log cosh u) একটি fixed-point পুনরাবৃত্তিতে সর্বোচ্চ করে: w ← E[x g(wᵀx)] − E[g′(wᵀx)] w, তারপর w-কে একক দৈর্ঘ্যে আনা আর অন্য উপাদানের থেকে আলাদা (decorrelate) রাখা। এখানে g হলো G-এর অন্তরজ, অর্থাৎ tanh.

ইতিহাস: ফ্রান্সের Jeanny Hérault আর Christian Jutten ১৯৮০-এর দশকের মাঝামাঝি একটি স্নায়ু-অনুপ্রাণিত পদ্ধতিতে মেশানো সংকেত আলাদা করার কাজ শুরু করেন। Pierre Comon ১৯৯৪ সালে (Signal Processing) “independent component analysis” ধারণাটিকে গাণিতিক রূপ দেন, আর Anthony Bell ও Terrence Sejnowski ১৯৯৫ সালে তথ্য-তত্ত্বভিত্তিক infomax algorithm দিয়ে একে জনপ্রিয় করেন। পরে মস্তিষ্কের EEG রেকর্ডিং থেকে চোখের পলকের মতো artifact আলাদা করতে ICA বহুল ব্যবহৃত হয়।

৫ · তিনটি সংকেত মিশিয়ে আবার আলাদা — PCA বনাম FastICA

নিচের ব্লকে তিনটি উৎস: একটি sine তরঙ্গ, একটি square তরঙ্গ আর একটি sawtooth (করাতের দাঁতের মতো) তরঙ্গ, সঙ্গে সামান্য noise. একটি 3 × 3 matrix A দিয়ে মিশিয়ে তিনটি “মাইক্রোফোন”-এর সংকেত বানানো হয়েছে। তারপর PCA (whitening-সহ) আর FastICA দুটিই চালিয়ে দেখা হয়েছে, পাওয়া উপাদান আর আসল উৎসের correlation কত। scipy.stats.kurtosis ডিফল্টে excess kurtosis দেয় (Gaussian-এ ০)।

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import kurtosis
from sklearn.decomposition import PCA, FastICA

rng = np.random.default_rng(41)
t = np.linspace(0, 8, 2000)
S = np.column_stack([np.sin(2 * t),                       # sine
                     np.sign(np.sin(3 * t)),              # square
                     2 * ((0.8 * t) % 1) - 1])            # sawtooth
S += 0.02 * rng.normal(size=S.shape)                      # সামান্য noise
A = np.array([[1.0, 1.0, 1.0], [0.5, 2.0, 1.0], [1.5, 1.0, 2.0]])
X = S @ A.T                                               # তিনটি “মাইক্রোফোন”
print("excess kurtosis of sources:", np.round(kurtosis(S), 2))
print("excess kurtosis of mixes  :", np.round(kurtosis(X), 2))

ica = FastICA(n_components=3, whiten="unit-variance", random_state=4)
S_ica = ica.fit_transform(X)
S_pca = PCA(n_components=3, whiten=True).fit_transform(X)
np.set_printoptions(suppress=True)
for name, E in [("PCA", S_pca), ("ICA", S_ica)]:
    C = np.corrcoef(S.T, E.T)[:3, 3:]                     # সারি = আসল উৎস, কলাম = পাওয়া উপাদান
    print(f"\n{name}: corr(true source, component)\n{np.round(C, 2)}")
    print("best |corr| per source:", np.round(np.abs(C).max(axis=1), 3))
print("\nstd: true", np.round(S.std(axis=0), 2), "| ICA", np.round(S_ica.std(axis=0), 2))

fig, ax = plt.subplots(3, 3, figsize=(10, 5.5), sharex=True)
for j in range(3):
    ax[j, 0].plot(t[:600], S[:600, j]); ax[j, 1].plot(t[:600], X[:600, j])
    ax[j, 2].plot(t[:600], S_ica[:600, j], color="tab:green")
for a, ttl in zip(ax[0], ["true sources", "mixtures (observed)", "FastICA output"]):
    a.set_title(ttl)
fig.subplots_adjust(hspace=0.3, wspace=0.25)

আসল আউটপুট (ব্রাউজারের Python), নিচে ৩ × ৩ ছবি:

excess kurtosis of sources: [-1.36 -1.99 -1.21]
excess kurtosis of mixes  : [-1.17 -1.68 -1.21]

PCA: corr(true source, component)
[[ 0.46 -0.57  0.67]
 [ 0.8   0.59  0.08]
 [ 0.43 -0.55 -0.72]]
best |corr| per source: [0.674 0.804 0.716]

ICA: corr(true source, component)
[[ 0.06 -1.   -0.05]
 [ 0.03 -0.03 -1.  ]
 [ 1.    0.03  0.06]]
best |corr| per source: [0.997 0.999 0.998]

std: true [0.69 1.   0.58] | ICA [1. 1. 1.]

Kurtosis: তিনটি উৎসই চ্যাপ্টা ধরনের non-Gaussian (square-এর −১.৯৯, প্রায় দুই মানের মধ্যে লাফানো সংকেতের সীমা −২)। মিশ্রণগুলোর গড় |κ| কম, অর্থাৎ মিশ্রণ বেশি Gaussian, ঠিক যেমন stepper বলেছিল। PCA-র প্রতিটি উপাদান তিন উৎসের জগাখিচুড়ি: প্রতিটি উৎসের সেরা মিল মাত্র ০.৬৭ থেকে ০.৮০. PCA-র উপাদানগুলো uncorrelated, কিন্তু স্বাধীন নয়। ICA-র ছকে প্রতিটি সারিতে একটিমাত্র ঘর প্রায় ±১, বাকিগুলো প্রায় শূন্য: প্রতিটি উৎস ফেরত এসেছে।

তবে ছকটি ভালো করে পড়ুন। Sine এসেছে দ্বিতীয় কলামে, square তৃতীয়তে, sawtooth প্রথমে: ক্রম বদলেছে। Sine আর square-এর corr −১: চিহ্ন উল্টো। আর শেষ লাইন: আসল উৎসের std ০.৬৯, ১.০০, ০.৫৮, কিন্তু ICA-র সব উপাদানের std ১, কারণ whiten="unit-variance" মাপটি ঠিক করে দেয়: মাপ হারিয়েছে। এগুলো FastICA-র ত্রুটি নয়, মডেলের গঠন: x = As-এ s-এর একটি উপাদানকে c দিয়ে গুণ আর A-র সংশ্লিষ্ট কলামকে c দিয়ে ভাগ করলে x বদলায় না। অন্য random_state-এ ক্রম আর চিহ্ন অন্যরকম আসতে পারে।

scikit-learn 1.8.0-এ whiten দুটি string নেয়: "unit-variance" (ডিফল্ট) বা "arbitrary-variance", অথবা False. পুরোনো টিউটোরিয়ালের whiten=True এখন InvalidParameterError দেয় (ডেস্কটপে 1.8.0-এ যাচাই করা)। বাস্তবে ICA-র বড় সীমা হলো মডেলটি নিজে: ঘরের সত্যিকারের রেকর্ডিংয়ে শব্দ দেয়ালে প্রতিফলিত হয়ে দেরিতে পৌঁছায়, তাই মিশ্রণ আর একটি সরল matrix নয় (convolutive mixing); আর মাইক্রোফোনের চেয়ে উৎস বেশি হলে x = As উল্টানো যায় না। EEG আর fMRI-এর মতো ক্ষেত্রে, যেখানে মিশ্রণ প্রায় তাৎক্ষণিক আর রৈখিক, ICA সবচেয়ে ভালো কাজ করে।

৬ · NMF — শুধু যোগ করে অংশ থেকে সম্পূর্ণ

মুদির দোকানের হিসাব খাতা ভাবুন। প্রতিটি ক্রেতার মাসের কেনাকাটা কয়েকটি “অভ্যাস”-এর যোগফল: কেউ পুরো সংসারের বাজার করেন, কারও বাড়িতে নতুন শিশু, কেউ হোস্টেলের ছাত্র। একজন ক্রেতা হয়তো ৭০% সংসারী আর ৩০% ছাত্র (বড় ভাইয়ের জন্যও কেনেন)। কিন্তু কেউ “−২০% শিশুর বাবা-মা” হতে পারেন না। অভ্যাস যোগ হয়, বিয়োগ হয় না। NMF ঠিক এই শর্ত চাপায়।

Non-negative matrix factorization (NMF) একটি অঋণাত্মক n × d matrix V-কে দুটি অঋণাত্মক matrix-এর গুণফল হিসেবে আন্দাজ করে: V ≈ WH, যেখানে W হলো n × k (প্রতিটি ক্রেতার প্রতিটি অংশে কতটা ওজন) আর H হলো k × d (প্রতিটি অংশে কোন বিভাগ কতটা)। এটি পর্ব ০৯-এর matrix factorization, শুধু একটি বাড়তি শর্তে: W ≥ ০ আর H ≥ ০, ঘরে ঘরে। লক্ষ্য Frobenius norm-এর বর্গ কমানো:

সর্বনিম্ন করুন ‖V − WH‖F² = ∑i,j (Vij − (WH)ij)²,  শর্ত W ≥ ০, H ≥ ০

যেহেতু কোনো বিয়োগ নেই, একটি অংশ আরেকটিকে কাটাকাটি করতে পারে না; ফলে অংশগুলো সাধারণত sparse আর পড়ার মতো হয়। একে parts-based representation বলে। Pentti Paatero আর Unto Tapper ১৯৯৪ সালে “positive matrix factorization” নামে বায়ুদূষণের উৎস খোঁজায় এই ধারণা ব্যবহার করেন; Daniel Lee আর Sebastian Seung ১৯৯৯ সালে Nature-এ মুখের ছবি থেকে চোখ, নাক, ঠোঁটের মতো অংশ শেখার উদাহরণ দিয়ে একে বিখ্যাত করেন, আর ২০০১ সালে সহজ multiplicative update নিয়ম প্রকাশ করেন:

H ← H ∘ (WᵀV) ⊘ (WᵀWH),   W ← W ∘ (VHᵀ) ⊘ (WHHᵀ)

এখানে ∘ মানে ঘরে ঘরে গুণ, ⊘ মানে ঘরে ঘরে ভাগ। নিয়মটি আসলে gradient descent, যার প্রতিটি ঘরের ধাপের মাপ এমনভাবে বাছা যে বিয়োগ চিহ্ন গুণ-ভাগে বদলে যায়। শুরুর মান ধনাত্মক হলে মান কখনো ঋণাত্মক হয় না, আর Lee ও Seung প্রমাণ করেন প্রতিটি ধাপে ত্রুটি বাড়ে না। নিচে একটি 2 × 2 উদাহরণে একটি পুরো ধাপ:

V ≈ W H, W: 2 × 1, H: 1 × 2‖V − WH‖² কমছে

            
  1. শুরু। V-কে একটিমাত্র অংশে (k = ১) ভাঙতে চাই। W আর H সব ১ দিয়ে শুরু; WH-এর সব ঘর ১, ত্রুটির বর্গ ৪ (শুধু কোণের ৩ − ১ = ২ থেকে)।
  2. H ধাপ। লব WᵀV = (৪, ২), হর WᵀWH = (২, ২)। ঘরে ঘরে ভাগ করে পুরোনো H দিয়ে গুণ: H = (২, ১)। কোনো বিয়োগ নেই, তাই চিহ্ন বদলানোর সুযোগও নেই।
  3. ত্রুটি কমেছে। নতুন WH-এর দুটি সারিই (২, ১); ত্রুটির বর্গ ৪ থেকে ২.
  4. W ধাপ। লব VHᵀ = (৭, ৩), হর WHHᵀ = (৫, ৫); W = (১.৪, ০.৬)। প্রথম সারির “ওজন” বেড়েছে, কারণ V-এর প্রথম সারি বড়।
  5. এক ধাপের পর। ত্রুটির বর্গ ০.৪০, আর এই V-এর সেরা rank-1 approximation-এর ত্রুটি (২ − √২)² ≈ ০.৩৪৩ (ছোট eigenvalue-এর বর্গ, Eckart–Young). আরও ধাপে ০.৩৪৩-এ পৌঁছায়; সব ঘর ধনাত্মকই থাকে।
NMF-এর তিনটি সতর্কতা

এক, সমস্যাটি convex নয়: W আর H একসঙ্গে বদলালে অনেক local minimum. শুরুর মান আর seed বদলালে অংশ বদলাতে পারে, তাই কয়েকটি seed-এ স্থিরতা দেখুন। Stephen Vavasis ২০০৯ সালে দেখান, NMF-এর হুবহু সংস্করণ NP-hard. দুই, উত্তর অনন্য নয়: WD আর D⁻¹H (D ধনাত্মক diagonal) একই গুণফল দেয়, তাই অংশের মাপ ইচ্ছামতো; তুলনার আগে H-এর সারিগুলো স্বাভাবিক করে নিন। তিন, ইনপুটে একটিও ঋণাত্মক মান থাকলে scikit-learn ValueError দেয়; তাই StandardScaler-এর পরে NMF চলে না।

৭ · বাজারঘরের কেনাকাটা ভেঙে প্রোফাইল

এই অংশের ডেটা কাল্পনিক, seed দিয়ে বানানো। বাজারঘরের ৮০ জন ক্রেতা, আটটি পণ্য-বিভাগ (চাল-ডাল, মাছ-মাংস, সবজি, মসলা, শিশুর পণ্য, প্রসাধনী, গ্যাজেট, বই), আর প্রতিটি ঘরে তিন মাসে সেই বিভাগে কতবার কেনা হয়েছে। ভেতরে তিনটি লুকানো অভ্যাস আছে, যা আমরা জানি কিন্তু algorithm জানে না। ব্লক ৩ আর ৪ এই module থেকে ডেটা নেয়।

# bazar_basket.py — বাজারঘরের কাল্পনিক ক্রেতা × পণ্য-বিভাগ কেনাকাটা (seed দিয়ে বানানো)
import numpy as np

CATS = ["rice_dal", "fish_meat", "vegetable", "spice", "baby", "cosmetic", "gadget", "book"]

def make_basket(n=80, seed=41):
    rng = np.random.default_rng(seed)
    H = np.array([[6, 4, 5, 3, 0, 0.5, 0, 0],      # সংসারের বাজার
                  [0.5, 0, 0.5, 0, 6, 3, 0, 0.5],  # নতুন বাবা-মা
                  [0, 0, 0, 0, 0, 1, 4, 5]])       # ছাত্র-ছাত্রী, গ্যাজেট
    W = rng.gamma(0.6, 1.0, size=(n, 3))           # প্রতিজন ক্রেতায় তিন অভ্যাসের মিশ্রণ
    V = rng.poisson(W @ H).astype(float)           # তিন মাসে প্রতিটি বিভাগে কেনার সংখ্যা
    return V, CATS

নিচের ব্লক multiplicative update হাতে লেখে (১,০০০ ধাপ, কয়েকটি ধাপে ত্রুটি ছাপে), তারপর scikit-learn-এর NMF-এর সঙ্গে ত্রুটি মেলায়। scikit-learn-এর reconstruction_err_ হলো ‖V − WH‖F, বর্গ নয়। init="nndsvda" একটি SVD-ভিত্তিক শুরু, আর ডিফল্ট solver "cd" (coordinate descent); max_iter যথেষ্ট রাখায় convergence-এর কোনো সতর্কবার্তা আসে না।

import numpy as np
from sklearn.decomposition import NMF
from bazar_basket import make_basket

V, cats = make_basket()                                   # ৮০ ক্রেতা × ৮ বিভাগ
print("V shape:", V.shape, " zeros:", f"{(V == 0).mean():.0%}")

def nmf_mu(V, k, iters=1000, seed=0, eps=1e-10):          # Lee–Seung multiplicative update
    rng = np.random.default_rng(seed)
    W = rng.random((V.shape[0], k)); H = rng.random((k, V.shape[1]))
    for it in range(iters):
        H *= (W.T @ V) / (W.T @ W @ H + eps)
        W *= (V @ H.T) / (W @ H @ H.T + eps)
        if it in (0, 9, 99, 999):
            print(f"iter {it + 1:4d}  ||V - WH||_F = {np.linalg.norm(V - W @ H):.4f}")
    return W, H

W, H = nmf_mu(V, k=3)
skl = NMF(n_components=3, init="nndsvda", solver="cd", max_iter=1000, random_state=0)
W2 = skl.fit_transform(V)
print(f"sklearn NMF (cd)          error = {skl.reconstruction_err_:.4f}, iters {skl.n_iter_}")
print("all entries of W and H >= 0:", bool((W >= 0).all() and (H >= 0).all()))

Hn = H / H.sum(axis=1, keepdims=True)                     # প্রতিটি অংশ একটি কেনাকাটার প্রোফাইল
for r, row in enumerate(Hn):
    top = np.argsort(row)[::-1][:3]
    print(f"part {r}: " + ", ".join(f"{cats[j]} {row[j]:.2f}" for j in top))
c = 7
print(f"\ncustomer {c}: buys", V[c].astype(int), "\n  weights W =", W[c].round(2))

আসল আউটপুট (ব্রাউজারের Python):

V shape: (80, 8)  zeros: 30%
iter    1  ||V - WH||_F = 72.8120
iter   10  ||V - WH||_F = 34.6105
iter  100  ||V - WH||_F = 32.4645
iter 1000  ||V - WH||_F = 32.4475
sklearn NMF (cd)          error = 32.4476, iters 33
all entries of W and H >= 0: True
part 0: book 0.49, gadget 0.36, cosmetic 0.12
part 1: baby 0.59, cosmetic 0.34, vegetable 0.04
part 2: rice_dal 0.33, vegetable 0.28, fish_meat 0.21

customer 7: buys [ 8  5  6  4  0  0 12 15]
  weights W = [2.33 0.   1.38]

মিল: হাতের ১,০০০ ধাপ শেষে ত্রুটি ৩২.৪৪৭৫, scikit-learn-এর coordinate descent ৩৩ ধাপে ৩২.৪৪৭৬: চতুর্থ দশমিকে পার্থক্য, দুটি ভিন্ন শুরু থেকে প্রায় একই সমাধানে পৌঁছেছে। ত্রুটি প্রথম ১০ ধাপেই ৭২.৮ থেকে ৩৪.৬-এ নামে, তারপর খুব ধীরে; multiplicative update-এর এটি পরিচিত চরিত্র, তাই বড় কাজে "cd" সাধারণত দ্রুত। অংশগুলো পড়ুন: H-এর প্রতিটি সারিকে তার যোগফল দিয়ে ভাগ করলে একটি প্রোফাইল। Part ০: বই আর গ্যাজেট (ছাত্র-গ্যাজেট); part ১: শিশুর পণ্য আর প্রসাধনী (নতুন বাবা-মা); part ২: চাল-ডাল, সবজি, মাছ-মাংস (সংসারের বাজার)। লুকানো তিনটি অভ্যাসই ফেরত এসেছে, নাম ছাড়া; নামগুলো আমরা দিয়েছি। ক্রেতা ৭ চাল-ডাল, মাছ-মাংস, সবজি আর গ্যাজেট-বই কেনেন, কিন্তু শিশুর পণ্য বা প্রসাধনী নয়; তাঁর W = (২.৩৩, ০, ১.৩৮): ছাত্র-গ্যাজেট আর সংসারের মিশ্রণ, শিশুর অংশ শূন্য। এমন পাঠযোগ্য ভাঙনই NMF-এর মূল লাভ: বিপণন দল প্রতিটি ক্রেতাকে তিনটি অঋণাত্মক সংখ্যায় বুঝতে পারে।

৮ · PCA, NMF আর factor analysis পাশাপাশি — আর কখন কোনটি

একই কেনাকাটার ডেটায় তিনটি পদ্ধতি চালিয়ে উপাদানগুলো পাশাপাশি দেখা যাক, আর k বাড়ালে পুনর্গঠনের ত্রুটি (reconstruction error) কীভাবে নামে। তিন রকম ত্রুটি তুলনা করা হয়েছে: SVD-এর সেরা rank-k approximation (গড় বাদ না দিয়ে), PCA (গড় বাদ দিয়ে k টি উপাদান, তারপর গড় ফেরত), আর NMF. Factor analysis চালানো হয়েছে varimax rotation দিয়ে, যা factor-গুলোকে এমনভাবে ঘোরায় যাতে প্রতিটি factor অল্প কয়েকটি feature-এ বড় ওজন পায়।

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import NMF, PCA, FactorAnalysis
from bazar_basket import make_basket

V, cats = make_basket()
err = lambda R: np.linalg.norm(V - R)                     # Frobenius ত্রুটি
s = np.linalg.svd(V, compute_uv=False)                    # singular value, বড় থেকে ছোট
print(" k  SVD(best rank k)  PCA(mean + k)  NMF")
for k in range(1, 7):
    p = PCA(n_components=k).fit(V)
    m = NMF(n_components=k, init="nndsvda", max_iter=2000, random_state=0)
    Wk = m.fit_transform(V)
    print(f"{k:2d}  {np.sqrt((s[k:] ** 2).sum()):14.2f}  {err(p.inverse_transform(p.transform(V))):13.2f}"
          f"  {err(Wk @ m.components_):6.2f}")

pca = PCA(n_components=3).fit(V)
nmf = NMF(n_components=3, init="nndsvda", max_iter=2000, random_state=0).fit(V)
fa = FactorAnalysis(n_components=3, rotation="varimax", random_state=0).fit(V)
print("\nnegative entries: PCA", (pca.components_ < 0).sum(), "of 24 | NMF",
      (nmf.components_ < 0).sum(), "| FA", (fa.components_ < 0).sum())
print("PCA explained variance ratio:", pca.explained_variance_ratio_.round(3))
print("FA noise variance per category:", fa.noise_variance_.round(2))

fig, ax = plt.subplots(1, 3, figsize=(11, 3.4))
for a, M, t in zip(ax, [pca.components_, nmf.components_, fa.components_],
                   ["PCA components", "NMF parts", "Factor analysis (varimax)"]):
    M = M / np.abs(M).max(axis=1, keepdims=True)          # প্রতিটি সারি নিজের সর্বোচ্চে ভাগ
    a.imshow(M, cmap="RdBu_r", vmin=-1, vmax=1, aspect="auto")
    a.set(title=t, yticks=range(3), xticks=range(8))
    a.set_xticklabels(cats, rotation=60, fontsize=8)
fig.subplots_adjust(wspace=0.3, bottom=0.32)

আসল আউটপুট (ব্রাউজারের Python), নিচে তিনটি heatmap (লাল ধনাত্মক, নীল ঋণাত্মক):

 k  SVD(best rank k)  PCA(mean + k)  NMF
 1           73.07          68.35   73.07
 2           52.80          52.70   53.02
 3           32.14          32.08   32.45
 4           26.60          26.59   27.00
 5           21.15          21.13   22.92
 6           15.97          15.96   17.28

negative entries: PCA 8 of 24 | NMF 0 | FA 9
PCA explained variance ratio: [0.428 0.232 0.214]
FA noise variance per category: [4.72 3.07 2.13 0.92 1.67 3.66 2.28 1.75]

ত্রুটির ছক: প্রতিটি k-তে SVD-এর কলামই NMF-এর চেয়ে কম বা সমান; Eckart–Young theorem অনুযায়ী rank-k-এর মধ্যে SVD-ই সেরা, তাই NMF-এর অঋণাত্মক শর্ত শুধু ত্রুটি বাড়াতে পারে। k = ১-এ দুটি হুবহু সমান (৭৩.০৭), কারণ অঋণাত্মক matrix-এর প্রথম singular vector-কে সবসময় অঋণাত্মক নেওয়া যায় (Perron–Frobenius). k = ৩-এ ফারাক মাত্র ৩২.১৪ বনাম ৩২.৪৫, কারণ ডেটা সত্যিই তিনটি অঋণাত্মক অংশে গড়া; k = ৫, ৬-এ ফারাক বাড়ে। PCA-র কলাম SVD-এর চেয়েও সামান্য কম, কারণ সে k টি উপাদানের সঙ্গে একটি গড় vector-ও রাখে (কার্যত আরেকটি অংশ), তাই তুলনাটি তার পক্ষে একটু হেলানো।

উপাদানগুলো: PCA-র ২৪টি ওজনের ৮টি ঋণাত্মক, আর ছবিতে তার তৃতীয় উপাদান “শিশু + প্রসাধনী − গ্যাজেট − বই” ধরনের বৈপরীত্য; গণিতে ঠিক, ক্রেতার প্রোফাইল হিসেবে পড়া কঠিন। প্রথম তিনটি উপাদান variance-এর ৮৭.৪% ব্যাখ্যা করে (০.৪২৮ + ০.২৩২ + ০.২১৪)। NMF-এ একটিও ঋণাত্মক নেই। Varimax-ঘোরানো factor analysis-ও তিনটি পরিষ্কার দল দেখায়, তবে ছোট ঋণাত্মক ওজনসহ (৯টি); আর সে প্রতিটি বিভাগের নিজস্ব noise variance দেয়: চাল-ডালে ৪.৭২, মসলায় ০.৯২, অর্থাৎ চাল-ডালের কেনাকাটার একটি বড় অংশ তিনটি factor দিয়ে ব্যাখ্যা হয় না।

কোন প্রশ্নে কোন পদ্ধতি

পদ্ধতিকী খোঁজেমূল শর্তভালো কাজেসাবধানতা
PCA / truncated SVDসবচেয়ে বেশি variance-এর লম্ব দিকরৈখিক; শুধু covarianceসংকোচন, noise কমানো, model-এর আগে feature কমানোscaling-এর ওপর নির্ভর; উপাদানে ঋণাত্মক মিশ্রণ
Probabilistic PCA / factor analysisগোপন factor + প্রতি feature-এর noiseGaussian মডেলlikelihood দরকার, প্রশ্নপত্র বা জরিপের factorrotation ইচ্ছামতো; factor-এর নাম মানুষ দেয়
Kernel PCAφ-এর জগতে varianceবৈধ kernel আর γবাঁকা গঠন, ছোট-মাঝারি nn × n খরচ; γ বাছাই; pre-image আনুমানিক
ICAপরিসংখ্যানে স্বাধীন উৎসx = As, non-Gaussian উৎসসংকেত আলাদা করা: EEG, সেন্সর, অডিও (সরল মিশ্রণ)চিহ্ন, মাপ, ক্রম অজানা; Gaussian উৎসে অচল
NMFঅঋণাত্মক অংশ, শুধু যোগV ≥ ০গণনা, কেনাকাটা, লেখার শব্দ-গণনা (পর্ব ৪৪), বর্ণালিconvex নয়, seed-এ বদলায়; ত্রুটি SVD-এর চেয়ে বেশি

শুধু ছবি আঁকার জন্য, যেখানে কাছের প্রতিবেশী ঠিক রাখাই লক্ষ্য, পর্ব ৪২ (t-SNE আর UMAP — manifold learning আর তার ফাঁদ) দেখবেন। আর মনে রাখবেন, NMF দিয়ে লেখার বিষয় খোঁজা পর্ব ৪৪ (Topic modeling — LDA আর NMF দিয়ে বাংলা রিভিউয়ের বিষয় খোঁজা)-এর বিষয়।

আরও গভীরে: NMF-এর ত্রুটি Frobenius ছাড়া KL divergence-ও হতে পারে (scikit-learn-এ beta_loss="kullback-leibler", তখন solver "mu"), যা গণনার ডেটায় Poisson মডেলের maximum likelihood-এর সমান। David Donoho আর Victoria Stodden ২০০৩ সালে (NIPS) দেখান কোন শর্তে NMF-এর অংশ অনন্য হয়। Kernel PCA, spectral clustering আর Laplacian eigenmap একই পরিবারের: সবাই একটি similarity matrix-এর eigenvector নেয়। আর Tipping–Bishop মডেলে EM algorithm চালালে PCA মিসিং মানসহ শেখা যায়, যার যন্ত্রপাতি পর্ব ৩৯ (Gaussian mixture আর EM algorithm — নরম clustering)-এ।

পরীক্ষা ও ইন্টারভিউতে:

  • Kernel PCA-তে Gram matrix কীভাবে কেন্দ্রে আনেন? K̃ = K − 1nK − K1n + 1nK1n; ঘরে ঘরে: Kij − সারির গড় − কলামের গড় + সব ঘরের গড়।
  • PCA আর ICA-র পার্থক্য? PCA লম্ব, uncorrelated, variance-ক্রমে সাজানো উপাদান দেয়; ICA স্বাধীন উপাদান দেয়, লম্ব হওয়া জরুরি নয়, ক্রম বা মাপ নেই।
  • ICA কেন Gaussian উৎসে কাজ করে না? Whitening-এর পর বাকি ঘূর্ণন; গোল Gaussian সব ঘূর্ণনে একই দেখায়, তাই ঘূর্ণন নির্ধারণ করা যায় না।
  • V = [[৩, ১], [১, ১]], W = (১, ১)ᵀ, H = (১, ১) থেকে NMF-এর এক H-ধাপ? H ← H ∘ (WᵀV) ⊘ (WᵀWH) = (৪/২, ২/২) = (২, ১)।
  • একই k-তে NMF আর SVD, কার ত্রুটি কম? SVD-এর (Eckart–Young); NMF বাড়তি শর্তের বিনিময়ে পাঠযোগ্যতা দেয়।

৯ · AI-এর যুগে — dimensionality reduction-এর কোড যাচাই

AI assistant-কে “এই ডেটায় ICA বা NMF চালাও” বললে সে সাধারণত চলনসই কোড দেয়, কিন্তু ভুল থাকে পুরোনো API, উপাদানের অর্থ আর মাপার পদ্ধতিতে। পরিচিত ভুলগুলো:

AI প্রায়ই যা করেকেন ভুলকীভাবে ধরবেন
FastICA(whiten=True) বা NMF(alpha=0.1) লেখে1.8.0-এ প্রথমটি InvalidParameterError, দ্বিতীয়টি TypeError; এখন alpha_W, alpha_Hনিজের version-এর docs আর একবার চালিয়ে দেখা
StandardScaler-এর পরে NMF চালায়scaling ঋণাত্মক মান আনে; NMF ValueError দেয়, বা AI ঋণাত্মক মান কেটে দেয়NMF-এর আগে ডেটা ≥ ০ কি না দেখুন
ICA-র উপাদান ১, ২, ৩-কে নির্দিষ্ট উৎস বলে নাম দেয়ক্রম, চিহ্ন আর মাপ নির্ধারিত নয়; seed বদলালে বদলায়জানা সংকেত বা বিশেষজ্ঞের সঙ্গে correlation দিয়ে মেলান
পুরো ডেটায় PCA/NMF fit করে তারপর train-test ভাগtest-এর তথ্য উপাদানে ঢোকে (leakage)Pipeline-এর ভেতরে, শুধু train-এ fit
“NMF-এর অংশগুলো আসল গ্রাহক-শ্রেণি” বলে দাবি করেঅংশ seed আর k-এ বদলায়; নাম মানুষের ব্যাখ্যাকয়েকটি seed আর k-এ স্থিরতা দেখুন
Kernel PCA-র γ ডিফল্ট রেখে “কাজ করেনি” বলেডিফল্ট γ = ১/feature সংখ্যা, ডেটার মাপের সঙ্গে না-ও মিলতে পারেlog-মাপে কয়েকটি γ, পরের কাজের CV দিয়ে বাছাই

AI-কে কোডের সঙ্গে কারণ আর যাচাইও চাইতে বলুন:

আমার কাছে বাজারঘরের ৮০ জন ক্রেতার ৮টি পণ্য-বিভাগে কেনার সংখ্যার একটি matrix আছে (সব মান অঋণাত্মক, scikit-learn 1.8.0). আমি কেনাকাটার কয়েকটি প্রোফাইল খুঁজতে চাই। ১. NMF আর PCA দুটিই চালাও, k = 1 থেকে 6; প্রতিটি k-তে ‖V − WH‖ আর PCA-র পুনর্গঠনের ত্রুটি পাশাপাশি দাও। ২. NMF-এ init আর random_state স্পষ্ট লেখো, max_iter এমন রাখো যাতে ConvergenceWarning না আসে; warning এলে চাপা দিও না, বলো। ৩. H-এর প্রতিটি সারি যোগফলে ভাগ করে প্রতিটি অংশের প্রধান ৩টি বিভাগ দেখাও। ৪. ৫টি ভিন্ন random_state-এ অংশগুলো কতটা একই থাকে, cosine similarity দিয়ে মাপো। ৫. যেসব argument ব্যবহার করছ, সেগুলো 1.8.0-এর docs-এ আছে কি না কীভাবে মিলাব বলো। ৬. অংশগুলোর নাম তুমি দিও না; শুধু সংখ্যা দাও, নাম আমি দেব।

AI-এর দেওয়া dimensionality reduction কোড হাতে পেলে — চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১Lee আর Seung-এর ১৯৯৯ সালের NMF প্রবন্ধ কেন এত প্রভাবশালী হলো, আর NMF কি সবসময় “অংশ” শেখে?

১৯৯৯ সালে Nature-এ প্রকাশিত প্রবন্ধে Daniel Lee আর Sebastian Seung একই মুখের ছবির ডেটায় PCA, vector quantization আর NMF পাশাপাশি দেখান। PCA-র উপাদান ছিল পুরো মুখের ভূতুড়ে ছবি (“eigenface”), যাদের ধনাত্মক-ঋণাত্মক যোগ-বিয়োগে মুখ গড়ে ওঠে। NMF-এর উপাদান ছিল স্থানীয়: কোনোটি চোখের এলাকা, কোনোটি নাক বা ঠোঁট। লেখার ডেটায় একইভাবে অংশগুলো অর্থবহ শব্দগুচ্ছ হলো। ছবিটি এত স্পষ্ট ছিল যে “parts-based representation” কথাটি জনপ্রিয় হয়ে যায়, আর মস্তিষ্ক অংশ দিয়ে বস্তু চেনে কি না, সেই আলোচনার সঙ্গেও এটি জুড়ে যায়।

তবে ধারণাটি নতুন ছিল না। Paatero আর Tapper ১৯৯৪ সালে বায়ুদূষণের উৎস খুঁজতে “positive matrix factorization” ব্যবহার করেছিলেন, আর রসায়নে বর্ণালি ভাঙার কাজে আগেই এমন শর্ত ছিল। Lee ও Seung-এর বড় অবদান ছিল সহজ multiplicative update আর তার পরের প্রবন্ধে প্রমাণ যে ত্রুটি কখনো বাড়ে না, যা যে কেউ কয়েক লাইনে লিখতে পারে।

NMF কি সবসময় অংশ শেখে? না। Donoho আর Stodden ২০০৩ সালে দেখান, অংশ অনন্য আর “সঠিক” হয় কিছু বিশেষ শর্তে, যেমন প্রতিটি অংশের এমন কিছু feature থাকা যা অন্য অংশে নেই। অনেক ডেটায় NMF ঘন, ওভারল্যাপ করা উপাদান দেয়; আরও sparse অংশ চাইলে L1 শাস্তি (scikit-learn-এ alpha_W, l1_ratio) যোগ করতে হয়। তার ওপর সমস্যাটি convex নয়, seed বদলালে অংশ বদলায়, আর k বাছাইও বিচারের বিষয়। বাজারঘরের উদাহরণে অংশ পরিষ্কার এসেছে কারণ ডেটা আমরাই তিনটি অঋণাত্মক অংশ দিয়ে বানিয়েছি; বাস্তবে এত পরিষ্কার পাওয়ার নিশ্চয়তা নেই।

মূল উপলব্ধি: NMF-এর অঋণাত্মক শর্ত প্রায়ই পাঠযোগ্য অংশ দেয়, কিন্তু তা নিশ্চিত নয়; স্থিরতা আর ডেটার গঠন যাচাই না করে অংশকে বাস্তব শ্রেণি বলা যায় না।

প্র ০২ICA “cocktail party problem”-এর সমাধান হিসেবে পরিচিত। তাহলে একটি সত্যিকারের ঘরে দুটি ফোনে রেকর্ড করা আড্ডা থেকে কেন সহজে আলাদা গলা পাওয়া যায় না?

ICA-র মূল মডেল x = As তাৎক্ষণিক রৈখিক মিশ্রণ ধরে: প্রতিটি মুহূর্তে প্রতিটি মাইক্রোফোন উৎসগুলোর একটি স্থির ওজনের যোগফল শোনে। ব্লক ২-এর কৃত্রিম ডেটা ঠিক এভাবে বানানো, তাই FastICA প্রায় নিখুঁত ফল দিয়েছে। সত্যিকারের ঘরে তিনটি জিনিস এই মডেল ভাঙে।

প্রথম, শব্দ দুই মাইক্রোফোনে ভিন্ন সময়ে পৌঁছায়, আর দেয়াল, ছাদ, আসবাব থেকে প্রতিফলিত হয়ে অনেকগুলো দেরিতে আসা প্রতিধ্বনি যোগ হয়। তাই মিশ্রণটি একটি matrix গুণ নয়, একটি convolution (সময়ের সঙ্গে ছড়ানো ফিল্টার)। গবেষকরা একে সামলাতে frequency domain-এ প্রতিটি frequency-তে আলাদা ICA চালান, কিন্তু তখন প্রতিটি frequency-তে ক্রম আর মাপের অস্পষ্টতা আলাদা করে মেলাতে হয়, যা নিজেই কঠিন সমস্যা। দ্বিতীয়, প্রায়ই মাইক্রোফোনের চেয়ে উৎস বেশি: চায়ের দোকানের আড্ডায় পাঁচজন কথা বলছেন, পাশে রেডিও, রাস্তায় রিকশার বেল। দুটি সমীকরণে সাতটি অজানা সাধারণভাবে উল্টানো যায় না। তৃতীয়, উৎস সরে যায়, তাই A সময়ের সঙ্গে বদলায়।

ইতিহাসের দিক থেকে, Hérault ও Jutten-এর ১৯৮০-এর দশকের কাজ, Comon-এর ১৯৯৪ সালের গাণিতিক রূপ আর Bell ও Sejnowski-এর ১৯৯৫ সালের infomax মিলে ICA-কে প্রতিষ্ঠিত করে, আর মস্তিষ্কের সংকেতে, যেখানে মিশ্রণ প্রায় তাৎক্ষণিক, সে খুব সফল হয়। আধুনিক বক্তা-বিভাজনের অনেক ব্যবস্থা এখন বড় ডেটায় শেখানো neural network ব্যবহার করে, যা প্রতিধ্বনি আর একক মাইক্রোফোনও সামলাতে চেষ্টা করে; তবে তারাও শেখানোর ডেটার বাইরের পরিবেশে ভুল করতে পারে।

মূল উপলব্ধি: ICA-র সাফল্য তার মডেলের সঙ্গে বাস্তবের মিলের ওপর নির্ভর করে; তাৎক্ষণিক রৈখিক মিশ্রণে সে চমৎকার, প্রতিধ্বনি আর বেশি উৎসের ঘরে সরল ICA যথেষ্ট নয়।

প্র ০৩Kernel PCA ১৯৯৮ সালেই এসেছিল। তবু কেন ডেটা আঁকার কাজে পরে t-SNE আর UMAP বেশি জনপ্রিয় হলো, আর kernel PCA-র জায়গা এখন কোথায়?

Schölkopf, Smola আর Müller-এর প্রবন্ধ দেখিয়েছিল যে PCA-কে kernel দিয়ে অরৈখিক করা যায়, আর সমাধান তখনো একটি eigen-decomposition: কোনো local minimum নেই, শুরুর মান নিয়ে দুশ্চিন্তা নেই। এটি বড় আকর্ষণ ছিল। কিন্তু ব্যবহারে কয়েকটি বাধা স্পষ্ট হয়।

প্রথম, kernel আর γ বাছাই। লেবেল ছাড়া কোন γ “ঠিক”, তা বলার সরাসরি মাপ নেই; ব্লক ১-এ γ = ২ ভালো কাজ করেছে, কারণ আমরা জানতাম কী দেখতে চাই। দ্বিতীয়, খরচ: n × n Gram matrix আর তার eigen-decomposition, যা কয়েক লাখ বিন্দুতে অসম্ভব হয়ে ওঠে (পর্ব ৩১-এর Nyström আংশিক সমাধান)। তৃতীয়, kernel PCA বিশ্বব্যাপী variance ধরে রাখে; অনেক ডেটায় সবচেয়ে বড় variance-এর দিক আঁকার জন্য সবচেয়ে তথ্যপূর্ণ নয়।

Laurens van der Maaten আর Geoffrey Hinton ২০০৮ সালে t-SNE প্রকাশ করেন, যা স্থানীয় প্রতিবেশ ধরে রাখায় মনোযোগ দেয়, আর ২০১৮ সালে Leland McInnes ও সহকর্মীদের UMAP একই ধরনের লক্ষ্যে দ্রুততর হয়। ছবিতে দল আলাদা দেখাতে এরা প্রায়ই বেশি চোখে পড়ার মতো ফল দেয়, তবে তাদের নিজস্ব ফাঁদ আছে: দলের আকার আর দলের মধ্যে দূরত্ব প্রায়ই অর্থহীন, যা পর্ব ৪২-এর বিষয়।

Kernel PCA হারিয়ে যায়নি। ছোট-মাঝারি ডেটায় অরৈখিক feature বানাতে, noise কমাতে (Mika ও সহকর্মীদের ১৯৯৯ সালের কাজ), আর তত্ত্বে এটি একটি গুরুত্বপূর্ণ সেতু: Isomap আর Laplacian eigenmap-এর মতো অনেক manifold পদ্ধতিকে একটি বিশেষ kernel-এর kernel PCA হিসেবে দেখা যায়, যা Jihun Ham ও সহকর্মীরা ২০০৪ সালে দেখান। তাছাড়া এর একটি fit-করা মডেল আছে যা নতুন বিন্দুতে transform করতে পারে, যা t-SNE-এর মূল রূপে নেই।

মূল উপলব্ধি: kernel PCA convex আর নতুন বিন্দুতে চলে, কিন্তু γ বাছাই আর n × n খরচ তাকে সীমিত করে; আঁকার জন্য স্থানীয়-প্রতিবেশ পদ্ধতি জনপ্রিয়, আর তত্ত্বে kernel PCA এখনো একীভূত করার ভাষা।

প্র ০৪Factor analysis-এর ইতিহাস মনোবিজ্ঞানে বিতর্কিত। factor “আবিষ্কার” আর factor-এর নাম দেওয়ার মধ্যে কী ফারাক, আর ML-এ এর শিক্ষা কী?

Charles Spearman ১৯০৪ সালে শিশুদের বিভিন্ন বিষয়ের নম্বরের মধ্যে ধনাত্মক correlation দেখে প্রস্তাব করেন, এর পেছনে একটি সাধারণ মানসিক ক্ষমতা “g” আছে। এই চিন্তা থেকে factor analysis গড়ে ওঠে: অনেক মাপা চলক কয়েকটি গোপন factor আর প্রতিটির নিজস্ব noise-এর যোগফল। পরে Louis Thurstone ১৯৩০-এর দশকে একাধিক factor আর rotation-এর ধারণা আনেন, আর ১৯৫৮ সালে Henry Kaiser varimax প্রস্তাব করেন, যা factor-গুলোকে এমনভাবে ঘোরায় যাতে প্রতিটি অল্প কয়েকটি চলকে বড় ওজন পায়।

বিতর্কের মূল একটি গাণিতিক সত্য: factor model-এ W-কে যেকোনো orthogonal matrix দিয়ে ঘোরালে x-এর covariance WWᵀ + Ψ একই থাকে। অর্থাৎ ডেটা factor-এর জগৎ ঠিক করে, কিন্তু অক্ষগুলোর দিক নয়। varimax একটি সুবিধাজনক দিক বাছে, “সত্য” দিক নয়। তারপর মানুষ বড় ওজনের চলক দেখে factor-এর নাম দেয়। Spearman-এর g বনাম Thurstone-এর একাধিক প্রাথমিক ক্ষমতার বিতর্ক অনেকটা এই স্বাধীনতা থেকেই এসেছিল: একই ডেটা ভিন্ন rotation-এ ভিন্ন গল্প বলতে পারে।

ML-এ শিক্ষাটি সরাসরি। ব্লক ৪-এ varimax factor analysis আর NMF দুটিই তিনটি পরিষ্কার দল দেখিয়েছে, আর আমরা সেগুলোর নাম দিয়েছি “সংসার”, “নতুন বাবা-মা”, “ছাত্র-গ্যাজেট”. নামগুলো আমাদের ব্যাখ্যা, algorithm-এর আবিষ্কার নয়; এখানে আমরা নিশ্চিত, কারণ ডেটা আমরাই বানিয়েছি। বাস্তবে একটি বিপণন দল যদি একটি NMF অংশকে “দামের প্রতি সংবেদনশীল ক্রেতা” নাম দিয়ে নীতি ঠিক করে, তবে সেই নাম যাচাই করতে আলাদা প্রমাণ লাগে: ভিন্ন সময়ের ডেটায় একই অংশ আসে কি না, seed বদলালে টেকে কি না, আর একটি ছোট পরীক্ষায় সেই অংশের ক্রেতারা সত্যিই দামে বেশি সাড়া দেন কি না।

মূল উপলব্ধি: latent factor-এর জগৎ ডেটা থেকে আসে, কিন্তু তার অক্ষ আর নাম অনেকটা আমাদের পছন্দ; নামকে আবিষ্কার মনে করার আগে স্থিরতা আর বাইরের প্রমাণ চাই।

প্র ০৫একই k-তে PCA বা SVD-এর পুনর্গঠনের ত্রুটি সবসময় কম। তাহলে কোনো দল কেন জেনেশুনে বেশি ত্রুটির NMF বা ICA বাছবে?

Carl Eckart আর Gale Young ১৯৩৬ সালে প্রমাণ করেন, rank-k-এর সব matrix-এর মধ্যে truncated SVD Frobenius ত্রুটিতে সেরা। ব্লক ৪-এর ছকে তাই প্রতিটি k-তে SVD-এর কলাম NMF-এর চেয়ে কম বা সমান। প্রশ্ন যদি শুধু “৮টি সংখ্যাকে ৩টিতে কত কম ক্ষতিতে চাপা যায়” হয়, উত্তর SVD বা PCA.

কিন্তু dimensionality reduction-এর উদ্দেশ্য প্রায়ই সংকোচন নয়। বাজারঘরের বিপণন দল জানতে চায় কোন ক্রেতার জন্য কোন অফার: তার কাছে “০.৪ × চাল − ০.৩ × বই” ধরনের PCA উপাদান অর্থহীন, কিন্তু “এই ক্রেতা ৭০% সংসারী, ৩০% ছাত্র” কাজের তথ্য। এখানে ৩২.১৪ বনাম ৩২.৪৫, এক শতাংশেরও কম বাড়তি ত্রুটি, পাঠযোগ্যতার জন্য খুব কম দাম। ICA-র ক্ষেত্রে লক্ষ্য একেবারে আলাদা: EEG-তে চোখের পলকের সংকেত আলাদা করে বাদ দেওয়া। PCA-র সবচেয়ে বড় উপাদান সেটি নয়, বরং মস্তিষ্ক আর চোখের সংকেতের একটি মিশ্রণ হতে পারে, যা বাদ দিলে দরকারি সংকেতও যায়।

অন্য একটি কারণ পরের কাজ। NMF-এর W (প্রতিটি ক্রেতার অংশের ওজন) একটি classifier-এর feature হিসেবে ব্যবহার করা যায়, আর অঋণাত্মক ও sparse হওয়ায় অনেক সময় সহজে ব্যাখ্যা করা যায়। তবে পরের কাজে কোনটি ভালো, তা মাপতে হয় সেই কাজেই, cross-validation দিয়ে, পুনর্গঠনের ত্রুটি দিয়ে নয়। আর k-এর বাছাইও একই নীতিতে: “ত্রুটির কনুই” দেখে নয়, পরের কাজের ফল আর ব্যাখ্যার স্থিরতা দেখে।

উল্টো দিকে সাবধানতা: পাঠযোগ্যতার দাবি নিজেই একটি ঝুঁকি। একটি NMF অংশ পাঠযোগ্য দেখালেও তা seed-নির্ভর হতে পারে (প্র ০১), তাই সংকোচনের সেরা পদ্ধতি ছেড়ে ব্যাখ্যার পদ্ধতি নিলে ব্যাখ্যার স্থিরতা যাচাইয়ের দায়ও নিতে হয়।

মূল উপলব্ধি: পুনর্গঠনের ত্রুটি একটি প্রশ্নের মাপ; উদ্দেশ্য ব্যাখ্যা, উৎস আলাদা করা বা পরের কাজ হলে বেশি ত্রুটির পদ্ধতিই ঠিক হতে পারে, যদি তার ফল স্থির হয়।

অনুশীলন

হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। চাইলে অংশ ৩, ৫ আর ৭-এর ব্লক বদলে যাচাই করুন।

  1. পূর্ণ numerical (kernel PCA আর NMF): (ক) তিনটি বিন্দুর Gram matrix K = [[২, ১, ০], [১, ২, ১], [০, ১, ২]]. double centring করে K̃ বের করুন আর দেখান প্রতিটি সারির যোগফল শূন্য। (খ) V = [[২, ২], [২, ০]], W = (১, ১)ᵀ, H = (১, ১) থেকে NMF-এর একটি H-ধাপ আর একটি W-ধাপ করুন, আর আগে-পরে ‖V − WH‖² লিখুন।

    (ক) সারির গড় = কলামের গড় = (১, ৪/৩, ১); সব ঘরের গড় g = ১০/৯. K̃ij = Kij − ri − rj + ১০/৯. যেমন K̃11 = ২ − ১ − ১ + ১০/৯ = ১০/৯ ≈ ১.১১১; K̃12 = ১ − ১ − ৪/৩ + ১০/৯ = −২/৯ ≈ −০.২২২; K̃13 = ০ − ১ − ১ + ১০/৯ = −৮/৯ ≈ −০.৮৮৯; K̃22 = ২ − ৮/৩ + ১০/৯ = ৪/৯ ≈ ০.৪৪৪. পুরো matrix:

     1.111  -0.222  -0.889
    -0.222   0.444  -0.222
    -0.889  -0.222   1.111

    সারির যোগফল: ১০/৯ − ২/৯ − ৮/৯ = ০; −২/৯ + ৪/৯ − ২/৯ = ০; তৃতীয়টিও ০ ✓.

    (খ) শুরুতে WH-এর সব ঘর ১; ত্রুটির বর্গ = ১ + ১ + ১ + ১ = ৪. H-ধাপ: WᵀV = (৪, ২), WᵀWH = (২, ২), H = (২, ১). W-ধাপ: VHᵀ = (২·২ + ২·১, ২·২ + ০·১) = (৬, ৪), HHᵀ = ৫, W = (৬/৫, ৪/৫) = (১.২, ০.৮). WH = [[২.৪, ১.২], [১.৬, ০.৮]]; পার্থক্য [[−০.৪, ০.৮], [০.৪, −০.৮]]; ত্রুটির বর্গ = ০.১৬ + ০.৬৪ + ০.১৬ + ০.৬৪ = ১.৬০.

  2. সমালোচনা ও নকশা: একজন বিশ্লেষক লিখেছেন: “বাজারঘরের ৫,০০০ ক্রেতার ১২টি বিভাগের খরচ (টাকায়) StandardScaler দিয়ে scale করে NMF চালিয়েছি; ঋণাত্মক মানগুলো শূন্য করে দিয়েছি। k = ৪ নিয়েছি কারণ ত্রুটির ছবিতে ওখানে বাঁক। একটি seed-এ চারটি অংশের নাম দিয়ে প্রতিটি ক্রেতাকে তাঁর সবচেয়ে বড় অংশের ‘segment’-এ রেখেছি; পরের মাসের প্রচার এই segment দিয়েই হবে।” কী কী সমস্যা, আর ভালো নকশা কী?

    সমস্যা: (১) StandardScaler গড় বাদ দেয়, তাই প্রায় অর্ধেক মান ঋণাত্মক; সেগুলো শূন্য করা ডেটা বিকৃত করে, আর NMF-এর “শুধু যোগ” অর্থ হারায়। (২) টাকার অঙ্কে বড় খরচের বিভাগ (যেমন গ্যাজেট) ত্রুটিতে আধিপত্য করে; অঋণাত্মক রেখে মাপ মেলানো দরকার, যেমন প্রতিটি কলামকে তার std বা সর্বোচ্চ দিয়ে ভাগ, বা log(১ + x). (৩) একটি seed: অংশ seed-নির্ভর, স্থিরতা দেখা হয়নি। (৪) k শুধু ত্রুটির বাঁক দেখে; ব্যবহারিক লক্ষ্য (প্রচারে সাড়া) দিয়ে যাচাই নেই। (৫) প্রতিটি ক্রেতাকে একটি segment-এ ফেলা NMF-এর মিশ্রণের তথ্য হারায়; ৫১% আর ৪৯% দুই অংশের ক্রেতা আলাদা।

    ভালো নকশা: অঋণাত্মক রূপান্তর (বিভাগভিত্তিক মাপ বা log), কয়েকটি k আর ৫–১০টি seed-এ NMF, অংশের cosine similarity দিয়ে স্থিরতা মাপা; H-এর সারি স্বাভাবিক করে বিশেষজ্ঞের সঙ্গে বসে নাম; W-এর মিশ্রণ রেখে দেওয়া; আর প্রচারের আগে একটি ছোট নিয়ন্ত্রিত পরীক্ষায় segment-ভিত্তিক অফার আসলে সাহায্য করে কি না মাপা।

  3. আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড কী ছাপবে।
    import numpy as np
    K = np.array([[2., 1.], [1., 2.]])
    J = np.full((2, 2), 0.5)
    Kc = K - J @ K - K @ J + J @ K @ J
    print(Kc)
    print(np.linalg.eigvalsh(Kc))
    [[ 0.5 -0.5]
     [-0.5  0.5]]
    [0. 1.]

    J @ K-এর প্রতিটি ঘর হলো K-এর কলামের গড় ১.৫; K @ J-ও সব ১.৫; J @ K @ J সব ঘরের গড় ১.৫. তাই K̃ = K − ১.৫ − ১.৫ + ১.৫ = K − ১.৫. দুটি বিন্দুর ক্ষেত্রে কেন্দ্রে আনার পর একটিই মাত্রা থাকে, তাই একটি eigenvalue শূন্য, অন্যটি trace = ০.৫ + ০.৫ = ১. ডেস্কটপে NumPy 2.5 আর Python 3.14-এ এভাবেই ছাপে (শূন্যটি কখনো খুব ছোট সংখ্যা হয়ে আসতে পারে)।

  4. ICA-র হিসাব: দুটি স্বাধীন উৎস, প্রতিটি গড় ০ আর variance ১-এ মাপা Laplace বণ্টনের (excess kurtosis +৩)। (ক) y = (s₁ + s₂)/√২-এর excess kurtosis কত? (খ) y = cos ৩০° · s₁ + sin ৩০° · s₂ হলে? (গ) উৎস দুটি Gaussian হলে একই প্রশ্নের উত্তর কী, আর ICA-র জন্য তার অর্থ কী?

    স্বাধীন উৎসে κ(a s₁ + b s₂) = a⁴κ₁ + b⁴κ₂, যখন a² + b² = ১. (ক) a = b = ১/√২: a⁴ + b⁴ = ১/৪ + ১/৪ = ০.৫; κ = ৩ × ০.৫ = ১.৫. (খ) cos²৩০° = ০.৭৫, sin²৩০° = ০.২৫; cos⁴ + sin⁴ = ০.৫৬২৫ + ০.০৬২৫ = ০.৬২৫; κ = ১.৮৭৫. (গ) Gaussian-এ κ = ০, তাই সব কোণে ০: কোনো দিক অন্যটির চেয়ে বেশি non-Gaussian নয়, তাই ICA ঘূর্ণন ঠিক করতে পারে না। মিশ্রণ যত সমান, κ তত শূন্যের কাছে, তাই ICA সবচেয়ে বড় |κ|-এর দিক খোঁজে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমহাতে kernel PCA করার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Kernel আর তার প্যারামিটার (যেমন RBF-এর γ) বাছাই করা
  2. সব জোড়ার kernel মান দিয়ে n × n Gram matrix K বানানো
  3. K − 1ₙK − K1ₙ + 1ₙK1ₙ দিয়ে K-কে কেন্দ্রে আনা
  4. কেন্দ্রে আনা K-এর eigenvalue আর eigenvector বের করে বড় থেকে ছোট সাজানো
  5. প্রথম k টি eigenvector-কে √λ দিয়ে গুণ করে projection পাওয়া

Kernel PCA-তে Gram matrix কেন কেন্দ্রে আনতে হয়?

  • যাতে eigenvalue বড় থেকে ছোট ক্রমে আসে
  • φ-এর জগতে ডেটার গড় শূন্য করার জন্য, যা φ না বানিয়ে সরাসরি করা যায় না
  • যাতে RBF kernel বৈধ (PSD) হয়
  • Gram matrix-এর আকার ছোট করার জন্য

তিনটি মিশ্রিত সংকেতে FastICA চালানো হলো। একটি পাওয়া উপাদানের সঙ্গে আসল sine তরঙ্গের correlation −০.৯৯৮. এর সবচেয়ে সঠিক ব্যাখ্যা কী?

  • ICA ব্যর্থ হয়েছে, কারণ correlation ঋণাত্মক
  • উপাদানটি অন্য কোনো উৎস
  • max_iter বাড়ালে correlation +০.৯৯৮ হয়ে যাবে
  • উৎস ঠিকই পাওয়া গেছে, শুধু চিহ্ন উল্টো, যা ICA-তে অনির্ধারিত

দুটি স্বাধীন Gaussian উৎস একটি অজানা matrix দিয়ে মিশিয়ে দুটি সংকেত পাওয়া গেল। ICA দিয়ে উৎস দুটি ফেরত আনা যাবে কি?

  • না, whitening-এর পর ঘূর্ণনটি নির্ধারণ করা যায় না
  • হ্যাঁ, kurtosis সর্বোচ্চ করে
  • হ্যাঁ, তবে শুধু PCA দিয়ে
  • হ্যাঁ, যদি যথেষ্ট বেশি নমুনা থাকে

V = [[৩, ১], [১, ১]], W = (১, ১)ᵀ, H = (১, ১). NMF-এর multiplicative নিয়ম H ← H ∘ (WᵀV) ⊘ (WᵀWH) দিয়ে একটি ধাপের পর H কত?

  • (৪, ২)
  • (৩, ১)
  • (২, ১)
  • (১, ১), কোনো বদল নেই

একটি অঋণাত্মক ডেটা matrix-এ k = ৩ নিয়ে truncated SVD আর NMF চালানো হলো। Frobenius পুনর্গঠনের ত্রুটি সম্পর্কে কোনটি সবসময় সত্য?

  • NMF-এর ত্রুটি সবসময় কম, কারণ অংশগুলো অর্থবহ
  • SVD-এর ত্রুটি NMF-এর চেয়ে কম বা সমান
  • দুটি সবসময় সমান
  • কোনটি কম তা seed-এর ওপর নির্ভর করে

Factor analysis আর probabilistic PCA-র মডেল x = Wz + μ + ε-এ মূল পার্থক্য কোথায়?

  • Factor analysis-এ z Gaussian নয়
  • Factor analysis অরৈখিক
  • Probabilistic PCA-তে W অবশ্যই orthogonal
  • Noise-এর covariance: PPCA-তে σ²I, factor analysis-এ diagonal Ψ

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. PCA-র সীমা: রৈখিক, শুধু covariance, লম্ব অক্ষ আর ঋণাত্মক ওজন। Probabilistic PCA = x = Wz + μ + ε, noise σ²I; diagonal Ψ হলে factor analysis.
  2. Kernel PCA: K̃ = K − 1nK − K1n + 1nK1n, তারপর eigenvector; projection = √λ · α, চিহ্ন ইচ্ছামতো। সমকেন্দ্রিক বৃত্তে ০.৬৯০ থেকে ১.০০০; খরচ n × n আর γ বাছাই কঠিন।
  3. ICA: x = As, স্বাধীন আর non-Gaussian উৎস; whitening-এর পর ঘূর্ণন খোঁজা, যেদিকে kurtosis বা negentropy সবচেয়ে বেশি। চিহ্ন, মাপ, ক্রম অজানা; Gaussian উৎসে অচল।
  4. NMF: V ≈ WH, সব ≥ ০; H ← H ∘ (WᵀV) ⊘ (WᵀWH), W ← W ∘ (VHᵀ) ⊘ (WHHᵀ)। অংশ পাঠযোগ্য, কিন্তু convex নয়, seed-নির্ভর।
  5. বাছাই: একই k-তে SVD-এর ত্রুটি সবচেয়ে কম (Eckart–Young); তবু প্রশ্ন ব্যাখ্যা বা উৎস আলাদা করা হলে NMF বা ICA. উপাদানের নাম মানুষের দেওয়া, স্থিরতা যাচাই করে নিন।

পরের পর্বে: পর্ব ৪২ (t-SNE আর UMAP — manifold learning আর তার ফাঁদ): পেঁচানো swiss roll-এ কেন রৈখিক পদ্ধতি হারে, t-SNE কীভাবে প্রতিবেশ ধরে রাখে, আর তার ছবি পড়তে গিয়ে কোন ফাঁদে পা দেওয়া সহজ।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান