PyTorch 深層学習の実践 (17) - マルチタスク学習

0. 序文

マルチタスク学習 ( Multi-Task LearningMTL) は、複数の関連タスクを同時に処理および学習するために使用される一般的な機械学習方法です。従来のシングルタスク学習では、通常、タスクごとに独立したモデルをトレーニングする必要がありますが、マルチタスク学習では、モデルの特徴表現を共有することで複数のタスクを同時に学習します。たとえば、猫と犬の分類とキーポイント検出で学んだことに基づいて、特定の画像内の人の年齢や性別を個別に予測するようにニューラル ネットワークをトレーニングすることができましたが、どのように予測するかはまだ検討していません。画像単一の 同じ写真を使用して同時に複数の予測を行う可能性があるため、同じ画像に基づいて複数の予測を行うことは、現実のシナリオでは非常に重要です。このセクションでは、マルチタスク学習の基本概念を紹介し、人の性別や年齢の予測など、複数の異なる種類のタスクを同時に実行するためのモデルを構築します。

1. マルチタスク学習

1.1 マルチタスク学習の基本概念

マルチタスク学習では、複数の関連タスクを同時に処理および学習し、複数の相互関連タスクを共同でトレーニングすることでモデルの汎化能力と効果を向上させ、1 つまたは複数の入力を使用して複数の異なる出力を予測できます。マルチタスク学習では、複数の異なるタスクが同じネットワーク モデルを使用し、モデルが複数のタスクの知識を同時に学習するため、トレーニング データをより効果的に利用できます。たとえば、自動運転では、モデルは障害物の識別、ルートの計画、適切な量のスロットル/ブレーキとステアリングの提供などを行い、(複数のセンサーからの) 同じ入力セットを考慮してこれらのタスクをリアルタイムで完了する必要があります。

1.2 マルチタスク学習の利点

マルチタスク学習の利点は、タスク間の相互促進と知識の共有にあります。タスク間の相関関係と依存関係を共有モデルに導入することで、全体的なパフォーマンス、汎化機能、データ効率を向上させることができます。

  • パフォーマンスの向上: 基礎となる特徴表現を共有することで、モデルはさまざまなタスクからより一般的な特徴を学習でき、知識を共有することでタスクのパフォーマンスを向上させることができます。
  • 汎化能力の強化: マルチタスク学習は、異なるが関連するタスクに関する共同トレーニングを通じてモデルがより広範でよりグローバルな特徴を学習するのに役立ち、それによって汎化能力が向上します。
  • データ効率の向上: データ不足に直面した場合、マルチタスク学習では共有モデルを介したタスク間のデータ共有を利用して、データの効果的な利用を向上させることができます。
  • ロバスト性の強化: マルチタスク学習では、異なるタスク間で機能を共有することにより、モデルのロバスト性と耐干渉能力を向上させることができます。1 つのタスクで異常やデータ欠落が発生した場合、他のタスクが追加情報を提供して、モデルがより適切に対処できるようにすることができます。

マルチタスク学習には、自然言語処理、コンピュータ ビジョン、音声認識など、幅広い応用分野があります。マルチタスク学習を通じて、基礎となる意味表現を共有することができ、タスクの全体的な効果を向上させることができます。

2. モデルとデータセットの分析

2.1 モデル分析

このセクションでは、単一の前方パスで連続値の予測と離散値 (カテゴリ) の予測の両方を行う方法を学びます。モデル構築戦略は次のとおりです。

  • 関連ライブラリをインポートする
  • 人物の画像、性別、年齢情報を含むデータセットを取得する
  • データを前処理し、トレーニング データセットとテスト データセットを作成する
  • モデルを構築します。
    • 特徴抽出レイヤーは、事前トレーニングされた VGG19 モデルを使用します
    • 2 つのブランチ独立レイヤーを作成します。1 つは年齢推定に対応し、もう 1 つは性別分類に対応します。
    • 各出力ブランチには異なる損失関数があり、年齢は連続値 (損失の計算mse) mae、性別はカテゴリ値 (クロスエントロピー損失の計算) です。
    • 加重年齢推定損失と性別分類損失
    • バックプロパゲーションを通じて重み値を最適化し、全体的な損失を最小限に抑える
  • モデルをトレーニングして新しい画像を予測する

2.2 データセットの概要

多分類モデルを構築するには、のチームによってリリースされた顔分析および顔認識用の多様性顔データセットであるFairFaceデータセットを使用しますこのデータセットの目的は、研究者がさまざまなグループで顔認識アルゴリズムのパフォーマンスをトレーニングおよび評価できるように、複数の人種、性別、年齢にわたる現実世界の顔画像のコレクションを提供することです。このデータ セットには、ラベル付きの顔画像が含まれています。このデータ セットをダウンロードして使用するには、抽出コードは次のとおりですFairFaceFairFace2019FairFace87,022cr6n

3. 年齢推定と性別分類の実装

次に、次を使用してPyTorchマルチタスク学習モデルを実装します。

(1)関連ライブラリをインポートします。

import torch
import numpy as np, cv2, pandas as pd, time
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms, models
device = 'cuda' if torch.cuda.is_available() else 'cpu'

(2)ダウンロードしたデータセットをロードし、データ構造を表示します。

trn_df = pd.read_csv('fairface-labels-train.csv')
val_df = pd.read_csv('fairface-labels-val.csv')
print(trn_df.head())

出力は次のとおりです。

          file  age  gender        race  service_test
0  train/1.jpg   59    Male  East Asian          True
1  train/2.jpg   39  Female      Indian         False
2  train/3.jpg   11  Female       Black         False
3  train/4.jpg   26  Female      Indian          True
4  train/5.jpg   26  Female      Indian          True

(3)GenderAgeClassクラスを構築し、ファイル名を入力として受け取り、対応する画像、性別、年齢を返します。このうち、年齢は連続数値であるためスケーリングする必要があり、勾配の消失を避けるためにデータがスケーリングされ、後処理中に再度復元されます。

__init__画像のファイル パスをメソッドの入力として取得します

IMAGE_SIZE = 224
class GenderAgeClass(Dataset):
    def __init__(self, df, tfms=None):
        self.df = df
        self.normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], 
                                              std=[0.229, 0.224, 0.225])

メソッドを使用して__len__、入力内の画像の数を返します。

    def __len__(self):
        return len(self.df)

指定された場所で画像情報を__getitem__取得するメソッドを定義します。ix

    def __getitem__(self, ix):
        f = self.df.iloc[ix].squeeze()
        file = f.file
        gen = f.gender == 'Female'
        age = f.age
        im = cv2.imread(file)
        im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
        return im, age, gen

画像のサイズ変更、画像チャネルの調整、画像の正規化などの画像前処理関数を作成します。

    def preprocess_image(self, im):
        im = cv2.resize(im, (IMAGE_SIZE, IMAGE_SIZE))
        im = torch.tensor(im).permute(2,0,1)
        im = self.normalize(im/255.)
        return im[None]

collate_fnバッチ データに対して次の前処理を実行するメソッドを作成します。

  • メソッドを使用してprocess_image各画像を処理します
  • すべての値がと の間に収まる80ように、年齢をスケールします (データセット内に存在する最大の年齢値で除算 - )。01
  • 性別を浮動小数点値に変換する
  • 画像、年齢、性別をテンソルオブジェクトに変換し、その逆を行う
    def collate_fn(self, batch):
        'preprocess images, ages and genders'
        ims, ages, genders = [], [], []
        for im, age, gender in batch:
            im = self.preprocess_image(im)
            ims.append(im)

            ages.append(float(int(age)/80))
            genders.append(float(gender))

        ages, genders = [torch.tensor(x).to(device).float() for x in [ages, genders]]
        ims = torch.cat(ims).to(device)

        return ims, ages, genders

(4)トレーニングおよび検証のデータ セットとデータ ローダーを定義します。

データセットを作成します。

trn = GenderAgeClass(trn_df)
val = GenderAgeClass(val_df)

データローダーを構築します。

train_loader = DataLoader(trn, batch_size=32, shuffle=True, drop_last=True, collate_fn=trn.collate_fn)
test_loader = DataLoader(val, batch_size=32, collate_fn=val.collate_fn)
a,b,c, = next(iter(train_loader))
print(a.shape, b.shape, c.shape)
# torch.Size([32, 3, 224, 224]) torch.Size([32]) torch.Size([32])

(5)モデル、損失関数、オプティマイザーを定義します。

関数 でget_model()、事前学習済みモデルを読み込みますVGG16

def get_model():
    model = models.vgg16(pretrained = True)

ロードされたモデルをフリーズします (パラメータを指定しますparam.requires_grad = False):

    for param in model.parameters():
        param.requires_grad = False

レイヤーをカスタム ネットワーク レイヤーに置き換えますavgpool

    model.avgpool = nn.Sequential(
        nn.Conv2d(512,512, kernel_size=3),
        nn.MaxPool2d(2),
        nn.ReLU(),
        nn.Flatten()
    )

ageGenderClassifier2 つの出力ブランチを持つニューラル ネットワークを作成するために、次の名前のニューラル ネットワーク クラスを構築します。

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

中間層を定義しますintermediate

            self.intermediate = nn.Sequential(
                nn.Linear(2048,512),
                nn.ReLU(),
                nn.Dropout(0.4),
                nn.Linear(512,128),
                nn.ReLU(),
                nn.Dropout(0.4),
                nn.Linear(128,64),
                nn.ReLU(),
            )

定義age_classifiergender_classifier:

            self.age_classifier = nn.Sequential(
                nn.Linear(64, 1),
                nn.Sigmoid()
            )
            self.gender_classifier = nn.Sequential(
                nn.Linear(64, 1),
                nn.Sigmoid()
            )

上記のコードでは、年齢の出力がとの間の値であり、性別の出力がまたはであるため、年齢予測レイヤーage_classifierと性別予測レイヤーの両方でアクティベーションがgender_classifier使用されますsigmoid0101

forwardネットワーク層age_classifierを使用して、フォワード パス メソッドを定義しますgender_classifier

        def forward(self, x):
            x = self.intermediate(x)
            age = self.age_classifier(x)
            gender = self.gender_classifier(x)
            return gender, age

VGG16事前トレーニングされたモデル分類子モジュールをカスタム ネットワークに置き換えます。

    model.classifier = ageGenderClassifier()

性別分類 (バイナリクロスエントロピー損失) と年齢予測 (損失)L1のための損失関数を定義します。オプティマイザーを定義し、モデル、損失関数、オプティマイザーを返します。

    gender_criterion = nn.BCELoss()
    age_criterion = nn.L1Loss()
    loss_functions = gender_criterion, age_criterion
    optimizer = torch.optim.Adam(model.parameters(), lr= 1e-4)
    
    return model.to(device), loss_functions, optimizer

関数を呼び出してget_model()変数の値を初期化します。

model, loss_functions, optimizer = get_model()

(6)トレーニング データ セットでトレーニングし、テスト データ セットで検証する関数を定義します。train_batchこのメソッドは、画像、性別、年齢、モデル、オプティマイザー、損失関数の実際の値を入力として受け取り、総損失を計算します。

適切な入力パラメータを使用してメソッドを定義しますtrain_batch()

def train_batch(data, model, optimizer, criteria):

トレーニング モデルを指定し、オプティマイザーを にリセットしzero_grad、年齢と性別の予測値を計算します。

    model.train()
    ims, age, gender = data
    optimizer.zero_grad()
    pred_gender, pred_age = model(ims) 

年齢推定と性別分類に対応する損失を計算する前に、年齢推定と性別分類の損失関数を取得します。

    gender_criterion, age_criterion = criteria
    gender_loss = gender_criterion(pred_gender.squeeze(), gender)
    age_loss = age_criterion(pred_age.squeeze(), age)

gender_loss全体の損失はとを加算して計算されage_loss、バックプロパゲーションが実行されてモデルのトレーニング可能な重みを最適化することで全体の損失が削減されます。

    total_loss = gender_loss + age_loss
    total_loss.backward()
    optimizer.step()
    return total_loss

validate_batch()この手法は、画像、モデル、損失関数、年齢と性別の実測値を入力として、年齢と性別の予測値と損失値を計算します。

必要な入力パラメータを使用して関数を定義しますvaidate_batch

def validate_batch(data, model, criteria):

モデルが評価フェーズにあるため、勾配計算が必要ないことを指定します。

    model.eval()
    ims, age, gender = data
    with torch.no_grad():
        pred_gender, pred_age = model(ims)

年齢と性別の予測に応じた損失値(gender_loss合計)を計算しますage_loss予測された形状を圧縮して(batch size, 1)、ターゲット値と同じ形状に再形成します ( batch size)。

    gender_criterion, age_criterion = criteria
    gender_loss = gender_criterion(pred_gender.squeeze(), gender)
    age_loss = age_criterion(pred_age.squeeze(), age)

全体的な損失、最終的に予測される性別カテゴリ ( pred_gender)、性別予測精度、および年齢推定誤差を計算します。

    total_loss = gender_loss + age_loss
    pred_gender = (pred_gender > 0.5).squeeze()
    gender_acc = (pred_gender == gender).float().sum()
    age_mae = torch.abs(age - pred_age).float().sum()
    return total_loss, gender_acc, age_mae

(7)トレーニングモデル。

トレーニングおよびテストの損失値を保存するリストを定義し、トレーニングepoch番号を指定します。

model, criteria, optimizer = get_model()
val_gender_accuracies = []
val_age_maes = []
train_losses = []
val_losses = []

n_epochs = 10
best_test_loss = 1000
start = time.time()

epochトレーニングとテストのそれぞれの開始時に損失値を再初期化します。

for epoch in range(n_epochs):
    epoch_train_loss, epoch_test_loss = 0, 0
    val_age_mae, val_gender_acc, ctr = 0, 0, 0
    _n = len(train_loader)

トレーニング データ ローダー ( train_loader) を反復処理し、モデルをトレーニングします。

    for ix, data in enumerate(train_loader):
        # if ix == 100: break
        loss = train_batch(data, model, optimizer, criteria)
        epoch_train_loss += loss.item()

テスト データ ローダーを反復処理して、性別と年齢の予測精度を計算します。

    for ix, data in enumerate(test_loader):
        # if ix == 10: break
        loss, gender_acc, age_mae = validate_batch(data, model, criteria)
        epoch_test_loss += loss.item()
        val_age_mae += age_mae
        val_gender_acc += gender_acc
        ctr += len(data[0])

年齢予測と性別分類の全体的な精度を計算します。

    val_age_mae /= ctr
    val_gender_acc /= ctr
    epoch_train_loss /= len(train_loader)
    epoch_test_loss /= len(test_loader)

epochそれぞれの最後にモデルのパフォーマンス メトリックを出力します。

    elapsed = time.time()-start
    best_test_loss = min(best_test_loss, epoch_test_loss)
    print('{}/{} ({:.2f}s - {:.2f}s remaining)'.format(epoch+1, n_epochs, time.time()-start, (n_epochs-epoch)*(elapsed/(epoch+1))))
    info = f'''Epoch: {
      
      epoch+1:03d}\tTrain Loss: {
      
      epoch_train_loss:.3f}\tTest: {
      
      epoch_test_loss:.3f}\tBest Test Loss: {
      
      best_test_loss:.4f}'''
    info += f'\nGender Accuracy: {
      
      val_gender_acc*100:.2f}%\tAge MAE: {
      
      val_age_mae:.2f}\n'
    print(info)

epochテスト データセットの年齢と性別の予測精度を次のそれぞれに保存します。

    val_gender_accuracies.append(val_gender_acc)
    val_age_maes.append(val_age_mae)

(8)年齢推定と性別予測のトレーニング プロセス中の精度の変化をプロットします。

epochs = np.arange(1,len(val_gender_accuracies)+1)
fig,ax = plt.subplots(1,2,figsize=(10,5))
ax = ax.flat
ax[0].plot(epochs, val_gender_accuracies, 'bo')
ax[1].plot(epochs, val_age_maes, 'r')
ax[0].set_xlabel('Epochs')
ax[1].set_xlabel('Epochs')
ax[0].set_ylabel('Accuracy')
ax[1].set_ylabel('MAE')
ax[0].set_title('Validation Gender Accuracy')
ax[0].set_title('Validation Age Mean-Absolute-Error')
plt.show()

モデルのパフォーマンス
年齢予測に関しては、実年齢との平均差は6約2歳、性別予測に関しては精度は約2歳です84%

(9)テスト画像をランダムに選択し、画像内の登場人物の年齢と性別を予測します。

画像を取得してtrnオブジェクトのメソッドにロードしますpreprocess_image

im = cv2.imread('4.jpeg')
im = trn.preprocess_image(im).to(device)

トレーニングされたモデルに画像を渡します。

gender, age = model(im)
pred_gender = gender.to('cpu').detach().numpy()
pred_age = age.to('cpu').detach().numpy()

画像をプロットし、真の値と予測値を出力します。

im = cv2.imread('4.jpeg')
im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
plt.imshow(im)
plt.show()
print('predicted gender:',np.where(pred_gender[0][0]<0.5,'Male','Female'), '; Predicted age', int(pred_age[0][0]*80))
# predicted gender: Female ; Predicted age 26

要約すると、年齢と性別を同時に予測できることがわかります。ただし、このセクションで構築されたモデルは非常に不安定であり、年齢値は画像の照明条件によって大きく変化することに注意することが重要です。データ拡張を使用すると、モデルのパフォーマンスの向上が観察されます。

まとめ

マルチタスク学習では、複数の関連タスクを同時に処理および学習できます。実際には、複数のタスクを共同トレーニングすることで、モデルはより一般的な特徴表現を学習でき、それによって各タスクのパフォーマンスが向上します。この知識の共有この方法は、タスク固有の大量のデータの必要性を減らし、トレーニングをより効率的にすることができると同時に、タスク間の相互促進と知識の共有により、モデルがデータの本質的な構造とパターンをよりよく理解できるようになります。

シリーズリンク

PyTorch ディープラーニングの実践 (1) - ニューラル ネットワークとモデルの学習プロセスの詳細説明
PyTorch ディープラーニングの実践 (2) - PyTorch の基礎
PyTorch ディープラーニングの実践 (3) - PyTorch を使用してニューラル ネットワークを構築する
PyTorch ディープラーニングの実践 (4) -よく使われる活性化関数と損失関数について詳しく解説
PyTorch ディープラーニングの実践 (5) - コンピュータ ビジョンの基礎
PyTorch ディープラーニングの実践 (6) - ニューラル ネットワークのパフォーマンス最適化技術
PyTorch ディープラーニングの実践 (7) - バッチ サイズがニューラル ネットワークに与える影響トレーニング
PyTorch ディープラーニングの実践 (8) - バッチ正規化
PyTorch ディープラーニングの実践 (9) - 学習率の最適化
PyTorch ディープラーニングの実践 (10) - 過学習とその解決
PyTorch ディープラーニングの実践 (11) - 畳み込みニューラル ネットワーク
PyTorch ディープラーニングの実践 ( 12) - データ強化
PyTorch ディープ ラーニングの実践 (13) - ニューラル ネットワークの中間層の出力の視覚化
PyTorch ディープ ラーニングの実践 (14) - クラス アクティベーション マップ
PyTorch ディープ ラーニングの実践 (15) - —転移学習
PyTorch ディープ ラーニングの実践(16) — 顔のキーポイント検出

おすすめ

転載: blog.csdn.net/LOVEmy134611/article/details/131928480