機(jī)器學(xué)習(xí)筆記-Python實(shí)現(xiàn)感知機(jī)(Perceptron)

寒假在看機(jī)器學(xué)習(xí)這本書,看神經(jīng)網(wǎng)絡(luò)這一章的時(shí)候開始手動(dòng)敲一些代碼來(lái)實(shí)現(xiàn)一些基本的神經(jīng)網(wǎng)絡(luò)程序。首先介紹一下基本概念。

神經(jīng)元

神經(jīng)元是神經(jīng)網(wǎng)絡(luò)的基本單元,接收多個(gè)神經(jīng)元傳遞過(guò)來(lái)的輸入信號(hào),然后通過(guò)激活函數(shù)計(jì)算輸出信號(hào)。

神經(jīng)元

圖是機(jī)器學(xué)習(xí)這本書里的。從圖里可以看到每個(gè)輸入信號(hào)都有一個(gè)權(quán)重w,這個(gè)權(quán)重是動(dòng)態(tài)改變的,我們平時(shí)所說(shuō)的訓(xùn)練神經(jīng)網(wǎng)絡(luò)主要就是訓(xùn)練(修正)這個(gè)權(quán)重w。

同時(shí)每個(gè)神經(jīng)元有一個(gè)參數(shù)θ,這個(gè)θ是閾值,生物意義上,如果輸入信號(hào)的加權(quán)和比閾值高,意味著這個(gè)神經(jīng)元被激活(處于興奮狀態(tài)),信號(hào)向下一個(gè)神經(jīng)元傳遞。但是在這里的感知機(jī)模型里,θ不過(guò)是個(gè)公式里的參數(shù)罷了。

感知機(jī)(Perceptron)

感知機(jī)本質(zhì)上就是個(gè)兩層神經(jīng)元構(gòu)成的簡(jiǎn)單神經(jīng)網(wǎng)絡(luò)。一層是輸入層,全部是輸入神經(jīng)元,直接把輸入信號(hào)傳遞給下一層神經(jīng)元,第二層就是輸出層。

感知機(jī)使用Sigmoid函數(shù)作為激活函數(shù):


使用Python可以用一行代碼表示出來(lái),不過(guò)得借助math庫(kù)。

lambda s: 1.0 / (1 + math.exp(-s))

每次感知機(jī)有輸出時(shí),內(nèi)部參數(shù)(w)都需要被訓(xùn)練和調(diào)整。其訓(xùn)練算法如下:


學(xué)習(xí)算法

學(xué)習(xí)率介于0和1之間,我們可以手動(dòng)設(shè)置。算法本身很簡(jiǎn)單。

接下來(lái)就是思考一下代碼實(shí)現(xiàn)的問題了。

  • 首先我們可以定義一個(gè)結(jié)點(diǎn)類Node用來(lái)表示一個(gè)神經(jīng)元節(jié)點(diǎn),從這個(gè)Node類可以進(jìn)一步派生出輸入節(jié)點(diǎn)類和輸出節(jié)點(diǎn)類。
  • 我們可以認(rèn)為每個(gè)節(jié)點(diǎn)Node都有一個(gè)輸入節(jié)點(diǎn)列表。對(duì)于輸入節(jié)點(diǎn),它的輸入節(jié)點(diǎn)列表就是空集 [] 。
  • 節(jié)點(diǎn)Node的默認(rèn)激活函數(shù)為sigmoid函數(shù)。
  • 本來(lái)輸入節(jié)點(diǎn)的職能就是把輸入信號(hào)傳遞給輸出層,不需要激活函數(shù)的。但是我們也可以把它視為一個(gè)M-P神經(jīng)元,它的輸入信號(hào)加權(quán)和為0(它本身就是輸入層,不存在額外的輸入),將它的閾值θ視為它要傳遞給輸出層的數(shù)值的相反數(shù)。將它的激活函數(shù)設(shè)置為f(x)=x。就可以等同于一個(gè)M-P神經(jīng)元了。

代碼實(shí)現(xiàn)

完整python代碼如下:

# coding:utf-8
import math


def sigmoid(x):
    return 1.0/(1+math.exp(-x))


class Record:# 輸入的一條訓(xùn)練數(shù)據(jù)
    def __init__(self):
        feature_vector = []# 特征向量
        label = None# 標(biāo)簽
        return


class Node: # 神經(jīng)元節(jié)點(diǎn)
    def __init__(self):
        self.input_list = []# 輸入的神經(jīng)元列表
        self.activated = False# 這個(gè)神經(jīng)元是否已經(jīng)計(jì)算出輸出信號(hào)
        self.recent_output = None# 上一次的輸出信號(hào)
        self.threshold = 0.0# 閾值θ
        self.activation_func = lambda s: 1.0 / (1 + math.exp(-s))  # default func: sigmoid  function
        return

    def add_input(self, node):# 添加輸入結(jié)點(diǎn)
        self.input_list.append([node, 1.0])# [結(jié)點(diǎn)對(duì)象,權(quán)重w] 權(quán)重默認(rèn)為1
        return

    def set_threshold(self, th):# 設(shè)置閾值θ
        self.threshold = th
        return

    def output(self):# 通過(guò)激活函數(shù)計(jì)算輸出信號(hào)
        sum_ = 0.0
        for p in self.input_list:
            prev_node = p[0]
            sum_ += prev_node.output() * p[1]
        self.recent_output = self.activation_func(sum_ - self.threshold)
        return self.recent_output


class InputNode(Node):# 神經(jīng)元輸入節(jié)點(diǎn)
    def __init__(self):
        Node.__init__(self)
        self.activation_func = lambda s: s# 注意激活函數(shù)是 f(x)=x 
        return

    def set_input_val(self, val):
        Node.set_threshold(self, -val)# 輸入的結(jié)點(diǎn)列表為空,設(shè)置閾值為 -val
        return


class OutputNode(Node):# 神經(jīng)元輸出節(jié)點(diǎn)
    def __init__(self):
        Node.__init__(self)
        self.threshold = 4.0
        return


class NeuralNetwork:# 抽象神經(jīng)網(wǎng)絡(luò)
    def __init__(self):
        self.eta = 0.5# 學(xué)習(xí)率η
        self.data_set = []# 輸入的數(shù)據(jù)集,列表內(nèi)的元素為 Record對(duì)象

    def set_data_set(self, data_set_):# 設(shè)置數(shù)據(jù)集
        self.data_set = data_set_
        return


class SingleLayerNeuralNetwork(NeuralNetwork):# 感知機(jī)模型
    def __init__(self):
        NeuralNetwork.__init__(self)
        self.perceptron = OutputNode()# 一個(gè)輸出結(jié)點(diǎn)
        self.input_node_list = []
        self.perceptron.threshold = 4.0
        return

    def add_input_node(self):# 添加輸入結(jié)點(diǎn)
        inode = InputNode()
        self.input_node_list.append(inode)
        self.perceptron.add_input(inode)
        return

    def set_input(self, value_list):# 給每個(gè)輸入結(jié)點(diǎn)設(shè)置輸入數(shù)據(jù)
        assert len(value_list) == len(self.input_node_list)
        for index in range(0, len(value_list), 1):
            value = value_list[index]
            node = self.input_node_list[index]
            node.set_input_val(value)
        return

    def adjust(self, label):# 每條記錄訓(xùn)練后,自動(dòng)調(diào)整內(nèi)部參數(shù)w
        for prev_node_pair in self.perceptron.input_list:
            delta_weight = self.eta * (label - self.perceptron.recent_output) * prev_node_pair[0].recent_output
            origin_weight = prev_node_pair[1]
            prev_node_pair[1] = origin_weight + delta_weight
        return

    def run(self):# 開始跑訓(xùn)練集
        for data in self.data_set:# 遍歷訓(xùn)練集
            self.set_input(data.feature_vector)
            record_ = self.perceptron.output()
            print record_
            self.adjust(data.label)# 每條記錄訓(xùn)練后,自動(dòng)調(diào)整內(nèi)部參數(shù)w
        return


現(xiàn)在讓這個(gè)感知機(jī)來(lái)解決或問題(x∨??),假設(shè)1表示真,0表示假,那么1和1進(jìn)行或運(yùn)算結(jié)果就是1,1和0進(jìn)行或運(yùn)算結(jié)果就是1,0和0進(jìn)行或運(yùn)算結(jié)果就是0。
輸入數(shù)據(jù)如下(一條記錄保存為一行,第一個(gè)和第二個(gè)數(shù)字是輸入,第3個(gè)數(shù)字是標(biāo)簽。數(shù)據(jù)假設(shè)保存在E://data/ann/train_0.txt):

1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0
1 0 1
0 0 0
1 0 1
1 1 1
1 1 1
0 1 1
1 0 1
0 0 0

有了以上代碼和數(shù)據(jù)后,用以下Python代碼開始跑訓(xùn)練集:


file_handler = open('E://data/ann/train_0.txt')
data_set = []
line = file_handler.readline()
while line:# 遍歷文件里的數(shù)據(jù)
    record = Record()
    item_feature_vector = []
    str_list = line.split()
    item_feature_vector.append(float(str_list[0]))
    item_feature_vector.append(float(str_list[1]))

    record.feature_vector = item_feature_vector
    record.label = float(str_list[2])
    data_set.append(record)
    line = file_handler.readline()
print len(data_set)

ann = SingleLayerNeuralNetwork()# 實(shí)例化感知機(jī)對(duì)象
ann.add_input_node()
ann.add_input_node()# 添加兩個(gè)輸入結(jié)點(diǎn)
ann.set_data_set(data_set)# 設(shè)置數(shù)據(jù)集
ann.run()# 等待結(jié)果

我跑的結(jié)果如下圖所示:

一開始的輸出

可以看到一開始的輸出有點(diǎn)離譜,但是隨著數(shù)據(jù)的增多輸出變得越來(lái)越像與函數(shù)的輸出了。


結(jié)果

實(shí)際上感知機(jī)能解決的問題很少,為了解決復(fù)雜問題,需要引入多層神經(jīng)網(wǎng)絡(luò)和新的學(xué)習(xí)算法:BP誤差逆?zhèn)鞑ニ惴?/strong>。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 文 | 三年 01 放假的時(shí)候,嬸嬸和我聊道,弟弟以前到了周末,經(jīng)常會(huì)一個(gè)人跑去市里的圖書館,一待就是一下午,到晚...
    三年不回來(lái)閱讀 212評(píng)論 1 1
  • 第一肩頸的結(jié)構(gòu)。親愛的家人們。你們知道嗎,科學(xué)家發(fā)現(xiàn)。這二十歲開始人體骨骼總重量,以每年百分之一的速度在遞減...
    77葉子閱讀 2,876評(píng)論 0 0
  • 惠鎖屏作為“鎖屏返現(xiàn)”的代表,自2013年上線以來(lái),版本不斷更新,功能不斷完善,目前已更新到第三代beta版。新版...
    惠鎖屏閱讀 3,832評(píng)論 0 0