------------------------------------本博客所有內容以學習、研究和分享為主,如需轉載,請聯系本人,標明作者和出處,並且是非商業用途,謝謝!--------------------------------
想寫這個系列很久了,最近剛好項目結束了閑下來有點時間,於是決定把之前學過的東西做個總結。之前看過一些機器學習方面的書,每本書都各有側重點,機器學習實戰和集體智慧編程更偏向與實戰,側重於對每個算法的實際操作過程,但是沒有對整個數據挖掘項目做介紹,李航老師的統計學習方法和周志華老師的機器學習這兩本書側重對原理的講解和公式的推導,但是實戰方面可能會少一點。我結合之前看過的書,以及自己的一些項目經驗做了一些總結,一是回顧自己還有哪些遺漏,二是希望給新入門的同學一個參考。至於編程語言,主要用python,也會有少部分R,java和scala之類,畢竟實際項目中也不可能使用一種語言。此外,本系列所用到的所有數據我會傳到Github上,需要的同學可以自行下載。為保證文章質量,每周二周四更新,下面是主要的目錄(可能會根據實際情況調整):
第一部分 模型的評估與數據處理
機器學習基礎與實踐(一)----數據清洗
機器學習基礎與實踐(二)----數據轉換
機器學習基礎與實踐(三)----數據降維
第二部分 特征工程
機器學習基礎與實踐(四)----特征選擇
機器學習基礎與實踐(五)----特征提取
機器學習基礎與實踐(六)----模型選擇與評估
第三部分 算法基礎之有監督算法
機器學習基礎與實踐(七)----廣義線性模型
機器學習基礎與實踐(八)----最小二乘法
機器學習基礎與實踐(九)----LDA
機器學習基礎與實踐(十)----SGD
機器學習基礎與實踐(十一)----K近鄰
機器學習基礎與實踐(十二)----高斯過程
機器學習基礎與實踐(十三)----決策樹(ID3,C4.5,C5.0,CART)
機器學習基礎與實踐(十四)----朴素貝葉斯
機器學習基礎與實踐(十五)----支持向量機
機器學習基礎與實踐(十六)----集成學習(Bagging,RF,AdaBoost,Gradient Tree Boosting,Voting Classifier)
機器學習基礎與實踐(十七)----感知機模型
機器學習基礎與實踐(十八)----多分類算法
第四部分 算法基礎之無監督算法
機器學習基礎與實踐(十九)----K-means
機器學習基礎與實踐(二十)----Affinity propagation
機器學習基礎與實踐(二十一)----Mean-shift
機器學習基礎與實踐(二十二)----Spectral clustering
機器學習基礎與實踐(二十三)----Ward hierachical
機器學習基礎與實踐(二十四)----Agglomerative clustering
機器學習基礎與實踐(二十五)----DBSCAN
機器學習基礎與實踐(二十六)----Gaussian mixtures
機器學習基礎與實踐(二十七)----Birch
第五部分 算法基礎之推薦算法
機器學習基礎與實踐(二十八)----相似度計算
機器學習基礎與實踐(二十九)----Arules關聯規則
機器學習基礎與實踐(三十)----Fp-Growth
機器學習基礎與實踐(三十一)----User-based or Item-based
第六部分 算法基礎之半監督模型
機器學習基礎與實踐(三十二)----Label Propagation
第七部分 算法基礎之其他模型
機器學習基礎與實踐(三十三)----概率圖模型
機器學習基礎與實踐(三十四)----最大熵模型
機器學習基礎與實踐(三十五)----規則學習
機器學習基礎與實踐(三十六)----強化學習
機器學習基礎與實踐(三十七)----條件隨機場
機器學習基礎與實踐(三十八)----保序回歸(Isotonic regression)
機器學習基礎與實踐(三十九)----Probability calibration
正文:
按照我做項目的經驗,來了項目,首先是分析項目的目的和需求,了解這個項目屬於什么問題,要達到什么效果。然后提取數據,做基本的數據清洗。第三步是特征工程,這個屬於臟活累活,需要耗費很大的精力,如果特征工程做的好,那么,后面選擇什么算法其實差異不大,反之,不管選擇什么算法,效果都不會有突破性的提高。第四步,是跑算法,通常情況下,我會把所有能跑的算法先跑一遍,看看效果,分析一下precesion/recall和f1-score,看看有沒有什么異常(譬如有好幾個算法precision特別好,但是recall特別低,這就要從數據中找原因,或者從算法中看是不是因為算法不適合這個數據),如果沒有異常,那么就進行下一步,選擇一兩個跑的結果最好的算法進行調優。調優的方法很多,調整參數的話可以用網格搜索、隨機搜索等,調整性能的話,可以根據具體的數據和場景進行具體分析。調優后再跑一邊算法,看結果有沒有提高,如果沒有,找原因,數據 or 算法?是數據質量不好,還是特征問題還是算法問題。一個一個排查,找解決方法。特征問題就回到第三步再進行特征工程,數據質量問題就回到第一步看數據清洗有沒有遺漏,異常值是否影響了算法的結果,算法問題就回到第四步,看算法流程中哪一步出了問題。如果實在不行,可以搜一下相關的論文,看看論文中有沒有解決方法。這樣反復來幾遍,就可以出結果了,寫技術文檔和分析報告,再向業務人員或產品講解我們做的東西,然后他們再提建議/該需求,不斷循環,最后代碼上線,改bug,直到結項。
直觀來看,可以用一個流程圖來表示:

今天講數據清洗,為什么要進行數據清洗呢?我們在書上看到的數據,譬如常見的iris數據集,房價數據,電影評分數據集等等,數據質量都很高,沒有缺失值,沒有異常點,也沒有噪音,而在真實數據中,我們拿到的數據可能包含了大量的缺失值,可能包含大量的噪音,也可能因為人工錄入錯誤導致有異常點存在,對我們挖據出有效信息造成了一定的困擾,所以我們需要通過一些方法,盡量提高數據的質量。數據清洗一般包括以下幾個步驟:
1 #一組年薪超過10萬元的經理收入 2 pay=c(11,19,14,22,14,28,13,81,12,43,11,16,31,16,23.42,22,26,17,22,13,27,180,16,43,82,14,11,51,76,28,66,29,14,14,65,37,16,37,35,39,27,14,17,13,38,28,40,85,32,25,26,16,12,54,40,18,27,16,14,33,29,77,50,19,34) 3 par(mfrow=c(2,2))#將繪圖窗口改成2*2,可同時顯示四幅圖 4 hist(pay)#繪制直方圖 5 dotchart(pay)#繪制點圖 6 barplot(pay,horizontal=T)#繪制箱型圖 7 qqnorm(pay);qqline(pay)#繪制Q-Q圖
1 import scipy as sp 2 data = sp.genfromtxt("web_traffic.tsv",delimiter = "\t")
數據情況如下:
1 >>>data 2 array([[ 1.00000000e+00, 2.27200000e+03], 3 [ 2.00000000e+00, nan], 4 [ 3.00000000e+00, 1.38600000e+03], 5 ..., 6 [ 7.41000000e+02, 5.39200000e+03], 7 [ 7.42000000e+02, 5.90600000e+03], 8 [ 7.43000000e+02, 4.88100000e+03]]) 9 10 >>> print data[:10] 11 [[ 1.00000000e+00 2.27200000e+03] 12 [ 2.00000000e+00 nan] 13 [ 3.00000000e+00 1.38600000e+03] 14 [ 4.00000000e+00 1.36500000e+03] 15 [ 5.00000000e+00 1.48800000e+03] 16 [ 6.00000000e+00 1.33700000e+03] 17 [ 7.00000000e+00 1.88300000e+03] 18 [ 8.00000000e+00 2.28300000e+03] 19 [ 9.00000000e+00 1.33500000e+03] 20 [ 1.00000000e+01 1.02500000e+03]] 21 22 >>> data.shape 23 (743, 2)
可以看到,第2列已經出現了缺失值,現在我們來看一下缺失值的數量:
1 >>> x = data[:,0] 2 >>> y = data[:,1] 3 >>> sp.sum(sp.isnan(y)) 4 8
在743個數據里只有8個數據缺失,所以刪除它們對於整體數據情況影響不大。當然,這是缺失值少的情況下,在缺失值值比較多,而這個維度的信息還很重要的時候(因為缺失值如果占了95%以上,可以直接去掉這個維度的數據了),直接刪除會對后面的算法跑的結果造成不好的影響。我們常用的方法有以下幾種:
1.直接刪除----適合缺失值數量較小,並且是隨機出現的,刪除它們對整體數據影響不大的情況
2.使用一個全局常量填充---譬如將缺失值用“Unknown”等填充,但是效果不一定好,因為算法可能會把它識別為一個新的類別,一般很少用
3.使用均值或中位數代替----優點:不會減少樣本信息,處理簡單。缺點:當缺失數據不是隨機數據時會產生偏差.對於正常分布的數據可以使用均值代替,如果數據是傾斜的,使用中位數可能更好。
4.插補法
1)隨機插補法----從總體中隨機抽取某個樣本代替缺失樣本2)多重插補法----通過變量之間的關系對缺失數據進行預測,利用蒙特卡洛方法生成多個完整的數據集,在對這些數據集進行分析,最后對分析結果進行匯總處理3)熱平台插補----指在非缺失數據集中找到一個與缺失值所在樣本相似的樣本(匹配樣本),利用其中的觀測值對缺失值進行插補。優點:簡單易行,准去率較高缺點:變量數量較多時,通常很難找到與需要插補樣本完全相同的樣本。但我們可以按照某些變量將數據分層,在層中對缺失值實用均值插補4)拉格朗日差值法和牛頓插值法(簡單高效,數值分析里的內容,數學公式以后再補 = =)5.建模法可以用回歸、使用貝葉斯形式化方法的基於推理的工具或決策樹歸納確定。例如,利用數據集中其他數據的屬性,可以構造一棵判定樹,來預測缺失值的值。
1 >>> import pandas as pd 2 >>> data = pd.read_table("web_traffic.tsv",header = None) 3 >>> data.describe() 4 0 1 5 count 743.000000 735.000000 6 mean 372.000000 1962.165986 7 std 214.629914 860.720997 8 min 1.000000 472.000000 9 25% 186.500000 1391.000000 10 50% 372.000000 1764.000000 11 75% 557.500000 2217.500000 12 max 743.000000 5906.000000
處理方法:
1.刪除異常值----明顯看出是異常且數量較少可以直接刪除
2.不處理---如果算法對異常值不敏感則可以不處理,但如果算法對異常值敏感,則最好不要用,如基於距離計算的一些算法,包括kmeans,knn之類的。
3.平均值替代----損失信息小,簡單高效。
4.視為缺失值----可以按照處理缺失值的方法來處理
1 #創建數據,data里包含重復數據 2 >>> data = pd.DataFrame({'v1':['a']*5+['b']* 4,'v2':[1,2,2,2,3,4,4,5,3]}) 3 >>> data 4 v1 v2 5 0 a 1 6 1 a 2 7 2 a 2 8 3 a 2 9 4 a 3 10 5 b 4 11 6 b 4 12 7 b 5 13 8 b 3 14 15 #DataFrame的duplicated方法返回一個布爾型Series,表示各行是否是重復行 16 >>> data.duplicated() 17 0 False 18 1 False 19 2 True 20 3 True 21 4 False 22 5 False 23 6 True 24 7 False 25 8 False 26 dtype: bool 27 28 #drop_duplicates方法用於返回一個移除了重復行的DataFrame 29 >>> data.drop_duplicates() 30 v1 v2 31 0 a 1 32 1 a 2 33 4 a 3 34 5 b 4 35 7 b 5 36 8 b 3 37 38 #這兩個方法默認會判斷全部列,你也可以指定部分列進行重復項判斷。假設你還有一列值,且只希望根據v1列過濾重復項: 39 >>> data['v3']=range(9) 40 >>> data 41 v1 v2 v3 42 0 a 1 0 43 1 a 2 1 44 2 a 2 2 45 3 a 2 3 46 4 a 3 4 47 5 b 4 5 48 6 b 4 6 49 7 b 5 7 50 8 b 3 8 51 >>> data.drop_duplicates(['v1']) 52 v1 v2 v3 53 0 a 1 0 54 5 b 4 5 55 56 #duplicated和drop_duplicates默認保留的是第一個出現的值組合。傳入take_last=True則保留最后一個: 57 >>> data.drop_duplicates(['v1','v2'],take_last = True) 58 v1 v2 v3 59 0 a 1 0 60 3 a 2 3 61 4 a 3 4 62 6 b 4 6 63 7 b 5 7 64 8 b 3 8
1 list0=['b','c', 'd','b','c','a','a'] 2 3 方法1:使用set() 4 5 list1=sorted(set(list0),key=list0.index) # sorted output 6 print( list1) 7 8 方法2:使用 {}.fromkeys().keys() 9 10 list2={}.fromkeys(list0).keys() 11 print(list2) 12 13 方法3:set()+sort() 14 15 list3=list(set(list0)) 16 list3.sort(key=list0.index) 17 print(list3) 18 19 方法4:迭代 20 21 list4=[] 22 for i in list0: 23 if not i in list4: 24 list4.append(i) 25 print(list4) 26 27 方法5:排序后比較相鄰2個元素的數據,重復的刪除 28 29 def sortlist(list0): 30 list0.sort() 31 last=list0[-1] 32 for i in range(len(list0)-2,-1,-1): 33 if list0[i]==last: 34 list0.remove(list0[i]) 35 else: 36 last=list0[i] 37 return list0 38 39 print(sortlist(list0))
五.噪音處理
Outlier: you are enumerating meticulously everything you have. You found 3 dimes, 1 quarter and wow a 100 USD bill you had put there last time you bought some booze and had totally forgot there. The 100 USD bill is an outlier, as it's not commonly expected in a pocket.
Noise: you have just come back from that club and are pretty much wasted. You try to find some money to buy something to sober up, but you have trouble reading the figures correctly on the coins. You found 3 dimes, 1 quarter and wow a 100 USD bill. But in fact, you have mistaken the quarter for a dime: this mistake introduces noise in the data you have access to.
To put it otherwise, data = true signal + noise. Outliers are part of the data.
離群點: 你正在從口袋的零錢包里面窮舉里面的錢,你發現了3個一角,1個五毛,和一張100元的毛爺爺向你微笑。這個100元就是個離群點,因為並不應該常出現在口袋里..
噪聲: 你晚上去三里屯喝的酩酊大醉,很需要買點東西清醒清醒,這時候你開始翻口袋的零錢包,嘛,你發現了3個一角,1個五毛,和一張100元的毛爺爺向你微笑。但是你突然眼暈,把那三個一角看成了三個1元...這樣錯誤的判斷使得數據集中出現了噪聲
- 用箱均值光滑:箱中每一個值被箱中的平均值替換。
- 用箱中位數平滑:箱中的每一個值被箱中的中位數替換。
- 用箱邊界平滑:箱中的最大和最小值同樣被視為邊界。箱中的每一個值被最近的邊界值替換。
一般而言,寬度越大,光滑效果越明顯。箱也可以是等寬的,其中每個箱值的區間范圍是個常量。分箱也可以作為一種離散化技術使用.
2. 回歸法
可以用一個函數擬合數據來光滑數據。線性回歸涉及找出擬合兩個屬性(或變量)的“最佳”直線,使得一個屬性能夠預測另一個。多線性回歸是線性回歸的擴展,它涉及多於兩個屬性,並且數據擬合到一個多維面。使用回歸,找出適合數據的數學方程式,能夠幫助消除噪聲。
1 #-*- coding :utf-8 -*- 2 #文本格式化處理,過濾掉空行 3 4 file = open('123.txt') 5 6 i = 0 7 while 1: 8 line = file.readline().strip() 9 if not line: 10 break 11 i = i + 1 12 line1 = line.replace('\r','') 13 f1 = open('filename.txt','a') 14 f1.write(line1 + '\n') 15 f1.close() 16 print str(i)
2.如何判斷文件的編碼格式
1 #-*- coding:utf8 -*- 2 #批量處理編碼格式轉換(優化) 3 import os 4 import chardet 5 6 path1 = 'E://2016txtutf/' 7 def dirlist(path): 8 filelist = os.listdir(path) 9 for filename in filelist: 10 filepath = os.path.join(path, filename) 11 if os.path.isdir(filepath): 12 dirlist(filepath) 13 else: 14 if filepath.endswith('.txt'): 15 f = open(filepath) 16 data = f.read() 17 if chardet.detect(data)['encoding'] != 'utf-8': 18 print filepath + "----"+ chardet.detect(data)['encoding'] 19 20 dirlist(path1)
3.文件編碼格式轉換,gbk與utf-8之間的轉換
這個主要是在一些對文件編碼格式有特殊需求的時候,需要批量將gbk的轉utf-8的或者將utf-8編碼的文件轉成gbk編碼格式的。
1 #-*- coding:gbk -*- 2 #批量處理編碼格式轉換 3 import codecs 4 import os 5 path1 = 'E://dir/' 6 def ReadFile(filePath,encoding="utf-8"): 7 with codecs.open(filePath,"r",encoding) as f: 8 return f.read() 9 10 def WriteFile(filePath,u,encoding="gbk"): 11 with codecs.open(filePath,"w",encoding) as f: 12 f.write(u) 13 14 def UTF8_2_GBK(src,dst): 15 content = ReadFile(src,encoding="utf-8") 16 WriteFile(dst,content,encoding="gbk") 17 18 def GBK_2_UTF8(src,dst): 19 content = ReadFile(src,encoding="gbk") 20 WriteFile(dst,content,encoding="utf-8") 21 22 def dirlist(path): 23 filelist = os.listdir(path) 24 for filename in filelist: 25 filepath = os.path.join(path, filename) 26 if os.path.isdir(filepath): 27 dirlist(filepath) 28 else: 29 if filepath.endswith('.txt'): 30 print filepath 31 #os.rename(filepath, filepath.replace('.txt','.doc')) 32 try: 33 UTF8_2_GBK(filepath,filepath) 34 except Exception,ex: 35 f = open('error.txt','a') 36 f.write(filepath + '\n') 37 f.close() 38 39 dirlist(path1)
