基于matlab实现,采用knn算法

导入数据

可以使用 readtable 函数从电子表格或文本文件导入表格数据,并将结果以表的形式存储。

letter = readtable("M.txt");
plot(letter.X,letter.Y)
axis equal

默认轴范围会使字母的纵横比失真。您可以使用 axis 命令强制轴保持数据的纵横比。使用命令 axis equal 修正绘图的纵横比。
若不是正方形屏幕显示:将表 letter 的变量 X 中的值乘以纵横比 1.5。将结果重新赋给 X,使 letter 包含修正后的数据

处理数据

时间归一化:对表 letter 的 Time 变量进行偏移以从 0 开始,通过从所有元素中减去第一个值并将结果除以 1000 以转换为秒。将结果重新赋给 Time,使 letter 包含调整后的数据。

lette.time = (letter.time-letter.time(1))/1000

计算特征

这些字母的哪些特点可用于将 J 与 M 或 V 区分开来?我们的目标不是使用原始信号,而是计算出可将整个信号提取为简单、有用的信息单元的值,这些信息单元称为特征。
对于字母 J 和 M,一个简单的特征可能是纵横比(字母的高度相对于宽度的比例)。J 可能高而窄,M 可能更接近正方形。
与 J 和 M 相比,书写 V 的速度更快,因此信号的持续时间也可能是一个区分特征。

特征

持续时间:通过提取 letter.Time 的最后一个值并将结果存储在名为 dur 的变量中,来计算书写该字母所需的时间。
字母的纵横比:使用 range 函数通过将 letter.Y 的值极差除以 letter.X 的值极差来计算字母的纵横比。将结果赋给名为 aratio 的变量。

dur = letter.Time(end)
aratio = range(letter.Y)/range(letter.X)

range 函数返回数组中值的极差。即 range(x) 等效于 max(x) - min(x)

猜测结果:
字母M的纵横比更接近于1,持续时间较长
字母J、V的纵横比较大,持续时间较短,V更短

提取特征

尚不清楚这些特征是否足以区分数据集中的三个字母(J、M 和 V):
1.使用 scatter 函数绘制提取的特征,水平轴上为纵横比,垂直轴上为持续时间。但没有着色,所以区分不了特征是否足够

scatter(features.AspectRatio,features.Duration)


2.gscatter 函数生成一个分组散点图 - 即根据分组变量对点进行着色的散点图。gscatter(x,y,g),使用 gscatter 函数创建散点图,根据字母(g)进行着色,字母存储在表 features 的 Character 变量中。

gscatter(features.AspectRatio,features.Duration,features.Character)

建立模型

没有一种绝对“正确”的方法将平面划分为 J、M 和 V 类。不同分类算法会产生不同划分。

一个简单模型是将一个观测值与最邻近的已知示例划分为相同的类。这称为 k 最近邻 (kNN) 模型。您可以通过将数据表传递给 fitcknn 函数来拟合 kNN 模型。

mdl = fitcknn(data,"ResponseVariable");
第二个输入是表中响应变量的名称(即模型要预测的类)。输出是包含拟合模型的变量。

knnmodel = fitcknn(features,"Character")

使用 fitcknn 函数对 features 中存储的数据进行模型拟合。已知的类存储在名为 Character 的变量中。将生成的模型存储在名为 knnmodel 的变量中。

预测

根据数据建立模型后,您可以使用它对新观测值进行分类。这只需计算新观测值的特征并确定它们位于预测变量空间的哪个区域。
predict 函数用于确定新观测值的预测类。
predClass = predict(model,newdata)
输入是经过训练的模型和新观测值。输出是 newdata 中每个观测值的预测类的分类数组。

通常情况下,新观测值以表的形式出现,其预测变量与用于训练模型的预测变量相同。然而,在本例中,模型使用了两个数值特征(纵横比和持续时间),因此观测值也能以具有两列的数值数组形式出现。
使用 predict 函数和经过训练的模型 knnmodel 对纵横比为 4、持续时间为 1.2 的一个字母进行分类。将预测存储在名为 predicted 的变量中。以二元素行向量形式提供字母特征。

newdata = [4,1.2]
predicted = predict(knnmodel,newdata)

预测结果为 V

k值选择

默认情况下,fitcknn 用 k= 1 对 kNN 模型进行拟合。也就是说,该模型仅使用最邻近的已知示例对给定观测值进行分类。因而模型对训练数据中的任何离群值都敏感,例如上图中突出显示的离群值。离群值附近的新观测值可能被误分类。
可以通过增大 k 的值(即,使用若干邻点的最常见类),使模型对训练数据中的特定观测值不太敏感。这通常会提高模型的总体性能。然而,模型在任何特定测试集上的性能取决于该测试集中的具体观测值。

通过在调用 fitcknn 时设置 "NumNeighbors" 属性,可以在 kNN 模型中指定 k 的值。
mdl = fitcknn(data,"ResponseVariable",..."NumNeighbors",10);

knnmodel = fitcknn(features,"Character",NumNeighbors=5)
newdata = [4,1.2]
predicted = predict(knnmodel,newdata)

重新生成模型
得到预测结果:J 。发现更改邻点的数量改变了对要测试的观测值的预测。

通过将新数据的矩阵或表传递给 predict 函数,可以获得对多个观测值的预测。

  1. 输入原始数据测试❌
    predictions = predict(knnmodel,features)
    可以串联分类数组来并排查看预测和真实类。[predictions,features.Character]
  2. 输入测试集测试
    predictions= predict(knnmodel,testdata)

评估模型

测试观测值的已知所属类存储在表 testdata 的变量 Character 中。

iscorrect= (predictions==testdata.Character)
accuracy = sum(iscorrect)/numel(iscorrect)

将正确预测数除以预测总数,计算出正确预测的比例。将结果存储在名为 accuracy 的变量中。可以使用 sum 函数确定正确预测数,使用 numel 函数来确定预测总数。

但常用的评估模型指标是误分类率(不正确预测的比例),而不是准确度(正确预测的比例)。

iscorrect= predictions~=testdata.Character
misclassrate = sum(iscorrect)/numel(iscorrect)

准确度和误分类率使用单个值来描述模型的整体性能,但进一步细分模型混淆了哪些类可能会很有用。
混淆矩阵显示真实类和预测类的每个组合的观测值数目。混淆矩阵通常根据元素的值给元素着色来实现可视化。通常,对角线元素(正确分类)用同一种颜色着色,其他元素(不正确分类)用另一种颜色着色。可以使用 confusionchart 函数来可视化混淆矩阵。
confusionchart(ytrue,ypred);其中 ytrue 是已知类的向量,ypred 是预测类的向量。

confusionchart(predictions,testdata.Character)

误分类观察

调查经常混淆的类的特征会很有用。尝试使用不正确分类的逻辑数组对 testdata 和 predictions 进行索引,以获得被误分类的观测值的数据。

data1 = testdata((iswrong ==1),:)
pre= predictions((iswrong ==1),:)

发现误判在重叠区

以同样流程尝试13个字母的分类

%导入数据
load featuredata13letters.mat
%观测值绘图
gscatter(features.AspectRatio,features.Duration,features.Character)
%建立模型
knnmodel= fitcknn(features,"Character",NumNeighbors=5)
predictions= predict(knnmodel,testdata)
%评估模型
iswrong= predictions~=testdata.Character
misclass= sum(iswrong)/numel(iswrong)
confusionchart(testdata.Character,predictions)
···
![](https://monsterju.github.io/post-images/1680353859983.png)

只有两个特征模型(用于区分三个特定字母)的模型无法泛化到许多字母。