Python Machine Learning Chapter 3 A Tour of ML Classifiers Using Scikit-learn 学习笔记

 

本帖是学习Sebastian Raschka 的《Python Machine Learning》做的笔记,便于需要时查看。

Chapter 3  A Tour of Machine Learning Classifiers Using Scikit-learn including:

  • Introduction to the concepts of popular classification algorithms
  • Using the scikit-learn machine learning library
  • Questions to ask when selecting a machine learning algorithm

1. Training a perceptron via scikit-learn

直接使用scikit-learn包里的数据Iris。

from sklearn import datasets
import numpy as np
iris = datasets.load_iris()
X = iris.data[:, [2, 3]]
y = iris.target

np.unique(y)查看每分类标签的名称,分别是Iris-Setosa, Iris-Versicolor,and Iris-Virginica,打上标签为(0,1,2)。

评估模型的好坏,先将数据分割成training 和test datasets:

from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

随机将X,y分割成70% training dataset,30% test dataset。

特征缩放(feature scaling)经常用于最优化(optimal performance),这里用StandardScaler:

from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

StandardScaler估计训练数据的每个特征的均值和标准差,测试集做同样的处理。

scikit-learn用One-vs.-Rest(OvR)方法处理多分类。

from sklearn.linear_model import Perceptron
ppn = Perceptron(n_iter=40, eta0=0.1, random_state=0)
ppn.fit(X_train_std, y_train)

eta0是学习率(learning rate),If the learning rate is too large, the algorithm will overshoot the global cost minimum. If the
learning rate is too small, the algorithm requires more epochs until convergence, which can make the learning slow。

用test dataset做预测:

y_pred = ppn.predict(X_test_std)
print('Misclassified samples: %d' % (y_test != y_pred).sum())

测试集45个样本中有4个错分,所以误分率为4/45=0.089.

计算模型的准确率:

from sklearn.metrics import accuracy_score
print('Accuracy: %.2f' % accuracy_score(y_test, y_pred))

模型可视化:

from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt
def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02):
    # setup marker generator and color map
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])
    # plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())
    # plot all samples
    X_test, y_test = X[test_idx, :], y[test_idx]
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
                    alpha=0.8, c=cmap(idx),
                    marker=markers[idx], label=cl)
        # highlight test samples
        if test_idx:
            X_test, y_test = X[test_idx, :], y[test_idx]
            plt.scatter(X_test[:, 0], X_test[:, 1], c='',
                        alpha=1.0, linewidth=1, marker='o',
                        s=55, label='test set')


X_combined_std = np.vstack((X_train_std, X_test_std))
y_combined = np.hstack((y_train, y_test))
plot_decision_regions(X=X_combined_std,
                      y=y_combined, classifier=ppn, test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()

                                        

从图中可以看出,分类的边界不能完全分开。下面我们再看看其它的分类器。

 

2. Modeling class probabilities via logistic regression

感知机的一个缺点是如果类别的边界不是完全线性分开,分类就不会收敛。可以改变learning rate 或增加迭代次数调优模型。下面我们看看另一个分类器--逻辑回归。

逻辑回归常见的是一个线性二分类模型,也可以通过OvR方法用于多分类。

 odd ratio = \frac{p}{1-p}

p是postive event的概率。

logit function:logit(p)=log\frac{p}{1-p}

                   logit(p(y=1|x))=w_{0}x_{0}+w_{1}x_{1}+...+w_{m}x_{m}=\sum_{i=0}^{m}w_{i}x_{i}=w^{T}x

p(y=1|x)是在给定x的条件下,y=1的条件概率。

sigmoid function:\Phi (z)=\frac{1}{1+e^{-z}}

logistic function 可以缩减写成sigmoid function,z=w^{T}x=w_{0}+w_{1}x_{1}+...w_{m}x_{m}.

sigmoid function 可视化:

import matplotlib.pyplot as plt
import numpy as np
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

z = np.arange(-7, 7, 0.1)
phi_z = sigmoid(z)
plt.plot(z, phi_z)
plt.axvline(0.0, color='k')
plt.axhspan(0.0, 1.0, facecolor='1.0', alpha=1.0, ls='dotted')
plt.axhline(y=0.5, ls='dotted', color='k')
plt.yticks([0.0, 0.5, 1.0])
plt.ylim(-0.1, 1.1)
plt.xlabel('z')
plt.ylabel('$\phi (z)$')
plt.show()

                                             

在chapter2中的Adaline,用的激活函数是\Phi (z)=z,逻辑回归用的激活函数是sigmoid函数,如下图:

                                  

sigmoid函数 \Phi (z)=P(y=1|x;w)的输出是属于分类1的概率,在给定特征x和其系数w。预测概率可以转换成二分类:

                                     \widehat{y}=\left\{\begin{matrix} 1,if \Phi (z)\geqslant 0.5\\ 0,otherwise\end{matrix}\right.

 

Learning the weights of the logistic cost function:

cost function:                 

maximize likelihood function:         

log-likelihood function:              l(w)=logL(w))=\sum_{i=1}^{n}y^{(i)}log(\Phi (z^{(i)}))+(1-y^{(i)})log(1-\Phi (z^{(i)}))

用梯度上升法求解最大log-likelihood方程,即找最优解,则cost function为:

                                              J(w)=\sum_{i=1}^{n}-y^{(i)}log(\Phi (z^{(i)}))-(1-y^{(i)})log(1-\Phi (z^{(i)}))     

用scikit-learn训练逻辑回归模型:

from sklearn.linear_model import LogisticRegression
lr = LogisticRegression(C=1000.0, random_state=0)
lr.fit(X_train_std, y_train)
plot_decision_regions(X_combined_std,
                      y_combined, classifier=lr,
                      test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()

                                                

预测样本属于哪个类别的概率:

lr.predict_proba(X_test_std[0,:])

用正则化处理过拟合:

过拟合是指模型在训练集上准确率很高,在测试集上准确率不高,可能原因是模型中特征太多,如下图:

                        

可以通过正则化调节模型的复杂度找到good bias-variance tradeoff。正则化可以解决模型的共线问题,过滤噪声数据,防止模型过拟合。常见的L2正则化公式为:           

可以将逻辑回归的代价函数(cost function)加上正则准则:

                            J(w)=[\sum_{i=1}^{n}(-y^{(i)}log(\Phi (z^{(i)}))-(1-y^{(i)})log(1-\Phi (z^{(i)})))]+\frac{\lambda }{2}\left \| w \right \|^{2}

通过调节正则参数\lambda,可以让特征系数趋于0来优化模型。\lambda越大,正则化力度越大。

在scikit-learn里,逻辑回归分类中,参数 C=\frac{1}{\lambda }.

weights, params = [], []
for c in np.arange(-5, 5):
    lr = LogisticRegression(C=10**c, random_state=0)
    lr.fit(X_train_std, y_train)
    weights.append(lr.coef_[1])
    params.append(10**c)

weights = np.array(weights)
plt.plot(params, weights[:, 0],label='petal length')
plt.plot(params, weights[:, 1], linestyle='--',label='petal width')
plt.ylabel('weight coefficient')
plt.xlabel('C')
plt.legend(loc='upper left')
plt.xscale('log')
plt.show()

                                          

从图中可以看出,降低C,系数减少趋于0. 降低C也就是增加正则化力度。

 

3. Maximun margin classification with support vector machines

SVM可以看成是感知机(perceptron)的延伸。感知机最小化误分率,SVM最大化边界。边界是靠近分离超平面的训练样本离分离超平面(decision boundary)的距离,也称作support vectors,如下图:

                            

最大化边界原理:

有较大边界的决策边界原理是,它们往往具有较低的泛化误差。较小边界的模型容易过拟合。

positive hyperplanes:   w_{0}+w^{T}x_{pos}=1      (1)

negative hyperplanes:   w_{0}+w^{T}x_{neg}=-1   (2)

两式相减:    w^{T}(x_{pos}-x_{neg})=2    (3)

向量w的长度为:   \left \| w \right \|=\sqrt{\sum_{j=1}^{m}w_{j}^{2}}    (4)

标准化公式(3):    \frac{w^{T}(x_{pos}-x_{neg})}{\left \| w \right \|}=\frac{2}{\left \| w \right \|}    (5)

公式(5)等号左边的为positive and negative hyperplane之间的距离,也就是需要最大化的距离。根据公式(5),我们只需要最大化  \frac{2}{\left \| w \right \|}, 也可以写成:  

                                                    w_{0}+w^{T}x^{(i)}\geq 1 ,if y^{(i)}=1

                                                    w_{0}+w^{T}x^{(i)}\leq - 1 ,if y^{(i)}=-1

 

用松弛变量(slack variable)处理非线性可分样本:

松弛变量是在不可分的情况下,尽可能地找出分离超平面,也就是软间隔。将线性约束条件加上松弛变量\xi> 0:

                                               w^{T}x^{(i)}\geq 1 ,if y^{(i)}=1-\xi ^{(i)}

                                               w^{T}x^{(i)}\leq - 1 ,if y^{(i)}=1+\xi ^{(i)}

so, 新的目标是最小化    \frac{1}{2}\left \| w \right \|^{2}+C\sum \xi ^{(i)}.

C控制误分类的惩罚项。较大的C值对应较大的错误惩罚,而如果我们为C选择较小的值,则对错误分类的要求就不那么严格。我们可以用C去控制边界的宽度,如下图:

                                                

增加C值,会增加bias,减少variance。

scikit-learn中SVM的可视化:

from sklearn.svm import SVC
svm = SVC(kernel='linear', C=1.0, random_state=0)
svm.fit(X_train_std, y_train)
plot_decision_regions(X_combined_std,y_combined, classifier=svm,test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()

                                                  

 

用核函数SVM解决非线性问题:

非线性问题比如XOR门,即异或门。NumPy中的logical_xor产生100个label 1, 100个label -1:

np.random.seed(0)
X_xor = np.random.randn(200, 2)
y_xor = np.logical_xor(X_xor[:, 0] > 0, X_xor[:, 1] > 0)
y_xor = np.where(y_xor, 1, -1)
plt.scatter(X_xor[y_xor==1, 0], X_xor[y_xor==1, 1], c='b', marker='x', label='1')
plt.scatter(X_xor[y_xor==-1, 0], X_xor[y_xor==-1, 1], c='r', marker='s', label='-1')
plt.ylim(-3.0)
plt.legend()
plt.show()

                                               

很显然,我们不能用线性超平面分类上图的数据。核函数用mapping function \Phi (.)在高维上构造原始特征的非线性组合。如下图可视,我们可以将二维数据转换到三维空间

                                          

 

Using the kernel trick to find separating hyperplanes in higher dimensional space:

为了用SVM解决非线性问题,我们用mapping function  \Phi (.)将训练数据转换到高维特征空间,然后在新的特征空间用线性SVM训练模型。映射方法在新的特征空间计算比较复杂,所以用核函数代替映射函数,k(x^{(i)},x^{(j)})=\Phi (x^{(i)})^{T}\Phi (x^{(j)}).

Radial Basis Function kernel (RBF kernel) or Gaussian kernel:

                                                         

The minus sign inverts the distance measure into a similarity score and, due to the exponential term, the resulting similarity score will fall into a range between 1 (for exactly similar samples) and 0 (for very dissimilar samples).

svm = SVC(kernel='rbf', random_state=0, gamma=0.10, C=10.0)
svm.fit(X_xor, y_xor)
plot_decision_regions(X_xor, y_xor, classifier=svm)
plt.legend(loc='upper left')
plt.show()

                                     

gamma=0.10是cut-off for Gaussian。 增加gamma值,即增加每个训练样本的影响力。

用RBF kernel SVM 可视化Iris:

svm = SVC(kernel='rbf', random_state=0, gamma=0.2, C=1.0)
svm.fit(X_train_std, y_train)
plot_decision_regions(X_combined_std, y_combined, classifier=svm, test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()

                                        

增加gamma的值:

svm = SVC(kernel='rbf', random_state=0, gamma=100.0, C=1.0)
svm.fit(X_train_std, y_train)
plot_decision_regions(X_combined_std, y_combined, classifier=svm, test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.show()

                                                     

当gamma增加时,the decision boundary around the classes 0 and 1 is much tighter。

 

4. Decision Tree Learning

决策树分类器的解释性比较好。

决策树通过信息增益(information gain,IG)选取分支点的特征。一般选择IG最大的特征,所以寻找最优树就要求最大信息增益。

information gain:      

                                  f:the feature to perform the split

                                  D_{p},D_{j}: the dataset of the parent and jth child node

                                  I: the impurity measure

                                  N_{p},N_{j} : the total number of samples at the parent node, the number of jth child node

    IG = the impurity of the parent node - the sum of child node impurities

对于二分类,信息增益的表达式为:   

二分类树的分类标准有:基尼指数(Gini index,I_{G}),熵(entropy,I_{H}),分类误差(classification error,I_{E})。

熵:                       

                p(i|t): the proportion of the samples that belongs to class c for a particular node t.

        当所有样本都属于同一类时,熵为0.当uniform class classification, 熵最大。eg. 如果p(i=1|t)=1 or p(i=0|t)=0,熵为0;如果             p(i=1|t)=0.5 and p(i=0|t)=0.5,熵为1。

基尼指数:             

                   基尼指数最小化误分的概率。

分类误差:     

                   分类误差适用于减枝,但不推荐用于生成决策树,因为它对节点的分类概率变化不太敏感。

三种分类标准可视化:

import matplotlib.pyplot as plt
import numpy as np
def gini(p):
    return (p)*(1 - (p)) + (1 - p)*(1 - (1-p))

def entropy(p):
    return - p*np.log2(p) - (1 - p)*np.log2((1 - p))

def error(p):
    return 1 - np.max([p, 1 - p])

x = np.arange(0.0, 1.0, 0.01)
ent = [entropy(p) if p != 0 else None for p in x]
sc_ent = [e*0.5 if e else None for e in ent]
err = [error(i) for i in x]
fig = plt.figure()
ax = plt.subplot(111)
for i, lab, ls, c, in zip([ent, sc_ent, gini(x), err],
                           ['Entropy', 'Entropy (scaled)','Gini Impurity','Misclassification Error'],
                           ['-', '-', '--', '-.'],
                           ['black', 'lightgray','red', 'green', 'cyan']):
    line = ax.plot(x, i, label=lab,
                   linestyle=ls, lw=2, color=c)

ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.15),
          ncol=3, fancybox=True, shadow=False)
ax.axhline(y=0.5, linewidth=1, color='k', linestyle='--')
ax.axhline(y=1.0, linewidth=1, color='k', linestyle='--')
plt.ylim([0, 1.1])
plt.xlabel('p(i=1)')
plt.ylabel('Impurity Index')
plt.show()

                                    

from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(criterion='entropy',max_depth=3, random_state=0)
tree.fit(X_train, y_train)
X_combined = np.vstack((X_train, X_test))
y_combined = np.hstack((y_train, y_test))
plot_decision_regions(X_combined, y_combined,classifier=tree, test_idx=range(105,150))
plt.xlabel('petal length [cm]')
plt.ylabel('petal width [cm]')
plt.legend(loc='upper left')
plt.show()

                                  

 

5. Combining weak to strong learners via random forests

随机森林是组合多个弱学习器成一个强学习器,能够更好的泛化误差,防止过拟合。

步骤:

  1. 用Bootstrap方法随机取样n次;
  2. 每个Bootstrap样本生成决策树;
  3. 重复k次步骤1和2;
  4. 整合每棵树的预测结果,用投票器觉得每个样本的标签。

Iris数据可视化:

from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(criterion='entropy',n_estimators=10,random_state=1,n_jobs=2)
forest.fit(X_train, y_train)
plot_decision_regions(X_combined, y_combined,classifier=forest, test_idx=range(105,150))
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.legend(loc='upper left')
plt.show()

                                                    

n_estimators是设置决策树的个数,上图用熵作为分类标准。

 

6. K-nearest neighbors -- a lazy learning algorithm

KNN 是一个懒惰学习器,因为KNN不学习discriminative function,而是记住训练集。

步骤:

  1. 选定k和距离度量方法;
  2. 找到需要分类样本附近的knn;
  3. 通过投票器赋值分类标签。

Iris数据可视化:

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5, p=2, metric='minkowski')
knn.fit(X_train_std, y_train)
plot_decision_regions(X_combined_std, y_combined, classifier=knn, test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.show()

                                        

 

Summary

这个chapter主要是介绍用scikit-learn库来实现感知机,逻辑回归,SVM,决策树,随机森林和KNN分类。将Iris数据用每种分类器可视化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值