You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ml-data-analysis/linear-reg/Linear Regression with Grad...

1020 lines
88 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Applying Gradient Descent on 1d feature set\n",
"\n",
"### Data:\n",
"**Math Scores and Drug Concentrations** from http://www.stat.ufl.edu/~winner/datasets.html\n",
"\n",
"[link to data](http://www.stat.ufl.edu/~winner/data/lsd.dat)\n",
"\n",
"\n",
"### Goal\n",
"Fit a linear regression to predict future math scores based on drug concentration using Gradient Descent Algorithm"
]
},
{
"cell_type": "code",
"execution_count": 218,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Library imports and dependencies\n",
"%matplotlib notebook\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"import time\n",
"\n",
"from matplotlib import animation, rc\n",
"from IPython.display import HTML\n",
"\n",
"\n",
"#rc('animation', html='html5')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Gradient descent algorithm"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 235,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" this.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width);\n",
" canvas.attr('height', height);\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'];\n",
" var y0 = fig.canvas.height - msg['y0'];\n",
" var x1 = msg['x1'];\n",
" var y1 = fig.canvas.height - msg['y1'];\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x;\n",
" var y = canvas_pos.y;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" // select the cell after this one\n",
" var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
" IPython.notebook.select(index + 1);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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v3LXXXc5ItXs299Zeeutt7jiiiscO+3atXPemx46dChvv/02b7zxBscee+wWY/z555/Tvn17jjnmGC699FLnWaOrr77a8f+FF16o8LOJxrXmlbR+tTz9kASwpzhDZ0wCOHQhkUMiIAJRIqCFSvkC+JSnT6H97u259uBroxRO+RoyAomEQsjcTcqdfBLA8UBMCN9xxx1bFMC2Q2zi1HZ+H330UWcXNn4HeMOGDey33340b96cl19+uULeP//8M3Xq1PnTz20sNW7cmBNPPJGnnnrK+Zn93TbbbEOlSpU21bUd6n322Qd7p9d2kLdUTjnlFL788kvnT+XKlZ2q//73vx3hbbverVq1Kvfjica15pWkfpV8rSQB7CvewI1LAAceAjkgAiIQZQJaqJQvgK949QqKC4oZetzQKIdXvgdMIJFQCNi9tJpPVgA/++yz3HPPPXzzzTeOSLNdxrvvvtsRZrHy/PPPO3VsB9QEmB0FtmPAF1xwgVNl8uTJXH/99UydOpXff//d+azZsePB2S7JCGATyM899xyfffYZe+6552YCeMyYMRx55JGMGzeOjh07ptwF262149DvvvvuFj97+umnOwLWuFZUTKzXrl3b2fG95ZZbNlX7448/nF3pfv36OUepyyuJxrXmlZRD6/kHJIA9RxoqgxLAoQqHnBEBEYgaAS1UyhfA9465l9E/jObtv70dtZDK3xARSCQUQuRq0q4kI4CHDRvGRRddxBlnnMHZZ5/NDz/84Aiq7bff3hGzNWvWdO6edu7c2Tn2e9xxx7F+/Xq+/vprR+hec801m+7Gmuiz+7EmoufOnescAbbd1S2VZO7p2udju57JdD6RAP7++++d+7fvvPMOhx56KE2aNNlMANsd3n/9618UFhY6R6tNpBoTE6x2xLl69eoVuvLLL7/QqFEjevXq5dz7raiUlJSwxx57OLu3r732WoX17IuJFi1aOEede/To8ad6toO87777MnLkSAngZAZHCOtIAIcwKB66JAHsIUyZEgERyD8CEsDlC+CXp77MjRNv5Ms+X+bfoFCPPSOQjwLYhOwuu+ziCChLzhQrEyZMcAShJZWy+6b33nsvAwcO5KeffiqXt4nDgw46yNlNNVuplPgjwRV9zp44S1Yom41EAth2dq3f9gWBlfIE8MUXX8xjjz3mHG+2nW67B2x3gs320UcfzSuv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\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"ename": "KeyboardInterrupt",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-235-b20746d8c1e7>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 106\u001b[0;31m \u001b[0mgradient_descent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.07\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1000\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 107\u001b[0m \u001b[0;31m#for i in range(40):\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[0;31m# w, b = 1, i\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-235-b20746d8c1e7>\u001b[0m in \u001b[0;36mgradient_descent\u001b[0;34m(alpha, iterations)\u001b[0m\n\u001b[1;32m 94\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 95\u001b[0m \u001b[0max_loss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautoscale_view\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 96\u001b[0;31m \u001b[0mfig\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcanvas\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 97\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msleep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0mfps\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/home/spike/.pyenv/versions/3.5.1/lib/python3.5/site-packages/matplotlib/backends/backend_webagg_core.py\u001b[0m in \u001b[0;36mdraw\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 181\u001b[0m \u001b[0mbackend_agg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRendererAgg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrelease\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 182\u001b[0m \u001b[0;31m# Swap the frames\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 183\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmanager\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrefresh_all\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 184\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 185\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdraw_idle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/home/spike/.pyenv/versions/3.5.1/lib/python3.5/site-packages/matplotlib/backends/backend_webagg_core.py\u001b[0m in \u001b[0;36mrefresh_all\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 472\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrefresh_all\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 473\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweb_sockets\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 474\u001b[0;31m \u001b[0mdiff\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcanvas\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_diff_image\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 475\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0ms\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweb_sockets\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 476\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend_binary\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdiff\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/home/spike/.pyenv/versions/3.5.1/lib/python3.5/site-packages/matplotlib/backends/backend_webagg_core.py\u001b[0m in \u001b[0;36mget_diff_image\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 238\u001b[0m _png.write_png(\n\u001b[1;32m 239\u001b[0m \u001b[0moutput\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0muint8\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreshape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 240\u001b[0;31m self._png_buffer)\n\u001b[0m\u001b[1;32m 241\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 242\u001b[0m \u001b[0;31m# Swap the renderer frames\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mKeyboardInterrupt\u001b[0m: "
]
}
],
"source": [
"df = pd.read_csv('lsd.dat.txt', '\\s+', header=None, names=['Drug', 'MathScore'])\n",
"\n",
"\n",
"x = df['Drug']\n",
"y = df['MathScore']\n",
"\n",
"_y = y/(y.max() - y.min())\n",
"_x = x/(x.max() - x.min())\n",
"\n",
"\n",
"fig = plt.figure(figsize=(12, 4))\n",
"ax1 = fig.add_subplot(1,2,1)\n",
"\n",
"\n",
"ax1.set_xlim(0, x.max() + 1)\n",
"\n",
"ax1.scatter(x, y, label='')\n",
"ax1.set_xlabel('Drug Concentration')\n",
"ax1.set_ylabel('Math Score')\n",
"ax1.set_title('Linear Regression')\n",
"\n",
"\n",
"ax_loss = fig.add_subplot(1,2,2)\n",
"ax_loss.set_xlabel('Iterations')\n",
"ax_loss.set_ylabel('Loss')\n",
"\n",
"\n",
"\n",
"## Helper Functions\n",
"def hypothesis(w, b, x):\n",
" return w*x + b\n",
" \n",
"def get_loss(w, b):\n",
" mean_squares = []\n",
" for (xi,yi) in zip(x,y):\n",
" mean_squares.append((hypothesis(w, b, xi) - yi)**2)\n",
" loss = sum(mean_squares) / 2*len(mean_squares)\n",
" return loss\n",
"\n",
"def draw_line(weight, bias):\n",
" xx = np.linspace(1, x.max())\n",
" ax1.plot(xx , xx*weight+bias, color='red', label='Loss = {}'.format(np.floor(loss(weight, bias))))\n",
" ax1.legend() \n",
"\n",
"def init_hyp_line():\n",
" line.set_data([], [])\n",
"\n",
"def setup_hypothesis(): \n",
" line, = ax1.plot([],[], color='red')\n",
" #print(line)\n",
" return line\n",
"\n",
"fps = 60\n",
"\n",
"def draw_hyp(weight, bias, line, loss):\n",
" weight, bias\n",
" xx = np.linspace(1, x.max())\n",
" yy = xx*weight+bias\n",
" line.set_data(xx,yy)\n",
" #line.set_label('Loss = {}'.format(np.floor(loss)))\n",
" #ax1.legend()\n",
" \n",
" #ax1.relim()\n",
" #ax1.autoscale_view(True,True,True) \n",
" #fig.canvas.draw()\n",
"\n",
"def gradient_descent(alpha, iterations):\n",
" \n",
" ax_loss.set_xlim(0, iterations)\n",
" hypothesis_line = setup_hypothesis()\n",
" \n",
" w, b = 1, 1\n",
" \n",
" \n",
" losses = []\n",
" iters = []\n",
" for i in range(iterations):\n",
" ax_loss.clear()\n",
" \n",
" hxx = hypothesis(w,b,x)\n",
" \n",
" \n",
" b = b - alpha/len(y)* sum([hx - y for hx, y in zip(hxx, y)])\n",
" w = w - alpha/len(y) * sum([(hx - y)*x for hx, y, x in zip(hxx, y, x)])\n",
" \n",
" loss = get_loss(w,b)\n",
" losses.append(loss)\n",
" iters.append(i)\n",
" ax_loss.plot(iters, losses, color='green', label='Loss = {}\\nIter={}'.format(np.floor(loss), i))\n",
" ax_loss.legend()\n",
" ax_loss.relim()\n",
" \n",
" draw_hyp(w,b,hypothesis_line, loss)\n",
" \n",
" ax_loss.autoscale_view(True,True,True)\n",
" fig.canvas.draw()\n",
" time.sleep(1/fps)\n",
"\n",
" \n",
"\n",
"\n",
" \n",
" \n",
" \n",
" \n",
"gradient_descent(0.07, 1000)\n",
"#for i in range(40):\n",
"# w, b = 1, i\n",
"# draw_hyp(w,b,hypothesis_line, loss(w,b))\n",
"# time.sleep(1/fps)\n",
" \n",
" \n",
"\n",
"\n",
"#plt.draw()\n",
"\n",
" \n",
"\n",
"#anim = animation.FuncAnimation(fig, animate, init_func=init_hyp_line, interval=10, frames=10)\n",
"#HTML(anim.to_html5_video())\n",
"#def gradient_descent(learn_rate):\n",
"# for i in range(100):\n",
" \n",
" \n",
" \n",
" \n",
"#test_w = -5\n",
"#test_b = 20\n",
"#draw_line(test_w, test_b)\n",
"#loss(test_w, test_b)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"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.5.1"
}
},
"nbformat": 4,
"nbformat_minor": 1
}