0%

最近幾個月來工作的職務上有些轉換,加入到一個小規模的app開發團隊,並且嘗試導入Flutter開發一套codebase就能夠佈署到多個平台的跨平台app。其實網路上已經有非常多介紹Flutter的文章,而原生與跨平台的解決方案比較也很多人討論過,所以這篇會以一個初入Flutter的新手,到成功交付產品上線後,一直到採用Flutter Web的心路歷程分享。

老闆問:能不能弄個跨平台app

其實跨平台一直都存在並且持續有新的技術出現,雖然Flutter還很年輕,但是在這幾年的熱度也快速的竄升。我認為採用跨平台技術對於單純開發給公司內部使用的系統來說,最大的優點就是在人力的節省。在一個開發了web就會被問能不能放在行動裝置上,作了手機app就會被問能不能web也有的環境下,為了提供多個平台管道給使用者,一支系統難道不能只開發一次就好了嗎(彷彿老闆的聲音飄出來了)。

要開發多個平台可以使用的應用程式,除了能夠支援不同裝置尺寸的web應用程式外,如果需求面上又希望能保留行動裝置功能的支援,那Flutter自然是一個適合的選擇。

Flutter官方網站直接的介紹是這樣寫的:

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.

Web Hit Stable Millestone

Android和iOS是Flutter最先開始穩定支援的平台,但終究web平台還是一個觸及度最高的管道,也許平板電腦不是每個人都有,但user們還是都用電腦上班。在專案達成mobile first的任務,接著要開始擴大觸及程度時,剛好迎來Flutter Engage在3月3號發佈了Flutter 2的更新。

Perhaps the single largest announcement in Flutter 2 is production-quality support for the web.

Flutter 2正式將Web支援放進stable channel,原本要打開Web支援還得使用beta channel才行,甚至官方也建議在web正式穩定支援前,要發佈成產品都需要仔細的確認執行起來是否有bug。當下還在觀望是否該導入Flutter Web才過沒多久,隨著Flutter 2的發佈後,馬上就在隔天拿起現有的Flutter專案進行POC,直到最近成功的將原本的app正式發佈給使用者使用。

從Flutter到Flutter Web

下面我分享三個如果今天從新手Flutter開發者,一路走到開發產品等級的Flutter Web可能會發生的心路歷程與重點學習資源:

  • 從零開始開始的Flutter菜鳥

身為第一次接觸Flutter的開發者,自己覺得還不算難上手,基本上跟著官方的文件把開發環境裝起來後,接著讀完stateless和stateful widget還有column和row的layout概念後,就可以作出簡單的切版畫面。

Flutter的widget其實很多,常常都是需要的時候邊查邊實作,通常第一步會先找是不是有Flutter已經提供的widget(像是drawer、search、bottom navigation等),如果沒有的話這裡推薦pub.dev還有Flutter Gems,這兩個網站的資源可以幫你找到很多你需要的功能。最後再上http加上json serialization,或是直接跟著cookbook作大致上就能接完後端了。

  • 一個好的狀態管理很重要

成功發佈完app的1.0.0版後,接著來的就是各種app的新功能需求,在追加越來越多的跳頁功能下,開始出現很多的渲染失敗或是改一改就出現畫面亂跳頁的bug。這時候才會發現真的需要一個好的狀態管理,領悟到沒有好好規畫狀態管理的app不止難以維護,更會造成app效能變差。如果這段是在趕鴨子上架的過程被忽略掉的,這時直接建議把Flutter狀態管理這篇讀完,至少用Provider好好設計每個widget的狀態變化與重新渲染的時機。

另外要注意在取後端資料時是否有使用FutureBuilder,FutureBuilder可以幫你確認取得async資料(通常是後端的一包資料)的各種狀態,例如在接到資料前可以先拋出一個自己準備有讀取效果的widget,並在取得資料後再使用這包資料渲染出要程現的widget(對Dashboard而言可能就是一些圖表)。

  • 打開web支援要怎麼作

Flutter web在開發上其實也不困難,而且可以在VSCode用debug模式就可以直接進行開發,Web support for Flutter官方有一個篇章在作介紹,如果是專案一開始就有打算作Web平台,那建議直接跟著文件說明就能夠建立Flutter Web應用程式

但如果今天是有一個已經開發好的app產品要打開Web平台的支援,通常就會遇到比較多的阻礙,當我以為一個彈指就能把web支援打開,但遇到的就是滿滿的error訊息。要將現有的app打開web支援,通常在開發上會遇到的問題我大致上分成三種。

  1. Responsive Design(響應式設計): 不管是從手機或是平板使用的app,如果沒有對螢幕尺寸作適當的處理,通常直接放到web都會直接跑版,官方文件也有一個篇章在介紹響應式設計

  2. Cross-Platform Support Package(跨平台支援套件): 在開發過程中會有一定程度的使用第三方的package,要注意是否有支援所需要的平台特別是web,通常可以在pub.get查到。

  3. Platform Dependency Feature(平台相依性功能): 因為不同平台還是會有差異存在,有些dart自帶的library本身就不支援,比如說web就不能使用dart:io相關的函式,因此在開發的時候需要特別注意。如果需要針對不同的平台用該平台才有支援的widget,通常會先用邏輯判斷目前使用者是使用哪個平台,再渲染該平台特有的widget。

以上是自己在投入開發Flutter到打開web支援的一些心得整理,開發過程也許遇到許多不同的問題,特別是在web支援的部份遇到了許多挑戰。雖然花了許多時間解決各種遇到的轉換問題,但確實可以達到透過single codebase在web和app渲染出一樣的畫面!

上一堂講到kernel logistic regression,並證明L2-regularized linear model都可以kernel化。這堂課會嘗試將kernel放進L2-regularized linear regression作出kernel ridge regression。

因為已經知道最佳的w會是βZ的線性組合,所以這裡一樣可以將w轉換為z的線性組合,當出現Z與Z乘積時就可以換成kernel,轉成透過kernel trick解β問題。

因為這是一個無條件的最佳化問題,所以可以求梯度為0,即對β微分等於0求出讓梯度為0的β最佳解。因為K一定大於0所以可以求出需要求解的反矩陣來解出最好的β。原本之前說的要作nonlinear的方法是先作transform轉換再作regression,而且現可以另一種方法即使用kernel來達成nonlinear regression。

比較linear ridge regression和kernel ridge regression,kernel ridge rgression具有非線性性質所以有更彈性的應用來解決較複雜的問題,但其缺點就是要付出較大的計算時間複雜度。

kernel ridge regression當然也可以拿來作classification,這個方法又稱為LSSVM(least-squares SVM)。在比較Soft-Margin SVM和LSSVM,兩者得到的邊界可能會差不多,但比起Soft-Margin裡面求出的α是稀疏的,前面在解kernel ridge時β沒有特別的限制,因此β會求出dense的解,因而得到比Soft-Margin更多的support vector。而support vector較多的情況下,會造成在預測時效率較差。所以再來的重點是如何讓β也可以和標準SVM一樣求出稀疏解。

這裡介紹到Tube Regression,比起原本的regression會計算每一點算與實際值的誤差,tube regression允許在一個範圍內,即落在tube內的點可以不計算error,而落在tube外的點除了計算與實際值的誤差外要再減掉tube的寬度,得到一種新的error計算方法。這種error被稱為ε-insensitive error,下一步會嘗試透過這個新的error來解出稀疏的β值。

在比較Tube Regression和Squared Regresion會發現,兩者在預測值和實際值接近時,error計算是比較接近的。但是在相差越來越大時,squared regression的error會成長的比較快,因此也比較容易受到雜訊的影響。

L2-Regularized Ture Regression在求解時可以使用任何unconstrain的方式,和之前SVM求hinge error時一樣會遇到max無法微分,雖然也可以使用reprsenter的方式作kernel化,但卻無法保證最後求出的是稀疏解。而標準的SVM一樣無法微分,但是可以被寫成QP問題求解,再透過求對偶問題達成kernel化,而且因為滿足KKT condition可以保證其具有稀疏性。因此這裡可以把L2-Regularized Tube Regression模擬成像原本的SVM再求解(加入C並將w0拆出b值)。

再進一步加入ξn來紀錄犯錯的程度,其中為了將限制式的絕對值拆掉,會得到upper tube violation和lower tube violation兩個ξn。這個式子就是一個Support Vector Regression(SVR)的標準式,而且也會符合QP問題。

這個SVR的標準式會有兩個參數,一個是C和之前的SVM一樣用來控制regularization和violation之間的trade-off。第二個是ε用來紀錄tube的寬度決定允許的犯錯範圍。

有了SVR的primal後,就可以引入Langrange multiplier來轉成對偶問題,所以這裡會引入兩個α來對應兩個ξn,再來就是重複之前課程的推導。

因為SVM和SVR的問題相似,所以可以發現兩者的對偶很相近,而且可以透過QP來解。

回到原本的問題,我們希望β可以是稀疏的,也就是β系數在某些情況要是0。可以發現如果資料是在tube內部ξ會為0,而ξ為0的情況下complementary slackness括號內部的值就不等於0,又因為α和括號內部其中要有一邊要為0,所以α必定要為0,也就是β會等於0。所以位在tube內部的β會為0,而tube外面或是邊界就是對w有貢獻的support vector,這就可以得到β要稀疏解。

到目前為止教過的linear model總共有5種,在分類問題上,除了最早教到的PLA/pocket方法,這幾講教的linear soft-margin SVM是透過找到具有soft-margin性質的超平面來解分類問題;在regression問題中除了前面講到的linear ridge regression,還引申出透過Tube與SVM概念所延伸出來的linear SVR。另外還有regularized logistic regression結合regularization作模型複雜度修正與maximum likelihood概念來處理分類問題。

當linear model具有regularized性質時都可以引入kernel的概念,而這6講的內容分別教了如何將SVM、ridge regression、SVR和logistic regression帶入kernel trick。其中第一排的PLA/pocket和linear SVR,因為其表現都沒有第二排linear soft-margin SVM和linear ridge regression來的好,所以實務上比較少用。第三排的kernel ridge regression和kernel logistic regression因為β解出來是不是稀疏解,所以實務上傾向使用第四排的SVR。

kernel是一個power的方法強化linear model來解更複雜的問題,但要注意的是要小心處理參數的選擇以避免發生overfit。

總結這一講的內容,教到將representer theorem應用在ridge regression上,並結合tube regression引申出SVR的primal形式,再透過求解對偶得到β的稀疏解。最後則比較到目前為止教到的線性模型,並且有嘗試使用kernel來解更複雜的問題時要小心使用,避免overfit。

參考資料:
Machine Learning Techniques 6

上一堂講到在允許違反部份邊界下,引入C當作懲罰值,將Hard-Margin SVM轉成Soft-Margin。而Soft-Margin的對偶問題和Hard-Margin非常相似,只差在對偶問題中的α有上限值C。這堂課會談如果將kernel trick引入logistic regression可以怎麼作。

回顧Soft-Margin SVM,裡面發生的違反邊界會被紀錄在ξn裡面,而ξn會是1-yn(W.Zn+b),因為會紀錄下與1的距離來表式違反的程度和嚴重性;相反的在沒有違反下ξn值為0。再來可以把ξn寫成另一個更簡單的式子,即為轉換成對1-yn(W.Zn+b)和0之間取最大值來算出ξn,並帶入SVM的最佳化式子中,將ξn轉成b和w算出來的結果。

這個轉換後的最佳式其實和之前教過的regularization非常相似,就是在求長度w控制複雜度之下加上regularization項次。所以從這裡可以看到,Soft-Margin SVM其實可以從regularization推導過來,但上一講會選擇從Hard-Margin推導過來的原因是因為這個式子並不是一個QP問題,而且在兩項取最大值時也會有微分求解問題。

在前面課程推導對偶問題時有提到SVM和regularization的相似地方,而這裡可以看到Soft-Margin SVM其實就和L2 regularization是相似的,所以SVM的large margin就是一個對regularization的實現。但其中細部的差異在於Soft-Margin會使用到特別的error表示,而C的大小即為控制regularization的程度(越大的C代表越小的regularization)。

在計算zero-one error時會是一個像階梯狀的函數,因為ys是正的error為0(猜的方向正確),ys如果是負的error為1(猜的方向錯誤)。而SVM的error會由兩個線段組成,在ys離1越遠error會越大,而ys為正時error為0,SVM的error為zero-one error的上限,所以Soft-Margin也是在間接的把zeor-one error作好。

如果將SVM和logistic regression比較的話,從圖上可以發現logistic regression會和SVM很接近,進一步將ys在正無限大和負無限大比較時,兩者的error也是相近的,所以SVM其實也很像在作L2-regularized,因為在加上error項後,其實error項和logistic regression的error項非常接近。

在比較三種兩元分類方法,最一開始教的PLA需要在線性可分的情況下才好作,如果在線性不可分要透過pocket也不好達成;logistic regression的函數有很好的最佳化性質,而且加上L2-regularized後還能對模型的複雜度有一定的保護;Soft-Margin SVM差異在於最佳化使用了QP,並且在有large margin的理論保證,其error的計算方式雖然和logistic regression不同,但確實非常接近,而且兩者都是在作最佳化zero-one error的上限值。因此regularized logistic regression其實就幾乎是在作Soft-Margin SVM。

在二元分類問題中可以有兩種簡易的方法,第一種是透過SVM找到最佳的b和w,再直接丟進logistic reression作出二元分類,但這樣的手法就缺少了點logistic regression像是maximum likelihood的特色;第二種則是將SVM找到的最佳b和w,放進logistic regression當作始初解,之後再作最佳化找到最佳解,但這樣和本來logistic regression用其他初始解再找最佳解的結果相同,而且解變動後也失去了SVM的特性。

如果要同時有兩種方法的特性,可以先作SVM找到分數,再加上放縮的A與平移的B,最後再放進logistic regression訓練,其意義為先透過SVM找到最佳的超平面,再透過logistic regression作放縮與平移調整到最佳。也就是先透過SVM當作轉換,再丟進logistic regression作兩階段的的學習。

透過這樣的方式就可得到SVM的Soft binary clasifier,因為引入縮放和平移所以會和原本的SVM會不太相同,在求解logistic regression時可以使用Gradient Descent即可。到這裡我們透過kernel SVM去推論logistic regression在z空間的近似解,但這裡不是真的在z空間解出logistic regression,而且透過SVM在z空間找近似解。

複習一下SVM能使用kernel的關鍵點,是在於將W.Z的內積問題,將W轉換成一堆Z的線性組合,成為求Z.Z內積問題,這時候就能讓kernel上場。在SVM中,W就是Zn的線性組合,組合的系數就是對偶問題的解;PLA的W也是Zn的線性組合,組合的系數取決定每次犯錯的修正,而logistic regression中的W也是Zn的線性組合,這些系數來自梯度下降最終求得的結果。所以這些方法應該都能夠應用kernel trick輕易的在取得z空間的解,最好的W即可透過這些Zn來表達出來。

老師在這裡用例子證明任何的L2-regularized線性模型皆可以應用kernel。

因此,在L2-regularized的logistic regression,就可以將W替換成Zn的線性組合,從求W轉成求所有β系數。在將Zn線數組合代進入取代W後,就能將Z.Z的部份使用kernel,接著就可以透過梯度下降來求最佳解,這就是kernel logistic regression。

如果看這個kernel logistic regreaaion,先從β的角度看起來就像是在求β的線性組合,而且其中還將kernel用來作轉換與正規化;如果是從w角度來看,即為藏有kernel轉換與L2正規化的線性模型。但要注意常常β解出來會有很多0。

總結這堂課從將Soft-Margin SVM解釋成使用hinge error的regularize model,再進一步和L2-regularized logistic regresion比較兩者的相似性,透過結合兩者建立two-level learning應用在Soft Binary Classification,最後真的證明logistic regression也可以應用kernel並求出z空間的解,但會付出解出來很多β會為0的代價。

參考資料:
Machine Learning Techniques 5

上一講教了透過kernel trick來處理dual SVM中的轉換與內積,並介紹了不同的kernel function,從線性到無限多維轉換的Gaussian,但SVM還是有可能會overfit。overfit有兩種可能的原因,第一種為使用了過度複雜的轉換,第二種則是堅持要把所有資料完全的切分開來。以上面這個例子來說,如果可以容忍有錯誤分類,但可以使用簡單的線性切分也可以得到不錯的邊界;相反的,如果使用比較複雜的轉換確實完美的將資料都區分正確,反而容易造成overfit。

但該如何解決這個問題呢?之前在講pocket演算法時,pocket會找到能分錯資料最少的超平面,但hard-margin SVM除了要求要全部分對之外,還要選w長度最小的超平面。所以如果可以讓SVM也能像pocket一樣容忍一些錯誤,即不管部份分錯的點,就能達到某種程度的條件放寬。這邊引入C參數來代表放寬條件的相對重要性,C較大時代表越重視不要犯錯越好,C越小時代表越重視w長度,即分對的資料的margin越寬越好。

但放寬後的新問題將會違反QP的條件(min為二次式,條件要為一次式),而且在允許犯錯的情況下,沒有辦法分辦到底犯錯的嚴重程度(小錯或是大錯)。為了解決這兩個問題,引入ξn來紀錄離想要的值(是否靠近1)有多近,即將犯了幾個錯誤轉成犯了多大的錯誤。除了可以解決無法分辨犯錯程度的問題,也可以讓問題符合QP。

如同前面說的,C可以用來控制large margin和margin violation,越大的C代表越重視分類的正確性,而越小的C代表margin可以比較大,但會造成某些資料分錯。

當我們將Hard-Margin轉成Soft-Margin後,再來就是將它轉成對偶問題,就可以如同Hard-Margin一樣引入不同不同維度轉換的kernel function。第一步就是帶入Lagrange,有別於原本的Hard-Margin只需要帶入一個α,因為這邊分成兩個部分,因此分別帶入α和β。再進一步將ξn作微分拿掉ξ,可以發現在最佳解的位置C會等於αn+βn,就可以將βn簡化成C-αn,又因前後兩項分別都有C-αn互相消掉,所以可以再將式子簡化。

到這邊可以發現內部要解的問題其實就和之前的Hard-Margin SVM相同,只差在外面的條件帶入了C的限制,所以這邊可以和之前一樣分別對b和w作微分來得到Soft-Margin SVM的對偶問題。

再仔細看一下Soft-Margin SVM的對偶問題,可以發現和Hard-Margin SVM的對偶問題幾乎一樣,只差在現在α會有一個上限值C,而這個C是來自引入β後得到的。

當我們把Gaussian SVM用在Soft-Margin上並調整不同的C值時,可以發現最左邊當C設成1的時候,會得到一個邊界但有部份的資料是分錯的;當C一直調到到100可以發現邊界會越來越複雜,但是分錯的資料會越來越少。其中最右邊的邊界雖然可以讓所有的資料分對,但也可以造成雜訊的容忍度下降,也就是容易造成overfit,因此C和γ值需要慎選。

介紹完Soft-Margin和kernel,參數該怎麼選呢?第一個方法可以嘗試使用cross validation的方式,並帶入不同的C和γ值來找到比較好的參數組合。

另一個有趣的方法則是使用Leave-One-Out CV,SVM在使用Leave-One-Out CV的error會小於等於support vector的比例(support vector的數量除上所有樣本數),其概念為即使少了non support vector(non-SV)的點,結果並不會影響到margin的計算。

所以也可以嘗使用support vector的數量來選擇模型,因此在作cross validation前可以透過檢查support vector的數量,去掉support vector數量較多的組合作模型的安全檢查。

總結這堂課介紹了soft-margin SVM,其核心概念在於不強求把所有的類別都分對,並加上犯錯的程度大小的懲罰項。在推導的部份,Soft-Margin和Hard-Margin結果幾乎一樣,只差在Soft-Margin在αn會存在上限值C。而SVM可以將資料分成三種,分別為non-SV、存在邊界上的free-SV與可能違反邊界的SV。最後在model selection可以使用cross-validation或是參數SV的數量來作選擇。

參考資料:
Machine Learning Techniques 4

上一講說明了dual SVM,好像可以讓SVM與高維度的z空間兩者可以脫鉤,但以計算的角度,仔細看會發現在整個求解過程中的Q矩陣還是會在z空間作內積。而在z空間的內積在運算可以拆成兩個部份,一個是先透過某個函式φ轉換到z空間,第二再進行轉換後的內積。但這樣的運算方法就會變成z空間的維度作內積。

透過這個polynomial transform可以發現,其實是能先在x維度作完內積,再作函式φ的轉換,這樣就可避免產生在z空間作內積的情況,透過這個簡化的方式計算內積稱為kernel function。

回到svm不管是在計算b和最佳的超平面gsvm時,都可以將kernel function應用在計算中。這種合併函式轉換和內積計算,來避免在在高級度空間進行內積計算的方法,又稱為kernel trick。

延續前面的Poly-2 kernel例子,在每項前面再加上不同的係數,就能再對計算進一步簡化。雖然不同的函式都是轉換到相同的維度,但是會計算出不一樣的內積得到不同的距離,所以得到的幾合意義和算出來的SVM margin也會不同。其中最常使用的kernel為加上根號2與γ值的General Poly-2 kernel。

舉例來說,中間為原本的Poly-2 kernel,而左右兩個為General Poly-2 kernel但代入不同的γ值,左邊帶很小的γ右邊帶很大的γ。可以看到得出的邊界是不同的,所以得出的支撐向量也不同(方框的點)。雖然三個kernel的邊界不同,margin的定義也不同,但這邊沒辦法說哪個邊界會比較好,因為三個kernel都可以把點分開。

前面提到的Poly-2可以再加入ζ參數,就會形成一個包含3個參數的一般型態Polynomial Kernel,包含了(1)γ控制x內積算完後的放縮程度,和(2)ζ控制常數項與乘上轉換係數如何對應(3)Q代表poly作幾次方的轉換。而且不管作了幾次方的轉換,都是透遇kernel而不會在高維空間計算。使用高次方的轉換仍然可能會有overfit的風險,但SVM會透過找到最大margin來避免overfit,所以透過kernel可以讓SVM達到避免ovefit,又可以使用較複雜的模型,同時不需要高度的計算複雜度。SVM結合polynomial kernel又稱為Polynomial kernel。

Polynomial kernel如果設定成1次轉換,γ設成1,ζ設成0的話,就等於是原本的linear kernel SVM。老師建議大家在使用SVM時,可以從linear SVM開始,如果linear就可以解決問題,就不需要作高次方的nonlinear轉換。

既然可以透過kernel trick來達成高維度轉換的運算,那是否有可能作到無限多維轉換呢?老師這裡透過證明可以發現,即使在無限多維的情況,使用高斯函數作轉換是可以辦到的。

所以高斯函數也是一種kernel trick的方法,讓資料映射到無限多維的空間後找到支撐向量和超平面。但其實際上的意義為他會產生以支撐向量為中心的高斯函數的線性組合,也常被稱為RBF(Radial Basis Function) kernel。

到目前為止,SVM可以辦到使用kernel trick作無限多維的轉換後,再透過最大margin找到超平面,而SVM裡面的最大margin計算,可以對無限多維的轉換有一定程度的保護。但要注意SVM仍然會有overfit的問題,當γ越調越大時會使原本Gaussan裡面的σ變小Gaussian就會變尖(γ=1/2σ^2),所以還是要小心γ參數的選用會決定SVM的表現,通常不建議使用太大的γ值。

前面介紹到許多的kernel,不同的kernel也有其好處和壞處

  1. Linear Kernel
    最簡單的kernel,就是不作任何的維度轉換,其好處就是最簡單安全。除了QP好解之外也具有可解釋性,因為是線性所以可以透過每個特徵的權重來看出特徵的重要性,應該是遇到問題時要先被拿來嘗試使用的kernel。但其壞處在於問題如果是非線性的,那只靠線性沒辦法很好的解決問題。
  1. Polynomial Kernel
    Polynomial的好處在經過非線性轉換後,會比linear限制還要少,而且可以透過設定Q來限制模型的維度。缺點在於Polynomial有3個要給定的參數較難選定,而且在計算值時QP不容易解,尤其是γ過大的時候,因此Polynomial比較適合用在心裡有假設而且使用比較小的Q。
  1. Gaussian Kernel
    Gaussian可以作到無限多維的轉換,所以會比Linear和Polynomial使用限制更少。而且Gaussian的數值會落在0到1之間,所以數值計算難度較低;又因為Gaussian只有一個參數要給定,比起Polynomial要選3個參數,Gaussian可以更容易作參數選擇。Gaussian的壞處在於被轉換到無限多維後,模型沒辦法容易的被解讀;另外他的速度會比單純使用線性還要慢,又因為轉換較複雜,因此使用的不好會造成overfit。

Kernel表示一種特徵的相似度,但是不是任何相似度都能用來當作Kernel呢?這邊老師給出必須要滿足對稱性還有postive semi-definite兩種條件才能當作Kernel。

這一講提到了如何透過kerlnel trick快速計算轉換和內積,避開最複雜的計算,另外也比較了不同的kernel的優點和缺點。

參考資料:
Machine Learning Techniques 3

前面第一堂課在介紹線性的SVM,透過二次規畫找到最大margin的支撐向量來建立更強健的模型。這堂課將會延讀svm並加上非線性轉換的方式,讓SVM不止可以控制模型複雜度,也能結合特微轉換來提高模型效果。

假設透過特徵轉換的方法將SVM轉為非線性,原本的x會被轉換到z空間,但又希望可以在解SVM最佳化問題時,能夠擺脫轉換到z空間高維度的依賴。即將非線性轉換轉成另一個對等問題,不管轉到幾維的空間,都只會有N個變數,變數數量不會和要轉換的維度的有關,這稱為原本SVM的對偶問題(dual problem)。

之前篇章在介紹regularization時,引入了Lagrange Multipliers的概念,將原本帶有W長度小於C的限制式,轉成加上Eaug後求最小值,其中λ會被乘上每個系數加進最佳化式子中。因為SVM也是一個有條件的最佳化問題,但是在引入λ時有別於regularization中被當作給定值,在SVM中會被當作未知的值來解最佳化問題。

為了把原本有限制式的SVM最佳化問題,在引入Lagrange Multiplier後轉成沒有限制式的問題,會將限制式移項乘上α(即λ在SVM文獻為α)後相加。但是將有限制式的最佳化的問題,轉成沒有限制式的最佳化的問題,是否能夠得到一樣的結果?老師在這裡舉了兩種例子來說明其實會得到一樣的結果。假設選到一組會違反原本限制式的B,W(即α乘上的項次會是正值),這種最終會在解最小值SVM問題時被逃汰;相反的,如果選到一組符合原本限制式的B,W(即α乘上的項次會負值),這種解反而在最後會被留下來。因此將限制式轉益沒有限制式的最佳化問題,其實只是把限制藏進求最大值的最佳化中。

在轉成無限制式的最佳化問題後,會發現假設在固定α之下,整個最佳化得出來的值會比任一的Lagrange值還要大,甚至取完最大值右邊的不等式也一樣成立,這個min和max互相交換稱為lagrange dual problem。

對於二次規畫,這個duel problem因為滿足強對偶關係,因此求出來的b,w,α值對於左右兩邊的式子都是最佳值,因此可以將原本左邊的最佳化問題,直接轉成解右邊的對偶問題來求最佳值。

再進一步化解最佳化問題,如果要得到內部式子的最佳值,因為其為無限制的最佳化問題,因此最佳解會產生在梯度為0,即內部式子微分為0。首先對b微分後加進原本的式子,因為加進去的條件值為0時為最佳解,所以不止不會影響原本求出的最佳解,也可以同時去掉b這個項次。

再來對w微分後加進原本的式子,可以將w轉換成α.y.z相乘,在去掉w項次後,也不需要對原本的w取最小值,並轉成一個只有對α作最佳化的問題,得到簡化版的對偶問題。

前面有提到如果要滿足對偶問題,即b,w,α的解對於左右兩邊的最佳化問題都是最佳值,需要滿足特定條件,這稱為KKT最佳化條件,再會來透過這些條件來解最佳的b,w。

再來先乘上-1先將最大值問題轉成最小值問題,再把平方向解開後,就可以將每個條件丟進QP來解出最佳值。

如果今天在滿足KKT條件下要找到b,w的最佳值,首先要找到最佳的w,因為只有一條條件和w相關所以很容易找到最佳值。在找最佳的b時,共有2個條件和b相關,在第2個條件可以發現如果在α大於0的情況下,y(W.z + b)即需要等於1才能滿足條件,而這個值就是原本要找的最佳解,因此在解對偶問題時,可以同時找到SVM的支稱向量。

既然證明在α大於0的情況下,可以找到支撐向量,相反的回到原本的SVM概念,其意義在於如果可以知道有哪些支稱向量,就可以找到最大的margin,其他的點皆可以不管。

SVM和PLA其實很相似都會找出區分不同類別資料的超平面,差別在於兩者著重的點是不同的,SVM是著重在使用支稱向量表現出來,而PLA是使用分類錯誤(犯錯)的點表現出來。

上一講介紹的原始SVM為Hard-Margin SVM,透過求特定放縮後的b,w最佳值,他和要轉換的空間維度有關,適合維度較小的問題;而這一講的SVM是引入Lagrange的SVM,透過找最佳的支稱向量和α來重構邊界,並且和資料量的大小有關,適合資料量小的問題。

但到目前為止仍然沒辦法完全擺脫處理高度空間維度轉換的問題,因為在QP求Q矩陣時,仍會碰到z向量內積的問題,z向量的維度等同於目前要解的維度。

這一講提到透過對偶問題來移除對d維度的依賴,並引入Lagrange與KKT條件,透過QP來解出最佳值,並發現解出的最佳值就是支稱向量,並可以用來建立最大margin。下一講將會說明如何真正擺脫和d維度相關的計算。

參考資料:
Machine Learning Techniques 2

機器學習技法是林軒田老師的開的機器學習後半堂課,主要在延續前面機器學習的基礎理論,並延申出不同的機器學習模型介紹。

而這堂課主要圍繞在特徵轉換上並分三個面向探討,分別為(1)如果處理大量且高複雜度的特徵轉換(2)找出具有預測性質的特徵來提升模型表現(3)找出資料中的隱藏特徵讓機器學習表現更好。

在線性可分的問題中,前面的課程有教過可以透過PLA或是POCKET來找到分開的超平面,但PLA在求解的過程中會得到很多種解,究竟怎麼樣的切線才會是比較好的切線呢?如果以雜訊的容忍度來看,當資料產生時可能會存在或多或少的雜訊(例如從實體感測器訊號收資料時,可能會有震盪或是偏移的現象),而雜訊是模型過擬合的因素之一。因此如果要讓模型對雜訊的容忍度最大,那麼就要讓超平面能夠離點越遠越好。例如最右邊的超平面能離點最遠,雜訊的容忍度(灰色區域)也最大。

換個方式來,這會是一個最佳化問題,而最好的線須要滿足兩大條件: (1)能作出最大邊界(Margin) (2)可以把不同的類別分對。其中灰色區域的算法,是把每個點和超平面計算距離後取最小距離。

距離的計算方法,可以想像在平面上有兩個點構成的向量,而w乘上平面上的向量為0,所以w為垂直於平面上的法向量。當有一個x要計算x和平面的距離,即為x和平面上任一個點構成的向量,並對垂直於平面的向量作投影,即為對w方向的投影。

因為要找的是一個可以區分出正確類別的分割超平面,所以可以拆掉距離的絕對值,變成乘上y大於0(即為分割正確時值皆會大於0,如果類別分錯乘上y會小於0)

為了簡化式子,假設將式子放縮到最小值會等於1,那margin就會簡化為成1/|w|,並從求margin的最小值,變成分數乘上y的最小值要等於1。而且因為其條件已經滿足大於0,所以可以再拿掉一條分數乘上y要大於0的限制式。

為了再簡化限制式條件,限制式條件可以再從最小值為1,放寬為分數乘上y大於等於1,而且這個放寬並不會違反原本的限制式。老師這裡舉了一個例子,假設找出來的解為大於等於1.126並且不滿足等於1的條件,這時候如果把b和w除上1.126作放縮讓他滿足原本的條件,會因為w變小而使得目標式變大。這個證明在說如果找出來的解不滿於原本等於1的條件下,就不會是最佳解,因此可以對限制式條件作放寬。最後再將最大化的問題轉為求最小值(倒數),得到最後的簡化的最佳化問題。

如果要找到最佳的平面,只要找到最靠近平面的點就行了,而這些點被稱為支撐向量(support vector),就像是這個超平面是由最靠近的點所支撐起來般,這也是SVM的概念。

因為svm要找到的最佳值是w的兩次函數,且限制式為b和w的一次式,有這樣的限制非常適合使用二次規畫作最佳化。

但為什麼svm可以作的好呢,老師這邊以兩個面向來說明使用svm會讓Ein和Eout越接近,且不容易overfit泛化性更佳。

之前在提到regularization的時候講到,為了讓Ein越小但又不希望造成overfit,於是加上w的限制條件限制其範圍。而svm剛好對調,svm是要讓w的長度越小且限制讓所有的類別的資料分對,所以svm和regularization是一體兩面,svm找出來的灰色區域讓為了容忍雜訊對模型的干擾。

假設平面上有三個點,如果是原本的pla,在任意切線上可以找到所有的類別組合(共八種)。但使用svm考量到需要維持最大特定margin區域情況,所以沒有辦法作出所有的類別排列組合。在vc維的介紹有講到,如果能作出的dichotomies越少,vc維就越小,Ein和Eout就會越接近,即泛化能力越好。

從上面兩個面向來看,svm可以帶來本質上泛化性更佳的好處,並且在加上特徵轉換的方法後,non-linear的svm可以同時辦到將Ein(即分對不同類別)與Eout(泛化能力)作好。

這一講主要在說明如何從邊界分類問題延申出最大margin提供更強健的方法來容忍雜訊,並作svm最大margin的最佳化式子推導,最後提到最大margin帶來本值上的好處在於可以提高更佳的泛化能力。

參考資料:
Machine Learning Techniques 1

上一篇提到一些基本的Feature Engineering概念與方法,這一篇則是會說明當要使用類別型的特徵來訓練機器學習模型時的技巧。

Categorical

如果遇到類別型的特徵又需要拿進來訓練模型,則可以用one-hot encoding來處理。例如當今天要對商品銷售預測建立模型時,想要把員工拿進來考量,也許不同員工對顧客的服務上會影響到商品銷售。雖然員工的編號是數值,但其並不存在實際上數值的意義,這時候就可以透過one-hot來處理,並以多個欄位變數表示來將每個值轉成0/1表示的稀疏向量。

有時候在某些資料則可以當作連續型處理,也可以使用one-hot來處理。例如顧客的評分,如果你認為4分和2分是差距很大的,這時也可以依個人考量當作類別型處理。要特別注意的是,如果今天顧客沒有提供評分資料,在處理missing value上,第一種數值型的處理方法是使用另一個欄位來紀錄是否有收到評分(1/0),並維持評分值為0;第二種類別型處理方法則將所有one-hot變數設為0,並一樣透過另一欄位紀錄是否有評分,注意不要使用自己的特別編碼(magic number)來處理。

Feature Cross

假設今天要建立模型來判斷車輛是否為計程車,而使用的特徵只有兩個,分別為車倆的顏色和車輛所屬城市。假設透過簡單的線性模型來作訓練,在調整權重的過程中,都沒辦法有好的辨識效果。因為模型在調整黃色和白色的權重時,當它看到黃色在紐約是計程車,提高了黃色的權重,但這反而造成所有黃色車倆比較容易判斷成計程車,這是不對的;相同的如果模型提高了紐約的權重,這也會造成所有紐約的車倆都容易判斷成計程車,這一樣是不對的。

這時候則可以嘗試將兩個變數結合變成第三個變數,並透過one-hot encoding來處理,而在訓練過程中就會將黃色X紐約的組合單獨調整權重,可以避開原本的問題。例如在預測計程車車資問題中,雖然知道在上下班時間的旅程時間會比較長,車資也可能比較高,但是這時可以不特別作新增rule的處理(例如標注某天的某時段為上下班時間),而是直接將Day of Week和24小時作feature cross建立組合。

Bucketize

以加州的房價預測來說,如果觀察緯度這個特徵會發現有兩個高峰,一個是舊金山灣區,另一個則是洛杉磯大都市,這時就可以透過資料分群(bin)來拆成100個bucket,轉成類別型資料來訓練模型。注意在預測時也要透過資料前處理來將資料作bucketize。

Wide and Deep

到這邊就會遇到一個問題,在銷售遇到時會有價格和員工兩種不同特性的變數,價格是密集的(Dense)的連續型變數,而員工編號是透過one-hot產生稀疏(Sparse)的類別型變數。而在使用類神經網路訓練模型時,因為在0在乘上權重還是為0,所以稀疏的矩陣可能造成訓練過程中收斂在區域最佳解跳不出來。但是以上面說的計程車例子,其實線性模型是比較容處理的。

因此在訓練模型時,可以嘗試合併兩種方法,透過類神經網路來使用連續型變數訓練深度(Deep)結果,再和類別型變數透過線性方式串聯(Wide),這即是一個wid-and-deep架構的神經網路模型。

參考資料:
Data Engineering on Google Cloud Platform 4 - Serverless Data Analysis with Google BigQuery and Cloud Dataflow

前陣子在上Coursera的Data Engineering on Google Cloud Platform這個系列課程,其中在Serverless Machine Learning with Tensorflow on Google Cloud Platform這週內有一個Feature Engineering單元,裡面展示了如何透過Feature Engineering來提升模型的表現。上完後覺得裡面提到一些關於Feature Engineering的技巧,決定還是找時間把筆記寫下來。

好的特徵必須要和預測目標值是有相關的,對於特徵和預測值之間,需要有合理的假設,而不是隨意丟任意的資料進來,就希望特徵和預測值間具有關聯性,否則會落入Data Dredge的問題。Data Dredge意指可能會從大量資料中找到另人意外的相關性,這並不是我們想要的結果。(例如荷蘭的研究中指出一個地方送子鳥被看到的數量,和9個月後嬰兒出生的數量相關)

Causality

好的特徵特性是要使用預測當下能夠掌握的資料當作特徵,例如當你要使用每日的銷售資料當作特徵值,但是這些資料可能需要一個月的資料才會產生,而不是及時會被收集到資料倉儲。像這種可能因為資料延遲造成在預測時無法取得完整的資料將可能造成模型失效。所以在訓練模型時,請確保這些特徵在預測時是可以完整取得的,否則不要使用在模型中。

Numeric & Magnitude

因為在機器學習的過程中,會對輸入的資料作許多的運算,因此使用的特徵必須要為數值形態,且其數值是有大小意義的(例如coupon提供的打折數20%和10%存在折數大小關係)。

Enough examples

好的特徵需要有足夠的資料,以講者的個人經驗來說,如果一個特徵中每個值出現至少5筆,才會將這個特徵用來訓練模型。舉例來說,有一個類別為自動交易,需要有足夠的詐欺/非詐欺資料才有辦法訓練出有效的機器學習模型。如果今天只有3筆自動交易資料,且3筆都是非詐欺,這樣數量的資料可能就無法訓練出可用的機器學習模型。我想這裡的用意是指一個特徵如果相似度太高,可能造成沒有鑑別度;例如在分類問題中,特徵在每個類別的值都一樣或相似,那麼這樣的特徵可能對分類問題沒辦法貢獻太多資訊,使用決策樹來切分也會找不到好的切點。

參考資料:
Data Engineering on Google Cloud Platform 4 - Serverless Data Analysis with Google BigQuery and Cloud Dataflow

Python的collection模組裡面其實包含了許多非常實用的資料結構,比如之前介紹過的
namedtuple。今天要談的是Counter,Counter是一個dict的子類別,用來對hashable的物件作計算。

比如說我們今天要來幫公司裡面每個不同的team訂飲料好了,以下簡易一點不接受客製化調味,在建立Counter可以有以下幾種方法

1
2
3
4
5
6
7
8
9
10
# 使用mapping建立Counter,輸入一個dict
>>> team1 = Counter({'BlackTea': 3, 'GreenTea': 2, 'MilkTea': 1})
# 使用iterable建立Counter,輸入一個list
>>> team2 = Counter(['BlackTea', 'BlackTea', 'BlackTea', 'MilkTea', 'MilkTea', 'MilkTea'])
# 使用keyword參數建立Counter,輸入key-value組合
>>> team3 = Counter(GreenTea=3, MilkTea=3)
>>> team1['BlackTea'] # 可以直接當作dict存取
3
>>> team2['GreanTea'] # 取出不存在的item即為0
0

可以看到透過Couter能透過三種不同的方法來建立,就看使用的情境比較適合哪一種。而Counter也可以直接就當作dict取出值,特別是如果取出不存有的item,其計數會為0,並不會出現dict的KeyError。

1
2
3
4
5
6
7
8
>>> team1 + team2  # 使用加法運算符
Counter({'BlackTea': 6, 'MilkTea': 4, 'GreenTea': 2})
>>> team2 - team3 # 使用減法運算符
Counter({'BlackTea': 3})
>>> team2 & team3 # 運算兩者的交集
Counter({'MilkTea': 3})
>>> team2 - team3 # 運算兩者的聯集
Counter({'MilkTea': 3, 'GreenTea': 3, 'BlackTea': 3})

Counter也可以支援不同的運算符來對Counter物件進行操作,來達成對不同集合元件的運算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> drink_order = team1 + team2 + team3
Counter({'MilkTea': 7, 'BlackTea': 6, 'GreenTea': 5})

>>> list(drink_order) # 將Counter的items轉成list
['BlackTea', 'GreenTea', 'MilkTea']
>>> set(drink_order) # 將Counter的items轉成set
['BlackTea', 'GreenTea', 'MilkTea']
>>> dict(drink_order) # 將Counter的items轉成一般dict
{'BlackTea': 6, 'GreenTea': 5, 'MilkTea': 7}

>>> drink_order.items() # 取出item pairs
dict_items([('MilkTea', 7), ('BlackTea', 6), ('GreenTea', 5)])
>>> drink_order.keys() # 取出key值
dict_keys(['MilkTea', 'BlackTea', 'GreenTea'])
>>> drink_order.values() # 取出value值
dict_values([7, 6, 5])

>>> sum(drink_order.values()) # 取出value值並加總得到全部數量
18

因為Counter是dict的一個字類別,所以基本上他可以辦到原本dict可以作到的資料轉型,包含透過items()來逐一取出每個pair,或是直接可以使用values()並加總來計算出Counter裡面總計有多少數量的東西。

1
2
3
4
5
6
list(drink_order.elements())
>>> ['BlackTea', 'BlackTea', 'BlackTea', 'BlackTea', 'BlackTea', 'BlackTea', 'GreenTea', 'GreenTea', 'GreenTea', 'GreenTea', 'GreenTea', 'MilkTea', 'MilkTea', 'MilkTea', 'MilkTea', 'MilkTea', 'MilkTea', 'MilkTea']
>>> drink_order.most_common(3)
[('MilkTea', 7), ('BlackTea', 6), ('GreenTea', 5)]
>>> drink_order.most_common(2)
[('MilkTea', 7), ('BlackTea', 6)]

Counter還支援兩個函式,elements可以幫你展開所有的items。而most_common則會使用數量作排序,並且可以指定要排出前幾名。

Counter其實使用上非常方便,讓我們看一下Counter在實戰上可以拿來解什麼樣的問題,以下選兩個LeetCode的題目來看看Counter可以怎麼樣被使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'''389. Find the Difference
Given two strings s and t which consist of only lowercase letters.
String t is generated by random shuffling string s and then add one more letter
at a random position. Find the letter that was added in t.
Input:
s = "abcd"
t = "abcde"
Output: e ('e' is the letter that was added.)
'''
def findTheDifference(self, s, t):
from collections import Counter
s_counter = Counter(s)
t_counter = Counter(t)
return list(t_counter - s_counter)[0]

LeetCode-389是給定兩個字串s和t,其中要判斷t比s多了哪個字元。這時候就可以直接將s和t分別以iterable的方式建立兩個Counter,而我們知道t會比s多一個字元,於是再透過減法來找到t和s的差集後,輸出是哪個item即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'''819. Most Common Word
Given a paragraph and a list of banned words, return the most frequent word
that is not in the list of banned words. It is guaranteed there is at least
one word that isn't banned, and that the answer is unique.
Words in the list of banned words are given in lowercase, and free of punctuation.
Words in the paragraph are not case sensitive. The answer is in lowercase.
Example:
Input:
paragraph = "Bob hit a ball, the hit BALL flew far after it was hit."
banned = ["hit"]
Output: "ball"
'''
def mostCommonWord(self, paragraph, banned):
import re
from collections import Counter
words = re.sub("[!|?|'|,|;|.]", '', paragraph).lower().split(' ')
return (Counter(word for word in words if word not in banned).most_common(1)[0][0])

LeetCode-819是給定一個段落,還有被禁掉的詞清單,接著輸出出現字詞次數最多次,而且沒有被禁掉的詞。這邊先將所有詞轉成小寫,接著再透過空白切分後,將特殊符號取代掉只留下字詞。再來就可以將不存在於禁用清單的詞拿來建立Counter,然後使用most_common取出次數最高的第一名,接著再輸出是哪個詞即可。

Counter其實在計算次數的時候非常實用,特別是在項目很多的情況下,而且還需要作加減運算時,可以嘗試使用Counter來解問題,絕對可以幫助你省下很多的時間的!

參考資料:
Python docs - Counter dict subclass for counting hashable objects