Spark與機器學習----數據的獲取、處理與準備

1. 數據獲取

常用公開數據集:

  • UCL機器學習知識庫: 包括近300個不同大小和類型的數據集,可用於分類、迴歸、聚類和推薦系統任務。數據集列表位於: http://archive.ics.uci.edu/ml/。
  • Amazon AWS公開數據集: 包含的通常是大型數據集,可通過Amazon S3訪問。相關信息可參見: http://aws.amazon.com/publicdatasets/。
  • Kaggle: 這裏集合了Kaggle舉行的各種機器學習競賽所用的數據集。下載:http://www.kaggle.com/competitions。
  • KDnuggets: 這裏包含一個詳細的公開數據集列表,其中一些上面提到過的。該列表位於: http://www.kdnuggets.com/datasets/index.html。

2. 探索與可視化數據

載入數據,通過IPython notebook的可視化編程,來分析數據結構,統計數據分佈,瞭解數據的缺失性,爲數據的整理和清洗做前期準備工作。
可通過Matplotlib對數據生成統計圖表,瞭解數據的分佈情況。
數值型數據通過統計:數量、最大值、最小值、平均值、中值、方差等數學指標瞭解其特徵
類別信數據通過統計:數量、類別分佈等信息瞭解其特徵。
數據探索可通過Spark中對RDD操作API函數實現。

3. 數據的處理與轉換

爲讓原始數據可用於機器學習算法,需要先對其進行清理,並可能需要將其進行各種轉換,之後才能從轉換後的數據裏提取有用的特徵。數據的轉換和特徵提取聯繫緊密。某些情況下,一些轉換本身便是特徵提取的過程。一般來說,現實中的數據會存在信息不規整、數據點缺失和異常值問題。理想情況下,我們會修復非規整數據。大致的處理方法如下:

  • 過濾掉或刪除非規整或有值缺失的數據: 這通常是必須的,但的確會損失這些數據裏那些好的信息。
  • 填充非規整或缺失的數據: 可以根據其他的數據來填充非規整或缺失的數據。方法包括用零值、全局期望或中值來填充,或是根據相鄰或類似的數據點來做插值(通常針對時序數據)等。選擇正確的方式並不容易,它會因數據、應用場景和個人經驗而不同。
  • 對異常值做魯棒處理: 異常值的主要問題在於即使它們是極值也不一定就是錯的。到底是對是錯通常很難分辨。異常值可被移除或是填充,但的確存在某些統計技術(如魯棒迴歸)可用於處理異常值或是極值。
  • 對可能的異常值進行轉換: 另一種處理異常值或極值的方法是進行轉換。對那些可能存在異常值或值域覆蓋過大的特徵,利用如對數或高斯覈對其轉換。這類轉換有助於降低變量存在的值跳躍的影響,並將非線性關係變爲線性的。

4. 數據中提取有用特徵

特徵(feature) 指那些用於模型訓練的變量。特徵可以概括地分爲如下幾種:

  • 數值特徵( numerical feature): 這些特徵通常爲實數或整數,比如年齡。
  • 類別特徵( categorical feature): 它們的取值只能是可能狀態集合中的某一種。我們數據集中的用戶性別、職業或電影類別便是這類。
  • 文本特徵( text feature): 它們派生自數據中的文本內容,比如名稱、描述或是評論。
  • 其他特徵: 大部分其他特徵都最終表示爲數值。比如圖像、視頻和音頻可被表示爲數值數據的集合。地理位置則可由經緯度或地理散列(geohash)表示。

4.1. 數值特徵

原始的數值和一個數值特徵之間的區別是什麼?實際上,任何數值數據都能作爲輸入變量。但是,機器學習模型中所學習的是各個特徵所對應的向量的權值。這些權值在特徵值到輸出或是目標變量(指在監督學習模型中)的映射過程中扮演重要角色。由此我們會想使用那些合理的特徵,讓模型能從這些特徵學到特徵值和目標變量之間的關係。比如年齡就是一個合理的特徵。年齡的增加和某項支出之間可能就存在直接關係。

當數值特徵仍處於原始形式時,其可用性相對較低,但可以轉化爲更有用的表示形式。位置信息便是如此。若使用原始位置信息(比如用經緯度表示的),我們的模型可能學習不到該信息和某個輸出之間的有用關係,這就使得該信息的可用性不高。然而若對位置進行聚合或挑選後(比如聚焦爲一個城市或國家),便容易和特定輸出之間存在某種關聯了。

4.2. 類別特徵

當類別特徵仍爲原始形式時,其取值來自所有可能取值所構成的集合而不是一個數字,故不能作爲輸入。
名義(nominal)變量,其各個可能取值之間沒有順序關係(比如職業)
有序(ordinal)變量,其值存在順序關係(比如評級)
將類別特徵表示爲數字形式,常可藉助k之1(1-of-k)方法進行。將名義變量表示爲可用於機器學習任務的形式,會需要藉助如k之1編碼這樣的方法。有序變量的原始值可能就能直接使用,但也常會經過和名義變量一樣的編碼處理。

假設變量可取的值有k個。如果對這些值用1到k編序,則可以用長度爲k的二元向量來表示一個變量的取值。在這個向量裏,該取值對應的序號所在的元素爲1,其他元素都爲0。

4.3. 派生特徵

從現有的一個或多個變量派生出新的特徵常常是有幫助的。理想情況下,派生出的特徵能比原始屬性帶來更多信息。從原始數據派生特徵的例子包括計算平均值、中位值、方差、和、差、最大值或最小值以及計數。數值特徵到類別特徵的轉換也很常見,比如劃分爲區間特徵。進行這類轉換的變量常見的有年齡、地理位置和時間。

def assign_tod(hr):
    times_of_day = {
        'morning' : range(7, 12),
        'lunch' : range(12, 14),
        'afternoon' : range(14, 18),
        'evening' : range(18, 23),
        'night' : range(23, 7)
    }
    for k, v in times_of_day.iteritems():
        if hr in v:
            return k
timestamps = rating_data.map(lambda fields: int(fields[3]))
hour_of_day = timestamps.map(lambda ts: extract_datetime(ts).hour)
time_of_day = hour_of_day.map(lambda hr: assign_tod(hr))
time_of_day.take(5)

4.4. 文本特徵

從某種意義上說,文本特徵也是一種類別特徵或派生特徵。文本的處理方式有很多種。自然語言處理便是專注於文本內容的處理、表示和建模的一個領域。
詞袋法將一段文本視爲由其中的文本或數字組成的集合,其處理過程如下:

  • 分詞( tokenization): 首先會應用某些分詞方法來將文本分隔爲一個由詞(一般如單詞、數字等)組成的集合。
  • 刪除停用詞( stop words removal): 之後,它通常會刪除常見的單詞,比如the、 and和but(這些詞被稱作停用詞)。
  • 提取詞幹( stemming): 下一步則是詞幹的提取。這是指將各個詞簡化爲其基本的形式或者幹詞。常見的例子如複數變爲單數(比如dogs變爲dog等)
  • 向量化( vectorization): 最後一步就是用向量來表示處理好的詞。二元向量可能是最爲簡單的表示方式。它用1和0來分別表示是否存在某個詞。從根本上說,這與之前提到的k之1編碼相同。
# 下面用簡單空白分詞法將標題分詞爲詞
title_terms = movie_titles.map(lambda t: t.split(" "))

# 下面取回所有可能的詞,以便構建一個詞到序號的映射字典
all_terms = title_terms.flatMap(lambda x: x).distinct().collect()
idx = 0
all_terms_dict = {}
for term in all_terms:
    all_terms_dict[term] = idx
    idx +=1

#以下代碼達到上面相同效果
all_terms_dict2 = title_terms.flatMap(lambda x: x).distinct().zipWithIndex().collectAsMap()

# 該函數輸入一個詞列表,
# 並用k之1編碼類似的方式將其編碼爲一個scipy稀疏向量
def create_vector(terms, term_dict):
    from scipy import sparse as sp
    num_terms = len(term_dict)
    x = sp.csc_matrix((1, num_terms))
    for t in terms:
        if t in term_dict:
            idx = term_dict[t]
            x[0, idx] = 1
    return x

#代碼中用Spark的broadcast函數來創建了一個包含詞字典的廣播變量。
#現實場景中該字典可能會極大,故適合使用廣播變量。
all_terms_bcast = sc.broadcast(all_terms_dict)
term_vectors = title_terms.map(lambda terms: create_vector(terms, all_terms_bcast.value))

4.5. 正則化特徵

在將特徵提取爲向量形式後,一種常見的預處理方式是將數值數據正則化(normalization)。其背後的思想是將各個數值特徵進行轉換,以將它們的值域規範到一個標準區間內。正則化的方法有如下幾種:

  • 正則化特徵:這實際上是對數據集中的單個特徵進行轉換。比如減去平均值(特徵對齊)或是進行標準的正則轉換(以使得該特徵的平均值和標準差分別爲0和1)
  • 正則化特徵向量:這通常是對數據中的某一行的所有特徵進行轉換,以讓轉換後的特徵向量的長度標準化。也就是縮放向量中的各個特徵以使得向量的範數爲1(常指一階或二階範數)。
#向量正則化可通過numpy的norm函數來實現
np.random.seed(42)
x = np.random.randn(10)
norm_x_2 = np.linalg.norm(x)
normalized_x = x / norm_x_2

#Spark在其MLlib機器學習庫中內置了一些函數用於特徵的縮放和標準化。
#它們包括供標準正態變換的StandardScaler,
#以及提供與上述相同的特徵向量正則化的Normalizer。
from pyspark.mllib.feature import Normalizer
normalizer = Normalizer()
vector = sc.parallelize([x])
normalized_x_mllib = normalizer.transform(vector).first().toArray()

4.6. 用軟件包提取特徵

我們可以通過開發的軟件包,藉助其中完善的工具箱來實現特徵的處理和提取,以及向量表示。特徵提取可藉助的軟件包有scikit-learn、gensim、 scikit-image、 matplotlib、 Python的NLTK、 Java編寫的OpenNLP以及用Scala編寫的Breeze和Chalk。

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