編寫基於機器學習的程序,有哪些編寫和調試的經驗和竅門?
最近做了一個課程作業,用matlab編程一個分類器,由於是控制和弱電出身,
對於這種數據處理的程序不是很熟悉,所以很是費勁,而這個作業數據只是兩維的特徵,最後出來的效果還是不如matlab自帶的程序。深感機器學習的演算法非常抽象,而且實際實現時,有很多細節資料上沒有提到。而且如果要優化演算法效率、提高適用性和可靠性,更是要對演算法理解很透徹,感覺這點很難,因為對數學要求特別高,而且數據特徵維數大了,根本不知道該如何調試·····求高人們指點一二!!
作為一個純做機器學習,主要搞probabilistic graphical model的苦逼Ph.D,大多數時間實際上是在推公式好吧摔!!!anyway,說說自己編寫、調試機器學習程序的經驗吧。
1,要用print !
大多數機器學習方法是優化問題,一般是迭代求解,演算法一般就是gradient descent的各種變種。自己編寫的時候,盡量列印每輪迭代時model的信息,包括目標函數值、訓練數據上的錯誤率,甚至測試數據上的錯誤率,probabilistic model經常列印log-likelihood。舉個栗子,如果迭代中,目標函數值增加了(diverge 了),先檢查是否是learning rate設的過大(最常見),gradient的正負搞反了(也很常見。。),如果不是,基本就要斷點調試了。再比如,如果訓練錯誤率一直在下降,測試錯誤率開始上升,那就是過擬合了。2,先在toy/simulated的data上測試通過
real data一般有不少預處理的問題,數據本身也有很多不符合模型假設的「個性」,很多時候,會影響你對機器學習模型、演算法本身正確性的判斷。最重要的,這樣調試大大節省了時間。3,不要吝嗇注釋
機器學習核心演算法的程序,通常非常短小,但是如果沒有注釋,可讀性會很差。因為代碼背後是公式。對絕大多數凡人來說,公式本身就已晦澀,從代碼裡面讀出公式,搞清楚程序在幹嗎,更是坑爹。所以,為了自己跟他人的身心健康,請加註釋。4,如果專註機器學習演算法本身,復用別人的代碼要慎重。
當你自己提了一個模型、演算法,想投NIPS、ICML、AIStats、UAI,需要對比前人的演算法時,如果不是特別難,建議自己動手實現前人的方法(實際上,這是當年老闆給我的忠告)。。首先,如果你只是把別人的代碼當做一個黑盒子,很難得到自己的方法為啥好,別人的方法為啥差的insight,如果不幸自己的方法差,也很難找到改進自己方法的竅門。再者,最新提出的方法,一般沒有可用的高質量代碼。注意!!如果你的主要目標不是提出新的演算法,而是想在某個應用上做出好的正確率,那麼按 @grapeot說的,儘可能復用別人的代碼。
5,先編寫簡單的機器學習演算法練手
比如logistic regression, 比如EM for Gaussian Mixture, 比如SVM。先用最簡單的實現。logistic regression就用gradient descent,linear svm就用sub-gradient decent。加了各種kernel的svm就用naive的quadratic programming,熟悉一些了,也可以用SMO。磨刀不誤砍柴工,能學到很多。6, 機器學習程序中的小bug很難發現難發現的原因在於,有小bug的程序工作的很好,沒有任何錯誤的徵兆~~甚至有些時候,帶小bug的程序,會比沒bug的程序得到的正確率更高...(有多年ML經驗的人,肯定遇到過這樣的情況)。原因是小bug會起到某種regularization的作用,防止了過擬合。所以盡人力後,順其自然吧。。。p.s.題外話,這個現象曾在12年NIPS的時候,被一個機器學習的大牛Dietterich拿來調侃巨牛Hinton提出的dropout方法。Hinton當時說,如果我的方法work得好,是因為有bug,那一定是世上最好的bug~~因為我的程序總是在贏各種競賽。。機器學習方向的博士生,個人有幾點體會。
1. 盡最大可能復用別人的代碼。哪怕編譯等等很麻煩,也可以在調試上省很多很多時間。血的教訓。2. 多把數據可視化出來,很多問題你看數字很難有直觀感受,但往往一畫出來就會發現哪裡錯了。這在高維數據下尤其明顯。3. 調試的時候和普通程序一樣,看到結果不對就逆推,找到問題是從哪個地方開始引入,從哪句話開始實際數值和你的期待不一樣的。定位問題就成功了一大半。可以用非常簡單的數據代入驗證,這樣你也知道正確的結果是什麼。4. 關於工具的選擇,原則實是先確認演算法是對的,再調優實現。具體說先用matlab/python之類方便可視化的工具快速迭代找到一個能得到正確結果的演算法。然後再用c++/mex優化速度。否則一個跑的很快的屎還是一坨屎。
不知道題主要寫編寫的是哪個分類器,不同分類器的編寫複雜度相差非常大。像KNN,幾句話就能寫出來,而像SVM,要複雜得多。
相對於其它語言,我覺得MATLAB是我遇到的最容易上手的。最大的優勢是方便查看各變數值與調試。說說我認為一些有用的技巧:1. 比較複雜的演算法,可以先用數學語言把大概流程寫出來,這樣編程的時候有個參照。對演算法流程非常清楚,是將它代碼實現的必要條件。
2. 由簡入難。先實現演算法的一些基本功能,而擴展功能留到後面做。
3. 邊寫邊調試,尤其是自己對編程語言不太熟悉的情況下,犯錯的機率非常大。如果寫了幾百行再調試,那絕對是個巨大的工程。所以建議每寫個幾行,就F5運行下,查看下各個變數的值,看與自己想像的是否一樣。這樣便於即時發現問題,改正問題。
另外,剛開始編寫,不建議把代碼寫在function里,然後用主程序調用,這樣不方便查看變數值與調試。我傾向先在主程序里把代碼功能實現,調試確定沒問題了,再把它轉移到一個function里,保持主程序代碼的簡潔。當然這種方法對Python, C之類的語言沒有用處。4. 自己構造一個簡單的樣例來調試程序。一個簡單的樣例,自己能清楚的判斷程序輸出結果是否正確。而如果是很複雜的樣例,判斷起來就麻煩得多。
暫時想到的就這麼多,希望能有幫助。所有機器學習問題都可以轉化成一個優化問題,所以先學習優化,特別是凸優化,先看無約束條件的優化,再學有約束條件的優化。你會發現所有分類器都差不多,只是目標方程不一樣,約束條件不一樣吧了。
個人體會:補充一些其他的
簡單演算法多用別人的程序,然而以後題主需要寫別人沒有現成的複雜的程序的時候,有這麼幾點個人體會:
1、很多演算法是基於數學優化設計的,存在優化目標,比如概率模型里的最大似然估計或最大後驗,然後使用迭代的演算法(EM,梯度下降,牛頓法等等)進行優化,因此在迭代的過程中可以把優化的目標輸出出來,看看是不是真的得到了優化。
2、有的時候優化了沒錯但優化的時候就優化到NAN去了(寫的Java)...這種時候需要採用倒推的方法一步一步回溯,看是哪裡出現了第一個NAN,是不是對應的求解公式有沒有寫錯,尤其是數學公式里的各種下標有沒有弄混。3、如之前各位答案說的,把中間結果畫出來看看4、機器學習演算法調試確實是一個很難的問題,記得以前看過一個段子說某deep learning大牛設計了演算法號稱有性能提升,然後開源了代碼發了論文,結果被人檢查出來代碼里有錯誤,把bug改了之後性能就下降了。不知道是誰是真是假...不知道題主說的適用性和可靠性是什麼意思。運行效率的話,我認為更多的是對數據結構和演算法的理解,對演算法複雜度的分析和優化,以及編程的優化,需要題主多多練習編程。學習一下Learning Theory,了解bias和variance的區別以及產生原因對設計好的feature,以及debug ML演算法非常重要。
如果英語比較好的話,強烈推薦看一下Stanford教授Andrew Ng關於應用以及debug ML演算法的建議 (slides在這裡)。這是Stanford計算機系Machine Learning課程的材料,這裡面的提供的方法在實際使用ML演算法的過程中很有效。弄出代價函數,選擇最優化的方法,就這樣。過程中,要想辦法算出參數的梯度
選好模型和最優化的演算法。從最簡單和常見的模型開始(logistic regression,decision tree ,KNN等等);MATLAB里的分類器用的最優化演算法基本都是很sophisticated的,簡單的梯度下降什麼的肯定搞不過人家的。
準備好測試用的程序和數據,包括測試在訓練數據和測試數據上的誤差,畫learning curve,ROC曲線啦,etc。這些測試結果就像儀錶盤一樣,讓你清楚自己現在所處的情況。cross validation (n-fold, loocv,etc)+ grid search 來選參數,libsvm 就是這麼做的。(跑程序的時候你可以去看看電影什麼的)不過你這個是二維的特徵啊,直接畫個圖出來看看唄。首先關於語言的選擇,個人認為還是Matlab比較合適,因為目前在學術領域,用Matlab的人感覺還是相對多一點的。題主如果在寫代碼的過程中遇到了問題,也比較方便向別人請教。目測開源的機器學習庫也是Matlab更多。當然如果對程序速度有嚴格要求,就可以考慮C++。
其次對於題主你來講,最重要的問題是要明確自己的目的。
說實話,如果只是一個課程作業的話,完全沒必要深入到很多演算法的細節,只要了解了演算法的大體流程即可,再選擇別人實現好的演算法,多次測試,一般結果是可以讓人滿意的。但如果題主是想繼續在機器學習領域深入研究的話,了解演算法細節就是必不可少的了。不過即便這樣,還是要從簡單入手逐漸加深難度。否則學習曲線太陡,一般人都hold不住。(另外自己感覺機器學習還算是入門容易精通難的,深入研究充滿了大量細節和數學,研究者們也在一直outperform其他人……)所以,綜上,題主最好從簡單入手!
先了解基本演算法思路,再使用現成代碼進行實驗!在此,我強烈推薦一個模式識別庫 PRTools5!在這裡下載:Software - Pattern Recognition Tools,作者叫R.P.W. Duin。這個庫最大的優點就是簡單方便,而且實現了大量大量常用演算法,對於初學者,完全可以上手就用,避免陷入細節的泥潭。而且都經過大量測試,基本沒有嚴重bug。簡單舉幾個栗子:首先,需要構造兩個數據集:A = prdataset(X1, Y1)
B = prdataset(X2, Y2)
其中,prdataset是PRTools5中的函數,目的就是構造數據集……
X是訓練數據矩陣,每一行是一個數據點(一個object),每列是一個特徵(feature)。Y是一個列向量,代表數據X的標籤。怎麼樣,簡單吧……之後,就可以進行分類(classification)了!w1 = knnc(A);
w2 = qdc(A);
w3 = svc(A);
w4 = naivebc(A);
% ......
其中,w1, w2, w3, w4 就是訓練好的分類器了。knnc是knn…qdc是Quadratic Bayes Normal Classifier,svc是Linear Support Vector Machine……naivebc是Naive Bayes。
至於各種參數呢?函數都已經自動選擇了,當然題主根據自己要處理的具體問題,也完全可以手動指定。之後就可以測試結果了,我們選擇B作為測試集。
e1 = testd(B*w1);
e2 = testd(B*w2);
e3 = testd(B*w3);
e4 = testd(B*w4);
% ......
這裡的e就是classification error……當然也可以選擇其他函數,使用其他判斷標準。
但鑒於題主的數據是二維的,因此可視化是一個很好很強大的手段。(二維的!這年頭哪還有這麼好的事└(T_T;)┘……)。以下舉例:A = gendatb([50 50]);
B = gendatb([400 400]);
此處使用了PRTools5自帶的數據生成函數,A、B都是二維數據,2 classes,每類中有50(400)個objects。
w1 = knnc(A);
w2 = qdc(A);
w3 = svc(A);
w4 = naivebc(A);
% ......
……不用解釋。
scatterd(B); % 做測試數據散點圖
hold on
h1 = plotc(w1); % 做出分類器邊界圖,以下類似
h2 = plotc(w2, "b");
h3 = plotc(w3, "g");
h4 = plotc(w4, "y");
legend([h1, h2, h3, h4], "knnc", "qdc", "svc", "naivebc")
結果如下:
這時真相大白,原來gendatb是生成banana形狀的數據…………而且顯然,對於這種數據,knn的效果最好。總之PRTools5是一個非常強大的模式識別庫。
而且這時再看,機器學習也就不麻煩且不抽象了吧~~
建議lz去Coursera聽幾節Machine Learning的課Coursera.org,然後做一下對應的作業。作業就是用MATLAB編的,如果你能把作業裡面的代碼都理解好了。我覺得應該就算入門了吧~ 而且你可以趁此機會熟悉熟悉MATLAB的矩陣化運算,以及其他的常用指令。
至於難不難,我覺得學控制的同學,應該有足夠的數學基礎(線性代數+概率論)去理解的哈。
同學你也可以參考一下我的代碼zihaolucky/Machine-Learning-and-UFLDL · GitHub . Lecture和code都有了。不過,如果你要跟那門課,還是自己完成吧,有honor code規則的哈。
至於vczh同學提到的「那些模型訓練出來的係數是不可理解的,調試他們幹什麼……」.確實如此,機器學習,就是學習出那些參數,以最好地貼近數據模式啊。這個是演算法。需要優化的,可能是你程序的速度。既然你已經知道 MATLAB 有這個功能了,那麼就看看 MATLAB 的幫助文檔是怎麼實現這個演算法的吧。
-------------接下來是黑 MATLAB 的分割線---------------
哈哈,看不到源代碼吧,哈哈,MATLAB 是閉源軟體!還不趁早用 Python!你搞機器學習的時候,那些模型訓練出來的係數是不可理解的,調試他們幹什麼……
感覺語言選好自己喜歡的就可以啦...不過還是推進python現在theano,scikit-learn什麼的都很好用.
我覺得要先理解一些基礎概念,知道為什麼這麼做
理解了的話就可以盡量參考一下已有的代碼(對象和類的定義)然後試著"想像一下"矩陣運算是怎麼操作的,用手多寫寫shape之間轉換關係?應該還要有一台運算速度快的電腦吧最主要還是理解概念,為什麼這麼做,這麼做會怎麼樣吧
推薦Andrew Ng的Cousera課程目前正好講到了機器學習系統設計和修復~最近在研究最小二乘支持向量機,可以看看支持向量機(SVM)的資料http://blog.csdn.net/v_july_v/article/details/7624837
那些代碼其實一點也不抽象,必須理解他們到非常不抽象的地步,並且深刻理解課題,以及課題和這些代碼的關係。ML的結果經常會有很多難以理解的時候,只能多看多想, 有時候要換換features 能得到更好的結果。但是一切的根源來自對問題的深刻理解。
自己編寫一個test的數據,隨即值的種子設成固定的,用來檢驗程序有沒有寫對
熟練使用CVX, 可以在一定程度上檢查機器學習演算法的中間輸出.
Python?討厭它的代碼採取的那種縮進格式!!
推薦閱讀:
※大公司招Python程序員具體是幹什麼?
※在MATLAB入門之後,平時應該怎麼練習,才能了解,掌握更多更加方便的函數,編程技巧?
※如何形象的描述反應式編程中的背壓(Backpressure)機制?
※怎麼去矽谷做碼農?