机器学习(周志华) 第一章 习题1.2参考答案

《机器学习》——周志华——清华大学出版社

习题1.2参考答案

以下内容仅供参考

对于《机器学习》课本习题1.2的答案,网上流传着一份答案,作者使用了栈的方式遍历 2 48 2^{48} 248种组合,但由于栈的特性,每次只能记录一个数值,这无法保证所有组合的去重,因此出现了假设空间大于了 2 18 2^{18} 218=262144的情况。因此,在我个人看来,该方法所得到的结果是明显错误的!该方法传送门:https://blog.csdn.net/icefire_tyh/article/details/52065626
本人针对这个问题写了一个python3的代码,在这里采用了对组合数进行穷举的方式,在穷举完毕后进行查重,得到的列表长度即是最终结果。首先先请您理解:析取范式有3*4*4=48种,而对应的的特征组合为2*3*3=18种。

  • 本文首先将西瓜的三个不同属性的特征分别记为(A1,A2),(B1,B2,B3),(C1,C2,C3)。这样西瓜的特征有18种不同的组合,我们用一个2*3*3的二元取值array来表示(后面也可以将其拉直成为一个长度为18的向量),每个位置的取值要么是0要么是1,这18个位置与18种特征组合一一对应。
    这样就构造出来了一个 2 18 2^{18} 218的特征空间(实际上这里没有考虑全0的空集情况,应该是 2 18 − 1 2^{18}-1 2181),只要两个特征对应的18维的向量不同,我们认为是两个不一样的特征;反之两个相同的向量即是相同的特征。
  • 接着,本文构造了一个从48种析取范式到18中特征组合的对应函数。48种析取范式的组合为(A1,A2,*),(B1,B2,B3,*),(C1,C2,C3,*),其中某个特征取*值即代表取该特征下所有可能的取值。这个对应函数的思路很清晰,您可以参考下方的代码进行理解。
  • 最后,就是对从48个析取范式中选出k个进行穷举了!我们对于某k个析取范式转化为的18维向量进行取并操作(对取并后得到的18维向量的某一维的取值,只要这k个向量中对应位置至少有一个值是1,那么该位置的取值就为1,也就是所谓的冗余操作),例如(1,1,0,0,…,0)与(1,0,1,0,0,…,0)取并后得到一个新的18维向量是(1,1,1,0,0,0,…,0)。
    我们把取并后得到的18维向量放到一个列表中,而这个18维向量仅仅是我们从48个析取范式中选出了某k个后取并所得到的一个结果!对于48个析取范式选出k个,我们一共可以得到 C 48 k C^{k}_{48} C48k个18维向量。穷举所有组合的可能情况后,我们得到一个 C 48 k C^{k}_{48} C48k大小的列表,其中每个元素都是一个18维的二值向量,对该列表进行去重操作后输出列表长度即为取k值的最终答案,而且这也保证了去重后的列表长度一定在 2 18 2^{18} 218以内!

下面我将给出我的代码(结果没有附在下面,希望您自己运行测试一下,就连最老的笔记本都可以运行)。如果您对上述文字描述有不理解的地方,建议您结合代码进行理解。我认为我针对该问题写的python的代码非常容易理解,而且可读性很强,供您学习参考。如果您有任何问题或者其他的想法欢迎留言共同讨论!

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

   import numpy as np
   import itertools as it
   
   def get_18_from_trible(trible):  #把三维向量变为代表特征的18维向量
       a = np.zeros([2,3,3])
       a1 = trible[0]
       a2 = trible[1]
       a3 = trible[2]
       if a1 == 3:  #对于第一种属性为"*"的情况
           a1 = [1,2]
       else:
           a1 = [a1]    
       if a2 == 4:  #对于第二种属性为"*"的情况
           a2 = [1,2,3]
       else:
           a2 = [a2]
        if a3 == 4:  #对于第三种属性为"*"的情况
            a3 = [1,2,3]
        else:
            a3 = [a3]
        #print (a1,a2,a3) 
        #print(a)
        for m1 in a1:
            for m2 in a2:
                for m3 in a3:
                    a[m1-1][m2-1][m3-1] = 1
        return a           #得到了一个18维向量(0/1二值),代表18种特征情况
    
    def turn_48_to_trible(num):   
    # num in [0,47],把一个小于48的数字对应到一个三维数组中
        for i in range(3): 
            for j in range(4):
                for k in range(4):
                    if i*16 + j*4 + k == num:
                        return [i+1,j+1,k+1]
    
    def from_48_to_18(num):  #把0-47的某个数唯一对应到某个18维向量
        a = turn_48_to_trible(num)
        b = get_18_from_trible(a)
        return b  
    
    def main(k):
        rset=[]
        for i in it.combinations(range(48),k):   
        #开始对48取k的组合数进行穷举,i是一个k元数组
            subset=[]
            for j in range(k): 
                p = from_48_to_18(i[j])  
                subset.append(p)
            subset = np.array(subset)    
            subset = subset.any(axis=0)  # 这是去除冗余操作!!!
            subset = np.reshape(subset,[18]) 
            subset = subset.tolist()  #从array变为list方便接下来的操作
            count = 0
            for i in range(18):
                count += 2 ** i *subset[i]  
                #这是简单的一一映射,18维二值向量可以一一对应到1~2^18的某个数
            subset = count  
            length = len(rset)
            rset.append(subset)    
            if len(rset) % 100000 == 0 :
                print(len(rset))  #为了证明程序确实在运行hhh,这个可以注释掉
            if len(rset) > 5000000: 
                rset = list(set(rset))  #set是集合,是对rset数组进行去重!
                #设置500W上限为了防止set操作时数组长度太长导致程序崩掉
        rset = list(set(rset)) #最终set操作一下得到最终结果
        #rset = list(set(tuple(t) for t in rset))
        
        print( "%d : %d examples"%(k,len(rset)))    
    
    # main(k) 运行即得k个析取范式的结果。注:当k≥9时花费时间过长!目前没法解决!
   

你可能感兴趣的:(机器学习(周志华) 第一章 习题1.2参考答案)