机器学习实战

贝叶斯算法预测托儿所推荐程度、回归算法预测电影评分

2018-10-27  本文已影响0人  YinliX

第一题:用贝叶斯算法分析数据集nursery

数据来源

https://archive.ics.uci.edu/ml/datasets/nursery

运行环境

python3.7、PyCharm 2018.2.4 (Community Edition)

思路:

根据所给的数据集及其说明可以看出,数据集共有12960组数据,每组数据包括8个特征和1个标签,其中每个特征都是类型数据,标签也是类型数据,并且每个特征相互独立,所以考虑采用朴素贝叶斯的方法来构造一个分类器,用12000组数据进行训练,960组数据用来测试。
此贝叶斯分类器用字典列表的形式来表示,字典列表共八个元素,每个元素均为一个字典,分别一一对应于八个特征,每个特征字典的键就是此特征的所有可能的取值,每个键对应的值为一个列表,列表共5个元素,分别对应与标签的5个取值,取值的意义为若测试集的此特征取此值的时候,属于这5个可能的标签取值的概率。
代码共有4个函数和一个主函数,fileToMatrix(filename)、creatDictListOfFeature()和trainNB(dataset, dictListOfFeature, labelsSum, labels)以及classify(dictListOfFeature, labelsProb, testVect),其中第一个函数用来处理data文件,将其转化为训练集和测试集,第二个函数用来初始化贝叶斯分类器,第三个函数用训练集来训练贝叶斯分类器,最后的函数用训练好的分类器来对要测试的数据进行分类并返回分类结果。主函数调用这些函数并对测试集逐行进行分类,和实际结果进行对比,统计错误数目并输出结果。

源代码

# -*- coding: UTF-8 -*-
#Author:Yinli

#训练数据和测试数据以及数据的总数
trainingNum = 12000
testingNum = 960
totalNum = 12960
featureNum = 8

#传入数据文件名,进行处理,返回训练集和测试集
def fileToMatrix(filename):
   fr = open(filename, 'r', encoding='utf-8')
   #逐行读取数据文件
   arrayOfLines = fr.readlines()
   #初始化测试集和训练集
   dataSet = []
   testSet = []
   #逐行处理数据,形成训练集
   for i in range(trainingNum):
      arrayOfLines[i] = arrayOfLines[i].strip()
      listFromLine = arrayOfLines[i].split(',')
      dataSet.append(listFromLine)
   #逐行处理数据,形成测试集
   for i in range(testingNum):
      arrayOfLines[i+testingNum] = arrayOfLines[i+testingNum].strip()
      listFromLine = arrayOfLines[i+testingNum].split(',')
      testSet.append(listFromLine)
   #返回测试集和训练集
   return dataSet, testSet

#初始化分类器,即特征字典列表
def creatDictListOfFeature():
   dictListOfFeature = []
   #zeros为包含5个0的列表
   zeros = [0 for _ in range(5)]
   #每个特征字典的键为特征可能的取值,对应的键值初始化为一个包含5个0的列表
   dictListOfFeature.append({"usual":zeros[:], "pretentious":zeros[:], "great_pret":zeros[:]})
   dictListOfFeature.append({"proper":zeros[:], "less_proper":zeros[:], "improper":zeros[:],"critical":zeros[:], "very_crit":zeros[:]})
   dictListOfFeature.append({"complete":zeros[:], "completed":zeros[:], "incomplete":zeros[:],"foster":zeros[:]})
   dictListOfFeature.append({"1":zeros[:], "2":zeros[:], "3":zeros[:],"more":zeros[:]})
   dictListOfFeature.append({"convenient":zeros[:], "less_conv":zeros[:], "critical":zeros[:]})
   dictListOfFeature.append({"convenient":zeros[:], "inconv":zeros[:]})
   dictListOfFeature.append({"nonprob":zeros[:], "slightly_prob":zeros[:], "problematic":zeros[:]})
   dictListOfFeature.append({"recommended":zeros[:], "priority":zeros[:], "not_recom":zeros[:]})
   #返回分类器
   return dictListOfFeature

#传入训练集、分类器训练分类器
def trainNB(dataSet, dictListOfFeature, labelsSum, labels):
   #遍历每组数据,统计每个特征取值所对应的标签计数
   for i in range(trainingNum):
      #记录这组数据的标签索引
      labelIndex = labels.index(dataSet[i][-1])
      #记录此标签出现的次数
      labelsSum[labelIndex] += 1
      #遍历每个特征
      for j in range(featureNum):
         #统计此特征的取值所对应的标签计数
         featureValue = dataSet[i][j]
         dictListOfFeature[j][featureValue][labelIndex] += 1
   #将标签计数经过计算转化为概率
   for i in range(featureNum):
      for k in dictListOfFeature[i].keys():
         for j in range(5):
            #对于每个特征取值,将5个标签取值分别与5个标签总数相除,得到后验概率,引入了拉普拉斯平滑,分子加一,分母加此属性可能的取值数
            dictListOfFeature[i][k][j] = (dictListOfFeature[i][k][j]+1) / float(labelsNum[j]+len(dictListOfFeature[i]))
   return

#传入分类器和类先验概率labelsProb以及测试数据,进行分类并返回分类结果
def classify(dictListOfFeature, labelsProb, testVec):
   #用probList来统计属于每个标签取值的概率,初始化为0
   probList = [0 for _ in range(5)]
   #对于每个标签取值进行遍历,计算概率
   for i in range(5):
      #先取类先验概率,表示不考虑特征的情况下,取此种标签取值的概率
      #也就是在训练集中此种标签取值出现的概率
      probList[i] = labelsProb[i]
      #遍历每个特征,得到此组数据取这个标签取值的概率
      for j in range(featureNum):
         #分别乘以特征的条件概率,即在属于此标签取值的前提下这个特征取这个取值的概率
         probList[i] *= dictListOfFeature[j][testVec[j]][i]
   #求出概率最高的标签取值,作为分类结果
   maxProb = max(probList)
   #返回分类结果的索引
   return probList.index(maxProb)

#主函数
if __name__ == "__main__":
   #初始化训练集、测试集以及分类器
   filename = r"D:\python_things\code\第3次作业\贝叶斯\数据\nursery.data"
   dataSet, testSet = fileToMatrix(filename)
   dictListOfFeature = creatDictListOfFeature()
   #初始化标签取值计数列表和标签先验概率列表以及标签取值列表
   labelsNum = [0 for _ in range(5)]
   labelsProb = [0 for _ in range(5)]
   labels = ["not_recom", "recommend", "very_recom", "priority", "spec_prior"]
   #训练贝叶斯分类器
   trainNB(dataSet, dictListOfFeature, labelsNum, labels)
   #计算每个标签取值的先验概率,及在训练集中所出现的概率
   #引入了拉普拉斯平滑,分子加一,分母加标签取值数
   for i in range(5):
      labelsProb[i] = (labelsNum[i]+1) / float(trainingNum+5)
   #遍历测试集,分类每组数据并统计错误结果
   errorNum = 0
   for i in range(testingNum):
      #对每组数据进行分类
      classifyResult = classify(dictListOfFeature, labelsProb, testSet[i])
      #打印分类结果和实际结果
      print("预测结果为%s,实际结果为%s" % (labels[classifyResult], testSet[i][-1]))
      #若不一致则记录下来
      if(classifyResult != labels.index(testSet[i][-1])):
         errorNum += 1
   #输出测试结果
   print("一共%d组数据,其中%d组数据用于训练,%d组数据用于测试" % (totalNum, trainingNum, testingNum))
   print("一共%d组测试数据,判断错误%d组数据,错误率为%.3f%%" % (testingNum, errorNum, errorNum / testingNum * 100))

运行结果

bayes.png

第二次作业:用回归算法分析电影数据

数据来源

https://archive.ics.uci.edu/ml/datasets/CSM+%28Conventional+and+Social+Media+Movies%29+Dataset+2014+and+2015

运行环境

python3.7、numpy1.15.2、PyCharm 2018.2.4 (Community Edition)

思路

根据所给数据及说明,数据共有231组,每组数据包括一部电影一系列的指标,比如评分、预算等,在此将除电影名字、年份、评分和总票房之外的指标视为特征,将评分视为要预测的指标,用180组数据进行训练,其余数据进行测试。因为这些指标都可视为连续值,并且评分也为连续值,所以考虑采用线性回归的算法进行预测,而这些特征数据中有缺失值,并且范围差距太大,所以要进行缺失值的处理以及特征缩放。另外,考虑到训练数大于特征数目,可以采用通用的梯度上升法进行训练也可以采用正规方程法进行训练,在本程序中,这两种方法均有采用,并进行比较。预测结果在实际结果取值正负一区间之内就是为预测正确。

源代码

# -*- coding: UTF-8 -*-
#Author:Yinli

import xlrd
import numpy as np

#数据集总数、测试集数目和训练集数目
totalNum = 231
trainingNum = 180
testingNum = totalNum - trainingNum

#传入文件名,进行处理,返回特征矩阵和结果向量
def fileToMatrix(filename):
   #用xlrd读取xlsx表格文件
   excel = xlrd.open_workbook(filename)
   #读取第一个表格
   table = excel.sheets()[0]
   #初始化特征矩阵和结果列表
   dataSet = []
   ratingList = []
   #计算数据集的数目
   numOfData = table.nrows-1
   #遍历每组数据
   for i in range(1,numOfData+1):
      #特征矩阵的第一列为1,为线性方程的常数项
      currentList = []
      currentList.append(1.0)
      #第二列为电影类型,第三列后为预算等特征
      currentList.append(table.row_values(i)[3])
      currentList.extend(table.row_values(i)[5:])
      #将此组数据处理后的列表加到特征矩阵末尾
      dataSet.append(currentList)
      #将评分结果加到结果列表末尾
      ratingList.append(table.row_values(i)[2])
   #返回特征矩阵和结果列表
   return dataSet, ratingList

#处理每一列的缺失值,用此列中其余值的平均值代替
def dealWithColumn(dataSet, j):
   #用列表记录此列的每个值
   columnList = []
   #遍历此列,记录每个不缺失的值
   for i in range(len(dataSet)):
      if dataSet[i][j] != '':
         columnList.append(dataSet[i][j])
   #计算平均值
   mean = np.mean(columnList)
   #遍历此列,若值缺失则替换为平均值
   for i in range(len(dataSet)):
      if dataSet[i][j] == '':
         dataSet[i][j] = mean
   return

#用来解决特征矩阵缺失值
def preProcess0(dataSet):
   dealWithColumn(dataSet, 2)
   dealWithColumn(dataSet, 3)
   dealWithColumn(dataSet, 10)
   return

#对此列进行特征缩放,使所有特征在一个数量级上
def scalingColumn(dataSet, j):
   #遍历此列,记录每一列的值
   columnList = []
   for i in range(len(dataSet)):
      columnList.append(dataSet[i][j])
   #计算平均值、最小值和最大值
   mean = np.mean(columnList)
   minValue = min(columnList)
   maxValue = max(columnList)
   #将特征值缩放为(-5, 5)区间中的值
   for i in range(len(dataSet)):
      dataSet[i][j] = (dataSet[i][j] - mean)*5/float(maxValue-minValue)
   return

#将特征矩阵中需要缩放的列进行特征缩放,都化为(-5,5)区间内的值
def preProcess1(dataSet):
   scalingColumn(dataSet, 2)
   scalingColumn(dataSet, 3)
   scalingColumn(dataSet, 6)
   scalingColumn(dataSet, 7)
   scalingColumn(dataSet, 8)
   scalingColumn(dataSet, 9)
   scalingColumn(dataSet, 10)

#用梯度上升法进行线性回归,返回参数向量
def regression(trainingSet, trainingList):
   #构造特征矩阵和结果向量以及初始化参数向量
   trainingMat = np.mat(trainingSet)
   listMat = np.mat(trainingList).transpose()
   weights = np.ones((11,1))
   #定义梯度上升步长
   step = 0.0002
   #计算预测结果向量h以及与实际结果向量的误差
   h = trainingMat * weights
   error = listMat - h
   #初始化迭代次数
   iterationNum = 0
   #当误差向量中的最大绝对值大于2.4的时候继续迭代,缩小误差
   while max(np.abs(error)) > 2.4:
      #将误差最大绝对值和迭代次数输出,可观察迭代过程是否收敛
      print("max = %f" % max(np.abs(error)))
      print(iterationNum)
      #更新参数向量
      weights = weights + step * trainingMat.transpose() * error
      #用更新后的参数计算新的预测结果及误差
      h = trainingMat * weights
      error = listMat - h
      #迭代次数加一
      iterationNum += 1
   #返回收敛之后的参数向量
   return weights.getA()

#用正规方程法计算参数向量并返回
def normalEquation(trainingSet, trainingList):
   #计算特征向量的转置xTran
   xTran = np.transpose(trainingSet)
   #计算特征矩阵的转置点乘特征矩阵xTranX
   xTranX = np.dot(xTran, trainingSet)
   #求xTranX的逆xTranXInv
   xTranXInv = np.linalg.inv(xTranX)
   #用xTranXInv点乘xTran再点乘结果向量得到参数向量
   weights = np.dot(np.dot(xTranXInv, xTran), trainingList)
   #返回计算出的参数向量
   return weights

#主函数
if __name__ == "__main__":
   #处理xlsx表格得到特征矩阵和结果列表,并对其进行缺失值和特征缩放预处理
   filename = r"D:\python_things\code\第3次作业\回归算法\2014 and 2015 CSM dataset.xlsx"
   dataSet, ratingList = fileToMatrix(filename)
   preProcess0(dataSet)
   preProcess1(dataSet)

   #从特征矩阵和结果列表中分出训练集和测试集
   trainingSet = []
   testingSet = []
   trainingRatingList = ratingList[:trainingNum]
   testingRatingList = ratingList[trainingNum:]
   for i in range(trainingNum):
      trainingSet.append(dataSet[i])
   for i in range(testingNum):
      testingSet.append(dataSet[i+trainingNum])

   #可采用两种方法梯度下降法和正规方程法计算参数向量
   #weights = normalEquation(trainingSet,trainingRatingList)
   weights = regression(trainingSet, trainingRatingList)

   #遍历测试集并统计结果
   errorNum = 0
   for i in range(testingNum):
      #用得到的线性方程计算预测结果
      predictValue =  weights.transpose() * np.mat(testingSet[i]).transpose()
      #输出预测结果和实际结果
      print("预测结果:%f,真实结果:%f" % (predictValue, testingRatingList[i]))
      #如果预测结果在实际结果的正负一区间之外则视为预测错误
      if abs(predictValue-testingRatingList[i]) > 1:
         errorNum += 1
   #输出结果
   print("一共%d组数据,其中%d组数据用于训练,%d组数据用于测试" % (totalNum, trainingNum, testingNum))
   print("一共%d组测试数据,判断错误%d组数据,错误率为%.3f%%" % (testingNum, errorNum, errorNum / testingNum * 100))

运行结果

梯度上升法
GD.png
正规方程法
NE.png
上一篇下一篇

猜你喜欢

热点阅读