Chương 5·Bài 5.1

Convolution và pooling

Bài 1Conv2d tay

Conv2d của PyTorch thật ra tính tương quan chéo (cross-correlation): kernel trượt trên ảnh không bị lật. Bài này chạy một lớp conv với trọng số cho sẵn.

Ảnh X có shape (1, 2, H, W): một ảnh, hai kênh. Trọng số weight có shape (3, 2, 2, 2): ba kernel, mỗi kernel 2×22\times2 trên cả hai kênh. Mỗi kernel có một bias. Ô đầu ra (o,i,j)(o, i, j) là

bo+∑c∑u,vWo,c,u,v Xc, i⋅s+u⋅d−p, j⋅s+v⋅d−p,b_o+\sum_{c}\sum_{u,v}W_{o,c,u,v}\,X_{c,\ i\cdot s+u\cdot d-p,\ j\cdot s+v\cdot d-p},

với stride ss, dilation dd, padding pp (ô nằm ngoài ảnh tính bằng 00).

Dữ liệu vào

Mỗi tệp input.json gồm X, weight, bias, stride, padding và dilation.

Kết quả

Một đối tượng JSON {"output": [...]} có shape (1, 3, H_out, W_out).

Chấm điểm

Có 20 test, mỗi test 5 điểm. Số thực được chấm với sai số khoảng 10−710^{-7}.

Ví dụ

Input mẫu

{
  "X": [
    [
      [
        [
          0.9941649847270383, 0.8714908410965864, 0.4778359318420114,
          -0.20362094367134093, 0.8034539093416588,
          -0.10027453719883658
        ],
        [
          -0.8648482046319442, -0.2130301157834983,
          -0.6578936361113877, 0.21772600459652044, 1.3850889084688136,
          1.8634260914104992
        ],
        [
          -1.2527331997986315, -0.4707816111094908,
          -0.7425036294286324, 0.6450882188831598,
          -0.29880416570218327, -1.0891469541920324
        ],
        [
          0.8033435534769493, -0.43478439722307866, 2.457454582730744,
          -0.6338210740064577, -0.7037795521940649, 0.7531111327089655
        ],
        [
          0.6263366991486764, -0.313342621950984, 1.3558802518118604,
          1.112885515204439, -1.3567707475615751, 0.3094659863268635
        ]
      ],
      [
        [
          0.7574717789521999, 1.105123118323312, 1.6236190880175154,
          -0.5761509831780386, 1.0722196283391285,
          -0.16145609361713362
        ],
        [
          -0.64338461600537, -0.9313659603090584, -0.1370129254299484,
          0.6511270684465356, 0.053839787449426926,
          -1.6471171249999417
        ],
        [
          0.2083156204451989, -0.7747172489119286, 2.823924984056371,
          -0.8487206457222503, 0.25661263805896567,
          0.32352377288020645
        ],
        [
          -0.3980148546181156, 0.07216566155558686, 0.5107334489314023,
          -0.9586381425138399, 0.6232986299065064, -1.2692790149342876
        ],
        [
          1.386728854325789, -0.21843193962922577, -0.832774545662875,
          0.7510103122104543, -1.1077306475074769, 1.4648801505380327
        ]
      ]
    ]
  ],
  "weight": [
    [
      [
        [-0.6656224886283981, -0.1864821966337601],
        [-0.1514703172479926, 0.37363081168314854]
      ],
      [
        [-1.0385591944125616, -0.3241147287416149],
        [0.3682096542710066, 0.8077865558183547]
      ]
    ],
    [
      [
        [0.29670538633665117, -0.8130354386403779],
        [0.8974657862190032, 1.2778490472464958]
      ],
      [
        [-1.3523799397398009, -0.5736624805112627],
        [1.523856789838555, -0.9977333998813952]
      ]
    ],
    [
      [
        [-0.230076087150105, 0.008629864788336928],
        [-0.7960030374661429, -0.25754399571856773]
      ],
      [
        [-1.288038371990468, -0.8996272217183028],
        [-0.8366453303762007, -0.1160305810858022]
      ]
    ]
  ],
  "bias": [0.5247383993976499, -0.062208514508075845, -1.0663812068072698],
  "stride": 1,
  "padding": 0,
  "dilation": 1
}

Output mẫu

{
  "output": [
    [
      [
        [
          -2.38222476995108, -2.4855847644766262, -0.5983110625955299,
          1.5290631510759474, -1.8768536239756517
        ],
        [
          1.5749405957397258, 3.5906655468621853, 1.56100474535285,
          -0.8867366761657783, -0.27261809752718286
        ],
        [
          1.1087373354756995, 2.289052364489781, -2.9544268740771185,
          0.9329133137681045, 0.14755473873677127
        ],
        [
          0.5832811376483922, -0.08368748701332762,
          -0.7020673548525129, 0.5776673526875621, 1.7133774746778758
        ]
      ],
      [
        [
          -3.2337077488793247, -4.932527690824646, -2.7907700210605375,
          2.292065245443437, 4.2499553837424
        ],
        [
          0.6233089817723445, -3.6217653998620474, 4.685346263683165,
          -3.3874331874570576, -1.885880653517321
        ],
        [
          -0.40156991980667, 2.179968254223109, -1.0088295518350647,
          -2.1781551185096415, 2.748982708180901
        ],
        [
          3.519335750001745, -0.6303690720382181, 1.6620981691971823,
          2.7754915264208067, -4.969915785377326
        ]
      ],
      [
        [
          -1.8678074126797433, -3.012729815532116, -2.2443477484648775,
          -2.316133965172847, -3.9242898509815003
        ],
        [
          1.831378518526195, 1.186322904046677, -3.161679952097028,
          -1.7478677229804596, 0.309587628126696
        ],
        [
          -0.5564457738335163, -2.9135382997682573, -5.872749310216174,
          1.0604459458662148, -1.6365637175646475
        ],
        [
          -2.3599491067264045, -1.3179618302766587, -2.188984852307358,
          -1.2888320897470753, 1.19818559529604
        ]
      ]
    ]
  ]
}

Bài 2Gradient max pool

Max pooling chỉ cho gradient đi qua ô lớn nhất của mỗi cửa sổ. Khi các cửa sổ chồng lên nhau, một ô có thể thắng ở nhiều cửa sổ, và gradient của nó được cộng từ tất cả.

Cho ảnh X cỡ H×WH\times W, các giá trị khác nhau đôi một. Áp MaxPool2d(kernel_size=2, stride=1), nên các cửa sổ 2×22\times2 chồng lên nhau. Loss là tổng mọi ô đầu ra. Hãy tính đầu ra pooling và gradient của loss theo từng ô của X.

Dữ liệu vào

Mỗi tệp input.json gồm X.

Kết quả

Một đối tượng JSON gồm pooled (cỡ (H−1)×(W−1)(H-1)\times(W-1)) và gradient (cỡ H×WH\times W).

Chấm điểm

Có 20 test, mỗi test 5 điểm.

Ví dụ

Input mẫu

{
  "X": [
    [2.0, 16.0, 1.0, 8.0, 18.0],
    [4.0, 17.0, 12.0, 0.0, 19.0],
    [11.0, 14.0, 6.0, 10.0, 7.0],
    [3.0, 13.0, 15.0, 5.0, 9.0]
  ]
}

Output mẫu

{
  "pooled": [
    [17.0, 17.0, 12.0, 19.0],
    [17.0, 17.0, 12.0, 19.0],
    [14.0, 15.0, 15.0, 10.0]
  ],
  "gradient": [[0, 0, 0, 0, 0], [0, 4, 2, 0, 2], [0, 1, 0, 1, 0], [0, 0, 2, 0, 0]]
}

Bài 3Shape và vùng nhìn

Trước khi dựng một CNN, nên lần theo shape qua từng lớp, cùng hai đại lượng: receptive field rr (mỗi ô đầu ra nhìn được bao nhiêu ô của ảnh gốc theo mỗi chiều) và jump jj (hai ô liền nhau ở đầu ra cách nhau bao nhiêu ô trên ảnh gốc). Bắt đầu với r=j=1r=j=1; qua một lớp kernel kk, stride ss, dilation dd:

r←r+(k−1) d j,j←j⋅s.r\leftarrow r+(k-1)\,d\,j,\qquad j\leftarrow j\cdot s.

Mỗi lớp là conv (Conv2d có bias, ra out_channels kênh) hoặc pool (MaxPool2d, giữ số kênh, bỏ qua out_channels). Kích thước không gian sau mỗi lớp là ⌊h+2p−d(k−1)−1s⌋+1\left\lfloor\frac{h+2p-d(k-1)-1}{s}\right\rfloor+1.

Hãy báo shape [1, C, H, W] sau từng lớp, rr và jj sau từng lớp, và tổng số tham số của các lớp conv.

Dữ liệu vào

Mỗi tệp input.json gồm height, width, channels và layers (mỗi lớp có kind, kernel, stride, padding, dilation, out_channels).

Kết quả

Một đối tượng JSON gồm shapes, receptive_field, jump và parameters.

Chấm điểm

Có 20 test, mỗi test 5 điểm.

Ví dụ

Input mẫu

{
  "height": 13,
  "width": 15,
  "channels": 2,
  "layers": [
    {
      "kind": "conv",
      "kernel": 3,
      "stride": 1,
      "padding": 1,
      "dilation": 1,
      "out_channels": 4
    },
    {
      "kind": "pool",
      "kernel": 2,
      "stride": 2,
      "padding": 0,
      "dilation": 1,
      "out_channels": 4
    },
    {
      "kind": "conv",
      "kernel": 3,
      "stride": 1,
      "padding": 1,
      "dilation": 1,
      "out_channels": 3
    }
  ]
}

Output mẫu

{
  "shapes": [[1, 4, 13, 15], [1, 4, 6, 7], [1, 3, 6, 7]],
  "receptive_field": [3, 4, 8],
  "jump": [1, 2, 2],
  "parameters": 187
}

Bài 4Chồng 3x3

Một conv 7×77\times7 và ba conv 3×33\times3 chồng lên nhau nhìn cùng một vùng 7×77\times7 của ảnh. Vì sao các CNN hiện đại chọn cách thứ hai? Bài này so bằng số.

Mỗi thiết kế là một dãy lớp Conv2d có bias. Lớp có kernel kk, stride ss, padding pp, nhận cc kênh và ra oo kênh:

  • Kích thước không gian mới: ⌊h+2p−ks⌋+1\left\lfloor\frac{h+2p-k}{s}\right\rfloor+1.
  • Số tham số: o⋅c⋅k2+oo\cdot c\cdot k^2+o.
  • Số phép nhân-cộng (MAC): mỗi ô đầu ra cần c⋅k2c\cdot k^2 phép, nhân với o⋅hout⋅wouto\cdot h_{out}\cdot w_{out} ô.
  • Receptive field: bắt đầu r=j=1r=j=1, qua mỗi lớp r←r+(k−1)jr\leftarrow r+(k-1)j rồi j←j⋅sj\leftarrow j\cdot s.

Với mỗi thiết kế, hãy báo shape [C, H, W] sau từng lớp, tổng tham số, tổng MAC và receptive field cuối. Thêm hai tỉ số: tham số và MAC của thiết kế thứ hai trong compare chia cho của thiết kế thứ nhất.

Dữ liệu vào

Một đối tượng JSON gồm channels, height, width (của ảnh vào), designs (từ tên thiết kế tới list lớp, mỗi lớp có kernel, stride, padding, out_channels) và compare (hai tên).

Kết quả

Một đối tượng JSON: mỗi thiết kế là một trường {"shapes": [...], "parameters": ..., "macs": ..., "receptive_field": ...}, cùng hai trường param_ratio và mac_ratio.

Chấm điểm

Có 20 test, mỗi test 5 điểm. Số thực được chấm với sai số khoảng 10−710^{-7}.

Ví dụ

Input mẫu

{
  "channels": 64,
  "height": 56,
  "width": 56,
  "compare": ["big", "stack"],
  "designs": {
    "big": [{"kernel": 7, "stride": 1, "padding": 3, "out_channels": 64}],
    "stack": [
      {"kernel": 3, "stride": 1, "padding": 1, "out_channels": 64},
      {"kernel": 3, "stride": 1, "padding": 1, "out_channels": 64},
      {"kernel": 3, "stride": 1, "padding": 1, "out_channels": 64}
    ]
  }
}

Output mẫu

{
  "big": {
    "shapes": [[64, 56, 56]],
    "parameters": 200768,
    "macs": 629407744,
    "receptive_field": 7
  },
  "stack": {
    "shapes": [[64, 56, 56], [64, 56, 56], [64, 56, 56]],
    "parameters": 110784,
    "macs": 346816512,
    "receptive_field": 7
  },
  "param_ratio": 0.5518010838380618,
  "mac_ratio": 0.5510204081632653
}

Bài 5im2col

Thư viện thường tính tích chập bằng một phép nhân ma trận lớn: trải mọi cửa sổ của ảnh thành các cột (im2col), rồi nhân với ma trận trọng số. Bài này làm đúng việc ấy.

Ảnh X có CC kênh, cỡ H×WH\times W. Trọng số có OO kernel, mỗi kernel cỡ C×k×kC\times k\times k. Với stride ss và padding pp (ô ngoài ảnh bằng 00), đầu ra cỡ Ho×WoH_o\times W_o.

  1. Dựng ma trận cols có Ck2C k^2 hàng và HoWoH_o W_o cột. Hàng c k2+u k+vc\,k^2+u\,k+v, cột i Wo+ji\,W_o+j là ô Xc, is+u−p, js+v−pX_{c,\ i s+u-p,\ j s+v-p}.
  2. Trải mỗi kernel thành một hàng Ck2C k^2 số theo cùng thứ tự (c,u,v)(c,u,v). Nhân ma trận trọng số (O×Ck2O\times Ck^2) với cols, cộng bias, rồi xếp lại thành O×Ho×WoO\times H_o\times W_o.
  3. blowup: số phần tử của cols chia số phần tử của ảnh.

Dữ liệu vào

Một đối tượng JSON gồm X, weight, bias, stride và padding.

Kết quả

Một đối tượng JSON gồm cols_shape, cols, output và blowup.

Giới hạn

  • C≤2C\le2, H,W≤6H,W\le6, k≤3k\le3, O≤3O\le3.

Chấm điểm

Có 20 test, mỗi test 5 điểm. Số thực được chấm với sai số khoảng 10−710^{-7}.

Ví dụ

Input mẫu

{
  "X": [[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]],
  "weight": [[[[1.0, 0.0], [0.0, -1.0]]]],
  "bias": [0.5],
  "stride": 1,
  "padding": 0
}

Output mẫu

{
  "cols_shape": [4, 4],
  "cols": [
    [1.0, 2.0, 4.0, 5.0],
    [2.0, 3.0, 5.0, 6.0],
    [4.0, 5.0, 7.0, 8.0],
    [5.0, 6.0, 8.0, 9.0]
  ],
  "output": [[[-3.5, -3.5], [-3.5, -3.5]]],
  "blowup": 1.7777777777777777
}