山东大学《机器学习》实验报告
---- 上机练习4.3节2题,4.4节3题
软件16-6 李昊 201600301309
问题背景:
题目给出了三个类别的三维数据各十个如下:
-
- 使用Parzen窗估计方法,h=1使用表中数据训练,然后对样本点分类,样本点为[0.5,1.0,0.0]T,[0.31,1.51,-0.50]T,[-0.3,0.44,-0.1]T
2)令h=0.1,重复第一问
-
1)对于一维的情况,当有n个数据样本点时,进行k-近邻概率密度估计。对表格中的类别w3中的特征x1,用程序画出当k=1、3、5时概率密度估计结果。
2)对于二维的情况,同第一问,数据变为类别w2中的特征(x1,x2)T。
3)对表格中的三个类别的三维特征,使用k-近邻概率密度估计方法。并且对下列点处的概率密度进行估计:[-0.41,0.82,0.88]T,[0.14,0.72,4.1]T,[-0.81,0.61,-0.38]T。
问题分析:
对于4.3节中的问题。
题目给出了窗函数,由于是正比关系,比例系数不影响分类,所以我们假设
φ
(
(
x
−
x
i
)
/
h
)
=
e
x
p
[
−
(
x
−
x
i
)
t
(
x
−
x
i
)
/
(
2
h
2
)
]
\varphi ((x-x_i)/h)=exp[-(x-x_i)^t(x-x_i)/(2h^2)]
φ((x−xi)/h)=exp[−(x−xi)t(x−xi)/(2h2)]
所以根据书中给出的公式
k
n
=
∑
1
n
φ
(
(
x
−
x
i
)
/
h
)
k_n=\sum_1^n \varphi((x-x_i)/h)
kn=1∑nφ((x−xi)/h)
p n ( x ) = 1 / n ∑ 1 n ( 1 / V n ) φ ( ( x − x i ) / h ) = 1 / n ∑ 1 n e x p [ − ( x − x i ) t ( x − x i ) / ( 2 h 2 ) ] / h 3 p_n(x)=1/n\sum_1^n(1/V_n) \varphi((x-x_i)/h)=1/n\sum_1^nexp[-(x-x_i)^t(x-x_i)/(2h^2)]/h^3 pn(x)=1/n1∑n(1/Vn)φ((x−xi)/h)=1/n1∑nexp[−(x−xi)t(x−xi)/(2h2)]/h3
可以看出,这个过程是对一系列关于x和xi的函数作平均。
我们得出这个表达式以后,剩下的就只是编程体现了。
对于4.4节中的问题。
第一问中,需生成一系列样本点,然后根据不同的k来计算它的V,这样就可以获得一系列的概率,进而画出结果。在求V时,重点是求出r,而r应该是训练数据中的,距离样本点最近的k个点中,最远的那个和样本点之间的距离。在这之后,我们就可以有这个公式:
p
n
(
x
)
=
(
k
n
/
n
)
/
V
n
p_n(x)=(k_n/n)/V_n
pn(x)=(kn/n)/Vn
第二问与第一问类似。
第三问的一个不确定点就是k的选择,k过小,可能不准确;k过大,又可能过拟合。所以,我们选择了k=3。有了k,再用与之前类似的方法求出V,再比较对应三个类别的概率大小即可解决。
问题解决:
读入数据还是用的前几次试验的方式。
首先解决4.3节中的问题。
我们对于这三个样本点,分别计算出其对于三个类别的p10(x),然后比较其大小即可。
下面是我计算parzen窗估计的代码:
def parzen(point,h):#Parzen
temp=0
for i in range(10):
temp+=math.exp(-(point-w1[:,i-1]).T*(point-w1[:,i-1])/(2*(h**2)))/h**3
out1=0.1*temp
temp=0
for i in range(10):
temp+=math.exp(-(point-w2[:,i-1]).T*(point-w2[:,i-1])/(2*(h**2)))/h**3
out2=0.1*temp
temp=0
for i in range(10):
temp+=math.exp(-(point-w3[:,i-1]).T*(point-w3[:,i-1])/(2*(h**2)))/h**3
out3=0.1*temp
print('the p10(x) of ','\n',point,'for w123 with h=',h,' is ','\n',out1,out2,out3)
tst=[out1,out2,out3]
i=1
for t in tst:
if t==max(tst):
print("This point due to kind",i)
i+=1
print("")
由于这个代码传入不同的点与h可以相应的计算,而一二两问只是h不同,所以我把两问一次性算出来了。
parzen(point1,1)
parzen(point2,1)
parzen(point3,1)
parzen(point1,0.1)
parzen(point2,0.1)
parzen(point3,0.1)
下面是运行结果。
经过验证,结果与分类都正确。
接下来解决4.4节的问题。
1)首先我们创建0-3之间的100个随机的数据样本。
list1=[]
for i in range(100):
list1.append(random.uniform(0,3))
然后开始求r,r是第k远的点,所以我们首先要求出样本点与数据集的距离,然后排序,取第k个元素,得到r。
得到r以后,进而求出pnx,重复求出所有的点,然后用plt输出。代码如下:
首先初始化样本集。
list1=[]
for i in range(100):
list1.append(random.uniform(0,3))
接下来是主程序。
def k_neighbor(list1,k):
outlist=[]
for i in range(100):
temp=list1[i]
j=w3[0]
#print(j)
h=[]
r_list=[]
for i in range(10):
h.append(float(j[:,i]))
for i in h:
r_list.append(abs(i-temp))
r_list.sort()
r=r_list[k-1]
outlist.append((k/10)/(2*r))
plt.title(k)
plt.xlabel('x')
plt.ylabel('p(x)')
plt.plot(list1,outlist,'ro')
plt.show()
w1,w2,w3=getmat()
k_neighbor(list1,1)
k_neighbor(list1,3)
k_neighbor(list1,5)
输出结果如下:
2)与第一问类似。这一问的难点我觉得反而是plt的三维表示,对于没用过plt的人来说是一个挑战,我弄的乱七八糟,好歹功能实现了。
代码如下:
首先初始化样本集:经过观察,样本处于-3到3之间
list1=[]
for i in range(100):
list1.append(random.uniform(-3,3))
list2=[]
for i in range(100):
list2.append(random.uniform(-3,3))
然后是主程序:
def k_neighbor2(list1,list2,k):
outlist=[]
#print(w2[0:2])
for i in range(100):
temp=np.mat([list1[i],list2[i]]).T
j=w2[0:2]
r_list=[]
for i in range(10):
h=j[:,i]
t=temp-h
r_list.append((float(t[0])**(2)+float(t[1])**(2))**(0.5))
r_list.sort()
r=r_list[k-1]
outlist.append((k/10)/(math.pi*r*r))
fig=plt.subplot(111,projection='3d')
#fig.xlim(-3,3)
#fig.ylim(-3,3)
fig.plot(list1,list2,outlist,'ro')
plt.show()
输出结果如下:
k=1
k=3
k=5
3)第三问就是推1及2的过程了,我们选择k=3,因为k=1不明显,而k=5可以在上图中看到,有些过拟合,因为“山峰”过多。
过程与第二问比较类似,代码如下:
数据初始化:
point4=(mat([-0.41,0.82,0.88])).T
point5=(mat([0.14,0.72,4.1])).T
point6=(mat([-0.81,0.61,-0.38])).T
主程序:
def k_neighbor3(point):
r_list1=[]
r_list2=[]
r_list3=[]
for i in range(10):
t1=w1[:,i]-point
t2=w2[:,i]-point
t3=w3[:,i]-point
r_list1.append((float(t1[0])**2+float(t1[1])**2+float(t1[2])**2)**(0.5))
r_list2.append((float(t2[0])**2+float(t2[1])**2+float(t2[2])**2)**(0.5))
r_list3.append((float(t3[0])**2+float(t3[1])**2+float(t3[2])**2)**(0.5))
r_list1.sort()
r_list2.sort()
r_list3.sort()
r1=r_list1[2]
r2=r_list2[2]
r3=r_list3[2]
p1=(9/40)/(math.pi*(r1**3))
p2=(9/40)/(math.pi*(r2**3))
p3=(9/40)/(math.pi*(r3**3))
print('the pnx of point','\n',point,'\n','for w123 with k=3 is','\n',p1,p2,p3)
tst=[p1,p2,p3]
i=1
for t in tst:
if t==max(tst):
print("This point due to kind",i)
i+=1
print("")
输出结果如下:
最后总代码中整合输出了所有题目的答案,不再赘述。
感想:
Parzen窗方法相对简单,理解过程、根据公式就可以解答。
对于K-近邻方法,通过试验过程我们可以发现,对于不同的k,结果也不相同,根据对于k的意义的思考,我觉得,当k过小时,不能很好的有一个分类的作用,因为只要满足很简单的条件就可以符合;而当k过大时,可以想象成有很复杂的条件来让这个点符合我们的标准,就会造成符合的点与我们的数据点过于相似,这显然也是不合适的。我们可以类比一下,假如我们是通过头发长短来区分男女,在我们的数据中,发短的是男的,发长的是女的,数据中男的发长3cm,但是一个发长2.5cm的人来了,却有很大可能被分类到女的中,这就是因为k的取值过大,导致模型认为只有3cm的才是男的。
除此之外,我还有一个深刻的感受,就是从0到1的过程永远是最难的,而从1到2简单很多。在试验之前,我从没接触过plt和对随机样本的估计。这就导致在对问题的理解上出现了很多偏差。好在有同学,有网络。慢慢的理解以后,才发现那些困难也不过如此。