WideResNet (ワイド残差ネットワーク) アルゴリズム分析 - 鳥認識分類 - パドル戦闘

今天详解一下WideResNet算法,WideResNet(宽残差网络)是在ResNet网络的基础上改进的深度卷积神经网络架构,其主要特点是使用宽卷积层,增加了网络的通道数,从而提高了网络的表达能力和性能。WideResNet已经在图像分类、对象检测、语义分割等领域取得了很好的性能表现。
本次实战就是一个经典的分类问题:鸟类分类。
本次项目实战鸟类数据集主要分为4类,分别为bananaquit(蕉林莺)、Black Skimmer (黑燕鸥类)、Black Throated Bushtiti (黑喉树莺)、Cockatoo (凤头鹦鹉或葵花鹦鹉),总计565张。

1. 理論的根拠


1 はじめに

  コンピュータ ビジョンの分野では、GoogLenet、VGG-16、Incepetion などの畳み込みニューラル ネットワーク (CNN) が最も主流の手法となっています。CNN の歴史における画期的な出来事は、より深い CNN モデルをトレーニングしてより高い精度を達成できる ResNet モデルの登場です。ResNet モデルの核心は、フロント層とバック層の間に「短絡接続」(ショートカット、スキップ接続) を確立することで、より深い CNN ネットワークをトレーニングすることです。

  しかし、ディープ ニューラル ネットワークが発展し続けるにつれて、ネットワーク内の層の数も増加しており、分類精度の向上率 1 パーセントあたりのコストは層数のほぼ 2 倍になります。したがって、非常に深い残差ネットワークをトレーニングする際の問題の 1 つは、特徴の使用率が徐々に低下するため、残差ネットワークのトレーニングが非常に遅くなるということです。したがって、これらの問題を解決するために、人々はモデルの幅に注目し始め、一般的に使用されるシンおよびディープ残差ネットワークよりも優れたパフォーマンスを備えたワイド残差ネットワーク (WRN) アーキテクチャが登場しました。

  今回は、ResNet を改良し、モデルの深さを減らして幅を広げる Wide Residual Networks (WRNs) アルゴリズムについて詳しく説明します。WRNs アルゴリズムの具体的なモデル構成は次の図のようになります。


図1 WideResNetネットワークの構成

Wideresnet のオリジナル論文: 「 Wide Residual Networks」

2. デザインコンセプト

  前述したように、モデルの深さを継続的に増加させても精度の点で良好な結果が得られなくなったため、研究者はモデルの幅について考え始め、最終的にモデルの幅を増やしながらモデルの深さを減らすことができることを発見しました。モデルの精度も向上します。そして比較実験の結果、このアーキテクチャを使用した単純な 16 層の深層残差ネットワークであっても、CIFAR、SVHN、COCO における千層の深層ネットワークを含む、以前のすべての深層残差ネットワークよりも精度と効率の点で優れていることが判明しました。新しい最先端の結果を達成し、ImageNet で大幅な改善を達成しました。

  Wide Residual Networks (WRN) アルゴリズムを説明する前に、よく知られている ResNet アルゴリズムについて説明しましょう。ResNet は、最初に提案されたときに ImageNet データセットで優勝したディープ ニューラル ネットワーク モデルです。従来のディープ ニューラル ネットワークとは異なり、ResNet は残差ユニット (残差ユニット) を使用し、ネットワークに残差ユニットを追加することで、ディープ ニューラル ネットワークの学習プロセス中に発生する勾配消失と勾配爆発の問題を解決できます。より深い層ほどより優れたパフォーマンスが実現されます。

  ResNet では、各残差ユニットには 2 つの畳み込み層とスキップ接続 (ショートカット) が含まれており、スキップ接続は入力信号を 1 つの残差ユニットから次の残差ユニットに直接渡し、勾配消失の問題を軽減します。ResNet の特殊な構造により、ディープ ニューラル ネットワークの表現能力が向上するだけでなく、モデルのパラメーター数が大幅に削減されるため、ResNet モデルはオーバーフィットすることなく、より優れたパフォーマンスを達成できます。

2.1 ResNetアルゴリズム

2.1.1 残留(残留構造)モジュール

ResNet が提案される前は、すべてのニューラル ネットワークは畳み込み層とプーリング層で構成されていました。
畳み込み層やプーリング層の層が多いほど、より完全な画像特徴情報が得られ、学習効果が高まると考えられています。しかし、実際の実験では、畳み込み層とプーリング層を重ね合わせると、学習効果がどんどん良くなるだけでなく、次の2つの問題があることが分かりました。

  • 消滅するグラデーションと爆発するグラデーション

    • 勾配が消える: 各層の誤差勾配が 1 未満の場合、ネットワークが深くなるほど、バックプロパゲーション中の勾配は 0 に近づきます。
    • 勾配爆発: 各層の誤差勾配が 1 より大きい場合、ネットワークが深くなるほど、バックプロパゲーション中の勾配が大きくなります。
  • 劣化の問題

    • レイヤーの数が増えると、予測効果はますます悪化します。写真2に示すように


    図2 層数増加による予測効果

勾配消失または勾配爆発の問題を解決するために、 ResNet の論文では、データの前処理とネットワーク内の BN (バッチ正規化) 層の使用を通じて問題を解決することを提案しています。

ディープネットワークにおける劣化問題を解決するために、ニューラルネットワークの一部の層を人為的に次の層のニューロンの接続をスキップさせ、各層を接続して各層間の強い接続を弱めることができます。このようなニューラル ネットワークは残差ネットワーク (ResNet) と呼ばれます。ResNet の論文では、劣化問題を軽減するために残差構造 (残差構造) が提案されています。次の図は残差構造を使用した畳み込みネットワークです。ネットワークが深くなり続けるにつれて、効果は劣化せず、より良くなることがわかります。 . . (点線はトレインエラー、実線はテストエラー。


図 3 残差構造を使用した後の畳み込みネットワーク効果

2.1.2 残差の計算方法

残余構造はショートカット接続方法を使用しており、これもショートカットとして理解できます。特徴行列を一定間隔で加算していきます。F(X) と X は同じ形状でなければならないことに注意してください。いわゆる加算とは、特徴行列の同じ位置にある数値を加算することです。

図4 残差計算方法

「ショートカット接続」とは、その名の通り「角を切る」という意味のショートカットで、一部の関数を参照(X)なしで学習するのではなく、各層の入力を参照(X)し、残差関数を形成することを学習します。この残差関数は最適化が容易で、ネットワーク層の数を大幅に増やすことができます。上図の残差ブ​​ロックでは、次の式に示すように 2 つの層があります。ここで、σ σσは非線形関数ReLUを表します。

次に、ショートカットと 2 番目の ReLU を介して、出力 y が取得されます。

入力および出力の次元を変更する必要がある場合 (チャネル数の変更など)、次の式に示すように、ショートカット中に x に対して線形変換 Ws を実行できます。

2.1.3 ResNet の 2 つの異なる残差

図 5 ResNet の 2 つの異なる残差構造

これら 2 つの構造はそれぞれ ResNet34 (左図) と ResNet50/101/152 (右図) 用であり、全体の構造は一般に「ビルディング ブロック」と呼ばれます。右の図は「ボトルネック設計」とも呼ばれ、パラメータの数を減らすことが目的で、実際には計算コストを考慮して残差ブロックを計算して最適化する、つまり2つの3x3畳み込み層を次の3x3畳み込み層に置き換えます。右に示すように、1x1 + 3x3 + 1x1。新しい構造の中央の 3x3 畳み込み層は、まず次元削減 1x1 畳み込み層で計算を削減し、次に別の 1x1 畳み込み層で計算を復元します。これにより、精度が維持されるだけでなく、計算量も削減されます。最初の 1x1 畳み込みは 256 次元のチャネルを 64 次元に削減し、最後に 1x1 畳み込みによって復元します。全体として使用されるパラメータの数: 1x1x256x64 + 3x3x64x64 + 1x1x64x256 = 69632 (ボトルネックを使用しない場合) 2 つの 3x3x256 Convolution、パラメータ数: 3x3x256x256x2 = 1179648、16.94 倍の差です。
従来の ResNet の場合は 34 層以下のネットワークで使用できますが、Bottleneck Design の ResNet の場合は、計算とパラメータを削減するために、通常 101 などのより深いネットワークで使用されます。

2.1.4 バッチ正規化(バッチ正規化)

画像の前処理のプロセスでは、通常、画像に対してバッチ正規化を実行します。これにより、ネットワークの収束を高速化できます。以下の図に示すように、Conv1 の場合、入力は特定の分布を満たす特徴行列ですが、 Conv2 入力された特徴マップは必ずしも特定の分布則を満たしているわけではありません(ここで特定の分布則を満たすとは、特定の特徴マップのデータが分布則を満たさなければならないという意味ではなく、理論的には全体に対応する特徴マップを指します)トレーニング サンプル セット。データは分布法則を満たす必要があります)。バッチ正規化の目的は、特徴マップが平均 0、分散 1 の分布則を満たすようにすることです。

図6 バッチ正規化処理

2.2 WideResNet (WRN) アルゴリズム

2.2.1 広い残留ブロック

  • 図 (a): ResNet の残差ブロック構造
  • 図 (b): RresNet が提案するより深い層のボトルネック構造
  • 図 (c): 幅の広い残差ブロック構造。出力チャネルの数を増やすことでモデルの幅が広がります。
  • 図 (d): 2 層の畳み込みにドロップアウトが追加される

ワイド残差ブロックとは、残差ブロックにさらに多くの畳み込みカーネルを追加することを指し、それによって特徴チャネルの数が増加します。従来の残差ブロックは 2 つの畳み込み層で構成され、通常、各畳み込み層には少数の畳み込みカーネルしか含まれません。WideResNet では、各残差ブロックには 2 つの畳み込み層が含まれており、2 番目の畳み込み層の畳み込みカーネルの数は非常に多く、これは特徴チャネルの数を数倍増やすことに相当します。
特徴チャネルの数を増やすことで、 WideResNet は画像内の豊富な特徴情報をより適切にキャプチャできるようになり、モデルの精度が向上します。さらに、幅の広い残差ブロックはより優れた勾配フローを備えており、勾配をより浅い畳み込み層に速く渡すことができるため、モデルの収束速度が向上します。

図 7 WideResNet ネットワークの構造

上の図に示すように、 Wide-Resnet の拡張係数 k は Resnet よりも 1 つだけ多いです。元のアーキテクチャは K=1 に相当し、N はグループ内のブロックの数を表します。K を 1 に変更するのが ResNet ネットワーク アーキテクチャです。 。
ネットワークは、最初の畳み込み層 conv1 と、それに続く 3 つの残差ブロック conv2、conv3、および conv4 (それぞれサイズ N) のグループ、その後に平均化プーリングと最終分類層で構成されます。実験では、conv1 のサイズはすべて固定されていますが、導入された拡張係数 k によって conv2 ~ 4 の 3 つのセット内の残りのブロックの幅が調整されます。
元のアーキテクチャと比較して、残差ブロック内のバッチ正規化、アクティブ化、畳み込みの順序が conv-BN-ReLU から BN-ReLU-conv に変更されています。コンボリューション カーネルは 3 × 3 3\times3を使用します3×3 ; 正則化ではドロップアウトが使用され、ResNet で使用される BN はここでは使いにくいです。

2.2.2 ドロップアウト(破棄メソッド)

ネットワークの拡大はパラメータの数の増加につながるため、 WideResNet の研究者は正則化手法を研究してきました。残差ネットワークにはすでに正則化効果を提供するバッチ正規化が備わっていますが、多くのデータ拡張が必要です。データ拡張を回避するために、研究者はドロップアウト正則化手法を使用して過学習を防ぎますが、以前の畳み込みニューラル ネットワークではネットワークのドロップアウトがすべての畳み込み演算の後に配置されますが、実験では、研究者はドロップアウトを 2 つの3 × 3 3\times3に配置しました。3×3 つの畳み込みの間、BN 演算は 2 つの残差ブロックの間に配置されますが、順序が調整され、Conv->BN->ReLU が BN->ReLU->Conv に変更され、より高速なトレーニングとより良い結果が得られます。

ドロップアウトとは、ディープラーニングにおける過学習を抑制するためによく使われる手法で、ニューラルネットワークの学習過程で一部のニューロンをランダムに削除する手法です。トレーニング中、いくつかのニューロンがランダムに選択され、その出力が 0 に設定され、これらのニューロンは外部に信号を送信しません。

下図はDropoutの模式図で、左側が完成したニューラルネットワーク、右側がDropout適用後のネットワーク構造です。Dropout を適用すると、 × \ 回マークされます×ニューロンは、後続の層に信号を渡さないようにネットワークから削除されます。学習プロセス中にどのニューロンが破棄されるかはランダムに決定されるため、モデルは特定のニューロンに過度に依存せず、過学習をある程度抑制できます。

図8のドロップアウト構造

2.2.3 畳み込みサイズの選択

論文では、著者はB ( M ) B(M)と仮定します。B ( M )は残差ブロック構造を表します。ここでMMMは、ブロック内の畳み込み層のカーネル サイズのリストです。たとえば、B ( 3 , 1 ) B(3, 1)B ( 3 , 1 )は3 × 3 3 × 3を持つことを意味します3×31×1 1×11×1畳み込み層の残りのブロック前に説明したボトルネック ブロックを考慮していないため、特徴面の数はブロック全体で常に同じままであることに注意してください。その中で、著者は、基本的な残差アーキテクチャにおける3 × 3 と 3 × 33×3畳み込み層はどの程度重要ですか、また、畳み込み層は計算コストの低い1 × 1 1 × 1で計算できますか?1×1層、または1 × 1 1 × 11×13×3 3×33×代わりに3 つの畳み込み層の組み合わせ、たとえばB ( 1 , 3 ) B(1, 3)B ( 1 , 3 )またはB (1, 3) B (1, 3)B ( 1,3 ) _ _ これにより、ブロックの代表電力が増減する可能性があります。したがって、次の組み合わせを試します (最後の組み合わせ、つまりB( 3 , 1 , 1 ) B(3, 1, 1) にB ( 3 , 1 , 1 )は、効率的なネットワークインネットワーク アーキテクチャに似ています)。

図 9 さまざまな残差ブロック構造

著者は最初にさまざまなブロックタイプで BBを使用しましたB のトレーニング済みネットワークで結果を報告し (報告された結果は CIFAR-10 上にあります)、ブロックB ( 1 , 3 , 1 ) B(1, 3, 1 )に WRN-40-2 を使用します。 B ( 1 3 1 ) B ( 3 , 1 ) B(3,1)B ( 3 1 ) B ( 1 , 3 ) B(1,3)B ( 1 , 3 ) および B(3, 1, 1) B(3, 1, 1)B ( 3 , 1 , 1 ) これらのブロックには 3 × 3 3 × 33×3 回の畳み込み。パラメータの数を同等に保つために、著者らはより少ないレイヤで他のネットワーク、WRN-28-2-B (3, 3) と WRN-22-2B (3, 1, 3) をトレーニングしました。5 5を含む結果を図 10 に示します。 5 回の実行および各トレーニング エポックの時間にわたるテスト精度の中央値ブロック B ( 3 , 3 ) B(3, 3 )B ( 3 , 3 ) が最良であることが証明されており、 B( 3 , 1 ) B(3, 1)B ( 3 , 1 ) および B(3, 1, 3) B(3, 1, 3)B ( 3,1,3 ) は、精度 B (3,3)においてB (3,3)に非常に近いです B ( 3 , 3 ) には、パラメーターとレイヤーが少なくなります。 B ( 3 , 1 , 3 ) B(3, 1, 3)B ( 3 , 1 , 3 ) は他のものよりわずかに高速です。

図 10 k=2 および異なるブロック タイプの残差ネットワークに対する CIFAR-10 のテスト エラー

4. 評価分析

CIFAR-10 および CIFAR-100 での深さと幅が異なる WideResNet モデルの評価結果を図 12 に示します。

図 12 CIFAR-10 および CIFAR-100 の評価結果

適度なデータ拡張(反転/変換)と平均/標準偏差の正規化を使用した、CIFAR-10 および CIFAR-100 でのさまざまなメソッドのテスト誤差。結果は図 13 の 2 列目に示されており、k は拡大係数です。

図 13 CIFAR-10 および CIFAR-100 でのさまざまなメソッドのテスト エラー

ネットワーク パフォーマンスに対するドロップアウトの導入の影響の結果を図 14 に示します。全体として、ドロップアウト自体が効果的な正則化手法であることがわかります。これは拡大の結果をさらに改善するために使用でき、拡大係数を補完するものでもあります。Chunky WRN は、従来の高さの薄い Resnet よりも高い精度を達成できます。

図 14 Dropout の導入がネットワーク パフォーマンスに与える影響

緑色の線は WideResNet 損失誤差曲線を表し、赤色の線は元の ResNet 損失曲線を表します。

以下の図は速度テストの結果を示しており、この結果から、幅広のネットワークは細いネットワークよりも何倍も効率的であることがわかります。

図15 速度テスト結果

2、実戦


1. データの前処理

  • データセットを解凍します
# 注意路径
!unzip /home/aistudio/data/data223822/bird_photos.zip -d /home/aistudio/work

データセットファイルを処理すると追加の ipynb_checkpoints ファイルが存在するため、次のコマンドで削除する必要があります。覚えて!必ず削除してください〜

%cd /home/aistudio/work
!rm -rf .ipynb_checkpoints
  • データセットを分割する
import os
import random

train_ratio = 0.7
test_ratio = 1-train_ratio

rootdata = "/home/aistudio/work/"

train_list, test_list = [],[]
data_list = []
class_flag = -1
for a,b,c in os.walk(rootdata):
    for i in range(len(c)):
        data_list.append(os.path.join(a,c[i]))

    for i in range(0, int(len(c)*train_ratio)):
        train_data = os.path.join(a, c[i])+' '+str(class_flag)+'\n'
        train_list.append(train_data)


    for i in range(int(len(c)*train_ratio),len(c)):
        test_data = os.path.join(a,c[i])+' '+str(class_flag)+'\n'
        test_list.append(test_data)

    class_flag += 1

random.shuffle(train_list)
random.shuffle(test_list)

with open('/home/aistudio/work/train.txt','w',encoding='UTF-8') as f:
    for train_img in train_list:
        f.write(str(train_img))

with open('/home/aistudio/work/test.txt', 'w', encoding='UTF-8') as f:
    for test_img in test_list:
        f.write(test_img)

2. データの読み込み

  • 次の必要なライブラリをインポートします
import paddle
import paddle.nn.functional as F
import numpy as np
import math
import random
import os
from paddle.io import Dataset  # 导入Datasrt库
import paddle.vision.transforms as transforms

from PIL import Image
  • paddle.io.DataLoader を使用してデータ リーダーを定義する
# 归一化
transform_BZ = transforms.Normalize(
    mean=[0.5, 0.5, 0.5],
    std=[0.5, 0.5, 0.5]
)

class LoadData(Dataset):
    def __init__(self, txt_path, train_flag=True):
        self.imgs_info = self.get_images(txt_path)
        self.train_flag = train_flag

        self.train_tf = transforms.Compose([
            transforms.Resize(32),                  # 调整图像大小为32x32
            transforms.RandomHorizontalFlip(),       #  随机左右翻转图像
            transforms.RandomVerticalFlip(),         # 随机上下翻转图像
            transforms.ToTensor(),                   # 将 PIL 图像转换为张量
            transform_BZ                             # 执行某些复杂变换操作
        ])
        self.val_tf = transforms.Compose([
            transforms.Resize(32),                  # 调整图像大小为32x32
            transforms.ToTensor(),                   # 将 PIL 图像转换为张量
            transform_BZ                             # 执行某些变换操作
        ])

    def get_images(self, txt_path):
        with open(txt_path, 'r', encoding='utf-8') as f:
            imgs_info = f.readlines()
            imgs_info = list(map(lambda x: x.strip().split(' '), imgs_info))
        return imgs_info

    def padding_black(self, img):
        w, h = img.size
        scale = 32. / max(w, h)
        img_fg = img.resize([int(x) for x in [w * scale, h * scale]])
        size_fg = img_fg.size
        size_bg = 32
        img_bg = Image.new("RGB", (size_bg, size_bg))
        img_bg.paste(img_fg, ((size_bg - size_fg[0]) // 2,
                              (size_bg - size_fg[1]) // 2))

        img = img_bg
        return img

    def __getitem__(self, index):
        img_path, label = self.imgs_info[index]
        
        img_path = os.path.join('',img_path)
        img = Image.open(img_path)
        img = img.convert("RGB")
        img = self.padding_black(img)
        if self.train_flag:
            img = self.train_tf(img)
        else:
            img = self.val_tf(img)
        label = int(label)
        return img, label

    def __len__(self):
        return len(self.imgs_info)
  • トレーニング セットとテスト セットをロードする
train_data = LoadData("/home/aistudio/work/train.txt", True)
test_data = LoadData("/home/aistudio/work/test.txt", True)

#数据读取
train_loader = paddle.io.DataLoader(train_data, batch_size=32, shuffle=True)
test_loader = paddle.io.DataLoader(test_data, batch_size=32, shuffle=True)

3. モデルをインポートする

import math
import paddle.nn as nn
import paddle
import paddle.nn.functional as F
from paddle import fluid
import numpy as np


class BasicBlock(nn.Layer):
    def __init__(self, in_planes, out_planes, stride, dropRate=0.0):
        super(BasicBlock, self).__init__()
        self.bn1 = nn.BatchNorm2D(in_planes)
        self.relu1 = nn.ReLU()
        self.conv1 = nn.Conv2D(in_planes, out_planes, kernel_size=3, stride=stride,
                               padding=1)
        self.bn2 = nn.BatchNorm2D(out_planes)
        self.relu2 = nn.ReLU()
        self.conv2 = nn.Conv2D(out_planes, out_planes, kernel_size=3, stride=1,
                               padding=1)
        self.droprate = dropRate
        self.equalInOut = (in_planes == out_planes)
        self.convShortcut = (not self.equalInOut) and nn.Conv2D(in_planes, out_planes, kernel_size=1, stride=stride,
                               padding=0) or None
    def forward(self, x):
        if not self.equalInOut:
            x = self.relu1(self.bn1(x))
        else:
            out = self.relu1(self.bn1(x))
        out = self.relu2(self.bn2(self.conv1(out if self.equalInOut else x)))
        if self.droprate > 0:
            out = F.dropout(out, p=self.droprate, training=True)
        out = self.conv2(out)
        return paddle.add(x if self.equalInOut else self.convShortcut(x), out)

class NetworkBlock(nn.Layer):
    def __init__(self, nb_layers, in_planes, out_planes, block, stride, dropRate=0.0):
        super(NetworkBlock, self).__init__()
        self.layer = self._make_layer(block, in_planes, out_planes, nb_layers, stride, dropRate)
    def _make_layer(self, block, in_planes, out_planes, nb_layers, stride, dropRate):
        layers = []
        for i in range(int(nb_layers)):
            layers.append(block(i == 0 and in_planes or out_planes, out_planes, i == 0 and stride or 1, dropRate))
        return nn.Sequential(*layers)
    def forward(self, x):
        return self.layer(x)

class WideResNet(nn.Layer):
    def __init__(self,num_classes, depth=28,widen_factor=1, dropRate=0.0):
        super(WideResNet, self).__init__()
        nChannels = [16, 16*widen_factor, 32*widen_factor, 64*widen_factor, 64*widen_factor*10]
        assert((depth - 4) % 6 == 0)
        n = (depth - 4) / 6
        block = BasicBlock
        # 1st conv before any network block
        self.conv1 = nn.Conv2D(3, nChannels[0], kernel_size=3, stride=1,
                               padding=1)
        self.dropout = nn.Dropout(0.3)
        # 1st block
        self.block1 = NetworkBlock(n, nChannels[0], nChannels[1], block, 1, dropRate)
        # 2nd block
        self.block2 = NetworkBlock(n, nChannels[1], nChannels[2], block, 2, dropRate)
        # 3rd block
        self.block3 = NetworkBlock(n, nChannels[2], nChannels[3], block, 2, dropRate)
        # global average pooling and classifier
        self.bn1 = nn.BatchNorm2D(nChannels[3])
        self.relu = nn.ReLU()

        self.ID_mat = paddle.eye(num_classes).cuda()

        self.fc = nn.Linear(nChannels[3], num_classes)
        self.fc.weight.requires_grad = False        # Freezing the weights during training
        self.nChannels = nChannels[3]

        for m in self.sublayers():
            if isinstance(m, nn.Conv2D):
                n = m.weight.shape[0] * m.weight.shape[1] * m.weight.shape[2]
                v = np.random.normal(loc=0.,scale=np.sqrt(2./n),size=m.weight.shape).astype('float32')
                m.weight.set_value(v)
            elif isinstance(m, nn.BatchNorm):
                m.weight.set_value(np.ones(m.weight.shape).astype('float32'))
                m.bias.set_value(np.zeros(m.bias.shape).astype('float32'))
    def forward(self, x):
        out = self.conv1(x)
        out = self.block1(out)
        out = self.block2(out)
        out = self.block3(out)

        out = self.relu(self.bn1(out))
        out = F.avg_pool2d(out, 8)
        out = fluid.layers.reshape(out,(-1, self.nChannels))

        out = self.fc(out)
        return out
        

4. モデルのパラメータ情報を出力します。

import paddle
model = WideResNet(num_classes=4)
params_info = paddle.summary(model,(1, 3, 32, 32))
print(params_info)

印刷結果は次のようになります。

5. モデルのトレーニング

epoch_num = 60 #训练轮数
batch_size = 16
learning_rate = 0.001 #学习率


val_acc_history = []
val_loss_history = []


def train(model):
    print('start training ... ')
    # turn into training mode
    model.train()

    opt = paddle.optimizer.Adam(learning_rate=learning_rate,
                                parameters=model.parameters())

    for epoch in range(epoch_num):
        acc_train = []
        for batch_id, data in enumerate(train_loader()):
            x_data = data[0]
            y_data = paddle.to_tensor(data[1],dtype="int64")
            y_data = paddle.unsqueeze(y_data, 1)
            logits = model(x_data)
            loss = F.cross_entropy(logits, y_data)
            acc = paddle.metric.accuracy(logits, y_data)
            acc_train.append(acc.numpy())
            if batch_id % 100 == 0:
                print("epoch: {}, batch_id: {}, loss is: {}".format(epoch, batch_id, loss.numpy()))
                avg_acc = np.mean(acc_train)
                print("[train] accuracy: {}".format(avg_acc))
            loss.backward()
            opt.step()
            opt.clear_grad()
        
        # evaluate model after one epoch
        model.eval()
        accuracies = []
        losses = []
        for batch_id, data in enumerate(test_loader()):
            x_data = data[0]
            y_data = paddle.to_tensor(data[1],dtype="int64")
            y_data = paddle.unsqueeze(y_data, 1)

            logits = model(x_data)
            loss = F.cross_entropy(logits, y_data)
            acc = paddle.metric.accuracy(logits, y_data)
            accuracies.append(acc.numpy())
            losses.append(loss.numpy())

        avg_acc, avg_loss = np.mean(accuracies), np.mean(losses)
        print("[test] accuracy/loss: {}/{}".format(avg_acc, avg_loss))
        val_acc_history.append(avg_acc)
        val_loss_history.append(avg_loss)
        model.train()

train(model)
paddle.save(model.state_dict(), "model.pdparams")

6. 結果の視覚化

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息

epochs_range = range(epoch_num)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, val_acc_history, label='Val Accuracy')
plt.legend(loc='lower right')
plt.title('Val Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, val_loss_history, label='Val Loss')
plt.legend(loc='upper right')
plt.title('Val Loss')
plt.show()

ここに画像の説明を挿入

7. 個別予測結果の表示

data_transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Resize((32, 32)),
     transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))])

img = Image.open("/home/aistudio/work/BlackSkimmer/008.jpg")
plt.imshow(img)
image=data_transform(img)
plt.rcParams['font.sans-serif']=['FZHuaLi-M14S']
name=['蕉林莺','黑燕鸥类','黑喉树莺','凤头鹦鹉']
image=paddle.reshape(image,[1,3,32,32])
model.eval()
predict=model(image)
print(predict.numpy()) #明显可以看出是第0个标签大
plt.title(name[predict.argmax(1)])
plt.show()

ここに画像の説明を挿入

3. まとめ

  • WideResNet ( Wide Residual Network) は、深層畳み込みニューラル ネットワーク アーキテクチャであり、その主な特徴は、ワイド畳み込み層の使用により、ネットワーク内のチャネル数が増加し、それによってネットワークの表現力とパフォーマンスが向上することです。このネットワークは、残留接続を導入することでディープ ネットワークの勾配消失問題を軽減し、バッチ正規化やドロップアウトなどの正則化手法を使用することでネットワークの堅牢性を向上させます。WideResNet は、画像分類、オブジェクト検出、セマンティック セグメンテーションなどの分野で優れたパフォーマンスを達成しています。

  • ワイド残差ブロックとは、残差ブロックにさらに多くの畳み込みカーネルを追加することを指し、それによって特徴チャネルの数が増加します。従来の残差ブロックは 2 つの畳み込み層で構成され、通常、各畳み込み層には少数の畳み込みカーネルしか含まれません。WideResNet では、各残差ブロックには 2 つの畳み込み層が含まれており、2 番目の畳み込み層の畳み込みカーネルの数は非常に多く、これは特徴チャネルの数を数倍増やすことに相当します。特徴チャネルの数を増やすことで、 WideResNet は画像内の豊富な特徴情報をより適切にキャプチャできるようになり、モデルの精度が向上します。さらに、幅の広い残差ブロックはより優れた勾配フローを備えており、勾配をより浅い畳み込み層に速く渡すことができるため、モデルの収束速度が向上します。

  • 論文では、著者はB ( M ) B(M)と仮定します。B ( M )は残差ブロック構造を表します。ここでMMMは、ブロック内の畳み込み層のカーネル サイズのリストです。たとえば、B ( 3 , 1 ) B(3, 1)B ( 3 , 1 )は3 × 3 3 × 3を持つことを意味します3×31×1 1×11×1畳み込み層の残りのブロック前に説明したボトルネック ブロックを考慮していないため、特徴面の数はブロック全体で常に同じままであることに注意してください。その中で、著者は、基本的な残差アーキテクチャにおける3 × 3 と 3 × 33×3畳み込み層はどの程度重要ですか、また、畳み込み層は計算コストの低い1 × 1 1 × 1で計算できますか?1×1層、または1 × 1 1 × 11×13×3 3×33×代わりに3 つの畳み込み層の組み合わせ、たとえばB ( 1 , 3 ) B(1, 3)B ( 1 , 3 )またはB (1, 3) B (1, 3)B ( 1,3 ) _ _ これにより、ブロックの代表電力が増減する可能性があります。

元のリンク: https://aistudio.baidu.com/aistudio/projectdetail/6451612

おすすめ

転載: blog.csdn.net/m0_63007797/article/details/131424155