ハイパースペクトルイメージを扱うPythonモジュールspectralの紹介
はじめに
画像処理のコンペで拡張子が.hdrの画像を扱う機会があり、調べても日本語の記事見つからなかったので記録しておきます。
spectralのインストール
conda install -c conda-forge spectral
画像の読み込み
# 画像の読み込みとメタデータの表示 In [1]: import spectral In [2]: img = spectral.open_image('hyper_spectral_image.HDR') In [3]: print(img) In [4]: img Out[4]: Data Source: './hyper_spectral_image' # Rows: 601 # Samples: 2384 # Bands: 50 Interleave: BSQ Quantization: 16 bits Data format: uint16 In [5]: img.__class__ Out[5]: spectral.io.bsqfile.BsqFile # 画像をNumpy Arrayに変換 In [6]: arr = img.load() In [7]: arr.shape Out[7]: (601, 2384, 50)
一旦Numpyに変えてしまえば他の画像と同様に扱えますね。
PytorchによるAutoEncoder Familyの実装
はじめに
AutoEncoderとはニューラルネットワークによる次元削減の手法で、日本語では自己符号化器と呼ばれています。DeepLearningの手法の中では使い道がよくわからないこともあり比較的不人気な気がします。(個人的には教師なしで抽象的な情報を取り出すのはとても面白そうだと思います。)
今回は自分の勉強のためにPyTorchでAutoEncoderを実装します。コードはこちら。
AutoEncoder
まずは3層MLPの一番簡単な奴。ネットワーク定義はこんな感じ。
class AutoEncoder(nn.Module): def __init__(self, input_size, embedding_dimension): # initialization of class super(AutoEncoder, self).__init__() # define the network self.layer1 = nn.Linear(input_size, 32) self.layer2 = nn.Linear(32, input_size) self.encoder = nn.Sequential( self.layer1, nn.ReLU() ) self.decoder = nn.Sequential( self.layer2, nn.Sigmoid() ) def forward(self, x): encoded = self.encoder(x) decoded = self.decoder(encoded) return encoded, decoded def initialization(self, mean=0, std=0.01): nn.init.normal(self.layer1.weight, mean, std) nn.init.normal(self.layer2.weight, mean, std)
シンプルですね。中間層の前にReLUを入れてしまっているのがちょっと微妙かも。パラメータの初期化は平均0標準偏差0.01の正規分布にしてます。実験では中間層のノードの数は10にしています。
再構成の結果はこんな感じ。
Deep AutoEncoder
今度は層の数を増やし、10層のNNを学習してみます。画像を見るとうまく学習できていないことがわかります。DeepなNNの学習の難しさが見て取れますね。
Stacked AutoEncoder
単に層を深くするだけでは学習がうまく行かないので工夫を加えます。層ごとに事前学習をしていくのですが、方法が2つあります。ひとつは入力に近い層から1層ずつ学習する方法。ふたつ目は両端の層から中心の層に向かって学習していく方法です。(下図参照)今回はJ. Xieらの論文*1を参考にして後者の方法で学習をします。初期化方法や学習率も論文に合わせています.論文を読み直したら普通に入力に近い層から1層ずつ学習していました...すみません.
実装は、クラスの中で層を積み上げる数に応じてアトリビュートを作成しています(ここが動的にできると良い?)また、学習時にはoptimizerを定義するときに学習するレイヤーを指定しています。かなり見苦しいコードになってしまったので早く修正したいところではあります。
結果はこちら。層が深くなっても学習できています。見栄えも良くなった気がします。
Convolutional AutoEncoder
CNNによるAutoEncoderです。画像の潜在表現を取り出す方法としてはこちらのほうが理にかなっている気はします。下の画像はCAEのネットワーク構造になります。*2
モデル定義はこのようになりました。kerasのようにpadding='same'とできないので少しブサイクな実装になっています。
class Convolutional_AutoEncoder(nn.Module): def __init__(self, embedding_dimension): super(Convolutional_AutoEncoder, self).__init__() # define the network # encoder self.conv1 = nn.Sequential(nn.ZeroPad2d((1,2,1,2)), nn.Conv2d(1, 32, kernel_size=5, stride=2), nn.ReLU()) self.conv2 = nn.Sequential(nn.ZeroPad2d((1,2,1,2)), nn.Conv2d(32, 64, kernel_size=5, stride=2), nn.ReLU()) self.conv3 = nn.Sequential(nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=0), nn.ReLU()) self.fc1 = nn.Conv2d(128, 10, kernel_size=3) # decoder self.fc2 = nn.Sequential(nn.ConvTranspose2d(10, 128, kernel_size=3), nn.ReLU()) self.conv3d = nn.Sequential(nn.ConvTranspose2d(128, 64, kernel_size=3, stride=2, padding=0), nn.ReLU()) self.conv2d = nn.Sequential(nn.ConvTranspose2d(64, 32, kernel_size=5, stride=2), nn.ReLU()) self.conv1d = nn.ConvTranspose2d(32, 1, kernel_size=5, stride=2) def forward(self, x): encoded = self.fc1(self.conv3(self.conv2(self.conv1(x)))) decoded = self.fc2(encoded) decoded = self.conv3d(decoded) decoded = self.conv2d(decoded)[:,:,1:-2,1:-2] decoded = self.conv1d(decoded)[:,:,1:-2,1:-2] decoded = nn.Sigmoid()(decoded) return encoded, decoded
再構成の結果はこちらです。きれいですね。
まとめ
今回はAE, SAE, CAEを実装しました。また新しく実装したものがあれば追記したいと思います。
*1:Xie, Junyuan et al. “Unsupervised Deep Embedding for Clustering Analysis.” ICML (2016). https://arxiv.org/abs/1511.06335
*2:Xifeng Guo, Xinwang Liu, En Zhu, Jianping Yin. Deep Clustering with Convolutional Autoencoders. ICONIP 2017. https://link.springer.com/chapter/10.1007/978-3-319-70096-0_39