摘要:要想入門以及往下理解深度學習,其中一些概念可能是無法避免地需要你理解一番,比如什么是感知器什么是神經網絡張量以及運算微分梯度下降帶著問題出發在開始之前希望你有一點機器學習方面的知識,解決問題的前提是提出問題,我們提出這樣一個問題,對數據集進
要想入門以及往下理解深度學習,其中一些概念可能是無法避免地需要你理解一番,比如:
什么是感知器
什么是神經網絡
張量以及運算
微分
梯度下降
帶著問題出發在開始之前希望你有一點機器學習方面的知識,解決問題的前提是提出問題,我們提出這樣一個問題,對MNIST數據集進行分析,然后在解決問題的過程中一步一步地來捋清楚其中涉及到的概念
MNIST數據集是一份手寫字訓練集,出自MNIST,相信你對它不會陌生,它是機器學習領域的一個經典數據集,感覺任意一個教程都拿它來說事,不過這也側面證明了這個數據集的經典,這里簡單介紹一下:
擁有60,000個示例的訓練集,以及10,000個示例的測試集
圖片都由一個28 ×28 的矩陣表示,每張圖片都由一個784 維的向量表示
圖片分為10類, 分別對應從0~9,共10個阿拉伯數字
壓縮包內容如下:
train-images-idx3-ubyte.gz: training set images (9912422 bytes)
train-labels-idx1-ubyte.gz: training set labels (28881 bytes)
t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)
t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)
上圖:
圖片生成代碼如下:
%matplotlib inline import matplotlib import matplotlib.pyplot as plt import numpy as np from keras.datasets import mnist (train_images, train_labels), (test_images, test_labels) = mnist.load_data() def plot_digits(instances, images_per_row=10, **options): size = 28 images_per_row = min(len(instances), images_per_row) images = instances n_rows = (len(instances) - 1) // images_per_row + 1 row_images = [] n_empty = n_rows * images_per_row - len(instances) images.append(np.zeros((size, size * n_empty))) for row in range(n_rows): rimages = images[row * images_per_row : (row + 1) * images_per_row] row_images.append(np.concatenate(rimages, axis=1)) image = np.concatenate(row_images, axis=0) plt.imshow(image, cmap = matplotlib.cm.binary, **options) plt.axis("off") plt.figure(figsize=(9,9)) plot_digits(train_images[:100], images_per_row=10) plt.show()
不過你不用急著嘗試,接下來我們可以一步一步慢慢來分析手寫字訓練集
看這一行代碼:
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
MNIST數據集通過keras.datasets加載,其中train_images和train_labels構成了訓練集,另外兩個則是測試集:
train_images.shape: (60000, 28, 28)
train_labels.shape: (60000,)
我們要做的事情很簡單,將訓練集丟到神經網絡里面去,訓練后生成了我們期望的神經網絡模型,然后模型再對測試集進行預測,我們只需要判斷預測的數字是不是正確的即可
在用代碼構建一個神經網絡之前,我先簡單介紹一下到底什么是神經網絡,讓我們從感知器開始
感知器感知器是Frank Rosenblatt提出的一個由兩層神經元組成的人工神經網絡,它的出現在當時可是引起了轟動,因為感知器是首個可以學習的神經網絡
感知器的工作方式如下所示:
左側三個變量分別表示三個不同的二進制輸入,output則是一個二進制輸出,對于多種輸入,可能有的輸入成立有的不成立,在這么多輸入的影響下,該如何判斷輸出output呢?Rosenblatt引入了權重來表示相應輸入的重要性
此時,output可以表示為:
上面右側的式子是一個階躍函數,就是和Sigmoid、Relu一樣作用的激活函數,然后我們就可以自己實現一個感知器:
import numpy as np class Perceptron: """ 代碼實現 Frank Rosenblatt 提出的感知器的與非門,加深對感知器的理解 blog: https://www.howie6879.cn/post/33/ """ def __init__(self, act_func, input_nums=2): """ 實例化一些基本參數 :param act_func: 激活函數 """ # 激活函數 self.act_func = act_func # 權重 已經確定只會有兩個二進制輸入 self.w = np.zeros(input_nums) # 偏置項 self.b = 0.0 def fit(self, input_vectors, labels, learn_nums=10, rate=0.1): """ 訓練出合適的 w 和 b :param input_vectors: 樣本訓練數據集 :param labels: 標記值 :param learn_nums: 學習多少次 :param rate: 學習率 """ for i in range(learn_nums): for index, input_vector in enumerate(input_vectors): label = labels[index] output = self.predict(input_vector) delta = label - output self.w += input_vector * rate * delta self.b += rate * delta print("此時感知器權重為{0},偏置項為{1}".format(self.w, self.b)) return self def predict(self, input_vector): if isinstance(input_vector, list): input_vector = np.array(input_vector) return self.act_func(sum(self.w * input_vector) + self.b) def f(z): """ 激活函數 :param z: (w1*x1+w2*x2+...+wj*xj) + b :return: 1 or 0 """ return 1 if z > 0 else 0 def get_and_gate_training_data(): """ AND 訓練數據集 """ input_vectors = np.array([[1, 1], [1, 0], [0, 1], [0, 0]]) labels = np.array([1, 0, 0, 0]) return input_vectors, labels if __name__ == "__main__": """ 輸出如下: 此時感知器權重為[ 0.1 0.2],偏置項為-0.2 與門 1 and 1 = 1 1 and 0 = 0 0 and 1 = 0 0 and 0 = 0 """ # 獲取樣本數據 and_input_vectors, and_labels = get_and_gate_training_data() # 實例化感知器模型 p = Perceptron(f) # 開始學習 AND p_and = p.fit(and_input_vectors, and_labels) # 開始預測 AND print("1 and 1 = %d" % p_and.predict([1, 1])) print("1 and 0 = %d" % p_and.predict([1, 0])) print("0 and 1 = %d" % p_and.predict([0, 1])) print("0 and 0 = %d" % p_and.predict([0, 0]))S型神經元
神經元和感知器本質上是一樣的,他們的區別在于激活函數不同,比如躍遷函數改為Sigmoid函數
神經網絡可以通過樣本的學習來調整人工神經元的權重和偏置,從而使輸出的結果更加準確,那么怎樣給?個神經?絡設計這樣的算法呢?
以數字識別為例,假設?絡錯誤地把?個9的圖像分類為8,我們可以讓權重和偏置做些?的改動,從而達到我們需要的結果9,這就是學習。對于感知器,我們知道,其返還的結果不是0就是1,很可能出現這樣一個情況,我們好不容易將一個目標,比如把9的圖像分類為8調整回原來正確的分類,可此時的閾值和偏置會造成其他樣本的判斷失誤,這樣的調整不是一個好的方案
所以,我們需要S型神經元,因為S型神經元返回的是[0,1]之間的任何實數,這樣的話權重和偏置的微?改動只會引起輸出的微?變化,此時的output可以表示為σ(w?x+b),而σ就是S型函數,S型函數中S指的是Sigmoid函數,定義如下:
神經網絡神經網絡其實就是按照一定規則連接起來的多個神經元,一個神經網絡由以下組件構成:
輸入層:接受傳遞數據,這里應該是 784 個神經元
隱藏層:發掘出特征
各層之間的權重:自動學習出來
每個隱藏層都會有一個精心設計的激活函數,比如Sigmoid、Relu激活函數
輸出層,10個輸出
上?層的輸出作為下?層的輸?,信息總是向前傳播,從不反向回饋:前饋神經網絡
有回路,其中反饋環路是可?的:遞歸神經網絡
從輸入層傳入手寫字訓練集,然后通過隱藏層向前傳遞訓練集數據,最后輸出層會輸出10個概率值,總和為1?,F在,我們可以看看Keras代碼:
第一步,對數據進行預處理,我們知道,原本數據形狀是(60000, 28, 28),取值區間為[0, 255],現在改為[0, 1]:
train_images = train_images.reshape((60000, 28 * 28)) train_images = train_images.astype("float32") / 255 test_images = test_images.reshape((10000, 28 * 28)) test_images = test_images.astype("float32") / 255
然后對標簽進行分類編碼:
from keras.utils import to_categorical train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels)
第二步,編寫模型:
from keras import models from keras import layers network = models.Sequential() network.add(layers.Dense(512, activation="relu", input_shape=(28 * 28,))) network.add(layers.Dense(10, activation="softmax") network.compile(optimizer="rmsprop",loss="categorical_crossentropy", metrics=["accuracy"]) network.fit(train_images, train_labels, epochs=5, batch_size=128)
一個隱藏層,激活函數選用relu,輸出層使用softmax返回一個由10個概率值(總和為 1)組成的數組
訓練過程中顯示了兩個數字:一個是網絡在訓練數據上的損失loss,另一個是網絡在 訓練數據上的精度acc
很簡單,我們構建和訓練一個神經網絡,就這么幾行代碼,之所以寫的這么剪短,是因為keras接接口封裝地比較好用,但是里面的理論知識我們還是需要好好研究下
神經網絡的數據表示TensorFlow里面的Tensor是張量的意思,上面例子里面存儲在多維Numpy數組中的數據就是張量:張量是數據容器,矩陣就是二維張量,張量是矩陣向任意維度的推廣,張量的維度稱為軸
標量包含一個數字的張量叫做標量(0D張量),如下:
x = np.array(12) print(x, x.ndim) # 12, 0
張量軸的個數也叫做階(rank)
向量數字組成的數組叫做向量(1D張量),如下:
x = np.array([12, 3, 6, 14, 7]) print(x, x.ndim) # [12 3 6 14 7] 1矩陣
向量組成的數組叫做矩陣(2D張量),如下:
x = np.array([[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4, 36, 2]]) print(x, x.ndim) # [[ 5 78 2 34 0] # [ 6 79 3 35 1] # [ 7 80 4 36 2]] 23D張量與更高維張量
將多個矩陣組合成一個新的數組就是一個3D張量,如下:
x = np.array([[[5, 78, 2, 34, 0], [6, 79, 3, 35, 1]], [[5, 78, 2, 34, 0], [6, 79, 3, 35, 1]], [[5, 78, 2, 34, 0], [6, 79, 3, 35, 1]]]) print(x, x.ndim) # (array([[[ 5, 78, 2, 34, 0], # [ 6, 79, 3, 35, 1]], # # [[ 5, 78, 2, 34, 0], # [ 6, 79, 3, 35, 1]], # # [[ 5, 78, 2, 34, 0], # [ 6, 79, 3, 35, 1]]]), 3)
將多個3D張量組合成一個數組,可以創建一個4D張量
關鍵屬性張量是由以下三個關鍵屬性來定義:
軸的個數:3D張量三個軸,矩陣兩個軸
形狀:是一個整數元祖,比如前面矩陣為(3, 5),向量(5,),3D張量為(3, 2, 5)
數據類型
在Numpy中操作張量以前面加載的train_images為:
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
比如進行切片選擇10~100個數字:
train_images[10:100].shape # (90, 28, 28)數據批量的概念
深度學習模型會將數據集隨機分割成小批量進行處理,比如:
batch = train_images[:128] batch.shape # (128, 28, 28)現實世界的數據張量
下面將介紹下現實世界中數據的形狀:
向量數據:2D張量,(samples, features)
時間序列數據或者序列數據:3D張量,(samples, timesteps, features)
圖像:4D張量,(samples, height, width, channels) 或 (samples, channels, height, width)
視頻:5D張量,(samples, frames, height, width, channels) 或 (samples, frames, channels, height, width)
張量運算類似于計算機程序的計算可以轉化為二進制計算,深度學習計算可以轉化為數值數據張量上的一些張量運算(tensor operation)
上面模型的隱藏層代碼如下:
keras.layers.Dense(512, activation="relu")
這一層可以理解為一個函數,輸入一個2D張量,輸出一個2D張量,就如同上面感知機那一節最后輸出的計算函數:
output = relu(dot(W, input) + b)逐元素計算
Relu 和加法運算都是逐元素的運算,比如:
# 輸入示例 input_x = np.array([[2], [3], [1]]) # 權重 W = np.array([[5, 6, 1], [7, 8, 1]]) # 計算輸出 z z = np.dot(W, input_x) # 實現激活函數 def naive_relu(x): assert len(x.shape) == 2 x = x.copy() for i in range(x.shape[0]): for j in range(x.shape[1]): x[i, j] = max(x[i, j], 0) return x # 激活函數對應的輸出 output = naive_relu(z) output廣播
張量運算那節中,有這樣一段代碼:
output = relu(dot(W, input) + b)
dot(W, input)是2D張量,b是向量,兩個形狀不同的張量相加,會發生什么?
如果沒有歧義的話,較小的張量會被廣播,用來匹配較大張量的形狀:
input_x = np.array([[1], [3]]) # 權重 W = np.array([[5, 6], [7, 8]]) b = np.array([1]) # 計算輸出 z z = np.dot(W, input_x) + b # array([[24], # [32]])張量點積
點積運算,也叫張量積,如:
import numpy as np # 輸入示例 input_x = np.array([[2], [3], [1]]) # 權重 W = np.array([[5, 6, 1], [7, 8, 1]]) np.dot(W, input_x)
兩個向量之間的點積是一個標量:
def naive_vector_dot(x, y): assert len(x.shape) == 1 assert len(y.shape) == 1 assert x.shape[0] == y.shape[0] z = 0. for i in range(x.shape[0]): z += x[i] * y[i] return z x = np.array([1,2]) y = np.array([1,2]) naive_vector_dot(x, y) # 5.0
矩陣和向量點積后是一個向量:
np.dot(W, [1, 2, 3]) # array([20, 26])張量變形
前面對數據進行預處理的時候:
train_images = train_images.reshape((60000, 28 * 28)) train_images = train_images.astype("float32") / 255
上面的例子將輸入數據的shape變成了(60000, 784),張量變形指的就是改變張量的行和列,得到想要的形狀,前后數據集個數不變,經常遇到一個特殊的張量變形是轉置(transposition),如下:
x = np.zeros((300, 20)) x = np.transpose(x) x.shape # (20, 300)梯度優化
針對每個輸入,神經網絡都會通過下面的函數對輸入數據進行變換:
output = relu(dot(W, input_x) + b)
其中:
relu:激活函數
W:是一個張量,表示權重,第一步可以取較小的隨機值進行隨機初始化
b:是一個張量,表示偏置
現在我們需要一個算法來讓我們找到權重和偏置,從而使得y=y(x)可以擬合樣本輸入的x
再回到感知器感知器學習的過程就是其中權重和偏置不斷調優更新的過程,其中的偏置可以理解成輸入為1的權重值,那么權重是怎么更新的呢?
首先,介紹一個概念,損失函數,引用李航老師統計學習方法書中的一個解釋:
監督學習問題是在假設空間中選取模型f作為決策函數,對于給定的輸入X,由f(X)給出相應的輸出Y,這個輸出的預測值f(X)與真實值Y可能一致也可能不一致,用一個損失函數(loss function)或代價函數(cost function)來度量預測錯誤的程度,損失函數是f(X)和Y的非負實值函數,記作L(Y,f(X))
其中模型f(X)關于訓練數據集的平均損失,我們稱之為:經驗風險(empirical risk),上述的權重調整,就是在不斷地讓經驗風險最小,求出最好的模型f(X),我們暫時不考慮正則化,此時我們經驗風險的最優化的目標函數就是:
求解出此目標函數最小時對應的權重值,就是我們感知器里面對應的權重值,在推導之前,我們還得明白兩個概念:
什么是導數
什么是梯度
什么是導數假設有一個連續的光滑函數f(x) = y,什么是函數連續性?指的是x的微小變化只能導致y的微小變化。
假設f(x)上的兩點a,b足夠接近,那么a,b可以近似為一個線性函數,此時他們斜率為k,那么可以說斜率k是f在b點的導數
總之,導數描述了改變x后f(x)會如何變化,如果你希望減小f(x)的值,只需要將x沿著導數的反方向移動一小步即可,反之亦然
什么是梯度梯度是張量運算的導數,是導數這一概念向多元函數導數的推廣,它指向函數值上升最快的方向,函數值下降最快的方向自然就是梯度的反方向
隨機梯度下降推導過程如下:
感知器代碼里面的這段:
self.w += input_vector * rate * delta
就對應上面式子里面推導出來的規則
總結再來看看全部的手寫字識別模型代碼:
from keras import models from keras import layers from keras.utils import to_categorical (train_images, train_labels), (test_images, test_labels) = mnist.load_data() train_images = train_images.reshape((60000, 28 * 28)) train_images = train_images.astype("float32") / 255 test_images = test_images.reshape((10000, 28 * 28)) test_images = test_images.astype("float32") / 255 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) network = models.Sequential() network.add(layers.Dense(512, activation="relu", input_shape=(28 * 28,))) network.add(layers.Dense(10, activation="softmax")) network.compile(optimizer="rmsprop",loss="categorical_crossentropy", metrics=["accuracy"]) network.fit(train_images, train_labels, epochs=5, batch_size=128) test_loss, test_acc = network.evaluate(test_images, test_labels) print("test_acc:", test_acc)
輸入數據保存在float32格式的Numpy張量中,形狀分別是(60000, 784)和(10000, 784)
神經網絡結構為:1個輸入層、一個隱藏層、一個輸出層
categorical_crossentropy是針對分類模型的損失函數
每批128個樣本,共迭代5次,一共更新(469 * 5) = 2345次
說明對本文有影響的書籍文章如下,感謝他們的付出:
[統計學習方法] 第一章
Neural Networks and Deep Learning 第一章
Deep Learning with Python 第二章
hands_on_Ml_with_Sklearn_and_TF
hanbt零基礎入門深度學習系列
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/42925.html
摘要:近日,發布了其關于神經網絡可解釋性的研究成果,他們通過刪除網絡中的某些神經元組,從而判定其對于整個網絡是否重要。泛化性良好的網絡對于刪除神經元的操作更具適應性。通過刪除單個神經元和神經元組,我們測量了破壞網絡對性能的影響。 深度學習算法近年來取得了長足的進展,也給整個人工智能領域送上了風口。但深度學習系統中分類器和特征模塊都是自學習的,神經網絡的可解釋性成為困擾研究者的一個問題,人們常常將其...
摘要:較大池化一個卷積神經網絡的典型架構卷積神經網絡的典型架構我們已經討論過卷積層用表示和池化層用表示只是一個被應用的非線性特征,類似于神經網絡。 這是作者在 Medium 上介紹神經網絡系列文章中的一篇,他在這里詳細介紹了卷積神經網絡。卷積神經網絡在圖像識別、視頻識別、推薦系統以及自然語言處理中都有很廣的應用。如果想瀏覽該系列文章,可點擊閱讀原文查看原文網址。跟神經網絡一樣,卷積神經網絡由神經元...
閱讀 2288·2021-11-16 11:51
閱讀 3508·2021-09-26 10:14
閱讀 1831·2021-09-22 15:58
閱讀 1100·2019-08-30 15:52
閱讀 2017·2019-08-30 15:43
閱讀 2619·2019-08-30 13:46
閱讀 913·2019-08-30 13:10
閱讀 1024·2019-08-29 18:32