朴素贝叶斯分类器






4.90/5 (22投票s)
维基百科“朴素贝叶斯分类器算法”的实现。
引言
这是一个基于贝叶斯定理的简单概率分类器,来自维基百科文章。该项目包含可以包含在任何 C# 项目中的源文件。
贝叶斯分类器能够根据输入计算最可能的输出。可以在运行时添加新的原始数据并拥有更好的概率分类器。朴素贝叶斯分类器假设给定类变量,类的特定特征的存在(或不存在)与任何其他特征的存在(或不存在)无关。例如,如果一个水果是红色的、圆形的,并且直径约为 4 英寸,则可以认为它是一个苹果。即使这些特征相互依赖或依赖于其他特征的存在,朴素贝叶斯分类器也会认为所有这些属性都独立地有助于该水果是苹果的概率。
贝叶斯解释
在贝叶斯(或认识论)解释中,概率衡量的是一种“信念程度”。然后,贝叶斯定理将考虑证据前后对一个命题的信念程度联系起来。例如,假设有人提出,一枚有偏差的硬币正面朝上的可能性是反面的两倍。最初对这一点的信念程度可能是 50%。然后多次抛硬币以收集证据。如果证据支持该主张,信念可能会上升到 70%。
对于命题 A 和证据 B,
- P(A),即先验,是对 A 的初始信念程度。
- P(A | B),即后验,是在考虑了 B 之后的信念程度。
- P(B | A) / P(B) 代表 B 为 A 提供的支持。
性别分类
问题:根据测量特征对给定的人进行男性或女性分类。特征包括身高、体重和脚的大小。
培训
示例训练集如下所示。
性别 | 身高(英尺) | 体重(磅) | 脚的大小(英寸) |
---|---|---|---|
男性 | 6 | 180 | 12 |
男性 | 5.92 (5'11") | 190 | 11 |
男性 | 5.58 (5'7") | 170 | 12 |
男性 | 5.92 (5'11") | 165 | 10 |
女性 | 5 | 100 | 6 |
女性 | 5.5 (5'6") | 150 | 8 |
女性 | 5.42 (5'5") | 130 | 7 |
女性 | 5.75 (5'9") | 150 | 9 |
从使用高斯分布假设的训练集中创建的分类器将是
性别 | 平均值(身高) | 方差(身高) | 平均值(体重) | 方差(体重) | 平均值(脚的大小) | 方差(脚的大小) |
---|---|---|---|---|---|---|
男性 | 5.855 | 3.5033e-02 | 176.25 | 1.2292e+02 | 11.25 | 9.1667e-01 |
女性 | 5.4175 | 9.7225e-02 | 132.5 | 5.5833e+02 | 7.5 | 1.6667e+00 |
假设我们有等概率的类,因此 P(男性)= P(女性) = 0.5。没有确定做出此假设的原因,因此这可能是一个坏主意。如果我们根据训练集中的频率确定 P(C),我们会得到相同的答案。
以下是将被分类为男性或女性的样本。
性别 | 身高(英尺) | 体重(磅) | 脚的大小(英寸) |
---|---|---|---|
示例 | 6 | 130 | 8 |
我们希望确定哪个后验更大,男性还是女性。对于男性分类,后验由下式给出
对于女性分类,后验由下式给出
可以计算证据(也称为归一化常数),因为后验之和等于 1。
由于证据是一个正的常数,因此可以忽略。 (正态分布总是正的。)我们现在确定样本的性别。
P(男性) = 0.5
,其中 μ = 5.855 且 σ2 = 3.5033e − 02 是正态分布的参数,这些参数先前已从训练集中确定。请注意,此处大于 1 的值是可以的 – 它是概率密度而不是概率,因为身高是一个连续变量。
p(体重 | 男性) = 5.9881e-06
p(脚的大小 | 男性) = 1.3112e-3
后验分子(男性)= 它们的乘积 = 6.1984e-09
P(女性) = 0.5
p(身高 | 女性) = 2.2346e-1
p(体重 | 女性) = 1.6789e-2
p(脚的大小 | 女性) = 2.8669e-1
后验分子(女性)= 它们的乘积 = 5.3778e-04
由于后验分子在女性情况下更大,因此我们预测样本为女性。
使用代码
DataTable table = new DataTable();
table.Columns.Add("Sex");
table.Columns.Add("Height", typeof(double));
table.Columns.Add("Weight", typeof(double));
table.Columns.Add("FootSize", typeof(double));
//training data.
table.Rows.Add("male", 6, 180, 12);
table.Rows.Add("male", 5.92, 190, 11);
table.Rows.Add("male", 5.58, 170, 12);
table.Rows.Add("male", 5.92, 165, 10);
table.Rows.Add("female", 5, 100, 6);
table.Rows.Add("female", 5.5, 150, 8);
table.Rows.Add("female", 5.42, 130, 7);
table.Rows.Add("female", 5.75, 150, 9);
table.Rows.Add("transgender", 4, 200, 5);
table.Rows.Add("transgender", 4.10, 150, 8);
table.Rows.Add("transgender", 5.42, 190, 7);
table.Rows.Add("transgender", 5.50, 150, 9);
Classifier classifier = new Classifier();
classifier.TrainClassifier(table);
//output would be transgender.
Console.WriteLine(classifier.Classify(new double[] { 4, 150, 12 }));
Console.Read();
public void TrainClassifier(DataTable table)
{
dataSet.Tables.Add(table);
//table
DataTable GaussianDistribution = dataSet.Tables.Add("Gaussian");
GaussianDistribution.Columns.Add(table.Columns[0].ColumnName);
//columns
for (int i = 1; i < table.Columns.Count; i++)
{
GaussianDistribution.Columns.Add(table.Columns[i].ColumnName + "Mean");
GaussianDistribution.Columns.Add(table.Columns[i].ColumnName + "Variance");
}
//calc data
var results = (from myRow in table.AsEnumerable()
group myRow by myRow.Field<string>(table.Columns[0].ColumnName) into g
select new { Name = g.Key, Count = g.Count() }).ToList();
for (int j = 0; j < results.Count; j++)
{
DataRow row = GaussianDistribution.Rows.Add();
row[0] = results[j].Name;
int a = 1;
for (int i = 1; i < table.Columns.Count; i++)
{
row[a] = Helper.Mean(SelectRows(table, i, string.Format("{0} = '{1}'",
table.Columns[0].ColumnName, results[j].Name)));
row[++a] = Helper.Variance(SelectRows(table, i,
string.Format("{0} = '{1}'",
table.Columns[0].ColumnName, results[j].Name)));
a++;
}
}
}
public string Classify(double[] obj)
{
Dictionary<string,> score = new Dictionary<string,>();
var results = (from myRow in dataSet.Tables[0].AsEnumerable()
group myRow by myRow.Field<string>(
dataSet.Tables[0].Columns[0].ColumnName) into g
select new { Name = g.Key, Count = g.Count() }).ToList();
for (int i = 0; i < results.Count; i++)
{
List<double> subScoreList = new List<double>();
int a = 1, b = 1;
for (int k = 1; k < dataSet.Tables["Gaussian"].Columns.Count; k = k + 2)
{
double mean = Convert.ToDouble(dataSet.Tables["Gaussian"].Rows[i][a]);
double variance = Convert.ToDouble(dataSet.Tables["Gaussian"].Rows[i][++a]);
double result = Helper.NormalDist(obj[b - 1], mean, Helper.SquareRoot(variance));
subScoreList.Add(result);
a++; b++;
}
double finalScore = 0;
for (int z = 0; z < subScoreList.Count; z++)
{
if (finalScore == 0)
{
finalScore = subScoreList[z];
continue;
}
finalScore = finalScore * subScoreList[z];
}
score.Add(results[i].Name, finalScore * 0.5);
}
double maxOne = score.Max(c => c.Value);
var name = (from c in score
where c.Value == maxOne
select c.Key).First();
return name;
}
Classifier
类非常易于使用,它具有两个函数 Train
和 Classify
。要训练分类器,需要创建训练数据集。该示例显示了如何使用一组与身高、体重、脚的大小相关的数据来对性别进行分类。
如果您认为可以获得更好的代码,请告诉我。