經過鳶尾花學習神經網絡

[鳶尾花的種類]python

預測鳶尾花的種類

Iris數據集在RA Fisher於1936年發表的經典論文《分類問題中的多重測量的使用》中使用,也能夠在UCI中找到。它包括三個種類,每一個種類有50個樣本以及每朵花的一些特性。一種花能夠與其餘兩種花是線性可分的,可是另兩種不是線性可分的。
如圖
在這裏插入圖片描述

算法

目標

分類

預測鳶尾花的種類。數組

進階

想辦法在預測模型上提升精度網絡

說明

  • 給出導入數據的方式
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target)
x_train, y_train    # train data
x_test, y_test      # test data
  • 數據均是實數沒有文本
  • 請在訓練集上訓練模型,在測試集上進行預測

分析問題

圖像的分類問題能夠使用神經網絡來寫。
這裏我建立了一個兩層的神經網絡Network
dom

交叉熵偏差

使用的是交叉熵偏差公式爲
E = − 1 N ∑ n ∑ k t n k l o g y n k E = -\frac{1}{N}\sum_{n}\sum_{k}t_{nk}logy_{nk} E=N1nktnklogynk
對應的代碼

ide

def cross_entropy_error(y, t):
	# y爲測試標籤,t爲正確標籤
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
    # 監督數據是one-hot-vector的狀況下,轉換爲正確解標籤的索引
    if t.size == y.size:
        t = t.argmax(axis=1)
             
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

sigmoid

我這個兩層神經網絡中使用的激活函數是 s i g m o i d ( x ) = 1 1 + e − x sigmoid(x) = \frac{1}{1+e^{-x}} sigmoid(x)=1+ex1
在反向傳播中他的導數爲sigmoid(x)(1 - sigomid(x))
sigmoid在反向傳播中的代碼以下:

函數

class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        out = sigmoid(x)
        self.out = out
        return out

    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out

        return dx

其中forward返回正向傳播的值,backward返回反向傳播的結果。
由於在反向傳播的時候須要他的輸出變量,因此在正向傳播的時候要將輸出值out存儲在實例變量out中。
學習

Affine

反向傳播中Affine層爲 W*X + B,由於W,X,B均爲矩陣,因此運用到了numpy中的dot函數,np.dot(X, W) 表示矩陣X和W相乘(矩陣X * W != W * X)
方向傳播能夠寫爲(T表明矩陣轉置的意思):
∂ L ∂ X = ∂ L ∂ Y ⋅ W T \frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} · W^T XL=YLWT
∂ L ∂ Y = X T ⋅ ∂ L ∂ Y \frac{\partial L}{\partial Y} = X^T·\frac{\partial L}{\partial Y} YL=XTYL
因此在反向傳播中Affine層所對應的代碼以下:



測試

class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b

        self.x = None
        self.original_x_shape = None
        # 權重和偏置參數的導數
        self.dW = None
        self.db = None

    def forward(self, x):
        # 對應張量
        self.original_x_shape = x.shape
        x = x.reshape(x.shape[0], -1)
        self.x = x
        out = np.dot(self.x, self.W) + self.b

        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)

        dx = dx.reshape(*self.original_x_shape)  # 還原輸入數據的形狀(對應張量)
        return dx

其中forward返回正向傳播的值,backward返回反向傳播的結果。(在兩層的神經網絡中他須要用到兩次~)spa

Softmax-With-Loss

Softmax

在介紹Softmax-With-Loss以前先介紹一下Softmax
它是用來計算輸出層每個神經元的機率的:
S o f t m a x = e y k ∑ e y k Softmax = \frac{e^{y_k}}{\sum{e^{y_k}}} Softmax=eykeyk
由於e的指數容易變得很是大,可能會致使溢出,因此對此函數進行了改進,以下:


S o f t m a x = e y k + C ∑ e y k + C Softmax = \frac{e^{y_k+C}}{\sum{e^{y_k+C}}} Softmax=eyk+Ceyk+C
這裏的C能夠是任何值,但爲了防止溢出,通常使用輸入信號的最大值,看一個具體的例子:

def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T 

    x = x - np.max(x)  # 溢出對策
    return np.exp(x) / np.sum(np.exp(x))

輸入的X是一個數組,對他進行exp操做會返回一個與他大小相同,但對其中每個元素都進行exp操做的數組。
下面看Softmax-With-Loss的方向傳播圖
在這裏插入圖片描述
能夠發現,反向傳播以後返回值的是y-t,因此Softmax-With-Loss的代碼以下:


class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None  # softmax的輸出
        self.t = None  # 監督數據

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        
        return self.loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        if self.t.size == self.y.size:  # 監督數據是one-hot-vector的狀況
            dx = (self.y - self.t) / batch_size
        else:
            dx = self.y.copy()
            dx[np.arange(batch_size), self.t] -= 1
            dx = dx / batch_size
        
        return dx

全部須要用到的函數以及類都介紹完了,下面開始寫Network

Network

class Network(object):
    def __init__(self, input_size, hide_size, output_size, weight_init_std=0.001):
        """ 用來聲明該神經網絡輸入輸出以及隱藏層的每一層層數 :param input_size: 輸入層數 :param hide_size: 第一個隱藏層層數 :param output_size: 輸出層層數 :param weight_init_std: 防止W過大 """
        self.params = { 'w1': weight_init_std * np.random.randn(input_size, hide_size), 'b1': np.zeros(hide_size),
                       'w2': weight_init_std * np.random.randn(hide_size, output_size), 'b2': np.zeros(output_size),}

        self.layers = OrderedDict()  # 使字典有序,能夠記住像字典中添加元素的順序
        self.layers = { 'Affine1': Affine(self.params['w1'], self.params['b1']),
                       'Sigmoid': Sigmoid(),
                       'Affine2': Affine(self.params['w2'], self.params['b2'])}
        self.layer_last = SoftmaxWithLoss()

    def predict(self, x):
        """ 用來預測數據經過該模型後獲得的結果 :param x: 預測的輸入數據 :return: 返回該網路預測的結果 """
        for layer in self.layers.values():
            x = layer.forward(x)
        return softmax(x)

    def loss(self, x, t):
        """ 計算該神經網絡所預測結果與實際結果的損失 其中y爲預測結果 :param x: 輸入數據 :param t: 真實結果 :return: 交叉熵損失 """
        y = self.predict(x)
        return self.layer_last.forward(y, t)

    def accuracy(self, x, t):
        """ 識別計算精度 其中y爲預測結果 :param x: 輸入數據 :param t: 真實結果 :return: 正確率 """
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1:
            t = np.argmax(t, axis=1)

        return np.sum(y == t) / float(x.shape[0])

    def back_gradient(self, x, t):
        """ 進行反向傳播算法 :return: 反向傳播獲得的偏導 """
        self.loss(x, t)
        # 將涉及到的layer經過列表存儲起來,以便於進行反向傳播的遍歷
        layers = list(self.layers.values())
        # 將layers倒序
        layers.reverse()
        # 第一個反向傳播的參數應該是一
        dout = 1
        # 反向傳播通過輸出層
        dout = self.layer_last.backward(dout)
        # 反向傳播從尾到頭依次傳播
        for layer in layers:
            dout = layer.backward(dout)
        # 得到反向傳播獲得的各個參數的偏導
        grads = { 'w1': self.layers['Affine1'].dW, 'b1': self.layers['Affine1'].db,
                 'w2': self.layers['Affine2'].dW, 'b2': self.layers['Affine2'].db
                 }

        return grads

network須要介紹的都在註釋中寫的很清楚。
下面來開始訓練:

# 讀入數據
iris = load_iris()
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target)
# x_train.shape = (112, 4), 因此input_size爲4,hide_size能夠根據本身的狀況隨意設置,output_size 覺得y有0, 1, 2三個元素,因此設置爲3
network = Network.Network(input_size=4, hide_size=50, output_size=3)

trying_time = 100000           # 循環的次數
train_size = x_train.shape[0]  # 訓練集的大小
batch_size = 1                 # 每一次訓練時挑選的一批數據的大小
learning_rate = 5e-4           # 學習率
min_loss = float('inf')        # 起初min_loss爲無窮大

# 每個epoch重複的次數
iter_per_epoch = max(train_size / batch_size, 1) 
# 開始訓練
for i in range(trying_time):
    batch_mask = np.random.choice(train_size, batch_size)  # 隨機選擇要進行訓練的下標,因爲choice是隨機選擇,因此大循環結束也不必定會對每個元素進行學習。
    x_batch = x_train[batch_mask]
    y_batch = y_train[batch_mask]

    # 經過反向傳播來求偏導,並記錄在grad中
    grad = network.back_gradient(x_batch, y_batch)

    # 更新各個參數
    for key in ('w1', 'b1', 'w2', 'b2'):
        network.params[key] -= learning_rate * grad[key]

    # 計算損失
    loss = network.loss(x_batch, y_batch)
    # 存放當前損失
    if loss < min_loss:
        min_loss = loss

    if i % iter_per_epoch == 0:
        # 訓練集的正確率
        train_acc = network.accuracy(x_train, y_train)
        # 測試集的正確率X
        test_acc = network.accuracy(x_test, y_test)
        print(train_acc, test_acc)

print('預測的結果爲:{}'.format(np.argmax(network.predict(x_test), axis=1)))
print('實際的結果爲:{}'.format(y_test))
print('預測的正確率爲:{}'.format(network.accuracy(x_test, y_test)))
print('損失爲:{}'.format(min_loss))

須要注意的是learning_rate的設置必定要合理,否則可能致使訓練不會產生任何效果。
運行結果以下:在這裏插入圖片描述

相關文章
相關標籤/搜索