{"id":1335,"date":"2023-07-16T12:41:59","date_gmt":"2023-07-16T12:41:59","guid":{"rendered":"http:\/\/www.philippeadjiman.com\/blog\/?p=1335"},"modified":"2025-07-18T13:14:33","modified_gmt":"2025-07-18T13:14:33","slug":"deep-learning-gymnastics-tensor-broadcasting","status":"publish","type":"post","link":"https:\/\/philippeadjiman.com\/blog\/2023\/07\/16\/deep-learning-gymnastics-tensor-broadcasting\/","title":{"rendered":"Deep Learning Gymnastics #1: Tensor Broadcasting"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In the heart of the implementation of modern deep learning models (yes, including LLMs) always lies some subtle and critical techniques and\/or tricks that are important to know and master.<strong> <\/strong>Tensor Broadcasting is one of them. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Official doc exists (for e.g. <a href=\"https:\/\/pytorch.org\/docs\/stable\/notes\/broadcasting.html\">pytorch<\/a> or <a href=\"https:\/\/www.tensorflow.org\/xla\/broadcasting\">tensorflow<\/a>) but in this post, we&#8217;ll try to introduce the topic in a simple and intuitive way, using a motivating example inspired from the amazing <a href=\"https:\/\/www.youtube.com\/playlist?list=PLAqhIrjkxbuWI23v9cThsA9GvCAUhRvKZ\">series of videos<\/a> from Andrej Karpathy on language modeling. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example of broadcasting in action<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose you have a tensor of size 3 x 4 (tensor having 2 dimensions can also be just called a matrix) , and each row represents a set of counts over 4 options you try to choose from (the higher, the more likely it is the right option), and your goal is to efficiently transform those counts into probability densities.  On a concrete example, you want to go from left to right here:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-1024x241.png?resize=422%2C99\" alt=\"\" class=\"wp-image-1394\" width=\"422\" height=\"99\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image.png?resize=1024%2C241&amp;ssl=1 1024w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image.png?resize=300%2C71&amp;ssl=1 300w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image.png?resize=768%2C181&amp;ssl=1 768w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image.png?w=1148&amp;ssl=1 1148w\" sizes=\"auto, (max-width: 422px) 100vw, 422px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The matrix on the left is our raw counts, and the one on the right is what we&#8217;d like to get. So we&#8217;d like to find an efficient (vectorized) way to sum up all the rows separately, and divide each count by the sum of its row. So we first need to create a matrix of shape 1&#215;3 which contains the sum of each row, typically :<br> \\begin{bmatrix} 150 \\\\  50 \\\\ 100  \\end{bmatrix} <br>The question then is whether the following operation is allowed:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-1.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-1.png?resize=290%2C104\" alt=\"\" class=\"wp-image-1396\" width=\"290\" height=\"104\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-1.png?w=658&amp;ssl=1 658w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-1.png?resize=300%2C108&amp;ssl=1 300w\" sizes=\"auto, (max-width: 290px) 100vw, 290px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">(for the sake of the explanation, we&#8217;re assuming that none of the rows&#8217; sum is equal to 0)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is where broadcasting comes into play. When presented such an operation, broadcasting will find a way to adapt the second matrix to be of the same dimension as the first one, by duplicating its columns, and then perform an efficient element wise division. As follows:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-2.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-2-1024x217.png?resize=658%2C139\" alt=\"\" class=\"wp-image-1404\" width=\"658\" height=\"139\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-2.png?resize=1024%2C217&amp;ssl=1 1024w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-2.png?resize=300%2C64&amp;ssl=1 300w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-2.png?resize=768%2C163&amp;ssl=1 768w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-2.png?w=1472&amp;ssl=1 1472w\" sizes=\"auto, (max-width: 658px) 100vw, 658px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Are your tensors broadcastable?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Whether your doing broadcasting using <a href=\"https:\/\/numpy.org\/doc\/stable\/user\/basics.broadcasting.html\">numpy<\/a>, <a href=\"https:\/\/pytorch.org\/docs\/stable\/notes\/broadcasting.html\">pytorch<\/a> or <a href=\"https:\/\/www.tensorflow.org\/xla\/broadcasting\">tensorflow<\/a> , in order to know if two tensors are &#8220;broadcastable&#8221;, you just need to align the shapes (or dimensions) of your two tensors from right to left, and for each dimension, check if they are either equal, or one of them is 1, or one of them does not exist. If it is the case for all dimensions, then the two tensors are broadcastable. What is the shape  of the resulting tensor? just take the max dimension along each dimension.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s try it on our example. The shape of the first tensor is [3,4] and the second one (before broadcasting) is [3,1] . So let&#8217;s align the shapes and go from right to left and compare each dimension:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-27.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-27-1024x411.png?resize=389%2C156\" alt=\"\" class=\"wp-image-1499\" width=\"389\" height=\"156\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-27.png?resize=1024%2C411&amp;ssl=1 1024w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-27.png?resize=300%2C120&amp;ssl=1 300w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-27.png?resize=768%2C308&amp;ssl=1 768w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-27.png?w=1122&amp;ssl=1 1122w\" sizes=\"auto, (max-width: 389px) 100vw, 389px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This method works also for tensors of any shapes. Let&#8217;s check a couple of other examples:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example 1: Two tensors with shapes <code>A.shape = [4,3,2]<\/code> and<code> B.shape = [3,1]<\/code><br>Example 2: Two tensors with shapes <code>A.shape = [4,3,2]<\/code> and <code>B.shape = [3,1,2]<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Which of the two examples are brodcastable tensors and which are not? Let&#8217;s start by Example 1:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-28.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-28-1024x275.png?resize=568%2C152\" alt=\"\" class=\"wp-image-1500\" width=\"568\" height=\"152\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-28.png?resize=1024%2C275&amp;ssl=1 1024w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-28.png?resize=300%2C80&amp;ssl=1 300w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-28.png?resize=768%2C206&amp;ssl=1 768w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-28.png?resize=1536%2C412&amp;ssl=1 1536w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-28.png?w=1798&amp;ssl=1 1798w\" sizes=\"auto, (max-width: 568px) 100vw, 568px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">All good, you can broadcast those two tensors. Note that for the case of the most left dimension, since it was not existing for the second tensor, it just acts as if it was a 1.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What about Example 2?<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-29.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-29-1024x239.png?resize=610%2C142\" alt=\"\" class=\"wp-image-1501\" width=\"610\" height=\"142\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-29.png?resize=1024%2C239&amp;ssl=1 1024w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-29.png?resize=300%2C70&amp;ssl=1 300w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-29.png?resize=768%2C179&amp;ssl=1 768w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-29.png?resize=1536%2C358&amp;ssl=1 1536w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-29.png?w=1810&amp;ssl=1 1810w\" sizes=\"auto, (max-width: 610px) 100vw, 610px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Because the most left dimension of those two tensors both exists but are not equal, and none of them is 1, then it breaks the conditions for them to be broadcastable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tensor brodcasting in Pytorch and Tensorflow <\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s see broadcasting in action with PyTorch on a example of a tensor of shape 3&#215;3 of counts, that we want to normalize in the same way as our previous example:<\/p>\n\n\n<p>[code language=&#8221;python&#8221;]<br \/>\nimport torch<\/p>\n<p>N = torch.tensor([[10, 20, 10],<br \/>\n                  [20, 5 , 25],<br \/>\n                  [10, 60, 30]], dtype=torch.int32)<br \/>\n# calculate sum along rows<br \/>\nrow_sums = N.sum(dim=1, keepdim=True)<br \/>\n# normalize each row<br \/>\nN_normalized = N \/ row_sums[\/code]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The parameter <code>dim=1 <\/code>is here to say that we want to sum over rows, and for the   <code>keepdim<\/code> parameter, wait for next section to see why we used it and why it is critical.<br>Let&#8217;s now print <code>N<\/code>, <code>row_sums<\/code> and <code>N_normalized<\/code> respectively:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-19.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-19.png?resize=320%2C144\" alt=\"\" class=\"wp-image-1445\" width=\"320\" height=\"144\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-19.png?w=726&amp;ssl=1 726w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-19.png?resize=300%2C135&amp;ssl=1 300w\" sizes=\"auto, (max-width: 320px) 100vw, 320px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">As we can see, the broadcast operation worked as expected as the sum on each row of the results is indeed equal to 1.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s see how the code looks like in tensorflow:<\/p>\n\n\n<p>[code language=&#8221;python&#8221;]<br \/>\nimport tensorflow as tf<\/p>\n<p>N = tf.constant([<br \/>\n    [10, 20, 10],<br \/>\n    [20, 5, 25],<br \/>\n    [10, 60, 30]<br \/>\n], dtype=tf.int32)<\/p>\n<p># calculate sum along rows<br \/>\nrow_sums = tf.reduce_sum(N, axis=1, keepdims=True)<br \/>\n# normalize each row<br \/>\nN_normalized = N \/ row_sums<br \/>\n[\/code]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see, the code is rather similar, up to some differences like the need to use the <code>tf.reduce_sum<\/code> function rather than doing the sum directly on the tensor, and also, the <code>keepdim<\/code> parameter is now in plural (<code>keepdim<strong>s<\/strong><\/code>)<strong>\ud83d\ude05<\/strong> . But printing <code>N_normalized<\/code> returns the same result as with the pytorch code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When things go wrong<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">So, what was this <code>keepdim=True<\/code> (or <code>keepdims=True<\/code> in tensorflow) all about?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you run e.g. the exact same pytorch code as above but without <code>keepdim=True<\/code>, this is what you&#8217;ll get when printing <code>N<\/code>, <code>row_sums<\/code> and <code>N_normalized<\/code> .<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-24.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-24.png?resize=320%2C117\" alt=\"\" class=\"wp-image-1465\" width=\"320\" height=\"117\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-24.png?w=720&amp;ssl=1 720w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-24.png?resize=300%2C110&amp;ssl=1 300w\" sizes=\"auto, (max-width: 320px) 100vw, 320px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see, <code>N_normalized<\/code> is completely messed up and the rows don&#8217;t sum to 1 anymore \ud83e\udd26<br>But how that happened? What did broadcasting do at all? <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, was the operation broadcastable? well, now you know how to check it from previous section. <code>N<\/code> is of shape <code>[3,3]<\/code> and the trick is that now <code>row_sums<\/code> is of shape <code>[3]<\/code> , because pytorch squeezed the dimension and created a line vector. Using the method explained before, you can see that the tensors are broadcastable. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And practically, what happens now is that <code>row_sums<\/code> gets duplicated horizontally instead of being duplicated vertically! In other words, during the operation <code>N \/ row_sums<\/code> , this is what happened to <code>row_sums<\/code> in the process:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-31.png\"><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/ner.jul.mybluehost.me\/wp-content\/uploads\/2023\/07\/image-31-1024x346.png?resize=608%2C205\" alt=\"\" class=\"wp-image-1514\" width=\"608\" height=\"205\" srcset=\"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-31.png?resize=1024%2C346&amp;ssl=1 1024w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-31.png?resize=300%2C101&amp;ssl=1 300w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-31.png?resize=768%2C260&amp;ssl=1 768w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-31.png?resize=1536%2C519&amp;ssl=1 1536w, https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-31.png?w=1662&amp;ssl=1 1662w\" sizes=\"auto, (max-width: 608px) 100vw, 608px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">So as you can see, in that case, the <code>keepdim<\/code> parameter was critical to keep <code>row_sums<\/code> with the same number of dimensions than the initial tensor and thus have the right shape for a proper broadcasting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">ChatBots can help, but only when you know what you&#8217;re doing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This statement holds for any code related generation coming from chat bots like Bard or ChatGPT.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Specifically on that one, depending on the version of the chatBot you&#8217;re using and how you ask your prompt, sometimes you&#8217;ll get the right code (using <code>keepdims=True<\/code>) and sometimes not. But now, for any broadcasting related question, you won&#8217;t be able to get fooled anymore \ud83e\udd29. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Broadcasting is a critical technique that every deep learning developer needs to master in order to efficiently and properly implement state of the art models in an efficient way. And you better understand the nuances and subtleties we discussed (like e.g. the <code>keepdims<\/code> param), otherwise you might silently introduce bugs that will render your whole model useless.  <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Master broadcasting like a pro and learn how a single trick can make your deep learning code faster, cleaner, and more elegant.<\/p>\n","protected":false},"author":1,"featured_media":1764,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[18,41,14,16],"tags":[],"class_list":["post-1335","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deep-learning","category-deep-learning-gymnastics","category-pytorch","category-tensorflow"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/philippeadjiman.com\/blog\/wp-content\/uploads\/2023\/07\/image-31-1.png?fit=1662%2C562&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/posts\/1335","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/comments?post=1335"}],"version-history":[{"count":2,"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/posts\/1335\/revisions"}],"predecessor-version":[{"id":1956,"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/posts\/1335\/revisions\/1956"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/media\/1764"}],"wp:attachment":[{"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/media?parent=1335"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/categories?post=1335"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/philippeadjiman.com\/blog\/wp-json\/wp\/v2\/tags?post=1335"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}