データ前処理部分:
- データ拡張: 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 warnings.filterwarnings("ignore") 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.Resize([96, 96]), transforms.RandomRotation(45),#随机旋转,-45到45度之间随机选 transforms.CenterCrop(64),#从中心开始裁剪 transforms.RandomHorizontalFlip(p=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([64, 64]), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), }
batch_size = 128 image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'valid']} 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
image_datasets
{'train': Dataset ImageFolder データポイントの数: 6552 ルートの場所: ./flower_data/train StandardTransform Transform: Compose( Resize(size=[96, 96], interpolation=bilinear, max_size=None, antialias=None) RandomRotation(degrees ) =[-45.0, 45.0]、interpolation=nearest、expand=False、fill=0) CenterCrop(size=(64, 64)) RandomhorizontalFlip(p=0.5) RandomVerticalFlip(p=0.5) ColorJitter(brightness=[0.8, 1.2 ]、コントラスト=[0.9, 1.1]、彩度=[0.9, 1.1]、色相=[-0.1, 0.1]) RandomGrayscale(p=0.025) ToTensor() Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ) , 'valid': データセット ImageFolder データポイント数: 818 ルートの場所: ./flower_data/valid StandardTransform 変換: Compose( Resize( size=[64, 64]、interpolation=bilinear、max_size=None、antialias=None) ToTensor() Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ) }
dataloaders
{'train': <torch.utils.data.dataloader.DataLoader 0x1e4c50b9400>, 'valid': <torch.utils.data.dataloader.DataLoader 0x1e4c51ad128>}
dataset_sizes
{'列車': 6552、'有効': 818}
-
タグに対応する実際の名前を読み取ります
with open('cat_to_name.json', 'r') as f: cat_to_name = json.load(f)
cat_to_name
{'1': 'ピンクのサクラソウ'、 '10': 'グローブ アザミ'、 '100': 'ブランケットフラワー'、 '101': 'ノウゼンカズラ'、 '102': 'ブラックベリー ユリ'、 '11': 'キンギョソウ'、 '12': 'フクロタの足'、 '13': 'キングプロテア'、 '14': 'スピアザミ'、 '15': 'イエローアイリス'、 '16': 'グローブフラワー'、 '17': 'パープルコーンフラワー'、 '18': 'ペルーユリ'、 '19': 'バルーンフラワー'、 '2': '硬葉ポケットラン'、 '20': 'ジャイアントホワイトアルムユリ'、 「21」:「ファイアリリー」、 「22」:「糸巻き花」、 「23」:「ヒョウモン」、「 24」:「レッドジンジャー」、 「25」:「ブドウヒヤシンス」、 「26」:「トウモロコシ」ポピー'、 '27': 'プリンス・オブ・ウェールズの羽'、 '47 ': 'マリーゴールド'、 「28」: 「茎のないリンドウ」、 「29」: 「アーティチョーク」、 「3」: 「カンタベリーベル」、 「30」: 「スイートウィリアム」、 「31」: 「カーネーション」、 「32」: 「フロックス」 '、 '33': '霧の中の愛'、 '34': 'メキシカンアスター'、 '35': 'アルペンシーヒイラギ'、 '36': 'ルビーリップカトレア'、 '37': 'ケープフラワー' '、 '38': 'グレートマスターワート'、 '39': 'サイアムチューリップ'、 '4': 'スイートピー'、 '40': 'レンテンローズ'、 '41': 'バルベトンデイジー'、 '42' :'水仙'、 '43': 'ソードリリー'、 '44': 'ポインセチア'、 '45': 'ボレロディープブルー'、 '46': 'ウォールフラワー'、'48' : 'キンポウゲ'、 '49' : 'オセアオイ', '5': 'イングリッシュマリーゴールド'、 '50': 'タンポポ'、 '51': 'ペチュニア'、 '52': 'ワイルドパンジー'、 '53': 'サクラソウ' 、 「54」:「ひまわり」、 「55」:「ペラルゴニウム」、 「56」:「ランダフ司教」、 「57」:「ガウラ」、 「58」:「ゼラニウム」、 「59」:「オレンジダリア」 '、 '6': 'オニユリ'、 '60': 'ピンクイエローダリア'、 '61': 'カウトレヤ・スピカタ'、 '62': 'ニホンイソギンチャク'、 '63': 'クロアイドスーザン' 、 「64」: 「シルバーブッシュ」、 「65」: 「カリフォルニアポピー」、 「66」: 「オステオスペルマム 」、「67」: 「スプリングクロッカス」、 「68」: 「ヒゲアイリス」、 「69」: 「ウィンドフラワー」 、 '7': 'ムーンラン'、 '70': '木ポピー'、 「71」:「ガザニア」、 「72」:「ツツジ」、 「73」:「スイレン」、 「74」:「バラ」、 「75」:「トゲリンゴ」 、 「76」:「アサガオ」、 「77」:「パッションフラワー」、 「78」:「ハスハス」、 「79」:「ヒキガエル」、 「8」:「極楽鳥」、 「80」 : 'アンスリウム'、 '81': 'プルメリア'、 '82': 'クレマチス'、 '83': 'ハイビスカス'、 '84': 'オダマキ'、 '85': 'デザートローズ'、 '86' :'アオイ科の植物'、 '87': 'モクレン'、 '88': 'シクラメン'、 '89': 'クレソン'、 '9': 'トリカブト'、 '90': 'カンナユリ'、 '91': 'ヒッペアストラム'、 '92': 'ビーバーム'、 '93': '玉苔'、 '94': 'ジギタリス'、 '95': 'ブーゲンビリア'、 '96': 'ツバキ'、 '97': 'アオイ科'、 '98': 'メキシコペチュニア'、 '99': 'ブロメリア'}
-
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")
CUDAは利用できません。CPUのトレーニング...
-
モデルパラメータを更新しますか?
- 他の人のモデルを使うこともありますが、更新どころかずっと使っていますし、自分たちでカスタマイズすることもできます。
def set_parameter_requires_grad(model, feature_extracting): if feature_extracting: for param in model.parameters(): param.requires_grad = False
model_ft = models.resnet18()#18层的能快点,条件好点的也可以选152 model_ft
ResNet( (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3),bias=False) (bn1): BatchNorm2d(64, eps = 1e-05、momentum=0.1、affine=True、track_running_stats=True) (relu): ReLU(inplace=True) (maxpool): MaxPool2d(kernel_size=3、stride=2、padding=1、dilation=1、ceil_mode=) False) (layer1): Sequential( (0): BasicBlock( (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias= False) (bn1): BatchNorm2d(64、eps=1e-05、momentum=0.1、affine=True、track_running_stats=True) ( relu): ReLU(inplace=True) (conv2): Conv2d(64, 64, kernel_size= (3, 3)、ストライド=(1, 1)、パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(64、eps=1e- 05、勢い=0.1、アフィン=True、track_running_stats=True) ) (1): BasicBlock( (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias=False) (bn1): BatchNorm2d(64, eps= 1e- 05、momentum=0.1、affine=True、track_running_stats=True) (relu): ReLU(inplace=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1),パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(64、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ) ) ( layer2 ): Sequential( (0): BasicBlock( (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2) 、2)、パディング=(1, 1)、バイアス=False) (bn1): BatchNorm2d(128, eps=1e-05, 勢い=0.1, affine=True, track_running_stats=True) ( relu): ReLU(inplace=True) (conv2): Conv2d(128, 128, kernel_size= (3, 3)、ストライド=(1, 1)、パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(128、eps=1e-05、momentum=0.1、affine=True、track_running_stats= True) (ダウンサンプル): Sequential( (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2),bias=False) (1): BatchNorm2d(128, eps= 1e- 05、勢い = 0.1、アフィン = True、track_running_stats = True) ) ) (1): BasicBlock( (conv1): Conv2d(128, 128, kernel_size = (3, 3)、stride = (1, 1)、padding = (1, 1)、バイアス=False) (bn1): BatchNorm2d(128、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ( relu): ReLU(inplace=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias=False) ) ) (layer3): Sequential( bn2 ): BatchNorm2d(128、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ( 0 ) : BasicBlock( (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1),bias=False) (bn1): BatchNorm2d(256, eps = 1e-05、momentum=0.1、affine=True、track_running_stats=True) (relu): ReLU(inplace=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1) )、パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(256、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) (ダウンサンプル): Sequential( (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2),bias=False) (1): BatchNorm2d(256, eps=1e-05, 勢い=0.1, affine=True, track_running_stats=True) ) ( conv1 BasicBlock ( ): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1)、パディング=(1, 1)、バイアス=False) (bn1): BatchNorm2d(256、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) (relu): ReLU(inplace=True) ) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias=False) (bn2): BatchNorm2d(256, eps=1e ) -05、勢い=0.1、アフィン=True、track_running_stats=True) ) ) (layer4): Sequential( (0): BasicBlock( (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=( 2, 2)、パディング=(1, 1)、バイアス=False) (bn1): BatchNorm2d(512、eps=1e-05、momentum=0.1、affine=True、track_running_stats=True) relu): ReLU(inplace=True) (conv2): Conv2d(512, 512, kernel_size= (3, 3)、ストライド=(1, 1)、パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(512、eps=1e-05、momentum=0.1、affine=True、track_running_stats= True) (ダウンサンプル): Sequential( (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2),bias=False) (1): BatchNorm2d(512, eps= 1e- 05、勢い = 0.1、アフィン = True、track_running_stats = True) ) ) (1): BasicBlock( (conv1): Conv2d(512, 512, kernel_size = (3, 3)、stride = (1, 1)、padding = (1, 1)、バイアス=False) (bn1): BatchNorm2d(512、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ( relu): ReLU(inplace=True) (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias=False) ) ) (avgpool): AdaptiveAvgPool2d(output_size= bn2 ): BatchNorm2d(512、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ( 1 ) 、1)) (fc): Linear(in_features=512、out_features=1000、bias=True)
-
モデル出力レイヤーを独自のものに変更します
def initialize_model(model_name, num_classes, feature_extract, use_pretrained=True): model_ft = models.resnet18(pretrained=use_pretrained) set_parameter_requires_grad(model_ft, feature_extract) num_ftrs = model_ft.fc.in_features model_ft.fc = nn.Linear(num_ftrs, 102)#类别数自己根据自己任务来 input_size = 64#输入大小根据自己配置来 return model_ft, input_size
どのレイヤーをトレーニングする必要があるかを設定する
model_ft, input_size = initialize_model(model_name, 102, feature_extract, use_pretrained=True) #GPU还是CPU计算 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)
学習するパラメータ: fc.weight fc.bias
-
model_ft
ResNet( (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3),bias=False) (bn1): BatchNorm2d(64, eps = 1e-05、momentum=0.1、affine=True、track_running_stats=True) (relu): ReLU(inplace=True) (maxpool): MaxPool2d(kernel_size=3、stride=2、padding=1、dilation=1、ceil_mode=) False) (layer1): Sequential( (0): BasicBlock( (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias= False) (bn1): BatchNorm2d(64、eps=1e-05、momentum=0.1、affine=True、track_running_stats=True) ( relu): ReLU(inplace=True) (conv2): Conv2d(64, 64, kernel_size= (3, 3)、ストライド=(1, 1)、パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(64、eps=1e- 05、勢い=0.1、アフィン=True、track_running_stats=True) ) (1): BasicBlock( (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias=False) (bn1): BatchNorm2d(64, eps= 1e- 05、momentum=0.1、affine=True、track_running_stats=True) (relu): ReLU(inplace=True) (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1),パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(64、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ) ) ( layer2 ): Sequential( (0): BasicBlock( relu): ReLU(inplace=True) (conv2): Conv2d(128, 128, kernel_size= (3, 3)、ストライド=(1, 1)、パディング=(1, 1)、バイアス=False) (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2) 、2)、パディング=(1, 1)、バイアス=False) (bn1): BatchNorm2d(128、eps=1e-05、momentum=0.1、affine=True、track_running_stats=True) (bn2): BatchNorm2d(128、eps=1e-05、momentum=0.1、affine=True、track_running_stats= True) (ダウンサンプル): Sequential( (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2),bias=False) (1): BatchNorm2d(128, eps= 1e- 05、勢い = 0.1、アフィン = True、track_running_stats = True) ) ) (1): BasicBlock( (conv1): Conv2d(128, 128, kernel_size = (3, 3)、stride = (1, 1)、padding = (1, 1)、バイアス=False) (bn1): BatchNorm2d(128、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ( relu): ReLU(inplace=True) (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias=False) ) ) (layer3): Sequential( bn2 ): BatchNorm2d(128、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ( 0 ) : BasicBlock( (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1),bias=False) (bn1): BatchNorm2d(256, eps = 1e-05、momentum=0.1、affine=True、track_running_stats=True) (relu): ReLU(inplace=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1) )、パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(256、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) (ダウンサンプル): Sequential( (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2),bias=False) (1): BatchNorm2d(256、eps=1e-05、momentum=0.1、affine=True、track_running_stats=True) ) ) (1) : BasicBlock ( (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias=False) (bn1): BatchNorm2d(256, eps=1e- ) 05、momentum=0.1、affine=True、track_running_stats=True) (relu): ReLU(inplace=True) (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1),パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(256、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ) ) ( layer4): Sequential( (0): BasicBlock( (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=( 2, 2)、パディング=(1, 1)、バイアス=False) (bn1): BatchNorm2d(512、eps=1e-05、momentum=0.1、affine=True、track_running_stats=True) ( relu): ReLU(inplace=True) (conv2): Conv2d(512, 512, kernel_size= (3, 3)、ストライド=(1, 1)、パディング=(1, 1)、バイアス=False) (bn2): BatchNorm2d(512、eps=1e-05、momentum=0.1、affine=True、track_running_stats=True) (ダウンサンプル): Sequential( (0): Conv2d (256, 512, kernel_size=(1, 1), stride=(2, 2)、bias=False) (1): BatchNorm2d(512、eps=1e-05、momentum=0.1、affine=True、track_running_stats=True) ) ) (1) : BasicBlock ( (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1),bias=False) (bn1): BatchNorm2d(512, eps=1e-05,勢い = 0.1、affine=True、track_running_stats=True) relu): ReLU(inplace=True) (conv2): Conv2d(512, 512、kernel_size=(3, 3)、stride=(1, 1)、padding=(1, 1)、bias=False) ( bn2 ): BatchNorm2d(512、eps=1e-05、勢い=0.1、affine=True、track_running_stats=True) ( 1 ) 、1)) (fc): Linear(in_features=512、out_features=102、bias=True) )
-
オプティマイザー設定
# 优化器设置 optimizer_ft = optim.Adam(params_to_update, lr=1e-2)#要训练啥参数,你来定 scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=10, gamma=0.1)#学习率每7个epoch衰减成原来的1/10 criterion = nn.CrossEntropyLoss()
トレーニングモジュール
def train_model(model, dataloaders, criterion, optimizer, num_epochs=25,filename='best.pt'): #咱们要算时间的 since = time.time() #也要记录最好的那一次 best_acc = 0 #模型也得放到你的CPU或者GPU 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()) #一个个epoch来遍历 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)#放到你的CPU或GPU labels = labels.to(device) # 清零 optimizer.zero_grad() # 只有训练的时候计算和更新梯度 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)#0表示batch那个维度 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#一个epoch我浪费了多少时间 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(),#字典里key就是各层的名字,值就是训练好的权重 '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() scheduler.step()#学习率衰减 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
トレーニングを始めましょう!
- これで出力層のトレーニングのみが完了しました。
model_ft, val_acc_history, train_acc_history, valid_losses, train_losses, LRs = train_model(model_ft, dataloaders, criterion, optimizer_ft, num_epochs=20)
エポック 0/19 ---------- 経過時間 0 分 39 秒 列車損失: 4.0874 検出数: 0.2355 経過時間 0 分 43 秒 有効損失: 3.5746 検出数: 0.2531 オプティマイザ学習率 : 0.0100000 エポック 1/19 ----- ----- 経過時間 1 分 22 秒の 列車 損失: 2.8185 一致度: 0.3953 経過時間 1 分 26 秒 有効 損失: 3.5450 一致度: 0.3142 オプティマイザの学習率 : 0.0100000 エポック 2/19 ---------- 経過時間 2 分 5 秒 列車の損失: 2.7673 Acc: 0.4174 経過時間 2 分 9 秒 有効 損失: 3.9110 Acc: 0.2653 オプティマイザの学習率 : 0.0100000 エポック 3/19 ---------- 経過時間 2 分 48 秒 列車の損失: 2.7962 Acc: 0.4255 経過時間 2 分 52 秒 有効 損失: 3.6922 Acc: 0.3142 オプティマイザーの学習率 : 0.0100000 エポック 4/19 ---------- 経過時間 3 分 32 秒の 列車の損失: 2.7453 Acc: 0.4428 経過時間3 分 36 秒 有効損失: 3.9310 有効損失: 0.3044 オプティマイザ学習率 : 0.0100000 エポック 5/19 ---------- 経過時間 4 分 14 秒の 列車損失: 2.2935 有効損失: 0.5043 経過時間 4 分 18 秒 有効損失: 3.3299 有効損失: 0.3435 オプティマイザーの学習率 : 0.0010000 エポック 6/19 ---------- 経過時間 4 分 57 秒 列車損失: 2.0654 ACC: 0.5258 経過時間 5 分 1 秒 有効損失: 3.2608 ACC: 0.3411 オプティマイザ学習率 : 0.0010000 Epoch 7/19 ---------- 経過時間 5 分 40 秒 列車損失: 1.9603 Acc: 0.5369 経過時間 5 分 44 秒 有効損失: 3.2618 Acc: 0.3472 オプティマイザ学習率 : 0.0010000 エポック 8 /19 ---------- 経過時間 6 分 23 秒 列車損失: 1.9216 精度: 0.5401 経過時間 6 分 27 秒 有効損失: 3.1651 精度: 0.3386 オプティマイザ学習率: 0.0010000 エポック 9/19 ------- --- 経過時間 7 分 5 秒の 列車損失: 1.9203 有効損失: 0.5458 経過時間 7 分 9 秒 有効損失: 3.0449 有効損失: 0.3680 オプティマイザ学習率 : 0.0010000 エポック 10/19 ---------- 経過時間 7 分 48 秒 train Loss: 1.8366 Acc: 0.5553 経過時間 7 分 52 秒 有効損失: 3.0722 有効損失: 0.3545 オプティマイザ学習率 : 0.0001000 エポック 11/19 ---------- 経過時間 8 分 31 秒の 列車損失: 1.8324 有効損失: 0.5546 経過時間8 分 35 秒 有効損失: 3.0115 有効損失: 0.3643 オプティマイザーの学習率 : 0.0001000 エポック 12/19 ---------- 経過時間 9 分 13 秒の 列車損失: 1.8054 ACC: 0.5553 経過時間 9 分 17 秒 の有効損失: 3.0688 ACC: 0.3619 オプティマイザーの学習率 : 0.0001000 エポック13 /19 ---------- 経過時間 9 分 56 秒の 列車 損失: 1.8436 一致度: 0.5534 オプティマイザ学習率 : 0.0001000 経過時間 10 分 0 秒 有効損失: 3.0100 Acc: 0.3631 Epoch 14/19 ---------- 経過時間 10 分 39 秒 列車損失: 1.7417 Acc: 0.5614 経過時間 10 分 43 秒 有効損失: 3.0129 Acc: 0.3655 オプティマイザ学習率 : 0.0001000 エポック 15 /19 ---------- 経過時間 11 分 22 秒の 列車 損失: 1.7610 一致度: 0.5672 経過時間 11 分 26 秒 有効な損失: 3.0220 一致度: 0.3606 オプティマイザーの学習率 : 0.0000100 エポック 16/19 ------- --- 経過時間 12 分 6 秒の 列車 損失: 1.7788 精度: 0.5676 経過時間 12 分 10 秒 有効損失: 3.0104 精度: 0.3557 オプティマイザーの学習率 : 0.0000100 エポック 17/19 ---------- 経過時間 12 分 49 秒 列車の損失: 1.8033 Acc: 0.5638 経過時間 12 分 53 秒 有効な損失: 3.0428 Acc: 0.3606 オプティマイザの学習率 : 0.0000100 Epoch 18/19 ---------- 経過時間 13 分 33 秒 列車の損失: 1.8294 Acc: 0.5568 経過時間13 分 37 秒 有効損失: 3.0307 有効損失: 0.3509 オプティマイザ学習率 : 0.0000100 エポック 19/19 ---------- 経過時間 14 分 16 秒 列車損失: 1.7949 有効損失: 0.5612 経過時間 14 分 20 秒 有効損失: 3.0396 有効損失: 0.364 3 オプティマイザーの学習率 : 0.0000100 トレーニングは 14 分 20 秒で完了 最良の値 Acc: 0.367971
-
その後、すべてのレイヤーのトレーニングを続行します
for param in model_ft.parameters(): param.requires_grad = True # 再继续训练所有的参数,学习率调小一点 optimizer = optim.Adam(model_ft.parameters(), lr=1e-3) scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1) # 损失函数 criterion = nn.CrossEntropyLoss()
# 加载之前训练好的权重参数 checkpoint = torch.load(filename) best_acc = checkpoint['best_acc'] model_ft.load_state_dict(checkpoint['state_dict'])
model_ft, val_acc_history, train_acc_history, valid_losses, train_losses, LRs = train_model(model_ft, dataloaders, criterion, optimizer, num_epochs=10,)
エポック 0/9 ---------- 経過時間 1 分 32 秒の 列車損失: 2.2451 一致度: 0.4846 経過時間 1 分 36 秒 有効な損失: 2.3190 一致度: 0.4633 オプティマイザ学習率 : 0.0010000 エポック 1/9 ----- ----- 経過時間 2 分 54 秒 列車 損失: 1.2920 一致度: 0.6505 経過時間 2 分 58 秒 有効 損失: 2.2263 一致度: 0.4670 オプティマイザー学習率 : 0.0010000 エポック 2/9 ---------- 経過時間 4 分 15 秒 train Loss: 1.1026 Acc: 0.6993 経過時間 4 分 19 秒 有効な Loss: 1.8115 Acc: 0.5452 Optimizer 学習率 : 0.0010000 Epoch 3/9 ---------- 経過時間 5 分 35 秒 train Loss: 0.9062 Acc: 0.7515 経過時間 5 分 39 秒 有効 損失: 2.0045 Acc: 0.5403 オプティマイザ学習率 : 0.0010000 Epoch 4/9 ---------- 経過時間 6 分 56 秒 train loss: 0.8392 Acc: 0.7643 経過時間7 分 0 秒 有効損失: 2.1381 有効損失: 0.5171 オプティマイザ学習率 : 0.0010000 エポック 5/9 ---------- 経過時間 8 分 17 秒 列車損失: 0.7081 有効損失: 0.7953 経過時間 8 分 21 秒 有効損失: 2.0461 有効損失: 0.5599 オプティマイザーの学習率 : 0.0010000 エポック 6/9 ---------- 経過時間 9 分 38 秒 列車損失: 0.6400 ACC: 0.8147 経過時間 9 分 42 秒 有効損失: 2.2603 精度: 0.5452 トレーニングは 13 分 45 秒で完了しました オプティマイザの学習率 : 0.0010000 エポック 7/9 ---------- 経過時間 10 分 59 秒 列車損失: 0.6406 加速度: 0.8117 経過時間 11 分 3 秒 有効損失: 1.4649 加速度: 0.6406 オプティマイザ学習率 : 0.0010000 エポック 8/9 ----- ----- 経過時間 12 分 20 秒 列車 損失: 0.5686 精度: 0.8300 経過時間 12 分 24 秒 有効損失: 1.7538 精度: 0.6100 オプティマイザ学習率 : 0.0010000 エポック 9/9 ---------- 経過時間 13 分 41 秒 train Loss: 0.5978 Acc: 0.8245 経過時間 13 分 45 秒 有効な Loss: 1.6953 Acc: 0.6161 オプティマイザーの学習率 : 0.0010000 Best val Acc: 0.640587
-
トレーニング済みモデルをロードする
model_ft, input_size = initialize_model(model_name, 102, feature_extract, use_pretrained=True) # GPU模式 model_ft = model_ft.to(device) # 保存文件的名字 filename='best.pt' # 加载模型 checkpoint = torch.load(filename) best_acc = checkpoint['best_acc'] model_ft.load_state_dict(checkpoint['state_dict'])
テストデータの前処理
- テストデータの処理方法はトレーニング時と同じにする必要があります。
- トリミング操作の目的は、入力サイズの一貫性を確保することです。
- 標準化操作も必要です。トレーニング データと同じ平均値と標準偏差を使用します。ただし、トレーニング データは 0 ~ 1 で標準化されているため、最初にテスト データも正規化する必要があることに注意してください。
- 最後に、PyTorch のカラー チャネルは最初の次元であり、多くのツールキットとは異なり、変換する必要があります。
-
# 得到一个batch的测试数据 dataiter = iter(dataloaders['valid']) images, labels = dataiter.next() model_ft.eval() if train_on_gpu: output = model_ft(images.cuda()) else: output = model_ft(images)
出力は、バッチ内の各データが各カテゴリに属する確率を表します。
output.shape
最も確率の高いものを入手する
_, preds_tensor = torch.max(output, 1) preds = np.squeeze(preds_tensor.numpy()) if not train_on_gpu else np.squeeze(preds_tensor.cpu().numpy()) preds
array([ 34, 49, 43, 54, 20, 14, 49, 43, 50, 20, 19, 100, 78, 96, 96, 62, 62, 63, 32, 38, 82, 43, 88, 73, 6, 51, 43, 89, 55, 75, 55, 11, 46, 82, 48, 82, 20, 100, 48, 20, 24, 49, 76, 93, 49, 46, 90, 75, 89, 75, 76, 99, 56, 48, 77, 66, 60, 72, 89, 97, 76, 73, 17, 48, 39, 31, 19, 74, 61, 46, 93, 80, 27, 11, 91, 18, 23, 47, 29, 54, 18, 93, 1, 50, 79, 96, 39, 53, 63, 60, 49, 23, 23, 52, 99, 89, 3, 50, 64, 15, 19, 60, 19, 75, 50, 78, 82, 18, 75, 18, 82, 53, 3, 52, 60, 38, 62, 47, 21, 59, 81, 48, 89, 64, 60, 55, 100, 60], dtype=int64)
予測結果を表示する
def im_convert(tensor): """ 展示数据""" image = tensor.to("cpu").clone().detach() image = image.numpy().squeeze() image = image.transpose(1,2,0) 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, 20)) columns =4 rows = 2 for idx in range (columns*rows): ax = fig.add_subplot(rows, columns, idx+1, xticks=[], yticks=[]) plt.imshow(im_convert(images[idx])) ax.set_title("{} ({})".format(cat_to_name[str(preds[idx])], cat_to_name[str(labels[idx].item())]), color=("green" if cat_to_name[str(preds[idx])]==cat_to_name[str(labels[idx].item())] else "red")) plt.show()