とある京大生の作業ログと日々の雑記

コンピュータサイエンスについて学んだことを可視化したり日々の雑記をまとめてます。

ニューラルネットワークでよくやりがちなデータの形の不一致

こんばんは!コミさん(@komi3__ )です!



最近は授業の課題とかで少し忙しかったので2週間ぶりくらいの更新になりますね。




前回はParrot SecurityというLinuxディストリビューションを使って2つほどクラッキングツールを試した感じでした。




まああんな感じでセキュリティ系の勉強とか結構がんばってたんですけど、割と忙しくて(授業とかバイトとかフランス語の勉強とか)思うように時間がとれなくて、とりあえず今回は手持ちのネタでなんか記事書いとくか〜〜〜くらいのモチベです(笑)





まあ本題に入りましょう。




おとといくらいにPyTorchのバージョン0.4がリリースされました!






更新内容としては


・メモリの省エネ化


Windowsへの対応


・CDF(累積分布関数)の実装


・ゼロ次元テンソル


テンソルとVariableの統合(!)


って感じですね〜



1つ目の省エネ化は置いといて、2つ目のWindowsのやつを見て、正直に言えば「え、Windowsだと使えてなかったの!?」という感想でした(自分のマシンはMacで、バイト先のマシンはWindows on Linuxなので...)




3つ目はベイズ系の機械学習系の対応ということでしょうか?




4つ目は、スカラーデータとの混同を避けるため、ネットワークでの処理の際はend-to-endでのデータ処理を行えるようにする配慮ですかね。




で、5つ目がテンソルとVariableの統合(全てテンソルになった)で、個人的にはこれが1番びっくりしました!




やっぱVariableはどんな実装でも使うものなので、これが統合されるのはネットワークがさらにスッキリ書けるのでちょっと嬉しいですね。





アーキテクチャが複雑で巨大なネットワークを実装する際、実装に慣れていないとVariableとテンソルがごったになって内部でのデータの処理がごちゃごちゃになり、最終的にエラーを大連発するということになるので、これはいい改変だと思います。





全体的な所感として、PyTorchは今回のアプデを通してKeras並みに取り扱いの優しい構成になり、さらにTensorFlow並みに細かいパラメータチューニングが可能ということで、現在たくさんあるモジュールの中で最も番人受けするツールになったと言えます。





それにPyTorchのチュートリアルは笑うほど解説が充実していて、画像分類やNLP、強化学習、転移学習など様々な用途のチュートリアルが用意してあるので、ホントにPyTorchは親切設計だなぁと感心しますね!(TensorFlowのチュートリアルは少し難しいかも....)





ただ今回のアプデで気になったのが、前々からPFNの人が「2017年から学習の進行状況をGUIで可視化するツールの開発がトレンド」とは言ってて、PyTorchもその流れに乗るのかなぁって思ってたんですけど、どうやらそちらの方のサポートはなかったですね(もしかしてもうすでにあるのかも?)





まあこんな感じでPyTorchが初心者へ親切設計になったということで、今回は掲題の通りネットワーク内でのデータの形を改めて復習しようという記事です!



データの形の不一致とは

クラスでネットワークを組んでいて、さあできあがって動かしてみようということで走らせてみたらデータの形が合ってなかった、ということは割とみんな経験があるのではないですかね?




当然、ぼくも素人なのでしょっちゅうやります(笑)




ということで、今回はメモ書き程度にそこらへんのポイントを改めて押さえておきます。



CNN

まずはCNN。



機械学習を始めるにあたって最初にやるタスクってみんなCNNによる画像分類ですよね(簡単だからかな?)



CNNの入出力の計算式を確認しておきましょう。



 L_{out} = \left\lfloor\frac{L_{in} + 2 * \text{padding} - \text{dilation}
          * (\text{kernel_size} - 1) - 1}{\text{stride}} + 1\right\rfloor



PyTorchでのCNNは使うとき

import torch
import torch.nn as nn

m = nn.Conv2d(input_channel, output_channel, kernel_size)


というような感じになりますよね。



このように最初のネットワークを設定するとき、最初の2つの引数が入力と出力の数(チャンネルの数)になります。




例えば、MNISTで手書き数字の画像分類をするとき、1枚の画像を入れて、8x8のフィルターを用いて、0から9までの10個の数字を分類するとき

m = nn.Conv2d(1, 10, 8)

というような設定をすることになります。



この層を設定して、実際に層に入力するときは以下のようになります。

m = nn.Conv2d(input_channel, output_channel, kernel_size)

input_image = torch.FloatTensor(batch_size, input_channel, image_size_height, image_size_width)


こうすると出力としては

output = m(input_image)

print(output.size())

-> torch.Size([batch_size, output_channel, output_size_height, output_size_width])

となります。


ここで、output_sizeはカーネルサイズやパディングをするかどうかで変わります。




入力のサイズは4次元であることに注意です。



Linear


画像分類でも最後の全結合層として使われる線型層ですね。


これはとても簡単で、

 m = nn.Linear(input_num, output_channel)

x = torch.randn(a, input_num)

y = torch.randn(a, b, c, input_num)

----------------------------------------------

m_x, m_y = m(x), m(y)

print(m_x.size())

-> torch.Size([a,  output_num])

print(m_y.size())

-> torch.Size([a, b, c, output_num])


というように入力のサイズの最後の数字が変化しているのがわかりますよね?



a, b, cは任意です。



それ以外は変わりません。



なので、データの次元がどのようでも線型層を使うことができます。


LSTM


とても複雑なやつです。



隠れ層の部分の意味については以下のサイトなどを見れば理解できると思います。


s0sem0y.hatenablog.com


この人の解説は非常に丁寧なのでLSTMの概要については理解できると思います。



ではデータの入力形式について以下を見ていきましょう。


m = nn.LSTM(input_num, hidden_num, num_layers)

x = torch.randn(a, b, input_num)

h = torch.randn(num_layers, b, hidden_num)

c = torch.randn(num_layers, b, hidden_num)

output, hidden, cell = m(x, (h, c))

-------------------------------------------------

print(output.size())

-> torch.Size([a,  b, hidden_num])

print(hidden.size())

-> torch.Size([num_layers, b, hidden_num])

print(cell.size())

-> torch.Size([num_layers, b, hidden_num])


このようにデータのサイズは変化します。


先ほど挙げた解説記事を見ていればまあ理解はできますが、実際にデータサイズがどういう風に変化するかはもうこのように覚えておいていいかもしれません。


まとめ

以上ではCNNやLinear、LSTMなど有名どころを挙げました。



他にも色々あるのですが、とりあえず今回はこの程度にしておこうと思います。



上にあげたデータの入出力制御ができれば、サイズ関係でのエラーは回避できるようになると思うので、この記事が誰かの役に立てばなぁって感じです。



ではお疲れ様でした!



P.S.


先日"再び"トビタテ留学JAPAN!の書類審査に通りました。


2週間後にまた文部科学省まで面接に行ってきます。


受かったらいいなぁ.....