写给 Web 开发者的深度学习教程 - 向量化 矩阵
前言
在這個科技發(fā)展日新月異的時代,行業(yè)的寵兒與棄兒就如同手掌的兩面,只需輕輕一翻,從業(yè)者的境遇便會有天翻地覆的改變。
人工智能作為近兩年來業(yè)界公認的熱門領域,不同于之前火熱的移動端開發(fā)或前端開發(fā),其距離傳統(tǒng)軟件開發(fā)行業(yè)之遠,入門門檻之高,都是以往不曾出現(xiàn)過的,這也讓許多希望能夠終身學習,持續(xù)關注行業(yè)發(fā)展的軟件工程師們望而卻步。
在我們進一步討論阻止傳統(tǒng)軟件工程師向人工智能領域轉型的障礙之前,讓我們先來明確幾個名詞的定義:
- 人工智能:以機器為載體展現(xiàn)出的人類智能,如圖像識別等傳統(tǒng)計算機無法完成的工作
- 機器學習:一種實現(xiàn)人工智能的方式
- 深度學習:一種實現(xiàn)機器學習的技術
那么到底是哪些障礙阻止了傳統(tǒng)軟件工程師進入人工智能領域呢?
- 數(shù)學:不同于后端,前端,移動端等不同領域之間的區(qū)別,人工智能,或者說我們接下來將要重點討論的深度學習,是一門以數(shù)學為基礎的科學。學習它的前置條件,不再是搭建某一個開發(fā)環(huán)境,了解某一門框架,而是需要去理解一些諸如矩陣,反向傳播,梯度下降等數(shù)學概念。
- 生態(tài):很久以來,學術界都以 Python 作為其研究的默認語言,創(chuàng)造了如 NumPy,Matplotlib 等一系列優(yōu)秀的科學計算工具。而對于終日與用戶界面打交道的 Web 開發(fā)者來說,一系列基礎工具的缺乏直接導致了哪怕是建立起來一個最基礎的深度學習模型都異常困難。
為了解決上面提到的這兩個障礙,筆者使用 TypeScript 以零依賴的方式初步完成了一個基于 JavaScript 的深度學習框架:deeplearning-js,希望可以以 Web 開發(fā)者熟悉的語言與生態(tài)為各位提供一種門檻更低的深度學習的入門方式,并將以寫給 Web 開發(fā)者的深度學習教程這一系列文章,幫助各位理解深度學習的基本思路以及其中涉及到的數(shù)學概念。
整體架構
src/ ├── activationFunction // 激活函數(shù) │ ├── index.ts │ ├── linear.spec.ts │ ├── linear.ts │ ├── linearBackward.spec.ts │ ├── linearBackward.ts │ ├── relu.spec.ts │ ├── relu.ts │ ├── reluBackward.spec.ts │ ├── reluBackward.ts │ ├── sigmoid.spec.ts │ ├── sigmoid.ts │ ├── sigmoidBackward.spec.ts │ ├── sigmoidBackward.ts │ ├── softmax.spec.ts │ ├── softmax.ts │ ├── softmaxBackward.spec.ts │ └── softmaxBackward.ts ├── costFunction // 損失函數(shù) │ ├── crossEntropyCost.spec.ts │ ├── crossEntropyCost.ts │ ├── crossEntropyCostBackward.spec.ts │ ├── crossEntropyCostBackward.ts │ ├── index.ts │ ├── quadraticCost.spec.ts │ ├── quadraticCost.ts │ ├── quadraticCostBackward.spec.ts │ └── quadraticCostBackward.ts ├── data // 數(shù)據(jù)結構:矩陣 & 標量 │ ├── Array2D.spec.ts │ ├── Array2D.ts │ ├── Scalar.spec.ts │ ├── Scalar.ts │ └── index.ts ├── index.ts ├── math // 計算:矩陣計算函數(shù) & 生成隨機數(shù)矩陣 & 生成零矩陣 │ ├── add.spec.ts │ ├── add.ts │ ├── divide.spec.ts │ ├── divide.ts │ ├── dot.spec.ts │ ├── dot.ts │ ├── index.ts │ ├── multiply.spec.ts │ ├── multiply.ts │ ├── randn.spec.ts │ ├── randn.ts │ ├── subtract.spec.ts │ ├── subtract.ts │ ├── transpose.spec.ts │ ├── transpose.ts │ ├── zeros.spec.ts │ └── zeros.ts ├── model // 模型:初始化參數(shù) & 正向傳播 & 反向傳播 & 更新參數(shù) │ ├── Cache.ts │ ├── backPropagation.ts │ ├── forwardPropagation.ts │ ├── index.ts │ ├── initializeParameters.spec.ts │ ├── initializeParameters.ts │ ├── train.ts │ └── updateParameters.ts ├── preprocess // 數(shù)據(jù)預處理:數(shù)據(jù)標準化 │ ├── index.ts │ └── normalization │ ├── index.ts │ ├── meanNormalization.spec.ts │ ├── meanNormalization.ts │ ├── rescaling.spec.ts │ └── rescaling.ts └── utils // 幫助函數(shù):數(shù)據(jù)結構轉換 & 矩陣廣播├── broadcasting.spec.ts├── broadcasting.ts├── convertArray1DToArray2D.ts├── convertArray2DToArray1D.ts└── index.ts 復制代碼作為一個專注于深度學習本身的框架,deeplearning-js 只負責構建及訓練深度學習模型,使用者可以使用提供的 API 在任意數(shù)據(jù)集的基礎上搭建深度學習模型并獲得訓練后的結果,具體的例子各位可以參考 Logistic regression。
我們將學習率,迭代次數(shù),隱藏層神經(jīng)元個數(shù)等這些超參數(shù)暴露給終端用戶,deeplearning-js 會自動調(diào)整模型,給出不同的輸出?;谶@些輸出,我們就可以自由地使用任意圖表或可視化庫來展現(xiàn)模型訓練后的結果。
另外,大家在閱讀本系列文章的同時,建議配合著 deeplearning-js 的源碼一起閱讀,相信這樣的話,你將會對深度學習到底在做一件什么樣的事情有一個更感性的認識。
向量化
不同于其他的機器學習教程,我們并不希望在一開始就將大量拗口的數(shù)學名詞及概念灌輸給大家,相反,我們將從訓練深度學習模型的第一步數(shù)據(jù)處理講起。
讓我們以學術界非常著名的 Iris 數(shù)據(jù)集為例。
現(xiàn)在我們擁有了 150 個分別屬于 3 個品種的鳶尾屬植物的花萼長度,寬度及花瓣長度,寬度的樣本數(shù)據(jù),目的是訓練一個輸入任意一個鳶尾屬植物的花萼長度,寬度及花瓣長度,寬度,判斷它是否是這 3 個品種中的某一個品種,即邏輯回歸。
雖然我們的最終模型是輸入任意一個樣本數(shù)據(jù)得到結果,但我們在訓練時,并不希望每次只能夠輸入一個樣本數(shù)據(jù),而是希望一次性地輸入所有樣本數(shù)據(jù),得到訓練結果與實際結果的差值,然后使用反向傳播來修正這些差異。
于是我們就需要將多個樣本數(shù)據(jù)組合成一個矩陣,如下圖所示:
在將數(shù)據(jù)向量化后,我們才有了處理大數(shù)據(jù)集的能力,即在整個數(shù)據(jù)集上而不是在某個數(shù)據(jù)樣本上訓練模型。這也是為什么在深度學習領域,GPU 比 CPU 要快得多的原因。在訓練深度學習模型時,所有的計算都是基于矩陣的,于是并行計算架構(處理多任務時計算時間等于最復雜任務的完成時間)的 GPU 就要比串行計算架構(處理多任務時計算時間等于所有任務運行時間的總和)的 CPU 快得多。
細心的讀者可能會觀察到上圖中的一個數(shù)據(jù)樣本中的不同維度的數(shù)據(jù)是豎排列的,這與傳統(tǒng)數(shù)組中數(shù)據(jù)的橫排列方式恰好相反,即我們需要將
[5.1, 3.5, 1.4, 0.2]復制代碼轉換為
[[5.1],[3.5],[1.4],[0.2], ]復制代碼細心的讀者可能又會問了,如 Iris 數(shù)據(jù)集,為什么一定要將初始數(shù)據(jù)轉換為 4 行 150 列的矩陣,用方便處理的 150 行 4 列的矩陣不可以嗎?
對于這個問題有以下兩方面的考慮。在接下來輸入數(shù)據(jù)與隱藏層做矩陣點乘時
隱藏層矩陣(W)的列數(shù)需要等于輸入層(A)的行數(shù),所以為了減少不必要的計算量,我們希望輸入層的行數(shù)盡可能得小,于是我們將數(shù)據(jù)樣本的維度數(shù)與樣本數(shù)量進行對比,不難得出在絕大多數(shù)情況下,數(shù)據(jù)樣本的維度數(shù)都遠遠小于樣本數(shù)量這個結論。另一方面,在點乘之后,結果矩陣的列數(shù)將等于輸入層的列數(shù),也就是說如果我們希望我們的輸出是一個 [X, 150] 的矩陣,輸入層就需要是一個 [4, 150] 的矩陣。
那么如何快速地在原始數(shù)據(jù)集與使用數(shù)據(jù)集之間進行這樣的轉換呢?這就涉及到矩陣的一個常用運算,矩陣轉置了。
矩陣
說起矩陣,它的許多奇怪的特性,如轉置,點乘等,想必是許多朋友大學時代的噩夢。在這里我們不談具體的數(shù)學概念,先嘗試用幾句話來描述一下矩陣及它的基礎運算。
從最直觀的角度來講,確定一個矩陣需要哪些信息?一是矩陣的形狀,即坐標系(空間),二是矩陣在這個坐標系下各個維度上的值(位置)。
- 矩陣(Array2D):N 維空間中的一個物體,在每一維度上都有其確定的位置
- 矩陣相加(add):在某一維度或多個維度上對原物體進行拉伸
- 矩陣相減(subtract):在某一維度或多個維度上對原物體進行裁剪
- 矩陣相乘(multiply):基于原物體的某一個原點對原物體進行等比放大
- 矩陣相除(divide):基于原物體的某一個原點對原物體進行等比縮放
- 矩陣轉置(transpose):基于原物體的原點對原物體進行翻轉
- 矩陣點乘(dot):對原物體進行左邊矩陣所描述的位置轉換,即移動
在 deeplearning-js 中我們使用二維數(shù)組的數(shù)據(jù)結構來表示矩陣,對于上述運算的具體代碼實現(xiàn)各位可以參考 Array2D。
一個簡單的數(shù)據(jù)轉換的例子如下:
function formatDataSet(dataset: Array<any>) {const datasetSize = dataset.length;let inputValues: Array<number> = [];map(dataset, (example: {"sepalLength": number,"sepalWidth": number,"petalLength": number,"petalWidth": number,"species": string,}) => {const input: any = omit(example, 'species');inputValues = inputValues.concat(values(input));});const input = new Array2D([datasetSize, inputValues.length / datasetSize],inputValues,).transpose();return input; }復制代碼小結
在理解了數(shù)據(jù)向量化及矩陣的概念后,相信大家已經(jīng)可以將大樣本量,以數(shù)組形式存儲的數(shù)據(jù)轉換為適合進行深度學習模型訓練的大型矩陣了,接下來讓我們從如何初始化參數(shù)開始,一步步搭建我們的第一個深度學習模型。
總結
以上是生活随笔為你收集整理的写给 Web 开发者的深度学习教程 - 向量化 矩阵的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Shell脚本入门基础
- 下一篇: 关于一些知名深度学习模型的转换