记一个神经网络中出现的混沌图样
?作者 |?葉耿杰
單位 |?武漢大學
研究方向 |?凝聚態物理
自己寫的 numpy BP 神經網絡(擬合 [0,2] 上的函數 sin(2 pi x)/4+0.5 )第一次跑通,興奮之余胡亂修改了網絡結構。看著 loss 曲線時而下降,時而振蕩,突然,有一個網絡(代碼附在文末)啥也沒學到,但給出了熟悉的圖案:
趕緊把 loss 曲線的數據小心翼翼的導出來,作散點圖,果不其然:
原來訓練集為間隔 0.1 采樣,反復試驗均無分岔圖案;當訓練集改為間隔 0.05 采樣時,出現了分岔圖案。
反復運行了好幾次,都能出現這種圖樣,說明與參數初始化無關。
抽去了一個 2 節點 /sigmoid 激活函數的層,仍然出現了類似的分岔圖(代碼是抽去之后的)。
沒有照抄大佬的代碼,以下代碼說不定有錯;但經過試驗,取間隔 0.1 采樣的數據集,很小的網絡規模(1,3,3,1,全 sigmoid),還是能看出它在試圖擬合一個函數的。
第一次更新:
補充一些實驗結果:
@Horizony大佬 和 @鎮戎大佬 所言甚妙:將待擬合的函數換成常函數 y=0.5,仍然出現相同的圖樣。
分岔的發生與采樣點的密度有關;而分岔“合并”的速率與學習率的衰減有關:學習率衰減越快,分岔合并得也越快。不負責任地推測,假設那兒已經有了一張完整的分岔圖,而加密采樣點起“平移”作用,加快/減慢學習率衰減起伸/縮作用。
注意到上圖的散點圖出現了一些臺階的特征,這是不必要的設置——每 100 次迭代衰減一次學習率造成的。去除這個設置后,散點圖顯得更平滑(相應地可以將迭代次數減少到百分之一)。
混沌很可能是 sigmoid 激活函數導致的,而 relu 不起作用:單層,甚至單個 sigmoid 神經元足以產生分岔圖樣,雖然并不像 Logistic。
解析計算似乎有希望了?
目前為止,最令我驚訝的是,我本以為最無關的參數——采樣間隔,反而是控制著分岔圖樣出現的最關鍵參數,實在匪夷所思。
會不會有這么一種可能:每個神經網絡的 loss 曲線都是某個混沌映射的反向的圖樣,只是平時因為位置或伸縮不合適而看不出來?
第二次更新:
非常感謝各位大佬推薦的論文和從動力系統的角度做出的解答…… 同時深刻地意識到自己實在太菜,看懂它們可能還得幾年。(′;ω;`)
再補充一些現象層面的東西:
正如?@年輕詩人?所說的,學習率增長就能得到正向的分岔圖。
學習率和采樣間隔似乎滿足某種尺度不變性。比如下面四張圖:
分別是固定學習率每次迭代增長 0.01%,而樣本間距與初始學習率的組合分別為
(0.05,0.3),
(0.005,0.03),
(0.0005,0.003),
(0.00005,0.0003)
的 loss 曲線(只使用單個神經元)。雖然 3 -周期的位置有細微的差別,但是比起其他的參數組合,它們確實非常相似。
如果這樣的假設是正確的,那么一方面,分岔可能在學習率很低的情況下發生,只要訓練集密度夠高;另一方面,訓練集的元素甚至可以只有一個,只要學習率高到離譜,同樣可以觸發分岔。
對于后者,實驗表明確實如此。由于不用迭代上百次就能求得周期點(中的一個)的位置,這可能可以用于編寫快速生成分岔圖的算法。鑒于其只有一個神經元、過高學習率以及不再要求網絡學到訓練集的特征的特點,可能已經偏離了神經網絡的問題,因此,請詳見:https://zhuanlan.zhihu.com/p/567118016
第三次更新:
上面那個算法并不準確,大家還是忘了它吧
按照@年輕詩人的意見,訓練集大小與學習率之間的關系是因為我在對 MSE 求導的時候沒有正確地歸一化(忘記除以樣本數目 n),導致訓練集擴大等效于學習率增大相同的倍數。
同時他敏銳地發現起作用的是最后一層,并給出了權重 迭代的表達式(見評論區)。這個迭代之所以能生成(類似)Logistic 映射的分岔圖,是因為它本身,在 取絕對值的意義下,與 Logistic 映射的迭代式 的圖象非常接近,而學習率恰好占據了 Logistic 映射中參數 的位置。
@Peter Griffin 的回答對我大有啟發。對于一個優化問題,大的步長確實會帶來混沌的可能。以一維梯度下降為例,在極值點的附近,我們可以對勢函數做簡諧近似,即展開到二階項。由于常數項、一次項系數可以通過平移消去,二次項系數可以通過重新選取長度量綱而歸一,我們實際上只要考慮
代入梯度下降的公式
可得
這個數列的命運隨 取值不同,只有單調收斂、振蕩收斂、周期為 2 的振蕩( ),和振蕩發散四種。
而在極值點附近稍遠一點的地方,簡諧近似失效,我們可以將勢函數展到三次項,考慮
其極小值點仍為 ,迭代關系為
可以看到,適當選取系數,它完全可以變成 Logistic 映射。用 Mathematica 硬解方程 ,這是一個 8 次方程,其中兩個根是不動點,兩個根是周期 2 的點,4 個根是周期 3 的點。決定周期 3 有實根的條件是,根式中的
其解為 大于等于有關 的某個值。這就說明,當學習率 足夠大時,我們就得到了產生任意周期和產生混沌的充分條件——實的、連續的、在極值點附近有界的、具有周期 3 的映射。
(用這種方式算出的 值似乎大于模擬出來的出現混沌時對應的 值)
而 值在一段時間中不變,給了 充足的時間,以演化到收斂、周期或混沌的軌道上,從而形成清晰的分岔圖。
由此可以猜測,在一個優化問題中,目標附近的非諧效應(幾乎總是存在)、過大的學習率和過慢的學習率下降是產生分岔和混沌的誘因。
import?numpy?as?np import?copy?as?cp import?matplotlib.pyplot?as?pltdef?sig(x):return?1?/?(1?+?np.exp(-x))def?dsig(x):s?=?sig(x)return?np.exp(-x)?*?s?*?sdef?relu(x):if?x?<?0:?return?0.1?*?xreturn?xdef?drelu(x):if?x?>=?0:?return?1return?0.1Sig?=?(sig,?dsig) Relu?=?(np.vectorize(relu),?np.vectorize(drelu))class?ConnectLayer:def?__init__(self,?inp,?output,?func_tup,?lrate_tup):self.inp?=?inpself.output?=?outputself.func?=?func_tup[0]self.dfunc?=?func_tup[1]self.lrate?=?lrate_tup[0]self.lrate_decay?=?lrate_tup[1]self.decay_cnt?=?0self.w?=?np.random.random((output,?inp))self.b?=?np.random.random((output,?1))self.yjs_cache?=?Noneself.xjs_cache?=?Nonedef?forward(self,?xs):#?n?=?xs.shape[1]?self.xjs_cache?=?xsyjs?=?self.w?@?xs?+?self.bself.yjs_cache?=?yjsreturn?self.func(yjs)def?backF(self,?upstream):??#?f對y求導return?self.dfunc(self.yjs_cache)?*?upstreamdef?backPass(self,?upstream):??#?y對x求導return?self.w.T?@?upstreamdef?refw(self,?upstream):??#?y對w求導return?upstream?@?self.xjs_cache.T?def?refb(self,?upstream):??#?y對b求導#?注意:不要直接使用np.sum,否則行列不穩定db?=?np.sum(upstream,?axis=1)return?db.reshape(self.b.shape)def?backward(self,?upstream):?M?=?self.backF(upstream)self.w?-=?self.lrate?*?self.refw(M)self.b?-=?self.lrate?*?self.refb(M)self.decay_cnt?+=?1if?self.decay_cnt?==?100:self.lrate?*=?self.lrate_decayself.decay_cnt?=?0return?self.backPass(M)def?g(x):return?np.sin(2?*?np.pi?*?x)?/?4?+?0.5def?genData():xs?=?np.arange(0,?2.001,?0.05)n?=?len(xs)ys?=?[]for?x?in?xs:ys.append(g(x))ys?=?np.array(ys)return?xs.reshape(1,?n),?ys.reshape(1,?n)def?mse(fjs,?yjs):?m,?n?=?yjs.shapedelta?=?fjs?-?yjsreturn?np.sum(delta?*?delta)?/?m?/?ndef?dmse(fjs,?yjs):return?2?*?(fjs?-?yjs)Mse?=?(mse,?dmse)class?ScalarLayer:def?__init__(self,?inp,?answer,?loss_tup):self.inp?=?inpself.answer?=?answerself.loss?=?loss_tup[0]self.dloss?=?loss_tup[1]def?forward(self,?fyjs):return?self.loss(fyjs,?self.answer)def?backward(self,?fyjs):??#?L對f求導return?self.dloss(fyjs,?self.answer)class?nn:def?__init__(self,?layer_msg,?funcs_msg,?loss,?xs,?ys,?lrate):self.layer_msg?=?layer_msgself.layers?=?[]self.xs?=?xsself.ys?=?ysfor?i?in?range(len(layers_msg)?-?1):self.layers.append(ConnectLayer(inp=layers_msg[i],output=layers_msg[i?+?1],func_tup=funcs_msg[i],lrate_tup=lrate))self.outlet?=?ScalarLayer(1,?ys,?loss)def?train(self,?TURNS):ls?=?[]for?t?in?range(TURNS):#?單步訓練data?=?cp.deepcopy(self.xs)for?lay?in?self.layers:data?=?lay.forward(data)L?=?self.outlet.forward(data)data?=?self.outlet.backward(data)for?lay?in?self.layers[::-1]:data?=?lay.backward(data)ls.append(L)return?lsdef?test(self):newxs?=?np.hstack((self.xs?-?1,?self.xs?+?1))data?=?cp.deepcopy(newxs)for?lay?in?self.layers:data?=?lay.forward(data)return?newxs,?dataif?__name__?==?'__main__':xs,?ys?=?genData()layers_msg?=?[1,?4,?2,?2,?1]funcs_msg?=?[Relu,?Sig,?Sig,?Sig]model?=?nn(layers_msg,?funcs_msg,?Mse,?xs,?ys,?lrate=(0.3,?0.999))ls?=?model.train(40000)plt.plot(ls)plt.show()print('end')更多閱讀
#投 稿?通 道#
?讓你的文字被更多人看到?
如何才能讓更多的優質內容以更短路徑到達讀者群體,縮短讀者尋找優質內容的成本呢?答案就是:你不認識的人。
總有一些你不認識的人,知道你想知道的東西。PaperWeekly 或許可以成為一座橋梁,促使不同背景、不同方向的學者和學術靈感相互碰撞,迸發出更多的可能性。?
PaperWeekly 鼓勵高校實驗室或個人,在我們的平臺上分享各類優質內容,可以是最新論文解讀,也可以是學術熱點剖析、科研心得或競賽經驗講解等。我們的目的只有一個,讓知識真正流動起來。
📝?稿件基本要求:
? 文章確系個人原創作品,未曾在公開渠道發表,如為其他平臺已發表或待發表的文章,請明確標注?
? 稿件建議以?markdown?格式撰寫,文中配圖以附件形式發送,要求圖片清晰,無版權問題
? PaperWeekly 尊重原作者署名權,并將為每篇被采納的原創首發稿件,提供業內具有競爭力稿酬,具體依據文章閱讀量和文章質量階梯制結算
📬?投稿通道:
? 投稿郵箱:hr@paperweekly.site?
? 來稿請備注即時聯系方式(微信),以便我們在稿件選用的第一時間聯系作者
? 您也可以直接添加小編微信(pwbot02)快速投稿,備注:姓名-投稿
△長按添加PaperWeekly小編
🔍
現在,在「知乎」也能找到我們了
進入知乎首頁搜索「PaperWeekly」
點擊「關注」訂閱我們的專欄吧
·
·
總結
以上是生活随笔為你收集整理的记一个神经网络中出现的混沌图样的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: maya支持的音频格式和时间线显示方法
- 下一篇: IPFS技术逐渐走到关键时刻!留给散户的