pytoch finetuning自分の写真を訓練します。


一、pytouch finetuning自分の写真を訓練します。
このような画像の読み取り方法は、tochが持参するImageFolderであり、読み取ったフォルダは大きなサブファイルの下で、カテゴリ別に分類される必要があります。
私は今三つの種類を区別します。

#perpare data set
#train data
train_data=torchvision.datasets.ImageFolder('F:/eyeDataSet/trainData',transform=transforms.Compose(
         [
            transforms.Scale(256),
            transforms.CenterCrop(224),
            transforms.ToTensor()
       ]))
print(len(train_data))
train_loader=DataLoader(train_data,batch_size=20,shuffle=True)
それからfine tuning自身のネットワークです。touchではネット全体を修正して、全部のパラメータを訓練してもいいです。その中の一部だけを訓練してもいいです。ここでは最後の全接続層だけを訓練します。
tochvisionの中で多くの常用する模型を提供しました。例えば、renet、Vgg、Alexnetなどです。

# prepare model
mode1_ft_res18=torchvision.models.resnet18(pretrained=True)
for param in mode1_ft_res18.parameters():
    param.requires_grad=False
num_fc=mode1_ft_res18.fc.in_features
mode1_ft_res18.fc=torch.nn.Linear(num_fc,3)
自分の最適化器を定義して、ここのパラメータは最後の階の

#loss function and optimizer
criterion=torch.nn.CrossEntropyLoss()
#parameters only train the last fc layer
optimizer=torch.optim.Adam(mode1_ft_res18.fc.parameters(),lr=0.001)
その後、トレーニングを開始し、各種パラメータを定義します。

#start train
#label  not  one-hot encoder
EPOCH=1
for epoch in range(EPOCH):
    train_loss=0.
    train_acc=0.
    for step,data in enumerate(train_loader):
        batch_x,batch_y=data
        batch_x,batch_y=Variable(batch_x),Variable(batch_y)
        #batch_y not one hot
        #out is the probability of eatch class
        # such as one sample[-1.1009  0.1411  0.0320],need to calculate the max index
        # out shape is batch_size * class
        out=mode1_ft_res18(batch_x)
        loss=criterion(out,batch_y)
        train_loss+=loss.data[0]
        # pred is the expect class
        #batch_y is the true label
        pred=torch.max(out,1)[1]
        train_correct=(pred==batch_y).sum()
        train_acc+=train_correct.data[0]
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if step%14==0:
            print('Epoch: ',epoch,'Step',step,
                  'Train_loss: ',train_loss/((step+1)*20),'Train acc: ',train_acc/((step+1)*20))
テスト部分とトレーニング部分は似ていますが、ここではいちいち説明しません。
このように自分のネットに対するトレーニングテストが完全になりました。完全なコードは以下の通りです。

import torch
import numpy as np
import torchvision
from torchvision import transforms,utils
from torch.utils.data import DataLoader
from torch.autograd import Variable
#perpare data set
#train data
train_data=torchvision.datasets.ImageFolder('F:/eyeDataSet/trainData',transform=transforms.Compose(
           [
               transforms.Scale(256),
               transforms.CenterCrop(224),
               transforms.ToTensor()
         ]))
print(len(train_data))
train_loader=DataLoader(train_data,batch_size=20,shuffle=True)
 
#test data
test_data=torchvision.datasets.ImageFolder('F:/eyeDataSet/testData',transform=transforms.Compose(
           [
         transforms.Scale(256),
         transforms.CenterCrop(224),
         transforms.ToTensor()
         ]))
test_loader=DataLoader(test_data,batch_size=20,shuffle=True)
 
# prepare model
mode1_ft_res18=torchvision.models.resnet18(pretrained=True)
for param in mode1_ft_res18.parameters():
    param.requires_grad=False
num_fc=mode1_ft_res18.fc.in_features
mode1_ft_res18.fc=torch.nn.Linear(num_fc,3)
 
#loss function and optimizer
criterion=torch.nn.CrossEntropyLoss()
#parameters only train the last fc layer
optimizer=torch.optim.Adam(mode1_ft_res18.fc.parameters(),lr=0.001)
 
#start train
#label  not  one-hot encoder
EPOCH=1
for epoch in range(EPOCH):
    train_loss=0.
    train_acc=0.
    for step,data in enumerate(train_loader):
        batch_x,batch_y=data
        batch_x,batch_y=Variable(batch_x),Variable(batch_y)
        #batch_y not one hot
        #out is the probability of eatch class
        # such as one sample[-1.1009  0.1411  0.0320],need to calculate the max index
        # out shape is batch_size * class
        out=mode1_ft_res18(batch_x)
        loss=criterion(out,batch_y)
        train_loss+=loss.data[0]
        # pred is the expect class
        #batch_y is the true label
        pred=torch.max(out,1)[1]
        train_correct=(pred==batch_y).sum()
        train_acc+=train_correct.data[0]
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if step%14==0:
            print('Epoch: ',epoch,'Step',step,
                  'Train_loss: ',train_loss/((step+1)*20),'Train acc: ',train_acc/((step+1)*20))
 
    #print('Epoch: ', epoch, 'Train_loss: ', train_loss / len(train_data), 'Train acc: ', train_acc / len(train_data))
 
# test model
mode1_ft_res18.eval()
eval_loss=0
eval_acc=0
for step ,data in enumerate(test_loader):
    batch_x,batch_y=data
    batch_x,batch_y=Variable(batch_x),Variable(batch_y)
    out=mode1_ft_res18(batch_x)
    loss = criterion(out, batch_y)
    eval_loss += loss.data[0]
    # pred is the expect class
    # batch_y is the true label
    pred = torch.max(out, 1)[1]
    test_correct = (pred == batch_y).sum()
    eval_acc += test_correct.data[0]
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
print( 'Test_loss: ', eval_loss / len(test_data), 'Test acc: ', eval_acc / len(test_data))
二、PyTorchはプリトレーニングモデルを利用してFine-tuningを行う。
Deep Learningの分野では、多くのサブ分野の応用、例えば動物の識別、食品の識別など、公開された利用可能なデータベースはImageNetなどのデータベースに対して、その規模が小さすぎて、深さネットワークモデルを利用して直接trin from scratchを利用できなくなり、フィットを引き起こしやすくなります。この時、大規模なデータベース上ですでに訓練済みのモデルを持ってきて、ターゲットデータベース上で直接Fine-tuning(微調整)を行う必要があります。これはすでに訓練されたモデルは目標データセットにとって、比較的良いパラメータ初期化方法です。特にビッグデータセットはターゲットデータセット構造と比較的に似ています。ターゲットデータセットに微調整することで、良い効果が得られます。
Fine-tune事前トレーニングネットワークのステップ:
1.まず事前訓練モデルの分類層の全接続層数を変更します。一般目標データセットのカテゴリ数と大規模データベースのカテゴリ数が一致しないため、目標データセット上のトレーニングセットのカテゴリ数に変更すればいいです。一致すれば変更する必要がありません。
2.分類器の前のネットワークのすべての層のパラメータを固定して、つまり勉強に参加させないで、逆方向に伝播しないで、分類層のネットワークだけを訓練しています。この時、学習率が設定できる大きな点は、元の初期学習率の10倍または数倍または0.01などです。この時、ネットワークトレーニングのほうが速いです。分類層を除いて、他の層は逆方向に伝播する必要がありません。学習率の設定をいろいろ試してみてもいいです。
3.次は比較的小さい学習率を設定して、ネットワーク全体を訓練すると、ネットワークトレーニングが遅くなります。
PyTorch深さ学習フレームワークFine-tuneプリトレーニングネットワークを利用する過程で関与する固定学習可能パラメータについて、異なる層に異なる学習率を設定するなどの詳細を説明します。
1.PyTorchは、ある階層の固定ネットワークに関する学習可能なパラメータの方法:

class Net(nn.Module):
    def __init__(self, num_classes=546):
        super(Net, self).__init__()
        self.features = nn.Sequential(
 
            nn.Conv2d(1, 64, kernel_size=3, stride=2, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
 
            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
        )
 
        self.Conv1_1 = nn.Sequential(
 
            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
 
            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
        )
 
  for p in self.parameters():
            p.requires_grad=False
        self.Conv1_2 = nn.Sequential(
 
            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
 
            nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(64),
        )
上記のコードのように、モデルNetネットワークにおいてself.feature sとself.Coonv 1_1階のパラメータは固定されていて、勉強できません。これは主にコードを見ます。

for p in self.parameters():
    p.requires_grad=False
挿入された位置、このコードの前のすべての層のパラメータは勉強できません。逆伝搬プロセスもありません。ある階のパラメータを指定しても学習できません。

for p in  self.features.parameters():
    p.requires_grad=False
self.feature層のすべてのパラメータは勉強できません。
なお、上記のコード設定が実際に有効になる場合は、ネットワークを訓練する際に、以下のように最適化器を設置する必要があります。

 optimizer = torch.optim.SGD(filter(lambda p: p.requires_grad, model.parameters()), args.lr,
        momentum=args.momentum,
        weight_decay=args.weight_decay)
2.PyTorchは異なる層に学習率を設定します。

model = Net()
conv1_2_params = list(map(id, model.Conv1_2.parameters()))
base_params = filter(lambda p: id(p) not in conv1_2_params,
                     model.parameters())
optimizer = torch.optim.SGD([
            {'params': base_params},
            {'params': model.Conv1_2.parameters(), 'lr': 10 * args.lr}], args.lr,             
            momentum=args.momentum, weight_decay=args.weight_decay)
上記のコードは、モデルNetネットワークのself.Conv 1_を表しています。2階の学習率は着信学習率の10倍に設定されています。パラmsの学習が明確に設定されていない場合、デフォルトは着信学習率args.lrです。
注意:

[{'params': base_params}, {'params': model.Conv1_2.parameters(), 'lr': 10 * args.lr}]
リスト内の辞書構造として表示されます。
この方法は学習率の違いによって柔軟さが足りなくなり、異なる層に柔軟な学習率を設定することができます。learning_rate関数で設定:

def adjust_learning_rate(optimizer, epoch, args):
    lre = []
    lre.extend([0.01] * 10)
    lre.extend([0.005] * 10)
    lre.extend([0.0025] * 10)
    lr = lre[epoch]
    optimizer.param_groups[0]['lr'] = 0.9 * lr
    optimizer.param_groups[1]['lr'] = 10 * lr
    print(param_group[0]['lr'])
    print(param_group[1]['lr'])
上記のコードの中のoptimizer.param_groups[0]は[{'params':base_]を表します。パラms、{'params':model.Conv 1_2.parameters()、'lr':10*args.lr]]の中の'params':base_パラms、optimizer.param_groups[1]代表{'params':model.Conv 1_2.parameters()、'lr':10*args.lr}ここで設定した学習率はargs.lrを上書きします。個人的には上記のコードは学習率を設定する上でより柔軟なものだと思います。上記のコードは、以下のように実現されてもよい(学習率が適当に設定されていて、上記のコードと一致していないことに注意して):

def adjust_learning_rate(optimizer, epoch, args):
    lre = np.logspace(-2, -4, 40)
    lr = lre[epoch]
    for i in range(len(optimizer.param_groups)):
        param_group = optimizer.param_groups[i]
        if i == 0:
            param_group['lr'] = 0.9 * lr
        else:
            param_group['lr'] = 10 * lr
        print(param_group['lr'])
以下にSGD最適化器のPyTorchを貼り付けて実現し、その各パラメータの設定と意味を表します。具体的には以下の通りです。

import torch
from .optimizer import Optimizer, required
 
class SGD(Optimizer):
    r"""Implements stochastic gradient descent (optionally with momentum).
    Nesterov momentum is based on the formula from
    `On the importance of initialization and momentum in deep learning`__.
    Args:
        params (iterable): iterable of parameters to optimize or dicts defining
            parameter groups
        lr (float): learning rate
        momentum (float, optional): momentum factor (default: 0)
        weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
        dampening (float, optional): dampening for momentum (default: 0)
        nesterov (bool, optional): enables Nesterov momentum (default: False)
    Example:
        >>> optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
        >>> optimizer.zero_grad()
        >>> loss_fn(model(input), target).backward()
        >>> optimizer.step()
    __ http://www.cs.toronto.edu/%7Ehinton/absps/momentum.pdf
    .. note::
        The implementation of SGD with Momentum/Nesterov subtly differs from
        Sutskever et. al. and implementations in some other frameworks.
        Considering the specific case of Momentum, the update can be written as
        .. math::
                  v = \rho * v + g \\
                  p = p - lr * v
        where p, g, v and :math:`\rho` denote the parameters, gradient,
        velocity, and momentum respectively.
        This is in contrast to Sutskever et. al. and
        other frameworks which employ an update of the form
        .. math::
             v = \rho * v + lr * g \\
             p = p - v
        The Nesterov version is analogously modified.
    """
 
    def __init__(self, params, lr=required, momentum=0, dampening=0,
                 weight_decay=0, nesterov=False):
        if lr is not required and lr < 0.0:
            raise ValueError("Invalid learning rate: {}".format(lr))
        if momentum < 0.0:
            raise ValueError("Invalid momentum value: {}".format(momentum))
        if weight_decay < 0.0:
            raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
 
        defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
                        weight_decay=weight_decay, nesterov=nesterov)
        if nesterov and (momentum <= 0 or dampening != 0):
            raise ValueError("Nesterov momentum requires a momentum and zero dampening")
        super(SGD, self).__init__(params, defaults)
 
    def __setstate__(self, state):
        super(SGD, self).__setstate__(state)
        for group in self.param_groups:
            group.setdefault('nesterov', False)
 
    def step(self, closure=None):
        """Performs a single optimization step.
        Arguments:
            closure (callable, optional): A closure that reevaluates the model
                and returns the loss.
        """
        loss = None
        if closure is not None:
            loss = closure()
 
        for group in self.param_groups:
            weight_decay = group['weight_decay']
            momentum = group['momentum']
            dampening = group['dampening']
            nesterov = group['nesterov']
 
            for p in group['params']:
                if p.grad is None:
                    continue
                d_p = p.grad.data
                if weight_decay != 0:
                    d_p.add_(weight_decay, p.data)
                if momentum != 0:
                    param_state = self.state[p]
                    if 'momentum_buffer' not in param_state:
                        buf = param_state['momentum_buffer'] = torch.zeros_like(p.data)
                        buf.mul_(momentum).add_(d_p)
                    else:
                        buf = param_state['momentum_buffer']
                        buf.mul_(momentum).add_(1 - dampening, d_p)
                    if nesterov:
                        d_p = d_p.add(momentum, buf)
                    else:
                        d_p = buf
 
                p.data.add_(-group['lr'], d_p)
 
        return loss
経験のまとめ:
Fine-tuningでは、層のパラメータの学習可能かどうかを間仕切りしない方がいいです。このように一般的な効果餅は理想的ではないです。つまり、まずFine-tuningの分類層で、学習率の設定が大きい方がいいです。そして、ネットワーク全体に小さな学習率を設定して、すべての層で一緒に訓練します。
Fine-tuneの分類層を経由しないで、ネットワーク全体の層を一緒に訓練して、ただ分類層の学習率が比較的に大きく設定してもいいです。どの効果がいいかは評価したことがありません。3元のグループの損失(triplet loss)でソフトマックスのlossトレーニングのネットワークを微調整すると、階段型の小学校の習度を設定して、ネットワーク全体の層で一緒に訓練します。効果はいいです。まずFine-tuneの分類層の前の層の出力を使わないでください。
以上は個人の経験ですので、参考にしていただければと思います。