এই পর্বে যা শিখবেন
- Notebook, kernel আর server কে কী করে, আর output কেন ফাইলেই জমা থাকে
- venv-এর ভেতরে JupyterLab বসিয়ে নিজের কম্পিউটারে, নিজের port-এ নিরাপদে চালানো আর ঠিকভাবে বন্ধ করা
- Execution count পড়ে বলা যে notebook ওপর থেকে নিচে চলেছিল কি না, আর লুকানো state ধরার উপায়
%time,%timeit,%who,%matplotlib,%pip-এর সঠিক ব্যবহার.ipynb-এর JSON গঠন, diff কেন কঠিন, আর notebook থেকে কখন কোড.pyফাইলে সরাবেন
১ · Notebook কী — cell, kernel আর ফাইলে জমা output
পর্ব ৭ (AI-এর জন্য Python — install, uv, venv আর package)-এ আপনি নিজের কম্পিউটারে Python আর একটি আলাদা পরিবেশ তৈরি করেছেন। এবার সেই পরিবেশে AI আর ডেটার কাজের সবচেয়ে পরিচিত হাতিয়ারটি দেখব: Jupyter notebook. ডেটা বিজ্ঞানী, গবেষক আর ছাত্রছাত্রীদের বড় একটি অংশ প্রথমে notebook-এই ডেটা খোলেন, chart আঁকেন আর ছোট model চালিয়ে দেখেন।
একটি সাধারণ Python script (.py ফাইল) চালালে পুরো ফাইল ওপর থেকে নিচে একবারে চলে, তারপর প্রোগ্রাম শেষ, মেমরিও মুছে যায়। Notebook আলাদা। এখানে কোড ছোট ছোট cell-এ ভাগ করা থাকে। আপনি একটি cell চালান, ফল দেখেন, ভাবেন, তারপর পরের cell লেখেন। মাঝের সব মান মনে রাখে পেছনে চলা একটি আলাদা প্রক্রিয়া, যার নাম kernel. আর প্রতিটি cell-এর output, যেমন ছাপা লেখা, টেবিল বা chart-এর ছবি, ফাইলের ভেতরেই জমা হয়। তাই পরে ফাইল খুললে কোড না চালিয়েও আগের ফল দেখা যায়।
চায়ের দোকানের হিসাবের খাতার কথা ভাবুন। দোকানি প্রতিটি লেনদেন আলাদা লাইনে লেখেন, আর মাথায় রাখেন “এখন ক্যাশে কত আছে।” খাতার পাতা হলো notebook-এর cell, আর দোকানির মাথা হলো kernel. সমস্যা হয় তখন, যখন দোকানি একটি লাইন কেটে দেন কিন্তু মাথার হিসাব থেকে সেটি বাদ দিতে ভোলেন, বা একই লেনদেন দুবার যোগ করেন। খাতা দেখে অন্য কেউ হিসাব মেলাতে গেলে মিলবে না। এই পর্বের বড় অংশ এই সমস্যা নিয়েই।
Notebook-এর তিন অংশ
Browser UI, Jupyter server, kernel process and the .ipynb file
ছবির সবচেয়ে গুরুত্বপূর্ণ কথাটি নিচের লাইনে: kernel-এর মেমরি আর ফাইল দুটি আলাদা জিনিস। ফাইলে যা লেখা আছে, kernel-এ ঠিক সেটিই আছে, এমন কোনো নিয়ম নেই। একটি cell মুছে ফেললে ফাইল থেকে কোড চলে যায়, কিন্তু সেই cell যে variable বানিয়েছিল, তা kernel-এ থেকে যায়। আর kernel যে ভাষার, notebook সেই ভাষার। Python-এর kernel-এর নাম ipykernel, যা IPython-এর ওপর তৈরি; R, Julia এমনকি অন্য অনেক ভাষার kernel-ও আছে।
২০০১ সালে পদার্থবিজ্ঞানের ছাত্র Fernando Pérez Python-এর জন্য একটি উন্নত interactive shell শুরু করেন, নাম IPython. প্রায় দশ বছর পরে, ২০১১ সালের দিকে IPython-এর সঙ্গে ব্রাউজারে চলা notebook যোগ হয়। ২০১৪ সালে ভাষা-নিরপেক্ষ অংশগুলো আলাদা করে Project Jupyter নামে নতুন প্রকল্প শুরু হয়; নামটি সাধারণত Julia, Python আর R, এই তিন ভাষার ইঙ্গিত হিসেবে ব্যাখ্যা করা হয়। ধারণাটির শিকড় আরও পুরোনো: Donald Knuth ১৯৮৪ সালে literate programming প্রস্তাব করেন, আর Mathematica-র মতো গাণিতিক সফটওয়্যার আশির দশকের শেষ দিকেই notebook ধাঁচের ইন্টারফেস দিত। বর্তমান প্রধান ইন্টারফেস JupyterLab; পুরোনো “Classic Notebook” ইন্টারফেসের নতুন সংস্করণ (Notebook 7) এখন JupyterLab-এর অংশ দিয়েই তৈরি বলে প্রকল্পের ঘোষণায় বলা হয়েছে।
২ · নিজের কম্পিউটারে JupyterLab — venv, 127.0.0.1 আর নিজের port
JupyterLab একটি Python package. তাই এটি বসাবেন প্রকল্পের নিজস্ব virtual environment-এ, সিস্টেমের Python-এ নয়। venv কেন আর কীভাবে, তার পুরো ব্যাখ্যা Python কোর্সের pip আর venv পর্বে আছে; এখানে শুধু Jupyter-এর জন্য যা লাগে। এই পর্বের জন্য একটি ছোট প্রকল্প সত্যিই বানিয়ে চালানো হয়েছে। নিচের আউটপুটে প্রকল্পের ফোল্ডারকে নিরপেক্ষ নাম ~/bazarghar-ai দেওয়া হয়েছে, আর গোপন token মুছে <TOKEN> লেখা হয়েছে।
cd ~/bazarghar-ai
python -m venv .venv
.venv\Scripts\activate # Linux/macOS: source .venv/bin/activate
python -m pip install jupyterlab nbstripout jupytext numpy matplotlib
jupyter --versionসত্যিকারের run, Windows-এ Python 3.14.4 দিয়ে, লেখার সময়ের version (এগুলো দ্রুত বদলায়):
Selected Jupyter core packages...
IPython : 9.18.1
ipykernel : 7.4.0
jupyter_client : 8.10.0
jupyter_core : 5.9.1
jupyter_server : 2.21.1
jupyterlab : 4.6.5
nbclient : 0.11.0
nbconvert : 7.17.2
nbformat : 5.11.1
notebook : not installed
…
খেয়াল করুন, notebook : not installed. আমরা শুধু JupyterLab বসিয়েছি; পুরোনো ধাঁচের ইন্টারফেস চাইলে pip install notebook দিয়ে আলাদা বসাতে হয়, তারপর jupyter notebook চালালে খোলে। দুটোই একই kernel আর একই .ipynb ফাইল ব্যবহার করে।
চালু করা: শুধু নিজের কম্পিউটারে, একটি অপরিচিত port-এ
Jupyter server আসলে একটি ছোট web server, আর এর মাধ্যমে যে কেউ আপনার কম্পিউটারে কোড চালাতে পারে। তাই দুটি সাবধানতা: server শুনবে শুধু 127.0.0.1-এ, অর্থাৎ নিজের কম্পিউটার থেকেই খোলা যাবে; আর নিজে একটি port বেছে দেবেন, যাতে অন্য কোনো প্রোগ্রামের সঙ্গে সংঘাত না হয়। এখানে নেওয়া হয়েছে 18931.
$p = Start-Process -PassThru -WindowStyle Hidden `
-FilePath .venv\Scripts\python.exe `
-ArgumentList "-m","jupyterlab","--no-browser","--ip=127.0.0.1","--port=18931" `
-RedirectStandardError server.log
"PID=$($p.Id)"Linux বা macOS-এ সাধারণত শুধু jupyter lab --no-browser --ip=127.0.0.1 --port=18931 লিখলেই হয়; terminal খোলা থাকে আর Ctrl+C দিয়ে বন্ধ করা যায়। এখানে পেছনে চালানো হয়েছে, যাতে process ID হাতে থাকে। সত্যিকারের run-এর log (কিছু লাইন বাদ):
PID=29492
[I 2026-11-04 19:20:55.360 ServerApp] jupyterlab | extension was successfully loaded.
[I 2026-11-04 19:20:55.361 ServerApp] jupyterlab_jupytext | extension was successfully loaded.
[I 2026-11-04 19:20:55.362 ServerApp] Serving notebooks from local directory: ~/bazarghar-ai
[I 2026-11-04 19:20:55.362 ServerApp] Jupyter Server 2.21.1 is running at:
[I 2026-11-04 19:20:55.362 ServerApp] http://127.0.0.1:18931/lab?token=<TOKEN>
[I 2026-11-04 19:20:55.362 ServerApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
…
file:/…/jupyter/runtime/jpserver-5316-open.html
লগের শেষ লাইনে একটি সংখ্যা আছে: jpserver-5316. Windows-এ venv-এর python.exe একটি ছোট launcher, যা আসল Python-কে চালু করে। তাই Start-Process দিয়েছে launcher-এর ID 29492, আর আসল server চলছে তার “সন্তান” প্রক্রিয়া 5316-এ। দুটোই আমাদের নিজের চালু করা। Server বেঁচে আছে কি না, যাচাই আর তারপর বন্ধ:
PS> (Invoke-WebRequest -UseBasicParsing http://127.0.0.1:18931/api).Content
{"version": "2.21.1"}
$ curl -s -o /dev/null -w "%{http_code}
" http://127.0.0.1:18931/api/contents
403
PS> Stop-Process -Id 5316; Stop-Process -Id 29492
PS> (Invoke-WebRequest -UseBasicParsing http://127.0.0.1:18931/api).Content
Unable to connect to the remote server
তিনটি জিনিস লক্ষ করুন। /api শুধু version জানায়, কিন্তু ফাইলের তালিকা (/api/contents) চাইলে token ছাড়া উত্তর 403, মানে “অনুমতি নেই”। বন্ধ করা হয়েছে নির্দিষ্ট ID দিয়ে, নাম দিয়ে নয়; python.exe নামের সব প্রক্রিয়া বন্ধ করলে আপনার অন্য কাজ, এমনকি অন্য প্রোগ্রামও মারা যেত। আর বন্ধের পর একই ঠিকানা আর সাড়া দেয় না। Jupyter-এর নিজের নরম উপায়ও আছে: jupyter server stop 18931, যা kernel-গুলো গুছিয়ে বন্ধ করে।
Log-এ আর browser-এর ঠিকানায় যে লম্বা token=… দেখবেন, সেটি একটি access token, অর্থাৎ আপনার কম্পিউটারে কোড চালানোর চাবি। Screenshot, ফোরামে প্রশ্ন বা AI chat-এ সেটি কখনো পেস্ট করবেন না। আর --ip=0.0.0.0 দিয়ে সব নেটওয়ার্কে খুলে দেওয়ার পরামর্শ কোথাও পেলে আগে বুঝুন কেন দরকার; একই Wi-Fi-এর অন্য কেউ তখন অন্তত দরজায় টোকা দিতে পারবেন।
৩ · Code cell, Markdown cell আর execution counter
Notebook-এ মূলত দুই ধরনের cell. Code cell-এর লেখা kernel-এ যায় আর চলে। Markdown cell-এ থাকে ব্যাখ্যা: শিরোনাম (#), তালিকা (-), মোটা লেখা (**…**), এমনকি সূত্র। ভালো notebook পড়লে মনে হয় একটি ছোট রিপোর্ট, যেখানে প্রতিটি কোডের আগে লেখা থাকে “কেন”।
সবচেয়ে বেশি ব্যবহৃত চাবি দুটি: Shift+Enter দিয়ে cell চালিয়ে পরের cell-এ যাওয়া, আর Esc চেপে “command mode”-এ গিয়ে A বা B দিয়ে ওপরে বা নিচে নতুন cell, M দিয়ে cell-কে Markdown আর Y দিয়ে আবার code বানানো। প্রতিটি code cell চালালে তার বাঁ পাশে [n] আকারে একটি সংখ্যা বসে, যার নাম execution count. চলার সময় সেখানে [*] দেখায়।
In [3] মানে “kernel চালু হওয়ার পর এটি তৃতীয়বার কোনো কোড চালানো।” এটি cell-এর অবস্থান নয়। Markdown cell চালালে সংখ্যা বাড়ে না। আর kernel restart করলে গোনা আবার ১ থেকে শুরু হয়।
নিচের ডায়াগ্রামে বাজারঘরের চার দিনের বিক্রির (হাজার টাকায়) ছোট একটি notebook ওপর থেকে নিচে চালানো হচ্ছে। “পরে” চেপে দেখুন kernel-এর মেমরি আর counter কীভাবে বদলায়।
md# Bazarghar weekly salesc1import numpy as npc1sales = np.array([120, 95, 140, 80])c2sales = sales * 1.1 # add 10% VATc3print("total:", round(float(sales.sum()), 2))
| শেষ In [n] | |
| মেমরিতে নাম | |
| sales |
- Kernel চালু হলো। Notebook খুললে বা নতুন kernel বাছলে একটি নতুন Python প্রক্রিয়া শুরু হয়। মেমরি ফাঁকা, এখনো কোনো
In [n]নেই। - Markdown cell. শিরোনামটি সাজানো লেখা হয়ে দেখাল। এটি kernel-এ যায়নি, তাই counter এখনো ফাঁকা, মেমরিও ফাঁকা।
- প্রথম code cell,
In [1]. kernelnpআরsalesনাম দুটি মেমরিতে রাখল। চার দিনের বিক্রির যোগফল 120 + 95 + 140 + 80 = 435. - দ্বিতীয় cell,
In [2]. প্রতিটি মান 1.1 দিয়ে গুণ: 120 × 1.1 = 132, 95 × 1.1 = 104.5, 140 × 1.1 = 154, 80 × 1.1 = 88. খেয়াল করুন, এই cell আগের মান বদলে দেয়; এটিই পরে বিপদ ডাকবে। - তৃতীয় cell,
In [3]. যোগফল 132 + 104.5 + 154 + 88 = 478.5, অর্থাৎ 435 × 1.1. ওপর থেকে নিচে একবার করে চললে সংখ্যাগুলো 1, 2, 3, একটানা। এটাই সুস্থ notebook-এর চেহারা।
কোনো cell-এর শেষ লাইন যদি একটি মান হয় (যেমন শুধু sales), notebook সেটি print ছাড়াই Out [n] হিসেবে দেখায়। pandas-এর DataFrame এভাবে সুন্দর টেবিল হয়ে আসে, যা পর্ব ২০ (pandas-এর শুরু)-এ অনেক দেখবেন। শেষ লাইনের পরে ; দিলে এই স্বয়ংক্রিয় দেখানো বন্ধ হয়।
৪ · লুকানো state — ক্রম ভাঙলে ফল বদলায়
এবার সেই বিপদ। ধরুন আপনি ওপরের notebook-এ কাজ করছেন। দ্বিতীয় cell চালানোর পর ওপরে স্ক্রল করে কিছু দেখলেন, তারপর ভুলে দ্বিতীয় cell-এ আবার Shift+Enter. VAT দুবার যোগ হলো। তৃতীয় cell এখন যে total দেখাবে, ফাইলের কোড ওপর থেকে নিচে পড়ে কেউ তা আর পাবেন না। Kernel-এর এমন মানকে বলে hidden state বা লুকানো state.
c1sales = np.array([120, 95, 140, 80])c2sales = sales * 1.1 # add 10% VATc3print("total:", round(float(sales.sum()), 2))💾Ctrl+S: ফাইলে save
| ঘর ১-এর পাশে | |
| ঘর ২-এর পাশে | |
| ঘর ৩-এর পাশে | |
| VAT কতবার | |
| sales |
- ঘর ১,
In [1]. চার দিনের বিক্রি মেমরিতে এলো, যোগফল 435. - ঘর ২,
In [2]. একবার VAT, প্রতিটি মান 1.1 গুণ। এ পর্যন্ত সব ঠিক। - ঘর ২ আবার,
In [3]. ভুল করে দ্বিতীয়বার চালানো। এবার গুণ হলো আগের ফলের ওপর: 132 × 1.1 = 145.2, 104.5 × 1.1 = 114.95, 154 × 1.1 = 169.4, 88 × 1.1 = 96.8. পর্দায় ঘর ২-এর পাশে শুধু[3]দেখাচ্ছে; আগের[2]মুছে গেছে, তাই দুবার চলার কোনো চিহ্ন চোখে পড়ে না। - ঘর ৩,
In [4]. total = 145.2 + 114.95 + 169.4 + 96.8 = 526.35, অর্থাৎ 435 × 1.1 × 1.1 = 435 × 1.21. Markdown-এ লেখা “VAT added once”, অথচ সংখ্যায় VAT দুবার। - Save. ফাইলে জমা হলো counter
[1] [3] [4]আর output526.35. যিনি ফাইলটি পাবেন, তিনি ওপর থেকে নিচে চালালে পাবেন 478.5. দুটি সংখ্যার কোনোটিই “ভুল টাইপ” নয়; ভুলটা ক্রমে। একমাত্র সূত্র: counter-এ ২ নেই, ফাঁক আছে।
নিজে দেখুন: পাতার দুটি “cell”, একই মেমরি
নিচের দুটি block এই পাতায় notebook-এর cell-এর মতো একই মেমরি ভাগ করে (পাতার ভাষায় data-shared). একটি নিয়ম জেনে রাখুন: এই পাতা shared block-গুলো পাতার ক্রমে চালায়। প্রথমেই দ্বিতীয় block চাপলে পাতা আগে নিজে থেকে প্রথমটি চালিয়ে নেয়, তারপর দ্বিতীয়টি। কিন্তু একবার চলার পর আপনি যেকোনো block যতবার খুশি, যে ক্রমে খুশি চালাতে পারেন, ঠিক notebook-এর মতো। এই পাতার Python চলে আপনার ব্রাউজারেই, Pyodide দিয়ে; প্রথম run-এ NumPy নামাতে কয়েক সেকেন্ড লাগতে পারে।
# ঘর ১: চার দিনের বিক্রি (হাজার টাকায়)
import numpy as np
sales = np.array([120, 95, 140, 80])
vat_runs = 0 # ঘর ২ কতবার চলল, তার হিসাব
print("ঘর ১ চলল | sales =", sales, "| total =", sales.sum())আসল run-এর আউটপুট: ঘর ১ চলল | sales = [120 95 140 80] | total = 435. মেমরিতে এখন তিনটি নাম: np, sales আর একটি গণনাকারী vat_runs. আসল notebook-এ এমন গণনাকারী থাকে না; এখানে রাখা হয়েছে, যাতে লুকানো state চোখে দেখা যায়।
# ঘর ২: ১০% VAT যোগ (একবারই চলার কথা)
sales = sales * 1.1
vat_runs += 1
print(f"ঘর ২ চলল, এ পর্যন্ত {vat_runs} বার")
print("sales =", np.round(sales, 2))
print("total =", round(float(sales.sum()), 2))
if vat_runs > 1:
print("সাবধান: VAT", vat_runs, "বার যোগ হয়েছে!")প্রথমবার চালালে আসল run-এর আউটপুট sales = [132. 104.5 154. 88. ] আর total = 478.5. এবার এই block-টিই আবার চালান: আসবে ঘর ২ চলল, এ পর্যন্ত 2 বার, total = 526.35 আর সতর্কবার্তা। তৃতীয়বার চালালে total = 578.99. এরপর ঘর ১ একবার চালিয়ে তারপর ঘর ২ চালান: আবার 478.5. কোড একটুও বদলায়নি; বদলেছে শুধু চালানোর ক্রম আর সংখ্যা। sales = sales * 1.1-এর মতো একই নামে নতুন মান বসানো cell-ই এই সমস্যার সবচেয়ে বড় উৎস। নিরাপদ রূপ: নতুন নাম দিন, যেমন sales_vat = sales * 1.1; তখন দুবার চালালেও একই ফল।
মুছে ফেলা cell: আরও চুপচাপ একটি ফাঁদ
দ্বিতীয় ধরনের লুকানো state আরও বিপজ্জনক। ধরুন একটি notebook-এ ছাড়ের হার rate = 0.2 একটি আলাদা cell-এ ছিল। সব চালানোর পর আপনি cell-টি “অপ্রয়োজনীয়” ভেবে মুছে দিলেন আর save করলেন। ফাইলে এখন rate কোথাও নেই, অথচ kernel-এ আছে, আর ফলও ঠিকঠাক দেখাচ্ছে। সত্যিকারের run-এ এমন একটি discount.ipynb বানিয়ে নতুন kernel-এ চালানো হয়েছে:
$ jupyter nbconvert --to notebook --execute discount.ipynb --output discount_clean.ipynb
[NbConvertApp] Converting notebook discount.ipynb to notebook
…
nbclient.exceptions.CellExecutionError: An error occurred while executing the following cell:
------------------
final = [p * (1 - rate) for p in prices]
print(final)
------------------
NameError Traceback (most recent call last)
Cell In[2], line 1
----> 1 final = [p * (1 - rate) for p in prices]
NameError: name 'rate' is not defined
সংরক্ষিত ফাইলে এই cell-এর output ছিল [400.0, 960.0, 240.0] আর counter [1], [3]; মাঝের [2] ছিল মুছে ফেলা cell-এর। নতুন kernel-এ সেটি NameError. আউটপুটের রং-নির্দেশক চিহ্ন মুছে দেখানো হয়েছে। Error হলে অন্তত চোখে পড়ে। আগের VAT-এর উদাহরণে কোনো error নেই, শুধু ভুল সংখ্যা; সেটিই বেশি ভয়ের।
AI আর ডেটার কাজে লুকানো state সাধারণত এভাবে আসে: ডেটা একবার পরিষ্কার করে আবার একই cell চালানো (দুবার normalize), একটি cell-এ model fit করে পরে ডেটা বদলে আগের model দিয়েই মাপা, বা test ডেটা দেখে কোনো সংখ্যা ঠিক করে সেই cell মুছে ফেলা। শেষটি data leakage-এর নীরব রূপ; পর্ব ৫৬ (Train, validation আর test)-এ বিস্তারিত আসবে।
৫ · Restart & Run All — সততার পরীক্ষা
লুকানো state ধরার সবচেয়ে সহজ আর নির্ভরযোগ্য উপায় একটি: Restart & Run All. JupyterLab-এ মেনু Kernel → Restart Kernel and Run All Cells…. এতে kernel-এর মেমরি পুরো মুছে যায়, তারপর প্রতিটি cell ওপর থেকে নিচে ঠিক একবার চলে। যা পাবেন, সেটাই notebook-এর সৎ ফল।
↻Kernel → Restartc1sales = np.array([120, 95, 140, 80])c2sales = sales * 1.1 # add 10% VATc3print("total:", round(float(sales.sum()), 2))
| মেমরি | |
| counter | |
| VAT কতবার | |
| আগে জমা ছিল |
- Restart. Kernel প্রক্রিয়া বন্ধ হয়ে নতুন করে চালু হলো, মেমরি ফাঁকা। পুরোনো output কিন্তু পর্দায় থেকে যায়, যতক্ষণ না cell আবার চলে; শুধু restart করে output দেখে তাই সিদ্ধান্ত নেবেন না।
- ঘর ১,
[1]. বিক্রির array নতুন করে তৈরি হলো, যোগফল 435. - ঘর ২,
[2]. এবার VAT ঠিক একবার, কারণ “Run All” প্রতিটি cell একবারই চালায়। - ঘর ৩,
[3]. total = 478.5. counter 1, 2, 3, কোনো ফাঁক নেই। - তুলনা। আগের জমা ফল 526.35 আর সৎ ফল 478.5 মেলে না। অর্থাৎ আগের সংখ্যাটি এসেছিল লুকানো state থেকে। এই পরীক্ষা শেয়ার করার আগে প্রতিবার করুন; কোনো notebook-এর ফল রিপোর্ট বা ইন্টারভিউর অ্যাসাইনমেন্টে দেওয়ার আগে তো অবশ্যই।
Terminal থেকে একই পরীক্ষা: jupyter nbconvert --execute
ব্রাউজার না খুলেও পরীক্ষাটি করা যায়, আর এটিই পরে স্বয়ংক্রিয় যাচাইয়ে (যেমন CI-তে) কাজে লাগে। nbconvert নতুন একটি kernel চালু করে সব cell ওপর থেকে নিচে চালায় আর ফল নতুন ফাইলে লেখে। সত্যিকারের run, ওপরের ক্রম-ভাঙা sales.ipynb-এর ওপর:
$ jupyter nbconvert --to notebook --execute sales.ipynb --output sales_clean.ipynb
[NbConvertApp] Converting notebook sales.ipynb to notebook
[NbConvertApp] Writing 2084 bytes to sales_clean.ipynb
sales.ipynb In [1] import numpy as np …
sales.ipynb In [3] sales = sales * 1.1 # add 10% VAT
sales.ipynb In [4] print("total:", …) -> total: 526.35
sales_clean.ipynb In [1] import numpy as np …
sales_clean.ipynb In [2] sales = sales * 1.1 # add 10% VAT
sales_clean.ipynb In [3] print("total:", …) -> total: 478.5
নিচের অংশটি দুটি ফাইল থেকে counter আর output পড়ে ছাপানো ছোট একটি Python script-এর ফল। একই কোড, দুই ফাইল, দুই উত্তর। কোনো cell error দিলে nbconvert থেমে যায় আর শূন্য নয় এমন exit code দেয় (মুছে ফেলা cell-এর উদাহরণে দেখেছেন), তাই script বা CI সহজেই বুঝতে পারে notebook ভাঙা। Error সত্ত্বেও পুরোটা চালিয়ে ফাইলে error লিখে রাখতে চাইলে --allow-errors দিন; মূল ফাইলেই লিখতে চাইলে --inplace.
পরীক্ষার খাতা দেখার নিয়মের মতো ভাবুন। পরীক্ষক শুধু শেষ উত্তর দেখেন না, ধাপগুলোও দেখেন, আর প্রয়োজনে নিজে ধাপ মেনে হিসাব করেন। ধাপ মেনে হিসাব করলে যদি অন্য উত্তর আসে, শেষ উত্তরটি যত সুন্দর করেই লেখা থাকুক, নম্বর মেলে না। Restart & Run All হলো notebook-এর নিজের খাতা নিজে ধাপ মেনে আবার দেখা।
৬ · Magic command — %time, %timeit, %who, %matplotlib আর %pip
IPython kernel কিছু বিশেষ কমান্ড বোঝে, যেগুলো Python নয়। এদের বলে magic command. এক % দিয়ে শুরু হলে line magic, শুধু সেই লাইনে কাজ করে। %% দিয়ে শুরু হলে cell magic; এটি cell-এর একদম প্রথম লাইনে থাকতে হয় আর পুরো cell-এ কাজ করে। আর ! দিয়ে শুরু লাইন হলো shell command, যা অপারেটিং সিস্টেমের terminal-এ যায়। নিচের সব আউটপুট সত্যিকারের run, একটি notebook-এ magic-গুলো লিখে nbconvert --execute দিয়ে চালিয়ে (Windows, Python 3.14.4, IPython 9.18.1, NumPy 2.5.4). সময়ের সংখ্যাগুলো একটি কম্পিউটারের একবারের মাপ; আপনার যন্ত্রে ভিন্ন আসবে।
In [1]: import numpy as np
prices = np.array([120, 95, 140, 80])
%time total = sum(range(1_000_000))
CPU times: total: 0 ns
Wall time: 5.6 ms
In [2]: %timeit sum([i * i for i in range(10_000)])
256 μs ± 6.73 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
In [3]: %timeit (np.arange(10_000) ** 2).sum()
8.04 μs ± 95.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
In [4]: %%time
sq = [i * i for i in range(200_000)]
print(len(sq))
200000
CPU times: total: 15.6 ms
Wall time: 6.65 ms
In [5]: %who
np prices sq total
%timeএকবার চালিয়ে সময় বলে। “Wall time” হলো ঘড়ির হিসাবে কত সময় গেল; “CPU times” হলো CPU কতক্ষণ এই কাজে ব্যস্ত ছিল। Windows-এ শুধুtotalদেখায়, আর খুব ছোট কাজে0 nsআসতে পারে, কারণ সেখানে CPU সময়ের মাপ মোটা দাগের। Linux-এuserআরsysআলাদা দেখাবে।%timeitকাজটি অনেকবার চালায় (এখানে ৭ দফায় হাজার বা লাখবার), তারপর গড় ± standard deviation বলে। একবারের মাপ এলোমেলো হয়, তাই তুলনার জন্য এটিই ভালো। এই run-এ list comprehension-এ প্রায় 256 μs আর NumPy-তে প্রায় 8 μs, মোটামুটি ৩০ গুণের পার্থক্য। কেন NumPy এত দ্রুত, তা পর্ব ১৩ (NumPy array) আর পর্ব ১৯ (NumPy-র গতি)-এ আসবে।%%timeপুরো cell-এর সময় মাপে; cell-এর output (200000) আগে, সময় পরে।%whokernel-এর মেমরিতে আপনার বানানো সব নাম দেখায়। লুকানো state সন্দেহ হলে এটি প্রথম চোখ বোলানোর জায়গা: মুছে ফেলা cell-এরrateএখানে দেখা যেত। বিস্তারিত চাইলে%whos.
In [6]: %matplotlib inline
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(3, 2))
ax.plot([1, 2, 3, 4], prices)
ax.set_title("Daily sales");
<display_data: image/png, text/plain> <Figure size 300x200 with 1 Axes>
%matplotlib inline chart-কে cell-এর নিচে ছবি হিসেবে বসায়। ওপরে ফাইলের ভেতরে কী জমা হলো, সেটি দেখানো হয়েছে: একটি PNG ছবি আর তার লেখা-রূপ। আধুনিক JupyterLab-এ এটি সাধারণত এমনিতেই চালু থাকে, তাই অনেক নতুন notebook-এ লাইনটি না থাকলেও ছবি আসে; পুরোনো notebook আর অনেক টিউটোরিয়ালে তবু দেখবেন। শেষের ; লেখা-রূপটি পর্দায় ছাপা বন্ধ করে। matplotlib নিয়ে পুরো আলোচনা পর্ব ২৮-এ।
%pip বনাম !pip: package কোন Python-এ বসছে?
Notebook-এ package বসানোর দুটি পরিচিত লেখা আছে, আর তাদের ফল এক না-ও হতে পারে। pip সবসময় সেই Python-এ package বসায়, যে Python দিয়ে সেটি চলছে। !pip লিখলে terminal খোঁজে PATH-এ প্রথম যে pip পায়। %pip লিখলে IPython চালায় kernel-এর নিজের Python দিয়ে python -m pip. সত্যিকারের run-এ ঠিক এই পার্থক্যটি ধরা পড়েছে (পথগুলো নিরপেক্ষ করে দেখানো):
In [7]: %pip install tabulate
Collecting tabulate
Downloading tabulate-0.10.0-py3-none-any.whl.metadata (40 kB)
Downloading tabulate-0.10.0-py3-none-any.whl (39 kB)
Installing collected packages: tabulate
Successfully installed tabulate-0.10.0
Note: you may need to restart the kernel to use updated packages.
In [8]: import sys
print(sys.executable)
~\bazarghar-ai\.venv\Scripts\python.exe
In [9]: !python -c "import sys; print(sys.executable)"
!pip --version
…\Python314\python.exe
pip 26.0.1 from …\Python314\Lib\site-packages\pip (python 3.14)
In [10]: %pip --version
pip 26.3 from ~\bazarghar-ai\.venv\Lib\site-packages\pip (python 3.14)
Kernel চলছে venv-এর Python-এ (In [8]). কিন্তু এই kernel চালু হয়েছিল venv “activate” না করেই, তাই terminal-এর PATH-এ প্রথমে আছে সিস্টেমের Python. ফলে !python আর !pip দুটোই গেল সিস্টেমের Python-এ (In [9]), আর %pip গেল ঠিক জায়গায়, venv-এ (In [10]). এই অবস্থায় !pip install tabulate লিখলে package বসত সিস্টেমের Python-এ, আর kernel-এ import tabulate তখন খুঁজে পাওয়ার কথা নয়। এটি যুক্তি থেকে বলা; সিস্টেমের Python-এ কিছু বসানো এড়াতে সেই ধাপটি এখানে চালানো হয়নি।
- দুটি লেখা, দেখতে প্রায় এক। ওপরের লাইন
!দিয়ে শুরু, তাই এটি অপারেটিং সিস্টেমের shell-এ যায়। নিচেরটি%দিয়ে শুরু, তাই এটি IPython নিজে সামলায়। - Python খোঁজা। Shell PATH তালিকা ধরে প্রথম যে
pipপায়, সেটিই চালায়।%pipকোনো খোঁজাখুঁজি করে না; kernel যে Python-এ চলছে (sys.executable), সেটি দিয়েই-m pipচালায়। - Package কোথায় বসল। venv activate না করে Jupyter চালু হলে PATH-এর প্রথম pip হতে পারে সিস্টেমের, যেমন সত্যিকারের run-এ
…\Python314.%pipবসায় প্রকল্পের venv-এ, যেখানে kernel আসলে খোঁজে। - লাল পথটিই ফাঁদ।
!pip-এর পথ শেষ হলো এমন Python-এ, যেটি kernel ব্যবহারই করে না। নিয়ম তাই সরল: notebook-এর ভেতরে সবসময়%pip, আর বসানোর পর kernel একবার restart. আরও ভালো: package বসান terminal থেকে, প্রকল্পের lockfile মেনে, যা পর্ব ৭ আর পর্ব ১২-এর বিষয়।
নিজে মাপুন: %timeit-এর একটি ছোট রূপ
Pyodide-এ magic চলে না, কারণ এটি সাধারণ Python. কিন্তু %timeit-এর মূল চিন্তা Python-এর time.perf_counter() দিয়ে নিজেই লেখা যায়: কাজটি অনেকবার চালানো, কয়েক দফায় মাপা, তারপর গড় আর ছড়ানো দেখা। নিচের block একই তুলনা আপনার ব্রাউজারে করে।
import time
import numpy as np
def timeit_lite(fn, number=50, repeat=5):
"""%timeit-এর ছোট রূপ: repeat বার মেপে প্রতি loop-এর সময়"""
per_loop = []
for _ in range(repeat):
t0 = time.perf_counter()
for _ in range(number):
fn()
per_loop.append((time.perf_counter() - t0) / number)
us = np.array(per_loop) * 1e6 # মাইক্রোসেকেন্ডে
return us.mean(), us.std(), us.min()
n = 10_000
def with_list():
return sum([i * i for i in range(n)])
def with_numpy():
return int((np.arange(n, dtype=np.int64) ** 2).sum())
assert with_list() == with_numpy() # আগে নিশ্চিত হই: দুটোর ফল এক
print("দুটোর ফল:", with_list())
res = {}
for name, fn in [("list comprehension", with_list), ("NumPy", with_numpy)]:
mean, std, best = timeit_lite(fn)
res[name] = mean
print(f"{name:<18} {mean:8.1f} µs ± {std:5.1f} µs (best {best:7.1f})")
ratio = res["list comprehension"] / res["NumPy"]
print(f"এই run-এ NumPy প্রায় {ratio:.0f} গুণ দ্রুত (একবারের মাপ)")প্রথম লাইন সবসময় এক: দুটোর ফল: 333283335000. সময়ের লাইনগুলো প্রতিবার আলাদা। লেখার সময় Edge ব্রাউজারে একবারের run-এ এসেছিল list comprehension প্রায় 734 µs আর NumPy প্রায় 24 µs, অর্থাৎ প্রায় ৩১ গুণ। ব্রাউজারের Python (WebAssembly) desktop-এর চেয়ে ধীর, তাই দুটো সংখ্যাই ওপরের %timeit-এর চেয়ে বড়; আপনার যন্ত্রে সংখ্যা আবার আলাদা আসবে। দুটি সূক্ষ্ম বিষয় দেখুন। এক, মাপার আগে assert দিয়ে নিশ্চিত হওয়া যে দুটো একই উত্তর দেয়; ভুল উত্তর দ্রুত দিলে তার কোনো দাম নেই। দুই, dtype=np.int64: ব্রাউজারের Pyodide 32-bit পরিবেশ, সেখানে NumPy-র স্বাভাবিক পূর্ণসংখ্যা 32-bit হতে পারে, আর 333,283,335,000 সেই সীমা (প্রায় ২১০ কোটি) ছাড়িয়ে নীরবে ভুল যোগফল দিত। assert ঠিক এই ধরনের ভুল ধরার জন্যই।
৭ · .ipynb ফাইলের ভেতরে — JSON, diff আর nbstripout / Jupytext
একটি .ipynb ফাইল আসলে সাধারণ লেখার ফাইল, JSON format-এ। ভেতরে আছে cells তালিকা, প্রতিটি cell-এর ধরন, লেখা (source), execution_count আর outputs, আর ওপরে metadata (কোন kernel, কোন ভাষা)। কোনো notebook-এ কী আছে বুঝতে Jupyter খোলারও দরকার নেই; Python-এর json module দিয়েই পড়া যায়। নিচের block পড়ছে সেই ক্রম-ভাঙা sales.ipynb, যার আসল লেখা হুবহু এই পাতায় একটি ছোট ফাইল হিসেবে রাখা আছে।
import json
with open("sales.ipynb", encoding="utf-8") as f:
nb = json.load(f) # .ipynb আসলে একটি JSON ফাইল
print("top-level keys:", list(nb))
print("nbformat:", f'{nb["nbformat"]}.{nb["nbformat_minor"]}',
"| kernel:", nb["metadata"]["kernelspec"]["name"])
counts = []
for i, cell in enumerate(nb["cells"]):
first = "".join(cell["source"]).splitlines()[0]
if cell["cell_type"] == "markdown":
print(f"[{i}] markdown {first}")
continue
n = cell["execution_count"]
counts.append(n)
out = "".join("".join(o.get("text", [])) for o in cell["outputs"]).strip()
print(f"[{i}] code In [{n}] {first[:24]:<24} -> {out or '-'}")
fresh = list(range(1, len(counts) + 1))
print("saved counts:", counts, "| fresh run:", fresh)
if counts != fresh:
print("সতর্কতা: cell-গুলো ওপর থেকে নিচে একবার করে চলেনি")আসল run-এর আউটপুটের মূল অংশ:
top-level keys: ['cells', 'metadata', 'nbformat', 'nbformat_minor']
nbformat: 4.5 | kernel: python3
[3] code In [4] print("total:", round(fl -> total: 526.35
saved counts: [1, 3, 4] | fresh run: [1, 2, 3]
পুরো notebook-টি চারটি চাবির একটি dictionary. Output (total: 526.35) ফাইলের ভেতরেই লেখা, তাই কোড না চালিয়েও পড়া গেল। আর শেষ লাইন দেখাচ্ছে, জমা counter [1, 3, 4], অথচ একবার করে ওপর থেকে নিচে চললে হতো [1, 2, 3]. এই ছোট যাচাই দিয়ে একটি ফোল্ডারের শত notebook-এ সন্দেহজনক ক্রম খুঁজে বের করা যায়। সাবধান: ফাঁক মানেই ভুল ফল নয় (হয়তো কেউ একটি সাধারণ cell দুবার চালিয়েছিলেন), আবার ফাঁক না থাকাও প্রমাণ নয়; চূড়ান্ত পরীক্ষা Restart & Run All.
Diff কেন কঠিন
কোড Git-এ রাখলে প্রতিটি পরিবর্তনের diff দেখা যায়, যা Software Engineering কোর্সের Git history আর diff পর্বে আছে। Notebook-এ সমস্যা হলো, কোড না বদলালেও ফাইল বদলায়। ওপরের দুই ফাইলের (sales.ipynb আর sales_clean.ipynb) কোড হুবহু এক; বদলেছে শুধু একটি output. সত্যিকারের run-এ সাধারণ diff -u দিয়ে তুলনা:
$ diff -u sales.ipynb sales_clean.ipynb
@@ -13,7 +13,14 @@
"cell_type": "code",
"execution_count": 1,
"id": "6d4cd742",
- "metadata": {},
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2026-11-04T13:19:27.274585Z",
+ "iopub.status.busy": "2026-11-04T13:19:27.274394Z",
…
- "execution_count": 3,
+ "execution_count": 2,
…
- "total: 526.35\n"
+ "total: 478.5\n"
…
+ "version": "3.14.4"
$ diff -u sales.ipynb sales_clean.ipynb | grep -c '^[-+][^-+]'
45
৪৫টি বদলানো লাইন, যার মধ্যে আসল অর্থপূর্ণ বদল একটিই। বাকি সব execution count, nbconvert-এর লেখা সময়ের ছাপ আর Python-এর version. দুজন একই notebook-এ কাজ করলে এমন “গোলমাল” থেকে Git-এর merge conflict-ও হয়, আর JSON-এর ভেতরে conflict হাতে মেটানো কষ্টের। Chart থাকলে আরও খারাপ: ছবি base64 লেখা হয়ে ফাইলে ঢোকে, এক লাইনে হাজার হাজার অক্ষর।
দুটি প্রচলিত সমাধান (দুটোই ঐচ্ছিক, দুটোরই বিকল্প আছে)
nbstripout notebook থেকে output আর execution count মুছে দেয়। এটিকে Git-এর filter হিসেবেও বসানো যায় (nbstripout --install), তখন commit-এর সময় নিজে থেকে মোছে, আপনার খোলা ফাইলে output থেকে যায়। সত্যিকারের run, nbstripout 0.9.1:
$ nbstripout stripped.ipynb
$ python show_cells.py stripped.ipynb
cell 0 markdown id='0'
cell 1 code id='1' execution_count=None outputs=[]
cell 2 code id='2' execution_count=None outputs=[]
cell 3 code id='3' execution_count=None outputs=[]
কাঁচা JSON না দেখিয়ে একটি ছোট script দিয়ে প্রতিটি cell-এর মূল ঘরগুলো ছাপানো হয়েছে (JSON-এর null Python-এ None). Counter মুছে গেছে, output খালি, আর লক্ষ করুন, এই version cell-এর এলোমেলো id-ও (6d4cd742…) বদলে 0, 1, 2… করে দিয়েছে, যাতে diff আরও স্থির থাকে। এটি এই version-এর আচরণ; id রাখতে চাইলে এর নিজস্ব option আছে, version অনুযায়ী ডকুমেন্টেশন দেখে নিন।
Jupytext অন্য পথ নেয়: notebook-এর পাশে একটি সাধারণ .py ফাইল রাখে, যেখানে cell-এর সীমা # %% মন্তব্য। Git-এ রাখা হয় .py, যার diff একদম সাধারণ কোডের মতো। সত্যিকারের run, Jupytext 1.19.6:
$ jupytext --to py:percent sales.ipynb -o sales.py
[jupytext] Reading sales.ipynb in format ipynb
[jupytext] Writing sales.py in format py:percent
$ cat sales.py
# ---
# jupyter:
# jupytext:
# text_representation:
# extension: .py
# format_name: percent
…
# %% [markdown]
# # Bazarghar weekly sales
# Four days of sales, VAT added once.
# %%
import numpy as np
sales = np.array([120, 95, 140, 80])
# %%
sales = sales * 1.1 # add 10% VAT
# %%
print("total:", round(float(sales.sum()), 2))
এই .py ফাইলে কোনো output নেই, কোনো counter নেই, তাই লুকানো state-এর কোনো সাক্ষ্যও নেই; ফাইলটি সাধারণ Python হিসেবেও চলে। Server-এর log-এ jupyterlab_jupytext extension load হতে দেখেছিলেন; JupyterLab-এ এটি থাকলে .py ফাইল সরাসরি notebook হিসেবে খোলা যায়। কোন পথ নেবেন, তা দলের অভ্যাস। কেউ output মুছে .ipynb রাখেন, কেউ শুধু .py, কেউ JupyterLab-এর jupyterlab-git-এর মতো notebook-বোঝা diff টুল বা nbdime ব্যবহার করেন।
৮ · Notebook না script — কখন কোড বাইরে নেবেন
Notebook আর script নিয়ে প্রোগ্রামারদের মধ্যে পুরোনো একটি বিতর্ক আছে, আর দুই পক্ষেরই যুক্তি শক্ত। Notebook খোঁজাখুঁজির জন্য দারুণ: ডেটা খোলা, chart দেখা, প্রশ্ন বদলানো, আর ফলসহ একটি গল্প অন্যকে দেখানো। কিন্তু একই কোড বারবার লাগলে, test লিখতে হলে, বা কাজটি প্রতিদিন নিজে থেকে চলতে হলে script আর module-ই ঠিক জায়গা।
| কাজ | Notebook | .py script / module |
|---|---|---|
| নতুন ডেটা খুলে দেখা, chart | খুব ভালো | সম্ভব, কিন্তু ধীর ঘোরাফেরা |
| ফলসহ ব্যাখ্যা অন্যকে দেখানো | খুব ভালো | আলাদা রিপোর্ট লাগে |
| একই function অনেক notebook-এ | কপি-পেস্ট, ভুল ছড়ায় | একবার লিখে import |
| Test লেখা আর চালানো | কঠিন | pytest দিয়ে সহজ |
| Git-এ diff আর code review | JSON, গোলমেলে | পরিষ্কার |
| প্রতিদিন নিজে থেকে চলা কাজ | ঝুঁকিপূর্ণ (লুকানো state) | ওপর থেকে নিচে, প্রতিবার একই |
বাস্তবে সবচেয়ে প্রচলিত মাঝামাঝি পথ: পুনর্ব্যবহারযোগ্য কোড .py module-এ, আর notebook-এ শুধু সেটি import করে খোঁজাখুঁজি। Module আর import কীভাবে কাজ করে, তা Python কোর্সের module আর package পর্বে আছে। Notebook-এ একটি ব্যবহারিক বাধা হলো, module একবার import হলে Python সেটি মনে রাখে; ফাইল বদলালেও পুরোনো রূপই চলতে থাকে। সত্যিকারের run-এ bazar_utils.py-তে add_vat() function রেখে, মাঝপথে একটি cell থেকে ফাইলটিতে VAT-এর হার 0.10 থেকে 0.15 করে দেখা হয়েছে (দুটি notebook, nbconvert দিয়ে চালানো, ফল সংক্ষেপে):
# autoreload ছাড়া
In [1]: from bazar_utils import add_vat; print(add_vat(100)) -> 110.0
In [2]: (bazar_utils.py-তে rate=0.15 করে save)
In [3]: from bazar_utils import add_vat; print(add_vat(100)) -> 110.0
# autoreload সহ
In [1]: %load_ext autoreload
%autoreload 2
In [2]: from bazar_utils import add_vat; print(add_vat(100)) -> 110.0
In [3]: (bazar_utils.py-তে rate=0.15 করে save)
In [4]: from bazar_utils import add_vat; print(add_vat(100)) -> 115.0
প্রথম ক্ষেত্রে ফাইল বদলানো সত্ত্বেও ফল 110.0; এটিও এক ধরনের লুকানো state, এবার মেমরিতে রাখা পুরোনো module. autoreload extension প্রতিটি cell চালানোর আগে বদলানো module নতুন করে load করে, তাই দ্বিতীয় ক্ষেত্রে 115.0. এটি সুবিধার, কিন্তু নিখুঁত নয়; IPython-এর ডকুমেন্টেশনেই কিছু ক্ষেত্রে (যেমন class-এর গঠন বদলালে) এটি ঠিকমতো কাজ না করার কথা বলা আছে। সন্দেহ হলে kernel restart করাই নিরাপদ।
কখন কোড notebook থেকে বের করবেন: কয়েকটি সংকেত
- একই কয়েক লাইন দ্বিতীয় notebook-এ কপি করতে যাচ্ছেন।
- একটি cell এত লম্বা যে স্ক্রল করতে হয়, বা ভেতরে একাধিক কাজ।
- কোনো হিসাবের ফল ভুল হলে ক্ষতি আছে, তাই test দরকার। ছোট test কীভাবে লিখবেন, তা পর্ব ১১ (AI কোডে পরিচ্ছন্নতা)-এ আসবে।
- কাজটি রাতে নিজে থেকে, বা server-এ চলতে হবে। তখন
python train.py, notebook নয়।
৯ · VS Code, Colab আর AI কাজের ভালো অভ্যাস
JupyterLab ছাড়াও .ipynb খোলার জনপ্রিয় জায়গা আছে। দুটির কথা জেনে রাখুন; দুটোরই বৈশিষ্ট্য আর শর্ত সময়ের সঙ্গে বদলায়, তাই ব্যবহারের আগে তাদের নিজস্ব ডকুমেন্টেশন দেখে নিন।
VS Code notebook
Microsoft-এর Python আর Jupyter extension বসালে VS Code নিজেই .ipynb খোলে। Kernel বাছতে হয় ওপরের ডান কোণ থেকে; সেখানে প্রকল্পের .venv বাছা জরুরি, নাহলে ভুল Python-এ চলবে। কোড সাজানো, debugger আর Git পাশাপাশি পাওয়া যায়।
Google Colab
Colab ব্রাউজারে notebook চালায় Google-এর কম্পিউটারে; Google account লাগে। কিছু GPU সুবিধা বিনামূল্যে পাওয়া যায় বলে জানা যায়, তবে সীমা আর শর্ত বদলায়। Session একটা সময় পরে বন্ধ হয়, তখন kernel-এর সব মেমরি আর বসানো package মুছে যায়।
অন্যের কম্পিউটার: ডেটা দেওয়ার আগে ভাবুনএই পর্বের জন্য Colab বা অন্য কোনো অনলাইন সেবায় login করা হয়নি; ওপরের সব আউটপুট নিজের কম্পিউটারের JupyterLab আর nbconvert থেকে। একটি বিষয় তিন জায়গাতেই এক: kernel, cell, counter আর লুকানো state. যেখানেই চালান, Restart & Run All-এর নিয়ম বদলায় না।
AI আর ডেটার কাজে notebook-এর ভালো অভ্যাস
আরও গভীরে: Notebook ফাইলের গঠনের আনুষ্ঠানিক বিবরণ nbformat-এর JSON schema-তে আছে, আর ব্রাউজার, server আর kernel-এর কথাবার্তার নিয়ম “Jupyter messaging protocol”-এ, যা ZeroMQ নামের message-লাইব্রেরির ওপর চলে; এর ফলেই যেকোনো ভাষার জন্য kernel লেখা যায়। লুকানো state-এর মূল সমস্যা এড়াতে কিছু নতুন notebook (যেমন marimo বা Observable) “reactive” নকশা নেয়: একটি cell বদলালে তার ওপর নির্ভরশীল সব cell নিজে থেকে আবার চলে, ঠিক spreadsheet-এর সূত্রের মতো। আর Pimentel ও সহকর্মীদের ২০১৯ সালের একটি বড় গবেষণা GitHub-এর লক্ষাধিক notebook আবার চালিয়ে পুনরুৎপাদনের হার মেপেছিল; ভাবনার প্রশ্ন ২-এ সেটি আছে।
পরীক্ষা ও ইন্টারভিউতে:
- Kernel কী? Notebook-এর পেছনে চলা আলাদা প্রক্রিয়া, যা cell-এর কোড চালায় আর সব variable মেমরিতে রাখে।
In [5]-এর ৫ কী বোঝায়? Kernel চালুর পর পঞ্চমবার কোড চালানো; cell-এর অবস্থান নয়।- Hidden state কীভাবে ধরবেন? Restart & Run All, বা terminal-এ
jupyter nbconvert --execute, তারপর জমা ফলের সঙ্গে মেলানো। !pipআর%pip-এর পার্থক্য?!pipshell-এর PATH-এর প্রথম pip চালায়;%pipkernel-এর নিজের Python-এ বসায়।- Notebook-কে Git-বান্ধব করবেন কীভাবে? nbstripout দিয়ে output মোছা, অথবা Jupytext দিয়ে জোড়া
.pyফাইল রাখা।
১০ · AI-এর যুগে notebook — সহকারীর লেখা cell যাচাই
আজকাল JupyterLab, VS Code আর Colab, প্রায় সব জায়গাতেই AI সহকারী cell লিখে দেয়, বা ভুল ব্যাখ্যা করে। এটি সময় বাঁচায়। কিন্তু notebook-এর পরিবেশে AI-এর কিছু চেনা ভুল আছে, আর লুকানো state সেগুলোকে আরও আড়াল করে।
| AI প্রায়ই যা করে | কীভাবে ধরা পড়ে |
|---|---|
পুরোনো API দেয়, যেমন pandas-এর df.append বা NumPy-র np.float | নতুন version-এ AttributeError; নিজের version-এর ডকুমেন্টেশন দেখুন |
| এমন function বা parameter বানিয়ে ফেলে, যা নেই | চালালে TypeError বা AttributeError; help() দিয়ে signature দেখুন |
| আগের cell-এর এমন variable ধরে নেয়, যা আপনার notebook-এ নেই | Restart & Run All-এ NameError |
Notebook-এ !pip install লেখে | Package বসে, অথচ import হয় না; %pip ব্যবহার করুন |
| পুরো ডেটায় scale বা fill করে তারপর train/test ভাগ করে | Data leakage; ভাগ আগে, অন্য সব পরে |
| Training ডেটাতেই accuracy মেপে বলে “model দারুণ” | Test সেট আর সরল baseline-এর সঙ্গে তুলনা |
Warning চেপে রাখে (warnings.filterwarnings("ignore")) | লাইনটি সরিয়ে আবার চালান; warning পড়ুন |
AI-কে notebook-এর জন্য কোড চাইলে প্রসঙ্গটা নির্দিষ্ট করে দিন: কোন version, কোন variable আগে থেকে আছে, আর কী আকারে ফল চান। নিচের কাঠামোটি কাজে লাগাতে পারেন:
AI-এর লেখা cell হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Notebook কি খারাপ অভ্যাস তৈরি করে? “Notebook বনাম script” বিতর্কে দুই পক্ষের যুক্তি কী?
২০১৮ সালে JupyterCon সম্মেলনে Joel Grus “I don't like notebooks” শিরোনামে একটি আলোচনা দেন, যা নিয়ে অনেক কথা হয়েছিল। তাঁর মূল অভিযোগগুলো এই পর্বের সঙ্গেই মেলে: লুকানো state আর ক্রম ভেঙে চালানো নতুনদের বিভ্রান্ত করে; notebook-এ module, test আর ভালো কাঠামোর অভ্যাস তৈরি হয় না; আর notebook-কে অন্যের হাতে দিলে তিনি প্রায়ই সেটি চালাতে পারেন না।
অন্য পক্ষের যুক্তিও শক্ত। ডেটার কাজে প্রশ্ন আগে থেকে জানা থাকে না; ডেটা দেখে প্রশ্ন বদলায়। এমন খোঁজাখুঁজিতে প্রতিবার পুরো script চালানো ধীর, আর ফল, chart আর ব্যাখ্যা একসঙ্গে রাখা অনেক কাজের। fast.ai-এর Jeremy Howard আর সহকর্মীরা এক ধাপ এগিয়ে nbdev নামের একটি পদ্ধতি তৈরি করেন, যেখানে notebook থেকেই library, test আর ডকুমেন্টেশন তৈরি হয়। তাঁদের মতে সমস্যা notebook-এ নয়, notebook ব্যবহারের অভ্যাসে।
বাস্তবের বেশিরভাগ দল এই দুইয়ের মাঝামাঝি থাকে। খোঁজাখুঁজি আর রিপোর্ট notebook-এ; বারবার লাগে এমন কোড, ডেটা পরিষ্কারের ধাপ আর model training .py module-এ, test-সহ; আর notebook সেই module import করে। নিয়মিত চলা কাজ (প্রতিরাতে model নতুন করে train) হয় script বা pipeline-এ। দলের নিয়মে অনেক সময় থাকে: notebook merge হওয়ার আগে nbconvert --execute দিয়ে চালিয়ে দেখা হবে।
বিতর্কটি “কোন টুল ভালো” নিয়ে নয়, “কোন কাজে কোন টুল” নিয়ে। ক্রিকেটে নেটে অনুশীলন আর আসল ম্যাচ দুটোই দরকার; নেটে একই বল দশবার খেলা যায়, ম্যাচে যায় না। Notebook হলো নেট, script হলো ম্যাচ।
মূল উপলব্ধি: Notebook খোঁজাখুঁজি আর ব্যাখ্যার জন্য, script পুনরাবৃত্তি আর নির্ভরযোগ্যতার জন্য। দক্ষতা হলো বুঝতে পারা কখন কোড এক জায়গা থেকে অন্য জায়গায় সরানোর সময় হয়েছে।
প্র ০২GitHub-এর লক্ষ লক্ষ notebook আবার চালালে কতগুলো একই ফল দেয়? এ থেকে কী শেখার আছে?
২০১৯ সালে João Felipe Pimentel, Leonardo Murta, Vanessa Braganholo আর Juliana Freire সফটওয়্যার গবেষণার সম্মেলন MSR-এ একটি গবেষণা প্রকাশ করেন: “A Large-scale Study about Quality and Reproducibility of Jupyter Notebooks”. তাঁরা GitHub থেকে প্রায় ১৪ লাখ notebook সংগ্রহ করে বিশ্লেষণ করেন, আর যেগুলো সম্ভব সেগুলো নতুন পরিবেশে আবার চালিয়ে দেখেন। পরে এর একটি বিস্তৃত সংস্করণ জার্নালেও প্রকাশিত হয়।
যেভাবে ফলটি সাধারণত উদ্ধৃত হয়: আবার চালানোর চেষ্টা করা notebook-এর মোটামুটি এক-চতুর্থাংশ (প্রায় ২৪%) error ছাড়া শেষ পর্যন্ত চলেছে, আর মাত্র প্রায় ৪% আগের জমা output-এর সঙ্গে একই ফল দিয়েছে। সঠিক শতাংশ আর কোন notebook গোনায় ধরা হয়েছে, তা মূল প্রবন্ধে দেখে নেওয়াই ভালো; তবে মোট চিত্রটি স্পষ্ট: বেশিরভাগ প্রকাশিত notebook অন্য কেউ চালিয়ে একই ফল পান না।
ব্যর্থতার কারণগুলোও এই পর্বের সঙ্গে মেলে। সবচেয়ে বড় কারণ ছিল নির্ভরতা: কোন package-এর কোন version লাগবে, তা লেখা নেই। এরপর লুকানো state আর ক্রম ভেঙে চালানো; গবেষণায় অনেক notebook-এর execution count ওপর থেকে নিচে একটানা ছিল না। আর ছিল ডেটা: notebook যে ফাইল পড়ে, তা repository-তে নেই, বা কারও নিজের কম্পিউটারের পথে রাখা।
AI-এর কাজে এর গুরুত্ব আরও বেশি, কারণ সেখানে random-ও আছে। Seed না দিলে ঠিকঠাক চালানো notebook-ও প্রতিবার একটু ভিন্ন accuracy দেয়। তাই এই কোর্সে বাজারঘরের ডেটা সবসময় seed দিয়ে তৈরি। পুনরুৎপাদনের পুরো ব্যবস্থা, যেমন lockfile, ডেটার version আর Git, পর্ব ১২ (পুনরুৎপাদনযোগ্য প্রকল্প)-এর বিষয়।
মূল উপলব্ধি: “আমার কম্পিউটারে চলেছিল” প্রমাণ নয়। Reproducibility আসে চারটি জিনিস থেকে: লেখা নির্ভরতা, ওপর থেকে নিচে চলা কোড, পাওয়া যায় এমন ডেটা আর নির্দিষ্ট seed.
প্র ০৩Literate programming থেকে Jupyter: কোড আর ব্যাখ্যা একসঙ্গে রাখার ধারণা কীভাবে এত জনপ্রিয় হলো?
১৯৮৪ সালে Donald Knuth “Literate Programming” নামে একটি প্রবন্ধে প্রস্তাব করেন, প্রোগ্রাম লেখা উচিত মূলত মানুষের পড়ার জন্য, যেখানে ব্যাখ্যার ভেতরে কোড বসানো থাকবে, আর সেখান থেকে কম্পিউটারের জন্য কোড আলাদা করে নেওয়া হবে। তিনি নিজের TeX টাইপসেটিং সফটওয়্যার এভাবে লেখেন। চিন্তাটি প্রভাবশালী হলেও সাধারণ প্রোগ্রামারদের মধ্যে খুব বেশি ছড়ায়নি।
বিজ্ঞানের জগতে আরেকটি ধারা ছিল। ১৯৮৮ সালের দিকে Mathematica আর পরে Maple-এর মতো গাণিতিক সফটওয়্যার “notebook” ইন্টারফেস দেয়, যেখানে সূত্র, হিসাব আর graph এক পাতায়। বিজ্ঞানীরা এমনিতেই ল্যাবের খাতা রাখতেন: কী পরীক্ষা, কী ফল, কী সিদ্ধান্ত। Notebook সেই খাতারই ডিজিটাল রূপ।
Python-এ এই ধারণা আসে IPython দিয়ে। ২০০১ সালে Fernando Pérez এটি শুরু করেন, আর প্রায় এক দশকের চেষ্টার পর ২০১১ সালের দিকে ব্রাউজারে চলা IPython Notebook প্রকাশিত হয়। ব্রাউজার বেছে নেওয়ার ফলে ছবি, টেবিল, সূত্র সব সহজে দেখানো গেল, আর দূরের server-এ চলা kernel-ও ব্যবহার করা গেল। ২০১৪ সালে ভাষা-নিরপেক্ষ অংশ আলাদা হয়ে Project Jupyter হয়। ২০১৭ সালে ACM প্রকল্পটির মূল দলকে Software System Award দেয় বলে জানা যায়। মোটামুটি একই সময়ে NumPy, pandas, matplotlib আর scikit-learn পরিণত হয়, আর deep learning-এর জোয়ার আসে; অনেক টিউটোরিয়াল, কোর্স আর গবেষণার কোড notebook আকারে ছড়িয়ে পড়ে।
Knuth-এর ধারণার সঙ্গে একটি পার্থক্য লক্ষণীয়। Literate programming-এর লক্ষ্য ছিল সুশৃঙ্খল, যত্নে লেখা প্রোগ্রাম। Jupyter-এর জনপ্রিয়তা এসেছে উল্টো গুণ থেকে: দ্রুত, এলোমেলো খোঁজাখুঁজি। সেই স্বাধীনতাই লুকানো state-এর জন্ম দেয়।
মূল উপলব্ধি: Notebook জনপ্রিয় হয়েছে কারণ এটি বিজ্ঞানের ল্যাব খাতার মতো চিন্তা, কোড আর ফল একসঙ্গে রাখে। ভালো ল্যাব খাতার মতোই, এর দাম তখনই, যখন অন্য কেউ পড়ে একই ফল পান।
প্র ০৪একটি notebook ফাইল খোলা কি বিপজ্জনক হতে পারে? Jupyter-এর নিরাপত্তা নিয়ে কী জানা দরকার?
Notebook-এর মূল কাজই কোড চালানো, আর kernel আপনার ব্যবহারকারী-অনুমতিতে চলে: আপনার ফাইল পড়তে, মুছতে, ইন্টারনেটে কিছু পাঠাতে পারে। তাই নিরাপত্তার প্রশ্ন দুই জায়গায়: server-এ কে ঢুকতে পারে, আর notebook-এর ভেতরে কী আছে।
প্রথমটির জন্য Jupyter কয়েক স্তরের সাবধানতা নেয়। স্বাভাবিকভাবে server শুধু localhost-এ শোনে, আর প্রতিবার চালুর সময় একটি এলোমেলো token বানায়; token ছাড়া ফাইলের তালিকা চাইলে সত্যিকারের run-এ 403 এসেছিল। এজন্যই --ip=0.0.0.0 দিয়ে সব নেটওয়ার্কে খোলা বা token বন্ধ করার পরামর্শ বিপজ্জনক। ইন্টারনেটে খোলা, পাসওয়ার্ডহীন Jupyter server আক্রমণকারীদের লক্ষ্য হওয়ার খবর নিরাপত্তা গবেষকেরা বিভিন্ন সময়ে প্রকাশ করেছেন। দূর থেকে কাজ করতে হলে সাধারণ পরামর্শ হলো SSH tunnel বা ঠিকমতো সাজানো JupyterHub.
দ্বিতীয়টি হলো অন্যের notebook. শুধু ফাইল খুললে কোড চলে না; চলে তখন, যখন আপনি cell চালান, বিশেষ করে “Run All” দেন। কিন্তু output-এর ভেতরে HTML বা JavaScript থাকতে পারে। এজন্য Jupyter একটি “trust” ধারণা ব্যবহার করে আসছে: আপনার নিজের চালানো নয় এমন notebook-এর সক্রিয় output স্বাভাবিকভাবে চালানো হয় না। বিস্তারিত নিয়ম version অনুযায়ী বদলেছে, তাই ডকুমেন্টেশন দেখে নিন। নিয়ম সহজ রাখুন: অপরিচিত উৎসের notebook আগে পড়ে দেখুন, তারপর চালান, আর ! দিয়ে শুরু লাইন বা %pip install বিশেষ মনোযোগে দেখুন।
তৃতীয় একটি নীরব ঝুঁকি হলো ফাঁস। Output-এ থেকে যাওয়া পাসওয়ার্ড, ডেটাবেসের ঠিকানা বা গ্রাহকের তথ্য notebook-এর সঙ্গে GitHub-এ চলে যায়। Cell মুছলেও output বা পুরোনো commit-এ থেকে যেতে পারে। Cybersecurity কোর্সে secret ব্যবস্থাপনা নিয়ে আরও আছে।
মূল উপলব্ধি: Notebook মানে আপনার কম্পিউটারে কোড চালানোর একটি দরজা। Localhost, token আর “পড়ে তারপর চালাও”, এই তিন অভ্যাস দরজাটি নিজের হাতে রাখে।
প্র ০৫%timeit গড় ± standard deviation দেখায়, অথচ Python-এর timeit ডকুমেন্টেশন সবচেয়ে কম সময়কে বেশি কাজের বলে। কোনটি ঠিক?
দুটোই নিজের জায়গায় যুক্তিসংগত, আর পার্থক্যটি বুঝলে সময় মাপার পুরো বিষয়টি পরিষ্কার হয়। একটি কাজের সময় মাপলে যে সংখ্যা আসে, তা কাজের আসল খরচ আর “গোলমাল”-এর যোগফল: অপারেটিং সিস্টেম অন্য প্রোগ্রামকে সময় দিল, CPU-র গতি তাপমাত্রার কারণে বদলাল, cache খালি ছিল, Python-এর garbage collector চলল। এই গোলমাল প্রায় সবসময় সময় বাড়ায়, কমায় না।
সেই যুক্তিতে Python-এর standard library-র timeit ডকুমেন্টেশন (Timer.repeat-এর অংশে) বলে, ফলাফলের তালিকা থেকে গড় আর standard deviation বের করা লোভনীয় হলেও খুব কাজের নয়; সাধারণত সবচেয়ে কম মানটিই বলে যন্ত্রটি কত দ্রুত কাজটি করতে পারে, বাকি পার্থক্য আসে অন্য প্রক্রিয়ার বাধা থেকে। IPython-এর নতুন version-গুলোর %timeit আবার গড় ± standard deviation দেখায় (এই পর্বের run-এ 256 μs ± 6.73 μs), যাতে ফল কতটা ছড়ানো, তা-ও চোখে পড়ে। ছড়ানো বেশি হলে বোঝা যায়, মাপটি অস্থির; তুলনা করার আগে সাবধান।
পাতার timeit_lite তাই দুটোই দেখায়: গড়, ছড়ানো আর সবচেয়ে কম। তুলনার সময় কয়েকটি নিয়ম মানুন। ফল এক কি না আগে যাচাই করুন। একই যন্ত্রে, একই অবস্থায়, পাশাপাশি মাপুন। খুব ছোট কাজ অনেকবার চালিয়ে মাপুন, কারণ ঘড়ির নিজস্ব নির্ভুলতার সীমা আছে; time.perf_counter() এই কাজের জন্যই, time.time() নয়, কারণ দ্বিতীয়টি দেয়াল-ঘড়ির সময়, যা সিস্টেম নিজে বদলাতে পারে। আর একটি কম্পিউটারের একবারের মাপকে কখনো সাধারণ সত্য বলবেন না; সেজন্যই এই পর্বে প্রতিটি সময়ের পাশে “একবারের মাপ” লেখা।
AI-এর কাজে এর ব্যবহার প্রচুর: কোন preprocessing ধীর, model-এর prediction কত দ্রুত, batch বাড়ালে কী হয়। ভুলভাবে মাপলে ভুল জায়গায় সময় খরচ হয়।
মূল উপলব্ধি: সবচেয়ে কম সময় বলে কাজটির সম্ভাব্য গতি, গড় আর ছড়ানো বলে বাস্তবে কেমন আর মাপটি কতটা স্থির। ভালো রিপোর্টে দুটোই থাকে, সঙ্গে যন্ত্র আর version-এর কথা।
অনুশীলন
প্রথমটি খাতা-কলমে, দ্বিতীয়টি কোড লিখে, বাকি দুটি ভেবে।
-
Counter থেকে হিসাব (output অনুমান): একটি notebook-এ তিনটি code cell: ঘর ক
x = 10, ঘর খx = x * 2, ঘর গx = x + 5; print(x). নতুন kernel-এ কেউ চালালেন এই ক্রমে: ক, খ, গ, খ, গ. (১) শেষ output কত? (২) Save করা ফাইলে তিনটি cell-এর পাশে কোন কোনIn [n]থাকবে? (৩) Restart & Run All দিলে output কত হবে?ধাপে ধাপে: ক চলে x = 10 (In 1); খ চলে x = 20 (In 2); গ চলে x = 25, ছাপে 25 (In 3); খ আবার, x = 50 (In 4); গ আবার, x = 55, ছাপে 55 (In 5).
(১) শেষ output 55. (২) প্রতিটি cell-এর পাশে থাকে শেষবারের সংখ্যা: ঘর ক
[1], ঘর খ[4], ঘর গ[5]. সংখ্যা 2 আর 3 কোথাও নেই, যা ক্রম ভাঙার সংকেত। (৩) নতুন kernel-এ ক, খ, গ একবার করে: 10 → 20 → 25, output 25. জমা ফল 55 পুনরুৎপাদনযোগ্য নয়। -
কোড লিখুন — অনেক notebook-এর counter যাচাই: একটি function
check_order(path)লিখুন, যা একটি.ipynbফাইল পড়ে code cell-গুলোর execution count-এর তালিকা দেয়, আর বলে সেটি[1, 2, …, n]কি না। যে cell কখনো চলেনি (None), সেটিও সন্দেহজনক ধরুন।একটি সম্ভাব্য সমাধান:
import json def check_order(path): with open(path, encoding="utf-8") as f: nb = json.load(f) counts = [c.get("execution_count") for c in nb["cells"] if c["cell_type"] == "code"] ok = counts == list(range(1, len(counts) + 1)) return counts, okওপরের
sales.ipynb-এ এটি দেবে([1, 3, 4], False); nbstripout করা ফাইলে([None, None, None], False), যা ঠিকই, কারণ output-বিহীন ফাইল দেখে চলার ক্রম বলা যায় না।pathlib.Path(".").glob("**/*.ipynb")দিয়ে পুরো ফোল্ডারে চালাতে পারেন। মনে রাখুন, এটি শুধু সংকেত; প্রমাণ Restart & Run All. -
Design ও critique: সহকর্মী একটি notebook পাঠিয়েছেন। তাতে: cell ৭-এ
import pandas as pd; cell ২-এ!pip install lightgbm; cell ৪-এDB_PASSWORD = "example-secret"; counter[3] [1] [9] [4] [12]; একটি output-এ ৫০ হাজার সারির পুরো DataFrame ছাপা; কোথাও seed নেই; model-এর accuracy training ডেটাতে মাপা। কী কী বদলাতে বলবেন, গুরুত্ব অনুযায়ী সাজান।- গোপন চাবি সরানো (সবচেয়ে জরুরি): পাসওয়ার্ড cell থেকে বাদ, environment variable বা গোপন ফাইল থেকে পড়া; ফাইলটি আগে কোথাও শেয়ার বা commit হয়ে থাকলে পাসওয়ার্ডটি বদলানো।
- Accuracy test সেটে, train/test ভাগ সবার আগে, আর একটি সরল baseline-এর পাশে।
- Restart & Run All: counter-এর ক্রম
[3] [1] [9]…বলছে notebook ওপর থেকে নিচে চলেনি; ফলগুলো বিশ্বাসযোগ্য নয়, যতক্ষণ না একটানা চলে। - সব import আর seed প্রথম cell-এ; নাহলে cell ৭-এর আগের cell-গুলো নতুন kernel-এ
NameErrorদেবে। !pip-এর বদলে%pip, বা ভালো হয় প্রকল্পের নির্ভরতার তালিকায় version-সহ লেখা।- বড় output বাদ:
df.head()বাdf.shapeযথেষ্ট; ৫০ হাজার সারি ফাইল ভারী করে, আর তাতে ব্যক্তিগত তথ্য থাকতে পারে।
-
ফল অনুমান — nbconvert আর nbstripout: একটি notebook-এ দুটি cell:
import random; random.seed(7); r = random.random()আরprint(round(r, 3)). Notebook-টি ঠিকঠাক ওপর থেকে নিচে চালানো। (১)jupyter nbconvert --executeদুবার চালালে দুবার কি একই সংখ্যা ছাপবে? (২) seed-এর লাইনটি মুছে দিলে? (৩)nbstripoutচালানোর পর ফাইলে দ্বিতীয় cell-এরexecution_countআরoutputsকী হবে?(১) হ্যাঁ, একই। প্রতিবার নতুন kernel, প্রতিবার একই seed 7, তাই
random.random()একই মান দেয়। (২) seed ছাড়া প্রতিটি নতুন kernel ভিন্ন শুরুর মান নেয়, তাই সংখ্যা সাধারণত প্রতিবার ভিন্ন হবে; notebook “চলে”, কিন্তু ফল পুনরুৎপাদনযোগ্য নয়। (৩)"execution_count": nullআর"outputs": []; কোড অক্ষত থাকে, শুধু ফল আর counter মোছে।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি notebook শেয়ার করার আগের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- সব import আর random seed প্রথম cell-এ আনা
- Kernel restart করা, যাতে মেমরি পুরো ফাঁকা হয়
- সব cell ওপর থেকে নিচে একবার করে চালানো (Run All)
- Error নেই, counter একটানা আর ফল আগের মতো কি না মিলিয়ে দেখা
- দরকার হলে output মুছে (nbstripout) গোপন তথ্য নেই নিশ্চিত হয়ে শেয়ার করা
Jupyter notebook-এ একটি code cell-এর পাশে In [7] লেখা থাকলে ৭ কী বোঝায়?
- Cell-টি notebook-এর সপ্তম cell
- Kernel চালুর পর সপ্তমবার কোড চালানোর সময় cell-টি শেষবার চলেছিল
- Cell-টি মোট ৭ বার চালানো হয়েছে
- Cell-টি চলতে ৭ সেকেন্ড লেগেছে
একটি notebook-এ rate = 0.2 লেখা cell চালানোর পর মুছে ফেলা হলো, কিন্তু পরের cell rate ব্যবহার করে আর ঠিক ফলও দেখাচ্ছে। নতুন kernel-এ jupyter nbconvert --execute দিলে কী হবে?
- আগের মতো একই ফল আসবে, কারণ rate মেমরিতে আছে
- nbconvert নিজে rate = 0.2 বসিয়ে নেবে
NameError: name 'rate' is not definedদিয়ে থেমে যাবে- Cell-টি বাদ দিয়ে বাকিগুলো চালাবে, কোনো সতর্কতা ছাড়া
venv activate না করে চালু করা একটি Jupyter kernel-এ !pip install tabulate লেখার পরও import tabulate কাজ করছে না। সবচেয়ে সম্ভাব্য কারণ ও সমাধান কী?
!pipঅন্য Python-এ বসিয়েছে;%pip install tabulateদিয়ে বসিয়ে kernel restart করা- tabulate নামে কোনো package নেই
- Notebook-এ বাইরের package ব্যবহার করা যায় না
- Cell-টিকে Markdown cell বানাতে হবে
একটি notebook-এর কোড একটুও না বদলে শুধু নতুন kernel-এ আবার চালিয়ে save করা হলো। Git-এর diff-এ তবু অনেক লাইন বদলানো দেখাচ্ছে। কোনটি এর কারণ নয়?
- Execution count বদলেছে
- Output-এর লেখা বা ছবি বদলেছে
- Metadata-য় সময়ের ছাপ বা version যোগ হয়েছে
- Git নিজে কোডের লাইন বদলে দিয়েছে
দুটি পদ্ধতির গতি তুলনার সবচেয়ে ভালো উপায় কোনটি?
- প্রতিটি একবার
time.time()দিয়ে মেপে তুলনা করা - আগে দুটোর ফল এক কি না যাচাই, তারপর
%timeitবাtime.perf_counter()দিয়ে অনেকবার মেপে তুলনা - একটি নিজের ল্যাপটপে, অন্যটি বন্ধুর কম্পিউটারে চালিয়ে তুলনা
- যে কোড ছোট, সেটিকেই দ্রুত ধরে নেওয়া
নিজের কম্পিউটারে JupyterLab চালানোর সবচেয়ে নিরাপদ উপায় কোনটি?
--ip=0.0.0.0আর token বন্ধ করে, যাতে সব ডিভাইস থেকে সহজে খোলা যায়- সিস্টেমের Python-এ বসিয়ে administrator হিসেবে চালানো
- প্রকল্পের venv-এ বসিয়ে
--ip=127.0.0.1আর নিজের বাছা port-এ চালানো, token গোপন রাখা, শেষে নিজের process ID দিয়ে বন্ধ করা - কাজ শেষে
python.exeনামের সব প্রক্রিয়া বন্ধ করে দেওয়া
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Notebook = cell + পেছনের kernel (মেমরি) +
.ipynbJSON ফাইল (কোড, counter, output). মেমরি আর ফাইল এক জিনিস নয়। In [n]চালানোর ক্রম, অবস্থান নয়। ফাঁক বা উল্টো ক্রম মানে লুকানো state-এর সম্ভাবনা।- সততার পরীক্ষা Restart & Run All, বা terminal-এ
jupyter nbconvert --execute. - Magic Python নয়:
%timeitঅনেকবার মাপে,%whoমেমরি দেখায়, package বসাতে%pip,!pipনয়। - পুনর্ব্যবহারযোগ্য কোড
.pymodule-এ; notebook-এ seed আর import ওপরে, ছোট ডেটায় শুরু, শেয়ারের আগে output পরিষ্কার, কোনো গোপন চাবি নয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ০৯ · ডেটার জন্য Python — list, dict, comprehension আর function-এর ঝালাইপরবর্তী পর্বডেটার কাজের Python-এর ভিত।
- AI-এর ভিত্তি আর Python for AI · কোর্স হোম৭৯টি পর্বপুরো কোর্সের রোডম্যাপ।
- Python প্রোগ্রামিং · pip আর venvপাশের ট্র্যাকvirtual environment আর package ব্যবস্থাপনার পুরো ব্যাখ্যা।
- Python প্রোগ্রামিং · module আর packageপাশের ট্র্যাকnotebook থেকে কোড
.pymodule-এ সরানোর ভিত্তি। - Software Engineering & Git · history আর diffগভীরে যানdiff কীভাবে কাজ করে, আর notebook-এ কেন তা কঠিন।