1. 程式人生 > >pytorch深度學習參加平安銀行數據大賽,從駕駛行為預測駕駛風險

pytorch深度學習參加平安銀行數據大賽,從駕駛行為預測駕駛風險

深度學習 數據競賽 數據分析 人工智能 算法

比賽鏈接http://www.datafountain.cn/#/competitions/284/intro
本賽題提供部分客戶1分鐘級駕駛行為數據及對應客戶的賠付率作為訓練集,包括經緯度定位及駕駛狀態等(已脫敏),參賽隊伍需要對其進行數據挖掘和必要的機器學習訓練。另外,我們會提供同期其他部分客戶的駕駛行為數據來做評測,檢測您的算法是否能準確的識別出當時客戶的駕駛風險。
與以往比賽不同的是, 由於數據安全的問題本次比賽數據訓練集跟評測集不對外公開, 參賽選手提交的不在是數據評測結果而是平臺可執行的Python代碼. 具體提交要求請參見[作品要求]. 為方便選手更好的理解數據,企業將會提供部分樣例數據.
比賽內容我不再重復,簡單來說就是從司機的駕駛行為數據預測保險賠付率。

數據示例
技術分享圖片
其中第一列為用戶的id 下載數據集合後該示例數據有一百個用戶
trip_id為用戶行程id
後面兩列為經緯度 當時行駛的海拔 速度 電話是否能接通 時間
Y值為賠付比例

讀取數據
采用pandas讀取數據
清洗數據,對於該數據表中 速度方向都不正常的值予以清除

特征工程
可以猜想一下什麽樣開車的人容易出事故,哪些路段容易出事故
對此我們做了一個前端顯示效果對比 Y =0 和 Y>0的行駛軌跡

先將數據放入數據庫,數據庫表結構如下,該數據庫主要為了取數據方便用,不必詳細考慮數據類型:

技術分享圖片
最後一列是為了將表格中的時間轉化為常用的時間,原來的時間為自從1970年1月1號開始的時間

我們從數據庫中調出不同的行駛軌跡:

這是Y=0的行駛軌跡:
技術分享圖片
這是Y>0的行駛軌跡:
技術分享圖片
如果將這兩個圖在百度地圖上詳細對比發現路段和賠付比例沒有必然聯系,暫時我們就不把經緯度作為一個特征。

駕駛數據的時間分布:
我們可以假設一個人喜歡晚上開車會比較容易出事故。
所以抽取他的定位數據在24個小時的分布作為特征。

假設有人喜歡急剎,擅長大轉彎也容易出事
所以將速度的一階差分,和速度均值,方向的一階差分作為特征

抽取特征的程序如下:

data = pd.read_csv(path_train)
train1 = []
label1 = []#分類標簽
label2 = []#回歸標簽
alluser = data[‘TERMINALNO‘].nunique()
# Feature Engineer, 對每一個用戶生成特征:
#計算特征:時間24小時分布 速度方差 速度均值 電話狀態分布
for item in data[‘TERMINALNO‘].unique():
    temp = data.loc[data[‘TERMINALNO‘] == item,:]
    temp.index = range(len(temp))
    # trip 特征
    num_of_trips = temp[‘TRIP_ID‘].nunique()
    # record 特征
    num_of_records = temp.shape[0]
    num_of_state = temp[[‘TERMINALNO‘,‘CALLSTATE‘]]
    nsh = num_of_state.shape[0]
    num_of_state_0 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==0].shape[0]/float(nsh)
    num_of_state_1 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==1].shape[0]/float(nsh)
    num_of_state_2 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==2].shape[0]/float(nsh)
    num_of_state_3 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==3].shape[0]/float(nsh)
    num_of_state_4 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==4].shape[0]/float(nsh)
    del num_of_state

    # 時間特征
    # temp[‘weekday‘] = temp[‘TIME‘].apply(lambda x:datetime.datetime.fromtimestamp(x).weekday())
    temp[‘hour‘] = temp[‘TIME‘].apply(lambda x:datetime.datetime.fromtimestamp(x).hour)
    hour_state = np.zeros([24,1])
    for i in range(24):
        hour_state[i] = temp.loc[temp[‘hour‘]==i].shape[0]/float(nsh)
    # 駕駛行為特征
    mean_speed = temp[‘SPEED‘].mean()#速度均值
    std_speed = temp[‘SPEED‘].std()#速度標準差
    mean_height = temp[‘HEIGHT‘].mean()#海拔均值
    std_height = temp[‘HEIGHT‘].std()#海拔標準差
    diffmean_direction = temp[‘DIRECTION‘].diff().mean()#方向一階差分均值
    # 添加label
    target = temp.loc[0, ‘Y‘]
    # 所有特征
    feature = [num_of_trips,num_of_records,num_of_state_0,num_of_state_1,num_of_state_2,num_of_state_3,num_of_state_4,               mean_speed,std_speed,mean_height,std_height,diffmean_direction            ,float(hour_state[0]),float(hour_state[1]),float(hour_state[2]),float(hour_state[3]),float(hour_state[4]),float(hour_state[5])
            ,float(hour_state[6]),float(hour_state[7]),float(hour_state[8]),float(hour_state[9]),float(hour_state[10]),float(hour_state[11])
            ,float(hour_state[12]),float(hour_state[13]),float(hour_state[14]),float(hour_state[15]),float(hour_state[16]),float(hour_state[17])
            ,float(hour_state[18]),float(hour_state[19]),float(hour_state[20]),float(hour_state[21]),float(hour_state[22]),float(hour_state[23])]
    train1.append(feature)
    label2.append([target])

讀取了數據之後做一個分類預測,將Y值分為三類,分別是=0 小於均值 和 其他
打標簽程序如下:

mean_y = np.mean(np.array(label2))
for label in label2:
    if label[0] == 0:
        label1.append([0])
    elif label[0] <= mean_y:
        label1.append([1])
    else:
        label1.append([2])

然後構建神經網絡:
通過我對參數的調整,以下神經網絡可以較好擬合數據,所以不再加深和加寬(增加神經元個數):
技術分享圖片
訓練代碼如下:

import torch
from torch.autograd import Variable
import torch.nn.functional as F
#數據類型轉化為tensor

x = torch.Tensor(train1)
y = torch.Tensor(label1)
y = torch.cat((y), ).type(torch.LongTensor)    # shape (100,) LongTensor = 64-bit integer
x, y = Variable(x), Variable(y)
#構造分類模型
net1 = torch.nn.Sequential(
    torch.nn.Linear(36, 13),
    torch.nn.ReLU(),
    torch.nn.Linear(13, 13),
    torch.nn.ReLU(),
    torch.nn.Linear(13, 3)
)
print(net1)  # 輸出網絡模型
optimizer = torch.optim.SGD(net1.parameters(), lr=0.002)
loss_func = torch.nn.CrossEntropyLoss()
#訓練分類模型
len_data = len(label1)
for t in range(100000):
    out = net1(x)                 # input x and predict based on x
    loss = loss_func(out, y)     # must be (1. nn output, 2. target), the target label is NOT one-hotted

    optimizer.zero_grad()   # clear gradients for next train
    loss.backward()         # backpropagation, compute gradients
    optimizer.step()        # apply gradients

    if t % 100 == 0:
        print("第" + str(t) + "次訓練")
        prediction = torch.max(out, 1)[1]
        pred_y = prediction.data.numpy().squeeze()
        target_y = y.data.numpy()
        accuracy = sum(pred_y == target_y)/(len_data+0.0)
        print(‘Accuracy=%.2f‘ % accuracy)
        if accuracy>0.95:
            # 保存模型
            torch.save(net1, ‘model/net1.pkl‘)
            print("保存分類模型")
            break
    if t ==99999:
        torch.save(net1, ‘model/net1.pkl‘)
        print("保存分類模型")

然後對所有Y>0的數據做回歸,回歸模型如下:
技術分享圖片

訓練代碼如下:

#讀取數據
data = pd.read_csv(path_train)
train1 = []#回歸訓練數據集
label2 = []#回歸標簽
alluser = data[‘TERMINALNO‘].nunique()
# Feature Engineer, 對每一個用戶生成特征:
#計算特征:時間24小時分布 速度方差 速度均值 電話狀態分布
for item in data[‘TERMINALNO‘].unique():
    temp = data.loc[data[‘TERMINALNO‘] == item,:]
    temp.index = range(len(temp))
    # trip 特征
    num_of_trips = temp[‘TRIP_ID‘].nunique()
    # record 特征
    num_of_records = temp.shape[0]
    num_of_state = temp[[‘TERMINALNO‘,‘CALLSTATE‘]]
    nsh = num_of_state.shape[0]
    num_of_state_0 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==0].shape[0]/float(nsh)
    num_of_state_1 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==1].shape[0]/float(nsh)
    num_of_state_2 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==2].shape[0]/float(nsh)
    num_of_state_3 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==3].shape[0]/float(nsh)
    num_of_state_4 = num_of_state.loc[num_of_state[‘CALLSTATE‘]==4].shape[0]/float(nsh)
    del num_of_state

    # 時間特征
    # temp[‘weekday‘] = temp[‘TIME‘].apply(lambda x:datetime.datetime.fromtimestamp(x).weekday())
    temp[‘hour‘] = temp[‘TIME‘].apply(lambda x:datetime.datetime.fromtimestamp(x).hour)
    hour_state = np.zeros([24,1])
    for i in range(24):
        hour_state[i] = temp.loc[temp[‘hour‘]==i].shape[0]/float(nsh)
    # 駕駛行為特征
    mean_speed = temp[‘SPEED‘].mean()#速度均值
    std_speed = temp[‘SPEED‘].std()#速度標準差
    mean_height = temp[‘HEIGHT‘].mean()#海拔均值
    std_height = temp[‘HEIGHT‘].std()#海拔標準差
    diffmean_direction = temp[‘DIRECTION‘].diff().mean()#方向一階差分均值
    # 添加label
    target = temp.loc[0, ‘Y‘]
    # 所有特征
    feature = [num_of_trips,num_of_records,num_of_state_0,num_of_state_1,num_of_state_2,num_of_state_3,num_of_state_4,               mean_speed,std_speed,mean_height,std_height,diffmean_direction            ,float(hour_state[0]),float(hour_state[1]),float(hour_state[2]),float(hour_state[3]),float(hour_state[4]),float(hour_state[5])
            ,float(hour_state[6]),float(hour_state[7]),float(hour_state[8]),float(hour_state[9]),float(hour_state[10]),float(hour_state[11])
            ,float(hour_state[12]),float(hour_state[13]),float(hour_state[14]),float(hour_state[15]),float(hour_state[16]),float(hour_state[17])
            ,float(hour_state[18]),float(hour_state[19]),float(hour_state[20]),float(hour_state[21]),float(hour_state[22]),float(hour_state[23])]
    if target>0:#只用大於零的做回歸
        train1.append(feature)
        label2.append([target])
#歸一化
train1 = AutoNorm(train1)
import torch
from torch.autograd import Variable
#構造回歸模型
net2 = torch.nn.Sequential(
    torch.nn.Linear(36, 4),
    torch.nn.ReLU(),
    torch.nn.Linear(4, 4),
    torch.nn.ReLU(),
    torch.nn.Linear(4,1)
    )
print(net2)  # 輸出網絡模型
#訓練回歸模型
x = torch.Tensor(train1)
y = torch.Tensor(label2)
y = torch.cat((y), ).type(torch.FloatTensor)    # shape (100,) LongTensor = 64-bit integer
y = torch.unsqueeze(y, 1)
x, y = Variable(x), Variable(y)

optimizer = torch.optim.SGD(net2.parameters(),lr=0.02)
loss_func = torch.nn.MSELoss()
for t in range(100000):
    prediction = net2(x)
    loss = loss_func(prediction,y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if t % 10 ==0:
        print("第" + str(t) + "次訓練")
        print(loss.data[0])
        #如果誤差小於%5即可停止訓練保存模型
        if loss.data[0]/mean_y<0.05:
            torch.save(net2, ‘model/net2.pkl‘)
            print("保存分類模型")
            break
    if t ==99999:
        torch.save(net2, ‘model/net2.pkl‘)
        print("保存分類模型")

這裏有兩個要註意的地方,在數據輸入神經網絡之前,為了避免不同的數據類型即特征之間量綱不一樣導致系統失靈的問題,要對所有的特征數據做歸一化:
歸一化代碼如下:

#歸一化
def width(lst):
    i = 0
    for j in lst[0]:
        i = i + 1
    return i

def AutoNorm(mat):
    n = len(mat)
    m = width(mat)
    MinNum = [9999999999] * m
    MaxNum = [0] * m
    for i in mat:
        for j in range(0, m):
            if i[j] > MaxNum[j]:
                MaxNum[j] = i[j]

    for p in mat:
        for q in range(0, m):
            if p[q] <= MinNum[q]:
                MinNum[q] = p[q]

    section = list(map(lambda x: x[0] - x[1], zip(MaxNum, MinNum)))
    print (section)
    NormMat = []

    for k in mat:
        distance = list(map(lambda x: x[0] - x[1], zip(k, MinNum)))
        value = list(map(lambda x: x[0] / x[1], zip(distance, section)))
        NormMat.append(value)
    return NormMat

另外需要說明在訓練模型的時候如何解決過擬合的問題:
學習曲線是什麽?

學習曲線就是通過畫出不同訓練集大小時訓練集和交叉驗證的準確率,可以看到模型在新數據上的表現,進而來判斷模型是否方差偏高或偏差過高,以及增大訓練集是否可以減小過擬合。
技術分享圖片

當訓練集和測試集的誤差收斂但卻很高時,為高偏差。
左上角的偏差很高,訓練集和驗證集的準確率都很低,很可能是欠擬合。
我們可以增加模型參數,比如,構建更多的特征,減小正則項。
此時通過增加數據量是不起作用的。

當訓練集和測試集的誤差之間有大的差距時,為高方差。
當訓練集的準確率比其他獨立數據集上的測試結果的準確率要高時,一般都是過擬合。
右上角方差很高,訓練集和驗證集的準確率相差太多,應該是過擬合。
我們可以增大訓練集,降低模型復雜度,增大正則項,或者通過特征選擇減少特征數。

理想情況是是找到偏差和方差都很小的情況,即收斂且誤差較小。

最後即可載入已經訓練好的神經網絡預測結果:

#載入網絡做分類預測
import torch
x = torch.Tensor(test1)
from torch.autograd import Variable
x = Variable(x)
print("載入分類網絡")
net1 = torch.load(‘model/net1.pkl‘)
out = net1(x)
prediction = torch.max(out, 1)[1]
pred_y = prediction.data.numpy().tolist()
x = torch.Tensor(test2)
x = Variable(x)
#載入網絡模型
net2 = torch.load(‘model/net2.pkl‘)
prediction = net2(x)
y =prediction.data.numpy().tolist()
print(y)

當然此處要完成這個比賽還是很不夠的,還需要綜合多個模型集成才能準確預測。

pytorch深度學習參加平安銀行數據大賽,從駕駛行為預測駕駛風險