如何用Python实现常见机器学习算法-4
四、SVM支持向量机
1、代价函数
在逻辑回归中,我们的代价为:
其中:
如图所示,如果y=1,cost代价函数如图所示
我们想让,即z>>0,这样的话cost代价函数才会趋于最小(这正是我们想要的),所以用图中红色的函数代替逻辑回归中的cost
当y=0时同样用代替
最终得到的代价函数为:
最后我们想要。
之前我们逻辑回归中的代价函数为:
可以认为这里的,只是表达形式问题,这里C的值越大,SVM的决策边界的margin也越大,下面会说明。
2、Large Margin
如下图所示,SVM分类会使用最大的margin将其分开
先说一下向量内积
表示U的欧几里德范数(欧式范数),
向量V在向量U上的投影的长度记为p,则:向量内积:
根据向量夹角公式推导一即可,
前面说过,当C越大时,margin也就越大,我们的目的是最小化代价函数J(θ),当margin最大时,C的乘积项
要很小,所以金丝猴为:
我们最后的目的就是求使代价最小的θ
由
可以得到:
p即为x在θ上的投影
如下图所示,假设决策边界如图,找其中的一个点,到θ上的投影为p,则或者,若是p很小,则需要很大,这与我们要求的θ使最小相违背,所以最后求的是large margin
3、SVM Kernel(核函数)
对于线性可分的问题,使用线性核函数即可
对于线性不可分的问题,在逻辑回归中,我们是将feature映射为使用多项式的形式,SVM中也有多项式核函数,但是更常用的是高斯核函数,也称为RBF核
高斯核函数为:
假设如图几个点,
令:
,
可以看出,若是x与距离较近,可以推出,(即相似度较大);
若是x与距离较远,可以推出,(即相似度较低)。
高斯核函数的σ越小,f下降的越快
如何选择初始的
训练集:
选择:
对于给出的x,计算f,令:,
所以:
最小化J求出θ,
如果,==》预测y=1
4、使用scikit-learn中的SVM模型代码
全部代码
1 import numpy as np 2 from scipy import io as spio 3 from matplotlib import pyplot as plt 4 from sklearn import svm 5 6 def SVM(): 7 '''data1——线性分类''' 8 data1 = spio.loadmat('data1.mat') 9 X = data1['X'] 10 y = data1['y'] 11 y = np.ravel(y) 12 plot_data(X,y) 13 14 model = svm.SVC(C=1.0,kernel='linear').fit(X,y) # 指定核函数为线性核函数 15 plot_decisionBoundary(X, y, model) # 画决策边界 16 '''data2——非线性分类''' 17 data2 = spio.loadmat('data2.mat') 18 X = data2['X'] 19 y = data2['y'] 20 y = np.ravel(y) 21 plt = plot_data(X,y) 22 plt.show() 23 24 model = svm.SVC(gamma=100).fit(X,y) # gamma为核函数的系数,值越大拟合的越好 25 plot_decisionBoundary(X, y, model,class_='notLinear') # 画决策边界 26 27 28 29 # 作图 30 def plot_data(X,y): 31 plt.figure(figsize=(10,8)) 32 pos = np.where(y==1) # 找到y=1的位置 33 neg = np.where(y==0) # 找到y=0的位置 34 p1, = plt.plot(np.ravel(X[pos,0]),np.ravel(X[pos,1]),'ro',markersize=8) 35 p2, = plt.plot(np.ravel(X[neg,0]),np.ravel(X[neg,1]),'g^',markersize=8) 36 plt.xlabel("X1") 37 plt.ylabel("X2") 38 plt.legend([p1,p2],["y==1","y==0"]) 39 return plt 40 41 # 画决策边界 42 def plot_decisionBoundary(X,y,model,class_='linear'): 43 plt = plot_data(X, y) 44 45 # 线性边界 46 if class_=='linear': 47 w = model.coef_ 48 b = model.intercept_ 49 xp = np.linspace(np.min(X[:,0]),np.max(X[:,0]),100) 50 yp = -(w[0,0]*xp+b)/w[0,1] 51 plt.plot(xp,yp,'b-',linewidth=2.0) 52 plt.show() 53 else: # 非线性边界 54 x_1 = np.transpose(np.linspace(np.min(X[:,0]),np.max(X[:,0]),100).reshape(1,-1)) 55 x_2 = np.transpose(np.linspace(np.min(X[:,1]),np.max(X[:,1]),100).reshape(1,-1)) 56 X1,X2 = np.meshgrid(x_1,x_2) 57 vals = np.zeros(X1.shape) 58 for i in range(X1.shape[1]): 59 this_X = np.hstack((X1[:,i].reshape(-1,1),X2[:,i].reshape(-1,1))) 60 vals[:,i] = model.predict(this_X) 61 62 plt.contour(X1,X2,vals,[0,1],color='blue') 63 plt.show() 64 65 66 if __name__ == "__main__": 67 SVM()
线性可分的代码,指定核函数为linear:
1 '''data1——线性分类''' 2 data1 = spio.loadmat('data1.mat') 3 X = data1['X'] 4 y = data1['y'] 5 y = np.ravel(y) 6 plot_data(X,y) 7 8 model = svm.SVC(C=1.0,kernel='linear').fit(X,y) # 指定核函数为线性核函数 9 plot_decisionBoundary(X, y, model) # 画决策边界
非线性可分的代码,默认核函数为rbf
1 '''data2——非线性分类''' 2 data2 = spio.loadmat('data2.mat') 3 X = data2['X'] 4 y = data2['y'] 5 y = np.ravel(y) 6 plt = plot_data(X,y) 7 plt.show() 8 9 model = svm.SVC(gamma=100).fit(X,y) # gamma为核函数的系数,值越大拟合的越好 10 plot_decisionBoundary(X, y, model,class_='notLinear') # 画决策边界
5、运行结果
线性可分的决策边界:
线性不可分的决策边界:
五、K-Means聚类算法
1、聚类过程
聚类属于无监督学习,不知道y的标记分为K类
K-Means算法分为两个步骤
第一步:簇分配,随机选K个点作为中心,计算到这K个点的距离,分为K个簇;
第二步:移动聚类中心,重新计算每个簇的中心,移动中心,重复以上步骤。
如下图所示
随机分配聚类中心:
重新计算聚类中心,移动一次
最后10步之后的聚类中心
计算每条数据到哪个中心最近的代码如下:
1 # 找到每条数据距离哪个类中心最近 2 def findClosestCentroids(X,initial_centroids): 3 m = X.shape[0] # 数据条数 4 K = initial_centroids.shape[0] # 类的总数 5 dis = np.zeros((m,K)) # 存储计算每个点分别到K个类的距离 6 idx = np.zeros((m,1)) # 要返回的每条数据属于哪个类 7 8 '''计算每个点到每个类中心的距离''' 9 for i in range(m): 10 for j in range(K): 11 dis[i,j] = np.dot((X[i,:]-initial_centroids[j,:]).reshape(1,-1),(X[i,:]-initial_centroids[j,:]).reshape(-1,1)) 12 13 '''返回dis每一行的最小值对应的列号,即为对应的类别 14 - np.min(dis, axis=1)返回每一行的最小值 15 - np.where(dis == np.min(dis, axis=1).reshape(-1,1)) 返回对应最小值的坐标 16 - 注意:可能最小值对应的坐标有多个,where都会找出来,所以返回时返回前m个需要的即可(因为对于多个最小值,属于哪个类别都可以) 17 ''' 18 dummy,idx = np.where(dis == np.min(dis, axis=1).reshape(-1,1)) 19 return idx[0:dis.shape[0]] # 注意截取一下
计算类中心代码实现:
1 # 计算类中心 2 def computerCentroids(X,idx,K): 3 n = X.shape[1] 4 centroids = np.zeros((K,n)) 5 for i in range(K): 6 centroids[i,:] = np.mean(X[np.ravel(idx==i),:], axis=0).reshape(1,-1) # 索引要是一维的,axis=0为每一列,idx==i一次找出属于哪一类的,然后计算均值 7 return centroids
2、目标函数
也叫做失真代价函数
最后我们想得到:
其中表示i条数据距离哪个类中心最近,其中即为聚类的中心
3、聚类中心的选择
随机初始化,从给定的数据中随机抽取K个作为聚类中心
随机一次的结果可能不好,可以随机多次,最后取使代价函数最小的作为中心。
代码实现:(这里随机一次)
1 # 初始化类中心--随机取K个点作为聚类中心 2 def kMeansInitCentroids(X,K): 3 m = X.shape[0] 4 m_arr = np.arange(0,m) # 生成0-m-1 5 centroids = np.zeros((K,X.shape[1])) 6 np.random.shuffle(m_arr) # 打乱m_arr顺序 7 rand_indices = m_arr[:K] # 取前K个 8 centroids = X[rand_indices,:] 9 return centroids
4、聚类个数K的选择
聚类是不知道y的label的,所以也不知道真正的聚类个数
肘部法则(Elbow method)
做代价函数J和K的图,若是出现一个拐点,如下图所示,K就取拐点处的值,下图显示K=3
若是很平滑就不明确,人为选择。
第二种就是人为观察选择
5、应用-图片压缩
将图片的像素分为若干类,然后用这个类代替原来的像素值。
执行聚类的算法代码:
1 # 聚类算法 2 def runKMeans(X,initial_centroids,max_iters,plot_process): 3 m,n = X.shape # 数据条数和维度 4 K = initial_centroids.shape[0] # 类数 5 centroids = initial_centroids # 记录当前类中心 6 previous_centroids = centroids # 记录上一次类中心 7 idx = np.zeros((m,1)) # 每条数据属于哪个类 8 9 for i in range(max_iters): # 迭代次数 10 print u'迭代计算次数:%d'%(i+1) 11 idx = findClosestCentroids(X, centroids) 12 if plot_process: # 如果绘制图像 13 plt = plotProcessKMeans(X,centroids,previous_centroids) # 画聚类中心的移动过程 14 previous_centroids = centroids # 重置 15 centroids = computerCentroids(X, idx, K) # 重新计算类中心 16 if plot_process: # 显示最终的绘制结果 17 plt.show() 18 return centroids,idx # 返回聚类中心和数据属于哪个类
6、使用scikit-learn库中的线性模型实现聚类
1 import numpy as np 2 from scipy import io as spio 3 from matplotlib import pyplot as plt 4 from sklearn.cluster import KMeans 5 6 def kMenas(): 7 data = spio.loadmat("data.mat") 8 X = data['X'] 9 model = KMeans(n_clusters=3).fit(X) # n_clusters指定3类,拟合数据 10 centroids = model.cluster_centers_ # 聚类中心 11 12 plt.scatter(X[:,0], X[:,1]) # 原数据的散点图 13 plt.plot(centroids[:,0],centroids[:,1],'r^',markersize=10) # 聚类中心 14 plt.show() 15 16 if __name__ == "__main__": 17 kMenas()
7、运行结果
二维数据中心的移动
图片压缩