এই পর্বে যা শিখবেন
- Vector আর matrix চলকের scalar function-এর gradient, আর কোন আকারে লেখা হয় তার নিয়ম (layout convention)
- aᵀx, xᵀAx, ‖Ax − b‖², ‖XW − Y‖² আর log det-এর gradient নিজে derive করা, আর প্রতিটি সংখ্যা দিয়ে যাচাই
- Jacobian, matrix আকারের chain rule, আর backpropagation কেন আসলে Jacobian-এর গুণ
- Hessian, দ্বিতীয় ক্রমের Taylor approximation, আর Newton-এর ধাপ কেন এত দ্রুত
- Least squares, ridge আর logistic regression-এর gradient ও Hessian matrix আকারে, scikit-learn-এর সঙ্গে মিলিয়ে
AI-এর ভিত্তি কোর্সের পর্ব ৩৪-এ derivative, partial derivative, gradient, chain rule আর gradient descent এক বা দুই চলকে এসেছিল; পর্ব ৫৮-এ normal equations আর পর্ব ৫৯-এ logistic regression-এর gradient সূত্র হিসেবে দেখেছেন। এই পর্বের প্রশ্ন: হাজার হাজার parameter থাকলে সেই সূত্রগুলো এক লাইনে কীভাবে লেখা আর derive করা যায়, আর লেখা সূত্রটি ঠিক কি না, তা কীভাবে নিশ্চিত হবেন? চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python, NumPy আর scikit-learn); প্রথমবার প্যাকেজ নামতে কয়েক সেকেন্ড লাগে, scikit-learn-এ প্রায় দশ সেকেন্ড।
১ · কেন matrix calculus — একটি একটি করে নয়, পুরো vector একসঙ্গে
চায়ের দোকানের মালিক জানতে চান, চায়ের দাম ১ টাকা বাড়ালে দিনের লাভ কত বদলায়। এটি একটি derivative. এবার দোকানে ৪০টি পণ্য: চা, বিস্কুট, পান, কলা… প্রতিটির দাম একটু একটু বদলালে লাভ কোন দিকে কত বদলায়, তার ৪০টি উত্তর একটি তালিকায় রাখলে সেটিই gradient. আর যদি প্রশ্ন হয় “একটির দাম বদলালে অন্য প্রতিটি পণ্যের বিক্রি কত বদলায়”, তাহলে উত্তর একটি পুরো ছক, ৪০ × ৪০: সেটি Jacobian. Matrix calculus এই তালিকা আর ছকগুলো একটি একটি ঘর না গুনে পুরোটা একসঙ্গে লেখার ভাষা।
ML-এ প্রায় সব শেখা একটি সংখ্যা কমানো: loss L(w), যেখানে w হলো সব parameter মিলে একটি লম্বা vector. Linear regression-এ w-এর দৈর্ঘ্য feature-এর সংখ্যার সমান, ছোট neural network-এ কয়েক হাজার, বড় model-এ কোটি কোটি। প্রতিটি ঘরের partial derivative আলাদা করে লেখা অসম্ভব, আর কোডেও আমরা loop নয়, matrix গুণ লিখি। তাই derivative-ও লিখতে হয় matrix ভাষায়। এই পর্বে তিনটি বস্তু:
| Function | Derivative-এর নাম | আকার | ঘর (i, j)-তে কী |
|---|---|---|---|
| f: ℝn → ℝ (যেমন loss) | Gradient ∇f(x) | n × 1, x-এর মতো | ∂f/∂xi |
| f: ℝn → ℝm (যেমন একটি layer) | Jacobian Jf(x) | m × n | ∂fi/∂xj |
| f: ℝn → ℝ, দুবার | Hessian ∇²f(x) | n × n | ∂²f/∂xi∂xj |
| f: ℝp×q → ℝ (চলক একটি matrix W) | Gradient ∇Wf | p × q, W-এর মতো | ∂f/∂Wij |
Vector মানে কলাম vector. Scalar f-এর gradient ∇f(x) x-এর হুবহু আকারের কলাম vector (matrix চলক হলে সেই matrix-এর আকারের)। f: ℝn → ℝm-এর Jacobian m × n: সারি i হলো fi-এর gradient-এর transpose. তাই scalar f-এর ক্ষেত্রে Jf = ∇fᵀ, একটি 1 × n সারি। বইভেদে উল্টো নিয়মও চলে (একে numerator বনাম denominator layout বলে); অন্য বই পড়ার সময় আগে দেখে নিন সে কোনটি মানে।
এই নিয়মের সবচেয়ে বড় উপকার: আকার মিলিয়ে ভুল ধরা যায়। Gradient descent-এর w ← w − η∇L লিখতে হলে ∇L-এর আকার w-এর সমান হতেই হবে; সূত্রের ডান দিকে যদি 1 × n বা n × n আসে, derivation-এ কোথাও ভুল আছে। এই পাতায় প্রতিটি সূত্রের পাশে আকারও লেখা থাকবে। চিহ্নের মানে: aᵀ মানে a-এর transpose, aᵀx মানে dot product, ‖v‖² = vᵀv মানে ঘরগুলোর বর্গের যোগফল, আর I হলো identity matrix.
২ · প্রথম তিনটি সূত্র — aᵀx, xᵀAx আর একটি নিরাপদ কৌশল
সূত্র ১: f(x) = aᵀx = a1x1 + … + anxn. ∂f/∂xi-তে শুধু aixi পদটি xi-এর ওপর নির্ভর করে, তাই উত্তর ai. সব ঘর মিলে ∇(aᵀx) = a, আকার n × 1. এক চলকের “ax-এর derivative a”-র হুবহু রূপ।
সূত্র ২: f(x) = xᵀAx, যেখানে A একটি n × n matrix. একে বলে quadratic form: সব পদ দ্বিঘাত, f = ∑i∑j aijxixj. এর আকার (বাটি, জিন, উপত্যকা) পর্ব ১০ (Positive definite matrix — quadratic form আর covariance)-এর বিষয়; এখানে শুধু তার ঢাল। নিচের stepper একটি 2 × 2 উদাহরণে প্রতিটি ঘর আলাদা করে derivative নেয়, তারপর ধাঁচটি matrix আকারে চিনে নেয়।
- ঘরে ঘরে লেখা। xᵀAx-এর চারটি পদ: a11x1², a12x1x2, a21x2x1, a22x2². মাঝের দুটি একই গুণফল x1x2, তাই মিলে (১ + ৩) = ৪.
- মান। x = (১, ২)-এ f = ২ + ৮ + ১৬ = ২৬. সরাসরি Ax = (৪, ১১) বের করে x-এর সঙ্গে dot product নিলেও ২৬.
- প্রথম partial derivative. x2 স্থির: 2x1²-এর ঢাল 4x1, 4x1x2-এর ঢাল 4x2. x = (১, ২)-এ ৪ + ৮ = ১২.
- দ্বিতীয় partial derivative. x1 স্থির: 4x1x2-এর ঢাল 4x1, 4x2²-এর ঢাল 8x2. মান ৪ + ১৬ = ২০.
- Matrix আকারে। Gradient-এর সহগগুলো সাজালে কর্ণে 2aii, বাইরে a12 + a21: ঠিক A + Aᵀ. তাই ∇(xᵀAx) = (A + Aᵀ)x = (১২, ২০).
- 2Ax কেন ভুল। 2Ax = (৮, ২২), দুটি ঘরই ভুল। xᵀAx শুধু A-এর প্রতিসম অংশ As = (A + Aᵀ)/2 দেখে; অপ্রতিসম অংশ বাতিল হয়ে যায়। A আগে থেকেই প্রতিসম হলে (covariance, XᵀX, Hessian) তবেই 2Ax সঠিক।
এক চলকের ax²-এর derivative 2ax; xᵀAx-এর ক্ষেত্রে তার সঠিক রূপ (A + Aᵀ)x. ঘরে ঘরে হিসাব সবসময় কাজ করে, কিন্তু বড় সূত্রে ক্লান্তিকর। এর চেয়ে নিরাপদ আর দ্রুত একটি কৌশল আছে, যাকে বলে differential পদ্ধতি।
x-কে খুব সামান্য dx সরালে f-এর বদল df লিখুন, আর তাকে df = gᵀdx আকারে সাজান। তাহলে g-ই gradient. উদাহরণ: f = xᵀAx হলে df = dxᵀAx + xᵀA dx. প্রথম পদটি একটি সংখ্যা, তাই তার transpose নিজেই: dxᵀAx = xᵀAᵀdx. যোগ করলে df = xᵀ(Aᵀ + A)dx = ((A + Aᵀ)x)ᵀdx. অর্থাৎ ∇f = (A + Aᵀ)x.
এই কৌশলের দুটি সুবিধা: যোগফলের সূচক (i, j) লিখতে হয় না, আর শেষে gᵀdx আকার পেলে layout নিয়ে সন্দেহ থাকে না। আসলে এটি সেই পুরোনো সংজ্ঞাই: f(x + dx) ≈ f(x) + ∇f(x)ᵀdx. অর্থাৎ gradient হলো সেই vector, যার সঙ্গে dot product নিলে যেকোনো ছোট সরণের জন্য f-এর বদল পাওয়া যায়। একক দিক u-তে এই বদলের হার ∇fᵀu-কে বলে directional derivative; u যখন ∇f-এর দিকে, তখন সবচেয়ে বেশি। এজন্যই gradient সবচেয়ে খাড়া চড়াইয়ের দিক দেখায়, আর অংশ ৬-এর ছবিতে দেখবেন সেটি contour-এর সঙ্গে লম্ব।
চলক যখন একটি matrix, আর log det
Matrix চলক W (আকার p × q)-এর scalar function-এও একই কৌশল খাটে, শুধু dot product-এর জায়গায় বসে tr(Gᵀ dW) = ∑ij Gij dWij (tr মানে trace, অর্থাৎ কর্ণের যোগফল)। উদাহরণ: অনেক output-এর linear regression-এ loss f(W) = ‖XW − Y‖F², যেখানে Frobenius norm ‖M‖F² মানে matrix-এর সব ঘরের বর্গের যোগফল। R = XW − Y লিখলে df = 2 tr(Rᵀ dR) = 2 tr(Rᵀ X dW) = tr((2XᵀR)ᵀ dW), তাই ∇W‖XW − Y‖F² = 2Xᵀ(XW − Y), আকার p × q, W-এর মতোই।
আরেকটি সূত্র এই কোর্সে বারবার আসবে, বিশেষ করে multivariate Gaussian-এর likelihood-এ (পর্ব ১৯): বর্গাকার, inverse আছে এমন S-এর জন্য ∇S log det S = S−ᵀ (S প্রতিসম হলে সেটি S−1-এর সমান)। এখানে det মানে determinant, আর log det বড় matrix-এ সরাসরি det না নিয়ে np.linalg.slogdet দিয়ে হিসাব করা হয়। এর প্রমাণ Jacobi-র একটি সূত্র থেকে আসে, d(det S) = det S · tr(S−1dS); এখানে আমরা শুধু সংখ্যা দিয়ে যাচাই করব।
৩ · ‖Ax − b‖² আর chain rule — ভেতর থেকে বাইরে, বাইরে থেকে ভেতরে
Least squares-এর loss f(x) = ‖Ax − b‖², যেখানে A একটি m × n matrix (m-টি সমীকরণ, n-টি অজানা), b দৈর্ঘ্য m-এর vector. এটিকে দুটি ধাপে ভাঙা যায়: প্রথমে r = Ax − b (residual, আকার m × 1), তারপর f = rᵀr. প্রতিটি ধাপের derivative সহজ: r-এর x-এর সাপেক্ষে Jacobian হুবহু A (m × n), আর f-এর r-এর সাপেক্ষে gradient 2r. Matrix আকারের chain rule এ দুটিকে জোড়ে:
∇xf = Jr(x)ᵀ · ∇rf = Aᵀ · 2(Ax − b)
আকার মিলিয়ে দেখুন: Aᵀ হলো n × m, 2r হলো m × 1, গুণফল n × 1, ঠিক x-এর আকার। Transpose কেন? Scalar f-এর Jacobian হলো সারি: Jf∘r = Jf(r) · Jr(x) = (2r)ᵀA, আকার 1 × n; gradient তার transpose, Aᵀ(2r). নিচের চিত্র আর stepper এই দুই দিকের হিসাব দেখায়।
সামনে মান, পেছনে gradient — ‖Ax − b‖²-এর হিসাবের পথ
Forward pass computes values; the backward pass multiplies by transposed Jacobians
| ধাপ | |
| হিসাব | |
| যাচাই |
- আকার আগে। বাজারঘরের তিনটি কাল্পনিক ডেলিভারি, দূরত্ব ১, ২ আর ৩ km, সময় লেগেছে ১, ২ আর ২ ঘণ্টা। Model: সময় ≈ x1 + x2 × দূরত্ব. A-এর প্রথম কলাম সব 1 (intercept), দ্বিতীয় কলাম দূরত্ব। শেষে gradient 2 × 1 হতেই হবে।
- Residual. x = (১, ০) মানে “সব ডেলিভারি ১ ঘণ্টা”। অনুমান (১, ১, ১) থেকে আসল সময় বাদ দিলে r = (০, −১, −১): দ্বিতীয় আর তৃতীয় ডেলিভারিতে model কম বলেছে।
- Loss. f = rᵀr = ০ + ১ + ১ = ২.
- শেষ ধাপের gradient. f = r1² + r2² + r3²-এর প্রতিটি ri-এ ঢাল 2ri, তাই ∂f/∂r = (০, −২, −২).
- Transpose করা Jacobian দিয়ে পেছনে। r = Ax − b-এর Jacobian A, তাই ∇xf = Aᵀ(2r). Aᵀ-এর প্রথম সারি (১, ১, ১), দ্বিতীয় (১, ২, ৩): ফল (−৪, −১০). ঘরে ঘরে যাচাই: ∂f/∂x2 = 2(০)(১) + 2(−১)(২) + 2(−১)(৩) = −১০. দুটি ঘরই ঋণাত্মক, তাই x1, x2 বাড়ালে loss কমবে।
- শূন্য বসালে normal equations. 2Aᵀ(Ax − b) = 0 মানে AᵀAx = Aᵀb. এখানে AᵀA = [[৩, ৬], [৬, ১৪]], Aᵀb = (৫, ১১); determinant ৬, সমাধান x = (০.৬৬৬৭, ০.৫). তখন r = (০.১৬৭, −০.৩৩৩, ০.১৬৭), Aᵀr = 0 অর্থাৎ residual A-এর প্রতিটি কলামের সঙ্গে লম্ব, আর loss ০.১৬৬৭.
শেষ ধাপের “residual কলামগুলোর সঙ্গে লম্ব” কথাটির জ্যামিতিক ছবি, অর্থাৎ least squares মানে b-কে A-এর column space-এ projection, পর্ব ০৭ (Matrix-এর জ্যামিতি — projection, rotation আর least squares)-এর বিষয়। এখানে একই উত্তর এসেছে calculus দিয়ে: ঢাল শূন্য যেখানে, সেখানেই সর্বনিম্ন। সর্বনিম্নই কেন, সর্বোচ্চ বা জিন নয়, তার উত্তর দেবে Hessian (অংশ ৬)।
৪ · সূত্রকে বিশ্বাস নয়, যাচাই — central difference দিয়ে gradient checking
হাতে derive করা gradient-এ চিহ্ন, transpose বা ২-এর গুণক ভুল হওয়া খুব স্বাভাবিক, আর ভুল gradient দিয়েও training চলে, শুধু খারাপ ফল দেয়। তাই নিয়ম: প্রতিটি নতুন gradient সূত্র সংখ্যা দিয়ে মিলিয়ে নিন। একে বলে gradient checking. AI-এর ভিত্তি কোর্সের পর্ব ৩৪-এ এক চলকে দেখেছেন, h খুব বড় হলে সূত্রের ভুল আর খুব ছোট হলে float-এর rounding ভুল। বহু চলকে প্রতিটি ঘর i-এর জন্য আলাদা করে:
∂f/∂xi ≈ [ f(x + h·ei) − f(x − h·ei) ] / (2h)
এখানে ei হলো i-তম ঘরে 1 আর বাকি সব 0 এমন vector, আর h একটি ছোট সংখ্যা (এখানে 10−5 নেওয়া হয়েছে)। দুই দিকে সমান সরে ভাগ করাকে বলে central difference; এর সূত্রগত ভুল h²-এর অনুপাতে কমে, এক দিকের (forward) finite difference-এর ভুল কমে কেবল h-এর অনুপাতে। নিচের ব্লক একটি ছোট সাহায্যকারী function লিখে এই পর্বের ছয়টি সূত্র seed-সহ এলোমেলো ডেটায় মেলায়। ইচ্ছা করে একটি ভুল সূত্রও (অপ্রতিসম A-তে 2Ax) রাখা হয়েছে, যাতে দেখা যায় ভুল কেমন দেখায়।
import numpy as np
rng = np.random.default_rng(42)
def num_grad(f, x, h=1e-5): # central difference, প্রতিটি ঘরে আলাদা করে
g = np.zeros_like(x)
for i in range(x.size):
e = np.zeros_like(x); e.flat[i] = h
g.flat[i] = (f(x + e) - f(x - e)) / (2 * h)
return g
n = 4
x, a = rng.normal(size=n), rng.normal(size=n)
A = rng.normal(size=(n, n)) # ইচ্ছা করে অপ্রতিসম
B, b = rng.normal(size=(6, n)), rng.normal(size=6)
X, Y = rng.normal(size=(5, 3)), rng.normal(size=(5, 2))
W = rng.normal(size=(3, 2)) # এবার চলকটি নিজেই একটি matrix
S = B.T @ B + np.eye(n) # প্রতিসম, positive definite
checks = [
("a.T x", lambda v: a @ v, x, a),
("x.T A x", lambda v: v @ A @ v, x, (A + A.T) @ x),
("x.T A x vs 2Ax", lambda v: v @ A @ v, x, 2 * A @ x),
("|Bx - b|^2", lambda v: np.sum((B @ v - b)**2), x, 2 * B.T @ (B @ x - b)),
("|XW - Y|_F^2", lambda V: np.sum((X @ V - Y)**2), W, 2 * X.T @ (X @ W - Y)),
("log det S", lambda M: np.linalg.slogdet(M)[1], S, np.linalg.inv(S).T),
]
for name, f, at, formula in checks:
num = num_grad(f, at)
err = np.max(np.abs(num - formula))
rel = err / np.max(np.abs(num))
print(f"{name:<15} shape {str(at.shape):<7} max err {err:.1e} rel {rel:.1e}")আসল আউটপুট (ব্রাউজারের Python):
a.T x shape (4,) max err 5.6e-12 rel 2.9e-12
x.T A x shape (4,) max err 4.5e-11 rel 1.0e-11
x.T A x vs 2Ax shape (4,) max err 2.2e+00 rel 5.1e-01
|Bx - b|^2 shape (4,) max err 7.9e-11 rel 1.2e-11
|XW - Y|_F^2 shape (3, 2) max err 6.7e-11 rel 1.3e-11
log det S shape (4, 4) max err 4.0e-11 rel 6.5e-11
ছয়টি সারির পাঁচটিতে পরম ভুল ৫.৬ × ১০−১২ থেকে ৭.৯ × ১০−১১, আপেক্ষিক ভুল ১০−১১-এর আশেপাশে। Central difference-এর নিজস্ব নির্ভুলতা এটুকুই, তাই সূত্রগুলো সঠিক। তৃতীয় সারিটি আলাদা: অপ্রতিসম A-তে 2Ax-এর ভুল ২.২, আপেক্ষিক ০.৫১, অর্থাৎ gradient-এর সবচেয়ে বড় ঘরের প্রায় অর্ধেক। ভুল সূত্র ঠিক এভাবেই ধরা পড়ে: সঠিক আর ভুলের আপেক্ষিক ভুলে ফারাক কয়েকশ কোটি গুণ, তাই রায় দিতে কোনো সূক্ষ্ম সীমারেখা লাগে না। একই num_grad matrix চলক W (আকার (3, 2)) আর S-এও চলে, কারণ e.flat দিয়ে প্রতিটি ঘর আলাদা করে নড়ানো হয়; ফলের আকার চলকের আকারের সমান, ঠিক এই পাতার layout-এর মতো। log det-এর সারিতে S-এর প্রতিটি ঘর স্বাধীনভাবে নড়ানো হয়েছে বলে উত্তর S−ᵀ; S প্রতিসম, তাই এখানে S−1-ও একই।
Gradient checking-এর তিনটি ফাঁদ: (১) float32-এ চালালে h = 10−5-এ rounding ভুল অনেক বড়; যাচাই সবসময় float64-এ করুন। (২) ReLU বা পরম মানের মতো ভাঁজ (kink) যেখানে, তার ঠিক ওপরে দুই দিকের পার্থক্য অর্থহীন; ভাঁজ থেকে দূরে একটি বিন্দু নিন। (৩) এটি n-টি ঘরের জন্য 2n বার f চালায়, তাই শুধু ছোট উদাহরণে বা কয়েকটি এলোমেলো ঘরে যাচাইয়ের কাজে লাগে, training-এ নয়।
৫ · Gradient শূন্য — least squares আর ridge-এর closed form
Linear regression-এ design matrix X (n × d, প্রতিটি সারি একজন গ্রাহক), label y (n × 1), weight w (d × 1). অংশ ৩-এর সূত্রে A-এর জায়গায় X আর b-এর জায়গায় y বসালেই:
L(w) = ‖Xw − y‖² ⇒ ∇L = 2Xᵀ(Xw − y) = 0 ⇒ XᵀXw = Xᵀy
এগুলোই normal equations; X-এর কলামগুলো স্বাধীন হলে XᵀX-এর inverse আছে, আর সমাধান একটিই। Ridge-এ loss-এ ওজনের আকারের শাস্তি যোগ হয়: L(w) = ‖Xw − y‖² + α‖w‖². α‖w‖² = α wᵀIw, সূত্র ২-এ A = αI বসালে gradient 2αw. তাই:
∇L = 2Xᵀ(Xw − y) + 2αw = 0 ⇒ (XᵀX + αI) w = Xᵀy
এক লাইনের এই হিসাবে ridge-এর দুটি গুণ দেখা যায়। প্রথমত, XᵀX + αI-এর প্রতিটি eigenvalue XᵀX-এর চেয়ে α বেশি, তাই প্রায় একই দুটি কলাম থাকলেও matrix-টির inverse থাকে আর condition number ছোট হয়। দ্বিতীয়ত, এটি একটি closed form: কোনো পুনরাবৃত্তি লাগে না। কিন্তু scikit-learn-এর সঙ্গে মেলাতে গেলে দুটি খুঁটিনাটি জানা দরকার। scikit-learn-এর ডকুমেন্টেশন অনুযায়ী Ridge কমায় ‖y − Xw‖² + alpha·‖w‖², অর্থাৎ n দিয়ে ভাগ নেই আর ½ নেই; তাই alpha হুবহু আমাদের α. আর fit_intercept=True (default) হলে intercept-এ শাস্তি দেওয়া হয় না: ডেটা গড় থেকে সরিয়ে (centering) ridge সমাধান করা হয়, তারপর b = ȳ − x̄ᵀw.
import numpy as np
from sklearn.linear_model import Ridge
rng = np.random.default_rng(42) # কাল্পনিক বাজারঘর: ৪০ জন গ্রাহক
n = 40
orders = rng.poisson(6, n) # গত ৬ মাসে অর্ডার
basket = rng.normal(900, 250, n) # গড় ঝুড়ি, টাকা
days = rng.integers(1, 60, n) # শেষ অর্ডারের পর দিন
X = np.column_stack([orders, basket / 100, days]).astype(float)
y = 300 + 120 * orders + 45 * basket / 100 - 8 * days + rng.normal(0, 150, n)
# ১) least squares: ∇ = 2Xᵀ(Xw − y) = 0 → XᵀX w = Xᵀy
X1 = np.column_stack([np.ones(n), X]) # intercept-এর জন্য সব-1 কলাম
w_ne = np.linalg.solve(X1.T @ X1, X1.T @ y)
w_ls = np.linalg.lstsq(X1, y, rcond=None)[0]
print("OLS w (b, orders, basket, days):", np.round(w_ls, 3))
print("solve vs lstsq :", f"{np.max(np.abs(w_ne - w_ls)):.1e}")
print("|gradient| at w_ls :", f"{np.max(np.abs(2 * X1.T @ (X1 @ w_ls - y))):.1e}")
# ২) ridge: ‖y − Xw‖² + α‖w‖², ∇ = 0 → (XᵀX + αI) w = Xᵀy
alpha = 50.0
w_r = np.linalg.solve(X.T @ X + alpha * np.eye(3), X.T @ y)
sk = Ridge(alpha=alpha, fit_intercept=False).fit(X, y)
print("ridge, no intercept :", f"{np.max(np.abs(w_r - sk.coef_)):.1e}")
# ৩) intercept থাকলে: কেন্দ্রে সরিয়ে solve, intercept-এ শাস্তি নেই
mx, my = X.mean(axis=0), y.mean()
Xc, yc = X - mx, y - my
w_c = np.linalg.solve(Xc.T @ Xc + alpha * np.eye(3), Xc.T @ yc)
b_c = my - mx @ w_c
sk = Ridge(alpha=alpha).fit(X, y)
print("ridge + intercept :", f"{max(np.max(np.abs(w_c - sk.coef_)), abs(b_c - sk.intercept_)):.1e}")
print("ridge w:", np.round(w_c, 3), " b:", round(b_c, 2))
wrong = np.linalg.solve(X1.T @ X1 + alpha * np.eye(4), X1.T @ y) # intercept-কেও শাস্তি
print("penalised intercept :", np.round(wrong, 2))আসল আউটপুট (ব্রাউজারের Python):
OLS w (b, orders, basket, days): [283.455 120.362 58.024 -10.898]
solve vs lstsq : 2.5e-12
|gradient| at w_ls : 7.4e-09
ridge, no intercept : 7.1e-14
ridge + intercept : 1.4e-14
ridge w: [ 97.764 50.328 -11.384] b: 501.29
penalised intercept : [ 18.51 119.37 75.6 -8.26]
OLS-এর ওজন: intercept ২৮৩.৪৫৫, প্রতি অর্ডারে ১২০.৩৬২, ঝুড়ির প্রতি ১০০ টাকায় ৫৮.০২৪, শেষ অর্ডারের পর প্রতি দিনে −১০.৮৯৮. ডেটা বানানো হয়েছিল ৩০০, ১২০, ৪৫ আর −৮ দিয়ে; noise-সহ মাত্র ৪০টি সারিতে এটুকু ফারাক স্বাভাবিক। Normal equations-এর solve আর lstsq-এর পার্থক্য ২.৫ × ১০−১২, আর সমাধানে gradient-এর সবচেয়ে বড় ঘর ৭.৪ × ১০−৯: float-এর হিসাবে শূন্য, কারণ y হাজারের ঘরে আর XᵀX-এর ঘর আরও বড়। Ridge-এ closed form আর scikit-learn-এর পার্থক্য intercept ছাড়া ৭.১ × ১০−১৪, intercept-সহ centering পদ্ধতিতে ১.৪ × ১০−১৪: একই objective, একই উত্তর। α = ৫০-এ অর্ডারের ওজন ১২০ থেকে ৯৭.৮-এ ছোট হয়েছে, আর শাস্তির বাইরে থাকা intercept বেড়ে ৫০১.২৯ হয়ে সেই ঘাটতি পুষিয়েছে। শেষ লাইন ভুল নকশা দেখায়: সব-১ কলামকেও শাস্তি দিলে intercept নেমে যায় ১৮.৫১-এ আর ঝুড়ির ওজন ফুলে ওঠে ৭৫.৬-এ। এটি scikit-learn-এর Ridge-এর সঙ্গে মিলবে না, আর y-এর একক বদলালে উত্তরও অযৌক্তিকভাবে বদলাবে।
সূত্র জানা আর কোডে লেখা আলাদা। (XᵀX)−1 সূত্রে থাকলেও কোডে inv নয়, solve বা lstsq; কারণ XᵀX গড়লে condition number বর্গ হয়ে যায়। এটি AI-এর ভিত্তি কোর্সের পর্ব ৫৮-এ সংখ্যা দিয়ে দেখানো হয়েছে, আর QR দিয়ে কেন solver আরও নিরাপদ, তা পর্ব ০৭-এ। Ridge-এর জ্যামিতি আর MAP হিসেবে তার ব্যাখ্যা আসবে পর্ব ১৭ (Regularization-এর তত্ত্ব — L1/L2-এর জ্যামিতি, MAP আর early stopping)-এ।
৬ · Hessian আর দ্বিতীয় ক্রমের Taylor — ঢালের ঢাল, আর বাঁকের মাপ
Gradient বলে কোন দিকে ঢাল, কিন্তু সেই ঢাল সামনে কতটা বদলাবে তা বলে না। বাটির তলা, জিনের মাঝখান আর সমতল মাঠ, তিন জায়গাতেই gradient শূন্য। পার্থক্য ধরে দ্বিতীয় derivative-এর matrix, Hessian:
[∇²f(x)]ij = ∂²f / ∂xi∂xj, আকার n × n
Hessian আসলে gradient-এর Jacobian: ∇f: ℝn → ℝn-এর Jacobian নিলেই পাওয়া যায়। দ্বিতীয় derivative যথেষ্ট মসৃণ হলে ক্রম বদলালে ফল বদলায় না (Schwarz-এর উপপাদ্য), তাই Hessian প্রতিসম। কয়েকটি তৈরি উদাহরণ: aᵀx-এর Hessian 0; xᵀAx-এর A + Aᵀ (সব x-এ একই); ‖Ax − b‖²-এর 2AᵀA; ridge-এর 2(XᵀX + αI). এগুলোর সঙ্গে আসে Taylor-এর দ্বিতীয় ক্রমের approximation:
f(x + d) ≈ f(x) + ∇f(x)ᵀd + ½ dᵀ∇²f(x) d
প্রথম পদ এখনকার মান, দ্বিতীয়টি ঢাল ধরে সরলরেখায় অনুমান, তৃতীয়টি বাঁকের সংশোধন। Taylor approximation-এর ভুল প্রথম ক্রমে সাধারণত d-এর দৈর্ঘ্যের বর্গের অনুপাতে, দ্বিতীয় ক্রমে ঘনের অনুপাতে কমে। Quadratic function-এ (least squares, ridge) দ্বিতীয় ক্রমের approximation হুবহু সত্য, কোনো ভুল নেই। আর gradient শূন্য এমন বিন্দুতে তৃতীয় পদই সব বলে: Hessian positive definite হলে (সব eigenvalue ধনাত্মক) প্রতিটি দিকে f বাড়ে, তাই সেটি local minimum; কিছু eigenvalue ধনাত্মক আর কিছু ঋণাত্মক হলে saddle point. 2AᵀA সবসময় positive semidefinite, কারণ dᵀ(2AᵀA)d = 2‖Ad‖² ≥ 0; এজন্যই least squares-এর loss বাটির মতো, অর্থাৎ convex. Positive definite হওয়ার পরীক্ষাগুলো পর্ব ১০-এ, আর convexity-র পুরো তত্ত্ব পর্ব ১২ (Convexity — কেন কিছু loss সহজ আর কিছু কঠিন)-এ।
একই বিন্দু থেকে দুটি দিক: খাড়া ঢাল বনাম Hessian দিয়ে বাঁকানো
Contours of f(x) = ½ xᵀHx with H = [[2.5, 1.5], [1.5, 2.5]]; from x0 = (2, −0.5) the steepest-descent direction −∇f is perpendicular to the contour, the Newton direction −H⁻¹∇f points at the minimum
Jacobian আর Taylor, সংখ্যায়
এবার একটি অ-quadratic উদাহরণ, বাজারঘরের ডেলিভারি থেকে। শহরের তিনটি এলাকা d1, d2, d3 (km-এ স্থানাঙ্ক), দিনে সেখানে ৩০, ৫০ আর ২০টি অর্ডার। নতুন হাব x-এ বসালে তিনটি দূরত্বের vector f(x) = (‖x − d1‖, ‖x − d2‖, ‖x − d3‖), অর্থাৎ f: ℝ² → ℝ³, তাই Jacobian 3 × 2. ‖x − dk‖-এর gradient হলো dk থেকে x-এর দিকের একক vector uk = (x − dk)/‖x − dk‖, তাই Jacobian-এর সারি k হলো ukᵀ. মোট রাইড-km L(x) = ∑ wk‖x − dk‖-এর gradient chain rule-এ Jᵀw, আর Hessian ∑ wk(I − ukukᵀ)/‖x − dk‖. নিচের ব্লক Jacobian সংখ্যা দিয়ে মেলায়, তারপর একটি দিকে ধাপ t ছোট করতে করতে দুই ক্রমের Taylor-এর ভুল দেখে।
import numpy as np
import matplotlib.pyplot as plt
D = np.array([[2.0, 1.0], [7.0, 2.0], [5.0, 6.0]]) # ৩টি ডেলিভারি এলাকা (km)
wt = np.array([30.0, 50.0, 20.0]) # দিনে অর্ডার
dist = lambda x: np.linalg.norm(x - D, axis=1) # f: R² → R³
def jac(x): # সারি k = (x − d_k)ᵀ / ‖x − d_k‖
return (x - D) / dist(x)[:, None]
L = lambda x: wt @ dist(x) # মোট রাইড-km
grad = lambda x: jac(x).T @ wt # chain rule: Jᵀ · ∂L/∂f
def hess(x):
U = jac(x)
return sum(w / r * (np.eye(2) - np.outer(u, u)) for w, r, u in zip(wt, dist(x), U))
x0, h = np.array([4.0, 3.0]), 1e-6 # প্রস্তাবিত হাবের জায়গা
J_num = np.column_stack([(dist(x0 + e) - dist(x0 - e)) / (2 * h) for e in np.eye(2) * h])
print("J analytic:\n", np.round(jac(x0), 4))
print("max |J_num - J|:", f"{np.max(np.abs(J_num - jac(x0))):.1e}")
g, H = grad(x0), hess(x0)
print("L(x0) =", round(L(x0), 3), " grad =", np.round(g, 3))
u = np.array([0.6, 0.8]) # একটি একক দিক
print(" t true L 1st-order err 2nd-order err")
for t in (2.0, 1.0, 0.5, 0.25):
d = t * u
lin = L(x0) + g @ d
quad = lin + 0.5 * d @ H @ d
print(f"{t:>4} {L(x0 + d):8.3f} {L(x0 + d) - lin:11.2e} {L(x0 + d) - quad:11.2e}")
ts = np.linspace(-3, 3, 121)
plt.figure(figsize=(6.2, 3.4), layout="constrained")
plt.plot(ts, [L(x0 + t * u) for t in ts], "k", lw=2, label="true loss")
plt.plot(ts, L(x0) + ts * (g @ u), "C0--", label="1st-order Taylor")
plt.plot(ts, L(x0) + ts * (g @ u) + 0.5 * ts**2 * (u @ H @ u), "C3", label="2nd-order Taylor")
plt.xlabel("step t along direction u (km)"); plt.ylabel("total ride-km")
plt.title("Taylor approximations near x0 = (4, 3)"); plt.legend(fontsize=8); plt.show()আসল আউটপুট (ব্রাউজারের Python; নিচে ছবিও আঁকা হয়):
J analytic:
[[ 0.7071 0.7071]
[-0.9487 0.3162]
[-0.3162 -0.9487]]
max |J_num - J|: 1.8e-10
L(x0) = 306.212 grad = [-32.546 18.051]
t true L 1st-order err 2nd-order err
2.0 330.897 3.49e+01 4.71e+00
1.0 309.351 8.23e+00 6.88e-01
0.5 305.645 1.98e+00 9.20e-02
0.25 305.423 4.83e-01 1.18e-02
Jacobian-এর প্রতিটি সারি একটি একক vector: যেমন প্রথম সারি (০.৭০৭১, ০.৭০৭১) হলো এলাকা (২, ১) থেকে x0 = (৪, ৩)-এর দিক। সংখ্যায় মাপা Jacobian-এর সঙ্গে পার্থক্য ১.৮ × ১০−১০. মোট রাইড-km ৩০৬.২১২, gradient (−৩২.৫৪৬, ১৮.০৫১): হাবকে ডানে আর একটু নিচে, অর্থাৎ ৫০টি অর্ডারের এলাকা (৭, ২)-এর দিকে সরালে loss কমে। টেবিলে t অর্ধেক হলে প্রথম ক্রমের ভুল প্রায় ৪ গুণ কমে (৩৪.৯ → ৮.২৩ → ১.৯৮ → ০.৪৮৩), দ্বিতীয় ক্রমের প্রায় ৭ থেকে ৮ গুণ (৪.৭১ → ০.৬৮৮ → ০.০৯২ → ০.০১১৮): t² আর t³-এর আচরণ, তত্ত্বের মতোই। ছবিতে x0-এর কাছে লাল parabola কালো রেখার প্রায় গায়ে লেগে থাকে, দূরে গেলে আলাদা হয়ে যায়: Taylor approximation একটি স্থানীয় মানচিত্র। Newton-এর পদ্ধতি এই parabola-র তলায় লাফ দেয়; parabola যত ভালো, লাফ তত নির্ভুল।
৭ · Logistic regression — gradient আর Hessian matrix আকারে, তারপর Newton
AI-এর ভিত্তি কোর্সের পর্ব ৫৯-এ সূত্রটি ছিল: logistic regression-এর log loss-এর gradient Xᵀ(σ(Xw) − y)/n. এবার সেটি chain rule দিয়ে derive করি, আর এক ধাপ এগিয়ে Hessian-ও। এখানে loss গড় নয়, যোগফল (n দিয়ে ভাগ করলে gradient আর Hessian দুটিই 1/n গুণ হয়, Newton-এর ধাপ বদলায় না)। প্রতিটি উদাহরণের জন্য zi = xiᵀw, pi = σ(zi), আর
L(w) = ∑i [ log(1 + ezi) − yizi ]
এটি −[y log p + (1 − y) log(1 − p)]-এরই অন্য রূপ, আর কোডে np.logaddexp(0, z) দিয়ে লিখলে বড় z-এ overflow হয় না। এখন chain rule: log(1 + ez)-এর z-এর সাপেক্ষে derivative ez/(1 + ez) = σ(z), তাই ∂L/∂zi = pi − yi. সব উদাহরণ একসঙ্গে z = Xw, যার Jacobian হলো n × d আকারের X নিজেই। অংশ ৩-এর নিয়মে:
∇L = Xᵀ(p − y), আকার d × 1
Hessian-এর জন্য এই gradient-এর আবার Jacobian নিই। p − y-এর z-এর সাপেক্ষে Jacobian একটি কর্ণ-matrix, কারণ pi শুধু zi-এর ওপর নির্ভর করে, আর sigmoid-এর derivative σ′(z) = σ(z)(1 − σ(z)). তাই S = diag(p1(1 − p1), …, pn(1 − pn)) লিখলে:
∇²L = Xᵀ S X, আকার d × d
S-এর প্রতিটি ঘর ০ থেকে ০.২৫-এর মধ্যে, ধনাত্মক। তাই যেকোনো d-তে dᵀXᵀSXd = ∑ si(xiᵀd)² ≥ 0: Hessian positive semidefinite, log loss convex, কোনো ভুয়া local minimum নেই। এই Hessian দিয়ে Newton-এর ধাপ: Taylor-এর দ্বিতীয় ক্রমের approximation-কে সর্বনিম্ন করা। f(w) + gᵀd + ½dᵀHd-এর d-এর সাপেক্ষে gradient g + Hd; শূন্য বসালে:
H d = −g, তারপর w ← w + d
- ডেটা। বাজারঘরের চারটি কাল্পনিক অর্ডার: ডেলিভারিতে ০, ১, ২, ৩ দিন লেগেছে, আর দ্বিতীয় ও চতুর্থটি ফেরত এসেছে। X-এর প্রথম কলাম intercept. শুরুতে সব ওজন শূন্য।
- Forward. w = 0 হলে প্রতিটি অর্ডারের ফেরতের সম্ভাবনা ০.৫, আর loss ৪ × ln ২ = ২.৭৭২৬.
- Gradient. p − y = (০.৫, −০.৫, ০.৫, −০.৫). প্রথম ঘর (intercept) এদের যোগফল ০; দ্বিতীয় ঘর দিন দিয়ে ওজন করা যোগফল −১. “দিন”-এর ওজন বাড়ালে loss কমবে।
- Hessian. সব p = ০.৫, তাই S-এর সব ঘর ০.২৫, আর H = ০.২৫ · XᵀX = [[১, ১.৫], [১.৫, ৩.৫]]. দুটি eigenvalue-ই ধনাত্মক (determinant ১.২৫, trace ৪.৫)।
- Newton-এর ধাপ। Hd = −g সমাধানে d = (−১.২, ০.৮). নতুন w-তে loss ২.৩৫২৬. একই শুরু থেকে নিরাপদ learning rate (η = ৪/λmax(XᵀX) = ০.২৩৮)-এ gradient descent-এর এক ধাপ শুধু ২.৬৩২১-এ নামে।
- পুনরাবৃত্তি। Gradient-এর দৈর্ঘ্য ১ → ০.০৯৬ → ০.০০৩৬ → ৫.৮ × ১০−৬ → ১.৬ × ১০−১১: প্রতি ধাপে সঠিক দশমিক অঙ্ক প্রায় দ্বিগুণ। একে বলে quadratic convergence. চার ধাপে সর্বনিম্ন loss ২.৩৪৭৫.
এই পুনরাবৃত্তির আরেকটি নাম আছে: IRLS, পুরো নাম iteratively reweighted least squares; অর্থাৎ প্রতি ধাপে ওজন বদলে নতুন করে least squares করা। Hd = −g লিখলে প্রতিটি ধাপ হয় একটি weighted least squares, ওজন S-এর ঘরগুলো। ১৯৭২ সালে Nelder আর Wedderburn-এর generalized linear model-এর কাঠামোয় এই পদ্ধতি প্রচলিত হয়, আর statsmodels-এর মতো পরিসংখ্যান library-তে logistic regression আজও সাধারণত এভাবে fit হয় (GLM আসবে পর্ব ২০-এ)। এবার বড় ডেটায় দুটি পদ্ধতি পাশাপাশি: ২০০টি কাল্পনিক অর্ডার, intercept আর দুটি feature. প্রথমে gradient আর Hessian সংখ্যা দিয়ে যাচাই, তারপর ছয় ধাপ Newton বনাম ছয় ধাপ gradient descent.
import numpy as np
rng = np.random.default_rng(42) # কাল্পনিক বাজারঘর: ২০০টি অর্ডার, ফেরত এল কি না
n = 200
X = np.column_stack([np.ones(n), rng.normal(size=(n, 2))]) # intercept + ২টি feature
y = (rng.random(n) < 1 / (1 + np.exp(-(X @ [-1.0, 2.0, -1.5])))).astype(float)
sig = lambda z: 1 / (1 + np.exp(-z))
loss = lambda w: np.sum(np.logaddexp(0, X @ w) - y * (X @ w)) # log loss-এর যোগফল
grad = lambda w: X.T @ (sig(X @ w) - y)
hess = lambda w: X.T @ (X * (sig(X @ w) * (1 - sig(X @ w)))[:, None]) # XᵀSX
w0, h = np.array([0.3, -0.2, 0.5]), 1e-5
E = np.eye(3) * h
g_num = np.array([(loss(w0 + e) - loss(w0 - e)) / (2 * h) for e in E])
H_num = np.array([(grad(w0 + e) - grad(w0 - e)) / (2 * h) for e in E])
print("gradient check:", f"{np.max(np.abs(g_num - grad(w0))):.1e}",
"| Hessian check:", f"{np.max(np.abs(H_num - hess(w0))):.1e}")
print("Hessian eigenvalues:", np.round(np.linalg.eigvalsh(hess(w0)), 2))
w_nt, w_gd = np.zeros(3), np.zeros(3)
eta = 4 / np.linalg.eigvalsh(X.T @ X).max() # GD-র নিরাপদ ধাপ: 1 / (‖X‖² / 4)
print("step Newton loss GD loss")
for t in range(1, 7):
w_nt = w_nt - np.linalg.solve(hess(w_nt), grad(w_nt)) # Newton
w_gd = w_gd - eta * grad(w_gd) # gradient descent
print(f"{t:>4} {loss(w_nt):.10f} {loss(w_gd):.6f}")
best, k = loss(w_nt), 0
while loss(w_gd) - best > 1e-6:
w_gd, k = w_gd - eta * grad(w_gd), k + 1
print("GD needs", 6 + k, "steps to get within 1e-6 of Newton")
print("w (Newton):", np.round(w_nt, 4))আসল আউটপুট (ব্রাউজারের Python):
gradient check: 2.5e-09 | Hessian check: 2.0e-09
Hessian eigenvalues: [33.19 45.82 47.73]
step Newton loss GD loss
1 89.1090771737 92.705837
2 82.1390890364 86.867112
3 81.3070395977 84.485820
4 81.2887610069 83.265540
5 81.2887496375 82.567522
6 81.2887496375 82.141213
GD needs 50 steps to get within 1e-6 of Newton
w (Newton): [-1.0208 1.9266 -1.5297]
প্রথম লাইনে gradient আর Hessian-এর সূত্র central difference-এর সঙ্গে ২.৫ × ১০−৯ আর ২.০ × ১০−৯ পর্যন্ত মেলে; loss এখানে প্রায় ১০০-এর ঘরে, তাই পরম ভুল অংশ ৪-এর চেয়ে বড়, আপেক্ষিকভাবে একই মানের। Hessian-এর তিনটি eigenvalue (৩৩.১৯, ৪৫.৮২, ৪৭.৭৩) ধনাত্মক: এই বিন্দুতে loss বাটির মতো বাঁকা। Newton-এর loss সর্বনিম্ন মান থেকে কত দূরে, তা ধাপে ধাপে দেখুন: প্রায় ৭.৮, ০.৮৫, ০.০১৮, ১.১ × ১০−৫, তারপর দশ দশমিক পর্যন্ত শূন্য। শেষের দিকে প্রতি ধাপে সঠিক অঙ্ক প্রায় দ্বিগুণ, এটিই quadratic convergence. Gradient descent ছয় ধাপে মাত্র ৮২.১৪-এ, আর Newton-এর ১০−৬-এর মধ্যে আসতে মোট ৫০ ধাপ লেগেছে। তবে প্রতিটি Newton ধাপ দামি: এখানে ৩ × ৩ সমীকরণ, তাই নগণ্য; d বড় হলে আর নয়। শেষের ওজন (−১.০২১, ১.৯২৭, −১.৫৩০) ডেটা বানানোর ওজন (−১, ২, −১.৫)-এর কাছে; ২০০টি উদাহরণে এটুকু ফারাক স্বাভাবিক।
Newton সবসময় জেতে না। d সংখ্যক parameter-এ Hessian রাখতে d² জায়গা আর সমাধানে প্রায় d³ কাজ লাগে; d = ১০ লাখ হলে তা অসম্ভব। Hessian positive definite না হলে (non-convex loss-এ প্রায়ই) Newton-এর দিক উপরের দিকেও যেতে পারে, আর ডেটা পুরো আলাদা করা গেলে (separable) logistic-এর S-এর ঘর শূন্যের দিকে যায়, H প্রায় singular হয়। Line search, L-BFGS আর Hessian-vector product দিয়ে এর সমাধান পর্ব ১৫ (Second-order পদ্ধতি — Newton, L-BFGS আর line search)-এ।
৮ · Automatic differentiation — chain rule-কে যন্ত্রের হাতে দেওয়া
এখন পর্যন্ত প্রতিটি gradient আমরা হাতে derive করে সংখ্যা দিয়ে মিলিয়েছি। কোটি parameter-এর network-এ হাতে derive করা অবাস্তব, আর finite difference-এ প্রতিটি parameter-এর জন্য দুবার পুরো model চালাতে হয়। তৃতীয় পথ automatic differentiation (autodiff): প্রোগ্রাম চলার সময় প্রতিটি ছোট হিসাব (গুণ, যোগ, exp, log) আর তার নিজস্ব ছোট Jacobian মনে রাখা, তারপর chain rule দিয়ে সেগুলো জোড়া। ফল সূত্রের মতোই নির্ভুল (শুধু float-এর rounding), finite difference-এর মতো আনুমানিক নয়। AI-এর ভিত্তি কোর্সের পর্ব ৬৮-এ PyTorch-এর autograd হাতে লেখা gradient-এর সঙ্গে মিলেছিল; এখানে প্রশ্ন, ভেতরে জোড়ার ক্রম কোনটি।
একটি হিসাব যদি x → h1 → h2 → … → L ধাপে চলে, তাহলে JL = Jk ⋯ J2J1. গুণের ক্রম দুই রকম হতে পারে:
Forward mode — ডান থেকে
একটি input দিক v নিয়ে J1v, তারপর J2(J1v)… একবার চালিয়ে পাওয়া যায় একটি Jacobian-vector product Jv, অর্থাৎ একটি input দিকে সব output-এর বদল। n-টি input-এর পুরো gradient-এ n বার চালাতে হয়। Forward mode কম input আর অনেক output-এ সুবিধাজনক।
Reverse mode — বাঁ থেকে
Output-এর দিক থেকে uᵀJk, তারপর (uᵀJk)Jk−1… একে বলে vector-Jacobian product. Scalar loss-এ u = 1, আর একবার পেছনে গেলেই সব parameter-এর gradient. Reverse mode-এর খরচ সাধারণত function চালানোর মাত্র কয়েক গুণ, parameter যত বেশিই হোক; বিনিময়ে মাঝের মানগুলো মনে রাখতে memory লাগে।
ML-এ loss একটি সংখ্যা আর parameter অসংখ্য, তাই reverse mode-ই স্বাভাবিক পছন্দ। অংশ ৩-এর ছবির লাল তীরগুলো ঠিক এটিই: Aᵀ(2r) মানে vector 2r-কে Jacobian A-এর বাঁ দিক থেকে গুণ। এজন্য পুরো Jacobian (যা বিশাল হতে পারে) কখনো বানাতে হয় না। ইতিহাসে forward mode-এর একটি প্রথম দিকের বর্ণনা R. E. Wengert-এর ১৯৬৪ সালের ছোট প্রবন্ধে (Communications of the ACM) পাওয়া যায়; reverse mode-এর আধুনিক রূপের কৃতিত্ব সাধারণত Seppo Linnainmaa-র ১৯৭০ সালের master's thesis-কে দেওয়া হয়, আর neural network-এ backpropagation নামে তা জনপ্রিয় হয় ১৯৮৬ সালে Rumelhart, Hinton আর Williams-এর Nature-এর প্রবন্ধে। কে প্রথম, তা নিয়ে গবেষকদের মধ্যে মতভেদ আছে, কারণ একই ধারণা নিয়ন্ত্রণ-তত্ত্ব আর সংখ্যাতত্ত্বে আলাদাভাবে এসেছিল। Neural network-এ autodiff-এর বিস্তারিত প্রয়োগ পরে Deep Learning কোর্সে আসবে।
আরও গভীরে: Matrix calculus-এর একটি পূর্ণাঙ্গ, differential-ভিত্তিক পাঠ্যবই Jan Magnus আর Heinz Neudecker-এর Matrix Differential Calculus with Applications in Statistics and Econometrics (প্রথম সংস্করণ ১৯৮৮); দ্রুত সূত্র খোঁজার জন্য অনেকে বিনামূল্যের The Matrix Cookbook (Petersen ও Pedersen) ব্যবহার করেন, তবে তার layout এই পাতার সঙ্গে সবসময় মেলে না, তাই আকার মিলিয়ে নিন। Autodiff-এর একটি পরিচিত সমীক্ষা Baydin ও সহকর্মীদের ২০১৮ সালের Journal of Machine Learning Research-এর প্রবন্ধ, আর পূর্ণ Hessian না বানিয়ে Hessian-vector product Hv একটি gradient-এর খরচে বের করার কৌশল Pearlmutter-এর ১৯৯৪ সালের প্রবন্ধে বর্ণিত।
পরীক্ষা ও ইন্টারভিউতে:
- ∇(xᵀAx) কত? কখন 2Ax? (A + Aᵀ)x; A প্রতিসম হলে তবেই 2Ax.
- ∇w‖Xw − y‖² আর normal equations? 2Xᵀ(Xw − y); শূন্য বসালে XᵀXw = Xᵀy.
- Ridge-এর closed form? (XᵀX + αI)w = Xᵀy, যেখানে loss ‖Xw − y‖² + α‖w‖²; intercept সাধারণত শাস্তির বাইরে।
- Logistic loss-এর Hessian, আর তা থেকে কী বোঝা যায়? XᵀSX, S = diag(p(1 − p)); positive semidefinite, তাই loss convex.
- Backpropagation কেন reverse mode? একটি scalar loss আর অনেক parameter; একবার পেছনে গেলেই পুরো gradient, খরচ forward-এর কয়েক গুণ।
৯ · AI-এর যুগে — AI-এর derive করা gradient আর optimizer কোড যাচাই
AI assistant gradient-এর সূত্র আত্মবিশ্বাসী আর পরিষ্কার ভাষায় লেখে। কিন্তু derivation দেখতে নির্ভুল হলেও একটি transpose, একটি ২-এর গুণক বা একটি “A প্রতিসম ধরে নিলাম” প্রায়ই চুপচাপ ঢুকে যায়। ভালো খবর: এই পর্বের প্রতিটি দাবি পাঁচ লাইনের কোডে যাচাই করা যায়।
| AI প্রায়ই যা করে | কেন ভুল | কীভাবে ধরবেন |
|---|---|---|
| ∇(xᵀAx) = 2Ax লেখে, A প্রতিসম কি না না দেখেই | অপ্রতিসম A-তে ভুল; অংশ ৪-এর run-এ আপেক্ষিক ভুল প্রায় অর্ধেক | Central difference দিয়ে একটি এলোমেলো অপ্রতিসম A-তে মিলিয়ে দেখুন |
| Layout মিশিয়ে ফেলে: কোথাও gradient সারি, কোথাও কলাম; Xᵀ-এর জায়গায় X | আকার মেলে না, বা বর্গাকার ক্ষেত্রে চুপচাপ ভুল সংখ্যা দেয় | প্রতিটি পদের পাশে আকার লিখুন; বর্গাকার নয় এমন ডেটায় (যেমন n ≠ d) পরীক্ষা করুন |
| Ridge-এর গড় loss-এর সূত্র লিখে scikit-learn-এর alpha বসায়, বা intercept-কেও শাস্তি দেয় | Objective আলাদা, তাই ওজনও আলাদা | Library-র docs-এ objective-টি পড়ুন; ছোট ডেটায় closed form আর library মিলিয়ে নিন |
scikit-learn-এ unpenalised logistic-এর জন্য পুরোনো বা বাদ পড়া parameter দেয় (যেমন multi_class) | নতুন version-এ warning বা error | নিজের version-এর docs দেখুন; চালিয়ে warning পড়ুন |
| Hessian-কে “সবসময় positive definite” বলে Newton চালায় | Non-convex বা separable ডেটায় ভুল দিকে বা অসীমে যায় | eigvalsh দিয়ে eigenvalue দেখুন; loss প্রতি ধাপে কমছে কি না দেখুন |
| Train loss কমেছে দেখে gradient ঠিক বলে ধরে নেয় | ভুল gradient-এও loss কিছুটা কমতে পারে | Gradient checking, আর আলাদা validation ডেটায় একটি সরল baseline-এর সঙ্গে তুলনা |
AI-কে শুধু সূত্র নয়, আকার, শর্ত আর যাচাইয়ের কোডও চাইতে বলুন:
AI-এর দেওয়া derivation আর কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Matrix calculus-এ layout convention নিয়ে এত বিভ্রান্তি কেন, আর নিজেকে নিরাপদ রাখার সবচেয়ে ভরসাযোগ্য উপায় কী?
বিভ্রান্তির মূল কারণ: একই derivative-কে দুইভাবে সাজানো যায়, আর দুটিই যুক্তিসঙ্গত। Numerator layout-এ f: ℝn → ℝm-এর derivative m × n (output সারিতে), যা Jacobian-এর প্রচলিত রূপ আর chain rule-কে বাঁ থেকে ডানে স্বাভাবিক গুণ বানায়। Denominator layout-এ একই জিনিস n × m, যাতে scalar f-এর gradient কলাম vector হয়ে x-এর পাশে বসে, আর gradient descent-এর w − η∇L সরাসরি লেখা যায়। পরিসংখ্যান আর অর্থনীতির কিছু বই এক রীতি নেয়, প্রকৌশল আর ML-এর কিছু বই অন্যটি, আর অনেক নোট দুটি মিশিয়ে লেখে। এই পাতার রীতি সেই মিশ্রণেরই একটি সচেতন রূপ: Jacobian m × n, কিন্তু scalar-এর gradient কলাম।
সমস্যা হয় যখন একটি বই থেকে সূত্র নিয়ে আরেক রীতির derivation-এ বসানো হয়। তখন একটি transpose হারিয়ে যায়, আর বর্গাকার matrix হলে আকারও মিলে যায়, তাই ভুল ধরা পড়ে না। অংশ ২-এর xᵀAx-এর উদাহরণে (A + Aᵀ)x আর 2Ax দুটিই n × 1, অথচ একটি ভুল।
নিরাপদ থাকার তিনটি অভ্যাস। প্রথম, differential পদ্ধতি: df লিখে gᵀdx বা tr(Gᵀ dW) আকারে সাজান; এতে layout-এর প্রশ্নই ওঠে না, কারণ df একটি সংখ্যা। Magnus আর Neudecker-এর বইয়ের মূল পরামর্শও এটি। দ্বিতীয়, প্রতিটি পদের পাশে আকার, আর পরীক্ষার ডেটায় সব মাত্রা আলাদা রাখা (n = ৫, d = ৩, k = ২), যাতে ভুল transpose গুণফল বানাতেই ব্যর্থ হয়। তৃতীয়, গণনামূলক যাচাই: central difference, যা কোনো রীতির ওপর নির্ভর করে না, কারণ সে প্রতিটি ঘর আলাদা করে মাপে।
ব্যবহারিক কোডে বিষয়টি আরও সরল: NumPy-তে gradient-এর array-এর shape চলকের shape-এর সমান হওয়া উচিত, আর PyTorch বা JAX-এর autodiff-ও এই নিয়মে gradient ফেরত দেয়। তাই কোডে assert grad.shape == w.shape একটি সস্তা পাহারাদার।
মূল উপলব্ধি: রীতি মুখস্থ করার চেয়ে রীতি-নিরপেক্ষ যাচাই বেশি কাজের: differential-এ লিখুন, আকার মেলান, আর সংখ্যা দিয়ে মিলিয়ে নিন।
প্র ০২Central difference দিয়েই যদি gradient পাওয়া যায়, তাহলে হাতে derive করা বা autodiff-এর দরকার কী?
তিনটি কারণে: খরচ, নির্ভুলতা আর মসৃণতা। খরচ: central difference প্রতিটি parameter-এর জন্য দুবার পুরো function চালায়। d = ১০ লাখ parameter-এর একটি মাঝারি network-এ একটি gradient মানে বিশ লাখ forward pass; প্রতি training ধাপে তা অসম্ভব। Reverse-mode autodiff একই gradient দেয় forward pass-এর মাত্র কয়েক গুণ খরচে, parameter যত বেশিই হোক। এই “সস্তা gradient”-এর ফলটি autodiff-এর তত্ত্বে একটি পরিচিত উপপাদ্য, আর আধুনিক deep learning এর ওপরই দাঁড়িয়ে।
নির্ভুলতা: AI-এর ভিত্তি কোর্সের পর্ব ৩৪-এ দেখেছেন, h বড় হলে সূত্রের ভুল আর h ছোট হলে float-এর বাতিল হওয়া অঙ্কের ভুল; float64-এ central difference-এ সাধারণত ৮ থেকে ১০টি সঠিক দশমিক অঙ্কের বেশি পাওয়া যায় না। এই পাতার logistic যাচাইয়ে loss-এর মান প্রায় ১০০ হওয়ায় পরম ভুল 10−9-এর কাছাকাছি এসেছে। Autodiff-এর ফল সূত্রের মতোই নির্ভুল, শুধু সাধারণ rounding. বহু ধাপের optimization-এ ছোট ছোট ভুল জমে, তাই training-এ আনুমানিক gradient ব্যবহার করলে সমস্যা হতে পারে।
মসৃণতা: ReLU-র মতো ভাঁজে বা if-শর্তের সীমানায় দুই দিকের পার্থক্য ভুল ধারণা দিতে পারে, যখন autodiff নির্দিষ্ট একটি নিয়ম মেনে একটি subgradient দেয়।
তাহলে central difference কেন শিখলাম? কারণ এটি সবচেয়ে সরল, সবচেয়ে কম অনুমাননির্ভর পরীক্ষক। নতুন একটি custom loss বা layer লিখলে, বা autodiff-এর বাইরে হাতে gradient লিখলে (যেমন কোনো C extension-এ), কয়েকটি এলোমেলো ঘরে central difference চালিয়ে মেলানোই মানসম্মত অভ্যাস। ইতিহাসেও এই দুটি পাশাপাশি চলেছে: Wengert-এর ১৯৬৪ সালের forward-mode বর্ণনা থেকে Linnainmaa-র reverse mode আর ১৯৮৬ সালের backpropagation পর্যন্ত, প্রতিটি নতুন পদ্ধতি যাচাই করা হয়েছে সংখ্যাগত পার্থক্যের সঙ্গে মিলিয়ে।
হাতে derive করার মূল্যও কমেনি: XᵀSX-এর মতো সূত্র থেকে convexity, শর্ত আর গণনার খরচ বোঝা যায়, যা একটি autodiff-এর সংখ্যা থেকে বোঝা যায় না।
মূল উপলব্ধি: autodiff হিসাব করে, finite difference পাহারা দেয়, আর হাতের derivation বোঝায়; তিনটির কাজ আলাদা।
প্র ০৩Newton-এর পদ্ধতি এত দ্রুত হলে বড় model-এর training-এ প্রায় সবাই gradient descent-এর পরিবার কেন ব্যবহার করে?
Newton-এর পদ্ধতির শিকড় পুরোনো: Isaac Newton সতেরো শতকে বহুপদীর মূল খোঁজার একটি পদ্ধতি লেখেন, Joseph Raphson ১৬৯০ সালে তার একটি সরল রূপ প্রকাশ করেন, আর optimization-এ “gradient শূন্য বসানোর” রূপটি পরে বিকশিত হয়; ইতিহাসের খুঁটিনাটি নিয়ে উৎসভেদে ভিন্নতা আছে। এর দ্রুততা আসে Hessian থেকে: এই পাতার logistic উদাহরণে চার-পাঁচ ধাপে সর্বনিম্ন, gradient descent-এর পঞ্চাশ ধাপের জায়গায়।
কিন্তু বড় model-এ তিনটি বাধা। আকার: d = ১০ কোটি parameter-এর Hessian-এ ১০১৬টি ঘর; float32-এ রাখতেও প্রায় ৪ কোটি গিগাবাইট। সমাধানের খরচ প্রায় d³. Non-convexity: neural network-এর loss-এ Hessian-এর অনেক eigenvalue ঋণাত্মক হতে পারে; তখন বিশুদ্ধ Newton-এর ধাপ saddle point-এর দিকেও টানে, কারণ সে শুধু gradient শূন্যের বিন্দু খোঁজে, সর্বনিম্ন আলাদা করে না। Noise: training-এ gradient আসে mini-batch থেকে, যা noisy; noisy Hessian-এর inverse আরও অস্থির।
তাই মাঝামাঝি পথগুলো জনপ্রিয়। L-BFGS শুধু কয়েকটি সাম্প্রতিক gradient-এর পার্থক্য থেকে Hessian-এর inverse-এর একটি সস্তা আন্দাজ বানায়, আর scikit-learn-এর LogisticRegression-এর default solver এটিই। Hessian-vector product (Pearlmutter, ১৯৯৪) দিয়ে পুরো H না বানিয়েও conjugate gradient-এ Newton-এর ধাপের কাছাকাছি যাওয়া যায়; এর নাম Hessian-free optimization. আর Adam-এর মতো optimizer প্রতিটি parameter-এর জন্য আলাদা ধাপের মাপ শেখে, যাকে কর্ণ-Hessian-এর একটি খুব মোটা আন্দাজ হিসেবে দেখা যায়, যদিও ব্যাখ্যাটি বিতর্কিত।
ছোট আর মাঝারি convex সমস্যায় (logistic, Poisson GLM, কয়েক হাজার feature) Newton বা IRLS আজও প্রথম পছন্দ, কারণ কয়েক ধাপে মেশিনের নির্ভুলতা পর্যন্ত পৌঁছায় আর learning rate বাছতে হয় না।
মূল উপলব্ধি: দ্রুততম পদ্ধতি নয়, আকার, আকৃতি আর noise-এর সঙ্গে মানানসই পদ্ধতিই সেরা; এই trade-off-এর পুরো ছবি পর্ব ১৩, ১৪ আর ১৫-এ আসবে।
প্র ০৪Ridge-এর (XᵀX + αI) সংখ্যাগতভাবে ঠিক কী উপকার করে, আর intercept-কে শাস্তি দেওয়া কেন ভুল?
XᵀX প্রতিসম আর positive semidefinite, তার eigenvalue λ1 ≥ … ≥ λd ≥ ০. দুটি feature প্রায় একই হলে (যেমন “মোট খরচ” আর “গড় ঝুড়ি × অর্ডার”) সবচেয়ে ছোট eigenvalue শূন্যের খুব কাছে চলে যায়, condition number λ1/λd বিশাল হয়, আর normal equations-এর সমাধান ডেটার সামান্য noise-এ লাফায়। αI যোগ করলে প্রতিটি eigenvalue α বাড়ে, eigenvector একই থাকে; নতুন condition number (λ1 + α)/(λd + α), যা অনেক ছোট। অর্থাৎ ridge সমস্যাটিকে well-posed বানায়: সমাধান সবসময় একটিই, আর ডেটার ছোট বদলে ছোট বদলায়।
ইতিহাসে ridge regression-এর পরিচিত সূত্রপাত Arthur Hoerl আর Robert Kennard-এর ১৯৭০ সালের Technometrics-এর প্রবন্ধে, যার শিরোনামেই আছে মূল প্রেরণা: প্রায়-collinear (nonorthogonal) feature-এ least squares-এর অস্থির coefficient. গণিতে একই ধারণা Tikhonov regularization নামে আরও আগে থেকে পরিচিত। পরিসংখ্যানের দিক থেকে ridge-এর বিনিময়টিও স্পষ্ট: সামান্য bias, অনেক কম variance; এর পূর্ণ ব্যাখ্যা AI-এর ভিত্তি কোর্সের পর্ব ৬১ আর এই কোর্সের পর্ব ১৭-এ।
Intercept-এর প্রশ্ন: শাস্তির উদ্দেশ্য হলো “feature-এর প্রভাব খুব বড় না হোক”। Intercept কোনো feature-এর প্রভাব নয়, সব গ্রাহকের গড় স্তর। একে শাস্তি দিলে model-এর অনুমান y-এর একক বদলানোর ওপর নির্ভর করে (টাকা না হাজার টাকা), যা অর্থহীন। অংশ ৫-এর run-এ intercept-কে শাস্তি দিলে সেটি ২৮৩ থেকে প্রায় ১৮-এ নেমে গেছে, আর বাকি coefficient সেই ঘাটতি পোষাতে বেঁকে গেছে। Centering করে সমাধান করলে ঠিক এই সমস্যা এড়ানো যায়, আর scikit-learn তা-ই করে।
আরেকটি সতর্কতা: ridge-এর শাস্তি সব ওজনে একই α দেয়, তাই feature-এর একক (km না মিটার) ফলকে বদলে দেয়। এজন্য ridge-এর আগে feature standardize করা প্রচলিত অভ্যাস, আর তা train ডেটা থেকে শিখে pipeline-এ করা উচিত।
মূল উপলব্ধি: αI হলো eigenvalue-এর নিচে একটি মেঝে; এটি সমস্যাকে স্থির করে, কিন্তু কোন জিনিসকে শাস্তি দিচ্ছেন আর কোন এককে, তা ভেবে বসাতে হয়।
প্র ০৫Logistic regression-এর Hessian XᵀSX থেকে model সম্পর্কে আর কী কী জানা যায়?
Convexity আর একক সমাধান: S-এর সব ঘর ধনাত্মক হলে XᵀSX positive semidefinite, আর X-এর কলাম স্বাধীন হলে positive definite; তখন loss strictly convex, সর্বনিম্ন একটিই। Gradient descent যেখান থেকেই শুরু করুক, একই উত্তরে পৌঁছায়।
Separable ডেটার বিপদ: কোনো রেখা যদি দুই শ্রেণিকে নিখুঁতভাবে আলাদা করে, তাহলে ওজন বাড়াতে থাকলে প্রতিটি pi ০ বা ১-এর দিকে যায়, loss শূন্যের দিকে নামে, কিন্তু কখনো পৌঁছায় না: সর্বনিম্ন বিন্দু অসীমে। তখন p(1 − p) → ০, S → ০, Hessian প্রায় singular, আর Newton-এর ধাপ বিশাল হয়। এর প্রতিকার regularization: L2 শাস্তি যোগ করলে Hessian হয় XᵀSX + λI, যা সবসময় positive definite.
অনিশ্চয়তা: পরিসংখ্যানে log-likelihood-এর ঋণাত্মক Hessian-কে বলে observed Fisher information. সর্বনিম্ন বিন্দুতে তার inverse, (XᵀSX)−1, coefficient-গুলোর আনুমানিক covariance matrix; তার কর্ণের বর্গমূল standard error. statsmodels-এর Logit summary-তে যে standard error আর p-value দেখেন, তা এভাবেই আসে। বাঁক যত তীক্ষ্ণ (Hessian বড়), coefficient তত নিশ্চিতভাবে নির্ধারিত; সমতল হলে অনিশ্চিত। এর বিস্তারিত পর্ব ১৮ (Maximum likelihood আর MAP)-এ আসবে।
Weighted least squares-এর ছায়া: S-এর ঘর p(1 − p) সবচেয়ে বড় যখন p = ০.৫, অর্থাৎ যে অর্ডারগুলোর ফেরত নিয়ে model সবচেয়ে অনিশ্চিত, Newton-এর প্রতিটি ধাপে সেগুলোই সবচেয়ে বেশি ওজন পায়। নিশ্চিত উদাহরণ (p প্রায় ০ বা ১) প্রায় কিছুই যোগ করে না। IRLS নামের মানে এটিই, আর Nelder ও Wedderburn-এর ১৯৭২ সালের GLM কাঠামো এই পর্যবেক্ষণকে Poisson, Gamma ইত্যাদি সব model-এ সাধারণ করে।
মূল উপলব্ধি: Hessian শুধু optimizer-এর হাতিয়ার নয়; সে বলে সমাধান একটি কি না, ডেটা কতটা তথ্য দিচ্ছে, আর কোন উদাহরণগুলো শেখায় সবচেয়ে বেশি ভূমিকা রাখছে।
অনুশীলন
হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। চাইলে অংশ ৪-এর num_grad দিয়ে যাচাই করুন।
-
পূর্ণ numerical: f(x) = xᵀAx + bᵀx, যেখানে A = [[১, ২], [০, ৩]] আর b = (১, −১). (ক) x = (১, ১)-এ gradient বের করুন। (খ) Hessian কত, আর f কি convex? (গ) কোন বিন্দুতে gradient শূন্য?
(ক) A + Aᵀ = [[২, ২], [২, ৬]]. ∇f = (A + Aᵀ)x + b = (২ + ২, ২ + ৬) + (১, −১) = (৫, ৭). (2Ax + b লিখলে আসত (৭, ৫): দুটি ঘরই ভুল।) (খ) Hessian = A + Aᵀ = [[২, ২], [২, ৬]], সব x-এ একই। Trace ৮, determinant ১২ − ৪ = ৮; eigenvalue ৪ ± √৮ ≈ ১.১৭ আর ৬.৮৩, দুটিই ধনাত্মক, তাই positive definite আর f strictly convex. (গ) (A + Aᵀ)x = −b: [[২, ২], [২, ৬]]x = (−১, ১). Determinant ৮; x = (১/৮)·[[৬, −২], [−২, ২]]·(−১, ১) = (১/৮)(−৮, ৪) = (−১, ০.৫). যাচাই: ২(−১) + ২(০.৫) = −১ আর ২(−১) + ৬(০.৫) = ১. Hessian positive definite বলে এটিই একমাত্র সর্বনিম্ন বিন্দু।
-
সমালোচনা ও নকশা: বাজারঘরের একজন সহকর্মী (বা একটি AI assistant) ridge নিজে লিখেছেন:
grad = X.T @ (X @ w - y) / n + alpha * w, gradient descent চালিয়েছেন, তারপর একই alpha দিয়ে scikit-learn-এরRidge(alpha=alpha)চালিয়ে দেখেছেন ওজন মেলে না। X-এ সব-1 কলামও রাখা আছে। কোথায় কোথায় অমিল, আর কীভাবে ঠিক করবেন?Objective আলাদা: এই gradient আসে (1/2n)‖Xw − y‖² + (alpha/2)‖w‖² থেকে। ২n দিয়ে গুণ করলে objective হয় ‖Xw − y‖² + n·alpha·‖w‖², অর্থাৎ scikit-learn-এর হিসাবে শাস্তি n·alpha. মেলাতে হলে scikit-learn-এ
alpha = n * alphaদিন, অথবা নিজের কোডেalpha / n. Intercept: সব-1 কলামের ওজনও শাস্তি পাচ্ছে, কিন্তু scikit-learn intercept-কে শাস্তি দেয় না; সব-1 কলাম বাদ দিয়ে X ও y centering করুন, বা ওই ঘরের শাস্তি শূন্য রাখুন। যাচাইয়ের নকশা: (১) objective-টি এক লাইনে লিখুন আর scikit-learn-এর docs-এর objective-এর সঙ্গে মেলান; (২) closed form (XᵀX + αI)w = Xᵀy দিয়ে উত্তর বের করুন, gradient descent-এর আগে; (৩) gradient checking চালান; (৪) feature standardize করুন, কারণ ridge-এর শাস্তি এককের ওপর নির্ভর করে; (৫) alpha বাছুন cross-validation-এ, test set ছুঁয়ে নয়। -
আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড কী ছাপবে, আর কোন vector-টি আসল gradient.
import numpy as np A = np.array([[0., 1.], [3., 0.]]) x = np.array([1., 2.]) f = lambda v: v @ A @ v print(f(x)) print(2 * A @ x, (A + A.T) @ x)8.0 [4. 6.] [8. 4.]xᵀAx = x1(০·x1 + ১·x2) + x2(৩·x1 + ০·x2) = ৪x1x2; x = (১, ২)-এ ৮. ঘরে ঘরে derivative: ∂f/∂x1 = ৪x2 = ৮, ∂f/∂x2 = ৪x1 = ৪. তাই আসল gradient (A + Aᵀ)x = [8. 4.]; 2Ax = [4. 6.] ভুল, কারণ A প্রতিসম নয়। NumPy float-কে “8.0” আর array-কে “[4. 6.]” আকারে ছাপে।
-
Jacobian derive করুন: Softmax s(z)i = ezi / ∑k ezk, অর্থাৎ ℝK → ℝK. দেখান এর Jacobian J = diag(p) − ppᵀ, যেখানে p = s(z). তারপর p = (০.৫, ০.৩, ০.২)-এ J লিখুন, আর বলুন প্রতিটি সারির যোগফল কত এবং কেন।
Quotient rule: i = j হলে ∂pi/∂zi = pi − pi² = pi(1 − pi); i ≠ j হলে ∂pi/∂zj = −pipj. একসঙ্গে J = diag(p) − ppᵀ, একটি প্রতিসম matrix. সংখ্যায়: কর্ণ ০.৫ − ০.২৫ = ০.২৫, ০.৩ − ০.০৯ = ০.২১, ০.২ − ০.০৪ = ০.১৬; বাইরে −০.১৫ (১,২), −০.১০ (১,৩), −০.০৬ (২,৩)। অর্থাৎ J = [[০.২৫, −০.১৫, −০.১০], [−০.১৫, ০.২১, −০.০৬], [−০.১০, −০.০৬, ০.১৬]]. প্রতিটি সারির যোগফল ০, কারণ সব zj-এ একই ধ্রুবক যোগ করলে softmax বদলায় না; অর্থাৎ J·(১, ১, ১) = 0. Cross-entropy-র সঙ্গে chain rule দিলে এই Jacobian থেকেই পরিচিত gradient p − y আসে (পর্ব ২৫-এ softmax).
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমLogistic regression-এ Newton-এর একটি ধাপের হিসাব সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- এখনকার w থেকে z = Xw আর p = σ(z) বের করা
- Gradient g = Xᵀ(p − y) হিসাব করা
- S = diag(p(1 − p)) দিয়ে Hessian H = XᵀSX বানানো
- সমীকরণ Hd = −g সমাধান করা (inverse না বানিয়ে)
- w ← w + d, তারপর loss কমেছে কি না দেখা
A একটি n × n matrix, প্রতিসম হওয়া জরুরি নয়। f(x) = xᵀAx-এর gradient কোনটি?
- 2Ax
- Ax
- (A + Aᵀ)x
- xᵀA
f: ℝ5 → ℝ3 একটি vector function, আর Jacobian-এর সারি i-তে fi-এর partial derivative-গুলো থাকে, অর্থাৎ numerator layout. Jacobian-এর আকার কত?
- 5 × 3
- 3 × 5
- 5 × 5
- 5 × 1
X আকার n × d, y দৈর্ঘ্য n. L(w) = ‖Xw − y‖² + α‖w‖² (α > 0)-এর সর্বনিম্ন বিন্দু কোন সমীকরণ মেনে চলে?
- (XᵀX + αI) w = Xᵀy
- XᵀX w = Xᵀy + αI
- (XXᵀ + αI) w = Xy
- XᵀX w = Xᵀy
Logistic regression-এ L(w) = ∑[log(1 + ezi) − yizi], z = Xw, p = σ(z). এর Hessian আর তার একটি বৈশিষ্ট্য কোনটি?
- XᵀX, সব w-এ একই
- XᵀSX, যা সাধারণত indefinite, তাই loss non-convex
- Xᵀ(p − y), যা শূন্য হলে সর্বনিম্ন
- XᵀSX, S = diag(p(1 − p)); positive semidefinite, তাই loss convex
একটি model-এর loss একটি সংখ্যা আর parameter ৫০ লাখ। পুরো gradient বের করতে কোন পদ্ধতি সাধারণত সবচেয়ে সস্তা?
- প্রতিটি parameter-এ central difference
- Forward-mode automatic differentiation
- Reverse-mode automatic differentiation (backpropagation)
- আগে পুরো Hessian বানিয়ে তা থেকে gradient বের করা
একটি মসৃণ, অ-quadratic loss-এ একটি বিন্দু থেকে ধাপের দৈর্ঘ্য t অর্ধেক করলে দ্বিতীয় ক্রমের Taylor approximation-এর ভুল সাধারণত প্রায় কত গুণ কমে?
- প্রায় ২ গুণ
- প্রায় ৮ গুণ
- প্রায় ৪ গুণ
- ভুল সবসময় শূন্য
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- আকার: scalar-এর gradient চলকের আকারের; f: ℝn → ℝm-এর Jacobian m × n; Hessian n × n, প্রতিসম, gradient-এর Jacobian.
- সূত্র: ∇(aᵀx) = a, ∇(xᵀAx) = (A + Aᵀ)x, ∇‖Ax − b‖² = 2Aᵀ(Ax − b), ∇W‖XW − Y‖F² = 2Xᵀ(XW − Y), ∇ log det S = S−ᵀ. Derive করার নিরাপদ পথ df = gᵀdx.
- Chain rule: Jg∘f = JgJf; scalar loss-এ ∇xL = Jrᵀ∇rL. Reverse-mode autodiff আর backpropagation এই vector-Jacobian product-এর শিকল।
- শূন্য gradient: least squares XᵀXw = Xᵀy, ridge (XᵀX + αI)w = Xᵀy (intercept শাস্তির বাইরে); logistic-এ g = Xᵀ(p − y), H = XᵀSX, Newton: Hd = −g.
- যাচাই: প্রতিটি নতুন সূত্র float64-এ central difference দিয়ে, অপ্রতিসম আর অ-বর্গাকার উদাহরণে; Taylor-এর ভুল প্রথম ক্রমে t², দ্বিতীয় ক্রমে t³ হারে কমে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ১২ · Convexity — কেন কিছু loss সহজ আর কিছু কঠিনপরবর্তী পর্বOptimization মডিউল শুরু: convex set ও function, κ আর Jensen.
- মেশিন লার্নিং · কোর্স হোম৭৭টি পর্ব১৪টি মডিউলের পুরো রোডম্যাপ; পর্ব ১২ (Convexity) এখান থেকে খুলবেন।
- AI-এর ভিত্তি · পর্ব ৩৪ · Derivative আর gradient descentআগের ভিত্তিDerivative, chain rule, finite difference-এর h আর gradient descent এক-দুই চলকে।
- AI-এর ভিত্তি · পর্ব ৫৮ · Linear regressionআগের ভিত্তিNormal equations, condition number আর কেন inv নয়, lstsq.
- AI-এর ভিত্তি · পর্ব ৫৯ · Logistic regressionআগের ভিত্তিSigmoid, log loss, gradient checking আর scikit-learn-এর সঙ্গে মিল।
- AI-এর ভিত্তি · পর্ব ৬৮ · Neural network-এর ধারণাআগের ভিত্তিBackpropagation হাতে, আর PyTorch-এর autograd-এর সঙ্গে মিলিয়ে দেখা।
- Data Structures & Algorithmsপাশের ট্র্যাকComplexity-র হিসাব, যা Newton-এর d³ আর autodiff-এর খরচ বুঝতে কাজে লাগে।