這篇文章首先發(fā)布在我的博客上:https://www.codewoody.com/posts/37730
Swift是蘋果公司研發(fā)的用來取代Objective C進行蘋果生態(tài)系統(tǒng)下軟件開發(fā)的語言膜宋。而且蘋果對于Swift的野心不至于一款A(yù)PP專用的開發(fā)語言而已。
從Swfit語言發(fā)布以來,蘋果公司就將Swift開源局扶,并且在Swift版本迭代過程中積極聽取來自普通開發(fā)者的意見征炼。蘋果致力于將Swift打造成跨平臺的
通用變成語言肛炮。我從Swfit發(fā)布起就開始使用了厚棵,當時接觸Swift的時候就為其所吸引烤宙,其引入的很多特性二汛,如Type Interference婿崭, Optional,以及
簡潔的語言形式等等肴颊,都能搞大大提高生產(chǎn)效率氓栈,并且提高程序的可讀性。現(xiàn)在我已經(jīng)不怎么做iOS的開發(fā)的婿着,用Swift也偏少授瘦。這兩天突然看到了一篇名為A Comprehensive Guide to Learn Swift from Scratch for Data Science的文章,便想立刻通讀一遍竟宋,也許在之后我可以多用Swift來做研究方面的內(nèi)容提完。
Overview
- Swift很快就成為了最為強大和有效的數(shù)據(jù)科學變成語言之一;
- Swift和Python比較類似丘侠,因此你可以很容易地遷移到Swift上徒欣;
- 這里我們將會涉及Swift的基礎(chǔ)知識,并學會如何快速搭建第一個數(shù)據(jù)科學模型蜗字;
簡介
Python在數(shù)據(jù)科學的領(lǐng)域的火熱程度自然不用多少打肝,各種各樣的排名和調(diào)查都將Python列為數(shù)據(jù)科學編程語言的佼佼者。
Python本身是非常靈活的挪捕,作為動態(tài)語言粗梭,你在使用Python不太需要遵守很多變成方面的潛規(guī)則,這帶來很大的靈活性级零。不過這導致隨著項目復(fù)雜度的增長断医,維護Python項目會變得比較困難。當然妄讯,性能也是一個重要的因素孩锡。一般腳本級別的數(shù)據(jù)科學應(yīng)用,Python的性能并不突出亥贸,Python一般被用來當做膠水語言躬窜,主要的計算一般是其他語言實現(xiàn)的模塊來完成。不過復(fù)雜項目中Python的性能還是會成為一個瓶頸炕置。
不過要記住的一點是荣挨,數(shù)據(jù)科學是一個含義廣泛且不斷演化的學科男韧。因此其使用的語言也要不斷演化。還記得R語言在數(shù)據(jù)科學中扮演老大角色的日子嗎默垄?與Python同時興起的還有Julia語言此虑。
沒錯,這里我們就要來討論一下將Swift語言應(yīng)用到數(shù)據(jù)科學中口锭。
“I always hope that when I start looking at a new language, there will be some mind-opening new ideas to find, and Swift definitely doesn’t disappoint. Swift tries to be expressive, flexible, concise, safe, easy to use, and fast. Most languages compromise significantly in at least one of these areas.” – Jeremy Howard
當Jeremy Howard【~Howard was the President and Chief Scientist at Kaggle】為一個語言背書朦前,且將這門語言應(yīng)用到他的日常數(shù)據(jù)科學研究中時,你就應(yīng)該暫時停止你手上的工作好好聽一聽了鹃操。
在這這篇文章中我們將學習Swift編程語言韭寸,以及如何將其應(yīng)用到數(shù)據(jù)科學領(lǐng)域中【~原作者真啰嗦】。如果你是Python用戶荆隘,你會發(fā)現(xiàn)Swift和Python之間有很多的相似性恩伺。
Why Swift?
<i>“PyTorch was created to overcome the gaps in Tensorflow. FastAI was built to fill gaps in tooling for PyTorch. But now we’re hitting the limits of Python, and Swift has the potential to bridge this gap” </i>
<p style="text-align: right"><i>– Jeremy Howard</i></p>
今年來數(shù)據(jù)科學領(lǐng)域?qū)τ赟wift的興趣日漸增長,幾乎人人都在討論這個話題椰拒。以下是你要學習Swfit語言的幾個原因:
- Swift很快晶渠,幾乎接近C語言的水平;
- 與此同時燃观,Swift語言非常簡潔褒脯,可讀性很高。這和Python類似仪壮『┑撸【~個人認為Swift的可讀性可比Python高多了】;
struct MyModel: Layer {
var conv = Conv2D<Float>(filterShaper: (5, 5, 3, 6))
var pool = MaxPool2D<Float>(2)
var flatten = Flatten<Float>()
var dense = Dense<Float>(16 * 5 * 5, 10)
@differentiable
func call(_ input: Tensor<Float>) -> Tensor<Float> {
return dense(flatten(pool(conv(input))))
}
}
class MyModel(nn.Model):
def __init__(self):
super().__init__()
self.conv = nn.Conv2d(3, 6, kernal_size=5)
self.pool = nn.MaxPool2d(2)
self.flatten = Flatten()
self.dense = nn.Linear(16 * 5 * 5, 10)
def forward(self, input):
return self.dense(self.flatten(self.pool(self.conv(input))))
- 相比于Python积锅,Swift是一門更高效爽彤,穩(wěn)定,安全的編程的語言缚陷;
- Swift更適合應(yīng)用到移動應(yīng)用場景适篙。Swift是iOS的官方變成語言;
- Swift對于自動微分操作支持非常好箫爷,因此非常適合數(shù)值計算【~參見上面的@differentiable】嚷节;
- Swift背后有Google,Apple和FastAI的支持
下面這個視頻是Jeremy Howard談?wù)揝wift的優(yōu)勢的視頻。
<iframe width="560" height="315" src="https://www.youtube.com/embed/drSpCwDFwnM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
Swift Basic for Data Analysis
在我們開始將Swift應(yīng)用于數(shù)據(jù)科學研究之前,我們先來學習一下Swift語言的基礎(chǔ)只是等缀。
Swift生態(tài)
目前Swift的數(shù)據(jù)科學應(yīng)用生態(tài)主要由兩個生態(tài)系統(tǒng)組成:
- 開源生態(tài)
- 蘋果生態(tài)
在開源生態(tài)系統(tǒng)中墙懂,我們可以在任何操作系統(tǒng)下載并運行swift憎茂。我們可以使用一些非常酷的Swift庫來構(gòu)建機器學習應(yīng)用,例如Swift for Tensorflow, SwiftAI以及SwiftPlot.
Swift也能讓我們無縫地從Python中引入成熟的數(shù)據(jù)科學庫雹食,例如Numpy, pandas, matplotlib以及scikit-learn缓屠。所以如果你之前還在擔心從Python遷移到Swift上有任何無法逾越的障礙的話奇昙,現(xiàn)在你可以寬心了。
另一方面敌完,蘋果公司的生態(tài)系統(tǒng)也有其優(yōu)勢储耐。蘋果公司提供了一些有用的庫,如CoreML滨溉,讓我們能夠在Python中訓練大型的模型并且直接導入到Swift中應(yīng)用什湘。另外,其中還包括了一些已經(jīng)提前訓練好了的成熟模型晦攒,我們可以直接在iOS和macOS應(yīng)用中使用禽炬。
還有一些其他的有意思的庫,比如Swift-CoreML-Transformers勤家,可以讓我們在iPhone上使用業(yè)界最新的文字生成模型,例如GPT-2, BERT等柳恐。
<i>There are multiple differences between the two ecosystems. But the most important one is that in order to use the Apple ecosystem, you need to have an Apple machine to work on and you can only build for Apple devices like the iOS, macOS etc.</i>
現(xiàn)在你對Swift有了一個宏觀的了解了伐脖,下面我們來走進代碼。
準備Swift環(huán)境
在Google Colab【~Colaboratory 是一個免費的 Jupyter 筆記本環(huán)境乐设,不需要進行任何設(shè)置就可以使用讼庇,并且完全在云端運行】上提供了支持GPU和TPU的Swift版本,這里我們直接使用這一服務(wù)近尚,從而省去安裝過程蠕啄。
<img src="https://imgs.codewoody.com/uploads/big/451eba98fbef8b87bf2473971390e66f.png" alt="" style="width: 60%"/>
你可以遵循下面的步驟創(chuàng)建一個啟用了Colab notebook。
- 打開一個空白的Swift notebook;
- 點擊"File"戈锻,然后選擇"Save a copy in Drive" - 這會將Swift notebook保存到你的Google Drive里面歼跟。
- 到這里我們就可以在Colab里面使用Swift了。我們來寫下第一行代碼:
print("hello world from Swift")
這就是Swift的hello world程序了格遭!接下來如果你想在本地運行Swift哈街,你可以按照如下的鏈接進行操作:
- Swift安裝指南:install instructions;
- 要在Ubuntu中安裝Jupyter Notebook:[Jeremy Howard's instructions to install Swift]拒迅;
- 在Ubuntu上也可以使用Docker來安裝Swift:Swift for Docker
如果在macOS下面骚秦,直接從應(yīng)用商店安裝xcode就行,可以創(chuàng)建一個Swift Playground來試試Swift語言的特性璧微。我記得iPad上也有Swift Playground的應(yīng)用作箍。
接下來讓我們快速過一下Swift的基本語言特性。
The Print function
hello world程序中前硫,print
函數(shù)的形式一點都不陌生啦胞得。
print("Swift is easy to learn!")
Variable in Swift
Swift提供了兩個創(chuàng)建變量的選項:let
和var
。其中let
被用來創(chuàng)建常量开瞭,常量的值在其聲明周期中是不能被改變的懒震。var
用來創(chuàng)建變量罩息,這意味著類似在Python里一樣,你可以修改變量的值个扰。
我們來看下面的例子瓷炮。創(chuàng)建兩個變量:
let a = "Analytics"
var b = "Vidhya"
讓我們來嘗試修改其值:
b = "AV"
a = "AV"
我們可以看到修改a的值時會出現(xiàn)錯誤:
這種支持創(chuàng)建常量的能力可以幫助我們消除很多潛在bug。后面你可以看到我們會用let
來創(chuàng)建那些非常重要且我們不希望修改的值递宅。例如訓練數(shù)據(jù)和結(jié)果我們會用let
來創(chuàng)建娘香,而一些臨時變量會使用var
來創(chuàng)建。
Swift的另一個很酷的特性是你可以使用emoji來作為變量名【~其實就是對Unicode的支持】
我們也可以使用希臘字母來作為變量名稱:
var π = 3.1415925
Swift數(shù)據(jù)類型
Swift支持一些通用的類型办龄,如整型烘绽,字符串,單精度浮點數(shù)(Float)和雙精度浮點數(shù)(Double)俐填。在創(chuàng)建變量時安接,Swift會根據(jù)初始化值自動推斷變量的類型。
let marks = 63
let percentage = 70.0
let name = "Sushil"
在創(chuàng)建變量時你也可以顯式的聲明變量類型英融。如果初始化值和聲明的類型不同盏檐,Swift會拋出錯誤。
let weight: Double = 62.8
字符串格式化的方式在Swift中非常簡潔驶悟。只需要用反斜杠\
后面跟上括號就可以了:
let no_of_apples = 3
print("I have \(no_of_apples) apples")
你可以使用連續(xù)的三個雙引號"""
來創(chuàng)建多行字符串胡野。
列表和字典(List and Dictionaries)
如同Python里面一樣,Swift里面也支持List和Dictionary數(shù)據(jù)結(jié)構(gòu)痕鳍。不同于Python硫豆,在Swift中這兩種類型都使用方括號[]
。
var shoppingList = ["catfish", "water", "tulip", "blue paint"]
shoppingList[1] = "bottle of water"
var occupationsDist = [
"Malcolm": "Caption",
"Kaylee": "Mechanic"
]
ccupationsDict["Jayne"] = "Public Relations"
循環(huán)
除了支持經(jīng)典的循環(huán)之外笼呆,Swift有一些自定義的比較獨特的循環(huán)形式:
for...in loop
類似Python的寫法熊响,在Swift中,你可以以如下形式來遍歷列表Lists或者ranges
for i in 0...5 {
print(i)
}
var someList = [20, 30, 10, 40]
for item in someList {
print(item * 2)
}
上面的連續(xù)三個點的符號用來創(chuàng)建ranges诗赌。...
創(chuàng)建的兩側(cè)是閉集耘眨, 如果要創(chuàng)建不包含最右側(cè)的變量的范圍,使用..<
符號即可境肾。
注意Swift使用花括號剔难,而非縮進形式來表示代碼層次結(jié)構(gòu)
在Swift中也可以使用比較經(jīng)典的while和for循環(huán)。You can learn more about loops in Swift here
條件
這里就是非常經(jīng)典的if語句了奥喻,不做贅述偶宫。
Swift中條件語句針對Optional類型做了專門的優(yōu)化。
函數(shù)
下圖是Swift函數(shù)的定義形式
代碼中的注釋
Swift中的注釋形式和C/C++比較像:用//
來開始行注釋环鲤,用/* ... */
來常見塊注釋纯趋。在代碼中多寫注釋是一個好習慣。
在Swift中使用Python的庫
Swift支持和Python的互操作,這意味著你可以直接在Swift中使用大部分Python庫:調(diào)用函數(shù)或者做變量的類型轉(zhuǎn)換吵冒。這個特性大大增強了Swift的功能纯命。盡管Swift的生態(tài)還非常年輕,但是我們可以直接使用非常成熟的Python庫痹栖,如Numpy亿汞,Pandas還有Matplotlib等。
為了引用Python模塊揪阿,我們只需要將Swift的Python
模塊導入疗我,然后使用這個模塊的接口即可:
import Python
// Load numpy from python
let np = Python.import("numpy")
// create array of zeros
var zeros = np.ones([2, 3])
print(zeros)
matplotlib庫也可以直接導入:
在Swift中使用Tensorflow創(chuàng)建一個基礎(chǔ)模型
Swift4Tensorflow是Swift生態(tài)中一個非常成熟的庫。我們可以用非常類似Keras的方式來創(chuàng)建機器學習和深度學習的模塊南捂。
有意思的是吴裤,Swift4Tensorflow不只是一個簡單的Tensorflow的Swift語言打包,而是根據(jù)Swift本身語言開發(fā)的庫溺健。未來這個庫可能會變成Swift的語言的核心部分麦牺。
<i>What this means is that the amazing set of Engineers from Apple’s Swift team and Google’s Tensorflow team will make sure that you are able to do high-performance machine learning in Swift.</i>
這個庫加入了一些Swift語言的有用特性,如自動微分支持(這讓我想起了PyTorch中的Autogrid)鞭缭。
關(guān)于數(shù)據(jù)集
首先讓我們來解釋一下這個section的問題枕面。如果你之前接觸過深度學習領(lǐng)域,你應(yīng)該比較熟悉了缚去。
我們將會建立一個卷積神經(jīng)網(wǎng)絡(luò)(CNN)模型來將MNIST數(shù)據(jù)集中的圖片識別為數(shù)字字符。MNIST數(shù)據(jù)集包括60,000個訓練圖像和10,000個測試圖像琼开。圖像為手寫的數(shù)字字符易结。
這個數(shù)據(jù)集是研究計算機視覺的時候一個非常常用的數(shù)據(jù)集,所以我在這里不做細節(jié)性的描述柜候。要了解更多搞动,你可以讀一下這個。
配置羨慕
在我們開始創(chuàng)建模塊之前渣刷。我們需要下載數(shù)據(jù)集并進行預(yù)處理鹦肿。為了你的方便我已經(jīng)創(chuàng)建了一個Github倉庫,提供了預(yù)處理代碼以及數(shù)據(jù)辅柴。讓我們下載配置代碼箩溃,下載數(shù)據(jù)集并導入黑色的庫。
%include "EnableIPythonDisplay.swift"
IPythonDisplay.shell.enable_matplotlib("inline")
import Foundation
import Python
let os = Python.import("os")
let plt = Python.import("matplotlib.pyplot")
os.system("git clone https://github.com/mohdsanadzakirizvi/swift-datascience.git")
os.chdir("/content/swift-datascience")
運行上面的代碼碌嘀,數(shù)據(jù)集就會下載到Colab的環(huán)境中了涣旨。
在本地運行時代碼應(yīng)該需要修改,這個我們后面來討論
不過這個操作太丑陋了股冗,沒有使用Swift的native方法來調(diào)用shell命令霹陡。
載入數(shù)據(jù)
%include "/content/swift-datascience/MNIST.swift"
// Load dataset
let dataset = MNIST(batchSize: 128)
// Get first 5 images
let imgs = dataset.trainingImages.minibatch(at: 0, batchSize: 5).makeNumpyArray()
print(imgs.shape)
查看一下數(shù)據(jù)集
我們嘗試畫出數(shù)據(jù)集中的圖片來看看我們要處理的問題:
# Display first 5 images
for img in imgs{
plt.imshow(img.reshape(28,28))
plt.show()
}
畫出來大概是下面的樣子:
定義模型結(jié)構(gòu)
現(xiàn)在讓我們來定義我們的模型的結(jié)構(gòu)。這里我使用了LeNet-5架構(gòu),一個非撑朊蓿基礎(chǔ)的CNN模型攒霹,包含兩個卷基層,average pooling還有三個Dense層【~應(yīng)該是全連接層浆洗?】催束。最后一級dense layer的輸出維數(shù)是10,因為我們有10個類別要輸出辅髓,分別代表0-9.
import TensorFlow
let epochCount = 100
let batchSize = 128
// The LeNet-5 model
var classifier = Sequential {
Conv2D<Float>(filterShape: (5, 5, 1, 6), padding: .same, activation: relu)
AvgPool2D<Float>(poolSize: (2, 2), strides: (2, 2))
Conv2D<Float>(filterShape: (5, 5, 6, 16), activation: relu)
AvgPool2D<Float>(poolSize: (2, 2), strides: (2, 2))
Flatten<Float>()
Dense<Float>(inputSize: 400, outputSize: 120, activation: relu)
Dense<Float>(inputSize: 120, outputSize: 84, activation: relu)
Dense<Float>(inputSize: 84, outputSize: 10, activation: softmax)
}
你可能已經(jīng)注意到了泣崩,上面的代碼和你在Keras(或者PyTorch,TensorFlow)中寫的Python代碼非常類似
<i>The simplicity of writing code is one of the biggest points of Swift.</i>
Swift4Tensorflow支持很多現(xiàn)成的多層模型洛口。更多閱讀參考:https://www.tensorflow.org/swift/api_docs/Structs
選擇梯度下降作為Optimizer
類似的矫付,這里我們也需要選擇Optimizer來優(yōu)化我們的模型。我們這里選擇使用隨機梯度下降算法(stochastic gradient descent, SGD)第焰。
let optimizer = SGD(for: classifier, learningRate: 0.1)
Swift4Tensorflow還支持很多Optimizer:
- AMSGrad
- AdaDelta
- AdaGrad
- AdaMax
- Adam
- Parameter
- RMSProp
- SGD
模型訓練
現(xiàn)在萬事俱備了买优,讓我們開始訓練模型吧。
print("Beginning training...")
struct Statistics {
var correctGuessCount: Int = 0
var totalGuessCount: Int = 0
var totalLoss: Float = 0
}
// Store accuracy results during training
var trainAccuracyResults: [Float] = []
var testAccuracyResults: [Float] = []
// The training loop.
for epoch in 1...epochCount {
var trainStats = Statistics()
var testStats = Statistics()
// Set context to training
Context.local.learningPhase = .training
for i in 0 ..< dataset.trainingSize / batchSize {
// Get mini-batches of x and y
let x = dataset.trainingImages.minibatch(at: i, batchSize: batchSize)
let y = dataset.trainingLabels.minibatch(at: i, batchSize: batchSize)
// Compute the gradient with respect to the model.
let ??model = classifier.gradient { classifier -> Tensor<Float> in
let ? = classifier(x)
let correctPredictions = ?.argmax(squeezingAxis: 1) .== y
trainStats.correctGuessCount += Int(Tensor<Int32>(correctPredictions).sum().scalarized())
trainStats.totalGuessCount += batchSize
let loss = softmaxCrossEntropy(logits: ?, labels: y)
trainStats.totalLoss += loss.scalarized()
return loss
}
// Update the model's differentiable variables along the gradient vector.
optimizer.update(&classifier, along: ??model)
}
// Set context to inference
Context.local.learningPhase = .inference
for i in 0 ..< dataset.testSize / batchSize {
let x = dataset.testImages.minibatch(at: i, batchSize: batchSize)
let y = dataset.testLabels.minibatch(at: i, batchSize: batchSize)
// Compute loss on test set
let ? = classifier(x)
let correctPredictions = ?.argmax(squeezingAxis: 1) .== y
testStats.correctGuessCount += Int(Tensor<Int32>(correctPredictions).sum().scalarized())
testStats.totalGuessCount += batchSize
let loss = softmaxCrossEntropy(logits: ?, labels: y)
testStats.totalLoss += loss.scalarized()
}
let trainAccuracy = Float(trainStats.correctGuessCount) / Float(trainStats.totalGuessCount)
let testAccuracy = Float(testStats.correctGuessCount) / Float(testStats.totalGuessCount)
// Save train and test accuracy
trainAccuracyResults.append(trainAccuracy)
testAccuracyResults.append(testAccuracy)
print("""
[Epoch \(epoch)] \
Training Loss: \(trainStats.totalLoss), \
Training Accuracy: \(trainStats.correctGuessCount)/\(trainStats.totalGuessCount) \
(\(trainAccuracy)), \
Test Loss: \(testStats.totalLoss), \
Test Accuracy: \(testStats.correctGuessCount)/\(testStats.totalGuessCount) \
(\(testAccuracy))
""")
}
上面的代碼中用了一些fancy的數(shù)學符號挺举,但是由于這些符號輸入并不方便杀赢,因此實際編程中我們不會這么做。
上面的代碼流程中我們將數(shù)據(jù)集的樣本傳遞給模型湘纵,幫助其改善預(yù)測精度脂崔。訓練步驟如下:
- 訓練重復(fù)若干次,每次我們遍歷整個訓練集梧喷。
- 在每次訓練迭代中砌左,我們逐個傳入features(
x
)和labels(y
),這對下一步非常重要铺敌。 - 使用樣本的features汇歹,使用模型做出預(yù)測,并與labels提供的真值進行比對偿凭,進而計算出模型的損失函數(shù)和下降梯度方向产弹。
- 這是梯度下降算法發(fā)揮了作用,我們沿著梯度方向更新模型的變量弯囊。
- 追蹤訓練過程中的一些統(tǒng)計數(shù)據(jù)來方便我們后續(xù)做可視化痰哨。
- 在第一步提到的重復(fù)訓練中,每次重復(fù)2至5步匾嘱。
epochCount
變量為重復(fù)遍歷數(shù)據(jù)集的次數(shù)作谭。你可以修改其值嘗試一下。
需要多少次遍歷來取得90%以上的正確率呢奄毡?我可以在12次訓練下在訓練集和測試集上獲得97%以上的正確率折欠。
<img src="https://imgs.codewoody.com/uploads/big/5b367d407d32802ecce0bcd33b3e9f9a.png" alt="" style="border: none;"/>
可視化輸出訓練過程
用下面的方法我么可以可視化輸出訓練過程中的誤差演變過程:
plt.figure(figsize: [12, 8])
let accuracyAxes = plt.subplot(2, 1, 1)
accuracyAxes.set_ylabel("Train Accuracy")
accuracyAxes.plot(trainAccuracyResults, color: "blue")
let lossAxes = plt.subplot(2, 1, 2)
lossAxes.set_ylabel("Test Accuracy")
lossAxes.set_xlabel("Epoch")
lossAxes.plot(testAccuracyResults, color: "yellow")
plt.show()
得到的結(jié)果如下圖所示:
Swift數(shù)據(jù)科學應(yīng)用的未來
有產(chǎn)業(yè)專家對Swift做出了很高的評價,認為其有潛力成為數(shù)據(jù)科學的主流語言,同時也能成為機器學習類應(yīng)用開發(fā)的主要工具锐秦。
目前咪奖,很多fancy的數(shù)據(jù)科學相關(guān)的Swift庫還在開發(fā)中,其背后有強大的業(yè)界支持酱床。我非逞蛘裕看好Swift生態(tài)的未來--甚至會比現(xiàn)在的Python更加強大。
下面是一些你可以進一步研究的Swift庫:
- Nifty(Demo): Swift的通用數(shù)值就散庫
- Swiftplot: Swift的數(shù)據(jù)可視化庫
- Swift for Tensorflow: 下一代的機器學習平臺
- Swift AI: 基于Swift的高性能深度學習框架
本文涉及的所有代碼托管在Github上扇谣。