1. 程式人生 > >服務價值特徵分佈的演化預測

服務價值特徵分佈的演化預測

問題描述:挖掘不同服務價值特徵(評價實體)並計算權重值,生成在各個時間段的分佈情況。通過已知的時間序列分佈,預測未來某個時刻的服務價值特徵權重值。

資料的形式如下圖所示:


每一行表示一個服務價值特徵(評價實體),每一列表示一段時間,比如某一個月、某一天等等,每一列是嚴格按照時間順序由遠及近排列的。假設我們的目標是預測上圖中month_j+1這一列中的所有數值(也可以預測更加久遠的,不過意義不大且精度差)。

解決思路

這個問題我一般看大資料競賽中有沒有相似的賽題,很顯然是有的,只不過資料不一樣,不過要做的事情是一樣的,也就是進行數值型預測。

由於未來的那個時間段並沒有發生,也就是說沒有任何的特徵。一般有兩種解決思路:1)基於機器學習的迴歸預測(滑窗法) 2)基於時間序列的預測。

技術方案

1)基於機器學習的迴歸預測(滑窗法):每個樣本是服務價值特徵,每個feature是時間,樣本在每個feature下的值為該時間段內這個服務價值特徵積累的權重值總和。滑窗法形如下圖所示:


月份m1~m4已知,求m5的預測值。先建模,假設滑窗大小=3,則使用m1~m3作為輸入X,m4作為目標值y,擬合一套模型。再用m2~m4作為輸入X,m5作為目標值y,將X扔進剛才建好的模型中就可以輸出m5的預測值了。當然還有很多細節問題需要在實驗中解決,比如滑窗大小的選擇,迴歸模型的選擇(目前暫定隨機森林和GBRT),模型引數等。

2)基於時間序列的預測:這個方法是在“阿里音樂流行趨勢預測”競賽中看到的,適用於一維時間序列的預測,主要應用在金融分析中比較多。使用的模型是ARIMA模型。優點是這套模型搞時間序列預測很專業,時間複雜度低,一次性預測未來好長時間的值。缺點是一維時間序列資料要先經過各種亂七八糟的變換,讓資料足夠規範化,才能進行時間序列預測。個人並不擅長這個,所以目前首選機器學習模型,搞好了有剩餘時間了再搞一下ARIMA模型看看有沒有驚喜,也許搞個雙模型bagging融合也會是個亮點呢。做出的demo像下圖中這樣,綠色部分是預測值:

--------------------  3.13更新  --------------------

做完好幾天了,一直沒時間記錄一下,我整理整理,後面有圖和原始碼。      

這次試驗還是選用上圖那套具有不規律週期波動的demo資料,用機器學習的辦法進行時間序列滾動預測,先說說結果吧,效果我個人感覺還算滿意,做交叉驗證的時候感覺確實能夠預測未來的一些變化趨勢。這個demo資料我個人感覺是一套很“複雜”的資料了,週期不太規律並且上下起伏非常的大,個別時間段還會出現波動比較爆炸的情況。如果在這套demo資料裡預測的還可以,那在做服務特徵分佈預測的時候回輕鬆不少。

建模方法

這套滾動預測建模方法我是在天池大資料論壇裡看到的,樓主說的也不詳細,只是說了“滾動預測”,也沒說說細節估計也是嫌麻煩沒說。我只能開動腦筋想一下是咋“滾動”的。是這樣:

以上面的資料為例,假設視窗大小為10,步進為1,首先選x1~x10這10個時間序列資料做X,再拿x11做目標值y建立第一個樣本(為啥這麼做,以及那倆引數咋選後面再說)上面一共有90個數據,如果拿10個數據作測試集的話,剩下80個數據做訓練集。這80個具有嚴格先後順序的數會被構建成69個樣本,第一個樣本上面說了,最後一個樣本是x69~x79為X,x80為y,為啥是69個自己琢磨一下就行就不贅述了。

現在訓練集就成了一個二維矩陣,每一行是一個樣本,每一列是一個特徵,這個特徵是時間特徵,可以理解為時間戳。我用機器學習裡的迴歸模型進行建模的目的就是為了尋找資料波動和目標值之間的聯絡充分學習資料的波動情況!

我把這69個樣本扔進GBDT和RF裡訓練了一下,全部預設引數(夠用了),那麼機器現在已經比較充分的學習到這套時間序列資料的波動情況了。

下面說說滑窗和步進的選取:滑窗大小表示的是這個樣本時間序列的“寬度”,太窄波動範圍太小了,太寬又會導致樣本比較少無法得到充分的訓練,我覺得通過交叉驗證選取最佳滑窗值比較好,步進是下一個滑窗離上一個滑窗滑動的距離,預設為1我覺得行,不同資料不同對待,依舊交叉驗證選取最佳值。

由於我要預測10個數據,而不是一個,所以要把最新預測出來的那個資料扔進訓練集再訓練一遍才能預測第二個數。也就是說,預測的誤差是逐漸擴大的,在實驗中我也驗證了這一點,時間序列資料的預測並不能預測的太遠,再強的大牛也做不到。預測最近一段時間的還是可以的。

預測結果(隨機森林RF)


預測結果(迭代迴歸樹GBRT)

RF滑窗在20的時候預測的比較好,GBRT滑窗在50的時候預測的比較好,應該跟模型特質有關。

普遍問題:最後一個時間點的預測應該是向下波動的,預測成了向上波動,我覺得是個別情況,畢竟週期波動性資料預測難就難在這:你不知道什麼時候往上走還是往下走,只要錯開一點那趨勢就完全反了還不如不預測。

原始碼:

# coding: utf-8

#
#  先選用 http://blog.csdn.net/u010414589/article/details/49622625 教程中的時間序列資料,用機器學習模型做個baseline
#


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
import copy

dta=[10930,10318,10595,10972,7706,6756,9092,10551,9722,10913,11151,8186,6422,
6337,11649,11652,10310,12043,7937,6476,9662,9570,9981,9331,9449,6773,6304,9355,
10477,10148,10395,11261,8713,7299,10424,10795,11069,11602,11427,9095,7707,10767,
12136,12812,12006,12528,10329,7818,11719,11683,12603,11495,13670,11337,10232,
13261,13230,15535,16837,19598,14823,11622,19391,18177,19994,14723,15694,13248,
9543,12872,13101,15053,12619,13749,10228,9725,14729,12518,14564,15085,14722,
11999,9390,13481,14795,15845,15271,14686,11054,10395]


# 先觀察一下資料
'''
dta = pd.Series(dta) # 做成時間序列模式
dta.index = pd.Index(sm.tsa.datetools.dates_from_range('2001','2090'))
dta.plot(figsize=(12,8)) # 最佳尺寸(20,11.5)
plt.show()'''


# 使用 RF/GBRT 預測一下最後5個年份的資料
train = [10930,10318,10595,10972,7706,6756,9092,10551,9722,10913,11151,8186,6422,
6337,11649,11652,10310,12043,7937,6476,9662,9570,9981,9331,9449,6773,6304,9355,
10477,10148,10395,11261,8713,7299,10424,10795,11069,11602,11427,9095,7707,10767,
12136,12812,12006,12528,10329,7818,11719,11683,12603,11495,13670,11337,10232,
13261,13230,15535,16837,19598,14823,11622,19391,18177,19994,14723,15694,13248,
9543,12872,13101,15053,12619,13749,10228,9725,14729,12518,14564,15085]
test = [14722,
11999,9390,13481,14795,15845,15271,14686,11054,10395]

windows = [45,47,49,50,52,55]# 設定一個滑窗
plt.figure(figsize=(15, 11.5))

for w,window in enumerate(windows):

    train_new = copy.deepcopy(train)
    output = []

    for round in range(20):

        train_X = []
        train_y = []

        for i in range(len(train_new)-window-1):

            train_X.append(train_new[i:i+window])
            train_y.append([train_new[i+window+1]])

        train_X = np.array(train_X)
        train_y = np.array(train_y)
        model = GradientBoostingRegressor()  # GBRT模型
        # model = RandomForestRegressor()  # RF模型
        model.fit(train_X, train_y)
        test_X = train_new[-window:]
        predicted = model.predict(test_X)
        print predicted[0]
        output.append(predicted)
        train_new.append(predicted[0])
    if w <= 1:
        plt.subplot2grid((3,2), (0,0+w))
    elif w<= 3:
        plt.subplot2grid((3,2), (1,w-2))
    else:
        plt.subplot2grid((3,2), (2,w-4))
    plt.title('Time series forecasting.  Step=1, Window=' + str(window))
    # plt.xlabel('Date')
    plt.ylabel('Frequency')
    plt.plot(train + output, color='r', label='forcast') #  預測值
    plt.plot(train + test, label='original') # 原始值
    plt.legend()
    plt.grid(True)

plt.show()