MPA
MPA

Reputation: 2028

How to convolve signal with 1D kernel in TensorFlow?

I am trying to filter a TensorFlow tensor of shape (N_batch, N_data), where N_batch is the batch size (e.g. 32), and N_data is the size of the (noisy) timeseries array. I have a Gaussian kernel (taken from here), which is one-dimensional. I then want to use tensorflow.nn.conv1d to convolve this kernel with my signal.

I have been trying for most of the morning to get the dimensions of the input signal and the kernel right, but obviously with no success. From what I gathered from the interwebs, the dimensions of both the input signal and the kernel need to be aligned in some finicky way, and I just can't figure out which way that is. The TensorFlow error messages aren't particularly meaningful either (Shape must be rank 4 but is rank 3 for 'conv1d/Conv2D' (op: 'Conv2D') with input shapes: [?,1,1000], [1,81]). Below I've included a little piece of code to reproduce the situation:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

# Based on: https://stackoverflow.com/a/52012658/1510542
# Credits to @zephyrus


def gaussian_kernel(size, mean, std):
    d = tf.distributions.Normal(tf.cast(mean, tf.float32), tf.cast(std, tf.float32))
    vals = d.prob(tf.range(start=-size, limit=size+1, dtype=tf.float32))

    kernel = vals   # Some reshaping is required here

    return kernel / tf.reduce_sum(kernel)


def gaussian_filter(input, sigma):
    size = int(4*sigma + 0.5)

    x = input   # Some reshaping is required here

    kernel = gaussian_kernel(size=size, mean=0.0, std=sigma)
    conv = tf.nn.conv1d(x, kernel, stride=1, padding="SAME")
    return conv


def run_filter():

    tf.reset_default_graph()

    # Define size of data, batch sizes
    N_batch = 32
    N_data = 1000

    noise = 0.2 * (np.random.rand(N_batch, N_data) - 0.5)
    x = np.linspace(0, 2*np.pi, N_data)
    y = np.tile(np.sin(x), N_batch).reshape(N_batch, N_data)
    y_noisy = y + noise

    input = tf.placeholder(tf.float32, shape=[None, N_data])
    smooth_input = gaussian_filter(input, sigma=10)

    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        y_smooth = smooth_input.eval(feed_dict={input: y_noisy})

        plt.plot(y_noisy[0])
        plt.plot(y_smooth[0])
        plt.show()


if __name__ == "__main__":
    run_filter()

Any ideas?

Upvotes: 1

Views: 1302

Answers (1)

xdurch0
xdurch0

Reputation: 10475

You need to add channel dimensions to your input/kernel, since TF convolutions are generally used for multi-channel inputs/outputs. As you are working with simple 1-channel input/output this amounts to just adding some size-1 "dummy" axes.
Since by default convolution expects channels to come last, your placeholder should have shape [None, N_data, 1] and your input be modified like

y_noisy = y + noise
y_noisy = y_noisy[:, :, np.newaxis] 

Similarly, you need to add input and output channel dimensions to your filter:

kernel = gaussian_kernel(size=size, mean=0.0, std=sigma)
kernel = kernel[:, tf.newaxis, tf.newaxis]

That is, the filter is expected to have shape [width, in_channels, out_cannels].

Upvotes: 2

Related Questions