AlexNet:深度學習時代的開始

2021-02-23 皮皮魯的科技星球
我的個人網站會持續更新,詳情:https://lulaoshi.info/machine-learning/convolutional/alexnet.html深度學習的前夜

雖然Yann LeCun在上個世紀就提出了卷積神經網絡LeNet,並使用LeNet進行圖像分類,但卷積神經網絡並沒有就此飛速發展。在LeNet提出後的將近20年裡,神經網絡一度被其他機器學習方法超越,如支持向量機。卷積神經網絡在當時未能快速發展主要受限於:

1. 缺少數據

深度學習需要大量的有標籤的數據才能表現得比其他經典方法更好。限於早期計算機有限的存儲和90年代有限的研究預算,大部分研究只基於小的公開數據集。例如,不少研究論文基於加州大學歐文分校(UCI)提供的若干個公開數據集,其中許多數據集只有幾百至幾千張圖像。這一狀況在2010年前後興起的大數據浪潮中得到改善。特別是,李飛飛主導的ImageNet數據集的構建。ImageNet數據集包含了1,000大類物體,每類有多達數千張不同的圖像,數據總量達到了上百GB。這一規模是當時其他公開數據集無法與之相提並論的。此外,社區每年都舉辦一個挑戰賽,名為ImageNet Large-Scale Visual Recognition Challenge (ILSVRC) ,參賽選手需要基於ImageNet數據集,優化計算機視覺相關任務。可以說,ImageNet數據集推動了計算機視覺和機器學習研究進入新的階段。

2. 缺少硬體

深度學習對計算資源要求很高。早期的硬體計算能力有限,這使訓練較複雜的神經網絡變得很困難。然而,通用GPU(General Purpose GPU,GPGPU)的到來改變了這一格局。很久以來,GPU都是為圖像處理和計算機遊戲設計的,尤其是針對大吞吐量的矩陣和向量乘法。值得慶幸的是,這其中的數學表達與深度網絡中的卷積層的表達類似。通用GPU這個概念在2001年開始興起,湧現出諸如CUDA和OpenCL之類的編程框架。CUDA編程接口上手難度沒那麼大,科研工作者可以使用CUDA在英偉達的GPU上加速自己的科學計算任務。一些計算密集型的任務在2010年左右開始被遷移到英偉達的GPU上。

人們普遍認為,當前這波人工智慧熱潮起源於2012年。當年,Alex Krizhevsky使用英偉達GPU成功訓練出了深度卷積神經網絡AlexNet,並憑藉該網絡在ImageNet挑戰賽上奪得冠軍,大幅提升圖像分類的準確度。當時,大數據的存儲和計算幾乎不再是瓶頸,AlexNet的提出也讓學術圈和工業界認識到深度神經網絡的驚人表現。

AlexNet網絡結構

AlexNet與LeNet的設計理念非常相似,但也有顯著的區別。

LeNet與AlexNet

第一,與相對較小的LeNet相比,AlexNet包含8層變換,其中有5層卷積和2層全連接隱藏層,以及1個全連接輸出層。下面我們來詳細描述這些層的設計。

AlexNet第一層中的卷積窗口形狀是11 × 11。因為ImageNet中絕大多數圖像的高和寬均比MNIST圖像的高和寬大10倍以上,ImageNet圖像的物體佔用更多的像素,所以需要更大的卷積窗口來捕獲物體。第二層中的卷積窗口形狀減小到5 × 5,之後全採用3 × 3。此外,第一、第二和第五個卷積層之後都使用了窗口形狀為3 × 3、步幅為2的最大池化層。而且,AlexNet使用的卷積通道數也大於LeNet中的卷積通道數數十倍。

緊接著最後一個卷積層的是兩個輸出個數為4096的全連接層。這兩個巨大的全連接層帶來將近1 GB的模型參數。由於早期顯存的限制,最早的AlexNet使用雙數據流的設計使一個GPU只需要處理一半模型。幸運的是,顯存在過去幾年得到了長足的發展,因此通常我們不再需要這樣的特別設計了。

第二,AlexNet將sigmoid激活函數改成了更加簡單的ReLU激活函數。一方面,ReLU激活函數的計算更簡單,例如它並沒有sigmoid激活函數中的求冪運算。另一方面,ReLU激活函數在不同的參數初始化方法下使模型更容易訓練。這是由於當sigmoid激活函數輸出極接近0或1時,這些區域的梯度幾乎為0,從而造成反向傳播無法繼續更新部分模型參數;而ReLU激活函數在正區間的梯度恆為1。因此,若模型參數初始化不當,sigmoid函數可能在正區間得到幾乎為0的梯度,從而令模型無法得到有效訓練。

第三,AlexNet通過丟棄法(Dropout)來控制全連接層的模型複雜度,避免過擬合。而LeNet並沒有使用丟棄法。

第四,AlexNet引入了大量的圖像增廣,如翻轉、裁剪和顏色變化,從而進一步擴大數據集來緩解過擬合。

下面是一個使用PyTorch實現的稍微簡化過的AlexNet。這個網絡假設使用1 × 224 × 224的輸入,即輸入只有一個通道,比如Fashion-MNIST這樣的黑白單顏色的數據集。

class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()

        # convolution layer will change input shape into: floor((input_shape - kernel_size + padding + stride) / stride)
        # input shape: 1 * 224 * 224
        # convolution part
        self.conv = nn.Sequential(
            # conv layer 1
            # floor((224 - 11 + 2 + 4) / 4) = floor(54.75) = 54
            # conv: 1 * 224 * 224 -> 96 * 54 * 54 
            nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
            # floor((54 - 3 + 2) / 2) = floor(26.5) = 26
            # 96 * 54 * 54 -> 96 * 26 * 26
            nn.MaxPool2d(kernel_size=3, stride=2), 
            # conv layer 2: decrease kernel size, add padding to keep input and output size same, increase channel number
            # floor((26 - 5 + 4 + 1) / 1) = 26
            # 96 * 26 * 26 -> 256 * 26 * 26
            nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, stride=1, padding=2), nn.ReLU(),
            # floor((26 - 3 + 2) / 2) = 12
            # 256 * 26 * 26 -> 256 * 12 * 12
            nn.MaxPool2d(kernel_size=3, stride=2),
            # 3 consecutive conv layer, smaller kernel size
            # floor((12 - 3 + 2 + 1) / 1) = 12
            # 256 * 12 * 12 -> 384 * 12 * 12
            nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, stride=1, padding=1), nn.ReLU(),
            # 384 * 12 * 12 -> 384 * 12 * 12
            nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, stride=1, padding=1), nn.ReLU(),
            # 384 * 12 * 12 -> 256 * 12 * 12
            nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, stride=1, padding=1), nn.ReLU(),
            # floor((12 - 3 + 2) / 2) = 5
            # 256 * 5 * 5
            nn.MaxPool2d(kernel_size=3, stride=2)
        )
        # fully connect part 
        self.fc = nn.Sequential(
            nn.Linear(256 * 5 * 5, 4096),
            nn.ReLU(),
            # Use the dropout layer to mitigate overfitting
            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            # Output layer. 
            # the number of classes in Fashion-MNIST is 10
            nn.Linear(4096, 10),
        )

    def forward(self, img):
        feature = self.conv(img)
        output = self.fc(feature.view(img.shape[0], -1))
        return output

模型訓練

雖然論文中AlexNet使用ImageNet數據集,但因為ImageNet數據集訓練時間非常長,我們使用Fashion-MNIST數據集來演示AlexNet。讀取數據的時候我們額外做了一步將圖像高和寬擴大到AlexNet使用的圖像高和寬224。這個可以通過torchvision.transforms.Resize實例來實現。也就是說,我們在ToTensor實例前使用Resize實例,然後使用Compose實例來將這兩個變換串聯以方便調用。

def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):
    """Use torchvision.datasets module to download the fashion mnist dataset and then load into memory."""
    trans = []
    if resize:
        trans.append(torchvision.transforms.Resize(size=resize))
    trans.append(torchvision.transforms.ToTensor())

    transform = torchvision.transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
    mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)
    if sys.platform.startswith('win'):
        num_workers = 0
    else:
        num_workers = 4
    train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)

    return train_iter, test_iter

load_data_fashion_mnist()方法定義了讀取數據的方式,Fashion-MNIST原來是1 × 28 × 28的大小。resize在原圖的基礎上修改了圖像的大小,可以將圖片調整為我們想要的大小。

def train(net, train_iter, test_iter, batch_size, optimizer, num_epochs, device=mlutils.try_gpu()):
    net = net.to(device)
    print("training on", device)
    loss = torch.nn.CrossEntropyLoss()
    timer = mlutils.Timer()
    # in one epoch, it will iterate all training samples
    for epoch in range(num_epochs):
        # Accumulator has 3 parameters: (loss, train_acc, number_of_images_processed)
        metric = mlutils.Accumulator(3)
        # all training samples will be splited into batch_size
        for X, y in train_iter:
            timer.start()
            # set the network in training mode
            net.train()
            # move data to device (gpu)
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            with torch.no_grad():
                # all the following metrics will be accumulated into variable `metric`
                metric.add(l * X.shape[0], mlutils.accuracy(y_hat, y), X.shape[0])
            timer.stop()
            # metric[0] = l * X.shape[0], metric[2] = X.shape[0]
            train_l = metric[0]/metric[2]
            # metric[1] = number of correct predictions, metric[2] = X.shape[0]
            train_acc = metric[1]/metric[2]
        test_acc = mlutils.evaluate_accuracy_gpu(net, test_iter)
        if epoch % 1 == 0:
            print(f'epoch {epoch + 1} : loss {train_l:.3f}, train acc {train_acc:.3f}, test acc {test_acc:.3f}')
    # after training, calculate images/sec
    # variable `metric` is defined in for loop, but in Python it can be referenced after for loop
    print(f'total training time {timer.sum():.2f}, {metric[2] * num_epochs / timer.sum():.1f} images/sec ' f'on {str(device)}')

在整個程序的main()方法中,先定義網絡,再使用load_data_fashion_mnist()加載訓練和測試數據,最後使用train()方法進行模型訓練:

def main(args):

    net = AlexNet()
    optimizer = torch.optim.Adam(net.parameters(), lr=args.lr)

    # load data
    train_iter, test_iter = mlutils.load_data_fashion_mnist(batch_size=args.batch_size, resize=224)
    # train
    train(net, train_iter, test_iter, args.batch_size, optimizer, args.num_epochs)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Image classification')
    parser.add_argument('--batch_size', type=int, default=128, help='batch size')
    parser.add_argument('--num_epochs', type=int, default=10, help='number of train epochs')
    parser.add_argument('--lr', type=float, default=0.001, help='learning rate')
    args = parser.parse_args()
    main(args)

其中,args為參數,可以在命令行中傳遞進來。

我將原始碼上傳到了GitHub(https://github.com/luweizheng/machine-learning-notes/tree/master/neural-network/cnn)上,並提供了PyTorch和TensorFlow兩個版本。

參考資料

Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). Imagenet classification with deep convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).http://d2l.ai/chapter_convolutional-modern/alexnet.htmlhttps://tangshusen.me/Dive-into-DL-PyTorch/#/chapter05_CNN/5.6_alexnet

相關焦點

  • 【深度學習系列】用PaddlePaddle和Tensorflow實現經典CNN網絡AlexNet
    ,熟悉Tensorflow,PaddlePaddle等深度學習框架,負責過多個機器學習落地項目,如垃圾評論自動過濾,用戶分級精準營銷,分布式深度學習平臺搭建等,都取了的不錯的效果。博客專欄:https://www.cnblogs.com/charlotte77/前文傳送門:【好書推薦&學習階段】三個月教你從零入門深度學習【深度學習系列】PaddlePaddle之手寫數字識別【深度學習系列】卷積神經網絡CNN原理詳解(一)——基本原理【深度學習系列】PaddlePaddle之數據預處理
  • 深度學習筆記16:CNN經典論文研讀之AlexNet及其Tensorflow實現
    2012年,深度學習三巨頭之一、具有神經網絡之父之稱的 Geoffrey Hinton 的學生 Alex Krizhevsky 率先提出了 AlexNet,並在當年度的 ILSVRC(ImageNet大規模視覺挑戰賽)以顯著的優勢獲得當屆冠軍,top-5 的錯誤率降至了 16.4%,相比於第二名 26.2% 的錯誤率有了極大的提升。
  • 從AlexNet到BERT:深度學習中那些最重要的idea回顧
    ,大家都基於這些發了無數的論文,它們依次是:AlexNet 和 Dropout:AlexNet 直接打開了深度學習時代,奠定了之後 CV 裡面 CNN 模型基本結構,Dropout 也不用說,都成了基本配置。
  • 從AlexNet到BERT:深度學習中那些最重要idea的最簡單回顧
    可以說得上是養活了無數人,大家都基於這些發了無數的論文,它們依次是:AlexNet 和 Dropout:AlexNet 直接打開了深度學習時代,奠定了之後 CV 裡面 CNN 模型基本結構,Dropout 也不用說,都成了基本配置。
  • 乾貨 TensorFlow之深入理解AlexNet
    state-of-art,甚至於搭到打敗人類的地步,看這邊文章的過程中,發現了很多以前零零散散看到的一些優化技術,但是很多沒有深入了解,這篇文章講解了他們alexnet如何做到能達到那麼好的成績,好的廢話不多說,來開始看文章這張圖是基本的caffe中alexnet的網絡結構,這裡比較抽象,我用caffe的draw_net把alexnet的網絡結構畫出來了
  • 深度學習的學習歷程
    alexnet、vgg、googlenet、resnet等網絡就像樂高一樣,把這些模塊當積木一樣組合起來,好像也沒啥特別的。又好像什麼都不懂,學會這些模塊的公式就算會深度學習了嗎?整個深度學習的學習周期是怎樣的,我下一步應該幹啥?這些模塊看起來平平無奇,為什麼組合在一起就能發揮這麼大威力?為什麼drop out能起到正則作用?L1正則和L2正則有什麼區別?
  • 擺好正確姿勢 看 Google 神級深度學習框架 TensorFlow 的實踐思路
  • 【深度學習系列】用PaddlePaddle和Tensorflow實現經典CNN網絡GoogLeNet
    點擊上圖,立即開啟AI急速修煉作者:Charlotte    高級算法工程師 ,博客專家;擅長用通俗易懂的方式講解深度學習和機器學習算法
  • 深度 | 從AlexNet到殘差網絡,理解卷積神經網絡的不同架構
    這種方法的另一個問題是它與人類學習識別物體的方式完全不同。嬰兒剛出生時無法感知周圍環境,但是隨著他不斷進步和處理數據,他學會了識別物體。這是深度學習背後的哲學,不存在內置硬編碼特徵提取器。它將提取和分類模塊整合進一個系統,它通過辨別不同圖像的表徵進行提取,根據監督數據進行分類。這樣的系統包括多層感知機,即多層神經元互相緊密相連的神經網絡。
  • AlexNet模型思想詳解及核心代碼實現
    也是在那年之後,更多更深的神經網絡被提出,網絡開始往深水區涉入,比如優秀的vgg,GoogLeNet等。AlexNet是在LeNet的基礎上加深了網絡的結構,學習更豐富更高維的圖像特徵。本文將詳細概述AlexNet的特點及核心思想,最後給出相關的代碼實現。AlexNet模型的核心思想問題LeNet是卷積神經網絡的祖師爺LeCun在1998年提出,用於解決手寫數字識別的視覺任務。
  • 零基礎入門深度學習(六):圖像分類任務之LeNet和AlexNet
    課程名稱 | 零基礎入門深度學習授課講師 | 孫高峰 百度深度學習技術平臺部資深研發工程師授課時間 | 每周二、周四晚20:00-21:0001 導讀本課程是百度官方開設的零基礎入門深度學習課程,主要面向沒有深度學習技術基礎或者基礎薄弱的同學,幫助大家在深度學習領域實現從0到1+的跨越。
  • 深度學習時代,分詞真的有必要嗎
    點擊上方「MLNLP」,選擇「星標」公眾號重磅乾貨,第一時間送達作者:老宋的茶書會https://zhuanlan.zhihu.com/p/66155616前言中文數據集是我一直儘量避免的問題,但生活所迫,畢竟咱還是要在國內混江湖的,於是,最近開始研究研究深度學習模型在中文數據集上的各種表現
  • 從這開始了解深度學習——視覺的深度學習與網絡
    「深度學習」,接下來就來詳細聊聊深度學習(為什麼要深度學習特徵???),然後來說說深度網絡的搭建,最後讓我們自己用手 DIY 屬於自己的網絡,現在就開始 ing......介紹一說起 「深度學習」,大家有想過為什麼要去搭建複雜網絡,去學習更高級的特徵呢?其實很簡單,因為趨勢是朝著類腦那個大方向,現在類腦工作已經得到很多研究員的關注。
  • CCL 2016| 中科院張鈸院士:後深度學習時代的計算語言學
    先簡單介紹下兩個會議的背景——CCL(全國計算語言學學術會議)從1991年開始每兩年舉辦一次,從2013年開始每年舉辦一次,著重於中國境內各類語言的計算處理,為研討和傳播計算語言學最新的學術和技術成果提供了高水平的交流平臺
  • 卷積神經網絡 AlexNet
    1.介紹LeNet 是最早推動深度學習領域發展的卷積神經網絡之一。這項由 Yann LeCun 完成的開創性工作自 1988 年以來多次成功迭代之後被命名為 LeNet5。AlexNet 可以說是具有歷史意義的一個網絡結構,在此之前,深度學習已經沉寂了很長時間,自 2012 年 AlexNet 誕生之後,後面的 ImageNet 冠軍都是用卷積神經網絡(CNN)來做的,並且層次越來越深,使得CNN成為在圖像識別分類的核心算法模型,帶來了深度學習的大爆發。本文將詳細講解 AlexNet 模型及其使用 Keras 實現過程。開始之前,先介紹一下卷積神經網絡。
  • 專欄 | 清華大學劉知遠:在深度學習時代用HowNet搞事情
    深度學習時代 HowNet 有什麼用進入深度學習時代,人們發現通過大規模文本數據也能夠很好地學習詞彙的語義表示。回顧最開始提及的張鈸院士的觀點,我們堅信 AI 未來的科學突破是建立一種同時基於知識和數據的 AI 系統。看清楚了這個大形勢,針對 NLP 深度學習模型的關鍵問題就在於,利用什麼知識,怎樣利用知識。在自然語言理解方面,HowNet 更貼近語言本質特點。自然語言中的詞彙是典型的符號信息,這些符號背後蘊藏豐富的語義信息。可以說,詞彙是最小的語言使用單位,卻不是最小的語義單位。
  • 2012年至今,細數深度學習領域這些年取得的經典成果
    該論文也被視作深度學習領域的經典之作。從原理來看,深度學習與神經網絡緊密相關:神經網絡由一層一層的神經元構成,層數越多,神經網絡越深,而所謂「深度學習」就是模擬人類大腦,運用深層神經網絡對輸入進行「思考」、「分析」並獲得目標輸出的過程。
  • 圖像分割深度學習從零開始學習路線
    你可能需要對深度學習和計算機視覺的基本任務有一個較為系統的學習,這塊應該轉看深度學習入門等相關話題,斯坦福的李飛飛cs231n被推薦的挺多的。這塊我就不多贅述了,我更多分享後面三個階段關於分割的學習。第一階段:看分割是分割這個階段的典型表現是,有了對深度學習和計算機視覺的基礎,開始痴迷於各種分割網絡和loss改進,對benchmark的每一個性能的改進都有很好的follow。這個階段應該梳理以下問題。
  • 深度學習是什麼,如何進行深度學習?
    開展深度學習的研究與實踐正是把握教學本質的一種積極努力,是我國課程教學改革走向深入的必需。 當前,智能機器尤其是智能化穿戴設備的大量出現,部分傳統職業已被替代,甚至有人認為教師和教學也可能被替代而消失。在這樣的情形下,我們不得不思考:在智能化時代,真的不需要教學了嗎?真的不需要教師了嗎?
  • 深度學習入門
    那麼深度學習到底是什麼,其與傳統的機器學習之間又有什麼樣的關聯。對於想入坑深度學習的同學,又該從哪些方面入手。這就是本文要回答的問題。深度學習的提出       先從深度學習的提出開始說起,深度學習的概念是由Hinton在2006年提出,他當時首次提出了深度信念網絡(DBN),相比之前,他採用無監督方式逐層訓練深層網絡,在深層網絡訓練中取得了跨越式的進展。