pytorch學習筆記——timm庫

  當使用ChatGPT幫我們工作的時候,確實很大一部分人就會失業,當然也有很大一部分人收益其中。我今天繼續使用其幫我瞭解新的內容,也就是timm庫。毫不誇張的說,Chat GPT比百分之80的博客講的更清楚更好,僅次於源碼。

  當提到計算機視覺的深度學習框架時,PyTorch無疑是最受歡迎的選擇之一。PyTorch擁有強大的自動求導功能、易於使用的API和廣泛的社區支持。而針對計算機視覺任務,timm庫則是一個值得推薦的PyTorch擴展庫。timm(Timm is a model repository for PyTorch)庫提供了預訓練模型、模型構建塊和模型訓練的實用工具。timm庫可以幫助開發者快速構建和訓練深度學習模型,同時支持多種圖像分類、分割和檢測任務,特別是結合torch和torchvision的使用,對你訓練模型,事半功倍。

  本文將介紹timm庫的基本用法,並使用timm庫訓練一個圖像分類模型作爲示例。本文將假設讀者已經對PyTorch和計算機視覺的基本概念有一定的瞭解,下面詳細說一下。

  首先簡單梳理一下timm的用途:

  1. 圖像分類(Image Classification):Timm庫包含了許多用於圖像分類的預訓練模型,如ResNet、VGG、EfficientNet等。你可以使用這些模型進行圖像分類任務,如圖像分類、圖像迴歸等。

    • 使用EfficientNet模型進行圖像分類:

      model = timm.create_model('efficientnet_b0', pretrained=True)
    • 使用ResNet模型進行圖像分類:

      model = timm.create_model('resnet50', pretrained=True)
  2. 目標檢測(Object Detection):Timm庫提供了一系列在目標檢測和物體識別任務上表現優秀的模型,如EfficientDet、YOLO、RetinaNet等。你可以使用這些模型進行目標檢測和物體識別任務。

    • 使用EfficientDet模型進行目標檢測:

      model = timm.create_model('efficientdet_d0', pretrained=True)
    • 使用YOLOv5模型進行目標檢測:

      model = timm.create_model('yolov5s', pretrained=True)
  3. 圖像分割(Image Segmentation):Timm庫支持各種圖像分割模型,如DeepLab、U-Net、PSPNet等。你可以使用這些模型進行圖像分割任務,例如語義分割、實例分割等。

    • 使用DeepLabV3模型進行語義分割:

      model = timm.create_model('deeplabv3_resnet50', pretrained=True)
    • 使用PSPNet模型進行圖像分割:

      model = timm.create_model('pspnet_resnet50', pretrained=True)
  4. 模型微調和遷移學習:Timm庫提供了方便的函數和工具,使你能夠輕鬆地微調和遷移學習預訓練模型。你可以使用Timm庫中的模型作爲基礎模型,並在自己的數據集上進行微調。

    • 使用預訓練的ResNet模型進行微調:
      model = timm.create_model('resnet50', pretrained=True) # 在新數據集上進行微調 # ...
  5. 模型評估和驗證:Timm庫提供了各種評估指標和工具,用於模型的性能評估和驗證。你可以使用這些工具來評估模型在不同任務上的性能,並進行模型選擇和比較。

    • 使用Timm庫提供的評估工具進行模型性能評估

  總之,Timm庫是一個功能齊全的模型庫,涵蓋了圖像分類、目標檢測、圖像分割等多個計算機視覺任務,並提供了方便的接口和實用工具,簡化了模型開發和實驗過程。你可以根據具體的需求使用Timm庫中的不同模型和功能來完成相應的任務。

  下面來簡單學習一下。

1,安裝timm庫

  timm庫可以通過pip命令進行安裝:

pip install timm

  安裝完成後,我們在Python腳本或者Jupyter Notebook中導入timm庫。

import timm

  

2,加載預訓練模型

  timm庫提供了多個預訓練模型,這些模型可以在ImageNet等數據集上進行預訓練,也可以在其他數據集上進行微調。

  加載預訓練模型的代碼非常簡單,下面我們加載需要的預訓練模型權重:

import timm

m = timm.create_model('vgg16', pretrained=True)
m.eval()

  上面代碼就會創建一個VGG-16的預訓練模型,我們可以通過修改模型名稱來加載其他預訓練模型,例如:

model = timm.create_model('efficientnet_b0', pretrained=True)

  上面代碼就會創建一個EfficientNet-B0的預訓練模型。

  加載所有的預訓練模型列表(pprint是美化打印的標準庫):

import timm
from pprint import pprint
model_names = timm.list_models(pretrained=True)
pprint(model_names)
>>> ['adv_inception_v3',
 'cspdarknet53',
 'cspresnext50',
 'densenet121',
 'densenet161',
 'densenet169',
 'densenet201',
 'densenetblur121d',
 'dla34',
 'dla46_c',
...
]

  利用通配符加載所有的預訓練模型列表:

import timm
from pprint import pprint
model_names = timm.list_models('*resne*t*')
pprint(model_names)
>>> ['cspresnet50',
 'cspresnet50d',
 'cspresnet50w',
 'cspresnext50',
...
]

  

3,構建自定義模型

  如果需要自定義模型,我們可以使用timm庫提供的模型構建塊。模型構建塊是模型的組成部分,可以靈活的組合和定製。例如我們可以使用timm庫提供的ConvBnAct模塊來定義一個卷積-BatchNorm-ReLU的模型構建塊:

import torch.nn as nn
from timm.models.layers import ConvBnAct

block = ConvBnAct(in_channels=3, out_channels=64, kernel_size=3, stride=1, act_layer=nn.ReLU)
print(block)

  這個代碼會創建一個輸入通道爲3、輸出通道爲64、卷積核大小爲3、步長爲1、激活函數爲ReLU的卷積-BatchNorm-ReLU模塊。

  我們打印一下,可以清晰的看到結果:

ConvNormAct(
  (conv): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  (bn): BatchNormAct2d(
    64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True
    (drop): Identity()
    (act): ReLU(inplace=True)
  )
)

  

  再來一個示例:

import timm

class CustomModel(timm.models.VisionTransformer):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.head = torch.nn.Linear(self.head.in_features, num_classes)

model = CustomModel(img_size=224, patch_size=16, embed_dim=768, depth=12, num_heads=12, num_classes=100)

  在上面的代碼中,我們創建了一個繼承自timm庫中VisionTransformer的自定義模型,並修改了模型的輸出層以適應我們的任務。

4,訓練圖像分類模型

  下面我們將介紹如何使用timm庫訓練和測試圖像分類模型。timm庫是一個用於計算機視覺任務的PyTorch庫,它提供了許多預訓練模型和常用的計算機視覺工具。我們將使用CIFAR-10數據集作爲示例。

1,準備數據集

  首先,我們需要準備CIFAR-10數據集。我們可以使用torchvision庫來下載和加載數據集:
import torch
import torchvision
import torchvision.transforms as transforms

# 數據預處理
transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),
    transforms.RandomCrop(32, padding=4),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# 加載CIFAR-10數據集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=100, shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=100, shuffle=False, num_workers=2)

  

2,創建模型

  接下來,我們將使用timm庫創建一個預訓練的ResNet50模型,並修改輸出層以適應CIFAR-10數據集:
import timm

model = timm.create_model('resnet50', pretrained=True, num_classes=10)

  

3,訓練模型

  現在我們可以開始訓練模型。我們將使用交叉熵損失函數和Adam優化器:
import torch.optim as optim

criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓練模型
num_epochs = 10
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)

for epoch in range(num_epochs):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data
        inputs, labels = inputs.to(device), labels.to(device)

        optimizer.zero_grad()

        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

    print(f"Epoch {epoch + 1}, Loss: {running_loss / (i + 1)}")

  

4,測試模型

  訓練完成後,我們可以使用測試數據集評估模型的性能:
correct = 0
total = 0
model.eval()

with torch.no_grad():
    for data in testloader:
        images, labels = data
        images, labels = images.to(device), labels.to(device)

        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f"Accuracy on test set: {100 * correct / total}%")

5,整體圖像分類模型代碼

  下面是一個簡單的圖像分類示例,使用timm庫中的ResNet50模型:

import torch
import timm
from PIL import Image
import torchvision.transforms as transforms

# 加載ResNet50模型
model = timm.create_model('vgg16', pretrained=True)

# 將模型設置爲評估模式
model.eval()

# 加載圖像並進行預處理
image = Image.open(r"D:\Desktop\workdata\data\segmentation_dataset\images\Abyssinian_24.jpg")
# 定義圖像預處理轉換
preprocess = transforms.Compose([
    transforms.Resize((256, 256)),  # 調整圖像大小爲256x256
    transforms.ToTensor(),  # 轉換爲Tensor類型
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),  # 歸一化
])

# 應用預處理轉換
processed_image = preprocess(image)

# 查看處理後的圖像形狀和數值範圍
print("Processed image shape:", processed_image.shape)
print("Processed image range:", processed_image.min(), "-", processed_image.max())

# 5. 可選:將處理後的圖像轉換回PIL圖像對象
processed_pil_image = transforms.ToPILImage()(processed_image)

# 6. 可選:顯示處理後的圖像
processed_pil_image.show()


# 將輸入張量轉換爲批處理張量
input_batch = processed_image.unsqueeze(0)

# 將輸入張量傳遞給模型並獲取輸出
with torch.no_grad():
    output = model(input_batch)

# 獲取預測結果
_, predicted = torch.max(output.data, 1)

# 打印預測結果
print(predicted.item())

  

  在上面的代碼中,我們首先加載了ResNet50模型,並將其設置爲評估模式。然後,我們加載了一個圖像,並使用timm庫中的預處理函數對其進行預處理。接下來,我們將輸入張量轉換爲批處理張量,並將其傳遞給模型以獲取輸出。最後,我們使用torch.max函數獲取預測結果,並將其打印出來。

5,特徵提取

  timm中所有模型都可以從模型中獲取各種類型的特徵,用於除分類之外的任務。

1,獲取Penultimate Layer Features:

  Penultimate Layer Features的中文含義是 "倒數第2層的特徵",即 classifier 之前的特徵。timm 庫可以通過多種方式獲得倒數第二個模型層的特徵,而無需進行模型的手術。

import torch
import timm
m = timm.create_model('resnet50', pretrained=True, num_classes=1000)
o = m(torch.randn(2, 3, 224, 224))
print(f'Pooled shape: {o.shape}')
# Pooled shape: torch.Size([2, 2048])

  獲取分類器之後的特徵:

import torch
import timm
m = timm.create_model('ese_vovnet19b_dw', pretrained=True)
o = m(torch.randn(2, 3, 224, 224))
print(f'Original shape: {o.shape}')
m.reset_classifier(0)
o = m(torch.randn(2, 3, 224, 224))
print(f'Pooled shape: {o.shape}')
# Pooled shape: torch.Size([2, 1024])

  輸出多尺度特徵:默認情況下,大多數模型將輸出5個stride(並非所有模型都有那麼多),第一個從 stride=2開始(有些從1,4開始)。

import torch
import timm
m = timm.create_model('resnest26d', features_only=True, pretrained=True)
o = m(torch.randn(2, 3, 224, 224))
for x in o:
  print(x.shape)

# output
torch.Size([2, 64, 112, 112])
torch.Size([2, 256, 56, 56])
torch.Size([2, 512, 28, 28])
torch.Size([2, 1024, 14, 14])
torch.Size([2, 2048, 7, 7])

  當你在使用timm庫進行特徵提取時,可以選擇使用create_model函數的features_only參數來直接返回模型的特徵提取部分,而不包括分類器部分。這可以幫助簡化代碼,避免手動移除最後一層分類器。

  也可以手動去除,代碼如下:

import torch
import timm

# 1. 加載預訓練模型
model = timm.create_model('resnet50', pretrained=True)

# 2. 移除最後一層分類器
model = torch.nn.Sequential(*list(model.children())[:-1])

# 3. 設置模型爲評估模式
model.eval()

# 4. 加載圖像
input = torch.randn(1, 3, 224, 224)  # 假設輸入爲224x224的RGB圖像

# 5. 特徵提取
features = model(input)

# 6. 打印提取到的特徵
print("Features shape:", features.shape)

    在上述示例中,我們使用timm庫加載了一個預訓練的ResNet-50模型,並通過移除最後一層分類器來獲得特徵提取模型。然後,我們將模型設置爲評估模式(model.eval())以確保不進行訓練。接下來,我們加載輸入圖像,並將其傳遞給模型以獲取特徵表示。最後,我們打印出提取到的特徵的形狀。

6,模型融合

  模型融合是一種提高模型性能的有效方法。當涉及到更復雜的模型融合時,以下是一些深層次的技巧和注意事項:

  1. 模型選擇和組合:選擇具有不同架構和特性的模型進行融合,例如卷積神經網絡(CNN)、循環神經網絡(RNN)和注意力機制(Attention)。確保選擇的模型能夠相互補充,以提高整體性能。

  2. 特徵融合:除了融合模型輸出,還可以考慮融合模型的中間層特徵。通過提取和融合模型的不同層級特徵,可以獲得更豐富和多樣化的信息。

  3. 加權融合:爲每個模型分配適當的權重,以平衡其貢獻。權重可以基於預訓練模型的性能、驗證集表現等進行選擇,也可以通過訓練得到。

  4. 模型蒸餾:使用一個更大、更復雜的模型(教師模型)來指導訓練一個較小、更輕量級的模型(學生模型)。學生模型可以通過蒸餾教師模型的知識,從而提高模型的泛化能力。

  5. 集成學習:除了簡單的模型融合,可以嘗試集成學習方法,如投票、堆疊(stacking)、混合(blending)等。這些方法通過結合多個模型的預測結果,提高模型的魯棒性和準確性。

  6. 數據增強:對訓練數據進行多樣化的增強操作,如隨機裁剪、旋轉、翻轉等,以增加數據的多樣性和模型的魯棒性。確保在融合模型中使用相同的數據增強方式,以保持一致性。

  7. 模型選擇和調優:通過交叉驗證等方法,選擇最佳的模型組合並進行超參數調優。可以使用網格搜索、隨機搜索等技術來搜索最佳超參數組合。

  總之,模型融合是一個靈活且有挑戰性的任務,需要結合具體問題和數據集來進行調整和優化。不同的技巧和策略適用於不同的場景,因此需要不斷實驗和調整以找到最佳的模型融合方法。

  以下是一個使用timm庫進行模型融合的示例:

import torch
import timm

class ModelEnsemble(torch.nn.Module):
    def __init__(self, models):
        super().__init__()
        self.models = torch.nn.ModuleList(models)

    def forward(self, x):
        outputs = [model(x) for model in self.models]
        return torch.mean(torch.stack(outputs), dim=0)

# 加載多個預訓練模型
model1 = timm.create_model('resnet18', pretrained=True, num_classes=100)
model2 = timm.create_model('vgg16', pretrained=True, num_classes=100)

# 創建模型融合
ensemble = ModelEnsemble([model1, model2])

# 使用融合模型進行預測
output = ensemble(input_tensor)

  在上面的代碼中,我們首先加載了兩個預訓練模型。然後,我們創建了一個模型融合類,該類將多個模型的輸出進行平均。最後,我們使用融合模型進行預測。

  我們打印一下模型結構,我省略一些代碼,只show關鍵點:

ModelEnsemble(
  (models): ModuleList(
    (0): 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)
      (act1): ReLU(inplace=True)
      (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
      (layer1): Sequential(
      ........ (這裏省略了ResNet的網絡結構)
      )
      (global_pool): SelectAdaptivePool2d (pool_type=avg, flatten=Flatten(start_dim=1, end_dim=-1))
      (fc): Linear(in_features=512, out_features=100, bias=True)
    )
    (1): VGG(
      (features): Sequential(
        (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (1): ReLU(inplace=True)
        (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (3): ReLU(inplace=True)
        (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
        .......(這裏省略了VGG16的模型結構)
      )
      (pre_logits): ConvMlp(
        (fc1): Conv2d(512, 4096, kernel_size=(7, 7), stride=(1, 1))
        (act1): ReLU(inplace=True)
        (drop): Dropout(p=0.0, inplace=False)
        (fc2): Conv2d(4096, 4096, kernel_size=(1, 1), stride=(1, 1))
        (act2): ReLU(inplace=True)
      )
      (head): ClassifierHead(
        (global_pool): SelectAdaptivePool2d (pool_type=avg, flatten=Flatten(start_dim=1, end_dim=-1))
        (fc): Linear(in_features=4096, out_features=100, bias=True)
        (flatten): Identity()
      )
    )
  )
)

  在上面模型融合過程中,首先將輸入圖像傳遞給第一個模型,獲取其輸出特徵。然後,將這些特徵作爲輸入傳遞給第二個模型,以獲得最終的分類結果。通過對兩個模型的輸出進行融合,可以綜合利用它們的優勢,提高整體性能或泛化能力。

  需要注意的是,這只是一個簡單的示例,實際的模型融合可能涉及更復雜的策略和技巧。具體的模型融合方法取決於任務的需求和數據集的特點。可以根據實際情況進行調整和改進,以獲得更好的結果 。

  當涉及更復雜的模型融合時,常見的技術包括集成學習方法,如堆疊集成和投票集成。下面是一個更復雜的模型融合示例,使用堆疊集成的方法:

import torch
import timm

# 1. 加載並初始化要融合的模型
model1 = timm.create_model('resnet50', pretrained=True)
model2 = timm.create_model('efficientnet_b0', pretrained=True)
model3 = timm.create_model('densenet121', pretrained=True)

# 2. 定義融合模型
class FusionModel(torch.nn.Module):
    def __init__(self, model1, model2, model3):
        super(FusionModel, self).__init__()
        self.model1 = model1
        self.model2 = model2
        self.model3 = model3
        self.fc = torch.nn.Linear(3, 1)  # 假設最終輸出爲單個值

    def forward(self, x):
        output1 = self.model1(x)
        output2 = self.model2(x)
        output3 = self.model3(x)
        fused_output = torch.cat([output1, output2, output3], dim=1)
        fused_output = self.fc(fused_output)
        return fused_output

# 3. 創建融合模型實例
fusion_model = FusionModel(model1, model2, model3)

# 4. 使用融合模型進行推理
input = torch.randn(1, 3, 224, 224)  # 假設輸入爲224x224的RGB圖像
output = fusion_model(input)

# 5. 打印輸出結果
print("Fused output shape:", output.shape)

  在上述示例中,我們加載了三個預訓練模型,並使用堆疊集成的方法將它們的輸出連接在一起。連接後的輸出經過一個全連接層進行進一步處理,最終得到融合模型的輸出。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章