{ "cells": [ { "cell_type": "markdown", "id": "ccfb832d-9cbc-40fb-9fd7-02e9ba9d4882", "metadata": { "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "# 线性回归\n", "\n", "## 目录\n", "\n", "- 上海房价预测问题\n", "- 线性回归的从零开始实现\n", "- 线性回归的简洁实现" ] }, { "cell_type": "markdown", "id": "a9ce58d2-70ce-46a0-887e-3ebc8827ca65", "metadata": { "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "## 上海房价预测问题" ] }, { "cell_type": "markdown", "id": "536d9a6d-23ed-4c1d-b28a-e7d33e8bf25c", "metadata": {}, "source": [ "数据采自统计局" ] }, { "cell_type": "code", "execution_count": 1, "id": "f4e408c7-3b77-4cfe-8c1e-5bd6956c496b", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Year Price\n", "0 2010 14213\n", "1 2011 13448\n", "2 2012 13870\n", "3 2013 16192\n", "4 2014 16415\n", "5 2015 21501\n", "6 2016 25910\n", "7 2017 24866\n", "8 2018 28981\n", "9 2019 32926\n", "10 2020 36741\n", "11 2021 40974\n" ] } ], "source": [ "import pandas as pd\n", "\n", "price_data = {\n", " 'Year': [str(y) for y in range(2010, 2022)],\n", " 'Price':[14213, 13448, 13870, 16192, 16415, 21501, 25910, 24866, 28981, 32926, 36741, 40974],\n", "}\n", "\n", "price_df = pd.DataFrame(price_data)\n", "\n", "print(price_df)" ] }, { "cell_type": "markdown", "id": "1dd6323f-510b-4964-ba20-8762f637885b", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "首选绘制散点图,观察数据分布特征" ] }, { "cell_type": "code", "execution_count": 2, "id": "2e408d58-b1e6-48b7-b71c-da0dd8ca6a1f", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "(12000.0, 42000.0)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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ixYtQmxsLHQ6HRRFQe/evfHSSy/h+++/V29TasvgwYNx8OBBTJ8+HV26dEF5eTlat26NESNGYP369Xj99dfrzP7GG2/giy++wIgRI9C6dWuUl5ejS5cumD59On7++WcMGDCgwe8LERERkadRROxcyUPNSkREBLKzs90dg4iIiC5CS9p+N+s9pURERETUNLAoJSIiIiK3Y1FKRERERG7HopSIiIiI3I5FKRERERG5HYtSIiIiInI7FqVERERE5HYsSomIiIjI7ViUEhEREZHbsSglIiIiIrdjUUpEREREbseilIiIiIjcjkUpEREREbkdi1IiIiIicjsWpURERETkdixKiYiIiMjtWJQSERERkduxKCUiIiIit2NRSkRERERux6KUiIiIiNyORSkRERERuR2LUiIiIiJyOxalREREROR2WncHICIiIqpNRKDX67F27Vrk5uYiLCwM8fHx6Nu3r7ujkYuwKCUiIiKPYjAYkJiYiKysLIgIjEYjtFotkpOTERkZiZSUFMTExLg7JjkZD98TERGRxzAYDOjXrx8yMjJQVVUFo9EIADAajaiqqkJGRgbi4uKQnp7u5qTkbCxKiYiIyCOICBITE1FWVgaTyWSzj8lkQllZGRISEho5Hbkai1IiIiLyCHq9HllZWXYLUjOTyYSsrCzo9fpGSkaNgUUpEREReYS1a9dCRBzqKyJITU11cSJqTCxKiYiIyCPk5uaq55DWx2g0Ii8vz8WJqDGxKCUiIiKPEBYWBq3WsYmBtFotQkNDXZyIGhOLUiIiIvII8fHxUBTFob6KomDs2LEuTkSNiUUpEREReYTY2FhERkZCo6m7PNFoNIiMjERsbGwjJaPGwKKUiIiIPIKiKEhJSYGfn5/dwlSj0cDPzw8pKSmNnI5cjUUpEREReYyYmBikpaUhOjoaOp1OPcdUq9VCp9MhOjoaaWlpvKNTM8TbjBIREZFHiYmJgcFggF6vR2pqKvLy8hAaGoqxY8fykH0zxqKUiIiIPFJsbCyL0BaEh++JiIiIyO1YlBIRERGR27EoJSIiIiK3Y1FKRERERG7HopSIiIiI3I5FKRERERG5nccXpXv27MEzzzyDW265Bd27d0dISAi8vb3RoUMH3HbbbVi5ciVExObYLl26QFGUOh+PPPJInevPz89HUlISevToAX9/f7Rp0wZDhgzB8uXLHcq/e/dujBs3DuHh4fDx8UGnTp0wZcoUpKen1zvWaDTirbfeQt++fRESEoLAwEBcddVVmDdvHsrKyhxaPxEREVFToIi9is5DJCYmWtxKLDAwECaTCaWlpeqyoUOH4vPPP0dQUJDF2C5duuD3339HcHAw/Pz8bD7//fffj1deecVm2+HDhzF06FDk5OSo6y4vL4fRaAQAjB8/Hp988ondW6EtXLgQc+fOhclkgqIoCA4ORmFhIQDAx8cHK1aswNixY22OLSkpwS233ILdu3er/b28vNTXHR0djW+//RZhYWE2x9cWERGB7Oxsh/oSERGRZ2hJ22+P31M6dOhQ/Pvf/8ZPP/2EoqIiFBUVoaSkBKdPn8bLL78MLy8vbNu2DXPmzLH7HIsWLUJOTo7Nh72CtLKyEqNGjUJOTg569OgBvV6vrnvx4sXQ6XRYvXo15s2bZ3P8li1bMGfOHJhMJkybNg25ubk4d+4cTpw4gdGjR6OiogITJ05EVlaWzfEzZszA7t27ERwcjJSUFJSWlqKkpARff/01wsPDkZGRgcTExIt/Q4mIiIg8kTRxTz/9tAAQPz8/qaystGjr3LmzAJAPPvjgop938eLF6vMePXrUqv2VV14RABIQECB5eXlW7X369BEAMmLECKu2iooK6dWrlwCQhIQEq/aDBw+KoigCQD755BOr9j179ggAASAbNmxw6PV07NjRoX5ERETkOVrS9tvj95TWJy4uDgBQVlaGgoICpz2v+ZzRxMREdO3a1ap95syZCAwMRElJCT777DOLtszMTOzfvx8A8NRTT1mN9fb2xty5cwEAn3/+OYqLiy3aV6xYARFBt27dkJCQYDW+f//+uOGGGwAAH3/88cW/OCIiIiIP0+SL0l27dgG4cL5n27ZtnfKcxcXF2LdvHwBg5MiRNvsEBgZi0KBBAIDNmzdbtG3ZsgUAEBQUhAEDBtgcb37e8vJy9TWYbd26FQAwYsQIKIpS5/ja6yYiIiJqippkUVpSUoKMjAw89dRTeP311wEAs2bNslvAvf766+jQoQO8vb0RFhaG4cOHY+nSpSgvL7fZ//Dhw+oV/b169bKbw9xW+0r6Q4cOAbhwMZKXl5fNsW3btlUvUqo5XkSQkZHh8LrPnDmD/Px8u/2IiIiImgKtuwM4KicnB+Hh4VbLdTodZs6ciRdffNHu2PT0dPj5+cHf3x95eXnYunUrtm7diqVLl2L9+vW4/PLLLfqfOnVK/X/Hjh3tPq+57fTp0zbH1zXW3J6bm2sxvqioSD2c78i6zeu/7LLL6lwXERERkSdrMntKvby80K5dO7Rr1w4+Pj4AAEVR8Nhjj+GJJ56wuUdy9OjR+PTTT5Gbm4vS0lKcO3cOp06dwksvvQRvb2/8/PPPuPXWW1FZWWkxruY5nv7+/nYzmduKiopsjq9rrL3xF7tuW+sHgOTkZERERKiP2uetEhEREXmSJlOUhoWFqdM4lZaW4ujRo5g5cyaSk5PRq1cvq/MyAeCNN97A2LFjERoaqi4LDw/HM888gzVr1gC4sBf1ww8/bKyX0Whmz56N7Oxs9REYGOjuSERERER2NZmitCaNRoOuXbti0aJF+Ne//oW8vDwkJiZaTKhfnzvuuEO9UOmLL76waKtZwNX1nOa22pP2m8fXl8fW+Itdt631ExERETU1TbIorWn69Onw8fHByZMnsWHDhosaa55O6ujRoxbLO3TooP7/5MmTdseb22qf62oeX9dYe+ODgoLUwtSRddtaPxEREVFT0+SLUl9fX/UinyNHjjjlOaOjo9Ur+Q0Gg91+5raYmBiL5T179gQAZGRkoLq62ubYs2fPIjc312q8oiiIjo52eN3t2rXjRU5ERETU5DX5orS4uFgt7i72vMm0tDQAsJocPyAgQN2LunHjRptjS0pKsHPnTgDAjTfeaNE2fPhwABcuQNqzZ4/N8ebn9fX1xcCBA22O37Rpkzo1lb3xtddNRERE1BR5dFFqNBrr7bNw4UJUVVUBAAYPHqwut1fMmX355ZdqUTlq1Cir9okTJwIAVq1ahWPHjlm1v/322yguLkZAQADGjBlj0RYVFYU+ffoAAObPn281tqqqCgsWLABwYYaA2sX0PffcA0VRcOTIEfWCrJrS0tKwbds2AMCkSZPqfJ1ERERETYJbb3Jajx9++EH69esnH374oZw4cUJdbjKZJD09XR5++GH1HvHjxo2zGPvII4/IzJkz5dtvv5WSkhJ1+enTp+WVV14RX19fASAxMTFSUVFhte6Kigq54oorBID07NlT9u/fry5fsmSJeHt7CwB58cUXbWbfvHmzmm369OmSn58vIiLZ2dkSHx8vAMTX11cyMzNtjp88ebIAkJCQEFm9erVUV1erz9uhQwcBIMOGDXP4vWxJ984lIiJqLlrS9tvji1IA6sPX11dCQ0PVgtL8uOOOOywKTxGRKVOmqO2KokirVq0kJCTEYtw111wjx48ft7v+jIwMad++vdo/KChIdDqd+vX48ePVYtGWBQsWqIWpOYN5rI+Pj3z66ad2xxYXF8uAAQMsXru/v7/6dXR0tJw9e9bh97Il/VATERE1Fy1p+62I1HOc243Kysqwbt06bN26FXq9Hjk5OcjPz4evry8iIiLQt29fTJw4ETfddJPV2O+++w5r167F3r178fvvvyMvLw8mkwmXXXYZrr32Wtx11124++67odPp6syQn5+PV199FZ9//jmOHz8OX19fXHnllZg6dapDh8537dqFhQsXYs+ePSgoKEDbtm0xdOhQJCUlWV0gVZvRaMSSJUvw8ccf4/DhwzCZTOjWrRvGjx+POXPmwM/Pr971m0VERCA7O9vh/kREROR+LWn77dFFKTlPS/qhJiIiai5a0vbboy90IiIiIqKWgUUpEREREbkdi1IiIiIicjsWpURERETkdixKiYiIiMjtWJQSERERkduxKCUiIiIit2NRSkRERERup3V3ACIiImpcIgK9Xo+1a9ciNzcXYWFhiI+PR9++fd0djVowFqVEREQtiMFgQGJiIrKysiAiMBqN0Gq1SE5ORmRkJFJSUuq9DTaRK/DwPRERUQthMBjQr18/ZGRkoKqqCkajEQBgNBpRVVWFjIwMxMXFIT093c1JqSViUUpERNQCiAgSExNRVlYGk8lks4/JZEJZWRkSEhIaOR0Ri1IiIqIWQa/XIysry25BamYymZCVlQW9Xt9IyYguYFFKRETUAqxduxYi4lBfEUFqaqqLExFZYlFKRETUAuTm5qrnkNbHaDQiLy/PxYmILLEoJSIiagHCwsKg1To26Y5Wq0VoaKiLExFZYlFKRETUAsTHx0NRFIf6KoqCsWPHujgRkSUWpURERC1AbGwsIiMjodHUvenXaDSIjIxEbGxsIyUjuoBFKRERUQugKApSUlLg5+dntzDVaDTw8/NDSkpKI6cjYlFKRETUYsTExCAtLQ3R0dHQ6XTqOaZarRY6nQ7R0dFIS0vjHZ3ILXibUSIiohYkJiYGBoMBer0eqampyMvLQ2hoKMaOHctD9uRWLEqJiIhaoNjYWBah5FF4+J6IiIiI3I5FKRERERG5HYtSIiIiInI7FqVERERE5HYsSomIiIjI7ViUEhEREZHbsSglIiIiIrdjUUpEREREbseilIiIiIjcjkUpEREREbkdi1IiIiIicjsWpURERETkdixKiYiIiMjtWJQSERERkduxKCUiIiIit2NRSkRERERux6KUiIiIiNyORSkRERERuR2LUiIiIiJyOxalREREROR2Hl2U7tmzB8888wxuueUWdO/eHSEhIfD29kaHDh1w2223YeXKlRCROp9j/fr1GDlyJNq2bQtfX19069YNDz/8ME6cOFHv+ktLS/Hyyy/jqquuQmBgIEJCQhAXF4e33noL1dXV9Y43GAyYMmUKOnXqBB8fH4SHh2PcuHHYvXu3Q69/+fLlGDJkCNq0aQN/f3/06NEDSUlJKCgocGg8ERERUZMhHiwhIUEAqI/AwEDx9/e3WDZ06FA5f/68zfGPPfaY2k+j0UhwcLD6dUhIiOzatcvuunNyciQqKkrt7+/vLz4+PurXgwYNktLSUrvjU1JSxNvb22J9iqKoWRYtWmR3rNFolLFjx6pjtVqtBAYGql+Hh4dLZmam42+kiHTs2PGi+hMREZH7taTtt0fvKR06dCj+/e9/46effkJRURGKiopQUlKC06dP4+WXX4aXlxe2bduGOXPmWI394IMP8MYbbwAAnnvuORQWFqKwsBCHDx/G9ddfj8LCQowePdruXsfx48cjMzMT4eHh+Oabb1BSUoLS0lKsWrUKQUFB2LlzJx555BGbYzMyMjB58mRUVlZi9OjROHHiBM6dO4fc3FxMmzYNJpMJjz/+OL799lub41988UWkpqZCp9Nh8eLFKCkpQVFREfR6PXr06IHTp0/jjjvuQFVVVcPeWCIiIiJP4+6q+FI8/fTTAkD8/PyksrJSXV5ZWSkdOnQQADJt2jSrcQUFBdK+fXsBIElJSVbt69evV/dK7tmzx6p95cqV6h7P9PR0q/Zx48YJAOndu7dFLrNbbrlFAEhcXJxV29mzZ9W9wf/85z+t2o8cOSJ+fn4CQJYuXWr9ptjRkv7SIiIiai5a0vbbo/eU1icuLg4AUFZWZrHHc8uWLTh16hQA4KmnnrIa17p1azz00EMAgBUrVlidl7p8+XIAF/bU9u/f32p8YmIiunbtCpPJhJUrV1q0nT9/Hv/73/8AAHPnzoVOp7Mab86UlpaGX3/91aItNTUVpaWlCAwMxMyZM63GduvWDQkJCQCAjz/+2KqdiIiIqClq0kXprl27AACBgYFo27atunzr1q0AgJ49e6Jz5842x44cORIAkJ2djczMTIs283hzn9oURcGIESMAAJs3b7bKVFlZCQBqn9oGDhyIoKAgm+PN6x48eDACAgLqzL53716Ulpba7ENERETUlDS5orSkpAQZGRl46qmn8PrrrwMAZs2aBUVR1D6HDh0CAPTq1cvu89RsS09PV/+fl5eH3Nxch8eb11V73W3btrUolGvy8vJCjx49rNZ9sdlNJhMyMjLs9iMiIiJqKrTuDuCInJwchIeHWy3X6XSYOXMmXnzxRYvl5kP3HTt2tPuc/v7+aNWqFc6dO4fTp09bja1vvLmtqKgIxcXFCAwMdHjd5na9Xm+xbkfH12yrPZ6IiIioKWoSe0q9vLzQrl07tGvXDj4+PgAuHEJ/7LHH8MQTT8DLy8uif3FxMYALhWddzO1FRUVWY+sbX7PN1viGrNvR8fbWXVNycjIiIiLUR83XRURERORpmkRRGhYWhpycHOTk5KC0tBRHjx7FzJkzkZycjF69eqnnltKfZs+ejezsbPVh3pNLRERE5ImaRFFak0ajQdeuXbFo0SL861//Ql5eHhITEy0u+DEXYPVdBGRuN190VHNsfeNrttka35B1Ozre3rqJiIiImqomV5TWNH36dPj4+ODkyZPYsGGDurxDhw4AgJMnT9odW1painPnzgGAxfmq5rH1jTe3BQUFWRSyjqy7Znvtc2UdGV+zzda5tkRERERNTZMuSn19fXHZZZcBAI4cOaIu79mzJ4AL9563p2ZbTEyM+v/Q0FCEhYU5PN68rtrrPnv2rHoVf23V1dU4fPiw1bovNrtGo0F0dLTdfkRERERNRZMuSouLi9XCr+beyuHDhwO4cLvP48eP2xy7ceNGAEBERASioqIs2szjzX1qExFs2rQJAHDjjTdatA0cOFC9GMve+N27d6sXKNUeb173zp077R7CNz9v//79672gioiIiKgp8Nii1Gg01ttn4cKF6v3fBw8erC4fNmwYOnToABHB/PnzrcadO3cO77zzDgBg4sSJFnOcmpcBwLZt25CWlmY1fs2aNTh69Cg0Gg0mTJhg0RYcHIxRo0YBABYsWGDz/vTmTP369cMVV1xh0RYfHw9/f38UFRVh8eLFVmOPHTuGVatWAQAmTZpk1U5ERETUJLn5Nqd2/fDDD9KvXz/58MMP5cSJE+pyk8kk6enp8vDDD4uiKAJAxo0bZzX+/fffFwCiKIq88MILUlxcLCIimZmZMnDgQAEgoaGhkp+fb3P9gwcPFgDSsWNH2bx5s4iIVFdXy+rVqyU4OFgAyP33329z7KFDh8THx0cASHx8vGRnZ4uISH5+vkyfPl3NtX37dpvjn332WQEg3t7esmTJEqmoqBARkf3790vPnj0FgERFRUllZaWD72bLuncuEZE7mUwmSUtLk6SkJLn//vslKSlJ0tLS3B2LmqiWtP326KIUgPrw9fWV0NBQ8fX1tVh+xx13SElJic3nePTRR9V+Xl5eEhISon4dHBwsu3btsrv+nJwciYqKUvv7+/tbrHvQoEFSWlpqd3xKSop4e3ur/Vu1aqUW0RqNRhYtWmR3rNFolLFjx6pjdTqdBAUFqV+Hh4dLZmam42+mtKwfaiIid/n5558lJiZGdDqdaLVaASBarVZ0Op3ExMSIwWBwd0RqYlrS9lsREWmEHbIXraysDOvWrcPWrVuh1+uRk5OD/Px8+Pr6IiIiAn379sXEiRNx00031fk869evx9tvv439+/ejqKgI4eHhGDlyJJ588klcfvnldY4tLS1FcnIyVq9erR6u79GjByZNmoQZM2ZYTdpfm8FgwGuvvYatW7ciNzcXbdq0wfXXX4/Zs2djwIAB9b4Hy5Ytw3vvvYeDBw+ivLwcnTt3xp133omkpCS0adOm3vE1RUREIDs7+6LGEBGR4wwGA/r164eysjKYTCardo1GAz8/P6SlpVld5EpkT0vafntsUUrO1ZJ+qImIGpuIoHfv3sjIyLBZkJqZZ02pa4YVoppa0vbbYy90IiIiair0ej2ysrLqLEgBwGQyISsrC3q9vpGSETUdLEqJiIgu0dq1a+HogUcRQWpqqosTETU9Wmc8SW5uLrZt24bjx4+jtLQUzz77rDOeloiIqEnIzc11aCpD4MKUh3l5eS5ORNT0XFJRWllZiblz5+Lf//63xXycNYvSc+fO4S9/+QtKSkpw6NAhdOvW7VJWSURE5HHCwsKg1WodKky1Wi1CQ0MbIRVR09Lgw/cmkwl33nkn3n77bVRVVaFr167Qaq1r3FatWmHy5MmorKzE6tWrLyksERGRJ4qPj7e6EYs9iqJg7NixLk5E1PQ0uChdtmwZNm3ahPbt22PPnj349ddf7U5TNH78eADA1q1bG7o6IiIijxUbG4vIyEhoNHVvVjUaDSIjIxEbG9tIyYiajgYXpR999BEURcHChQsRFxdXZ99rr70WGo0Ghw4daujqiIiIPJaiKEhJSYGfn5/dwtQ8T2lKSkojpyNqGhpclB48eBCKouCOO+6ot6+Pjw9CQkJ4YjcRETVbMTExSEtLQ3R0NHQ6nXpKm1arhU6nQ3R0NCfOJ6pDgy90KikpQVBQEHx9fR3qX1VVZfOcUyIiouYiJiYGBoMBer0eqampyMvLQ2hoKMaOHctD9kT1aHCVGBYWhlOnTqGkpAQBAQF19j1y5AiKi4vRvXv3hq6OiIioyYiNjWURSnSRGnz4vn///gCAdevW1ds3OTkZiqJg8ODBDV0dERERETVjDS5Kp02bBhHB3//+dxw9etRmHxHBq6++iqVLlwIAZsyY0dDVEREREVEz1uDD98OHD8fUqVPx3//+F3369MHo0aNRUlICAHjllVdw/PhxbNy4ESdOnAAAPP7447jmmmuck5qIiIiImhVFHL1Zrw3V1dV4+umnsWDBAphMpgtPWGPyYBGBRqPBE088gXnz5jk8sTA5X0REBLKzs90dg4iIiC5CS9p+X1JRanb06FF8+OGH2Lt3L06fPo3q6mq0a9cO119/Pe69915ERkY6Iytdgpb0Q01ERNRctKTtt1OKUvJ8LemHmoiIqLloSdvvBl/oRERERETkLA0uSsvKyrBjxw7o9fp6++r1euzYsQPl5eUNXR0RERERNWMNLkpXrlyJoUOHYsWKFfX2fffddzF06FDe75eIiIiIbGpwUZqamgoAmDRpUr19p06dChHBp59+2tDVEREREVEz1uCiNCMjAzqdzqG5R2NjY6HT6ZCRkdHQ1RERERFRM9bgojQnJwfBwcHQaOp/Ci8vLwQHB+P06dMNXR0RERERNWMNLkr9/f1RWFgIo9FYb9+qqioUFhbC29u7oasjIiIiomaswUVpVFQUjEYjvv7663r7btq0CUajEd27d2/o6oiIiIioGWtwUTpq1CiICGbPno2CggK7/fLz8zFnzhwoioI77rijoasjIiIiomaswUXpww8/jHbt2uGXX37B1Vdfjffee8/inNHTp0/jP//5D6655hr88ssvaNu2LWbNmuWU0ERERETUvFzSbUbT0tJw66234o8//oCiKAAA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/99++83m9FC7du3CwoULsWfPHhQUFKBt27YYOnQokpKS6r1IyGg0YsmSJfj4449x+PBhmEwmdOvWDePHj8ecOXPqvGMTcOHc0VdffRUbNmzA6dOnERQUhD59+uCRRx7BbbfdVu9rWr9+Pd5++23s378fRUVFCA8Px8iRI/Hkk0/i8ssvr3d8TS3ph5qIiKi5aEnbb48tSsm5WtIPNRERUXPRkrbfHn2hExERERG1DCxKiYiIiMjtWJQSERERkduxKCUiIiIit2NRSkRERERux6KUiIiIiNyORSkRERERuR2LUiIiIiJyOxalREREROR2LEqJiIiIyO1YlBIRERGR27EoJSIiIiK3Y1FKRERERG7HopSIiIiI3I5FKRERERG5HYtSIiIiInI7FqVERERE5HYsSomIiIjI7ViUEhEREZHbsSglIiIiIrdjUUpEREREbseilIiIiIjcjkUpEREREbkdi1IiIiIicjsWpURERETkdixKiYiIiMjtWJQSERERkdtp3R2AiOhSiQj0ej3Wrl2L3NxchIWFIT4+Hn379nV3NCIichCLUiJq0gwGAxITE5GVlQURgdFohFarRXJyMiIjI5GSkoKYmBh3xyQionqwKCWiOnnyXkiDwYB+/fqhrKwMJpNJXW40GgEAGRkZiIuLQ1paGgtTIiIPp4iIuDsEuV5ERASys7PdHYOaGHt7IRVFcfteSBFB7969kZGRYVGQ1qbRaBAdHQ2DwdCI6YiInKMlbb95oRMR2WTeC5mRkYGqqip176PRaERVVZW6FzI9Pd0t+fR6PbKysuosSAHAZDIhKysLer2+kZIREVFDsCglIisigsTERKvD4jWZTCaUlZUhISGhkdNdsHbtWjh6oEdEkJqa6uJERER0KViUEpGVprAXMjc3V917Wx+j0Yi8vDwXJyIiokvBopSIrDSFvZBhYWHQah27VlOr1SI0NNTFiYiI6FKwKCUiK01hL2R8fDwURXGor6IoGDt2rIsTERHRpWBRSkRWmsJeyNjYWERGRkKjqftjTKPRIDIyErGxsY2UjIiIGoJFKRFZaQp7IRVFQUpKCvz8/OwWphqNBn5+fkhJSWnkdEREdLFYlBKRlaayFzImJgZpaWmIjo6GTqdT9+5qtVrodDpER0dz4nwioiaCk+e3EC1p8l1yjvT0dMTFxdmdFsq8F9JTij69Xo/U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7qJeG/eU0iWbPXu2uyPUixmdgxmdgxmdgxmdgxmdo7EydujQAYsWLQIAZGdnQ1EUvP/++wgICAAAvP/++wCAe++912ZBCgDjxo2Dj48PCgoK8P333zu87q5duyImJgYigj179tjtN3ny5IsuSAHuKSUiIiJqcq655hr8+OOPGD16ND777DN1+RVXXIEjR44gJCQEvr6+dsfn5ubCZDIhJSUF48ePt2hbt24dli9fju+//x5nz55FWVmZ1fh//etfmDt3rsUy857STz75BImJiRf9mrQXPYKIiIiI3CokJMTiX7PTp08DAAoLC1FYWFjv85SWlqr/r66uxoQJE7B69Wp1mU6nQ5s2bdRD/AUFBaiqqkJJSYnd52zobD88fE9ERETUTFRXVwMAli9fDhGp93HvvfeqY9977z2sXr0aXl5eeP755/Hrr7+ioqIC+fn5yMnJQU5ODuLi4gAAdR1o9/LyalB27iklIiIiaibatWuH48eP4/jx4xc9NiUlBQAwdepUPPfcczb7nDlz5pLy1YV7SomIiIiaieuvvx4AsH79+osee+LECQAXzle15ffff8evv/7a8HD1YFFKRERE1Ezcf//9AIC9e/fik08+qbPvH3/8YfG1+fzUn3/+2Wb/p59+us7D9peKRSkRERFRM3HTTTchISEBwIWpmZ599lmcPHlSbS8uLsbWrVsxdepUDBo0yGLsLbfcAgB499138f7776OyshIAcPz4cUyZMgWffPIJWrdu7bLsLEqJiDyAyWRydwSHmC+O8ETV1dVWe348mae+j9T0ffDBB7j77rthNBrx0ksvISIiAiEhIWjVqhWCg4MxfPhwvPfee6ioqLAYN2fOHHTv3h1GoxEPPPAA/P390bp1a3Tu3BnLli3Diy++iCuvvNJluVmUktOZN67mD9zaX9PFqf0+ehLzVZ5mnpjRzNN//jQajUe/f3l5ecjLy4OiKOpchJ7mhRdewBtvvOHuGA7z1PfRzJM/e7idqZufnx9WrlyJzZs34+6778bll1+OiooKlJeXo1OnTrj11luRnJyMb7/91mJc69atsXfvXsycOROdOnWCRqOBTqfDiBEj8OWXX+KZZ55xaW5Onk8uYTKZkJeXh1atWsHb2xsmkwkajUZtM/+fHFNdXa1OseFp719FRYV6uzk/Pz8AFzYMnrDBzcnJweHDhzFw4EBo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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "plt.rcParams['font.size'] = 22\n", "\n", "fig = plt.figure(figsize=(8, 6), dpi=80)\n", "ax = fig.add_subplot(111)\n", "ax.plot(price_df.index, price_data['Price'], 'ko', markersize=10)\n", "ax.set_xlabel(\"Year\")\n", "ax.set_xticks(price_df.index)\n", "ax.set_xticklabels(price_data['Year'], rotation=60)\n", "ax.xaxis.set_label_coords(1.05, 0.015)\n", "ax.set_ylabel(\"Price\")\n", "ax.set_ylim(12000, 42000)" ] }, { "cell_type": "markdown", "id": "6d94511c-d2ac-4f74-9820-08bd3dd41554", "metadata": {}, "source": [ "从图形上看,房价大致与年份成线性关系,特别是2017年-2021年间。\n", "\n", "因此,选用线性回归模型进行预测大体上是合理的。" ] }, { "cell_type": "markdown", "id": "e59521ef-f904-4d6d-b098-7f97f539151c", "metadata": { "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "### 最小二乘解" ] }, { "cell_type": "code", "execution_count": 5, "id": "2e118155-96e6-4a04-beef-d838da36010d", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "

\n", "\n", "
class LinearRegressionLS(LinearRegression):\n",
       "    """\n",
       "    solve linear regression problem via least squares\n",
       "    """\n",
       "    X: np.array\n",
       "    Y: np.array\n",
       "    w: np.array\n",
       "    \n",
       "    def __init__(self, ylist):\n",
       "        num_data = len(ylist)\n",
       "        self.X = self.homogeneous([x for x in range(num_data)])\n",
       "        self.Y = np.array(ylist).reshape(num_data, 1)\n",
       "        self.w = np.random.rand(2)\n",
       "        \n",
       "    def homogeneous(self, xlist):\n",
       "        """ build homogeneous coordinates """\n",
       "        raise NotImplementedError\n",
       "    \n",
       "    def linout(self, xlist):\n",
       "        """ linear output for given data """\n",
       "        raise NotImplementedError\n",
       "    \n",
       "    def loss_sq(self, X, Y):\n",
       "        """ loss function: (half) sum of square errors """\n",
       "        raise NotImplementedError\n",
       "        \n",
       "    def solve(self, lr, nepoch):\n",
       "        """ form normal equation """\n",
       "        raise NotImplementedError\n",
       "
\n", "\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import sys\n", "sys.path.insert(1, '../')\n", "from utils.utils import *\n", "from LinearRegression_2 import *\n", "\n", "\n", "psource(LinearRegressionLS)" ] }, { "cell_type": "markdown", "id": "be5afa50-d1e3-4519-ae98-300d5f526077", "metadata": {}, "source": [ "#### 实现要点" ] }, { "cell_type": "markdown", "id": "ca886ab0-47e1-4f43-a5e6-4c8b908666b4", "metadata": {}, "source": [ "```py\n", "def linout(self, xlist):\n", " \"\"\" linear output for given data \"\"\"\n", " return 自变量 * 权重 + 偏置\n", "```" ] }, { "cell_type": "markdown", "id": "6feb8387-9f9d-40b3-90fe-a468eb7d6a76", "metadata": {}, "source": [ "```py\n", "def homogeneous(self, xlist):\n", " \"\"\" build homogeneous coordinates \"\"\"\n", " xlist 转换成列向量\n", " return xlist + 全是1的列向量\n", "```" ] }, { "cell_type": "markdown", "id": "6207cb3b-c29e-4696-bb16-cdf810bd27f3", "metadata": {}, "source": [ "```py\n", "def loss_sq(self, X, Y):\n", " \"\"\" loss function: (half) sum of square errors \"\"\"\n", " return ((预测值 - 真实值)的平方)求和后除以2\n", "```" ] }, { "cell_type": "markdown", "id": "9aaac662-d290-43af-8375-c173216ba30e", "metadata": {}, "source": [ "```py\n", "def solve(self, lr, nepoch):\n", " \"\"\" form normal equation \"\"\"\n", " XtX = X转置 * X\n", " XtY = X转置 * Y\n", " 权重W = XtX求逆 * XtY\n", "```" ] }, { "cell_type": "markdown", "id": "1ef5e483-6592-4991-80f5-1a3b4ff007c8", "metadata": { "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "### 梯度下降法" ] }, { "cell_type": "code", "execution_count": 6, "id": "e6589f47-5922-42c8-8221-c0c34347d021", "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "

\n", "\n", "
class LinearRegressionGD1(LinearRegression):\n",
       "    """\n",
       "    solve linear regression problem via gradient descent,\n",
       "    using single weight vector: homogeneous coordinates\n",
       "    """\n",
       "    X: np.array\n",
       "    Y: np.array\n",
       "    w: np.array\n",
       "    \n",
       "    def __init__(self, ylist):\n",
       "        num_data = len(ylist)\n",
       "        self.X = self.homogeneous([x for x in range(num_data)])\n",
       "        self.Y = np.array(ylist).reshape(num_data, 1)\n",
       "        self.w = np.random.rand(2)\n",
       "        \n",
       "    def homogeneous(self, xlist):\n",
       "        """ build homogeneous coordinates """\n",
       "        raise NotImplementedError\n",
       "    \n",
       "    def linout(self, xlist):\n",
       "        """ linear output for given data """\n",
       "        raise NotImplementedError\n",
       "    \n",
       "    def loss_sq(self, X, Y):\n",
       "        """ loss function: (half) sum of square errors """\n",
       "        raise NotImplementedError\n",
       "\n",
       "    def gd(self, lr):\n",
       "        """ gradient descent update """\n",
       "        raise NotImplementedError\n",
       "        \n",
       "    def solve(self, lr, nepoch):\n",
       "        """ iterative solver """\n",
       "        for epoch in range(num_epochs):\n",
       "            self.gd(lr)\n",
       "            print(f'epoch {epoch + 1}, loss {self.loss_sq(self.X, self.Y)}')\n",
       "
\n", "\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "psource(LinearRegressionGD1)" ] }, { "cell_type": "markdown", "id": "9b4ae99c-d68c-451b-836e-edb463ffea02", "metadata": {}, "source": [ "#### 实现要点" ] }, { "cell_type": "markdown", "id": "910185b9-0fd7-4911-af7a-9c803bb2c44a", "metadata": {}, "source": [ "```py\n", "def gd(self, lr):\n", " \"\"\" gradient descent update \"\"\"\n", " def gradient(Y_hat, Y, X):\n", " return ((预测值 - 真实值Y) * 自变量X)求和\n", "\n", " 预测值 = linout(X)\n", " 梯度值 = gradient(预测值, Y, X)\n", " 梯度下降更新:权重W -= 学习率lr * 梯度值\n", "```" ] }, { "cell_type": "markdown", "id": "d4845484-98dc-40f1-8ffe-0c31580f1970", "metadata": { "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "### 学习率测试\n", "\n", "学习率的取值可以决定优化过程是否收敛。\n", "\n", "- 尝试将学习率改成$0.03$并重新运行,观察误差的变化并解释原因" ] }, { "cell_type": "markdown", "id": "a2c6a000-43ee-4d5c-bf31-1bdd642f9c6f", "metadata": { "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "## 线性回归的从零开始实现" ] }, { "cell_type": "markdown", "id": "939eb9d6-7b43-4703-94a5-4d9488df3c05", "metadata": {}, "source": [ "我们将从零开始实现整个方法,\n", "包括数据流水线、模型、损失函数和小批量随机梯度下降优化器" ] }, { "cell_type": "code", "execution_count": 1, "id": "de28883e-7e4d-45bf-b7fa-ce618d1b131b", "metadata": { "origin_pos": 2, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "%matplotlib inline\n", "import random\n", "import torch\n", "from d2l import torch as d2l" ] }, { "cell_type": "markdown", "id": "20bb6de7-a972-4798-b521-c05b7a6f3e43", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "根据带有噪声的线性模型构造一个人造数据集。\n", "我们使用线性模型参数$\\mathbf{w} = [2, -3.4]^\\top$、$b = 4.2$\n", "和噪声项$\\epsilon$生成数据集及其标签:\n", "\n", "$$\\mathbf{y}= \\mathbf{X} \\mathbf{w} + b + \\mathbf\\epsilon$$" ] }, { "cell_type": "code", "execution_count": 2, "id": "e26dcefc-d894-4e45-9ae2-e1f5004d0645", "metadata": { "origin_pos": 7, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "def synthetic_data(w, b, num_examples): \n", " \"\"\"生成y=Xw+b+噪声\"\"\"\n", " X = torch.normal(0, 1, (num_examples, len(w)))\n", " y = torch.matmul(X, w) + b\n", " y += torch.normal(0, 0.01, y.shape)\n", " return X, y.reshape((-1, 1))\n", "\n", "true_w = torch.tensor([2, -3.4])\n", "true_b = 4.2\n", "features, labels = synthetic_data(true_w, true_b, 1000)" ] }, { "cell_type": "markdown", "id": "54118bf5-96e2-412c-8ea1-4d17ff07389a", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "`features`中的每一行都包含一个二维数据样本,\n", "`labels`中的每一行都包含一维标签值(一个标量)" ] }, { "cell_type": "code", "execution_count": 3, "id": "25b8aaf8-9e8e-4ec6-9fb2-bd24587e629a", "metadata": { "origin_pos": 9, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "features: tensor([-1.9279, -0.3628]) \n", "label: tensor([1.5922])\n" ] } ], "source": [ "print('features:', features[0],'\\nlabel:', labels[0])" ] }, { "cell_type": "code", "execution_count": 4, "id": "cf24318d-203e-47b6-a508-47fc5cb38403", "metadata": { "origin_pos": 11, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2023-02-26T14:01:17.224479\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.5.1, https://matplotlib.org/\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "d2l.set_figsize()\n", "d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);" ] }, { "cell_type": "markdown", "id": "9810a549-14d5-45a6-b766-5a6dbf50fa18", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "定义一个`data_iter`函数,\n", "该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为`batch_size`的小批量" ] }, { "cell_type": "code", "execution_count": 5, "id": "6ca5e14c-f1d6-4f2b-bd57-e72c9252f452", "metadata": { "origin_pos": 16, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "def data_iter(batch_size, features, labels):\n", " num_examples = len(features)\n", " indices = list(range(num_examples))\n", " random.shuffle(indices) # 这些样本是随机读取的,没有特定的顺序\n", " for i in range(0, num_examples, batch_size):\n", " batch_indices = torch.tensor(\n", " indices[i: min(i + batch_size, num_examples)])\n", " yield features[batch_indices], labels[batch_indices]" ] }, { "cell_type": "code", "execution_count": 6, "id": "89ed6ef0-e020-4f6f-b788-9f0625ec0161", "metadata": { "origin_pos": 16, "slideshow": { "slide_type": "subslide" }, "tab": [ "pytorch" ], "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([[ 0.4983, -0.6839],\n", " [ 0.9086, 0.1984],\n", " [-0.9412, 1.7741],\n", " [ 0.5633, 0.5367],\n", " [-0.5475, 1.1045],\n", " [-0.4858, 0.1637],\n", " [-0.6546, 0.7451],\n", " [ 0.0917, -0.5540],\n", " [ 1.0365, 0.3422],\n", " [-1.3411, 1.1863]]) \n", " tensor([[ 7.5235],\n", " [ 5.3381],\n", " [-3.7026],\n", " [ 3.5037],\n", " [-0.6498],\n", " [ 2.6669],\n", " [ 0.3536],\n", " [ 6.2596],\n", " [ 5.1234],\n", " [-2.5214]])\n" ] } ], "source": [ "batch_size = 10\n", "\n", "for X, y in data_iter(batch_size, features, labels):\n", " print(X, '\\n', y)\n", " break" ] }, { "cell_type": "markdown", "id": "a23201e5-a1e7-460e-83e9-fac956fb7ade", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "在我们开始用小批量随机梯度下降优化我们的模型参数之前\n", "我们需要先有一些参数" ] }, { "cell_type": "code", "execution_count": 7, "id": "ebe326c7-d78f-42d2-abc3-57741be363a5", "metadata": { "origin_pos": 19, "tab": [ "pytorch" ], "tags": [] }, "outputs": [], "source": [ "w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)\n", "b = torch.zeros(1, requires_grad=True)" ] }, { "cell_type": "markdown", "id": "a4d10480-4182-4c33-9cd7-54320f2f3453", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "定义模型,将模型的输入和参数同模型的输出关联起来" ] }, { "cell_type": "code", "execution_count": 8, "id": "f2779003-d348-4712-afcc-b0e5a2f588b9", "metadata": { "origin_pos": 22, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "def linreg(X, w, b): \n", " \"\"\"线性回归模型\"\"\"\n", " return torch.matmul(X, w) + b" ] }, { "cell_type": "markdown", "id": "e817d119-d0d3-4ced-b559-8fcd7286615e", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "定义损失函数" ] }, { "cell_type": "code", "execution_count": 9, "id": "a2160898-f709-4b0d-a4c2-d30f47775eff", "metadata": { "origin_pos": 24, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "def squared_loss(y_hat, y): \n", " \"\"\"均方损失\"\"\"\n", " return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2" ] }, { "cell_type": "markdown", "id": "3c1eab90-6484-409c-b01f-1547ae043d9a", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "定义优化算法" ] }, { "cell_type": "code", "execution_count": 10, "id": "20cc3c52-4538-43e6-acf8-130846cba391", "metadata": { "origin_pos": 27, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "def sgd(params, lr, batch_size): \n", " \"\"\"小批量随机梯度下降\"\"\"\n", " with torch.no_grad():\n", " for param in params:\n", " param -= lr * param.grad / batch_size\n", " param.grad.zero_()" ] }, { "cell_type": "markdown", "id": "9e3b351c-4673-4278-afd6-4a7ab6262757", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "训练过程" ] }, { "cell_type": "code", "execution_count": 11, "id": "a3d0fbca-3007-4d70-a529-bcc3c89d140a", "metadata": { "origin_pos": 32, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "epoch 1, loss 0.035369\n", "epoch 2, loss 0.000128\n", "epoch 3, loss 0.000051\n" ] } ], "source": [ "lr = 0.03\n", "num_epochs = 3\n", "net = linreg\n", "loss = squared_loss\n", "\n", "for epoch in range(num_epochs):\n", " for X, y in data_iter(batch_size, features, labels):\n", " l = loss(net(X, w, b), y) # X和y的小批量损失\n", " # 因为l形状是(batch_size,1),而不是一个标量。\n", " l.sum().backward() # l中的所有元素被加到一起,并以此计算关于[w,b]的梯度\n", " sgd([w, b], lr, batch_size)\n", " with torch.no_grad():\n", " train_l = loss(net(features, w, b), labels)\n", " print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')" ] }, { "cell_type": "markdown", "id": "e92e5c1e-cfbf-4fb1-8593-bc53e531976c", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "比较真实参数和通过训练学到的参数来评估训练的成功程度" ] }, { "cell_type": "code", "execution_count": 12, "id": "1822da05-a22e-43cd-a443-d09f640daafe", "metadata": { "origin_pos": 35, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "w的估计误差: tensor([ 0.0011, -0.0005], grad_fn=)\n", "b的估计误差: tensor([0.0004], grad_fn=)\n" ] } ], "source": [ "print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')\n", "print(f'b的估计误差: {true_b - b}')" ] }, { "cell_type": "markdown", "id": "a9ea5874-18f0-4c81-b536-1f3ffabfe38c", "metadata": { "slideshow": { "slide_type": "slide" }, "tags": [] }, "source": [ "## 线性回归的简洁实现" ] }, { "cell_type": "markdown", "id": "48641c86-e087-4daa-a591-0c8c41fe3fd3", "metadata": {}, "source": [ "通过使用深度学习框架来简洁地实现线性回归模型\n", "\n", "首先生成数据集" ] }, { "cell_type": "code", "execution_count": 13, "id": "713a636a-30b5-473d-8614-70f8cabf080e", "metadata": { "origin_pos": 4, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "import numpy as np\n", "import torch\n", "from torch.utils import data\n", "from d2l import torch as d2l\n", "\n", "true_w = torch.tensor([2, -3.4])\n", "true_b = 4.2\n", "features, labels = d2l.synthetic_data(true_w, true_b, 1000)" ] }, { "cell_type": "markdown", "id": "ebbf6c90-48e9-4dd1-98d4-dba177b4d84a", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "调用框架中现有的API来读取数据" ] }, { "cell_type": "code", "execution_count": 14, "id": "f73bdcc1-8586-491b-880f-6812bf6313b9", "metadata": { "origin_pos": 11, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "[tensor([[-0.7491, 1.8152],\n", " [ 2.4920, 0.1592],\n", " [ 1.9233, 0.3220],\n", " [ 0.8503, 1.4565],\n", " [ 0.4859, 0.6209],\n", " [-0.9299, -1.5807],\n", " [ 1.8116, 1.2680],\n", " [ 0.7691, 0.2507],\n", " [ 0.8320, -0.9070],\n", " [ 0.5925, 0.2233]]),\n", " tensor([[-3.4789],\n", " [ 8.6513],\n", " [ 6.9465],\n", " [ 0.9517],\n", " [ 3.0479],\n", " [ 7.7058],\n", " [ 3.5047],\n", " [ 4.8805],\n", " [ 8.9502],\n", " [ 4.6297]])]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def load_array(data_arrays, batch_size, is_train=True): \n", " \"\"\"构造一个PyTorch数据迭代器\"\"\"\n", " dataset = data.TensorDataset(*data_arrays)\n", " return data.DataLoader(dataset, batch_size, shuffle=is_train)\n", "\n", "batch_size = 10\n", "data_iter = load_array((features, labels), batch_size)\n", "\n", "next(iter(data_iter))" ] }, { "cell_type": "markdown", "id": "cf07e6f2-f783-4187-a0ff-70e35643dfcd", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "使用框架的预定义好的层" ] }, { "cell_type": "code", "execution_count": 15, "id": "474fee23-20ce-45ad-88cb-1cf4d1eacf12", "metadata": { "origin_pos": 17, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "from torch import nn # nn是神经网络的缩写\n", "\n", "net = nn.Sequential(nn.Linear(2, 1))" ] }, { "cell_type": "markdown", "id": "f6bd2056-9dd5-47c1-8c77-9e0626484b1b", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "初始化模型参数" ] }, { "cell_type": "code", "execution_count": 16, "id": "f197ee0c-f05b-4470-9430-46127dd15228", "metadata": { "origin_pos": 24, "tab": [ "pytorch" ] }, "outputs": [ { "data": { "text/plain": [ "tensor([0.])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net[0].weight.data.normal_(0, 0.01)\n", "net[0].bias.data.fill_(0)" ] }, { "cell_type": "markdown", "id": "e0758d6e-a034-443d-9002-5c5ac5178e0a", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "计算均方误差使用的是`MSELoss`类,也称为平方$L_2$范数" ] }, { "cell_type": "code", "execution_count": 17, "id": "441fabfd-b551-4d3b-b2b6-aa1749d76e9e", "metadata": { "origin_pos": 34, "tab": [ "pytorch" ], "tags": [] }, "outputs": [], "source": [ "loss = nn.MSELoss()" ] }, { "cell_type": "markdown", "id": "24c5b7e6-19fd-40bf-a554-13da0c3c450b", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "实例化一个`SGD`实例" ] }, { "cell_type": "code", "execution_count": 18, "id": "aad90c0a-d5b9-4486-8baa-e001b4ef9739", "metadata": { "origin_pos": 41, "tab": [ "pytorch" ] }, "outputs": [], "source": [ "trainer = torch.optim.SGD(net.parameters(), lr=0.03)" ] }, { "cell_type": "markdown", "id": "c43537c5-ce8b-4587-8d77-9202abd47bf9", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "训练过程代码与我们从零开始实现时所做的非常相似" ] }, { "cell_type": "code", "execution_count": 19, "id": "92b54dcc-bc68-4463-978b-ea86b285e742", "metadata": { "origin_pos": 45, "tab": [ "pytorch" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "epoch 1, loss 0.000356\n", "epoch 2, loss 0.000104\n", "epoch 3, loss 0.000105\n" ] } ], "source": [ "num_epochs = 3\n", "for epoch in range(num_epochs):\n", " for X, y in data_iter:\n", " l = loss(net(X) ,y)\n", " trainer.zero_grad()\n", " l.backward()\n", " trainer.step()\n", " l = loss(net(features), labels)\n", " print(f'epoch {epoch + 1}, loss {l:f}')" ] }, { "cell_type": "markdown", "id": "764227db-eac3-483c-827f-a3f009a4049d", "metadata": { "slideshow": { "slide_type": "subslide" }, "tags": [] }, "source": [ "比较生成数据集的真实参数和通过有限数据训练获得的模型参数" ] }, { "cell_type": "code", "execution_count": 20, "id": "cc70dacd-d411-47c3-923e-59fda6975788", "metadata": { "origin_pos": 49, "tab": [ "pytorch" ], "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "w的估计误差: tensor([-0.0005, 0.0009])\n", "b的估计误差: tensor([-0.0008])\n" ] } ], "source": [ "w = net[0].weight.data\n", "print('w的估计误差:', true_w - w.reshape(true_w.shape))\n", "b = net[0].bias.data\n", "print('b的估计误差:', true_b - b)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.8.16" } }, "nbformat": 4, "nbformat_minor": 5 }