学习目标
掌握特征选择的基本原理及方法,实现特征的选择
1.特征选择原理
对于一个学习任务而言,有些特征很关键,但有些特征对于最后的分类可能并没有什么用,我们称前者为相关特征,后者为无关特征,特征选择是一个选择相关特征,去除无关特征的过程。
为什么要进行特征选择?
1.降维。 同PCA
2.减少任务的难度。 较少的干扰会降低任务的难度
无关特征不同于冗余特征,无关特征是指与学习任务无关的特征,而冗余特征是指可以从当前特征中推演出来的特征
去除冗余特征在很大程度上可以降低任务的难度,但是若冗余特征充当了中间变量,冗余特征的存在也是有益的
常见的特征选择方法包括:
1.过滤式选择 2.嵌入式选择 3.包裹式选择
1.1 过滤式选择
过滤式选择是先进行特征选择,后进行训练器学习;即,特征选择与训练器学习是具有先后顺序的
过滤式选择的方法采用Relief法,该方法设计了 “相关统计量” 来描述各个特征的重要程度
最终可以指定一个阈值,选择比阈值大的相关统计变量所对应的特征;或者直接选择前k大的相关统计变量对应的特征作为最终特征
确定相关统计变量的方法
1.在同类样本中找到距离最近的样本
x
n
h
x_nh
xnh作为”猜中近邻"
2.在不同类的样本中找到距离最近的样本
x
n
m
x_nm
xnm作为"猜错近邻“
根据公式:
− d i f f ( x i j , x n h j ) 2 + d i f f ( x i j , x n m j ) 2 -diff(x_i^j,x_{nh}^j)^2+diff(x_i^j,x_{nm}^j)^2 −diff(xij,xnhj)2+diff(xij,xnmj)2
可求得样本 x i x_i xi在特征 j j j上的相关统计变量
若特征 j j j为离散变量:
当
x
i
j
=
x
n
h
j
x_i^j=x_{nh}^j
xij=xnhj时,diff=0
当
x
i
j
≠
x
n
h
j
x_i^j \neq x_{nh}^j
xij=xnhj时,diff=1
若特征 j j j为连续变量:
d i f f = ∣ x i j − x n h j ∣ diff=|x_i^j-x_{nh}^j| diff=∣xij−xnhj∣
若当前问题为多分类问题,相关统计变量的公式可以转化为
− d i f f ( x i j , x n h j ) 2 + ∑ k ≠ p ( L k / L ) ∗ d i f f ( x i j , x n m j ) 2 -diff(x_i^j,x_{nh}^j)^2+\sum_{k\neq p} (L_k/L)*diff(x_i^j,x_{nm}^j)^2 −diff(xij,xnhj)2+∑k=p(Lk/L)∗diff(xij,xnmj)2
此时的 x n m j x_{nm}^j xnmj为在类别 k k k上的猜错近邻( k k k不为样本 x i x_i xi的标签 p p p)
实战
1.计算同类最近击中和非同类最近击中
#计算最近击中和最远击中
def caculate_distance(x,grap):
distance={}
for i in range(0,len(grap)):
xj=np.array(list(grap.iloc[i]))
x=np.array(x)#计算xj与x之间的距离
if any(xj!=x):#若计算的样本不是x本身
dis=np.sum(np.sqrt(np.square(xj-x)))#计算距离
distance[dis]=xj
distance=sorted(distance.items(),key=lambda x:x[0])
return distance[0][1]
def caculate_hit_and_miss(x):
miss_all_categoral=[]
hit=np.array([])
for col,grap in data.groupby(by='花瓣种类'):
if col==x[-1]: #计算同类最近击中距离
hit=caculate_distance(x,grap)
else:
mis=caculate_distance(x,grap)#计算非同类最近击中距离
miss_all_categoral.append((len(grap)/len(data),mis))
return hit,miss_all_categoral
2.计算相关统计量
def caculate_statistical(x,hit,miss):
statisitical=[]
# 形如[(0.3333333333333333, array([5.1, 2.5, 3. , 1.1, 2. ]))]
for i in range(0,len(hit)-1):#对于第i个特征
a=-np.square(abs(x[i]-hit[i]))#计算同类相关量
b=0
for j in range(0,len(miss)): #计算非同类相关量
b+=np.square(abs(miss[j][1][i]-x[i]))*miss[j][0]
statisitical.append(a+b)
return statisitical
3.分别汇总各特征 在所有样本上的的相关统计量之和
def filter_chioce():
all_statistical=[]
for i in range(0,len(data)):
hit,miss=caculate_hit(data.iloc[i])#计算对于样本x的最近击中和最远击中
all_statistical.append(caculate_statistical(data.iloc[i],hit,miss))
result=np.array(all_statistical).sum(axis=0)
return result
4.得到结果
result=filter_chioce()
result=DataFrame(result,column=['花萼长度', '花萼宽度', '花瓣长度', '花瓣宽度'])
可以看出,花瓣长度与花瓣宽度的相关统计变量较大,特征对分类过程的贡献较大
1.2 嵌入式选择
嵌入式选择是指将训练器学习的过程与特征选择的过程融为一体,最有代表性的嵌入式选择就是神经网络的误差传播过程
实战
for epoch in range(iter):
print(".......")
for step,(x,y) in enumerate(train_db):
#x:[b,28,28]
x=tf.reshape(x,[-1,784])
with tf.GradientTape() as tape:
out=net(x)
#out:[b,10]
#y:[b]
y=tf.one_hot(y,depth=10)
loss_mse=tf.reduce_mean(tf.losses.MSE(y,out))
loss_ce=tf.reduce_mean(tf.losses.categorical_crossentropy(y,out,from_logits=True))
grad=tape.gradient(loss_ce,net.trainable_variables)#求解梯度,进行优化器优化
optimizer.apply_gradients(zip(grad,net.trainable_variables))
if step % 20==0:
print("epoch={0},loss={1}".format(epoch,loss_ce))
test_accurancy=0
print("testing.......")
for step,(x_test,y_test) in enumerate(test_db):
x_test= tf.reshape(x_test, [-1, 784])
y_test=tf.cast(y_test,tf.int64)
test_out=net(x_test)
#test_out:[b,10]
#y_test:[b]
test_out=tf.nn.softmax(test_out,axis=1)
test_out=tf.argmax(test_out,axis=1)
# test_out:[b]
acc=tf.reduce_mean(tf.cast(tf.equal(test_out,y_test),dtype=tf.float32))
test_accurancy+=float(acc)
test_accurancy=test_accurancy/step
print("epoch={0},test_accurancy={1}".format(epoch, test_accurancy))
1.3 包裹式选择
包裹式选择是指根据训练器的学习结果来选择特征,包裹式特征选择可以为学习器选择 “量身定做” 的特征子集
包裹式选择的学习效果一般比过滤式选择要好,但是包裹式选择在选择特征的过程中需要多次训练学习器,计算开销比较大
实战
data1=np.genfromtxt('iris.data.txt',delimiter=',')
data=data1[:,:4]
labels=data1[:,-1]
m,n=data.shape
numstep=10 #确定步长
best_state={}
best_predict_labels=np.ones((m,1))
min_error=np.inf
data=np.mat(data)#获得数据
labels=np.mat(labels)
for i in range(0,n):#遍历所有的特征
max_num=max(data[:,i])
min_num=min(data[:,i])
stepsize=(max_num-min_num)/numstep #求解当前列所需要走过的步数
for j in range(-1,int(numstep+1)):
thresh=(min_num+j*stepsize)#获得阈值,注意阈值的求法
for flag in ['less','great']:
predict_labels=weakclassifier(data,i,flag,thresh)#训练弱分类器,找到最好的分类方法
error=np.mat(np.ones((m,1)))#定义误差矩阵
error[labels.T==predict_labels]=0#如果预测对了就是0
weighted_error=(D.T*error) #公式中是按照加权的方法构造的矩阵
# if weighted_error<min_error :#如果说当前的错误率是最小的话
min_error=weighted_error
best_state['dim']=i
best_state['thresh']=thresh
best_state['flag']=flag
best_predict_labels=predict_labels.copy()
print('best character:',best_state['dim'])