第L9周:无监督学习|K-means聚类算法

news2024/10/4 14:00:48
  • 本文为365天深度学习训练营 中的学习记录博客
  • 原作者:K同学啊

任务描述:
●学会调用sklearn实现KMeans算法。
●了解误差平方和与轮廓系数。

1.聚类算法是什么?

聚类就是将一个庞杂数据集中具有相似特征的数据自动归类到一起,称为一个簇,簇内的对象越相似,聚类的效果越好。“相似”这一概念,是利用距离标准来衡量的,我们通过计算对象与对象之间的距离远近来判断它们是否属于同一类别,即是否是同一个簇。
聚类是一种无监督学习(Unsupervised Learning)的方法,不需要预先标注好训练集。聚类与分类最大的区别就是分类的目标事先已知,对于一个动物集来说,你并不清楚这个数据集内部有多少种类的动物,你能做的只是利用聚类方法将它自动按照特征分为多类,然后人为给出这个聚类结果的定义(即簇识别)。例如,你将一个动物集分为了三簇(类),然后通过观察这三类动物的特征,你为每一个簇起一个名字,如大象、狗、猫等,这就是聚类的基本思想。
K-means就是一个聚类的算法,属于无监督学习算法,也是就样本没有标签。算法会根据某种规则进行“分割”,把相同的或者相近的数据放在一起。K-means算法的基本思想是通过不断更新簇的中心点,将数据集划分为预定数量的簇。这一过程涉及到计算数据点之间的距离,通常使用欧式距离作为相似性度量。在算法执行过程中,每个数据点被分配到距离最近的簇,然后更新簇的中心,迭代进行直至收敛。

2.K-means 算法思想

在聚类过程中,基于相似性度量,使得相同子集中各元素间差异性最小,而不同子集间的元素差异性最大,这就是(空间)聚类算法的本质,K-Means正是这样一种算法的代表。
K-Means聚类于1957年由J.B. MacQueen首次提出,目前已经超过60年,但仍然是应用最广泛、地位最核心的空间数据划分聚类方法之一。K-means算法算法的输入为一个样本集(或者称为点集),通过该算法可以将样本进行聚类,具有相似特征的样本聚为一类。
针对每个点,计算距离该点最近的中心点,然后将该点归为最近中心点代表的簇。一次迭代结束之后,针对每个簇类,重新计算中心点,然后针对每个点,重新寻找距离自己最近的中心点。如此循环,直到前后两次迭代的簇类没有变化。

在这里插入图片描述

3.K-means 聚类过程

K-means算法接受一个参数K用以决定结果中簇的数目。算法开始时,要在数据集中随机选择K个数据对象用来当做K个簇的初始中心,而将剩下的各个数据对象就根据他们和每个聚类簇心的距离选择簇心最近的簇分配到其中。然后重新计算各个聚类簇中的所有数据对象的平均值,并将得到的结果作为新的簇心;逐步重复上述的过程直至目标函数收敛为止。其步骤具体地:

●第一步:从N个样本数据中随机选取K个对象,作为初始的聚类中心;
●第二步:分别计算每个样本点到各个聚类中心的距离,并逐个分配到距离其最近的簇中;
●第三步:所有对象分配完成后,更新K个类中心位置,类中心定义为簇内所有对象在各个维度的均值;
●第四步:与前一次计算得到的K个聚类中心比较,如果聚类中心发生变化,转至步骤2,否则转至步骤5;
●第五步:当类中心不再发生变化,停止并输出聚类结果,然后整理我们所需要的信息,如各个样本所属的类等等,进行后续的统计和分析。

聚类结束之前,类中心会不断移动,而随着类中心的移动,样本的划分情况也会持续发生改变。

在这里插入图片描述

在这里插入图片描述

4.K-means 代码实现

我的环境:
●语言环境:Python3.9
●编译器:Jupyter Lab
●数据集:Iris.csv

需要提前安装的包为:pandas、matplotlib、sklearn,你需要逐一在命令行(cmd)中输入以下语句对第三方库进行安装:

●pip install pandas
●pip install matplotlib
●pip install scikit-learn

示例:

在这里插入图片描述

4.1.导入数据

import time
import pandas as pd
import numpy  as np
import matplotlib.pyplot as plt
from numpy import nonzero, array
from sklearn.decomposition import PCA

# 数据保存在.csv文件中
iris = pd.read_csv("L8/Iris.csv", header=0)  # 鸢尾花数据集 Iris  class=3
df   = iris  # 设置要读取的数据集
df

代码输出:

sepal lengthsepal widthpetal lengthpetal widthclass
05.13.51.40.2Iris-setosa
14.93.01.40.2Iris-setosa
24.73.21.30.2Iris-setosa
34.63.11.50.2Iris-setosa
45.03.61.40.2Iris-setosa
..................
1456.73.05.22.3Iris-virginica
1466.32.55.01.9Iris-virginica
1476.53.05.22.0Iris-virginica
1486.23.45.42.3Iris-virginica
1495.93.05.11.8Iris-virginica

150 rows × 5 columns

4.2.数据预处理

columns  = list(df.columns)  # 获取数据集的第一行,第一行通常为特征名,所以先取出
columns

代码输出:

['sepal length', 'sepal width', 'petal length', 'petal width', 'class']
# 数据集的特征名(去除了最后一列,因为最后一列存放的是标签,不是数据)
features = columns[:-1]  
features

代码输出:

['sepal length', 'sepal width', 'petal length', 'petal width']
# 预处理之后的数据,去除掉了第一行的数据(因为其为特征名,如果数据第一行不是特征名,可跳过这一步)
dataset  = df[features]
dataset

代码输出:

sepal lengthsepal widthpetal lengthpetal width
05.13.51.40.2
14.93.01.40.2
24.73.21.30.2
34.63.11.50.2
45.03.61.40.2
...............
1456.73.05.22.3
1466.32.55.01.9
1476.53.05.22.0
1486.23.45.42.3
1495.93.05.11.8

150 rows × 4 columns

attributes      = len(df.columns) - 1   # 属性数量(数据集维度)
original_labels = list(df[columns[-1]]) # 原始标签
attributes

代码输出:

4
original_labels

代码输出:

['Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-setosa',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-versicolor',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica',
 'Iris-virginica']

4.3.绘制聚类结果散点图

def draw_cluster(dataset, centers, labels, k):
    center_array = array(centers)
    if attributes > 2:
        # 将三维数据降为二维,方便进行可视化
        dataset      = PCA(n_components=2).fit_transform(dataset)  # 如果属性数量大于2,降维
        center_array = PCA(n_components=2).fit_transform(center_array)  # 如果属性数量大于2,降维
    else:
        dataset = array(dataset)
    # 做散点图
    label = array(labels)
    plt.scatter(dataset[:, 0], 
                dataset[:, 1], 
                marker='o', 
                c='black', s=7)  # 原图

    colors = np.array(
        ["#FF0000", "#0000FF", "#00FF00", "#FFFF00", "#00FFFF", "#FF00FF", "#800000", "#008000", "#000080", "#808000",
         "#800080", "#008080", "#444444", "#FFD700", "#008080"])
    
    # 循换打印k个簇,每个簇使用不同的颜色
    for i in range(k):
        plt.scatter(dataset[nonzero(label == i), 0], 
                    dataset[nonzero(label == i), 1], 
                    c=colors[i], 
                    s=7, marker='o')

    plt.show()

4.4.进行聚类分析

from sklearn.cluster import KMeans

k = 3

# 使用KMeans进行聚类
kmeans = KMeans(n_clusters=k, n_init=10).fit(dataset)  # 指定要分的簇数
labels = kmeans.labels_
labels

代码输出:

array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0,
       0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0,
       0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2], dtype=int32)
centers = kmeans.cluster_centers_
centers

代码输出:

array([[6.85      , 3.07368421, 5.74210526, 2.07105263],
       [5.006     , 3.418     , 1.464     , 0.244     ],
       [5.9016129 , 2.7483871 , 4.39354839, 1.43387097]])
draw_cluster(dataset, centers, labels, k)

代码输出:

在这里插入图片描述

5.k 值的选择

k-means算法因为手动选取k值和初始化随机质心的缘故,每一次的结果不会完全一样,而且由于手动选取k值,我们需要知道我们选取的k值是否合理,聚类效果好不好,那么如何来评价某一次的聚类效果呢?
也许将它们画在图上直接观察是最好的办法,但现实是,我们的数据不会仅仅只有两个特征,一般来说都有十几个特征,而观察十几维的空间对我们来说是一个无法完成的任务。因此,我们需要一个公式来帮助我们判断聚类的性能,这个公式就是SSE (Sum of Squared Error, 误差平方和),它其实就是每一个点到其簇内质心的距离的平方值的总和,SSE值越小表示数据点越接近于它们的质心,聚类效果也越好。
因为对误差取了平方,因此更加重视那些远离中心的点。一种肯定可以降低SSE值的方法是增加簇的个数,但这违背了聚类的目标。聚类的目标是在保持簇数目不变的情况下提高簇的质量。

5.1.误差平方和(SSE)

在这里插入图片描述

在这里插入图片描述

这是因为在确定聚类簇数时,还必须考虑计算成本。如果我们增加聚类的数量,计算成本也会增加。K-means算法还存在一个问题,它有可能会停留在一个局部最小值处,而这取决于初始化的情况。为了解决这个问题,我们通常需要多次运行K-means算法,每一次都重新进行随机初始化,最后再比较多次运行K-means的结果。

代码实现:

import pandas as pd  
import numpy  as np  
import matplotlib.pyplot as plt  
from sklearn.cluster import KMeans  
  
# 提供的数据  
data = {  
    'K1': [3, 3, 4, 4, 1, 1, 2, 2],  
    'K2': [1, 2, 1, 2, 3, 4, 3, 4]  
}  
  
# 创建DataFrame  
df = pd.DataFrame(data)  
  
# 提取用于聚类的特征  
X = df[['K1', 'K2']]
  
# 初始化KMeans模型列表,并设定k的范围  
range_n_clusters = [1, 2, 3, 4, 5, 6, 7, 8]  # 扩大了k的范围  
inertia_scores = []  

# 对每个k值进行聚类并计算SSE值  
for n_clusters in range_n_clusters:  
    # 使用KMeans算法  
    kmeans = KMeans(n_init=10, 
                    n_clusters=n_clusters, 
                    random_state=123)  
    kmeans.fit(X)  
      
    # 获取SSE值
    inertia = kmeans.inertia_  
    inertia_scores.append(inertia) 
    
# 绘制惯性指标随聚类数量变化的图形  
plt.figure(figsize=(8, 4))   
plt.plot(range_n_clusters, inertia_scores, marker='o')  
plt.xlabel('Number of clusters')  
plt.ylabel('SSE')  
plt.title('SSE for Different Number of Clusters')  
plt.xticks(range_n_clusters)  # 显示所有x轴刻度  
plt.show()

代码输出:

在这里插入图片描述

5.2.轮廓系数(silhouette)

轮廓系数用于评估数据点与其所属簇的凝聚度及其与最近邻簇的分离度,得分范围在[-1, 1]之间,分值越高表示聚类效果越好。每个样本都有对应的轮廓系数,轮廓系数由两个得分组成:

● a:样本与同一簇类中的其他样本点的平均距离 ( 即凝聚度);
● b:样本与距离最近簇类中所有样本点的平均距离 (即分离度)。

每个样本的轮廓系数定义为:

在这里插入图片描述

一组数据集的轮廓系数等于该数据集中每一个样本轮廊系数的平均值。轮廓系数既要考虑聚类结果的凝聚度,又要考虑聚类结果之间的分离度。如果一个数据点与自己所属的簇内的其他数据点的距离很小,但是与其他簇中的数据点的距离很大,就表示这个数据点所在的簇内紧密度高,簇间分离度大,那么该数据点的轮廓系数就会越大。

代码实现:

import pandas as pd  
import numpy as np  
import matplotlib.pyplot as plt  
from sklearn.cluster import KMeans  
from sklearn.metrics import silhouette_score  
  
# 提供的数据  
data = {  
    'K1': [3, 3, 4, 4, 1, 1, 2, 2],  
    'K2': [1, 2, 1, 2, 3, 4, 3, 4]  
}  
  
# 创建DataFrame  
df = pd.DataFrame(data)  
  
# 提取用于聚类的特征  
X = df[['K1', 'K2']]
  
# 初始化KMeans模型列表,并设定k的范围  
range_n_clusters = [2, 3, 4, 5, 6, 7]  # 根据需要调整范围  
silhouette_avg_scores = []  
  
# 对每个k值进行聚类并计算轮廓系数  
for n_clusters in range_n_clusters:  
    # 使用KMeans算法  
    kmeans = KMeans(n_init=10, n_clusters=n_clusters, random_state=42)  
    kmeans.fit(X)  
      
    # 获取聚类标签  
    labels = kmeans.labels_  
      
    # 计算轮廓系数  
    silhouette_avg = silhouette_score(X, labels)  
    silhouette_avg_scores.append(silhouette_avg)  

# 绘制轮廓系数随聚类数量变化的图形  
plt.figure(figsize=(8, 4))  
plt.plot(range_n_clusters, silhouette_avg_scores, marker='o')  
plt.xlabel('Number of clusters')  
plt.ylabel('Silhouette Score')  
plt.title('Silhouette Score for Different Number of Clusters')  
plt.show()

代码输出:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2188139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode 1498. 满足条件的子序列数目

1.题目基本信息 1.1.题目描述 给你一个整数数组 nums 和一个整数 target 。 请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。 由于答案可能很大,请将结果对 109 7 取余后返回。 1.2.题目地址 https://leet…

【优选算法之队列+宽搜/优先级队列】No.14--- 经典队列+宽搜/优先级队列算法

文章目录 前言一、队列宽搜示例:1.1 N 叉树的层序遍历1.2 ⼆叉树的锯⻮形层序遍历1.3 ⼆叉树最⼤宽度1.4 在每个树⾏中找最⼤值 二、优先级队列(堆)示例:2.1 最后⼀块⽯头的重量2.2 数据流中的第 K ⼤元素2.3 前 K 个⾼频单词2.4 …

气象网格数据与卫星轨道数据如何匹配??

🏆本文收录于《全栈Bug调优(实战版)》专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&am…

IDEA里面的长截图插件

1.我的悲惨经历 兄弟们啊,我太惨了,我刚刚在准备这个继承和多态的学习,写博客的时候想要截图代码,因为这个代码比较大,一张图截取不下来,所以需要长截图,之前使用的qq截图突然间拉胯&#xff0…

栈和队列相互实现(Java)

本篇任务 前篇我们分别介绍了栈和队列,并对其进行了简单的自我实现,本篇我们将通过栈和队列的相互实现来进一步熟悉和运用栈和队列,如下是我们将要完成的题目: 用队列实现栈https://leetcode-cn.com/problems/implement-stack-u…

【2022工业图像异常检测文献】CFLOW-AD: 通过条件归一化流实现实时无监督定位异常检测

CFLOW-AD: Real-Time Unsupervised Anomaly Detection with Localization via Conditional Normalizing Flows 1、Background 虽然最近提出针对此类数据设置的模型在准确性指标上取得了很高的成绩,但它们的复杂性限制了实时处理的能力。 CFLOW-AD由一个经过判别式…

区块链+Web3学习笔记

学习资料来源于B站: 17小时最全Web3教程:ERC20,NFT,Hardhat,CCIP跨链_哔哩哔哩_bilibili 该课程提供的Github代码地址,相关资料详见README.md: Web3_tutorial_Chinese/README.md at main sm…

Netty系列-8 Netty处理粘包和半包问题

1.半包和粘包问题 TCP协议是基于字节流的数据通讯协议,数据被看做是一连串的字节流;不具备边界信息,给接收方带来半包和粘包问题。 半包:TCP传输时,将数据切割成一个个数据包进行传输。接收方一次读取操作&#xff0c…

吉他弹唱打谱软件哪个好用 吉他弹唱制谱教程

吉他这门乐器一直受到大众的欢迎,究其原因,还是因为其成本低廉、易上手的特性。但是吉他是一个入门容易精通难的乐器,想要成为一个资深的吉他玩家,那么就少不了用到一些吉他弹唱打谱软件。今天我们就来说一说吉他弹唱打谱软件哪个…

学习 CSS 新的属性 conic-gradient 实现环形进度条

我们在工作中用到环形进度条的时候&#xff0c;一般都是使用组件库提供的&#xff0c;那么你有没有想过这是怎么实现的呢&#xff1f; <divclass"progress"style"--progress: 80%; --last: 20%"data-progress"80%"></div><style …

【宽搜】2. leetcode 102 二叉树的层序遍历

题目描述 题目链接&#xff1a;二叉树的层序遍历 根据上一篇文章的模板可以直接写代码&#xff0c;需要改变的就是将N叉树的child改为二叉树的left和right。 代码 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector&…

k8s的学习和使用

为什么用k8s&#xff0c;不用docker&#xff1f; k8s更适合复杂的微服务架构和大规模的容器应用。 Pods(Pod) Pod是k8s最小可部署单元&#xff0c;他包含一个或多个相关容器。这些容器共享网络命名空间和存储卷&#xff0c;他们通常协同工作来构成一个应用程序。 Serv…

开启AI新篇章:探索GPT-4与大模型!订阅方案!简单支付!

开启AI新篇章&#xff1a;探索GPT-4的无限可能 随着人工智能技术的飞速发展&#xff0c;我们正处于一个前所未有的变革时代。作为人工智能领域的领导者&#xff0c;OpenAI 推出的GPT-4&#xff0c;以其卓越的自然语言处理能力和强大的计算潜力&#xff0c;引发了行业内外的广泛…

深入浅出MySQL

深入浅出MySQL 以下内容参考自 《MySQL是怎样运行的&#xff1a;从根儿上理解MySQL》一书&#xff0c;强烈推荐 存储引擎 对于不同的表可以设置不同的存储引擎 CREATE TABLE tableName (xxxx ) ENGINE 引擎名称; # 修改 ALTER TABLE tableName ENGINE xxx; 编码格式 my…

(C语言贪吃蛇)10.贪吃蛇向右自行行走

目录 前言 本节内容 实现效果 修改后的代码 其他封装函数&#xff1a; 运行效果 总结 前言 我们上节讲解了关于贪吃蛇撞墙然后死翘翘重新初始化蛇身的操作&#xff0c;主要是关于程序初始化释放内存的操作&#xff0c;不理解的再去看看&#x1f618;(贪吃蛇撞墙找死详解)。…

SpringBoot技术栈:构建高效古典舞交流平台

第二章 相关技术介绍 2.1Java技术 Java是一种非常常用的编程语言&#xff0c;在全球编程语言排行版上总是前三。在方兴未艾的计算机技术发展历程中&#xff0c;Java的身影无处不在&#xff0c;并且拥有旺盛的生命力。Java的跨平台能力十分强大&#xff0c;只需一次编译&#xf…

openpnp - 吸嘴校正失败的opencv参数分析

文章目录 openpnp - 吸嘴校正失败的opencv参数分析概述笔记阶段验证 - N2吸嘴校验完NT1NT2 阶段验证 - 底部相机高级校验完NT1NT2 参数比对保存 “阶段验证 - N2吸嘴校验完” 的NT1/NT2图像重建参数检测环境NT1ok的3个参数值NT1err的3个参数值NT2ok的3个参数值NT2err的3个参数值…

如何入门运动规划算法? 50篇教程教你手把手推导公式! 实现代码!

经常听到有想入门规划算法的同学说: 各路教程不成体系, 不知从何学起? 网上的规划算法教程资料确实很多. 但是东一篇frenet, 西一篇QP优化, 大部分都是各路大佬写给自己看的学习笔记, 杂乱无章不成体系. 有没有给小白看的, 完整成体系的运动规划算法教程呢? 穷学生囊中羞…

Redis入门第四步:Redis发布与订阅

欢迎继续跟随《Redis新手指南&#xff1a;从入门到精通》专栏的步伐&#xff01;在本文中&#xff0c;我们将深入探讨Redis的发布与订阅&#xff08;Pub/Sub&#xff09;模式。这是一种强大的消息传递机制&#xff0c;适用于各种实时通信场景&#xff0c;如聊天应用、实时通知和…

反调试—1

IsDebuggerPresent() CheckRemoteDebuggerPresent() 其内部实际调用NtQueryInformationProcess() bool _stdcall ThreadCall() {while (true){BOOL pbDebuggerPresent FALSE;CheckRemoteDebuggerPresent(GetCurrentProcess(), &pbDebuggerPresent);if (pbDebuggerPres…