(五)测试集评估模型性能(datawhale学习)

文章目录

一、安装配置环境

!pip install numpy pandas scikit-learn matplotlib seaborn requests tqdm opencv-python pillow kaleido -i https://pypi.tuna.tsinghua.edu.cn/simple

# 下载安装Pytorch
!pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113

# 下载中文字体文件
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf

import matplotlib.pyplot as plt
# windows操作系统设置matplotlib中文字体
plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签 
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号
  1. Scikit-learn(sklearn)

    Scikit-learn 是一个基于 Python 语言开发的机器学习库,提供了各种常用的机器学习算法和数据处理工具。该库支持多种模型评估指标和交叉验证方法,并且提供了许多实用的函数和类,能够方便地处理各种机器学习任务,如分类、回归、聚类降维 等。

  2. Seaborn

    Seaborn 是一个基于 Matplotlib 的 Python 可视化库,提供了更高级别的界面和语法,能够更轻松地 制作各种复杂的图形。该库支持许多类型的统计图形,如散点图、条形图、直方图、热图 等,并且具有更好的默认样式和颜色。Seaborn 还提供了许多实用的函数和工具,如数据集加载、线性回归分析等,方便进行数据探索和分析。

  3. Kaleido

    Kaleido 是一种用于静态图片生成的软件工具,可以将 Matplotlib、Seaborn 和 Plotly 等库生成的图形 导出为高质量的矢量格式(SVG、PDF、EPS)和栅格格式(PNG、JPEG)的图片。Kaleido 支持多种图片尺寸和分辨率设置,并且可以直接集成到 Python 脚本中,方便进行批量图片生成。

二、准备数据集&映射字典&模型文件

# 下载数据集压缩包
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/fruit30/fruit30_split.zip
# 解压
!unzip fruit30_split.zip >> /dev/null
# 删除压缩包
!rm fruit30_split.zip
# 下载 类别名称 和 ID索引号 的映射字典
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/fruit30/idx_to_labels.npy
# 下载样例模型文件(这里只需将自己训练的模型复制到指定文件夹就好,可以不下载)
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/checkpoints/fruit30_pytorch_20220814.pth -P checkpoints

三、测试集图像分类分类预测结果

1. 导入工具包

import os
from tqdm import tqdm

import numpy as np
import pandas as pd

from PIL import Image

import torch
import torch.nn.functional as F

# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('device', device)

2. 图像预处理

from torchvision import transforms

# 测试集图像预处理-RCTN:缩放、裁剪、转 Tensor、归一化
test_transform = transforms.Compose([transforms.Resize(256),
                                     transforms.CenterCrop(224),
                                     transforms.ToTensor(),
                                     transforms.Normalize(
                                         mean=[0.485, 0.456, 0.406], 
                                         std=[0.229, 0.224, 0.225])
                                    ])

3. 载入测试集

# 数据集文件夹路径
dataset_dir = 'fruit30_split'
test_path = os.path.join(dataset_dir, 'val')
from torchvision import datasets
# 载入测试集
test_dataset = datasets.ImageFolder(test_path, test_transform)
print('测试集图像数量', len(test_dataset))
print('类别个数', len(test_dataset.classes))
print('各类别名称', test_dataset.classes)
# 载入类别名称 和 ID索引号 的映射字典
idx_to_labels = np.load('idx_to_labels.npy', allow_pickle=True).item()
# 获得类别名称
classes = list(idx_to_labels.values())
print(classes)

4. 导入训练好的模型

model = torch.load('checkpoints/best-0.880.pth')
model = model.eval().to(device)

5. 表格A-测试集图像路径及标注

test_dataset.imgs[:10]

image-20230222194715669

img_paths = [each[0] for each in test_dataset.imgs]

df = pd.DataFrame()
df['图像路径'] = img_paths
df['标注类别ID'] = test_dataset.targets
df['标注类别名称'] = [idx_to_labels[ID] for ID in test_dataset.targets]
df

image-20230222193650889

6. 表格B-测试集每张图像的图像分类预测结果,以及各类别置信度

# 记录 top-n 预测结果
n = 3

df_pred = pd.DataFrame()
for idx, row in tqdm(df.iterrows()):
    img_path = row['图像路径']
    img_pil = Image.open(img_path).convert('RGB')
    input_img = test_transform(img_pil).unsqueeze(0).to(device) # 预处理
    pred_logits = model(input_img) # 执行前向预测,得到所有类别的 logit 预测分数
    pred_softmax = F.softmax(pred_logits, dim=1) # 对 logit 分数做 softmax 运算

    pred_dict = {}

    top_n = torch.topk(pred_softmax, n) # 取置信度最大的 n 个结果
    pred_ids = top_n[1].cpu().detach().numpy().squeeze() # 解析出类别
    
    # top-n 预测结果
    for i in range(1, n+1):
        pred_dict['top-{}-预测ID'.format(i)] = pred_ids[i-1]
        pred_dict['top-{}-预测名称'.format(i)] = idx_to_labels[pred_ids[i-1]]
    pred_dict['top-n预测正确'] = row['标注类别ID'] in pred_ids
    # 每个类别的预测置信度
    for idx, each in enumerate(classes):
        pred_dict['{}-预测置信度'.format(each)] = pred_softmax[0][idx].cpu().detach().numpy()
        
    df_pred = df_pred.append(pred_dict, ignore_index=True)
df_pred

image-20230222194856131

7. 拼接AB两张表格

df = pd.concat([df, df_pred], axis=1)
df

image-20230222195023381

8. 导出完整表格

df.to_csv('测试集预测结果.csv', index=False)

四、准确率、混淆矩阵、PR曲线、ROC曲线

公共部分

导入工具包&设置Matplotlib中文字体

import pandas as pd
import numpy as np
from tqdm import tqdm

import math
import cv2

import matplotlib.pyplot as plt
%matplotlib inline

# windows操作系统
plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签 
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号

载入类别名称和ID&测试集预测结果表格

idx_to_labels = np.load('idx_to_labels.npy', allow_pickle=True).item()
# 获得类别名称
classes = list(idx_to_labels.values())

df = pd.read_csv('测试集预测结果.csv')

1. 准确率

  • **准确率(accuracy)**是指分类正确的样本数占总样本数的比例。

1.1 测试集总体准确率

sum(df['标注类别名称'] == df['top-1-预测名称']) / len(df)

1.2 测试集总体top-n准确率

sum(df['top-n预测正确']) / len(df)

1.3 各类其他的指标

from sklearn.metrics import classification_report

report = classification_report(df['标注类别名称'], df['top-1-预测名称'], target_names=classes, output_dict=True)
del report['accuracy']
df_report = pd.DataFrame(report).transpose()
df_report

image-20230222200619002

1.4 各类别准确率(其实就是recall)

accuracy_list = []
for fruit in tqdm(classes):
    df_temp = df[df['标注类别名称']==fruit]
    accuracy = sum(df_temp['标注类别名称'] == df_temp['top-1-预测名称']) / len(df_temp)
    accuracy_list.append(accuracy)
    
# 计算 宏平均准确率 和 加权平均准确率
acc_macro = np.mean(accuracy_list)
acc_weighted = sum(accuracy_list * df_report.iloc[:-2]['support'] / len(df))

accuracy_list.append(acc_macro)
accuracy_list.append(acc_weighted)

df_report['accuracy'] = accuracy_list
df_report.to_csv('各类别准确率评估指标.csv', index_label='类别')
df_report

image-20230222200822594

2. 混淆矩阵

混淆矩阵(Confusion Matrix)是一种常用的分类模型性能评估工具,通常用于衡量分类模型的预测准确性和误差类型。

混淆矩阵是一个二维矩阵,其中每一行代表着真实标签,每一列代表着预测标签,矩阵中的每个元素代表着对应真实标签和预测标签下的样本数量。因此,对于一个二分类模型而言,混淆矩阵的形式如下:

预测为 0预测为 1
真实标签为 0TNFP
真实标签为 0FNTP

其中,TN(True Negative)表示真实标签为 0,预测标签也为 0 的样本数量,FP(False Positive)表示真实标签为 0,但预测标签为 1 的样本数量,FN(False Negative)表示真实标签为 1,但预测标签为 0 的样本数量,TP(True Positive)表示真实标签为 1,预测标签也为 1 的样本数量。

在多分类问题中,混淆矩阵的形式会有所不同。例如,对于一个三分类模型而言,混淆矩阵的形式如下:

预测为类别 0预测为类别 1预测为类别 2
真实标签为类别 0TN(0,0)FP(0,1)FP(0,2)
真实标签为类别 1FP(1,0)TN(1,1)FP(1,2)
真实标签为类别 2FP(2,0)FP(2,1)TN(2,2)

混淆矩阵的每一个元素都可以通过分类模型的预测结果和真实标签统计得到。在得到混淆矩阵后,我们可以进一步计算出一些常用的分类指标,例如准确率(accuracy)、精确度(Precision)、召回率(Recall)、F1 值等,来评估模型的性能。

  1. **准确率(accuracy)**是指分类正确的样本数占总样本数的比例,即:
    Accuracy = TP+TN TP+TN+FP+FN \text{Accuracy} = \frac{\text{TP+TN}}{\text{TP+TN+FP+FN}} Accuracy=TP+TN+FP+FNTP+TN
    其中,TP表示真正例(True Positive),TN表示真负例(True Negative),FP表示假正例(False Positive),FN表示假负例(False Negative)。

  2. **精确度(precision)**是指模型预测为正例中,真正例的比例,即:
    Precision = TP TP+FP \text{Precision} = \frac{\text{TP}}{\text{TP+FP}} Precision=TP+FPTP

  3. **召回率(recall)**是指真正例中,模型预测为正例的比例,即:
    Recall = TP TP+FN \text{Recall} = \frac{\text{TP}}{\text{TP+FN}} Recall=TP+FNTP

  4. **F1值 **是精确度和召回率的加权平均,其中1表示对精确度和召回率的平衡赋予相同的重要性,即:
    F1-score = 2 × Precision × Recall Precision + Recall \text{F1-score} = 2 \times \frac{\text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}} F1-score=2×Precision+RecallPrecision×Recall
    其中,F1值的取值范围为0到1,其值越大则表示模型的性能越好。

2.1 生成混淆矩阵

from sklearn.metrics import confusion_matrix

confusion_matrix_model = confusion_matrix(df['标注类别名称'], df['top-1-预测名称'])

2.2 可视化混淆矩阵(模板)

import itertools
def cnf_matrix_plotter(cm, classes, cmap=plt.cm.Blues):
    """
    传入混淆矩阵和标签名称列表,绘制混淆矩阵
    """
    plt.figure(figsize=(10, 10))
    
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    # plt.colorbar() # 色条
    tick_marks = np.arange(len(classes))
    
    plt.title('混淆矩阵', fontsize=30)
    plt.xlabel('预测类别', fontsize=25, c='r')
    plt.ylabel('真实类别', fontsize=25, c='r')
    plt.tick_params(labelsize=16) # 设置类别文字大小
    plt.xticks(tick_marks, classes, rotation=90) # 横轴文字旋转
    plt.yticks(tick_marks, classes)
    
    # 写数字
    threshold = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > threshold else "black",
                 fontsize=12)

    plt.tight_layout()

    plt.savefig('混淆矩阵.pdf', dpi=300) # 保存图像
    plt.show()
cnf_matrix_plotter(confusion_matrix_model, classes, cmap='Blues')

image-20230222202845366

2.3 筛选出测试集中,真实为A类,但被误判为B类的图像

true_A = '荔枝'
pred_B = '杨梅'

wrong_df = df[(df['标注类别名称']==true_A)&(df['top-1-预测名称']==pred_B)]
wrong_df

image-20230222203149241

2.4 可视化表中所有被误判的图像

for idx, row in wrong_df.iterrows():
    img_path = row['图像路径']
    # img_bgr = cv2.imread(img_path)
    img_bgr = cv2.imdecode(np.fromfile(img_path,dtype=np.uint8),cv2.IMREAD_COLOR)
    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
    plt.imshow(img_rgb)
    title_str = img_path + '\nTrue:' + row['标注类别名称'] + ' Pred:' + row['top-1-预测名称']
    plt.title(title_str)
    plt.show()

image-20230222203243413

3. PR曲线

PR 曲线(Precision-Recall Curve)是一种用于二分类器评估的可视化工具,可以帮助我们更好地理解分类器的表现。

PR 曲线基于精确度(Precision)和召回率(Recall)这两个指标,其中精确度是指分类器预测的正类别样本中真正是正类别的比例,召回率是指真正是正类别的样本中被分类器正确预测为正类别的比例。

在 PR 曲线中,我们将分类器的 不同阈值下的 Precision 和 Recall 作为横纵坐标,分别绘制出来。一般而言,PR 曲线下的面积越大,说明分类器的表现越好。PR 曲线关注的是分类器不同阈值下的精确度和召回率。
Precision = TP TP+FP Recall = TP TP+FN \text{Precision} = \frac{\text{TP}}{\text{TP+FP}}\\ \text{Recall} = \frac{\text{TP}}{\text{TP+FN}} Precision=TP+FPTPRecall=TP+FNTP

3.1 绘制某一类别的PR曲线

specific_class = '荔枝'

# 二分类标注
y_test = (df['标注类别名称'] == specific_class)

# 二分类预测置信度
y_score = df['荔枝-预测置信度']

from sklearn.metrics import precision_recall_curve
from sklearn.metrics import average_precision_score
precision, recall, thresholds = precision_recall_curve(y_test, y_score)
AP = average_precision_score(y_test, y_score, average='weighted')

plt.figure(figsize=(12, 8))
# 绘制 PR 曲线
plt.plot(recall, precision, linewidth=5, label=specific_class)

# 随机二分类模型
# 阈值小,所有样本都被预测为正类,recall为1,precision为正样本百分比
# 阈值大,所有样本都被预测为负类,recall为0,precision波动较大
plt.plot([0, 0], [0, 1], ls="--", c='.3', linewidth=3, label='随机模型')
plt.plot([0, 1], [0.5, sum(y_test==1)/len(df)], ls="--", c='.3', linewidth=3)

plt.xlim([-0.01, 1.0])
plt.ylim([0.0, 1.01])
plt.rcParams['font.size'] = 22
plt.title('{} PR曲线  AP:{:.3f}'.format(specific_class, AP))
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.legend()
plt.grid(True)
plt.savefig('{}-PR曲线.pdf'.format(specific_class), dpi=120, bbox_inches='tight')
plt.show()

image-20230222205146868

3.2 绘制所有类别的PR曲线

from matplotlib import colors as mcolors
import random
random.seed(124)
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan', 'black', 'indianred', 'brown', 'firebrick', 'maroon', 'darkred', 'red', 'sienna', 'chocolate', 'yellow', 'olivedrab', 'yellowgreen', 'darkolivegreen', 'forestgreen', 'limegreen', 'darkgreen', 'green', 'lime', 'seagreen', 'mediumseagreen', 'darkslategray', 'darkslategrey', 'teal', 'darkcyan', 'dodgerblue', 'navy', 'darkblue', 'mediumblue', 'blue', 'slateblue', 'darkslateblue', 'mediumslateblue', 'mediumpurple', 'rebeccapurple', 'blueviolet', 'indigo', 'darkorchid', 'darkviolet', 'mediumorchid', 'purple', 'darkmagenta', 'fuchsia', 'magenta', 'orchid', 'mediumvioletred', 'deeppink', 'hotpink']
markers = [".",",","o","v","^","<",">","1","2","3","4","8","s","p","P","*","h","H","+","x","X","D","d","|","_",0,1,2,3,4,5,6,7,8,9,10,11]
linestyle = ['--', '-.', '-']

def get_line_arg():
    '''
    随机产生一种绘图线型
    '''
    line_arg = {}
    line_arg['color'] = random.choice(colors)
    # line_arg['marker'] = random.choice(markers)
    line_arg['linestyle'] = random.choice(linestyle)
    line_arg['linewidth'] = random.randint(1, 4)
    # line_arg['markersize'] = random.randint(3, 5)
    return line_arg

plt.figure(figsize=(14, 10))
plt.xlim([-0.01, 1.0])
plt.ylim([0.0, 1.01])
# plt.plot([0, 1], [0, 1],ls="--", c='.3', linewidth=3, label='随机模型')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.rcParams['font.size'] = 22
plt.grid(True)

ap_list = []
for each_class in classes:
    y_test = list((df['标注类别名称'] == each_class))
    y_score = list(df['{}-预测置信度'.format(each_class)])
    precision, recall, thresholds = precision_recall_curve(y_test, y_score)
    AP = average_precision_score(y_test, y_score, average='weighted')
    plt.plot(recall, precision, **get_line_arg(), label=each_class)
    plt.legend()
    ap_list.append(AP)

plt.legend(loc='best', fontsize=12)
plt.savefig('各类别PR曲线.pdf'.format(specific_class), dpi=120, bbox_inches='tight')
plt.show()

image-20230222205348473

3.3 将AP增加至各类别准确率评估指标表格中

AP值,即平均精度(Average Precision),用于衡量模型在多分类问题中的表现。计算过程中,首先需要计算每个类别的精度-召回率曲线(PR曲线),然后计算该曲线下的面积,即为AP值。

# 计算 AP值 的 宏平均 和 加权平均
macro_avg_auc = np.mean(ap_list)
weighted_avg_auc = sum(ap_list * df_report.iloc[:-2]['support'] / len(df))

ap_list.append(macro_avg_auc)
ap_list.append(weighted_avg_auc)

df_report['AP'] = ap_list

# 保存表格
df_report.to_csv('各类别准确率评估指标.csv', index=False)
df_report

image-20230222205745121

4. ROC曲线

ROC 曲线(Receiver Operating Characteristic curve)是一种用于评估二元分类模型性能的曲线。ROC 曲线的 横轴是假阳性率(False Positive Rate,FPR),纵轴是真阳性率(True Positive Rate,TPR

在二元分类中,模型的输出是一个概率值或得分,模型将其与一个阈值进行比较,将概率大于或等于该阈值的样本分类为正类,将概率小于该阈值的样本分类为负类。因此,ROC 曲线反映了模型在不同阈值下的真阳性率和假阳性率之间的关系。当阈值为 0 时,模型将所有样本都预测为正类,此时真阳性率为 1,假阳性率为 1;当阈值为 1 时,模型将所有样本都预测为负类,此时真阳性率为 0,假阳性率为 0。

ROC 曲线越靠近左上角,则代表该分类器的性能越好,因为此时真阳性率高、假阳性率低。通常会使用 **ROC 曲线下的面积(Area Under Curve,AUC)**来评估分类器的性能。AUC 的取值范围在 0 到 1 之间,当 AUC=0.5 时,表示分类器的性能等同于随机猜测;当 AUC=1 时,表示分类器完美分类。
$$
FPR=\frac{FP}{FP+TN}\

TPR=\frac{TP}{TP+FN}
$$

4.1 绘制某一类别的ROC曲线

specific_class = '荔枝'

# 二分类标注
y_test = (df['标注类别名称'] == specific_class)

# 二分类置信度
y_score = df['荔枝-预测置信度']

from sklearn.metrics import roc_curve, auc
fpr, tpr, threshold = roc_curve(y_test, y_score)

plt.figure(figsize=(12, 8))
plt.plot(fpr, tpr, linewidth=5, label=specific_class)
plt.plot([0, 1], [0, 1],ls="--", c='.3', linewidth=3, label='随机模型')
plt.xlim([-0.01, 1.0])
plt.ylim([0.0, 1.01])
plt.rcParams['font.size'] = 22
plt.title('{} ROC曲线  AUC:{:.3f}'.format(specific_class, auc(fpr, tpr)))
plt.xlabel('False Positive Rate (1 - Specificity)')
plt.ylabel('True Positive Rate (Sensitivity)')
plt.legend()
plt.grid(True)

plt.savefig('{}-ROC曲线.pdf'.format(specific_class), dpi=120, bbox_inches='tight')
plt.show()

# yticks = ax.yaxis.get_major_ticks()
# yticks[0].label1.set_visible(False)

image-20230223100053844

4.2 绘制所有类别的ROC曲线

from matplotlib import colors as mcolors
import random
random.seed(124)
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan', 'black', 'indianred', 'brown', 'firebrick', 'maroon', 'darkred', 'red', 'sienna', 'chocolate', 'yellow', 'olivedrab', 'yellowgreen', 'darkolivegreen', 'forestgreen', 'limegreen', 'darkgreen', 'green', 'lime', 'seagreen', 'mediumseagreen', 'darkslategray', 'darkslategrey', 'teal', 'darkcyan', 'dodgerblue', 'navy', 'darkblue', 'mediumblue', 'blue', 'slateblue', 'darkslateblue', 'mediumslateblue', 'mediumpurple', 'rebeccapurple', 'blueviolet', 'indigo', 'darkorchid', 'darkviolet', 'mediumorchid', 'purple', 'darkmagenta', 'fuchsia', 'magenta', 'orchid', 'mediumvioletred', 'deeppink', 'hotpink']
markers = [".",",","o","v","^","<",">","1","2","3","4","8","s","p","P","*","h","H","+","x","X","D","d","|","_",0,1,2,3,4,5,6,7,8,9,10,11]
linestyle = ['--', '-.', '-']

def get_line_arg():
    '''
    随机产生一种绘图线型
    '''
    line_arg = {}
    line_arg['color'] = random.choice(colors)
    # line_arg['marker'] = random.choice(markers)
    line_arg['linestyle'] = random.choice(linestyle)
    line_arg['linewidth'] = random.randint(1, 4)
    # line_arg['markersize'] = random.randint(3, 5)
    return line_arg

plt.figure(figsize=(14, 10))
plt.xlim([-0.01, 1.0])
plt.ylim([0.0, 1.01])
plt.plot([0, 1], [0, 1],ls="--", c='.3', linewidth=3, label='随机模型')
plt.xlabel('False Positive Rate (1 - Specificity)')
plt.ylabel('True Positive Rate (Sensitivity)')
plt.rcParams['font.size'] = 22
plt.grid(True)

auc_list = []
for each_class in classes:
    y_test = list((df['标注类别名称'] == each_class))
    y_score = list(df['{}-预测置信度'.format(each_class)])
    fpr, tpr, threshold = roc_curve(y_test, y_score)
    plt.plot(fpr, tpr, **get_line_arg(), label=each_class)
    plt.legend()
    auc_list.append(auc(fpr, tpr))

plt.legend(loc='best', fontsize=12)
plt.savefig('各类别ROC曲线.pdf'.format(specific_class), dpi=120, bbox_inches='tight')
plt.show()

image-20230223100605624

4.3 将AUC增加至各类别准确率评估指标表格中

df_report = pd.read_csv('各类别准确率评估指标.csv')

# 计算 AUC值 的 宏平均 和 加权平均
macro_avg_auc = np.mean(auc_list)
weighted_avg_auc = sum(auc_list * df_report.iloc[:-2]['support'] / len(df))

auc_list.append(macro_avg_auc)
auc_list.append(weighted_avg_auc)

df_report['AUC'] = auc_list

df_report.to_csv('各类别准确率评估指标.csv', index=False)
df_report

image-20230223101243984

5. 绘制各类别准确率评估指标柱状图

5.1 导入各类别准确率评估指标表格

df = pd.read_csv('各类别准确率评估指标.csv')

5.2 选择评估指标

# feature = 'precision'
# feature = 'recall'
# feature = 'f1-score'
feature = 'accuracy'
# feature = 'AP'
# feature = 'AUC'

5.3 绘制柱状图

df_plot = df.sort_values(by=feature, ascending=False)

plt.figure(figsize=(22, 7))

x = df_plot['类别']
y = df_plot[feature]

ax = plt.bar(x, y, width=0.6, facecolor='#1f77b4', edgecolor='k')
plt.bar_label(ax, fmt='%.2f', fontsize=15) # 置信度数值

plt.xticks(rotation=45)
plt.tick_params(labelsize=15)
# plt.xlabel('类别', fontsize=20)
plt.ylabel(feature, fontsize=20)
plt.title('准确率评估指标 {}'.format(feature), fontsize=25)

plt.savefig('各类别准确率评估指标柱状图-{}.pdf'.format(feature), dpi=120, bbox_inches='tight')

plt.show()

image-20230223101541306

五、图像分类测试集语义特征降维可视化

可视化卷积神经网络:https://www.bilibili.com/video/BV1K7411W7So?p=6&vd_source=42d3114e0cbd425b127080837c741e59

  • 带全连接层的卷积神经网络(eg:VGG):

    用最后一层全连接层(输出层的前一层)输出的结果作为语义特征。

image-20230223160416993

image-20230223160213312

  • 无全连接层的卷积神经网络(eg:本次学习中用到的预训练模型——resnet18)

    用最后一层卷积层进行 全局平均池化(global average pooling),如下图为最后一层卷积层输出的四个通道,每个通道各自求平均值,就把最后一层卷积层的 feature map 变成了 4 个标量,这四个标量组成了一个 4 维的向量,这个 4 维的向量就变成了语义特征。再用这 4 为维的语义特征去做线性的分类得到 n 个类别的 logit 分数,再通过 softmax 得到 n 个类别的后验置信度。

    image-20230223160337797

1. 计算测试集图像语义特征

抽取Pytorch训练得到的图像分类模型中间层的输出特征,作为输入图像的语义特征。计算测试集所有图像的语义特征,使用t-SNE和UMAP两种降维方法降维至二维和三维,可视化。分析不同类别的语义距离、异常数据、细粒度分类、高维数据结构。

1.1 导入工具包

from tqdm import tqdm

import pandas as pd
import numpy as np

import torch

import cv2
from PIL import Image

# 忽略烦人的红色提示
import warnings
warnings.filterwarnings("ignore")

# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('device', device)

1.2 定义测试集图像预处理

from torchvision import transforms

# 测试集图像预处理-RCTN:缩放、裁剪、转 Tensor、归一化
test_transform = transforms.Compose([transforms.Resize(256),
                                     transforms.CenterCrop(224),
                                     transforms.ToTensor(),
                                     transforms.Normalize(
                                         mean=[0.485, 0.456, 0.406], 
                                         std=[0.229, 0.224, 0.225])
                                    ])

1.3 导入训练好的模型

model = torch.load('checkpoints/best-0.880.pth')
model = model.eval().to(device)

1.4 抽取模型中间层输出结果作为语义特征

from torchvision.models.feature_extraction import create_feature_extractor

model_trunc = create_feature_extractor(model, return_nodes={'avgpool': 'semantic_feature'})

这段代码是用来创建一个 **特征提取器(feature extractor)**的,用于从预训练模型中提取出特征。在这里,我们使用了 PyTorch 中的 create_feature_extractor 函数,该函数可以帮助我们创建一个新的模型,该模型会 **截取(truncate)**原始模型的一部分,并返回我们指定的节点作为输出。在这里,我们使用了一个预训练模型 model(resnet,无全连接层),并指定了返回 avgpool 节点的输出作为 语义特征(semantic feature)。这个语义特征可以被用来训练一个分类器,比如用来识别图片中的物体。

1.5 计算单张图片的语义特征

img_path = 'fruit30_split/val/菠萝/105.jpg'
img_pil = Image.open(img_path)
input_img = test_transform(img_pil) # 预处理
input_img = input_img.unsqueeze(0).to(device)
# 执行前向预测,得到指定中间层的输出
pred_logits = model_trunc(input_img) 

# 保存为本地的 npy 文件
np.save('测试集语义特征.npy', encoding_array)

1.6 计算测试集每张图像的语义特征

# 载入测试集图像分类结果
df = pd.read_csv('测试集预测结果.csv')

encoding_array = []
img_path_list = []

for img_path in tqdm(df['图像路径']):
    img_path_list.append(img_path)
    img_pil = Image.open(img_path).convert('RGB')
    input_img = test_transform(img_pil).unsqueeze(0).to(device) # 预处理
    feature = model_trunc(input_img)['semantic_feature'].squeeze().detach().cpu().numpy() # 执行前向预测,得到 avgpool 层输出的语义特征
    encoding_array.append(feature)
encoding_array = np.array(encoding_array)

# 保存为本地的 npy 文件
np.save('测试集语义特征.npy', encoding_array)
  1. 载入一个CSV文件,包含了测试集中每个图像的路径和分类结果。
  2. 创建一个空的编码数组和一个图像路径列表。
  3. 使用一个循环遍历了测试集中的每个图像路径,将每个图像转换为PIL格式,进行预处理后将其传递给已经预训练的模型,从中提取出avgpool层的输出的语义特征,然后将该特征添加到编码数组中。
  4. 将编码数组保存到本地的.npy文件中,以便后续的分析和处理。

UMAP(Uniform Manifold Approximation and Projection)和t-SNE(t-Distributed Stochastic Neighbor Embedding)都是流行的降维算法,它们的目标都是将高维数据映射到低维空间中,以便可视化或聚类。

UMAP相对于t-SNE有以下几个优势:

  1. 更快的运行速度:UMAP通常比t-SNE更快,特别是在处理大型数据集时。这是因为UMAP使用了一些优化策略,如贪心算法和k-近邻图剪枝,可以有效地减少计算量。
  2. 更好的可伸缩性:UMAP在大型数据集上的表现比t-SNE更好。这是因为UMAP的计算复杂度是基于样本数量的对数级别,而t-SNE的计算复杂度是基于样本数量的平方级别。
  3. 更好的保留全局结构:t-SNE在保留全局结构方面的表现可能不如UMAP。这是因为t-SNE倾向于聚焦于局部结构,而UMAP能够更好地平衡全局和局部结构之间的权衡。
  4. 更灵活的参数设置:UMAP的参数比t-SNE更灵活,可以更好地控制降维过程中的全局和局部结构。例如,UMAP中的重要参数包括邻居数量、距离度量函数和最小距离等。

需要注意的是,UMAP和t-SNE之间的差异并不是绝对的,实际上它们的表现取决于数据集的特征和所需的结果。在实践中,为了得到最佳结果,可以尝试使用不同的算法和参数设置,并评估它们在特定数据集上的表现。

2. 语义特征t-SNE降维可视化

2.1 导入工具包、设置matplotlib中文字体

import numpy as np
import pandas as pd
import cv2
import matplotlib.pyplot as plt

# windows操作系统设置matplotlib中文字体
plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签 
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号

2.2 载入测试集语义特征、载入测试集图像分类结果

encoding_array = np.load('测试集语义特征.npy', allow_pickle=True)

df = pd.read_csv('测试集预测结果.csv')
classes = df['标注类别名称'].unique()

2.3 可视化配置

import seaborn as sns
marker_list = ['.', ',', 'o', 'v', '^', '<', '>', '1', '2', '3', '4', '8', 's', 'p', 'P', '*', 'h', 'H', '+', 'x', 'X', 'D', 'd', '|', '_', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

class_list = np.unique(df['标注类别名称'])

n_class = len(class_list) # 测试集标签类别数
palette = sns.hls_palette(n_class) # 配色方案
sns.palplot(palette)

image-20230223152659869

# 随机打乱颜色列表和点型列表
import random
random.seed(1234)
random.shuffle(marker_list)
random.shuffle(palette)

2.4 t-SNE降维至二维

# 降维到二维和三维
from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, n_iter=20000)
X_tsne_2d = tsne.fit_transform(encoding_array)
  1. n_components参数指定了要降到的维数,这里设为2。
  2. n_iter参数指定了t-SNE的迭代次数,这里设为20000。
  3. 最终结果被存储在X_tsne_2d中,可以用于可视化操作。

2.5 可视化展示

# 不同的 符号 表示 不同的 标注类别
show_feature = '标注类别名称'

plt.figure(figsize=(14, 14))
for idx, fruit in enumerate(class_list): # 遍历每个类别
    # 获取颜色和点型
    color = palette[idx]
    marker = marker_list[idx%len(marker_list)]

    # 找到所有标注类别为当前类别的图像索引号
    indices = np.where(df[show_feature]==fruit)
    plt.scatter(X_tsne_2d[indices, 0], X_tsne_2d[indices, 1], color=color, marker=marker, label=fruit, s=150)

plt.legend(fontsize=16, markerscale=1, bbox_to_anchor=(1, 1))
plt.xticks([])
plt.yticks([])
plt.savefig('语义特征t-SNE二维降维可视化.pdf', dpi=300) # 保存图像
plt.show()
  • 使用循环遍历每个类别,在二维空间中绘制不同的样本点,颜色和点型代表不同类别。
    • 用indices变量获取所有标注类别为当前类别的图像索引号。
    • 使用plt.scatter方法在二维空间中绘制样本点。
  • 最后,添加图例、去除坐标轴标签,保存图像,并展示出来。

image-20230223153317544

2.6 plotply交互式可视化

import plotly.express as px

df_2d = pd.DataFrame()
df_2d['X'] = list(X_tsne_2d[:, 0].squeeze())
df_2d['Y'] = list(X_tsne_2d[:, 1].squeeze())
df_2d['标注类别名称'] = df['标注类别名称']
df_2d['预测类别'] = df['top-1-预测名称']
df_2d['图像路径'] = df['图像路径']
df_2d.to_csv('t-SNE-2D.csv', index=False)
df_2d

image-20230223153459278

fig = px.scatter(df_2d, 
                 x='X', 
                 y='Y',
                 color=show_feature, 
                 labels=show_feature,
                 symbol=show_feature, 
                 hover_name='图像路径',
                 opacity=0.8,
                 width=1000, 
                 height=600
                )
# 设置排版
fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
fig.show()
fig.write_html('语义特征t-SNE二维降维plotly可视化.html')

image-20230223154148328

# 查看异常聚类的图像(这里是一堆荔枝中出现了些杨梅)
img_path_temp = 'fruit30_split/val/杨梅/127.jpg'
# img_bgr = cv2.imread(img_path_temp)
img_bgr = cv2.imdecode(np.fromfile(img_path_temp,dtype=np.uint8),cv2.IMREAD_COLOR)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
temp_df = df[df['图像路径'] == img_path_temp]
title_str = img_path_temp + '\nTrue:' + temp_df['标注类别名称'].item() + ' Pred:' + temp_df['top-1-预测名称'].item()
plt.title(title_str)
plt.show()

image-20230223154219087

2.7 t-SNE降维至三维,并可视化

# 降维到三维
from sklearn.manifold import TSNE
tsne = TSNE(n_components=3, n_iter=10000)
X_tsne_3d = tsne.fit_transform(encoding_array)

show_feature = '标注类别名称'
# show_feature = '预测类别'

df_3d = pd.DataFrame()
df_3d['X'] = list(X_tsne_3d[:, 0].squeeze())
df_3d['Y'] = list(X_tsne_3d[:, 1].squeeze())
df_3d['Z'] = list(X_tsne_3d[:, 2].squeeze())
df_3d['标注类别名称'] = df['标注类别名称']
df_3d['预测类别'] = df['top-1-预测名称']
df_3d['图像路径'] = df['图像路径']
df_3d.to_csv('t-SNE-3D.csv', index=False)
df_3d

image-20230223154500944

fig = px.scatter_3d(df_3d, 
                    x='X', 
                    y='Y', 
                    z='Z',
                    color=show_feature, 
                    labels=show_feature,
                    symbol=show_feature, 
                    hover_name='图像路径',
                    opacity=0.6,
                    width=1000, 
                    height=800)

# 设置排版
fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
fig.show()
fig.write_html('语义特征t-SNE三维降维plotly可视化.html')

image-20230223154730804

3. 语义特征UMAP降维可视化

3.1 安装UMAP、导入工具包、设置matplotlib中文字体

# 安装UMAP
# 官方文档:https://umap-learn.readthedocs.io/en/latest/index.html
!pip install umap-learn datashader bokeh holoviews scikit-image colorcet

# 导入工具包
import numpy as np
import pandas as pd
import cv2
import matplotlib.pyplot as plt

# 设置matplotlib中文字体
# windows操作系统
plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签 
plt.rcParams['axes.unicode_minus']=False  # 用来正常显示负号

3.2 载入测试集图像语义特征、载入测试集图像分类结果

encoding_array = np.load('测试集语义特征.npy', allow_pickle=True)

df = pd.read_csv('测试集预测结果.csv')
classes = df['标注类别名称'].unique()

3.3 可视化配置

import seaborn as sns
marker_list = ['.', ',', 'o', 'v', '^', '<', '>', '1', '2', '3', '4', '8', 's', 'p', 'P', '*', 'h', 'H', '+', 'x', 'X', 'D', 'd', '|', '_', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

class_list = np.unique(df['标注类别名称'])

n_class = len(class_list) # 测试集标签类别数
palette = sns.hls_palette(n_class) # 配色方案
sns.palplot(palette)

image-20230223152659869

# 随机打乱颜色列表和点型列表
import random
random.seed(1234)
random.shuffle(marker_list)
random.shuffle(palette)

3.4 UMAP降维至二维可视化

import umap
import umap.plot

mapper = umap.UMAP(n_neighbors=10, n_components=2, random_state=12).fit(encoding_array)

X_umap_2d = mapper.embedding_


# 不同的 符号 表示 不同的 标注类别
show_feature = '标注类别名称'

plt.figure(figsize=(14, 14))
for idx, fruit in enumerate(class_list): # 遍历每个类别
    # 获取颜色和点型
    color = palette[idx]
    marker = marker_list[idx%len(marker_list)]

    # 找到所有标注类别为当前类别的图像索引号
    indices = np.where(df[show_feature]==fruit)
    plt.scatter(X_umap_2d[indices, 0], X_umap_2d[indices, 1], color=color, marker=marker, label=fruit, s=150)

plt.legend(fontsize=16, markerscale=1, bbox_to_anchor=(1, 1))
plt.xticks([])
plt.yticks([])
plt.savefig('语义特征UMAP二维降维可视化.pdf', dpi=300) # 保存图像
plt.show()

image-20230223182649979

3.5 来了一张新图像,可视化语义特征

UMAP可以方便的加入一张新图片的语义特征,而不像 t-SNE 只能一次性处理所有图像。

# 来了一张新图像,可视化语义特征

# 下载新图像
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/test/0818/test_kiwi.jpg
    
# 导入模型、预处理

import cv2
import torch
from PIL import Image
from torchvision import transforms

# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

model = torch.load('checkpoints/best-0.880.pth')
model = model.eval().to(device)

from torchvision.models.feature_extraction import create_feature_extractor
model_trunc = create_feature_extractor(model, return_nodes={'avgpool': 'semantic_feature'})

# 测试集图像预处理-RCTN:缩放、裁剪、转 Tensor、归一化
test_transform = transforms.Compose([transforms.Resize(256),
                                     transforms.CenterCrop(224),
                                     transforms.ToTensor(),
                                     transforms.Normalize(
                                         mean=[0.485, 0.456, 0.406], 
                                         std=[0.229, 0.224, 0.225])
                                    ])

# 计算新图像的语义特征

img_path = 'test_kiwi.jpg'
img_pil = Image.open(img_path)
input_img = test_transform(img_pil) # 预处理
input_img = input_img.unsqueeze(0).to(device)
# 执行前向预测,得到指定中间层的输出
pred_logits = model_trunc(input_img)
semantic_feature = pred_logits['semantic_feature'].squeeze().detach().cpu().numpy().reshape(1,-1)

# 对新图像语义特征降维
# umap降维
new_embedding = mapper.transform(semantic_feature)[0]

plt.figure(figsize=(14, 14))
for idx, fruit in enumerate(class_list): # 遍历每个类别
    # 获取颜色和点型
    color = palette[idx]
    marker = marker_list[idx%len(marker_list)]

    # 找到所有标注类别为当前类别的图像索引号
    indices = np.where(df[show_feature]==fruit)
    plt.scatter(X_umap_2d[indices, 0], X_umap_2d[indices, 1], color=color, marker=marker, label=fruit, s=150)

plt.scatter(new_embedding[0], new_embedding[1], color='r', marker='X', label=img_path, s=1000)

plt.legend(fontsize=16, markerscale=1, bbox_to_anchor=(1, 1))
plt.xticks([])
plt.yticks([])
plt.savefig('语义特征UMAP二维降维可视化-新图像.pdf', dpi=300) # 保存图像
plt.show()

image-20230223183716504

3.6 plotply交互式可视化

import plotly.express as px

df_2d = pd.DataFrame()
df_2d['X'] = list(X_umap_2d[:, 0].squeeze())
df_2d['Y'] = list(X_umap_2d[:, 1].squeeze())
df_2d['标注类别名称'] = df['标注类别名称']
df_2d['预测类别'] = df['top-1-预测名称']
df_2d['图像路径'] = df['图像路径']
df_2d.to_csv('UMAP-2D.csv', index=False)

# 增加新图像的一行
new_img_row = {
    'X':new_embedding[0],
    'Y':new_embedding[1],
    '标注类别名称':img_path,
    '图像路径':img_path
}

df_2d = df_2d.append(new_img_row, ignore_index=True)

fig = px.scatter(df_2d, 
                 x='X', 
                 y='Y',
                 color=show_feature, 
                 labels=show_feature,
                 symbol=show_feature, 
                 hover_name='图像路径',
                 opacity=0.8,
                 width=1000, 
                 height=600
                )
# 设置排版
fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
fig.show()
fig.write_html('语义特征UMAP二维降维plotly可视化.html')

image-20230223184852477

# 查看图像(一堆杨梅里面混了一个荔枝的图片)
img_path_temp = 'fruit30_split/val/荔枝/66.jpg'
# img_bgr = cv2.imread(img_path_temp)
img_bgr = cv2.imdecode(np.fromfile(img_path_temp,dtype=np.uint8),cv2.IMREAD_COLOR)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
temp_df = df[df['图像路径'] == img_path_temp]
title_str = img_path_temp + '\nTrue:' + temp_df['标注类别名称'].item() + ' Pred:' + temp_df['top-1-预测名称'].item()
plt.title(title_str)
plt.show()

image-20230223184738599

3.7 UMAP降维至三维,并可视化

mapper = umap.UMAP(n_neighbors=10, n_components=3, random_state=12).fit(encoding_array)

X_umap_3d = mapper.embedding_

show_feature = '标注类别名称'
# show_feature = '预测类别'

df_3d = pd.DataFrame()
df_3d['X'] = list(X_umap_3d[:, 0].squeeze())
df_3d['Y'] = list(X_umap_3d[:, 1].squeeze())
df_3d['Z'] = list(X_umap_3d[:, 2].squeeze())
df_3d['标注类别名称'] = df['标注类别名称']
df_3d['预测类别'] = df['top-1-预测名称']
df_3d['图像路径'] = df['图像路径']
df_3d.to_csv('UMAP-3D.csv', index=False)
df_3d

image-20230223185306481

fig = px.scatter_3d(df_3d, 
                    x='X', 
                    y='Y', 
                    z='Z',
                    color=show_feature, 
                    labels=show_feature,
                    symbol=show_feature, 
                    hover_name='图像路径',
                    opacity=0.6,
                    width=1000, 
                    height=800)

# 设置排版
fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
fig.show()
fig.write_html('语义特征UMAP三维降维plotly可视化.html')

image-20230223185042756

3.8 来了一张新图像,可视化语义特征

# umap降维
new_embedding = mapper.transform(semantic_feature)[0]

# 增加新图像的一行
new_img_row = {
    'X':new_embedding[0],
    'Y':new_embedding[1],
    'Z':new_embedding[2],
    '标注类别名称':img_path,
    '图像路径':img_path
}

df_3d = df_3d.append(new_img_row, ignore_index=True)
df_3d

image-20230223184124073

fig = px.scatter_3d(df_3d, 
                    x='X', 
                    y='Y', 
                    z='Z',
                    color=show_feature, 
                    labels=show_feature,
                    symbol=show_feature, 
                    hover_name='图像路径',
                    opacity=0.6,
                    width=1000, 
                    height=800)

# 设置排版
fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
fig.show()
fig.write_html('语义特征UMAP三维降维plotly可视化.html')

image-20230223184240709

4. 思考

  1. 语义特征图的符号,应该显示标注类别,还是预测类别?

    语义特征图的符号应该显示标注类别,因为语义特征的目的是提取图像中的语义信息,标注类别是对图像语义信息的概括和描述,预测类别是对图像语义信息的推断和预测,两者在可视化中有所区别,应该优先显示标注类别。

  2. 如果同一个类别,语义特征降维可视化后却有两个或多个聚类簇,可能原因是什么?

    该类别内部存在多个子类别或者类别具有多样性,而模型对这些子类别或多样性进行了区分,导致在低维度空间中出现了多个聚类簇。

  3. 如果一个类别里混入了另一个类别的图像,可能原因是什么?

    1. 标注错误:在数据集标注时,可能因为各种原因出现标注错误,导致图像被错误地归为了某个类别。
    2. 图像中包含多类别:有些图像可能包含多种不同的物体或者场景,而这些物体或场景又属于不同的类别,因此将这些图像归为一个类别可能会导致混淆。
    3. 太像:一些图像非常相似,以至于很难从视觉上区分它们所属的类别,这可能会导致错误的分类。
    4. 细粒度分类:一些类别在细节方面差异很小,如品种、型号等,这就需要更精细的分类方法来区分它们,否则可能会将它们归为同一类别而造成混淆。
  4. 取由浅至深不同中间层的输出结果作为语义特征,效果会有何不同?

    在神经网络中,不同层的输出结果反映了不同的信息,通常来说,越靠近输入层的层学到的是低级特征(如边缘、角落等),而越靠近输出层的层学到的是高级特征(如物体的形状、结构等)。因此,使用不同层的输出作为语义特征,效果会有所不同。

    一般来说,使用较浅的层(如卷积层)的输出作为语义特征,能够更好地保留物体的局部特征,对于细粒度分类等任务效果比较好;使用较深的层(如全连接层)的输出作为语义特征,能够更好地捕捉物体的全局信息,对于大类别分类等任务效果比较好。在实际应用中,可以根据具体任务需要灵活选择不同层的输出作为语义特征。

  5. ”神经网络的强大之处,就在于,输入无比复杂的图像像素分布,输出在高维度线性可分的语义特征分布,最后一层分类层就是线性分类器"。如何理解这句话?

    神经网络通过多个层次的计算,将输入的图像像素信息转化为一个高维度的语义特征分布。在这个高维度的特征空间中,不同的类别数据分布区域是线性可分的,也就是说,不同类别的数据在特征空间中可以用一条直线(或超平面)分隔开来。因此,神经网络的最后一层分类层可以通过一个简单的线性分类器来区分不同的类别。这种特征提取和分类的方式被称为端到端的深度学习模型,它可以用来处理各种复杂的图像分类问题,而不需要手工设计特征或分类器。

  6. 如何进一步改进语义特征的可视化?

    可以通过交互式可视化来改进语义特征的可视化,例如在点上增加鼠标悬停事件,点击点后显示对应的图像或者更详细的信息。

    考虑使用谷歌可视化降维工具Embedding Projector https://www.bilibili.com/video/BV1iJ41127wr?p=11

    PS:五万张ImageNet 验证集图像的语义特征降维可视化:https://cs.stanford.edu/people/karpathy/cnnembed/

  7. 如何对视频或者摄像头实时画面绘制语义特征降维点的轨迹?

    要对视频或者摄像头实时画面绘制语义特征降维点的轨迹,可以将每一帧图像提取出语义特征并进行降维,然后在二维空间中绘制点,并用不同的颜色表示不同时间点的点的位置,从而绘制出轨迹。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值