{"id":807,"date":"2026-08-17T17:57:38","date_gmt":"2026-08-17T09:57:38","guid":{"rendered":"https:\/\/www.toothlessos.xyz\/?p=807"},"modified":"2026-08-17T17:57:42","modified_gmt":"2026-08-17T09:57:42","slug":"t-sne-in-50-lines-of-code-with-pytorch","status":"publish","type":"post","link":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/","title":{"rendered":"t-SNE in 50 lines of code with PyTorch"},"content":{"rendered":"\n\n\n<p class=\"wp-block-paragraph\">t-SNE is one of the most popular <strong>dimensional reduction<\/strong> techniques for visualizing high-dimensional data. It preserves the <strong>local structure<\/strong> in the high-dimensional dataset &#8211; In other words, similar data points that are close to each other should still be close to each other after dimension reduction. One classic example here is applying t-SNE on the MNIST hand-written digits dataset, where we can find nice clusters for each of the digits:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"753\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/uploads\/2026\/08\/image-1024x753.png\" alt=\"\" class=\"wp-image-808\" srcset=\"https:\/\/www.toothlessos.xyz\/wp-content\/uploads\/2026\/08\/image-1024x753.png 1024w, https:\/\/www.toothlessos.xyz\/wp-content\/uploads\/2026\/08\/image-300x221.png 300w, https:\/\/www.toothlessos.xyz\/wp-content\/uploads\/2026\/08\/image-768x565.png 768w, https:\/\/www.toothlessos.xyz\/wp-content\/uploads\/2026\/08\/image.png 1181w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Different digits form different clusters after t-SNE, illustrating that t-SNE successfully preserves the local structure of the original dataset. Figure taken from: <a href=\"https:\/\/www.kaggle.com\/code\/evangelosgakias\/t-sne-mnist-dataset#4.-Dataset:-MNIST-Handwritten-Digits\">t-SNE-Mnist-Dataset<\/a><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This ToothlessOS Log provides a demo explanation of t-SNE with step by step walk through to help you better understand this beautiful algorithm.<\/p>\n\n\n\n\n\n<h2 id=\"aioseo-intuition-behind-t-sne-5\" class=\"wp-block-heading\">Intuition behind t-SNE<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Given a dataset <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-d4ee28752517d6062a3ca0314890342d_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#88;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"16\" style=\"vertical-align: 0px;\"\/>, t-SNE builds a probability distribution <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-650eb7688af6737ac325425b5c9a5982_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#80;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"14\" style=\"vertical-align: 0px;\"\/> over pairs of high-dimensional data points. Then, it learns a low dimensional embedding <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-82606c3098bb09002088b0f6f9ffbb2a_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#89;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"14\" style=\"vertical-align: 0px;\"\/> by minimizing the KL-divergence between <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-650eb7688af6737ac325425b5c9a5982_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#80;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"14\" style=\"vertical-align: 0px;\"\/> and a low-dimensional distribution <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-2c758bec4c272382411b95fc0e7ee250_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#81;\" title=\"Rendered by QuickLaTeX.com\" height=\"16\" width=\"14\" style=\"vertical-align: -4px;\"\/>. Personally, the keywords that I keep for t-SNE is <strong>pairwise distances<\/strong> and <strong>matching probability distributions<\/strong>, which we will walk through in the next section. I find it very helpful to use such keywords to capture the <strong>objectives<\/strong> of dimensional reduction algorithms &#8211; to make sure that I use them properly in practice.<\/p>\n\n\n\n<h2 id=\"aioseo-implementation-walkthrough-7\" class=\"wp-block-heading\">Implementation walkthrough<\/h2>\n\n\n\n<h3 id=\"aioseo-step-1-pairwise-squared-distances-8\" class=\"wp-block-heading\">Step 1: Pairwise squared distances<\/h3>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"\u590d\u5236\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>X_dist = torch.sum((X_mat&#91;:, None, :&#93; - X_mat&#91;None, :, :&#93;)**2, axis=-1)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color: #24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E1E4E8\">X_dist <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.sum((X_mat&#91;:, <\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :&#93; <\/span><span style=\"color: #F97583\">-<\/span><span style=\"color: #E1E4E8\"> X_mat&#91;<\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :, :&#93;)<\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=-<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">We start by computing squared Euclidean distance between all pairs of input points:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>d<\/mi><mrow><mi>i<\/mi><mi>j<\/mi><\/mrow><\/msub><mo>=<\/mo><mi>\u2016<\/mi><msub><mi>x<\/mi><mi>i<\/mi><\/msub><mo>\u2212<\/mo><msub><mi>x<\/mi><mi>j<\/mi><\/msub><msup><mi>\u2016<\/mi><mn>2<\/mn><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">d_{ij} = \\|x_i &#8211; x_j\\|^2<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<h3 id=\"aioseo-step-2-high-dimensional-similarities-pji-12\" class=\"wp-block-heading\">Step 2: High dimensional similarities <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-aecc2f29802658e87d6128c1c43468c5_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#80;&#40;&#106;&#124;&#105;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"19\" width=\"46\" style=\"vertical-align: -5px;\"\/><\/h3>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"\u590d\u5236\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>X_sim_scores = torch.exp(-X_dist \/ (2 * (perplexity ** 2)))\nX_cond = X_sim_scores \/ torch.sum(X_sim_scores, axis=1, keepdim=True)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color: #24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E1E4E8\">X_sim_scores <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.exp(<\/span><span style=\"color: #F97583\">-<\/span><span style=\"color: #E1E4E8\">X_dist <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> (<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">*<\/span><span style=\"color: #E1E4E8\"> (perplexity <\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">)))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">X_cond <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> X_sim_scores <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> torch.sum(X_sim_scores, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">keepdim<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">True<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The pairwise distances are converted into similarities of each node <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-695d9d59bd04859c6c99e7feb11daab6_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"6\" style=\"vertical-align: 0px;\"\/> with all other nodes <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-43c82d5bb00a7568d935a12e3bd969dd_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#106;\" title=\"Rendered by QuickLaTeX.com\" height=\"16\" width=\"9\" style=\"vertical-align: -4px;\"\/> using a Gaussian kernel. We can get the conditional probabilities <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-aecc2f29802658e87d6128c1c43468c5_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#80;&#40;&#106;&#124;&#105;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"19\" width=\"46\" style=\"vertical-align: -5px;\"\/> after normalization:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>p<\/mi><mrow><mi>j<\/mi><mi>|<\/mi><mi>i<\/mi><\/mrow><\/msub><mo>=<\/mo><mfrac><mrow><mrow><mi>exp<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mi>\u2016<\/mi><msub><mi>x<\/mi><mi>i<\/mi><\/msub><mo>\u2212<\/mo><msub><mi>x<\/mi><mi>j<\/mi><\/msub><msup><mi>\u2016<\/mi><mn>2<\/mn><\/msup><mi>\/<\/mi><mn>2<\/mn><msubsup><mi>\u03c3<\/mi><mi>i<\/mi><mn>2<\/mn><\/msubsup><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><mrow><msub><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>k<\/mi><mo>\u2260<\/mo><mi>i<\/mi><\/mrow><\/msub><mrow><mi>exp<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mi>\u2016<\/mi><msub><mi>x<\/mi><mi>i<\/mi><\/msub><mo>\u2212<\/mo><msub><mi>x<\/mi><mi>k<\/mi><\/msub><msup><mi>\u2016<\/mi><mn>2<\/mn><\/msup><mi>\/<\/mi><mn>2<\/mn><msubsup><mi>\u03c3<\/mi><mi>i<\/mi><mn>2<\/mn><\/msubsup><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">p_{j|i} = \\frac{\\exp(-\\|x_i &#8211; x_j\\|^2 \/ 2\\sigma_i^2)}{\\sum_{k \\neq i} \\exp(-\\|x_i &#8211; x_k\\|^2 \/ 2\\sigma_i^2)}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The width of the Gaussian kernel <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c202e7eb4d32c2030102aa2961fdd946_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"11\" width=\"15\" style=\"vertical-align: -3px;\"\/> can be tuned via the hyper-parameter <em>perplexity<\/em> &#8211;  a smaller <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c202e7eb4d32c2030102aa2961fdd946_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"11\" width=\"15\" style=\"vertical-align: -3px;\"\/> means the <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-6c40b5dae3cb6ba53a385437339ddffc_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#101;&#120;&#112;&#40;&#45;&#92;&#124;&#120;&#95;&#105;&#45;&#120;&#95;&#106;&#92;&#124;&#94;&#50;&#32;&#47;&#32;&#50;&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;&#94;&#50;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"21\" width=\"170\" style=\"vertical-align: -6px;\"\/> term decays very quickly, and the similarity between far away points are almost 0 (i.e. a small number of effective neighbours); while a larger <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c202e7eb4d32c2030102aa2961fdd946_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"11\" width=\"15\" style=\"vertical-align: -3px;\"\/> makes the exponential term decays slowly, so far away points gets higher probabilities, resulting in a flatter distribution (i.e. a large number of effective neighbours). In this way, <em>perplexity<\/em> becomes the most important <strong>hyperparameter<\/strong> to tune when using t-SNE.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Warning<\/strong>: for simplicity, this implementation uses a fixed Gaussian bandwidth <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c202e7eb4d32c2030102aa2961fdd946_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"11\" width=\"15\" style=\"vertical-align: -3px;\"\/> = <em>perplexity<\/em>, which is not the same procedure used in original t-SNE. This keeps to code short for now and makes things easier to explain for now. For detailed explanation of perplexity, its tuning, and the original implementation with binary search, please refer to the appendix.<\/p>\n\n\n\n<h3 id=\"aioseo-step-3-get-joint-distribution-18\" class=\"wp-block-heading\">Step 3: Get joint distribution<\/h3>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"\u590d\u5236\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>X_joint = (X_cond + X_cond.T) \/ (2 * X.shape&#91;0&#93;)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color: #24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E1E4E8\">X_joint <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> (X_cond <\/span><span style=\"color: #F97583\">+<\/span><span style=\"color: #E1E4E8\"> X_cond.T) <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> (<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">*<\/span><span style=\"color: #E1E4E8\"> X.shape&#91;<\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">&#93;)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">We then symmetrize the conditional probabilities to obtain the joint probabilities <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-10777b855c19c9effe9138e0dfe50271_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#80;&#95;&#123;&#105;&#106;&#125;\" title=\"Rendered by QuickLaTeX.com\" height=\"18\" width=\"22\" style=\"vertical-align: -6px;\"\/>:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>p<\/mi><mrow><mi>i<\/mi><mi>j<\/mi><\/mrow><\/msub><mo>=<\/mo><mfrac><mrow><msub><mi>p<\/mi><mrow><mi>j<\/mi><mi>|<\/mi><mi>i<\/mi><\/mrow><\/msub><mo>+<\/mo><msub><mi>p<\/mi><mrow><mi>i<\/mi><mi>|<\/mi><mi>j<\/mi><\/mrow><\/msub><\/mrow><mrow><mn>2<\/mn><mi>n<\/mi><\/mrow><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">p_{ij} = \\frac{p_{j|i} + p_{i|j}}{2n}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">With these three steps, we now have a joint distribution to model the local structure of the high dimensional data points. Now, we will build the low dimensional embeddings and distribution (for similarities) to <strong>match<\/strong> this high dimensional distribution.<\/p>\n\n\n\n<h3 id=\"aioseo-step-4-initialization-of-low-dimensional-embeddings-y-23\" class=\"wp-block-heading\">Step 4: Initialization of low dimensional embeddings Y<\/h3>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"\u590d\u5236\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>Y, _, _, _, _ = PCA(X, n_components=2) # See prereq PCA implementation\nY = torch.tensor(Y, dtype=torch.float32, requires_grad=True)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color: #24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E1E4E8\">Y, _, _, _, _ <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> PCA(X, <\/span><span style=\"color: #FFAB70\">n_components<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">) <\/span><span style=\"color: #6A737D\"># See prereq PCA implementation<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">Y <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.tensor(Y, <\/span><span style=\"color: #FFAB70\">dtype<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\">torch.float32, <\/span><span style=\"color: #FFAB70\">requires_grad<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">True<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of random initialization, we initialize the low-dimensional embedding with PCA. This usually leads to faster convergence and better results.<\/p>\n\n\n\n<h3 id=\"aioseo-step-5-low-dimensional-similarity-distribution-qi-j-26\" class=\"wp-block-heading\">Step 5: Low-dimensional similarity\/distribution <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-d4203d3bb8403e63adec1f6a470350fb_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#81;&#40;&#105;&#44;&#32;&#106;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"19\" width=\"49\" style=\"vertical-align: -5px;\"\/><\/h3>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"\u590d\u5236\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>Y_dist = torch.sum((Y&#91;:, None, :&#93; - Y&#91;None, :, :&#93;)**2, axis=-1)\nY_sim_scores = 1 \/ (1 + Y_dist)\nY_joint = Y_sim_scores \/ torch.sum(Y_sim_scores)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color: #24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E1E4E8\">Y_dist <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.sum((Y&#91;:, <\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :&#93; <\/span><span style=\"color: #F97583\">-<\/span><span style=\"color: #E1E4E8\"> Y&#91;<\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :, :&#93;)<\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=-<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">Y_sim_scores <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> (<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">+<\/span><span style=\"color: #E1E4E8\"> Y_dist)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">Y_joint <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> Y_sim_scores <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> torch.sum(Y_sim_scores)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">In the low-dimensional space, we use a Student-t kernel with one degree of freedom to measure similarity:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>q<\/mi><mrow><mi>i<\/mi><mi>j<\/mi><\/mrow><\/msub><mo>=<\/mo><mfrac><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">(<\/mo><mn>1<\/mn><mo>+<\/mo><mi>\u2016<\/mi><msub><mi>y<\/mi><mi>i<\/mi><\/msub><mo>\u2212<\/mo><msub><mi>y<\/mi><mi>j<\/mi><\/msub><msup><mi>\u2016<\/mi><mn>2<\/mn><\/msup><msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><\/mrow><mrow><msub><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>k<\/mi><mo>\u2260<\/mo><mi>l<\/mi><\/mrow><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mn>1<\/mn><mo>+<\/mo><mi>\u2016<\/mi><msub><mi>y<\/mi><mi>k<\/mi><\/msub><mo>\u2212<\/mo><msub><mi>y<\/mi><mi>l<\/mi><\/msub><msup><mi>\u2016<\/mi><mn>2<\/mn><\/msup><msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><\/mrow><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">q_{ij} = \\frac{(1 + \\|y_i &#8211; y_j\\|^2)^{-1}}{\\sum_{k \\neq l} (1 + \\|y_k &#8211; y_l\\|^2)^{-1}}\n<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why Student-t kernel instead of Gaussian?<\/strong> In the high-dimensional space, t-SNE uses a Gaussian kernel to convert distances into probabilities. The Gaussian kernel decays quickly, which is ideal for capturing local neighborhoods. However, if we used a Gaussian kernel in the low-dimensional space as well, we would run into the\u00a0<strong>crowding problem<\/strong>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider the original dataset lies uniformly on the high dimension sphere with <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c28bb811669e7727dd3f0bd8ff4433bc_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#68;&#32;&#61;&#32;&#49;&#48;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"57\" style=\"vertical-align: 0px;\"\/> and radius <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-960c55c47192a701c61e73192580b687_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#50;&#114;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"17\" style=\"vertical-align: 0px;\"\/> with one data point at the origin. Take the data point at the origin, we consider points that lie between <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-e15a0b52dcd0ce416df990f3a202e9e2_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#91;&#48;&#44;&#32;&#114;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"19\" width=\"34\" style=\"vertical-align: -5px;\"\/> to be neighbours and points that lie between <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-e6e73912ca99171359c91cefa77dcb67_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#91;&#114;&#44;&#32;&#50;&#114;&#93;\" title=\"Rendered by QuickLaTeX.com\" height=\"18\" width=\"39\" style=\"vertical-align: -5px;\"\/> to be far away. The volume of the neighbouring region is <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c1555259544ee500f05aacc2e77e122c_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#114;&#94;&#68;\" title=\"Rendered by QuickLaTeX.com\" height=\"15\" width=\"19\" style=\"vertical-align: 0px;\"\/> while the volume of the far away region is <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-0e9c05373bccda86bd1aa199fec5fbdd_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#40;&#50;&#94;&#68;&#45;&#49;&#41;&#114;&#94;&#68;\" title=\"Rendered by QuickLaTeX.com\" height=\"20\" width=\"84\" style=\"vertical-align: -5px;\"\/>. In the example here where we reduce D from 10 to 2, we are cramming the 1000x far away data points into only x3 space, causing the crowding. This is part of the paradox that we called <strong>the curse of dimensionality<\/strong>. With the Gaussian kernel that decays quickly, we will run into the same issue that we do not have enough space to pack the far away data points into 2D, causing everything to be mixed up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">While the student-t distribution has a <strong>heavier tail \/ decays slowly<\/strong> &#8211; it assigns higher similarity to points that are relatively farther apart. This gives more space to the far away data points by allowing them to be placed closer to the center. This help to solve the crowding problem.<\/p>\n\n\n\n<h3 id=\"aioseo-step-6-kl-divergence-and-optimization-33\" class=\"wp-block-heading\">Step 6: KL-divergence and optimization<\/h3>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"\u590d\u5236\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>loss = torch.sum(X_joint_clipped * torch.log(X_joint_clipped \/ Y_joint_clipped))\nloss.backward()\noptimizer.step()<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color: #24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E1E4E8\">loss <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.sum(X_joint_clipped <\/span><span style=\"color: #F97583\">*<\/span><span style=\"color: #E1E4E8\"> torch.log(X_joint_clipped <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> Y_joint_clipped))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">loss.backward()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">optimizer.step()<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">We minimize the KL divergence (i.e. matching distributions) between the high dimensional distribution <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-650eb7688af6737ac325425b5c9a5982_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#80;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"14\" style=\"vertical-align: 0px;\"\/> and the low dimensional distribution <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-2c758bec4c272382411b95fc0e7ee250_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#81;\" title=\"Rendered by QuickLaTeX.com\" height=\"16\" width=\"14\" style=\"vertical-align: -4px;\"\/> with respect to the low dimensional embeddings <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-82606c3098bb09002088b0f6f9ffbb2a_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#89;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"14\" style=\"vertical-align: 0px;\"\/>. After several iterations of <strong>gradient descent<\/strong>, we are able to get the embeddings <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-82606c3098bb09002088b0f6f9ffbb2a_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#89;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"14\" style=\"vertical-align: 0px;\"\/> that represents the local structures in the high dimensional dataset, which can be used for visualization.<\/p>\n\n\n\n<h2 id=\"aioseo-full-implementation-36\" class=\"wp-block-heading\">Full implementation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Limitations and practical notes<\/strong>: This implementation is for demo purpose and not suitable for production: The original t-SNE selects\u00a0<img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c202e7eb4d32c2030102aa2961fdd946_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"11\" width=\"15\" style=\"vertical-align: -3px;\"\/>\u200b\u00a0by binary search to match a given perplexity. The code stores full n-by-n matrices, so it does not scale well beyond a few thousand points. Techniques like <em>early exaggeration<\/em> and <em>momentum<\/em> are omitted in this demo.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"\u590d\u5236\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly># PCA as a pre-requisite\n\nimport numpy as np\n\ndef PCA(X: pd.DataFrame, n_components=2):\n    # Step 1: Standardize X (&amp; Get covariance matrix) - preprocessing\n    X_mat = X.to_numpy()\n    X_centered = X_mat - np.mean(X_mat, axis=0, keepdims=True)\n    X_std = X_centered \/ np.std(X_centered, axis=0, keepdims=True)\n    X_cov = np.cov(X_std, rowvar=False, ddof=0)\n\n    # Step 2: SVD on X_std\n    U, S, Vt = np.linalg.svd(X_std)\n\n    # Step 3: Select n components (e.g., top two) from V and project X onto them\n    components = Vt&#91;:n_components, :&#93;\n    eigs = S&#91;:n_components&#93;**2\n\n    # Step 4: Project X onto the selected components (Reconstruction)\n    X_reduced = X_std @ components.T\n\n    # Step 5: Compute explained variance\n    explained_variance = np.sum(eigs) \/ np.sum(S**2)\n\n    return X_reduced, components, eigs, explained_variance, X_cov<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color: #24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #6A737D\"># PCA as a pre-requisite<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F97583\">import<\/span><span style=\"color: #E1E4E8\"> numpy <\/span><span style=\"color: #F97583\">as<\/span><span style=\"color: #E1E4E8\"> np<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F97583\">def<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #B392F0\">PCA<\/span><span style=\"color: #E1E4E8\">(X: pd.DataFrame, n_components<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 1: Standardize X (&amp; Get covariance matrix) - preprocessing<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_mat <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> X.to_numpy()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_centered <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> X_mat <\/span><span style=\"color: #F97583\">-<\/span><span style=\"color: #E1E4E8\"> np.mean(X_mat, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">keepdims<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">True<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_std <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> X_centered <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> np.std(X_centered, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">keepdims<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">True<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_cov <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> np.cov(X_std, <\/span><span style=\"color: #FFAB70\">rowvar<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">False<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">ddof<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 2: SVD on X_std<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    U, S, Vt <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> np.linalg.svd(X_std)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 3: Select n components (e.g., top two) from V and project X onto them<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    components <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> Vt&#91;:n_components, :&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    eigs <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> S&#91;:n_components&#93;<\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #79B8FF\">2<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 4: Project X onto the selected components (Reconstruction)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_reduced <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> X_std <\/span><span style=\"color: #F97583\">@<\/span><span style=\"color: #E1E4E8\"> components.T<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 5: Compute explained variance<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    explained_variance <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> np.sum(eigs) <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> np.sum(S<\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #F97583\">return<\/span><span style=\"color: #E1E4E8\"> X_reduced, components, eigs, explained_variance, X_cov<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"\u590d\u5236\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly># main t-SNE implementation\n\nimport torch\nimport torch.nn.functional as F\n\ndef t_SNE(X: pd.DataFrame, perplexity=30, random_state=42):\n\n    torch.manual_seed(random_state)\n    \n    # Step 1: Compute Squared Distances\n    X_mat = X.to_numpy()\n    X_mat = torch.tensor(X_mat, dtype=torch.float32)\n    X_dist = torch.sum((X_mat&#91;:, None, :&#93; - X_mat&#91;None, :, :&#93;)**2, axis=-1)\n\n    # Step 2: Similarity score &amp; Conditional probabilities (P(j|i))\n    X_sim_scores = torch.exp(-X_dist \/ (2 * (perplexity ** 2))) # Gaussian kernel\n    X_sim_scores.fill_diagonal_(0)\n    X_cond = X_sim_scores \/ torch.sum(X_sim_scores, axis=1, keepdim=True) # Normalize\n\n    # Step 3: Symmetrize to get joint probabilities P(i,j)\n    X_joint = (X_cond + X_cond.T) \/ (2 * X.shape&#91;0&#93;)\n    \n    # Step 4: Initialize low-dimensional embedding Y\n    # Here, we make use of the PCA function defined earlier\n    Y, _, _, _, _ = PCA(X, n_components=2)\n    Y = torch.tensor(Y, dtype=torch.float32, requires_grad=True)\n\n    # Step 5: Low dimensional distances => Similarity scores => Joint probabilities Q(i,j)\n    Y_dist = torch.sum((Y&#91;:, None, :&#93; - Y&#91;None, :, :&#93;)**2, axis=-1)\n    Y_sim_scores = 1 \/ (1 + Y_dist) # Student t-kernel\n    Y_sim_scores.fill_diagonal_(0)\n    Y_joint = Y_sim_scores \/ torch.sum(Y_sim_scores)\n\n    # Step 6: Minimize KL Divergence between P and Q using gradient descent\n    optimizer = torch.optim.Adam(&#91;Y&#93;, lr=0.05)\n    for epoch in range(1000):\n        optimizer.zero_grad()\n        # KL(P||Q) = sum(P * log(P\/Q))\n        Y_joint_clipped = torch.clamp(Y_joint, min=1e-12)\n        X_joint_clipped = torch.clamp(X_joint, min=1e-12)\n        loss = torch.sum(X_joint_clipped * torch.log(X_joint_clipped \/ Y_joint_clipped))\n        loss.backward()\n        optimizer.step()\n\n        if epoch % 100 == 0:\n            print(f'Epoch {epoch}, KL Divergence: {loss.item()}')\n\n        # Recompute Q(i,j) for current Y\n        Y_dist = torch.sum((Y&#91;:, None, :&#93; - Y&#91;None, :, :&#93;)**2, axis=-1)\n        Y_sim_scores = 1 \/ (1 + Y_dist) # Student t-kernel\n        Y_sim_scores.fill_diagonal_(0)\n        Y_joint = Y_sim_scores \/ torch.sum(Y_sim_scores)\n\n    return Y.detach().numpy()<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color: #24292e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #6A737D\"># main t-SNE implementation<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F97583\">import<\/span><span style=\"color: #E1E4E8\"> torch<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F97583\">import<\/span><span style=\"color: #E1E4E8\"> torch.nn.functional <\/span><span style=\"color: #F97583\">as<\/span><span style=\"color: #E1E4E8\"> F<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F97583\">def<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #B392F0\">t_SNE<\/span><span style=\"color: #E1E4E8\">(X: pd.DataFrame, perplexity<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">30<\/span><span style=\"color: #E1E4E8\">, random_state<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">42<\/span><span style=\"color: #E1E4E8\">):<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    torch.manual_seed(random_state)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 1: Compute Squared Distances<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_mat <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> X.to_numpy()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_mat <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.tensor(X_mat, <\/span><span style=\"color: #FFAB70\">dtype<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\">torch.float32)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_dist <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.sum((X_mat&#91;:, <\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :&#93; <\/span><span style=\"color: #F97583\">-<\/span><span style=\"color: #E1E4E8\"> X_mat&#91;<\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :, :&#93;)<\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=-<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 2: Similarity score &amp; Conditional probabilities (P(j|i))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_sim_scores <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.exp(<\/span><span style=\"color: #F97583\">-<\/span><span style=\"color: #E1E4E8\">X_dist <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> (<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">*<\/span><span style=\"color: #E1E4E8\"> (perplexity <\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">))) <\/span><span style=\"color: #6A737D\"># Gaussian kernel<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_sim_scores.fill_diagonal_(<\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_cond <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> X_sim_scores <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> torch.sum(X_sim_scores, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">keepdim<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">True<\/span><span style=\"color: #E1E4E8\">) <\/span><span style=\"color: #6A737D\"># Normalize<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 3: Symmetrize to get joint probabilities P(i,j)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    X_joint <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> (X_cond <\/span><span style=\"color: #F97583\">+<\/span><span style=\"color: #E1E4E8\"> X_cond.T) <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> (<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">*<\/span><span style=\"color: #E1E4E8\"> X.shape&#91;<\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">&#93;)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 4: Initialize low-dimensional embedding Y<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Here, we make use of the PCA function defined earlier<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    Y, _, _, _, _ <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> PCA(X, <\/span><span style=\"color: #FFAB70\">n_components<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    Y <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.tensor(Y, <\/span><span style=\"color: #FFAB70\">dtype<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\">torch.float32, <\/span><span style=\"color: #FFAB70\">requires_grad<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">True<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 5: Low dimensional distances =&gt; Similarity scores =&gt; Joint probabilities Q(i,j)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    Y_dist <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.sum((Y&#91;:, <\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :&#93; <\/span><span style=\"color: #F97583\">-<\/span><span style=\"color: #E1E4E8\"> Y&#91;<\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :, :&#93;)<\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=-<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    Y_sim_scores <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> (<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">+<\/span><span style=\"color: #E1E4E8\"> Y_dist) <\/span><span style=\"color: #6A737D\"># Student t-kernel<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    Y_sim_scores.fill_diagonal_(<\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    Y_joint <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> Y_sim_scores <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> torch.sum(Y_sim_scores)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #6A737D\"># Step 6: Minimize KL Divergence between P and Q using gradient descent<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    optimizer <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.optim.Adam(&#91;Y&#93;, <\/span><span style=\"color: #FFAB70\">lr<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">0.05<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #F97583\">for<\/span><span style=\"color: #E1E4E8\"> epoch <\/span><span style=\"color: #F97583\">in<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #79B8FF\">range<\/span><span style=\"color: #E1E4E8\">(<\/span><span style=\"color: #79B8FF\">1000<\/span><span style=\"color: #E1E4E8\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        optimizer.zero_grad()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        <\/span><span style=\"color: #6A737D\"># KL(P||Q) = sum(P * log(P\/Q))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        Y_joint_clipped <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.clamp(Y_joint, <\/span><span style=\"color: #FFAB70\">min<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">1e-12<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        X_joint_clipped <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.clamp(X_joint, <\/span><span style=\"color: #FFAB70\">min<\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #79B8FF\">1e-12<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        loss <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.sum(X_joint_clipped <\/span><span style=\"color: #F97583\">*<\/span><span style=\"color: #E1E4E8\"> torch.log(X_joint_clipped <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> Y_joint_clipped))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        loss.backward()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        optimizer.step()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        <\/span><span style=\"color: #F97583\">if<\/span><span style=\"color: #E1E4E8\"> epoch <\/span><span style=\"color: #F97583\">%<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #79B8FF\">100<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">==<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">            <\/span><span style=\"color: #79B8FF\">print<\/span><span style=\"color: #E1E4E8\">(<\/span><span style=\"color: #F97583\">f<\/span><span style=\"color: #9ECBFF\">&#39;Epoch <\/span><span style=\"color: #79B8FF\">{<\/span><span style=\"color: #E1E4E8\">epoch<\/span><span style=\"color: #79B8FF\">}<\/span><span style=\"color: #9ECBFF\">, KL Divergence: <\/span><span style=\"color: #79B8FF\">{<\/span><span style=\"color: #E1E4E8\">loss.item()<\/span><span style=\"color: #79B8FF\">}<\/span><span style=\"color: #9ECBFF\">&#39;<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        <\/span><span style=\"color: #6A737D\"># Recompute Q(i,j) for current Y<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        Y_dist <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> torch.sum((Y&#91;:, <\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :&#93; <\/span><span style=\"color: #F97583\">-<\/span><span style=\"color: #E1E4E8\"> Y&#91;<\/span><span style=\"color: #79B8FF\">None<\/span><span style=\"color: #E1E4E8\">, :, :&#93;)<\/span><span style=\"color: #F97583\">**<\/span><span style=\"color: #79B8FF\">2<\/span><span style=\"color: #E1E4E8\">, <\/span><span style=\"color: #FFAB70\">axis<\/span><span style=\"color: #F97583\">=-<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        Y_sim_scores <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> (<\/span><span style=\"color: #79B8FF\">1<\/span><span style=\"color: #E1E4E8\"> <\/span><span style=\"color: #F97583\">+<\/span><span style=\"color: #E1E4E8\"> Y_dist) <\/span><span style=\"color: #6A737D\"># Student t-kernel<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        Y_sim_scores.fill_diagonal_(<\/span><span style=\"color: #79B8FF\">0<\/span><span style=\"color: #E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">        Y_joint <\/span><span style=\"color: #F97583\">=<\/span><span style=\"color: #E1E4E8\"> Y_sim_scores <\/span><span style=\"color: #F97583\">\/<\/span><span style=\"color: #E1E4E8\"> torch.sum(Y_sim_scores)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #E1E4E8\">    <\/span><span style=\"color: #F97583\">return<\/span><span style=\"color: #E1E4E8\"> Y.detach().numpy()<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h2 id=\"aioseo-appendix-perplexity-40\" class=\"wp-block-heading\">Appendix: Perplexity<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: <a href=\"https:\/\/www.cs.toronto.edu\/%7ehinton\/absps\/tsne.pdf#5#2\">tsne.pdf<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Perplexity is defined as:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mtext>Perp<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>P<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><msup><mn>2<\/mn><mrow><mi>H<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>P<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/msup><mo separator=\"true\">,<\/mo><mspace width=\"1em\"><\/mspace><mi>H<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>P<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mrow><munder><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>j<\/mi><mo>\u2260<\/mo><mi>i<\/mi><\/mrow><\/munder><\/mrow><msub><mi>p<\/mi><mrow><mi>j<\/mi><mi>|<\/mi><mi>i<\/mi><\/mrow><\/msub><msub><mi>log<\/mi><mn>2<\/mn><\/msub><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><msub><mi>p<\/mi><mrow><mi>j<\/mi><mi>|<\/mi><mi>i<\/mi><\/mrow><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">\\text{Perp}(P_i) = 2^{H(P_i)},\\quad\nH(P_i) = -\\sum_{j \\neq i} p_{j|i} \\log_2 p_{j|i}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">,where<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>P<\/mi><mi>i<\/mi><\/msub><mo>=<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>p<\/mi><mrow><mn>1<\/mn><mi>|<\/mi><mi>i<\/mi><\/mrow><\/msub><mo separator=\"true\">,<\/mo><msub><mi>p<\/mi><mrow><mn>2<\/mn><mi>|<\/mi><mi>i<\/mi><\/mrow><\/msub><mo separator=\"true\">,<\/mo><mo>\u2026<\/mo><mo separator=\"true\">,<\/mo><msub><mi>p<\/mi><mrow><mi>n<\/mi><mi>|<\/mi><mi>i<\/mi><\/mrow><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P_i = (p_{1|i}, p_{2|i}, \\dots, p_{n|i})<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">is the distribution of &#8220;given data point <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-695d9d59bd04859c6c99e7feb11daab6_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"6\" style=\"vertical-align: 0px;\"\/>, the probability of selecting other points as its neighbours&#8221;, modelled by the Gaussian kernel of step 2.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Therefore, we have the following chain (Note the <strong>monotonicity<\/strong> here):<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Smaller <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c202e7eb4d32c2030102aa2961fdd946_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"11\" width=\"15\" style=\"vertical-align: -3px;\"\/> (width of Gaussian kernel) => More concentrated distribution => Lower entropy => Lower perplexity<\/li>\n\n\n\n<li>Larger <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c202e7eb4d32c2030102aa2961fdd946_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"11\" width=\"15\" style=\"vertical-align: -3px;\"\/> => More flat \/ even distribution => Higher entropy => Higher perplexity<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">In this way, we can use <strong>binary search<\/strong> to find the <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-c202e7eb4d32c2030102aa2961fdd946_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#92;&#115;&#105;&#103;&#109;&#97;&#95;&#105;\" title=\"Rendered by QuickLaTeX.com\" height=\"11\" width=\"15\" style=\"vertical-align: -3px;\"\/> for each data point such that its perplexity <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.toothlessos.xyz\/wp-content\/ql-cache\/quicklatex.com-9b28ce1e2f2d876fca627a6952a74cf0_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#80;&#40;&#105;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"19\" width=\"33\" style=\"vertical-align: -5px;\"\/> matches the value that we set. In this way, perplexity controls the effective number of neighbours for each data point in t-SNE. Usually, we should try multiple perplexity values between 5 and 50 in practice.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>t-SNE is one of the most popular dimensional reduction  [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,5,19],"tags":[45,43,20,37,44],"class_list":["post-807","post","type-post","status-publish","format-standard","hentry","category-cs","category-math","category-ml","tag-dim-reduction","tag-high-dim-data-analysis","tag-machine-learning","tag-optimization","tag-t-sne"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"[latexpage] t-SNE is one of the most popular dimensiona\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"ToothlessOS\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"zh_CN\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ToothlessOS Log - ToothlessOS 14(*Iroha*) - 14.0.0(2603)\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log\" \/>\n\t\t<meta property=\"og:description\" content=\"[latexpage] t-SNE is one of the most popular dimensiona\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/38.246.252.17:8080\/wp-content\/uploads\/2024\/09\/avatar.png\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/38.246.252.17:8080\/wp-content\/uploads\/2024\/09\/avatar.png\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-08-17T09:57:38+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-08-17T09:57:42+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log\" \/>\n\t\t<meta name=\"twitter:description\" content=\"[latexpage] t-SNE is one of the most popular dimensiona\" \/>\n\t\t<meta name=\"twitter:image\" content=\"http:\/\/38.246.252.17:8080\/wp-content\/uploads\/2024\/09\/avatar.png\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#blogposting\",\"name\":\"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log\",\"headline\":\"t-SNE in 50 lines of code with PyTorch\",\"author\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/author\\\/toothlessos\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/www.toothlessos.xyz\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/image.png\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#articleImage\",\"width\":1181,\"height\":869},\"datePublished\":\"2026-08-17T17:57:38+08:00\",\"dateModified\":\"2026-08-17T17:57:42+08:00\",\"inLanguage\":\"zh-CN\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#webpage\"},\"articleSection\":\"Computer Science, Math, ML, Dim reduction, High dim data analysis, Machine Learning, Optimization, t-SNE\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz#listItem\",\"position\":1,\"name\":\"\\u4e3b\\u9801\",\"item\":\"https:\\\/\\\/www.toothlessos.xyz\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/category\\\/cs\\\/#listItem\",\"name\":\"Computer Science\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/category\\\/cs\\\/#listItem\",\"position\":2,\"name\":\"Computer Science\",\"item\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/category\\\/cs\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/category\\\/cs\\\/ml\\\/#listItem\",\"name\":\"ML\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz#listItem\",\"name\":\"\\u4e3b\\u9801\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/category\\\/cs\\\/ml\\\/#listItem\",\"position\":3,\"name\":\"ML\",\"item\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/category\\\/cs\\\/ml\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#listItem\",\"name\":\"t-SNE in 50 lines of code with PyTorch\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/category\\\/cs\\\/#listItem\",\"name\":\"Computer Science\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#listItem\",\"position\":4,\"name\":\"t-SNE in 50 lines of code with PyTorch\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/category\\\/cs\\\/ml\\\/#listItem\",\"name\":\"ML\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/#organization\",\"name\":\"ToothlessOS Log\",\"description\":\"ToothlessOS 14(*Iroha*) - 14.0.0(2603)\",\"url\":\"https:\\\/\\\/www.toothlessos.xyz\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/author\\\/toothlessos\\\/#author\",\"url\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/author\\\/toothlessos\\\/\",\"name\":\"ToothlessOS\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/b54ba28fe16d230cf0621b13014eac65ececff7214e06c1d08d78ab2e0e66147?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"ToothlessOS\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#webpage\",\"url\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/\",\"name\":\"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log\",\"description\":\"[latexpage] t-SNE is one of the most popular dimensiona\",\"inLanguage\":\"zh-CN\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/2026\\\/08\\\/17\\\/t-sne-in-50-lines-of-code-with-pytorch\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/author\\\/toothlessos\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/index.php\\\/author\\\/toothlessos\\\/#author\"},\"datePublished\":\"2026-08-17T17:57:38+08:00\",\"dateModified\":\"2026-08-17T17:57:42+08:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/#website\",\"url\":\"https:\\\/\\\/www.toothlessos.xyz\\\/\",\"name\":\"ToothlessOS Log\",\"description\":\"ToothlessOS 14(*Iroha*) - 14.0.0(2603)\",\"inLanguage\":\"zh-CN\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.toothlessos.xyz\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log","description":"[latexpage] t-SNE is one of the most popular dimensiona","canonical_url":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#blogposting","name":"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log","headline":"t-SNE in 50 lines of code with PyTorch","author":{"@id":"https:\/\/www.toothlessos.xyz\/index.php\/author\/toothlessos\/#author"},"publisher":{"@id":"https:\/\/www.toothlessos.xyz\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/www.toothlessos.xyz\/wp-content\/uploads\/2026\/08\/image.png","@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#articleImage","width":1181,"height":869},"datePublished":"2026-08-17T17:57:38+08:00","dateModified":"2026-08-17T17:57:42+08:00","inLanguage":"zh-CN","mainEntityOfPage":{"@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#webpage"},"isPartOf":{"@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#webpage"},"articleSection":"Computer Science, Math, ML, Dim reduction, High dim data analysis, Machine Learning, Optimization, t-SNE"},{"@type":"BreadcrumbList","@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz#listItem","position":1,"name":"\u4e3b\u9801","item":"https:\/\/www.toothlessos.xyz","nextItem":{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/#listItem","name":"Computer Science"}},{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/#listItem","position":2,"name":"Computer Science","item":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/ml\/#listItem","name":"ML"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz#listItem","name":"\u4e3b\u9801"}},{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/ml\/#listItem","position":3,"name":"ML","item":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/ml\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#listItem","name":"t-SNE in 50 lines of code with PyTorch"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/#listItem","name":"Computer Science"}},{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#listItem","position":4,"name":"t-SNE in 50 lines of code with PyTorch","previousItem":{"@type":"ListItem","@id":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/ml\/#listItem","name":"ML"}}]},{"@type":"Organization","@id":"https:\/\/www.toothlessos.xyz\/#organization","name":"ToothlessOS Log","description":"ToothlessOS 14(*Iroha*) - 14.0.0(2603)","url":"https:\/\/www.toothlessos.xyz\/"},{"@type":"Person","@id":"https:\/\/www.toothlessos.xyz\/index.php\/author\/toothlessos\/#author","url":"https:\/\/www.toothlessos.xyz\/index.php\/author\/toothlessos\/","name":"ToothlessOS","image":{"@type":"ImageObject","@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/b54ba28fe16d230cf0621b13014eac65ececff7214e06c1d08d78ab2e0e66147?s=96&d=mm&r=g","width":96,"height":96,"caption":"ToothlessOS"}},{"@type":"WebPage","@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#webpage","url":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/","name":"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log","description":"[latexpage] t-SNE is one of the most popular dimensiona","inLanguage":"zh-CN","isPartOf":{"@id":"https:\/\/www.toothlessos.xyz\/#website"},"breadcrumb":{"@id":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/#breadcrumblist"},"author":{"@id":"https:\/\/www.toothlessos.xyz\/index.php\/author\/toothlessos\/#author"},"creator":{"@id":"https:\/\/www.toothlessos.xyz\/index.php\/author\/toothlessos\/#author"},"datePublished":"2026-08-17T17:57:38+08:00","dateModified":"2026-08-17T17:57:42+08:00"},{"@type":"WebSite","@id":"https:\/\/www.toothlessos.xyz\/#website","url":"https:\/\/www.toothlessos.xyz\/","name":"ToothlessOS Log","description":"ToothlessOS 14(*Iroha*) - 14.0.0(2603)","inLanguage":"zh-CN","publisher":{"@id":"https:\/\/www.toothlessos.xyz\/#organization"}}]},"og:locale":"zh_CN","og:site_name":"ToothlessOS Log - ToothlessOS 14(*Iroha*) - 14.0.0(2603)","og:type":"article","og:title":"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log","og:description":"[latexpage] t-SNE is one of the most popular dimensiona","og:url":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/","og:image":"https:\/\/38.246.252.17:8080\/wp-content\/uploads\/2024\/09\/avatar.png","og:image:secure_url":"https:\/\/38.246.252.17:8080\/wp-content\/uploads\/2024\/09\/avatar.png","article:published_time":"2026-08-17T09:57:38+00:00","article:modified_time":"2026-08-17T09:57:42+00:00","twitter:card":"summary_large_image","twitter:title":"t-SNE in 50 lines of code with PyTorch - ToothlessOS Log","twitter:description":"[latexpage] t-SNE is one of the most popular dimensiona","twitter:image":"http:\/\/38.246.252.17:8080\/wp-content\/uploads\/2024\/09\/avatar.png"},"aioseo_meta_data":{"post_id":"807","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"BlogPosting","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-08-17 02:58:36","updated":"2026-08-18 02:17:13","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.toothlessos.xyz\" title=\"\u4e3b\u9801\">\u4e3b\u9801<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/\" title=\"Computer Science\">Computer Science<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/ml\/\" title=\"ML\">ML<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tt-SNE in 50 lines of code with PyTorch\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"\u4e3b\u9801","link":"https:\/\/www.toothlessos.xyz"},{"label":"Computer Science","link":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/"},{"label":"ML","link":"https:\/\/www.toothlessos.xyz\/index.php\/category\/cs\/ml\/"},{"label":"t-SNE in 50 lines of code with PyTorch","link":"https:\/\/www.toothlessos.xyz\/index.php\/2026\/08\/17\/t-sne-in-50-lines-of-code-with-pytorch\/"}],"_links":{"self":[{"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/posts\/807","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/comments?post=807"}],"version-history":[{"count":23,"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/posts\/807\/revisions"}],"predecessor-version":[{"id":832,"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/posts\/807\/revisions\/832"}],"wp:attachment":[{"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/media?parent=807"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/categories?post=807"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.toothlessos.xyz\/index.php\/wp-json\/wp\/v2\/tags?post=807"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}