算法思想

计算预测元素与所有训练集样本之间的距离,参考前n个最近距离的样本,预测该样本

 
# 计算距离,每个样本与所有样本的距离
res = []
for x in X:
    # 在axis=1列的方向上相加,每个样本得到一个结果
    distance = np.sqrt(((self.X - x)**2).sum(axis=1))
    idxes = np.argsort(distance)[:self.n_neighbors]
    res.append(np.round(a=self.y[idxes].mean(),decimals=3))

 

    

 

    

 
KNN的核心思想是 环境决定个体 

比如,
你是一个Java开发,三年工作经验,有个人想知道你的工资,
他不需要问你,只需要知道这一个类多少工资就可以了

又比如,你去相亲,对方说出她的职业,你就知道他的很多信息... 

你周围的人不是在搬砖,就是在拉车,一年365天只有过年休息几天,你估计也是如此

你周围的人活的如牛马,勤勤恳恳地劳动,你估计也是

你周围的人都是坐办公室的,你估计也是

你周围的人非富即贵,你估计也是

你周围的人谈笑有鸿儒,往来无白丁,你估计也是
    

 

    

 

    

 
环境决定个体,通过分析环境确定个本的性质,这种思想是伟大的
但不是完美的,缺少不维度之间的比较方法

向量内积运算,各个维度上的元素相乘之后,最终相加之后是要化为一个数的 

比如,谈对象,有 身高,年龄,相貌,潜力,资产 五项
身高之间的波动一般不超过1米,
资产之间可能有上万的差距,

KNN是计算距离,这种情况下,资产波动几十块钱,就淹没了身高的波动,这不合理

这就是出现了两类解决办法

百分比

 
不同维度之间使用百分比进行比较,
还是归一化的思想,但百分比是更贴切的描述

所有维度的最大值化为1,这种每个元素值的天花板是一样的,
重在一样/差不多,可以是1.2,1.7... 
不要出现一个维度波动可以淹没其他维度变化的情况,
让每个维度的波动维持在一个相对统一的量纲上 

此时,针对如何归一的细节,普遍有两个方法:
1. 每个元素除以集合中的最大值,批归一化

2. 一个样本中的元素除以这个样本的最大值,层归一化

3. 标准化,减均值,除以标准差,使得元素之间的平均距离化为1,可以针对全体也可以针对某个样本

4. 概率化,
就是将一个样本看作一个概率,不同的特征就是事件,这通常是分类的最后一步,
比如,n分类问题,最终是分为n个类别,不是这一类就是那一类,必定是一种一个类别,
它们地位同等(因为数据比例等比),概率之和为1,也就是某个样本,必是n分类中有的一类
通常的实现方式是softmax,这也是为什么softmax会放在整个算法的最后一步的原因

这些都是归一化的思路,百分比更合适的一些,
但百分比大家想到的是最后之和是1,就是概率了,
但在AI中都是近似计算,
只要能展现出差异即可,所以最终结果不是1也没关系的,可以是90%,也可以是170%,差不多就行

 
总体来说,就是将所有元素拉到同一个量纲,通常是1附近,
这样,元素之间的比较,就可以跨维度进行对比,

在同一量纲上,差异越大,越好分类
深度学习之间有多个网络,可以理解为刚开始的时候,
有的元素之间相差0.5%,
到最后一层网络的时候,元素之间出现200%的差异,这就好分类
比如,一个水果,51%像苹果,49%像梨,
另外一种情况是,10%像苹果,120%像梨,显示这种情况更好区分一些

注意,这里再次强调了AI是近似,虽然10%+120% !=1,违反了概率的定义,
但也是有用的,表达了后者更像梨的倾向

 
还有另外一个思路,加权

 
身高,年龄,相貌,潜力,资产,情绪价值 六项进行综合比较,选出一个你满意的

不同维度之间整合,就是各个维度计算之后 相加,没错,就是加法

加法,相加,相融,就代表整合/融合 

比如,资产变化会覆盖身高的变化,
那可不可以在资产前面乘一个系数,使资产的变化与身高的变化处于一个量级上?

可以,这就是权加,为每个元素都加一个系数,让所有维度处于一个量级上 
  

 
接下来的问题是,如何计算这个系数?
人算是不可能了,由计算机算,
可以理解为计算机进行大量的尝试,最终以结果为导向选择了合适的系数

但总体来说,前提让各个维度的量纲处于同一水平上,对后面的计算是有益的

 

  

 
AI重全局,不重个体
生的是全局的差异,即一个样本在全体数据集中的位置
是相对位置
是百分比

而KNN,强调的是与自己最近的,最相似的样本,取n个 

就是要在全体数据集中找到自己的位置
遍历计算距离,计算距离是过程,目的在于找到离自己最近的样本,
那就是自己在整体中的位置... 

 


 


数据集

房价:回归问题

 
from sklearn.datasets import load_boston
X,y = load_boston(return_X_y=True)
            
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.15, random_state=73)
    
KNN调用示例

 
from sklearn.neighbors import KNeighborsRegressor
knn = KNeighborsRegressor(n_neighbors=5)
knn.fit(X=X_train,y=y_train)
y_test_pred1=knn.predict(X=X_test)

    

 
y_test_pred1[:7]
array([23.56, 24.7 , 40.6 , 15.16, 18.04, 19.7 , 18.02])
  
mse = ((y_test_pred1 - y_test)**2).mean()
mse
21.96151578947368

自定义KNN

 
import numpy as np

class MyKNNRegressor():
    def __init__(self,n_neighbors=5):
        """
        自定义KNN算法
        """
        self.n_neighbors = n_neighbors 
        
    def fit(self, X, y):
        """
        本质什么也没有做,但可以做一次排序优化工作
        """
        self.X = np.array(X) 
        self.y = np.array(y)
    
    def predict(self, X):
        """
            求距离的目的是为了找到距离最近的样本的值
            求距离只是一个过程
        """
        X = np.array(X)
        if X.ndim !=2:
            raise Exception("默认是2维结构,批量预测")
        
        # 计算距离,每个样本与所有样本的距离
        res = []
        for x in X:
            # 在axis=1列的方向上相加,每个样本得到一个结果
            distance = np.sqrt(((self.X - x)**2).sum(axis=1))
            idxes = np.argsort(distance)[:self.n_neighbors]
            res.append(np.round(a=self.y[idxes].mean(),decimals=3))
        return np.array(res)
            

 
knn_my = MyKNNRegressor()
knn_my.fit(X=X_train,y=y_train)

y_test_pred2 = knn_my.predict(X=X_test)
mse = ((y_test_pred2 - y_test)**2).mean()
mse
21.96151578947368
    

 
y_test_pred2[:7]
array([23.56, 24.7 , 40.6 , 15.16, 18.04, 19.7 , 18.02])
    

 

    

 

    

 
def fit(self, X, y):
    """
    本质什么也没有做,但可以做一次排序优化工作
    """
    self.X = np.array(X) 
    self.y = np.array(y)
    

 
KNN的训练代码没有处理逻辑,直接在处理逻辑在预测中
将预测时,即时计算

如果数据量大,比较慢  

 

-----------------------------------------------------------

 
def fit(self, X, y):

这里的X,y就是超参数,参数就参数了,为什么带个超?

这是因为在“参数”一词在AI中特指算法内部的参数,
而X,y是外部输入的,就再起一个名字,叫超参数

 
当你告诉不懂AI的人,说你要进行超参数优化,
听起来是不是比参数优化高级那么一点点...
  

 
----------------------------------------------------------------
  

 
import numpy as np

class KNNCorr():
    def __init__(self,n_neighbors=1):
        """相似推荐,从指定数据集寻找最自己最接受的数据
        """
        self.n_neighbors = n_neighbors 
        
    def fit(self, X=None):
        """目标数据,训练数据
        - 以训练
        """
        self.X = X 
        # 在fit方法中预存转置矩阵  
        # self.X_T = self.X.T.copy(order='C')  # C连续内存布局  
    
    def predict(self, X):
        """预测
        - 预测目标数据中与自己相似度最高的数据的索引
        """
        X = np.array(X)

        if X.ndim !=2:
            raise Exception("默认是2维结构,批量预测")
        
        # 计算距离,每个样本与所有样本的距离
        res = []
        
        # 将for循环替换为矢量化计算 
        # 在数据的第2维,即Index=1的位置上增加一维,index=0上代表批次维度,增加一维后,表示每个元素是(1,3)的矩阵,
        # 减X时,会先进行广播,[b,1,n] -- [b,m,n] m为X的元素个数,即self.X中的每个元素会先自复制到与X同元素个数
        distance_matrix = np.sqrt(((X[:, np.newaxis] - self.X)**2).sum(axis=2)) 
        index = distance_matrix.argmin(axis=1)
        return index
        
  

 
import numpy as np 
np.random.seed(42)
X = np.random.rand(7,3)
X_test =X[:3]

y_index = model.predict(X_test)
X[y_index]
  
array([[0.37454012, 0.95071431, 0.73199394],
       [0.59865848, 0.15601864, 0.15599452],
       [0.05808361, 0.86617615, 0.60111501]])
       

 

X_test
array([[0.37454012, 0.95071431, 0.73199394],
    [0.59865848, 0.15601864, 0.15599452],
    [0.05808361, 0.86617615, 0.60111501]])
  

 


距离的度量·优化

 
from tpf import load_boston
X_train, y_train, X_test, y_test = load_boston(split=True,test_size=0.15)

 
import numpy as np

class MyKNNRegressor2():
    """改变距离的度量方法
    - 没有开平方
    """
    def __init__(self,n_neighbors=5):
        """
        自定义KNN算法
        """
        self.n_neighbors = n_neighbors 
        
    def fit(self, X, y):
        """
        本质什么也没有做,但可以做一次排序优化工作
        """
        self.X = np.array(X) 
        self.y = np.array(y)
    
    def predict(self, X):
        """
            求距离的目的是为了找到距离最近的样本的值
            求距离只是一个过程
        """
        X = np.array(X)
        if X.ndim !=2:
            raise Exception("默认是2维结构,批量预测")
        
        # 计算距离,每个样本与所有样本的距离
        res = []
        for x in X:
            # 在axis=1列的方向上相加,每个样本得到一个结果
            distance = ((self.X - x)**2).sum(axis=1)
            idxes = np.argsort(distance)[:self.n_neighbors]
            res.append(np.round(a=self.y[idxes].mean(),decimals=3))
        return np.array(res)

    
knn_my = MyKNNRegressor2()
knn_my.fit(X=X_train,y=y_train)

y_test_pred2 = knn_my.predict(X=X_test)
mse = ((y_test_pred2 - y_test)**2).mean()
mse
21.96151578947368
    

 

    

 
class MyKNNRegressor3():
    """改变距离的度量方法
    - 没有开平方
    """
    def __init__(self,n_neighbors=5):
        """
        自定义KNN算法
        """
        self.n_neighbors = n_neighbors 
        
    def fit(self, X, y):
        """
        本质什么也没有做,但可以做一次排序优化工作
        """
        self.X = np.array(X) 
        self.y = np.array(y)
    
    def predict(self, X):
        """
            求距离的目的是为了找到距离最近的样本的值
            求距离只是一个过程
        """
        X = np.array(X)
        if X.ndim !=2:
            raise Exception("默认是2维结构,批量预测")
        
        # 计算距离,每个样本与所有样本的距离
        res = []
        for x in X:
            # 在axis=1列的方向上相加,每个样本得到一个结果
            distance = (np.abs((self.X - x))).sum(axis=1)
            idxes = np.argsort(distance)[:self.n_neighbors]
            res.append(np.round(a=self.y[idxes].mean(),decimals=3))
        return np.array(res)

 
knn_my = MyKNNRegressor3()
knn_my.fit(X=X_train,y=y_train)

y_test_pred2 = knn_my.predict(X=X_test)
mse = ((y_test_pred2 - y_test)**2).mean()
mse
    

 
18.638584210526318
    

 
平方与不平方没有任何的区别
而L1距离与L2距离有所区别,从KNN的效果来看,L1距离比L2距离的MSE更小一些

 
距离的计算不是目的,是一个过程,
计算距离是为了找出离目标最近的n个样本

找最近的n个样本才是目的
如此,达到同样的目的,就可以有多种方案选择

 
一段代码要注意分析该代码的目的是什么?
哪些只是计算的一种方式,只是一个过程

要区分过程和目的... 
-----------------------------------------------------------------------

 


 

  

 


参考
    sklearn2pmml github

    PMML讲解及使用