机器学习实战(python3.7也可跑)-支持向量机
程序员文章站
2024-02-03 21:09:10
...
如果觉得本篇文章对您的学习起到帮助作用,请 点赞 + 关注 + 评论 ,留下您的足迹????????????
本篇文章为我对机器学习实战-支持向量机的理解与我在学习时所做笔记,一是为了日后查找方便并加深对代码的理解,二是希望能帮助到使用这本书遇到困难的人。
代码可在python3.7跑通
因此代码相对原书做了一些修改,增加了可读性,同时也解决了一些问题。
代码svm.py及详细注释如下:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
def loadDataset(filename):
'''
# 加载数据,第一、二列以列表形式保存到 dataMat 中,第三列标签保存到 labelMat 中
:param filename: 数据为文本文件;由三列组成,其中第三列为标签
:return:
'''
dataMat = list(); labelMat = list()
with open(filename) as fr:
for line in fr.readlines():
# .split('\t')以制表符为分隔符将字符串分拆成多个部分,并存储到列表中
# .strip()该方法只能删除开头或是结尾的字符,不能删除中间部分的字符
lineArr = line.strip().split('\t')
# 数据需要由字符串转化为浮点型
dataMat.append([float(lineArr[0]), float(lineArr[1])])
labelMat.append(float(lineArr[2]))
dataMat = np.array(dataMat)
# 转化为(-1, 1)的数据,可进行后面乘法计算相乘
labelMat = np.array(labelMat).reshape((-1, 1))
return dataMat, labelMat
def selectJrand(i, m):
'''
# SMO 算法的辅助函数
# 只要函数值不等于输入值i,函数就会进行随机选择
# 用于随机选择一个与 i 不同的值,用于内层循环的alpha2
:param i: 第一个alpha的下标
:param m: 所有alpha的数目为m
:return:
'''
j = i
while(j==i):
j = int(np.random.uniform(0, m))
return j
def clipAlpha(aj, H, L):
'''
# SMO 算法的辅助函数
# 裁剪函数;SMO算法所选择的两个alpha,是有范围的,L< alpha <H
:param aj: 计算得到第j个alpha的值
:param H: alpha的上界
:param L: alpha的下界
:return:
'''
if aj > H:
aj = H
if L > aj:
aj = L
return aj
def draw(inX, wStar, bStar):
'''
# 画数据点,以及分割超平面
:param inX: 输入数据点
:param wStar: 参数w
:param bStar: 参数b
:return:
'''
# 显示汉字
mpl.rcParams['font.sans-serif'] = [u'SimHei']
# 解决负号错误问题
mpl.rcParams['axes.unicode_minus'] = False
inX = np.array(inX)
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111)
ax.plot(inX[:,0], inX[:,1], 'go', linewidth=1)
x = np.arange(-3.0, 10.0, 0.1)
y = (-bStar[0] - wStar[0, 0] * x) / wStar[0, 1]
ax.plot(x, y, 'r-', linewidth=2)
plt.title('支持向量机', fontsize=18)
plt.xlabel('X1', fontsize=15)
plt.ylabel('X2', fontsize=15)
plt.show()
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
'''
# 简单版本SMO,第一个参数选择违反KKT最严重的点,第二个参数随机选择
# 如果数据集遍历maxIter次都没有参数改变,则退出循环,程序运行结束
:param dataMatIn: 数据集
:param classLabels: 数据标签
:param C: 超参数C
:param toler: 容忍度,软间隔里面的kesei
:param maxIter: 最大循环次数
:return:
'''
m, n = np.shape(dataMatIn) # m为训练集个数,n为训练集维度
alphas = np.zeros((m, 1)) # alpha初始化为0
b = 0.0
iter = 0 # 在alpha没有任何改变时的情况下遍历数据集次数,当iter大于最大循环次数maxIter时,函数运行结束
while(iter < maxIter):
alphaPairsChanged = 0 # 无参数改变为0,有参数改变为1.标志位
for i in range(m):
# 计算预测值g(xi)
fXi = float(np.dot((alphas * classLabels).T, np.dot(dataMatIn, dataMatIn[i,:].T))) + b
Ei = fXi - float(classLabels[i, 0])
# 外层循环,寻找违反KKT条件最严重的点
if ((classLabels[i, 0]*Ei < -toler) and (alphas[i] < C)) or ((classLabels[i, 0]*Ei > toler) and (alphas[i] > 0)):
# 随机选择一个不同于 i 的 alpha 索引作为 j
j = selectJrand(i, m)
fXj = float(np.dot((alphas * classLabels).T, np.dot(dataMatIn, dataMatIn[j,:].T))) + b
Ej = fXj - float(classLabels[j, 0])
alphaIold = alphas[i].copy() # 复制一个值用于存储更新前的alpha值
alphaJold = alphas[j].copy()
# 裁剪alpha到合法范围
if (classLabels[i, 0] != classLabels[j, 0]):
L = max(0, (alphaJold - alphaIold))
H = min(C, (C + alphaJold - alphaIold))
else:
L = max(0, (alphaJold + alphaIold - C))
H = min(C, (alphaJold + alphaIold))
if H == L: print('H=L');continue # 如果L和H相等,不做任何改变,继续下一次循环
# E11+E22-2E12
eta = np.dot(dataMatIn[i,:].T, dataMatIn[i,:]) + np.dot(dataMatIn[j,:].T, dataMatIn[j,:]) \
- 2 * np.dot(dataMatIn[i,:].T, dataMatIn[j,:])
if eta <= 0: continue # 理论上eta不可能出现负数
alphas[j] += classLabels[j, 0] * (Ei - Ej) / eta # 更新内循环参数alpha(未裁剪)
alphas[j] = clipAlpha(alphas[j], H, L) # 进行裁剪
if (np.abs(alphas[j] - alphaJold) < 0.00001): continue # 新旧参数相差极小,则不更新
alphas[i] += classLabels[i, 0] * classLabels[j, 0] * (alphaJold - alphas[j]) # 更新内循环alpha参数
# 根据所求得alpha计算对应的b
b1 = b - Ei - classLabels[i, 0] * (alphas[i] - alphaIold) * np.dot(dataMatIn[i,:].T, dataMatIn[i,:]) \
- classLabels[j, 0] * (alphas[j] - alphaJold) * np.dot(dataMatIn[j,:].T, dataMatIn[i,:])
b2 = b - Ej - classLabels[i, 0] * (alphas[i] - alphaIold) * np.dot(dataMatIn[i,:].T, dataMatIn[j,:]) \
- classLabels[j, 0] * (alphas[j] - alphaJold) * np.dot(dataMatIn[j,:].T, dataMatIn[j,:])
if (alphas[i] > 0) and (alphas[i] < C): b = b1 # 如果外循环alpha为[0, C]范围内,则 b = b1
elif (alphas[j] > 0) and (alphas[j] < C): b = b2 # 如果内循环alpha为[0, C]范围内,则 b = b2
else: b = (b1 + b2) / 2.0 # 此时内循环alpha,外循环alpha为0或者C,则取平均值
alphaPairsChanged = 1
print('iter:%d i:%d, num of change:%d' % (iter, i, alphaPairsChanged))
if (alphaPairsChanged == 0): # 标志alphaPairsChanged为0则证明未更新参数,iter加1
iter += 1
else:
iter = 0
print('迭代次数:%d' % iter)
# 此处b即为最优参数b
return b, alphas
def calculateOptiPara(dataArr, labelArr, alphas):
'''
# 计算最优的 w 参数,用于构造分割超平面
:param dataArr: 输入数据
:param labelArr: 输入标签
:param alphas: alphas数据列表
:return:
'''
wstar = np.dot((alphas * labelArr).T, dataArr)
return wstar
if __name__ == '__main__':
dataArr, labelArr = loadDataset('dataset//testSet.txt')
b, alphas = smoSimple(dataArr, labelArr, 0.6, 0.001, 40)
wStar = calculateOptiPara(dataArr, labelArr, alphas)
draw(dataArr, wStar, b)
分类结果如下:
希望文章内容可以帮助到你,快来动手敲代码吧!!
上一篇: Keras 实现多层感知机
下一篇: 机器学习实战学习笔记(二)决策树