Repository: d2l-ai/d2l-en

Issue:
bug in pytorch naive bayes code
In [sec 18.9](http://d2l.ai/chapter_appendix-mathematics-for-deep-learning/naive-bayes.html), the pytorch code does not binarize the data, unlike the mxnet code.
Typo in 5.2.2.1. Built-in Initialization
The TensorFlow tab for the final block in this section of 5.2.2.1 has a typo in the initialization.

![image](https://user-images.githubusercontent.com/8428972/116166071-30122700-a6cb-11eb-9d6e-107c3d53fd99.png)

The text says the constant initialization is 42 while the code uses a constant of 1.


Discussion:

Resolving patch:
diff --git a/d2l/mxnet.py b/d2l/mxnet.py
--- a/d2l/mxnet.py
+++ b/d2l/mxnet.py
@@ -1410,6 +1410,7 @@ def evaluate_accuracy_gpus(net, data_iter, split_f=d2l.split_batch):
 # Defined in file: ./chapter_computer-vision/image-augmentation.md
 def train_batch_ch13(net, features, labels, loss, trainer, devices,
                      split_f=d2l.split_batch):
+    """Train for a minibatch with mutiple GPUs (defined in Chapter 13)."""
     X_shards, y_shards = split_f(features, labels, devices)
     with autograd.record():
         pred_shards = [net(X_shard) for X_shard in X_shards]
@@ -1418,7 +1419,7 @@ def train_batch_ch13(net, features, labels, loss, trainer, devices,
             for pred_shard, y_shard in zip(pred_shards, y_shards)]
     for l in ls:
         l.backward()
-    # The True flag allows parameters with stale gradients, which is useful
+    # The `True` flag allows parameters with stale gradients, which is useful
     # later (e.g., in fine-tuning BERT)
     trainer.step(labels.shape[0], ignore_stale_grad=True)
     train_loss_sum = sum([float(l.sum()) for l in ls])
@@ -1431,11 +1432,13 @@ def train_batch_ch13(net, features, labels, loss, trainer, devices,
 # Defined in file: ./chapter_computer-vision/image-augmentation.md
 def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,
                devices=d2l.try_all_gpus(), split_f=d2l.split_batch):
+    """Train a model with mutiple GPUs (defined in Chapter 13)."""
     timer, num_batches = d2l.Timer(), len(train_iter)
     animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0, 1],
                             legend=['train loss', 'train acc', 'test acc'])
     for epoch in range(num_epochs):
-        # Store training_loss, training_accuracy, num_examples, num_features
+        # Sum of training loss, sum of training accuracy, no. of examples,
+        # no. of predictions
         metric = d2l.Accumulator(4)
         for i, (features, labels) in enumerate(train_iter):
             timer.start()
@@ -1462,7 +1465,7 @@ def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,
 
 # Defined in file: ./chapter_computer-vision/bounding-box.md
 def box_corner_to_center(boxes):
-    """Convert from (upper_left, bottom_right) to (center, width, height)"""
+    """Convert from (upper-left, lower-right) to (center, width, height)."""
     x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
     cx = (x1 + x2) / 2
     cy = (y1 + y2) / 2
@@ -1473,7 +1476,7 @@ def box_corner_to_center(boxes):
 
 
 def box_center_to_corner(boxes):
-    """Convert from (center, width, height) to (upper_left, bottom_right)"""
+    """Convert from (center, width, height) to (upper-left, lower-right)."""
     cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
     x1 = cx - 0.5 * w
     y1 = cy - 0.5 * h
@@ -1486,8 +1489,8 @@ def box_center_to_corner(boxes):
 # Defined in file: ./chapter_computer-vision/bounding-box.md
 def bbox_to_rect(bbox, color):
     """Convert bounding box to matplotlib format."""
-    # Convert the bounding box (top-left x, top-left y, bottom-right x,
-    # bottom-right y) format to matplotlib format: ((upper-left x,
+    # Convert the bounding box (upper-left x, upper-left y, lower-right x,
+    # lower-right y) format to the matplotlib format: ((upper-left x,
     # upper-left y), width, height)
     return d2l.plt.Rectangle(xy=(bbox[0], bbox[1]), width=bbox[2] - bbox[0],
                              height=bbox[3] - bbox[1], fill=False,
@@ -1496,16 +1499,17 @@ def bbox_to_rect(bbox, color):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def multibox_prior(data, sizes, ratios):
+    """Generate anchor boxes with different shapes centered on each pixel."""
     in_height, in_width = data.shape[-2:]
     device, num_sizes, num_ratios = data.ctx, len(sizes), len(ratios)
     boxes_per_pixel = (num_sizes + num_ratios - 1)
     size_tensor = d2l.tensor(sizes, ctx=device)
     ratio_tensor = d2l.tensor(ratios, ctx=device)
-    # Offsets are required to move the anchor to center of a pixel
-    # Since pixel (height=1, width=1), we choose to offset our centers by 0.5
+    # Offsets are required to move the anchor to the center of a pixel. Since
+    # a pixel has height=1 and width=1, we choose to offset our centers by 0.5
     offset_h, offset_w = 0.5, 0.5
-    steps_h = 1.0 / in_height  # Scaled steps in y axis
-    steps_w = 1.0 / in_width  # Scaled steps in x axis
+    steps_h = 1.0 / in_height  # Scaled steps in y-axis
+    steps_w = 1.0 / in_width  # Scaled steps in x-axis
 
     # Generate all center points for the anchor boxes
     center_h = (d2l.arange(in_height, ctx=device) + offset_h) * steps_h
@@ -1513,23 +1517,21 @@ def multibox_prior(data, sizes, ratios):
     shift_x, shift_y = d2l.meshgrid(center_w, center_h)
     shift_x, shift_y = shift_x.reshape(-1), shift_y.reshape(-1)
 
-    # Generate boxes_per_pixel number of heights and widths which are later
+    # Generate `boxes_per_pixel` number of heights and widths that are later
     # used to create anchor box corner coordinates (xmin, xmax, ymin, ymax)
-    # concat (various sizes, first ratio) and (first size, various ratios)
     w = np.concatenate((size_tensor * np.sqrt(ratio_tensor[0]),
-                        sizes[0] * np.sqrt(ratio_tensor[1:])))\
-                        * in_height / in_width  # handle rectangular inputs
+                        sizes[0] * np.sqrt(ratio_tensor[1:]))) \
+                        * in_height / in_width  # Handle rectangular inputs
     h = np.concatenate((size_tensor / np.sqrt(ratio_tensor[0]),
                         sizes[0] / np.sqrt(ratio_tensor[1:])))
     # Divide by 2 to get half height and half width
     anchor_manipulations = np.tile(
         np.stack((-w, -h, w, h)).T, (in_height * in_width, 1)) / 2
 
-    # Each center point will have boxes_per_pixel number of anchor boxes, so
-    # generate grid of all anchor box centers with boxes_per_pixel repeats
+    # Each center point will have `boxes_per_pixel` number of anchor boxes, so
+    # generate a grid of all anchor box centers with `boxes_per_pixel` repeats
     out_grid = d2l.stack([shift_x, shift_y, shift_x, shift_y],
                          axis=1).repeat(boxes_per_pixel, axis=0)
-
     output = out_grid + anchor_manipulations
     return np.expand_dims(output, axis=0)
 
@@ -1559,39 +1561,43 @@ def _make_list(obj, default_values=None):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def box_iou(boxes1, boxes2):
-    """Compute IOU between two sets of boxes of shape (N,4) and (M,4)."""
-    # Compute box areas
+    """Compute pairwise IoU across two lists of anchor or bounding boxes."""
     box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) *
                               (boxes[:, 3] - boxes[:, 1]))
-    area1 = box_area(boxes1)
-    area2 = box_area(boxes2)
-    lt = np.maximum(boxes1[:, None, :2], boxes2[:, :2])  # [N,M,2]
-    rb = np.minimum(boxes1[:, None, 2:], boxes2[:, 2:])  # [N,M,2]
-    wh = (rb - lt).clip(min=0)  # [N,M,2]
-    inter = wh[:, :, 0] * wh[:, :, 1]  # [N,M]
-    unioun = area1[:, None] + area2 - inter
-    return inter / unioun
+    # Shape of `boxes1`, `boxes2`, `areas1`, `areas2`: (no. of boxes1, 4),
+    # (no. of boxes2, 4), (no. of boxes1,), (no. of boxes2,)
+    areas1 = box_area(boxes1)
+    areas2 = box_area(boxes2)
+    # Shape of `inter_upperlefts`, `inter_lowerrights`, `inters`: (no. of
+    # boxes1, no. of boxes2, 2)
+    inter_upperlefts = np.maximum(boxes1[:, None, :2], boxes2[:, :2])
+    inter_lowerrights = np.minimum(boxes1[:, None, 2:], boxes2[:, 2:])
+    inters = (inter_lowerrights - inter_upperlefts).clip(min=0)
+    # Shape of `inter_areas` and `union_areas`: (no. of boxes1, no. of boxes2)
+    inter_areas = inters[:, :, 0] * inters[:, :, 1]
+    union_areas = areas1[:, None] + areas2 - inter_areas
+    return inter_areas / union_areas
 
 
 # Defined in file: ./chapter_computer-vision/anchor.md
-def match_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5):
-    """Assign ground-truth bounding boxes to anchor boxes similar to them."""
+def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5):
+    """Assign closest ground-truth bounding boxes to anchor boxes."""
     num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0]
-    # Element `x_ij` in the `i^th` row and `j^th` column is the IoU
-    # of the anchor box `anc_i` to the ground-truth bounding box `box_j`
+    # Element x_ij in the i-th row and j-th column is the IoU of the anchor
+    # box i and the ground-truth bounding box j
     jaccard = box_iou(anchors, ground_truth)
-    # Initialize the tensor to hold assigned ground truth bbox for each anchor
+    # Initialize the tensor to hold the assigned ground-truth bounding box for
+    # each anchor
     anchors_bbox_map = np.full((num_anchors,), -1, dtype=np.int32, ctx=device)
-    # Assign ground truth bounding box according to the threshold
+    # Assign ground-truth bounding boxes according to the threshold
     max_ious, indices = np.max(jaccard, axis=1), np.argmax(jaccard, axis=1)
     anc_i = np.nonzero(max_ious >= 0.5)[0]
     box_j = indices[max_ious >= 0.5]
     anchors_bbox_map[anc_i] = box_j
-    # Find the largest iou for each bbox
     col_discard = np.full((num_anchors,), -1)
     row_discard = np.full((num_gt_boxes,), -1)
     for _ in range(num_gt_boxes):
-        max_idx = np.argmax(jaccard)
+        max_idx = np.argmax(jaccard)  # Find the largest IoU
         box_idx = (max_idx % num_gt_boxes).astype('int32')
         anc_idx = (max_idx / num_gt_boxes).astype('int32')
         anchors_bbox_map[anc_idx] = box_idx
@@ -1602,6 +1608,7 @@ def match_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def offset_boxes(anchors, assigned_bb, eps=1e-6):
+    """Transform for anchor box offsets."""
     c_anc = d2l.box_corner_to_center(anchors)
     c_assigned_bb = d2l.box_corner_to_center(assigned_bb)
     offset_xy = 10 * (c_assigned_bb[:, :2] - c_anc[:, :2]) / c_anc[:, 2:]
@@ -1612,26 +1619,29 @@ def offset_boxes(anchors, assigned_bb, eps=1e-6):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def multibox_target(anchors, labels):
+    """Label anchor boxes using ground-truth bounding boxes."""
     batch_size, anchors = labels.shape[0], anchors.squeeze(0)
     batch_offset, batch_mask, batch_class_labels = [], [], []
     device, num_anchors = anchors.ctx, anchors.shape[0]
     for i in range(batch_size):
         label = labels[i, :, :]
-        anchors_bbox_map = match_anchor_to_bbox(label[:, 1:], anchors, device)
+        anchors_bbox_map = assign_anchor_to_bbox(label[:, 1:], anchors,
+                                                 device)
         bbox_mask = np.tile((np.expand_dims(
             (anchors_bbox_map >= 0), axis=-1)), (1, 4)).astype('int32')
-        # Initialize class_labels and assigned bbox coordinates with zeros
+        # Initialize class labels and assigned bounding box coordinates with
+        # zeros
         class_labels = d2l.zeros(num_anchors, dtype=np.int32, ctx=device)
         assigned_bb = d2l.zeros((num_anchors, 4), dtype=np.float32,
                                 ctx=device)
-        # Assign class labels to the anchor boxes using matched gt bbox labels
-        # If no gt bbox is assigned to an anchor box, then let the
-        # class_labels and assigned_bb remain zero, i.e the background class
+        # Label classes of anchor boxes using their assigned ground-truth
+        # bounding boxes. If an anchor box is not assigned any, we label its
+        # class as background (the value remains zero)
         indices_true = np.nonzero(anchors_bbox_map >= 0)[0]
         bb_idx = anchors_bbox_map[indices_true]
         class_labels[indices_true] = label[bb_idx, 0].astype('int32') + 1
         assigned_bb[indices_true] = label[bb_idx, 1:]
-        # offset transformations
+        # Offset transformation
         offset = offset_boxes(anchors, assigned_bb) * bbox_mask
         batch_offset.append(offset.reshape(-1))
         batch_mask.append(bbox_mask.reshape(-1))
@@ -1644,19 +1654,20 @@ def multibox_target(anchors, labels):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def offset_inverse(anchors, offset_preds):
-    c_anc = d2l.box_corner_to_center(anchors)
-    c_pred_bb_xy = (offset_preds[:, :2] * c_anc[:, 2:] / 10) + c_anc[:, :2]
-    c_pred_bb_wh = d2l.exp(offset_preds[:, 2:] / 5) * c_anc[:, 2:]
-    c_pred_bb = d2l.concat((c_pred_bb_xy, c_pred_bb_wh), axis=1)
-    predicted_bb = d2l.box_center_to_corner(c_pred_bb)
-    return predicted_bb
+    """Predict bounding boxes based on anchor boxes with predicted offsets."""
+    anc = d2l.box_corner_to_center(anchors)
+    pred_bbox_xy = (offset_preds[:, :2] * anc[:, 2:] / 10) + anc[:, :2]
+    pred_bbox_wh = d2l.exp(offset_preds[:, 2:] / 5) * anc[:, 2:]
+    pred_bbox = d2l.concat((pred_bbox_xy, pred_bbox_wh), axis=1)
+    predicted_bbox = d2l.box_center_to_corner(pred_bbox)
+    return predicted_bbox
 
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def nms(boxes, scores, iou_threshold):
-    # sorting scores by the descending order and return their indices
+    """Sort confidence scores of predicted bounding boxes."""
     B = scores.argsort()[::-1]
-    keep = []  # boxes indices that will be kept
+    keep = []  # Indices of predicted bounding boxes that will be kept
     while B.size > 0:
         i = B[0]
         keep.append(i)
@@ -1668,8 +1679,10 @@ def nms(boxes, scores, iou_threshold):
     return np.array(keep, dtype=np.int32, ctx=boxes.ctx)
 
 
+# Defined in file: ./chapter_computer-vision/anchor.md
 def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,
-                       pos_threshold=0.00999999978):
+                       pos_threshold=0.009999999):
+    """Predict bounding boxes using non-maximum suppression."""
     device, batch_size = cls_probs.ctx, cls_probs.shape[0]
     anchors = np.squeeze(anchors, axis=0)
     num_classes, num_anchors = cls_probs.shape[1], cls_probs.shape[2]
@@ -1679,7 +1692,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,
         conf, class_id = np.max(cls_prob[1:], 0), np.argmax(cls_prob[1:], 0)
         predicted_bb = offset_inverse(anchors, offset_pred)
         keep = nms(predicted_bb, conf, nms_threshold)
-        # Find all non_keep indices and set the class_id to background
+        # Find all non-`keep` indices and set the class to background
         all_idx = np.arange(num_anchors, dtype=np.int32, ctx=device)
         combined = d2l.concat((keep, all_idx))
         unique, counts = np.unique(combined, return_counts=True)
@@ -1688,7 +1701,8 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,
         class_id[non_keep] = -1
         class_id = class_id[all_id_sorted].astype('float32')
         conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted]
-        # threshold to be a positive prediction
+        # Here `pos_threshold` is a threshold for positive (non-background)
+        # predictions
         below_min_idx = (conf < pos_threshold)
         class_id[below_min_idx] = -1
         conf[below_min_idx] = 1 - conf[below_min_idx]
@@ -1868,7 +1882,7 @@ def load_data_voc(batch_size, crop_size):
 
 # Defined in file: ./chapter_computer-vision/kaggle-cifar10.md
 def read_csv_labels(fname):
-    """Read fname to return a name to label dictionary."""
+    """Read `fname` to return a filename to label dictionary."""
     with open(fname, 'r') as f:
         # Skip the file header line (column name)
         lines = f.readlines()[1:]
@@ -1884,7 +1898,8 @@ def copyfile(filename, target_dir):
 
 
 def reorg_train_valid(data_dir, labels, valid_ratio):
-    # The number of examples of the class with the least examples in the
+    """Split the validation set out of the original training set."""
+    # The number of examples of the class that has the fewest examples in the
     # training dataset
     n = collections.Counter(labels.values()).most_common()[-1][1]
     # The number of examples per class for the validation set
@@ -1893,18 +1908,15 @@ def reorg_train_valid(data_dir, labels, valid_ratio):
     for train_file in os.listdir(os.path.join(data_dir, 'train')):
         label = labels[train_file.split('.')[0]]
         fname = os.path.join(data_dir, 'train', train_file)
-        # Copy to train_valid_test/train_valid with a subfolder per class
         copyfile(
             fname,
             os.path.join(data_dir, 'train_valid_test', 'train_valid', label))
         if label not in label_count or label_count[label] < n_valid_per_label:
-            # Copy to train_valid_test/valid
             copyfile(
                 fname,
                 os.path.join(data_dir, 'train_valid_test', 'valid', label))
             label_count[label] = label_count.get(label, 0) + 1
         else:
-            # Copy to train_valid_test/train
             copyfile(
                 fname,
                 os.path.join(data_dir, 'train_valid_test', 'train', label))
@@ -1913,6 +1925,7 @@ def reorg_train_valid(data_dir, labels, valid_ratio):
 
 # Defined in file: ./chapter_computer-vision/kaggle-cifar10.md
 def reorg_test(data_dir):
+    """Organize the testing set for data loading during prediction."""
     for test_file in os.listdir(os.path.join(data_dir, 'test')):
         copyfile(
             os.path.join(data_dir, 'test', test_file),
diff --git a/d2l/tensorflow.py b/d2l/tensorflow.py
--- a/d2l/tensorflow.py
+++ b/d2l/tensorflow.py
@@ -1008,7 +1008,7 @@ def __exit__(self, *args):
 
 # Defined in file: ./chapter_computer-vision/bounding-box.md
 def box_corner_to_center(boxes):
-    """Convert from (upper_left, bottom_right) to (center, width, height)"""
+    """Convert from (upper-left, lower-right) to (center, width, height)."""
     x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
     cx = (x1 + x2) / 2
     cy = (y1 + y2) / 2
@@ -1019,7 +1019,7 @@ def box_corner_to_center(boxes):
 
 
 def box_center_to_corner(boxes):
-    """Convert from (center, width, height) to (upper_left, bottom_right)"""
+    """Convert from (center, width, height) to (upper-left, lower-right)."""
     cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
     x1 = cx - 0.5 * w
     y1 = cy - 0.5 * h
@@ -1032,8 +1032,8 @@ def box_center_to_corner(boxes):
 # Defined in file: ./chapter_computer-vision/bounding-box.md
 def bbox_to_rect(bbox, color):
     """Convert bounding box to matplotlib format."""
-    # Convert the bounding box (top-left x, top-left y, bottom-right x,
-    # bottom-right y) format to matplotlib format: ((upper-left x,
+    # Convert the bounding box (upper-left x, upper-left y, lower-right x,
+    # lower-right y) format to the matplotlib format: ((upper-left x,
     # upper-left y), width, height)
     return d2l.plt.Rectangle(xy=(bbox[0], bbox[1]), width=bbox[2] - bbox[0],
                              height=bbox[3] - bbox[1], fill=False,
diff --git a/d2l/torch.py b/d2l/torch.py
--- a/d2l/torch.py
+++ b/d2l/torch.py
@@ -1518,8 +1518,9 @@ def resnet_block(in_channels, out_channels, num_residuals,
 
 # Defined in file: ./chapter_computer-vision/image-augmentation.md
 def train_batch_ch13(net, X, y, loss, trainer, devices):
+    """Train for a minibatch with mutiple GPUs (defined in Chapter 13)."""
     if isinstance(X, list):
-        # Required for BERT Fine-tuning (to be covered later)
+        # Required for BERT fine-tuning (to be covered later)
         X = [x.to(devices[0]) for x in X]
     else:
         X = X.to(devices[0])
@@ -1538,12 +1539,14 @@ def train_batch_ch13(net, X, y, loss, trainer, devices):
 # Defined in file: ./chapter_computer-vision/image-augmentation.md
 def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,
                devices=d2l.try_all_gpus()):
+    """Train a model with mutiple GPUs (defined in Chapter 13)."""
     timer, num_batches = d2l.Timer(), len(train_iter)
     animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0, 1],
                             legend=['train loss', 'train acc', 'test acc'])
     net = nn.DataParallel(net, device_ids=devices).to(devices[0])
     for epoch in range(num_epochs):
-        # Store training_loss, training_accuracy, num_examples, num_features
+        # Sum of training loss, sum of training accuracy, no. of examples,
+        # no. of predictions
         metric = d2l.Accumulator(4)
         for i, (features, labels) in enumerate(train_iter):
             timer.start()
@@ -1570,7 +1573,7 @@ def train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,
 
 # Defined in file: ./chapter_computer-vision/bounding-box.md
 def box_corner_to_center(boxes):
-    """Convert from (upper_left, bottom_right) to (center, width, height)"""
+    """Convert from (upper-left, lower-right) to (center, width, height)."""
     x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
     cx = (x1 + x2) / 2
     cy = (y1 + y2) / 2
@@ -1581,7 +1584,7 @@ def box_corner_to_center(boxes):
 
 
 def box_center_to_corner(boxes):
-    """Convert from (center, width, height) to (upper_left, bottom_right)"""
+    """Convert from (center, width, height) to (upper-left, lower-right)."""
     cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
     x1 = cx - 0.5 * w
     y1 = cy - 0.5 * h
@@ -1594,8 +1597,8 @@ def box_center_to_corner(boxes):
 # Defined in file: ./chapter_computer-vision/bounding-box.md
 def bbox_to_rect(bbox, color):
     """Convert bounding box to matplotlib format."""
-    # Convert the bounding box (top-left x, top-left y, bottom-right x,
-    # bottom-right y) format to matplotlib format: ((upper-left x,
+    # Convert the bounding box (upper-left x, upper-left y, lower-right x,
+    # lower-right y) format to the matplotlib format: ((upper-left x,
     # upper-left y), width, height)
     return d2l.plt.Rectangle(xy=(bbox[0], bbox[1]), width=bbox[2] - bbox[0],
                              height=bbox[3] - bbox[1], fill=False,
@@ -1604,13 +1607,14 @@ def bbox_to_rect(bbox, color):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def multibox_prior(data, sizes, ratios):
+    """Generate anchor boxes with different shapes centered on each pixel."""
     in_height, in_width = data.shape[-2:]
     device, num_sizes, num_ratios = data.device, len(sizes), len(ratios)
     boxes_per_pixel = (num_sizes + num_ratios - 1)
     size_tensor = d2l.tensor(sizes, device=device)
     ratio_tensor = d2l.tensor(ratios, device=device)
-    # Offsets are required to move the anchor to center of a pixel
-    # Since pixel (height=1, width=1), we choose to offset our centers by 0.5
+    # Offsets are required to move the anchor to the center of a pixel. Since
+    # a pixel has height=1 and width=1, we choose to offset our centers by 0.5
     offset_h, offset_w = 0.5, 0.5
     steps_h = 1.0 / in_height  # Scaled steps in y axis
     steps_w = 1.0 / in_width  # Scaled steps in x axis
@@ -1621,23 +1625,21 @@ def multibox_prior(data, sizes, ratios):
     shift_y, shift_x = torch.meshgrid(center_h, center_w)
     shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1)
 
-    # Generate boxes_per_pixel number of heights and widths which are later
+    # Generate `boxes_per_pixel` number of heights and widths that are later
     # used to create anchor box corner coordinates (xmin, xmax, ymin, ymax)
-    # cat (various sizes, first ratio) and (first size, various ratios)
     w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]),
                    sizes[0] * torch.sqrt(ratio_tensor[1:])))\
-                   * in_height / in_width  # handle rectangular inputs
+                   * in_height / in_width  # Handle rectangular inputs
     h = torch.cat((size_tensor / torch.sqrt(ratio_tensor[0]),
                    sizes[0] / torch.sqrt(ratio_tensor[1:])))
     # Divide by 2 to get half height and half width
     anchor_manipulations = torch.stack(
         (-w, -h, w, h)).T.repeat(in_height * in_width, 1) / 2
 
-    # Each center point will have boxes_per_pixel number of anchor boxes, so
-    # generate grid of all anchor box centers with boxes_per_pixel repeats
+    # Each center point will have `boxes_per_pixel` number of anchor boxes, so
+    # generate a grid of all anchor box centers with `boxes_per_pixel` repeats
     out_grid = torch.stack([shift_x, shift_y, shift_x, shift_y],
                            dim=1).repeat_interleave(boxes_per_pixel, dim=0)
-
     output = out_grid + anchor_manipulations
     return output.unsqueeze(0)
 
@@ -1667,40 +1669,44 @@ def _make_list(obj, default_values=None):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def box_iou(boxes1, boxes2):
-    """Compute IOU between two sets of boxes of shape (N,4) and (M,4)."""
-    # Compute box areas
+    """Compute pairwise IoU across two lists of anchor or bounding boxes."""
     box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) *
                               (boxes[:, 3] - boxes[:, 1]))
-    area1 = box_area(boxes1)
-    area2 = box_area(boxes2)
-    lt = torch.max(boxes1[:, None, :2], boxes2[:, :2])  # [N,M,2]
-    rb = torch.min(boxes1[:, None, 2:], boxes2[:, 2:])  # [N,M,2]
-    wh = (rb - lt).clamp(min=0)  # [N,M,2]
-    inter = wh[:, :, 0] * wh[:, :, 1]  # [N,M]
-    unioun = area1[:, None] + area2 - inter
-    return inter / unioun
+    # Shape of `boxes1`, `boxes2`, `areas1`, `areas2`: (no. of boxes1, 4),
+    # (no. of boxes2, 4), (no. of boxes1,), (no. of boxes2,)
+    areas1 = box_area(boxes1)
+    areas2 = box_area(boxes2)
+    # Shape of `inter_upperlefts`, `inter_lowerrights`, `inters`: (no. of
+    # boxes1, no. of boxes2, 2)
+    inter_upperlefts = torch.max(boxes1[:, None, :2], boxes2[:, :2])
+    inter_lowerrights = torch.min(boxes1[:, None, 2:], boxes2[:, 2:])
+    inters = (inter_lowerrights - inter_upperlefts).clamp(min=0)
+    # Shape of `inter_areas` and `union_areas`: (no. of boxes1, no. of boxes2)
+    inter_areas = inters[:, :, 0] * inters[:, :, 1]
+    union_areas = areas1[:, None] + areas2 - inter_areas
+    return inter_areas / union_areas
 
 
 # Defined in file: ./chapter_computer-vision/anchor.md
-def match_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5):
-    """Assign ground-truth bounding boxes to anchor boxes similar to them."""
+def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5):
+    """Assign closest ground-truth bounding boxes to anchor boxes."""
     num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0]
-    # Element `x_ij` in the `i^th` row and `j^th` column is the IoU
-    # of the anchor box `anc_i` to the ground-truth bounding box `box_j`
+    # Element x_ij in the i-th row and j-th column is the IoU of the anchor
+    # box i and the ground-truth bounding box j
     jaccard = box_iou(anchors, ground_truth)
-    # Initialize the tensor to hold assigned ground truth bbox for each anchor
+    # Initialize the tensor to hold the assigned ground-truth bounding box for
+    # each anchor
     anchors_bbox_map = torch.full((num_anchors,), -1, dtype=torch.long,
                                   device=device)
-    # Assign ground truth bounding box according to the threshold
+    # Assign ground-truth bounding boxes according to the threshold
     max_ious, indices = torch.max(jaccard, dim=1)
     anc_i = torch.nonzero(max_ious >= 0.5).reshape(-1)
     box_j = indices[max_ious >= 0.5]
     anchors_bbox_map[anc_i] = box_j
-    # Find the largest iou for each bbox
     col_discard = torch.full((num_anchors,), -1)
     row_discard = torch.full((num_gt_boxes,), -1)
     for _ in range(num_gt_boxes):
-        max_idx = torch.argmax(jaccard)
+        max_idx = torch.argmax(jaccard)  # Find the largest IoU
         box_idx = (max_idx % num_gt_boxes).long()
         anc_idx = (max_idx / num_gt_boxes).long()
         anchors_bbox_map[anc_idx] = box_idx
@@ -1711,6 +1717,7 @@ def match_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def offset_boxes(anchors, assigned_bb, eps=1e-6):
+    """Transform for anchor box offsets."""
     c_anc = d2l.box_corner_to_center(anchors)
     c_assigned_bb = d2l.box_corner_to_center(assigned_bb)
     offset_xy = 10 * (c_assigned_bb[:, :2] - c_anc[:, :2]) / c_anc[:, 2:]
@@ -1721,27 +1728,30 @@ def offset_boxes(anchors, assigned_bb, eps=1e-6):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def multibox_target(anchors, labels):
+    """Label anchor boxes using ground-truth bounding boxes."""
     batch_size, anchors = labels.shape[0], anchors.squeeze(0)
     batch_offset, batch_mask, batch_class_labels = [], [], []
     device, num_anchors = anchors.device, anchors.shape[0]
     for i in range(batch_size):
         label = labels[i, :, :]
-        anchors_bbox_map = match_anchor_to_bbox(label[:, 1:], anchors, device)
+        anchors_bbox_map = assign_anchor_to_bbox(label[:, 1:], anchors,
+                                                 device)
         bbox_mask = ((anchors_bbox_map >= 0).float().unsqueeze(-1)).repeat(
             1, 4)
-        # Initialize class_labels and assigned bbox coordinates with zeros
+        # Initialize class labels and assigned bounding box coordinates with
+        # zeros
         class_labels = torch.zeros(num_anchors, dtype=torch.long,
                                    device=device)
         assigned_bb = torch.zeros((num_anchors, 4), dtype=torch.float32,
                                   device=device)
-        # Assign class labels to the anchor boxes using matched gt bbox labels
-        # If no gt bbox is assigned to an anchor box, then let the
-        # class_labels and assigned_bb remain zero, i.e the background class
+        # Label classes of anchor boxes using their assigned ground-truth
+        # bounding boxes. If an anchor box is not assigned any, we label its
+        # class as background (the value remains zero)
         indices_true = torch.nonzero(anchors_bbox_map >= 0)
         bb_idx = anchors_bbox_map[indices_true]
         class_labels[indices_true] = label[bb_idx, 0].long() + 1
         assigned_bb[indices_true] = label[bb_idx, 1:]
-        # offset transformations
+        # Offset transformation
         offset = offset_boxes(anchors, assigned_bb) * bbox_mask
         batch_offset.append(offset.reshape(-1))
         batch_mask.append(bbox_mask.reshape(-1))
@@ -1754,19 +1764,20 @@ def multibox_target(anchors, labels):
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def offset_inverse(anchors, offset_preds):
-    c_anc = d2l.box_corner_to_center(anchors)
-    c_pred_bb_xy = (offset_preds[:, :2] * c_anc[:, 2:] / 10) + c_anc[:, :2]
-    c_pred_bb_wh = d2l.exp(offset_preds[:, 2:] / 5) * c_anc[:, 2:]
-    c_pred_bb = d2l.concat((c_pred_bb_xy, c_pred_bb_wh), axis=1)
-    predicted_bb = d2l.box_center_to_corner(c_pred_bb)
-    return predicted_bb
+    """Predict bounding boxes based on anchor boxes with predicted offsets."""
+    anc = d2l.box_corner_to_center(anchors)
+    pred_bbox_xy = (offset_preds[:, :2] * anc[:, 2:] / 10) + anc[:, :2]
+    pred_bbox_wh = d2l.exp(offset_preds[:, 2:] / 5) * anc[:, 2:]
+    pred_bbox = d2l.concat((pred_bbox_xy, pred_bbox_wh), axis=1)
+    predicted_bbox = d2l.box_center_to_corner(pred_bbox)
+    return predicted_bbox
 
 
 # Defined in file: ./chapter_computer-vision/anchor.md
 def nms(boxes, scores, iou_threshold):
-    # sorting scores by the descending order and return their indices
+    """Sort confidence scores of predicted bounding boxes."""
     B = torch.argsort(scores, dim=-1, descending=True)
-    keep = []  # boxes indices that will be kept
+    keep = []  # Indices of predicted bounding boxes that will be kept
     while B.numel() > 0:
         i = B[0]
         keep.append(i)
@@ -1778,8 +1789,10 @@ def nms(boxes, scores, iou_threshold):
     return d2l.tensor(keep, device=boxes.device)
 
 
+# Defined in file: ./chapter_computer-vision/anchor.md
 def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,
-                       pos_threshold=0.00999999978):
+                       pos_threshold=0.009999999):
+    """Predict bounding boxes using non-maximum suppression."""
     device, batch_size = cls_probs.device, cls_probs.shape[0]
     anchors = anchors.squeeze(0)
     num_classes, num_anchors = cls_probs.shape[1], cls_probs.shape[2]
@@ -1789,7 +1802,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,
         conf, class_id = torch.max(cls_prob[1:], 0)
         predicted_bb = offset_inverse(anchors, offset_pred)
         keep = nms(predicted_bb, conf, nms_threshold)
-        # Find all non_keep indices and set the class_id to background
+        # Find all non-`keep` indices and set the class to background
         all_idx = torch.arange(num_anchors, dtype=torch.long, device=device)
         combined = torch.cat((keep, all_idx))
         uniques, counts = combined.unique(return_counts=True)
@@ -1798,7 +1811,8 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5,
         class_id[non_keep] = -1
         class_id = class_id[all_id_sorted]
         conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted]
-        # threshold to be a positive prediction
+        # Here `pos_threshold` is a threshold for positive (non-background)
+        # predictions
         below_min_idx = (conf < pos_threshold)
         class_id[below_min_idx] = -1
         conf[below_min_idx] = 1 - conf[below_min_idx]
@@ -1980,7 +1994,7 @@ def load_data_voc(batch_size, crop_size):
 
 # Defined in file: ./chapter_computer-vision/kaggle-cifar10.md
 def read_csv_labels(fname):
-    """Read fname to return a name to label dictionary."""
+    """Read `fname` to return a filename to label dictionary."""
     with open(fname, 'r') as f:
         # Skip the file header line (column name)
         lines = f.readlines()[1:]
@@ -1996,7 +2010,8 @@ def copyfile(filename, target_dir):
 
 
 def reorg_train_valid(data_dir, labels, valid_ratio):
-    # The number of examples of the class with the least examples in the
+    """Split the validation set out of the original training set."""
+    # The number of examples of the class that has the fewest examples in the
     # training dataset
     n = collections.Counter(labels.values()).most_common()[-1][1]
     # The number of examples per class for the validation set
@@ -2005,18 +2020,15 @@ def reorg_train_valid(data_dir, labels, valid_ratio):
     for train_file in os.listdir(os.path.join(data_dir, 'train')):
         label = labels[train_file.split('.')[0]]
         fname = os.path.join(data_dir, 'train', train_file)
-        # Copy to train_valid_test/train_valid with a subfolder per class
         copyfile(
             fname,
             os.path.join(data_dir, 'train_valid_test', 'train_valid', label))
         if label not in label_count or label_count[label] < n_valid_per_label:
-            # Copy to train_valid_test/valid
             copyfile(
                 fname,
                 os.path.join(data_dir, 'train_valid_test', 'valid', label))
             label_count[label] = label_count.get(label, 0) + 1
         else:
-            # Copy to train_valid_test/train
             copyfile(
                 fname,
                 os.path.join(data_dir, 'train_valid_test', 'train', label))
@@ -2025,6 +2037,7 @@ def reorg_train_valid(data_dir, labels, valid_ratio):
 
 # Defined in file: ./chapter_computer-vision/kaggle-cifar10.md
 def reorg_test(data_dir):
+    """Organize the testing set for data loading during prediction."""
     for test_file in os.listdir(os.path.join(data_dir, 'test')):
         copyfile(
             os.path.join(data_dir, 'test', test_file),


Test patch: