本文主要对KNN的分类算法进行验证,以及如何编写KNN,以及KNN的应用。
KNN主要运用于数据分类,本文通过某电站的仿真数据进行验证分析。
官方KNN的调用:
from sklearn.neighbors import KNeighborsClassifier
# 3表示最近的3个点作为分类标准
knn = KNeighborsClassifier(3)
# x表示训练数据, y表示训练数据标签
knn.fit(x, y)
# X表示测试数据
result = knn.predict(X)
官方KNN验证,本文通过电厂仿真数据验证。数据分别是100%、90%、80%工况下的平稳数据。
# 制作数据
data1 = pd.read_excel("100.xlsx")
data2 = pd.read_excel("90.xlsx")
data3 = pd.read_excel("80.xlsx")
d1 = np.array(data1.values)[:, 1:]
d2 = np.array(data2.values)[:, 1:]
d3 = np.array(data3.values)[:, 1:]
# 取前100个数据作为训练数据
x = list(d1[:100, :])
x.extend(list(d2[:100, :]))
x.extend(list(d3[:100, :]))
# 制作数据标签
y = []
for i in range(100):
y.append(1)
for i in range(100):
y.append(2)
for i in range(100):
y.append(3)
结果:
# 取100%工况下100后的所有数据测试
result = knn.predict(d1[100:, :])
# 取90%工况下100后的所有数据测试
result = knn.predict(d2[100:, :])
# 取80%工况下100后的所有数据测试
result = knn.predict(d3[100:, :])
KNN自己编写:
class KNeighborsClassifier:
def __init__(self, k, d="e", p=3):
"""
:param k: 样本的前几个最近点
:param d: 距离的计算方式,"e" 欧式距离 "ma"曼哈顿距离 "c"切比雪夫距离 "mi"闵氏距离
:param p: "mi"距离下p的值
"""
self.k = k
self.d = d
self.p = p
def EuchdeanDistance(self, sub_X, sub_x):
# 欧氏距离
return np.sqrt(np.dot(sub_x - sub_X, sub_x - sub_X))
def ManhattanDistance(self, sub_X, sub_x):
# 曼哈顿距离
return np.sum(np.abs(sub_X - sub_x))
def ChebyshevDistance(self, sub_X, sub_x):
# 切比雪夫距离
return max(np.abs(sub_x - sub_X))
def MinkowskiDistance(self, sub_X, sub_x, p):
# 闵氏距离
return np.power(np.sum(np.power(sub_x - sub_X, p)), 1/p)
def fit(self, x, y):
"""
:param x: 训练样本
:param y: 训练样本标签
:return: 无返回值
"""
# 检查数据格式是否正确
self.x = np.array(x)
self.y = np.array(y)
if self.x.shape[0] != self.y.shape[0]:
raise ValueError("x 与 y维度不同!需要相同的维度")
if self.x.ndim != 2:
raise ValueError("x必须是一个二维数组")
def predict(self, X):
"""
:param X: 测试样本
:return result: 预测的测试样本标签
"""
self.X = np.array(X)
# 判断测试样本X与训练样本x的维度是否相同
if self.X.shape[1] != self.x.shape[1]:
ValueError("x与X维度不同!需要相同的维度")
# 记录X中每一个样本所对应的标签
result = []
# 记录最小距离
mini_dis = []
# 计算X中每个样本与x中所有样本的距离
for i in self.X:
# 记录每一个X样本对应所有x样本的距离
distance = []
# 记录前self.k个距离最小样本对应的标签
index = []
for j in self.x:
if self.d == "e":
distance.append(self.EuchdeanDistance(i, j))
if self.d == "ma":
distance.append(self.ManhattanDistance(i, j))
if self.d == "c":
distance.append(self.ChebyshevDistance(i, j))
if self.d == "mi":
distance.append(self.MinkowskiDistance(i, j, self.p))
# 将距离进行排序返回对应的索引,并选取前k个最短距离
sort = np.argsort(distance)[:self.k]
mini_dis.append(distance[sort[0]])
for j in sort:
index.append(self.y[j])
# 计算重复元素的个数
counter = Counter(index)
# 查找最大元素对应的标签
label = max(counter)
result.append(label)
return result, mini_dis
自己编写程序验证:
# 100%工况,曼哈顿距离下验证
knn = KNeighborsClassifier(3,d="ma")
knn.fit(x, y)
# 只取分类结果,_表示舍弃
result, _ = knn.predict(d1[100:, :])
# 90%工况,曼哈顿距离下验证
knn = KNeighborsClassifier(3,d="ma")
knn.fit(x, y)
# 只取分类结果,_表示舍弃
result, _ = knn.predict(d2[100:, :])
# 80%工况,曼哈顿距离下验证
knn = KNeighborsClassifier(3,d="ma")
knn.fit(x, y)
# 只取分类结果,_表示舍弃
result, _ = knn.predict(d3[100:, :])
从上面可以看出自己编写的KNN可以进行正确的分类。(但运行速度可能较慢,由于官方KNN由kd树,并行加速等,等学会再更)。
KNN进行异常或故障数据判断,KNN不仅可以用来分类,还能进行异常或故障数据判断,通过测试数据每个数据点与训练数据所有样本最小距离来判断,正常情况下的数据点最小距离较小,当发生异常时异常样本与正常样本距离会变大,可以通过此方法来判断异常数据。
验证:
data1 = pd.read_excel("100.xlsx")
data4 = pd.read_excel("100%工况下故障数据.xlsx")
d1 = np.array(data1.values)[:, 1:]
d4 = np.array(data4.values)[:, 1:]
x = list(d1[:100, :])
y = []
for i in range(100):
y.append(1)
knn = KNeighborsClassifier(3, d="e")
knn.fit(x, y)
_, mini_dis = knn.predict(d4)
print(mini_dis)
plt.plot(mini_dis)
plt.xlabel("时间(s)")
plt.ylabel("距离")
plt.show()
数据在400s左右发生异常。
由上图可见正常数据与异常数据可以通过距离来进行区别。