统计学习方法之感知机

声明:图片均来自网络,若有侵权,联系本人后立即删除

1. 定义

  • 假设输入空间(特征空间)是 χ ⊆ R n \chi \subseteq\mathsf{R^n} χRn,输出空间是 γ = { + 1 , − 1 } \gamma =\{+1,-1\} γ={+1,1}。输入我们 x ∈ χ x\in\chi xχ表示实例的特征向量,对应于输入空间(特征空间)的点:输出 y ∈ γ y\in\gamma yγ表示实例的类别。由输入空间到输出空间的如下函数:
    f ( x ) = s i g n ( w ⋅ x + b ) f(x) = sign(w·x +b) f(x)=sign(wx+b)
    称为感知机。其中,
    s i g n ( x ) = { + 1 , x ≥ 0 − 1 , x &lt; 0 sign(x) = \begin{cases} +1,\quad x\ge0 \\ -1, \quad x&lt;0 \end{cases} sign(x)={+1,x01,x<0

  • 感知机模型对应于输入空间(特征空间)中的分离超平面 w ⋅ x + b = 0 w·x+b=0 wx+b=0

2. 感知机学习算法

  • 输入:训练数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x N , y N ) } T = \{(x_1,y_1),(x_2,y_2),...,(x_N,y_N)\} T={(x1,y1),(x2,y2),...,(xN,yN)},其中 x i ∈ χ = R n x_i\in\chi=\mathsf{R^n} xiχ=Rn y i ∈ γ = { − 1 , + 1 } y_i\in\gamma=\{-1,+1\} yiγ={1,+1},   i = 1 , 2 , . . . , N \ i=1,2,...,N  i=1,2,...,N; 学习率 η ( 0 &lt; η ≤ 1 ) \eta(0&lt;\eta\le1) η(0<η1)
  • 输出: w , b w,b w,b:感知机模型 f ( x ) = s i g n ( w ⋅ x + b ) f(x)=sign(w·x+b) f(x)=sign(wx+b)
  1. 选取初值 w 0 , b 0 w_0,b_0 w0,b0
  2. 在训练集中选取数据 ( x i , y i ) (x_i,y_i) (xi,yi)
  3. 如果 y i ( w ⋅ x i + b ) ≤ 0 y_i(w·x_i +b)\le0 yi(wxi+b)0
    w ← w + η y i x i b ← b + η y i   w\gets w + \eta y_i x_i\\ b\gets b+ \eta y_i \quad\ ww+ηyixibb+ηyi 
  4. 跳至(2),直至训练集中没有误分类点。

注意

  • 该算法采用的随机梯度下降法(SGD, stochastic gradient descent)。经本人实现发现,如果采用批梯度下降(mini-batch GD),该算法的时间效率和精确度都会下降,所以个人建议还是用SGD更加靠谱。
  • 感知机学习的策略是极小化损失函数 min ⁡ w , b L ( w , b ) = − ∑ x i ∈ M y i ( w ⋅ x i + b ) \mathop{\min_{w,b}}L(w,b)=-\sum_{x_i\in M} y_i(w·x_i+b) w,bminL(w,b)=xiMyi(wxi+b)
    损失函数对应于误分类点到分离超平面的总距离

3. 数据集介绍

关于数据集,参考的是wds2006sdo写的感知机实现的博文中的MINST数据集。MNIST是一个入门级的计算机视觉数据集,它包含各种手写数字图片:MINST数据集
由于我们上述描述的感知机只能进行二分类,所以用该数据集中专门为了训练二分类问题的数据集来训练我们的感知机模型。

4. 特征

为了训练我们的感知机,首先要确定样本的特征,对于该问题,我们既可以用整张图像作为特征,也可以用图像的HOG特征来进行训练。具体的图像的HOG特征参考的是李航《统计学习方法》第二章——用Python实现感知器模型(MNIST数据集)
具体的提取HOG特征的代码会在下面呈现,除此之外我们还要一个hog.xml文件来保存hog的配置信息,如下所示:

<?xml version="1.0"?>
<opencv_storage>
<hog type_id="opencv-object-detector-hog">
  <winSize>
    28 28</winSize>
  <blockSize>
    14 14</blockSize>
  <blockStride>
    7 7</blockStride>
  <cellSize>
    7 7</cellSize>
  <nbins>9</nbins>
  <derivAperture>1</derivAperture>
  <winSigma>4.</winSigma>
  <histogramNormType>0</histogramNormType>
  <L2HysThreshold>2.0000000000000001e-001</L2HysThreshold>
  <gammaCorrection>1</gammaCorrection>
  <nlevels>64</nlevels></hog>
</opencv_storage>

5.代码实现

这里代码的基本框架也是参考的wds2006sdo,但是其中核心代码是自己写的或者在原来的基础上做了些改动。
代码用python实现。

import pandas as pd
import numpy as np
import cv2
import time

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score


class Perceptron(object):
    def __init__(self, learning_step=0.00001, max_iteration=5000, max_correct_count = 10000):
        self.learning_step = learning_step
        self.max_iteration = max_iteration
        self.max_correct_count = max_correct_count

    def train(self, features, labels):
        # 初始化参数为0
        labels_ = labels * 2 - 1
        self.w = np.array([0.0] * features.shape[1])
        self.b = 0.0
        time = 0
        correct_count = 0
        while time < self.max_iteration:
            time += 1
            rand_idx = np.random.randint(0, len(labels))
            img = features[rand_idx]
            # 这里是min_batch GD的实现
            # batch_idx = np.random.randint(0, features.shape[0], size=(512,))
            # batch_features = features[batch_idx]
            # batch_labels = labels_[batch_idx]
            # y = np.sum(batch_features * self.w, axis=1) + self.b
            # idx = np.where(y*batch_labels.reshape(-1, 1) <= 0)[0]
            # delta_w = np.sum(batch_features[idx] * batch_labels[idx].reshape(-1, 1), axis=0)
            # delta_b = np.sum(batch_labels[idx])
            y = np.sum(img * self.w) + self.b
            if y * labels_[rand_idx] > 0:
                correct_count += 1
                if correct_count > self.max_correct_count:
                    break
                else:
                    continue
            delta_w = features[rand_idx] * labels_[rand_idx]
            delta_b = labels_[rand_idx]
            delta = np.sum(np.abs(delta_w)) + np.abs(delta_b)
            # 梯度下降更新参数
            self.w = self.w + self.learning_step * delta_w
            self.b = self.b + self.learning_step * delta_b
            if time % int(self.max_iteration / 5) == 0:
                print("Training iterations:", str(time))

    def predict_(self, x):
        y = np.sum(self.w * x) + self.b
        return int(y > 0)

    def predict(self, features):
        labels = []
        for feature in features:
            labels.append(self.predict_(feature))
        return labels


def get_hog_features(trainset):
    features = []
    hog = cv2.HOGDescriptor('../hog.xml')
    for img in trainset:
        img = np.reshape(img, (28, 28))
        cv_img = img.astype(np.uint8)

        hog_feature = hog.compute(cv_img)

        features.append(hog_feature)
    features = np.array(features)
    features_np = np.reshape(features, (-1, 324))

    return features_np


if __name__ == '__main__':

    print("---start load data----")

    time_1 = time.time()
    raw_data = pd.read_csv('../data/train_binary.csv', header=0)
    data = raw_data.values
    imgs = data[:, 1:]
    imgs_hog = get_hog_features(imgs)
    labels = data[:, 0]
    train_features, test_features, train_labels, test_labels = train_test_split(
        imgs_hog, labels, test_size=0.33, random_state=23323)
    time_2 = time.time()
    print("read data cost:", time_2 - time_1, 'second', '\n')

    print('----start training----')
    p = Perceptron()
    p.train(train_features, train_labels)
    time_3 = time.time()
    print("training cost:", time_3 - time_2, 'second', '\n')

    print('----start predicting----')
    test_predict = p.predict(test_features)
    time_4 = time.time()
    print("predicting cost", time_4 - time_3, 'second', '\n')

    score = accuracy_score(test_labels, test_predict)
    print('The accuracy score is: ', score)

6. 代码运行结果

在macbook pro上的运行结果如下:
运行结果
代码实现需要注意的点:

  1. def train()中定义的correct_count变量是为了让模型能正确估计大部分数据时停下,如果不加这个变量,运行时间会更长,而且准确率会降低。
  2. def train()中定义的time是为了让训练足够久之后能够终止。
  3. 前面也说过,经过亲测,SGD不仅在时间上,而且在准确度上都完胜mini-batch GD,所以推荐用SGD。

参考文献

[1] 《统计学习方法》 李航
[2]   wds2006sdo的博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值