敞开成长之旅!这是我参加「日新方案 12 月更文应战」的第21天,点击检查活动概况
视频作者:菜菜TsaiTsai 链接:【技术干货】菜菜的机器学习sklearn【全85集】Python进阶_哔哩哔哩_bilibili
样本不均衡是指在一组数据集中,标签的一类天生占有很大的份额,但咱们有着捕捉出某种特定的分类的需求的情况。比方,咱们现在要对潜在犯罪者和普通人进行分类,潜在犯罪者占总人口的份额是相当低的,也许只要2%左右,98%的人都是普通人,而咱们的方针是要捕获出潜在犯罪者。这样的标签散布会带来许多问题。
- 首先,分类模型天生会倾向于大都的类,让大都类更简单被判别正确,少量类被牺牲掉。由于关于模型而言,样本量越大的标签能够学习的信息越多,算法就会愈加依赖于从大都类中学到的信息来进行判别。假如咱们希望捕获少量类,模型就会失败。
- 其次,模型评价目标会失掉意义。这种分类情况下,即使模型什么也不做,全把一切人都当成不会犯罪的人,准确率也能十分高,这使得模型评价目标accuracy变得毫无意义,底子无法达到咱们的“要识别出会犯罪的人”的建模目的。
所以现在,咱们首先要让算法意识到数据的标签是不均衡的,经过施加一些惩罚或者改变样本自身,来让模型向着捕获少量类的方向建模。然后,咱们要改进咱们的模型评价目标,运用愈加针关于少量类的目标来优化模型。 关于支撑向量机这个样本总是对核算速度影响巨大的算法来说,咱们不想轻易地添加样本数量。况且,支撑向量机中地决议计划仅仅遭到决议计划鸿沟的影响,而决议计划鸿沟又仅仅遭到参数C和支撑向量的影响,单纯地添加样本数量不只会添加核算时间,可能还会添加无数对决议计划鸿沟无影响的样本点。因而在支撑向量机中,咱们要大力依赖咱们调理样本均衡的参数:SVC类中的class_weight和接口fit中能够设定的sample_weight。 在SVM中,咱们的分类判别是依据决议计划鸿沟的,而终究决定终究运用怎样的支撑向量和决议计划鸿沟的参数是参数C,所以一切的样本均衡都是经过参数C来调整的。
SVC的参数:class_weight
可输入字典或者”balanced”,可不填,默许None
对SVC,将类i的参数C设置为class_weight [i] * C
。假如没有给出具体的class_weight
,则一切类都被假设为占有相同的权重1,模型会依据数据本来的情况去训练。假如希望改进样本不均衡情况,请输入形如{"标签的值1":权重1,"标签的值2":权重2}
的字典,则参数C将会主动被设为:标签的值1的C:权重1 * C,标签的值2的C:权重2*C
或者,能够运用“balanced”形式,这个形式运用y的值主动调整与输入数据中的类频率成反比的权重为n_samples/(n_classes * np.bincount(y))
说一下np.bincount,计数非负整数数组中每个值的出现次数。
import numpy as np a = [1,2,3,5,1,3,1,1,9] b = np.bincount(a) b --- array([0, 4, 1, 2, 0, 1, 0, 0, 0, 1], dtype=int64)
关于它的返回值,一维array的索引和索引对应的元素值组成一对,例如上例,b[0]=0,即在a中数值0出现的次数为0次;b[1]=4,即在a中数值1出现的次数为4次 注意只能接收非负整数,其余值报错 因而关于咱们“balanced”形式中
n_samples/(n_classes * np.bincount(y))
,其实也是一个相似字典的目标,key便是索引,value便是元素值
SVC的接口fit的参数:sample_weight
数组,结构为 (n_samples, ),有必要对应输入fit中的特征矩阵的每个样本
每个样本在fit时的权重,让权重 * 每个样本对应的C值
来迫使分类器强调设定的权重更大的样本。一般,较大的权重加在少量类的样本上,以迫使模型向着少量类的方向建模
一般来说,这两个参数咱们只选取一个来设置。假如咱们一起设置了两个参数,则C会一起遭到两个参数的影响,即class_weight中设定的权重 * sample_weight中设定的权重 * C
利用数据集,观察运用class_weight前后的效果
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
class_1 = 500
class_2 = 50
# 标签为0的有500个,标签为1的有50个
centers = [[0.0,0.0],[2.0,2.0]]# 设定两个类别的中心
clusters_std = [1.5,0.5] # 设定两个类别的方差,一般来说,样本量比较大的类别会愈加松散
X,y = make_blobs(n_samples=[class_1,class_2]
,centers=centers
,cluster_std=clusters_std
,random_state=0
,shuffle=False
)
# make_blobs默许n_features=2
plt.scatter(X[:,0],X[:,1],c=y,cmap="rainbow",s=10)
分别建模并比较
clf = SVC(kernel="linear",C=1)
clf.fit(X,y)
wclf = SVC(kernel="linear",class_weight={1:10})
# 将标签为1的权重设置为10,标签为0的默许为1
wclf.fit(X,y)
clf.score(X,y)
---
0.9418181818181818
wclf.score(X,y) # 做样本均衡之后,准确率下降
---
0.9127272727272727
plt.figure(figsize=(6,5))
plt.scatter(X[:,0],X[:,1],c=y,cmap="rainbow",s=10)
ax = plt.gca()
X_min,X_max = X[:,0].min(),X[:,0].max()
y_min,y_max = X[:,1].min(),X[:,1].max()
XX,YY = np.mgrid[X_min:X_max:30j,y_min:y_max:30j]
Z_clf = clf.decision_function(np.c_[XX.ravel(),YY.ravel()]).reshape(XX.shape)
Z_wclf = wclf.decision_function(np.c_[XX.ravel(),YY.ravel()]).reshape(XX.shape)
a = ax.contour(XX,YY,Z_clf,levels=[0],alpha=0.5,linestyles=['-'],colors='black')
b = ax.contour(XX,YY,Z_wclf,levels=[0],alpha=0.5,linestyles=['-'],colors='red')
plt.legend([a.collections[0],b.collections[0]]
,["non weighted","weighted"]
,loc='upper right'
)
# legend([线],[名称],loc位置)
plt.show()
从图像上能够看出
- 灰色是咱们做样本平衡之前的决议计划鸿沟。灰色线上方的点被分为一类,下方的点被分为另一类。能够看到,大约有一半少量类(赤色)被分错,大都类(紫色点)简直都被分类正确了。
- 赤色是咱们做样本平衡之后的决议计划鸿沟,同样是赤色线上方一类,赤色线下方一类。做了样本平衡后,少量类简直悉数都被分类正确了,但是大都类有许多被分错了。
再依据之前score的值,咱们能够得出结论,不做样本平衡的时分准确率反而更高,做了样本平衡准确率反而变低了。这是由于做了样本平衡后,为了要更有效地捕捉出少量类,模型误伤了许多大都类样本,而大都类被分错的样本数量 > 少量类被分类正确的样本数量
,使得模型整体的精确性下降。现在,假如咱们的目的是模型整体的准确率,那咱们就要回绝样本平衡,运用class_weight被设置之前的模型。
a.collections # 返回一个惰性目标,是一个列表,列表里边有一条线,由于上面levels=[0]代表只制作一条线,这条线代表高度值为0
---
<a list of 1 mcoll.LineCollection objects>
[*a.collections] # 里边便是等高线例一切的线的列表
---
[<matplotlib.collections.LineCollection at 0x1d71110b5c0>]
# 现在咱们只要一条线,所以咱们能够运用索引0来确定这个目标
a.collections[0]
然而在现实中,咱们往往都在寻求捕捉少量类,由于在许多情况下,将少量类判别错的代价是巨大的。比方咱们之前说到的,判别潜在犯罪者和普通人的比如,假如咱们没有能够识别出潜在犯罪者,那么这些人就可能去损害社会,形成恶劣影响,但假如咱们把普通人错以为是潜在犯罪者,咱们也许只是需要添加一些监控和人为甄别的本钱。所以对咱们来说,咱们宁愿把普通人判错,也不想放过任何一个潜在犯罪者。