相关知识
KNN
k临近算法K nearest neighbor,用于分类和回归。主要思想为,训练一个样本和所有训练样本之间的举例,找到和这个样本最接近的k个样本,根据这k个样本的类别进行投票,票数最高的类就是分类的结果。模型效果主要受三个要素影响:
- k值:该样本的分类是由它k个邻居决定的。k越小,越容易被噪声影响。k越大,类别划分越不明显。
- 距离度量:反映特征空间中两个样本间的相似度,距离越小,越相似。如Lp距离、曼哈顿距离、海明距离等。
- 分类决策规则:通常是多数表决,或者基于距离加权的多数表决。
KNN分类
预测算法(分类)的流程如下:
(1)在训练样本集中找出距离待测样本x_test最近的k个样本,并保存至集合N中;
(2)统计集合N中每一类样本的个数 C i , i = 1 , 2 , 3 , . . . , c C_{i}, i=1,2,3,...,c Ci,i=1,2,3,...,c;
(3)最终的分类结果为argmax C i C_{i} Ci (最大的对应的 C i C_{i} Ci)那个类。
这里也可以将每个样本设为不同的权重,也就是带权重的KNN。
KNN回归
假设离测试样本最近的k个训练样本的标签值为 y i y_{i} yi,则对样本的回归预测输出值为:
y ^ = ( ∑ i = 1 n y i ) / k \hat y = (\sum_{i=1}^{n}{y_{i}})/k y^=(i=1∑nyi)/k
即为所有邻居的标签均值。
带样本权重的回归预测函数为:
y ^ = ( ∑ i = 1 n w i y i ) / k \hat y = (\sum_{i=1}^{n}{w_{i}y_{i}})/k y^=(i=1∑nwiyi)/k
其中 w i w_{i} wi为第个 i i i样本的权重。
欧氏距离
最常用的是欧几里得距离。用于计算两个向量的距离。
d
(
x
,
y
)
=
∑
i
=
1
n
(
x
i
−
y
i
)
2
d(x,y) = \sqrt{\sum_{i=1}^{n}{(x_{i}-y_{i})^2}}
d(x,y)=i=1∑n(xi−yi)2
使用该方式时应先将特征向量的每个分量归一化,减少因特征尺寸范围不同带来的问题。
实验
数据准备
本章使用Wine数据集,是模式识别中的经典数据集。数据内容为三种葡萄酒的化学分析结果,包含了如下13种成分的量
- Alcohol,酒精
- Malic acid,苹果酸
- Ash,灰
- Alcalinity of ash,灰的碱度
- Magnesium,镁
- Total phenols,总酚
- Flavanoids,类黄酮
- Nonflavanoid phenols,非黄酮酚
- Proanthocyanins,原花青素
- Color intensity,色彩强度
- Hue,色调
- OD280/OD315 of diluted wines,稀释酒的OD280/OD315
- Proline,脯氨酸
首先查看一下数据
with open('wine.data') as csv_file:
data = list(csv.reader(csv_file, delimiter=','))
print(data[56:62]+data[130:133])
[[‘1’, ‘14.22’, ‘1.7’, ‘2.3’, ‘16.3’, ‘118’, ‘3.2’, ‘3’, ‘.26’, ‘2.03’, ‘6.38’, ‘.94’, ‘3.31’, ‘970’], [‘1’, ‘13.29’, ‘1.97’, ‘2.68’, ‘16.8’, ‘102’, ‘3’, ‘3.23’, ‘.31’, ‘1.66’, ‘6’, ‘1.07’, ‘2.84’, ‘1270’], [‘1’, ‘13.72’, ‘1.43’, ‘2.5’, ‘16.7’, ‘108’, ‘3.4’, ‘3.67’, ‘.19’, ‘2.04’, ‘6.8’, ‘.89’, ‘2.87’, ‘1285’], [‘2’, ‘12.37’, ‘.94’, ‘1.36’, ‘10.6’, ‘88’, ‘1.98’, ‘.57’, ‘.28’, ‘.42’, ‘1.95’, ‘1.05’, ‘1.82’, ‘520’], [‘2’, ‘12.33’, ‘1.1’, ‘2.28’, ‘16’, ‘101’, ‘2.05’, ‘1.09’, ‘.63’, ‘.41’, ‘3.27’, ‘1.25’, ‘1.67’, ‘680’], [‘2’, ‘12.64’, ‘1.36’, ‘2.02’, ‘16.8’, ‘100’, ‘2.02’, ‘1.41’, ‘.53’, ‘.62’, ‘5.75’, ‘.98’, ‘1.59’, ‘450’], [‘3’, ‘12.86’, ‘1.35’, ‘2.32’, ‘18’, ‘122’, ‘1.51’, ‘1.25’, ‘.21’, ‘.94’, ‘4.1’, ‘.76’, ‘1.29’, ‘630’], [‘3’, ‘12.88’, ‘2.99’, ‘2.4’, ‘20’, ‘104’, ‘1.3’, ‘1.22’, ‘.24’, ‘.83’, ‘5.4’, ‘.74’, ‘1.42’, ‘530’], [‘3’, ‘12.81’, ‘2.31’, ‘2.4’, ‘24’, ‘98’, ‘1.15’, ‘1.09’, ‘.27’, ‘.83’, ‘5.7’, ‘.66’, ‘1.36’, ‘560’]]
将13个属性作为自变量,类别作为因变量
X = np.array([[float(x) for x in s[1:]] for s in data[:178]], np.float32)
Y = np.array([s[0] for s in data[:178]], np.int32)
划分训练集与验证集
train_idx = np.random.choice(178, 128, replace=False)
test_idx = np.array(list(set(range(178)) - set(train_idx)))
X_train, Y_train = X[train_idx], Y[train_idx]
X_test, Y_test = X[test_idx], Y[test_idx]
模型构建
class KnnNet(nn.Cell):
def __init__(self, k):
super(KnnNet, self).__init__()
self.k = k
def construct(self, x, X_train):
#平铺输入x以匹配X_train中的样本数
x_tile = ops.tile(x, (128, 1))
square_diff = ops.square(x_tile - X_train)
square_dist = ops.sum(square_diff, 1)
dist = ops.sqrt(square_dist)
#-dist表示值越大,样本就越接近
values, indices = ops.topk(-dist, self.k)
return indices
def knn(knn_net, x, X_train, Y_train):
x, X_train = ms.Tensor(x), ms.Tensor(X_train)
indices = knn_net(x, X_train)
topk_cls = [0]*len(indices.asnumpy())
for idx in indices.asnumpy():
topk_cls[Y_train[idx]] += 1
cls = np.argmax(topk_cls)
return cls
模型预测
设k=5,得到最终的精度约为80%,证明分类有效。
acc = 0
knn_net = KnnNet(5)
for x, y in zip(X_test, Y_test):
pred = knn(knn_net, x, X_train, Y_train)
acc += (pred == y)
print('Validation accuracy is %f' % (acc/len(Y_test)))
Validation accuracy is 0.720000
总结
本章使用KNN完成红酒分类任务,影响knn效果的主要是k值选择,距离算法与分类决策规则(多数投票或带权重)。最终得到十三个属性和红酒分类效果有关,即可有效完成品类预测。但由于KNN是非参数化的机器学习算法,没有显式的模型参数,因此没办法直接通过KNN确认每一个特征与因变量的具体关系或相关性。