kaggle數據挖掘競賽初步--Titanic<派生屬性&維歸約>


完整代碼: https://github.com/cindycindyhi/kaggle-Titanic

特征工程系列:

Titanic系列之原始數據分析和數據處理

Titanic系列之數據變換

Titanic系列之派生屬性&維歸約

為什么有的機器學習項目成功了有的卻失敗了呢?畢竟算法是有限的改進也是有限的,最主要的因素就是特征的選擇了。如果我們有一些與類別非常相關同時又相互獨立的特征,學習起來是很容易的,相反就不一定了。通常情況下,並不是直接把原始數據作為特征,而是從中構建一些特征。這是機器學習中的主要工作。在這一步驟中,通常直覺、創造性、魔法和技術一樣重要。

當然,機器學習的一個終極目標就是將特征工程過程越來越多地自動化。現在經常采用的一種方式是先自動產生大量的候選特征,然后根據它們與分類類別的信息增益等方法來選取最好的特征。但是,運行包含大量特征的學習器來尋找有用的特征組合太耗時,也容易導致過擬合。還是需要人為的介入特征的選擇中。

什么是派生屬性呢?派生屬性就是從原始數據中得到的一些屬性,比如上一節從Age屬性經過Factorize得到的Age_bin屬性就是一個派生屬性,當然這種派生只是非常簡單的派生。為什么要對這些屬性做各種各樣的統計和處理呢,這其實是特征工程的一部分,先構建足夠多可能會對結果有意義的屬性,然后再從這些候選集中選擇我們想要的特征。特征工程非常繁瑣,但是對數據挖掘非常重要,一般來說,做一個數據挖掘項目,百分之八十的努力要用在特征工程上。除了基本的轉換和interaction屬性,我們也要創造性的從原始屬性中發現新屬性。比如電話號碼,可以從中提取出來國家和區域特征。

一 研究業務邏輯提取特征

Titanic的數據集相對較為簡單,但是對於一些字符串類型的屬性,比如Name我們可以從中提取出來一些可以揭示其社會地位的稱號。

名字的長度也會代表一個人的社會地位,社會地位高的人可能會更容易得到救生船。

1 df['Names'] = df['Name'].map(lambda x: len(re.split(' ',x)))

對於名字中間的爵位,可以看到稱號有Mr Mrs Master等,經過統計可以看到有以下幾種稱號:

這些稱號有法語還有英語,需要依據當時的文化環境將其歸類,如何將其歸類可以參考這篇文章trevorstephens.com/post/73461351896/titanic-getting-started-with-r-part-4-feature

1     df['Title'] = df['Name'].map(lambda x: re.compile(",(.*?)\.").findall(x)[0])
2     df['Title'][df.Title=='Jonkheer'] = 'Master'
3     df['Title'][df.Title.isin(['Ms','Mlle'])] = 'Miss'
4     df['Title'][df.Title == 'Mme'] = 'Mrs'
5     df['Title'][df.Title.isin(['Capt', 'Don', 'Major', 'Col', 'Sir'])] = 'Sir'
6     df['Title'][df.Title.isin(['Dona', 'Lady', 'the Countess'])] = 'Lady'
7     df['Title_id'] = pd.factorize(df.Title)[0]+1

對於Ticket屬性也需要處理,可以看到Ticket字段有的全是數字有的是字母和數字的集合,進一步對數據分析發現約25%的數據有前綴,前綴共有45種,如果把 . 和 / 去掉的話還剩29種,數字部分也有一定的規律:以1開頭的一般是一等艙2開頭的是二等艙3開頭的是三等艙,4-9開頭的大都是三等艙。以上這些數據告訴我們處理Ticket是有意義的,能夠發現其內部蘊涵的信息。

 1 def processTicket():
 2     global df
 3     df['TicketPrefix'] = df['Ticket'].map(lambda x: getTicketPrefix(x.upper()))
 4     df['TicketPrefix'] = df['TicketPrefix'].map(lambda x: re.sub\
 5                             ('[\.?\/?]','',x))
 6     df['TicketPrefix'] = df['TicketPrefix'].map(lambda x:re.sub\
 7                                                 ('STON','SOTON',x))
 8     df['TicketPrefix'] = pd.factorize(df['TicketPrefix'])[0]
 9     df['TicketNumber'] = df['Ticket'].map(lambda x: getTicketNumber(x) )
10     df['TicketNumberLength'] = df['TicketNumber'].map(lambda x: len(x)).\
11                                astype(int)
12     df['TicketNumberStart'] = df['TicketNumber'].map(lambda x: x[0:1]).\
13                               astype(int)
14     df['TicketNumber'] = df['TicketNumber'].astype(int)
15 def getTicketPrefix(ticket):
16     match = re.compile("([a-zA-Z\.\/]+)").search(ticket)
17     if match:
18         return match.group()
19     else:
20         return 'U'
21 def getTicketNumber(ticket):
22     match = re.compile("([0-9]+$)").search(ticket)
23     if match:
24         return match.group()
25     else:
26         return '0'

二 簡單組合屬性提取特征

一些屬性可以從它本身的數據里提取一些信息,有些屬性則需要和其他屬性組合來產生信息。比如對淘寶上的一個商品來說,購買數/點擊率可以反應商品的轉化率,也是商品的一個非常重要的特征。

對於Titanic來說,我們用Age*Pclass組合產生一個屬性,雖然沒有一個名詞來解釋它,但是從結果數據上來看,我們增大了年紀大的人的權重也提高了高等艙的權重,從最后幸存的結果上看,這個組合還是有意義的。除了這兩個屬性之外,我們還可以對其他數值屬性進行數學運算,以得到更大的候選特征集。

 1     numerics = df.loc[:, ['Age_scaled', 'Fare_scaled', 'Pclass_scaled', 'Parch_scaled', 'SibSp_scaled', 
 2                           'Names_scaled', 'CabinNumber_scaled', 'Age_bin_id_scaled', 'Fare_bin_id_scaled']]
 3     print "\nFeatures used for automated feature generation:\n", numerics.head(10)
 4     
 5     new_fields_count = 0
 6     for i in range(0, numerics.columns.size-1):
 7         for j in range(0, numerics.columns.size-1):
 8             if i <= j:
 9                 name = str(numerics.columns.values[i]) + "*" + str(numerics.columns.values[j])
10                 df = pd.concat([df, pd.Series(numerics.iloc[:,i] * numerics.iloc[:,j], name=name)], axis=1)
11                 new_fields_count += 1
12             if i < j:
13                 name = str(numerics.columns.values[i]) + "+" + str(numerics.columns.values[j])
14                 df = pd.concat([df, pd.Series(numerics.iloc[:,i] + numerics.iloc[:,j], name=name)], axis=1)
15                 new_fields_count += 1
16             if not i == j:
17                 name = str(numerics.columns.values[i]) + "/" + str(numerics.columns.values[j])
18                 df = pd.concat([df, pd.Series(numerics.iloc[:,i] / numerics.iloc[:,j], name=name)], axis=1)
19                 name = str(numerics.columns.values[i]) + "-" + str(numerics.columns.values[j])
20                 df = pd.concat([df, pd.Series(numerics.iloc[:,i] - numerics.iloc[:,j], name=name)], axis=1)
21                 new_fields_count += 2
22       
23     print "\n", new_fields_count, "new features generated"

這個過程自動產生大量的特征,這里用了9個特征產生了176個特征,可能這些特征有些過於多了,但是它只是一個候選集,我們可以通過一些處理篩選掉一些特征。當然有些模型也很適合大量特征的訓練集,比如隨機森林(有論文驗證,隨機森林是分類算法中表現最好的模型)。

產生的這些特征可能高度相關於原始特征,線性模型處理這類特征時會產生multicollinearity問題,可以用計算這些特征的皮爾遜相關系數,篩選相關性特征。如果用隨機森林模型訓練的話,可以不需要這個步驟。

三 用PCA進行維歸約

通過上面三個部分的處理,我們得到了具有大量特征的維度很高的數據集,特征較多不能直接用來作為模型輸入,一是因為這些特征間具有多重共線性,可能 會導致空間的不穩定;二是因為高維空間本身具有稀疏性,一維正態分布有68%的值落於正負標准差之間,而在十維空間上只有0.02%;三是由於過多的屬性 會使挖掘需要很長時間。對於一些模型來說,比如使用L1(Lasso),當有大量屬性時效果很好,因為它可以有效忽略掉噪聲變量。而一些模型則容易過擬 合。

數據歸約技術可以用來得到數據集的規約表示,它小得多,但仍接近於保持原始數據的完整性。也就是說,在歸約后的數據集上進行數據挖掘將更加有效,仍然產生幾乎相同的數據分析結果。

PCA(主成份分析)是一種維歸約的方法,它搜索k個最能代表數據的n維正交向量,將原始數據投影到一個小的多的空間上,導致維歸約。PCA通過創建一個替換的較小的變量集組合屬性的基本要素。具體原理及python的實現過程可以參考這篇blog Implementing a Principal Component Analysis (PCA) in Python step by step

我們可以直接使用scikit-learn的PCA函數進行維規約

1     X = df.values[:, 1::] 2 y = df.values[:, 0] 3 variance_pct = .99 4 # Create PCA object 5 pca = PCA(n_components=variance_pct) 6 # Transform the initial features 7 X_transformed = pca.fit_transform(X,y) 8 # Create a data frame from the PCA'd data 9 pcaDataFrame = pd.DataFrame(X_transformed)

實驗發現,PCA對於線性模型(不使用Lasso)非常有效,但是對於隨機森林模型沒有提高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM