পাঠ ৫০ · ৫৭-এর মধ্যে · মডিউল ১১
Home / Courses / Numerical Methods / ফরওয়ার্ড ও ব্যাকওয়ার্ড এরর

ফরওয়ার্ড ও ব্যাকওয়ার্ড এরর অ্যানালাইসিস

Forward & Backward Error Analysis
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ফরওয়ার্ড এরর ও ব্যাকওয়ার্ড এররের সংজ্ঞা এবং এই দুইয়ের মধ্যে ধারণাগত পার্থক্য
  • কেন বাস্তব-বিশ্বের অ্যালগরিদম যাচাইয়ে ব্যাকওয়ার্ড এরর প্রায়ই বেশি ব্যবহারযোগ্য
  • একটি সত্যিকারের Newton-Raphson ডেমোতে উভয় এরর প্রতি ইটারেশনে গণনা করে পর্যবেক্ষণ করা
  • ফাংশনের ডেরিভেটিভ কীভাবে ফরওয়ার্ড ও ব্যাকওয়ার্ড এররের মধ্যে একটি আনুমানিক গাণিতিক সেতু তৈরি করে (কন্ডিশনিং-এর সাথে সংযোগ, M1/L04-এ বিস্তারিত)

১ · ফরওয়ার্ড এরর — "আমার উত্তর কতটা ভুল?"

কোনো সমস্যার প্রকৃত (কিন্তু হয়তো অজানা) সমাধান x* এবং আমাদের অ্যালগরিদমের কম্পিউটেড উত্তর x̂ হলে, ফরওয়ার্ড এররForward errorকম্পিউটেড উত্তর ও প্রকৃত উত্তরের মধ্যে সরাসরি দূরত্ব — |x̂ - x*|। হলো সবচেয়ে স্বজ্ঞাত (intuitive) এরর ধারণা — সরাসরি দূরত্ব:

$$ e_{\text{forward}} = |\hat{x} - x^*| $$

এটি ঠিক সেই প্রশ্নের উত্তর দেয় যা আমরা সবচেয়ে বেশি জানতে চাই — "আমার উত্তর কতটা সঠিক?" — কিন্তু একটি বড় সমস্যা আছে: এটি গণনা করতে x* জানা লাগে, যা বাস্তবে প্রায় কখনোই সরাসরি জানা থাকে না (যদি জানা থাকত, তাহলে সমস্যাটা সমাধান করারই দরকার হতো না!)।

২ · ব্যাকওয়ার্ড এরর — "আমি আসলে কোন সমস্যাটা সমাধান করেছি?"

ব্যাকওয়ার্ড এররBackward errorকম্পিউটেড উত্তর x̂-কে ঠিক/নিখুঁত সমাধান বানাতে মূল সমস্যাটিকে কতটা পার্টার্ব (সামান্য পরিবর্তন) করতে হতো, তার পরিমাপ। ভিন্ন প্রশ্ন করে — "আমার কম্পিউটেড উত্তর x̂-টা যদি সঠিক ধরে নিই, তাহলে সেটা ঠিক কোন সমীকরণের নিখুঁত সমাধান?" রুট-ফাইন্ডিং সমস্যা f(x) = 0-এর ক্ষেত্রে, যদি f(x̂) ≠ 0 হয়, তাহলে x̂ আসলে f(x) = δ সমীকরণের নিখুঁত সমাধান, যেখানে δ = f(x̂)। তাই:

$$ e_{\text{backward}} = |f(\hat{x})| $$

এটাকে বলা যায় "মূল সমস্যাটাকে কতটা নাড়ালে আমার উত্তর নিখুঁত হয়ে যেত।" এর সবচেয়ে বড় সুবিধা — f(x̂) গণনা করতে x* জানার দরকার নেই, শুধু x̂ ফাংশনে বসিয়ে দিলেই হয়। এই কারণেই বাস্তব সংখ্যাসূচক সফটওয়্যার প্রায়ই ব্যাকওয়ার্ড এরর (residual) দেখেই "কনভার্জ করেছে" বলে সিদ্ধান্ত নেয়।

ফরওয়ার্ড এরর
"উত্তর কতটা ভুল" — সরাসরি স্বজ্ঞাত, কিন্তু x* লাগে যা সাধারণত অজানা।
ব্যাকওয়ার্ড এরর
"কোন সমস্যাটা ঠিক সমাধান হলো" — x* ছাড়াই সরাসরি গণনাযোগ্য (residual)।

৩ · একটি সত্যিকারের ডেমো — Newton-Raphson দিয়ে x³ - x - 2 = 0

নিচের কোড সেলে f(x) = x³ - x - 2 সমীকরণটি Newton-Raphson (M2/L07) দিয়ে সমাধান করা হয়েছে x₀ = 1.2 থেকে শুরু করে। প্রথমে ১০০টি ইটারেশন চালিয়ে একটি অতি-নির্ভুল "রেফারেন্স" মূল বের করা হয়েছে (এটাকেই আমরা x* ধরে নিচ্ছি), তারপর মাত্র কয়েকটি ইটারেশন চালিয়ে প্রতি ধাপে উভয় এরর — ব্যাকওয়ার্ড (|f(x)|) ও ফরওয়ার্ড (|x - x*|) — সত্যিই গণনা করে প্রিন্ট করা হয়েছে।

Python
import math

def f(x):
    return x**3 - x - 2

def fprime(x):
    return 3 * x**2 - 1

# রেফারেন্স "প্রকৃত" মূল বের করতে ১০০টি Newton ইটারেশন চালাই (উচ্চ নির্ভুলতা)
x = 1.5
for _ in range(100):
    x = x - f(x) / fprime(x)
true_root = x
print(f"রেফারেন্স মূল x* (১০০ ইটারেশন পর) = {true_root:.10f}")
print()

# এখন মাত্র কয়েকটি ইটারেশন চালিয়ে প্রতি ধাপে backward ও forward error দুটোই মাপি
x0 = 1.2
x = x0
print(f"{'ইটার':>4} | {'x (বর্তমান অনুমান)':>20} | {'backward |f(x)|':>18} | {'forward |x-x*|':>18}")
for i in range(1, 7):
    fx = f(x)
    backward_err = abs(fx)
    forward_err = abs(x - true_root)
    print(f"{i:>4} | {x:>20.10f} | {backward_err:>18.10f} | {forward_err:>18.10f}")
    x = x - fx / fprime(x)

    
রেফারেন্স মূল x* = 1.5213797068। প্রথম ইটারেশনে (x = 1.2) backward error 1.472 আর forward error 0.3214 — সম্পূর্ণ ভিন্ন মাত্রার সংখ্যা। ৩ নং ইটারেশনে (x = 1.5314552593) backward error 0.0603513138, কিন্তু forward error মাত্র 0.0100755525 — লক্ষ্য করুন backward error, forward error-এর প্রায় ৬ গুণ! ৬ নং ইটারেশনে দুটো এররই 0-এ নেমে আসে — Newton-Raphson-এর কোয়াড্রেটিক কনভারজেন্সের কারণে (M2/L07) মাত্র কয়েক ধাপেই ডাবল-প্রিসিশন ফ্লোটের সীমায় পৌঁছে যায় (L02-এ বিস্তারিত)।

৪ · ফরওয়ার্ড ও ব্যাকওয়ার্ড এররের সম্পর্ক — কন্ডিশনিং

লক্ষ্য করার মতো বিষয়, backward error আর forward error সরাসরি সমান নয় কেন? উত্তরটা ফাংশনের ঢাল (derivative)-এর সাথে সম্পর্কিত। যদি রুটের কাছাকাছি ফাংশনটি খুব খাড়া হয় (|f'(x*)| বড়), তাহলে ছোট একটা backward error-ও ছোট forward error-এ পরিণত হয় — কারণ x-অক্ষে সামান্য নড়াচড়াতেই f(x)-এর মান অনেকখানি বদলে যায়। বিপরীতভাবে, ফাংশনটি যদি রুটের কাছে প্রায় সমতল হয় (|f'(x*)| ছোট), একই backward error অনেক বড় forward error-এ পরিণত হতে পারে — এটাই ইল-কন্ডিশনড (ill-conditioned) রুট (M1/L04-এ conditioning নিয়ে বিস্তারিত)। প্রথম মাত্রার (linear) আনুমানিকতায়:

$$ e_{\text{forward}} \approx \frac{e_{\text{backward}}}{|f'(x^*)|} $$

নিচের কোড সেলে এই সম্পর্কটা সত্যিকারের সংখ্যা দিয়ে যাচাই করা হয়েছে।

Python
import math

def f(x):
    return x**3 - x - 2

def fprime(x):
    return 3 * x**2 - 1

x = 1.5
for _ in range(100):
    x = x - f(x) / fprime(x)
true_root = x

# ২টি Newton ইটারেশনের পর আমরা যেখানে পৌঁছাই
x0 = 1.2
x = x0
for _ in range(2):
    x = x - f(x) / fprime(x)
computed = x

backward_err = abs(f(computed))
forward_err = abs(computed - true_root)
fprime_at_root = fprime(true_root)
predicted_forward = backward_err / abs(fprime_at_root)

print(f"২ ইটারেশন পর computed x           = {computed:.10f}")
print(f"backward error  |f(computed)|      = {backward_err:.10f}")
print(f"প্রকৃত forward error |computed-x*|  = {forward_err:.10f}")
print(f"f'(x*)                             = {fprime_at_root:.10f}")
print(f"পূর্বাভাসিত forward = backward/|f'(x*)| = {predicted_forward:.10f}")

    
প্রকৃত forward error 0.0100755525, আর backward/|f'(x*)| সূত্র দিয়ে পূর্বাভাসিত মান 0.0101536776 — দুটো প্রায় হুবহু কাছাকাছি (পার্থক্য মাত্র ~0.008-এর মধ্যে)। এই আনুমানিক সূত্রটাই matrix condition number-এর ধারণার (M3/L14) মূল ভিত্তি — একই backward error, ভিন্ন কন্ডিশনিং-এর সমস্যায়, সম্পূর্ণ ভিন্ন মাত্রার forward error তৈরি করতে পারে।
মূল কথা · Key takeaway

ফরওয়ার্ড এরর বলে "উত্তর কতটা ভুল," ব্যাকওয়ার্ড এরর বলে "কোন সমস্যাটা ঠিক সমাধান হলো" — আর এই দুইয়ের মধ্যে সেতু হলো সমস্যার সেনসিটিভিটি/কন্ডিশনিং। বাস্তব সফটওয়্যারে যেহেতু x* সাধারণত অজানা, ব্যাকওয়ার্ড এরর (residual) হলো সবচেয়ে ব্যবহারযোগ্য, সরাসরি-গণনাযোগ্য পরিমাপ — পরের পাঠ (L51) এই সেনসিটিভিটির ধারণাকে মন্টে কার্লো সিমুলেশন দিয়ে আরও সাধারণভাবে প্রসারিত করবে।

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

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

প্র ০১ উপরের ডেমোতে ৩ নং ইটারেশনে backward error (0.0603513138), forward error (0.0100755525)-এর প্রায় ৬ গুণ ছিল। এটা কেন ঘটল?

কারণ রুটের কাছে f'(x*) ≈ 5.9438 — অর্থাৎ ফাংশনটি বেশ খাড়া। যেহেতু e_forward ≈ e_backward / |f'(x*)|, আর |f'(x*)| > 1, তাই forward error backward error-এর চেয়ে ছোট হয়ে যায়। যদি ফাংশনটি রুটের কাছে সমতল হতো (|f'(x*)| < 1), সম্পর্কটা উল্টো হতো — forward error backward error-এর চেয়ে বড় হতো।

প্র ০২ ব্যাকওয়ার্ড এরর খুব ছোট হলেই কি নিশ্চিতভাবে ধরে নেওয়া যায় যে forward error-ও ছোট?

না, নিশ্চিতভাবে নয়। সম্পর্কটা নির্ভর করে |f'(x*)|-এর উপর — যদি রুটের কাছে ফাংশনটি প্রায় সমতল হয় (|f'(x*)| খুবই ছোট, যেমন কোনো রুট যেখানে ফাংশন রুটকে "স্পর্শ" করে যায়), তাহলে একটি অতি-ছোট backward error-ও বিশাল forward error-এ পরিণত হতে পারে। এটাই একটি ইল-কন্ডিশনড রুট-ফাইন্ডিং সমস্যার লক্ষণ (M1/L04)।

প্র ০৩ বাস্তব সংখ্যাসূচক সফটওয়্যার (যেমন লিনিয়ার-সিস্টেম সলভার) প্রায়ই "কনভার্জ করেছে" বলে সিদ্ধান্ত নেয় শুধু residual (backward error) দেখে, forward error নয় কেন?

কারণ forward error গণনা করতে প্রকৃত উত্তর x* জানা লাগে — যা সমাধান করার আগেই জানা থাকলে সমস্যাটা সমাধান করারই দরকার হতো না। কিন্তু backward error (|f(x̂)| বা |Ax̂ - b|) সবসময় সরাসরি, সহজে গণনাযোগ্য — তাই এটাই ব্যবহারিক থামার শর্ত (stopping criterion) হিসেবে ব্যবহৃত হয়।

অনুশীলন

  1. চিন্তা করুন: উপরের প্রথম কোড সেলে দেখা গেছে Newton-Raphson কোয়াড্রেটিক্যালি কনভার্জ করে — প্রতি ইটারেশনে forward error মোটামুটি বর্গ (square) হয়ে যায়। ৫ নং ইটারেশনের forward error 0.0000000046 হলে, যদি আরও একটি ইটারেশন চালানো হতো, নতুন forward error মোটামুটি কত মাত্রার হবে বলে আপনার ধারণা?

    কোয়াড্রেটিক কনভারজেন্স মানে নতুন error ≈ পুরনো error² (একটি ধ্রুবক গুণক সহ) — তাই (0.0000000046)² ≈ 2 × 10⁻¹⁷ মাত্রার, যা ইতিমধ্যে ডাবল-প্রিসিশন ফ্লোটের নির্ভুলতা সীমার (মেশিন এপসিলন, L02-এ বিস্তারিত) কাছাকাছি বা তার চেয়ে ছোট — তাই ব্যবহারিকভাবে error 0-এ নেমে যাবে।

  2. পরীক্ষা করুন: উপরের প্রথম কোড সেলে for i in range(1, 7):-কে for i in range(1, 8):-এ পরিবর্তন করে Run চেপে দেখুন ৭ নং ইটারেশনে backward ও forward error সত্যিই কী মান দেখায় — আপনার অনুমান মিলল কিনা যাচাই করুন।

    ৬ নং ইটারেশনেই x ডাবল-প্রিসিশন ফ্লোটে সম্পূর্ণ নির্ভুল রেফারেন্স মূলের সমান হয়ে যায় (backward ও forward error উভয়ই 0.0000000000) — তাই ৭ নং ইটারেশনেও একই ফলাফল দেখাবে। এটাই দেখায় floating-point নির্ভুলতার একটি বাস্তব সীমা আছে — এরপর আর কোনো ইটারেশন প্রকৃত উন্নতি আনতে পারে না, যতই "কোয়াড্রেটিক কনভারজেন্স" থিওরিটিক্যালি প্রতিশ্রুতি দিক না কেন।

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

আগের পাঠ
প্যারালাল ও ভেক্টরাইজড নিউমেরিক্যাল কম্পিউটিং