<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                作者: [寒小陽](http://blog.csdn.net/han_xiaoyang?viewmode=contents) 時間:2015年11月。 出處:[http://blog.csdn.net/han_xiaoyang/article/details/49797143](http://blog.csdn.net/han_xiaoyang/article/details/49797143) 聲明:版權所有,轉載請注明出處,謝謝。 ### 1.引言 先說一句,年末雙十一什么的一來,真是非(mang)常(cheng)歡(gou)樂(le)!然后push自己抽出時間來寫這篇blog的原因也非常簡單: - 寫完前兩篇邏輯回歸的介紹和各個角度理解之后,我們討論群([戳我入群](http://blog.csdn.net/han_xiaoyang/article/details/49624963))的小伙伴們紛紛表示『好像很高級的樣紙,but 然并卵 啊!你們倒是拿點實際數據來給我們看看,這玩意兒 有!什!么!用!啊!』 - talk is cheap, show me the code! - no example say a jb! OK,OK,這就來了咯,同學們別著急,我們先找個簡單的實際例子,來看看,所謂的數據挖掘或者機器學習實際應用到底是怎么樣一個過程。 『喂,那幾個說要看大數據上機器學習應用的,對,就是說你們!別著急好么,我們之后拉點大一點實際數據用[liblinear](http://www.csie.ntu.edu.tw/~cjlin/liblinear/)或者[spark,MLlib](http://spark.apache.org/mllib/)跑給你們看,行不行?咱們先拿個實例入入門嘛』 好了,我是一個嚴肅的技術研究和分享者,咳咳,不能廢話了,各位同學繼續往下看吧! ### 2.背景 **2.1 關于Kaggle** - [我是Kaggle地址,翻我牌子](https://www.kaggle.com/) - 親,逼格這么高的地方,你一定聽過對不對?是!這就是那個無數『數據挖掘先驅』們,在回答”槍我有了,哪能找到靶子練練手啊?”時候的答案! - 這是一個要數據有數據,要實際應用場景有場景,要一起在數據挖掘領域high得不要不要的小伙伴就有小伙伴的地方啊!!! 艾瑪,逗逼模式開太猛了。恩,不鬧,不鬧,說正事,Kaggle是一個數據分析建模的應用競賽平臺,有點類似[KDD-CUP](http://www.kdd.org/)(國際知識發現和數據挖掘競賽),企業或者研究者可以將問題背景、數據、期望指標等發布到Kaggle上,以競賽的形式向廣大的數據科學家征集解決方案。而熱愛數(dong)據(shou)挖(zhe)掘(teng)的小伙伴們可以下載/分析數據,使用統計/機器學習/數據挖掘等知識,建立算法模型,得出結果并提交,排名top的可能會有獎金哦! **2.2 關于泰坦尼克號之災** - 帶大家去[該問題頁面](https://www.kaggle.com/c/titanic)溜達一圈吧 - 下面是問題背景頁 ![](https://box.kancloud.cn/2016-03-11_56e24303be733.png) - 下面是可下載Data的頁面 ![Data頁面](https://box.kancloud.cn/2016-03-11_56e24303e6877.png "") - 下面是小伙伴們最愛的forum頁面,你會看到各種神級人物厲(qi)害(pa)的數據處理/建模想法,你會直視『世界真奇妙』。 ![論壇頁面](https://box.kancloud.cn/2016-03-11_56e24304211aa.png "") - 泰坦尼克號問題之背景 - 就是那個大家都熟悉的『Jack and Rose』的故事,豪華游艇倒了,大家都驚恐逃生,可是救生艇的數量有限,無法人人都有,副船長發話了『lady and kid first!』,所以是否獲救其實并非隨機,而是基于一些背景有rank先后的。 - 訓練和測試數據是一些乘客的個人信息以及存活狀況,要嘗試根據它生成合適的模型并預測其他人的存活狀況。 - 對,這是一個二分類問題,是我們之前討論的logistic regression所能處理的范疇。 ### 3.說明 接觸過Kaggle的同學們可能知道這個問題,也可能知道RandomForest和SVM等等算法,甚至還對多個模型做過融合,取得過非常好的結果,那maybe這篇文章并不是針對你的,你可以自行略過。 我們因為之前只介紹了Logistic Regression這一種分類算法。所以本次的問題解決過程和優化思路,都集中在這種算法上。其余的方法可能我們之后的文章里會提到。 說點個人的觀點。不一定正確。 **『解決一個問題的方法和思路不止一種』** **『沒有所謂的機器學習算法優劣,也沒有絕對高性能的機器學習算法,只有在特定的場景、數據和特征下更合適的機器學習算法。』** ### 4.怎么做? 手把手教程馬上就來,先來兩條我看到的,覺得很重要的經驗。 1. 印象中Andrew Ng老師似乎在coursera上說過,應用機器學習,千萬不要一上來就試圖做到完美,先擼一個baseline的model出來,再進行后續的分析步驟,一步步提高,所謂后續步驟可能包括『分析model現在的狀態(欠/過擬合),分析我們使用的feature的作用大小,進行feature selection,以及我們模型下的bad case和產生的原因』等等。 1. Kaggle上的大神們,也分享過一些experience,說幾條我記得的哈: - **『對數據的認識太重要了!』** - **『數據中的特殊點/離群點的分析和處理太重要了!』** - **『特征工程(feature engineering)太重要了!在很多Kaggle的場景下,甚至比model本身還要重要』** - **『要做模型融合(model ensemble)啊啊啊!』** 更多的經驗分享請加討論群,具體方式請聯系作者,或者參見[《“ML學分計劃”說明書》](http://blog.csdn.net/han_xiaoyang/article/details/49624963) ### 5.初探數據 先看看我們的數據,長什么樣吧。在Data下我們train.csv和test.csv兩個文件,分別存著官方給的訓練和測試數據。 ~~~ import pandas as pd #數據分析 import numpy as np #科學計算 from pandas import Series,DataFrame data_train = pd.read_csv("/Users/Hanxiaoyang/Titanic_data/Train.csv") data_train ~~~ pandas是常用的python數據處理包,把csv文件讀入成dataframe各式,我們在ipython notebook中,看到data_train如下所示: ![訓練數據](https://box.kancloud.cn/2016-03-11_56e2430449f5c.png "") 這就是典型的dataframe格式,如果你沒接觸過這種格式,完全沒有關系,你就把它想象成Excel里面的列好了。 我們看到,總共有12列,其中Survived字段表示的是該乘客是否獲救,其余都是乘客的個人信息,包括: - PassengerId => 乘客ID - Pclass => 乘客等級(1/2/3等艙位) - Name => 乘客姓名 - Sex => 性別 - Age => 年齡 - SibSp => 堂兄弟/妹個數 - Parch => 父母與小孩個數 - Ticket => 船票信息 - Fare => 票價 - Cabin => 客艙 - Embarked => 登船港口 逐條往下看,要看完這么多條,眼睛都有一種要瞎的趕腳。好吧,我們讓dataframe自己告訴我們一些信息,如下所示: ~~~ data_train.info() ~~~ 看到了如下的信息: ![數據信息](https://box.kancloud.cn/2016-03-11_56e24304756cb.png "") 上面的數據說啥了?它告訴我們,訓練數據中總共有891名乘客,但是很不幸,我們有些屬性的數據不全,比如說: - Age(年齡)屬性只有714名乘客有記錄 - Cabin(客艙)更是只有204名乘客是已知的 似乎信息略少啊,想再瞄一眼具體數據數值情況呢?恩,我們用下列的方法,得到數值型數據的一些分布(因為有些屬性,比如姓名,是文本型;而另外一些屬性,比如登船港口,是類目型。這些我們用下面的函數是看不到的): ![數值型數據基本信息](https://box.kancloud.cn/2016-03-11_56e243049eea5.png "") 我們從上面看到更進一步的什么信息呢? mean字段告訴我們,大概0.383838的人最后獲救了,2/3等艙的人數比1等艙要多,平均乘客年齡大概是29.7歲(計算這個時候會略掉無記錄的)等等… ### 6.數據初步分析 每個乘客都這么多屬性,那我們咋知道哪些屬性更有用,而又應該怎么用它們啊?說實話這會兒我也不知道,但我們記得前面提到過 - **『對數據的認識太重要了!』** - **『對數據的認識太重要了!』** - **『對數據的認識太重要了!』** 重要的事情說三遍,恩,說完了。僅僅最上面的對數據了解,依舊無法給我們提供想法和思路。我們再深入一點來看看我們的數據,看看每個/多個 屬性和最后的Survived之間有著什么樣的關系呢。 **6.1 乘客各屬性分布** 腦容量太有限了…數值看花眼了。我們還是統計統計,畫些圖來看看屬性和結果之間的關系好了,代碼如下: ~~~ import matplotlib.pyplot as plt fig = plt.figure() fig.set(alpha=0.2) # 設定圖表顏色alpha參數 plt.subplot2grid((2,3),(0,0)) # 在一張大圖里分列幾個小圖 data_train.Survived.value_counts().plot(kind='bar')# 柱狀圖 plt.title(u"獲救情況 (1為獲救)") # 標題 plt.ylabel(u"人數") plt.subplot2grid((2,3),(0,1)) data_train.Pclass.value_counts().plot(kind="bar") plt.ylabel(u"人數") plt.title(u"乘客等級分布") plt.subplot2grid((2,3),(0,2)) plt.scatter(data_train.Survived, data_train.Age) plt.ylabel(u"年齡") # 設定縱坐標名稱 plt.grid(b=True, which='major', axis='y') plt.title(u"按年齡看獲救分布 (1為獲救)") plt.subplot2grid((2,3),(1,0), colspan=2) data_train.Age[data_train.Pclass == 1].plot(kind='kde') data_train.Age[data_train.Pclass == 2].plot(kind='kde') data_train.Age[data_train.Pclass == 3].plot(kind='kde') plt.xlabel(u"年齡")# plots an axis lable plt.ylabel(u"密度") plt.title(u"各等級的乘客年齡分布") plt.legend((u'頭等艙', u'2等艙',u'3等艙'),loc='best') # sets our legend for our graph. plt.subplot2grid((2,3),(1,2)) data_train.Embarked.value_counts().plot(kind='bar') plt.title(u"各登船口岸上船人數") plt.ylabel(u"人數") plt.show() ~~~ ![數據基本信息圖示](https://box.kancloud.cn/2016-03-11_56e24304d4b37.png "") bingo,圖還是比數字好看多了。所以我們在圖上可以看出來,被救的人300多點,不到半數;3等艙乘客灰常多;遇難和獲救的人年齡似乎跨度都很廣;3個不同的艙年齡總體趨勢似乎也一致,2/3等艙乘客20歲多點的人最多,1等艙40歲左右的最多(→_→似乎符合財富和年齡的分配哈,咳咳,別理我,我瞎扯的);登船港口人數按照S、C、Q遞減,而且S遠多于另外倆港口。 這個時候我們可能會有一些想法了: - 不同艙位/乘客等級可能和財富/地位有關系,最后獲救概率可能會不一樣 - 年齡對獲救概率也一定是有影響的,畢竟前面說了,副船長還說『小孩和女士先走』呢 - 和登船港口是不是有關系呢?也許登船港口不同,人的出身地位不同? 口說無憑,空想無益。老老實實再來統計統計,看看這些屬性值的統計分布吧。 **6.2 屬性與獲救結果的關聯統計** ~~~ #看看各乘客等級的獲救情況 fig = plt.figure() fig.set(alpha=0.2) # 設定圖表顏色alpha參數 Survived_0 = data_train.Pclass[data_train.Survived == 0].value_counts() Survived_1 = data_train.Pclass[data_train.Survived == 1].value_counts() df=pd.DataFrame({u'獲救':Survived_1, u'未獲救':Survived_0}) df.plot(kind='bar', stacked=True) plt.title(u"各乘客等級的獲救情況") plt.xlabel(u"乘客等級") plt.ylabel(u"人數") plt.show() ~~~ ![各乘客等級的獲救情況](https://box.kancloud.cn/2016-03-11_56e243053ef66.png "") 嘖嘖,果然,錢和地位對艙位有影響,進而對獲救的可能性也有影響啊←_← 咳咳,跑題了,我想說的是,明顯等級為1的乘客,獲救的概率高很多。恩,這個一定是影響最后獲救結果的一個特征。 ~~~ #看看各性別的獲救情況 fig = plt.figure() fig.set(alpha=0.2) # 設定圖表顏色alpha參數 Survived_m = data_train.Survived[data_train.Sex == 'male'].value_counts() Survived_f = data_train.Survived[data_train.Sex == 'female'].value_counts() df=pd.DataFrame({u'男性':Survived_m, u'女性':Survived_f}) df.plot(kind='bar', stacked=True) plt.title(u"按性別看獲救情況") plt.xlabel(u"性別") plt.ylabel(u"人數") plt.show() ~~~ ![各乘客等級的獲救情況](https://box.kancloud.cn/2016-03-11_56e243055bead.png "") 歪果盆友果然很尊重lady,lady first踐行得不錯。性別無疑也要作為重要特征加入最后的模型之中。 再來個詳細版的好了。 ~~~ #然后我們再來看看各種艙級別情況下各性別的獲救情況 fig=plt.figure() fig.set(alpha=0.65) # 設置圖像透明度,無所謂 plt.title(u"根據艙等級和性別的獲救情況") ax1=fig.add_subplot(141) data_train.Survived[data_train.Sex == 'female'][data_train.Pclass != 3].value_counts().plot(kind='bar', label="female highclass", color='#FA2479') ax1.set_xticklabels([u"獲救", u"未獲救"], rotation=0) ax1.legend([u"女性/高級艙"], loc='best') ax2=fig.add_subplot(142, sharey=ax1) data_train.Survived[data_train.Sex == 'female'][data_train.Pclass == 3].value_counts().plot(kind='bar', label='female, low class', color='pink') ax2.set_xticklabels([u"未獲救", u"獲救"], rotation=0) plt.legend([u"女性/低級艙"], loc='best') ax3=fig.add_subplot(143, sharey=ax1) data_train.Survived[data_train.Sex == 'male'][data_train.Pclass != 3].value_counts().plot(kind='bar', label='male, high class',color='lightblue') ax3.set_xticklabels([u"未獲救", u"獲救"], rotation=0) plt.legend([u"男性/高級艙"], loc='best') ax4=fig.add_subplot(144, sharey=ax1) data_train.Survived[data_train.Sex == 'male'][data_train.Pclass == 3].value_counts().plot(kind='bar', label='male low class', color='steelblue') ax4.set_xticklabels([u"未獲救", u"獲救"], rotation=0) plt.legend([u"男性/低級艙"], loc='best') plt.show() ~~~ ![各性別和艙位的獲救情況](https://box.kancloud.cn/2016-03-11_56e2430582bce.png "") 恩,堅定了之前的判斷。 我們看看各登船港口的獲救情況。 ~~~ fig = plt.figure() fig.set(alpha=0.2) # 設定圖表顏色alpha參數 Survived_0 = data_train.Embarked[data_train.Survived == 0].value_counts() Survived_1 = data_train.Embarked[data_train.Survived == 1].value_counts() df=pd.DataFrame({u'獲救':Survived_1, u'未獲救':Survived_0}) df.plot(kind='bar', stacked=True) plt.title(u"各登錄港口乘客的獲救情況") plt.xlabel(u"登錄港口") plt.ylabel(u"人數") plt.show() ~~~ ![各登船港口的獲救情況](https://box.kancloud.cn/2016-03-11_56e24305a8d0d.png "") 下面我們來看看 堂兄弟/妹,孩子/父母有幾人,對是否獲救的影響。 ~~~ g = data_train.groupby(['SibSp','Survived']) df = pd.DataFrame(g.count()['PassengerId']) print df g = data_train.groupby(['SibSp','Survived']) df = pd.DataFrame(g.count()['PassengerId']) print df ~~~ ![堂兄弟/妹影響](https://box.kancloud.cn/2016-03-11_56e24305c7345.png "") ![父母/孩子影響](https://box.kancloud.cn/2016-03-11_56e24305e6bec.png "") 好吧,沒看出特別特別明顯的規律(為自己的智商感到捉急…),先作為備選特征,放一放。 ~~~ #ticket是船票編號,應該是unique的,和最后的結果沒有太大的關系,先不納入考慮的特征范疇把 #cabin只有204個乘客有值,我們先看看它的一個分布 data_train.Cabin.value_counts() ~~~ 部分結果如下: ![Cabin分布](https://box.kancloud.cn/2016-03-11_56e243060a114.png "") 這三三兩兩的…如此不集中…我們猜一下,也許,前面的ABCDE是指的甲板位置、然后編號是房間號?…好吧,我瞎說的,別當真… 關鍵是Cabin這鬼屬性,應該算作類目型的,本來缺失值就多,還如此不集中,注定是個棘手貨…第一感覺,這玩意兒如果直接按照類目特征處理的話,太散了,估計每個因子化后的特征都拿不到什么權重。加上有那么多缺失值,要不我們先把Cabin缺失與否作為條件(雖然這部分信息缺失可能并非未登記,maybe只是丟失了而已,所以這樣做未必妥當),先在有無Cabin信息這個粗粒度上看看Survived的情況好了。 ~~~ fig = plt.figure() fig.set(alpha=0.2) # 設定圖表顏色alpha參數 Survived_cabin = data_train.Survived[pd.notnull(data_train.Cabin)].value_counts() Survived_nocabin = data_train.Survived[pd.isnull(data_train.Cabin)].value_counts() df=pd.DataFrame({u'有':Survived_cabin, u'無':Survived_nocabin}).transpose() df.plot(kind='bar', stacked=True) plt.title(u"按Cabin有無看獲救情況") plt.xlabel(u"Cabin有無") plt.ylabel(u"人數") plt.show() ~~~ ![有無Cabin記錄影響](https://box.kancloud.cn/2016-03-11_56e2430630efb.png "") 咳咳,有Cabin記錄的似乎獲救概率稍高一些,先這么著放一放吧。 ### 7.簡單數據預處理 大體數據的情況看了一遍,對感興趣的屬性也有個大概的了解了。 下一步干啥?咱們該處理處理這些數據,為機器學習建模做點準備了。 對了,我這里說的數據預處理,其實就包括了很多Kaggler津津樂道的feature engineering過程,灰常灰常有必要! **『特征工程(feature engineering)太重要了!』** **『特征工程(feature engineering)太重要了!』** **『特征工程(feature engineering)太重要了!』** 恩,重要的事情說三遍。 先從最突出的數據屬性開始吧,對,Cabin和Age,有丟失數據實在是對下一步工作影響太大。 先說Cabin,暫時我們就按照剛才說的,按Cabin有無數據,將這個屬性處理成Yes和No兩種類型吧。 再說Age: 通常遇到缺值的情況,我們會有幾種常見的處理方式 - 如果缺值的樣本占總數比例極高,我們可能就直接舍棄了,作為特征加入的話,可能反倒帶入noise,影響最后的結果了 - 如果缺值的樣本適中,而該屬性非連續值特征屬性(比如說類目屬性),那就把NaN作為一個新類別,加到類別特征中 - 如果缺值的樣本適中,而該屬性為連續值特征屬性,有時候我們會考慮給定一個step(比如這里的age,我們可以考慮每隔2/3歲為一個步長),然后把它離散化,之后把NaN作為一個type加到屬性類目中。 - 有些情況下,缺失的值個數并不是特別多,那我們也可以試著根據已有的值,擬合一下數據,補充上。 本例中,后兩種處理方式應該都是可行的,我們先試試擬合補全吧(雖然說沒有特別多的背景可供我們擬合,這不一定是一個多么好的選擇) 我們這里用scikit-learn中的RandomForest來擬合一下缺失的年齡數據(注:RandomForest是一個用在原始數據中做不同采樣,建立多顆DecisionTree,再進行average等等來降低過擬合現象,提高結果的機器學習算法,我們之后會介紹到) ~~~ from sklearn.ensemble import RandomForestRegressor ### 使用 RandomForestClassifier 填補缺失的年齡屬性 def set_missing_ages(df): # 把已有的數值型特征取出來丟進Random Forest Regressor中 age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']] # 乘客分成已知年齡和未知年齡兩部分 known_age = age_df[age_df.Age.notnull()].as_matrix() unknown_age = age_df[age_df.Age.isnull()].as_matrix() # y即目標年齡 y = known_age[:, 0] # X即特征屬性值 X = known_age[:, 1:] # fit到RandomForestRegressor之中 rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1) rfr.fit(X, y) # 用得到的模型進行未知年齡結果預測 predictedAges = rfr.predict(unknown_age[:, 1::]) # 用得到的預測結果填補原缺失數據 df.loc[ (df.Age.isnull()), 'Age' ] = predictedAges return df, rfr def set_Cabin_type(df): df.loc[ (df.Cabin.notnull()), 'Cabin' ] = "Yes" df.loc[ (df.Cabin.isnull()), 'Cabin' ] = "No" return df data_train, rfr = set_missing_ages(data_train) data_train = set_Cabin_type(data_train) ~~~ ![處理Cabin和Age之后](https://box.kancloud.cn/2016-03-11_56e2430653356.png "") 恩。目的達到,OK了。 因為邏輯回歸建模時,需要輸入的特征都是數值型特征,我們通常會先對類目型的特征因子化。 什么叫做因子化呢?舉個例子: 以Cabin為例,原本一個屬性維度,因為其取值可以是[‘yes’,’no’],而將其平展開為’Cabin_yes’,’Cabin_no’兩個屬性 - 原本Cabin取值為yes的,在此處的”Cabin_yes”下取值為1,在”Cabin_no”下取值為0 - 原本Cabin取值為no的,在此處的”Cabin_yes”下取值為0,在”Cabin_no”下取值為1 我們使用pandas的”get_dummies”來完成這個工作,并拼接在原來的”data_train”之上,如下所示。 ~~~ dummies_Cabin = pd.get_dummies(data_train['Cabin'], prefix= 'Cabin') dummies_Embarked = pd.get_dummies(data_train['Embarked'], prefix= 'Embarked') dummies_Sex = pd.get_dummies(data_train['Sex'], prefix= 'Sex') dummies_Pclass = pd.get_dummies(data_train['Pclass'], prefix= 'Pclass') df = pd.concat([data_train, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1) df.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1, inplace=True) df ~~~ ![離散/因子化之后](https://box.kancloud.cn/2016-03-11_56e2430687a13.png "") bingo,我們很成功地把這些類目屬性全都轉成0,1的數值屬性了。 這樣,看起來,是不是我們需要的屬性值都有了,且它們都是數值型屬性呢。 有一種臨近結果的寵寵欲動感吧,莫急莫急,我們還得做一些處理,仔細看看Age和Fare兩個屬性,乘客的數值幅度變化,也忒大了吧!!如果大家了解邏輯回歸與梯度下降的話,會知道,各屬性值之間scale差距太大,將對收斂速度造成幾萬點傷害值!甚至不收斂! (╬▔皿▔)…所以我們先用scikit-learn里面的preprocessing模塊對這倆貨做一個scaling,所謂scaling,其實就是將一些變化幅度較大的特征化到[-1,1]之內。 ~~~ import sklearn.preprocessing as preprocessing scaler = preprocessing.StandardScaler() age_scale_param = scaler.fit(df['Age']) df['Age_scaled'] = scaler.fit_transform(df['Age'], age_scale_param) fare_scale_param = scaler.fit(df['Fare']) df['Fare_scaled'] = scaler.fit_transform(df['Fare'], fare_scale_param) df ~~~ ![scaling](https://box.kancloud.cn/2016-03-11_56e24306ad1da.png "") 恩,好看多了,萬事俱備,只欠建模。馬上就要看到成效了,哈哈。我們把需要的屬性值抽出來,轉成scikit-learn里面LogisticRegression可以處理的格式。 ### 8.邏輯回歸建模 我們把需要的feature字段取出來,轉成numpy格式,使用scikit-learn中的LogisticRegression建模。 ~~~ from sklearn import linear_model # 用正則取出我們要的屬性值 train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*') train_np = train_df.as_matrix() # y即Survival結果 y = train_np[:, 0] # X即特征屬性值 X = train_np[:, 1:] # fit到RandomForestRegressor之中 clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6) clf.fit(X, y) clf ~~~ good,很順利,我們得到了一個model,如下: ![modeling](https://box.kancloud.cn/2016-03-11_56e24306d4cda.png "") 先淡定!淡定!你以為把test.csv直接丟進model里就能拿到結果啊…騷年,圖樣圖森破啊!我們的”test_data”也要做和”train_data”一樣的預處理啊!! ~~~ data_test = pd.read_csv("/Users/Hanxiaoyang/Titanic_data/test.csv") data_test.loc[ (data_test.Fare.isnull()), 'Fare' ] = 0 # 接著我們對test_data做和train_data中一致的特征變換 # 首先用同樣的RandomForestRegressor模型填上丟失的年齡 tmp_df = data_test[['Age','Fare', 'Parch', 'SibSp', 'Pclass']] null_age = tmp_df[data_test.Age.isnull()].as_matrix() # 根據特征屬性X預測年齡并補上 X = null_age[:, 1:] predictedAges = rfr.predict(X) data_test.loc[ (data_test.Age.isnull()), 'Age' ] = predictedAges data_test = set_Cabin_type(data_test) dummies_Cabin = pd.get_dummies(data_test['Cabin'], prefix= 'Cabin') dummies_Embarked = pd.get_dummies(data_test['Embarked'], prefix= 'Embarked') dummies_Sex = pd.get_dummies(data_test['Sex'], prefix= 'Sex') dummies_Pclass = pd.get_dummies(data_test['Pclass'], prefix= 'Pclass') df_test = pd.concat([data_test, dummies_Cabin, dummies_Embarked, dummies_Sex, dummies_Pclass], axis=1) df_test.drop(['Pclass', 'Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1, inplace=True) df_test['Age_scaled'] = scaler.fit_transform(df_test['Age'], age_scale_param) df_test['Fare_scaled'] = scaler.fit_transform(df_test['Fare'], fare_scale_param) df_test ~~~ ![modeling](https://box.kancloud.cn/2016-03-11_56e24306f2d25.png "") 不錯不錯,數據很OK,差最后一步了。 下面就做預測取結果吧!! ~~~ test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*') predictions = clf.predict(test) result = pd.DataFrame({'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)}) result.to_csv("/Users/Hanxiaoyang/Titanic_data/logistic_regression_predictions.csv", index=False) ~~~ ![預測結果](https://box.kancloud.cn/2016-03-11_56e243072522f.png "") 嘖嘖,挺好,格式正確,去make a submission啦啦啦! 在Kaggle的Make a submission頁面,提交上結果。如下: ![Kaggle排名](https://box.kancloud.cn/2016-03-11_56e243074098b.png "") 0.76555,恩,結果還不錯。畢竟,這只是我們簡單分析處理過后出的一個baseline模型嘛。 ### 9.邏輯回歸系統優化 **9.1 模型系數關聯分析** 親,你以為結果提交上了,就完事了? 我不會告訴你,這只是萬里長征第一步啊(淚牛滿面)!!!這才剛擼完baseline model啊!!!還得優化啊!!! 看過Andrew Ng老師的machine Learning課程的同學們,知道,我們應該分析分析模型現在的狀態了,是過/欠擬合?,以確定我們需要更多的特征還是更多數據,或者其他操作。我們有一條很著名的learning curves對吧。 不過在現在的場景下,先不著急做這個事情,我們這個baseline系統還有些粗糙,先再挖掘挖掘。 - 首先,Name和Ticket兩個屬性被我們完整舍棄了(好吧,其實是因為這倆屬性,幾乎每一條記錄都是一個完全不同的值,我們并沒有找到很直接的處理方式)。 - 然后,我們想想,年齡的擬合本身也未必是一件非常靠譜的事情,我們依據其余屬性,其實并不能很好地擬合預測出未知的年齡。再一個,以我們的日常經驗,小盆友和老人可能得到的照顧會多一些,這樣看的話,年齡作為一個連續值,給一個固定的系數,應該和年齡是一個正相關或者負相關,似乎體現不出兩頭受照顧的實際情況,所以,說不定我們把年齡離散化,按區段分作類別屬性會更合適一些。 上面只是我瞎想的,who knows是不是這么回事呢,老老實實先把得到的model系數和feature關聯起來看看。 ~~~ pd.DataFrame({"columns":list(train_df.columns)[1:], "coef":list(clf.coef_.T)}) ~~~ ![LR模型系數](https://box.kancloud.cn/2016-03-11_56e2430768553.png "") 首先,大家回去[前兩篇文章](http://blog.csdn.net/han_xiaoyang/article/details/49123419)里瞄一眼公式就知道,這些系數為正的特征,和最后結果是一個正相關,反之為負相關。 我們先看看那些權重絕對值非常大的feature,在我們的模型上: - Sex屬性,如果是female會極大提高最后獲救的概率,而male會很大程度拉低這個概率。 - Pclass屬性,1等艙乘客最后獲救的概率會上升,而乘客等級為3會極大地拉低這個概率。 - 有Cabin值會很大程度拉升最后獲救概率(這里似乎能看到了一點端倪,事實上從最上面的有無Cabin記錄的Survived分布圖上看出,即使有Cabin記錄的乘客也有一部分遇難了,估計這個屬性上我們挖掘還不夠) - Age是一個負相關,意味著在我們的模型里,年齡越小,越有獲救的優先權(還得回原數據看看這個是否合理) - 有一個登船港口S會很大程度拉低獲救的概率,另外倆港口壓根就沒啥作用(這個實際上非常奇怪,因為我們從之前的統計圖上并沒有看到S港口的獲救率非常低,所以也許可以考慮把登船港口這個feature去掉試試)。 - 船票Fare有小幅度的正相關(并不意味著這個feature作用不大,有可能是我們細化的程度還不夠,舉個例子,說不定我們得對它離散化,再分至各個乘客等級上?) 噢啦,觀察完了,我們現在有一些想法了,但是怎么樣才知道,哪些優化的方法是promising的呢? 因為test.csv里面并沒有Survived這個字段(好吧,這是廢話,這明明就是我們要預測的結果),我們無法在這份數據上評定我們算法在該場景下的效果… 而『每做一次調整就make a submission,然后根據結果來判定這次調整的好壞』其實是行不通的… **9.2 交叉驗證** 重點又來了: **『要做交叉驗證(cross validation)!』** **『要做交叉驗證(cross validation)!』** **『要做交叉驗證(cross validation)!』** 恩,重要的事情說三遍。我們通常情況下,這么做cross validation:把train.csv分成兩部分,一部分用于訓練我們需要的模型,另外一部分數據上看我們預測算法的效果。 我們用scikit-learn的cross_validation來幫我們完成小數據集上的這個工作。 先簡單看看cross validation情況下的打分 ~~~ from sklearn import cross_validation #簡單看看打分情況 clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6) all_data = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*') X = all_data.as_matrix()[:,1:] y = all_data.as_matrix()[:,0] print cross_validation.cross_val_score(clf, X, y, cv=5) ~~~ 結果是下面醬紫的: [0.81564246 0.81005587 0.78651685 0.78651685 0.81355932] 似乎比Kaggle上的結果略高哈,畢竟用的是不是同一份數據集評估的。 等等,既然我們要做交叉驗證,那我們干脆先把交叉驗證里面的bad case拿出來看看,看看人眼審核,是否能發現什么蛛絲馬跡,是我們忽略了哪些信息,使得這些乘客被判定錯了。再把bad case上得到的想法和前頭系數分析的合在一起,然后逐個試試。 下面我們做數據分割,并且在原始數據集上瞄一眼bad case: ~~~ # 分割數據,按照 訓練數據:cv數據 = 7:3的比例 split_train, split_cv = cross_validation.train_test_split(df, test_size=0.3, random_state=0) train_df = split_train.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*') # 生成模型 clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6) clf.fit(train_df.as_matrix()[:,1:], train_df.as_matrix()[:,0]) # 對cross validation數據進行預測 cv_df = split_cv.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass_.*') predictions = clf.predict(cv_df.as_matrix()[:,1:]) origin_data_train = pd.read_csv("/Users/HanXiaoyang/Titanic_data/Train.csv") bad_cases = origin_data_train.loc[origin_data_train['PassengerId'].isin(split_cv[predictions != cv_df.as_matrix()[:,0]]['PassengerId'].values)] bad_cases ~~~ 我們判定錯誤的 bad case 中部分數據如下: ![預測錯誤的原始數據](https://box.kancloud.cn/2016-03-11_56e24307924e6.png "") 大家可以自己跑一遍試試,拿到bad cases之后,仔細看看。也會有一些猜測和想法。其中會有一部分可能會印證在系數分析部分的猜測,那這些優化的想法優先級可以放高一些。 現在有了”train_df” 和 “vc_df” 兩個數據部分,前者用于訓練model,后者用于評定和選擇模型。可以開始可勁折騰了。 我們隨便列一些可能可以做的優化操作: - Age屬性不使用現在的擬合方式,而是根據名稱中的『Mr』『Mrs』『Miss』等的平均值進行填充。 - Age不做成一個連續值屬性,而是使用一個步長進行離散化,變成離散的類目feature。 - Cabin再細化一些,對于有記錄的Cabin屬性,我們將其分為前面的字母部分(我猜是位置和船層之類的信息) 和 后面的數字部分(應該是房間號,有意思的事情是,如果你仔細看看原始數據,你會發現,這個值大的情況下,似乎獲救的可能性高一些)。 - Pclass和Sex倆太重要了,我們試著用它們去組出一個組合屬性來試試,這也是另外一種程度的細化。 - 單加一個Child字段,Age<=12的,設為1,其余為0(你去看看數據,確實小盆友優先程度很高啊) - 如果名字里面有『Mrs』,而Parch>1的,我們猜測她可能是一個母親,應該獲救的概率也會提高,因此可以多加一個Mother字段,此種情況下設為1,其余情況下設為0 - 登船港口可以考慮先去掉試試(Q和C本來就沒權重,S有點詭異) - 把堂兄弟/兄妹 和 Parch 還有自己 個數加在一起組一個Family_size字段(考慮到大家族可能對最后的結果有影響) - Name是一個我們一直沒有觸碰的屬性,我們可以做一些簡單的處理,比如說男性中帶某些字眼的(‘Capt’, ‘Don’, ‘Major’, ‘Sir’)可以統一到一個Title,女性也一樣。 大家接著往下挖掘,可能還可以想到更多可以細挖的部分。我這里先列這些了,然后我們可以使用手頭上的”train_df”和”cv_df”開始試驗這些feature engineering的tricks是否有效了。 試驗的過程比較漫長,也需要有耐心,而且我們經常會面臨很尷尬的狀況,就是我們靈光一閃,想到一個feature,然后堅信它一定有效,結果試驗下來,效果還不如試驗之前的結果。恩,需要堅持和耐心,以及不斷的挖掘。 我最好的結果是在『Survived~C(Pclass)+C(Title)+C(Sex)+C(Age_bucket)+C(Cabin_num_bucket)Mother+Fare+Family_Size』下取得的,結果如下(抱歉,博主君commit的時候手抖把頁面關了,于是沒截著圖,下面這張圖是在我得到最高分之后,用這次的結果重新make commission的,截了個圖,得分是0.79426,不是目前我的最高分哈,因此排名木有變…): ![做完feature engineering調整之后的結果](https://box.kancloud.cn/2016-03-11_56e24307bef28.png "") **9.3 learning curves** 有一個很可能發生的問題是,我們不斷地做feature engineering,產生的特征越來越多,用這些特征去訓練模型,會對我們的訓練集擬合得越來越好,同時也可能在逐步喪失泛化能力,從而在待預測的數據上,表現不佳,也就是發生過擬合問題。 從另一個角度上說,如果模型在待預測的數據上表現不佳,除掉上面說的過擬合問題,也有可能是欠擬合問題,也就是說在訓練集上,其實擬合的也不是那么好。 額,這個欠擬合和過擬合怎么解釋呢。這么說吧: - 過擬合就像是你班那個學數學比較刻板的同學,老師講過的題目,一字不漏全記下來了,于是老師再出一樣的題目,分分鐘精確出結果。but數學考試,因為總是碰到新題目,所以成績不咋地。 - 欠擬合就像是,咳咳,和博主level差不多的差生。連老師講的練習題也記不住,于是連老師出一樣題目復習的周測都做不好,考試更是可想而知了。 而在機器學習的問題上,對于過擬合和欠擬合兩種情形。我們優化的方式是不同的。 對過擬合而言,通常以下策略對結果優化是有用的: - 做一下feature selection,挑出較好的feature的subset來做training - 提供更多的數據,從而彌補原始數據的bias問題,學習到的model也會更準確 而對于欠擬合而言,我們通常需要更多的feature,更復雜的模型來提高準確度。 著名的learning curve可以幫我們判定我們的模型現在所處的狀態。我們以樣本數為橫坐標,訓練和交叉驗證集上的錯誤率作為縱坐標,兩種狀態分別如下兩張圖所示:過擬合(overfitting/high variace),欠擬合(underfitting/high bias) ![過擬合](https://box.kancloud.cn/2016-03-11_56e24307e45bd.png "") ![欠擬合](https://box.kancloud.cn/2016-03-11_56e243080a8cc.png "") 我們也可以把錯誤率替換成準確率(得分),得到另一種形式的learning curve(sklearn 里面是這么做的)。 回到我們的問題,我們用scikit-learn里面的learning_curve來幫我們分辨我們模型的狀態。舉個例子,這里我們一起畫一下我們最先得到的baseline model的learning curve。 ~~~ import numpy as np import matplotlib.pyplot as plt from sklearn.learning_curve import learning_curve # 用sklearn的learning_curve得到training_score和cv_score,使用matplotlib畫出learning curve def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None, n_jobs=1, train_sizes=np.linspace(.05, 1., 20), verbose=0, plot=True): """ 畫出data在某模型上的learning curve. 參數解釋 ---------- estimator : 你用的分類器。 title : 表格的標題。 X : 輸入的feature,numpy類型 y : 輸入的target vector ylim : tuple格式的(ymin, ymax), 設定圖像中縱坐標的最低點和最高點 cv : 做cross-validation的時候,數據分成的份數,其中一份作為cv集,其余n-1份作為training(默認為3份) n_jobs : 并行的的任務數(默認1) """ train_sizes, train_scores, test_scores = learning_curve( estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes, verbose=verbose) train_scores_mean = np.mean(train_scores, axis=1) train_scores_std = np.std(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) test_scores_std = np.std(test_scores, axis=1) if plot: plt.figure() plt.title(title) if ylim is not None: plt.ylim(*ylim) plt.xlabel(u"訓練樣本數") plt.ylabel(u"得分") plt.gca().invert_yaxis() plt.grid() plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, alpha=0.1, color="b") plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, alpha=0.1, color="r") plt.plot(train_sizes, train_scores_mean, 'o-', color="b", label=u"訓練集上得分") plt.plot(train_sizes, test_scores_mean, 'o-', color="r", label=u"交叉驗證集上得分") plt.legend(loc="best") plt.draw() plt.show() plt.gca().invert_yaxis() midpoint = ((train_scores_mean[-1] + train_scores_std[-1]) + (test_scores_mean[-1] - test_scores_std[-1])) / 2 diff = (train_scores_mean[-1] + train_scores_std[-1]) - (test_scores_mean[-1] - test_scores_std[-1]) return midpoint, diff plot_learning_curve(clf, u"學習曲線", X, y) ~~~ ![學習曲線](https://box.kancloud.cn/2016-03-11_56e2430826f5a.png "") 在實際數據上看,我們得到的learning curve沒有理論推導的那么光滑哈,但是可以大致看出來,訓練集和交叉驗證集上的得分曲線走勢還是符合預期的。 目前的曲線看來,我們的model并不處于overfitting的狀態(overfitting的表現一般是訓練集上得分高,而交叉驗證集上要低很多,中間的gap比較大)。因此我們可以再做些feature engineering的工作,添加一些新產出的特征或者組合特征到模型中。 ### 10.模型融合(model ensemble) 好了,終于到這一步了,我們要祭出機器學習/數據挖掘上通常最后會用到的大殺器了。恩,模型融合。 『強迫癥患者』打算繼續喊喊口號… **『模型融合(model ensemble)很重要!』** **『模型融合(model ensemble)很重要!』** **『模型融合(model ensemble)很重要!』** 重要的事情說三遍,恩,噢啦。 先解釋解釋,一會兒再回到我們的問題上哈。 啥叫模型融合呢,我們還是舉幾個例子直觀理解一下好了。 大家都看過知識問答的綜藝節目中,求助現場觀眾時候,讓觀眾投票,最高的答案作為自己的答案的形式吧,每個人都有一個判定結果,最后我們相信答案在大多數人手里。 再通俗一點舉個例子。你和你班某數學大神關系好,每次作業都『模仿』他的,于是絕大多數情況下,他做對了,你也對了。突然某一天大神腦子犯糊涂,手一抖,寫錯了一個數,于是…恩,你也只能跟著錯了。 我們再來看看另外一個場景,你和你班5個數學大神關系都很好,每次都把他們作業拿過來,對比一下,再『自己做』,那你想想,如果哪天某大神犯糊涂了,寫錯了,but另外四個寫對了啊,那你肯定相信另外4人的是正確答案吧? 最簡單的模型融合大概就是這么個意思,比如分類問題,當我們手頭上有一堆在同一份數據集上訓練得到的分類器(比如logistic regression,SVM,KNN,random forest,神經網絡),那我們讓他們都分別去做判定,然后對結果做投票統計,取票數最多的結果為最后結果。 bingo,問題就這么完美的解決了。 模型融合可以比較好地緩解,訓練過程中產生的過擬合問題,從而對于結果的準確度提升有一定的幫助。 話說回來,回到我們現在的問題。你看,我們現在只講了logistic regression,如果我們還想用這個融合思想去提高我們的結果,我們該怎么做呢? 既然這個時候模型沒得選,那咱們就在數據上動動手腳咯。大家想想,如果模型出現過擬合現在,一定是在我們的訓練上出現擬合過度造成的對吧。 那我們干脆就不要用全部的訓練集,每次取訓練集的一個subset,做訓練,這樣,我們雖然用的是同一個機器學習算法,但是得到的模型卻是不一樣的;同時,因為我們沒有任何一份子數據集是全的,因此即使出現過擬合,也是在子訓練集上出現過擬合,而不是全體數據上,這樣做一個融合,可能對最后的結果有一定的幫助。對,這就是常用的Bagging。 我們用scikit-learn里面的Bagging來完成上面的思路,過程非常簡單。代碼如下: ~~~ from sklearn.ensemble import BaggingRegressor train_df = df.filter(regex='Survived|Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title') train_np = train_df.as_matrix() # y即Survival結果 y = train_np[:, 0] # X即特征屬性值 X = train_np[:, 1:] # fit到BaggingRegressor之中 clf = linear_model.LogisticRegression(C=1.0, penalty='l1', tol=1e-6) bagging_clf = BaggingRegressor(clf, n_estimators=20, max_samples=0.8, max_features=1.0, bootstrap=True, bootstrap_features=False, n_jobs=-1) bagging_clf.fit(X, y) test = df_test.filter(regex='Age_.*|SibSp|Parch|Fare_.*|Cabin_.*|Embarked_.*|Sex_.*|Pclass.*|Mother|Child|Family|Title') predictions = bagging_clf.predict(test) result = pd.DataFrame({'PassengerId':data_test['PassengerId'].as_matrix(), 'Survived':predictions.astype(np.int32)}) result.to_csv("/Users/HanXiaoyang/Titanic_data/logistic_regression_bagging_predictions.csv", index=False) ~~~ 然后你再Make a submission,恩,發現對結果還是有幫助的。 ![](https://box.kancloud.cn/2016-03-11_56e243084ed2e.png) ### 11.總結 文章稍微有點長,非常感謝各位耐心看到這里。 總結的部分,我就簡短寫幾段,出現的話,很多在文中有對應的場景,大家有興趣再回頭看看。 對于任何的機器學習問題,不要一上來就追求盡善盡美,先用自己會的算法擼一個baseline的model出來,再進行后續的分析步驟,一步步提高。 在問題的結果過程中: - **『對數據的認識太重要了!』** - **『數據中的特殊點/離群點的分析和處理太重要了!』** - **『特征工程(feature engineering)太重要了!』** - **『模型融合(model ensemble)太重要了!』** 本文中用機器學習解決問題的過程大概如下圖所示: ![機器學習解決問題的過程](https://box.kancloud.cn/2016-03-11_56e2430870e6c.png "") ### 12.關于數據和代碼 本文中的數據和代碼已經上傳至[github](https://github.com/HanXiaoyang/Kaggle_Titanic)中,歡迎大家下載和自己嘗試。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看