計算神經科學、學習理論與分析學的千絲萬縷的聯繫
_________________ 再談畢業論文
在碩士學位論文中,我提出了一種可以稱之爲“改良神經網絡”的模型 ----- 類額葉編碼學習模型,相對於當前主流的深度學習模型:基於代價函數梯度下降的多層BP神經網絡(Back Propagation Neural Network)在概念和一些任務的效果上取得了一些突破.一切在論文中已經詳細討論過的問題在這裏就不再贅述,在這裏主要是擴展性地討論一些論文中未曾涉及過的要點.
本文的定位
首先,本文將回歸論文主體,討論論文的本質核心思想(Core Ideas)尤其是文章的立意與基本假設;之後,本文將分章節地分階段地討論一些與論文內容相關但不重複的內容.最後,總結性地進行適當的擴展.因此,本文的定位是在原論文上的一次“昇華”.
論文的核心思想
論文的核心思想可以概括如下(自頂向下地):
- 計算神經科學是分析學的一個特例:因此許多分析學的理論可以延拓至神經科學爲基礎的學習理論上.(注意:神經網絡的許多理論卻不能反過來變成分析學的通用理論,特殊到一般是不行的)
- 智能過程可以被拆分爲編碼與認知:不嚴格地,可以將編碼直觀理解爲“加工記憶”,將認知直觀理解爲“思考推斷”.
- 編碼過程是尋找一個數值空間來“解構”客觀事物,認知過程是在編碼空間基礎上尋找算子來“模擬”實體之間的抽象概念.
計算神經科學與分析學
神經元的運作特點
在這之前我想先談一談工科生耳熟能詳的幾個數學概念:函數、映射、泛函、算子,這幾個名詞是經常在各個教材中被提及的,但是他們的內涵大家清楚嗎?
-
映射:考慮兩個非空的空間,其元素分別爲和,映射是這樣一個規則:的每一個元素對應一個的元素,這個映射運算符號表示爲
一些有意義的特定的映射是:
- 函數:表示爲,把具有元素的標量空間映射到具有元素的標量空間;
- 泛函:表示爲,把具有元素的函數空間映射到具有元素的標量空間(函數集合到數集上的映射:定義域爲函數,而值域爲實數的"函數");
- 算子:表示爲,把一個函數空間映射到自己中,即是同一函數空間的元素(函數空間到函數空間上的映射.廣義上的算子可以推廣到任何空間,如內積空間等);
明確了以上內容後,現在再來談及神經元的運作特點,最後我們會來看看神經元的運作特點最像哪種映射.
神經元是人體神經系統的基本結構和功能單位,神經系統是無數個神經元相連而成,這是一個非常複雜的網絡系統.神經元間聯繫方式是互相接觸,而不是細胞質的互相溝通.接觸部位的結構特化稱爲突觸,通常是一個神經元的軸突與另一個神經元的樹突或胞體借突觸發生機能上的聯繫,神經衝動由一個神經元通過突觸傳遞到另一個神經元.
生物課上學習到過的神經元結構,比較有意思的是如下兩個組成部分:
- 細胞核周邊冒出來的分支稱爲“樹突”,用於接收刺激信號;
- 中間長長的絲稱爲“軸突”,其末端也有一些分支,和其他神經元的“樹突”連在一起,作用是將信號傳遞給其他神經細胞;
同理,從人體神經系統的構成可以進一步創建更加複雜的人造神經系統,比如我們讓多個人造神經元(感知機)相連接,便形成了人工智能裏面聲稱的“神經網絡”.至此,我們可以給神經元建立一個數學描述:神經元接受一個輸入的生物電信號進而輸出一個生物電信號,而輸入的生物電信號來自多個其他神經元輸出的加權即,也就是:
其中的機制和的形成機制、乃至權重集合的更新機制目前看來還具有很大的不確定性,因此各種神經網絡的改進和創新都是基於這些部分的實際機制來討論的.
因此,我們可以看出,單個神經元的運作特點至少是一個映射,功能上可能是函數或者泛函,但單個神經元的功能應該不能構成算子這麼複雜的映射.至此,我們可以開始引入一些理論來模擬神經元及其集合的功能.
條件數學期望
首先,我們需要回答一個基本問題:在數學上應該如何理解(定義)什麼是隨機過程的“隨機性”?人們總是希望通過(過去和現在)已知的信息或條件來預測“未來”.因此所謂”隨機性”可以理解爲“不可預測性”.那麼,自然要問什麼是“預測”?其實,這很簡單,就是“條件數學期望”,也就是說當我們已知某個或某些隨機變量時,另一個隨機變量在此條件下的期望是多少或者分佈是什麼,也可以理解爲在此條件下的“預測,預估”是多少.因此,我們首先要定義”條件數學期望”,並學會如何計算它.此處的這個概念與(初等)概率論,應用隨機過程條件期望既有區別又有聯繫.而嚴格的定義要用到測度論的知識,在這門課程中對這一概念的引入又比測度論中的直觀簡單… …
_______________北京大學數學科學學院 劉勇
我很認同劉勇老師關於“條件數學期望”這個數學定義的解讀:即“預測”,另一邊,我們仍然需要回到神經元功能上來討論“預測”這個目標是如何聯繫起“條件數學期望”和神經元系統的.
傳統觀點認爲,大腦不斷地接收新的感知信息,並在越來越複雜的信息中尋找規律,然後構建起大腦對環境的認知,這是一種以“由下對上”的控制爲主的感知結構.而預測編碼理論恰恰與此相反,是以“由上對下”的控制爲主的:我們的大腦用已有的模型製造出豐富的感官數據,形成一個預測性的內在世界,去匹配即將發生的真實世界.
這就是Predictive Coding理論:預測編碼理論的擁躉者,或許會在論文中以“實驗結果表明,大腦在處理XXX信號上與貝葉斯優化推理相似”等諸如此類的描述作結論,這樣的結論很容易讓外行人斷章取義爲“大腦就是一個機器”.事實上,可以認爲,依賴預測這個目的大腦神經系統可以收集誤差來優化神經元的參數,而具體的機制是貝葉斯優化推理、隨機梯度下降還是其他的機制尚未蓋棺定論.但是至此,我們有一定的生物學說支撐來聯繫預測機制和分析學裏的條件數學期望以構建類腦的學習模型.
定義(條件數學期望)設爲上的一族隨機變量,爲上的隨機變量,且,稱上的隨機變量是關於的條件數學期望,若滿足:
- ;
- 對於任意有界的上的隨機變量,下式成立:
記或.
而條件數學期望的形式又是條件概率的積分,至此就解釋了爲何在本學位論文中要將神經元的輸出定義爲積分的形式(實際上,傳統神經網絡的加權激活可以視爲離散數學期望).
神經編碼:分而治之的思想
當我們在評價一個學生時,實質上是在評價其成績、品行、體質的優劣程度,也就是我們是先拿出了一系列的“屬性”標準來評判學生,而這些屬性之間往往是獨立不相干的(這非常重要).這種思想可以認爲是“分而治之”的體現,事實上在各個學科對客觀事物的認知中常常體現出這種思想.在數學和計算機科學中,我們就會發現這種思維的體現:
- 在線性代數中,一個內積空間的正交基(orthogonal basis)是元素兩兩正交的基.稱基中的元素爲基向量.假若,一個正交基的基向量的模長都是單位長度1,則稱這正交基爲標準正交基或"規範正交基"(Orthonormal basis).
- DAP模型:第一層是原始輸入層,例如一張電子圖片(可以用像素的方式進行描述);第二層是p維特徵空間,每一維代表一個特徵(例如是否有尾巴、是否有毛等等);第三層是輸出層,輸出模型對輸出樣本的類別判斷.其實就是建立特徵空間與語義空間之間的映射,可以用均方誤差加範數約束訓練:
在論文中我們僅僅給出了神經編碼方式的一種,在這裏我們給出另一種:結合傅里葉變換的神經編碼方式;
本學位論文使用的編碼方式本來是即沿的膜電壓輸出積分(期望),在這裏我們給出另一種可行的方案:
這裏我們直接捨棄了原來的沿積分的方案,至於解碼,就需要用到論文中提及的空間(幾階積分可積的討論)定理,可以推得:空間上的傅里葉變換是不可逆映射,因此這給解碼運算帶來了不便…綜上,我們的神經編碼方式可以套用的傅里葉變換:
傅里葉變換的一個核心思想即爲:找到一個不相干的正交特徵空間,和“分而治之”的思想又一次不謀而合.
認知功能與算子
端到端(end-to-end)模型回顧
相對於深度學習,傳統機器學習的流程往往由多個獨立的模塊組成,比如在一個典型的自然語言處理(Natural Language Processing)問題中,包括分詞、詞性標註、句法分析、語義分析等多個獨立步驟,每個步驟是一個獨立的任務,其結果的好壞會影響到下一步驟,從而影響整個訓練的結果,這是非端到端的.
而深度學習模型在訓練過程中,從輸入端(輸入數據)到輸出端會得到一個預測結果,與真實結果相比較會得到一個誤差,這個誤差會在模型中的每一層傳遞(反向傳播),每一層的表示都會根據這個誤差來做調整,直到模型收斂或達到預期的效果才結束,這是端到端的.
兩者相比,端到端的學習省去了在每一個獨立學習任務執行之前所做的數據標註,爲樣本做標註的代價是昂貴的、易出錯的.
但是端到端模型蘊含的學習成果即爲神經網絡內部的訓練好的權重矩陣,但是這樣一個巨大的高維度矩陣是不可解釋的,其遷移性和可重用性也很差,這是端到端(end-to-end)模型的一個固有缺陷.
什麼是算子
一言以蔽之:函數空間到函數空間的映射,那麼就是對於函數,有一個映射,將其映射到函數上:
算子有很多形式,比如把平面上的點旋轉,連續傅立葉變換等等.在泛函數分析中通常把映照成爲算子,而取值於實數域或者複數域的算子叫做泛函.本文的神經編碼方式即可看作一個函數,那麼不同神經編碼之間的映射關係即可看作算子;由於神經編碼可以看作對客觀事物的記憶加工,因此對這些事物的認知即可類比爲算子,這就是本文引入泛函分析理論的啓發.
定義(有界線性算子)如果一個算子,滿足:
那麼就稱該算子爲線性算子,進一步,若存在滿足對於任何的都有:
則就是一個有界線性算子,注意到,不是有界,而是有界.
有界線性算子這樣的性質具備很好的應用價值,比如本學位論文中的“實體間的關係概念是可累加的”這樣的設定就是依賴有界線性算子的定義.
大腦功能的再探討
現代機器學習的三個特徵:
- 專注於優化成本函數;
- 引入複雜的成本函數:這包括空間和時間上不平均的成本函數,以及網絡內部產生的成本函數;
- 機器學習結構本身的多元化:新發展的結構包括記憶單元,“膠囊”,外部記憶,指針和硬編碼的算術指令等;
成本函數是一個特別寬泛的概念,爲神經系統的活動找到成本函數似乎是一個理所應當的結果.對應這三個特徵,本論文實際上並未引入什麼不同尋常的成本函數,只是用了很傳統的一個範數代價.但是在成本函數的優化和機器學習結構上下了功夫.
這裏要再探討一下“機器學習結構”這個點,本文的一大核心思想即:編碼方式和認知功能是拆分的,編碼是分離的(因此可以在編碼實體間做算子映射,也可以採用各種編碼方式:膜電壓、傅里葉變換等等),認知功能是多元的(一個抽象概念對應一個轉移算子,討論“大小”屬性時並不涉及“顏色”屬性).這樣的設計較端到端模型(end-to-end model)在可解釋性上和複雜任務上的優勢是明顯的.
未完待續
目前該總結的已經說得足夠多了,但是目前至少還有如下問題:
- 計算神經科學爲基礎的連接主義足夠了嗎:畢加索的名畫格爾尼卡(Guernica)中充滿了抽象的牛頭馬面,痛苦嚎哭的人臉,扭曲破碎的肢體.我們可以毫不費力地辨認出這些誇張的幾 何形體.其實,圖中大量信息丟失,但是提供了足夠的整體模式.由此可見,視覺高級中樞忽略色彩、紋理、光照等局部細節,側重整體模式匹配和上下文關係,並可以主動補充大量缺失信息.人們傾向於認爲大腦神經元網絡依隨學習和訓練,其聯結方式會得到優化,但這樣的想法套用到深度學習上就足夠了嗎?本學位論文目前亦未能跳出這個疑點.
- 符號主義應當被遺忘嗎:人工智能中,符號主義的一個代表就是機器定理證明,其巔峯之作是吳文俊先生創立的吳文俊方法.目前機器定理證明的理論根基是希爾伯特定理:多元多項式環中的理想都是有限生成的.我們首先將一個幾何命題的條件轉換成代數多項式,同時把結論也轉換成多項式,然後證明條件多項式生成的理想包含結論對應的多項式,即將定理證明轉換爲理想成員判定問題.和人類智慧相比,人工智能的符號主義方法依然處於相對幼稚的階段.
- 如何深入結合符號主義和連接主義:首先,就如同代數學是對分析學的抽象一樣,符號學的數學內容也比神經網絡的數學要複雜很多,也許足夠深入的對代數學的學習才能觸及智能方法的真正進步.
實現代碼的numpy demo:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import numpy as np;
class Encode_Layer(object):
"""docstring for ClassName"""
def __init__(self, encode_dim):
super(ClassName, self).__init__();
self.encode_dim = encode_dim;
self.en_weights = 0.0;
self.de_weights = 0.0;
self.input_dim = 0;
self.code = 0.0;
self.X_output = 0.0;
self.X_input = 0.0;
self.delta = 0.01;
# 編碼模塊;
def encode(self,X_input,weights=0.0):
self.input_dim = len(X_input);
if self.en_weights == 0.0:self.en_weights = np.random.rand(input_dim,encode_dim);
#self.code = np.dot(np.fft.fft(X_input),self.weights);
#return self.code;
if weights==0.0:return np.dot(np.fft.fft(X_input),self.weights);
return np.dot(np.fft.fft(X_input),weights);
# 解碼模塊;
def decode(self,code,weights=0.0):
if self.de_weights == 0.0:self.de_weights = np.random.rand(encode_dim,input_dim);
#self.X_output = np.fft.ifft(np.dot(self.code,self.weights));
#return self.X_output;
if weights==0.0:return np.fft.ifft(np.dot(code,self.weights));
return np.fft.ifft(np.dot(code,weights));
# 計算誤差模塊;
def error(self,W,weights_class,X_input):
if weights_class=='encode':
X_output = self.decode(self.encode(X_input,W));
return np.linalg.norm(X_input - X_output);
if weights_class=='decode':
X_output = self.decode(self.encode(X_input),W);
return np.linalg.norm(X_input - X_output);
# 求梯度模塊;
def grad(self,weights_class,i,j,X_input):
if weights_class == 'encode':
weights = self.en_weights[i][j];
weights[i][j] = weights[i][j] + self.delta;
return error(weights,weights_class,X_input)/self.delta;
if weights_class == 'decode':
weights = self.en_weights[i][j];
weights[i][j] = weights[i][j] + self.delta;
return error(weights,weights_class,X_input)/self.delta;
# 更新參數模塊;
def update(self,X_input):
for i in range(len(self.en_weights)):
for j in range(len(self.en_weights[i])):
self.en_weights[i][j] = self.en_weights[i][j] - self.nita * grad('encode',i,j,X_input);
for i in range(len(self.de_weights)):
for j in range(len(self.de_weights[i])):
self.de_weights[i][j] = self.de_weights[i][j] - self.nita * grad('decode',i,j,X_input);