使用python實現深度神經網絡 2(轉)

https://blog.csdn.net/oxuzhenyi/article/details/73026796html

導數與梯度、矩陣運算性質、科學計算庫numpy

1、實驗介紹

1.1 實驗內容

雖然在實驗一中我想盡可能少的引入(會讓人放棄繼續學習的)數學概念,但我彷佛仍是失敗了。不過這幾乎是沒有辦法的事,要想真正學會深度學習,沒有必定的數學基礎(高等數學、線性代數、機率論、信息論等),(幾乎)是不可能的。學深度學習不學其中的原理你可能可以學會搭建模型,但當模型出了問題或者沒法訓練出好的結果時,不懂原理是很難調試的。python

不過話說回來,要想理解深度學習中的基本概念(而不是想要在深度學習領域作研究),要學的數學知識也不是很難。你應該很快就能掌握這些知識。算法

因此本次實驗課,咱們介紹本課程會涉及到的數學知識以及在以後「 圖片英文字母識別」的項目中要用到的python numpy模塊。shell

警告:本次實驗介紹的數學知識只是爲了讓你更好地理解本課程中的相關概念,有些地方不夠嚴謹,請勿等同於數學教科書參考編程

1.2 實驗知識點

  • 導數、偏導、梯度、鏈式法則
  • 矩陣運算基本法則
  • numpy基本運算介紹

1.3 實驗環境

  • python 2.7
  • numpy 1.12.1

2、實驗步驟

2.1 導數、偏導、梯度、複合函數求導鏈式法則

2.1.1 函數值隨自變量的變化速率--導數

高中數學裏面咱們已經學過,函數值隨自變量的變化速率是導數。導數衡量的,實際上是一個變量對函數值影響能力的大小。導數值越大,則該變量每改變一點對最終函數值的影響越大。且導數值爲正時,表明自變量增大時函數值增大,反之若導數值爲負,則自變量增大時函數值減少。
常見函數的導函數:數組

原函數f 導函數f'
任何常數 0
x 1
e^x e^x
x^2 2*x
1/x -1/x^2
ln(x) 1/x

2.1.2 從單變量到多變量--偏導

上面咱們列舉的都是隻有一個自變量的函數,若是自變量有多個,如何求導數呢?好比對於函數f=x+y,怎樣衡量x和y分別對函數值f的影響快慢呢?
數學上引入了偏導的概念,對一個多變量函數f,求f對其中一個自變量x的偏導很簡單,就是將與x無關的其餘自變量視爲常亮,再使用單變量求導的方法去求導。獲得的即爲f對x的偏導。好比:網絡

令f=x+2y, 則f對x求偏導的結果爲1,對y求偏導的結果爲2。
令f=x*y, 則f對x求偏導結果爲y,對y求偏導結果爲x。dom

2.1.3 多變量函數變化最快的方向--梯度

2.1.1 中咱們提到了,對單變量函數來講,導數值的正負表明自變量對函數值影響的「方向」:變大或變小。那對於多變量函數來講,如何表達這個方向呢?這就引入了梯度的概念:函數

梯度是一個向量,向量長度與自變量的個數相等,且其中的每個元素爲函數對於對應變量求偏導的值。學習

好比對於函數f=x*y, 其梯度向量爲(y,x), 對於具體的自變量的值,好比x=1,y=1的點,其梯度向量就爲(1,1), 又好比x=10,y=-20點,其梯度向量就爲(-20,10)

梯度做爲一個向量,指向的是使函數值增大最快的方向(回想第一次實驗中的損失函數圖,梯度所指的方向是向上的)。

2.1.4 複合函數求導鏈式法則

上面咱們講的求導數和求偏導,都是對於「簡單函數」,對於「複合函數」,好比下面這樣的函數:

  1.  f1(x)= 1/x
     
  2.   f2(x)=e^x
     
  3.  
     
  4.  f=f1(f2(x))
     

f函數是一個複合函數,它由f1f2函數「串聯」而來。其中f1的輸入是f2的輸出。

對於複合函數求導,一種方法是將複合函數展開,好比對於上面的函數, 獲得f=1/(e^x),而後再根據簡單函數求導法則對自變量求導。過程以下:
f' = -1/((e^x)^2)*((e^x)') = -(e^x)/((e^x)^2) = -1/(e^x)
即 f' = -1/(e^x)

其實,在上面的求導過程當中,咱們已經使用了求導鏈式法則(chain rule),只是你沒有察覺而已。求導鏈式法則讓咱們能夠一部分一部分地,對複合函數求導,而不用放在一塊兒求。這對於編程來講十分重要,它使得對複合函數求導變得十分簡單。
可是這裏描述起來可能稍顯複雜。以f爲例,當咱們須要對自變量x求導時,咱們能夠先將f2(x)看作一個自變量f2,先讓f1f2求導,獲得第一部分導函數-1/(f2^2),再讓f2x求導,獲得第二部分導函數e^x。求好以後,直接將兩部分導數乘起來,即獲得最終複合函數總體的導數。不過要先使用實際的表達式替換掉第一部分導數中的f2, 即第一部分導數爲-1/((e^x)^2), 第二部分導數爲e^x。兩部分乘起來就獲得了最終正確的-1/(e^x)

如今你可能以爲這個鏈式法則是複雜乏味的,可是下一次實驗你會發現鏈式法則真是太強大了。實際上,咱們最後實現的深度神經網絡,就是不斷在運用求導鏈式法則。

2.2 矩陣及其基本運算性質

若是你上過本科線性代數課程,你十有八九會對矩陣沒有什麼感受,甚至對這麼一個運算法則十分奇怪的東西感到厭惡。但我但願你從此能改變對矩陣、對線性代數的見解,不要讓糟糕的教材和老師糟糕的ppt毀掉線性代數可能帶給你的巨大的提高本身(是的,這並不誇張)的機會。矩陣其實很是很是很是有用,在現代科學的每個角落,幾乎都能看到矩陣的身影,深度學習中更是如此。

限於篇幅,本節只會介紹必要的矩陣相關知識,線性代數中的更多東西,請你經過其餘途徑學習(推薦使用英文教材學習)。

2.2.1 矩陣的表達形式

一個m*n的矩陣爲一個m行n列的數組,好比:

此處輸入圖片的描述

a是一個3*2的矩陣,b是一個2*3的矩陣,c是一個3*1的矩陣,d是一個1*2的矩陣。

其中,c只有一列,咱們也能夠稱c列向量d只有一行,咱們也能夠稱d行向量。本課程中,對於向量,默認都是指列向量

2.2.2 矩陣的運算法則

  1. 矩陣的數乘運算
    一個標量(你能夠直接理解爲一個數字)乘以矩陣,獲得的結果爲矩陣中的每一個元素和該標量相乘,以下圖:
    此處輸入圖片的描述
  1. 矩陣的轉置運算
    轉置運算經過在矩陣右上角添加一「撇」表示。
    此處輸入圖片的描述
    轉置就是矩陣翻轉一下,轉置會改變矩陣的形狀。注意觀察轉置是繞着哪一個軸翻轉的。

  2. 矩陣之間的加減法
    矩陣之間的加減法要求參與運算的兩個矩陣尺寸相同,運算的結果等於兩個矩陣對應元素相加減。
    此處輸入圖片的描述

  3. 矩陣魔力的來源--矩陣之間的乘法
    矩陣的乘法有些複雜,但在第一講實驗中你已經見過它了。矩陣的乘法其實就是表明了一個線性方程組參數和自變量如何結合的過程(矩陣乘法還有更多豐富的含義,若有興趣,請你本身去探索)。
    此處輸入圖片的描述
    矩陣乘法的具體規則就是,第一個矩陣中的第i行的全部元素,與第二個矩陣中的第j列的全部元素,分別相乘以後再求和,獲得結果矩陣中第i行第j列的元素。
    上面的描述只看一遍很難弄懂,請你結合圖片中的例子仔細揣摩。
    矩陣乘法首先要求參與乘法運算的兩個矩陣的尺寸可以「兼容」,具體的要求就是,第一個矩陣的列數與第二個矩陣的行數必須相同。你能夠觀察圖片中的示例,第一個矩陣的列數都是2,第二個矩陣的行數也都是2,這樣才能保證「第一個矩陣中的第i行全部元素」與「第二個矩陣中第j列的全部元素」可以一一對應。
    矩陣乘法運算獲得的結果矩陣,其行數等於第一個矩陣的行數,其列數等於第二個矩陣的列數。
    矩陣乘法不知足交換律!!首先,交換兩個矩陣的位置以後它們的尺寸不必定可以兼容,而後即便兼容,運算獲得的結果也不必定與原來相同。你能夠本身隨便舉幾個例子試一下。

2.3 科學計算庫 numpy

實現咱們的深度神經網絡,須要進行不少數學運算,尤爲是矩陣運算。而你也看到了,矩陣的(乘法)運算很複雜,本身編程實現比較困難並且容易出錯。爲了解決這些問題,咱們將會使用python中的科學計算庫numpy。有了numpy, 咱們的代碼將大大簡化,同時速度也會有很大提高。

2.3.1 使用numpy

實驗樓環境已經安裝了numpy,使用import語句導入便可,爲了簡化代碼,導入後咱們將numpy命名爲np。

  1.   import numpy as np
     
  2.   print numpy.__version__ # 查看numpy版本
     

當你使用numpy進行計算時,在terminal 裏輸入top命令,你會發現有多個"同樣"的python進程在運行,這是由於numpy會自動進行多進程運算,提升計算速度。

>> top

如下的實例請你本身在python shell 中一塊兒實驗一遍。

2.3.2 numpy基本數據類型

numpy中的數據類型被稱爲ndarray(即 N-dimensional array,多維數組),建立一個ndarray很簡單:

  1.   import numpy as np
  2.   
  3.  array=np.array([ 1,2,3],dtype=np.uint8)
  4.   print array

即向np.array()函數傳入一個python列表便可。注意dtype參數是可選的,它指定了生成的數組的數據長度和類型,這裏是長度爲8bit的無符號整數。

2.3.3 快速建立矩陣

mat1=np.zeros((2,3)) 

np.zeros()快速建立一個指定維度的全0矩陣,注意傳進去的參數是一個tuple

2.3.4 numpy中的高維矩陣

"矩陣"通常指有行和列的「二維」矩陣,但numpy還支持高維矩陣,好比下面:

  1.  nd=np.zeros(( 1,2,3,4))
  2.   print nd.shape
  3.   print nd.size

nd就能夠看做是一個1x2x3x4尺寸的高維矩陣。ndarray.shape保存的是數組的「形狀」,也就是高維矩陣每一維的長度。ndarray.size保存的是數組每一維長度相乘的結果,即數組元素的個數。

2.3.5 標準矩陣運算

首先你要注意的是,numpy中的運算和數學中的運算不是徹底同樣的,實際上,numpy不只爲咱們提供了標準運算,還提供了更多方便咱們編程的運算類型和特性。

咱們先來看標準的矩陣運算:

  1. 標量與矩陣相乘
    1.  scalar= 2
    2.  mat=np.zeros(( 2,3))
    3.  mat1=scalar*mat
  2. 矩陣轉置
    1.  mat=np.zeros(( 2,3))
    2.  tmat=mat.T
    3.   print mat.shape, tmat.shape
    4.  mat3=np. array((1,2,3))
    5.  tmat3=mat3.T
    6.   print mat3.shape, tmat3.shape
    對於二維矩陣,ndarray.T便可獲得其轉置。對於高維矩陣,ndarray.T會將維度的順序徹底翻轉(順序逆過來)。
  3. 矩陣相加
    1.  mat1=np.array([[ 1,2],[3,4]])
    2.  mat2=np.array([[ 1,0],[0,1]])
    3.  mat3=mat1+mat2
  4. 矩陣乘法
    1.  mat1=np.array([[ 1,2],[3,4]])
    2.  mat2=np.array([[ 5,6],[7,8]])
    3.  mat3=mat1.dot(mat2)
    注意這裏有一些變化,矩陣相乘不能直接使用*號,而是經過.dot()函數

2.3.6 擴展運算

numpy內置的擴展運算用起來很是方便。

  1. 兩個矩陣的對應元素相乘(內積?)

    1.  mat1=np.array([[ 1,2],[3,4]])
    2.  mat2=np.array([[ 5,6],[7,8]])
    3.  mat=mat1*mat2

    注意相乘的兩個矩陣尺寸必須相同。

  2. 標量與矩陣相加

    1.  scalar= 2
    2.  mat=np.array([[ 1,2],[3,4]])
    3.  mat1=scalar+mat

    標量與矩陣相加就至關於對矩陣的每一個元素都加上該標量。

  3. 操縱高維矩陣的維度

    1.  mat3=np.zeros(( 1,2,3))
    2.  tmat3=mat3.transpose( 0,2,1)
    3.   print mat3.shape,tmat3.shape

    有時候,咱們想要改變高維矩陣維度的順序,但ndarray.T只能徹底翻轉,沒法知足咱們的需求,這個時候就能夠調用ndarray.transpose(),其參數表明本來矩陣的維度從新排列的順序。因此這裏的例子實際上至關於第0維不變,第1第2維交換。

  4. broad cast--拓寬操做
    國內有些人將numpy的broadcast按照字面意思翻譯爲「廣播」,這樣顯然是容易誤導人的。根據broadcast在numpy中的實際做用,我我的更傾向於將braodcast 拆開並翻譯爲「拓寬」(向更寬的矩陣拓展)。其具體做用爲:
    當兩個矩陣進行加/減法運算時,好比咱們須要將一個列向量加到一個矩陣的每一列上,因爲尺寸不一樣,沒法直接進行運算,一種直接粗暴的作法就是循環遍歷矩陣的每一列,再把列向量加到每一列上,這樣代碼會顯得很複雜。而 numpy會自動執行的broadcast操做則會先將列向量「拓寬」成一個相同尺寸的矩陣,且其每一列都是對原列向量的複製,而後再進行運算。以下:
    1.  
      mat1=np.zeros(( 3,2))
    2.  vec=np.array([[ 1],[2],[3]])
       
    3.   print mat1+vec
       
    對於行向量和高維矩陣也是如此。
    更詳細的描述,請參考numpy文檔:broadcasting

2.3.7 雜項操做

本節介紹一些後面的項目會用到的其餘雜項操做

  1. 生成隨機數據

  2. rannum=np.random.randn(5,10)

  3.  這裏的 np.random.randn()函數生成一個指定尺寸的矩陣,且矩陣中的全部數字符合 正態分佈(normal distribution)
    1.  l=[ 1,2,3]
    2.  np.random.shuffle(l)
    3.   print l

    np.random.shuffle()函數能夠接收python list或者numpy ndarray,並將數組中的元素隨機打亂。

  4. 對矩陣求和

    1.  a=np.random.randn( 3,2)
    2.   print np.sum(a)

    np.sum()函數會對矩陣中的全部元素求和。

  5. numpy中的「軸(axis)」
    咱們以前使用「維度」描述矩陣的形狀,這樣容易和以前提到的向量的維度(長度)混淆,numpy中有另外一個概念叫作「軸(axis)」與這裏所說的「維度」很相似,指的是對一個矩陣進行操做時,所執行的「方向」。文字不太好描述,咱們結合實例來理解:

    1.  a=np.zeros(( 3,2))
       
    2.  a=a+ 1
       
    3.   print np.sum(a,axis=0)
       
    4.   print np.sum(a,axis=1)
       

    np.sum(a,axis=0)就是對矩陣a,在第一個「軸」上求和,具體效果就是對矩陣的每一列求和。np.sum(a,axis=1)就是對矩陣a,在第二個「軸」上求和,具體效果就是對矩陣的每一行求和。
    這裏可能不太好理解,請本身多舉幾個例子實驗一下。

  6. e的指數

    1.   a=np.random.randn(3,2)
       
    2.   print np.exp(a)
       

    np.exp()返回輸入中的每一個元素x都對e求指數的結果。

  7. 求一個數組中最大元素的下標

    1.  
      a=[1,2,3,4,3,2,1]
    2.   print np.argmax(a)
       

    np.argmax()返回一個python列表或numpy ndarray中的最大元素的下標。

3、實驗總結

本次實驗的內容已經被我儘可能精簡了,只保留了後面的項目當中會用到的內容。我但願你能儘可能理解上面的知識,雖然對於一些人來講這可能有些難,但數學最能體現人類的智慧不是嗎,數學是深度學習,乃至人工智能得以發展的重要基礎。
若是你以爲本次實驗內容太簡單或者寫的不夠好,請自行查閱其餘資料學習相關內容。

本次實驗,咱們學習了:

  1. 導數衡量一個自變量對函數值影響的能力大小。
  2. 偏導用來衡量多變量函數中的一個自變量對函數值影響的能力大小。
  3. 梯度是一個向量,指向函數值增大最快的方向。
  4. 鏈式法則是指,對於複合函數,其求導過程能夠一部分一部分地進行,再「連接」起來。
  5. 能夠認爲向量是矩陣的一種特殊形式。
  6. 矩陣乘法與線性方程組關係密切。
  7. numpy庫中的ndarray能夠很方便的用來進行矩陣運算。

4、課後做業

    1. 請你回想本次實驗所講的每個知識點,確保你對它們都理解的很清楚。
    2. numpy是一個很是著名的,常常被使用的庫,值得你進一步學習,請你本身繼續學習numpy中的其餘東西:numpy官網
相關文章
相關標籤/搜索