全連接前向神經網絡與手寫數字的實踐

文章首發於 個人博客

引言

上一篇文章提到了 logistics regression 、多分類的 softmax 算法及梯度等概念,其實就可以很自然的引出深度學習了。

引用WiKi的定義:

深度學習(deep learning)是機器學習的分支,是一種試圖使用包含複雜結構或由多重非線性變換構成的多個處理層對數據進行高層抽象的算法。

早在1958年就提出了 perceptron 的模型,即最簡單的線性感知機模型,在當時引起了很大的轟動,甚至提出了機器可以取代人的說法,然而後來就被人質疑,現在看來線性感知機的限制顯而易見。

然後在20世紀80年代,根據之前 perceptron 提出了 multi-layer perceptron(又叫 Neural Network), 這個模型和當今的深度神經網絡是沒有顯著區別的。1986年提出了反向傳播的概念,但是通常大於三層的 hidden layer 就沒有效果了,神經網絡學習出現了梯度消失的問題。

後來在 2006年,在上述神經網絡的算法模型上,取得了一些改進(RBM initialization),將之前 multi-layer perceptron 改了個名字 —— Deep Learning 重新提了出來,2009年的時候 DL 的運算開始利用 GPU,後面其在各個領域取得了一些突破性的應用進展,就火起來了。

所以,深度學習並不是什麼新鮮事物,只是換了個名字的稍微改進的舊模型。

全連接前向神經網絡

一個全連接的前向神經網絡示例如下所示,其激活函數是之前提到的 sigmod 函數,經過這個全連接的神經網絡,其 weight 和 bias 都知道的情況下,輸入的向量就會不斷的變化,最後輸出一個向量。

3AE7177E-8176-41EF-B7AE-54874C0E6DE8

一般來說,Fully Connect Feedforward Network 的架構如下圖所示,前一層每個輸入都連接到下一層的所有神經元中:
819348CB-888B-4214-8A58-3E9E8EE9

其輸入層和輸出層都是一個 vector,但是其 dimension 不一定相同,其中的 hidden layer 一般有多層,這也是 Deep Learning 的 Deep 所在。

而神經網絡的運算實質是矩陣運算,這也是爲什麼 GPU 能加速神經網絡的原因所在。

076D7461-22D5-4378-BED1-DA8F23AFB6B0

實例

以之前一直在用的手寫數字識別爲例,分別使用 keras 和 pytorch 搭建兩個 fully connect feedforward network 模型,使用 Mnist 數據集進行訓練。

首先是 keras (Using TensorFlow backend.)的代碼如下:

#!/usr/local/bin/python3.6

import numpy as np
import os
import matplotlib.pyplot as plt
import keras
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.utils import np_utils
from keras import backend as K

# 多核 CPU 使用設置
K.set_session(K.tf.Session(config=K.tf.ConfigProto(device_count={"CPU": 8},
                inter_op_parallelism_threads=8,
                intra_op_parallelism_threads=8,
                log_device_placement=True)))

# tensorboard 可視化        
tbCallBack = keras.callbacks.TensorBoard(log_dir='./Graph',
                                         histogram_freq=1,
                                         write_graph=True,
                                         write_images=True)

# 加載數據集
def load_data(file_path):
    f = np.load(file_path)
    x_train, y_train = f['x_train'], f['y_train']
    x_test, y_test = f['x_test'], f['y_test']
    f.close()
    return (x_train, y_train), (x_test, y_test)


# 初始化數據
(X_train, y_train), (X_test, y_test) = load_data('./mnist.npz')

X_train = X_train.reshape(60000, 784)
X_test = X_test.reshape(10000, 784)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255

nb_classes = 10
# 將 label 數據轉化爲 one-hot,因爲模型訓練 loss 參數爲 categorical_crossentropy
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)

# 初始化一個 model
model = Sequential()
# 添加第一層,輸入是784維,第一層節點爲 500,激活函數爲 relu
model.add(Dense(500, input_shape=(784,)))
model.add(Activation('relu'))
# model.add(Dropout(0.2))
# 添加第二層,節點爲 500,激活函數爲 relu
model.add(Dense(500))
model.add(Activation('relu'))
# model.add(Dropout(0.2))
# 添加輸出層,輸出 10 維,激活函數爲 softmax
model.add(Dense(10))
model.add(Activation('softmax'))

# 配置模型訓練參數,loss 使用多類的對數損失函數,optimizer 優化器使用 adam,模型性能評估使用 accuracy
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

# 開始訓練,batch_size爲100, 10 個 epoch,callbacks調用 tensorboard
model.fit(X_train, Y_train,
          batch_size=100, epochs=10,
          validation_data=(X_test, Y_test),
          callbacks=[tbCallBack]
          )

score = model.evaluate(X_test, Y_test, verbose=0)
print('Test score:', score[0])
print('Test accuracy:', score[1])

這是一個兩層的全連接前向神經網絡,訓練了 10 epochs,準確率如下:
1F78B36F-9654-4FF3-9166-F827DD83B6B9

沒有 GPU,純 CPU 跑起來的不算慢,準確率達到 97.7%,其神經網絡結構圖如下:
96202340-46DD-40B4-97DB-7642D377A42D

pytorch 使用起來就沒 keras 那麼簡單了,其代碼如下:

import os
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms

# 多核 cpu 設置
os.environ["OMP_NUM_THREADS"] = "8"
os.environ["MKL_NUM_THREADS"] = "8"

# 設置使用 CPU
device = torch.device('cpu')

# 參數配置
input_size = 784
hidden_size = 500
num_classes = 10
num_epochs = 10
batch_size = 100
learning_rate = 0.001
# 1 MNIST dataset 加載圖像數據
train_dataset = torchvision.datasets.MNIST(root='.',
                                           train=True,
                                           transform=transforms.ToTensor(),
                                           download=True)

test_dataset = torchvision.datasets.MNIST(root='.',
                                          train=False,
                                          transform=transforms.ToTensor())

# 2 Data loader pytorch的數據加載方式,tensorflow是沒有的
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)

test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                          batch_size=batch_size,
                                          shuffle=False)


# 3 Fully connected neural network with one hidden layer 定義網絡
class NeuralNet(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(NeuralNet, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, num_classes)

    def forward(self, x):
        out = self.fc1(x)
        out = self.relu(out)
        out = self.fc2(out)
        return out


model = NeuralNet(input_size, hidden_size, num_classes).to(device)

# 4 Loss and optimizer 定義損失和優化函數
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),
                             lr=learning_rate)

# 5 Train the model 訓練模型
total_step = len(train_loader)
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):  # batch size的大小
        # Move tensors to the configured device
        images = images.reshape(-1, 28*28).to(device)
        labels = labels.to(device)

        # Forward pass 前向傳播
        outputs = model(images)
        loss = criterion(outputs, labels)

        # Backward and optimize 後向傳播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if (i+1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'
                   .format(epoch+1, num_epochs, i+1, total_step, loss.item()))

# Test the model 預測
# In test phase, we don't need to compute gradients (for memory efficiency)
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.reshape(-1, 28*28).to(device)
        labels = labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print('Accuracy of the network on the 10000 test images: {} %'
            .format(100 * correct / total))

# Save the model checkpoint
torch.save(model.state_dict(), 'model.ckpt')

準確率如下:
4E84C574-A959-4E97-9C9F-3BB5057F954F

總體時間上,要比 TF 的慢,從源碼編譯了一遍安裝還是慢。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章