机器学习编程作业-决策树

决策树

  • 作业说明
  • 任务1-手撕决策树代码预测隐形眼镜类型
  • 任务2-预测用户所在房间
  • 任务3-泰坦尼克幸存者预测
  • 后记

作业说明

本次作业分三个任务。
第一个是使用决策树预测隐形眼镜类型。数据集是非常经典的隐形眼镜数据集,使用时数据集是txt文本文件的形式。要求是手撕算法不能够直接调库。
第二个是根据用户采集的 WiFi信息采用决策树预测用户所在房间。这个数据集应该是老师以前在学校搞的一个手机的log。在后面编程作业中也经常使用到这个数据集。可以调库。
第三个是预测泰坦尼克幸存者。这个似乎也是很有名的一个项目,给出数据集用决策树的方法进行预测,得出结果上传到网站上和全球玩家比试看谁的预测精度高。
网站地址为https://www.kaggle.com/c/titanic/leaderboard,可能需要魔法,数据集可以在网站上下载

任务1-手撕决策树代码预测隐形眼镜类型

首先上图,这个是西瓜书里面对决策树算法的伪码表示,写的还是比较清楚的,手撕代码也就是把这个伪码翻译成自己的代码。
机器学习编程作业-决策树_第1张图片
训练集D中包含了 x i x_{i} xi y i y_{i} yi,分别是一个样本的属性值向量和样本对应的标签,属性集A也就只保存了当前循环中还没有用来生成决策树的属性的名字。决策树生成的算法中最核心的就是从A中划分最优属性。
采用最简单的方法:信息增益。选择信息增益最大的属性进行划分。
G a i n ( D , a ) = E n t ( D ) − ∑ v = 1 V D v D E n t ( D v ) Gain(D,a)=Ent(D)-\sum_{v=1}^{V} \frac{D^{v} }{D} Ent(D^{v}) Gain(D,a)=Ent(D)v=1VDDvEnt(Dv)
E n t ( D ) = − ∑ k = 1 ∣ y ∣ p k l o g 2 p k Ent(D)=-\sum_{k=1}^{|y|} p_{k} log_{2}p_{k} Ent(D)=k=1ypklog2pk
所以就能写出计算熵和计算信息增益的代码

def getent(Data):  # 计算熵
    datanum = len(Data)
    ent = 0
    dic = {}
    for i in range(datanum):
        if Data[i][-1] not in dic:
            dic[Data[i][-1]] = 1
        else:
            dic[Data[i][-1]] += 1
    for key in dic:
        p = dic[key] / datanum
        ent -= p * log(p, 2)
    return ent
def getbestattr(Data):
    # 计算maxGain(D,a)
    attrnum = len(Data[0])
    datanum = len(Data)
    ent = getent(Data)
    maxgain = 0
    bestattr = None
    for i in range(attrnum-1):
        attrlist = [da[i] for da in Data]
        dir = {}
        for attr in attrlist:
            if attr not in dir:
                dir[attr] = 1
            else:
                dir[attr] += 1
        attrs = dir.keys()
        entattr = 0
        for attr in attrs:
            subdata = splitdata(Data, i, attr)
            p = dir[attr]/datanum
            entattr += p * getent(subdata)
        gain = ent - entattr
        if gain > maxgain:
            maxgain = gain
            bestattr = i
    return bestattr

核心部分就这么多,剩下的都是读取txt文件生成数组或向量、数组、字典计数这些杂事
最终把伪码翻译成自己的代码

def treegenerate(Data, attr):
    classlist = [da[-1] for da in Data]
    if classlist.count(classlist[0]) == len(classlist):return classlist[0]  # d中样本全属于同一类别C,返回叶节点
    if not attr or len(Data[0]) == 1:return maxnumlist(classlist)  # 返回叶节点

    splitattr = getbestattr(Data)  # 通过计算最大Gain得到的属性在Data[i]中的序号
    attrname = attr[splitattr]  # 获取属性名字
    tree = {attrname: {}}
    del attr[splitattr]
    attrlist = [da[splitattr] for da in Data]
    attrvs = set(attrlist)
    for attrv in attrvs:
        subdata = splitdata(Data, splitattr, attrv)
        if subdata:tree[attrname][attrv] = treegenerate(subdata, copy.deepcopy(attr))  # 生成分支
        else:tree[attrname][attrv] = maxnumlist(classlist)  # Dv为空则叶节点标记为D中样本最多的类
    return tree

任务2-预测用户所在房间

数据集的每一条内容都包含了BSSIDLabel,RSSLabel,RoomLabel,SSIDLabel,finLabel
一个BSSIDLabel就是AP的一个MAC地址。
RSSLabel是信号强度大小,是一个连续值
RoomLabel是房间号,用来预测
SSIDLabel是WiFi名称,没啥用
finLabel是指纹,finLabel标号相同,表示这部分BSSID在同一时刻被采集到;我们将在同一时刻采集的所有BSSID及其相应RSS构成的矢量称为一个指纹 f i = [ B S S I D 1 : R S S 1 , B S S I D 2 : R S S 2 , . . . , R o o m L a b e l ] f_{i}=[BSSID_{1}:RSS_{1},BSSID_{2}:RSS_{2},...,RoomLabel] fi=[BSSID1:RSS1,BSSID2:RSS2,...,RoomLabel];由于BSSID的RSS在不同位置大小不同,因此指纹可以唯一的标识一个位置。
数据集是csv文件,需要用pandas库进行读取。
读取完数据之后,用pandas库的groughby函数将相同finLabel的作为一组。
训练不需要手撕,直接用sklearn库中函数即可

# 处理原始数据
def dataprocess(fins, bssid):
    x, y = [], []
    for finlabel, v in fins:
        # print(finlabel, len(list(v['BSSIDLabel'])))
        # finlabellist = list(v['BSSIDLabel'])
        # print(v['RSSLabel'])
        finlabellist = list(v['BSSIDLabel'])
        rsslabellist = list(v['RSSLabel'])
        # fi = [0] * bssnums
        fi = [-100] * bssnums
        for bss in bssid:
            if bss in finlabellist:
                # fi[bssid.index(bss)] = 1
                fi[bssid.index(bss)] = rsslabellist[finlabellist.index(bss)]

        x.append(fi)
        roomlabel = list(v['RoomLabel'])
        # print(len(roomlabel))
        y.append(roomlabel[0])
    return x, y

if __name__ == "__main__":
    # 读取与数据预处理
    traindata_ori = pd.read_csv('TrainDT.csv', encoding='gbk')  # utf-8编码读取会报错
    testdata_ori = pd.read_csv('TrainDT.csv', encoding='gbk')
    # print(testdata_ori.info())
    # 感觉ssr的数值没有什么用,判断的时候直接用finLabel进行判断
    # 补全缺失的ssr,设置值为100
    # imp = SimpleImputer(missing_values=np.nan, strategy='constant', fill_value=-100)
    # testdata = pd.DataFrame(imp.fit_transform(testdata_ori))
    # traindata = pd.DataFrame(imp.fit_transform(traindata_ori))
    # traindata.columns = traindata_ori.columns
    # testdata.columns = testdata_ori.columns  # 对齐
    traindata = traindata_ori[['BSSIDLabel', 'RSSLabel', 'RoomLabel', 'finLabel']]
    testdata = testdata_ori[['BSSIDLabel', 'RSSLabel', 'RoomLabel', 'finLabel']]
    trainfins = traindata.groupby('finLabel')  # 相同指纹作为一组
    testfins = testdata.groupby('finLabel')
    print(len(testfins))
    bssid = list(set(traindata['BSSIDLabel']))
    bssnums = len(bssid)

    # x为bssid向量组,大小为bssnum*数据量,一条数据大小是bssnum*1。该条数据有存在的bss就为1
    # y为room向量组,x中一条数据对应y的一个数据
    trainx, trainy = dataprocess(trainfins, bssid)
    testx, testy = dataprocess(testfins, bssid)

    # 调库构建决策树并进行预测
    wifitree = tree.DecisionTreeClassifier()
    wifitree.fit(trainx, trainy)
    predict = list(wifitree.predict(testx))
    print('实际结果', np.array(testy))
    print('预测结果', np.array(predict))
    print('精度', accuracy_score(testy, predict))

任务3-泰坦尼克幸存者预测

可以直接复用任务2的代码,没什么多说的。主要就是选取属性不同会导致结果不同。

变量名 变量解释
PassengerId 乘客编号
Survived 乘客是否存活(0=NO, 1=Yes)
Pclass 乘客所在的船舱等级(1=1st, 2=2nd, 3=3rd; 1st = Upper, 2nd = Middle, 3rd = Lower)
Name 乘客姓名
Sex 乘客性别
Age 乘客年龄(Age is fractional if less than 1. If the age is estimated, is it in the form of xx.5)[有缺失值]
SibSp 乘客的兄弟姐妹/配偶数量
Parch 乘客的父母/子女数量
Ticket 票的编号
Fare 票价
Cabin 座位号[有缺失值]
Embarked 乘客登船码头[有缺失值]

实际训练时不可能把所有的属性都用上,必选的属性有Pclass,Sex,Age。其他属性自由搭配。我达到的最高准确率是0.78229 。

后记

写blog写的比我实验报告还长我是没有想到的,可能是因为贴了代码?
我打算代码再打包上传一遍。
放假在家,效率好低啊,写一篇用了我一下午,其他时间都在刷手机。。。

你可能感兴趣的:(机器学习,机器学习,决策树,算法)