643 lines
222 KiB
Python
Raw Normal View History

2021-11-05 02:15:02 +06:00
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"accelerator": "GPU",
"colab": {
"name": "linear-clf.ipynb",
"provenance": [],
"collapsed_sections": [],
"toc_visible": true
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "ri6UiGU5T5aj"
},
"source": [
"# **সাইকিট-লার্ন দিয়ে একটা সহজ লিনিয়ার ক্লাসিফিকেশন **\n",
"\n",
"চারটার জায়গায় দুটো ফিচার, তিনটার জায়গায় দুটো টার্গেট ভ্যারিয়েবল"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "3sKNXPKNBqwO"
},
"source": [
"চারটার জায়গায় দুটো। প্রস্তাব - এটাকে দুটো দিয়ে দেখান না কেন? বুঝলাম - জিনিসটাকে আরো পানির মতো করতে হবে। আমাকে অনেকে বলেন, আইরিস ডেটাসেটে চারটা অ্যাট্রিবিউট। ফলে ডেটা ভিজ্যুয়ালাইজেশনে একটার ভেতরে আরেকটা চলে যায়। খালি চোখে ডেটার মধ্যে ফারাক বের করা তো দুস্কর। প্রস্তাবটা ভালো। এটা একটা বড় সমস্যাকে আরো রিফাইন করে আনবে আমাদের ভালোভাবে বুঝতে। \n",
"\n",
" সত্যি বলতে সেই আইডিয়াটা নিয়ে লিখেছেন বেশ কয়েকজন লেখক। তবে, এখানে আইডিয়াটা এলো আমার একটা প্রিয় বই থেকে, ২০১৩তে লেখা। লার্নিং সাইকিট-লার্ন:: মেশিন লার্নিং ইন পাইথন, রাউল গ্যারেটার। \"কী করবো সামনে?\" চ্যাপ্টারে দ্রষ্টব্য। \n",
"\n",
"আচ্ছা, তিনটা প্রজাতি না বের করে, একটা প্রজাতি বের করা যায় না? আরো, ভালো! তাহলে তো একটা প্রজাতি ভার্সেস ওই প্রজাতি নয়। মানে, প্রেডিক্ট করতে হবে - ধরুন, ফুলটা \"সেটোসা\" অথবা \"সেটোসা নয়\"! তাহলে তো জিনিসটা একদম পানি হয়ে যাবে। "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "gfRxmu0QT5a5"
},
"source": [
"## লোড করে নেই আইরিস ডেটাসেট "
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "c32_FG83T5a7",
"colab": {}
},
"source": [
"import sklearn\n",
"from sklearn import datasets\n",
"\n",
"iris = datasets.load_iris()\n",
"X_temp = iris.data\n",
"y_temp = iris.target"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "TrVDNHPBT5bL"
},
"source": [
"### ভাগ করে ফেলি টেস্ট এবং ট্রেনিং ডেটাসেট (ফিচার স্কেলিং সহ)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "zTvelXXOT5bM"
},
"source": [
"এখানে আমাদের কাজ হচ্ছে ডেটাসেটকে দুভাগে ভাগ করে ফেলা। ৭৫% ব্যবহার হবে আমাদের ক্লাসিফায়ারকে ট্রেনিং করাতে। ২৫% যাবে ইভ্যালুয়েট করতে। ৪টা ফিচারের জায়গায় আমরা ব্যবহার করবো ২টা মাত্র। সিপাল দৈর্ঘ্য এবং প্রস্থ। শুধুমাত্র সিপাল অংশ। \n",
"\n",
"এর পাশাপাশি আমরা ফিচার স্কেলিং করবো আমাদের ফিচারগুলোর ডেটা রেঞ্জ স্ট্যান্ডার্ডাইজ করার জন্য। প্রতিটা ফিচারের জন্য এটা সব ভ্যালুকে গড় করে সেটাকে বিয়োগ দেয় ওই ফিচার ভ্যালু থেকে। এরপর তার উত্তরকে ভাগ দেয় সেটার স্ট্যান্ডার্ড ডেভিয়েশন দিয়ে। আমাদের এই স্কেলিং এর পর প্রতিটা ফিচারের গড় হবে শূন্য। পাশাপাশি স্ট্যান্ডার্ড ডেভিয়েশন হচ্ছে ১। \n",
"\n",
"এর ফলে ভ্যালুগুলোর স্ট্যান্ডার্ডাইজেশন হয়ে আসে। এটা খানিকটা স্ট্যান্ডার্ড প্র্যাক্টিস হয়ে গেছে ইন্ডিপেন্ডেন্ট ফিচার/ভ্যারিয়েবলগুলোর রেঞ্জকে একটা স্কেলের মধ্যে নিয়ে আসা। এটাকে আমরা ডেটা নর্মালাইজেশন বলতে পারি। এটা আমরা করি ডেটা প্রি-প্রসেসিং এর সময়। \n",
"\n",
"ডেটার রেঞ্জ নিয়ে আমাদের যেহেতু কোন ফিল্টার নেই, সেকারণে একটা ডেটাসেটে বিক্ষিপ্ত ডেটা মেশিন লার্নিংকে বিপদে ফেলতে পারে। বড় বড় ভ্যালুগুলো ফাইনাল আউটকামে সমস্যা করে। আর সেকারণে মেশিন লার্নিং অ্যালগরিদমকে ভালোভাবে কাজ করানোর জন্য এই স্কেলিং দরকার পড়ে অনেক সময়। তবে, \"\"গ্রাডিয়েন্ট ডিসেন্ট\"\" কনভার্জেন্স ভালো কাজ করে স্কেলিং দিয়ে। মজার কথা হচ্ছে এক্স ভ্যালুগুলোকে প্লট করলে আগে এবং পরে একই জিনিস পাওয়া যায়। "
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "RknPx8MPT5bN",
"colab": {}
},
"source": [
"from sklearn.model_selection import train_test_split\n",
"from sklearn import preprocessing\n",
"\n",
"# শুধুমাত্র প্রথম দুটো অ্যাট্রিবিউট নিয়ে আমাদের ডেটাসেট \n",
"X, y = X_temp[:, [0,1]], y_temp\n",
"# আমাদের টেস্টসেট হবে ২৫%, দৈবচয়নের ভিত্তিতে \n",
"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=33)\n",
" \n",
"# ফিচারগুলোকে স্ট্যান্ডার্ডাইজ করছি এখানে \n",
"scaler = preprocessing.StandardScaler().fit(X_train)\n",
"X_train = scaler.transform(X_train)\n",
"X_test = scaler.transform(X_test)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "IRRoCZxmT5bQ"
},
"source": [
"চলুন, দেখি ফিচার স্কেলিং এর পর কি অবস্থা? এখানে গড় হচ্ছে \"\", স্ট্যান্ডার্ড ডেভিয়েশন হচ্ছে \"\"। ট্রেনিংসেটে ঠিকমতো হবে সবকিছু, তবে টেস্টসেটে ব্যাপারটা কাছাকাছি হবে। "
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "Xon7VkoGT5bR",
"outputId": "2b912fb5-aed9-4975-8443-308c01074409",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 50
}
},
"source": [
"import numpy as np\n",
"print ('Training set mean:{:.2f} and standard deviation:{:.2f}'.format(np.average(X_train),np.std(X_train)))\n",
"print ('Testing set mean:{:.2f} and standard deviation:{:.2f}'.format(np.average(X_test),np.std(X_test)))\n"
],
"execution_count": 3,
"outputs": [
{
"output_type": "stream",
"text": [
"Training set mean:0.00 and standard deviation:1.00\n",
"Testing set mean:0.13 and standard deviation:0.71\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "jFf9lDTeT5bV"
},
"source": [
"ফিচার স্কেলিং এর পর ট্রেনিং ডেটাকে প্লটিং করি। একই জিনিস। "
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "DPCGNVefT5bW",
"outputId": "9885b09b-f504-4c82-8d26-ea0dc7e37370",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 295
}
},
"source": [
"# প্লটিং লাইব্রেরি লোড করে নেই \n",
"import matplotlib.pyplot as plt\n",
"\n",
"# তিন প্রজাতির তিনটা আলাদা রং, মার্কার সহ \n",
"colour_mk = [ ['red','s'], ['green','o'], ['blue','x']]\n",
"plt.figure('Training Data')\n",
"\n",
"# লুপে ফেলে দিলাম, x এবং y এক্সিসে \n",
"for i in range(len(colour_mk)):\n",
" xs = X_train[:, 0][y_train == i]\n",
" ys = X_train[:, 1][y_train == i]\n",
" plt.scatter(xs, ys, c=colour_mk[i][0], marker=colour_mk[i][1])\n",
"\n",
"# সাদা ব্যাকগ্রাউন্ড দরকার আমার, গুগল কোলাবে বাড়তি শেড দরকার নেই \n",
"# plt.rcParams['axes.facecolor'] = 'white'\n",
"plt.style.use('default')\n",
"plt.grid(c='grey')\n",
"\n",
"# প্লটিং প্যারামিটার \n",
"plt.title('Training instances, after scaling')\n",
"plt.legend(iris.target_names)\n",
"plt.xlabel('Sepal length')\n",
"plt.ylabel('Sepal width')\n",
"plt.show()\n"
],
"execution_count": 4,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "-fYy0VkkT5bb"
},
"source": [
"### একটা লিনিয়ার বাইনারি ক্লাসিফিকেশন "
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "pfEKD6K5T5bc"
},
"source": [
"মানুষের মাথা প্যাটার্ন বুঝতে ওস্তাদ। এই প্লট থেকে কী বুঝতে পারছেন? ঠিক ধরেছেন। খালি চোখে সেটোসা প্রজাতিকে বোঝা যাচ্ছে একদম আলাদা করে। কেমন হয়, কমপ্লেক্সিটি এড়াতে আমরা যদি বের করতে চাই শুধুমাত্র সেটোসা প্রজাতি বের করতে চাই। মানে, প্রেডিক্ট করতে হবে হয় \"সেটোসা\" অথবা \"সেটোসা না\"? এখন আমাদের দুটো টার্গেট ভ্যারিয়েবল। সেকারণে এটাকে আমরা কনভার্ট করছি বাইনারি ক্লাসিফিকেশন টাস্কে। আমাদের দুটো টার্গেট। হয় \"\" অথবা \"\", তাহলে কী করতে হবে? \"\" নম্বর এবং \"\" নম্বর ক্লাসকে আমরা \"\" বানিয়ে ফেলেছি। \n",
"\n"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "yLoZKBnCT5bd",
"outputId": "7240f679-1330-47c9-80f4-426d5c4bf9c6",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 101
}
},
"source": [
"import copy \n",
"y_train_setosa = copy.copy(y_train) \n",
"# আমাদের ট্রেনিংসেটের ১ এবং ২ ক্লাসকে ১ বানিয়ে ফেলছি \n",
"y_train_setosa[y_train_setosa > 0]=1\n",
"y_test_setosa = copy.copy(y_test)\n",
"y_test_setosa[y_test_setosa > 0]=1\n",
"# এখন দেখি ট্রেনিং টার্গেট ক্লাসগুলো কী কী?\n",
"print ('New training target classes:\\n{0}'.format(y_train_setosa))\n"
],
"execution_count": 5,
"outputs": [
{
"output_type": "stream",
"text": [
"New training target classes:\n",
"[1 0 1 1 1 0 0 1 0 1 0 0 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 1 1 1 1 1 0 0 1 1 0\n",
" 1 1 1 1 0 1 0 1 0 1 1 0 1 1 0 0 1 0 0 0 1 1 0 1 0 1 0 1 1 1 1 1 0 1 0 1 1\n",
" 0 0 0 0 1 1 0 1 1 1 1 0 0 1 1 1 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1\n",
" 0]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "mj_02w5mT5bi"
},
"source": [
"ছবিটা দেখে কী মনে হচ্ছে? একটা প্রজাতি একেবারে আলাদা। এটা আমাদের জন্য ভালো। \n",
"\n",
"আমরা যদি ভালোমতো করে ছবিটা দেখি - তাহলে \"সেটোসা\" প্রজাতিতে আমরা একেবারে আলাদা হাইপারপ্লেন এ দেখতে পাচ্ছি। অর্থাৎ একটা লাইন টেনে দুটো প্রজাতিকে আলাদা করতে পারছি। ব্যাপারটা কমন মেশিন লার্নিং কনসেপ্টে। আমাদের প্রশ্ন হচ্ছে নতুন মাপজোক দিলে সেটা থেকে বের করতে হবে নতুন জিনিসটা কোন প্রজাতির? এখন বাকি প্রজাতিগুলো যেহেতু একটা আরেকটার ভেতরে ঢুকে গেছে, সেকারনে ওই দুটোকে একটা প্রজাতি হিসেবে দেখাচ্ছি। \n",
"\n",
"যেহেতু, আমরা \"সেটোসা\"কে একেবারে একটা লাইন টেনে আলাদা করতে পারছি, সেকারণে এই জিনিসটাকে একটা লিনিয়ার ক্লাসিফিকেশন মডেলে পাঠাতে পারি। মানে, একটা সোজা লাইন টেনে দুটো টার্গেট ক্লাসকে আলাদা করবো এখানে। এটাকে আমরা বলতে পারি ফিচার স্পেসে একটা হাইপারপ্লেন। দুটো ফিচার স্পেসের মধ্যে লাইনটা ডিসিশন বাউন্ডারি। কে কোন প্রজাতির, সেটা নির্ভর করবে কে ওই লাইনটার কোন দিকে আছে। \n",
"\n",
"মনে আছে, আমাদের ওই এরর কমানোর কথা? লিস্ট স্কয়ার রিগ্রেশন, লস ফাংশন? যেটা আসলে বের করে আমাদের প্রতিটা ইনস্ট্যান্স থেকে ডিসিশন বাউন্ডারি কতো দুরে। এখানে আমাদের এই অ্যালগরিদম হাইপারপ্লেনের \"কোএফিসিয়েন্ট\" জানবে লসকে কমিয়ে। এখানে আমরা ইন্টারসেপ্টও জানবো সামনে। \n",
"\n",
"এ কারণে আমরা সাইকিট লার্ন থেকে `SGDClassifier` ব্যবহার করবো এই লিনিয়ার মডেল তৈরি করতে। আমাদের সাইকিট লার্নে \"SGDClassifier\" মডেল হিসেবে থাকলেও এটা আসলে ক্লাসিফায়ার নয়। বরং এটা একটা লিনিয়ার তবে এটাকে অপ্টিমাইজড করা হয়েছে স্টোকাস্টিক গ্র্যাডিয়েন্ট ডিসেন্ট দিয়ে। \n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "5KCIy6waT5bm"
},
"source": [
"এই কাজ আমরা আগেও করেছি। \"linear_model\" কে ইম্পোর্ট করে নিয়ে আসছি sklearn থেকে। একটা ক্লাসিফায়ারের ইনস্ট্যান্স তৈরি করে হাইপারপ্যারামিটারকে বলছি \"লগ\" লস ফাংশন ব্যবহার করতে। এখানে ক্লাসিফায়ার হচ্ছে \"linear_model.SGDClassifier\"। এমুহুর্তে ব্যবহার করবো সব ডিফল্ট ভ্যালু। বেশি ঝামেলায় যাবো না। \n",
"\n"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "0jrNibUVT5bo",
"colab": {}
},
"source": [
"from sklearn import linear_model \n",
"clf = linear_model.SGDClassifier(loss='log', random_state=42)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "djVcNsMYT5bv"
},
"source": [
"এখন কি বাকি? ট্রেনিং করানো। ফিট মেথড কল করছি আমাদের ক্লাসিফায়ারকে ট্রেনিং করানোর জন্য। এখানে আমাদের ট্রেনিং ডেটা হচ্ছে \"সেটোসা\" সেট। \n"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "h9Tf4RvNT5bx",
"outputId": "05109615-5878-442d-ffce-dbf0f07e5968",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 118
}
},
"source": [
"clf.fit(X_train, y_train_setosa)"
],
"execution_count": 7,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"SGDClassifier(alpha=0.0001, average=False, class_weight=None,\n",
" early_stopping=False, epsilon=0.1, eta0=0.0, fit_intercept=True,\n",
" l1_ratio=0.15, learning_rate='optimal', loss='log', max_iter=1000,\n",
" n_iter_no_change=5, n_jobs=None, penalty='l2', power_t=0.5,\n",
" random_state=42, shuffle=True, tol=0.001, validation_fraction=0.1,\n",
" verbose=0, warm_start=False)"
]
},
"metadata": {
"tags": []
},
"execution_count": 7
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "dVVYycW3T5b0"
},
"source": [
"লিনিয়ার মডেল। মনে আছে \"y = mx + b\" এর কথা? নাহ, অংক পিছু ছাড়ছেই না, আমাদেরকে m এবং b পেতে হবে। মানে, clf.coef_ এবং clf.intercept_ ছাড়া আমাদের গতি নেই। \n",
"\n",
"\n",
"এখন এই সমীকরণকে y = mx + b ধারণায় লিখলে কেমন দেখা যাবে?"
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "94ScDMvbT5b1",
"outputId": "4a52e7ed-2c14-4ad4-c105-959ce00bbfbe",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"print (clf.coef_,clf.intercept_)\n"
],
"execution_count": 8,
"outputs": [
{
"output_type": "stream",
"text": [
"[[ 21.76180378 -10.51985219]] [13.90763026]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "bg1L0kSLT5b5"
},
"source": [
"এখন তো ডিসিশন বাউন্ডারি আঁকাই যায়, কি বলুন? কোড দিলাম না ইচ্ছে করে। মেইন লিংকে পাওয়া যাবে। \n",
"\n",
"<img src=\"https://github.com/raqueeb/ml-python/blob/master/assets/line.png?raw=1\">"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "OhODP9vIRYxk",
"colab_type": "text"
},
"source": [
"## এখানে কোড\n",
"\n",
"ধন্যবাদ \"A Gentle Introduction to Machine Learning with Python and Scikit-learn\" বইটাকে। ষষ্ঠ অধ্যায় দেখুন।"
]
},
{
"cell_type": "code",
"metadata": {
"id": "5_JvEW9JReec",
"colab_type": "code",
"outputId": "0ef65ae8-0904-4bda-c2a9-1dcd4eb9bb1c",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 472
}
},
"source": [
"x_min, x_max = X_train[:, 0].min() - .5, X_train[:, 0].max() + .5\n",
"y_min, y_max = X_train[:, 1].min() - .5, X_train[:, 1].max() + .5\n",
"xs = np.arange(x_min, x_max, 0.5)\n",
"\n",
"fig,axes = plt.subplots()\n",
"\n",
"axes.set_aspect('equal')\n",
"axes.set_title('Setosa species classification')\n",
"axes.set_xlabel('Sepal length')\n",
"axes.set_ylabel('Sepal width')\n",
"axes.set_xlim(x_min, x_max)\n",
"axes.set_ylim(y_min, y_max)\n",
"\n",
"plt.sca(axes)\n",
"\n",
"plt.scatter(X_train[:, 0][y_train == 0], X_train[:, 1][y_train == 0], c='red', marker='s')\n",
"plt.scatter(X_train[:, 0][y_train == 1], X_train[:, 1][y_train == 1], c='black', marker='x')\n",
"\n",
"ys = (-clf.intercept_[0]- xs * clf.coef_[0, 0]) / clf.coef_[0, 1]\n",
"\n",
"plt.plot(xs, ys)\n",
"plt.show()"
],
"execution_count": 9,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {
"tags": []
}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "ViC_b6UrT5b-"
},
"source": [
"ঠিক ধরেছেন। এই নীল/কালো রেখাটাই হচ্ছে আমাদের ডিসিশন বাউন্ডারি। প্রতিবার ৩০. x \"সিপাল দৈর্ঘ্য\" - ১৭.৭৮ x \"সিপাল প্রস্থ্য\" - ১৭.৩১ এর আউটপুট যখন শূন্য থেকে বড় হবে তখন সেটা হবে আইরিস সেটোসা, মানে ক্লাস ০। \n",
"\n",
"চলুন, একটা প্রেডিক্ট করি। ফুলটা কি সেটোসা কি না? যদি একটা ফুলের পেটাল প্রস্থ্য .৬ এবং পেটাল দৈর্ঘ্য ৩.২ হয়, তাহলে প্রজাতিটা কি সেটোসা হবে কি হবে না? "
]
},
{
"cell_type": "code",
"metadata": {
"colab_type": "code",
"id": "J3_a65KZ8Si6",
"outputId": "1d41c074-4803-42be-9d00-4d8487f79fc3",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"print ('If the flower has 4.6 petal width and 3.2 petal length is a {}'.format(\n",
" iris.target_names[clf.predict(scaler.transform([[4.6, 3.2]]))]))"
],
"execution_count": 10,
"outputs": [
{
"output_type": "stream",
"text": [
"If the flower has 4.6 petal width and 3.2 petal length is a ['setosa']\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "gVKeWGJq9gZ4"
},
"source": [
"উত্তর: সেটোসা!"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "9dr7-Y2WWeHk",
"colab_type": "text"
},
"source": [
"## মজার বোনাস কাজ\n",
"\n",
"কোড বোঝার দরকার নেই শুরুতে"
]
},
{
"cell_type": "code",
"metadata": {
"id": "Q_JYuiBdWqrP",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 861
},
"outputId": "bf47d8ba-4ca8-40e9-caff-984dfb3c3bb5"
},
"source": [
"from sklearn.tree import DecisionTreeClassifier, plot_tree\n",
"\n",
"# কিছু প্যারামিটার\n",
"n_classes = 3\n",
"plot_colors = \"ryb\"\n",
"plot_step = 0.02\n",
"\n",
"for pairidx, pair in enumerate([[0, 1], [0, 2], [0, 3],\n",
" [1, 2], [1, 3], [2, 3]]):\n",
" # দুটো করেসপন্ডিং ফিচার\n",
" X = iris.data[:, pair]\n",
" y = iris.target\n",
"\n",
" # ট্রেনিং\n",
" clf = DecisionTreeClassifier().fit(X, y)\n",
"\n",
" # ডিসিশন বাউন্ডারি প্লট\n",
" plt.subplot(2, 3, pairidx + 1)\n",
"\n",
" x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1\n",
" y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1\n",
" xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),\n",
" np.arange(y_min, y_max, plot_step))\n",
" plt.tight_layout(h_pad=0.5, w_pad=0.5, pad=2.5)\n",
"\n",
" Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])\n",
" Z = Z.reshape(xx.shape)\n",
" cs = plt.contourf(xx, yy, Z, cmap=plt.cm.RdYlBu)\n",
"\n",
" plt.xlabel(iris.feature_names[pair[0]])\n",
" plt.ylabel(iris.feature_names[pair[1]])\n",
"\n",
" # ট্রেনিং পয়েন্ট প্লট\n",
" for i, color in zip(range(n_classes), plot_colors):\n",
" idx = np.where(y == i)\n",
" plt.scatter(X[idx, 0], X[idx, 1], c=color, label=iris.target_names[i],\n",
" cmap=plt.cm.RdYlBu, edgecolor='black', s=15)\n",
"\n",
"plt.suptitle(\"Decision surface of a decision tree using paired features\")\n",
"plt.legend(loc='lower right', borderpad=0, handletextpad=0)\n",
"plt.axis(\"tight\")\n",
"\n",
"plt.figure()\n",
"clf = DecisionTreeClassifier().fit(iris.data, iris.target)\n",
"plot_tree(clf, filled=True)\n",
"plt.show()"
],
"execution_count": 11,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAkMAAAHHCAYAAAC88FzIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nOydeXhNx/vAPzf7vogEQbPVvhOU1BY0aBWlStWuSilaRVFLUar91trauqC0P61d29gTtVQ1qtbYiZ1URITs987vj5N7k7tklbhZ5vM8eZI7Z+6Z95zMmfPOO+/7jkoIIZBIJBKJRCIppViYWwCJRCKRSCQScyKVIYlEIpFIJKUaqQxJJBKJRCIp1UhlSCKRSCQSSalGKkMSiUQikUhKNVIZkkgkEolEUqqRypBEIpFIJJJSjVSGJBKJRCKRlGqkMiSRSCQSiaRUI5UhMxEVFYVKpWLVqlV5+l7r1q1p3bp1ochUVLh37x49evTAw8MDlUrFggULzC1SvomIiKB58+Y4OjqiUqk4fvz4M21fpVIxffr0InX+AQMG4OvrWyjySPI/thRVnmV/WbVqFSqViqioqBzr7tixg/r162NnZ4dKpeLhw4eFL6Ck0Ci1ypC202t/7Ozs8Pb2JiQkhEWLFhEfH29uEUst77//Pjt37mTixImsWbOGDh06mFukfJGamsrrr7/OgwcPmD9/PmvWrMHHx8fcYpVabt++zfTp05+5QiopecTExNCzZ0/s7e35+uuvWbNmDY6OjgXeTmRkJNOnT8+VciZ5OqzMLYC5mTFjBn5+fqSmpnL37l327dvHmDFjmDdvHtu2baNu3bqF0q6Pjw+JiYlYW1vn6Xu7du0qFHmKEmFhYXTp0oUPP/zQ3KI8FZcvX+batWt88803DBkyxNziFAqJiYlYWeVtGPnmm2/QaDSFJFHW3L59m08++QRfX1/q16//zNt/VuR3bCmqmKu/ZEdERATx8fHMnDmTdu3aFVo7kZGRfPLJJ7Ru3VpaUwuZUq8MdezYkcDAQN3niRMnEhYWxiuvvMKrr77K2bNnsbe3L/B2tdaovGJjY1PgshQF0tLS0Gg02NjYEB0djZubm7lFemqio6MBSsS1ZEV++nBxeUknJCTg4OBgbjHyTH7HlqJKbvpL5vHjWVDcn+0nT54UiiWrWCNKKStXrhSAiIiIMHl89uzZAhArVqzQKz979qzo3r27cHd3F7a2tqJRo0Zi69atRt+PjY0VY8aMET4+PsLGxkZUrFhR9O3bV/z3339CCCGuXr0qALFy5Urdd+7cuSMGDBggKlasKGxsbET58uXFq6++Kq5evaqr06pVK9GqVSu9tu7duycGDRokvLy8hK2trahbt65YtWqVXh1te1988YVYvny58Pf3FzY2NiIwMFD8/fffOd6vlJQUMX36dPH8888LW1tbUaZMGREUFCR27dqVrWxCCNG/f3/h4+NjUpb58+cLf39/YWFhIebPny8Aox8hhIiJiRFjx44VtWvXFo6OjsLZ2Vl06NBBHD9+3Ki9xMREMW3aNFGlShVha2srypcvL7p16yYuXbqkq6NWq8X8+fNFzZo1ha2trfDy8hJDhw4VDx48yPFeCCHE3r17xYsvvigcHByEq6urePXVV0VkZKTeNRteh6l7oyUv12eKpKQkMWbMGFG2bFnh5OQkOnfuLG7cuCEAMW3aNL26N2/eFAMHDhReXl7CxsZG1KxZU3z33XdG58zNfTQ8/6NHj8To0aN1/d7T01O0a9dO/PPPP3r3JnN/EEKIx48fiw8++EBUqlRJ2NjYiKpVq4ovvvhCaDQavXqAGDFihNi8ebOoVauWTv7t27dne3/Cw8NN9i3t89eqVStRq1YtcfToUdGiRQthb28vRo8erbu3U6dOFQEBAcLGxkZUqlRJjBs3TiQlJRm1s2bNGtGwYUNhZ2cn3N3dxRtvvCGuX7+erWxZ3RMhhJg2bZowHKZ37dolgoKChKurq3B0dBRVq1YVEydO1B03Nbb0799fODo6ips3b4ouXboIR0dHUbZsWTF27FiRlpamd/779++Lt956Szg7OwtXV1fRr18/cfz4caNzmkI7rv7xxx9i6NChokyZMsLZ2Vn07dvX6NnasmWL6NSpk6hQoYKwsbER/v7+YsaMGUby5Hb8+Pfff4UQuR+jT58+Ldq0aSPs7OxExYoVxcyZM8V3330nAL0x15BWrVoZ9aP+/fvrjv/1118iJCREuLi4CHt7e9GyZUtx8OBBvXNERUWJ4cOHi6pVqwo7OztRpkwZ0aNHD712tffS8Cc8PFwIYfzsafHx8dGTR3ueffv2ieHDhwtPT0/h5uamOx4aGqoby5ycnESnTp3E6dOn9c6Zm3dTcafUW4ayom/fvkyaNIldu3bx9ttvA3DmzBmCgoKoWLEiH330EY6Ojvzyyy907dqVjRs30q1bNwAeP35MixYtOHv2LIMGDaJhw4bcv3+fbdu2cfPmTcqWLWuyze7du3PmzBnee+89fH19iY6OZvfu3Vy/fj1LE2liYiKtW7fm0qVLjBw5Ej8/P9avX8+AAQN4+PAho0eP1qv/008/ER8fzzvvvINKpeLzzz/ntdde48qVK9nOwKZPn86cOXMYMmQITZo04dGjRxw9epRjx47Rvn37fNxhWLlyJUlJSQwdOhRbW1saNmzImjVr6Nu3L+3bt6dfv366uleuXGHLli28/vrr+Pn5ce/ePZYvX06rVq2IjIzE29sbALVazSuvvMLevXvp1asXo0ePJj4+nt27d3P69GkCAgIAeOedd1i1ahUDBw5k1KhRXL16la+++op///2XQ4cOZXsv9uzZQ8eOHfH392f69OkkJiayePFigoKCOHbsGL6+vrzzzjtUrFiR2bNnM2rUKBo3bky5cuWyPGdury8rhgwZwtq1a3nzzTdp3rw5YWFhvPzyy0b17t27xwsvvIBKpWLkyJF4enqyfft2Bg8ezKNHjxgzZkye7qMhw4YNY8OGDYwcOZKaNWsSExPDwYMHOXv2LA0bNjT5HSEEr776KuHh4QwePJj69euzc+dOxo0bx61bt5g/f75e/YMHD7Jp0ybeffddnJ2dWbRoEd27d+f69et4eHiYbKNGjRrMmDGDqVOnMnToUFq0aAFA8+bNdXViYmLo2LEjvXr14q233qJcuXJoNBpeffVVDh48yNChQ6lRowanTp1i/vz5XLhwgS1btui+/+mnnzJlyhR69uzJkCFD+O+//1i8eDEtW7bk33//LRArwpkzZ3jllVeoW7cuM2bMwNbWlkuXLnHo0KEcv6tWqwkJCaFp06b873//Y8+ePXz55ZcEBAQwfPhwADQaDZ07d+bvv/9m+PDhVK9ena1bt9K/f/88yTly5Ejc3NyYPn0658+fZ+nSpVy7do19+/ahUqkAxW/TycmJDz74ACcnJ8LCwpg6dSqPHj3iiy++yLENw/GjTJkyuR6j7969S5s2bUhLS9PVW7FiRa5WASZPnky1atVYsWKFzs1C+zyEhYXRsWNHGjVqxLRp07CwsGDlypUEBwdz4MABmjRpAijLbH/++Se9evWiUqVKREVFsXTpUlq3bk1kZCQODg60bNmSUaNGsWjRIiZNmkSNGjUAdL/zyrvvvounpydTp07lyZMnAKxZs4b+/fsTEhLC3LlzSUhIYOnSpbz44ov8+++/uvdOft5NxQ5za2PmIifLkBBCuLq6igYNGug+t23bVtSpU0dvRqjRaETz5s1FlSpVdGVTp04VgNi0aZPRObUzXcPZW2xsrG62kx2G1pcFCxYIQKxdu1ZXlpKSIpo1ayacnJzEo0eP9Nrz8PDQm6Ft3bpVAOLXX3/Ntt169eqJl19+OU+yaclqZufi4iKio6ON6pM++89MUlKSUKvVemVXr14Vtra2YsaMGbqy77//XgBi3rx5RufV3vsDBw4IQPz44496x3fs2GGy3JD69esLLy8vERMToys7ceKEsLCwEP369dOVaa0R69evz/Z8ebk+U2hn7e+++65e+Ztvvmk0exw8eLCoUKGCuH//v
"text/plain": [
"<Figure size 640x480 with 6 Axes>"
]
},
"metadata": {
"tags": []
}
},
{
"output_type": "display_data",
"data": {
"image/png": "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
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {
"tags": []
}
}
]
}
]
}