前言
本篇博客对模式识别中的“判别函数与几何分类法”进行了讨论,并重点实现了最小平方误差法(Least Mean Square Error, LMSE)算法,也对不同的多类判别法、分段线性判别法进行了一定的实现,并通过随机生成的数据集加以测试。
项目源码及所使用的数据集参见:PR-EXPT2
基础知识
我们首先了解判别函数与几何分类法的基本知识,在此目中不追求科学语言的精确性,旨在用形式化的表达阐述其大致的思想。
判别函数与几何分类
上图呈现了典型的几何分类法的特点,其中左侧为线性判别法,右侧为非线性判别法,蓝色的直线与绿色的圆圈就称为判别函数。
判别函数
若将二维模式推广至n维,则线性判别函数的一般形式为:
其中,X
为增广模式向量,W
为增广权向量。
我们以两类情况为例,对于模式在w1
、w2
中的两类情况,线性判别法的判别方式为:
当d(X) = 0
时,为不可判别情况。
至此,我们已经知道了用判别函数来进行几何分类法的整个过程,即:
- 得到判别函数
d(X)
:即求出增广权向量W
- 用判别函数进行识别:对于一个待测模式
X'
,我们能够用判别函数进行相应判别.
上述过程的第二步:“用判别函数进行识别”是非常简单的,只需要做两个矩阵的乘法运算。关键是第一步,我们该怎么得到判别函数呢,即该怎么求这个W
呢?
可以很直观的想到,判别函数一定是通过训练得到的,即用已有的、已知其所属类别的模式,学习得到的。
不妨假设对已知的w_k
类来说,这个类里面有X1, X2, ..., Xn
这n个模式,那么我们所要求解的W
,首先一定是要满足W * Xi > 0 (i = 1, 2, ..., n)
这个不等式组的。除此之外,还需要满足什么额外条件呢?
而这些额外条件,恰恰是与我们的类别数有关的。
二分类
如果整个模式集只有两类,w1
、w2
,假设这两个类的判别函数分别为d1(X)
、d2(X)
,那么如果一个模式X0
属于w1
类,就需要满足:
d1(X0) > 0
,即这个模式在第一类里d2(X0) < 0
,即这个模式不在第二类里
那么再回到我们刚才讨论的“额外条件”问题:
假设w1
类里有模式X1_1, X1_2, ..., X1_n
,w2
类有模式X2_1, X2_2, ..., X2_n
,那么对于判别函数d1
的训练,我们首先需要:
d1(X1_i) > 0 (i = 1, 2, ..., n)
其次,还需要满足额外条件:d1(X2_i) < 0
,即:
d1(- X2_i) > 0 (i = 1, 2, ..., n)
多分类
那么现在如果模式集有多类,额外条件又该如何确定呢?
在这里我们介绍两种方法:
i_non_i
两分法,或者“正负类”法,即对于类wi
来说,我们把所有的模式集分为两种:一种是属于wi
的,另一种是不属于wi
的。那么求解判别函数di(X)
的不等式组为:di(Xi_j) > 0 (j = 1, 2, ..., n)
di(- Xk_j) > 0 (j = 1, 2, ..., n; k为其他的模式类)
i_j
两分法,即对于类wi
与类wj
来说,这就成了一个二分类的问题。因此,若假设总共有k
个模式类,那么如果要把类wi
与其他k-1
个模式类区分开,就需要求解k-1
个判别函数di_1, di_2, ..., di_k-1
。此时,到了”用判别函数进行识别”的时候,若模式
X0
属于wi
类,就需要满足:di_j(X0) > 0
,即对于以i
打头的所有判别函数,其判别值均需为正。
线性分类算法
到了此处,我们已经明确了整个几何分类法的过程,也知道了在训练判别函数的时候,所要满足的不等式组是什么。那么这个不等式组又该如何求解呢?
这就涉及到了不同的线性分类算法。
虽然这些算法在形式上看起来十分复杂,但我们只需要记住一点:这些算法其实就是在解一个不等式方程组。
我们可以发散地想,这些算法甚至是独立于“模式识别”而存在的。假如你在线性代数的课堂上遇到了一道解不等式方程组的问题,就可以通过这些算法来解决它。其之所以应用在模式识别的领域上,只不过因为,模式识别的几何分类法需要解不等式方程组
(即:求判别函数)。
下面我们列举了几种经典的线性分类算法。
- 感知器算法
- 梯度法
- 最小平方误差算法
在实际应用中,我们应该明确这三种算法之间的关系:
- 感知器算法是梯度法的一种特例。
- 最小平方误差算法利用了梯度法。
- 感知器算法与梯度算法在迭代运算时,算法有可能始终不收敛,因此在程序运行的过程中,我们无法得知,这究竟是因为迭代次数还不够,还是因为数据集本身的就线性不可分。而最小平方误差法改善了这一困境。
因此我们可以看到最小平方误差法的优势所在,本篇博客也主要实现了最小平方误差法。
分段线性判别法
在此我们简要介绍分段线性判别法的思想。
- 分段线性判别法首先对已知类进行划分,将其分为父类和子类。
- 我们需要训练出同一父类的子类之间的判别函数。
- 对于某个待测模式,我们先用同一父类的各子类判别函数进行计算,并用效果最好的子类作为父类的判别函数。
- 之后,我们再通过父类的判别函数,将待测模式划分到某一父类。
方案设计
实验要求
1、请实现以下功能之一
1)编写程序,使用感知器算法、梯度法和最小平方误差法中的一种算法,
训练线性判别函数并进行数据分类,分类时采用多分类情况的两种。2)编写程序,使用感知器算法、梯度法和最小平方误差法中的两种算法,
训练线性判别函数并进行数据分类,分类时采用多分类情况的一种。样本数据请自拟并不能少于30个数据样本,类别不少于5类。
其中部分样本用于训练判别函数,部分用于验证,并计算出正确率
2、请编写程序实现分段线性判别法进行数据分类。
样本数据请自拟并不能少于30个数据样本,类别不少于3类,每类至少有2个以上子类。
设计步骤
实验的实现共分为以下几个过程:
数据集的获取。在本实验中,随机生成了模式数据集。训练样本共分为6类,每类有8个模式;测试样本共分为6类,每类2个模式。
最小平方误差法(Least Mean Square Error, LMSE)算法的实现:
最小平方误差算法(Least Mean Square Error, LMSE) (1)输入参数:规范化增广样本矩阵x (2)求X的伪逆矩阵 x# = (xT x)-1 xT (3)设置初值c和B(1) (4)计算e(k),并进行可分性判别 (5)计算W(k+1)、B(k+1)
采用
i_non_i
分类法训练判别函数,并评测识别正确率采用
i_j
分类法训练判别函数,并评测识别正确率实现分段线性判别法,训练子类的判别函数,并评测识别正确率
实验环境
操作系统:MacOS X
开发语言:Python 2.7
具体实现
数据集生成
如上图所示,在[0, 50]
的范围内随机生成模式集,并具体划分在0, 1, 2, 3, 4, 5
这6个类中。
具体实现如下:
# encoding:utf-8
# 生成数据集
import numpy as np
# 生成数据集
def gen_data_set(sample_size, class_size):
x_k = []
y_k = []
for k in range(6):
x = []
y = []
if k == 0:
for i in range(sample_size):
m = np.random.random() * 20
n = np.random.random() * m
x.append(m)
y.append(n)
if k == 1:
for i in range(sample_size):
m = np.random.random() * 30 + 20
if m < 25:
n = np.random.random() * m
else:
n = np.random.random() * ((-1) * m + 50)
x.append(m)
y.append(n)
if k == 2:
for i in range(sample_size):
m = np.random.random() * 25 + 25
n = np.random.random() * (2 * m - 50) + ((-1) * m + 50)
x.append(m)
y.append(n)
if k == 3:
for i in range(sample_size):
m = np.random.random() * 30 + 20
if m < 25:
n = np.random.random() * m + ((-1) * m + 50)
else:
n = np.random.random() * (50 - m) + m
x.append(m)
y.append(n)
if k == 4:
for i in range(sample_size):
m = np.random.random() * 20
n = np.random.random() * m + ((-1) * m + 50)
x.append(m)
y.append(n)
if k == 5:
for i in range(sample_size):
m = np.random.random() * 20
n = np.random.random() * ((-2) * m + 50) + m
x.append(m)
y.append(n)
# 记录坐标
# for i in range(sample_size):
# print str(k) + "," + str(x[i]) + "," + str(y[i])
x_k.append(x)
y_k.append(y)
return x_k, y_k
# 生成数据集并导入文件
def gen_data_to_file(train_file, test_file, class_size=6, train_set_size=10, test_set_size=5):
train_sample_size = 8
test_sample_size = 2
train_x_k, train_y_k = gen_data_set(train_sample_size, class_size)
test_x_k, test_y_k = gen_data_set(test_sample_size, class_size)
# 数据集导入文件
with open(train_file, 'w') as f:
for i in range(class_size):
for j in range(train_sample_size):
f.write(str(i) + "," + str(train_x_k[i][j]) + "," + str(train_y_k[i][j]) + "\n")
with open(test_file, 'w') as f:
for i in range(class_size):
for j in range(test_sample_size):
f.write(str(i) + "," + str(test_x_k[i][j]) + "," + str(test_y_k[i][j]) + "\n")
并将生成的数据集导入文件。
最小平方误差法
# encoding:utf-8
import numpy as np
from numpy.linalg import inv
# 最小平方误差算法(Least Mean Square Error, LMSE)
# (1)输入参数:规范化增广样本矩阵x
# (2)求X的伪逆矩阵 x# = (xT x)-1 xT
# (3)设置初值c和B(1)
# (4)计算e(k),并进行可分性判别
# (5)计算W(k+1)、B(k+1)
def least_mean_square_error(data, b_1=0.1, c=1, k_n=100000):
# x:规范化增广样本"矩阵"
x = np.array(data, dtype=np.float64)
x_sharp = inv(x.T.dot(x)).dot(x.T)
row, col = x.shape
# print x
k = 1
while k < k_n:
if k % 10000 == 0:
print "第" + str(k) + "次迭代..."
if k == 1:
# 设置初值B(1)
b_k = (np.zeros(row) + b_1).T
# b_k = x[:, 0]
w_k = x_sharp.dot(b_k)
else:
b_k = b(k + 1, b_k, c, e_k)
w_k = w(k + 1, w_k, c, x_sharp, e_k)
# w_k = w(k+1, x_sharp, b_k) # 方法二
e_k = e(k + 1, x, w_k, b_k)
if (e_k == 0).all():
print "第" + str(k) +