numerical-methods-homeworks.../HW1/kmeans_solution.ipynb

668 lines
407 KiB
Plaintext
Raw Normal View History

2023-10-05 00:27:43 +03:00
{
"cells": [
{
"cell_type": "code",
"execution_count": 182,
"metadata": {
"id": "dfXNsL1pva-M"
},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": 183,
"metadata": {
"id": "z-2z3kejva-O"
},
"outputs": [],
"source": [
"def scatter_plot(data, col=None):\n",
" from mpl_toolkits.mplot3d import Axes3D\n",
" %matplotlib inline\n",
" fig = plt.figure()\n",
" ax = fig.add_subplot(111, projection='3d')\n",
" ax.scatter(data[:,0], data[:,1], data[:,2], s = 0.5, color=col)\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 184,
"metadata": {
"id": "aOHWooBTva-O"
},
"outputs": [],
"source": [
"N = 1000\n",
"K = 3\n",
"d = 3\n",
"L = 10"
]
},
{
"cell_type": "code",
"execution_count": 185,
"metadata": {
"id": "cobkrTwrva-O"
},
"outputs": [],
"source": [
"# Generate some data\n",
"np.random.seed(42)\n",
"mu_true = np.random.uniform(-L, L, size = (K, d))\n",
"data = np.random.normal(mu_true, size = (N, K, d))\n",
"data = np.vstack(data)\n",
"np.random.shuffle(data)"
]
},
{
"cell_type": "code",
"execution_count": 186,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 413
},
"id": "fN4RBXksva-O",
"outputId": "bf587173-2253-4552-ceed-9a0e29275fd1"
},
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {}
}
],
"source": [
"if d == 3:\n",
" scatter_plot(data, None)"
]
},
{
"cell_type": "code",
"execution_count": 187,
"metadata": {
"id": "kSd05Udfva-P"
},
"outputs": [],
"source": [
"mu = data[np.random.choice(range(data.shape[0]), K, replace=False)]"
]
},
{
"cell_type": "markdown",
"source": [
"## **Slow implementation**"
],
"metadata": {
"id": "WZN75AXlwLgL"
}
},
{
"cell_type": "code",
"execution_count": 188,
"metadata": {
"id": "-bQpVYXWva-P"
},
"outputs": [],
"source": [
"def dist_i(x, mu):\n",
" # x: N datapoints, mu: N cluster centers\n",
" # returns: D_{i}, squared distances from x[i] to mu[i]\n",
" dist = np.zeros(x.shape[0])\n",
" for i in range(x.shape[0]):\n",
" dist[i] = np.sum((x[i] - mu[i])**2)\n",
" return dist\n",
"def dist_ij(x, mu):\n",
" # x: N datapoints, mu: K cluster centers\n",
" # returns: D_{ij}, squared distances from x[i] to mu[j]\n",
" dist = np.zeros((x.shape[0], mu.shape[0]))\n",
" for i in range(x.shape[0]):\n",
" for j in range(mu.shape[0]):\n",
" dist[i, j] += np.sum((x[i] - mu[j])**2)\n",
" return dist"
]
},
{
"cell_type": "code",
"execution_count": 189,
"metadata": {
"id": "uGu5-pGYva-P"
},
"outputs": [],
"source": [
"def kmeans(data, mu):\n",
" ss_list = []\n",
" for n in range(10):\n",
" c = np.argmin(dist_ij(data, mu), axis = 1)\n",
" ss = np.mean(dist_i(data, mu[c]))\n",
" ss_list.append(ss)\n",
" for i in range(K):\n",
" cluster_members = data[c == i]\n",
" cluster_members = cluster_members.mean(axis = 0)\n",
" mu[i] = cluster_members\n",
" return ss_list, c"
]
},
{
"cell_type": "code",
"execution_count": 190,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 430
},
"id": "N5fihPeqva-P",
"outputId": "e99820af-847e-43cd-c9f5-bd989f945880"
},
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {}
}
],
"source": [
"res, c = kmeans(data, mu)\n",
"plt.plot(res);"
]
},
{
"cell_type": "code",
"source": [
"%time kmeans(data, mu)\n",
"%time kmeans(data, mu)\n",
"%time kmeans(data, mu)\n",
"\"\""
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 140
},
"id": "PtEqaxIDSzv9",
"outputId": "07677ae5-6edd-4239-b0fb-91c8984a281a"
},
"execution_count": 191,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"CPU times: user 1.41 s, sys: 963 µs, total: 1.41 s\n",
"Wall time: 1.46 s\n",
"CPU times: user 908 ms, sys: 223 µs, total: 908 ms\n",
"Wall time: 917 ms\n",
"CPU times: user 760 ms, sys: 2.65 ms, total: 763 ms\n",
"Wall time: 765 ms\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"''"
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 191
}
]
},
{
"cell_type": "code",
"execution_count": 192,
"metadata": {
"id": "mt_AmTnRva-P"
},
"outputs": [],
"source": [
"colors = np.array([plt.cm.cool(i/(K-1)) for i in range(K)])"
]
},
{
"cell_type": "code",
"execution_count": 193,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 413
},
"id": "w9EG7ndhva-Q",
"outputId": "3cd8f596-c60d-480d-ccee-3f5bec578424"
},
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {}
}
],
"source": [
"if d == 3:\n",
" scatter_plot(data, colors[c])"
]
},
{
"cell_type": "markdown",
"source": [
"## **Fast implementation** \n",
"To speed up `dist_i` we will simply use `numpy` vectorization. \n",
"For `dist_iо` we will note that the $ij$-element of the output matrix $C$ has the form: \n",
"\n",
"$c_{ij} = \\sum_{k=1}^{n}(x_{ik} + mu_{jk})^2 = \\sum_{k=1}^{n}x_{ik}^2 - 2\\sum_{k=1}^{n}x_{ik}mu_{jk} + \\sum_{k=1}^{n}mu_{jk}^2$. \n",
"\n",
"Using vectorization of `numpy`, you can represent the terms as: \n",
"\n",
"\n",
"---\n",
"\n",
"\n",
"$(\\sum_{k=1}^{n}x_{1k}^2 \\sum_{k=1}^{n}x_{2k}^2 ... \\sum_{k=1}^{n}x_{mk}^2)^T =$ `np.sum(x**2, axis=1).reshape((x.shape[0], 1))` \n",
"\n",
"\n",
"---\n",
"\n",
"\n",
"$\\underset{1 ≤ i ≤ m\\\\1 ≤ j ≤ n}{(-2\\sum_{k=1}^{n}x_{ik}mu_{jk})} = $ `-2*(x @ mu.T)` \n",
"\n",
"\n",
"---\n",
"\n",
"\n",
"$(\\sum_{k=1}^{n}mu_{1k}^2 \\sum_{k=1}^{n}mu_{2k}^2 ... \\sum_{k=1}^{n}mu_{nk}^2)^T = $ `np.sum(mu**2, axis = 1).reshape((1, mu.shape[0]))`\n",
"\n",
"\n",
"---\n",
"\n",
"Using `numpy` broadcasting we can add these terms and get the desired matrix."
],
"metadata": {
"id": "74HbN5R2wRCu"
}
},
{
"cell_type": "code",
"source": [
"# Generate some data\n",
"np.random.seed(42)\n",
"mu_true = np.random.uniform(-L, L, size = (K, d))\n",
"data = np.random.normal(mu_true, size = (N, K, d))\n",
"data = np.vstack(data)\n",
"np.random.shuffle(data)"
],
"metadata": {
"id": "d1qp6tO4U6cz"
},
"execution_count": 194,
"outputs": []
},
{
"cell_type": "code",
"source": [
"mu = data[np.random.choice(range(data.shape[0]), K, replace=False)]"
],
"metadata": {
"id": "RdcERcHHUtpe"
},
"execution_count": 195,
"outputs": []
},
{
"cell_type": "code",
"source": [
"def dist_i(x, mu):\n",
" # x: N datapoints, mu: N cluster centers\n",
" # returns: D_{i}, squared distances from x[i] to mu[i]\n",
" dist = np.sum((x - mu)**2, axis=1)\n",
" return dist\n",
"def dist_ij(x, mu):\n",
" # x: N datapoints, mu: K cluster centers\n",
" # returns: D_{ij}, squared distances from x[i] to mu[j]\n",
" sum_xij = np.sum(x**2, axis = 1).reshape((x.shape[0], 1))\n",
" sum_muij = np.sum(mu**2, axis = 1).reshape((1, mu.shape[0]))\n",
" sum_xij_muij = -2 * (x @ mu.T)\n",
" dist = sum_xij + sum_xij_muij + sum_muij\n",
" return dist"
],
"metadata": {
"id": "NsVoKN2mwQiG"
},
"execution_count": 196,
"outputs": []
},
{
"cell_type": "code",
"source": [
"res, c = kmeans(data, mu)\n",
"plt.plot(res);"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 430
},
"id": "E4OAApKsS9L1",
"outputId": "8e2dd22a-7e2c-40ce-cacf-c9cfc0f880d0"
},
"execution_count": 197,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"source": [
"%time kmeans(data, mu)\n",
"%time kmeans(data, mu)\n",
"%time kmeans(data, mu)\n",
"\"\""
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 140
},
"outputId": "85e0c979-2082-49b1-f572-4c4c880a47c3",
"id": "k450H4hjZkpo"
},
"execution_count": 198,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"CPU times: user 11.5 ms, sys: 0 ns, total: 11.5 ms\n",
"Wall time: 12.5 ms\n",
"CPU times: user 7.45 ms, sys: 0 ns, total: 7.45 ms\n",
"Wall time: 7.46 ms\n",
"CPU times: user 7.05 ms, sys: 0 ns, total: 7.05 ms\n",
"Wall time: 7.06 ms\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"''"
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 198
}
]
},
{
"cell_type": "code",
"source": [
"colors = np.array([plt.cm.cool(i/(K-1)) for i in range(K)])"
],
"metadata": {
"id": "38N1euNRTKBq"
},
"execution_count": 199,
"outputs": []
},
{
"cell_type": "code",
"source": [
"if d == 3:\n",
" scatter_plot(data, colors[c])"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 413
},
"id": "N5kLSOWgTLDg",
"outputId": "d58fa579-3fb6-44ee-ba3c-3308b7357402"
},
"execution_count": 200,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": [
"## Now let's run a faster algorithm for $H = 10000$"
],
"metadata": {
"id": "-SU0tN1AdKCt"
}
},
{
"cell_type": "code",
"execution_count": 201,
"metadata": {
"id": "Rv5bbq9cdbOU"
},
"outputs": [],
"source": [
"N = 10000\n",
"K = 3\n",
"d = 3\n",
"L = 10"
]
},
{
"cell_type": "code",
"execution_count": 202,
"metadata": {
"id": "v9Ix7FhfdbOV"
},
"outputs": [],
"source": [
"# Generate some data\n",
"np.random.seed(42)\n",
"mu_true = np.random.uniform(-L, L, size = (K, d))\n",
"data = np.random.normal(mu_true, size = (N, K, d))\n",
"data = np.vstack(data)\n",
"np.random.shuffle(data)"
]
},
{
"cell_type": "code",
"execution_count": 203,
"metadata": {
"id": "XWNOXpG1dbOV"
},
"outputs": [],
"source": [
"mu = data[np.random.choice(range(data.shape[0]), K, replace=False)]"
]
},
{
"cell_type": "code",
"source": [
"res, c = kmeans(data, mu)\n",
"plt.plot(res);"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 430
},
"outputId": "851de315-4b00-4437-dba2-7af188f4c4d5",
"id": "TEHtJrspdgtz"
},
"execution_count": 204,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {}
}
]
},
{
"cell_type": "code",
"source": [
"%time kmeans(data, mu)\n",
"%time kmeans(data, mu)\n",
"%time kmeans(data, mu)\n",
"\"\""
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 140
},
"outputId": "9096e0e8-7c69-47a5-c87c-379c7c8084d9",
"id": "6Mhrk1gDdgt0"
},
"execution_count": 205,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"CPU times: user 88.5 ms, sys: 69.7 ms, total: 158 ms\n",
"Wall time: 83.9 ms\n",
"CPU times: user 95.6 ms, sys: 68.2 ms, total: 164 ms\n",
"Wall time: 85.1 ms\n",
"CPU times: user 87.7 ms, sys: 62 ms, total: 150 ms\n",
"Wall time: 84.5 ms\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"''"
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 205
}
]
},
{
"cell_type": "code",
"source": [
"colors = np.array([plt.cm.cool(i/(K-1)) for i in range(K)])"
],
"metadata": {
"id": "QM15hXyPdgt0"
},
"execution_count": 206,
"outputs": []
},
{
"cell_type": "code",
"source": [
"if d == 3:\n",
" scatter_plot(data, colors[c])"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 414
},
"outputId": "76097871-351f-49b5-d56c-fdcc39e34eea",
"id": "GPB2uN_udgt0"
},
"execution_count": 207,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {}
}
]
}
],
"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.9.12"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
},
"colab": {
"provenance": []
}
},
"nbformat": 4,
"nbformat_minor": 0
}