A slow but more straightforward implementation of a 2D "convolution" (technically, cross-correlation) of input `X` with a collection of kernels `W`. Notes ----- This implementation uses ``for`` loops and direct indexing to perform the convolution. As a result, it is slower
(X, W, stride, pad, dilation=0)
| 795 | |
| 796 | |
| 797 | def conv2D_naive(X, W, stride, pad, dilation=0): |
| 798 | """ |
| 799 | A slow but more straightforward implementation of a 2D "convolution" |
| 800 | (technically, cross-correlation) of input `X` with a collection of kernels `W`. |
| 801 | |
| 802 | Notes |
| 803 | ----- |
| 804 | This implementation uses ``for`` loops and direct indexing to perform the |
| 805 | convolution. As a result, it is slower than the vectorized :func:`conv2D` |
| 806 | function that relies on the :func:`col2im` and :func:`im2col` |
| 807 | transformations. |
| 808 | |
| 809 | Parameters |
| 810 | ---------- |
| 811 | X : :py:class:`ndarray <numpy.ndarray>` of shape `(n_ex, in_rows, in_cols, in_ch)` |
| 812 | Input volume. |
| 813 | W: :py:class:`ndarray <numpy.ndarray>` of shape `(kernel_rows, kernel_cols, in_ch, out_ch)` |
| 814 | The volume of convolution weights/kernels. |
| 815 | stride : int |
| 816 | The stride of each convolution kernel. |
| 817 | pad : tuple, int, or 'same' |
| 818 | The padding amount. If 'same', add padding to ensure that the output of |
| 819 | a 2D convolution with a kernel of `kernel_shape` and stride `stride` |
| 820 | produces an output volume of the same dimensions as the input. If |
| 821 | 2-tuple, specifies the number of padding rows and colums to add *on both |
| 822 | sides* of the rows/columns in `X`. If 4-tuple, specifies the number of |
| 823 | rows/columns to add to the top, bottom, left, and right of the input |
| 824 | volume. |
| 825 | dilation : int |
| 826 | Number of pixels inserted between kernel elements. Default is 0. |
| 827 | |
| 828 | Returns |
| 829 | ------- |
| 830 | Z : :py:class:`ndarray <numpy.ndarray>` of shape `(n_ex, out_rows, out_cols, out_ch)` |
| 831 | The covolution of `X` with `W`. |
| 832 | """ |
| 833 | s, d = stride, dilation |
| 834 | X_pad, p = pad2D(X, pad, W.shape[:2], stride=s, dilation=d) |
| 835 | |
| 836 | pr1, pr2, pc1, pc2 = p |
| 837 | fr, fc, in_ch, out_ch = W.shape |
| 838 | n_ex, in_rows, in_cols, in_ch = X.shape |
| 839 | |
| 840 | # update effective filter shape based on dilation factor |
| 841 | fr, fc = fr * (d + 1) - d, fc * (d + 1) - d |
| 842 | |
| 843 | out_rows = int((in_rows + pr1 + pr2 - fr) / s + 1) |
| 844 | out_cols = int((in_cols + pc1 + pc2 - fc) / s + 1) |
| 845 | |
| 846 | Z = np.zeros((n_ex, out_rows, out_cols, out_ch)) |
| 847 | for m in range(n_ex): |
| 848 | for c in range(out_ch): |
| 849 | for i in range(out_rows): |
| 850 | for j in range(out_cols): |
| 851 | i0, i1 = i * s, (i * s) + fr |
| 852 | j0, j1 = j * s, (j * s) + fc |
| 853 | |
| 854 | window = X_pad[m, i0 : i1 : (d + 1), j0 : j1 : (d + 1), :] |