鱼C论坛

 找回密码
 立即注册
查看: 1458|回复: 15

[已解决]基于VGGNet网络的多标签分类任务

[复制链接]
发表于 2023-4-22 10:33:51 | 显示全部楼层 |阅读模式
10鱼币
在基于原项目代码的基础上,根据以下要求做出修改使其满足多标签分类任务,并高亮对应文件的相关代码段注明修改原因:

1.修改softmax激活函数为sigmoid激活函数使其在输出层输出多个标签类别。

2.修改loss函数CrossEntropyLoss为BCELoss。

3.增添评价指标Hamming Loss,Accuracyexam,Precisionexam,Recallexam,Fβexam,并输出这些评价结果。

4.可视化训练时的loss损失,横坐标为epochs,epochs为训练轮数,纵坐标分别为train_loss和val_loss,train_loss为训练过程中的损失,val_loss为验证过程中的损失。

5.可视化预测结果,展示的图片中有输出的多标签类别。

6.当涉及到变量及函数定义时,请使用项目文件中的相关变量及函数定义,并与上下代码段保证顺畅。新定义的变量及函数除外。

将修改好的项目代码文件逐个展示自己的完整内容,并说明每个文件的具体用法。


主要项目代码如下:
model.py:定义了VGG模型的架构,并提供了辅助函数和配置信息。
import torch.nn as nn
import torch

# official pretrain weights
model_urls = {
    'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth',
    'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth',
    'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth',
    'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth'
}


class VGG(nn.Module):
    def __init__(self, features, num_classes=1000, init_weights=False):
        super(VGG, self).__init__()
        self.features = features
        self.classifier = nn.Sequential(
            nn.Linear(512*7*7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, num_classes)
        )
        if init_weights:
            self._initialize_weights()

    def forward(self, x):
        # N x 3 x 224 x 224
        x = self.features(x)
        # N x 512 x 7 x 7
        x = torch.flatten(x, start_dim=1)
        # N x 512*7*7
        x = self.classifier(x)
        return x

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                nn.init.xavier_uniform_(m.weight)
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.xavier_uniform_(m.weight)
                # nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)


def make_features(cfg: list):
    layers = []
    in_channels = 3
    for v in cfg:
        if v == "M":
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else:
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            layers += [conv2d, nn.ReLU(True)]
            in_channels = v
    return nn.Sequential(*layers)


cfgs = {
    'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
    'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}


def vgg(model_name="vgg16", **kwargs):
    assert model_name in cfgs, "Warning: model number {} not in cfgs dict!".format(model_name)
    cfg = cfgs[model_name]

    model = VGG(make_features(cfg), **kwargs)
    return model
predict.py:基于训练好的VGG模型,对输入的图像进行分类预测,并输出预测结果。
import os
import json

import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt

from model import vgg


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose(
        [transforms.Resize((224, 224)),
         transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # load image
    img_path = "../tulip.jpg"
    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
    img = Image.open(img_path)
    plt.imshow(img)
    # [N, C, H, W]
    img = data_transform(img)
    # expand batch dimension
    img = torch.unsqueeze(img, dim=0)

    # read class_indict
    json_path = './class_indices.json'
    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)

    with open(json_path, "r") as f:
        class_indict = json.load(f)
    
    # create model
    model = vgg(model_name="vgg16", num_classes=5).to(device)
    # load model weights
    weights_path = "./vgg16Net.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path, map_location=device))

    model.eval()
    with torch.no_grad():
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()
        predict = torch.softmax(output, dim=0)
        predict_cla = torch.argmax(predict).numpy()

    print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],
                                                 predict[predict_cla].numpy())
    plt.title(print_res)
    for i in range(len(predict)):
        print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],
                                                  predict[i].numpy()))
    plt.show()


if __name__ == '__main__':
    main()
train.py:基于给定的数据集,训练VGG模型,最终得到可用于图像分类的模型文件。
import os
import sys
import json

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from tqdm import tqdm

from model import vgg


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))

    data_transform = {
        "train": transforms.Compose([transforms.RandomResizedCrop(224),
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
        "val": transforms.Compose([transforms.Resize((224, 224)),
                                   transforms.ToTensor(),
                                   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}

    data_root = os.path.abspath(os.path.join(os.getcwd(), "../"))  # get data root path
    image_path = os.path.join(data_root, "data_set", "PlantVillage")  # flower data set path
    assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])
    train_num = len(train_dataset)

    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    flower_list = train_dataset.class_to_idx
    cla_dict = dict((val, key) for key, val in flower_list.items())
    # write dict into json file
    json_str = json.dumps(cla_dict, indent=4)
    with open('class_indices.json', 'w') as json_file:
        json_file.write(json_str)

    batch_size = 32
    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    print('Using {} dataloader workers every process'.format(nw))

    train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=nw)

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
                                            transform=data_transform["val"])
    val_num = len(validate_dataset)
    validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                                  batch_size=batch_size, shuffle=False,
                                                  num_workers=nw)
    print("using {} images for training, {} images for validation.".format(train_num,
                                                                           val_num))

    # test_data_iter = iter(validate_loader)
    # test_image, test_label = test_data_iter.next()

    model_name = "vgg16"
    net = vgg(model_name=model_name, num_classes=4, init_weights=True)
    net.to(device)
    loss_function = nn.CrossEntropyLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.0001)

    epochs = 30
    best_acc = 0.0
    save_path = './{}Net.pth'.format(model_name)
    train_steps = len(train_loader)
    for epoch in range(epochs):
        # train
        net.train()
        running_loss = 0.0
        train_bar = tqdm(train_loader, file=sys.stdout)
        for step, data in enumerate(train_bar):
            images, labels = data
            optimizer.zero_grad()
            outputs = net(images.to(device))
            loss = loss_function(outputs, labels.to(device))
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()

            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)

        # validate
        net.eval()
        acc = 0.0  # accumulate accurate number / epoch
        with torch.no_grad():
            val_bar = tqdm(validate_loader, file=sys.stdout)
            for val_data in val_bar:
                val_images, val_labels = val_data
                outputs = net(val_images.to(device))
                predict_y = torch.max(outputs, dim=1)[1]
                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()

        val_accurate = acc / val_num
        print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
              (epoch + 1, running_loss / train_steps, val_accurate))

        if val_accurate > best_acc:
            best_acc = val_accurate
            torch.save(net.state_dict(), save_path)

    print('Finished Training')


if __name__ == '__main__':
    main()
最佳答案
2023-4-22 10:33:52
黎明丿晓小 发表于 2023-4-22 16:42
在运行以下代码时,报错“RuntimeError: Error(s) in loading state_dict for VGG:
        size mismatch for  ...

这个错误是因为您试图将预训练模型的权重(输出层有4个神经元)加载到具有不同输出层结构的模型中(输出层有5个神经元)。这会导致权重和模型的形状不匹配,因此无法加载权重。

要解决这个问题,您需要确保模型的类别数量与预训练模型权重的类别数量一致。在这个例子中,您应该将模型的类别数量改为4:
model = vgg(model_name="vgg16", num_classes=4).to(device)

然后,您需要更新`class_indices.json`文件,确保它包含正确的类别数量和类别名称。您可以手动编辑文件或在训练模型时自动生成该文件。

请注意,如果您需要处理5个类别,您需要重新训练模型,以适应5个类别的任务。在这种情况下,您需要使用适当数量的类别重新训练模型,并确保`class_indices.json`文件包含正确的类别信息。

最佳答案

查看完整内容

这个错误是因为您试图将预训练模型的权重(输出层有4个神经元)加载到具有不同输出层结构的模型中(输出层有5个神经元)。这会导致权重和模型的形状不匹配,因此无法加载权重。 要解决这个问题,您需要确保模型的类别数量与预训练模型权重的类别数量一致。在这个例子中,您应该将模型的类别数量改为4: 然后,您需要更新`class_indices.json`文件,确保它包含正确的类别数量和类别名称。您可以手动编辑文件或在训练模 ...
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-22 10:33:52 | 显示全部楼层    本楼为最佳答案   
黎明丿晓小 发表于 2023-4-22 16:42
在运行以下代码时,报错“RuntimeError: Error(s) in loading state_dict for VGG:
        size mismatch for  ...

这个错误是因为您试图将预训练模型的权重(输出层有4个神经元)加载到具有不同输出层结构的模型中(输出层有5个神经元)。这会导致权重和模型的形状不匹配,因此无法加载权重。

要解决这个问题,您需要确保模型的类别数量与预训练模型权重的类别数量一致。在这个例子中,您应该将模型的类别数量改为4:
model = vgg(model_name="vgg16", num_classes=4).to(device)

然后,您需要更新`class_indices.json`文件,确保它包含正确的类别数量和类别名称。您可以手动编辑文件或在训练模型时自动生成该文件。

请注意,如果您需要处理5个类别,您需要重新训练模型,以适应5个类别的任务。在这种情况下,您需要使用适当数量的类别重新训练模型,并确保`class_indices.json`文件包含正确的类别信息。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-22 10:39:31 | 显示全部楼层
[b]在基于原项目代码的基础上,根据要求做出修改以满足多标签分类任务。以下是修改后的代码文件及修改原因:

1. 修改 model.py 文件
在 model.py 文件中,为了实现多标签分类任务,我们需要修改分类器部分的最后一个线性层输出数量以及激活函数。将 softmax 激活函数改为 sigmoid 激活函数。
class VGG(nn.Module):
    def __init__(self, features, num_classes=1000, init_weights=False):
        super(VGG, self).__init__()
        self.features = features
        self.classifier = nn.Sequential(
            nn.Linear(512*7*7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, num_classes),
            nn.Sigmoid()  # Add Sigmoid activation function
        )
        if init_weights:
            self._initialize_weights()

2. 修改 train.py 文件
在 train.py 文件中,我们需要修改损失函数为 BCELoss 以满足多标签分类任务。同时,我们还需要修改评价指标。在此,我们添加了 Hamming Loss,Accuracyexam,Precisionexam,Recallexam,Fβexam 作为评价指标。

首先,我们需要安装 scikit-learn 库以使用 hamming_loss 函数:
pip install scikit-learn

然后,修改 train.py 文件:

import os
import sys
import json

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from tqdm import tqdm
from sklearn.metrics import hamming_loss, accuracy_score, precision_score, recall_score, fbeta_score

from model import vgg

# Define the evaluation function
def evaluate(y_true, y_pred):
    hamming = hamming_loss(y_true, y_pred)
    accuracy = accuracy_score(y_true, y_pred)
    precision = precision_score(y_true, y_pred, average='micro')
    recall = recall_score(y_true, y_pred, average='micro')
    fbeta = fbeta_score(y_true, y_pred, beta=1, average='micro')

    return hamming, accuracy, precision, recall, fbeta

# ... (same as before)

# Change the loss function to BCELoss
loss_function = nn.BCELoss()
# ... (same as before)

# Modify the training and validation loop to calculate evaluation metrics
# ... (same as before)

# Modify the validation part
net.eval()
acc = 0.0
val_true_labels = []
val_pred_labels = []
with torch.no_grad():
    val_bar = tqdm(validate_loader, file=sys.stdout)
    for val_data in val_bar:
        val_images, val_labels = val_data
        outputs = net(val_images.to(device))
        val_true_labels.append(val_labels.cpu().numpy())
        val_pred_labels.append((outputs > 0.5).float().cpu().numpy())

val_true_labels = np.vstack(val_true_labels)
val_pred_labels = np.vstack(val_pred_labels)
hamming, accuracy, precision, recall, fbeta = evaluate(val_true_labels, val_pred_labels)
print('[epoch %d] train_loss: %.3f val_hamming: %.3f val_accuracy: %.3f val_precision: %.3f val_recall: %.3f')

[b]接下来,我们根据要求对代码进行修改。

首先,我们需要修改VGG模型的输出层激活函数为sigmoid激活函数。这一步我们在`model.py`文件中修改VGG模型的架构。将原本的`nn.Linear(4096, num_classes)`修改为:
nn.Sequential(
    nn.Linear(4096, num_classes),
    nn.Sigmoid()
)

接下来,我们需要在`train.py`中修改损失函数为BCELoss。首先,在文件开头导入所需的模块:
from torch.nn import BCELoss, BCEWithLogitsLoss

然后,修改损失函数定义:
loss_function = BCEWithLogitsLoss()

注意:我们使用`BCEWithLogitsLoss`损失函数,因为它结合了`Sigmoid`激活函数和`BCELoss`损失函数的计算,具有更好的数值稳定性。

接下来,我们需要在`train.py`中增添评价指标,并输出这些评价结果。首先,在文件开头导入所需的模块:
from sklearn.metrics import hamming_loss, accuracy_score, precision_score, recall_score, f1_score

然后,在训练和验证循环的适当位置计算并输出评价指标。为了计算这些指标,我们需要将模型输出转换为二进制格式。这可以通过使用阈值(如0.5)来实现:
threshold = 0.5
binary_outputs = (outputs.sigmoid() > threshold).float()

现在,我们可以使用这些二进制输出计算各种评价指标:
hamming = hamming_loss(labels.cpu().numpy(), binary_outputs.cpu().numpy())
accuracy = accuracy_score(labels.cpu().numpy(), binary_outputs.cpu().numpy())
precision = precision_score(labels.cpu().numpy(), binary_outputs.cpu().numpy(), average='samples')
recall = recall_score(labels.cpu().numpy(), binary_outputs.cpu().numpy(), average='samples')
f1 = f1_score(labels.cpu().numpy(), binary_outputs.cpu().numpy(), average='samples')

随后,在训练循环的输出部分,添加这些评价指标的结果:
print('hamming_loss: {:.3f}, accuracy: {:.3f}, precision: {:.3f}, recall: {:.3f}, f1_score: {:.3f}'.format(hamming, accuracy, precision, recall, f1))

对于可视化训练时的loss损失,可以使用Matplotlib库来绘制损失图。首先,在`train.py`的开头导入必要的库:
import matplotlib.pyplot as plt

接下来,在训练循环之前,创建两个空列表以存储训练损失和验证损失:
train_losses = []
val_losses = []

在训练循环中,将损失值添加到相应的列表中:
train_losses.append(running_loss / train_steps)
val_losses.append(val_loss / val_steps)  # 需要在验证循环中计算val_loss

最后,在训练循环结束后,使用Matplotlib绘制损失曲线:
plt.figure(figsize=(10, 5))
plt.plot(train_losses, label='Training Loss')
plt.plot(val_losses, label='Validation Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

这将显示一个图形,展示了训练损失和验证损失随着训练周期的变化情况。通常,随着训练的进行,损失值应该逐渐减小。如果验证损失在某个时刻停止下降并开始上升,这可能意味着模型过拟合。在这种情况下,可以考虑使用早停法等策略来优化模型。

在对模型进行了上述修改后,可以重新运行`train.py`来训练模型。在训练结束时,可以观察到损失曲线,了解模型在训练过程中的表现。此外,还可以观察各种评价指标,包括Hamming损失、准确率、精确度、召回率和F1分数,以评估模型在多标签分类任务上的性能。

总结一下,我们对代码进行了如下修改:

1. 修改VGG模型输出层的激活函数为sigmoid激活函数。
2. 将损失函数更改为BCEWithLogitsLoss。
3. 计算并输出各种评价指标。
4. 使用Matplotlib绘制损失曲线。

这些更改有助于将现有的单标签分类模型适应多标签分类任务,并提供了关于模型性能的直观信息。

[/b]
[/b]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-4-22 11:35:25 | 显示全部楼层
isdkz 发表于 2023-4-22 10:39
在基于原项目代码的基础上,根据要求做出修改以满足多标签分类任务。以下是修改后的代码文件及修改原因:
...

我有以下问题需要你来回答:
1.np.vstack中的np是numpy吗?
2.val_loss是如何定义的?
3.请在基于predict.py代码的基础上,修改部分代码使其满足以下图片要求:
   
   其中,red和dress为多标签预测,后面对应的数字为置信度,也就是概率,可以设置一个阈值threshold=0.5,当预测一个图片的输出结果时,某一个标签的置信度大于阈值即可显示在输出结果图片上面。
   
20190326113925310.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-22 11:38:15 | 显示全部楼层
黎明丿晓小 发表于 2023-4-22 11:35
我有以下问题需要你来回答:
1.np.vstack中的np是numpy吗?
2.val_loss是如何定义的?

1. 是的,`np`通常是`numpy`库的缩写。在Python程序中,您经常会看到这样的导入语句:`import numpy as np`。这意味着`numpy`库被导入并使用别名`np`,以便在后续代码中更方便地引用。

2. `val_loss`是验证损失(Validation Loss)的缩写,它表示在验证集上计算得到的损失值。验证集是从训练集中划分出来的一个数据子集,用于在训练过程中评估模型的泛化性能。在训练过程中,您需要监视验证损失,以了解模型是否过拟合或者是否需要调整超参数。

3. 假设您有一个名为`predict.py`的脚本,用于进行多标签预测。您可以使用以下代码来实现输出图片的要求:
import numpy as np
import cv2
from model import MyModel
import torch
import torchvision.transforms as transforms

def predict(image_path, model, threshold=0.5):
    # Load image and perform preprocessing
    img = cv2.imread(image_path)
    img = cv2.resize(img, (224, 224))
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    # Convert image to PyTorch tensor and normalize
    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
    img_tensor = transform(img)
    img_tensor = img_tensor.unsqueeze(0)

    # Get predictions
    model.eval()
    with torch.no_grad():
        output = model(img_tensor)
        prob = torch.sigmoid(output).numpy().flatten()
        pred_labels = np.where(prob > threshold)[0]

    return pred_labels, prob

def draw_labels(image_path, pred_labels, prob, labels):
    img = cv2.imread(image_path)
    img = cv2.resize(img, (224, 224))

    for idx in pred_labels:
        label = labels[idx]
        confidence = prob[idx]
        cv2.putText(img, f'{label}: {confidence:.2f}', (5, 15 * (idx + 1)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)

    cv2.imshow('Output Image', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    image_path = 'path/to/image'
    model_path = 'path/to/model.pth'

    # Load the model
    model = MyModel()
    model.load_state_dict(torch.load(model_path))

    # Assume a list of labels in the same order as the output of the model
    labels = ['label_0', 'label_1', 'label_2', 'label_3', 'label_4', 'red', 'dress']

    pred_labels, prob = predict(image_path, model, threshold=0.5)
    draw_labels(image_path, pred_labels, prob, labels)

这段代码定义了一个`predict`函数,用于从图像文件路径加载图像并使用模型进行预测。然后,它计算预测标签的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-4-22 11:46:15 | 显示全部楼层
isdkz 发表于 2023-4-22 11:38
1. 是的,`np`通常是`numpy`库的缩写。在Python程序中,您经常会看到这样的导入语句:`import numpy as n ...

我的意思是val_loss在以下代码是如何定义的:
import os
import sys
import json

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from tqdm import tqdm
from sklearn.metrics import hamming_loss,accuracy_score,precision_score,recall_score,fbeta_score
import numpy as np
import matplotlib.pyplot as plt

from model import vgg

# Define the evaluation function
def evaluate(y_true, y_pred):
    hamming = hamming_loss(y_true, y_pred)
    accuracy = accuracy_score(y_true, y_pred)
    precision = precision_score(y_true, y_pred, average='micro')
    recall = recall_score(y_true, y_pred, average='micro')
    fbeta = fbeta_score(y_true, y_pred, beta=1, average='micro')

    return hamming, accuracy, precision, recall, fbeta

def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))

    data_transform = {
        "train": transforms.Compose([transforms.RandomResizedCrop(224),
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
        "val": transforms.Compose([transforms.Resize((224, 224)),
                                   transforms.ToTensor(),
                                   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}

    data_root = os.path.abspath(os.path.join(os.getcwd(), "../"))  # get data root path
    image_path = os.path.join(data_root, "data_set", "PlantVillage")  # flower data set path
    assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])
    train_num = len(train_dataset)

    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    flower_list = train_dataset.class_to_idx
    cla_dict = dict((val, key) for key, val in flower_list.items())
    # write dict into json file
    json_str = json.dumps(cla_dict, indent=4)
    with open('class_indices.json', 'w') as json_file:
        json_file.write(json_str)

    batch_size = 32
    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    print('Using {} dataloader workers every process'.format(nw))

    train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=nw)

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
                                            transform=data_transform["val"])
    val_num = len(validate_dataset)
    validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                                  batch_size=batch_size, shuffle=False,
                                                  num_workers=nw)
    print("using {} images for training, {} images for validation.".format(train_num,
                                                                           val_num))

    # test_data_iter = iter(validate_loader)
    # test_image, test_label = test_data_iter.next()

    model_name = "vgg16"
    net = vgg(model_name=model_name, num_classes=4, init_weights=True)
    net.to(device)
    # Change the loss function to BCELoss
    loss_function = nn.BCEWithLogitsLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.0001)

    train_losses = []
    val_losses = []

    # Modify the training and validation loop to calculate evaluation metrics
    epochs = 30
    best_acc = 0.0
    save_path = './{}Net.pth'.format(model_name)
    train_steps = len(train_loader)
    for epoch in range(epochs):
        # train
        net.train()
        running_loss = 0.0
        train_bar = tqdm(train_loader, file=sys.stdout)
        for step, data in enumerate(train_bar):
            images, labels = data
            optimizer.zero_grad()
            outputs = net(images.to(device))
            loss = loss_function(outputs, labels.to(device))
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()

            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)
            train_losses.append(running_loss / train_steps)

        # validate, Modify the validation part
        net.eval()
        acc = 0.0  # accumulate accurate number / epoch
        val_steps = len(validate_loader)
        val_true_labels = []
        val_pred_labels = []
        with torch.no_grad():
            val_bar = tqdm(validate_loader, file=sys.stdout)
            for val_data in val_bar:
                val_images, val_labels = val_data
                outputs = net(val_images.to(device))
                val_true_labels.append(val_labels.cpu().numpy())
                val_pred_labels.append((outputs > 0.5).float().cpu().numpy())
                val_losses.append(val_loss / val_steps)
                # predict_y = torch.max(outputs, dim=1)[1]
                # acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
        val_true_labels = np.vstack(val_true_labels)
        val_pred_labels = np.vstack(val_pred_labels)
        hamming, accuracy, precision, recall, fbeta = evaluate(val_true_labels, val_pred_labels)
        print('[epoch %d] train_loss: %.3f val_hamming: %.3f val_accuracy: %.3f val_precision: %.3f val_recall: %.3f')

        # val_accurate = acc / val_num
        # print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
        #       (epoch + 1, running_loss / train_steps, val_accurate))
        #
        # if val_accurate > best_acc:
        #     best_acc = val_accurate
        #     torch.save(net.state_dict(), save_path)
    plt.figure(figsize=(10, 5))
    plt.plot(train_losses, label='Training Loss')
    plt.plot(val_losses, label='Validation Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    plt.show()

    print('Finished Training')


if __name__ == '__main__':
    main()
另外,请在基于以下代码的基础上,修改部分代码使其满足以下图片要求:
其中,red和dress为多标签预测,后面对应的数字为置信度,也就是概率,可以设置一个阈值threshold=0.5,当预测一个图片的输出结果时,某一个标签的置信度大于阈值即可显示在输出结果图片上面。
import os
import json

import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt

from model import vgg


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose(
        [transforms.Resize((224, 224)),
         transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # load image
    img_path = "./test.jpg"
    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
    img = Image.open(img_path)
    plt.imshow(img)
    # [N, C, H, W]
    img = data_transform(img)
    # expand batch dimension
    img = torch.unsqueeze(img, dim=0)

    # read class_indict
    json_path = './class_indices.json'
    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)

    with open(json_path, "r") as f:
        class_indict = json.load(f)
    
    # create model
    model = vgg(model_name="vgg16", num_classes=5).to(device)
    # load model weights
    weights_path = "./vgg16Net.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path, map_location=device))

    model.eval()
    with torch.no_grad():
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()
        predict = torch.softmax(output, dim=0)
        predict_cla = torch.argmax(predict).numpy()

    print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],
                                                 predict[predict_cla].numpy())
    plt.title(print_res)
    for i in range(len(predict)):
        print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],
                                                  predict[i].numpy()))
    plt.show()


if __name__ == '__main__':
    main()
20190326113925310.png
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-22 11:50:57 | 显示全部楼层
黎明丿晓小 发表于 2023-4-22 11:46
我的意思是val_loss在以下代码是如何定义的:

另外,请在基于以下代码的基础上,修改部分代码使其满足 ...

首先,针对您提供的第一个代码片段,关于`val_loss`的定义问题。实际上,在这段代码中,并没有显式地定义`val_loss`变量。代码中的`running_loss`是累加每个批次的训练损失,然后将累加结果除以`train_steps`来得到每个`epoch`的平均训练损失。对于验证损失,您可以类似地计算,但在此代码中,它并未明确计算。要计算验证损失,您可以在验证阶段的循环中累加损失,然后除以`val_steps`得到每个`epoch`的平均验证损失。

对于您的第二个问题,根据您的要求,我对代码进行了修改,以满足阈值和输出的需求。请查看以下修改后的代码:
import os
import json

import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt

from model import vgg


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose(
        [transforms.Resize((224, 224)),
         transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # load image
    img_path = "./test.jpg"
    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
    img = Image.open(img_path)
    plt.imshow(img)
    # [N, C, H, W]
    img = data_transform(img)
    # expand batch dimension
    img = torch.unsqueeze(img, dim=0)

    # read class_indict
    json_path = './class_indices.json'
    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)

    with open(json_path, "r") as f:
        class_indict = json.load(f)

    # create model
    model = vgg(model_name="vgg16", num_classes=5).to(device)
    # load model weights
    weights_path = "./vgg16Net.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path, map_location=device))

    model.eval()
    threshold = 0.5
    with torch.no_grad():
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()
        probabilities = torch.sigmoid(output)
        predicted_labels = (probabilities > threshold).float().numpy()
        label_confidences = {class_indict[str(i)]: prob.item() for i, prob in enumerate(probabilities) if predicted_labels[i] == 1}

    for label, confidence in label_confidences.items():
        print("Label: {}    Confidence: {:.3f}".format(label, confidence))

    plt.title("\n".join("{}: {:.3f}".format(label, confidence) for label, confidence in label_confidences.items()))
    plt.show()


if __name__ == '__main__':
    main()

这段代码首先计算概率(置信度),然后根据阈值过滤预测标签。最后,它将满
足阈值条件的标签及其置信度显示在图像上方。在这个示例中,我将概率更改为使用`sigmoid`函数而不是`softmax`,因为对于多标签分类问题,使用`sigmoid`通常更合适。`softmax`主要用于单一标签分类问题,因为它会使得概率之和为1。而在多标签分类问题中,多个类别的存在可能是独立的,因此使用`sigmoid`可以单独计算每个类别的概率。

以下是修改后的代码:
import os
import json

import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt

from model import vgg


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose(
        [transforms.Resize((224, 224)),
         transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # load image
    img_path = "./test.jpg"
    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
    img = Image.open(img_path)
    plt.imshow(img)
    # [N, C, H, W]
    img = data_transform(img)
    # expand batch dimension
    img = torch.unsqueeze(img, dim=0)

    # read class_indict
    json_path = './class_indices.json'
    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)

    with open(json_path, "r") as f:
        class_indict = json.load(f)

    # create model
    model = vgg(model_name="vgg16", num_classes=5).to(device)
    # load model weights
    weights_path = "./vgg16Net.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path, map_location=device))

    model.eval()
    threshold = 0.5
    with torch.no_grad():
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()
        probabilities = torch.sigmoid(output)
        predicted_labels = (probabilities > threshold).float().numpy()
        label_confidences = {class_indict[str(i)]: prob.item() for i, prob in enumerate(probabilities) if predicted_labels[i] == 1}

    for label, confidence in label_confidences.items():
        print("Label: {}    Confidence: {:.3f}".format(label, confidence))

    plt.title("\n".join("{}: {:.3f}".format(label, confidence) for label, confidence in label_confidences.items()))
    plt.show()


if __name__ == '__main__':
    main()

这段代码应该满足您的要求,当一个图片的输出结果中,某个标签的置信度大于阈值时,会显示在输出结果图片上面。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-4-22 12:24:20 | 显示全部楼层
isdkz 发表于 2023-4-22 11:50
首先,针对您提供的第一个代码片段,关于`val_loss`的定义问题。实际上,在这段代码中,并没有显式地定义 ...

在训练以下代码时报错ValueError:Target size (torch.Size([32])) must be the same as input size (torch.Size([32,4]))
import os
import sys
import json

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from tqdm import tqdm
from sklearn.metrics import hamming_loss,accuracy_score,precision_score,recall_score,fbeta_score
import numpy as np
import matplotlib.pyplot as plt

from model import vgg

# Define the evaluation function
def evaluate(y_true, y_pred):
    hamming = hamming_loss(y_true, y_pred)
    accuracy = accuracy_score(y_true, y_pred)
    precision = precision_score(y_true, y_pred, average='micro')
    recall = recall_score(y_true, y_pred, average='micro')
    fbeta = fbeta_score(y_true, y_pred, beta=1, average='micro')

    return hamming, accuracy, precision, recall, fbeta

def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))

    data_transform = {
        "train": transforms.Compose([transforms.RandomResizedCrop(224),
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
        "val": transforms.Compose([transforms.Resize((224, 224)),
                                   transforms.ToTensor(),
                                   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}

    data_root = os.path.abspath(os.path.join(os.getcwd(), "../"))  # get data root path
    image_path = os.path.join(data_root, "data_set", "PlantVillage")  # flower data set path
    assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])
    train_num = len(train_dataset)

    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    flower_list = train_dataset.class_to_idx
    cla_dict = dict((val, key) for key, val in flower_list.items())
    # write dict into json file
    json_str = json.dumps(cla_dict, indent=4)
    with open('class_indices.json', 'w') as json_file:
        json_file.write(json_str)

    batch_size = 32
    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    print('Using {} dataloader workers every process'.format(nw))

    train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=nw)

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
                                            transform=data_transform["val"])
    val_num = len(validate_dataset)
    validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                                  batch_size=batch_size, shuffle=False,
                                                  num_workers=nw)
    print("using {} images for training, {} images for validation.".format(train_num,
                                                                           val_num))

    # test_data_iter = iter(validate_loader)
    # test_image, test_label = test_data_iter.next()

    model_name = "vgg16"
    net = vgg(model_name=model_name, num_classes=4, init_weights=True)
    net.to(device)
    # Change the loss function to BCELoss
    loss_function = nn.BCEWithLogitsLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.0001)

    train_losses = []
    val_losses = []

    # Modify the training and validation loop to calculate evaluation metrics
    epochs = 30
    best_acc = 0.0
    save_path = './{}Net.pth'.format(model_name)
    train_steps = len(train_loader)
    for epoch in range(epochs):
        # train
        net.train()
        running_loss = 0.0
        train_bar = tqdm(train_loader, file=sys.stdout)
        for step, data in enumerate(train_bar):
            images, labels = data
            optimizer.zero_grad()
            outputs = net(images.to(device))
            loss = loss_function(outputs, labels.to(device))
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()

            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)
            train_losses.append(running_loss / train_steps)

        # validate, Modify the validation part
        net.eval()
        validate_loss = 0.0
        acc = 0.0  # accumulate accurate number / epoch
        val_steps = len(validate_loader)
        val_true_labels = []
        val_pred_labels = []
        with torch.no_grad():
            val_bar = tqdm(validate_loader, file=sys.stdout)
            for val_data in val_bar:
                val_images, val_labels = val_data
                outputs = net(val_images.to(device))
                val_loss = loss_function(outputs,val_labels.to(device))
                val_loss.backward()
                optimizer.step()
                validate_loss += val_loss.item()
                val_bar.desc = "val epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     val_loss)
                val_losses.append(validate_loss / val_steps)
                val_true_labels.append(val_labels.cpu().numpy())
                val_pred_labels.append((outputs > 0.5).float().cpu().numpy())

                # predict_y = torch.max(outputs, dim=1)[1]
                # acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
        val_true_labels = np.vstack(val_true_labels)
        val_pred_labels = np.vstack(val_pred_labels)
        hamming, accuracy, precision, recall, fbeta = evaluate(val_true_labels, val_pred_labels)
        print('[epoch %d] train_loss: %.3f val_hamming: %.3f val_accuracy: %.3f val_precision: %.3f val_recall: %.3f')

        # val_accurate = acc / val_num
        # print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
        #       (epoch + 1, running_loss / train_steps, val_accurate))
        #
        # if val_accurate > best_acc:
        #     best_acc = val_accurate
        #     torch.save(net.state_dict(), save_path)
    plt.figure(figsize=(10, 5))
    plt.plot(train_losses, label='Training Loss')
    plt.plot(val_losses, label='Validation Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    plt.show()

    print('Finished Training')


if __name__ == '__main__':
    main()
请指出错误原因并更改相应代码段
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-22 12:25:54 | 显示全部楼层
黎明丿晓小 发表于 2023-4-22 12:24
在训练以下代码时报错ValueError:Target size (torch.Size([32])) must be the same as input size (tor ...

根据您提供的代码,错误出现在处理多标签分类问题时的损失函数计算部分。`nn.BCEWithLogitsLoss`需要将标签的尺寸更改为与模型输出相同的尺寸。因为您的模型的输出是大小为 `[32, 4]` 的张量,因此标签的大小也应为 `[32, 4]`。为了解决这个问题,请在数据加载器(DataLoader)部分添加一个函数,将分类标签转换为独热编码。以下是相关代码的修改:

1. 定义一个函数将标签转换为独热编码:
def label_to_onehot(labels, num_classes):
    batch_size = len(labels)
    one_hot = torch.zeros(batch_size, num_classes)
    one_hot[torch.arange(batch_size), labels] = 1
    return one_hot

2. 在训练和验证循环中使用该函数转换标签:
将以下代码:
images, labels = data
更改为:
images, labels = data
labels = label_to_onehot(labels, num_classes=4).to(device)
同时,将验证部分的这段代码:
val_images, val_labels = val_data
更改为:
val_images, val_labels = val_data
val_labels = label_to_onehot(val_labels, num_classes=4).to(device)

这些更改应解决您遇到的问题。现在,标签的大小与模型输出的大小相同,因此可以正确计算损失函数。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-4-22 12:31:09 | 显示全部楼层
isdkz 发表于 2023-4-22 12:25
根据您提供的代码,错误出现在处理多标签分类问题时的损失函数计算部分。`nn.BCEWithLogitsLoss`需要将标 ...

应该是“将标签的尺寸更改为与模型输入相同的尺寸”吧,另外,这样改会失去多标签分类任务的功能吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-22 12:43:33 | 显示全部楼层
黎明丿晓小 发表于 2023-4-22 12:31
应该是“将标签的尺寸更改为与模型输入相同的尺寸”吧,另外,这样改会失去多标签分类任务的功能吗?

抱歉,我在描述中的确有误,应该是“将标签的尺寸更改为与模型输出相同的尺寸”。

另外,不用担心,这样的更改不会失去多标签分类任务的功能。实际上,通过将标签转换为独热编码,我们确保了多标签分类任务的正确处理。多标签分类意味着每个样本可以属于多个类别。在这种情况下,通过使用独热编码,我们可以表示每个样本属于多个类别的情况。`nn.BCEWithLogitsLoss()`损失函数适用于多标签分类任务,因为它在每个类别之间独立计算二元交叉熵损失。这意味着对于每个类别,模型都将学习一个独立的二元分类器。因此,通过执行上述更改,您的模型仍然可以处理多标签分类任务。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-4-22 16:16:04 | 显示全部楼层
本帖最后由 黎明丿晓小 于 2023-4-22 16:17 编辑
isdkz 发表于 2023-4-22 12:43
抱歉,我在描述中的确有误,应该是“将标签的尺寸更改为与模型输出相同的尺寸”。

另外,不用担心,这 ...


运行以下代码时,在训练过程中报出警告“UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use zero_division parameter to control this behavior._warn_prf(average, modifier, msg_start, len(result))”,请解释报出警告的原因,并给出解决办法:
import os
import sys
import json

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from tqdm import tqdm
from sklearn.metrics import hamming_loss,accuracy_score,precision_score,recall_score,fbeta_score
import numpy as np
import matplotlib.pyplot as plt
import warnings

from model import vgg

# Define the evaluation function
def evaluate(y_true, y_pred):
    hamming = hamming_loss(y_true, y_pred)
    accuracy = accuracy_score(y_true, y_pred)
    precision = precision_score(y_true, y_pred, average='micro')
    recall = recall_score(y_true, y_pred, average='micro')
    fbeta = fbeta_score(y_true, y_pred, beta=1, average='micro')

    return hamming, accuracy, precision, recall, fbeta

def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))

    data_transform = {
        "train": transforms.Compose([transforms.RandomResizedCrop(224),
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
        "val": transforms.Compose([transforms.Resize((224, 224)),
                                   transforms.ToTensor(),
                                   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}

    data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  # get data root path
    image_path = os.path.join(data_root, "data_set", "plantvillage_demo1")  # flower data set path
    assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])
    train_num = len(train_dataset)

    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    flower_list = train_dataset.class_to_idx
    cla_dict = dict((val, key) for key, val in flower_list.items())
    # write dict into json file
    json_str = json.dumps(cla_dict, indent=4)
    with open('class_indices.json', 'w') as json_file:
        json_file.write(json_str)

    batch_size = 32
    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    print('Using {} dataloader workers every process'.format(nw))

    def label_to_onehot(labels, num_classes):
        batch_size = len(labels)
        one_hot = torch.zeros(batch_size, num_classes)
        one_hot[torch.arange(batch_size), labels] = 1
        return one_hot

    train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=nw)

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
                                            transform=data_transform["val"])
    val_num = len(validate_dataset)
    validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                                  batch_size=batch_size, shuffle=False,
                                                  num_workers=nw)
    print("using {} images for training, {} images for validation.".format(train_num,
                                                                           val_num))

    # test_data_iter = iter(validate_loader)
    # test_image, test_label = test_data_iter.next()

    model_name = "vgg16"
    net = vgg(model_name=model_name, num_classes=4, init_weights=True)
    net.to(device)
    # Change the loss function to BCELoss
    loss_function = nn.BCEWithLogitsLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.0001)

    train_losses = []
    #val_losses = []

    # Modify the training and validation loop to calculate evaluation metrics
    #warnings.filterwarnings("ignore")
    epochs = 30
    best_acc = 0.0
    save_path = './{}Net.pth'.format(model_name)
    train_steps = len(train_loader)
    for epoch in range(epochs):
        # train
        net.train()
        running_loss = 0.0
        train_bar = tqdm(train_loader, file=sys.stdout)
        for step, data in enumerate(train_bar):
            images, labels = data
            labels = label_to_onehot(labels, num_classes=4).to(device)
            optimizer.zero_grad()
            outputs = net(images.to(device))
            loss = loss_function(outputs, labels.to(device))
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()

            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)
            train_losses.append(running_loss / train_steps)

        # validate, Modify the validation part
        net.eval()
        acc = 0.0  # accumulate accurate number / epoch
        val_true_labels = []
        val_pred_labels = []
        with torch.no_grad():
            val_bar = tqdm(validate_loader, file=sys.stdout)
            for val_data in val_bar:
                val_images, val_labels = val_data
                #val_labels = label_to_onehot(val_labels, num_classes=4).to(device)
                outputs = net(val_images.to(device))
                val_true_labels.append(label_to_onehot(val_labels,num_classes=4).cpu().numpy())
                val_pred_labels.append((outputs > 0.5).float().cpu().numpy())

                predict_y = torch.max(outputs, dim=1)[1]
                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
        val_true_labels = np.vstack(val_true_labels)
        val_pred_labels = np.vstack(val_pred_labels)
        hamming, accuracy, precision, recall, fbeta = evaluate(val_true_labels, val_pred_labels)
        val_accurate = acc / val_num
        print('[epoch %d] train_loss: %.3f val_hamming: %.3f val_accuracy: %.3f val_precision: %.3f val_recall: %.3f fbeta: %.3f' %
              (epoch + 1, running_loss / train_steps, hamming, val_accurate, precision, recall, fbeta))


        # print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
        #       (epoch + 1, running_loss / train_steps, val_accurate))

        if val_accurate > best_acc:
            best_acc = val_accurate
            torch.save(net.state_dict(), save_path)
    plt.figure(figsize=(10, 5))
    plt.plot(train_losses, label='Training Loss')
    plt.plot(val_accurate, label='Validation Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Loss/Accuracy')
    plt.legend()
    plt.show()

    print('Finished Training')


if __name__ == '__main__':
    main()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-22 16:21:41 | 显示全部楼层
黎明丿晓小 发表于 2023-4-22 16:16
运行以下代码时,在训练过程中报出警告“UndefinedMetricWarning: Precision is ill-defined and being ...

这个警告出现的原因是,在计算precision(精确度)时,有些标签在预测结果中没有出现,导致在计算这些标签的精确度时产生除以0的情况。在这种情况下,`sklearn`将精确度设置为0.0,并发出“UndefinedMetricWarning”警告。为了解决这个问题,您可以使用`zero_division`参数来控制这种行为。例如,您可以将`zero_division`设置为1,这样当分母为0时,精确度将被设置为1。

您可以将`precision_score`函数更改为:
precision = precision_score(y_true, y_pred, average='micro', zero_division=1)

这将消除警告,并在计算精确度时处理除以0的情况。但请注意,这种方法可能会导致某些标签的精确度被高估。另一种方法是在计算评估指标之前,检查是否存在这种情况,例如,您可以检查预测结果中是否包含所有标签。如果所有标签都在预测结果中,那么计算精确度时就不会出现除以0的问题。

需要注意的是,`UndefinedMetricWarning`警告不会影响其他评估指标的计算。所以,如果您觉得精确度对您的任务来说并不是关键指标,您可以选择忽略这个警告。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-4-22 16:42:56 | 显示全部楼层
isdkz 发表于 2023-4-22 16:21
这个警告出现的原因是,在计算precision(精确度)时,有些标签在预测结果中没有出现,导致在计算这些标 ...

在运行以下代码时,报错“RuntimeError: Error(s) in loading state_dict for VGG:
        size mismatch for classifier.6.weight: copying a param with shape torch.Size([4, 4096]) from checkpoint, the shape in current model is torch.Size([5, 4096]).
        size mismatch for classifier.6.bias: copying a param with shape torch.Size([4]) from checkpoint, the shape in current model is torch.Size([5]).”
请给出错误原因以及修改意见
import os
import json

import torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt

from model import vgg


def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose(
        [transforms.Resize((224, 224)),
         transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # load image
    img_path = "./test.jpg"
    assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
    img = Image.open(img_path)
    plt.imshow(img)
    # [N, C, H, W]
    img = data_transform(img)
    # expand batch dimension
    img = torch.unsqueeze(img, dim=0)

    # read class_indict
    json_path = './class_indices.json'
    assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)

    with open(json_path, "r") as f:
        class_indict = json.load(f)
    
    # create model
    model = vgg(model_name="vgg16", num_classes=5).to(device)
    # load model weights
    weights_path = "./vgg16Net.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path, map_location=device))

    model.eval()
    threshold = 0.5
    with torch.no_grad():
        # predict class
        output = torch.squeeze(model(img.to(device))).cpu()
        probabilities = torch.sigmoid(output)
        predicted_labels = (probabilities > threshold).float().numpy()
        label_confidences = {class_indict[str(i)]: prob.item() for i, prob in enumerate(probabilities) if
                             predicted_labels[i] == 1}

    for label, confidence in label_confidences.items():
        print("Label: {}    Confidence: {:.3f}".format(label, confidence))

    plt.title("\n".join("{}: {:.3f}".format(label, confidence) for label, confidence in label_confidences.items()))
    plt.show()
    #     output = torch.squeeze(model(img.to(device))).cpu()
    #     predict = torch.softmax(output, dim=0)
    #     predict_cla = torch.argmax(predict).numpy()
    #
    # print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],
    #                                              predict[predict_cla].numpy())
    # plt.title(print_res)
    # for i in range(len(predict)):
    #     print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],
    #                                               predict[i].numpy()))
    # plt.show()


if __name__ == '__main__':
    main()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-4-22 17:15:56 | 显示全部楼层
黎明丿晓小 发表于 2023-4-22 16:16
运行以下代码时,在训练过程中报出警告“UndefinedMetricWarning: Precision is ill-defined and being ...

在运行此代码的过程中,hamming,  precision, recall, fbeta的输出分别一直为0.250,0.000,0.000,0.000。请说明原因并给出解决方法
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-4-22 18:37:20 | 显示全部楼层
isdkz 发表于 2023-4-22 16:45
这个错误是因为您试图将预训练模型的权重(输出层有4个神经元)加载到具有不同输出层结构的模型中(输出 ...

请修改以下代码的数据读取方式以满足多标签分类任务的数据读取方式:
import os
import sys
import json

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from tqdm import tqdm
from sklearn.metrics import hamming_loss,accuracy_score,precision_score,recall_score,fbeta_score
import numpy as np
import matplotlib.pyplot as plt
import warnings

from model import vgg

# Define the evaluation function
def evaluate(y_true, y_pred):
    hamming = hamming_loss(y_true, y_pred)
    accuracy = accuracy_score(y_true, y_pred)
    precision = precision_score(y_true, y_pred, average='micro')
    recall = recall_score(y_true, y_pred, average='micro')
    fbeta = fbeta_score(y_true, y_pred, beta=1, average='micro')

    return hamming, accuracy, precision, recall, fbeta

def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))

    data_transform = {
        "train": transforms.Compose([transforms.RandomResizedCrop(224),
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
        "val": transforms.Compose([transforms.Resize((224, 224)),
                                   transforms.ToTensor(),
                                   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}

    data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  # get data root path
    image_path = os.path.join(data_root, "data_set", "plantvillage_demo1")  # flower data set path
    assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])
    train_num = len(train_dataset)

    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    flower_list = train_dataset.class_to_idx
    cla_dict = dict((val, key) for key, val in flower_list.items())
    # write dict into json file
    json_str = json.dumps(cla_dict, indent=4)
    with open('class_indices.json', 'w') as json_file:
        json_file.write(json_str)

    batch_size = 32
    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    print('Using {} dataloader workers every process'.format(nw))

    def label_to_onehot(labels, num_classes):
        batch_size = len(labels)
        one_hot = torch.zeros(batch_size, num_classes)
        one_hot[torch.arange(batch_size), labels] = 1
        return one_hot

    train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=nw)

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
                                            transform=data_transform["val"])
    val_num = len(validate_dataset)
    validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                                  batch_size=batch_size, shuffle=False,
                                                  num_workers=nw)
    print("using {} images for training, {} images for validation.".format(train_num,
                                                                           val_num))

    # test_data_iter = iter(validate_loader)
    # test_image, test_label = test_data_iter.next()

    model_name = "vgg16"
    net = vgg(model_name=model_name, num_classes=4, init_weights=True)
    net.to(device)
    # Change the loss function to BCELoss
    loss_function = nn.BCEWithLogitsLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.0001)

    train_losses = []
    #val_losses = []

    # Modify the training and validation loop to calculate evaluation metrics
    warnings.filterwarnings("ignore")
    epochs = 30
    best_acc = 0.0
    save_path = './{}Net.pth'.format(model_name)
    train_steps = len(train_loader)
    for epoch in range(epochs):
        # train
        net.train()
        running_loss = 0.0
        train_bar = tqdm(train_loader, file=sys.stdout)
        for step, data in enumerate(train_bar):
            images, labels = data
            labels = label_to_onehot(labels, num_classes=4).to(device)
            optimizer.zero_grad()
            outputs = net(images.to(device))
            loss = loss_function(outputs, labels.to(device))
            loss.backward()
            optimizer.step()

            # print statistics
            running_loss += loss.item()

            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)
            train_losses.append(running_loss / train_steps)

        # validate, Modify the validation part
        net.eval()
        acc = 0.0  # accumulate accurate number / epoch
        val_true_labels = []
        val_pred_labels = []
        with torch.no_grad():
            val_bar = tqdm(validate_loader, file=sys.stdout)
            for val_data in val_bar:
                val_images, val_labels = val_data
                #val_labels = label_to_onehot(val_labels, num_classes=4).to(device)
                outputs = net(val_images.to(device))
                val_true_labels.append(label_to_onehot(val_labels,num_classes=4).cpu().numpy())
                val_pred_labels.append((outputs > 0.5).float().cpu().numpy())

                predict_y = torch.max(outputs, dim=1)[1]
                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
        val_true_labels = np.vstack(val_true_labels)
        val_pred_labels = np.vstack(val_pred_labels)
        hamming, accuracy, precision, recall, fbeta = evaluate(val_true_labels, val_pred_labels)
        val_accurate = acc / val_num
        print('[epoch %d] train_loss: %.3f val_hamming: %.3f val_accuracy: %.3f val_precision: %.3f val_recall: %.3f fbeta: %.3f' %
              (epoch + 1, running_loss / train_steps, hamming, val_accurate, precision, recall, fbeta))


        # print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
        #       (epoch + 1, running_loss / train_steps, val_accurate))

        if val_accurate > best_acc:
            best_acc = val_accurate
            torch.save(net.state_dict(), save_path)
    plt.figure(figsize=(10, 5))
    plt.plot(train_losses, label='Training Loss')
    plt.plot(val_accurate, label='Validation Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Loss/Accuracy')
    plt.legend()
    plt.show()

    print('Finished Training')


if __name__ == '__main__':
    main()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-9-23 15:23

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表