torchvision に基づく CV 転移学習

以前に cifar10 を使用しました。モデルのサイズが大きいため、より複雑なデータ セットを理解できるため、ここでは cifar100 と呼ばれるより複雑なデータ セットを使用します。名前が示すとおり、これは 100 カテゴリの画像データ セットです。データの機密性が高まり、より複雑になります。

データセットを定義する

import torchvision
import torch


#定义数据集
class Dataset(torch.utils.data.Dataset):

    def __init__(self, train):

        #在线加载数据集
        #更多数据集:https://pytorch.org/vision/stable/datasets.html
        self.data = torchvision.datasets.CIFAR100(root='data',
                                                  train=train,
                                                  download=True)

        #更多数据增强:https://pytorch.org/vision/stable/transforms.html
        self.compose = torchvision.transforms.Compose([

            #原本是32*32的,缩放到300*300,这是为了适应预训练模型的习惯,便于它抽取图像特征
            torchvision.transforms.Resize(300),

            #随机左右翻转,这是一种图像增强,很显然,左右翻转不影响图像的分类结果
            torchvision.transforms.RandomHorizontalFlip(p=0.5),

            #图像转矩阵数据,值域是0-1之间
            torchvision.transforms.ToTensor(),

            #让图像的3个通道的数据分别服从3个正态分布,这3分数据是从一个大的数据集上统计得出的
            #投影也是为了适应预训练模型的习惯
            torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                             std=[0.229, 0.224, 0.225]),
        ])

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

    def __getitem__(self, i):
        #取数据
        x, y = self.data[i]

        #应用compose,图像转数据
        x = self.compose(x)

        return x, y


dataset = Dataset(train=True)

x, y = dataset[0]

print(len(dataset), x.shape, y)

ここでは、オンラインでデータセットをロードできる torchvision のデータセットのロード方法を使用するため、上記のコメントの接続を通じてより多くのデータセットを取得できます。root='data' は、ダウンロードされたデータ セットをローカル ディスクに保存するパス、つまりデータ キャッシュの場所を指します。次のパラメータ トレインの値は、データのトレーニングを指すブール値です。ダウンロードされるセット この部分はまだテスト部分です。トレーニング セットの両方の部分が必要であるため、ここでは変数が使用されます。

compose 変数は、torchvision によって提供されるもう 1 つの関数であり、画像のデータ拡張です。特定の方法は、コメント内のリンクからも見つけることができます。以下に、一般的に使用されるものを示します。最初の 1 つはサイズ変更です。画像のズーム。元は 32x32 でしたが、ここでは 300x300 に均一にスケーリングされています。これは、事前トレーニング モデルの習慣に適応し、事前トレーニング モデルによる画像特徴の抽出を容易にするためです。トレーニング モデルは、トレーニングに 300x300 の画像を使用します。2 番目のアプリケーションの画像強調はランダムな左右反転であり、反転確率は 0.5 に設定されていますが、cifar100 データ セットの場合、左右反転は画像の分類結果に影響を与えません。さらに、このデータ強調はデータセットをより豊富にするためです。ツール クラス ToTensor を使用してイメージを行列に変換します。値の範囲は 0 ~ 1 です。最後に、データを正規化します。つまり、画像データの 3 つのチャネルを 3 つの正規分布に従わせ、3 つの正規分布の平均と標準偏差をその上に書き込みます。

getitem 関数は毎回データのバッチを取得し、この画像に合成を適用して画像を強化し、画像をデータに変換します。

ローダーを定義する

#每次从loader获取一批数据时回回调,可以在这里做一些数据整理的工作
#这里写的只是个例子,事实上这个回调函数什么也没干..
def collate_fn(data):
    #取数据
    x = [i[0] for i in data]
    y = [i[1] for i in data]

    #比如可以手动转换数据格式
    x = torch.stack(x)
    y = torch.LongTensor(y)

    return x, y


#数据加载器
loader = torch.utils.data.DataLoader(dataset=dataset,
                                     batch_size=8,
                                     shuffle=True,
                                     drop_last=True,
                                     collate_fn=collate_fn)

x, y = next(iter(loader))

print(len(loader), x.shape, y)

ローダーのコードについては何も言うことはありません。唯一言及すべきことは、collat​​e_fn です。この関数は、ローダーからデータのバッチがフェッチされるたびにコールバックされるため、この関数内でデータの照合を行うことができます。

(6250, torch.Size([8, 3, 300, 300]), tensor([50, 54, 98, 51, 77, 96, 72, 81]))

明らかに、x は 8 つのイメージ、y は 8 つの整数、値は 0 ~ 100 です。

転移学習

一般的なモデルの最初の部分では、データを読み込み、次にレイヤーごとに特徴を抽出し、最後にデータをベクトルに抽出し、それを完全に接続されたニューラル ネットワークに入れて分類し、次に訓練されたニューラル ネットワークに入れます。ネットワーク モデルに関しては、層の多くは実際に再利用できます。たとえば、ここでのモデルは回帰の結果ですが、回帰したくない場合はどうすればよいでしょうか。それは非常に単純です。最後の層を切り取り、次に 3 つの新しい層を再接続し、これら 3 つの層の間で分類または回帰を行うかどうかを決定します。つまり、前のレイヤーをトレーニングしないか、これらのレイヤーは基本的にトレーニング済みであるため、再トレーニングしたとしても難易度は低くなります。

これは移行学習です。その中心的なアイデアは、以前にトレーニングされたモデルと、その一部の層 (特に浅い層) のパラメータを再利用することです。これらの層は画像データの特徴抽出を担当するためです。私の新しいモデルでは、次のことができます。データ特徴抽出の作業も行っているため、再利用されます。

モデルを定義する

前に述べたように、事前トレーニングされたモデルが必要であり、この作業を完了するには torchvision を使用します。その中で、多くの事前トレーニングされたモデルが提供されます。リンクにはその他のオプションもあります。 torchvision 、モデルを再組み立てします。ここでは特徴部分のみが必要で、後でそれを完全に接続された出力層に接続します。その後、分類するか返すかを決定できます。

class Model(torch.nn.Module):

    def __init__(self):
        super().__init__()

        #加载预训练模型
        #更多模型:https://pytorch.org/vision/stable/models.html#table-of-all-available-classification-weights
        pretrained = torchvision.models.efficientnet_v2_s(
            weights=torchvision.models.EfficientNet_V2_S_Weights.IMAGENET1K_V1)

        #重新组装模型,只要特征抽取部分
        pretrained = torch.nn.Sequential(
            pretrained.features,
            pretrained.avgpool,
            torch.nn.Flatten(start_dim=1),
        )

        #锁定参数,不训练
        for param in pretrained.parameters():
            param.requires_grad_(False)

        pretrained.eval()
        self.pretrained = pretrained

        #线性输出层,这部分是要重新训练的
        self.fc = torch.nn.Sequential(
            torch.nn.Linear(1280, 256),
            torch.nn.ReLU(),
            torch.nn.Linear(256, 256),
            torch.nn.ReLU(),
            torch.nn.Linear(256, 100),
        )

    def forward(self, x):
        #调用预训练模型抽取参数,因为预训练模型是不训练的,所以这里不需要计算梯度
        with torch.no_grad():
            #[8, 3, 300, 300] -> [8, 1280]
            x = self.pretrained(x)

        #计算线性输出
        #[8, 1280] -> [8, 100]
        return self.fc(x)


model = Model()

x = torch.randn(8, 3, 300, 300)

print(model.pretrained(x).shape, model(x).shape)

モデルトレーニング

#训练
def train():
    #注意这里的参数列表,只包括要训练的参数即可
    optimizer = torch.optim.Adam(model.fc.parameters(), lr=1e-3)
    loss_fun = torch.nn.CrossEntropyLoss()
    model.fc.train()

    #定义计算设备,优先使用gpu
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    model.to(device)

    print('device=', device)

    for i, (x, y) in enumerate(loader):
        #如果使用gpu,数据要搬运到显存里
        x = x.to(device)
        y = y.to(device)

        out = model(x)
        loss = loss_fun(out, y)

        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

        if i % 500 == 0:
            acc = (out.argmax(dim=1) == y).sum().item() / len(y)
            print(i, loss.item(), acc)

    #保存模型,只保存训练的部分即可
    torch.save(model.fc.to('cpu'), 'model/8.model')

デバイスの定義は通常、GPU がある場合は GPU を使用して計算します。

テスト

@torch.no_grad()
def test():

    #加载保存的模型
    model.fc = torch.load('model/8.model')
    model.fc.eval()

    #加载测试数据集,共10000条数据
    loader_test = torch.utils.data.DataLoader(dataset=Dataset(train=False),
                                              batch_size=8,
                                              shuffle=True,
                                              drop_last=True)

    correct = 0
    total = 0
    for i in range(100):
        x, y = next(iter(loader_test))

        #这里因为数据量不大,使用cpu计算就可以了
        out = model(x).argmax(dim=1)

        correct += (out == y).sum().item()
        total += len(y)

    print(correct / total)

テスト データ セットはここにロードされます。ここでのトレーニングは False であり、最終的な正解率は 70% と比較的高いことに注意してください。

おすすめ

転載: blog.csdn.net/m0_62919535/article/details/131741805