{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Linear Regression using Theano" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Import the required modules\n", "%pylab inline\n", "from theano import tensor as T\n", "from theano import function\n", "from theano import pp\n", "from theano import Param\n", "from theano import grad\n", "from theano import shared\n", "import numpy as np\n", "\n", "# Define the symbolic Variables\n", "X, y, W = T.matrices('training-set', 'testing-set', 'weights')\n", "m, a = T.scalars('no-of-samples', 'learning-rate')\n", "\n", "# Define the cost function and the gradient\n", "cost_fn = (((T.dot(X, W) - y)**2)/(2. * m)).sum()\n", "grad_fn = (T.dot(T.transpose(X), T.dot(X, W) - y))/(m)\n", "updated_wts = W - a * grad_fn\n", "\n", "# Compile the Theano functions\n", "cost = function([X, y, W, m], cost_fn)\n", "new_wts = function([X, y, W, m, a], updated_wts)\n", "\n", "# Prepare the dataset\n", "X_val = np.array([[1],[4],[6],[8],[10],[12],[14]])\n", "bias = np.ones((X_val.shape[0], 1))\n", "X_val = np.hstack((X_val, bias))\n", "y_val = np.array([[2], [3], [4], [5], [6], [7], [8]])\n", "m_val = X_val.shape[0]\n", "W_val = np.zeros((X_val[0].shape[0],1), dtype=np.float)\n", "a_val = .001\n", "iterations = 10000\n", "\n", "# Calculate the cost function and gradient descent \n", "# and update the weights\n", "for i in xrange(iterations):\n", " cst = cost(X_val, y_val, W_val, m_val)\n", " W_val = new_wts(X_val, y_val, W_val, m_val, a_val) \n", " \n", "# Perform the perdictions\n", "X_test = np.array([[8, 1], [9, 1], [10, 1]])\n", "y_test = np.array([[5], [5.5], [6]])\n", "y_pred = np.dot(X_test, W_val)\n", "for X_test_c , y_test_c, y_pred_c in zip(X_test, y_test, y_pred):\n", " print \"Sample {} Actual Value = {} Predicted Value = {} \".format(X_test_c, y_test_c, y_pred_c)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Populating the interactive namespace from numpy and matplotlib\n", "Sample [8 1] Actual Value = [ 5.] Predicted Value = [ 5.03978118] " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n", "Sample [9 1] Actual Value = [ 5.5] Predicted Value = [ 5.52552955] \n", "Sample [10 1] Actual Value = [ 6.] Predicted Value = [ 6.01127793] \n" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Corresponding Class with convenient `fit`, `predict` interfaces as in case of sklearn." ] }, { "cell_type": "code", "collapsed": false, "input": [ "class LinearRegression:\n", " '''\n", " This class performs linear regression.\n", " '''\n", " def __init__(self, alpha=0.001, iterations=1000):\n", " '''\n", " 1. alpha is the learning rate. \n", " The default value of alpha is 0.001.\n", " 2. iterations is the number of times the weights will be updated.\n", " The default value is 1000\n", " '''\n", " # Set the learning rate\n", " self.alpha = alpha\n", " # Set the number of iterations\n", " self.iterations = iterations\n", " \n", " # Define the symbolic Variables\n", " X, y, W = T.matrices('training-set', 'testing-set', 'weights')\n", " m, a = T.scalars('no-of-samples', 'learning-rate')\n", " \n", " # Define the cost function and the gradient functions\n", " cost_fn = (((T.dot(X, W) - y)**2)/(2. * m)).sum()\n", " grad_fn = (T.dot(T.transpose(X), T.dot(X, W) - y))/(m)\n", " updated_wts = W - a * grad_fn\n", " \n", " # Compile the Theano functions\n", " self.cost = function([X, y, W, m], cost_fn)\n", " self.new_wts = function([X, y, W, m, a], updated_wts)\n", " self.pred = function([X, W], T.dot(X, W))\n", " \n", " def fit(self, X, y):\n", " '''\n", " The fit member function is used to train the classifier.\n", " 1. X is array-like data vector\n", " 2. y is array-like target vector\n", " '''\n", " # Add the bias feature to the data samples\n", " bias = np.ones((X.shape[0], 1))\n", " X = np.hstack((X, bias))\n", " \n", " # m is the number of data samples used\n", " m = X.shape[0]\n", " \n", " # Initialize all weights to 0\n", " self.W = np.zeros((X[0].shape[0],1), dtype=np.float)\n", " \n", " # Iterate and update the cost function and the weights\n", " for i in xrange(self.iterations):\n", " cst = self.cost(X, y, self.W, m)\n", " self.W = self.new_wts(X, y, self.W, m, self.alpha) \n", " \n", " # Set the coef_ and intercept_\n", " self.coef_ = self.W[:-1]\n", " self.intercept_ = self.W[-1]\n", " \n", " def predict(self, X):\n", " '''\n", " This is function to predict target. \n", " \n", " 1. X is array-like data vector whose target vector is predicted\n", " '''\n", " # Return the predicted target values\n", " return self.pred(X, self.W)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": [ "# Preparea a small dataset\n", "# The dataset is a generated\n", "# using the function of y = X/2 + 1\n", "X_train = np.array([[1],[4],[6],[8],[10],[12],[14]])\n", "y_train = np.array([[2], [3], [4], [5], [6], [7], [8]])\n", "X_test = np.array([[8, 1], [9, 1], [10, 1]])\n", "y_test = np.array([[5], [5.5], [6]])\n", "\n", "# Create the classifier object\n", "clf = LinearRegression(iterations=10000)\n", "# Perform the training\n", "clf.fit(X_train, y_train)\n", "# Predict using the classifier\n", "y_pred = clf.predict(X_test)\n", "\n", "# Display the results\n", "for X_test_c , y_test_c, y_pred_c in zip(X_test, y_test, y_pred):\n", " print \"Sample {} Actual Value = {} Predicted Value = {} \".format(X_test_c, y_test_c, y_pred_c)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Sample [8 1] Actual Value = [ 5.] Predicted Value = [ 5.03978118] \n", "Sample [9 1] Actual Value = [ 5.5] Predicted Value = [ 5.52552955] \n", "Sample [10 1] Actual Value = [ 6.] Predicted Value = [ 6.01127793] \n" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "# NOTE :: This graphic will only work for 1-D features, although the\n", "# code can work for any dimensions\n", "# Draw the regression line\n", "plot(X_train, y_train, \"*\")\n", "x_min, x_max, y_min, y_max = 0 , 15, 0, 15\n", "axis([x_min, x_max, y_min, y_max])\n", "y_min_val = (clf.coef_[0][0] * x_min + clf.intercept_[0])\n", "y_max_val = (clf.coef_[0][0] * x_max + clf.intercept_[0])\n", "plot([x_min, x_max], [y_min_val, y_max_val])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 4, "text": [ "[]" ] }, { "metadata": {}, "output_type": "display_data", "png": "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\nG1SyAAAAAElFTkSuQmCC\n", "text": [ "" ] } ], "prompt_number": 4 } ], "metadata": {} } ] }