機器學習建模全流程及資料總結——以文本分類風控建模實驗爲例(1)

 

場景爲根據用戶手機短信的風控建模,以此爲例總結一套數據處理,特徵工程,實驗及結果分析的全流程珍貴的代碼,並把常用的工具包函數記錄清楚,免得每次用都要百度。

系列文章包括以下部分:

  1. 數據預處理
  2. 特徵工程
  3. 數據存儲
  4. 模型實驗
  5. 結果分析

 

一. 數據預處理

1. 關於數據讀取

原始數據可能以各種格式的文件和各種邏輯關係存儲的,常用的讀取方式包括:open(file, mode)函數,pandas的pd.read_csv等。

open函數讀取時,要注意每一行數據的解析方法,並存儲爲 list ,dict 等進一步格式。

pandas的工具讀取後爲DataFrame格式,還需熟悉pandas那一套操作方法。

個人之前兩篇關於讀文件數據的文章:

python的目錄/文件/文本處理概述(入門)

讀取多個文件並合併爲一個dataframe對象

 

2. 清洗處理

這一部分要進行的工作比較雜,總歸來說目的是 形成規整的數據,便於提取特徵

因此我們可能需要 清洗髒數據,處理缺失值,處理異常值,如果數據源有多個,還要進行一些數據查詢和關聯,可以用哈希來關聯,數據不多的情況還是建議用DataFrame 對象來處理。

數據量大的情況,可以用df試試看一下內存佔用,然後進行優化。

傳送門: df對象內存優化,該博客參考的文章是 https://www.kaggle.com/arjanso/reducing-dataframe-memory-size-by-65/notebook  用此方法優化了50%的內存

 

二. 特徵工程

總結:sklearn機器學習之特徵工程

數據一般有兩種形式: 數字和文字。下面我們以短信這個問題爲例,介紹文本數據的特徵工程。

1. 文本分詞

這時必須用到的兩款工具 jieba分詞和nltk包.

中文分詞一般用jieba,網上教程很多,使用方法也很簡單。簡明 jieba 中文分詞教程

最好是對分詞的原理和幾種分詞模式有一定的瞭解,才能得到適合後面工作的詞語。

英文分詞原理上就圖森破了,下面有兩種方法

import nltk.tokenize as tk
tokenizer = tk.WordPunctTokenizer()
tokens = tokenizer.tokenize("Your loan is due 19/02."). # ['Your', 'loan', 'is', 'due', '19', '/', '02', '.']
# 還有切句子的方法 tokenizer.tokenize_sents()

# 這一方法可能切分粗糙一些
from nltk import word_tokenize
tokens = word_tokenize("Your loan is due 19/02.") #['Your', 'loan', 'is', 'due', '19/02', '.']

# 停用詞
from nltk.corpus import stopwords
stopwds = stopwords.words("english")

需要注意的是,分詞後尤其是英文,需要一些額外的處理,比如轉換小寫,比如詞幹還原等,以及停用詞/標點符號等去除。

2. 特徵提取

2.1 文本的特徵提取

分詞後每個詞的出現都是特徵,一元詞沒有保留語序,因此還可以提取2-gram (N-gram)保留詳盡兩個詞,豐富我們提取到特徵的數量,以便後續進一步篩選。這裏介紹兩個工具。

  • sklearn中 CountVectorizer
  • nltk中BigramCollocationFinder
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()                    
CountVectorizer(analyzer=...'word', binary=False, decode_error=...'strict',
 dtype=<... 'numpy.int64'>, encoding=...'utf-8', input=...'content',
 lowercase=True, max_df=1.0, max_features=None, min_df=1,
 ngram_range=(1, 1), preprocessor=None, stop_words=None,
 strip_accents=None, token_pattern=...'(?u)\\b\\w\\w+\\b',
 tokenizer=None, vocabulary=None)

corpus = [
        'This is the first document.',
        'This is the second second document.',
        'And the third one.',
        'Is this the first document?',
        ]
X = vectorizer.fit_transform(corpus)

另外還有 

from sklearn.feature_extraction.text import HashingVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer

參數衆多,但是竊以爲並不好用。

sklearn中對於文本的特徵提取可以參考 https://sklearn.apachecn.org/docs/master/39.html#523-文本特徵提取

該工具可以直接將文本向量化,或是one-hot或者tf-idf的特徵。對於初學或簡單實驗的可以直接一用,十分省事。

如果要調整參數,加入一些其他步驟的精細實驗,感覺還是可以自己實現每一步,包括建立詞表,統計詞頻,篩選特徵,再向量化。在鄙人的代碼中,中間進行了一步特徵詞篩選,因此不方便直接用這個工具進行transform,因此是自己一步步實現的。

import nltk
from nltk.collocations import *
words = ["cat", "run", "dog", "run"]

tflist = nltk.FreqDist(words)

bigram_measures = nltk.collocations.BigramAssocMeasures()
finder = BigramCollocationFinder.from_words(words)

如果是自己一步步提取特徵,對於一元詞,可以直接用Counter,上述代碼裏的FreqDist道理一樣,統計詞及頻率,作爲後面工作的基礎。二元詞也可以自己寫一個,然後統計詞頻。

對於文本來說,不用深度學習技術的特徵提取,就統計詞頻即可,然後原文本的序列關係就拋棄掉了。我們只需要保留一個詞頻的字典即可。

2.2 數值特徵提取

對於數值的特徵提取,可以進行一些特徵的構造,加減乘除,指數對數,對於一些有實際業務意義的特徵,可以再根據業務構造有意義的特徵。

# 生成多項式特徵
preprocessing.PolynomialFeatures()

# 自定義轉換器
preprocessing.FunctionTransformer()
transformer = FunctionTransformer(np.log1p, validate=True)

2.3 挖掘特徵

簡單來說可能是這樣的,對於一段文本string1,除了前述分詞後的特徵,可以制定一個確定的邏輯,相當於一個函數f,f(string) 可以得到one-hot特徵,數值特徵,布爾類型特徵等。

 

3.特徵預處理

https://sklearn.apachecn.org/docs/master/40.html#53-預處理數據

這裏的預處理,指對模型輸入特徵值的一些轉換,是爲了更好的爲下游服務。

主要針對一般線性模型的輸入值,做一個預處理,內容及相關工具如下:

​# 1. 標準化,去均值和方差按比例縮放
“”“
在機器學習算法的目標函數(例如SVM的RBF內核或線性模型的l1和l2正則化),許多學習算法中目標函數的基礎都是假設所有的特徵都是零均值並且具有同一階數上的方差。如果某個特徵的方差比其他特徵大幾個數量級,那麼它就會在學習算法中佔據主導位置,導致學習器並不能像我們說期望的那樣,從其他特徵中學習。
”“”
​preprocessing.MinMaxScaler()
preprocessing.MaxAbsScaler()
​preprocessing.StandardScaler(copy=True, with_mean=True, with_std=True)

# 2. 非線性變換
“”“
有兩種類型的轉換是可用的:分位數轉換和冪函數轉換。分位數和冪變換都基於特徵的單調變換,從而保持了每個特徵值的秩。
分位數變換根據公式G-1(F(X))將所有特徵置於相同的期望分佈中,其中F是特徵的累積分佈函數,G-1是期望輸出值分佈G的分位數函數.這個公式基於以下兩個事實:
如果X是具有連續累積分佈函數F的隨機變量,那麼F(X)均勻分佈在[0,1]
如果U是在[0,1]上的隨機分佈,那麼G-1(U)有分佈G.
通過執行秩變換,分位數變換平滑了異常分佈,並且比縮放方法受異常值的影響更小。但是它的確使特徵間及特徵內的關聯和距離失真了。
冪變換則是一組參數變換,其目的是將數據從任意分佈映射到接近高斯分佈的位置。
”“”
# 映射到均勻分佈
preprocessing.QuantileTransformer(random_state=0)

# 映射到高斯分佈
preprocessing.PowerTransformer(method='box-cox', standardize=False)

# 3. 歸一化
preprocessing.normalize(X, norm='l2')

# 4. 類別特徵編碼
# 序數編碼,一般不可用,我們就用下面的獨熱編碼即可
preprocessing.OrdinalEncoder()
# 獨熱碼或dummy encoding
OneHotEncoder(categorical_features=None, categories=None,
       dtype=<... 'numpy.float64'>, handle_unknown='error',
       n_values=None, sparse=True)

# 5. 離散化 (Discretization)
“”“
將連續特徵劃分爲離散特徵值的方法。 某些具有連續特徵的數據集會受益於離散化,因爲 離散化可以把具有連續屬性的數據集變換成只有名義屬性(nominal attributes)的數據集。 (譯者注: nominal attributes 其實就是 categorical features, 可以譯爲 名稱屬性,名義屬性,符號屬性,離散屬性 等)
”“”
# 使用k個等寬的bins把特徵離散化
preprocessing.KBinsDiscretizer(n_bins=[3, 2, 2], encode='ordinal')
# 將數值特徵用閾值過濾得到布爾值
preprocessing.Binarizer(copy=True, threshold=0.0)

這是一些常用的用於特徵預處理的工具,用哪個怎麼用,需要許多建模經驗,結合數據特點和下游模型。。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章