首次上传时间:2019-4-16(持续修改)
由于暂时没有深入研究,理论方面暂时不写了。具体参考大牛写的,据说修补三年成型。如果你要了解SVM,打滚推荐。
传送点:https://blog.csdn.net/v_july_v/article/details/7624837
下面的代码几乎每一行都一一和上面博客对照了,有问题欢迎讨论。
实现思路:
出于测试的目的,我将整个代码分为了三个独立运行的文件。
-
文件data.py:这是一份数据伪造代码,我假设我们要找的线性分类器是:
然后我随机在这条线的上下两侧,分别随机取10个点(点越多训练结果越准确)。直线上面10个点为正类,标记值为1。直线下面10个点为负类,标记值为-1。然后其放入一个n*3(我这里是20)的矩阵中,随机打乱其顺序,这样得到数据集。存放到了 ‘D:/pycharm/aicourse/test.txt’ -
文件visual.py:这份代码,纯粹是参照,目的是把自己伪造的点可视化,与最终结果对比用的
-
文件svm.py:这是算法的主要实现代码。这份代码几乎是参考网上的,运用时是SMO算法。过程大致是:
确定目标函数约束条件(损失函数)→转化为拉格朗日函数→转化为对偶函数(由于是2维,不需要核函数降维)→SMO算法
因为我是根据已知线伪造的数据,所以我没有进行精确度测试,直接用原直线和训练结果对比。蓝色为已知直线,红色为训练之后得到的直线,画圈的点为支持向量,结果还是可以的。此外,我的数据是随机伪造的,每次运行data.py都不同。
代码
data.py文件
"""
2019-3-27
这是制造SVM的数据集代码,点集存放在D:/pycharm/aicourse/test.txt'文件
生成2*x+1面两侧的随机点,一面有tol_point个点
"""
import numpy as np
filename = 'D:/pycharm/aicourse/test.txt'
tol_point = 10
x0 = np.random.uniform(0, 100, tol_point)
y0 = np.random.uniform(0, 2*x0+1, tol_point)
x1 = np.random.uniform(0, 100, tol_point)
y1 = np.random.uniform(2*x1+1, 201, tol_point)
np.set_printoptions(precision=2)
z = [-1 for i in range(2*tol_point)]
for i in range(tol_point):
z[i+tol_point] = 1
x = np.append(x0, x1)
y = np.append(y0, y1)
poit_set = x, y, z
t_set = np.mat(poit_set).T
np.random.shuffle(t_set)
np.savetxt(filename, t_set)
visualdata.py文件
"""
2019-3-27
点集存放在D:/pycharm/aicourse/test.txt'文件
将点集在坐标轴上显示
"""
import numpy as np
import matplotlib.pylab as plt
filename = 'D:/pycharm/aicourse/test.txt'
tol_point = 100 # 点集数量
face_x = np.arange(0, tol_point)
face_y = 2*face_x + 1
"""
函数说明:读取数据
Parameters:
fileName - 文件名
Returns:
dataMat - 数据矩阵
labelMat - 数据标签
"""
def loadDataSet(fileName):
dataMat = [];
labelMat = []
fr = open(fileName)
for line in fr.readlines(): # 逐行读取,滤除空格等
lineArr = line.strip().split(' ')
# dataMat.append([map(float, lineArr[0]), map(float, lineArr[1])]) # 添加数据
dataMat.append([float(lineArr[0]), float(lineArr[1])]) # 添加数据
labelMat.append(float(lineArr[2])) # 添加标签
return dataMat, labelMat
def showDataSet(dataMat, labelMat):
data_plus = [] # 正样本
data_minus = [] # 负样本
for i in range(len(dataMat)):
if labelMat[i] > 0:
data_plus.append(dataMat[i])
else:
data_minus.append(dataMat[i])
data_plus_np = np.array(data_plus) # 转换为numpy矩阵
data_minus_np = np.array(data_minus) # 转换为numpy矩阵
plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1]) # 正样本散点图
plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1]) # 负样本散点图
plt.plot(face_x, face_y, color='red', linewidth=1.5, linestyle='-')
plt.show()
if __name__ == '__main__':
dataMat, labelMat = loadDataSet(filename)
dataMatrix = np.mat(dataMat)
labelMat = np.mat(labelMat).transpose()
showDataSet(dataMat, labelMat)
svm.py文件
from time import sleep
import matplotlib.pylab as plt
import numpy as np
import random
filename = 'D:/pycharm/aicourse/test.txt'
"""
函数说明:读取数据
"""
def loadDataSet (fileName):
dataMat = [];
labelMat = []
fr = open(fileName)
for line in fr.readlines(): # 逐行读取,滤除空格等
lineArr = line.strip().split(' ')
# dataMat.append([map(float, lineArr[0]), map(float, lineArr[1])]) # 添加数据
dataMat.append([float(lineArr[0]), float(lineArr[1])]) # 添加数据
labelMat.append(float(lineArr[2])) # 添加标签
return dataMat, labelMat
"""
函数说明:随机选择alpha
Parameters:
i:alpha
m:alpha参数个数
"""
def selectJrand(i, m):
j = i
# 选择一个不等于i的j
while (j == i):
j = int(random.uniform(0, m))
return j
"""
函数说明:修剪alpha
Parameters:
aj:alpha的值
H:alpha上限
L:alpha下限
Returns:
aj:alpha的值
"""
def clipAlpha(aj, H, L):
if aj > H:
aj = H
if L > aj:
aj = L
return aj
"""
函数说明:简化版SMO算法
Parameters:
dataMatIn:数据矩阵
classLabels:数据标签
C:松弛变量
toler:容错率
maxIter:最大迭代次数
"""
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
# 转换为numpy的mat存储
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).transpose()
# 初始化b参数,统计dataMatrix的维度
b = 0
m, n = np.shape(dataMatrix)
# 初始化alpha参数,设为0
alphas = np.mat(np.zeros((m, 1)))
# 初始化迭代次数
iter_num = 0
# 最多迭代matIter次
while (iter_num < maxIter):
alphaPairsChanged = 0
for i in range(m):
# 步骤1:计算误差Ei
fXi = float(np.multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[i, :].T)) + b # y = wx+b
Ei = fXi - float(labelMat[i])
# 优化alpha,设定一定的容错率。
if ((labelMat[i] * Ei < -toler) and (alphas[i] < C)) or ((labelMat[i] * Ei > toler) and (alphas[i] > 0)):
# 随机选择另一个与alpha_i成对优化的alpha_j
j = selectJrand(i, m)
# 步骤1:计算误差Ej
fXj = float(np.multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[j, :].T)) + b
Ej = fXj - float(labelMat[j])
# 保存更新前的aplpha值,使用深拷贝
alphaIold = alphas[i].copy()
alphaJold = alphas[j].copy()
# 步骤2:计算上下界L和H
if (labelMat[i] != labelMat[j]):
L = max(0, alphas[j] - alphas[i])
H = min(C, C + alphas[j] - alphas[i])
else:
L = max(0, alphas[j] + alphas[i] - C)
H = min(C, alphas[j] + alphas[i])
if L == H:
print("L==H"); continue
# 步骤3:计算eta
eta = 2.0 * dataMatrix[i, :] * dataMatrix[j, :].T - dataMatrix[i, :] * dataMatrix[i, :].T -\
dataMatrix[j,:] * dataMatrix[j, :].T # LS的二阶导数,eta是阿尔法的更新步长,也叫学习率
if eta >= 0:
print("eta>=0");
continue
# 步骤4:更新alpha_j
alphas[j] -= labelMat[j] * (Ei - Ej) / eta
# 步骤5:修剪alpha_j
alphas[j] = clipAlpha(alphas[j], H, L) # 控制阿尔法的约束
if (abs(alphas[j] - alphaJold) < 0.00001): print("alpha_j变化太小"); continue
# 步骤6:更新alpha_i
alphas[i] += labelMat[j] * labelMat[i] * (alphaJold - alphas[j])
# 步骤7:更新b_1和b_2
b1 = b - Ei - labelMat[i] * (alphas[i] - alphaIold) * dataMatrix[i, :] * dataMatrix[i, :].T - labelMat[
j] * (alphas[j] - alphaJold) * dataMatrix[i, :] * dataMatrix[j, :].T
b2 = b - Ej - labelMat[i] * (alphas[i] - alphaIold) * dataMatrix[i, :] * dataMatrix[j, :].T - labelMat[
j] * (alphas[j] - alphaJold) * dataMatrix[j, :] * dataMatrix[j, :].T
# 步骤8:根据b_1和b_2更新b
if (0 < alphas[i]) and (C > alphas[i]):
b = b1
elif (0 < alphas[j]) and (C > alphas[j]):
b = b2
else:
b = (b1 + b2) / 2.0
# 统计优化次数
alphaPairsChanged += 1
# 打印统计信息
print("第%d次迭代 样本:%d, alpha优化次数:%d" % (iter_num, i, alphaPairsChanged))
# 更新迭代次数
if (alphaPairsChanged == 0):
iter_num += 1
else:
iter_num = 0
print("迭代次数: %d" % iter_num)
return b, alphas
#
#
# """
# 函数说明:分类结果可视化
# """
#
def showClassifer(dataMat, w, b):
# 绘制样本点
data_plus = [] # 正样本
data_minus = [] # 负样本
for i in range(len(dataMat)):
if labelMat[i] > 0:
data_plus.append(dataMat[i])
else:
data_minus.append(dataMat[i])
data_plus_np = np.array(data_plus) # 转换为numpy矩阵
data_minus_np = np.array(data_minus) # 转换为numpy矩阵
plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1], s=30, alpha=0.7) # 正样本散点图
plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1], s=30, alpha=0.7) # 负样本散点图
# 绘制直线
x1 = max(dataMat)[0]
x2 = min(dataMat)[0]
print(w)
print(b)
a1, a2 = w
b = float(b)
a1 = float(a1[0])
a2 = float(a2[0])
y1, y2 = (-b - a1 * x1) / a2, (-b - a1 * x2) / a2
plt.plot([x1, x2], [y1, y2])
init_x = np.arange(0, 100) # 画出真实函数
init_y = 2*init_x + 1 # 画出真实函数
plt.plot(init_x, init_y, color='red', linewidth=2.5, linestyle='-') # 画出真实函数
# 找出支持向量点
for i, alpha in enumerate(alphas):
if abs(alpha) > 0:
x, y = dataMat[i]
plt.scatter([x], [y], s=150, c='none', alpha=0.7, linewidth=1.5, edgecolor='red')
plt.show()
"""
函数说明:计算w
"""
def get_w(dataMat, labelMat, alphas):
alphas, dataMat, labelMat = np.array(alphas), np.array(dataMat), np.array(labelMat)
w = np.dot((np.tile(labelMat.reshape(1, -1).T, (1, 2)) * dataMat).T, alphas)
return w.tolist()
if __name__ == '__main__':
dataMat, labelMat = loadDataSet(filename)
b, alphas = smoSimple(dataMat, labelMat, 0.6, 0.001, 40)
w = get_w(dataMat, labelMat, alphas)
showClassifer(dataMat, w, b)