TI男选隐形眼镜之机器学习

来源:互联网 发布:语音识别算法有哪些 编辑:程序博客网 时间:2024/04/28 13:03

  • 背景
  • 选择
  • 开发环境
  • 过程
    • 信息增量
      • 香农熵
      • 根据特征量划分数据集
      • 选择最好的划分方式
    • 决策树构建
      • 分类筛选
      • 决策树实现
      • 存储决策树
    • 分类器构建
    • 交互方法
    • 附简单数据集
  • 总结

背景

一天,程序员小东要去约会,但是他觉得戴着眼镜贼不好看。他感觉梳个油头,拿下眼镜去约会会加分很多,但他一只近视四眼狗没有眼镜,连女孩长什么样都看不清,很凄凉。于是,他决定要去配一副隐形眼镜,但配隐形要做一下检测什么的吧,所以在去配眼镜之前他决定自己搞点事情,去测一下自己配什么类型的隐形眼镜比较好。

选择

既然确定目标,就要有所选择,用什么方法去实现比较好呢?小东最近在学习机器学习,他决定用机器学习去进行开发。他先在网上找相关的隐形眼镜的数据集,找到了一个比较合适的数据集 Dataset of contact lenses

  分别有四个特征量:age, prescript, astigmatic, tearRate  每一个特征量的值如下:  age: young/pre/presbyopic  prescript: myope/hyper  astigmatic: yes/no  tearRate: reduced/normal  四个特征量的取值不同对应了三种类别:no lenses, soft, hard

看到这些特征量都是标称型数值,他决定采用刚学的ID3算法构建决策树,进行预测。

开发环境

小东电脑的系统开发环境是anaconda3, 可以在百度上直接搜索下载,里面包含了很多机器学习所用到的环境和工具,包括python,在这里说明一下,我用的是python3.6,如果有用python2的小伙伴,可能后面的代码需要自己改动一下。所以,只要安装anaconda3,你就可以拥有开发机器学习所需要的准备的东西,简单方便。

过程

使用ID3算法构建决策树,主要是采用递归的方法,将数据集划分为子数据集,直到子数据集都为同一类别,返回该类别 或者 特征量都使用完了,从子数据集中选取同一类别最多返回。
如何进行划分是关键。

信息增量

划分数据集的大原则是:将无序的数据变得更加有序
在划分数据集之前之后信息发生的变化称为信息增量,知道如何计算信息增量,我们就可以计算每个特征值划分数据集获得的信息增量,获得信息增量最高的特征就是最好的选择

香农熵

集合信息的度量方式称为香农熵,简称为熵香农熵,这个概念是克劳德·香农提出来的,据说这个哥们是20世纪最聪明的人之一,大家可以自行了解一下。这里直接列出计算香侬熵的公式:H(x) = -∑p(xi)log(2,p(xi)) (i=1,2,..n)其中p(xi)是选择该分类的概率计算数据集香农熵的代码如下:
from math import logimport numpyimport operator'''函数功能:计算香侬熵'''def calcShannonEnt(dataSet):    numEntries = len(dataSet) #计算数据的个数    labelCounts = {} #初始化字典    for featVec in dataSet:        currentLabel = featVec[-1] #获取每一条数据的分类        if currentLabel not in labelCounts.keys():            labelCounts[currentLabel] = 0        labelCounts[currentLabel] += 1 #这三句,统计每一个分类出现的次数    shannonEnt = 0.0 #香侬熵    for key in labelCounts:        prob = float(labelCounts[key]) / numEntries #计算每一种分类出现的概率        shannonEnt -= prob * log(prob, 2) # 计算熵的公式    return shannonEnt

根据特征量划分数据集

在计算最好划分数据集的方法之前,要先写一个根据某一特征量和特征值进行数据集划分的方法,直接附上代码
'''函数功能:划分数据集'''def splitDataSet(dataSet, axis, value):    retDataSet = [] #初始化列表    for featVec in dataSet:        if featVec[axis] == value: #比较第axis中的特征量与value相同与否            reducedFeatVec = featVec[:axis] #复制featVec[0:axis]            reducedFeatVec.extend(featVec[axis + 1:]) #将剩余的的特征也复制,用extend()方法            retDataSet.append(reducedFeatVec) #将新列表添加进retDataSet中    return retDataSet

选择最好的划分方式

主要根据每一个特征量进行划分后计算得出的香农熵与基本香农熵相见进行比较,看谁得大,就说明信息增量大,划分最合适代码如下
'''函数功能:选择最好的划分方式'''def chooseBestFeatureToSplit(dataSet):    numFeatures = len(dataSet[0]) - 1 #计算特征量的个数    baseEntropy = calcShannonEnt(dataSet) #计算整个数据集的基本香侬熵    bestInfoGain = 0.0    bestFeature = -1    for i in range(numFeatures):        featList = [example[i] for example in dataSet]#用列表推导式计算整个数据集第i个特征量        uniqueVals = set(featList)#用set生成集合数据,剔除相同值        newEntropy = 0.0        for value in uniqueVals:            subDataSet = splitDataSet(dataSet, i, value) #根据每一个不同的特征值进行数据集的分割            prob = len(subDataSet) / float(len(dataSet)) #计算每个子数据集的概率            newEntropy += prob * calcShannonEnt(subDataSet) #累加每一个字数据集的熵        infoGain = baseEntropy - newEntropy #信息增益是熵的减少或者是数据无序度的减少,减少的越多说明分类越合适        if(infoGain > bestInfoGain):            bestInfoGain = infoGain            bestFeature = i    return bestFeature

决策树构建

分类筛选

在构建决策树之前,需要写一个筛选方法。当数据集处理完所有属性,类标签还不是唯一的时候,采用多数投票的方法决定该叶子节点的分类
'''函数功能:返回出现次数最多的分类名称,当数据集处理完所有属性,类标签还不是唯一的时候,         采用多数投票的方法决定该叶子节点的分类'''def majorityCnt(classList): #返回出现次数最多的分类名称    classCount = {}    for vote in classList:        if vote not in classCount.keys():            classCount[vote] = 0        classCount[vote] += 1    sortedClassCount = sorted(classCount.items(), \                              key=operator.itemgetter(1), reverse=True)    return classCount[0][0]

决策树实现

'''函数功能:构造决策树'''def createTree(dataSet, labels):    classList = [example[-1] for example in dataSet] #获取数据集中出现的类别    if classList.count(classList[0]) == len(classList): #类别完全相同则停止继续划分        return classList[0]    if len(dataSet[0]) == 1: #遍历完所有特征时返回出现次数最多的类别        return majorityCnt(classList)    bestFeat = chooseBestFeatureToSplit(dataSet) #选取做合适的划分特征量    bestFeatLabel = labels[bestFeat] #保存特征量的偏移量    myTree = {bestFeatLabel:{}} #用字典存储数的结构    del(labels[bestFeat]) #    featValues = [example[bestFeat] for example in dataSet]    uniqueVals = set(featValues)    for value in uniqueVals:        subLabels = labels[:] #为了保证每次调用createTree()时不改变原始列表的内容,使用新变量subLabels代替原始列表        myTree [bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),\               subLabels)    return myTree #返回构造的决策树

存储决策树

由于每次决策树生成都会消耗大量时间,但进行分类计算的时候会很快。一般如果数据集不改变的情况下,我们可以将生成的决策树通过pickle进行存储,需要用的时候读取出来,这样会节约很多时间。
实现代码如下:

'''函数功能:利用pickle来存储构造好的决策树'''def storeTree(inputTree, filename):    import pickle    fw = open(filename, 'wb')    pickle.dump(inputTree, fw)    fw.close()'''函数功能:获取构造好的决策树'''def grabTree(filename):    import pickle    fr = open(filename, 'rb')    return pickle.load(fr)

分类器构建

通过分类器,可以将我们输入的参数得到适合我们自己的隐形眼镜

'''函数功能:构造分类器'''def classify(inputTree, featLabels, testVec):    firstStr = list(inputTree.keys())[0] #获取决策树,第一个分类    secondDict = inputTree[firstStr]    featIndex = featLabels.index(firstStr) #获取输入的的数据的Label偏移量    for key in secondDict.keys():        if testVec[featIndex] == key: #读出输入数据的值与决策树对应分类的值进行比较            if type(secondDict[key]).__name__ == 'dict' :#如果这个值对应的是一个字典,说明还没找到合适的分类,继续递归去找                classLabel = classify(secondDict[key], featLabels, testVec)            else: #如果不是,就说明已经匹配出结果,返回匹配的类型结果                classLabel = secondDict[key]    return classLabel

交互方法

为了更人性化的操作,需要写一个简单的交互方法,代码如下:

'''函数功能:获取隐形眼镜的数据'''def GetContactLensesData(filename):    fr = open(filename)    lenses = [inst.strip().split('\t') for inst in fr.readlines()]    lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate']    return lenses, lensesLabels'''函数功能:交互方法 '''def ChooseContactLenses():    lenses, lensesLabels = GetContactLensesData(r'D:\Learning\DataSet\lenses.txt')    labels = lensesLabels.copy() #因为生成决策树时,修改了labels,我们需要拷贝一份    lensesTree =  createTree(lenses, labels)    tearRate = input("How many tears?Options:reduced/normal\n")    astigmatic = input("Does the eye have astigmatic? Options: yes/no\n")    prescript = input("Are you myope or hyper? Options: myope/hyper\n")    age = input("How many years you? Options:pre/presbyopic/young\n")    personList = [age, prescript, astigmatic, tearRate]    classLabel = classify(lensesTree, lensesLabels, personList)    print("you should choose the contact lenses:%s" %classLabel)

附简单数据集

young   myope   no  reduced no lensesyoung   myope   no  normal  softyoung   myope   yes reduced no lensesyoung   myope   yes normal  hardyoung   hyper   no  reduced no lensesyoung   hyper   no  normal  softyoung   hyper   yes reduced no lensesyoung   hyper   yes normal  hardpre myope   no  reduced no lensespre myope   no  normal  softpre myope   yes reduced no lensespre myope   yes normal  hardpre hyper   no  reduced no lensespre hyper   no  normal  softpre hyper   yes reduced no lensespre hyper   yes normal  no lensespresbyopic  myope   no  reduced no lensespresbyopic  myope   no  normal  no lensespresbyopic  myope   yes reduced no lensespresbyopic  myope   yes normal  hardpresbyopic  hyper   no  reduced no lensespresbyopic  hyper   no  normal  softpresbyopic  hyper   yes reduced no lensespresbyopic  hyper   yes normal  no lenses

大家也可以去找其他的资源。

总结

这是小东自己做的一个简单的预测器,由于水平不足,很多内容都是一笔带过,具体的含义大家阅读相关书籍进行学习,小东这就可以去配隐形眼镜了。

阅读全文
1 0
原创粉丝点击