使用Python编写朴素贝叶斯分类器

上例的数据格式如下:

  1. both sedentary moderate yes i100
  2. both sedentary moderate no i100
  3. health sedentary moderate yes i500
  4. appearance active moderate yes i500
  5. appearance moderate aggressive yes i500
  6. appearance moderate aggressive no i100
  7. health moderate aggressive no i500
  8. both active moderate yes i100
  9. both moderate aggressive yes i500
  10. appearance active aggressive yes i500
  11. both active aggressive no i500
  12. health active moderate no i500
  13. health sedentary aggressive yes i500
  14. appearance active moderate no i100
  15. health sedentary moderate no i100

虽然这个这个例子中只有15条数据,但是我们还是保留十折交叉验证的过程,以便用于更大的数据集。十折交叉验证要求数据集等分成10份,这个例子中我们简单地将15条数据全部放到一个桶里,其它桶留空。

朴素贝叶斯分类器包含两个部分:训练和分类。

训练

训练的输出结果应该是:

  • 先验概率,如P(i100) = 0.4;
  • 条件概率,如P(健康|i100) = 0.167

我们使用如下代码表示先验概率:

  1. self.prior = {'i500': 0.6, 'i100': 0.4}

条件概率的表示有些复杂,用嵌套的字典来实现:

  1. {'i500': {1: {'appearance': 0.3333333333333333, 'health': 0.4444444444444444,
  2. 'both': 0.2222222222222222},
  3. 2: {'active': 0.4444444444444444, 'sedentary': 0.2222222222222222,
  4. 'moderate': 0.3333333333333333},
  5. 3: {'aggressive': 0.6666666666666666, 'moderate': 0.3333333333333333},
  6. 4: {'yes': 0.6666666666666666, 'no': 0.3333333333333333}},
  7. 'i100': {1: {'both': 0.5, 'health': 0.16666666666666666,
  8. 'appearance': 0.3333333333333333},
  9. 2: {'active': 0.3333333333333333, 'sedentary': 0.5,
  10. 'moderate': 0.16666666666666666},
  11. 3: {'aggressive': 0.16666666666666666, 'moderate': 0.8333333333333334},
  12. 4: {'yes': 0.3333333333333333, 'no': 0.6666666666666666}}}

1、2、3、4表示第几列,所以第一行可以解释为购买i500的顾客中运动目的是外表的概率是0.333。

首先我们要来进行计数,比如以下几行数据:

  1. both sedentary moderate yes i100
  2. both sedentary moderate no i100
  3. health sedentary moderate yes i500
  4. appearance active moderate yes i500

我们用字典来统计每个模型的次数,变量名为classes,逐行扫描后的结果是:

  1. # 第一行
  2. {'i100': 1}
  3. # 第二行
  4. {'i100': 2}
  5. # 第三行
  6. {'i500': 1, 'i100': 2}
  7. # 全部
  8. {'i500': 9, 'i100': 6}

要获取模型的先验概率,只要将计数结果除以总数就可以了。

计算后验概率也需要计数,变量名为counts。这个字典较为复杂,如扫完第一行第一列的结果是:

  1. {'i100': {1: {'both': 1}}}

处理完所有数据后的计数结果是:

  1. {'i500': {1: {'appearance': 3, 'health': 4, 'both': 2},
  2. 2: {'active': 4, 'sedentary': 2, 'moderate': 3},
  3. 3: {'aggressive': 6, 'moderate': 3},
  4. 4: {'yes': 6, 'no': 3}},
  5. 'i100': {1: {'both': 3, 'health': 1, 'appearance': 2},
  6. 2: {'active': 2, 'sedentary': 3, 'moderate': 1},
  7. 3: {'aggressive': 1, 'moderate': 5},
  8. 4: {'yes': 2, 'no': 4}}}

计算概率时,只需将计数除以该模型的总数就可以了:

P(外表|i100) = 2 / 6 = 0.333

以下是训练用的Python代码:

  1. class Classifier:
  2. def __init__(self, bucketPrefix, testBucketNumber, dataFormat):
  3. """bucketPrefix 分桶数据集文件前缀
  4. testBucketNumber 测试桶编号
  5. dataFormat 数据格式,形如:attr attr attr attr class
  6. """
  7. total = 0
  8. classes = {}
  9. counts = {}
  10. # 从文件中读取数据
  11. self.format = dataFormat.strip().split('\t')
  12. self.prior = {}
  13. self.conditional = {}
  14. # 遍历十个桶
  15. for i in range(1, 11):
  16. # 跳过测试桶
  17. if i != testBucketNumber:
  18. filename = "%s-%02i" % (bucketPrefix, i)
  19. f = open(filename)
  20. lines = f.readlines()
  21. f.close()
  22. for line in lines:
  23. fields = line.strip().split('\t')
  24. ignore = []
  25. vector = []
  26. for i in range(len(fields)):
  27. if self.format[i] == 'num':
  28. vector.append(float(fields[i]))
  29. elif self.format[i] == 'attr':
  30. vector.append(fields[i])
  31. elif self.format[i] == 'comment':
  32. ignore.append(fields[i])
  33. elif self.format[i] == 'class':
  34. category = fields[i]
  35. # 处理该条记录
  36. total += 1
  37. classes.setdefault(category, 0)
  38. counts.setdefault(category, {})
  39. classes[category] += 1
  40. # 处理各个属性
  41. col = 0
  42. for columnValue in vector:
  43. col += 1
  44. counts[category].setdefault(col, {})
  45. counts[category][col].setdefault(columnValue, 0)
  46. counts[category][col][columnValue] += 1
  47. # 计数结束,开始计算概率
  48. # 计算先验概率P(h)
  49. for (category, count) in classes.items():
  50. self.prior[category] = count / total
  51. # 计算条件概率P(h|D)
  52. for (category, columns) in counts.items():
  53. self.conditional.setdefault(category, {})
  54. for (col, valueCounts) in columns.items():
  55. self.conditional[category].setdefault(col, {})
  56. for (attrValue, count) in valueCounts.items():
  57. self.conditional[category][col][attrValue] = (
  58. count / classes[category])
  59. self.tmp = counts

分类

分类函数会这样使用:

  1. c.classify(['health', 'moderate', 'moderate', 'yes'])

我们需要计算:

使用Python编写朴素贝叶斯分类器 - 图1

  1. def classify(self, itemVector):
  2. """返回itemVector所属类别"""
  3. results = []
  4. for (category, prior) in self.prior.items():
  5. prob = prior
  6. col = 1
  7. for attrValue in itemVector:
  8. if not attrValue in self.conditional[category][col]:
  9. # 属性不存在,返回0概率
  10. prob = 0
  11. else:
  12. prob = prob * self.conditional[category][col][attrValue]
  13. col += 1
  14. results.append((prob, category))
  15. # 返回概率最高的结果
  16. return(max(results)[1])

让我们测试一下:

  1. >>> c = Classifier('iHealth/i', 10, 'attr\tattr\tattr\tattr\tclass')
  2. >>> c.classify(['health' 'moderate', 'moderate', 'yes'])
  3. i500

使用Python编写朴素贝叶斯分类器 - 图2