当前位置: 首页 > article >正文

目标检测指标:AP,mAP

目标检测指标:AP,mAP

论文:A Survey on Performance Metrics for Object-Detection Algorithms

文章目录

摘要

AP通过估计precision × recall 曲线的AUC来评估目标检测模型的准确率。

根据precision × recall 曲线使用的插值方法,有两种不同的AP计算方法。

不同工作中和AP实现中缺乏共识是学术和科学界面临的问题。用不同计算语言编写的度量实现通常与相应的数据集一起分发,共享给定的边界框描述。这些项目确实为社区提供了评估工具,但需要额外的工作来适应其他数据集和边界框格式。本工作回顾了用于对象检测的最常用度量标准,分析了它们的差异、应用和主要概念。

1 介绍

一个目标检测模型的输出由包含边界框(bonding boxes,bboxes)、置信度(confidence levels)和类别(classes)的列表组成。然而,不同模型的输出格式却不尽相同。对于bboxes的表示大多使用左上↖️和右下↘️的坐标:
( x i n i , y i n i , x e n d , y e n d ) (x_{ini},y_{ini},x_{end},y_{end}) (xini,yini,xend,yend)
而YOLO的bboxes表示则是采用中心坐标以及宽度和高度:
( x c e n t e r i m a g e   w i d t h , y c e n t e r i m a g e   h e i g h t , b o x   w i d t h i m a g e   w i d t h , b o x   h e i g h t i m a g e   h e i g h t ) (\frac{x_{center}}{image \ width},\frac{y_{center}}{image \ height},\frac{box \ width}{image \ width}, \frac{box \ height}{image \ height}) (image widthxcenter,image heightycenter,image widthbox width,image heightbox height)

在大多目标检测竞赛中,平均精度(average precision,AP)以及它的变种被用做评价指标进行排名。PASCAL VOC提供了AP以及mAP(所有类别AP的平均值)的源码。Lyft 3D自动驾驶车辆物体识别挑战赛,使用了10个不同阈值下AP的平均值,叫做AP@50:5:95

AP的计算是针对一个类别的,而mAP是当前检测数据集中所有类别的AP的平均值。

2 主要的性能指标

TP、FP、FN

计算AP所需要了解的一些概念:

  • True Positive (TP) : 对真实边界框的正确检测
  • False Positive (FP) : 对一个不存在目标的错误检测或者对一个存在目标的错误检测
  • False Negative (FN) : 未检测到的真实边界框;

原文:

  • True Positive (TP) : A correct detection of a ground-truth bounding box;
  • False positive (FP) : An incorrect detection of a nonexistent object or a misplaced detection of an existing object;
  • False negative (FN) : An undetected ground-truth bounding box;

在目标检测中,不使用true negative (TN,对负样本的正确检测) 。

在上面的定义中,需要明确什么是正确检测,什么是错误检测。通常使用**交并比 (intersection over union, IOU)**去界定,公式如下:
J ( B p , B g t ) = I O U = area ⁡ ( B p ∩ B g t ) area ⁡ ( B p ∪ B g t ) J\left(B_p, B_{g t}\right)=\mathrm{IOU}=\frac{\operatorname{area}\left(B_p \cap B_{g t}\right)}{\operatorname{area}\left(B_p \cup B_{g t}\right)} J(Bp,Bgt)=IOU=area(BpBgt)area(BpBgt)
其中, B p B_p Bp B g t B_{gt} Bgt分别表示预测边界框和真实边界框。可视化如下,红框为 B p B_p Bp,绿框为 B g t B_{gt} Bgt,分子为两者交集,分母为两者并集。
在这里插入图片描述
因此,我们可以设置一个阈值 t t t,当预测边界框与真实边界框的IOU大于等于这个阈值的时候,则为正确检测,否则就是错误检测。

IOU检测
≥ t \geq t t正确检测
< t \lt t <t错误检测

因此,可以想象,当要计算一个类别的AP时,需要用哪些东西。以“person”这个类别为例,首先,将数据集中所有类别为“person”的边界框取出,然后将检测模型输出中所有类别为“person”的边界框取出,设定阈值为0.5。然后每个预测边界框计算与真实边界框的IOU,并于阈值比较,大于阈值是TP,小于阈值是FP。另外,在真实边界框中,没有任何一个预测边界框与其IOU大于阈值,那么这个真实边界框就是FN。

在上面的FP中,“对一个不存在目标的错误检测或者对一个存在目标的错误检测”,前者是IOU为0,相当于检测了不存在的目标,后者就是与真实边界框相交了,但相交很少。总之,两者与真实边界框的IOU都小于阈值。

一般情况下,IOU>0.5就认为是比较好了。下图为几种不同的IOU的示例。
在这里插入图片描述

P、R

P   =   T P T P + F P = T P a l l   d e t e c t i o n s P \ = \ \frac{TP}{TP+FP} = \frac{TP}{all \ detections} P = TP+FPTP=all detectionsTP
P,precision,精确率,也叫查准率,预测正确的边界框占所有预测边界框的比例。
R   =   T P T P + F N = T P a l l   g r o u n d   t r u t h s R \ = \ \frac{TP}{TP+FN} = \frac{TP}{all \ ground \ truths} R = TP+FNTP=all ground truthsTP
R,recall,召回率,也叫查全率,预测正确的边界框占真实边界框的比例。

其实,第二个等号后面有一点点小问题,假设当非极大值抑制的阈值设置较大,一个真实检测框上有两个预测框,它们与真实框的阈值都大于0.5,那么all ground truths就是1,TP就是2,R=2,显然不合适,但是用第一个等号后面计算R就是1,没什么问题。当然,非极大值抑制的阈值设置比较大是不好的,会导致很多重叠框。

AP

P和R指标涉及了“边界框,置信度,类别”中的“边界框和类别”,并没有对置信度这一指标进行考量。综合考量就需要使用AP指标了,而AP指标来自于PR曲线。

precision × recall 曲线(PR曲线)可以看作是不同检测器生成的边界框的置信度值下,精准率和召回率之间的权衡。

如下面的表所示。一般情况下,当要求检测器置信度较高时,那么假阳性FP会减少,精准率P上升,同时,可能会漏掉一些真实的目标,导致假阴性FN上升,召回率R变低。反之,降低检测器的置信度标准,假阳性FP上升,精确率P下降,更多的真实目标会被检测到,召回率R提升。

置信度假阳性FP假阴性FN精确率P召回率R
越高⬇️⬆️⬆️⬇️
越低⬆️⬇️⬇️⬆️

我们对一个好检测器的期盼:

  • 能找到所有的真实目标,FN越接近0越好,即高召回率R;
  • 尽可能只识别想要的目标,不去乱猜,FP越接近0越好,即高准确率P;

因此,如果一个检测器在提高召回率的同时精准率保持较高,就可以被认为是好的检测器,这意味着即使置信度阈值发生变化,精准率和召回率仍然能保持较高。因此,PR曲线的曲线下面积(AUC)高往往意味着精准率和召回率都较高。

开个玩笑:究极目标检测器——人,COCO来一张,IOU阈值直接设置到0.8。多扫几遍,直接找到所有的检测目标,置信度全输出100%,无论你置信度怎样变化,P是1,R是1,究极好目标检测器,就是有点慢。

其实也不尽然,有时候看目标检测模型的输出,心想,我去这么小都可以看到吗?

不幸的是,在实际情况下,精准率-召回率曲线通常呈现锯齿状,这对AUC的准确测量提出了挑战。为了解决这个问题,在估计AUC之前,需要对精准率-召回率曲线进行处理,消除锯齿行为。基本上有两种方法来实现:11点插值法(11-point interpolation)和全点插值法(all-point interpolation)。全点插值法就是平常所见COCO和Pascal VOC评价指标的AP。

原文

Therefore, a particular object detector can be considered good if its precision stays high as its recall increases, which means that if the confidence threshold varies, the precision and recall will still be high. Hence, a high area under the curve (AUC) tends to indicate both high precision and high recall. Unfortunately, in practical cases, the precision × recall plot is often a zigzag-like curve, posing challenges to an accurate measurement of its AUC. This is circumvented by processing the precision × recall curve in order to remove the zigzag behavior prior to AUC estimation.

A P 11 AP_{11} AP11

公式如下:
A P 11 = 1 11 ∑ R ∈ { 0 , 0.1 , … , 0.9 , 1 } P interp  ( R ) \mathrm{AP}_{11}=\frac{1}{11} \sum_{R \in\{0,0.1, \ldots, 0.9,1\}} P_{\text {interp }}(R) AP11=111R{0,0.1,,0.9,1}Pinterp (R)

其中
P interp  ( R ) = max ⁡ R ~ : R ~ ≥ R P ( R ~ ) P_{\text {interp }}(R)=\max _{\tilde{R}: \tilde{R} \geq R} P(\tilde{R}) Pinterp (R)=R~:R~RmaxP(R~)

A P a l l AP_{all} APall

公式如下:
A P a l l = ∑ n ( R n + 1 − R n ) P interp  ( R n + 1 ) \mathrm{AP}_{\mathrm{all}}=\sum_n\left(R_{n+1}-R_n\right) P_{\text {interp }}\left(R_{n+1}\right) APall=n(Rn+1Rn)Pinterp (Rn+1)
其中
P interp  ( R n + 1 ) = max ⁡ R ~ : R ~ ≥ R n + 1 P ( R ~ ) P_{\text {interp }}\left(R_{n+1}\right)=\max _{\tilde{R}: \tilde{R} \geq R_{n+1}} P(\tilde{R}) Pinterp (Rn+1)=R~:R~Rn+1maxP(R~)
后面结合例子,运行一下这两个公式就全明白了。看这些公式,倒是看明白一件事情,为什么是——AP,Average Precision, A P 11 AP_{11} AP11求了11个P值,然后取平均, A P a l l AP_{all} APall是一种加权平均,两者都是平均精确度。不过在这上面召回率R也发挥了作用,之前只听AP和P,还以为AP和R没啥关系呢,实则AP是P和R的权衡考量。

上述公式只是一个类别的计算,而往往展示给我们的如COCO的AP50是80个类的均值,Pascal VOC的mAP是20个类AP的均值,所有这些叫平均平均精度比较好,Average/mean了两次。这样说的话,VOC的mAP比COCO的AP50的缩写更准确一点。
m A P = 1 N ∑ i = 1 N A P i \mathrm{mAP}=\frac{1}{N} \sum_{i=1}^N \mathrm{AP}_i mAP=N1i=1NAPi
其中 N N N 是类别总数。

COCO的AP@50: 5: 95平均了三次

例子

红框是detection,共A~V,X,Y共24个(26个字母没W和Z),绿框是真实目标的检测框共15个。红框的数字是置信度,至于是否超过30%的IOU阈值只能自己感觉了,不过基本大差不差。

在这里插入图片描述

下表为当IOU阈值为30%的时候,准确度P和召回率R的情况:

在这里插入图片描述

按照上面的公式去计算 A P 11 AP_{11} AP11

R R R R ~ \tilde{R} R~ P ( R ~ ) P(\tilde{R}) P(R~) P i n t e r p ( R ) P_{interp}(R) Pinterp(R)
11个R值表中所有比R值大的召回率RecallRecall对应的Precision值Precision集合中的最大值
00.0666, 0.0666, 0.1333, … , 0.46661, 0.5, 0.6666, … , 0.29161
0.10.1333, 0.1333, 0.1333, … , 0.46660.6666, 0.5, 0.4, … ,0.29160.6666
0.20.2, 0.2, 0.2666, … , 0.46660.3, 0.2727, 0.3333, … , 0.29160.4285
0.30.3333, 0.4, 0.4, … , 0.46660.3846,0.4285, 0.4, … , 0.29160.4285
0.40.4, 0.4, 0.4, … , 0.46660.4285, 0.4, 0.375, … , 0.29160.4285
0.5
0.6-0.9

计算:
A P 11 = 1 11 ( 1 + 0.6666 + 0.4285 + 0.4285 + 0.4285 ) = 26.84 % AP_{11} = \frac1{11}(1+0.6666+0.4285+0.4285+0.4285)=26.84\% AP11=111(1+0.6666+0.4285+0.4285+0.4285)=26.84%
原论文计算的 A P a l l AP_{all} APall
A P all  = 1 ∗ ( 0.0666 − 0 ) + 0.6666 ∗ ( 0.1333 − 0.0666 ) + 0.4285 ∗ ( 0.4 − 0.1333 ) + 0.3043 ∗ ( 0.4666 − 0.4 ) = 24.56 % \begin{aligned} \mathrm{AP}_{\text {all }} & =1 *(0.0666-0)+0.6666 *(0.1333-0.0666)+0.4285 *(0.4-0.1333)+0.3043 *(0.4666-0.4)=24.56\% \end{aligned} APall =1(0.06660)+0.6666(0.13330.0666)+0.4285(0.40.1333)+0.3043(0.46660.4)=24.56%

对于 A P a l l AP_{all} APall的计算,按照所给的公式,并不能直接得出和原论文一样的式子,因为其省略了一些步骤。原论文的公式没有说 R n R_n Rn指的是什么,根据推测,于是就将其看作是表中的每一个recall值,特别地,认为 R 0 = 0 R_0=0 R0=0

R n R_{n} Rn R n   v a l u e R_{n}\ value Rn value P ( R n ) P(R_{n}) P(Rn) P i n t e r p ( R n ) P_{interp}(R_n) Pinterp(Rn) R n − R n − 1 R_{n}-R_{n-1} RnRn1
R 0 R_{0} R00---
R 1 R_{1} R10.0666110.0666
R 2 R_{2} R20.06660.510
R 3 R_{3} R30.13330.66660.66660.0667
R 4 R_{4} R40.13330.50.66660
R 5 R_{5} R50.13330.40.66660
R 6 R_{6} R60.13330.33330.66660
R 7 R_{7} R70.13330.28570.66660
R 8 R_{8} R80.13330.250.66660
R 9 R_{9} R90.13330.22220.66660
R 10 R_{10} R100.20.30.42850.0667
R 11 R_{11} R110.20.27270.42850
R 12 R_{12} R120.26660.33330.42850.0666
R 13 R_{13} R130.33330.38460.42850.0667
R 14 R_{14} R140.40.42850.42850.0667
R 15 R_{15} R150.40.40.42850
R 16 R_{16} R160.40.3750.42850
R 17 R_{17} R170.40.35290.42850
R 18 R_{18} R180.40.33330.42850
R 19 R_{19} R190.40.31570.42850
R 20 R_{20} R200.40.30.42850
R 21 R_{21} R210.40.28570.42850
R 22 R_{22} R220.40.27270.42850
R 23 R_{23} R230.46660.30430.30430.0666
R 24 R_{24} R240.46660.29160.30430

按照这样进行计算的话,

A P all  = 1 ∗ ( 0.0666 − 0 ) + 0.6666 ∗ ( 0.1333 − 0.0666 ) + 0.4285 ∗ ( 0.2 − 0.1333 ) + 0.4285 ∗ ( 0.2666 − 0.2 ) + 0.4285 ∗ ( 0.3333 − 0.2666 ) + 0.4285 ∗ ( 0.4 − 0.3333 ) + 0.3043 ∗ ( 0.4666 − 0.4 ) = 24.56 % \begin{align} \mathrm{AP}_{\text {all }} & = 1 * (0.0666 - 0) \\ &\quad + 0.6666 * (0.1333 - 0.0666) \\ &\quad + 0.4285 * (0.2 - 0.1333) \\ &\quad + 0.4285 * (0.2666 - 0.2) \\ &\quad + 0.4285 * (0.3333 - 0.2666) \\ &\quad + 0.4285 * (0.4 - 0.3333) \\ &\quad + 0.3043 * (0.4666 - 0.4) \\ & = 24.56\% \end{align} APall =1(0.06660)+0.6666(0.13330.0666)+0.4285(0.20.1333)+0.4285(0.26660.2)+0.4285(0.33330.2666)+0.4285(0.40.3333)+0.3043(0.46660.4)=24.56%
插值前后的PR曲线对比:

将R作为横轴,P作为纵轴,绘制PR曲线。R是递增或者不变的,P时而递增,时而不变,时而递减,从而造成了PR曲线呈现锯齿状。

下面的两幅图像分别是插值后的曲线。

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

题外话-为什么要插值

AUC(Area Under the Curve)是通常用于评估二分类模型的性能。它是 ROC 曲线(Receiver Operating Characteristic Curve,受试者工作特征曲线)下的面积,取值范围在0-1之间,AUC越接近与1,模型的分类效果越好。

ROC曲线的绘制和PR曲线的绘制方法一样,ROC曲线计算的两个概率为真阳性率 (TPR)、假阳性率 (FPR),分别表示模型正确预测为正类的比例(召回率),模型错误地将负类预测为正类的比例。计算不同阈值下的TPR和FPR,接下来以FPR为横轴,TPR为纵轴,连接不同的点绘制出ROC曲线。

那么为什么,ROC曲线可以直接连接点来绘制,而PR曲线要来插值呢?

ROC曲线中,TPR和FPR不会出现锯齿状的情况,一般情况下,随着阈值的下降,TPR和FPR都是非递减的,即逐步增大或者保持不变。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression

# 1. 生成一个示例数据集
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)

# 2. 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 3. 训练一个逻辑回归模型
model = LogisticRegression()
model.fit(X_train, y_train)

# 4. 获取预测概率
y_probs = model.predict_proba(X_test)[:, 1]  # 预测为正类的概率

# 5. 计算 FPR 和 TPR
fpr, tpr, thresholds = roc_curve(y_test, y_probs)

# 6. 计算 AUC
roc_auc = auc(fpr, tpr)

# 7. 绘制 ROC 曲线
plt.figure()
plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (AUC = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')  # 对角线代表随机猜测
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC) Curve')
plt.legend(loc="lower right")
plt.show()

在这里插入图片描述

# 输出出来看看
for i, j, k in zip(fpr, tpr, thresholds):
    print(i, j, k)
    
# 0.0 0.0 inf
# 0.0 0.0064516129032258064 0.9999033225668299
# 0.0 0.3870967741935484 0.9608405238251244
# 0.006896551724137931 0.3870967741935484 0.9606797417680791
# 0.006896551724137931 0.4645161290322581 0.9429496860114247
# 0.013793103448275862 0.4645161290322581 0.9376365787112844
# 0.013793103448275862 0.4838709677419355 0.9358461731156758
# 0.020689655172413793 0.4838709677419355 0.9352570352185332
# 0.020689655172413793 0.49032258064516127 0.9334172228945368
# 0.027586206896551724 0.49032258064516127 0.9316159594166492
# 0.027586206896551724 0.5096774193548387 0.9198797972920243
# 0.034482758620689655 0.5096774193548387 0.9183739292910866
# 0.034482758620689655 0.5419354838709678 0.9087877675251759

AUC,曲线下面积,我们一般用积分去求,但是这些是离散的点,直接积分不适用,因此采用数值积分的方法去求近似面积。在计算 ROC 曲线的AUC时,常用的是梯形法则(Trapezoidal Rule)。其实就是学积分的时候,将曲线分割成一个一个的小梯形。

使用这种方法的话,是不是锯齿状都能算。主要原因是锯齿状的这种波动会导致估算误差(不太懂!!!)。

AUC是非递减的,类楼梯,而PR是锯齿状。但是插值后的PR,比如计算 A P a l l AP_{all} APall的那幅图,由锯齿状平滑成楼梯状了,这下全都是楼梯了,不过一个是上楼梯一个是下楼梯。

猜想:由于点太少,导致曲线不够光滑。或许有一条真实反映模型能力的PR图线,由无数个点组成,十分光滑。插值后的PR曲线,比插值的锯齿状PR曲线更加解决理想真实的PR曲线,所以采用了插值的方法。

其实 A P 11 AP_{11} AP11的动机很明确,召回率比0大的时候,找个最高精确率代表模型水平,召回率比0.1大的时候,找个最高精确率代表模型水平,依次类推到0.9。只选择11个区间,虽说笼统,但也全面。


题题外话-为什么要叫做ROC

为什么要叫ROC —— Receiver Operating Characteristic Curve —— 接收者操作特征曲线

GPT:

ROC 曲线的名字——Receiver Operating Characteristic curve(接收者操作特征曲线)来源于其最早的应用背景:雷达信号检测。尽管如今它已广泛用于机器学习和统计学中的分类模型性能评估,但它的名称和历史背景源自二战时期雷达信号处理的应用。

名称由来

ROC 曲线最初是在第二次世界大战期间由美国的工程师和科学家发展出来的。当时,雷达技术用于检测敌方飞机或其他目标,而需要解决的核心问题是如何在检测到真实目标的情况下,尽量减少误报(即不把噪声当成目标)。

  • Receiver(接收者):指的是雷达的信号接收者。在当时的雷达系统中,接收器需要从噪声中分辨出信号(例如飞机),这需要做出决策:是信号还是噪声。
  • Operating(操作):指的是操作接收器的决策过程。操作员或机器需要根据接收到的信号和噪声水平选择不同的阈值,以决定是否发出警报(认为有目标存在)。
  • Characteristic(特征):指的是接收者操作过程中产生的特征关系——即在不同的阈值下,系统如何在真实信号识别噪声误报之间做出权衡。

4 目标检测数据集

这部分主要总结主要的目标检测数据集以及它们使用的AP变种。

PASCAL VOC

更多详细内容参考这个链接:目标检测数据集PASCAL VOC简介

PASCAL —— Pattern Analysis, Statical Modeling and Computational Learning,是一个组织

VOC —— Visual Object Classes

Pascal挑战赛于2005年开始举办,2012年终止。一开始只有4类,后来发展到20个类。下表是PASCAL中的20个类别。

其中较为常用的两个数据集为PASCAL VOC 2007和PASCAL VOC 2012,07的时候类别达到20个类。这两个数据集是互斥的,没有重复的图片。

Pascal VOC的AP计算使用的是11点插值法

MS COCO

MS —— MicroSoft

COCO —— Common Objects in COntext

20万张图像,80个类。通常使用的是COCO2017

主要评价指标为AP@50:5:95,从0.5, 0.55, 0.6, 0.65, …, 0.95,10个不同IOU阈值下AP的平均值,使用 A P a l l AP_{all} APall的计算方式。也报告0.5和0.75IOU阈值下的AP。

在这里插入图片描述

官方给的说明coco-eval:

  1. 除非另有说明,平均精度(AP)和平均召回率(AR)是通过多个交并比(IoU)值取平均计算的。具体而言,我们使用了10个IoU阈值,范围为0.50到0.95,间隔为0.05。这与传统方法不同,传统上AP是在单一IoU为0.50时计算的(对应于我们的指标APIoU=0.50)。在多个IoU上的平均奖励了定位更准确的检测器。
  2. AP在所有类别上取平均。传统上,这被称为“平均平均精度”(mAP)。我们对AP和mAP(以及AR和mAR)不做区分,并假设上下文可以清楚地说明两者的区别。
  3. AP(在所有10个IoU阈值和80个类别上取平均)将决定挑战赛的获胜者。这被认为是在COCO数据集上衡量性能时最重要的单一指标。
  4. 在COCO数据集中,小物体的数量多于大物体。具体来说,约41%的物体是小物体(面积< 3 2 2 32^2 322),34%是中等物体( 3 2 2 32^2 322 <面积< 9 6 2 96^2 962),24%是大物体(面积> 9 6 2 96^2 962)。面积是根据分割掩码中的像素数量来衡量的。
  5. AR是给定每张图像的固定检测数量时的最大召回率,并在类别和IoU上取平均。AR与在这篇论文中使用的同名指标相关,但这里是基于每个类别计算的。
  6. 所有的度量都允许每张图像最多进行100次最高评分的检测(跨所有类别)。
  7. 目标检测的边界框和分割掩码的评估指标在所有方面都是相同的,唯一的区别是IoU的计算方式(分别在边界框或掩码上进行)。

http://www.kler.cn/news/340379.html

相关文章:

  • 【C语言进阶教程】数据结构与算法(1.简介 2.数据结构基础 3.链表)
  • Zig开发环境搭建
  • 如何通过Kubectl 重启Pod的六种方法
  • .NET周刊【9月第4期 2024-09-22】
  • MidJourney瞬间提升画面质量关键词:构图篇,材质篇
  • Maven 三种项目打包方式:POM、JAR 和 WAR 的区别详解
  • git 还原修改,放弃当前的修改
  • docker-compose查看容器日志和实时查看日志
  • 四种数据库对比MySQL、PostgreSQL、ClickHouse、MongoDB——特点、性能、扩展性、安全性、适用场景
  • 黑马JavaWeb开发跟学(十)SpringBootWeb案例
  • 【2024】前端学习笔记12-JavaScript初体验-Js操作window
  • 共享单车轨迹数据分析:以厦门市共享单车数据为例(十)
  • 顶级域名子域名
  • C++ 语言特性30 - 模板介绍
  • 安卓APP 构建
  • Meta MovieGen AI:颠覆性的文本生成视频技术详解
  • 机器学习中的回归分析:理论与实践
  • 基于SSM的校园教务系统的设计与实现(论文+源码)_kaic
  • Python数据分析-城市空气质量与健康影响分析
  • node高版本报错: digital envelope routines::unsupported