深層学習 - 畳み込みニューラル ネットワーク (CNN) のフルノート、コード付き

 この記事の内容は、ステーション[Convolutional Neural Network-CNN] Deep Learning (Tang Yudi Takes you to learn AI): Detailed Preparation of Convolutional Neural Network Theory and Project Combat、Computer Vision、Image Recognition Module Combat_ の学習ノートです。哔哩哔哩_bilibili


目次

ディープラーニングの基礎

ディープラーニングとは何ですか?

機械学習プロセス 

特徴量エンジニアリングの役割

特徴を抽出する方法

なぜディープラーニングが必要なのか 

 深層学習の応用

ディープラーニングの欠点

 従来のアルゴリズムとディープラーニング編集

コンピュータビジョン

コンピュータビジョンの課題 

機械学習ルーチン

K最近傍法 

K 最近傍計算処理

K 最近傍分析

データベースサンプル: CIFAR-10 

K 最近傍法を画像分類に使用できない理由

ニューラル ネットワークの基礎

一次関数 

W の重量値はどのようにして得られるのでしょうか? 

損失関数 

ソフトマックス分類器 

 順伝播

 畳み込みニューラル ネットワーク

畳み込みニューラル ネットワークでは何ができるのでしょうか? 

 畳み込みネットワークと従来のネットワークの違い

全体構造 

畳み込み層  

 畳み込みは何をするのでしょうか?

畳み込みを 1 つだけ行うだけで十分ですか?

畳み込み層にはパラメータが含まれます

プーリング層 

最大プーリング

 全体構成の具体的な考え方

機能マップの変更 

クラシック ネットワーク - Alexnet 

クラシック ネットワーク - Vgg 

クラシック ネットワーク - レスネット 

受容野 

プロジェクト戦闘 - CNN 1 に基づく認識モデルの構築

まずはデータを読み込む

畳み込みネットワークモジュールの構築

評価基準としての精度  

ネットワークモデルをトレーニングする  

プロジェクト戦闘 - CNN II に基づく認識モデルの構築 

画像認識でよく使用されるモジュールの解釈

データ前処理部

ネットワークモジュールの設定

ネットワークモデルの保存とテスト

データの読み取りと前処理操作 

 適切なデータソースを作成する

>> データ拡張

 タグに対応する実際の名前を読み取ります

データを見せる

 >> 転移学習

models で提供されるモデルをロードし、トレーニングされた重みを初期化パラメータとして直接使用します

どのレイヤーをトレーニングする必要があるかを設定する

オプティマイザ設定

 トレーニングモジュール 


ディープラーニングの基礎

ディープラーニングとは何ですか?

ディープラーニングは機械学習の一部であり、その効果はより優れています。

ニューラル ネットワーク (CNN) はアルゴリズムと呼ぶべきではなく、特徴抽出の方法と呼ぶべきです。

機械学習プロセス 

データ取得 - 特徴量エンジニアリング - モデル構築 - 評価と適用 

特徴量エンジニアリングの最も重要かつ核となる部分 。

ディープラーニングは、機械学習における人為的な問題をある程度解決します。抽出された特徴を自分で判断し、最適な処理方法を選択することができます。機械学習には手動による特徴抽出が必要

特徴量エンジニアリングの役割

  • データの特性によってモデルの上限が決まります。
  • 前処理とサーマル証明書の抽出がとなります。
  • アルゴリズムとパラメーターの選択によって、この上限 に近づく方法が決まります。

特徴を抽出する方法

なぜディープラーニングが必要なのか 

 ディープラーニングは実際にどのような特徴が最適であるかを学習することができます。

 ソリューションの核心は特徴を抽出する方法です

 深層学習の応用

 コンピュータビジョンと自然言語処理で何かができる

 顔認識

 医療用途

 顔の交換

 ビデオリカバリ

 幅広い用途

ディープラーニングの欠点

ディープラーニングの計算量が非常に多く、速度が遅すぎる可能性があります。

 従来のアルゴリズムとディープラーニング

コンピュータビジョン

 

コンピュータビジョンの課題 

 

 

機械学習ルーチン

  • データを収集してラベルを割り当てる
  • 分類器を訓練する
  • テストする、評価する

 

K最近傍法 

 K=3、三角形、K=5、正方形。

 

K 最近傍計算処理

  1. 現在のポイントから既知のクラスのデータセット内のポイントまでの距離を計算します。
  2. 距離順に並べ替える
  3. 現在の点からの距離が最小の K 点を選択します
  4. 最初の K 点のカテゴリの発生確率を決定します。
  5. 最初の K 点の出現頻度が最も高いカテゴリを現在の点予測カテゴリとして返します 

K 最近傍分析

  •  KNN アルゴリズム自体はシンプルかつ効果的であり、遅延学習アルゴリズムです。
  • 分類器はトレーニングにトレーニング セットを使用する必要がなく、トレーニング時間の複雑さは 0 です。
  • KNN 分類の計算量はトレーニング セット内のドキュメントの数に比例します。つまり、トレーニング セット内のドキュメントの総数が n の場合、KNN 分類の時間計算量は O(n) です。
  • K 値の選択、距離測定、および分類決定ルールは、アルゴリズムの 3 つの基本要素です。

 

データベースサンプル: CIFAR-10 

 画像分類のための K 最近傍

 ​​​​​

 問題は、画像の中に気づかれていない、被写体や背景を伝えていない何かがあることです。

K 最近傍法を画像分類に使用できない理由

  • 文脈の優位性が最大の問題だが、我々が注目するのは主語(主成分)である

機械はどのようにして重要な材料を学習できるのでしょうか?


ニューラル ネットワークの基礎

一次関数 

 目、耳、背景などのピクセルは結果にさまざまな影響を与え、猫であることを促進するピクセルもあれば、猫であることを抑制するピクセルもあります。したがって、各ピクセルは異なる重要度に対応し、W は重みパラメータと呼ばれます。

 

 3072 ピクセル、現在のカテゴリでは各ピクセルに独自の重みがあり、3072 ピクセルには 3072 の重みがあります。たとえば、ピクセルには猫のカテゴリには猫の体重があり、犬のカテゴリには犬の体重が含まれます。価値観が違います。10 個のカテゴリに分割される場合、10*3072 個の重みパラメーターが存在します。つまり、W は 10*3072 の行列、x は 3072 ピクセルを表し、3072*1 の行列になります。

W*X は 10*1 の行列で、各カテゴリのスコアの結果です。

b はバイアス パラメーターであり、微調整の役割を果たす 10*1 行列であり、各カテゴリには独自の微調整値があります。

 

  • 猫が 4 つのピクセルで構成されていると仮定します。x は 3 つのカテゴリ (猫、犬、ボート) を含む 4*1 行列です。W は 3*4 行列で、3 は 3 つのカテゴリを表し、4 は各カテゴリの 4 ピクセルを表します。 b の; b は 3*1 のバイアス パラメーターです。
  • W の値が大きいほど、このカテゴリではピクセルの影響が比較的大きいことを意味します。たとえば、2.1 は犬のカテゴリでピクセル値 24 の影響が 2.1 であることを意味し、0 はそれほど影響がないことを意味します。重要。
  • Wの±は、正の値は促進効果、負の値は抑制効果を表す

W の重量値はどのようにして得られるのでしょうか? 

体重の値を最適化します。体重の初期値を乱数に設定できます。たとえば、上の写真では明らかに猫ですが、最終的には犬になります。理由は何ですか?

データは変更されません 画像入力時、xの値は変更されませんが、Wの値は変更可能です。

ニューラル ネットワークが行うことは、W が現在のタスクを実行するデータにより適したものになるように W を最適化することです。

 W は最初にランダムな値に設定できます。次に、反復プロセスで、W パラメータを継続的に改善するための最適化方法を考えます。

 重みパラメータは決定境界の傾向を制御し、バイアス パラメータは微調整にすぎません。

 

損失関数 

 

 s_{j}: その他のエラー カテゴリ

s_{y_{i}}: 正しいクラス

1: 許容レベルに相当します。0 は損失がないことを意味します。正解と不正解のスコアが類似している場合、区別できない場合があります。現時点では、+1 することで誤った結果と正しい結果をよりよく区別できます。

 

 A は B モデルとは異なります。A は過学習を引き起こしますが、これは望ましくないため、損失関数の構築では、正則化ペナルティ項を追加する必要があります。

 R(W): 重みパラメータのみが考慮され、10 カテゴリの場合、R(W)=W1 平方 + W2 平方 +...+...+W10 平方

\ラムダ: ペナルティ係数。値が大きいほど、過学習は避けられ、正則化ペナルティも大きくなります。

ソフトマックス分類器 

 

 

exp:e^{x} マッピングを作成します。e^{3.2}=24.5、5.1 はマッピング後 164、-1.7 は 0.18 になります。

正規化: この項目のスコア/すべてを合計する

損失値を計算します。入力は正しいカテゴリの確率値です。正しい画像は元は猫ですが、髪の毛の確率値は 0.13 であるため、損失値 L=-log(0.13)=0.89

 順伝播

 W, x から最終損失値 L までのステップは、順伝播と呼ばれます。モデルを更新して W を最適化するには、勾配降下法が必要です

 

 畳み込みニューラル ネットワーク

畳み込みニューラル ネットワークでは何ができるのでしょうか? 

 

 検索: 画像を入力すると、同様の結果が返されます。

 

 

 

 

 畳み込みネットワークと従来のネットワークの違い

 左側は従来のネットワーク (NN)、右側は畳み込みネットワーク (CNN)、左側の入力はピクセル、右側の入力は画像です

たとえば、左側に 784 (784 ピクセル) と入力し、右側の画像は 28*28*1 (3 次元) と入力します。

畳み込みネットワークは、最初にデータをベクトルに取り込むのではなく、画像データから直接特徴を抽出します。すごいです 

全体構造 

 

 入力層は入力画像、3 次元、畳み込みは特徴を抽出すること、プーリングは特徴を圧縮することです

畳み込み層  

 畳み込みは何をするのでしょうか?

 

画像データの領域ごとに特性が異なり、重要度も異なります。

まず画像を異なる領域に分割し、各領域から異なる特徴を抽出します。ニューラル ネットワークと同様に、一連の重みパラメータを使用して特徴値を取得します。

青色画像の3×3は画像データを分割した小領域(3×3分割ごと)とみなすことができ、添え字の数字がこの領域に対応する重みパラメータ行列となる。最終的に、現在の分割領域の代表値である 12 という値が得られます。

緑色の画像は、畳み込みを実行した後に得られた特徴マップを表します。

 RGB

32*32*3、3 は 3 つのカラー チャネルを表します

実際に計算を行う場合、各カラー チャネルを再度計算する必要があり、最終的に各カラー チャネルの畳み込み結果が合計されます。

 

 たとえば、3 つのカラー チャネルの場合、対応する分割領域で R/G/B が 1 回計算され、R チャネルの対応する結果が 1、B チャネルの対応する結果が 2、およびG チャネルは 3、最終結果は 1+2 +3=6 です。

 異なる重みパラメータは異なる特徴マップを取得します。多くの可能性があります

 

 

入力データが 7*7*3 であるとします。

フィルター W0 : 重みパラメーターのセットをランダム化することを意味します。最初の 2 つの 3*3 は畳み込みカーネルであり、選択された領域のサイズが 3*3 であること、つまり 3*3 領域ごとに特徴が選択されることを示します。 3 つのチャネルの値は同じではありません。チャネルが異なるとピクセル値が異なるためです。

計算:計算には内積を使用します。つまり、対応する位置を乗算し、すべての結果を加算します。例: 上図の 3 つのカラー チャネルの内積計算結果はそれぞれ 0、2、0 で、最終結果は 3 つのカラー チャネルが加算されて 0+2+0+b、b になります。はバイアス項目であり、この図では b= 1 であるため、最終結果は 3、つまり最初の 3*3 領域に対応する結果は 3 になります。

フィルタ W1: W1 は W0 と同じ仕様ですが、値が異なります

緑色の画像は 3*3*2 で、2 は深さ、つまり特徴マップの数を表します。

 最初のエリアが終了したら、次のエリアに進み、2 マス前に移動します (サイズは自分で設定できます)。

畳み込みを 1 つだけ行うだけで十分ですか?

 一度行うだけでは十分ではなく、何度も行う必要があります

入力: 32*32*3

eg6 は 6 つの異なるフィルターを表し、生成された特徴マップには 6 つのレイヤーがあります

コンボリューション カーネルの深さは、前の入力データの深さと一致している必要があります。

畳み込み層にはパラメータが含まれます

コンボリューションカーネルのステップサイズ

ステップ サイズが小さいほど、画像の特徴がより繊細に抽出され、効率が遅くなります。通常1 

ステップ サイズ +1 ごとに、特徴マップ h と w は 2 ずつ減ります。

コンボリューションカーネルサイズ

3*3 領域と 4*4 領域のサイズは異なり、コンボリューション カーネルのサイズが小さいほど、画像の特徴がより繊細に抽出されます。通常 3*3

エッジパディング

 画像データがエッジに近づくほど特徴抽出への影響は小さくなり、中央に近づくほど計算時間が増加し、特徴抽出結果への影響が大きくなります。境界充填は、元の画像の境界を内側に入れることができ、特徴抽出の度合いを高め、情報の損失をある程度回避できます。

 他の値ではなく 0 を追加することを選択するのはなぜですか?

他の値を追加する場合、フィルターで計算する場合、いくつかの値が生成され、結果に影響します

コンボリューションカーネルの数

最終的にいくつの特徴マップを取得する必要があるか、コンボリューションカーネルの数がその数になります 

コンボリューションカーネルの結果計算式

 入力データが 32*32*3 画像の場合、畳み込み演算には 5*5*3 のフィルターを 10 個使用し、ステップ サイズを 1、境界パディングを 2 に指定し、最終的な出力サイズを指定します。

(32-5+2*2)/1+1=32、最終的な出力サイズは 32*32*10 です。

畳み込み演算の後、特徴マップの長さと幅も変更せずに維持することができます。

畳み込みパラメータの共有 

 

 

プーリング層 

 プーリング層は圧縮用であり、行列計算は含まれず、単なるフィルタリング操作です。

 h、wは変更されましたが、cは変更されません

最大プーリング

 特定の特徴マップ (4*4) に対して MAX POOLING を実行すると、最初に異なる領域が選択され、各領域の最大値が選択されます。

なぜ最大値が選択されるのでしょうか?

 畳み込みニューラル ネットワークでは、多くの場合、得られる値が大きいほど重要になります。

 全体構成の具体的な考え方

 

 まず、特徴抽出には畳み込みが用いられますが、畳み込み後の RELU は 1 回の畳み込み後に非線形変換を追加する必要があり、2 回の畳み込み後に 1 回のプーリングが実行されます。

最終的な特徴マップが 32*32*10 であると仮定すると、それをどのように分類して分類確率値に変換するか?

全結合層 FC が必要です。FC は前に複数の畳み込みを行った後、高度に抽象化された特徴を統合し、正規化してさまざまな分類状況の確率を出力できます。後続の分類器は全結合確率分類に従って取得できます。

FC の行列サイズ: [10240, 5]; 5 つのカテゴリなので 2 番目の値は 5、FC は 3 次元接続できないため、前の 3 次元特徴マップを特徴ベクトルに取り込む必要があり、ベクトル サイズは 32*32*10=10240 です。

何をレイヤーと呼べるでしょうか?このグラフにはニューラル ネットワークの層が何層ありますか?

パラメーターを使用してパラメーターを計算する機能は層と呼ばれ、畳み込み層、RELU 層なし、プーリング層なし、全結合層あり。したがって、この図には 7 層のニューラル ネットワークがあります。

機能マップの変更 

 変換: 3 次元の特徴マップを 1 次元のベクトルにプルします。

クラシック ネットワーク - Alexnet 

 

  • 11*11 フィルターは急激で良くありません。この段階でのコンボリューション カーネルが小さいほど良いです。
  • ストライドは 4、ステップ サイズが長すぎます。パッドは 0、パディングなしです。
  • 8層ネットワーク、5層コンボリューション、3層フルコネクション

クラシック ネットワーク - Vgg 

 

  • すべての畳み込みサイズは 3*3 で、繊細です
  • ネットワークは 16/19 層あります
  • maxpool の後に情報が失われるため、Vgg は失われた情報を補うために maxpool の後に機能マップを 2 倍にします。
  • Vgg の分類精度は Alexnet よりも約 15% 高いですが、ネットワークのトレーニング時間は Alexnet よりもはるかに長くする必要があります。

なぜ Vgg は 16 レイヤーを使用するのですか? レイヤーが高いほど良いのでしょうか?

 検証の結果、畳み込み層が増加すると、以前に抽出された特徴に基づいて特徴が抽出されるため、必ずしもすべての畳み込み層がうまく機能するとは限らず、16 層の効果が他の層よりも優れていることがわかります。 、以前に抽出された特徴より必ずしも優れているとは限りません

クラシック ネットワーク - レスネット 

 

 質問: 56 層のエラー率は 20 層のエラー率よりも高くなります

分析: 20 階と 56 階の間には、うまくできていないレイヤーがいくつかあるはずです。

解法: x: 畳み込みのある層で、畳み込みを2回行うと効果が得られない場合がありますが、その際、追加の行でxを重ねて畳み込みの結果を加算してください。 、F(x) に注意してください。形状は x の形状と同じでなければなりません。いわゆる加算は、特徴行列の同じ位置にある数値を加算することです。畳み込み層が常に損失値を増加させる場合、ネットワークは、このレイヤーのすべてのパラメーターを 0 にし、残りの下位 x レベルを作成します。これは白のプレーのこの層に相当しますが、少なくとも元の結果より悪くなることはありません。

解決策の効果: 

 左側は従来のネットワーク、右側は Resnet ネットワークです。左側の層数が多いほど誤差値は大きくなり、右側の層数が多いほど誤差値は小さくなります。

 ネットワークが分類であるか回帰であるかは、損失関数と最終層の接続方法に依存するため、Resnet は特徴抽出です。

 

受容野 

入力画像上にマッピングされた畳み込みニューラルネットワークの各層が出力する特徴マップ上の画素点の面積サイズ。

 

 

プロジェクト戦闘 - CNN 1 に基づく認識モデルの構築

畳み込みニューラル ネットワークを構築する 

 畳み込みネットワークの入力と層は従来のニューラル ネットワークとは若干異なるため、再設計する必要がありますが、トレーニング モジュールは基本的に同じです

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets,transforms 
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

 

まずはデータを読み込む

  • トレーニング セットとテスト セット (検証セット) を個別に構築する
  • データを繰り返しフェッチする DataLoader
# 定义超参数 
input_size = 28  #图像的总尺寸28*28*1,三维的
num_classes = 10  #标签的种类数
num_epochs = 3  #训练的总循环周期
batch_size = 64  #一个撮(批次)的大小,64张图片
 
# 训练集
train_dataset = datasets.MNIST(root='./data',  
                            train=True,   
                            transform=transforms.ToTensor(),  
                            download=True) 
 
# 测试集
test_dataset = datasets.MNIST(root='./data', 
                           train=False, 
                           transform=transforms.ToTensor())
 
# 构建batch数据
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, 
                                           batch_size=batch_size, 
                                           shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, 
                                           batch_size=batch_size, 
                                           shuffle=True)

 

畳み込みネットワークモジュールの構築

  • 一般的な畳み込み層、relu層、プーリング層をパッケージとして記述可能
  • 畳み込みの最終結果は依然として特徴マップであることに注意してください。分類または回帰タスクを実行するには、これをベクトルに変換する必要があります。
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(         # 输入大小 (1, 28, 28) conv1不是第一个卷积层,而是第一个卷积模块,包括卷积relu池化
            nn.Conv2d(
                in_channels=1,              # 灰度图,当前输入的特征图个数
                out_channels=16,            # 要得到几多少个特征图
                kernel_size=5,              # 卷积核大小
                stride=1,                   # 步长
                padding=2,                  # 如果希望卷积后大小跟原来一样,需要设置padding=(kernel_size-1)/2 if stride=1
            ),                              # 输出的特征图为 (16, 28, 28)
            nn.ReLU(),                      # relu层
            nn.MaxPool2d(kernel_size=2),    # 进行池化操作(2x2 区域), 输出结果为: (16, 14, 14)
        )
        self.conv2 = nn.Sequential(         # 下一个套餐的输入 (16, 14, 14)
            nn.Conv2d(16, 32, 5, 1, 2),     #参数的简单写法,与conv1对应。# 输出 (32, 14, 14)
            nn.ReLU(),                      # relu层
            nn.MaxPool2d(2),                # 输出 (32, 7, 7)
        )
        self.out = nn.Linear(32 * 7 * 7, 10)   # 全连接层得到的结果
 
    def forward(self, x):   #前向传播
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(x.size(0), -1)           # flatten操作,结果为:(batch_size, 32 * 7 * 7)
        output = self.out(x)
        return output

評価基準としての精度  

def accuracy(predictions, labels):
    pred = torch.max(predictions.data, 1)[1] 
    rights = pred.eq(labels.data.view_as(pred)).sum() 
    return rights, len(labels) 

ネットワークモデルをトレーニングする  

# 实例化
net = CNN() 
#损失函数
criterion = nn.CrossEntropyLoss() 
#优化器
optimizer = optim.Adam(net.parameters(), lr=0.001) #定义优化器,普通的随机梯度下降算法
 
#开始训练循环
for epoch in range(num_epochs):
    #当前epoch的结果保存下来
    train_rights = [] 
    
    for batch_idx, (data, target) in enumerate(train_loader):  #针对容器中的每一个批进行循环
        net.train()                             
        output = net(data) 
        loss = criterion(output, target) 
        optimizer.zero_grad() 
        loss.backward() 
        optimizer.step() 
        right = accuracy(output, target) 
        train_rights.append(right) 
 
    
        if batch_idx % 100 == 0: 
            
            net.eval() 
            val_rights = [] 
            
            for (data, target) in test_loader:
                output = net(data) 
                right = accuracy(output, target) 
                val_rights.append(right)
                
            #准确率计算
            train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
            val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))
 
            print('当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集正确率: {:.2f}%'.format(
                epoch, batch_idx * batch_size, len(train_loader.dataset),
                100. * batch_idx / len(train_loader), 
                loss.data, 
                100. * train_r[0].numpy() / train_r[1], 
                100. * val_r[0].numpy() / val_r[1]))

プロジェクト戦闘 - CNN II に基づく認識モデルの構築 

画像認識でよく使用されるモジュールの解釈

データの前処理

  • データ拡張: torchvision の変換モジュールには、より実用的な独自の関数があります。
  • データの前処理: torchvision の変換もそれを実現するのに役立ちます。直接呼び出すだけです。
  • DataLoader モジュールはバッチ データを直接読み取ります

ネットワークモジュールの設定

  • 事前トレーニングされたモデルをロードします。torchvision には多くの古典的なネットワーク アーキテクチャがあり、呼び出すのが非常に便利です。また、他の人がトレーニングした重みパラメータを使用してトレーニングを継続できます。これがいわゆる転移学習です。
  • 他の人が訓練したタスクは私たちのタスクとまったく同じではないことに注意する必要があり、最後のヘッド層 (通常は最後に完全に接続された層) を変更して、それを私たち自身のタスクに変更する必要があります。
  • 最初の数層はすべて特徴抽出用であり、本質的なタスクの目標は同じであるため、トレーニング中に、最初からトレーニングをやり直すことも、タスクの最後の層だけをトレーニングすることもできます。

ネットワークモデルの保存とテスト

  • モデルは選択的に保存できます。たとえば、現在の効果が検証セットで良好であれば、それを保存します。
  • 実際のテスト用にモデルを読み取る

 

import os
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import torch
from torch import nn
import torch.optim as optim
import torchvision
#需要先安装pip install torchvision,安装后就可以用里面的三大模块了
from torchvision import transforms, models, datasets
#https://pytorch.org/docs/stable/torchvision/index.html
import imageio
import time
import warnings
import random
import sys
import copy
import json
from PIL import Image

データの読み取りと前処理操作 

data_dir = './flower_data/'
train_dir = data_dir + '/train'
valid_dir = data_dir + '/valid'

 適切なデータソースを作成する

  • すべての画像前処理操作は data_transforms で指定されます
  • ImageFolder は、すべてのファイルがフォルダーに保存され、同じカテゴリの画像が各フォルダーの下に保存され、フォルダーの名前がカテゴリの名前になることを前提としています。

>> データ拡張

 データが足りない場合は、元の画像を反転、回転、拡大、縮小して、より多くのデータを取得できます


data_transforms = {#训练集做数据增强
    'train': transforms.Compose([transforms.RandomRotation(45),#随机旋转,45是-45到45度之间随机选
        transforms.CenterCrop(224),#从中心开始裁剪(非随机),留下224*244的大小区域
        transforms.RandomHorizontalFlip(p=0.5),#随机水平翻转 p=0.5,有50%的概率进行翻转,50%概率不动,一般情况下都是0.5
        transforms.RandomVerticalFlip(p=0.5),#随机垂直翻转
        transforms.ColorJitter(brightness=0.2, contrast=0.1, saturation=0.1, hue=0.1),#参数1为亮度,参数2为对比度,参数3为饱和度,参数4为色相
        transforms.RandomGrayscale(p=0.025),#概率转换成灰度率,3通道就是R=G=B
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])#标准化: 均值,标准差   (减均值/标准差)
    ]),
    #验证集不需要数据增强了
    'valid': transforms.Compose([transforms.Resize(256),  #做验证的时候需要resize
        transforms.CenterCrop(224),#中心裁剪
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])#标准化
    ]),  #训练集是怎么做预处理的验证集也需要怎么做预处理
}
batch_size = 8#显存不够可调小
 
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'valid']}  #datasets.ImageFolder(实际路径,刚才的预处理方法)
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True) for x in ['train', 'valid']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'valid']}
class_names = image_datasets['train'].classes

 タグに対応する実際の名前を読み取ります

with open('cat_to_name.json', 'r') as f:
    cat_to_name = json.load(f)

データを見せる

  • テンソル データは numpy 形式に変換する必要があり、また正規化された結果に復元する必要があることに注意してください。

 

def im_convert(tensor):
    """ 展示数据"""
    
    image = tensor.to("cpu").clone().detach()
    image = image.numpy().squeeze()
    image = image.transpose(1,2,0)   #将H W C还原回去
    image = image * np.array((0.229, 0.224, 0.225)) + np.array((0.485, 0.456, 0.406))#还原标准化,先乘再加
    image = image.clip(0, 1)
 
    return image
fig=plt.figure(figsize=(20, 12))
columns = 4
rows = 2
 
dataiter = iter(dataloaders['valid'])   #一组batch数据
inputs, classes = dataiter.next()
 
for idx in range (columns*rows):
    ax = fig.add_subplot(rows, columns, idx+1, xticks=[], yticks=[])
    ax.set_title(cat_to_name[str(int(class_names[classes[idx]]))])
    plt.imshow(im_convert(inputs[idx]))
plt.show()

 >> 転移学習

ネットワークをトレーニングする過程で、さまざまな問題が発生する可能性があります。

  • おそらく、手元にデータがあまりないため、モデルの過剰適合が発生し、結果として良いデータ強化が得られない可能性があります。 
  • ネットワークモデルをトレーニングするには、さまざまなパラメータを調整する必要があり、時間がかかります
  • モデルのトレーニングに時間がかかりすぎる 

ここでは転移学習を使用する必要があります

 自分自身のデータが十分でない場合は、他の人のモデルを使用して、他の人のトレーニングされた重みパラメータとバイアスパラメータを使用できます。

ただし、すべての構造、入力および出力形式が他のものと一貫していることを確認する必要があります。

 

通常、前のレイヤーには 2 つのオプションがあります。

  • A: 他の人の畳み込み層を自分自身の重みパラメータの初期化として使用し、トレーニングを続行します。
  • B: 他の人の畳み込み層を取得してフリーズし、変更しないで、重みパラメーターを独自の結果として使用します。

データ量が少ない場合は、多くのレイヤーをフリーズする必要があります。データ量が中程度の場合は、前のレイヤーをフリーズできます。データ量が多い場合は、フリーズしないことを選択できます。 

完全に接続された層は通常、独自の方法で再定義および再トレーニングされます。 

 

models で提供されるモデルをロードし、トレーニングされた重みを初期化パラメータとして直接使用します

model_name = 'resnet'  #可选的比较多 ['resnet', 'alexnet', 'vgg', 'squeezenet', 'densenet', 'inception']
#是否用人家训练好的特征来做
feature_extract = True 

# 是否用GPU训练
train_on_gpu = torch.cuda.is_available()
 
if not train_on_gpu:
    print('CUDA is not available.  Training on CPU ...')
else:
    print('CUDA is available!  Training on GPU ...')
    
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#迁移学习
def set_parameter_requires_grad(model, feature_extracting): #要不要把某些层冻住,参数不用训练了
    if feature_extracting:
        for param in model.parameters():
            param.requires_grad = False
model_ft = models.resnet152()
model_ft

pytorch公式Webサイトの例を参照してください 

def initialize_model(model_name, num_classes, feature_extract, use_pretrained=True):
    # 选择合适的模型,不同模型的初始化方法稍微有点区别
    model_ft = None
    input_size = 0
 
    if model_name == "resnet":
        """ Resnet152
        """
        model_ft = models.resnet152(pretrained=use_pretrained) #pretrained 是否下载用人家的模型
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.fc.in_features #返回原来模型的全连接层2048
        model_ft.fc = nn.Sequential(nn.Linear(num_ftrs, 102),
                                   nn.LogSoftmax(dim=1))  #加一个全连接层2048*102
        input_size = 224
 
    elif model_name == "alexnet":
        """ Alexnet
        """
        model_ft = models.alexnet(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.classifier[6].in_features
        model_ft.classifier[6] = nn.Linear(num_ftrs,num_classes)
        input_size = 224
 
    elif model_name == "vgg":
        """ VGG11_bn
        """
        model_ft = models.vgg16(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.classifier[6].in_features
        model_ft.classifier[6] = nn.Linear(num_ftrs,num_classes)
        input_size = 224
 
    elif model_name == "squeezenet":
        """ Squeezenet
        """
        model_ft = models.squeezenet1_0(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        model_ft.classifier[1] = nn.Conv2d(512, num_classes, kernel_size=(1,1), stride=(1,1))
        model_ft.num_classes = num_classes
        input_size = 224
 
    elif model_name == "densenet":
        """ Densenet
        """
        model_ft = models.densenet121(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.classifier.in_features
        model_ft.classifier = nn.Linear(num_ftrs, num_classes)
        input_size = 224
 
    elif model_name == "inception":
        """ Inception v3
        Be careful, expects (299,299) sized images and has auxiliary output
        """
        model_ft = models.inception_v3(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        # Handle the auxilary net
        num_ftrs = model_ft.AuxLogits.fc.in_features
        model_ft.AuxLogits.fc = nn.Linear(num_ftrs, num_classes)
        # Handle the primary net
        num_ftrs = model_ft.fc.in_features
        model_ft.fc = nn.Linear(num_ftrs,num_classes)
        input_size = 299
 
    else:
        print("Invalid model name, exiting...")
        exit()
 
    return model_ft, input_size

どのレイヤーをトレーニングする必要があるかを設定する

model_ft, input_size = initialize_model(model_name, 102, feature_extract, use_pretrained=True)  #要不要冻住一些层,要不要用人家的model
 
#GPU计算
model_ft = model_ft.to(device)
 
# 模型保存
filename='checkpoint.pth'
 
# 是否训练所有层
params_to_update = model_ft.parameters()
print("Params to learn:")
if feature_extract:
    params_to_update = []
    for name,param in model_ft.named_parameters():
        if param.requires_grad == True:
            params_to_update.append(param)
            print("\t",name)
else:
    for name,param in model_ft.named_parameters():
        if param.requires_grad == True:
            print("\t",name)
model_ft#改完后,打印网络架构,看最后

オプティマイザ設定

# 优化器设置
optimizer_ft = optim.Adam(params_to_update, lr=1e-2)
scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)#学习率每7个epoch衰减成原来的1/10
#最后一层已经LogSoftmax()了,所以不能nn.CrossEntropyLoss()来计算了,nn.CrossEntropyLoss()相当于logSoftmax()和nn.NLLLoss()整合
criterion = nn.NLLLoss()

 トレーニングモジュール 

def train_model(model, dataloaders, criterion, optimizer, num_epochs=25, is_inception=False,filename=filename): #模型、一个一个batch取数据、损失函数、优化器、训练多少epoch、要不要用其他的网络、
    since = time.time()
    best_acc = 0  #保存一个最好的准确率
    """
    checkpoint = torch.load(filename)
    best_acc = checkpoint['best_acc']
    model.load_state_dict(checkpoint['state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer'])
    model.class_to_idx = checkpoint['mapping']
    """
    model.to(device)
 
    val_acc_history = []
    train_acc_history = []
    train_losses = []
    valid_losses = []
    LRs = [optimizer.param_groups[0]['lr']]  #学习率
 
    best_model_wts = copy.deepcopy(model.state_dict()) #最好的一次存下来
 
    for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)
 
        # 训练和验证
        for phase in ['train', 'valid']:
            if phase == 'train':
                model.train()  # 训练
            else:
                model.eval()   # 验证
 
            running_loss = 0.0
            running_corrects = 0
 
            # 把数据都取个遍
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)  #传到GPU当中
                labels = labels.to(device)
 
                # 清零
                optimizer.zero_grad()
                # 只有训练的时候计算和更新梯度
                with torch.set_grad_enabled(phase == 'train'):
                    if is_inception and phase == 'train':
                        outputs, aux_outputs = model(inputs)
                        loss1 = criterion(outputs, labels)
                        loss2 = criterion(aux_outputs, labels)
                        loss = loss1 + 0.4*loss2
                    else:#resnet执行的是这里
                        outputs = model(inputs)
                        loss = criterion(outputs, labels)
 
                    _, preds = torch.max(outputs, 1)
 
                    # 训练阶段更新权重
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()
 
                # 计算损失
                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels.data)
 
            epoch_loss = running_loss / len(dataloaders[phase].dataset)
            epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)
            
            
            time_elapsed = time.time() - since
            print('Time elapsed {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
            print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
            
 
            # 得到最好那次的模型
            if phase == 'valid' and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy(model.state_dict())
                state = {
                  'state_dict': model.state_dict(),
                  'best_acc': best_acc,
                  'optimizer' : optimizer.state_dict(),
                }
                torch.save(state, filename)
            if phase == 'valid':
                val_acc_history.append(epoch_acc)
                valid_losses.append(epoch_loss)
                scheduler.step(epoch_loss)
            if phase == 'train':
                train_acc_history.append(epoch_acc)
                train_losses.append(epoch_loss)
        
        print('Optimizer learning rate : {:.7f}'.format(optimizer.param_groups[0]['lr']))
        LRs.append(optimizer.param_groups[0]['lr'])
        print()
 
    time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:4f}'.format(best_acc))
 
    # 训练完后用最好的一次当做模型最终的结果
    model.load_state_dict(best_model_wts)
    return model, val_acc_history, train_acc_history, valid_losses, train_losses, LRs 

コードリファレンス: PyTorch 画像認識の実践_I am Xiaobai's blog-CSDN blog_pytorch 認識

おすすめ

転載: blog.csdn.net/weixin_58176527/article/details/125530000