出力ノード数を手計算せずにConvolutionの出力をLinearに入力する（chainer 1.15）

　chainerの[Convolution2D](http://docs.chainer.org/en/stable/reference/links.html#chainer.links.Convolution2D)や[Linear](http://docs.chainer.org/en/stable/reference/links.html#chainer.links.Linear)は、入力チャンネル数`in_channels`やノード数`in_size`にNoneを指定すると、最初のデータをforwardした時に自動で合わせてくれる機能があります。ドキュメントの引数の説明で言及されていますが、意外と気づきにくいです（chainer version 1.15.0.1）。

> in_size (int) – Dimension of input vectors. If None, parameter initialization will be deferred until the first forward data pass at which time the size will be determined.

　とりあえず試します。

```python
import chainer
import numpy


class LinearLinear(chainer.Chain):
    def __init__(self):
        super().__init__(
            linear1=chainer.functions.Linear(in_size=10, out_size=20),
            linear2=chainer.functions.Linear(in_size=None, out_size=30),
        )

    def __call__(self, x):
        h = self.linear1(x)
        h = self.linear2(h)
        return h


linearlinear = LinearLinear()

x = numpy.random.rand(64, 10).astype(numpy.float32)
h = linearlinear(x)
print(h.data.shape)  # (64, 30)
```

```python
import chainer
import numpy


class ConvConv(chainer.Chain):
    def __init__(self):
        super().__init__(
            conv1=chainer.functions.Convolution2D(in_channels=4, out_channels=4, ksize=3),
            conv2=chainer.functions.Convolution2D(in_channels=None, out_channels=3, ksize=3),
        )

    def __call__(self, x):
        h = self.conv1(x)
        h = self.conv2(h)
        return h


convconv = ConvConv()

x = numpy.random.rand(64, 4, 32, 32).astype(numpy.float32)
h = convconv(x)
print(h.data.shape)  # (64, 3, 28, 28)
```

　おかげさまでコードの変更を行いやすくなりました。ありがたや。

　ところで、先ほどこのようなやり取りを目にしました。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ConvolutionからLinerにつなぐとき、ノード数の数みんな手で計算してるのかな。結構めんどくさい・・・</p>&mdash; ピクシィ (@icoxfog417) <a href="https://twitter.com/icoxfog417/status/776323353728561152">2016年9月15日</a></blockquote><blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ChainerはLinearの入力サイズをNoneにすることで手計算を省けるように最近なりました <a href="https://t.co/vLhHy7QO78">https://t.co/vLhHy7QO78</a></p>&mdash; ビーム | Seiya Tokui (@beam2d) <a href="https://twitter.com/beam2d/status/776414674560557056">2016年9月15日</a></blockquote>

　特に多層なConvolutionの出力ノード数を計算するのはかなり面倒なため、Conv->Linearで入力ノード数を省けるのは待望の機能です。さっそく試してみました。

```python
import chainer
import numpy


class ConvLinear(chainer.Chain):
    def __init__(self):
        super().__init__(
            conv1=chainer.functions.Convolution2D(in_channels=4, out_channels=4, ksize=3),
            linear2=chainer.functions.Linear(in_size=None, out_size=30),
        )

    def __call__(self, x):
        h = self.conv1(x)
        h = self.linear2(h)
        return h


convlinear = ConvLinear()

x = numpy.random.rand(64, 4, 32, 32).astype(numpy.float32)
h = convlinear(x)
print(h.data.shape)

# Invalid operation is performed in: LinearFunction (Forward)
# Expect: prod(in_types[0].shape[1:]) == in_types[1].shape[1]
# Actual: 3600 != 4
```

　んー・・・？よくわからないけど、エラーが出ました。

　原因はさておいて、やっぱり手計算は必要なのかと悲観することはなく、**`reshape`を挟んであげると自動で推定してくれます**。

```python
class ConvLinear(chainer.Chain):
    def __init__(self):
        super().__init__(
            conv1=chainer.functions.Convolution2D(in_channels=4, out_channels=4, ksize=3),
            linear2=chainer.functions.Linear(in_size=None, out_size=30),
        )

    def __call__(self, x):
        h = self.conv1(x)
        h = chainer.functions.reshape(h, (h.data.shape[0], -1, 1))
        h = self.linear2(h)
        return h


convlinear = ConvLinear()

x = numpy.random.rand(64, 4, 32, 32).astype(numpy.float32)
h = convlinear(x)
print(h.data.shape)  # (64, 30)
```

　ありがたや。

　しかし先ほどのエラーは何かしらの手違いかもしれません。これはコミットチャンスです。そしておそらく[この辺り](https://github.com/pfnet/chainer/blob/1dd699fa0a7da737b03c089bee4834e8a1ed3cc5/chainer/links/connection/linear.py#L85)です。しかしこれは[このコミット](https://github.com/pfnet/chainer/pull/1551/commits/a86166c3036d8eacb1c6fd913c687fa9ae78c4cf)で修正されています。うーん、残念。（？）

　[このコミットを含むPR](https://github.com/pfnet/chainer/pull/1551)は現在masterにマージ済みで、最新のものを利用すれば先程のエラーは発生しません。もうしばらく待って、chainer1.16をインストールすればこの苦しい手計算から解放されるはずです。やったぜ。

### 追記
　さきほど紹介させて頂いたツイートの方から。

<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hiho_karuta">@hiho_karuta</a> qiita記事ありがとうございます、僕の確認不足でそこの対応はおっしゃる通り次リリースでした。火曜日まで待っていただけると……！(書いていただいた通りreshape挟めばいまでも使えますね)</p>&mdash; ビーム | Seiya Tokui (@beam2d) <a href="https://twitter.com/beam2d/status/776534639733112832">2016年9月15日</a></blockquote>

　やったぜ。