Skip to content

Instantly share code, notes, and snippets.

@yashppawar
Created July 3, 2021 13:02
Show Gist options
  • Save yashppawar/391598d73ed1c5f15758a2b992814318 to your computer and use it in GitHub Desktop.
Save yashppawar/391598d73ed1c5f15758a2b992814318 to your computer and use it in GitHub Desktop.
Attempting to create a custom ML model using scikit learn
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# In this notebook We will be creating a model on our own using basic Classes from sklearn\n",
"\n",
"refering [Building A Custom Model in Scikit-Learn](https://towardsdatascience.com/building-a-custom-model-in-scikit-learn-b0da965a1299)\n",
"\n",
"\n",
"## <span style=\"color: #ff6363;\">Warning: OOP Ahead!</span> ⚠\n",
"\n",
"#### what is a mixin?\n",
"[What is a mixin and why are they useful](https://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-are-they-useful)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"from sklearn.base import RegressorMixin\n",
"from sklearn.base import BaseEstimator\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scikit-Learn’s Template\n",
"One of the best things about Scikit-Learn is its incredible consistency. Fitting one type of model is nominally the same as fitting any other type of model. That is, modeling in Scikit-Learn is as easy as:\n",
"\n",
"```py\n",
"model = MyModel(parameters)\n",
"model.fit(X, y)\n",
"```\n",
"\n",
"And that’s it! You can now analyze your model, probably with the help of the model’s `.predict()` and `.score()` methods. In fact, there are 5 methods every Scikit-Learn estimator is guaranteed to have:\n",
"\n",
"- `.fit()`\n",
"- `.predict()`\n",
"- `.score()`\n",
"- `.set_params()`\n",
"- `.get_params()`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Interface\n",
"before we start making one model on our own, we should take a look about what are interfaces.\n",
"> Interfaces specify what a class must do and not how. It is the blueprint of the class.\n",
"\n",
"Why are we using the base class to make our own model?\n",
"\n",
"**Ans: We are using the `BaseEstimator` to create our model because it helps us to be consistent and enables us to use them with sklearn `pipelines`, those base classes act like Interface for our model.**\n",
"\n",
"## What Does Scikit-Learn Give Us?\n",
"In order to comply with Scikit-Learn, our model needs to inherit from some mixin. A mixin is just a class that was never intended to work on its own, instead it just contains a lot of methods that you can add to your current class via inheritance.\n",
"\n",
"example Mixin: `RegerssorMixin`, `ClassifierMixin`, `ClusterMixin`, `TransformerMixin`\n",
"\n",
"let's build one\n",
"# Example 1: the Null Model"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"import numpy as np\n",
"from sklearn.base import RegressorMixin"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
"# creating the model\n",
"\n",
"class NullRegressor(RegressorMixin, BaseEstimator): # base estimator needed for cv (cross-validation) \n",
" def fit(self, X=None, y=None):\n",
" # prediction is the mean of training `y`\n",
" self.y_bar_ = np.mean(y)\n",
" return self\n",
"\n",
" def predict(self, X=None):\n",
" '''\n",
" Returns the prediction for the given `X`\n",
" '''\n",
" # x.shape -> (100, 2)\n",
" # x.shape[0] is the length of the dataset\n",
" return np.ones(X.shape[0]) * self.y_bar_ # return predictions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**RegerssorMinix**: gives the basic methods needed, with the default regression scoring, etc.\n",
"\n",
"**BaseEstimator**: gives the `.get_params()` and other methods"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"# create a random dataset\n",
"X = np.random.randn(100, 2) # create a feature matrix\n",
"y = np.random.randn(100) # create labels"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(100, 2)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X.shape"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"# spliting the data \n",
"from sklearn.model_selection import train_test_split\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y, \n",
" test_size=0.2)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"# testing our state of the art 😂😅 model\n",
"model = NullRegressor()\n",
"model.fit(X_train, y_train) # train the model on training data\n",
"predictions = model.predict(X_test)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([-0.24879846, -0.24879846, -0.24879846, -0.24879846, -0.24879846,\n",
" -0.24879846, -0.24879846, -0.24879846, -0.24879846, -0.24879846,\n",
" -0.24879846, -0.24879846, -0.24879846, -0.24879846, -0.24879846,\n",
" -0.24879846, -0.24879846, -0.24879846, -0.24879846, -0.24879846])"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# let's have a look on the predictions\n",
"predictions"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"-0.24879846400007533"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.mean(predictions)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3.0814879110195774e-33"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.var(predictions)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5.551115123125783e-17"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.std(predictions) \n",
"# we have very less standard deviation and variance"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"-0.525798882480081"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.score(X_test, y_test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"See, we didn't define the `.score()` method, but we can use it on our model 🤔 this is because of using the `RegerssorMixin`\n",
"\n",
"let's score our model using mae, mse and others"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"from sklearn.metrics import mean_absolute_error, mean_squared_error\n",
"from sklearn.metrics import r2_score"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.798658648269714"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mean_absolute_error(y_test, predictions)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1.0838184915804245"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.sqrt(mean_squared_error(y_test, predictions))"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"-0.525798882480081"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"r2_score(y_test, predictions)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"# lets try cross validation with our created model, will be fun\n",
"from sklearn.model_selection import cross_val_score"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([-0.2288877 , -0.06137582, -0.04124627, -0.03664761, -0.06969603])"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cross_val_score(NullRegressor(), X, y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Example 2: \"Truning\" Your Cluster Using Grid Search"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [],
"source": [
"from sklearn.base import TransformerMixin\n",
"from sklearn.cluster import KMeans\n",
"from sklearn.preprocessing import OneHotEncoder\n",
"\n",
"class KMeansTransformer(TransformerMixin):\n",
" '''\n",
" A custom transformer which will help in creating a extra col in the dataset\n",
" this extra col is determined from KMeans algorithm 🤷‍♂️\n",
" '''\n",
" def __init__(self,*args,**kw):\n",
" self.model = KMeans(*args,**kw)\n",
"\n",
" def fit(self, X):\n",
" self.x = X\n",
" self.model.fit(X)\n",
"\n",
" def transform(self, X):\n",
" # Need to reshape into column vector in order to use \n",
" classification = self.model.predict(X).reshape(-1, 1)\n",
"\n",
" self.one_hot = OneHotEncoder(\n",
" categories=\"auto\",\n",
" sparse=False,\n",
" drop=\"first\"\n",
" )\n",
"\n",
" classification_matrix = self.one_hot.fit_transform(classification)\n",
" return np.hstack([self.x, classification_matrix])\n",
"\n",
" def fit_transform(self, X, y=None):\n",
" self.fit(X)\n",
" return self.transform(X)"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [],
"source": [
"# another imports\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.linear_model import LogisticRegression\n",
"from sklearn.datasets import make_blobs"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [],
"source": [
"X, y = make_blobs(\n",
" n_samples=100, \n",
" n_features=2, \n",
" centers=3\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [],
"source": [
"import seaborn as sns"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"sns.scatterplot(x=X[:, 0], y=X[:,1], hue=y, palette='viridis');"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1.0"
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.pipeline import Pipeline\n",
"\n",
"pipe = Pipeline([\n",
" ('scalar', StandardScaler()),\n",
" ('Kmeans_tranformer', KMeansTransformer()),\n",
" ('logistic_regression', LogisticRegression(\n",
" penalty = 'none',\n",
" solver = 'lbfgs'\n",
" ))\n",
"])\n",
"\n",
"pipe.fit(X, y)\n",
"pipe.score(X, y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### perfect score 👍"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
"interpreter": {
"hash": "f012af5569369bb11e6fef237cb06042ce00a0eac0407b1eeb6f6a98b6e7f07b"
},
"kernelspec": {
"display_name": "Python 3.9.1 64-bit ('base': conda)",
"name": "python3"
},
"language_info": {
"name": "python",
"version": ""
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment