機器學習之step by step實戰及知識積累筆記

世有因果知因求果發表於2018-06-13

資料工作者工作時間劃分

據crowdflower資料科學研究報告,資料科學工作者的時間分配主要在以下幾個領域:

首先是資料收集要佔20%左右的時間和精力,接著就是資料清洗和再組織需要佔用60%的時間。也就是說資料科學家80%的精力都花在了資料收集和預處理,從而生成能夠用於訓練模型的訓練集。真正的演算法優化和訓練只佔4%左右,另外10%左右用於特徵提取,資料再造。

正確的特徵集及足夠的資料量決定了機器學習效果的上限,演算法的優化可以無限逼近這個上限

機器學習的一般流程

獲取kaggle titanic資料集

以前通過requests以及session就能夠先登入kaggle,然後直接get到相應的dataset,但是似乎kaggle也在做相應的轉型,現在只能通過kaggle的命令列操作:

kaggle.exe datasets download -d rashigoel/titanic-machine-learning-from-disaster

#Downloading titanic-machine-learning-from-disaster.zip to C:\Users\zhenghuz\.kaggle\datasets\rashigoel\titanic-machine-learning-from-disaster
100%|▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒| 33.9k/33.9k [00:00<00:00, 105kB/s]

cleaning and organizing

這個過程具體有哪些工作要做:

經過資料收集這個過程拿到raw data後,我們首先需要通過常規的統計分析及資料視覺化工具手段來熟悉和學習這些資料,該過程我們稱之為EDA(exploratory Data Analysis),比如這些資料中有沒有缺失的欄位,有沒有明顯背離常識範圍的資料?這個階段我們發現資料問題,隨後我們要經過一個所謂"資料清洗"的過程(data munging)來解決這些問題。比如對於缺失部分欄位的資料可能需要使用某種平均值的演算法來做填充,或者徹底清除。對於明顯超範的資料做拋棄處理等。。到這裡,資料基本上是可以用的並且“乾淨”可靠的了,但是在餵給學習演算法之前,往往我們要做一個名為“特徵工程”(feature engineering)的工程過程,主要是從已有的資料attribute屬性中,我們找出那些真正對結果有重要影響的屬性作為"feature特徵",或者我們可能會組合創造出新的feature(特徵)用於演算法學習。同時我們可能要通過相關性探究發現那些重複的特徵,剔除冗餘資訊,只保留獨立的特徵資料。

在這個過程中,我們可能會用到一些交叉資料特徵探究的高階資料可視方法(advanced visulization),來輔助對資料的認識和特徵構造及提取。

numpy and pandas

numpy是python資料科學的基礎庫,其提供了非常強大高效的n維陣列。pandas基於numpy,提供了dataframe,series等資料結構,非常方便地以類似表格的形式來做資料檢索和處理。pandas也提供基於matplotlib的圖形庫,方便資料學習和特徵提取。pandas的dataframe每一行都可以看作是一個observation,每一列都可以看作是一個feature

Exploratory Data Analysis(EDA)

EDA階段我們主要探索資料集的基礎結構(basic structure), 綜合統計(summary statistics),資料分佈特性(distribution),分組特徵(grouping),交叉特徵和pivot

基礎結構(basic structure)

  • 有多少行資料(observation)?
  • 每個observation有多少個feature?
  • feature對應的資料型別
  • 每一行大概長什麼樣子?(可能要通過tail,head命令)

 首先讀取資料

import pandas as pd
import numpy as np
import os
raw_data_path = os.path.join(os.path.curdir,'data','raw')
train_file_path = os.path.join(raw_data_path,'train.csv')
test_file_path = os.path.join(raw_data_path,'test.csv')
# read the data with pandas into datafram
# train_df,test_df: pandas.core.frame.DataFrame
train_df = pd.read_csv(train_file_path,index_col='PassengerId')
test_df = pd.read_csv(test_file_path,index_col='PassengerId')

隨後使用基礎pandas資料檢查方法

train_df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 891 entries, 1 to 891
Data columns (total 11 columns):
Survived    891 non-null int64
Pclass      891 non-null int64
Name        891 non-null object
Sex         891 non-null object
Age         714 non-null float64
SibSp       891 non-null int64
Parch       891 non-null int64
Ticket      891 non-null object
Fare        891 non-null float64
Cabin       204 non-null object
Embarked    889 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 83.5+ KB

綜合統計(summary statistics)

每個observation行資料的列可以分為兩類:數值類和類別類,我們可以分別從以下幾個方面來關注:

數值類別

中值度量:(mean, meadian)

mean:簡單對所有value的算術平均,可以說是資料的中點。

median是針對mean均值的改進,如果一個資料集中有幾個非常巨大的異常資料,這時我們簡單使用均值則不能反映資料的真實特徵,但是如果我們先把這個資料集做一下排序,隨後取這個排序序列的中間的值,則能有效剔除那些巨大或者巨小的資料帶來的均值問題。也就是說如果變數的分佈是skiew偏離值比較大,則非常適合使用median值來描述該variable

mean和median指標非常適合描述quantitative型變數

離散度量(dispersion):(range, percentile, variance, standard deviation)

離散(分散)的度量指標描述資料相對中心點的分散情況。

類似的range本身也會受到巨大或者巨小異常資料的干擾,我們可以使用percentile這個來度量。

percentitle: x percentile is y意味著:x%的值都小於y這個數值。比如 50 percentile是10可以這樣解讀:50%的值都小於10這個資料, 75 percentile 是12則解讀為75%的值都小於12

反映percentitle指標常用Box-Whisker Plot來形象表達:

方差(variance)度量每個數值對均值的偏離程度,方差越小意味著資料分散性越小

方差這個指標同樣會受巨大或者巨小數值的影響,同時由於方差是偏差的平方,其單位將失去比較的意義,因此更多時候我們使用標準偏差(standard deviation)這個指標,因為該指標可以清晰的看到資料的分散情況

# dispersion measures
print('Min fare : {0}'.format(df.Fare.min())) # minimum
print('Max fare : {0}'.format(df.Fare.max())) # maximum
print('Fare range : {0}'.format(df.Fare.max()  - df.Fare.min())) # range
print('25 percentile : {0}'.format(df.Fare.quantile(.25))) # 25 percentile
print('50 percentile : {0}'.format(df.Fare.quantile(.5))) # 50 percentile
print('75 percentile : {0}'.format(df.Fare.quantile(.75))) # 75 percentile
print('Variance fare : {0}'.format(df.Fare.var())) # variance
print('Standard deviation fare : {0}'.format(df.Fare.std())) # standard deviation
# 注意:如果資料集中有空值,則pandas對應的統計資料就會出現Nan值,這時我們就必須對這些資料處理
df.Fare.plot(kind='box')
df.describe(include='all')
類別資料

總類別數(total count),唯一類別數(unique count),類別總數及其佔比(category counts and proportion),每類別統計(per category stastics)。

mode: 發生頻率最高的那個value,非常適合categorical特徵的度量

 

資料分佈特性(distribution)

單變數的分佈圖(univariate)可以使用直方圖(Histogram), Kernel Density Estimation(KDE) plot來完美呈現

直方圖histogram概念模型解釋:

kde實際上將直方圖中的頻率換算成了對應的概率。相應地,後面我們也會引入概率分佈,比如正態分佈,skewness等概念

對於標準正態分佈,沒有任何skewness,其mean均值和median的值相等,大於均值和小於均值的數值均等分佈。

如果median<mean則稱為左偏分佈,說明一定有巨大的值參雜其中或者較大的值佔比比較高;

如果median>mean則稱為右偏分佈,說明一定有巨小的值參雜其中或者較小的值佔比比較高

兩個變數的(bivariate)分佈圖可以使用散點圖Scatter plot, scatter plot非常適合描述兩個feture之間的關聯關係.例如你可能設想Age這個特徵和Height這個特徵應該具有一定的關係(pattern),我們就可以非常方便地通過這個散點圖一眼看出他們的關係。

http://seaborn.pydata.org/tutorial/categorical.html#categorical-tutorial

變數之間的相關性:pearson's R

在特徵工程中,如果兩個特徵之間具有線性相關性,那麼不應該都應用到機器學習中,而應該剔除其中一個。研究特徵之間線性相關性的指標為pearson's R.該指標可以指示是正相關,還是負相關以及相關的程度。其值在-1到1之間,0為不相關,1為最大正相關,-1為最大負相關。其計算方法:

其中

     

SDx和SDy是x和y兩個feature的標準差

 

pearson R計算示意圖:

 

 

資料的grouping

資料的grouping是指根據選擇的feature,對row進行分組聚合,分類組合後,我們可以對每個分組應用不同的數字統計計算,比如mean,median,count等

 

交叉表格(crosstabs)

crosstab非常適合於探究category型別的資料,其按照crosstab的feature羅列出每個交叉類別對應的observation次數

Pivot

pivot table是對crosstable的自然延伸,和crosstable不同的是,他使用某個feature的值來代替crosstable中表示發生次數的數值。

 

df.pivot_table(index='Sex',columns='Pclass',values='Age',aggfunc='mean')

 

Data Munging

正如前面所述,在EDA階段,我們只是對我們的資料集做了初步的探索,發現可能存在的問題,而在這個data munging階段,我們則需要解決EDA階段發現的資料問題。

  • 缺失的資料項處理(value missing);
  • 對巨大值巨小值的處理(extreme value/outliers);
  • 錯誤資料處理(erroneous values)

value missing

產生的原因:數字確實未知,資料錄入錯誤,裝置錯誤(特別是物聯網感測器資料產生了錯誤)

解決方案:

  • 刪除
  • 資料補償
    • 使用mean均值(適合於數值型feature)
    • 使用median均值(適合於數值型feature)
    • 使用最高頻的category值來填充(適合於category型別feature)
    • 使用左鄰或者右鄰資料來填充(適合於category型別feature,並且資料是有序排列的)
    • 使用某種預測模型來填充(比如線性模型)                                               

處理過程:先使用df.info()獲取missing的是哪些欄位,使用df[df.xxx.isnull()]過濾列出那些observation,結合缺失資料已有的其他資訊欄位,來推斷我們應該用什麼統計資訊來填寫

df[df.Fare.isnull()]
medianFare_in_embarkedS_pclass3 = df[(df.Pclass==3)&(df.Embarked=='S')].Fare.median()
df.Fare.fillna(medianFare_in_embarkedS_pclass3, inplace=True)

df[df.Age.notnull()].boxplot('Age',['Pclass','Sex']) # 通過boxplot羅列出Age和Pclass, Sex組合下各種分類的Age分佈情況,如果發現分佈差異巨大,則
# 非常適合選擇這些特徵作為輔助專案。相反,如果發現groupby分佈差異不大,則最好不要使用該groupby的median來做填充!!

從上面的圖中,我們可以看到Age的分佈針對Sex和Pclass來groupby的話,其值具有明顯的差異性,因此非常適合我們就使用Pclass+Sex對應的Age median值來做缺值的填充

outlier值的處理

outlier是指明顯遠遠大於或者遠遠小於正常均值的值,這些outlier在EDA階段會產生Biased analysis,比如mean, ,range, deviation都受到巨大值的巨大影響。而如果這些outlier值用於訓練模型併產生預測,則會產生Biased model。

但是outlier也往往會攜帶非常有意義的資訊,給我們以重要的啟示,因此不能一概而論,outlier存在於資料集就是不好的。

outlier值的檢測:

單變數的hitogram

單變數的Boxplot

兩個變數組合時可以使用scatter plot

outlier值處理:

  • 刪除
  • 變換transformation,比如log
  • 分段binning
  • imputation: 替換為更有意義的值(需要小心,因為outlier值可能攜帶重要資訊,你一旦替換可能丟失)

Feature Engineering(Domain knowledge + Technical Expertise):

特徵工程(feature engineering)是而對原始資料進行變換以便更能表徵特徵pattern(better representative)從而能夠建立更好預測模型的流程

  • 變換(transformation)
  • 再生新feature(依賴於領域專家知識和經驗)
  • 類別型feature的編碼:

類別型feature的編碼

 由於機器學習只能使用數值型變數,因此對於category型別的特徵feature我們必須編碼成對應的數字型資料,否則無法進入機器學習。一般地,常見的編碼方法有:

binary encoding

非常適合於只有兩個類別值的category特徵編碼,比如性別:男和女,中國人和外國人。。這種直接用is_male, is_chinese其值編碼為1,0

label encoding

非常適合於有多個類別值,同時其類別有序列意義的category feature編碼,比如收入:底,中,高,成績:不及格,及格,良好,優秀,滿分。這種型別的資料本身直接用0,1,2,3,來編碼其類別值是有意義的。

one-hot encoding

如果待編碼的類別資料本身沒有序列意義,則最安全的編碼方式就是這種one-hot編碼方式了,其根據cate類別值分別建立is_catea,is_cateb,is_catec等。

advanced data visulization

matplotlib相比於pandas視覺化有更強大的功能,比如支援subplot,一個visulization同時展示多張圖片

機器學習模型訓練

有了資料後,我們就可以根據問題型別預先設定一個模型,比如logistic regression, SVM,神經網路,將這些資料應用到學習演算法中,找到能夠較好擬合這些資料的模型。

這時我們就有了一個trained model,再將test data輸入到這個已訓練模型中,根據預測值和實際值的差異得出評價指標(比如邏輯迴歸的正確率),如果不滿意,則可以通過調整對應模型的超引數繼續重新訓練得到一個較好結果的模型。或者我們重新選擇模型訓練得到一個完全重構的已訓練模型,直到對模型預測指標滿意為止。

一般的,我們在應用資料建立訓練模型之前我們可以先設定一個基礎模型,該模型的輸出對於分類器來說,我們就選擇那個最高出現頻率的類別,比如對於泰坦尼克號存活率如果大部分都是不能倖存,比如60%,那麼我們就將基礎模型設定輸出為不能倖存。那麼如果我們新訓練出來的模型準確率小於 60%,那麼就表明我們的模型是完全失敗的,預測模型是沒有任何意義的!!!

還有一點需要注意的是對於邏輯迴歸,我們要檢視一下0和1輸出的佔比,如果嚴重失衡的話,我們有必要處理(im-balanced classifications)

https://www.analyticsvidhya.com/blog/2016/03/practical-guide-deal-imbalanced-classification-problems/

 

import pandas as pd
import numpy as np
import os
processed_data_path = os.path.join(os.path.curdir,'data','processed')
train_file_path = os.path.join(processed_data_path,'train.csv')
test_file_path = os.path.join(processed_data_path,'test.csv')
train_df = pd.read_csv(train_file_path,index_col='PassengerId')
test_df = pd.read_csv(test_file_path,index_col='PassengerId')
X = train_df.loc[:, 'Age':].as_matrix().astype('float')
y = train_df['Survived'].ravel()
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,random_state=0)

 下面我們建立一個dummy model用作baseline model:

import sklearn
from sklearn.dummy import DummyClassifier
#create dummy model
model_dummy = DummyClassifier(strategy='most_frequent',random_state=0)
#train dummy model
model_dummy.fit(X_train,y_train)
model_dummy.get_params()
print('score for dummy classifier is: {0:.2f}'.format(model_dummy.score(X_test,y_test))) # 0.61

logistic model

邏輯迴歸模型可以簡單看作是線性迴歸模型應用在sigmoid函式後實現非線性後形成的model,輸出值為概率值0到1

我們來看看Logistic model對應的程式碼

from sklearn.linear_model import LogisticRegression
# create model
model_lr_1 = LogisticRegression(random_state=0)
# train the model
model_lr_1.fit(X_train,y_train)
# evaluate model
print('score for logistic regression -version 1: {0:.2f}'.format(model_lr_1.score(X_test,y_test))) # 0.83
model_lr_1.intercept_ # array([1.10324257]) 截距
model_lr_1.coef_ # 對應每個特徵的係數
'''
array([[-0.02840734,  0.00455631, -0.50017007,  0.61922839, -0.81414743,
         0.12823264, -0.17253859, -0.39355489,  0.52215008,  1.09939125,
         0.40346551, -0.18369316, -0.30021028,  0.37253571,  0.73070686,
         0.16297325,  0.2474635 ,  0.27998253,  0.41282329,  0.28258585,
         1.21850069,  0.56334182, -1.44612508,  1.07146232, -0.11345497,
        -0.47306807,  0.49202885,  0.46214499,  0.14906873,  0.96558544,
         0.48281793, -0.3451608 ]])
'''

分類器效果評估(precision, recall,accuracy)

Precision:查準率(預測為1中,真值為1的比例(概率)) $Precision = \frac{TP}{TP+FP} = P(y_i=1|\widehat{y_i}=1)$

Recall:查全率(真值為1中,預測值也為1的比例(概率))$Recall = \frac{TP}{TP+FN} = P(\widehat{y_i}=1|y_i=1)$

Precision和Recall都是針對Positive事件的,往往是我們感興趣的事件,相對地,我們也可以取Negative事件的指標。

總準確率: (所有預測正確的比例(含1,0))$accuracy = \frac{TP+TN}{TP+TN+FP+FN} = P(\widehat{y_i}==y_i)$

 

模型調優(model tuning)

在通過sklearn logistic regression model訓練後我們得到第一版trained model,效果還不錯達到83%的準確率。下面我們看看還有哪些可以繼續優化的地方來不斷優化模型,有一個更好的呈現效果。

underfitting vs overfitting

underfitting(欠擬合):模型對訓練集的擬合不足,也就是說即使是訓練集,模型本身都不能準確預測

overfitting(過擬合):模型過於複雜,完全是基於training data的擬合,無法泛化

regularization

我們知道模型如果過於複雜容易產生過擬合,如果太簡單容易產生欠擬合,針對邏輯迴歸模型我們有一些引數來調整模型的複雜度等引數。

比如C參數列徵複雜度,數字越大越複雜,penalty是正規懲罰項。除了邏輯迴歸Model,越複雜的模型類別,其超引數越多。資料科學家很重要的一個

工作是尋求一組最優化的超引數組合,使得model預測效果最佳。

hyperparameter tuning

超引數優化的基本思路是將引數組合做成表格,分別針對這些不同的組合來訓練模型給出效能指標,後面我們選擇一個最佳的指標來。

cross validation

 

樣例程式碼:

model_lr_base = LogisticRegression(random_state=0)
from sklearn.model_selection import GridSearchCV
hparameters = {'C':[1.0,10.0,50.0,100.0,1000.0],'penalty':['l1','l2']}
clf = GridSearchCV(model_lr_base,param_grid=hparameters,cv=3) # cv表示3 K FOLD CROSS VALIDATION
clf.fit(X_train,y_train)
clf.best_params_  # {'C': 1.0, 'penalty': 'l1'}

Feature normalization and Feature Standadization(特徵正規化)

如果輸入特徵資料的range變化很大,很有可能對訓練出來的模型產生偏離效果,我們最好在訓練模型之前將輸入資料正規化處理,使得所有feature都在0到1的範圍內,並且具有正態分佈的特徵(期望為0,方差為1)

更換其他型別的邏輯迴歸

幾個概念:centered, standardized,normalized:

model_lr_base = LogisticRegression(random_state=0)
hparameters = {'C':[1.0,10.0,50.0,100.0,1000.0],'penalty':['l1','l2']}
clf = GridSearchCV(model_lr_base,param_grid=hparameters,cv=3) # cv表示3 K FOLD CROSS VALIDATION
clf.fit(X_train_scaled,y_train)
clf.best_params_

實驗結果表明正規化引數後並未對預測結果產生明顯的改進,反而有點下降

模型持久化和API

model api要實現的功能是:接收raw data,並且處理他,做出預測,並且返回json

Feature normalization

機器學習實戰中遇到的問題解決方案

Traceback (most recent call last):
  File "C:/Users/Administrator/devenvironment/Code/intro_ds/ch04-linear/simple_example/linear_stat.py", line 14, in <module>
    import statsmodels.api as sm
  File "C:\Users\Administrator\Anaconda3\lib\site-packages\statsmodels\api.py", line 5, in <module>
    from . import regression
  File "C:\Users\Administrator\Anaconda3\lib\site-packages\statsmodels\regression\__init__.py", line 1, in <module>
    from .linear_model import yule_walker
  File "C:\Users\Administrator\Anaconda3\lib\site-packages\statsmodels\regression\linear_model.py", line 43, in <module>
    from scipy.stats.stats import ss
ImportError: cannot import name 'ss'
解決方案
conda upgrade statsmodels/pip install statsmodels --upgrade
ImportError: No module named 'tensorflow'

解決方案:

conda install tensorflow

SKLearn演算法庫選擇指南

https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html

 

 

相關文章