code: def fuse_first_affine(net, params, removed_tensors):
      net = copy.deepcopy(net)
      params = copy.deepcopy(params)
  
      for (i, current), (j, next_) in pairwise(enumerate(net.op)):
          if next_.input[0] != current.output[0]:
              continue
  
          if (
              current.type not in ("Conv", "ConvTranspose")
              or next_.type != "AffineChannel"
          ):
              continue
          if (
              current.output[0] != next_.output[0]
              and len(blob_uses(net, current.output[0])) != 1
          ):
              # Can't fuse if more than one user unless AffineChannel is inplace
              continue
  
          # else, can fuse
          conv = current
          affine = next_
          fused_conv = copy.deepcopy(conv)
          fused_conv.output[0] = affine.output[0]
          conv_weight = params[conv.input[1]]
          conv_has_bias = len(conv.input) > 2
          conv_bias = params[conv.input[2]] if conv_has_bias else 0
  
          A = params[affine.input[1]]
          B = params[affine.input[2]]
  
          # Thus, can just have the affine transform
          # X * A + B
          # where
          # A = bn_scale * 1.0 / (sqrt(running_var + eps))
          # B =  (bias - running_mean * (1.0 / sqrt(running_var + eps))
          # * bn_scale)
  
          # This identify should hold if we have correctly fused
          # np.testing.assert_array_equal(
          #     params[conv.output[0]] * A + B,
          #     params[bn.output[0]])
  
          # Now, we have that the computation made is the following:
          # ((X `conv` W) + b) * A + B
          # Then, we can simply fuse this as follows:
          # (X `conv` (W * A)) + b * A + B
          # which is simply
          # (X `conv` Q) + C
          # where
  
          # Q = W * A
          # C = b * A + B
  
          # For ConvTranspose, from the view of convolutions as a
          # Toepeliz multiplication, we have W_ = W^T, so the weights
          # are laid out as (R, S, K, K) (vs (S, R, K, K) for a Conv),
          # so the weights broadcast slightly differently. Remember, our
          # BN scale 'B' is of size (S,)
  
          A_ = (
              A.reshape(-1, 1, 1, 1)
              if conv.type == "Conv"
              else A.reshape(1, -1, 1, 1)
          )
  
          C = conv_bias * A + B
          Q = conv_weight * A_
  
          assert params[conv.input[1]].shape == Q.shape
  
          params[conv.input[1]] = Q
          if conv_has_bias:
              assert params[conv.input[2]].shape == C.shape
              params[conv.input[2]] = C
          else:
              # make af_bias to be bias of the conv layer
              fused_conv.input.append(affine.input[2])
              params[affine.input[2]] = B
  
          new_ops = net.op[:i] + [fused_conv] + net.op[j + 1 :]
          del net.op[:]
          if conv_has_bias:
              del params[affine.input[2]]
              removed_tensors.append(affine.input[2])
          removed_tensors.append(affine.input[1])
          del params[affine.input[1]]
          net.op.extend(new_ops)
          break
      return net, params, removed_tensors
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  from __future__ import unicode_literals
  from functools import wraps
  import copy
  import numpy
  from caffe2.python import core, workspace
  from caffe2.proto import caffe2_pb2
label_desc: Use image node_id 11 for calling a global function with example usage: fuse_first_affine(net, params, removed_tensors) and returns: net,  params,  removed_tensors