1. 程式人生 > >[tensorflow應用之路]10行程式碼實現一個完整的SVM分類器

[tensorflow應用之路]10行程式碼實現一個完整的SVM分類器

SVM是一種常用的機器學習分類器模型,其原理為最大化類間隔(被稱為支援向量),來達到分類的目的。它是一種有監督的模型。

SVM原理簡述

SVM通過預測值y=wx+b與真實值y之間的差值實現最大間隔分類。即

wx+b1,y=1wx+b1,y=1
非常簡單,兩個支援向量的間隔為2w2,所以最小化w2,就可以使支援向量的間隔最大化。
由拉格朗日變換,可得最終的損失定義為:
loss=hinge(
y,y)+w2hinge(y,y)=max(1y(wx+b),0)

ref:Computing the SVM classifier

7行程式碼實現SVM核心

import tensorflow as tf

w = tf.Variable(tf.random_uniform([1, 2]))
b = tf.Variable(tf.random_uniform((1,)))
logit = tf.matmul(w, next_x, transpose_b=True) + b
logit = tf.tanh(logit)
prediction = tf.tanh(tf.matmul
(w, next_x, transpose_b=True) + b) loss = tf.losses.hinge_loss(next_y, logit) + tf.norm(w, 2) opt = tf.train.RMSPropOptimizer(1e-2).minimize(loss)

傳統的SVM使用SMO演算法求解,但是既然我們有RMS/ADAM這樣的區域性優化方法,也可以直接用這種方法。
ref:Implementation

The parameters of the maximum-margin hyperplane are derived by solving the optimization. There exist several specialized algorithms for quickly solving the QP problem that arises from SVMs, mostly relying on heuristics for breaking the problem down into smaller, more-manageable chunks.
\
Another approach is to use an interior point method that uses Newton-like iterations

to find a solution of the Karush–Kuhn–Tucker conditions of the primal and dual problems.[34] Instead of solving a sequence of broken down problems, this approach directly solves the problem altogether. To avoid solving a linear system involving the large kernel matrix, a low rank approximation to the matrix is often used in the kernel trick.
\
Another common method is Platt’s **sequential minimal optimization (SMO)**algorithm, which breaks the problem down into 2-dimensional sub-problems that are solved analytically, eliminating the need for a numerical optimization algorithm and matrix storage. This algorithm is conceptually simple, easy to implement, generally faster, and has better scaling properties for difficult SVM problems

測試結果

構造資料輸入

我們構造一個線性的分類問題,使得y<x為-1類,y>x為1類,注意這裡寫的xy只是表示二維資料中的第一位和第二位,實際上同屬於輸入資料(input),(-1,1)構成輸入資料的真實標籤(label)。資料生成器如下:

def generate_point():
    while True:
        input = (np.random.rand(2) - 0.5) * 20
        output = input[1] > input[0]
        yield input, output.astype(np.float32)

利用tensorflow.data生成batch序列:

    gen = generate_point
    data = tf.data.Dataset.from_generator(gen, (tf.float32, tf.float32))
    data = data.batch(batchsize).prefetch(2)
    data = data.make_one_shot_iterator()
    next_x, next_y = data.get_next()

完整實現程式碼

以下是實現線性SVM分類器的完整程式碼

import tensorflow as tf
import numpy as np
import os
import matplotlib.pyplot as plt


def generate_point():
    while True:
        input = (np.random.rand(2) - 0.5) * 20
        output = input[1] > input[0]
        yield input, output.astype(np.float32)


def SVM():
    batchsize = 256
    # 構建資料輸入
    gen = generate_point
    data = tf.data.Dataset.from_generator(gen, (tf.float32, tf.float32))
    data = data.batch(batchsize).prefetch(2)
    data = data.make_one_shot_iterator()
    next_x, next_y = data.get_next()

    # 構建SVM
    w = tf.Variable(tf.random_uniform([1, 2]))
    b = tf.Variable(tf.random_uniform((1,)))
    logit = tf.matmul(w, next_x, transpose_b=True) + b
    logit = tf.tanh(logit)
    prediction = tf.tanh(tf.matmul(w, next_x, transpose_b=True) + b)
    loss = tf.losses.hinge_loss(next_y, logit) + tf.norm(w, 2)
    opt = tf.train.RMSPropOptimizer(1e-2).minimize(loss)

    xy = []
    for i in range(batchsize):
        xy += [next(gen())[0]]
    xy = np.stack(xy, 0)
    i = 0
    gpu_options = tf.GPUOptions(allow_growth=True)
    with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess:
        sess.run(tf.global_variables_initializer())
        while True:
            sess.run(opt)
            prd = sess.run(prediction, {next_x:xy})
            for p, l in zip(xy, np.squeeze(prd)):
                if l > 0:
                    plt.scatter(p[0], p[1], c='red')
                else:
                    plt.scatter(p[0], p[1], c='green')
            plt.show()
            i += 1


if __name__ == '__main__':
    os.environ['CUDA_VISIBLE_DEVICES'] = '1'
    SVM()

結果

這裡寫圖片描述

為什麼沒有非線性SVM

SVM的非線性是通過核函式實現的。在實現核函式的後,還需要用到標註資料y和訓練資料x才可以完成結果預測。也就是說,非線性SVM的時間複雜度不僅和模型本身有關係,還和訓練資料的數量有關係,從公式看,大概是平方反比的關係。那麼就會造成一種情況:理論上來說訓練資料愈多,模型的泛化能力越強,但訓練資料增多,反而會減慢非線性SVM的預測速度
基於此,我認為這種方法的用處太有限,所以沒有實現它。
有興趣的朋友可以參照tensorflow_cookbook/nonliner_SVM

結論

我寫這篇的目的是為了讓更多剛接觸tf的人看到,tensorflow不僅是一個深度學習的框架,而更多的是——正如google當初的願景一樣——做成一個機器學習通用的工具包。你可以用它實現Adaboost,random forest,Conditional random field, naive bayes等所有你想得到的經典機器學習演算法。
我剛接觸tensorflow時也認為這個庫太大,冗餘api太多。但是實際上,tf的冗餘另一方面也帶來了更多的可能性。所以加油吧,你越多的接觸這個庫,你越會喜歡上這個庫!

最後,祝您身體健康,再見!