52213


Kerasで次のようなLSTMオートエンコーダーが実装されています。

[code]

    import numpy as np
    from keras.layers import Input, GRU
    from keras.models import Model
    
    input_feat = Input(shape=(30, 2000))
    l = GRU( 100, return_sequences=True, activation="tanh",    recurrent_activation="hard_sigmoid")(input_feat)
    l = GRU(2000, return_sequences=True, activation="tanh", recurrent_activation="hard_sigmoid")(l)
    model = Model(input_feat, l)
    model.compile(optimizer="RMSprop", loss="mean_squared_error")
    
    feat = np.load("feat.npy")
    model.fit(feat, feat[:, ::-1, :], epochs=200, batch_size=250)
    
[/code]

ここで、featは3次元配列で、feat.shape = (269, 30, 2000) です。  
このコードをChainerに書き直すために次のようなコードを書きましたが、学習結果をみるに、どこかが間違っているようです。どこに間違いがあるかわかるでしょうか。

[code]

    import numpy as np
    from chainer import Chain, Variable, optimizers
    import chainer.functions as F
    import chainer.links as L
    
    class GRUAutoEncoder(Chain):
        def __init__(self):
            super().__init__()
            with self.init_scope():
                self.encode = L.GRU(2000, 100)
                self.decode = L.GRU(100, 2000)
    
        def __call__(self, h, mode):
            if mode == "encode":
                h = F.tanh(self.encode(h))
                return h 
    
            if mode == "decode":
                h = F.tanh(self.decode(h))
                return h
    
        def reset(self):
            self.encode.reset_state()
            self.decode.reset_state()
    
    def main():
        feat = np.load("feat.npy") #(269, 30, 2000)
    
        gru_autoencoder = GRUAutoEncoder()
        optimizer = optimizers.RMSprop(lr=0.01).setup(gru_autoencoder)
    
        N = len(feat)
        batch_size = 250
        for epoch in range(200):
            index = np.random.randint(0, N-batch_size+1)
            input_feat = feat[index:index+batch_size] #(250, 30, 2000)
            #Encoding
            input_vector = np.zeros((30, batch_size, 2000), dtype="float32")
            h = []
            for i in range(frame_rate):
                input_vector[i] = input_feat[:, i, :] #(250, 1, 2000)
                tmp = Variable(input_vector[i])
                h.append(gru_autoencoder(tmp, "encode")) #(250, 100)
    
            #Decoding
            output_vector = []
            for i in range(frame_rate):
                tmp = h[i]
                output_vector.append(gru_autoencoder(tmp, "decode"))
    
            x = input_vector[0]
            t = output_vector[0]
            for i in range(len(output_vector)):
                x = F.concat((x,input_vector[i]), axis=1)
                t = F.concat((t,output_vector[i]), axis=1)
    
            loss = F.mean_squared_error(x, t)
            gru_autoencoder.cleargrads()
            loss.backward()
            optimizer.update()
            gru_autoencoder.reset()
    
    if __name__ == "__main__":
        main()
    
[/code]

Kerasのコードではfit(feat, feat[:, ::-1, :])となっているので、x =
input_vector[0]の直前でoutput_vector.reverse()とするべきかと思って、それで学習をさせてみましたが、やはり結果がおかしいです。

ご助言いただけると幸いです。  
よろしくお願いします。

**追記1**  
Motoki
Satoさん、ご回答ありがとうございます。いろいろと調べましたが、上のKerasのコードをNStepGRUを使って書き直す場合は、n_layers=1にするという理解であっているでしょうか。つまり、ネットワークは

[code]

    class GRUAutoEncoder(Chain):
    def __init__(self):
        super().__init__()
        with self.init_scope():
            self.encode = L.NStepGRU(1, 2000, 100, 0)
            self.decode = L.NStepGRU(1, 100, 2000, 0)
    
    def __call__(self, h, encode=False):
        h = F.tanh(self.encode(h))
        if encode:
            return h
        h = F.tanh(self.decode(h))
        return h
    
    def reset(self):
        self.encode.reset_state()
        self.decode.reset_state()
    
[/code]

として、学習すればよいということで間違いないでしょうか。  
よろしくお願いします。

**追記2**

追記１のネットワークを下のように学習させようとしたのですが、TypeError: forward() missing 1 required
positional argument: 'xs'というエラーが出ますね。

[code]

    def main():
        feat = np.load("feat.npy")
        gru_autoencoder = GRUAutoEncoder()
        optimizer = optimizers.RMSprop(lr=0.01).setup(gru_autoencoder)
        N = len(feat)
        batch_size = 250
        for epoch in range(200):
            index = np.random.randint(0, N-batch_size+1)
            batch = feat[index:index+batch_size] #(250, 30, 2000)
            t = gru_autoencoder(batch)
            loss = F.mean_squared_error(batch, t)
            gru_autoencoder.cleargrads()
            loss.backward()
            optimizer.update()
            gru_autoencoder.reset()
    
[/code]


CC BY-SA 4.0
2019-01-21T05:36:42.543
52162
2019-01-22T12:09:49.727
2019-01-22T05:16:47.687
31087
31087
question
python 深層学習 chainer keras
KerasのコードをChainerに書き換えたい(LSTM Autoencoderの実装)


L.GRUでも書けないことは無いですが、  
L.NStepGRUを使うとより簡単に書けます。  
`L.NStepGRU`の方がcuDNNを使う場合高速に計算ができます。

使い方は NStepLSTMと同じなので以下のURLが参考になると思います。  
もし不明な点などあれば聞いて頂ければと思います。

参考:  
<https://qiita.com/aonotas/items/8e38693fb517e4e90535>


CC BY-SA 4.0
2019-01-22T02:38:46.610
52189
2019-01-22T02:38:46.610
31871
52162
answer


結論から言えば，以下のコードで動くと思います．(データローダの部分は適当に書き換えてください)

個別に疑問点があれば，コメントで聞いてください．

[code]

    ### dataset.py
    from chainer.dataset import DatasetMixin
    
    import numpy as np
    
    
    class MyDataset(DatasetMixin):
        N_SAMPLES = 269
        N_TIMESERIES = 30
        N_DIMS = 2000
    
        def __init__(self):
            super().__init__()
            self.data = np.random.randn(self.N_SAMPLES, self.N_TIMESERIES, self.N_DIMS) \
                .astype(np.float32)
    
        def __len__(self):
            return self.N_SAMPLES
    
        def get_example(self, i):
            return self.data[i, :, :]
    
    
    ### model.py
    import chainer
    from chainer import links as L
    from chainer import functions as F
    from chainer.link import Chain
    
    
    class MyModel(Chain):
        N_IN_CHANNEL = 2000
        N_HIDDEN_CHANNEL = 100
        N_OUT_CHANNEL = 2000
    
        def __init__(self):
            super().__init__()
            self.encoder = L.NStepGRU(n_layers=1, in_size=self.N_IN_CHANNEL, out_size=self.N_HIDDEN_CHANNEL, dropout=0)
            self.decoder = L.NStepGRU(n_layers=1, in_size=self.N_HIDDEN_CHANNEL, out_size=self.N_OUT_CHANNEL, dropout=0)
    
        def to_gpu(self, device=None):
            self.encoder.to_gpu(device)
            self.decoder.to_gpu(device)
    
        def to_cpu(self):
            self.encoder.to_cpu()
            self.decoder.to_cpu()
    
        @staticmethod
        def flip_list(source_list):
            return [F.flip(source, axis=1) for source in source_list]
    
        def __call__(self, source_list):
            """
            .. note:
                This implementation makes use of "auto-encoding"
                by avoiding redundant copy in GPU device.
                In the typical implementation, this function should receive
                both of ``source_list`` and ``target_list``.
            """
            target_list = self.flip_list(source_list)
            _, h_list = self.encoder(hx=None, xs=source_list)
            _, predicted_list = self.decoder(hx=None, xs=h_list)
            diff_list = [F.mean_squared_error(target, predicted).reshape((1,)) for target, predicted in zip(target_list, predicted_list)]
            loss = F.sum(F.concat(diff_list, axis=0))
    
            chainer.report({'loss': loss}, self)
    
            return loss
    
    
    ### converter.py (referring examples/seq2seq/seq2seq.py)
    from chainer.dataset import to_device
    
    
    def convert(batch, device):
        """
        .. note:
            batch must be list(batch_size) of array
        """
        if device is None:
            return batch
        else:
            return [to_device(device, x) for x in batch]
    
    
    ### train.py
    from chainer.iterators import SerialIterator
    from chainer.optimizers import Adam
    from chainer.training.updaters import StandardUpdater
    from chainer.training.trainer import Trainer
    
    dataset = MyDataset()
    
    BATCH_SIZE = 32
    iterator = SerialIterator(dataset, BATCH_SIZE)
    
    model = MyModel()
    optimizer = Adam()
    optimizer.setup(model)
    
    updater = StandardUpdater(iterator, optimizer, convert, device=0)
    trainer = Trainer(updater, (100, 'iteration'))
    
    from chainer.training.extensions import snapshot_object
    trainer.extend(snapshot_object(model, "model_iter_{.updater.iteration}"), trigger=(10, 'iteration'))
    
    from chainer.training.extensions import LogReport, PrintReport, ProgressBar
    trainer.extend(LogReport(['epoch', 'iteration', 'main/loss'], (1, 'iteration')))
    trainer.extend(PrintReport(['epoch', 'iteration', 'main/loss']), trigger=(1, 'iteration'))
    trainer.extend(ProgressBar(update_interval=1))
    
    trainer.run()
    
[/code]

話はそれますが，このモデル，いろいろと問題があると思います．  
例えば，timestep==0の予測について考えると，inputの0ステップ目だけをみて，inputの最終ステップを当てなきゃいけない，ということで，全然自己符号化器になっていません．

普通に考えれば，encoderの最終タイムステップの出力を，decoderの入力にするべきだと思われます．  
逆に，このモデルで当てられてしまう，ということは，全ステップでinputの0ステップ目を返せばそれなりの精度になる，ということを示唆しており，深層学習が不要である，という結論が導き出されてしまうような気がします．

そもそも，元のコードからして，出力層に`tanh`がかかってるので，入力が[-1,
1]に正規化されていることが前提になっています．この性質は一般に成り立つものではないので，ちょっと微妙な気がします．


CC BY-SA 4.0
2019-01-22T12:09:49.727
52213
2019-01-22T12:09:49.727
29620
52162
answer