Help us understand the problem. What is going on with this article?

Pytorchによる1D-CNN,2D-CNNスクラッチ実装まとめ

はじめに

TensorFlowからPytorchに移行して半年ほど経ったので基礎的なところをまとめておきます。
今回は以下の3つに焦点を当てたいと思います。

  1. 事前学習モデルの利用
  2. 1DCNNの実装
  3. 2DCNNの実装

*1DCNNは簡単に説明して、2DCNNに焦点を当てたいと思います。
*理論的なことについては書いておりません。実装中心の記事です。

1.事前学習モデルの利用

現在使える事前学習済モデルは以下のモデルです。

  • AlexNet
  • VGG
  • ResNet
  • SqueezeNet
  • DenseNet
  • Inception v3
  • GoogLeNet
  • ShuffleNet v2
  • MobileNet v2
  • ResNeXt
  • Wide ResNet
  • MNasNet

詳しくはこちら

ImageNetでの学習済モデルを使用する際は以下のように使います。

import torchvision
model = torchvision.models.alexnet(pretrained=True)
  • pretrained=TrueにしないとImageNetでの学習済の重みはロードされません。
  • デフォがpretrained=Falseになっているので注意してください。

  • モデルの構造を確認したければprint(model)で確認出来ます。以下が実行結果です。

AlexNet(
  (features): Sequential(
    (0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))
    (1): ReLU(inplace=True)
    (2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
    (3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (4): ReLU(inplace=True)
    (5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (7): ReLU(inplace=True)
    (8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (9): ReLU(inplace=True)
    (10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU(inplace=True)
    (12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (avgpool): AdaptiveAvgPool2d(output_size=(6, 6))
  (classifier): Sequential(
    (0): Dropout(p=0.5, inplace=False)
    (1): Linear(in_features=9216, out_features=4096, bias=True)
    (2): ReLU(inplace=True)
    (3): Dropout(p=0.5, inplace=False)
    (4): Linear(in_features=4096, out_features=4096, bias=True)
    (5): ReLU(inplace=True)
    (6): Linear(in_features=4096, out_features=1000, bias=True)
  )
)

もし自分のデータでクラス分類したいときは、以下のように変更します。
2クラス分類を例にします。

model.classifier[6].out_features = 2

もう一度print(model)を実行すると変わっているのが確認出来ます。

AlexNet(
  (features): Sequential(
    (0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))
    (1): ReLU(inplace=True)
    (2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
    (3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (4): ReLU(inplace=True)
    (5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (7): ReLU(inplace=True)
    (8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (9): ReLU(inplace=True)
    (10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): ReLU(inplace=True)
    (12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (avgpool): AdaptiveAvgPool2d(output_size=(6, 6))
  (classifier): Sequential(
    (0): Dropout(p=0.5, inplace=False)
    (1): Linear(in_features=9216, out_features=4096, bias=True)
    (2): ReLU(inplace=True)
    (3): Dropout(p=0.5, inplace=False)
    (4): Linear(in_features=4096, out_features=4096, bias=True)
    (5): ReLU(inplace=True)
    (6): Linear(in_features=4096, out_features=2, bias=True)
  )
)

2. 1DCNNの実装

それでは本題に入ります。
今回は1DのCNNをスクラッチで実装します。
簡単な例です。

import torch
import torch.nn as nn


class Net1D(nn.Module):
    def __init__(self):
        super(SimpleNet,self).__init__()

        self.conv1 = nn.Conv1d(1, 8,kernel_size=3, stride=1)
        self.bn1 = nn.BatchNorm1d(8)
        self.relu = nn.ReLU()
        self.maxpool = nn.MaxPool1d(kernel_size=3, stride=2)

        self.conv2 = nn.Conv1d(8, 16,kernel_size=3, stride=1)
        self.bn2 = nn.BatchNorm1d(16)
        self.conv3 = nn.Conv1d(16,64,kernel_size=3, stride=1)
        self.gap = nn.AdaptiveAvgPool1d(1)
        self.fc = nn.Linear(64,2)


    def forward(self,x):

        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.conv2(x)
        x = self.bn2(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.conv3(x)
        x = self.gap(x)
        x = x.view(x.size(0),-1)
        x = self.fc(x)

        return x


もし、このモデルがうまく出来ているか確認するなら以下のように試してください。

model = SimpleNet()
in_data = torch.randn(8,1,50)
out_data = model(data)
print(out_size.size()) #torch.Size([8, 2])
  • torch.randn()で適当な入力データを用意します。←このやり方便利です!2Dでも応用出来ます!
  • 入力はtorch.randn(バッチサイズ、チャネル数、1次元配列の大きさ)です。 出力のサイズがtorch.Size([8, 2])となっているのは、torch.Size(バッチサイズ、最後の出力)と言うことです。
  • 分類タスクをやりたければこの後に、softmaxを通してあげれば良いです。

  • また、torchsummaryと言う特徴マップの大きさを確認できる便利なライブラリがあるのでそちらも使ってみてください。以前に記事を書いたのでリンクを貼っておきます。

===============================================================

Pytorchでモデル構築するとき、torchsummaryがマジ使える件について

===============================================================

【nn.Conv1d】
nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
parameters 概要
in_channels 入力のチャネル数。
out_channels 畳み込みをした後のチャネル数。フィルター数。
kernel_size カーネルの大きさ。
stride カーネルをどのくらい移動させるか。
padding   パディングの大きさ。1を指定すると両端に挿入するので2だけ大きくなる。デフォは0。
dilation フィルターの間の空間を変更。atrous convなどで利用。
groups デフォは1。数を大きくすると計算コスト削減。
bias biasを含むかどうか。デフォはTrue
 padding_mode   パディングのモード。デフォは0。
【nn.BatchNorm1d】
nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

num_featuresはひつつ前のレイヤーのout_channelsの値と同じ数を入れれば大丈夫です。

3. 2DのCNN

簡単なCNNのサンプルを書きました。
今回フィルター数やカーネルサイズは適当に決めています。
自作でネットワークを作成する場合は、考慮しながら値を決めてください。

import torch
import torch.nn as nn

class Net2D(nn.Module):
    def __init__(self):
        super(Net,self).__init__()

        self.conv1 = nn.Conv2d(3,16,kernel_size=3,stride=2)
        self.bn1 = nn.BatchNorm2d(16)
        self.relu = nn.ReLU()
        self.maxpool = nn.MaxPool2d(2)
        self.conv2 = nn.Conv2d(16,32,kernel_size=3,stride=2)
        self.bn2 = nn.BatchNorm2d(32)
        self.conv3 = nn.Conv2d(32,64,kernel_size=3,stride=2)
        self.gap = nn.AdaptiveAvgPool2d(1)
        self.fc1 = nn.Linear(64,32)
        self.fc2 = nn.Linear(32,2)


    def forward(self,x):

        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.conv2(x)
        x = self.bn2(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.conv3(x)
        x = self.gap(x)
        x = x.view(x.size(0),-1)
        x = self.fc1(x)
        x = self.fc2(x)
        return x

  • 自作モデルを作る際は、nn.Moduleを継承する必要があります。
  • 基本的には、initで使用する層を定義します。
    パラメタがあるものをinitに、パラメタがないものはforwardに定義するような記事をよく見ますが、print(model)の際にreluなどが表示されないので、私はパラメタがないreluのようなものでも今回のようにinitに定義します。

  • forwardのほうで、モデルの構造を決めます。

【nn.Conv2d】
nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
parameters 概要
in_channels 入力のチャネル数。RGBの画像では3になる。
out_channels 畳み込みをした後のチャネル数。フィルター数。
kernel_size カーネルの大きさ。
stride カーネルをどのくらい移動させるか。
padding   パディングの大きさ。1を指定すると両端に挿入するので2だけ大きくなる。デフォは0。
dilation フィルターの間の空間を変更。atrous convなどで利用。
groups デフォは1。数を大きくすると計算コスト削減。
bias biasを含むかどうか。デフォはTrue
 padding_mode   パディングのモード。デフォは0。
【nn.BatchNorm2d】
nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  • バッチ正規化は、バッチ内の要素ごとに平均と標準偏差を求めます。
    畳み込みのときは、バッチ内のチャネルの対して正規化します。全結合層のときはユニットになります。

  • 他にも、Layer NormInstance NormGroup Normなどがあるので気になるのがある方は検索してみてください。

【nn.ReLU】
nn.ReLU(inplace=False)
  • (x) = max(0,x)
  • ReLUは活性化関数です。他には、ReLU6RReLUSELUCELUSigmoidなどあります。
【nn.MaxPool2d】
nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

特徴を強調されるためにプーリング層を使います。

  • 大きく分けて2パターンの方法があるので以下で確認します。

①poolのサイズが正方形のとき

m = nn.MaxPool2d(3, stride=2)  #(pool of square window of size=3, stride=2)

②poolの大きさをカスタマイズしたいとき

m = nn.MaxPool2d((3, 2), stride=(2, 1)) #(pool of non-square window)
【nn.AdaptiveMaxPool2d】
nn.AdaptiveMaxPool2d(output_size, return_indices=False)

よく、Global Max Poolingと呼ばれるものです。
各チャネルを値一つにしてくれるので、全結合層につなぐ前によく使われます。
output_sizeに1チャネルのアウトプットサイズを入れます。
output_size=1が使うことが多いと思います。

【nn.Linear】
nn.Linear(in_features, out_features, bias=True)

in_features, out_featuresを指定して使います。
全結合層を実装したときは、こちらを使います。

終わりに

Pytorchに移行して半年ぐらい経ちますが、非常に使いやすいです。
今回の記事が少しでも参考になればと思います。

参考文献

tatsuya11bbs
I'm a second year master student. Machine Learning / Python / Scala / Image Processing / Keras / Tensorflow / Pytorch / .
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした