第一章 绪论

试答系列:“西瓜书”-周志华《机器学习》习题试答

1.1 表1.1中若只包含编号1和4两个样例,试给出相应的版本空间。

答: P5页中对版本空间的定义是这样说:可能有多个假设与训练集一致,即存在着一个与训练集一致的“假设集合”,我们称之为“版本空间”(version space)。
表一只包含 1,4样例,则为:

编号 色泽 根蒂 敲声 好瓜
1 青绿 蜷缩 浊响
4 乌黑 稍蜷 沉闷

类似于图1.2的做法,结果应该为:

版本空间

1.2 若使用最多包含k个合取式的析合范式来表达1.1西瓜分类问题的假设空间,试估算共有多少种可能的假设。

答: 如果没有析合范式,由单个合取式所构成的假设空间共包含3×4×4+1=49种可能的假设(见教材P5,这里根据表1.1,色泽属性的取值只考虑“青绿”,“乌黑”两种情况)。如果用这49个合取式彼此做并集来构成析合范式,将会产生更多的假设。因此,通过析合范式的方式可以扩大假设空间。

数据可视化

首先,为了方便讨论,可以先将数据进行可视化,随后再来分析问题。表1.1中的西瓜分类问题只有三个属性,每个属性有2~3种取值,可以将其三维可视化:

数据可视化

那么,合取式可以根据其中通配符数目可以分为四种类型,在图中表现出来便是:点,线,面,体。“单个合取式”等价于图中的“点,线,面,体”:

四种合取式

单个样本对应于图中的一个点,共有18个点,为了下文的讨论方便,将每个点进行编号:

输入图片说明

这样,对于上面的单个合取式可以这样称呼它们:

(色泽=乌黑,根蒂=稍蜷,敲声=浊响)----点{6}

(色泽=乌黑,根蒂=稍蜷,敲声=*)---------线{6,12,18}

(色泽=*,根蒂=稍蜷,敲声=*)--------------面{5,6,11,12,17,18}

问题分析

现在,开始来考虑题目中的问题,用最多包含k个合取式的析合范式来表达的假设空间,共有多少种可能的假设?

  • k=1 时,假设空间即由所有的点线面体所组成,其中点-18个,线-21条,面-8面,体-1个,再加上空集,总共的假设空间数为 18+21+8+1+1=49.

  • k=2 时,考虑通过两个合取式析合的方式可以新增出哪些假设,可能有以下情况:


    点+点:C218-9=144
        (扣去包含了9条竖单线的情况,比如点{1}v点{2}与单线{1,2}等同)
    线+点:9×(18-2)+12×(18-3)=324
    面+点:2×9+6×12=90
    线+线:C221-6-12×3=168
        (扣去与竖向面,线-点重复的情况,比如,线{5,11,17}+线{6,12,18}与面{5,6,11,12,17,18}等同,线{5,11,17}+线{5,6}与线{5,11,17}+点{6}等同)
    面+线:2×6+6×(21-5)=108
        (未考虑与面-点重复的情况)
    面+面:C26=15
        (未考虑与体,面-线重复的情况)

    合计,新增假设144+324+90+168+108+15=849个。因此,最大包含2个合取式所构成的假设空间包含49+849=898种可能的假设。


  • 对于k=3及以上的情况可以做类似分析,但是从上面k=2的情况已经看出,很麻烦,要考虑多种组合情况,还有剔除重复的情况。因此,接下来考虑编程实现。

编程计算

编程实现需要换一个思路来考虑问题,合取式就是一个假设,析合式也是假设,假设就是一个函数,这里的函数可以用正例样本点的集合来表示。从集合观点来看,可以把单个合取式看成一个集合(判定为正例的样本集合),单个合取式对应于集合:


  • 基础集合

    空集:{}

    :{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18}

    线:{1,2},{3,4},{5,6},{7,8},{9,10},{11,12},{13,14},{15,16},{17,18},
    {1,3,5},{2,4,6},{7,9,11},{8,10,12},{13,15,17},{14,16,18},
    {1,7,13},{3,9,15},{5,11,17},{2,8,14},{4,10,16},{6,12,18},

    :{1,3,5,7,9,11,13,15,17},{2,4,6,8,10,12,14,16,18},
    {1,2,3,4,5,6},{7,8,9,10,11,12},{13,14,15,16,17,18},
    {1,2,7,8,13,14},{3,4,9,10,15,16},{5,6,11,12,17,18}

    :{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18}


我们将这些集合称之为“基础集合”,将“由多个合取式构成析合范式”的过程称之为“由基础集合产生出新的集合”的过程。

问题“最多包含k个合取式的析合范式来表达的假设空间共有多少种假设?”可以重述为“最多由k个基础集合做并集,可以产生出多少种不同的集合?”

首先,有几个基本事实,我们先捋一捋:

  • 基本事实

    (1) 假设数目,或者说集合可能数目,最多不超过 218=262144个。可以这样看,集合中对于1~18各个点,要么选入,要不选入,因此有218种可能集合。
    (2) 18个单点集合{1},{2}…{18}可以合成所有可能的集合,因此,所有集合最多需要个18基础集合来合成。
    (3) 全集{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18}不能产生新的集合,因为它和其他任何集合的并集还是全集。所以,基础集合可以不考虑全集。
    (4) 空集也没有产生新集合的能力,基础集合也可以不考虑空集。

最多由k个基础集合做并集,可以产生出多少种不同的集合?

一种做法是,考察所有由1~k个基础集合来做并集,剔除其中重复考虑的集合,统计最终产生了多少种不同的集合。这样很麻烦,我们换一种四路。

换一种做法,考察所有可能集合(或者说“假设”),共218个,彼此互不等同,考察每一个集合最少需要由几个基础集合来生成。这样的好处在于,不必像前一种方法那样,需要考虑重复计数的情况。那么,“最多由k个基础集合做并集,可以产生出多少种不同的集合?”等同于“最少需要1~k个基础集合来产生的集合,共有多少个?”

代码实现

基于Python语言,很方便的是,Python语言中本身就有集合数据类型,不知道其他语言里有没有。
详细编程代码附后
计算结果如下:

  • 程序运行结果
    最少需要1个合取式来合成的假设数目:49
    最少需要2个合取式来合成的假设数目:849
    最少需要3个合取式来合成的假设数目:7452
    最少需要4个合取式来合成的假设数目:32907
    最少需要5个合取式来合成的假设数目:72351
    最少需要6个合取式来合成的假设数目:84843
    最少需要7个合取式来合成的假设数目:49296
    最少需要8个合取式来合成的假设数目:12933
    最少需要9个合取式来合成的假设数目:1464
    最少需要10-18个合取式来合成的假设数目:0

于是,现在可以回答题目给出的问题了:

  • 回答问题
    当k=1时,假设空间包含假设数目为 49
    当k=2时,假设空间包含假设数目为 49+849=898
    当k=3时,假设空间包含假设数目为 898+7452=8350
    当k=4时,假设空间包含假设数目为 8350+32907=41257
    当k=5时,假设空间包含假设数目为 41257+72351=113608
    当k=6时,假设空间包含假设数目为 113608+84843=198451
    当k=7时,假设空间包含假设数目为 198451+49296=247747
    当k=8时,假设空间包含假设数目为 247747+12933=260680
    当k>=9时,假设空间包含假设数目为 260680+1464=262144 (218,亦即所有可能的假设数)

1.3 若数据包含噪声,假设空间中有可能不存在与所有训练样本都一致的假设。试设计一种归纳偏好用于假设选择。

:比如,归纳偏好于“训练误差低”以及“尽可能一般”的假设。

1.4 将式(1.1) 中性能度量的指示函数II(·)换成其他性能度量l,试证明NFL定理仍成立。

:设性能度量函数l为:

输入图片说明

于是(1.2)式可变为:

输入图片说明

最终表达式为:

输入图片说明

l10+l101-l00-l00=0时, 上式与算法ζa无关,免费午餐定理成立。

1.5 试述机器学习能在互联网搜索的哪些环节起作用。

:比如,在浏览网页时,根据浏览记录跳出的推荐商品广告。搜索某件商品后,推荐相关商品,还提示说“80%的用户也买了这个”,“根据你浏览的某商品推荐了这个”。

附:编程代码

习题1.2(Python)

#==================================
#            定义一些函数
#==================================
# 函数 inside(set1,set2)判断两个集合是否存在包含关系
# set1,set2为两个集合
def inside(set1,set2): 
    return (set1|set2==set1)|(set1|set2==set2)

# 函数 combine(ListSets)返回列表中所有集合的并集
# ListSets为列表类型,列表元素为集合类型
def combine(ListSets):
    cset=ListSets[0]
    for subset in ListSets:
        cset=cset|subset
    return cset

# 函数DeleteSubset(ListSets)对一组集合进行精简,使集合数目最小化
# 首先删去其中被其余集合所包含的集合,然后删去多余的集合(删去不影响并集结果)
# 输入ListSets为列表类型,列表元素为集合类型
# 返回精简后的集合列表
def DeleteSubset(ListSets):
    #删除被包含的集合
    over=False
    while not over:
        over=True
        N=len(ListSets) #当前集合列表的集合数目
        for i in range(N-1,0,-1):
            set1=ListSets[i]
            for set2 in ListSets[:i]:
                if inside(set1,set2):
                    over=False
                    ListSets.pop(i)
                    break
    #删除多余的集合
    over=False
    while not over:
        over=True
        N=len(ListSets) #当前集合列表的集合数目
        for i in range(N-1,0,-1):
            if combine(ListSets)==combine(ListSets[:i]+ListSets[i+1:]):
                over=False
                ListSets.pop(i)
    return ListSets

# 函数Findset(base,set)
# 用于在基础集合列表base中找出合并出目标集合set的最佳方式(基础集合数最少)
def Findset(base,set):
    # 首先找出在base中所有set的子集
    sub_base=[k for k in base[::-1] if (k|set)==set]   
    # 然后删除这些子集中存在包含关系的子集
    return DeleteSubset(sub_base)

# 函数GenerateSet(N1,N2,k)  递归
# 用递归方法从N1-N2的自然数中挑选k个元素所组成的所有可能集合
# 返回集合列表,包含个CkN2-N1+1个集合,每个集合包含k个元素
def GenerateSet(N1,N2,k):
    if k==1:
        return [{i} for i in range(N1,N2+1)]
    else:
        sets=[]
        for i in range(N1,N2+2-k):
            for subset in GenerateSet(i+1,N2,k-1):
                sets+=[{i}|subset]
        return sets

# 函数GenerateSet1(code)  由编码产生集合
# code为整数,比如,5,对应的二进制码为000...00101
# 那么由此产生集合{1,3}
# 返回一个集合
def GenerateSet1(code):
    TransCode=list('{:018b}'.format(code))
    oneset=set()
    for k in range(-1,-len(TransCode)-1,-1):
        if TransCode[k]=='1':
            oneset|={-k}
    return oneset

#==================================
#            主程序
#==================================
# 基础集合
base=[{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},
      {1,2},{3,4},{5,6},{7,8},{9,10},{11,12},{13,14},{15,16},{17,18},
      {1,3,5},{2,4,6},{7,9,11},{8,10,12},{13,15,17},{14,16,18},
      {1,7,13},{3,9,15},{5,11,17},{2,8,14},{4,10,16},{6,12,18},
      {1,3,5,7,9,11,13,15,17},{2,4,6,8,10,12,14,16,18},
      {1,2,3,4,5,6},{7,8,9,10,11,12},{13,14,15,16,17,18},
      {1,2,7,8,13,14},{3,4,9,10,15,16},{5,6,11,12,17,18}]
# 计数器,初始为零
counter=18*[0]
# 两种方式择其一来遍历所有集合:递归和编码
'''
# 递归方式产生集合
for k in range(1,18):  #考虑从1-18中挑选1~17个元素组成集合的情况
    Sets=GenerateSet(1,18,k)
    for EachSet in Sets:
        num=len(Findset(base,EachSet))  #每个集合最少需要多少个基础集合来合成
        counter[num-1]+=+1
'''
# 编码方式遍历所有集合
for code in range(1,2**18-1):
    EachSet=GenerateSet1(code)
    num=len(Findset(base,EachSet))
    counter[num-1]+=+1

counter[0]+=2          #单独考虑空集和全集的情况
for k in range(18):
    print('最少需要%d个合取式来合成的假设数目:%d'%(k+1,counter[k]))
    

你可能感兴趣的:(第一章 绪论)