Python · 神經網路(零)· 簡介

(這裡是最終成品的 GitHub 地址)

由於神經網路聽上去最厲害(?),所以打算先講一講這部分

題圖算是最終結果中 CNN 的一個比較簡易的呈現;可能有些童鞋還不知道是什麼意思,可能有些大神已經看出了一些端倪。總之,我個人的習慣是先說明最終能幹什麼、然後再來說怎麼實現,這樣也能避免一些不必要的信息篩選。所以,這一部分主要用於讓已經知道一定的基礎知識的童鞋知道最後能走多遠,如果是想從頭開始學的話可以無視這一章直接看第一章

圖文並茂是個不錯的選擇(廢話

  1. 普通的神經網路,支持各種常見的激活函數如 Sigmoid、Tanh、ReLU 等。

    以下代碼定義了一個含單層隱藏層的、含有 400 個神經元的、以 ReLU 為激活函數、以 Cross Entropy 為損失的神經網路:

    nn.add("ReLU", (x.shape[1], 400))nn.add("CrossEntropy", (y.shape[1], ))

    實際應用中可用來訓練著名的手寫字體數據集 Mnist,10 次迭代後正確率有 98%

    如果想更簡單粗暴點的話:

    nn.build([x.shape[1], 24, 24, y.shape[1]])

    這定義了一個含兩個隱藏層的、每層有 24 個神經元的、以 Sigmoid 為激活函數、以 Cross Entropy 為損失的神經網路

  2. 普通的 CNN,只要在 1. 中支持的激活函數、這裡都會支持

    題圖就是一個例子;當然題圖那種結構不怎麼用於實際(其實也可以用但更多是覺得好玩才定義了諸如 (3, 1) 和 (1, 3) 這樣的 Filter……)(但其實這種 Filter 有時很厲害的,這裡按下不表)。有如下三個功能是值得一提的:
    1. padding 即可以自己定義(對應 tensorflow 中的 VALID)也可以直接用 SAME(默認是 SAME)
    2. 支持兩種附加層 Dropout 和 Normalize;不僅是在普通神經網路中支持(參見下面的栗子),在 CNN 中也支持(參見下面分形 CNN 中的栗子)

    3. 可以用循環體來堆重複的結構,比如:

      nn.add("ConvReLU", (x.shape[1:], (32, 3, 3)))for _ in range(100): nn.add("ConvReLU", ((32, 3, 3), ))nn.add("ReLU", (512, ))nn.add("ReLU", (64, ))nn.add("Normalize")nn.add("Dropout")nn.add("CrossEntropy", (y.shape[1], ))

      當然了,我相信上面這個 103 層的玩意兒不宜使用……但它沒 bug(科科)

  3. 分形的 CNN,典型例子就是 GooLeNet,下面放出一個長得比較像的實現

    1. 用函數體把每一塊分形封裝起來:

      def add_layers(_nn): _nn.add("Pipe", 3) _nn.add_pipe_layer(0, "ConvReLU", ((32, 1, 3), )) _nn.add_pipe_layer(0, "ConvReLU", ((32, 3, 1), )) _nn.add_pipe_layer(1, "ConvReLU", ((32, 2, 3), )) _nn.add_pipe_layer(1, "ConvReLU", ((32, 3, 2), )) _nn.add_pipe_layer(2, "ConvReLU", ((32, 1, 1), )) _nn.add_pipe_layer(2, "Pipe", 2) _pipe = nn.get_current_pipe(2) _pipe.add_pipe_layer(0, "ConvReLU", ((16, 1, 3), )) _pipe.add_pipe_layer(1, "ConvReLU", ((16, 3, 1), ))

    2. 堆!!!

      nn.add("ConvReLU", (x.shape[1:], (32, 3, 3)))nn.add("ConvReLU", ((32, 3, 3), ))nn.add("MaxPool", ((3, 3), ), 2)nn.add("ConvNorm")add_layers(nn)nn.add("MaxPool", ((3, 3), ), 2)nn.add("ConvNorm")add_layers(nn)nn.add("AvgPool", ((3, 3), ), 2)nn.add("ConvNorm")add_layers(nn)nn.add("ReLU", (512, ))nn.add("ReLU", (64, ))nn.add("Normalize")nn.add("CrossEntropy", (y.shape[1], ))

  4. 可擴展性。個人認為在這方面我還是下了比較大功夫的、所以可擴展性應該算不錯。目前的話,用 tensorflow 的話支持自定義激活函數,如果用我自己寫的 Numpy 演算法(大概這東西叫做一個新的 nn 輪子?不太懂相關定義……)的話就還支持自定義 Optimizer。由於使用我自己寫的演算法來進行擴展的話,雖然自由度更高、但需要更多相關的基礎知識,我打算把它放在一個額外的章節來講,這裡就只說怎麼在使用 tensorflow 的情況下自定義激活函數吧;經評論區的童鞋提醒,我打算講兩種自定義方式:
    1. 在原有的激活函數上修改。比如我覺得 ReLU 在處理輸入很大時的方法太豪放了想讓它安分一點、給激活後的輸出一個上界 6 的話(返回 min(max(x, 0), 6)) (這東西好像叫 ReLU6 ?),我可以這樣把原來的 ReLU 類

      class ReLU(Layer): def _activate(self, x, predict): return tf.nn.relu(x)

      改成

      class ReLU(Layer): def _activate(self, x, predict): return tf.minimum(tf.maximum(x, 0), 6)

      就行了,同時該激活函數會自動被 ConvReLU、也就是 CNN 中使用的 ReLU 繼承

      是不是相當方便呢 ( σ"ω")σ

    2. 可能會有觀眾老爺說,你這個只是把現有的東西偷偷換了一下,如果我想定義一個屬於自己的、名字也是自己隨便定的激活函數怎麼辦?!

      不妨將我們新的激活函數叫做 CF0910,它返回的值同樣是 min(max(x, 0), 6) :
      1. 定義 CF0910 類並繼承 Layer 類,同時定義其激活函數:

        class CF0910(Layer): def _activate(self, x, predict): return tf.minimum(tf.maximum(x, 0), 6)

      2. 在下面的「工廠」裡面「註冊」一下這個層的信息:

        class LayerFactory: available_root_layers = { "CF0910": CF0910, ...

      3. 完了。

是的沒錯,這就完了!!嶄新的激活函數出現了!!拿著它去玩吧觀眾老爺們!!各種發 Paper 拿大獎不是夢!!(不

有些細心的觀眾老爺可能會發現:咦你這不是只定義了普通的神經網路層嗎?那如果我想把這個激活函數應用到 CNN 裡面呢?

嗯,為此你可以這樣做:

    1. 定義一個繼承了各種亂七八糟的東西的類(重點:順序不能錯!)(重點2:繼承的第二個類就是剛剛定義的那個類!):

      class ConvCF0910(ConvLayer, CF0910, metaclass=ConvLayerMeta): pass

    2. 再去工廠註冊一下:

      class LayerFactory: available_root_layers = { "CF0910": CF0910, "ConvCF0910": ConvCF0910, ...

    3. 完了。

是不是很棒???(不

誇我吧!贊我吧!!GitHub 上面 star 我吧!!!(喂

那麼怎麼應用呢?方法是一樣一樣的:

nn.add("ConvCF0910", (x.shape[1:], (32, 3, 3)))for _ in range(100): nn.add("ConvCF0910", ((32, 3, 3), ))nn.add("ReLU", (512, ))nn.add("ReLU", (64, ))nn.add("Normalize")nn.add("Dropout")nn.add("CrossEntropy", (y.shape[1], ))

Again,建議不要真的去跑這個結構……

如果想要了解我自己造的那個自由度很高的輪子的話,歡迎私信或者等我以後的附加章節~

此外,1. 和 2. 的結構是可以保存下來的(3. 的還沒寫因為感覺好麻煩)(喂):

nn.save()

讀取也很方便:

nn.load()

然後可視化也做了一定的工作、包括但不限於:

  1. 預覽結構

  2. 進度條

  3. 耗時統計

  4. CNN 結果的直觀理解

  5. 以及其它……要說的話我還用 cv2 弄了一個動態的可視化:

========== 分割線的說 ==========

以上就是一個大概的功能呈現,感覺還是不錯的(驕傲臉)(被打飛)

下一章開始就是從零起步了,具體而言會分為三種:

  1. Python 的基礎,這一部分文章的標題形如 「Python · ***」
  2. 實現的思想、原理及代碼,這一部分的標題就和這一章的類似,隨介紹的演算法不同而不同
  3. 數學的基礎,這一部分的標題也與這一章類似,只不過前綴改為 「數學 · *** · ***」
  4. 額外的章節,這些章節的標題形如「Python · 神經網路(二*) · 層」、也就是說章節數的右上角會帶一個星號。在這些的章節中,我會介紹我自己寫的、只用到 Numpy 的演算法,主要目的是服務想了解演算法細節的觀眾老爺們 ( σ"ω")σ

自然,1. 的知識是各機器學習演算法實現都共享的,2. 3. 的內容有一定相關性但分開來看不會有什麼問題(大概)(喂

希望觀眾老爺們能夠喜歡~

(猛戳我進入下一章!( σ"ω")σ)


推薦閱讀:

譯文:機器學習的首要條件不是數學而是數據分析
零基礎學習Python數據挖掘(修改版)
用 TensorFlow 訓練 Doom 機器人
機器學習領域,如何選擇研究方向?
如何評價 2017 年 IHMSC 上發表的探測流量的論文?

TAG:Python | 神经网络 | 机器学习 |