机器学习算法是从数据中产生模型,也就是进行学习的算法,我们提供经验给算法,它可以根据经验产生模型, 面对新的情况时,模型就会给我们提供判断(预测)结果。从数据中学得模型的过程称为学习或者训练,在训练过程中使用的数据称为训练数据,每个样本称为训练样本,训练样本所组成的集合称为训练集。
如果希望获取一个模型,除了数据还有给样本贴上对应的标签,例如,((个子高、腿长、体重轻),好苗子)就是拥有标签的样本,称为样例。
学得模型后,为了测试模型得效果还有对其进行测试,被测试得样本就是测试样本,输入测试样本时,不提供标签(目标类别),而是由模型决定样本得标签(属于那个类),比较测试样本预测得标签与实际样本标签之间的差别,就可以计算出模型的精确度。。。这就是机器学习嘛。。
大多数机器学习算法都来源于生活实践,K 近邻算法就是最简单的机器学习算法之一。。。主要用于将对象划分到已知类中,例如,教练选运动员,选择个子高,腿长,轻等特征的候选人,这些人的特征核运动员的特征很相近。。
K近邻算法本质就是将指定对象根据已知特征值分类,例如,通过年龄特征判断老人和孩子。在实际场景中,会有多个特征维度。例如,为一段视频分类,判断时乒乓球还是足球比赛。
首先定义两个特征,一个是 挥手动作,一个是踢脚动作 ,统计特定时间内这两个动作的次数,发现:乒乓球中挥手动作远多于踢脚动作,足球中,踢脚远多于挥手。
每个点对应一个样本,可以发现数据点呈现聚集特征:
此时,有一个视频,挥手2000,踢脚100次,绘制在上图中,可以发现测试视频的位置离乒乓球最近,就可以判断这个视频是乒乓球比赛视频。
这个例子中非黑即白,实际的分类数据中参数是非常多的,为了提高算法可靠性,会取 k 个近邻点,这K 个点中属于哪一类的较多,就将当前待识别点分为哪一类,为了方便 k 值通常取奇数,这是为了防止两边一样的情况。
例如,两个双胞胎A 和 B ,要判断一个图形T 上的人是哪个,采用K 近邻算法实现的具体步骤:
以上就是K 近邻算法的基本思想,很简单对不对。。。让计算机还要算啊算,人一眼基本就看出来了,,果然还是人类比较厉害些。。
计算机的 感觉 是通过逻辑计算和数值计算实现的,所以我们要对计算机要处理的对象进行数值化处理,将其量化为具体的值以便后续处理。典型的就是取某几个固定的特征,然后将这些特征量化。例如,人脸识别中,根据人脸部器官的的形状描述以及它们之间的距离特征来获取有助于分类的特征数据,这些特征数据可能包括特征点的距离,曲率和角度等。这样就可以将人脸图像表示为类似 [1,2,3,4] 这样的数据形式了。
K 近邻算法在获取各个样本的特征值后,计算待识别样本的特征值与各个已知分类的样本特征值之间的距离,然后找出 k 个最邻近的样本,根据这些最邻近样本中占比最高的样本所属的分类,来确定识别样本的分类。
对于简单的情况,直接计算与特征值的距离(差值)即可,例如,,警察通过技术手段获知嫌疑人的身高为 180 cm,缺一根手指;受害人身高为 173cm,十指健全。此时,前来投案的甲、乙二人都宣称自己是受害人。
这时有多个参数,将这些参数构成列表(数组)进行综合判断,因此嫌疑人的特征值 (180,9)受害人(175,10)。然后可以对 甲,乙两人判断,
当然我们知道上述过程是错误的,,因为身高,手指数量应该有不同的权重,所以计算与特征值的距离时要充分考虑不同参数之间的权重。通常我们要对参数进行处理,让所有参数具有相等的权重。
一般来说,进行归一化就行,就是使用特征值除以所有特征值中的最大值(或最大值与最小值之差),例如上例中,归一化特征 =(身高/最高身高 180,手指数量/10)
经过归一化,嫌疑人的特征值为(180/180, 9/10) = (1, 0.9),受害人的特征值为(173/180, 10/10) = (0.96, 1)
甲:甲的特征值为(175/180, 9/10)=(0.97, 0.9)。甲与嫌疑人特征值的距离= (1-0.97) + (0.9-0.9) = 0.03。甲与受害人特征值的距离= (0.97-0.96) + (1-0.9) = 0.11。同理乙,判定为受害人。
上例中,计算距离就是单纯的相减,也亏没有出现负数。。。通常我们计算绝对值的和 = |185-170|+|75-80| = 15+5 = 20。这种用绝对值之和表示的距离称为 曼哈顿距离。。讲究
当然我们计算平方和在开平方更好,,就是我们广泛使用的欧式距离。
以手写数字识别为例进行介绍多特征维度的情况。
为了偷懒,将图像集减少到两个,将特征值简化为四个 (4个子块),然后计算距离
opencv 中提供函数 cv2. KNearest() 实现K 近邻算法,
num=100 # 共有特征值的数量
row=50 # 特征图像的行数
col=50 # 特征图像的列数
a=np.zeros((num,row,col)) # a 用来存储所有特征的值
n=0 # n 用来存储当前图像的编号。
for i in range(0,10):
for j in range(1,11):
a[n,:,:]=cv2.imread('num\\'+str(i)+'_'+str(j)+'.bmp',0)
n=n+1
feature=np.zeros((num,round(row/10),round(col/10))) # feature 存储所有样本的特征值
for ni in range(0,num):
for nr in range(0,row):
for nc in range(0,col):
if a[ni,nr,nc]==255: # 遍历,白色值就加1.特征值是 5 X 5 的
feature[ni,int(nr/10),int(nc/10)]+=1
f=feature #简化变量名称
d=np.zeros(100)
for i in range(0,100):
d[i]=np.sum((of-f[i,:,:])*(of-f[i,:,:]))
数组d 中的索引对应各特征图像的编号。例如 d[mn] 表示待识别图像与 数字 m 的第 n 各特征图像的距离
d=d.tolist()
temp=[]
Inf = max(d)
#print(Inf)
k=7
for i in range(k):
temp.append(d.index(min(d))) # 注意得到的是索引,,
d[d.index(min(d))]=Inf
s='num\\' # 图像所在的路径
num=100 # 共有特征值的数量
row=50 # 特征图像的行数
col=50 # 特征图像的列数
a=np.zeros((num,row,col)) # a 用来存储所有特征的值
n=0 # n 用来存储当前图像的编号。
for i in range(0,10):
for j in range(1,11):
a[n,:,:]=cv2.imread('num\\'+str(i)+'_'+str(j)+'.bmp',0)
n=n+1
feature=np.zeros((num,round(row/10),round(col/10))) # feature 存储所有样本的特征值
for ni in range(0,num):
for nr in range(0,row):
for nc in range(0,col):
if a[ni,nr,nc]==255:
feature[ni,int(nr/10),int(nc/10)]+=1
f=feature #简化变量名称
of=cv2.imread('test3.bmp',0) # 加载测试图像
o=np.zeros((round(row/10),round(col/10)))
for nr in range(0,row):
for nc in range(0,col):
if of[nr,nc]==255:
o[int(nr/10),int(nc/10)]+=1
print(o.shape)
d=np.zeros(100)
for i in range(0,100):
d[i]=np.sqrt(np.sum((o-f[i,:,:])*(o-f[i,:,:])))
d=d.tolist()
temp=[]
Inf = max(d)
k=7
for i in range(k):
temp.append(d.index(min(d)))
d[d.index(min(d))]=Inf
temp=[int(i/10) for i in temp]
r=np.zeros(10)
for i in temp:
r[int(i)]+=1
print('当前的数字可能为:'+str(np.argmax(r)))
这个没有用到cv2. KNearest() 函数,虽然麻烦但可以很好的帮助理解过程,,不过有一说一识别的正确率是真的低。。。
我们不用编写那么麻烦的代码,只要用自带的模块函数就行。
上述两组数据中,位于左下角的一组数据,其 x、y 坐标值都在(0, 30)范围内。位于右上角的数据,其 x、y 坐标值都在(70, 100)范围内。
根据上述分析,创建两组数据,每组包含 20 对随机数(20 个随机数据点):
rand1 = np.random.randint(0, 30, (20, 2)).astype(np.float32)
rand2 = np.random.randint(70, 100, (20, 2)).astype(np.float32)
# 将 rand1 和 rand2 拼接为训练数据
trainData = np.vstack((rand1, rand2))
# 数据标签,共两类:0 和 1
# r1Label 对应着 rand1 的标签,为类型 0
r1Label=np.zeros((20,1)).astype(np.float32)
# r2Label 对应着 rand2 的标签,为类型 1
r2Label=np.ones((20,1)).astype(np.float32)
tdLable = np.vstack((r1Label, r2Label))
# 使用绿色标注类型 0
g = trainData[tdLable.ravel() == 0] # 转换为一维数组
plt.scatter(g[:,0], g[:,1], 80, 'g', 'o')
# 使用蓝色标注类型 1
b = trainData[tdLable.ravel() == 1]
plt.scatter(b[:,0], b[:,1], 80, 'b', 's')
# test 为用于测试的随机数,该数在 0 到 100 之间
test = np.random.randint(0, 100, (1, 2)).astype(np.float32)
plt.scatter(test[:,0], test[:,1], 80, 'r', '*')
# 调用 OpenCV 内的 K 近邻模块,并进行训练
knn = cv2.ml.KNearest_create()
knn.train(trainData, cv2.ml.ROW_SAMPLE, tdLable) # ROW_SAMPLE每个训练样本是一行样本
# 使用 K 近邻算法分类,查找邻居并预测 测试数 的响应。
ret, results, neighbours, dist = knn.findNearest(test, 5) # 5 就是 K
# 显示处理结果
print("当前随机数可以判定为类型:", results)
print("距离当前点最近的 5 个邻居是:", neighbours)
print("5 个最近邻居的距离: ", dist)
# 可以观察一下显示,对比上述输出
plt.show()
num=100
row=50
col=50
a=np.zeros((num,row,col))
n=0
for i in range(0,10):
for j in range(1,11):
a[n,:,:]=cv2.imread('num\\'+str(i)+'_'+str(j)+'.bmp',0)
n=n+1
feature=np.zeros((num,round(row/5),round(col/5)))
for ni in range(0,num):
for nr in range(0,row):
for nc in range(0,col):
if a[ni,nr,nc]==255:
feature[ni,int(nr/5),int(nc/5)]+=1
f=feature
# 将 feature 处理为单行形式
train = feature[:,:].reshape(-1,round(row/5)*round(col/5)).astype(np.float32)
# 贴标签,要注意,是 range(0,100)而非 range(0,101)
trainLabels = [int(i/10) for i in range(0,100)]
trainLabels=np.asarray(trainLabels)
#print(*trainLabels) # 打印测试看看标签值
##读取图像值
o=cv2.imread('test.bmp',0) # 读取待识别图像
of=np.zeros((round(row/5),round(col/5))) # 用来存储待识别图像的特征值
for nr in range(0,row):
for nc in range(0,col):
if o[nr,nc]==255:
of[int(nr/5),int(nc/5)]+=1
test=of.reshape(-1,round(row/5)*round(col/5)).astype(np.float32)
# 调用函数识别图像
knn=cv2.ml.KNearest_create()
knn.train(train,cv2.ml.ROW_SAMPLE, trainLabels)
ret,result,neighbours,dist = knn.findNearest(test,k=15)
print("当前随机数可以判定为类型:", result)
print("距离当前点最近的邻居是:", neighbours)
print("最近邻居的距离: ", dist)
调一下k值,分块的大小,三个测试图总算成功了一个。。还是训练集太小了吧。。。Mnist数据集不会用…我的路还很长啊.