1. 程式人生 > 實用技巧 >權重不確定的概率線性迴歸

權重不確定的概率線性迴歸

作者|Ruben Winastwan
編譯|VK
來源|Towards Data Science

當你學習資料科學和機器學習時,線性迴歸可能是你遇到的第一個統計方法。我猜這不是你們第一次使用線性迴歸了。因此,在本文中,我想討論概率線性迴歸,而不是典型的/確定性線性迴歸。

但在此之前,讓我們簡要討論一下確定性線性迴歸的概念,以便快速瞭解本文的主要討論要點。

線性迴歸是一種基本的統計方法,用來建立一個或多個輸入變數(或自變數)與一個或多個輸出變數(或因變數)之間的線性關係。

上式中,a為截距,b為斜率。x是自變數,y是因變數,也就是我們要預測的值。

a和b的值需要用梯度下降演算法進行優化。然後,我們得到了一條自變數和因變數之間最合適的迴歸直線。通過迴歸線,我們可以預測任意輸入x的y的值。這些是如何建立典型的或確定性的線性迴歸演算法的步驟。

然而,這種確定性的線性迴歸演算法並不能真正描述資料。這是為什麼呢?

實際上,當我們進行線性迴歸分析時,會出現兩種不確定性:

  • 任意不確定性,即由資料產生的不確定性。
  • 認知的不確定性,這是從迴歸模型中產生的不確定性。

我將在文章中詳細闡述這些不確定性。考慮到這些不確定性,應採用概率線性迴歸代替確定性線性迴歸。

在本文中,我們將討論概率線性迴歸以及它與確定性線性迴歸的區別。我們將首先看到確定性線性迴歸是如何在TensorFlow中構建的,然後我們將繼續構建一個包含TensorFlow概率的概率線性迴歸模型。

首先,讓我們從載入本文將使用的資料集開始。

載入和預處理資料

本文將使用的資料集是car的MPG資料集。像往常一樣,我們可以用pandas載入資料。

import pandas as pd

auto_data = pd.read_csv('auto-mpg.csv')
auto_data.head()

以下是資料的統計彙總。

接下來,我們可以使用下面的程式碼檢視資料集中變數之間的相關性。

import matplotlib.pyplot as plt
import seaborn as sns

corr_df = auto_data.corr()

sns.heatmap(corr_df, cmap="YlGnBu", annot = True)
plt.show()

現在如果我們看一下相關性,汽車的每加侖英里數(MPG)和汽車的重量有很強的負相關性。

在本文中,為了視覺化的目的,我將做一個簡單的線性迴歸分析。自變數是車的重量,因變數是車的MPG。

現在,讓我們用Scikit-learn將資料分解為訓練資料和測試資料。拆分資料後,我們現在可以縮放因變數和自變數。這是為了確保兩個變數在相同的範圍內這也將提高線性迴歸模型的收斂速度。

from sklearn import preprocessing
from sklearn.model_selection import train_test_split

x = auto_data['weight']
y = auto_data['mpg']

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state=5)

min_max_scaler = preprocessing.MinMaxScaler()

x_train_minmax = min_max_scaler.fit_transform(x_train.values.reshape(len(x_train),1))
y_train_minmax = min_max_scaler.fit_transform(y_train.values.reshape(len(y_train),1))
x_test_minmax = min_max_scaler.fit_transform(x_test.values.reshape(len(x_test),1))
y_test_minmax = min_max_scaler.fit_transform(y_test.values.reshape(len(y_test),1))

現在如果我們將訓練資料視覺化,我們得到如下視覺化:

太棒了!接下來,讓我們繼續使用TensorFlow構建我們的確定性線性迴歸模型。


基於TensorFlow的確定性線性迴歸

用TensorFlow建立一個簡單的線性迴歸模型是非常容易的。我們所需要做的就是建立一個沒有任何啟用函式的單一全連線層模型。對於成本函式,通常使用均方誤差。在本例中,我將使用RMSprop作為優化器,模型將在100個epoch內進行訓練。我們可以用下面的幾行程式碼構建和訓練模型。

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.losses import MeanSquaredError

model = Sequential([
    Dense(units=1, input_shape=(1,))
])

model.compile(loss=MeanSquaredError(), optimizer=RMSprop(learning_rate=0.01))
history = model.fit(x_train_minmax, y_train_minmax, epochs=100, verbose=False)

在我們訓練了模型之後,讓我們看看模型的損失來檢查損失的收斂性。

def plot_loss(history):
    
  plt.plot(history.history['loss'], label='loss')
  plt.xlabel('Epoch')
  plt.ylabel('Error')
  plt.legend()
  plt.grid(True)
  
plot_loss(history)

看來損失已經收斂了。現在,如果我們使用訓練過的模型來預測測試集,我們可以看到下面的迴歸線。

就是這樣。我們完成了!

正如我前面提到的,使用TensorFlow構建一個簡單的線性迴歸模型是非常容易的。有了迴歸線,我們現在可以在任何給定的汽車的重量輸入來近似的汽車的MPG。舉個例子,假設特徵縮放後的汽車重量是0.64。通過將該值傳遞給訓練過的模型,我們可以得到相應的汽車MPG值,如下所示。

現在你可以看到,模型預測汽車的MPG是0.21。簡單地說,對於任何給定的汽車重量,我們得到一個確定的汽車MPG值

然而,輸出並不能說明全部問題。這裡我們應該注意兩件事。首先,我們只有有限的資料點。第二,正如我們從線性迴歸圖中看到的,大多數資料點並不是真的在迴歸線上。

雖然我們得到的輸出值是0.21,但我們知道實際汽車的MPG不是確切的0.21。它可以略低於這個值,也可以略高於這個值。換句話說,需要考慮到不確定性。這種不確定性稱為任意不確定性。

確定性線性迴歸不能捕捉資料的任意不確定性。為了捕捉這種任意的不確定性,可以使用概率線性迴歸代替。

TensorFlow概率的概率線性迴歸

由於有了TensorFlow概率,建立概率線性迴歸模型也非常容易。但是,你需要首先安裝tensorflow_probability庫。你可以使用pip命令安裝它,如下所示:

pip install tensorflow_probability

安裝此庫的先決條件是你需要擁有TensorFlow 2.3.0版本。因此,請確保在安裝TensorFlow Probability之前升級你的TensorFlow版本。

針對不確定性建立概率線性迴歸模型

在本節中,我們將建立一個考慮不確定性的概率線性迴歸模型。

這個模型與確定性線性迴歸非常相似。但是,與之前只使用一個單一的全連線層不同,我們需要再新增一個層作為最後一層。最後一層將最終輸出值從確定性轉換為概率分佈。

在本例中,我們將建立最後一層,它將輸出值轉換為正態分佈的概率值。下面是它的實現。

import tensorflow_probability as tfp
import tensorflow as tf
tfd = tfp.distributions
tfpl = tfp.layers

model = Sequential([
  Dense(units=1+1, input_shape=(1,)),
  tfpl.DistributionLambda(
      lambda t: tfd.Normal(loc=t[..., :1],
                           scale=tf.math.softplus(t[...,1:]))),
])

注意,我們在最後應用了一個額外的層TensorFlow概率層。該層將前一個全連線層的兩個輸出(一個為均值,一個為標準差)轉化為具有可訓練均值(loc)和標準差(scale)正態分佈的概率值。

我們可以使用RMSprop作為優化器,但是如果你願意,你也可以使用其他優化器。對於損失函式,我們需要使用負對數似然。

但是為什麼我們使用負對數似然作為損失函式呢

負對數似然作為成本函式

為了對一些資料擬合一個分佈,我們需要使用似然函式。通過似然函式,我們在給定我們在資料中嘗試估計未知的引數(例如,正態分佈資料的平均值和標準差)。

在我們的概率迴歸模型中,優化器的工作是找到未知引數的最大似然估計。換句話說,我們訓練模型從我們的資料中找到最有可能的引數值。

最大化似然估計和最小化負對數似然是一樣的。在優化領域,通常目標是最小化成本而不是最大化成本。這就是為什麼我們使用負對數似然作為代價函式。

下面是實現的負對數似然作為我們的自定義損失函式。

def negative_log_likelihood(y_true, y_pred):
    
    return -y_pred.log_prob(y_true)

隨機不確定性概率線性迴歸模型的訓練與預測結果

現在我們已經構建了模型並定義了優化器以及loss函式,接下來讓我們編譯並訓練這個模型。

model.compile(optimizer=RMSprop(learning_rate=0.01), loss=negative_log_likelihood)
history = model.fit(x_train_minmax, y_train_minmax, epochs=200, verbose=False);

現在我們可以從訓練好的模型中抽取樣本。我們可以通過下面的程式碼視覺化測試集和從模型生成的示例之間的比較。

y_model = model(x_test_minmax)
y_sample = y_model.sample()

plt.scatter(x_test_minmax, y_test_minmax, alpha=0.5, label='test data')
plt.scatter(x_test_minmax, y_sample, alpha=0.5, color='green', label='model sample')
plt.xlabel('Weight')
plt.ylabel('MPG')
plt.legend()
plt.show()

正如你從上面的視覺化中看到的,現在對於任何給定的輸入值,模型都不會返回確定性值。相反,它將返回一個分佈,並基於該分佈繪製一個樣本。

如果你比較測試集的資料點(藍點)和訓練模型預測的資料點(綠點),你可能會認為綠點和藍點來自相同的分佈。

接下來,我們還可以視覺化訓練模型生成的分佈的均值和標準差,給定訓練集中的資料。我們可以通過應用以下程式碼來實現這一點。

y_mean = y_model.mean()
y_sd = y_model.stddev()
y_mean_m2sd = y_mean - 2 * y_sd
y_mean_p2sd = y_mean + 2 * y_sd

plt.scatter(x_test_minmax, y_test_minmax, alpha=0.4, label='data')
plt.plot(x_test_minmax, y_mean, color='red', alpha=0.8, label='model $\mu$')
plt.plot(x_test_minmax, y_mean_m2sd, color='green', alpha=0.8, label='model $\mu \pm 2 \sigma$')
plt.plot(x_test_minmax, y_mean_p2sd, color='green', alpha=0.8)
plt.xlabel('Weight')
plt.ylabel('MPG')
plt.legend()
plt.show()

我們可以看到,概率線性迴歸模型給我們比迴歸線更多。它也給出了資料的標準差的近似值。可以看出,大約95%的測試集資料點位於兩個標準差範圍內。

建立隨機和認知不確定性的概率線性迴歸模型

到目前為止,我們已經建立了一個概率迴歸模型,它考慮了來自資料的不確定性,或者我們稱之為任意不確定性。

然而,在現實中,我們還需要處理來自迴歸模型本身的不確定性。由於資料的不完善,迴歸引數的權重或斜率也存在不確定性。這種不確定性稱為認知不確定性。

到目前為止,我們建立的概率模型只考慮了一個確定的權重。正如你從視覺化中看到的,模型只生成一條迴歸線,而且這通常不是完全準確的。

在本節中,我們將改進同時考慮任意和認知不確定性的概率迴歸模型。我們可以使用貝葉斯的觀點來引入迴歸權值的不確定性。

首先,在我們看到資料之前,我們需要定義我們之前對權重分佈的看法。通常,我們不知道會發生什麼,對吧?為了簡單起見,我們假設權值的分佈是正態分佈均值為0,標準差為1。

def prior(kernel_size, bias_size, dtype=None):
  n = kernel_size + bias_size
  return Sequential([
      tfpl.DistributionLambda(lambda t: tfd.Independent(
          tfd.Normal(loc=tf.zeros(n), scale=tf.ones(n))))
  ])

由於我們硬編碼了平均值和標準差,這種先驗是不可訓練的。

接下來,我們需要定義迴歸權重的後驗分佈。後驗分佈顯示了我們的信念在看到資料中的模式後發生了怎樣的變化。因此,該後驗分佈中的引數是可訓練的。下面是定義後驗分佈的程式碼實現。

def posterior(kernel_size, bias_size, dtype=None):
  n = kernel_size + bias_size
  return Sequential([
      tfpl.VariableLayer(2 * n, dtype=dtype),
      tfpl.DistributionLambda(lambda t: tfd.Independent(
          tfd.Normal(loc=t[..., :n],
                     scale=tf.nn.softplus(t[..., n:]))))
  ])

現在的問題是,這個後驗函式中的變數定義是什麼?這個可變層背後的想法是我們試圖接近真實的後驗分佈。一般情況下,不可能推匯出真正的後驗分佈,因此我們需要對其進行近似。

在定義了先驗函式和後驗函式後,我們可以建立權重不確定性的概率線性迴歸模型。下面是它的程式碼實現。

model = Sequential([
    tfpl.DenseVariational(units = 1 + 1, 
                          make_prior_fn = prior,
                          make_posterior_fn = posterior,
                          kl_weight=1/x.shape[0]),
    tfpl.DistributionLambda(
      lambda t: tfd.Normal(loc=t[..., :1],
                           scale=tf.math.softplus(t[...,1:])))
])

正如你可能注意到的,此模型與前面的概率迴歸模型之間的唯一區別只是第一層。我們用DenseVariational層代替了普通的全連線層。在這一層中,我們通過前面和後面的函式作為引數。第二層與前面的模型完全相同。

隨機不確定性和認知不確定性概率線性迴歸模型的訓練和預測結果

現在是時候編譯和訓練模型了。

優化器和成本函式仍然與以前的模型相同。我們使用RMSprop作為優化器和負對數似然作為我們的成本函式。讓我們編譯和訓練。

model.compile(optimizer= RMSprop(learning_rate=0.01), loss=negative_log_likelihood)
history = model.fit(x_train_minmax, y_train_minmax, epochs=500, verbose=False);

現在是時候視覺化迴歸模型的權值或斜率的不確定性了。下面是視覺化結果的程式碼實現。

plt.scatter(x_test_minmax, y_test_minmax, marker='.', alpha=0.8, label='data')
for i in range(10):
    y_model = model(x_test_minmax)
    y_mean = y_model.mean()
    y_mean_m2sd = y_mean - 2 * y_model.stddev()
    y_mean_p2sd = y_mean + 2 * y_model.stddev()
    
    if i == 0:
        plt.plot(x_test_minmax, y_mean, color='red', alpha=0.8, label='model $\mu$')
        plt.plot(x_test_minmax, y_mean_m2sd, color='green', alpha=0.8, label='model $\mu \pm 2 \sigma$')
        plt.plot(x_test_minmax, y_mean_p2sd, color='green', alpha=0.8)
    else:
        plt.plot(x_test_minmax, y_mean, color='red', alpha=0.8)
        plt.plot(x_test_minmax, y_mean_m2sd, color='green', alpha=0.8)
        plt.plot(x_test_minmax, y_mean_p2sd, color='green', alpha=0.8)
plt.xlabel('Weight')
plt.ylabel('MPG')
plt.legend()
plt.show()

在上面的視覺化圖中,你可以看到,經過訓練的模型的後驗分佈產生的線性線(均值)和標準差在每次迭代中都是不同的。所有這些線都是擬合測試集中資料點的合理解決方案。但是,由於認知的不確定性,我們不知道哪條線是最好的。

通常,我們擁有的資料點越多,我們看到的迴歸線的不確定性就越小。


今晚

現在你已經看到了概率線性迴歸與確定性線性迴歸的不同之處。在概率線性迴歸中,兩種不確定性產生於資料(任意)和迴歸模型(認知)可以被考慮在內。

如果我們想建立一個深度學習模型,讓不準確的預測導致非常嚴重的負面後果,例如在自動駕駛和醫療診斷領域,考慮這些不確定性是非常重要的。

通常,當我們有更多的資料點時,模型的認知不確定性將會減少。

原文連結:https://towardsdatascience.com/probabilistic-linear-regression-with-weight-uncertainty-a649de11f52b

歡迎關注磐創AI部落格站:
http://panchuang.net/

sklearn機器學習中文官方文件:
http://sklearn123.com/

歡迎關注磐創部落格資源彙總站:
http://docs.panchuang.net/