[譯] 機器學習可以建模簡單的數學函式嗎?

hu_minghao發表於2019-05-04

使用機器學習建模一些基礎數學函式

Photo Credits: [Unsplash](https://unsplash.com/)

近來,在各種任務上應用機器學習已經成為了一個慣例。似乎在每一個 Gartner's 技術迴圈曲線 上的新興技術都對機器學習有所涉及,這是一種趨勢。這些演算法被當做 figure-out-it-yourself 的模型:將任何型別的資料都分解為一串特徵,應用一些黑盒的機器學習模型,對每個模型求解並選擇結果最好的那個。

但是機器學習真的能解決所有的問題嗎?還是它只適用於一小部分的任務?在這篇文章中,我們試圖回答一個更基本的問題,即機器學習能否推匯出那些在日常生活中經常出現的數學關係。在這裡,我會嘗試使用一些流行的機器學習技術來擬合幾個基礎的函式,並觀察這些演算法能否識別並建模這些基礎的數學關係。

我們將要嘗試的函式:

  • 線性函式
  • 指數函式
  • 對數函式
  • 冪函式
  • 模函式
  • 三角函式

將會用到的機器學習演算法:

  • XGBoost
  • 線性迴歸
  • 支援向量迴歸(SVR)
  • 決策樹
  • 隨機森林
  • 多層感知機(前饋神經網路)

[譯] 機器學習可以建模簡單的數學函式嗎?

資料準備

我會保持因變數(譯者注:原文錯誤,應該為自變數)的維度為 4(選擇這個特殊的數字並沒有什麼原因)。所以,X(自變數)和 Y(因變數)的關係為:

[譯] 機器學習可以建模簡單的數學函式嗎?

f :- 我們將要擬合的函式

Epsilon:- 隨機噪聲(使 Y 更加真實一點,因為現實生活中的資料中總是存在一些噪聲)

每個函式型別都會用到一系列的引數。這些引數通過生成隨機數得到,方法如下:

numpy.random.normal()
numpy.random.randint()
複製程式碼

randint() 用於獲取冪函式的引數,以免 Y 的值特別小。normal() 用於所有其他情況。

生成自變數(即 X):

function_type = 'Linear'

if function_type=='Logarithmic':
    X_train = abs(np.random.normal(loc=5, size=(1000, 4)))
    X_test = abs(np.random.normal(loc=5, size=(500, 4)))
else:
    X_train = np.random.normal(size=(1000, 4))
    X_test = np.random.normal(size=(500, 4))
複製程式碼

對於對數函式,使用均值為 5(均值遠大於方差)的正態分佈來避免得到負值。

獲取因變數(即 Y):

def get_Y(X, function_type, paras):
    X1 = X[:,0]
    X2 = X[:,1]
    X3 = X[:,2]
    X4 = X[:,3]
    if function_type=='Linear':
        [a0, a1, a2, a3, a4] = paras
        noise = np.random.normal(scale=(a1*X1).var(), size=X.shape[0])
        Y = a0+a1*X1+a2*X2+a3*X3+a4*X4+noise
    elif function_type=='Exponential':
        [a0, a1, a2, a3, a4] = paras
        noise = np.random.normal(scale=(a1*np.exp(X1)).var(), size=X.shape[0])
        Y = a0+a1*np.exp(X1)+a2*np.exp(X2)+a3*np.exp(X3)+a4*np.exp(X4)+noise
    elif function_type=='Logarithmic':
        [a0, a1, a2, a3, a4] = paras
        noise = np.random.normal(scale=(a1*np.log(X1)).var(), size=X.shape[0])
        Y = a0+a1*np.log(X1)+a2*np.log(X2)+a3*np.log(X3)+a4*np.log(X4)+noise
    elif function_type=='Power':
        [a0, a1, a2, a3, a4] = paras
        noise = np.random.normal(scale=np.power(X1,a1).var(), size=X.shape[0])
        Y = a0+np.power(X1,a1)+np.power(X2,a2)+np.power(X2,a2)+np.power(X3,a3)+np.power(X4,a4)+noise
    elif function_type=='Modulus':
        [a0, a1, a2, a3, a4] = paras
        noise = np.random.normal(scale=(a1*np.abs(X1)).var(), size=X.shape[0])
        Y = a0+a1*np.abs(X1)+a2*np.abs(X2)+a3*np.abs(X3)+a4*np.abs(X4)+noise
    elif function_type=='Sine':
        [a0, a1, b1, a2, b2, a3, b3, a4, b4] = paras
        noise = np.random.normal(scale=(a1*np.sin(X1)).var(), size=X.shape[0])
        Y = a0+a1*np.sin(X1)+b1*np.cos(X1)+a2*np.sin(X2)+b2*np.cos(X2)+a3*np.sin(X3)+b3*np.cos(X3)+a4*np.sin(X4)+b4*np.cos(X4)+noise
    else:
        print('Unknown function type')

    return Y


if function_type=='Linear':
    paras = [0.35526578, -0.85543226, -0.67566499, -1.97178384, -1.07461643]
    Y_train = get_Y(X_train, function_type, paras)
    Y_test = get_Y(X_test, function_type, paras)
elif function_type=='Exponential':
    paras = [ 0.15644562, -0.13978794, -1.8136447 ,  0.72604755, -0.65264939]
    Y_train = get_Y(X_train, function_type, paras)
    Y_test = get_Y(X_test, function_type, paras)
elif function_type=='Logarithmic':
    paras = [ 0.63278503, -0.7216328 , -0.02688884,  0.63856392,  0.5494543]
    Y_train = get_Y(X_train, function_type, paras)
    Y_test = get_Y(X_test, function_type, paras)
elif function_type=='Power':
    paras = [2, 2, 8, 9, 2]
    Y_train = get_Y(X_train, function_type, paras)
    Y_test = get_Y(X_test, function_type, paras)
elif function_type=='Modulus':
    paras = [ 0.15829356,  1.01611121, -0.3914764 , -0.21559318, -0.39467206]
    Y_train = get_Y(X_train, function_type, paras)
    Y_test = get_Y(X_test, function_type, paras)
elif function_type=='Sine':
    paras = [-2.44751615,  1.89845893,  1.78794848, -2.24497666, -1.34696884, 0.82485303,  0.95871345, -1.4847142 ,  0.67080158]
    Y_train = get_Y(X_train, function_type, paras)
    Y_test = get_Y(X_test, function_type, paras)
複製程式碼

噪聲是在 0 均值的正態分佈中隨機抽樣得到的。我設定了噪聲的方差等於 f(X) 的方差,藉此保證我們資料中的“訊號和噪聲”具有可比性,且噪聲不會在訊號中有損失,反之亦然。

訓練

注意:在任何模型中都沒有做超引數的調參。 我們的基本想法是隻在這些模型對所提及的函式上的表現做一個粗略的估計,因此沒有對這些模型做太多的優化。

model_type = 'MLP'

if model_type=='XGBoost':
    model = xgb.XGBRegressor()
elif model_type=='Linear Regression':
    model = LinearRegression()
elif model_type=='SVR':
    model = SVR()
elif model_type=='Decision Tree':
    model = DecisionTreeRegressor()
elif model_type=='Random Forest':
    model = RandomForestRegressor()
elif model_type=='MLP':
    model = MLPRegressor(hidden_layer_sizes=(10, 10))

model.fit(X_train, Y_train)
複製程式碼

[譯] 機器學習可以建模簡單的數學函式嗎?

結果

Results

大多數的表現結果比平均基線要好很多。計算出的平均R方是 70.83%我們可以說,機器學習技術對這些簡單的數學函式確實可以有效地建模

但是通過這個實驗,我們不僅知道了機器學習能否建模這些函式,同時也瞭解了不同的機器學習技術在各種基礎函式上的表現是怎樣的。

有些結果是令人驚訝的(至少對我來說),有些則是合理的。總之,這些結果重新認定了我們的一些先前的想法,也給出了新的想法。

最後,我們可以得到下列結論:

  • 儘管線性迴歸是一個簡單的模型,但是線上性相關的資料上,它的表現是優於其他模型的
  • 大多數情況下,決策樹 < 隨機森林 < XGBoost,這是根據實驗的表現得到的(在以上 6 個結果中有 5 個是顯而易見的)
  • 不像最近實踐中的流行趨勢那樣,XGBoost(6 個結果中只有 2 個表現最好)不應該成為所有型別的列表資料的一站式解決方案,我們仍然需要對每個模型進行公平地比較。
  • 和我們的猜測相反的是,線性函式不一定是最容易預測的函式。我們在對數函式上得到了最好的聚合R方結果,達到了 92.98%
  • 各種技術在不同的基礎函式上的效果(相對地)差異十分大,因此,對一個任務選擇何種技術必須經過完善的思考和實驗

完整程式碼見我的 github


來點贊,評論和分享吧。建設性的批評和反饋總是受歡迎的!

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章