利用mAP评估目标检测模型

在本文中,我们将了解如何使用 precision 和召回率来计算平均精度 (mAP)。mAP 将真实边界框与检测到的框进行比较并返回分数。分数越高,模型的检测越准确。

之前我们详细研究了混淆矩阵、模型准确性、精确度和召回率。我们也使用 Scikit-learn 库来计算这些指标。现在我们将扩展讨论以了解如何使用精度和召回率来计算 mAP

1. 从预测分数到类别标签

在本节中,我们将快速回顾一下如何从预测分数中派生出类标签。鉴于有两个类别,正类和负类,这里是 10 个样本的真实标签。

y_true = ["positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive", "negative", "positive"]

当这些样本被输入模型时,它会返回以下预测分数。基于这些分数,我们如何对样本进行分类(即为每个样本分配一个类标签)?

pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3]

为了将分数转换为类别标签,使用了一个阈值。当分数等于或高于阈值时,样本被归为一类。否则,它被归类为其他类别。如果样本的分数高于或等于阈值,则该样本为阳性。否则,它是负面的。下一个代码块将分数转换为阈值为 0.5 的类别标签。

import numpy

pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3]
y_true = ["positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive", "negative", "positive"]

threshold = 0.5
y_pred = ["positive" if score >= threshold else "negative" for score in pred_scores]
print(y_pred)
  • y_pred
['positive', 'negative', 'positive', 'positive', 'positive', 'positive', 'negative', 'negative', 'negative', 'negative']

现在 y_truey_pred 变量中都提供了真实标签和预测标签。基于这些标签,可以计算混淆矩阵、精度和召回率。

r = numpy.flip(sklearn.metrics.confusion_matrix(y_true, y_pred))
print(r)

precision = sklearn.metrics.precision_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
print(precision)

recall = sklea
  • 结果
# Confusion Matrix (From Left to Right & Top to Bottom: True Positive, False Negative, False Positive, True Negative)
[[4 2]
 [1 3]]

# Precision = 4/(4+1)
0.8

# Recall = 4/(4+2)
0.6666666666666666

在快速回顾了计算准确率和召回率之后,在下一节中我们将讨论创建准确率-召回率曲线。

2. PR 曲线

根据第 1 部分给出的精度和召回率的定义,请记住精度越高,模型将样本分类为正时的置信度就越高。召回率越高,模型正确分类为正的正样本就越多

当一个模型的召回率高但精度低时,该模型会正确分类大部分正样本,但它有很多误报(即将许多负样本分类为正样本)。当模型具有高精度但召回率低时,模型将样本分类为正样本时是准确的,但它可能仅对部分正样本进行分类。

由于精度和召回率的重要性,精度-召回率曲线显示了不同阈值的精度和召回率值之间的权衡。该曲线有助于选择最佳阈值以最大化两个指标。

创建精确-召回曲线需要一些输入:

  1. 真实标签。
  2. 样本的预测分数。
  3. 将预测分数转换为类别标签的一些阈值。

下面的代码块创建 y_true 列表来保存真实标签,pred_scores 列表用于预测分数,最后是不同阈值的阈值列表。

import numpy

y_true = ["positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive", "negative", "positive", "positive", "positive", "positive", "negative", "negative", "negative"]

pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3, 0.7, 0.5, 0.8, 0.2, 0.3, 0.35]

以下是保存在阈值列表中的阈值。因为有 10 个阈值,所以将创建 10 个精度和召回值。

[0.2, 
 0.25, 
 0.3, 
 0.35, 
 0.4, 
 0.45, 
 0.5, 
 0.55, 
 0.6, 
 0.65]

下面是名为 precision_recall_curve() 的函数,其接受真实标签、预测分数和阈值。它返回两个代表精度和召回值的等长列表。

import sklearn.metrics

def precision_recall_curve(y_true, pred_scores, thresholds):
    precisions = []
    recalls = []
    
    for threshold in thresholds:
        y_pred = ["positive" if score >= threshold else "negative" for score in pred_scores]

        precision = sklearn.metrics.precision_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
        recall = sklearn.metrics.recall_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
        
        precisions.append(precision)
        recalls.append(recall)

    return precisions, recalls

下一段代码在三个先前准备好的列表后调用 precision_recall_curve() 函数。它返回精度和召回列表,分别包含精度和召回的所有值。

precisions, recalls = precision_recall_curve(y_true=y_true, 
                                             pred_scores=pred_scores,
                                             thresholds=thresholds)
  • 以下是精度列表中的返回值。
[0.5625,
 0.5714285714285714,
 0.5714285714285714,
 0.6363636363636364,
 0.7,
 0.875,
 0.875,
 1.0,
 1.0,
 1.0]
  • 这是召回列表中的值列表。
[1.0,
 0.8888888888888888,
 0.8888888888888888,
 0.7777777777777778,
 0.7777777777777778,
 0.7777777777777778,
 0.7777777777777778,
 0.6666666666666666,
 0.5555555555555556,
 0.4444444444444444]

给定两个长度相等的列表,可以在二维图中绘制它们的值,如下所示。

matplotlib.pyplot.plot(recalls, precisions, linewidth=4, color="red")
matplotlib.pyplot.xlabel("Recall", fontsize=12, fontweight='bold')
matplotlib.pyplot.ylabel("Precision", fontsize=12, fontweight='bold')
matplotlib.pyplot.title("Precision-Recall Curve", fontsize=15, fontweight="bold")
matplotlib.pyplot.show()

准确率-召回率曲线如下图所示。请注意,随着召回率的增加,精度会降低。原因是当正样本数量增加(高召回率)时,正确分类每个样本的准确率降低(低精度)。这是预料之中的,因为当有很多样本时,模型更有可能预测出错。

准确率-召回率曲线可以很容易地确定准确率和召回率都高的点。根据上图,最好的点是(recall, precision)=(0.778, 0.875)。

使用上图以图形方式确定精度和召回率的最佳值可能有效,因为曲线并不复杂。更好的方法是使用称为 f1 分数的指标,它是根据下一个等式计算的。

f1 指标衡量准确率和召回率之间的平衡。当 f1 的值很高时,这意味着精度和召回率都很高。较低的 f1 分数意味着精确度和召回率之间的失衡更大。

根据前面的例子,f1 是根据下面的代码计算的。根据 f1 列表中的值,最高分是 0.82352941。它是列表中的第 6 个元素(即索引 5)。召回率和精度列表中的第 6 个元素分别为 0.778 和 0.875。相应的阈值为 0.45。

f1 = 2 * ((numpy.array(precisions) * numpy.array(recalls)) / (numpy.array(precisions) + numpy.array(recalls)))

下图以蓝色显示了与召回率和准确率之间的最佳平衡相对应的点的位置。总之,平衡精度和召回率的最佳阈值是 0.45,此时精度为 0.875,召回率为 0.778。

matplotlib.pyplot.plot(recalls, precisions, linewidth=4, color="red", zorder=0)
matplotlib.pyplot.scatter(recalls[5], precisions[5], zorder=1, linewidth=6)

matplotlib.pyplot.xlabel("Recall", fontsize=12, fontweight='bold')
matplotlib.pyplot.ylabel("Precision", fontsize=12, fontweight='bold')
matplotlib.pyplot.title("Precision-Recall Curve", fontsize=15, fontweight="bold")
matplotlib.pyplot.show()

3. AP

平均精度 (AP) 是一种将精度召回曲线汇总为表示所有精度平均值的单个值的方法。根据面等式计算 AP。使用遍历所有精度/召回率的循环,计算当前召回率和下一次召回率之间的差异,然后乘以当前精度。换句话说,AP 是每个阈值的精度加权和,其中权重是召回率的增加。

分别在召回率和准确率列表上附加 0 和 1 很重要。例如,如果召回列表为 0.8、0.60.8、0.6,则应在 0.8、0.6、0.00.8、0.6、0.0 后附加 0。精度列表也是如此,但附加了 1 而不是 0(例如 0.8、0.2、1.00.8、0.2、1.0)。

鉴于召回率和精度都是 NumPy 数组,前面的等式根据下面 Python 代码建模。

AP = numpy.sum((recalls[:-1] - recalls[1:]) * precisions[:-1])
  • 这是计算 AP 的完整代码。
import numpy
import sklearn.metrics

def precision_recall_curve(y_true, pred_scores, thresholds):
    precisions = []
    recalls = []
    
    for threshold in thresholds:
        y_pred = ["positive" if score >= threshold else "negative" for score in pred_scores]

        precision = sklearn.metrics.precision_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
        recall = sklearn.metrics.recall_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
        
        precisions.append(precision)
        recalls.append(recall)

    return precisions, recalls

y_true = ["positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive", "negative", "positive", "positive", "positive", "positive", "negative", "negative", "negative"]
pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3, 0.7, 0.5, 0.8, 0.2, 0.3, 0.35]
thresholds=numpy.arange(start=0.2, stop=0.7, step=0.05)

precisions, recalls = precision_recall_curve(y_true=y_true, 
                                             pred_scores=pred_scores, 
                                             thresholds=thresholds)

precisions.append(1)
recalls.append(0)

precisions = numpy.array(precisions)
recalls = numpy.array(recalls)

AP = numpy.sum((recalls[:-1] - recalls[1:]) * precisions[:-1])
print(AP)
  • 这都是关于平均精度的。以下是计算 AP 的步骤摘要:
  1. 使用模型生成预测分数。
  2. 将预测分数转换为类别标签。
  3. 计算混淆矩阵。
  4. 计算精度和召回率指标。
  5. 创建精确召回曲线。
  6. 测量平均精度。

4. IoU

要训练目标检测模型,通常有 2 个输入:

  1. 图片
  2. 图像检测结果的真实框

该模型预测检测到的对象的边界框。预计预测框不会与真实框完全匹配。下图显示了猫的图像。对象的真实框为红色,而预测框为黄色。基于 2 个框的可视化,模型是否做出了高匹配分数的良好预测?

很难主观地评估模型预测。例如,有人可能会得出匹配率为 50% 的结论,而其他人则注意到匹配率为 60%。

更好的替代方法是使用定量测量来对真实框和预测框的匹配程度进行评分。此度量是交并集 (IoU)。 IoU 有助于了解一个区域是否有对象。

IoU 是根据下面等式计算的,通过将 2 个框之间的交叉区域除以它们的联合区域。 IoU 越高,预测越好。

下图显示了具有不同 IoU 的 3 个案例。请注意,每个案例顶部的 IoU 都是客观测量的,可能与现实略有不同,但它是有道理的。

对于案例 A,预测的黄色框远未与红色真值框对齐,因此 IoU 得分为 0.2(即两个框之间只有 20% 的重叠)。

对于情况 B,2 个框之间的交叉区域更大,但 2 个框仍然没有很好地对齐,因此 IoU 分数为 0.5。

对于案例 C,两个框的坐标非常接近,因此它们的 IoU 为 0.9(即两个框之间有 90% 的重叠)。

请注意,当预测框和真实框之间的重叠率为 0% 时,IoU 为 0.0。当 2 个框彼此 100% 匹配时,IoU 为 1.0。

要计算图像的 IoU,这里有一个名为 intersection_over_union() 的函数。它接受以下 2 个参数:

  1. gt_box:真实边界框。
  2. pred_box:预测边界框。

它分别计算交集和并集变量中两个框之间的交集和并集。此外,IoU 是在 iou 变量中计算的。它返回所有这 3 个变量。

def intersection_over_union(gt_box, pred_box):
    inter_box_top_left = [max(gt_box[0], pred_box[0]), max(gt_box[1], pred_box[1])]
    inter_box_bottom_right = [min(gt_box[0]+gt_box[2], pred_box[0]+pred_box[2]), min(gt_box[1]+gt_box[3], pred_box[1]+pred_box[3])]

    inter_box_w = inter_box_bottom_right[0] - inter_box_top_left[0]
    inter_box_h = inter_box_bottom_right[1] - inter_box_top_left[1]

    intersection = inter_box_w * inter_box_h
    union = gt_box[2] * gt_box[3] + pred_box[2] * pred_box[3] - intersection
    
    iou = intersection / union

    return iou, intersection, union

传递给函数的边界框是一个包含 4 个元素的列表,它们是:

  1. 左上角的 x 轴。
  2. 左上角的 y 轴。

这是汽车图像的真实边界框和预测边界框。

gt_box = [320, 220, 680, 900]
pred_box = [500, 320, 550, 700]

图像名为 cat.jpg,下面是在图像上绘制边界框的完整示例。

import imageio
import matplotlib.pyplot
import matplotlib.patches

def intersection_over_union(gt_box, pred_box):
    inter_box_top_left = [max(gt_box[0], pred_box[0]), max(gt_box[1], pred_box[1])]
    inter_box_bottom_right = [min(gt_box[0]+gt_box[2], pred_box[0]+pred_box[2]), min(gt_box[1]+gt_box[3], pred_box[1]+pred_box[3])]

    inter_box_w = inter_box_bottom_right[0] - inter_box_top_left[0]
    inter_box_h = inter_box_bottom_right[1] - inter_box_top_left[1]

    intersection = inter_box_w * inter_box_h
    union = gt_box[2] * gt_box[3] + pred_box[2] * pred_box[3] - intersection
    
    iou = intersection / union

    return iou, intersection, union

im = imageio.imread("cat.jpg")

gt_box = [320, 220, 680, 900]
pred_box = [500, 320, 550, 700]

fig, ax = matplotlib.pyplot.subplots(1)
ax.imshow(im)

gt_rect = matplotlib.patches.Rectangle((gt_box[0], gt_box[1]),
                                       gt_box[2],
                                       gt_box[3],
                                       linewidth=5,
                                       edgecolor='r',
                                       facecolor='none')

pred_rect = matplotlib.patches.Rectangle((pred_box[0], pred_box[1]),
                                         pred_box[2],
                                         pred_box[3],
                                         linewidth=5,
                                         edgecolor=(1, 1, 0),
                                         facecolor='none')
ax.add_patch(gt_rect)
ax.add_patch(pred_rect)

ax.axes.get_xaxis().set_ticks([])
ax.axes.get_yaxis().set_ticks([])

下图显示了带有边界框的图像。

要计算 IoU,只需调用 intersection_over_union() 函数。基于边界框,IoU 得分为 0.54。

iou, intersect, union = intersection_over_union(gt_box, pred_box)
print(iou, intersect, union)
  • 结果

IoU 分数 0.54 意味着真实边界框和预测边界框之间有 54% 的重叠。看着方框,有人可能在视觉上觉得它足以得出模型检测到猫对象的结论。其他人可能会觉得模型还不准确,因为预测框与真实框不太吻合。

为了客观地判断模型是否正确预测了框的位置,使用了一个阈值。如果模型预测 IoU 分数大于或等于阈值的框,则预测框与其中一个真实框之间存在高度重叠。这意味着该模型能够成功检测到一个对象。检测到的区域被归类为阳性(即包含一个对象)。

另一方面,当 IoU 分数小于阈值时,模型做出了错误的预测,因为预测框与真实框不重叠。这意味着检测到的区域被归类为负面(即不包含对象)。

让我们举个例子来阐明 IoU 分数如何帮助将区域分类为对象。假设对象检测模型由下一张图像提供,其中有 2 个目标对象,其真实框为红色,预测框为黄色。

下一个代码读取图像(假设它被命名为 pets.jpg),绘制框,并计算每个对象的 IoU。左侧对象的 IoU 为 0.76,而另一个对象的 IoU 分数为 0.26。

import matplotlib.pyplot
import matplotlib.patches
import imageio

def intersection_over_union(gt_box, pred_box):
    inter_box_top_left = [max(gt_box[0], pred_box[0]), max(gt_box[1], pred_box[1])]
    inter_box_bottom_right = [min(gt_box[0]+gt_box[2], pred_box[0]+pred_box[2]), min(gt_box[1]+gt_box[3], pred_box[1]+pred_box[3])]

    inter_box_w = inter_box_bottom_right[0] - inter_box_top_left[0]
    inter_box_h = inter_box_bottom_right[1] - inter_box_top_left[1]

    intersection = inter_box_w * inter_box_h
    union = gt_box[2] * gt_box[3] + pred_box[2] * pred_box[3] - intersection
    
    iou = intersection / union

    return iou, intersection, union, 

im = imageio.imread("pets.jpg")

gt_box = [10, 130, 370, 350]
pred_box = [30, 100, 370, 350]

iou, intersect, union = intersection_over_union(gt_box, pred_box)
print(iou, intersect, union)

fig, ax = matplotlib.pyplot.subplots(1)
ax.imshow(im)

gt_rect = matplotlib.patches.Rectangle((gt_box[0], gt_box[1]),
                                       gt_box[2],
                                       gt_box[3],
                                       linewidth=5,
                                       edgecolor='r',
                                       facecolor='none')

pred_rect = matplotlib.patches.Rectangle((pred_box[0], pred_box[1]),
                                         pred_box[2],
                                         pred_box[3],
                                         linewidth=5,
                                         edgecolor=(1, 1, 0),
                                         facecolor='none')
ax.add_patch(gt_rect)
ax.add_patch(pred_rect)

gt_box = [645, 130, 310, 320]
pred_box = [500, 60, 310, 320]

iou, intersect, union = intersection_over_union(gt_box, pred_box)
print(iou, intersect, union)

gt_rect = matplotlib.patches.Rectangle((gt_box[0], gt_box[1]),
                                       gt_box[2],
                                       gt_box[3],
                                       linewidth=5,
                                       edgecolor='r',
                                       facecolor='none')

pred_rect = matplotlib.patches.Rectangle((pred_box[0], pred_box[1]),
                                         pred_box[2],
                                         pred_box[3],
                                         linewidth=5,
                                         edgecolor=(1, 1, 0),
                                         facecolor='none')
ax.add_patch(gt_rect)
ax.add_patch(pred_rect)

ax.axes.get_xaxis().set_ticks([])
ax.axes.get_yaxis().set_ticks([])

鉴于 IoU 阈值为 0.6,则只有 IoU 分数大于或等于 0.6 的区域被归类为正(即有物体)。因此,IoU 得分为 0.76 的框为正,而另一个 IoU 为 0.26 的框为负。

如果阈值更改为 0.2 而不是 0.6,则两个预测都是Positive的。如果阈值为 0.8,则两个预测都是负面的。

作为总结,IoU 分数衡量预测框与真实框的接近程度。它的范围从 0.0 到 1.0,其中 1.0 是最佳结果。当 IoU 大于阈值时,该框被分类为正,因为它围绕着一个对象。否则,它被归类为负面。

5. mAP

通常,目标检测模型使用不同的 IoU 阈值进行评估,其中每个阈值可能给出与其他阈值不同的预测。假设模型由一个图像提供,该图像具有分布在 2 个类中的 10 个对象。如何计算mAP?

要计算 mAP,首先要计算每个类的 AP。所有类别的 AP 的平均值是 mAP。

假设使用的数据集只有 2 个类。对于第一类,这里分别是 y_truepred_scores 变量中的真实标签和预测分数。

y_true = ["positive", "negative", "positive", "negative", "positive", "positive", "positive", "negative", "positive", "negative"]

pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.75, 0.2, 0.8, 0.3]

这是第二类的 y_truepred_scores 变量。

y_true = ["negative", "positive", "positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive"]

pred_scores = [0.32, 0.9, 0.5, 0.1, 0.25, 0.9, 0.55, 0.3, 0.35, 0.85]

IoU 阈值列表从 0.2 到 0.9,步长为 0.25。

thresholds = numpy.arange(start=0.2, stop=0.9, step=0.05)

要计算一个类的 AP,只需将其 y_truepred_scores 变量提供给下一个代码。

precisions, recalls = precision_recall_curve(y_true=y_true, 
                                             pred_scores=pred_scores, 
                                             thresholds=thresholds)

matplotlib.pyplot.plot(recalls, precisions, linewidth=4, color="red", zorder=0)

matplotlib.pyplot.xlabel("Recall", fontsize=12, fontweight='bold')
matplotlib.pyplot.ylabel("Precision", fontsize=12, fontweight='bold')
matplotlib.pyplot.title("Precision-Recall Curve", fontsize=15, fontweight="bold")
matplotlib.pyplot.show()

precisions.append(1)
recalls.append(0)

precisions = numpy.array(precisions)
recalls = numpy.array(recalls)

AP = numpy.sum((recalls[:-1] - recalls[1:]) * precisions[:-1])
print(AP)

对于第一类,这是它的精确召回曲线。基于此曲线,AP 为 0.949。

第二类的precision-recall曲线如下图所示。它的 AP 是 0.958。

基于 2 个类(0.949 和 0.958)的 AP,根据下面等式计算目标检测模型的 mAP

基于此等式,mAP 为 0.9535。

mAP = (0.949 + 0.958)/2 = 0.9535

总结

本教程讨论了如何计算目标检测模型的平均精度 (mAP)。我们首先讨论如何将预测分数转换为类别标签。使用不同的阈值,创建精确召回曲线。从该曲线可以测量平均精度 (AP)。

对于目标检测模型,阈值是对检测到的对象进行评分的 IoU。一旦为数据集中的每个类测量了 AP,就会计算出 mAP。

本文转载于网络 如有侵权请联系删除

相关文章

  • java基础:数组篇

    java数组篇数组:数组(array)是一种最简单的复合数据类型,它是有序数据的集合,数组中的每个元素具有相同的数据类型,可以用一个统一的数组名和不同的下标来确定数组中唯一的元素。根据数组的维度,可以将其分为一维数组、二维数组和多维数组等。声明数组必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法int[]arr; //或 inttest[]; //建议使用int[]arr;的声明风格声明数组变量。复制分配空间声明了数组,只是得到了一个存放数组的变量,并没有为数组元素分配内存空间,不能使用。因此要为数组分配内存空间,这样数组的每一个元素才有一个空间进行存储。简单地说,分配空间就是要告诉计算机在内存中为它分配几个连续的位置来存储数据。在Java中可以使用new关键字来给数组分配空间。分配空间的语法格式如下://动态初始化 type[]arrayName=newtype[size]; //数据类型[]数组名=new数据类型[数组长度]; //动态初始化后就可以根据下标取到数组某个下标的值了,只不过是该类型的默认值 //动态初始化后就可以进行赋值了,赋值的类型必须和声明的类

  • 盘点Pandas中csv文件读取的方法所带参数usecols知识

    大家好,我是皮皮。 一、前言前几天在Python最强王者群有个叫【老松鼠】的粉丝问了一个关于Pandas中csv文件读取的方法所带参数usecols知识问题,这里拿出来给大家分享下,一起学习。其实usecols参数是指定列读取。二、解决过程下面是【德善堂小儿推拿-瑜亮老师】大佬解答:举个栗子,就像你手中只有常见的人民币面值,让你把面值等于5元,10元,10000元的拿出来。你是不是只能拿出来5元的和10元的。读取,那不是有啥就拿出来啥,手中没有,当然就不用给了。后来【月神】给补充了一些知识,不知道你有没有注意到usecols这个参数其实是有返回值的?大部分小伙伴是没有注意到的。usecols是先从读取到的数据判断出当前的列名并作为返回值,类似于列表,使用函数调用时,例如lambdax:各个元素都会被使用到,类似于map(lambdax:x,iterable),iterable就是usecols的返回值,lambdax与此处一致,再将结果传入至read_csv中,返回指定列的数据框。对应这个例子中就是lambdac:ciniterable,其实不管iterable是列表还是集合,两者中包

  • arduino小车笔记

    模块L298narduino 拼装焊接 代码 马达焊接导线 uno5vL298n5v unogndl298ngnd 电池红线连开关l298n12v+ 电池黑线l298ngnd 马达线l2980out1out2out3out4 l298n4个引脚1234连uno1267复制代码voidsetup() { pinMode(1,OUTPUT); pinMode(2,OUTPUT); pinMode(6,OUTPUT); pinMode(7,OUTPUT); } voidloop() { digitalWrite(1,HIGH); digitalWrite(2,LOW); digitalWrite(6,HIGH); digitalWrite(7,LOW); }复制前言ArduinoUno简介IMGL298NESP8266ESP8266WIFI蓝牙模块(HC-05)arduino蓝牙模块控制教程

  • 千万级规模遗留系统债务度量改造实践

    作者|白嗣健 编辑|支小亚 本文由极客时间整理自华为云化平台技术专家白嗣健在QCon+案例研习社的演讲《千万级规模遗留系统债务度量改造实践》。 今天跟大家分享的内容是架构债务与代码债务、债务度量及偿还实践相关的内容。架构债务与代码债务架构债务和代码债务的度量是什么?请思考一下这个问题:你做了一个架构,一个设计,这个设计如何成年呢?罗马不是一天建成的,架构亦不可能一天设计成,未来10年它不可能保持原貌不变,架构一定是不断演进的。想让架构活到成年,就要让架构不断地演进。比如从单体到云化,从云化到CloudNative,然后到容器化到function,再到function加data。架构如何持续演进首先,我认为架构演进的关键在于架构师一定要把自己的架构意图真正地在团队中落地。落地说起来非常简单,但我认为世界上最远的距离就是知道和落地,就像敏捷非常好但就是落不了地一样。落地考察的是你的硬功夫,比如个人的魅力。你要在团队中有非常大的魅力,同时也要有一群懂这个且跟你一样有追求的人才可以共同把这件事情做成。架构意图如何去落地呢?意图不是口号,不是喊喊口号写一些slogan,画一个logo,让

  • 这就是鸿蒙系统?

    我手头使用的是一部华为Mate20pro手机,快三年时间了。作为一名数码爱好者,对于系统升级非常积极,每次收到系统更新通知,都会在第一时间升级。这次鸿蒙系统发布,有些迫不及待,还是内测版本就申请进行了升级。距离升级到鸿蒙系统2.0版本已经五天时间,我最大的感受是,没变化。升级到新的系统,数据还在,应用还在,所有的系统设置都还在,从界面到操作方式,完全没变。应用商店里的应用,也没有见少,差不多都是安卓的应用程序。安卓的程序运行在鸿蒙系统上,非常欢畅,一点也没感觉到运行在一个不是安卓的操作系统上。使用的这几天,我将我手头的app都使用了一下,没出现崩溃、无法启动、界面异常等情况。 要说变化,感觉运行更加流畅了。当然这只是个人感觉,没有拿软件测试,也没有进行定量的对比分析,可能是心理作用。界面也做了一些小小的调整,比如将通知消息和控制中心分开,但如果不仔细体验,基本上感知不到。 前几天,媒体报道,从6月2日华为发布HarmonyOS(鸿蒙操作系统)手机系统算起,在不到一周时间里升级的手机用户数已经超过1000万。这无疑是一个非常值得称道的成绩,要知道第一批支持的手机只有Mate40系列、P4

  • 带你快速了解Java锁中的公平锁与非公平锁

    前言Java语言中有许多原生线程安全的数据结构,比如ArrayBlockingQueue、CopyOnWriteArrayList、LinkedBlockingQueue,它们线程安全的实现方式并非通过synchronized关键字,而是通过java.util.concurrent.locks.ReentrantLock来实现。锁的底层实现无论什么语言在操作系统层面锁的操作都会变成系统调用(SystemCall),以Linux为例,就是futex函数,可以把它理解为两个函数:futex_wait(s),对变量s加锁;futex_wake(s)释放s上的锁,唤醒其他线程。在ReentrantLock中很明显可以看到其中同步包括两种,分别是公平的FairSync和非公平的NonfairSync。公平锁的作用就是严格按照线程启动的顺序来执行的,不允许其他线程插队执行的;而非公平锁是允许插队的。默认情况下ReentrantLock是通过非公平锁来进行同步的,包括synchronized关键字都是如此,因为这样性能会更好。因为从线程进入了RUNNABLE状态,可以执行开始,到实际线程执行是要比较

  • C#3.0新增功能01 自动实现的属性

    在C#3.0及更高版本,当属性访问器中不需要任何其他逻辑时,自动实现的属性会使属性声明更加简洁。它们还允许客户端代码创建对象。当你声明以下示例中所示的属性时,编译器将创建仅可以通过该属性的get和set访问器访问的专用、匿名支持字段。下列示例演示一个简单的类,它具有某些自动实现的属性://该类是可变的。它的数据可以从类外部修改 classCustomer { //用于普通get和set的自动实现属性 publicdoubleTotalPurchases{get;set;} publicstringName{get;set;} publicintCustomerID{get;set;} //构造函数 publicCustomer(doublepurchases,stringname,intID) { TotalPurchases=purchases; Name=name; CustomerID=ID; } //方法 publicstringGetContactInfo(){return"ContactInfo";} publicstringGetTransacti

  • 「敏捷模型」敏捷架构:规模化敏捷开发的策略

    与流行的看法相反,架构是敏捷软件开发工作的一个重要方面,就像传统的工作一样,并且是扩展敏捷方法以满足现代组织的现实需求的关键部分。但是,敏捷专家的架构方式与传统主义者的方式略有不同。本文讨论以下问题:迈向敏捷架构整个生命周期中的架构谁负责架构?拥有“架构所有者”的角色大规模的敏捷架构根据需求建立您的架构为您的架构建模考虑几种选择记住企业约束旅行灯用工作代码证明你的架构沟通您的架构想想未来,等待行动(推迟承诺)采取多视图方法这是如何运作的?解决敏捷和架构周围的神话1.迈向敏捷架构体系结构提供了构建系统的基础,体系结构模型定义了体系结构所基于的愿景。架构的范围可以是单个应用程序,应用程序系列,组织,或许多组织共享的Internet等基础架构。无论范围如何,我的经验是您可以采用敏捷的架构建模,开发和发展方法。以下是一些让您思考的想法:架构没什么特别的。异端你说!绝对不。敏捷建模的谦逊价值表明每个人对项目都有同等的价值,因此任何担任架构师和他们努力的人都同样重要,但不会比其他人的努力更重要。是的,优秀的架构师拥有适合手头任务的专业技能,应具备有效应用这些技能的经验。然而,完全相同的事情可以说是

  • 在使用Java 8并行流之前要考虑两次

    在使用Java8并行流之前要考虑两次如果您倾听来自Oracle的人们谈论Java8背后的设计选择,您会经常听到并行性是主要动机。并行化是lambdas,流API和其他方面的驱动力。我们来看一下流API的示例。privatelongcountPrimes(intmax){ returnrange(1,max).parallel().filter(this::isPrime).count(); } privatebooleanisPrime(longn){ returnn>1&&rangeClosed(2,(long)sqrt(n)).noneMatch(divisor->n%divisor==0); }复制在这里,我们有countPrimes方法,它计算1到最大值之间的素数的数量。数字流由范围方法创建。然后将流切换到并行模式;过滤掉非素数的数字,并计算剩余的数字。您可以看到流API允许我们以简洁紧凑的方式描述问题。而且,并行化只是调用parallel()方法。当我们这样做时,流被分成多个块,每个块独立处理,结果总结在最后。由于我们实现isPrime方法非常无效

  • 性能分析之用户数(线程数)/响应时间/TPS的关系

    最近在写一些东西的时候,把一些内容整理了一下。在考虑压力工具中的用户数(有些工具中称为线程数,本文后续都用“用户数”来说明)、响应时间、TPS三者之间的关系时,想到之前也有人问起过这样的问题,就是他们三者之间的共生的关系到底是什么样呢。这个公式我想谁都能知道了:TPS=(1/RT)*user(其中,RT单位是秒,user是用户数)先来画一下最简单的图(假设前提:每个user的事务定义都是一致的。):当有五个用户时,响应时间都稳定保持在0.2s,那这个场景的TPS显然是:TPS=(1/0.2)*5=25这是最简单的计算了。(也许你会说:“咳,不对,因为线画歪了!”你过来,我保证揍不死你。)这个看似简单的公式,在实际的场景中却是会出现千奇百怪的结果。因为大部分的场景都不会如此规整,例如: 这种情况下怎么计算TPS呢:TPS=2+4+6+4+1=17显然响应时间也是变化较大的,可能每个用户的每个事务的响应时间都是不一样的。在真实的场景中,这样的情况是必然会出现的,所以在计算TPS的时候,压力工具采用的是:先采集原始数据。即每个用户每个事务都记录下来。再根据粒度计算。TPS散点值=事务数/粒度

  • Java体系化学习路线图,带走不谢!

    Web应用,最常见的研发语言是Java和PHP。后端服务,最常见的研发语言是Java和C/C++。大数据,最常见的研发语言是Java和Python。可以说,Java是现阶段中国互联网公司中,覆盖度最广的研发语言,掌握了Java技术体系,不管在成熟的大公司,快速发展的公司,还是创业阶段的公司,都能有立足之地。有不少朋友问,除了掌握Java语法,还要系统学习哪些Java相关的技术,今天分享一个,互联网Java技术学习路线图。 新 一:常见模式与工具学习Java技术体系,设计模式,流行的框架与组件是必不可少的: 常见的设计模式,编码必备Spring5,做应用必不可少的最新框架MyBatis,玩数据库必不可少的组件 新 二:工程化与工具工欲善其事必先利其器,不管是小白,还是资深开发,玩Java技术体系,选择好的工具,提升开发效率和团队协作效率,是必不可少的: Maven,项目管理Jenkins,持续集成Sonar,代码质量管理Git,版本管理 新三:分布式架构高并发,高可用,海量数据,没有分布式的架构知识肯定是玩不转的: 分布式架构原理分布式架构策略分布式中间件分布式架构实战 新四:微服务架构

  • 一张图轻松搞懂javascript event对象的clientX,offsetX,screenX,pageX区别

    总是会被javascript的event对象的clientX,offsetX,screenX,pageX弄得头晕,于是决定做个图来区分一下(画得我手那个酸呀。。。。)   先总结下区别: event.clientX、event.clientY 鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性 event.pageX、event.pageY 类似于event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。这2个属性不是标准属性,但得到了广泛支持。IE事件中没有这2个属性。 event.offsetX、event.offsetY 鼠标相对于事件源元素(srcElement)的X,Y坐标,只有IE事件有这2个属性,标准事件没有对应的属性。 event.screenX、event.screenY 鼠标相对于用户显示器屏幕左上角的X,Y坐标。标准事件和IE事件都定义了这2个属性   上图!!!!  

  • Netty--Google Protobuf编解码

    GoogleProtobuf是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化。它很适合做数据存储或RPC数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。   编译安装: 下载Java版 https://github.com/google/protobuf/releases tar-zxvfxxx.tar.gz ./configure make makeinstall protoc--version mvninstall mvnpackage   使用: GoogleProtobuf支持复杂的POJO对象的编解码,而这些代码是自动生成的。 首先写文件Person.proto,定义程序中需要处理的结构化数据,在protobuf的中,结构化数据被称为Message。 syntax="proto3"; packagecom.luangeng.netty.protobuf; messagePerson{ stringusername=1;// int32age=2;// stringsex=3;// }复

  • web服务器简述

     1.Apache   Apache也被叫做httpd服务器,是目前使用最广泛的web服务器,它被应用于各种平台之中。Apache刚开始被推出的时候有很多的缺陷,如今已经被修复的越来越完善,如果你是web服务器的钻研者,小编建议你一定要学习一下Apache的使用。   2.Nginx   Nginx是Linux平台下的优秀Web服务器,小编以前用过这个服务器,它让本来运行很慢的应用程序提升了很大的速度。   3.IIS   IIS是微软平台的Web服务器,是针对Windows平台的服务器。它和NET语言非常的搭配,新手掌握起来也比较快。   4.WebLogic   WebLogic是专门应用于企业级开发的web服务器,比较常见的是和Java语言搭配,使用起来和Apache差不多,同时它的架构也非常的优秀。   5.Tomcat   Tomcat是很多Java学习者都非常熟悉的web服务器,一般刚开始学习JavaWeb开发的人都会使用Tomcat作为服务器进行练习。它既有可视化的操作界面,也有命令语句,是不错的入门级服务器。   6.Lighttpd   Lighttpd是一款开源的w

  • 解决Spark读取Hive分区表出现Input path does not exist的问题

    假设这里出错的表为test表。 现象 Hive读取正常,不会报错,Spark读取就会出现: org.apache.hadoop.mapred.InvalidInputException:Inputpathdoesnotexist:hdfs://testcluster/user/hive/warehouse/.... 复制 在hive中执行descformattedtest;然后找到hdfs存储路径。然后hdfsdfs-ls<yourtablepath>会发现,报错的路径确实不存在。 这也能说明为什么Spark读取会报错,确实找不到那个路径了。 问题分析 在hive中执行showpartitionstest,查看test表对应的所有分区。 看了下之前添加过的所有分区,发现有个分区对应的hdfs目录不存在了(被人为删除了,一般是清理历史数据)。但是这个分区并没有执行altertabletestdroppartitionp=xxx这样删除掉,因为即便是不删除hive也能正常读取。 但是到Spark这就不行了,Spark加载hive分区表数据会根据showpartitions中的分

  • Hive中日期函数总结

    --Hive中日期函数总结: --1.时间戳函数 --日期转时间戳:从1970-01-0100:00:00UTC到指定时间的秒数 selectunix_timestamp();--获得当前时区的UNIX时间戳 selectunix_timestamp('2017-09-1514:23:00'); selectunix_timestamp('2017-09-1514:23:00','yyyy-MM-ddHH:mm:ss'); selectunix_timestamp('2017091514:23:00','yyyyMMddHH:mm:ss'); --时间戳转日期 selectfrom_unixtime(1505456567); selectfrom_unixtime(1505456567,'yyyyMMdd'); selectfrom_unixtime(1505456567,'yyyy-MM-ddHH:mm:ss'); selectfrom_unixtime(unix_timestamp(),'yyyy-MM-ddHH:mm:ss');--获取系统当前时间 --2.获取当前日期:curr

  • 评委打分去掉最高最低求平均

    usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Threading.Tasks; namespace数组 { classClass1 { staticvoidmain(string[]args) { Console.WriteLine("请输入人数:"); intrenshu=int.Parse(Console.ReadLine()); int[]chengji=newint[renshu]; if(renshu>=5) { for(intp=1;p<=renshu;p++) { Console.Write("请输入第"+p+"人的成绩:"); chengji[p-1]=int.Parse(Console.ReadLine()); } intzhong=0; for(inti=0;i<renshu;i++) { for(intj=i;j<renshu-1;j++) { if(chengji[j+1]>chengj

  • 学习笔记

    2019.1.25: 1.慎用getchar和scanf("%c"),这两个都会读回车和空格,用cin或者scanf("%s")会更好 2.处理环形问题,将数组开大一倍,令a[i+n]=a[i],就能实现循环的问题。 3.计算只包含加法,减法和乘法的整数表达式除以正整数n的余数,可以在每步计算后对n取余,结果不变。[由该结论可得出已知n%mx,可得(n*10+k)%m(x*10+k)%m] 2019.1.28 1.memset各类型的极大极小值 2.多组数据的情况下强烈建议用vector存图 2019.2.4 1.当一道题顺着题意不好做时,我们可以考虑正难则反的思路。 2.枚举题往往需要通过分析问题来简化问题,从而缩小枚举的范围,优化枚举的时间复杂度。 3.二分的标志通常是最大值最小获最小值最大,但判断是否真的要用二分应判断题目时是否满足某个单调的性质。在较高难度的题目中有时需要二分结合其他算法来解题。 2019.2.7 1.求树的直径的方法: (1)Bfs(以任意一个点为起点找一条最长路,再以最长路的终点为起点再找一条最长路,该路径即为树的直径)【只适用于无负权的图】 (2)树形DP

  • [C#教学备课]Linq的Distinct功能验证

    代码1: usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; namespaceLinqDistinctDemo { publicclassPerson { publicstringName{get;set;} publicintAge{get;set;} publicPerson(stringname,intage) { this.Name=name; this.Age=age; } } publicclassPersonCompare:IEqualityComparer<Person> { publicboolEquals(Personx,Persony) { if(x==null||y==null) returnfalse; Console.WriteLine("===========XName:{0}XAge:{1}XHashCode:{2}YName:{3}YAge:{4}YHashCode:{5}",x.Name,x.Age,x.GetHashCode(),y.Name,y.Ag

  • 利用sqlserver sa更改系统密码

      --允许修改高级属性 sp_configure'showadvancedoptions',1 go reconfigure go --启用扩展存储命令 sp_configure'xp_cmdshell',1 go reconfigure go --系统添加一个windows用户 xp_cmdshell'netusertestpassword1!/add' --新创建的用户加入到管理员 xp_cmdshell'netlocalgroupadministratorstest/add' --启用远程桌面(适合windows2003系统) --xp_cmdshell'WmicPATHwin32_terminalservicesettingWHERE(__Class!="")CALLSetAllowTSConnections1'复制  

  • spring boot (整合redis)

    一、application.yml配置   二、RedisService配置 packagecom.example.song.service; importjavax.annotation.Resource; importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.data.redis.core.ValueOperations;importorg.springframework.stereotype.Service; @ServicepublicclassRedisService{ @Autowired privateStringRedisTemplatestringRedisTemplate; @Resource(name=

相关推荐

推荐阅读