說真的

我寧願直接使用 pytorch 來訓練

但有時候就是身不由己

因為包套的東西有時候會逼著你需要修改套件

但是這肯定是不合理的

變成需要花很多時間來研究問題與錯誤的原因

 

以下程式範例主要是用自定義one hot 格式 Dataset

也就是輸出是 [0,1] [1,0] 這樣的格式

然後模型用單純的 pytorch 模型

 

 

# 匯入必要的套件
import torch
import torch.nn as nn
from fastai.vision.all import *

# 自定義產生假資料的資料產生器
class CustomDataset(torch.utils.data.Dataset):
    def __init__(self, size=200):
        self.size = size

    def __len__(self):
        return self.size

    def __getitem__(self, idx):
        # 假設我們的資料是一個介於0到1之間的隨機數
        data = np.zeros((3, 64, 64))
        # 假設我們的標籤是介於0到4之間的隨機整數
        label = np.eye(2)[idx % 2]
        label = torch.tensor(label).type(torch.FloatTensor)
        # print("label = ", label)


        return torch.tensor(data).type(torch.FloatTensor), label

# 建立資料集
datasetT = CustomDataset()
datasetV = CustomDataset()

train_loader = DataLoader(datasetT, bs=2, 
                          shuffle=True, num_workers=0)
val_loader = DataLoader(datasetV, bs=2, 
                          shuffle=True, num_workers=0)

dls = DataLoaders(train_loader, val_loader)


class Net(nn.Module):
    def __init__(self, num_classes=2, num_channels=3):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(num_channels, 16, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
        self.fc1 = nn.Linear(32 * 16 * 16, 128)
        self.fc2 = nn.Linear(128, num_classes)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, kernel_size=2, stride=2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, kernel_size=2, stride=2)
        x = x.view(-1, 32 * 16 * 16)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x


from fastai.optimizer import OptimWrapper

from torch import optim
from functools import partial

opt_func = partial(OptimWrapper, opt=optim.Adam)
from fastai.metrics import accuracy
import torch.nn.functional as F

learn = Learner(dls, Net(), 
                loss_func=nn.BCEWithLogitsLoss(), opt_func=opt_func, 
                # metrics=accuracy,
                metrics=accuracy_multi, # 這邊是重點
                )


# learn.fit_one_cycle(n_epoch=1, lr_max=1e-2)
# learn.fit(50,
#           lr=0.1,
#           cbs=[
#               EarlyStoppingCallback(monitor='valid_loss', min_delta=0.001, patience=3),
#               SaveModelCallback(monitor='valid_loss', fname='/best_w.pth'),
#           ])

learn.fine_tune(
    freeze_epochs = 1,
    epochs=50,
    base_lr=0.001,
    cbs=[
        EarlyStoppingCallback(monitor='valid_loss', patience=3),
        SaveModelCallback(monitor='valid_loss', fname='/best_w.pth'),
    ],

)

 

 

這邊的坑是如果你 metrics=accuracy

會直接報錯

而且會發現這錯誤是很奇怪的錯誤

就是他算 accuracy 居然先用 numpy.argmax 取得分類編號

然後再與你的 標籤 比對

但是我們的標籤已經是 one hot 格式了

這樣一比對就會出錯

然後我找文件才發現

https://github.com/fastai/fastai2/blob/master/nbs/13b_metrics.ipynb

這邊有說如果是多分類 (one hot) 形式

要用 multi 的版本

也就是 metrics=accuracy_multi

這問題用 chatgpt 也答不出來

因為它顯示的錯誤根本連想不到是這問題

"Exception occured in `Recorder` when calling event `after_batch`"

只能翻程式碼一步一步找回去看

當然也是我不夠謹慎吧...

google 老半天也很少看到有人跟我一樣需求的

好吧

那就在這邊做個註記

希望對大家有幫助囉