Make automatic insertions of dnn_conv() use the optimal cuDNN code path

To address #2268, this PR changes `dnn_conv()` and the accompanying graph optimizers to not replace all `GpuConv` ops with `GpuConvDnn`, but use `GpuConvDnnGradI` and `GpuConvDnnGradW` when appropriate.

This is work in progress; for now it just uses `GpuConvDnnGradI` for full convolutions and doesn't touch anything else. Getting the correct shape for the convolution descriptor (and keeping it symbolic) was a bit tricky (I had to use `tensor.stack` and then take the first row of it because `tensor.concatenate` was not happy with scalar inputs). I'll try to finish this sometime tomorrow.