点一下关注吧!!!非常感谢!!持续更新!!!
目前已经更新到了:
- Hadoop(已更完)
- HDFS(已更完)
- MapReduce(已更完)
- Hive(已更完)
- Flume(已更完)
- Sqoop(已更完)
- Zookeeper(已更完)
- HBase(已更完)
- Redis (已更完)
- Kafka(已更完)
- Spark(已更完)
- Flink(已更完)
- ClickHouse(已更完)
- Kudu(已更完)
- Druid(已更完)
- Kylin(已更完)
- Elasticsearch(已更完)
- DataX(已更完)
- Tez(已更完)
- 数据挖掘(正在更新…)
章节内容
上节我们完成了如下的内容:
- scikit-learn 泛化能力
- scikit-learn 交叉验证
归一化
距离类模型归一化要求
我们把 X 放到数据框中看一眼,是否观察到,每个特征值的均值差异很大?有的特征数值很大,有的特征数据很小,这种现象在机器学习中称为“量纲不统一”,KNN 是距离类模型,欧式距离的计算公式中存在着特征上的平方和:
如果某个特征 Xi 的取值非常大,其他特征的取值和它比起来就不算什么,那么距离的大小很大程度上都回由这个 Xi 来决定,其他的特征之间的距离可能无法对 d(A,B)的大小产生什么影响,这种现象会让 KNN 这样的距离类模型的效果大打折扣。
然而实际分析情况中,绝大多数数据集都会存在各特征量纲不同的情况,此时若要使用KNN 分类器,则需要先对数据集进行归一化处理,即是将所有的数据压缩都同一个范围内。
当数据(X)按照最小值中心化后,再按极差(最大值-最小值)缩放,数据移动了最小值个单位,并且会被收敛到【0,1】之间,而这个过程,就称做数据归一化(Normalization,又称 Min-Max Scaling)。
先分数据集 再做归一化
直接在全数据集 X 上进行归一化,然后放入交叉验证绘制学习曲线,这种做法是错误的。
真正正确的方式是,先分训练集和测试集,再归一化。
我们是使用数据中的最小值和极差在对数据进行压缩处理,如果我们在全数据集上进行归一化,那最小值和极差的选取是会参考测试集中的数据状况的。因此,当我们归一化后,无论我们如何分割数据,都会有一部分测试集的信息被“泄露”给训练集,这会使我们的模型效果被高估。
在现实业务中,我们只知道训练集的数据,不了解测试集究竟会长什么样子,所以我们要训练集上的最小值和极差归一化测试集。
data = [[-1,2],[-0.5,6],[0,10],[1,18]]
data=pd.DataFrame(data)
(data-np.min(data,axis=0))/(np.max(data,axis=0)-np.min(data,axis=0))
运行结果如下所示:
通过 sklearn 实现
from sklearn.preprocessing import MinMaxScaler as mms
Xtrain,Xtest,Ytrain,Ytest=train_test_split(X,y,test_size=0.2,random_state=420)
# 归一化
# 求训练集最大/小值
MMS_01=mms().fit(Xtrain)
# 求测试集最大/小值
MMS_02=mms().fit(Xtest)
# 转换
X_train=MMS_01.transform(Xtrain)
X_test =MMS_02.transform(Xtest)
score=[]
var=[]
for i in range(1,20):
clf=KNeighborsClassifier(n_neighbors=i)
# 交叉验证的每次得分
cvresult=CVS(clf,X_train,Ytrain,cv=5)
score.append(cvresult.mean())
var.append(cvresult.var())
plt.plot(krange,score,color="k")
plt.plot(krange,np.array(score)+np.array(var)*2,c="red",linestyle="--")
plt.plot(krange,np.array(score)-np.array(var)*2,c="red",linestyle="--")
plt.show()
运行之后,我们查看结果如下所示:
再次查看最大值,运行结果:
score.index(max(score))+1
输出结果是:
最终值是:7
无论 random_state取什么值,最优的 K 值都不会相差太多。
把经过交叉验证、归一化处理之后,我们得到最优 K 是 8,放在归一化后的训练集重新建模,然后在归一化后的测试集中查看结果分数:
clf=KNeighborsClassifier(n_neighbors=6,weights='distance').fit(X_train,Ytrain)
score=clf.score(X_test,Ytest)
score
执行结果如下图所示:
距离的惩罚
最近邻点距离远近修正在对未知分类过程中,一点一票的规则是 KNN 模型优化的一个重要步骤,也就是说,对于原始分类模型而言,在选取最近的 K 个元素之后,将参考这些点的所属类别,并对其进行简单计数,而在计数的过程中这些点一点一票,这些点每个点对分类目标点的分类过程中影响效力相同。
但这实际上不公平的,就算是最近邻的 K 个点,每个点的分类目标点的距离仍然有远近之别,而近的点往往和目标分类点有更大的可能性属于同一类别(该假设也是 KNN 分类模型的基本假设)。
关于惩罚因子的选取有很多种方法,最常用的就是根据每个近邻 x=距离的不同对其做加权,加权方法设置 Wi 权重,该权重计算公式为:
这里需要注意的是,关于模型的优化方法只是在理论上而言进行优化会提升模型判别效力,但实际应用过程中最终能否发挥作用,本质上缓释取决于优化方法和实际数据情况的契合程度,如果数据本身存在大量异常值点,则采用距离远近作为惩罚因子则会有较好的效果,反之则不然。
因此在实际我们进行模型优化的过程中,是否起到优化效果还是要以最终模型运行结果为准,在 sklearn 中,我们可以通过参数 weights 来控制是否适用距离作为惩罚因子。
for i in range(1,20):
clf=KNeighborsClassifier(n_neighbors=i,weights='distance')
# 交叉验证的每次得分
cvresult=CVS(clf,X_train,Ytrain,cv=5)
score.append(cvresult.mean())
var.append(cvresult.var())
plt.plot(krange,score,color="k")
plt.plot(krange,np.array(score)+np.array(var)*2,c="red",linestyle="--")
plt.plot(krange,np.array(score)-np.array(var)*2,c="red",linestyle="--")
plt.show()
执行结果如下所示:
生成的图片如下所示:
此时查看最大值:
score.index(max(score))+1
输出结果如下:
clf=KNeighborsClassifier(n_neighbors=6,weights='distance').fit(X_train,Ytrain)
score=clf.score(X_test,Ytest)
score
执行结果如下所示: