# 支持向量机(SVM)之银杏树识别
小组第二次机器学习作业,使用svm分类器进行银杏树识别。
## 项目快速开始
### 一、项目架构
```
----SVM
|--data_handle
|--load_dataset.py (加载数据集,对数据进行一些预处理)
|--data_august.py (数据增强,正在开发中)
|--ginkgo (银杏树数据集,300张银杏树,200张其他树)
|--train
|--test
|--svm-smo
|--smo.py (smo算法,求解svm最优解)
|--run.py (运行项目)
|--requirements.txt
```
### 二、环境安装
用pycharm为项目venv后,安装所需库
```
pip install -r requirements.txt
pip install scikit-learn (若需要sklearn)
```
### 三、运行demo
```
运行svm-smo/run.py文件
```
#### 运行流程
```
run.py --------> load_dataset.py ------------>smo.py
(加载数据) 进行smop计算,进行迭代
```
#### 运行结果
本小组以银杏树数据集为例,进行银杏树识别,运行结果如下
![image-20220524163125914](https://raw.githubusercontent.com/mengxiangqiqin/IMAGES/main/imgesimage-20220524163125914.png)
------
## 项目进阶
**通过项目进阶你可以大体了解svm支持向量机的原理过程,理解项目大体架构,了解数据预处理,项目运行、项目调优。**
### 一、数据集预处理
数据集处理在data_handle文件夹中
#### load_dataset.py
- 制作数据集图片对应的标签,正标签为1,负标签为-1
- 数据处理技巧:灰度图处理、二值化处理、图片大小resize等等,要给根据自己的数据集特点进行调整。
- **注意,最后必须把数据集图片进行flatten,最后数据集格式必须为**
```
data:
[[ ]
[ ]
[ ]
...... ] data为二维列表,每一行是每一张图片拉伸后的数据,行数为数据量
label:
[1, 1, -1, 1, -1, 1, ,......] label存有正负标签的列表
```
#### 数据增强 (data_august.py)
数据增强为了解决数据集图像过少或者质量不高的情况,目前正在编写中......
### 二、smo算法
**求解svm问题的smo算法在smo.py中**
[参考博客](https://cuijiahua.com/blog/2017/11/ml_8_svm_1.html)
这位大哥将svm以及smo算法写的非常详细,在此不做过多讲述,就主要讲解一下我所理解的调参技巧。
smo算法的核心就是求偏置项b和样本误差量alphas,在smop(完整smo算法)中我们完成了这一点,参数详情
```
def smoP(dataMatIn, classLabels, C, toler, maxIter, kTup = ('rbf',0)): (smo.py)
Parameters:
dataMatIn - 数据矩阵
classLabels - 数据标签
C - 松弛变量
toler - 容错率
maxIter - 最大迭代次数
kTup - 包含核函数信息的元组
Returns:
oS.b - SMO算法计算的b
oS.alphas - SMO算法计算的alphas
```
- dataMatIn、classLabels是数据和标签
- C:松弛变量(惩罚函数),目的就是让我们的分类超平面变得更具有普适性,使得可以适应更复杂的分类问题,C若过大,则导致分类能力变弱,也就是成为绝对的一刀切;C若过小,则导致对svm分类器的惩罚过小,导致我们的分类很容易在训练集上过拟合。
**根据实际情况调整C,通常可以先用一个比较大的C,然后不断根据测试集的错误率来缩小C,从而达到最佳。**
- toler:容错率,即我们在计算alphas时需要计算全集误差Ek与当前样本误差Ei的差的abs,有一些误差差值很小我们一般希望直接忽略它,否则会使得分类器分类找不到最佳分类标准,不断迭代却无法快速收敛,这一点在大规模数据集或着真实非理想数据集上尤为重要。
toler就是为我们的误差(损失)设置这么一个容忍度,当我们在迭代过程中,如果误差基本都下降到toler之内,我们就可以认为模型训练成功。
**对于比较简单的数据集并且数据集直观上比较好分类(比如手写数字识别),这类情况就让我们的toler小一点,不允许出太大错误;对于比较复杂且不理想的数据集(比如本demo中的银杏树识别),就可以让toler大一点,从而达到一个比较不错的分类效果。**
- maxIter:最大迭代次数,不用改,20即可,一般情况若太多次迭代都没法收敛到toler,则这个模型训练是不成功的,需要调整数据集或者更改其他参数。
- kTup:包含核函数信息的元组。对于简单分类,我们用一根曲线就可以做到分类,但对于规律性比较差的分类,比如下面这张图![image-20220525224410277](https://raw.githubusercontent.com/mengxiangqiqin/IMAGES/main/imgesimage-20220525224410277.png)
无法很简单的进行线性分类,这时候就需要进行非线性分类,核函数的作用就出现了,核函数就相当于用一个非线性公式去提取我们数据的特征,从而达到对数据升维的效果。如下图![image-20220525224749572](https://raw.githubusercontent.com/mengxiangqiqin/IMAGES/main/imgesimage-20220525224749572.png)
常用的核函数有线性核函数liner,高斯核函数(高斯正态升维)rbf等,一般用rbf就欧克。
注意,kTup是一个核函数的信息数组,格式为('kernel_name', Gauss_C),Gass_C可以理解为高斯正态系数,升维公式为
$$
K' = exp(K/(-1*GaussC**2))
$$
从公式可以看出,Gass_C的取值会影响升维数据离散结果。
- **总而言之,在搞定数据集之后,调参显得尤为重要,合理调节C、toler、Gauss_C、核函数等等超参,可以帮助我们提高模型效果。**
### 三、结果处理
到这里 ,让我们来梳理一下smo算法到底是如何进行svm最优化问题求解的。
得到数据之后,将数据和标签处理好送入模型。之后利用核函数对数据进行升维,然后进行smop计算,得到全局误差数据,之后不断利用随机选择当前数据误差与全局误差进行计算,从而不断迭代更改调整分类超平面,当所有的随机数据误差与全局误差abs(Ek-Ej)符合toler时,就可以停止迭代,得到svm模型,也就是得到了分类超平面。
此时将数据集中的数据(数据包括正负样本,默认设为+x和-x),我们的模型中分类超平面两边的支持向量标签分别为+1和-1,当数据与超平面的距离为正数时,可认为该数据被分类器分配到了支持向量+1的那一区域,反之亦然。之后将分类结果与样本标签进行比对,符号相同即分类正确,符号相反即分类错误,最后统计正确率、错误率,代表我们分类器的性能指标。
**宿舍停电了,下次继续,补充项目迁移和数据增强部分**。