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.

1037 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": 234,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" Drug MathScore\n",
"0 1.17 78.93\n",
"1 2.97 58.20\n",
"2 3.26 67.47\n",
"3 4.69 37.47\n",
"4 5.83 45.65\n",
"5 6.00 32.92\n",
"6 6.41 29.97\n"
]
},
{
"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,iVBORw0KGgoAAAANSUhEUgAAA8AAAAFACAYAAABz+RVqAAAgAElEQVR4XuydCbyV0/rHv5WTJkIhGUIyXFIZMxWKImPcf8YmZMhNhlCZypAUmSLK7Lo3EXEvzpUUmaVkKHOEonnOOdX5f563d2c77X323me/737f/e7f+nzOR85Z7/M+6/usc9b67bXWs6qgIgIiIAIiIAIiIAIiIAIiIAIiIAIFQKBKAbRRTRQBERABERABERABERABERABERABJIDVCURABERABERABERABERABERABAqCgARwQYRZjRQBERABERABERABERABERABEZAAVh8QAREQAREQAREQAREQAREQAREoCAISwAURZjVSBERABERABERABERABERABERAAlh9QAREQAREQAREQAREQAREQAREoCAISAAXRJjVSBEQAREQAREQAREQAREQAREQAQlg9QEREAEREAEREAEREAEREAEREIGCICABXBBhViNFQAREQAREQAREQAREQAREQAQkgNUHREAEREAEREAEREAEREAEREAECoKABHBBhFmNFAEREAEREAEREAEREAEREAERkABWHxABERABERABERABERABERABESgIAhLABRFmNVIEREAEREAEREAEREAEREAEREACWH1ABHJDoAvwGLAb8H2SV9rPWwO75sYlz97SCPghzto6YB7wBnAt8LNnbwqvIWv/m0D38Looz0RABERABERABERABCSA1QdEIDcETAA/CjSpQADvAmwOfJoblzx7S0wA3wq8DFQHWgI3ATOBg4G1nr0tnIaaAUvLfRAQTk/llQiIgAiIgAiIgAgUMAEJ4AIOvpqeUwLpCOCcOpThyzYB1iR5JiaAz3dFfqxaP+Bm4BDgwwzfl031qoD9bYu66M6GkZ4VAREQAREQAREQgYIkIAFckGFXowMgkI4AftzdAm0rwVZiwvIiYHvgAqAm8DZwMfBLuXbYz3sCewDLgXFAH2BRXD37+dluHROKtkJrIvWVuDqx91rdnYFzgG2BesCSBOySCeD2rt3/A56Le87aYKvDf3fbZe0YBQwCyuLq7QfcAxwA/A6MAGoA1wPme6zYluvbgGXAhcCO7jO2km4+3wKcCNR3V2jvAkbGPW9tuwNo69ZfCHzsbmeeD1Rz/T3T9dfYznC3d7/r2pkFTCi3Bfog1y9bAbe/te8D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OyIgAgVJQAI4sQBufEdjHuz4IMc0PqYg+4UanVsChSiATRTuvffeJNrmaxmQDzjgAMaMGYMJyaOOOoqzzjrLSTBlK5t169q15euLsbP6JgptBdhWSm1FNZ1iq8ypigngFi1aVFjNsj/feOON3H333Y6f33//PSZMv/76a+x8sAlSK7aV21a6LUFWr169HEFrGZdNEJvotNXsZOXnn392eJ188sk8+eSTSestX77c+SDAVsWNXbJiPl9//fWsXr16Q/ItqysBnKpH5MfPJYDzI06V9VICuLLk9JwIiIAIuJNHdzJpM8qlgoIzrhz6wKFcdOhFnNvsXCERAd8JFKIAjq30/ve//3XO68aXQw45xNn+HEsqNXbsWO677z5nW7NtFTaRa1f3NG26fofG9OnTnRXL119/3RHEJhRtG3DHjh0rjF2qld3YwxVtgbY6paWljqB95JFHWLNmjSMoTYxb1mf7+xq/wm1C2c4z28qrrQZ36tQJE60mku3KqIqKbQE3cR1bUU5W195tia1iK8qJ6tn2cKunLdC+/3oH8gIJ4ECw5+ylEsA5Q60XiYAIRJGAzyvANwCdgfpACTAF7HQtn8axXAesBtYANmaXAYcA65dM1pcBwPngiFOz0bPcz+2yy/uA/e1IHjDSfSY+ZKlsxOo648qpT5zKobsdylWHXhXFsKtNISNQiAI4tgJsotHOxcYXW9G1636effbZv3x/5cqVWJKmq6++2jl7a6ui8cW2VVtWZdve+5///MfZFv23v/0tabS92gIde4HF0bI877jjjs653fr16zv36projS8mTE3I2tZpq2M+2jbuxx9/vMKeefzxxzvnm7/88ssK69nZXrNlvJIVE+f2QYKJc9u+HSuTJk1yVtztvmIlwQrZH4oM3JEAzgBWHlaVAM7DoMllERCB8BDwWQA3AX43QQlsAvQCrga2c4WugTABbClI30xCxdKqXgrYEtF3gM0kTVTvDtjsrg7wNWD3eNg9IPb9V4GhwD2uzVQ24l/tjCsXPnchdTarw9BjzYyKCPhLoBAFsInV7bffHruvNv6uXFvltW3OlsHZhFyiYqvBlhTr999/dxJAlS+fffYZzZo1c7ZQn3baaUmDl84WaHvYMjRnWmyF9YorrnDO79p25GTltddec5J7WbvtjHCyYsLaVrytPfHXFpWvb33J6jVu3NjJ5Jys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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-234-210d7d2046cf>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 109\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 110\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 111\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-234-210d7d2046cf>\u001b[0m in \u001b[0;36mgradient_descent\u001b[0;34m(alpha, iterations)\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 98\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---> 99\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 100\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 101\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",
"print(df)\n",
"\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
}