言尽于此,完结
无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。
- 第一,设计模式能让专业人之间交流方便,如下:
程序员A:这里我用了XXX设计模式
程序员B:那我大致了解你程序的设计思路了
- 第二,易维护
项目经理:今天客户有这样一个需求…
程序员:明白了,这里我使用了XXX设计模式,所以改起来很快
- 第三,设计模式是编程经验的总结
程序员A:B,你怎么想到要这样去构建你的代码
程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题
- 第四,学习设计模式并不是必须的
程序员A:B,你这段代码使用的是XXX设计模式对吗?
程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的
从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!
搜集费时费力,能看到此处的都是真爱!
standardScaler.mean_
standardScaler.scale_ # 表述数据分布范围的变量,替代std_# 使用transformX_train_standard = standardScaler.transform(X_train)
X_test_standard = standardScaler.transform(X_test)
如此就能输出归一化后的数据了。
1.5 自己实现均值方差归一化
同样地,我们仿照sklearn的风格,可以自己实现一下均值方差归一化的方法。
我们在之前的工程中创建processing.py:
import numpy as npclass StandardScaler:
def init(self):
self.mean_ = None
self.scale_ = None
def fit(self, X):
“”“根据训练数据集X获得数据的均值和方差”“”
assert X.ndim == 2, “The dimension of X must be 2”
求出每个列的均值
self.mean_ = np.array([np.mean(X[:,i] for i in range(X.shape[1]))])
self.scale_ = np.array([np.std(X[:, i] for i in range(X.shape[1]))]) return self def tranform(self, X):
“”“将X根据StandardScaler进行均值方差归一化处理”“”
assert X.ndim == 2, “The dimension of X must be 2”
assert self.mean_ is not None and self.scale_ is not None, \ “must fit before transform”
assert X.shape[1] == len(self.mean_), \ “the feature number of X must be equal to mean_ and std_”
创建一个空的浮点型矩阵,大小和X相同
resX = np.empty(shape=X.shape, dtype=float) # 对于每一列(维度)都计算
for col in range(X.shape[1]):
resX[:,col] = (X[:,col] - self.mean_[col]) / self.scale_[col] return resX
0x02 kNN优缺点
KNN的主要优点有:
-
理论成熟,思想简单,既可以用来做分类也可以用来做回归
-
天然解决多分类问题,也可用于回归问题
-
和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
-
由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合
KNN的主要缺点有:
- 计算量大,效率低。
即使优化算法,效率也不高。
-
高度数据相关,样本不平衡的时候,对稀有类别的预测准确率低
-
相比决策树模型,KNN模型可解释性不强
-
维数灾难:
随着维度的增加,“看似相近”的两个点之间的距离越来越大,而knn非常依赖距离
| 维数 | 点到点 | 距离 |
| — | — | — |
| 1维 | 0到1的距离 | 1 |
| 2维 | (0,0)到(1,1)的距离 | 1.414 |
| 3维 | (0,0,0)到(1,1,1)的距离 | 1.73 |
| 64维 | (0,0,…0)到(1,1,…1) | 8 |
| 10000维 | (0,0,…0)到(1,1,…1) | 100 |
大家感觉一万维貌似很多,但实际上就是100*100像素的黑白灰图片。
以上就是关于kNN算法的总结。
你是不是以为这一篇就两节内容就结束了?没想到吧!下面还有一波干货:kNN优化之KD树。
3 KD树
K近邻法的重要步骤是对所有的实例点进行快速k近邻搜索。如果采用线性扫描(linear scan),要计算输入点与每一个点的距离,时间复杂度非常高。因此在查询操作是,使用kd树。
3.1 kd树的原理
kd树是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构,且kd树是一种二叉树,表示对k维空间的一个划分。
k-d tree是每个节点均为k维样本点的二叉树,其上的每个样本点代表一个超平面,该超平面垂直于当前划分维度的坐标轴,并在该维度上将空间划分为两部分,一部分在其左子树,另一部分在其右子树。即若当前节点的划分维度为d,其左子树上所有点在d维的坐标值均小于当前值,右子树上所有点在d维的坐标值均大于等于当前值,本定义对其任意子节点均成立。
3.2 kd树的构建
常规的k-d tree的构建过程为:
-
循环依序取数据点的各维度来作为切分维度,
-
取数据点在该维度的中值作为切分超平面,
-
将中值左侧的数据点挂在其左子树,将中值右侧的数据点挂在其右子树,
-
递归处理其子树,直至所有数据点挂载完毕。
对于构建过程,有两个优化点:
-
选择切分维度:根据数据点在各维度上的分布情况,方差越大,分布越分散,从方差大的维度开始切分,有较好的切分效果和平衡性。
-
确定中值点:预先对原始数据点在所有维度进行一次排序,存储下来,然后在后续的中值选择中,无须每次都对其子集进行排序,提升了性能。也可以从原始数据点中随机选择固定数目的点,然后对其进行排序,每次从这些样本点中取中值,来作为分割超平面。该方式在实践中被证明可以取得很好性能及很好的平衡性。
例子:
- 构建根节点时,此时的切分维度为x,如上点集合在x维从小到大排序为(2,3),(4,7),(5,4),(7,2),(8,1),(9,6);
其中值为(7,2)。
(注:
2,4,5,7,8,9在数学中的中值为(5 + 7)/2=6,但因该算法的中值需在点集合之内,所以本文中值计算用的是len(points)//2=3, points[3]=(7,2))
-
(2,3),(4,7),(5,4)挂在(7,2)节点的左子树,(8,1),(9,6)挂在(7,2)节点的右子树。
-
构建(7,2)节点的左子树时,点集合(2,3),(4,7),(5,4)此时的切分维度为y,中值为(5,4)作为分割平面,(2,3)挂在其左子树,(4,7)挂在其右子树。
-
构建(7,2)节点的右子树时,点集合(8,1),(9,6)此时的切分维度也为y,中值为(9,6)作为分割平面,(8,1)挂在其左子树。
至此k-d tree构建完成。
上述的构建过程结合下图可以看出,构建一个k-d tree即是将一个二维平面逐步划分的过程。
需要注意的是,对于每次切分,都是循环顺序选择维度的,二维是:x->y->x…;三维则是:x->y->z->x…。
下面从三维空间来看一下k-d tree的构建及空间划分过程。首先,边框为红色的竖直平面将整个空间划分为两部分,此两部分又分别被边框为绿色的水平平面划分为上下两部分。最后此4个子空间又分别被边框为蓝色的竖直平面分割为两部分,变为8个子空间,此8个子空间即为叶子节点。
points为实例点集合,depth深度,为用来确定取维度的参数def kd_tree(points, depth):
if 0 == len(points): return None
指定切分维度,len(points[0])是数据的实际维度,这样计算可以保证循环
cutting_dim = depth % len(points[0]) # 切分点初始化
medium_index = len(points) # 对所有的实例点按照指定维度进行排序,itemgetter用于获取对象哪些维度上的数据,参数为需要获取的数据在对象中的序号
points.sort(key=itemgetter(cutting_dim))
将该维度的中值点作为根节点
node = Node(points[medium_index])
对于左子树,重复构建(depth+1)
node.left = kd_tree(points[:medium_index], depth + 1)
对于右子树,重复构建(depth+1)
node.right = kd_tree(points[medium_index + 1:], depth + 1)
return node
3.3 kd树的检索
kd树的检索是KNN算法至关重要的一步,给定点p,查询数据集中与其距离最近点的过程即为最近邻搜索。
如在构建好的k-d tree上搜索(3,5)的最近邻时,对二维空间的最近邻搜索过程作分析。首先从根节点(7,2)出发,将当前最近邻设为(7,2),对该k-d tree作深度优先遍历。以(3,5)为圆心,其到(7,2)的距离为半径画圆(多维空间为超球面),可以看出(8,1)右侧的区域与该圆不相交,所以(8,1)的右子树全部忽略。接着走到(7,2)左子树根节点(5,4),与原最近邻对比距离后,更新当前最近邻为(5,4)。以(3,5)为圆心,其到(5,4)的距离为半径画圆,发现(7,2)右侧的区域与该圆不相交,忽略该侧所有节点,这样(7,2)的整个右子树被标记为已忽略。遍历完(5,4)的左右叶子节点,发现与当前最优距离相等,不更新最近邻。所以(3,5)的最近邻为(5,4)。
3.4 sklearn中的KDTree
Sklearn中有KDTree的实现,仅构建了一个二维空间的k-d tree,然后对其作k近邻搜索及指定半径的范围搜索。多维空间的检索,调用方式与此例相差无多。
import numpy as npfrom matplotlib import pyplot as pltfrom matplotlib.patches import Circlefrom sklearn.neighbors import KDTree
np.random.seed(0)
points = np.random.random((100, 2))
tree = KDTree(points)
point = points[0]# kNNdists, indices = tree.query([point], k=3)
print(dists, indices)# query radiusindices = tree.query_radius([point], r=0.2)
print(indices)
fig = plt.figure()
ax = fig.add_subplot(111, aspect=‘equal’)
ax.add_patch(Circle(point, 0.2, color=‘r’, fill=False))
X, Y = [p[0] for p in points], [p[1] for p in points]
plt.scatter(X, Y)
plt.scatter([point[0]], [point[1]], c=‘r’)
plt.show()
图像化展示:
0xFF 总结
到这里,我们kNN算法就算告一段落了。我们回顾一下:
在《机器学习的敲门砖:kNN算法(上)》中,我们了解了非常适合入门机器学习的算法:k近邻算法。
我们学习了kNN算法的流程,并且在jupyter notebook上手动实现了代码,并且在外部也进行了封装。最后我们学习了sklearn中的kNN算法。
最后
由于文案过于长,在此就不一一介绍了,这份Java后端架构进阶笔记内容包括:Java集合,JVM、Java并发、微服务、SpringNetty与 RPC 、网络、日志 、Zookeeper 、Kafka 、RabbitMQ 、Hbase 、MongoDB、Cassandra 、Java基础、负载均衡、数据库、一致性算法、Java算法、数据结构、分布式缓存等等知识详解。
本知识体系适合于所有Java程序员学习,关于以上目录中的知识点都有详细的讲解及介绍,掌握该知识点的所有内容对你会有一个质的提升,其中也总结了很多面试过程中遇到的题目以及有对应的视频解析总结。
解。
[外链图片转存中…(img-ee7vNzYC-1715675281237)]
本知识体系适合于所有Java程序员学习,关于以上目录中的知识点都有详细的讲解及介绍,掌握该知识点的所有内容对你会有一个质的提升,其中也总结了很多面试过程中遇到的题目以及有对应的视频解析总结。
[外链图片转存中…(img-NG1wqqUl-1715675281238)]
[外链图片转存中…(img-OPrl1C3T-1715675281238)]