菜鳥入門_Python_機器學習(1)_線性可分的雙月實驗


@sprt
*寫在開頭:博主在開始學習機器學習和Python之前從未有過任何編程經驗,這個系列寫在學習這個領域一個月之後,完全從一個入門級菜鳥的角度記錄我的學習歷程,代碼未經優化,僅供參考。有錯誤之處歡迎大家指正。
系統:win7-CPU;
編程環境:Anaconda2-Python2.7,IDE:pycharm5;
參考書籍:
《Neural Networks and Learning Machines(Third Edition)》- Simon Haykin;
《Machine Learning in Action》- Peter Harrington;
《Building Machine Learning Systems with Python》- Wili Richert;
C站裏都有資源,也有中文譯本。
我很慶幸能跟隨老師從最基礎的東西學起,進入機器學習的世界。*


第一課主要了解一下機器學習的概念和深度學習現在的主流的模型和成果,這方面在這篇博文裏沒什麼可說的,網上隨便搜。然後簡單強調了Python的幾個用到的庫,numpy、math、matplotlib等,我大部分還是網上自學,給幾個參考鏈接:
http://www.2cto.com/kf/201407/317115.html
http://www.jb51.net/article/49397.htm
http://www.mamicode.com/info-detail-507676.html
http://www.xuebuyuan.com/1420264.html
http://www.2cto.com/kf/web/Python/

我花了一天的時間瞭解了Python的基本語句,然後來看我們的任務:

Generate random data in two groups controlled by parameters r, w, d, Na, Nb, and plot out. (ref. p61 on Haykin)
•Implement Perceptron Convergence Algorithm p54 on Haykin in PyCharm, and then apply to your data sets with following different parameters
•r=10, w=2, d=2, Na=100, Nb=100
•r=10, w=2, d=-2, Na=100, Nb=100

《Neural Networks and Learning Machines(Third Edition)》這本書中對所謂的‘雙月’做了很多處理,簡單來說就是生成這樣一個東西:
雙月

看起來很簡單,但是我第一次編程還是比較矇蔽的,莫名的恐懼和厭惡……總之多敲敲代碼就好了。好歹最後搞了出來:

# -*- coding:gb2312 -*-
import matplotlib.pyplot as plt
import numpy as np
import math
# 生成雙月
def point_sample(N,r,w,d):
    x_sample = []
    y_sample = []
    inner_r = r - (w/2)
    outer_r = r + (w/2)

    #point the area A
    while True:
        data_x_A = np.random.uniform(-outer_r,outer_r)
        data_y_A = np.random.uniform(0,outer_r)
        r_A = math.sqrt((data_x_A**2) + (data_y_A**2))
        if r_A >= inner_r and r_A <= outer_r:
            x_sample.append(data_x_A)
            y_sample.append(data_y_A)
            if len(x_sample) == N:
                break
            else:
                continue
        else:
            continue

    #point the area B
    while True:
        data_x_B = np.random.uniform(-outer_r + r,outer_r + r)
        data_y_B = np.random.uniform(-d - outer_r,-d)
        r_B = math.sqrt(((data_x_B - r)**2) + ((data_y_B + d)**2))
        if r_B >= inner_r and r_B <= outer_r:
            x_sample.append(data_x_B)
            y_sample.append(data_y_B)
            if len(x_sample) == 2 * N:
                break
            else:
                continue
        else:
            continue
    plt.figure(1)
    plt.plot(x_sample,y_sample,'b*')

    data_xy = np.array([np.reshape(x_sample,len(x_sample)),np.reshape(y_sample,len(y_sample))]).transpose()

    return data_xy

最後,爲了方便之後處理都轉置成了二維的數據。
呃……似乎有點挫,給一個網上的版本,簡化了很多:

def dbmoon(N=100, d=2, r=10, w=2):
    N1 = 10*N
    w2 = w/2
    done = True
    data = np.empty(0)
    while done:
        #generate Rectangular data
        tmp_x = 2*(r+w2)*(np.random.random([N1, 1])-0.5)
        tmp_y = (r+w2)*np.random.random([N1, 1])
        tmp = np.concatenate((tmp_x, tmp_y), axis=1)
        tmp_ds = np.sqrt(tmp_x*tmp_x + tmp_y*tmp_y)
        #generate double moon data ---upper
        idx = np.logical_and(tmp_ds > (r-w2), tmp_ds < (r+w2))
        idx = (idx.nonzero())[0]

        if data.shape[0] == 0:
            data = tmp.take(idx, axis=0)
        else:
            data = np.concatenate((data, tmp.take(idx, axis=0)), axis=0)
        if data.shape[0] >= N:
            done = False
    print data
    db_moon = data[0:N, :]
    print db_moon
    #generate double moon data ----down
    data_t = np.empty([N, 2])
    data_t[:, 0] = data[0:N, 0] + r
    data_t[:, 1] = -data[0:N, 1] - d
    db_moon = np.concatenate((db_moon, data_t), axis=0)
    return db_moon

可以發現充分利用numpy庫可以讓代碼簡化很多。OK,雙月得到了,兩個區域之間的距離我們定義的較大,讓他們線性可分,然後用一個Rosenblatt感知機分開。

很多地方介紹機器學習都會首先介紹單層感知機,作爲之後神經網絡的基礎,它可以看做類似於神經細胞的結構,多個輸入,各自有可訓練權值,經過求和並激活之後輸出

到底爲什麼要加偏置Bias呢?一種思想是,假設模型的輸出始終與真實的數據有差距,我們爲了逐步調整這個差距,我們在輸入層對每個樣本添加了一維特徵,相當於在輸出端添加了一個始終爲一的觀察點。從這個角度去看,偏置是可以去掉的,我們可以通過不斷調整輸入的權值來優化網絡。通常爲了方便計算將偏置作爲輸入的一部分:

其中所謂的學習率是自己設定的,我理解的就是給誤差一個權值,它不能太大,否則會讓權值在更新時跳過最優值而不斷震盪,也不能太小,否則會導致尋找最有權值的過程太慢。激活函數的目的是將輸入的加權和歸一化並更好的體現分類特性,因爲我們要分類的雙月是線性可分的而且是兩類,激活函數用符號函數就好。而統計誤差的時候我們用常用的最小均方誤差(這個不瞭解的可以自己查),之後我們用這個誤差不斷更新權值,達到想要的分類效果。有兩種方式來判斷網絡時候達到我們想要的效果,一種是限定迭代次數,另一種是看代價函數(誤差)值時候降低到我們設定的足夠低的標準。我這裏使用第一種方法,一開始嘗試迭代50次,直接上代碼:

N = 100
d = -2
r = 10
w = 2
a = 0.1
num_MSE = []
num_step = []
data = dbmoon(N, d, r, w)

x0 = [1 for x in range(1,201)]
x = np.array([np.reshape(x0, len(x0)), np.reshape(data[0:2*N, 0], len(data)), np.reshape(data[0:2*N, 1], len(data))]).transpose()
m = np.array([1, 0, 0])
b_pre = [1 for y in range(1, 101)]
b_pos = [-1 for y in range(1, 101)]
b=b_pre+b_pos

def sgn(v):
    if v >= 0:
        return 1
    else:
        return -1
#compute y(n)
def compute_yn(myw, myx):
    return sgn(np.dot(myw.T, myx))
#Update the weights
def new_w(old_w, myd, myx, a):
    return old_w+a*(myd-compute_yn(old_w,myx))*myx

for ii in range(50):
    i = 0
    sum=0
    for xn in x:
        m = new_w(m, b[i], xn, a)
        sum += (b[i]-compute_yn(m, xn))**2+0.0
        i += 1
    #compute MSE
    mean_square =np.sqrt(sum/N/2)
    num_MSE.append(mean_square)
    print mean_square
    print ii
    num_step.append(ii+1)
plt.subplot(212)
plt.plot(num_step, num_MSE, 'r-')

print m
#draw The decision boundary
testx = np.array(range(-15, 25))
testy = -testx*m[1]/m[2]-m[0]/m[2]
plt.subplot(211)
plt.plot(data[0:N, 0], data[0:N, 1], 'r*', data[N:2*N, 0], data[N:2*N, 1], 'b*', testx, testy, 'g--')
plt.show()

最後的結果:
分類結果
MSE曲線

分得很好,MSE曲線下降的也很快。但是當我們把雙月之間的距離調成線性不可分時,分類效果很差,而且MSE曲線不斷震盪,如何應對這種情況我們之後討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章