{
  "source_file": "0930",
  "source_platform": "个人博客",
  "author": "Christopher Olah / colah's blog",
  "title_listed": "Understanding Convolutions",
  "keywords": "卷积,CNN,图像",
  "domain": "图像处理",
  "content_type": "深度技术解析",
  "is_paid_flag": "否",
  "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/",
  "publish_date": "2014-07",
  "corpus_stage": "预训练/SFT",
  "notes": "卷积运算图解",
  "id": "9aba8464b48f",
  "collected_at": "2026-10-02T14:28:15",
  "title": "Understanding Convolutions - colah's blog",
  "body_markdown": "Understanding Convolutions\nPosted on July 13, 2014\nneural networks\n,\nconvolutional neural networks\n,\nconvolution\n,\nmath\n,\nprobability\nIn a\nprevious post\n, we built up an understanding of convolutional neural networks, without referring to any significant mathematics. To go further, however, we need to understand convolutions.\nIf we just wanted to understand convolutional neural networks, it might suffice to roughly understand convolutions. But the aim of this series is to bring us to the frontier of convolutional neural networks and explore new options. To do that, we’re going to need to understand convolutions very deeply.\nThankfully, with a few examples, convolution becomes quite a straightforward idea.\nLessons from a Dropped Ball\nImagine we drop a ball from some height onto the ground, where it only has one dimension of motion.\nHow likely is it that a ball will go a distance\n\\(c\\)\nif you drop it and then drop it again from above the point at which it landed?\nLet’s break this down. After the first drop, it will land\n\\(a\\)\nunits away from the starting point with probability\n\\(f(a)\\)\n, where\n\\(f\\)\nis the probability distribution.\nNow after this first drop, we pick the ball up and drop it from another height above the point where it first landed. The probability of the ball rolling\n\\(b\\)\nunits away from the new starting point is\n\\(g(b)\\)\n, where\n\\(g\\)\nmay be a different probability distribution if it’s dropped from a different height.\nIf we fix the result of the first drop so we know the ball went distance\n\\(a\\)\n, for the ball to go a total distance\n\\(c\\)\n, the distance traveled in the second drop is also fixed at\n\\(b\\)\n, where\n\\(a+b=c\\)\n. So the probability of this happening is simply\n\\(f(a) \\cdot g(b)\\)\n.\n1\nLet’s think about this with a specific discrete example. We want the total distance\n\\(c\\)\nto be 3. If the first time it rolls,\n\\(a=2\\)\n, the second time it must roll\n\\(b=1\\)\nin order to reach our total distance\n\\(a+b=3\\)\n. The probability of this is\n\\(f(2) \\cdot g(1)\\)\n.\nHowever, this isn’t the only way we could get to a total distance of 3. The ball could roll 1 units the first time, and 2 the second. Or 0 units the first time and all 3 the second. It could go any\n\\(a\\)\nand\n\\(b\\)\n, as long as they add to 3.\nThe probabilities are\n\\(f(1) \\cdot g(2)\\)\nand\n\\(f(0) \\cdot g(3)\\)\n, respectively.\nIn order to find the\ntotal likelihood\nof the ball reaching a total distance of\n\\(c\\)\n, we can’t consider only one possible way of reaching\n\\(c\\)\n. Instead, we consider\nall the possible ways\nof partitioning\n\\(c\\)\ninto two drops\n\\(a\\)\nand\n\\(b\\)\nand sum over the\nprobability of each way\n.\n\\[...~~ f(0)\\!\\cdot\\! g(3) ~+~ f(1)\\!\\cdot\\! g(2) ~+~ f(2)\\!\\cdot\\! g(1)~~...\\]\nWe already know that the probability for each case of\n\\(a+b=c\\)\nis simply\n\\(f(a) \\cdot g(b)\\)\n. So, summing over every solution to\n\\(a+b=c\\)\n, we can denote the total likelihood as:\n\\[\\sum_{a+b=c} f(a) \\cdot g(b)\\]\nTurns out, we’re doing a convolution! In particular, the convolution of\n\\(f\\)\nand\n\\(g\\)\n, evluated at\n\\(c\\)\nis defined:\n\\[(f\\ast g)(c) = \\sum_{a+b=c} f(a) \\cdot g(b)~~~~\\]\nIf we substitute\n\\(b = c-a\\)\n, we get:\n\\[(f\\ast g)(c) = \\sum_a f(a) \\cdot g(c-a)\\]\nThis is the standard definition\n2\nof convolution.\nTo make this a bit more concrete, we can think about this in terms of positions the ball might land. After the first drop, it will land at an intermediate position\n\\(a\\)\nwith probability\n\\(f(a)\\)\n. If it lands at\n\\(a\\)\n, it has probability\n\\(g(c-a)\\)\nof landing at a position\n\\(c\\)\n.\nTo get the convolution, we consider all intermediate positions.\nVisualizing Convolutions\nThere’s a very nice trick that helps one think about convolutions more easily.\nFirst, an observation. Suppose the probability that a ball lands a certain distance\n\\(x\\)\nfrom where it started is\n\\(f(x)\\)\n. Then, afterwards, the probability that it started a distance\n\\(x\\)\nfrom where it landed is\n\\(f(-x)\\)\n.\nIf we know the ball lands at a position\n\\(c\\)\nafter the second drop, what is the probability that the previous position was\n\\(a\\)\n?\nSo the probability that the previous position was\n\\(a\\)\nis\n\\(g(-(a-c)) = g(c-a)\\)\n.\nNow, consider the probability each intermediate position contributes to the ball finally landing at\n\\(c\\)\n. We know the probability of the first drop putting the ball into the intermediate position a is\n\\(f(a)\\)\n. We also know that the probability of it having been in\n\\(a\\)\n, if it lands at\n\\(c\\)\nis\n\\(g(c-a)\\)\n.\nSumming over the\n\\(a\\)\ns, we get the convolution.\nThe advantage of this approach is that it allows us to visualize the evaluation of a convolution at a value\n\\(c\\)\nin a single picture. By shifting the bottom half around, we can evaluate the convolution at other values of\n\\(c\\)\n. This allows us to understand the convolution as a whole.\nFor example, we can see that it peaks when the distributions align.\nAnd shrinks as the intersection between the distributions gets smaller.\nBy using this trick in an animation, it really becomes possible to visually understand convolutions.\nBelow, we’re able to visualize the convolution of two box functions:\nFrom Wikipedia\nArmed with this perspective, a lot of things become more intuitive.\nLet’s consider a non-probabilistic example. Convolutions are sometimes used in audio manipulation. For example, one might use a function with two spikes in it, but zero everywhere else, to create an echo. As our double-spiked function slides, one spike hits a point in time first, adding that signal to the output sound, and later, another spike follows, adding a second, delayed copy.\nHigher Dimensional Convolutions\nConvolutions are an extremely general idea. We can also use them in a higher number of dimensions.\nLet’s consider our example of a falling ball again. Now, as it falls, it’s position shifts not only in one dimension, but in two.\nConvolution is the same as before:\n\\[(f\\ast g)(c) = \\sum_{a+b=c} f(a) \\cdot g(b)\\]\nExcept, now\n\\(a\\)\n,\n\\(b\\)\nand\n\\(c\\)\nare vectors. To be more explicit,\n\\[(f\\ast g)(c_1, c_2) = \\sum_{\\begin{array}{c}a_1+b_1=c_1\\\\a_2+b_2=c_2\\end{array}} f(a_1,a_2) \\cdot g(b_1,b_2)\\]\nOr in the standard definition:\n\\[(f\\ast g)(c_1, c_2) = \\sum_{a_1, a_2} f(a_1, a_2) \\cdot g(c_1-a_1,~ c_2-a_2)\\]\nJust like one-dimensional convolutions, we can think of a two-dimensional convolution as sliding one function on top of another, multiplying and adding.\nOne common application of this is image processing. We can think of images as two-dimensional functions. Many important image transformations are convolutions where you convolve the image function with a very small, local function called a “kernel.”\nFrom the\nRiver Trail documentation\nThe kernel slides to every position of the image and computes a new pixel as a weighted sum of the pixels it floats over.\nFor example, by averaging a 3x3 box of pixels, we can blur an image. To do this, our kernel takes the value\n\\(1/9\\)\non each pixel in the box,\nDerived from the\nGimp documentation\nWe can also detect edges by taking the values\n\\(-1\\)\nand\n\\(1\\)\non two adjacent pixels, and zero everywhere else. That is, we subtract two adjacent pixels. When side by side pixels are similar, this is gives us approximately zero. On edges, however, adjacent pixels are very different in the direction perpendicular to the edge.\nDerived from the\nGimp documentation\nThe gimp documentation has\nmany other examples\n.\nConvolutional Neural Networks\nSo, how does convolution relate to convolutional neural networks?\nConsider a 1-dimensional convolutional layer with inputs\n\\(\\{x_n\\}\\)\nand outputs\n\\(\\{y_n\\}\\)\n, like we discussed in the\nprevious post\n:\nAs we observed, we can describe the outputs in terms of the inputs:\n\\[y_n = A(x_{n}, x_{n+1}, ...)\\]\nGenerally,\n\\(A\\)\nwould be multiple neurons. But suppose it is a single neuron for a moment.\nRecall that a typical neuron in a neural network is described by:\n\\[\\sigma(w_0x_0 + w_1x_1 + w_2x_2 ~...~ + b)\\]\nWhere\n\\(x_0\\)\n,\n\\(x_1\\)\n… are the inputs. The weights,\n\\(w_0\\)\n,\n\\(w_1\\)\n, … describe how the neuron connects to its inputs. A negative weight means that an input inhibits the neuron from firing, while a positive weight encourages it to. The weights are the heart of the neuron, controlling its behavior.\n3\nSaying that multiple neurons are identical is the same thing as saying that the weights are the same.\nIt’s this wiring of neurons, describing all the weights and which ones are identical, that convolution will handle for us.\nTypically, we describe all the neurons in a layer at once, rather than individually. The trick is to have a weight matrix,\n\\(W\\)\n:\n\\[y = \\sigma(Wx + b)\\]\nFor example, we get:\n\\[y_0 = \\sigma(W_{0,0}x_0 + W_{0,1}x_1 + W_{0,2}x_2 ...)\\]\n\\[y_1 = \\sigma(W_{1,0}x_0 + W_{1,1}x_1 + W_{1,2}x_2 ...)\\]\nEach row of the matrix describes the weights connecting a neuron to its inputs.\nReturning to the convolutional layer, though, because there are multiple copies of the same neuron, many weights appear in multiple positions.\nWhich corresponds to the equations:\n\\[y_0 = \\sigma(W_0x_0 + W_1x_1 -b)\\]\n\\[y_1 = \\sigma(W_0x_1 + W_1x_2 -b)\\]\nSo while, normally, a weight matrix connects every input to every neuron with different weights:\n\\[W = \\left[\\begin{array}{ccccc} \nW_{0,0} & W_{0,1} & W_{0,2} & W_{0,3} & ...\\\\\nW_{1,0} & W_{1,1} & W_{1,2} & W_{1,3} & ...\\\\\nW_{2,0} & W_{2,1} & W_{2,2} & W_{2,3} & ...\\\\\nW_{3,0} & W_{3,1} & W_{3,2} & W_{3,3} & ...\\\\\n...     &   ...   &   ...   &  ...    & ...\\\\\n\\end{array}\\right]\\]\nThe matrix for a convolutional layer like the one above looks quite different. The same weights appear in a bunch of positions. And because neurons don’t connect to many possible inputs, there’s lots of zeros.\n\\[W = \\left[\\begin{array}{ccccc} \nw_0 & w_1 &  0  &  0  & ...\\\\\n 0  & w_0 & w_1 &  0  & ...\\\\\n 0  &  0  & w_0 & w_1 & ...\\\\\n 0  &  0  &  0  & w_0 & ...\\\\\n... & ... & ... & ... & ...\\\\\n\\end{array}\\right]\\]\nMultiplying by the above matrix is the same thing as convolving with\n\\([...0, w_1, w_0, 0...]\\)\n. The function sliding to different positions corresponds to having neurons at those positions.\nWhat about two-dimensional convolutional layers?\nThe wiring of a two dimensional convolutional layer corresponds to a two-dimensional convolution.\nConsider our example of using a convolution to detect edges in an image, above, by sliding a kernel around and applying it to every patch. Just like this, a convolutional layer will apply a neuron to every patch of the image.\nConclusion\nWe introduced a lot of mathematical machinery in this blog post, but it may not be obvious what we gained. Convolution is obviously a useful tool in probability theory and computer graphics, but what do we gain from phrasing convolutional neural networks in terms of convolutions?\nThe first advantage is that we have some very powerful language for describing the wiring of networks. The examples we’ve dealt with so far haven’t been complicated enough for this benefit to become clear, but convolutions will allow us to get rid of huge amounts of unpleasant book-keeping for us.\nSecondly, convolutions come with significant implementational advantages. Many libraries provide highly efficient convolution routines. Further, while convolution naively appears to be an\n\\(O(n^2)\\)\noperation, using some rather deep mathematical insights, it is possible to create a\n\\(O(n\\log(n))\\)\nimplementation. We will discuss this in much greater detail in a future post.\nIn fact, the use of highly-efficient parallel convolution implementations on GPUs has been essential to recent progress in computer vision.\nNext Posts in this Series\nThis post is part of a series on convolutional neural networks and their generalizations. The first two posts will be review for those familiar with deep learning, while later ones should be of interest to everyone. To get updates, subscribe to my\nRSS feed\n!\nPlease comment below or on the side. Pull requests can be made on\ngithub\n.\nAcknowledgments\nI’m extremely grateful to Eliana Lorch, for extensive discussion of convolutions and help writing this post.\nI’m also grateful to Michael Nielsen and Dario Amodei for their comments and support.\nWe want the probability of the ball rolling\n\\(a\\)\nunits the first time and also rolling\n\\(b\\)\nunits the second time. The distributions\n\\(P(A) = f(a)\\)\nand\n\\(P(b) = g(b)\\)\nare independent, with both distributions centered at 0. So\n\\(P(a,b) = P(a) * P(b) = f(a) \\cdot g(b)\\)\n.\n↩\nThe non-standard definition, which I haven’t previously seen, seems to have a lot of benefits. In future posts, we will find this definition very helpful because it lends itself to generalization to new algebraic structures. But it also has the advantage that it makes a lot of algebraic properties of convolutions really obvious.\nFor example, convolution is a commutative operation. That is,\n\\(f\\ast g = g\\ast f\\)\n. Why?\n\\[\\sum_{a+b=c} f(a) \\cdot g(b) ~~=~  \\sum_{b+a=c} g(b) \\cdot f(a)\\]\nConvolution is also associative. That is,\n\\((f\\ast g)\\ast h = f\\ast (g\\ast h)\\)\n. Why?\n\\[\\sum_{(a+b)+c=d} (f(a) \\cdot g(b)) \\cdot h(c) ~~=~ \\sum_{a+(b+c)=d} f(a) \\cdot (g(b) \\cdot h(c))\\]\n↩\nThere’s also the bias, which is the “threshold” for whether the neuron fires, but it’s much simpler and I don’t want to clutter this section talking about it.\n↩\nSubscribe to the\nRSS feed\n.\n                    \n                    Built by\nOinkina\nwith\nHakyll\nusing\nBootstrap\n,\nMathJax\n, and\nDisqus\n.\nEnable JavaScript for footnotes, Disqus comments, and other cool stuff.",
  "metadata": {
    "title": "Understanding Convolutions - colah's blog",
    "description": null,
    "keywords": null,
    "author": null
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [],
  "images": [
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-fagb.png",
      "bytes": 22730,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-ee30b38a580a341863e1.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-fagb.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-split-21.png",
      "bytes": 9481,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-abd6eafc0dc2d6dcfd8f.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-split-21.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-splits-12-03.png",
      "bytes": 16560,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-773c876569d5c28a9ca5.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-splits-12-03.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-OnePath.png",
      "bytes": 32882,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-5caa189693957bb1e64a.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-OnePath.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-SumPaths.png",
      "bytes": 68815,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-a02469e40f35a95c732a.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-SumPaths.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-Reverse.png",
      "bytes": 30554,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-f89754a18e7e9055512b.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-Reverse.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-BackProb.png",
      "bytes": 49425,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-7ff8f62041453dde5baa.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-BackProb.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-Intermediate.png",
      "bytes": 104933,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-96276fb4e67475f67263.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-Intermediate.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-Intermediate-Align.png",
      "bytes": 84580,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-9f53df8645d11fa042cd.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-Intermediate-Align.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-Intermediate-Sep.png",
      "bytes": 77476,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-f0c9f90b6b2c5bb89754.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-Intermediate-Sep.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Wiki-BoxConvAnim.gif",
      "bytes": 84640,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-e11631d6f4f3fc749711.gif",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Wiki-BoxConvAnim.gif",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-TwoDim.png",
      "bytes": 104166,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-6dbed237ba82690b42ac.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/ProbConv-TwoDim.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/RiverTrain-ImageConvDiagram.png",
      "bytes": 14329,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-8cbec854450313aa4b67.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/RiverTrain-ImageConvDiagram.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Gimp-Blur.png",
      "bytes": 393482,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-00f3e64d6059a571db85.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Gimp-Blur.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Gimp-Edge.png",
      "bytes": 274841,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-691f8ab4af902fdbbbf2.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Gimp-Edge.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Conv-9-Conv2-XY.png",
      "bytes": 26023,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-2fbf997e53c70d654d13.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Conv-9-Conv2-XY.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Conv-9-Conv2-XY-W.png",
      "bytes": 28574,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-69ec7e0b9ce90222c40d.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Conv-9-Conv2-XY-W.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Conv2-5x5-Conv2-XY.png",
      "bytes": 83933,
      "reused": false,
      "local_path": "attachments/9aba8464b48f/images/body-3a45fb0272f72080cca3.png",
      "src": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/img/Conv2-5x5-Conv2-XY.png",
      "alt": "",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://colah.github.io/posts/tags/neural_networks.html",
      "label": "neural networks",
      "type": "a"
    },
    {
      "href": "https://colah.github.io/posts/tags/convolutional_neural_networks.html",
      "label": "convolutional neural networks",
      "type": "a"
    },
    {
      "href": "https://colah.github.io/posts/tags/convolution.html",
      "label": "convolution",
      "type": "a"
    },
    {
      "href": "https://colah.github.io/posts/tags/math.html",
      "label": "math",
      "type": "a"
    },
    {
      "href": "https://colah.github.io/posts/tags/probability.html",
      "label": "probability",
      "type": "a"
    },
    {
      "href": "https://colah.github.io/posts/2014-07-Conv-Nets-Modular/",
      "label": "previous post",
      "type": "a"
    },
    {
      "href": "http://intellabs.github.io/RiverTrail/tutorial/",
      "label": "River Trail documentation",
      "type": "a"
    },
    {
      "href": "http://docs.gimp.org/en/plug-in-convmatrix.html",
      "label": "Gimp documentation",
      "type": "a"
    },
    {
      "href": "https://colah.github.io/rss.xml",
      "label": "RSS feed",
      "type": "a"
    },
    {
      "href": "https://github.com/colah/Conv-Nets-Series",
      "label": "github",
      "type": "a"
    },
    {
      "href": "https://github.com/oinkina",
      "label": "Oinkina",
      "type": "a"
    },
    {
      "href": "http://jaspervdj.be/hakyll",
      "label": "Hakyll",
      "type": "a"
    },
    {
      "href": "http://getbootstrap.com/",
      "label": "Bootstrap",
      "type": "a"
    },
    {
      "href": "http://www.mathjax.org/",
      "label": "MathJax",
      "type": "a"
    },
    {
      "href": "http://disqus.com/",
      "label": "Disqus",
      "type": "a"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:52:33Z",
  "source_url": "https://colah.github.io/posts/2014-07-Understanding-Convolutions/",
  "papers": [],
  "actual_video_files": [],
  "body_chars": 13402,
  "expected_body_images": 18,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/9aba8464b48f/pages/source.html",
  "local_html": "attachments/9aba8464b48f/pages/article.html",
  "body_base_path": "attachments/9aba8464b48f/pages/",
  "body_markdown_path": "attachments/9aba8464b48f/pages/article.md"
}