給定訓練集spam_train.csv,要求根據每一個ID各類屬性值來判斷該ID對應角色是Winner仍是Losser(收入是否大於50K),這是一個典型的二分類問題。html
訓練集介紹:算法
(1)、CSV文件,大小爲4000行X59列;機器學習
(2)、4000行數據對應着4000個角色,ID編號從1到4001;ide
(3)、59列數據中, 第一列爲角色ID,最後一列爲分類結果,即label(0、1兩種),中間的57列爲角色對應的57種屬性值;函數
(4)、數據集地址:https://pan.baidu.com/s/1mG7ndtlT4jWYHH9V-Rj_5g, 提取碼:hwzf 。學習
這是一個典型的二分類問題,結合課上所學內容,決定採用Logistic迴歸算法。優化
與線性迴歸用於預測不一樣,Logistic迴歸則經常使用於分類(一般是二分類問題)。Logistic迴歸實質上就是在普通的線性迴歸後面加上了一個sigmoid函數,把線性迴歸預測到的數值壓縮成爲一個機率,進而實現二分類(關於線性迴歸模型,可參考上一次做業)。編碼
在損失函數方面,Logistic迴歸並無使用傳統的歐式距離來度量偏差,而使用了交叉熵(用於衡量兩個機率分佈之間的類似程度)。spa
在機器學習中,數據的預處理是很是重要的一環,能直接影響到模型效果的好壞。本次做業的數據相對簡單純淨,在數據預處理方面並不須要花太多精力。3d
首先是空值處理(儘管沒看到空值,但爲了以防萬一,仍是作一下),全部空值用0填充(也能夠用平均值、中位數等,視具體狀況而定)。
接着就是把數據範圍儘可能scale到同一個數量級上,觀察數據後發現,多數數據值爲0,非0值也都在1附近,只有倒數第二列和倒數第三列數據值較大,能夠將這兩列分別除上每列的平均值,把數值範圍拉到1附近。
因爲並無給出這57個屬性具體是什麼屬性,所以沒法對數據進行進一步的挖掘應用。
上述操做完成後,將表格的第2列至58列取出爲x(shape爲4000X57),將最後一列取出作label y(shape爲4000X1)。進一步劃分訓練集和驗證集,分別取x、y中前3500個樣本爲訓練集x_test(shape爲3500X57),y_test(shape爲3500X1),後500個樣本爲驗證集x_val(shape爲500X57),y_val(shape爲500X1)。
數據預處理到此結束。
1 # 從csv中讀取有用的信息 2 df = pd.read_csv('spam_train.csv') 3 # 空值填0 4 df = df.fillna(0) 5 # (4000, 59) 6 array = np.array(df) 7 # (4000, 57) 8 x = array[:, 1:-1] 9 # scale 10 x[-1] /= np.mean(x[-1]) 11 x[-2] /= np.mean(x[-2]) 12 # (4000, ) 13 y = array[:, -1] 14 15 # 劃分訓練集與驗證集 16 x_train, x_val = x[0:3500, :], x[3500:4000, :] 17 y_train, y_val = y[0:3500], y[3500:4000]
先對數據作線性迴歸,得出每一個樣本對應的迴歸值。下式爲對第n個樣本的迴歸,迴歸結果爲
。
1 y_pre = weights.dot(x_val[j, :]) + bias
以後將回歸結果送進sigmoid函數,獲得機率值。
1 sig = 1 / (1 + np.exp(-y_pre)
接着就到重頭戲了。衆所周知,無論線性迴歸仍是Logistic迴歸,其關鍵和核心就在於經過偏差的反向傳播來更新參數,進而使模型不斷優化。所以,損失函數的肯定及對各參數的求導就成了重中之重。在分類問題中,模型通常針對各種別輸出一個機率分佈,所以經常使用交叉熵做爲損失函數。交叉熵可用於衡量兩個機率分佈之間的類似、統一程度,兩個機率分佈越類似、越統一,則交叉熵越小;反之,兩機率分佈之間差別越大、越混亂,則交叉熵越大。
下式表示k分類問題的交叉熵,P爲label,是一個機率分佈,經常使用one_hot編碼。例如針對3分類問題而言,若樣本屬於第一類,則P爲(1,0,0),若屬於第二類,則P爲(0,1,0),若屬於第三類,則爲(0,0,1)。即所屬的類機率值爲1,其餘類機率值爲0。Q爲模型得出的機率分佈,能夠是(0.1,0.8,0.1)等。
在實際應用中,爲求導方便,常使用以e爲底的對數。
針對本次做業而言,雖然模型只輸出了一個機率值p,但因爲處理的是二分類問題,所以能夠很快求出另外一機率值爲1-p,便可視爲模型輸出的機率分佈爲Q(p,1-p)。將本次的label視爲機率分佈P(y,1-y),即Winner(label爲1)的機率分佈爲(1,0),分類爲Losser(label爲0)的機率分佈爲(0,1)。
損失函數對權重w求偏導,可得:
由於:
因此有:
同理,損失函數對偏置b求偏導,可得:
1 # 在全部數據上計算梯度,梯度計算時針對損失函數求導,num爲樣本數量 2 for j in range(num): 3 # 線性函數 4 y_pre = weights.dot(x_train[j, :]) + bias 5 # sigmoid函數壓縮回歸值,求得機率 6 sig = 1 / (1 + np.exp(-y_pre)) 7 # 對偏置b求梯度 8 b_g += (-1) * (y_train[j] - sig) 9 # 對權重w求梯度,2 * reg_rate * weights[k] 爲正則項,防止過擬合 10 for k in range(dim): 11 w_g[k] += (-1) * (y_train[j] - sig) * x_train[j, k] + 2 * reg_rate * weights[k]
求出梯度後,再拿原參數減去梯度與學習率的乘積,便可實現參數的更新。
1 # num爲樣本數量 2 b_g /= num 3 w_g /= num 4 5 # adagrad 6 bg2_sum += b_g**2 7 wg2_sum += w_g**2 8 9 # 更新權重和偏置 10 bias -= learning_rate/bg2_sum**0.5 * b_g 11 weights -= learning_rate/wg2_sum**0.5 * w_g
1 import pandas as pd 2 import numpy as np 3 4 5 # 更新參數,訓練模型 6 def train(x_train, y_train, epoch): 7 num = x_train.shape[0] 8 dim = x_train.shape[1] 9 bias = 0 # 偏置值初始化 10 weights = np.ones(dim) # 權重初始化 11 learning_rate = 1 # 初始學習率 12 reg_rate = 0.001 # 正則項係數 13 bg2_sum = 0 # 用於存放偏置值的梯度平方和 14 wg2_sum = np.zeros(dim) # 用於存放權重的梯度平方和 15 16 for i in range(epoch): 17 b_g = 0 18 w_g = np.zeros(dim) 19 # 在全部數據上計算梯度,梯度計算時針對損失函數求導 20 for j in range(num): 21 y_pre = weights.dot(x_train[j, :]) + bias 22 sig = 1 / (1 + np.exp(-y_pre)) 23 b_g += (-1) * (y_train[j] - sig) 24 for k in range(dim): 25 w_g[k] += (-1) * (y_train[j] - sig) * x_train[j, k] + 2 * reg_rate * weights[k] 26 b_g /= num 27 w_g /= num 28 29 # adagrad 30 bg2_sum += b_g ** 2 31 wg2_sum += w_g ** 2 32 # 更新權重和偏置 33 bias -= learning_rate / bg2_sum ** 0.5 * b_g 34 weights -= learning_rate / wg2_sum ** 0.5 * w_g 35 36 # 每訓練100輪,輸出一次在訓練集上的正確率 37 # 在計算loss時,因爲涉及到log()運算,所以可能出現無窮大,計算並打印出來的loss爲nan 38 # 有興趣的同窗能夠把下面涉及到loss運算的註釋去掉,觀察一波打印出的loss 39 if i % 3 == 0: 40 # loss = 0 41 acc = 0 42 result = np.zeros(num) 43 for j in range(num): 44 y_pre = weights.dot(x_train[j, :]) + bias 45 sig = 1 / (1 + np.exp(-y_pre)) 46 if sig >= 0.5: 47 result[j] = 1 48 else: 49 result[j] = 0 50 51 if result[j] == y_train[j]: 52 acc += 1.0 53 # loss += (-1) * (y_train[j] * np.log(sig) + (1 - y_train[j]) * np.log(1 - sig)) 54 # print('after {} epochs, the loss on train data is:'.format(i), loss / num) 55 print('after {} epochs, the acc on train data is:'.format(i), acc / num) 56 57 return weights, bias 58 59 60 # 驗證模型效果 61 def validate(x_val, y_val, weights, bias): 62 num = 500 63 # loss = 0 64 acc = 0 65 result = np.zeros(num) 66 for j in range(num): 67 y_pre = weights.dot(x_val[j, :]) + bias 68 sig = 1 / (1 + np.exp(-y_pre)) 69 if sig >= 0.5: 70 result[j] = 1 71 else: 72 result[j] = 0 73 74 if result[j] == y_val[j]: 75 acc += 1.0 76 # loss += (-1) * (y_val[j] * np.log(sig) + (1 - y_val[j]) * np.log(1 - sig)) 77 return acc / num 78 79 80 def main(): 81 # 從csv中讀取有用的信息 82 df = pd.read_csv('spam_train.csv') 83 # 空值填0 84 df = df.fillna(0) 85 # (4000, 59) 86 array = np.array(df) 87 # (4000, 57) 88 x = array[:, 1:-1] 89 # scale 90 x[:, -1] /= np.mean(x[:, -1]) 91 x[:, -2] /= np.mean(x[:, -2]) 92 # (4000, ) 93 y = array[:, -1] 94 95 # 劃分訓練集與驗證集 96 x_train, x_val = x[0:3500, :], x[3500:4000, :] 97 y_train, y_val = y[0:3500], y[3500:4000] 98 99 epoch = 30 # 訓練輪數 100 # 開始訓練 101 w, b = train(x_train, y_train, epoch) 102 # 在驗證集上看效果 103 acc = validate(x_val, y_val, w, b) 104 print('The acc on val data is:', acc) 105 106 107 if __name__ == '__main__': 108 main()
能夠看出,在訓練30輪後,分類正確率能達到94%左右。
參考資料:
李宏毅老師機器學習課程視頻:https://www.bilibili.com/video/av10590361
李宏毅老師機器學習課程講義資料:http://speech.ee.ntu.edu.tw/~tlkagk/courses_ML17_2.html