function conv2d(
  inputs: Tensor, 
  filters: number, 
  kernel_size: number, 
  graph: Graph, 
  strides: number = 1, 
  padding = "valid", 
  data_format = "channels_last", 
  activation?, 
  kernel_initializer: Initializer = new VarianceScalingInitializer(),
  bias_initializer: Initializer = new ZerosInitializer(),
  name: string = "") {

  // get the channels parameter from the input
  const channel_axis = data_format == "channels_last" ? inputs.shape[2] : inputs.shape[0];

  // shape of the kernel to create the filters
  const depthwise_kernel_shape = [kernel_size, kernel_size, channel_axis, filters];

  // Create a new variable for weights of the filters and apply the initializer
  var weights = graph.variable(name + "w",
    kernel_initializer.initialize(depthwise_kernel_shape, kernel_size * kernel_size * channel_axis * filters,
      filters));

  // create a new variable for bias and apply the initializer
  var bias = graph.variable(name + "b", bias_initializer.initialize([filters], kernel_size, filters))

  // call the actual conv2d function
  const layer = graph.conv2d(inputs, weights, bias, kernel_size, filters, strides, padding == "valid" || padding == "VALID" ? 0 : undefined);

  // return the tensor. Apply the activation if defined
  return activation == undefined ? layer : activation(layer, graph);
}